putty-0.76/0000755000175000017500000000000014072266321007637 500000000000000putty-0.76/Makefile.in0000644000175000017500000042242514072266321011635 00000000000000# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ # Makefile.am for putty under Unix with Autoconf/Automake. # # This file was created by `mkfiles.pl' from the `Recipe' file. # DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead. VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : @HAVE_GTK_FALSE@bin_PROGRAMS = plink$(EXEEXT) pscp$(EXEEXT) \ @HAVE_GTK_FALSE@ psftp$(EXEEXT) psusan$(EXEEXT) \ @HAVE_GTK_FALSE@ puttygen$(EXEEXT) @HAVE_GTK_TRUE@bin_PROGRAMS = plink$(EXEEXT) pscp$(EXEEXT) \ @HAVE_GTK_TRUE@ psftp$(EXEEXT) psusan$(EXEEXT) \ @HAVE_GTK_TRUE@ puttygen$(EXEEXT) pageant$(EXEEXT) \ @HAVE_GTK_TRUE@ pterm$(EXEEXT) putty$(EXEEXT) puttytel$(EXEEXT) @HAVE_GTK_FALSE@noinst_PROGRAMS = cgtest$(EXEEXT) fuzzterm$(EXEEXT) \ @HAVE_GTK_FALSE@ osxlaunch$(EXEEXT) psocks$(EXEEXT) \ @HAVE_GTK_FALSE@ testcrypt$(EXEEXT) testsc$(EXEEXT) \ @HAVE_GTK_FALSE@ testzlib$(EXEEXT) uppity$(EXEEXT) @HAVE_GTK_TRUE@noinst_PROGRAMS = cgtest$(EXEEXT) fuzzterm$(EXEEXT) \ @HAVE_GTK_TRUE@ osxlaunch$(EXEEXT) psocks$(EXEEXT) \ @HAVE_GTK_TRUE@ testcrypt$(EXEEXT) testsc$(EXEEXT) \ @HAVE_GTK_TRUE@ testzlib$(EXEEXT) uppity$(EXEEXT) \ @HAVE_GTK_TRUE@ ptermapp$(EXEEXT) puttyapp$(EXEEXT) @AUTO_GIT_COMMIT_TRUE@am__append_1 = -DSOURCE_COMMIT=\"`git --git-dir=$(srcdir)/.git rev-parse HEAD 2>/dev/null`\" subdir = . ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(top_srcdir)/configure \ $(am__configure_deps) $(am__DIST_COMMON) am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \ configure.lineno config.status.lineno mkinstalldirs = $(install_sh) -d CONFIG_HEADER = uxconfig.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(man1dir)" PROGRAMS = $(bin_PROGRAMS) $(noinst_PROGRAMS) LIBRARIES = $(noinst_LIBRARIES) ARFLAGS = cru AM_V_AR = $(am__v_AR_@AM_V@) am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@) am__v_AR_0 = @echo " AR " $@; am__v_AR_1 = libversion_a_AR = $(AR) $(ARFLAGS) libversion_a_LIBADD = am_libversion_a_OBJECTS = libversion_a-version.$(OBJEXT) libversion_a_OBJECTS = $(am_libversion_a_OBJECTS) am__dirstamp = $(am__leading_dot)dirstamp am_cgtest_OBJECTS = cgtest.$(OBJEXT) conf.$(OBJEXT) console.$(OBJEXT) \ ecc.$(OBJEXT) import.$(OBJEXT) marshal.$(OBJEXT) \ memory.$(OBJEXT) millerrabin.$(OBJEXT) misc.$(OBJEXT) \ mpint.$(OBJEXT) mpunsafe.$(OBJEXT) notiming.$(OBJEXT) \ pockle.$(OBJEXT) primecandidate.$(OBJEXT) \ smallprimes.$(OBJEXT) sshaes.$(OBJEXT) sshargon2.$(OBJEXT) \ sshauxcrypt.$(OBJEXT) sshbcrypt.$(OBJEXT) sshblake2.$(OBJEXT) \ sshblowf.$(OBJEXT) sshdes.$(OBJEXT) sshdss.$(OBJEXT) \ sshdssg.$(OBJEXT) sshecc.$(OBJEXT) sshecdsag.$(OBJEXT) \ sshhmac.$(OBJEXT) sshmd5.$(OBJEXT) sshprime.$(OBJEXT) \ sshprng.$(OBJEXT) sshpubk.$(OBJEXT) sshrand.$(OBJEXT) \ sshrsa.$(OBJEXT) sshrsag.$(OBJEXT) sshsh256.$(OBJEXT) \ sshsh512.$(OBJEXT) sshsha.$(OBJEXT) sshsha3.$(OBJEXT) \ stripctrl.$(OBJEXT) time.$(OBJEXT) tree234.$(OBJEXT) \ unix/uxcons.$(OBJEXT) unix/uxgen.$(OBJEXT) \ unix/uxmisc.$(OBJEXT) unix/uxnogtk.$(OBJEXT) \ unix/uxnoise.$(OBJEXT) unix/uxpoll.$(OBJEXT) \ unix/uxstore.$(OBJEXT) unix/uxutils.$(OBJEXT) utils.$(OBJEXT) \ wcwidth.$(OBJEXT) cgtest_OBJECTS = $(am_cgtest_OBJECTS) cgtest_DEPENDENCIES = libversion.a am_fuzzterm_OBJECTS = be_none.$(OBJEXT) callback.$(OBJEXT) \ charset/fromucs.$(OBJEXT) charset/localenc.$(OBJEXT) \ charset/macenc.$(OBJEXT) charset/mimeenc.$(OBJEXT) \ charset/sbcs.$(OBJEXT) charset/sbcsdat.$(OBJEXT) \ charset/slookup.$(OBJEXT) charset/toucs.$(OBJEXT) \ charset/utf8.$(OBJEXT) charset/xenc.$(OBJEXT) conf.$(OBJEXT) \ config.$(OBJEXT) dialog.$(OBJEXT) fuzzterm.$(OBJEXT) \ logging.$(OBJEXT) marshal.$(OBJEXT) memory.$(OBJEXT) \ minibidi.$(OBJEXT) misc.$(OBJEXT) miscucs.$(OBJEXT) \ settings.$(OBJEXT) stripctrl.$(OBJEXT) terminal.$(OBJEXT) \ time.$(OBJEXT) timing.$(OBJEXT) tree234.$(OBJEXT) \ unix/uxcfg.$(OBJEXT) unix/uxmisc.$(OBJEXT) \ unix/uxnogtk.$(OBJEXT) unix/uxprint.$(OBJEXT) \ unix/uxstore.$(OBJEXT) unix/uxucs.$(OBJEXT) utils.$(OBJEXT) \ wcwidth.$(OBJEXT) fuzzterm_OBJECTS = $(am_fuzzterm_OBJECTS) fuzzterm_DEPENDENCIES = libversion.a am_osxlaunch_OBJECTS = unix/osxlaunch.$(OBJEXT) osxlaunch_OBJECTS = $(am_osxlaunch_OBJECTS) osxlaunch_LDADD = $(LDADD) am__pageant_SOURCES_DIST = aqsync.c be_misc.c be_none.c callback.c \ conf.c console.c ecc.c errsock.c logging.c marshal.c memory.c \ misc.c mpint.c nocproxy.c nogss.c nullplug.c pageant.c proxy.c \ settings.c sshaes.c sshargon2.c sshauxcrypt.c sshblake2.c \ sshdes.c sshdss.c sshecc.c sshhmac.c sshmd5.c sshprng.c \ sshpubk.c sshrsa.c sshsh256.c sshsh512.c sshsha.c sshsha3.c \ stripctrl.c time.c timing.c tree234.c unix/gtkask.c \ unix/gtkmisc.c unix/ux_x11.c unix/uxagentc.c \ unix/uxagentsock.c unix/uxcliloop.c unix/uxcons.c \ unix/uxfdsock.c unix/uxmisc.c unix/uxnet.c unix/uxnoise.c \ unix/uxpeer.c unix/uxpgnt.c unix/uxpoll.c unix/uxproxy.c \ unix/uxsel.c unix/uxsignal.c unix/uxstore.c unix/uxutils.c \ utils.c wcwidth.c x11fwd.c @HAVE_GTK_TRUE@am_pageant_OBJECTS = aqsync.$(OBJEXT) be_misc.$(OBJEXT) \ @HAVE_GTK_TRUE@ be_none.$(OBJEXT) callback.$(OBJEXT) \ @HAVE_GTK_TRUE@ conf.$(OBJEXT) console.$(OBJEXT) ecc.$(OBJEXT) \ @HAVE_GTK_TRUE@ errsock.$(OBJEXT) logging.$(OBJEXT) \ @HAVE_GTK_TRUE@ marshal.$(OBJEXT) memory.$(OBJEXT) \ @HAVE_GTK_TRUE@ misc.$(OBJEXT) mpint.$(OBJEXT) \ @HAVE_GTK_TRUE@ nocproxy.$(OBJEXT) nogss.$(OBJEXT) \ @HAVE_GTK_TRUE@ nullplug.$(OBJEXT) pageant.$(OBJEXT) \ @HAVE_GTK_TRUE@ proxy.$(OBJEXT) settings.$(OBJEXT) \ @HAVE_GTK_TRUE@ sshaes.$(OBJEXT) sshargon2.$(OBJEXT) \ @HAVE_GTK_TRUE@ sshauxcrypt.$(OBJEXT) sshblake2.$(OBJEXT) \ @HAVE_GTK_TRUE@ sshdes.$(OBJEXT) sshdss.$(OBJEXT) \ @HAVE_GTK_TRUE@ sshecc.$(OBJEXT) sshhmac.$(OBJEXT) \ @HAVE_GTK_TRUE@ sshmd5.$(OBJEXT) sshprng.$(OBJEXT) \ @HAVE_GTK_TRUE@ sshpubk.$(OBJEXT) sshrsa.$(OBJEXT) \ @HAVE_GTK_TRUE@ sshsh256.$(OBJEXT) sshsh512.$(OBJEXT) \ @HAVE_GTK_TRUE@ sshsha.$(OBJEXT) sshsha3.$(OBJEXT) \ @HAVE_GTK_TRUE@ stripctrl.$(OBJEXT) time.$(OBJEXT) \ @HAVE_GTK_TRUE@ timing.$(OBJEXT) tree234.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/gtkask.$(OBJEXT) unix/gtkmisc.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/ux_x11.$(OBJEXT) unix/uxagentc.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/uxagentsock.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/uxcliloop.$(OBJEXT) unix/uxcons.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/uxfdsock.$(OBJEXT) unix/uxmisc.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/uxnet.$(OBJEXT) unix/uxnoise.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/uxpeer.$(OBJEXT) unix/uxpgnt.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/uxpoll.$(OBJEXT) unix/uxproxy.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/uxsel.$(OBJEXT) unix/uxsignal.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/uxstore.$(OBJEXT) unix/uxutils.$(OBJEXT) \ @HAVE_GTK_TRUE@ utils.$(OBJEXT) wcwidth.$(OBJEXT) \ @HAVE_GTK_TRUE@ x11fwd.$(OBJEXT) pageant_OBJECTS = $(am_pageant_OBJECTS) am__DEPENDENCIES_1 = @HAVE_GTK_TRUE@pageant_DEPENDENCIES = libversion.a \ @HAVE_GTK_TRUE@ $(am__DEPENDENCIES_1) am_plink_OBJECTS = agentf.$(OBJEXT) aqsync.$(OBJEXT) \ be_all_s.$(OBJEXT) be_misc.$(OBJEXT) callback.$(OBJEXT) \ clicons.$(OBJEXT) cmdline.$(OBJEXT) conf.$(OBJEXT) \ console.$(OBJEXT) cproxy.$(OBJEXT) ecc.$(OBJEXT) \ errsock.$(OBJEXT) ldisc.$(OBJEXT) logging.$(OBJEXT) \ mainchan.$(OBJEXT) marshal.$(OBJEXT) memory.$(OBJEXT) \ misc.$(OBJEXT) mpint.$(OBJEXT) noterm.$(OBJEXT) \ nullplug.$(OBJEXT) pgssapi.$(OBJEXT) pinger.$(OBJEXT) \ portfwd.$(OBJEXT) proxy.$(OBJEXT) raw.$(OBJEXT) \ rlogin.$(OBJEXT) sessprep.$(OBJEXT) settings.$(OBJEXT) \ ssh.$(OBJEXT) ssh1bpp.$(OBJEXT) ssh1censor.$(OBJEXT) \ ssh1connection-client.$(OBJEXT) ssh1connection.$(OBJEXT) \ ssh1login.$(OBJEXT) ssh2bpp-bare.$(OBJEXT) ssh2bpp.$(OBJEXT) \ ssh2censor.$(OBJEXT) ssh2connection-client.$(OBJEXT) \ ssh2connection.$(OBJEXT) ssh2kex-client.$(OBJEXT) \ ssh2transhk.$(OBJEXT) ssh2transport.$(OBJEXT) \ ssh2userauth.$(OBJEXT) sshaes.$(OBJEXT) ssharcf.$(OBJEXT) \ sshargon2.$(OBJEXT) sshauxcrypt.$(OBJEXT) sshblake2.$(OBJEXT) \ sshblowf.$(OBJEXT) sshccp.$(OBJEXT) sshcommon.$(OBJEXT) \ sshcrc.$(OBJEXT) sshcrcda.$(OBJEXT) sshdes.$(OBJEXT) \ sshdh.$(OBJEXT) sshdss.$(OBJEXT) sshecc.$(OBJEXT) \ sshgssc.$(OBJEXT) sshhmac.$(OBJEXT) sshmac.$(OBJEXT) \ sshmd5.$(OBJEXT) sshprng.$(OBJEXT) sshpubk.$(OBJEXT) \ sshrand.$(OBJEXT) sshrsa.$(OBJEXT) sshsh256.$(OBJEXT) \ sshsh512.$(OBJEXT) sshsha.$(OBJEXT) sshsha3.$(OBJEXT) \ sshshare.$(OBJEXT) sshutils.$(OBJEXT) sshverstring.$(OBJEXT) \ sshzlib.$(OBJEXT) stripctrl.$(OBJEXT) supdup.$(OBJEXT) \ telnet.$(OBJEXT) time.$(OBJEXT) timing.$(OBJEXT) \ tree234.$(OBJEXT) unix/ux_x11.$(OBJEXT) \ unix/uxagentc.$(OBJEXT) unix/uxcliloop.$(OBJEXT) \ unix/uxcons.$(OBJEXT) unix/uxfdsock.$(OBJEXT) \ unix/uxgss.$(OBJEXT) unix/uxmisc.$(OBJEXT) \ unix/uxnet.$(OBJEXT) unix/uxnogtk.$(OBJEXT) \ unix/uxnoise.$(OBJEXT) unix/uxpeer.$(OBJEXT) \ unix/uxplink.$(OBJEXT) unix/uxpoll.$(OBJEXT) \ unix/uxproxy.$(OBJEXT) unix/uxsel.$(OBJEXT) \ unix/uxser.$(OBJEXT) unix/uxshare.$(OBJEXT) \ unix/uxsignal.$(OBJEXT) unix/uxstore.$(OBJEXT) \ unix/uxutils.$(OBJEXT) utils.$(OBJEXT) wcwidth.$(OBJEXT) \ wildcard.$(OBJEXT) x11fwd.$(OBJEXT) plink_OBJECTS = $(am_plink_OBJECTS) plink_DEPENDENCIES = libversion.a am_pscp_OBJECTS = agentf.$(OBJEXT) aqsync.$(OBJEXT) be_misc.$(OBJEXT) \ be_ssh.$(OBJEXT) callback.$(OBJEXT) clicons.$(OBJEXT) \ cmdline.$(OBJEXT) conf.$(OBJEXT) console.$(OBJEXT) \ cproxy.$(OBJEXT) ecc.$(OBJEXT) errsock.$(OBJEXT) \ logging.$(OBJEXT) mainchan.$(OBJEXT) marshal.$(OBJEXT) \ memory.$(OBJEXT) misc.$(OBJEXT) mpint.$(OBJEXT) \ nullplug.$(OBJEXT) pgssapi.$(OBJEXT) pinger.$(OBJEXT) \ portfwd.$(OBJEXT) proxy.$(OBJEXT) pscp.$(OBJEXT) \ psftpcommon.$(OBJEXT) settings.$(OBJEXT) sftp.$(OBJEXT) \ sftpcommon.$(OBJEXT) ssh.$(OBJEXT) ssh1bpp.$(OBJEXT) \ ssh1censor.$(OBJEXT) ssh1connection-client.$(OBJEXT) \ ssh1connection.$(OBJEXT) ssh1login.$(OBJEXT) \ ssh2bpp-bare.$(OBJEXT) ssh2bpp.$(OBJEXT) ssh2censor.$(OBJEXT) \ ssh2connection-client.$(OBJEXT) ssh2connection.$(OBJEXT) \ ssh2kex-client.$(OBJEXT) ssh2transhk.$(OBJEXT) \ ssh2transport.$(OBJEXT) ssh2userauth.$(OBJEXT) \ sshaes.$(OBJEXT) ssharcf.$(OBJEXT) sshargon2.$(OBJEXT) \ sshauxcrypt.$(OBJEXT) sshblake2.$(OBJEXT) sshblowf.$(OBJEXT) \ sshccp.$(OBJEXT) sshcommon.$(OBJEXT) sshcrc.$(OBJEXT) \ sshcrcda.$(OBJEXT) sshdes.$(OBJEXT) sshdh.$(OBJEXT) \ sshdss.$(OBJEXT) sshecc.$(OBJEXT) sshgssc.$(OBJEXT) \ sshhmac.$(OBJEXT) sshmac.$(OBJEXT) sshmd5.$(OBJEXT) \ sshprng.$(OBJEXT) sshpubk.$(OBJEXT) sshrand.$(OBJEXT) \ sshrsa.$(OBJEXT) sshsh256.$(OBJEXT) sshsh512.$(OBJEXT) \ sshsha.$(OBJEXT) sshsha3.$(OBJEXT) sshshare.$(OBJEXT) \ sshutils.$(OBJEXT) sshverstring.$(OBJEXT) sshzlib.$(OBJEXT) \ stripctrl.$(OBJEXT) time.$(OBJEXT) timing.$(OBJEXT) \ tree234.$(OBJEXT) unix/uxagentc.$(OBJEXT) \ unix/uxcliloop.$(OBJEXT) unix/uxcons.$(OBJEXT) \ unix/uxfdsock.$(OBJEXT) unix/uxgss.$(OBJEXT) \ unix/uxmisc.$(OBJEXT) unix/uxnet.$(OBJEXT) \ unix/uxnogtk.$(OBJEXT) unix/uxnoise.$(OBJEXT) \ unix/uxpeer.$(OBJEXT) unix/uxpoll.$(OBJEXT) \ unix/uxproxy.$(OBJEXT) unix/uxsel.$(OBJEXT) \ unix/uxsftp.$(OBJEXT) unix/uxshare.$(OBJEXT) \ unix/uxstore.$(OBJEXT) unix/uxutils.$(OBJEXT) utils.$(OBJEXT) \ wcwidth.$(OBJEXT) wildcard.$(OBJEXT) x11fwd.$(OBJEXT) pscp_OBJECTS = $(am_pscp_OBJECTS) pscp_DEPENDENCIES = libversion.a am_psftp_OBJECTS = agentf.$(OBJEXT) aqsync.$(OBJEXT) be_misc.$(OBJEXT) \ be_ssh.$(OBJEXT) callback.$(OBJEXT) clicons.$(OBJEXT) \ cmdline.$(OBJEXT) conf.$(OBJEXT) console.$(OBJEXT) \ cproxy.$(OBJEXT) ecc.$(OBJEXT) errsock.$(OBJEXT) \ logging.$(OBJEXT) mainchan.$(OBJEXT) marshal.$(OBJEXT) \ memory.$(OBJEXT) misc.$(OBJEXT) mpint.$(OBJEXT) \ nullplug.$(OBJEXT) pgssapi.$(OBJEXT) pinger.$(OBJEXT) \ portfwd.$(OBJEXT) proxy.$(OBJEXT) psftp.$(OBJEXT) \ psftpcommon.$(OBJEXT) settings.$(OBJEXT) sftp.$(OBJEXT) \ sftpcommon.$(OBJEXT) ssh.$(OBJEXT) ssh1bpp.$(OBJEXT) \ ssh1censor.$(OBJEXT) ssh1connection-client.$(OBJEXT) \ ssh1connection.$(OBJEXT) ssh1login.$(OBJEXT) \ ssh2bpp-bare.$(OBJEXT) ssh2bpp.$(OBJEXT) ssh2censor.$(OBJEXT) \ ssh2connection-client.$(OBJEXT) ssh2connection.$(OBJEXT) \ ssh2kex-client.$(OBJEXT) ssh2transhk.$(OBJEXT) \ ssh2transport.$(OBJEXT) ssh2userauth.$(OBJEXT) \ sshaes.$(OBJEXT) ssharcf.$(OBJEXT) sshargon2.$(OBJEXT) \ sshauxcrypt.$(OBJEXT) sshblake2.$(OBJEXT) sshblowf.$(OBJEXT) \ sshccp.$(OBJEXT) sshcommon.$(OBJEXT) sshcrc.$(OBJEXT) \ sshcrcda.$(OBJEXT) sshdes.$(OBJEXT) sshdh.$(OBJEXT) \ sshdss.$(OBJEXT) sshecc.$(OBJEXT) sshgssc.$(OBJEXT) \ sshhmac.$(OBJEXT) sshmac.$(OBJEXT) sshmd5.$(OBJEXT) \ sshprng.$(OBJEXT) sshpubk.$(OBJEXT) sshrand.$(OBJEXT) \ sshrsa.$(OBJEXT) sshsh256.$(OBJEXT) sshsh512.$(OBJEXT) \ sshsha.$(OBJEXT) sshsha3.$(OBJEXT) sshshare.$(OBJEXT) \ sshutils.$(OBJEXT) sshverstring.$(OBJEXT) sshzlib.$(OBJEXT) \ stripctrl.$(OBJEXT) time.$(OBJEXT) timing.$(OBJEXT) \ tree234.$(OBJEXT) unix/uxagentc.$(OBJEXT) \ unix/uxcliloop.$(OBJEXT) unix/uxcons.$(OBJEXT) \ unix/uxfdsock.$(OBJEXT) unix/uxgss.$(OBJEXT) \ unix/uxmisc.$(OBJEXT) unix/uxnet.$(OBJEXT) \ unix/uxnogtk.$(OBJEXT) unix/uxnoise.$(OBJEXT) \ unix/uxpeer.$(OBJEXT) unix/uxpoll.$(OBJEXT) \ unix/uxproxy.$(OBJEXT) unix/uxsel.$(OBJEXT) \ unix/uxsftp.$(OBJEXT) unix/uxshare.$(OBJEXT) \ unix/uxstore.$(OBJEXT) unix/uxutils.$(OBJEXT) utils.$(OBJEXT) \ wcwidth.$(OBJEXT) wildcard.$(OBJEXT) x11fwd.$(OBJEXT) psftp_OBJECTS = $(am_psftp_OBJECTS) psftp_DEPENDENCIES = libversion.a am_psocks_OBJECTS = be_misc.$(OBJEXT) callback.$(OBJEXT) \ conf.$(OBJEXT) console.$(OBJEXT) errsock.$(OBJEXT) \ logging.$(OBJEXT) marshal.$(OBJEXT) memory.$(OBJEXT) \ misc.$(OBJEXT) nocproxy.$(OBJEXT) norand.$(OBJEXT) \ portfwd.$(OBJEXT) proxy.$(OBJEXT) psocks.$(OBJEXT) \ sshutils.$(OBJEXT) stripctrl.$(OBJEXT) time.$(OBJEXT) \ timing.$(OBJEXT) tree234.$(OBJEXT) unix/uxcliloop.$(OBJEXT) \ unix/uxcons.$(OBJEXT) unix/uxfdsock.$(OBJEXT) \ unix/uxmisc.$(OBJEXT) unix/uxnet.$(OBJEXT) \ unix/uxnogtk.$(OBJEXT) unix/uxpeer.$(OBJEXT) \ unix/uxpoll.$(OBJEXT) unix/uxproxy.$(OBJEXT) \ unix/uxsel.$(OBJEXT) unix/uxsignal.$(OBJEXT) \ unix/uxsocks.$(OBJEXT) utils.$(OBJEXT) wcwidth.$(OBJEXT) psocks_OBJECTS = $(am_psocks_OBJECTS) psocks_DEPENDENCIES = libversion.a am_psusan_OBJECTS = be_misc.$(OBJEXT) be_none.$(OBJEXT) \ callback.$(OBJEXT) conf.$(OBJEXT) cproxy.$(OBJEXT) \ ecc.$(OBJEXT) errsock.$(OBJEXT) logging.$(OBJEXT) \ marshal.$(OBJEXT) memory.$(OBJEXT) millerrabin.$(OBJEXT) \ misc.$(OBJEXT) mpint.$(OBJEXT) mpunsafe.$(OBJEXT) \ nogss.$(OBJEXT) nullplug.$(OBJEXT) pgssapi.$(OBJEXT) \ pockle.$(OBJEXT) portfwd.$(OBJEXT) primecandidate.$(OBJEXT) \ proxy.$(OBJEXT) scpserver.$(OBJEXT) sesschan.$(OBJEXT) \ settings.$(OBJEXT) sftpcommon.$(OBJEXT) sftpserver.$(OBJEXT) \ smallprimes.$(OBJEXT) ssh1bpp.$(OBJEXT) ssh1censor.$(OBJEXT) \ ssh1connection-server.$(OBJEXT) ssh1connection.$(OBJEXT) \ ssh1login-server.$(OBJEXT) ssh2bpp-bare.$(OBJEXT) \ ssh2bpp.$(OBJEXT) ssh2censor.$(OBJEXT) \ ssh2connection-server.$(OBJEXT) ssh2connection.$(OBJEXT) \ ssh2kex-server.$(OBJEXT) ssh2transhk.$(OBJEXT) \ ssh2transport.$(OBJEXT) ssh2userauth-server.$(OBJEXT) \ sshaes.$(OBJEXT) ssharcf.$(OBJEXT) sshargon2.$(OBJEXT) \ sshauxcrypt.$(OBJEXT) sshblake2.$(OBJEXT) sshblowf.$(OBJEXT) \ sshccp.$(OBJEXT) sshcommon.$(OBJEXT) sshcrc.$(OBJEXT) \ sshcrcda.$(OBJEXT) sshdes.$(OBJEXT) sshdh.$(OBJEXT) \ sshdss.$(OBJEXT) sshecc.$(OBJEXT) sshgssc.$(OBJEXT) \ sshhmac.$(OBJEXT) sshmac.$(OBJEXT) sshmd5.$(OBJEXT) \ sshprime.$(OBJEXT) sshprng.$(OBJEXT) sshpubk.$(OBJEXT) \ sshrand.$(OBJEXT) sshrsa.$(OBJEXT) sshrsag.$(OBJEXT) \ sshserver.$(OBJEXT) sshsh256.$(OBJEXT) sshsh512.$(OBJEXT) \ sshsha.$(OBJEXT) sshsha3.$(OBJEXT) sshutils.$(OBJEXT) \ sshverstring.$(OBJEXT) sshzlib.$(OBJEXT) stripctrl.$(OBJEXT) \ time.$(OBJEXT) timing.$(OBJEXT) tree234.$(OBJEXT) \ unix/procnet.$(OBJEXT) unix/ux_x11.$(OBJEXT) \ unix/uxagentsock.$(OBJEXT) unix/uxcliloop.$(OBJEXT) \ unix/uxfdsock.$(OBJEXT) unix/uxmisc.$(OBJEXT) \ unix/uxnet.$(OBJEXT) unix/uxnogtk.$(OBJEXT) \ unix/uxnoise.$(OBJEXT) unix/uxpeer.$(OBJEXT) \ unix/uxpoll.$(OBJEXT) unix/uxproxy.$(OBJEXT) \ unix/uxpsusan.$(OBJEXT) unix/uxpty.$(OBJEXT) \ unix/uxsel.$(OBJEXT) unix/uxsftpserver.$(OBJEXT) \ unix/uxsignal.$(OBJEXT) unix/uxstore.$(OBJEXT) \ unix/uxutils.$(OBJEXT) utils.$(OBJEXT) wcwidth.$(OBJEXT) \ wildcard.$(OBJEXT) x11fwd.$(OBJEXT) psusan_OBJECTS = $(am_psusan_OBJECTS) psusan_DEPENDENCIES = libversion.a am__pterm_SOURCES_DIST = be_none.c callback.c charset/fromucs.c \ charset/localenc.c charset/macenc.c charset/mimeenc.c \ charset/sbcs.c charset/sbcsdat.c charset/slookup.c \ charset/toucs.c charset/utf8.c charset/xenc.c cmdline.c conf.c \ config.c dialog.c ldisc.c logging.c marshal.c memory.c \ minibidi.c misc.c miscucs.c nocproxy.c nogss.c sessprep.c \ settings.c stripctrl.c terminal.c time.c timing.c tree234.c \ unix/gtkcfg.c unix/gtkcols.c unix/gtkcomm.c unix/gtkdlg.c \ unix/gtkfont.c unix/gtkmain.c unix/gtkmisc.c unix/gtkwin.c \ unix/uxcfg.c unix/uxmisc.c unix/uxprint.c unix/uxpterm.c \ unix/uxpty.c unix/uxsel.c unix/uxsignal.c unix/uxstore.c \ unix/uxucs.c unix/x11misc.c unix/xkeysym.c unix/xpmptcfg.c \ unix/xpmpterm.c utils.c wcwidth.c @HAVE_GTK_TRUE@am_pterm_OBJECTS = be_none.$(OBJEXT) callback.$(OBJEXT) \ @HAVE_GTK_TRUE@ charset/fromucs.$(OBJEXT) \ @HAVE_GTK_TRUE@ charset/localenc.$(OBJEXT) \ @HAVE_GTK_TRUE@ charset/macenc.$(OBJEXT) \ @HAVE_GTK_TRUE@ charset/mimeenc.$(OBJEXT) \ @HAVE_GTK_TRUE@ charset/sbcs.$(OBJEXT) \ @HAVE_GTK_TRUE@ charset/sbcsdat.$(OBJEXT) \ @HAVE_GTK_TRUE@ charset/slookup.$(OBJEXT) \ @HAVE_GTK_TRUE@ charset/toucs.$(OBJEXT) charset/utf8.$(OBJEXT) \ @HAVE_GTK_TRUE@ charset/xenc.$(OBJEXT) cmdline.$(OBJEXT) \ @HAVE_GTK_TRUE@ conf.$(OBJEXT) config.$(OBJEXT) \ @HAVE_GTK_TRUE@ dialog.$(OBJEXT) ldisc.$(OBJEXT) \ @HAVE_GTK_TRUE@ logging.$(OBJEXT) marshal.$(OBJEXT) \ @HAVE_GTK_TRUE@ memory.$(OBJEXT) minibidi.$(OBJEXT) \ @HAVE_GTK_TRUE@ misc.$(OBJEXT) miscucs.$(OBJEXT) \ @HAVE_GTK_TRUE@ nocproxy.$(OBJEXT) nogss.$(OBJEXT) \ @HAVE_GTK_TRUE@ sessprep.$(OBJEXT) settings.$(OBJEXT) \ @HAVE_GTK_TRUE@ stripctrl.$(OBJEXT) terminal.$(OBJEXT) \ @HAVE_GTK_TRUE@ time.$(OBJEXT) timing.$(OBJEXT) \ @HAVE_GTK_TRUE@ tree234.$(OBJEXT) unix/gtkcfg.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/gtkcols.$(OBJEXT) unix/gtkcomm.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/gtkdlg.$(OBJEXT) unix/gtkfont.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/gtkmain.$(OBJEXT) unix/gtkmisc.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/gtkwin.$(OBJEXT) unix/uxcfg.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/uxmisc.$(OBJEXT) unix/uxprint.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/uxpterm.$(OBJEXT) unix/uxpty.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/uxsel.$(OBJEXT) unix/uxsignal.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/uxstore.$(OBJEXT) unix/uxucs.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/x11misc.$(OBJEXT) unix/xkeysym.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/xpmptcfg.$(OBJEXT) unix/xpmpterm.$(OBJEXT) \ @HAVE_GTK_TRUE@ utils.$(OBJEXT) wcwidth.$(OBJEXT) pterm_OBJECTS = $(am_pterm_OBJECTS) @HAVE_GTK_TRUE@pterm_DEPENDENCIES = libversion.a $(am__DEPENDENCIES_1) am__ptermapp_SOURCES_DIST = be_none.c callback.c charset/fromucs.c \ charset/localenc.c charset/macenc.c charset/mimeenc.c \ charset/sbcs.c charset/sbcsdat.c charset/slookup.c \ charset/toucs.c charset/utf8.c charset/xenc.c conf.c config.c \ dialog.c ldisc.c logging.c marshal.c memory.c minibidi.c \ misc.c miscucs.c nocmdline.c nocproxy.c nogss.c sessprep.c \ settings.c stripctrl.c terminal.c time.c timing.c tree234.c \ unix/gtkapp.c unix/gtkcfg.c unix/gtkcols.c unix/gtkcomm.c \ unix/gtkdlg.c unix/gtkfont.c unix/gtkmisc.c unix/gtkwin.c \ unix/uxcfg.c unix/uxmisc.c unix/uxprint.c unix/uxpterm.c \ unix/uxpty.c unix/uxsel.c unix/uxsignal.c unix/uxstore.c \ unix/uxucs.c unix/x11misc.c unix/xkeysym.c unix/xpmptcfg.c \ unix/xpmpterm.c utils.c wcwidth.c @HAVE_GTK_TRUE@am_ptermapp_OBJECTS = be_none.$(OBJEXT) \ @HAVE_GTK_TRUE@ callback.$(OBJEXT) charset/fromucs.$(OBJEXT) \ @HAVE_GTK_TRUE@ charset/localenc.$(OBJEXT) \ @HAVE_GTK_TRUE@ charset/macenc.$(OBJEXT) \ @HAVE_GTK_TRUE@ charset/mimeenc.$(OBJEXT) \ @HAVE_GTK_TRUE@ charset/sbcs.$(OBJEXT) \ @HAVE_GTK_TRUE@ charset/sbcsdat.$(OBJEXT) \ @HAVE_GTK_TRUE@ charset/slookup.$(OBJEXT) \ @HAVE_GTK_TRUE@ charset/toucs.$(OBJEXT) charset/utf8.$(OBJEXT) \ @HAVE_GTK_TRUE@ charset/xenc.$(OBJEXT) conf.$(OBJEXT) \ @HAVE_GTK_TRUE@ config.$(OBJEXT) dialog.$(OBJEXT) \ @HAVE_GTK_TRUE@ ldisc.$(OBJEXT) logging.$(OBJEXT) \ @HAVE_GTK_TRUE@ marshal.$(OBJEXT) memory.$(OBJEXT) \ @HAVE_GTK_TRUE@ minibidi.$(OBJEXT) misc.$(OBJEXT) \ @HAVE_GTK_TRUE@ miscucs.$(OBJEXT) nocmdline.$(OBJEXT) \ @HAVE_GTK_TRUE@ nocproxy.$(OBJEXT) nogss.$(OBJEXT) \ @HAVE_GTK_TRUE@ sessprep.$(OBJEXT) settings.$(OBJEXT) \ @HAVE_GTK_TRUE@ stripctrl.$(OBJEXT) terminal.$(OBJEXT) \ @HAVE_GTK_TRUE@ time.$(OBJEXT) timing.$(OBJEXT) \ @HAVE_GTK_TRUE@ tree234.$(OBJEXT) unix/gtkapp.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/gtkcfg.$(OBJEXT) unix/gtkcols.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/gtkcomm.$(OBJEXT) unix/gtkdlg.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/gtkfont.$(OBJEXT) unix/gtkmisc.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/gtkwin.$(OBJEXT) unix/uxcfg.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/uxmisc.$(OBJEXT) unix/uxprint.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/uxpterm.$(OBJEXT) unix/uxpty.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/uxsel.$(OBJEXT) unix/uxsignal.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/uxstore.$(OBJEXT) unix/uxucs.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/x11misc.$(OBJEXT) unix/xkeysym.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/xpmptcfg.$(OBJEXT) unix/xpmpterm.$(OBJEXT) \ @HAVE_GTK_TRUE@ utils.$(OBJEXT) wcwidth.$(OBJEXT) ptermapp_OBJECTS = $(am_ptermapp_OBJECTS) @HAVE_GTK_TRUE@ptermapp_DEPENDENCIES = libversion.a \ @HAVE_GTK_TRUE@ $(am__DEPENDENCIES_1) am__putty_SOURCES_DIST = agentf.c aqsync.c be_all_s.c be_misc.c \ callback.c charset/fromucs.c charset/localenc.c \ charset/macenc.c charset/mimeenc.c charset/sbcs.c \ charset/sbcsdat.c charset/slookup.c charset/toucs.c \ charset/utf8.c charset/xenc.c cmdline.c conf.c config.c \ cproxy.c dialog.c ecc.c errsock.c ldisc.c logging.c mainchan.c \ marshal.c memory.c minibidi.c misc.c miscucs.c mpint.c \ nullplug.c pgssapi.c pinger.c portfwd.c proxy.c raw.c rlogin.c \ sessprep.c settings.c ssh.c ssh1bpp.c ssh1censor.c \ ssh1connection-client.c ssh1connection.c ssh1login.c \ ssh2bpp-bare.c ssh2bpp.c ssh2censor.c ssh2connection-client.c \ ssh2connection.c ssh2kex-client.c ssh2transhk.c \ ssh2transport.c ssh2userauth.c sshaes.c ssharcf.c sshargon2.c \ sshauxcrypt.c sshblake2.c sshblowf.c sshccp.c sshcommon.c \ sshcrc.c sshcrcda.c sshdes.c sshdh.c sshdss.c sshecc.c \ sshgssc.c sshhmac.c sshmac.c sshmd5.c sshprng.c sshpubk.c \ sshrand.c sshrsa.c sshsh256.c sshsh512.c sshsha.c sshsha3.c \ sshshare.c sshutils.c sshverstring.c sshzlib.c stripctrl.c \ supdup.c telnet.c terminal.c time.c timing.c tree234.c \ unix/gtkcfg.c unix/gtkcols.c unix/gtkcomm.c unix/gtkdlg.c \ unix/gtkfont.c unix/gtkmain.c unix/gtkmisc.c unix/gtkwin.c \ unix/ux_x11.c unix/uxagentc.c unix/uxcfg.c unix/uxfdsock.c \ unix/uxgss.c unix/uxmisc.c unix/uxnet.c unix/uxnoise.c \ unix/uxpeer.c unix/uxpoll.c unix/uxprint.c unix/uxproxy.c \ unix/uxputty.c unix/uxsel.c unix/uxser.c unix/uxshare.c \ unix/uxsignal.c unix/uxstore.c unix/uxucs.c unix/uxutils.c \ unix/x11misc.c unix/xkeysym.c unix/xpmpucfg.c unix/xpmputty.c \ utils.c wcwidth.c wildcard.c x11fwd.c @HAVE_GTK_TRUE@am_putty_OBJECTS = agentf.$(OBJEXT) aqsync.$(OBJEXT) \ @HAVE_GTK_TRUE@ be_all_s.$(OBJEXT) be_misc.$(OBJEXT) \ @HAVE_GTK_TRUE@ callback.$(OBJEXT) charset/fromucs.$(OBJEXT) \ @HAVE_GTK_TRUE@ charset/localenc.$(OBJEXT) \ @HAVE_GTK_TRUE@ charset/macenc.$(OBJEXT) \ @HAVE_GTK_TRUE@ charset/mimeenc.$(OBJEXT) \ @HAVE_GTK_TRUE@ charset/sbcs.$(OBJEXT) \ @HAVE_GTK_TRUE@ charset/sbcsdat.$(OBJEXT) \ @HAVE_GTK_TRUE@ charset/slookup.$(OBJEXT) \ @HAVE_GTK_TRUE@ charset/toucs.$(OBJEXT) charset/utf8.$(OBJEXT) \ @HAVE_GTK_TRUE@ charset/xenc.$(OBJEXT) cmdline.$(OBJEXT) \ @HAVE_GTK_TRUE@ conf.$(OBJEXT) config.$(OBJEXT) \ @HAVE_GTK_TRUE@ cproxy.$(OBJEXT) dialog.$(OBJEXT) ecc.$(OBJEXT) \ @HAVE_GTK_TRUE@ errsock.$(OBJEXT) ldisc.$(OBJEXT) \ @HAVE_GTK_TRUE@ logging.$(OBJEXT) mainchan.$(OBJEXT) \ @HAVE_GTK_TRUE@ marshal.$(OBJEXT) memory.$(OBJEXT) \ @HAVE_GTK_TRUE@ minibidi.$(OBJEXT) misc.$(OBJEXT) \ @HAVE_GTK_TRUE@ miscucs.$(OBJEXT) mpint.$(OBJEXT) \ @HAVE_GTK_TRUE@ nullplug.$(OBJEXT) pgssapi.$(OBJEXT) \ @HAVE_GTK_TRUE@ pinger.$(OBJEXT) portfwd.$(OBJEXT) \ @HAVE_GTK_TRUE@ proxy.$(OBJEXT) raw.$(OBJEXT) rlogin.$(OBJEXT) \ @HAVE_GTK_TRUE@ sessprep.$(OBJEXT) settings.$(OBJEXT) \ @HAVE_GTK_TRUE@ ssh.$(OBJEXT) ssh1bpp.$(OBJEXT) \ @HAVE_GTK_TRUE@ ssh1censor.$(OBJEXT) \ @HAVE_GTK_TRUE@ ssh1connection-client.$(OBJEXT) \ @HAVE_GTK_TRUE@ ssh1connection.$(OBJEXT) ssh1login.$(OBJEXT) \ @HAVE_GTK_TRUE@ ssh2bpp-bare.$(OBJEXT) ssh2bpp.$(OBJEXT) \ @HAVE_GTK_TRUE@ ssh2censor.$(OBJEXT) \ @HAVE_GTK_TRUE@ ssh2connection-client.$(OBJEXT) \ @HAVE_GTK_TRUE@ ssh2connection.$(OBJEXT) \ @HAVE_GTK_TRUE@ ssh2kex-client.$(OBJEXT) ssh2transhk.$(OBJEXT) \ @HAVE_GTK_TRUE@ ssh2transport.$(OBJEXT) ssh2userauth.$(OBJEXT) \ @HAVE_GTK_TRUE@ sshaes.$(OBJEXT) ssharcf.$(OBJEXT) \ @HAVE_GTK_TRUE@ sshargon2.$(OBJEXT) sshauxcrypt.$(OBJEXT) \ @HAVE_GTK_TRUE@ sshblake2.$(OBJEXT) sshblowf.$(OBJEXT) \ @HAVE_GTK_TRUE@ sshccp.$(OBJEXT) sshcommon.$(OBJEXT) \ @HAVE_GTK_TRUE@ sshcrc.$(OBJEXT) sshcrcda.$(OBJEXT) \ @HAVE_GTK_TRUE@ sshdes.$(OBJEXT) sshdh.$(OBJEXT) \ @HAVE_GTK_TRUE@ sshdss.$(OBJEXT) sshecc.$(OBJEXT) \ @HAVE_GTK_TRUE@ sshgssc.$(OBJEXT) sshhmac.$(OBJEXT) \ @HAVE_GTK_TRUE@ sshmac.$(OBJEXT) sshmd5.$(OBJEXT) \ @HAVE_GTK_TRUE@ sshprng.$(OBJEXT) sshpubk.$(OBJEXT) \ @HAVE_GTK_TRUE@ sshrand.$(OBJEXT) sshrsa.$(OBJEXT) \ @HAVE_GTK_TRUE@ sshsh256.$(OBJEXT) sshsh512.$(OBJEXT) \ @HAVE_GTK_TRUE@ sshsha.$(OBJEXT) sshsha3.$(OBJEXT) \ @HAVE_GTK_TRUE@ sshshare.$(OBJEXT) sshutils.$(OBJEXT) \ @HAVE_GTK_TRUE@ sshverstring.$(OBJEXT) sshzlib.$(OBJEXT) \ @HAVE_GTK_TRUE@ stripctrl.$(OBJEXT) supdup.$(OBJEXT) \ @HAVE_GTK_TRUE@ telnet.$(OBJEXT) terminal.$(OBJEXT) \ @HAVE_GTK_TRUE@ time.$(OBJEXT) timing.$(OBJEXT) \ @HAVE_GTK_TRUE@ tree234.$(OBJEXT) unix/gtkcfg.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/gtkcols.$(OBJEXT) unix/gtkcomm.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/gtkdlg.$(OBJEXT) unix/gtkfont.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/gtkmain.$(OBJEXT) unix/gtkmisc.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/gtkwin.$(OBJEXT) unix/ux_x11.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/uxagentc.$(OBJEXT) unix/uxcfg.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/uxfdsock.$(OBJEXT) unix/uxgss.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/uxmisc.$(OBJEXT) unix/uxnet.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/uxnoise.$(OBJEXT) unix/uxpeer.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/uxpoll.$(OBJEXT) unix/uxprint.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/uxproxy.$(OBJEXT) unix/uxputty.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/uxsel.$(OBJEXT) unix/uxser.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/uxshare.$(OBJEXT) unix/uxsignal.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/uxstore.$(OBJEXT) unix/uxucs.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/uxutils.$(OBJEXT) unix/x11misc.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/xkeysym.$(OBJEXT) unix/xpmpucfg.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/xpmputty.$(OBJEXT) utils.$(OBJEXT) \ @HAVE_GTK_TRUE@ wcwidth.$(OBJEXT) wildcard.$(OBJEXT) \ @HAVE_GTK_TRUE@ x11fwd.$(OBJEXT) putty_OBJECTS = $(am_putty_OBJECTS) @HAVE_GTK_TRUE@putty_DEPENDENCIES = libversion.a $(am__DEPENDENCIES_1) am__puttyapp_SOURCES_DIST = agentf.c aqsync.c be_all_s.c be_misc.c \ callback.c charset/fromucs.c charset/localenc.c \ charset/macenc.c charset/mimeenc.c charset/sbcs.c \ charset/sbcsdat.c charset/slookup.c charset/toucs.c \ charset/utf8.c charset/xenc.c conf.c config.c cproxy.c \ dialog.c ecc.c errsock.c ldisc.c logging.c mainchan.c \ marshal.c memory.c minibidi.c misc.c miscucs.c mpint.c \ nocmdline.c nullplug.c pgssapi.c pinger.c portfwd.c proxy.c \ raw.c rlogin.c sessprep.c settings.c ssh.c ssh1bpp.c \ ssh1censor.c ssh1connection-client.c ssh1connection.c \ ssh1login.c ssh2bpp-bare.c ssh2bpp.c ssh2censor.c \ ssh2connection-client.c ssh2connection.c ssh2kex-client.c \ ssh2transhk.c ssh2transport.c ssh2userauth.c sshaes.c \ ssharcf.c sshargon2.c sshauxcrypt.c sshblake2.c sshblowf.c \ sshccp.c sshcommon.c sshcrc.c sshcrcda.c sshdes.c sshdh.c \ sshdss.c sshecc.c sshgssc.c sshhmac.c sshmac.c sshmd5.c \ sshprng.c sshpubk.c sshrand.c sshrsa.c sshsh256.c sshsh512.c \ sshsha.c sshsha3.c sshshare.c sshutils.c sshverstring.c \ sshzlib.c stripctrl.c supdup.c telnet.c terminal.c time.c \ timing.c tree234.c unix/gtkapp.c unix/gtkcfg.c unix/gtkcols.c \ unix/gtkcomm.c unix/gtkdlg.c unix/gtkfont.c unix/gtkmisc.c \ unix/gtkwin.c unix/ux_x11.c unix/uxagentc.c unix/uxcfg.c \ unix/uxfdsock.c unix/uxgss.c unix/uxmisc.c unix/uxnet.c \ unix/uxnoise.c unix/uxpeer.c unix/uxpoll.c unix/uxprint.c \ unix/uxproxy.c unix/uxputty.c unix/uxsel.c unix/uxser.c \ unix/uxshare.c unix/uxsignal.c unix/uxstore.c unix/uxucs.c \ unix/uxutils.c unix/x11misc.c unix/xkeysym.c unix/xpmpucfg.c \ unix/xpmputty.c utils.c wcwidth.c wildcard.c x11fwd.c @HAVE_GTK_TRUE@am_puttyapp_OBJECTS = agentf.$(OBJEXT) aqsync.$(OBJEXT) \ @HAVE_GTK_TRUE@ be_all_s.$(OBJEXT) be_misc.$(OBJEXT) \ @HAVE_GTK_TRUE@ callback.$(OBJEXT) charset/fromucs.$(OBJEXT) \ @HAVE_GTK_TRUE@ charset/localenc.$(OBJEXT) \ @HAVE_GTK_TRUE@ charset/macenc.$(OBJEXT) \ @HAVE_GTK_TRUE@ charset/mimeenc.$(OBJEXT) \ @HAVE_GTK_TRUE@ charset/sbcs.$(OBJEXT) \ @HAVE_GTK_TRUE@ charset/sbcsdat.$(OBJEXT) \ @HAVE_GTK_TRUE@ charset/slookup.$(OBJEXT) \ @HAVE_GTK_TRUE@ charset/toucs.$(OBJEXT) charset/utf8.$(OBJEXT) \ @HAVE_GTK_TRUE@ charset/xenc.$(OBJEXT) conf.$(OBJEXT) \ @HAVE_GTK_TRUE@ config.$(OBJEXT) cproxy.$(OBJEXT) \ @HAVE_GTK_TRUE@ dialog.$(OBJEXT) ecc.$(OBJEXT) \ @HAVE_GTK_TRUE@ errsock.$(OBJEXT) ldisc.$(OBJEXT) \ @HAVE_GTK_TRUE@ logging.$(OBJEXT) mainchan.$(OBJEXT) \ @HAVE_GTK_TRUE@ marshal.$(OBJEXT) memory.$(OBJEXT) \ @HAVE_GTK_TRUE@ minibidi.$(OBJEXT) misc.$(OBJEXT) \ @HAVE_GTK_TRUE@ miscucs.$(OBJEXT) mpint.$(OBJEXT) \ @HAVE_GTK_TRUE@ nocmdline.$(OBJEXT) nullplug.$(OBJEXT) \ @HAVE_GTK_TRUE@ pgssapi.$(OBJEXT) pinger.$(OBJEXT) \ @HAVE_GTK_TRUE@ portfwd.$(OBJEXT) proxy.$(OBJEXT) raw.$(OBJEXT) \ @HAVE_GTK_TRUE@ rlogin.$(OBJEXT) sessprep.$(OBJEXT) \ @HAVE_GTK_TRUE@ settings.$(OBJEXT) ssh.$(OBJEXT) \ @HAVE_GTK_TRUE@ ssh1bpp.$(OBJEXT) ssh1censor.$(OBJEXT) \ @HAVE_GTK_TRUE@ ssh1connection-client.$(OBJEXT) \ @HAVE_GTK_TRUE@ ssh1connection.$(OBJEXT) ssh1login.$(OBJEXT) \ @HAVE_GTK_TRUE@ ssh2bpp-bare.$(OBJEXT) ssh2bpp.$(OBJEXT) \ @HAVE_GTK_TRUE@ ssh2censor.$(OBJEXT) \ @HAVE_GTK_TRUE@ ssh2connection-client.$(OBJEXT) \ @HAVE_GTK_TRUE@ ssh2connection.$(OBJEXT) \ @HAVE_GTK_TRUE@ ssh2kex-client.$(OBJEXT) ssh2transhk.$(OBJEXT) \ @HAVE_GTK_TRUE@ ssh2transport.$(OBJEXT) ssh2userauth.$(OBJEXT) \ @HAVE_GTK_TRUE@ sshaes.$(OBJEXT) ssharcf.$(OBJEXT) \ @HAVE_GTK_TRUE@ sshargon2.$(OBJEXT) sshauxcrypt.$(OBJEXT) \ @HAVE_GTK_TRUE@ sshblake2.$(OBJEXT) sshblowf.$(OBJEXT) \ @HAVE_GTK_TRUE@ sshccp.$(OBJEXT) sshcommon.$(OBJEXT) \ @HAVE_GTK_TRUE@ sshcrc.$(OBJEXT) sshcrcda.$(OBJEXT) \ @HAVE_GTK_TRUE@ sshdes.$(OBJEXT) sshdh.$(OBJEXT) \ @HAVE_GTK_TRUE@ sshdss.$(OBJEXT) sshecc.$(OBJEXT) \ @HAVE_GTK_TRUE@ sshgssc.$(OBJEXT) sshhmac.$(OBJEXT) \ @HAVE_GTK_TRUE@ sshmac.$(OBJEXT) sshmd5.$(OBJEXT) \ @HAVE_GTK_TRUE@ sshprng.$(OBJEXT) sshpubk.$(OBJEXT) \ @HAVE_GTK_TRUE@ sshrand.$(OBJEXT) sshrsa.$(OBJEXT) \ @HAVE_GTK_TRUE@ sshsh256.$(OBJEXT) sshsh512.$(OBJEXT) \ @HAVE_GTK_TRUE@ sshsha.$(OBJEXT) sshsha3.$(OBJEXT) \ @HAVE_GTK_TRUE@ sshshare.$(OBJEXT) sshutils.$(OBJEXT) \ @HAVE_GTK_TRUE@ sshverstring.$(OBJEXT) sshzlib.$(OBJEXT) \ @HAVE_GTK_TRUE@ stripctrl.$(OBJEXT) supdup.$(OBJEXT) \ @HAVE_GTK_TRUE@ telnet.$(OBJEXT) terminal.$(OBJEXT) \ @HAVE_GTK_TRUE@ time.$(OBJEXT) timing.$(OBJEXT) \ @HAVE_GTK_TRUE@ tree234.$(OBJEXT) unix/gtkapp.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/gtkcfg.$(OBJEXT) unix/gtkcols.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/gtkcomm.$(OBJEXT) unix/gtkdlg.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/gtkfont.$(OBJEXT) unix/gtkmisc.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/gtkwin.$(OBJEXT) unix/ux_x11.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/uxagentc.$(OBJEXT) unix/uxcfg.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/uxfdsock.$(OBJEXT) unix/uxgss.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/uxmisc.$(OBJEXT) unix/uxnet.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/uxnoise.$(OBJEXT) unix/uxpeer.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/uxpoll.$(OBJEXT) unix/uxprint.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/uxproxy.$(OBJEXT) unix/uxputty.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/uxsel.$(OBJEXT) unix/uxser.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/uxshare.$(OBJEXT) unix/uxsignal.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/uxstore.$(OBJEXT) unix/uxucs.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/uxutils.$(OBJEXT) unix/x11misc.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/xkeysym.$(OBJEXT) unix/xpmpucfg.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/xpmputty.$(OBJEXT) utils.$(OBJEXT) \ @HAVE_GTK_TRUE@ wcwidth.$(OBJEXT) wildcard.$(OBJEXT) \ @HAVE_GTK_TRUE@ x11fwd.$(OBJEXT) puttyapp_OBJECTS = $(am_puttyapp_OBJECTS) @HAVE_GTK_TRUE@puttyapp_DEPENDENCIES = libversion.a \ @HAVE_GTK_TRUE@ $(am__DEPENDENCIES_1) am_puttygen_OBJECTS = cmdgen.$(OBJEXT) conf.$(OBJEXT) \ console.$(OBJEXT) ecc.$(OBJEXT) import.$(OBJEXT) \ marshal.$(OBJEXT) memory.$(OBJEXT) millerrabin.$(OBJEXT) \ misc.$(OBJEXT) mpint.$(OBJEXT) mpunsafe.$(OBJEXT) \ notiming.$(OBJEXT) pockle.$(OBJEXT) primecandidate.$(OBJEXT) \ smallprimes.$(OBJEXT) sshaes.$(OBJEXT) sshargon2.$(OBJEXT) \ sshauxcrypt.$(OBJEXT) sshbcrypt.$(OBJEXT) sshblake2.$(OBJEXT) \ sshblowf.$(OBJEXT) sshdes.$(OBJEXT) sshdss.$(OBJEXT) \ sshdssg.$(OBJEXT) sshecc.$(OBJEXT) sshecdsag.$(OBJEXT) \ sshhmac.$(OBJEXT) sshmd5.$(OBJEXT) sshprime.$(OBJEXT) \ sshprng.$(OBJEXT) sshpubk.$(OBJEXT) sshrand.$(OBJEXT) \ sshrsa.$(OBJEXT) sshrsag.$(OBJEXT) sshsh256.$(OBJEXT) \ sshsh512.$(OBJEXT) sshsha.$(OBJEXT) sshsha3.$(OBJEXT) \ stripctrl.$(OBJEXT) time.$(OBJEXT) tree234.$(OBJEXT) \ unix/uxcons.$(OBJEXT) unix/uxgen.$(OBJEXT) \ unix/uxmisc.$(OBJEXT) unix/uxnogtk.$(OBJEXT) \ unix/uxnoise.$(OBJEXT) unix/uxpoll.$(OBJEXT) \ unix/uxstore.$(OBJEXT) unix/uxutils.$(OBJEXT) utils.$(OBJEXT) \ wcwidth.$(OBJEXT) puttygen_OBJECTS = $(am_puttygen_OBJECTS) puttygen_DEPENDENCIES = libversion.a am__puttytel_SOURCES_DIST = be_misc.c be_nos_s.c callback.c \ charset/fromucs.c charset/localenc.c charset/macenc.c \ charset/mimeenc.c charset/sbcs.c charset/sbcsdat.c \ charset/slookup.c charset/toucs.c charset/utf8.c \ charset/xenc.c cmdline.c conf.c config.c dialog.c errsock.c \ ldisc.c logging.c marshal.c memory.c minibidi.c misc.c \ miscucs.c nocproxy.c nogss.c norand.c pinger.c proxy.c raw.c \ rlogin.c sessprep.c settings.c stripctrl.c supdup.c telnet.c \ terminal.c time.c timing.c tree234.c unix/gtkcfg.c \ unix/gtkcols.c unix/gtkcomm.c unix/gtkdlg.c unix/gtkfont.c \ unix/gtkmain.c unix/gtkmisc.c unix/gtkwin.c unix/uxcfg.c \ unix/uxfdsock.c unix/uxmisc.c unix/uxnet.c unix/uxpeer.c \ unix/uxpoll.c unix/uxprint.c unix/uxproxy.c unix/uxputty.c \ unix/uxsel.c unix/uxser.c unix/uxsignal.c unix/uxstore.c \ unix/uxucs.c unix/uxutils.c unix/x11misc.c unix/xkeysym.c \ unix/xpmpucfg.c unix/xpmputty.c utils.c wcwidth.c @HAVE_GTK_TRUE@am_puttytel_OBJECTS = be_misc.$(OBJEXT) \ @HAVE_GTK_TRUE@ be_nos_s.$(OBJEXT) callback.$(OBJEXT) \ @HAVE_GTK_TRUE@ charset/fromucs.$(OBJEXT) \ @HAVE_GTK_TRUE@ charset/localenc.$(OBJEXT) \ @HAVE_GTK_TRUE@ charset/macenc.$(OBJEXT) \ @HAVE_GTK_TRUE@ charset/mimeenc.$(OBJEXT) \ @HAVE_GTK_TRUE@ charset/sbcs.$(OBJEXT) \ @HAVE_GTK_TRUE@ charset/sbcsdat.$(OBJEXT) \ @HAVE_GTK_TRUE@ charset/slookup.$(OBJEXT) \ @HAVE_GTK_TRUE@ charset/toucs.$(OBJEXT) charset/utf8.$(OBJEXT) \ @HAVE_GTK_TRUE@ charset/xenc.$(OBJEXT) cmdline.$(OBJEXT) \ @HAVE_GTK_TRUE@ conf.$(OBJEXT) config.$(OBJEXT) \ @HAVE_GTK_TRUE@ dialog.$(OBJEXT) errsock.$(OBJEXT) \ @HAVE_GTK_TRUE@ ldisc.$(OBJEXT) logging.$(OBJEXT) \ @HAVE_GTK_TRUE@ marshal.$(OBJEXT) memory.$(OBJEXT) \ @HAVE_GTK_TRUE@ minibidi.$(OBJEXT) misc.$(OBJEXT) \ @HAVE_GTK_TRUE@ miscucs.$(OBJEXT) nocproxy.$(OBJEXT) \ @HAVE_GTK_TRUE@ nogss.$(OBJEXT) norand.$(OBJEXT) \ @HAVE_GTK_TRUE@ pinger.$(OBJEXT) proxy.$(OBJEXT) raw.$(OBJEXT) \ @HAVE_GTK_TRUE@ rlogin.$(OBJEXT) sessprep.$(OBJEXT) \ @HAVE_GTK_TRUE@ settings.$(OBJEXT) stripctrl.$(OBJEXT) \ @HAVE_GTK_TRUE@ supdup.$(OBJEXT) telnet.$(OBJEXT) \ @HAVE_GTK_TRUE@ terminal.$(OBJEXT) time.$(OBJEXT) \ @HAVE_GTK_TRUE@ timing.$(OBJEXT) tree234.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/gtkcfg.$(OBJEXT) unix/gtkcols.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/gtkcomm.$(OBJEXT) unix/gtkdlg.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/gtkfont.$(OBJEXT) unix/gtkmain.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/gtkmisc.$(OBJEXT) unix/gtkwin.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/uxcfg.$(OBJEXT) unix/uxfdsock.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/uxmisc.$(OBJEXT) unix/uxnet.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/uxpeer.$(OBJEXT) unix/uxpoll.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/uxprint.$(OBJEXT) unix/uxproxy.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/uxputty.$(OBJEXT) unix/uxsel.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/uxser.$(OBJEXT) unix/uxsignal.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/uxstore.$(OBJEXT) unix/uxucs.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/uxutils.$(OBJEXT) unix/x11misc.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/xkeysym.$(OBJEXT) unix/xpmpucfg.$(OBJEXT) \ @HAVE_GTK_TRUE@ unix/xpmputty.$(OBJEXT) utils.$(OBJEXT) \ @HAVE_GTK_TRUE@ wcwidth.$(OBJEXT) puttytel_OBJECTS = $(am_puttytel_OBJECTS) @HAVE_GTK_TRUE@puttytel_DEPENDENCIES = libversion.a \ @HAVE_GTK_TRUE@ $(am__DEPENDENCIES_1) am_testcrypt_OBJECTS = ecc.$(OBJEXT) marshal.$(OBJEXT) \ memory.$(OBJEXT) millerrabin.$(OBJEXT) mpint.$(OBJEXT) \ mpunsafe.$(OBJEXT) pockle.$(OBJEXT) primecandidate.$(OBJEXT) \ smallprimes.$(OBJEXT) sshaes.$(OBJEXT) ssharcf.$(OBJEXT) \ sshargon2.$(OBJEXT) sshauxcrypt.$(OBJEXT) sshblake2.$(OBJEXT) \ sshblowf.$(OBJEXT) sshccp.$(OBJEXT) sshcrc.$(OBJEXT) \ sshcrcda.$(OBJEXT) sshdes.$(OBJEXT) sshdh.$(OBJEXT) \ sshdss.$(OBJEXT) sshdssg.$(OBJEXT) sshecc.$(OBJEXT) \ sshecdsag.$(OBJEXT) sshhmac.$(OBJEXT) sshmd5.$(OBJEXT) \ sshprime.$(OBJEXT) sshprng.$(OBJEXT) sshpubk.$(OBJEXT) \ sshrsa.$(OBJEXT) sshrsag.$(OBJEXT) sshsh256.$(OBJEXT) \ sshsh512.$(OBJEXT) sshsha.$(OBJEXT) sshsha3.$(OBJEXT) \ testcrypt.$(OBJEXT) tree234.$(OBJEXT) unix/uxutils.$(OBJEXT) \ utils.$(OBJEXT) testcrypt_OBJECTS = $(am_testcrypt_OBJECTS) testcrypt_LDADD = $(LDADD) am_testsc_OBJECTS = ecc.$(OBJEXT) marshal.$(OBJEXT) memory.$(OBJEXT) \ mpint.$(OBJEXT) sshaes.$(OBJEXT) ssharcf.$(OBJEXT) \ sshargon2.$(OBJEXT) sshauxcrypt.$(OBJEXT) sshblake2.$(OBJEXT) \ sshblowf.$(OBJEXT) sshccp.$(OBJEXT) sshcrc.$(OBJEXT) \ sshcrcda.$(OBJEXT) sshdes.$(OBJEXT) sshdh.$(OBJEXT) \ sshdss.$(OBJEXT) sshecc.$(OBJEXT) sshhmac.$(OBJEXT) \ sshmac.$(OBJEXT) sshmd5.$(OBJEXT) sshpubk.$(OBJEXT) \ sshrsa.$(OBJEXT) sshsh256.$(OBJEXT) sshsh512.$(OBJEXT) \ sshsha.$(OBJEXT) sshsha3.$(OBJEXT) testsc.$(OBJEXT) \ tree234.$(OBJEXT) unix/uxutils.$(OBJEXT) utils.$(OBJEXT) \ wildcard.$(OBJEXT) testsc_OBJECTS = $(am_testsc_OBJECTS) testsc_LDADD = $(LDADD) am_testzlib_OBJECTS = marshal.$(OBJEXT) memory.$(OBJEXT) \ sshzlib.$(OBJEXT) testzlib.$(OBJEXT) utils.$(OBJEXT) testzlib_OBJECTS = $(am_testzlib_OBJECTS) testzlib_LDADD = $(LDADD) am_uppity_OBJECTS = be_misc.$(OBJEXT) be_none.$(OBJEXT) \ callback.$(OBJEXT) conf.$(OBJEXT) cproxy.$(OBJEXT) \ ecc.$(OBJEXT) errsock.$(OBJEXT) logging.$(OBJEXT) \ marshal.$(OBJEXT) memory.$(OBJEXT) millerrabin.$(OBJEXT) \ misc.$(OBJEXT) mpint.$(OBJEXT) mpunsafe.$(OBJEXT) \ nullplug.$(OBJEXT) pgssapi.$(OBJEXT) pockle.$(OBJEXT) \ portfwd.$(OBJEXT) primecandidate.$(OBJEXT) proxy.$(OBJEXT) \ scpserver.$(OBJEXT) sesschan.$(OBJEXT) settings.$(OBJEXT) \ sftpcommon.$(OBJEXT) sftpserver.$(OBJEXT) \ smallprimes.$(OBJEXT) ssh1bpp.$(OBJEXT) ssh1censor.$(OBJEXT) \ ssh1connection-server.$(OBJEXT) ssh1connection.$(OBJEXT) \ ssh1login-server.$(OBJEXT) ssh2bpp-bare.$(OBJEXT) \ ssh2bpp.$(OBJEXT) ssh2censor.$(OBJEXT) \ ssh2connection-server.$(OBJEXT) ssh2connection.$(OBJEXT) \ ssh2kex-server.$(OBJEXT) ssh2transhk.$(OBJEXT) \ ssh2transport.$(OBJEXT) ssh2userauth-server.$(OBJEXT) \ sshaes.$(OBJEXT) ssharcf.$(OBJEXT) sshargon2.$(OBJEXT) \ sshauxcrypt.$(OBJEXT) sshblake2.$(OBJEXT) sshblowf.$(OBJEXT) \ sshccp.$(OBJEXT) sshcommon.$(OBJEXT) sshcrc.$(OBJEXT) \ sshcrcda.$(OBJEXT) sshdes.$(OBJEXT) sshdh.$(OBJEXT) \ sshdss.$(OBJEXT) sshecc.$(OBJEXT) sshgssc.$(OBJEXT) \ sshhmac.$(OBJEXT) sshmac.$(OBJEXT) sshmd5.$(OBJEXT) \ sshprime.$(OBJEXT) sshprng.$(OBJEXT) sshpubk.$(OBJEXT) \ sshrand.$(OBJEXT) sshrsa.$(OBJEXT) sshrsag.$(OBJEXT) \ sshserver.$(OBJEXT) sshsh256.$(OBJEXT) sshsh512.$(OBJEXT) \ sshsha.$(OBJEXT) sshsha3.$(OBJEXT) sshutils.$(OBJEXT) \ sshverstring.$(OBJEXT) sshzlib.$(OBJEXT) stripctrl.$(OBJEXT) \ time.$(OBJEXT) timing.$(OBJEXT) tree234.$(OBJEXT) \ unix/procnet.$(OBJEXT) unix/ux_x11.$(OBJEXT) \ unix/uxagentsock.$(OBJEXT) unix/uxcliloop.$(OBJEXT) \ unix/uxfdsock.$(OBJEXT) unix/uxgss.$(OBJEXT) \ unix/uxmisc.$(OBJEXT) unix/uxnet.$(OBJEXT) \ unix/uxnogtk.$(OBJEXT) unix/uxnoise.$(OBJEXT) \ unix/uxpeer.$(OBJEXT) unix/uxpoll.$(OBJEXT) \ unix/uxproxy.$(OBJEXT) unix/uxpty.$(OBJEXT) \ unix/uxsel.$(OBJEXT) unix/uxserver.$(OBJEXT) \ unix/uxsftpserver.$(OBJEXT) unix/uxsignal.$(OBJEXT) \ unix/uxstore.$(OBJEXT) unix/uxutils.$(OBJEXT) utils.$(OBJEXT) \ wcwidth.$(OBJEXT) wildcard.$(OBJEXT) x11fwd.$(OBJEXT) uppity_OBJECTS = $(am_uppity_OBJECTS) uppity_DEPENDENCIES = libversion.a SCRIPTS = $(noinst_SCRIPTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ depcomp = $(SHELL) $(top_srcdir)/depcomp am__maybe_remake_depfiles = depfiles am__depfiles_remade = ./$(DEPDIR)/agentf.Po ./$(DEPDIR)/aqsync.Po \ ./$(DEPDIR)/be_all_s.Po ./$(DEPDIR)/be_misc.Po \ ./$(DEPDIR)/be_none.Po ./$(DEPDIR)/be_nos_s.Po \ ./$(DEPDIR)/be_ssh.Po ./$(DEPDIR)/callback.Po \ ./$(DEPDIR)/cgtest.Po ./$(DEPDIR)/clicons.Po \ ./$(DEPDIR)/cmdgen.Po ./$(DEPDIR)/cmdline.Po \ ./$(DEPDIR)/conf.Po ./$(DEPDIR)/config.Po \ ./$(DEPDIR)/console.Po ./$(DEPDIR)/cproxy.Po \ ./$(DEPDIR)/dialog.Po ./$(DEPDIR)/ecc.Po \ ./$(DEPDIR)/errsock.Po ./$(DEPDIR)/fuzzterm.Po \ ./$(DEPDIR)/import.Po ./$(DEPDIR)/ldisc.Po \ ./$(DEPDIR)/libversion_a-version.Po ./$(DEPDIR)/logging.Po \ ./$(DEPDIR)/mainchan.Po ./$(DEPDIR)/marshal.Po \ ./$(DEPDIR)/memory.Po ./$(DEPDIR)/millerrabin.Po \ ./$(DEPDIR)/minibidi.Po ./$(DEPDIR)/misc.Po \ ./$(DEPDIR)/miscucs.Po ./$(DEPDIR)/mpint.Po \ ./$(DEPDIR)/mpunsafe.Po ./$(DEPDIR)/nocmdline.Po \ ./$(DEPDIR)/nocproxy.Po ./$(DEPDIR)/nogss.Po \ ./$(DEPDIR)/norand.Po ./$(DEPDIR)/noterm.Po \ ./$(DEPDIR)/notiming.Po ./$(DEPDIR)/nullplug.Po \ ./$(DEPDIR)/pageant.Po ./$(DEPDIR)/pgssapi.Po \ ./$(DEPDIR)/pinger.Po ./$(DEPDIR)/pockle.Po \ ./$(DEPDIR)/portfwd.Po ./$(DEPDIR)/primecandidate.Po \ ./$(DEPDIR)/proxy.Po ./$(DEPDIR)/pscp.Po ./$(DEPDIR)/psftp.Po \ ./$(DEPDIR)/psftpcommon.Po ./$(DEPDIR)/psocks.Po \ ./$(DEPDIR)/raw.Po ./$(DEPDIR)/rlogin.Po \ ./$(DEPDIR)/scpserver.Po ./$(DEPDIR)/sesschan.Po \ ./$(DEPDIR)/sessprep.Po ./$(DEPDIR)/settings.Po \ ./$(DEPDIR)/sftp.Po ./$(DEPDIR)/sftpcommon.Po \ ./$(DEPDIR)/sftpserver.Po ./$(DEPDIR)/smallprimes.Po \ ./$(DEPDIR)/ssh.Po ./$(DEPDIR)/ssh1bpp.Po \ ./$(DEPDIR)/ssh1censor.Po ./$(DEPDIR)/ssh1connection-client.Po \ ./$(DEPDIR)/ssh1connection-server.Po \ ./$(DEPDIR)/ssh1connection.Po ./$(DEPDIR)/ssh1login-server.Po \ ./$(DEPDIR)/ssh1login.Po ./$(DEPDIR)/ssh2bpp-bare.Po \ ./$(DEPDIR)/ssh2bpp.Po ./$(DEPDIR)/ssh2censor.Po \ ./$(DEPDIR)/ssh2connection-client.Po \ ./$(DEPDIR)/ssh2connection-server.Po \ ./$(DEPDIR)/ssh2connection.Po ./$(DEPDIR)/ssh2kex-client.Po \ ./$(DEPDIR)/ssh2kex-server.Po ./$(DEPDIR)/ssh2transhk.Po \ ./$(DEPDIR)/ssh2transport.Po \ ./$(DEPDIR)/ssh2userauth-server.Po ./$(DEPDIR)/ssh2userauth.Po \ ./$(DEPDIR)/sshaes.Po ./$(DEPDIR)/ssharcf.Po \ ./$(DEPDIR)/sshargon2.Po ./$(DEPDIR)/sshauxcrypt.Po \ ./$(DEPDIR)/sshbcrypt.Po ./$(DEPDIR)/sshblake2.Po \ ./$(DEPDIR)/sshblowf.Po ./$(DEPDIR)/sshccp.Po \ ./$(DEPDIR)/sshcommon.Po ./$(DEPDIR)/sshcrc.Po \ ./$(DEPDIR)/sshcrcda.Po ./$(DEPDIR)/sshdes.Po \ ./$(DEPDIR)/sshdh.Po ./$(DEPDIR)/sshdss.Po \ ./$(DEPDIR)/sshdssg.Po ./$(DEPDIR)/sshecc.Po \ ./$(DEPDIR)/sshecdsag.Po ./$(DEPDIR)/sshgssc.Po \ ./$(DEPDIR)/sshhmac.Po ./$(DEPDIR)/sshmac.Po \ ./$(DEPDIR)/sshmd5.Po ./$(DEPDIR)/sshprime.Po \ ./$(DEPDIR)/sshprng.Po ./$(DEPDIR)/sshpubk.Po \ ./$(DEPDIR)/sshrand.Po ./$(DEPDIR)/sshrsa.Po \ ./$(DEPDIR)/sshrsag.Po ./$(DEPDIR)/sshserver.Po \ ./$(DEPDIR)/sshsh256.Po ./$(DEPDIR)/sshsh512.Po \ ./$(DEPDIR)/sshsha.Po ./$(DEPDIR)/sshsha3.Po \ ./$(DEPDIR)/sshshare.Po ./$(DEPDIR)/sshutils.Po \ ./$(DEPDIR)/sshverstring.Po ./$(DEPDIR)/sshzlib.Po \ ./$(DEPDIR)/stripctrl.Po ./$(DEPDIR)/supdup.Po \ ./$(DEPDIR)/telnet.Po ./$(DEPDIR)/terminal.Po \ ./$(DEPDIR)/testcrypt.Po ./$(DEPDIR)/testsc.Po \ ./$(DEPDIR)/testzlib.Po ./$(DEPDIR)/time.Po \ ./$(DEPDIR)/timing.Po ./$(DEPDIR)/tree234.Po \ ./$(DEPDIR)/utils.Po ./$(DEPDIR)/wcwidth.Po \ ./$(DEPDIR)/wildcard.Po ./$(DEPDIR)/x11fwd.Po \ charset/$(DEPDIR)/fromucs.Po charset/$(DEPDIR)/localenc.Po \ charset/$(DEPDIR)/macenc.Po charset/$(DEPDIR)/mimeenc.Po \ charset/$(DEPDIR)/sbcs.Po charset/$(DEPDIR)/sbcsdat.Po \ charset/$(DEPDIR)/slookup.Po charset/$(DEPDIR)/toucs.Po \ charset/$(DEPDIR)/utf8.Po charset/$(DEPDIR)/xenc.Po \ unix/$(DEPDIR)/gtkapp.Po unix/$(DEPDIR)/gtkask.Po \ unix/$(DEPDIR)/gtkcfg.Po unix/$(DEPDIR)/gtkcols.Po \ unix/$(DEPDIR)/gtkcomm.Po unix/$(DEPDIR)/gtkdlg.Po \ unix/$(DEPDIR)/gtkfont.Po unix/$(DEPDIR)/gtkmain.Po \ unix/$(DEPDIR)/gtkmisc.Po unix/$(DEPDIR)/gtkwin.Po \ unix/$(DEPDIR)/osxlaunch.Po unix/$(DEPDIR)/procnet.Po \ unix/$(DEPDIR)/ux_x11.Po unix/$(DEPDIR)/uxagentc.Po \ unix/$(DEPDIR)/uxagentsock.Po unix/$(DEPDIR)/uxcfg.Po \ unix/$(DEPDIR)/uxcliloop.Po unix/$(DEPDIR)/uxcons.Po \ unix/$(DEPDIR)/uxfdsock.Po unix/$(DEPDIR)/uxgen.Po \ unix/$(DEPDIR)/uxgss.Po unix/$(DEPDIR)/uxmisc.Po \ unix/$(DEPDIR)/uxnet.Po unix/$(DEPDIR)/uxnogtk.Po \ unix/$(DEPDIR)/uxnoise.Po unix/$(DEPDIR)/uxpeer.Po \ unix/$(DEPDIR)/uxpgnt.Po unix/$(DEPDIR)/uxplink.Po \ unix/$(DEPDIR)/uxpoll.Po unix/$(DEPDIR)/uxprint.Po \ unix/$(DEPDIR)/uxproxy.Po unix/$(DEPDIR)/uxpsusan.Po \ unix/$(DEPDIR)/uxpterm.Po unix/$(DEPDIR)/uxpty.Po \ unix/$(DEPDIR)/uxputty.Po unix/$(DEPDIR)/uxsel.Po \ unix/$(DEPDIR)/uxser.Po unix/$(DEPDIR)/uxserver.Po \ unix/$(DEPDIR)/uxsftp.Po unix/$(DEPDIR)/uxsftpserver.Po \ unix/$(DEPDIR)/uxshare.Po unix/$(DEPDIR)/uxsignal.Po \ unix/$(DEPDIR)/uxsocks.Po unix/$(DEPDIR)/uxstore.Po \ unix/$(DEPDIR)/uxucs.Po unix/$(DEPDIR)/uxutils.Po \ unix/$(DEPDIR)/x11misc.Po unix/$(DEPDIR)/xkeysym.Po \ unix/$(DEPDIR)/xpmptcfg.Po unix/$(DEPDIR)/xpmpterm.Po \ unix/$(DEPDIR)/xpmpucfg.Po unix/$(DEPDIR)/xpmputty.Po am__mv = mv -f AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libversion_a_SOURCES) $(cgtest_SOURCES) \ $(fuzzterm_SOURCES) $(osxlaunch_SOURCES) $(pageant_SOURCES) \ $(plink_SOURCES) $(pscp_SOURCES) $(psftp_SOURCES) \ $(psocks_SOURCES) $(psusan_SOURCES) $(pterm_SOURCES) \ $(ptermapp_SOURCES) $(putty_SOURCES) $(puttyapp_SOURCES) \ $(puttygen_SOURCES) $(puttytel_SOURCES) $(testcrypt_SOURCES) \ $(testsc_SOURCES) $(testzlib_SOURCES) $(uppity_SOURCES) DIST_SOURCES = $(libversion_a_SOURCES) $(cgtest_SOURCES) \ $(fuzzterm_SOURCES) $(osxlaunch_SOURCES) \ $(am__pageant_SOURCES_DIST) $(plink_SOURCES) $(pscp_SOURCES) \ $(psftp_SOURCES) $(psocks_SOURCES) $(psusan_SOURCES) \ $(am__pterm_SOURCES_DIST) $(am__ptermapp_SOURCES_DIST) \ $(am__putty_SOURCES_DIST) $(am__puttyapp_SOURCES_DIST) \ $(puttygen_SOURCES) $(am__puttytel_SOURCES_DIST) \ $(testcrypt_SOURCES) $(testsc_SOURCES) $(testzlib_SOURCES) \ $(uppity_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } man1dir = $(mandir)/man1 NROFF = nroff MANS = $(man1_MANS) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) \ $(LISP)uxconfig.in # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags CSCOPE = cscope AM_RECURSIVE_TARGETS = cscope am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/uxconfig.in README \ ar-lib compile depcomp install-sh missing DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) distdir = $(PACKAGE)-$(VERSION) top_distdir = $(distdir) am__remove_distdir = \ if test -d "$(distdir)"; then \ find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \ && rm -rf "$(distdir)" \ || { sleep 5 && rm -rf "$(distdir)"; }; \ else :; fi am__post_remove_distdir = $(am__remove_distdir) DIST_ARCHIVES = $(distdir).tar.gz GZIP_ENV = --best DIST_TARGETS = dist-gzip distuninstallcheck_listfiles = find . -type f -print am__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \ | sed 's|^\./|$(prefix)/|' | grep -v '$(infodir)/dir$$' distcleancheck_listfiles = find . -type f -print ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ GREP = @GREP@ GTK1_CONFIG = @GTK1_CONFIG@ GTK_CFLAGS = @GTK_CFLAGS@ GTK_LIBS = @GTK_LIBS@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LDFLAGS = @LDFLAGS@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LTLIBOBJS = @LTLIBOBJS@ MAKEINFO = @MAKEINFO@ MKDIR_P = @MKDIR_P@ OBJEXT = @OBJEXT@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RANLIB = @RANLIB@ SETID_CMD = @SETID_CMD@ SETID_MODE = @SETID_MODE@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ VERSION = @VERSION@ WARNINGOPTS = @WARNINGOPTS@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build_alias = @build_alias@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host_alias = @host_alias@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AUTOMAKE_OPTIONS = subdir-objects allsources = agentf.c aqsync.c be_all_s.c be_misc.c be_none.c be_nos_s.c \ be_ssh.c callback.c cgtest.c charset/charset.h \ charset/enum.c charset/fromucs.c charset/internal.h \ charset/localenc.c charset/macenc.c charset/mimeenc.c \ charset/sbcs.c charset/sbcsdat.c charset/slookup.c \ charset/toucs.c charset/utf8.c charset/xenc.c clicons.c \ cmdgen.c cmdline.c conf.c config.c console.c console.h \ cproxy.c defs.h dialog.c dialog.h ecc.c ecc.h errsock.c \ fuzzterm.c import.c ldisc.c ldisc.h licence.h logging.c \ mainchan.c marshal.c marshal.h memory.c millerrabin.c \ minibidi.c misc.c misc.h miscucs.c mpint.c mpint.h mpint_i.h \ mpunsafe.c mpunsafe.h network.h nocmdline.c nocproxy.c \ nogss.c norand.c noshare.c noterm.c notiming.c nullplug.c \ pageant.c pageant.h pgssapi.c pgssapi.h pinger.c pockle.c \ portfwd.c primecandidate.c proxy.c proxy.h pscp.c psftp.c \ psftp.h psftpcommon.c psocks.c psocks.h putty.h puttymem.h \ puttyps.h raw.c rlogin.c scpserver.c sesschan.c sessprep.c \ settings.c sftp.c sftp.h sftpcommon.c sftpserver.c \ smallprimes.c ssh.c ssh.h ssh1bpp.c ssh1censor.c \ ssh1connection-client.c ssh1connection-server.c \ ssh1connection.c ssh1connection.h ssh1login-server.c \ ssh1login.c ssh2bpp-bare.c ssh2bpp.c ssh2censor.c \ ssh2connection-client.c ssh2connection-server.c \ ssh2connection.c ssh2connection.h ssh2kex-client.c \ ssh2kex-server.c ssh2transhk.c ssh2transport.c \ ssh2transport.h ssh2userauth-server.c ssh2userauth.c \ sshaes.c ssharcf.c sshargon2.c sshauxcrypt.c sshbcrypt.c \ sshblake2.c sshblowf.c sshblowf.h sshbpp.h sshccp.c \ sshchan.h sshcommon.c sshcr.h sshcrc.c sshcrcda.c sshdes.c \ sshdh.c sshdss.c sshdssg.c sshecc.c sshecdsag.c sshgss.h \ sshgssc.c sshgssc.h sshhmac.c sshkeygen.h sshmac.c sshmd5.c \ sshppl.h sshprime.c sshprng.c sshpubk.c sshrand.c sshrsa.c \ sshrsag.c sshserver.c sshserver.h sshsh256.c sshsh512.c \ sshsha.c sshsha3.c sshshare.c sshsignals.h sshttymodes.h \ sshutils.c sshverstring.c sshzlib.c storage.h stripctrl.c \ supdup.c telnet.c terminal.c terminal.h testcrypt.c \ testcrypt.h testsc.c testzlib.c time.c timing.c tree234.c \ tree234.h unix/gtkapp.c unix/gtkask.c unix/gtkcfg.c \ unix/gtkcols.c unix/gtkcols.h unix/gtkcomm.c \ unix/gtkcompat.h unix/gtkdlg.c unix/gtkfont.c unix/gtkfont.h \ unix/gtkmain.c unix/gtkmisc.c unix/gtkmisc.h unix/gtkwin.c \ unix/osxlaunch.c unix/procnet.c unix/unix.h unix/ux_x11.c \ unix/uxagentc.c unix/uxagentsock.c unix/uxcfg.c \ unix/uxcliloop.c unix/uxcons.c unix/uxfdsock.c unix/uxgen.c \ unix/uxgss.c unix/uxmisc.c unix/uxnet.c unix/uxnogtk.c \ unix/uxnoise.c unix/uxpeer.c unix/uxpgnt.c unix/uxplink.c \ unix/uxpoll.c unix/uxprint.c unix/uxproxy.c unix/uxpsusan.c \ unix/uxpterm.c unix/uxpty.c unix/uxputty.c unix/uxsel.c \ unix/uxser.c unix/uxserver.c unix/uxsftp.c \ unix/uxsftpserver.c unix/uxshare.c unix/uxsignal.c \ unix/uxsocks.c unix/uxstore.c unix/uxucs.c unix/uxutils.c \ unix/uxutils.h unix/x11misc.c unix/x11misc.h unix/xkeysym.c \ unix/xpmptcfg.c unix/xpmpterm.c unix/xpmpucfg.c \ unix/xpmputty.c utils.c version.c version.h wcwidth.c \ wildcard.c windows/pageant-rc.h windows/pageant.rc \ windows/plink.rc windows/pscp.rc windows/psftp.rc \ windows/putty.rc windows/puttygen-rc.h windows/puttygen.rc \ windows/puttytel.rc windows/rcstuff.h windows/sizetip.c \ windows/version.rc2 windows/win_res.h windows/win_res.rc2 \ windows/wincapi.c windows/wincapi.h windows/wincfg.c \ windows/wincliloop.c windows/wincons.c windows/winctrls.c \ windows/windefs.c windows/windlg.c windows/window.c \ windows/wingss.c windows/winhandl.c windows/winhelp.c \ windows/winhelp.h windows/winhelp.rc2 windows/winhsock.c \ windows/winjump.c windows/winmisc.c windows/winmiscs.c \ windows/winnet.c windows/winnohlp.c windows/winnoise.c \ windows/winnojmp.c windows/winnpc.c windows/winnps.c \ windows/winpgen.c windows/winpgnt.c windows/winpgntc.c \ windows/winplink.c windows/winprint.c windows/winproxy.c \ windows/winseat.h windows/winsecur.c windows/winsecur.h \ windows/winselcli.c windows/winselgui.c windows/winser.c \ windows/winsftp.c windows/winshare.c windows/winsocks.c \ windows/winstore.c windows/winstuff.h windows/wintime.c \ windows/winucs.c windows/winutils.c windows/winx11.c \ x11fwd.c AM_CPPFLAGS = -I$(srcdir)/./ -I$(srcdir)/charset/ -I$(srcdir)/windows/ \ -I$(srcdir)/unix/ @HAVE_GTK_FALSE@AM_CFLAGS = $(COMPAT) $(XFLAGS) $(WARNINGOPTS) @HAVE_GTK_TRUE@AM_CFLAGS = $(GTK_CFLAGS) $(COMPAT) $(XFLAGS) $(WARNINGOPTS) libversion_a_SOURCES = version.c libversion_a_CFLAGS = $(COMPAT) $(XFLAGS) $(WARNINGOPTS) \ $(am__append_1) noinst_LIBRARIES = libversion.a cgtest_SOURCES = cgtest.c conf.c console.c ecc.c import.c marshal.c memory.c \ millerrabin.c misc.c mpint.c mpunsafe.c notiming.c pockle.c \ primecandidate.c smallprimes.c sshaes.c sshargon2.c \ sshauxcrypt.c sshbcrypt.c sshblake2.c sshblowf.c sshdes.c \ sshdss.c sshdssg.c sshecc.c sshecdsag.c sshhmac.c sshmd5.c \ sshprime.c sshprng.c sshpubk.c sshrand.c sshrsa.c sshrsag.c \ sshsh256.c sshsh512.c sshsha.c sshsha3.c stripctrl.c time.c \ tree234.c unix/uxcons.c unix/uxgen.c unix/uxmisc.c \ unix/uxnogtk.c unix/uxnoise.c unix/uxpoll.c unix/uxstore.c \ unix/uxutils.c utils.c wcwidth.c cgtest_LDADD = libversion.a fuzzterm_SOURCES = be_none.c callback.c charset/fromucs.c charset/localenc.c \ charset/macenc.c charset/mimeenc.c charset/sbcs.c \ charset/sbcsdat.c charset/slookup.c charset/toucs.c \ charset/utf8.c charset/xenc.c conf.c config.c dialog.c \ fuzzterm.c logging.c marshal.c memory.c minibidi.c misc.c \ miscucs.c settings.c stripctrl.c terminal.c time.c timing.c \ tree234.c unix/uxcfg.c unix/uxmisc.c unix/uxnogtk.c \ unix/uxprint.c unix/uxstore.c unix/uxucs.c utils.c wcwidth.c fuzzterm_LDADD = libversion.a osxlaunch_SOURCES = unix/osxlaunch.c @HAVE_GTK_TRUE@pageant_SOURCES = aqsync.c be_misc.c be_none.c callback.c conf.c console.c \ @HAVE_GTK_TRUE@ ecc.c errsock.c logging.c marshal.c memory.c misc.c mpint.c \ @HAVE_GTK_TRUE@ nocproxy.c nogss.c nullplug.c pageant.c proxy.c settings.c \ @HAVE_GTK_TRUE@ sshaes.c sshargon2.c sshauxcrypt.c sshblake2.c sshdes.c \ @HAVE_GTK_TRUE@ sshdss.c sshecc.c sshhmac.c sshmd5.c sshprng.c sshpubk.c \ @HAVE_GTK_TRUE@ sshrsa.c sshsh256.c sshsh512.c sshsha.c sshsha3.c \ @HAVE_GTK_TRUE@ stripctrl.c time.c timing.c tree234.c unix/gtkask.c \ @HAVE_GTK_TRUE@ unix/gtkmisc.c unix/ux_x11.c unix/uxagentc.c \ @HAVE_GTK_TRUE@ unix/uxagentsock.c unix/uxcliloop.c unix/uxcons.c \ @HAVE_GTK_TRUE@ unix/uxfdsock.c unix/uxmisc.c unix/uxnet.c unix/uxnoise.c \ @HAVE_GTK_TRUE@ unix/uxpeer.c unix/uxpgnt.c unix/uxpoll.c unix/uxproxy.c \ @HAVE_GTK_TRUE@ unix/uxsel.c unix/uxsignal.c unix/uxstore.c unix/uxutils.c \ @HAVE_GTK_TRUE@ utils.c wcwidth.c x11fwd.c @HAVE_GTK_TRUE@pageant_LDADD = libversion.a $(GTK_LIBS) plink_SOURCES = agentf.c aqsync.c be_all_s.c be_misc.c callback.c clicons.c \ cmdline.c conf.c console.c cproxy.c ecc.c errsock.c ldisc.c \ logging.c mainchan.c marshal.c memory.c misc.c mpint.c \ noterm.c nullplug.c pgssapi.c pinger.c portfwd.c proxy.c \ raw.c rlogin.c sessprep.c settings.c ssh.c ssh1bpp.c \ ssh1censor.c ssh1connection-client.c ssh1connection.c \ ssh1login.c ssh2bpp-bare.c ssh2bpp.c ssh2censor.c \ ssh2connection-client.c ssh2connection.c ssh2kex-client.c \ ssh2transhk.c ssh2transport.c ssh2userauth.c sshaes.c \ ssharcf.c sshargon2.c sshauxcrypt.c sshblake2.c sshblowf.c \ sshccp.c sshcommon.c sshcrc.c sshcrcda.c sshdes.c sshdh.c \ sshdss.c sshecc.c sshgssc.c sshhmac.c sshmac.c sshmd5.c \ sshprng.c sshpubk.c sshrand.c sshrsa.c sshsh256.c sshsh512.c \ sshsha.c sshsha3.c sshshare.c sshutils.c sshverstring.c \ sshzlib.c stripctrl.c supdup.c telnet.c time.c timing.c \ tree234.c unix/ux_x11.c unix/uxagentc.c unix/uxcliloop.c \ unix/uxcons.c unix/uxfdsock.c unix/uxgss.c unix/uxmisc.c \ unix/uxnet.c unix/uxnogtk.c unix/uxnoise.c unix/uxpeer.c \ unix/uxplink.c unix/uxpoll.c unix/uxproxy.c unix/uxsel.c \ unix/uxser.c unix/uxshare.c unix/uxsignal.c unix/uxstore.c \ unix/uxutils.c utils.c wcwidth.c wildcard.c x11fwd.c plink_LDADD = libversion.a pscp_SOURCES = agentf.c aqsync.c be_misc.c be_ssh.c callback.c clicons.c \ cmdline.c conf.c console.c cproxy.c ecc.c errsock.c \ logging.c mainchan.c marshal.c memory.c misc.c mpint.c \ nullplug.c pgssapi.c pinger.c portfwd.c proxy.c pscp.c \ psftpcommon.c settings.c sftp.c sftpcommon.c ssh.c ssh1bpp.c \ ssh1censor.c ssh1connection-client.c ssh1connection.c \ ssh1login.c ssh2bpp-bare.c ssh2bpp.c ssh2censor.c \ ssh2connection-client.c ssh2connection.c ssh2kex-client.c \ ssh2transhk.c ssh2transport.c ssh2userauth.c sshaes.c \ ssharcf.c sshargon2.c sshauxcrypt.c sshblake2.c sshblowf.c \ sshccp.c sshcommon.c sshcrc.c sshcrcda.c sshdes.c sshdh.c \ sshdss.c sshecc.c sshgssc.c sshhmac.c sshmac.c sshmd5.c \ sshprng.c sshpubk.c sshrand.c sshrsa.c sshsh256.c sshsh512.c \ sshsha.c sshsha3.c sshshare.c sshutils.c sshverstring.c \ sshzlib.c stripctrl.c time.c timing.c tree234.c \ unix/uxagentc.c unix/uxcliloop.c unix/uxcons.c \ unix/uxfdsock.c unix/uxgss.c unix/uxmisc.c unix/uxnet.c \ unix/uxnogtk.c unix/uxnoise.c unix/uxpeer.c unix/uxpoll.c \ unix/uxproxy.c unix/uxsel.c unix/uxsftp.c unix/uxshare.c \ unix/uxstore.c unix/uxutils.c utils.c wcwidth.c wildcard.c \ x11fwd.c pscp_LDADD = libversion.a psftp_SOURCES = agentf.c aqsync.c be_misc.c be_ssh.c callback.c clicons.c \ cmdline.c conf.c console.c cproxy.c ecc.c errsock.c \ logging.c mainchan.c marshal.c memory.c misc.c mpint.c \ nullplug.c pgssapi.c pinger.c portfwd.c proxy.c psftp.c \ psftpcommon.c settings.c sftp.c sftpcommon.c ssh.c ssh1bpp.c \ ssh1censor.c ssh1connection-client.c ssh1connection.c \ ssh1login.c ssh2bpp-bare.c ssh2bpp.c ssh2censor.c \ ssh2connection-client.c ssh2connection.c ssh2kex-client.c \ ssh2transhk.c ssh2transport.c ssh2userauth.c sshaes.c \ ssharcf.c sshargon2.c sshauxcrypt.c sshblake2.c sshblowf.c \ sshccp.c sshcommon.c sshcrc.c sshcrcda.c sshdes.c sshdh.c \ sshdss.c sshecc.c sshgssc.c sshhmac.c sshmac.c sshmd5.c \ sshprng.c sshpubk.c sshrand.c sshrsa.c sshsh256.c sshsh512.c \ sshsha.c sshsha3.c sshshare.c sshutils.c sshverstring.c \ sshzlib.c stripctrl.c time.c timing.c tree234.c \ unix/uxagentc.c unix/uxcliloop.c unix/uxcons.c \ unix/uxfdsock.c unix/uxgss.c unix/uxmisc.c unix/uxnet.c \ unix/uxnogtk.c unix/uxnoise.c unix/uxpeer.c unix/uxpoll.c \ unix/uxproxy.c unix/uxsel.c unix/uxsftp.c unix/uxshare.c \ unix/uxstore.c unix/uxutils.c utils.c wcwidth.c wildcard.c \ x11fwd.c psftp_LDADD = libversion.a psocks_SOURCES = be_misc.c callback.c conf.c console.c errsock.c logging.c \ marshal.c memory.c misc.c nocproxy.c norand.c portfwd.c \ proxy.c psocks.c sshutils.c stripctrl.c time.c timing.c \ tree234.c unix/uxcliloop.c unix/uxcons.c unix/uxfdsock.c \ unix/uxmisc.c unix/uxnet.c unix/uxnogtk.c unix/uxpeer.c \ unix/uxpoll.c unix/uxproxy.c unix/uxsel.c unix/uxsignal.c \ unix/uxsocks.c utils.c wcwidth.c psocks_LDADD = libversion.a psusan_SOURCES = be_misc.c be_none.c callback.c conf.c cproxy.c ecc.c \ errsock.c logging.c marshal.c memory.c millerrabin.c misc.c \ mpint.c mpunsafe.c nogss.c nullplug.c pgssapi.c pockle.c \ portfwd.c primecandidate.c proxy.c scpserver.c sesschan.c \ settings.c sftpcommon.c sftpserver.c smallprimes.c ssh1bpp.c \ ssh1censor.c ssh1connection-server.c ssh1connection.c \ ssh1login-server.c ssh2bpp-bare.c ssh2bpp.c ssh2censor.c \ ssh2connection-server.c ssh2connection.c ssh2kex-server.c \ ssh2transhk.c ssh2transport.c ssh2userauth-server.c sshaes.c \ ssharcf.c sshargon2.c sshauxcrypt.c sshblake2.c sshblowf.c \ sshccp.c sshcommon.c sshcrc.c sshcrcda.c sshdes.c sshdh.c \ sshdss.c sshecc.c sshgssc.c sshhmac.c sshmac.c sshmd5.c \ sshprime.c sshprng.c sshpubk.c sshrand.c sshrsa.c sshrsag.c \ sshserver.c sshsh256.c sshsh512.c sshsha.c sshsha3.c \ sshutils.c sshverstring.c sshzlib.c stripctrl.c time.c \ timing.c tree234.c unix/procnet.c unix/ux_x11.c \ unix/uxagentsock.c unix/uxcliloop.c unix/uxfdsock.c \ unix/uxmisc.c unix/uxnet.c unix/uxnogtk.c unix/uxnoise.c \ unix/uxpeer.c unix/uxpoll.c unix/uxproxy.c unix/uxpsusan.c \ unix/uxpty.c unix/uxsel.c unix/uxsftpserver.c \ unix/uxsignal.c unix/uxstore.c unix/uxutils.c utils.c \ wcwidth.c wildcard.c x11fwd.c psusan_LDADD = libversion.a @HAVE_GTK_TRUE@pterm_SOURCES = be_none.c callback.c charset/fromucs.c charset/localenc.c \ @HAVE_GTK_TRUE@ charset/macenc.c charset/mimeenc.c charset/sbcs.c \ @HAVE_GTK_TRUE@ charset/sbcsdat.c charset/slookup.c charset/toucs.c \ @HAVE_GTK_TRUE@ charset/utf8.c charset/xenc.c cmdline.c conf.c config.c \ @HAVE_GTK_TRUE@ dialog.c ldisc.c logging.c marshal.c memory.c minibidi.c \ @HAVE_GTK_TRUE@ misc.c miscucs.c nocproxy.c nogss.c sessprep.c settings.c \ @HAVE_GTK_TRUE@ stripctrl.c terminal.c time.c timing.c tree234.c \ @HAVE_GTK_TRUE@ unix/gtkcfg.c unix/gtkcols.c unix/gtkcomm.c unix/gtkdlg.c \ @HAVE_GTK_TRUE@ unix/gtkfont.c unix/gtkmain.c unix/gtkmisc.c unix/gtkwin.c \ @HAVE_GTK_TRUE@ unix/uxcfg.c unix/uxmisc.c unix/uxprint.c unix/uxpterm.c \ @HAVE_GTK_TRUE@ unix/uxpty.c unix/uxsel.c unix/uxsignal.c unix/uxstore.c \ @HAVE_GTK_TRUE@ unix/uxucs.c unix/x11misc.c unix/xkeysym.c unix/xpmptcfg.c \ @HAVE_GTK_TRUE@ unix/xpmpterm.c utils.c wcwidth.c @HAVE_GTK_TRUE@pterm_LDADD = libversion.a $(GTK_LIBS) @HAVE_GTK_TRUE@ptermapp_SOURCES = be_none.c callback.c charset/fromucs.c charset/localenc.c \ @HAVE_GTK_TRUE@ charset/macenc.c charset/mimeenc.c charset/sbcs.c \ @HAVE_GTK_TRUE@ charset/sbcsdat.c charset/slookup.c charset/toucs.c \ @HAVE_GTK_TRUE@ charset/utf8.c charset/xenc.c conf.c config.c dialog.c \ @HAVE_GTK_TRUE@ ldisc.c logging.c marshal.c memory.c minibidi.c misc.c \ @HAVE_GTK_TRUE@ miscucs.c nocmdline.c nocproxy.c nogss.c sessprep.c \ @HAVE_GTK_TRUE@ settings.c stripctrl.c terminal.c time.c timing.c tree234.c \ @HAVE_GTK_TRUE@ unix/gtkapp.c unix/gtkcfg.c unix/gtkcols.c unix/gtkcomm.c \ @HAVE_GTK_TRUE@ unix/gtkdlg.c unix/gtkfont.c unix/gtkmisc.c unix/gtkwin.c \ @HAVE_GTK_TRUE@ unix/uxcfg.c unix/uxmisc.c unix/uxprint.c unix/uxpterm.c \ @HAVE_GTK_TRUE@ unix/uxpty.c unix/uxsel.c unix/uxsignal.c unix/uxstore.c \ @HAVE_GTK_TRUE@ unix/uxucs.c unix/x11misc.c unix/xkeysym.c unix/xpmptcfg.c \ @HAVE_GTK_TRUE@ unix/xpmpterm.c utils.c wcwidth.c @HAVE_GTK_TRUE@ptermapp_LDADD = libversion.a $(GTK_LIBS) @HAVE_GTK_TRUE@putty_SOURCES = agentf.c aqsync.c be_all_s.c be_misc.c callback.c \ @HAVE_GTK_TRUE@ charset/fromucs.c charset/localenc.c charset/macenc.c \ @HAVE_GTK_TRUE@ charset/mimeenc.c charset/sbcs.c charset/sbcsdat.c \ @HAVE_GTK_TRUE@ charset/slookup.c charset/toucs.c charset/utf8.c \ @HAVE_GTK_TRUE@ charset/xenc.c cmdline.c conf.c config.c cproxy.c dialog.c \ @HAVE_GTK_TRUE@ ecc.c errsock.c ldisc.c logging.c mainchan.c marshal.c \ @HAVE_GTK_TRUE@ memory.c minibidi.c misc.c miscucs.c mpint.c nullplug.c \ @HAVE_GTK_TRUE@ pgssapi.c pinger.c portfwd.c proxy.c raw.c rlogin.c \ @HAVE_GTK_TRUE@ sessprep.c settings.c ssh.c ssh1bpp.c ssh1censor.c \ @HAVE_GTK_TRUE@ ssh1connection-client.c ssh1connection.c ssh1login.c \ @HAVE_GTK_TRUE@ ssh2bpp-bare.c ssh2bpp.c ssh2censor.c \ @HAVE_GTK_TRUE@ ssh2connection-client.c ssh2connection.c ssh2kex-client.c \ @HAVE_GTK_TRUE@ ssh2transhk.c ssh2transport.c ssh2userauth.c sshaes.c \ @HAVE_GTK_TRUE@ ssharcf.c sshargon2.c sshauxcrypt.c sshblake2.c sshblowf.c \ @HAVE_GTK_TRUE@ sshccp.c sshcommon.c sshcrc.c sshcrcda.c sshdes.c sshdh.c \ @HAVE_GTK_TRUE@ sshdss.c sshecc.c sshgssc.c sshhmac.c sshmac.c sshmd5.c \ @HAVE_GTK_TRUE@ sshprng.c sshpubk.c sshrand.c sshrsa.c sshsh256.c sshsh512.c \ @HAVE_GTK_TRUE@ sshsha.c sshsha3.c sshshare.c sshutils.c sshverstring.c \ @HAVE_GTK_TRUE@ sshzlib.c stripctrl.c supdup.c telnet.c terminal.c time.c \ @HAVE_GTK_TRUE@ timing.c tree234.c unix/gtkcfg.c unix/gtkcols.c \ @HAVE_GTK_TRUE@ unix/gtkcomm.c unix/gtkdlg.c unix/gtkfont.c unix/gtkmain.c \ @HAVE_GTK_TRUE@ unix/gtkmisc.c unix/gtkwin.c unix/ux_x11.c unix/uxagentc.c \ @HAVE_GTK_TRUE@ unix/uxcfg.c unix/uxfdsock.c unix/uxgss.c unix/uxmisc.c \ @HAVE_GTK_TRUE@ unix/uxnet.c unix/uxnoise.c unix/uxpeer.c unix/uxpoll.c \ @HAVE_GTK_TRUE@ unix/uxprint.c unix/uxproxy.c unix/uxputty.c unix/uxsel.c \ @HAVE_GTK_TRUE@ unix/uxser.c unix/uxshare.c unix/uxsignal.c unix/uxstore.c \ @HAVE_GTK_TRUE@ unix/uxucs.c unix/uxutils.c unix/x11misc.c unix/xkeysym.c \ @HAVE_GTK_TRUE@ unix/xpmpucfg.c unix/xpmputty.c utils.c wcwidth.c wildcard.c \ @HAVE_GTK_TRUE@ x11fwd.c @HAVE_GTK_TRUE@putty_LDADD = libversion.a $(GTK_LIBS) @HAVE_GTK_TRUE@puttyapp_SOURCES = agentf.c aqsync.c be_all_s.c be_misc.c callback.c \ @HAVE_GTK_TRUE@ charset/fromucs.c charset/localenc.c charset/macenc.c \ @HAVE_GTK_TRUE@ charset/mimeenc.c charset/sbcs.c charset/sbcsdat.c \ @HAVE_GTK_TRUE@ charset/slookup.c charset/toucs.c charset/utf8.c \ @HAVE_GTK_TRUE@ charset/xenc.c conf.c config.c cproxy.c dialog.c ecc.c \ @HAVE_GTK_TRUE@ errsock.c ldisc.c logging.c mainchan.c marshal.c memory.c \ @HAVE_GTK_TRUE@ minibidi.c misc.c miscucs.c mpint.c nocmdline.c nullplug.c \ @HAVE_GTK_TRUE@ pgssapi.c pinger.c portfwd.c proxy.c raw.c rlogin.c \ @HAVE_GTK_TRUE@ sessprep.c settings.c ssh.c ssh1bpp.c ssh1censor.c \ @HAVE_GTK_TRUE@ ssh1connection-client.c ssh1connection.c ssh1login.c \ @HAVE_GTK_TRUE@ ssh2bpp-bare.c ssh2bpp.c ssh2censor.c \ @HAVE_GTK_TRUE@ ssh2connection-client.c ssh2connection.c ssh2kex-client.c \ @HAVE_GTK_TRUE@ ssh2transhk.c ssh2transport.c ssh2userauth.c sshaes.c \ @HAVE_GTK_TRUE@ ssharcf.c sshargon2.c sshauxcrypt.c sshblake2.c sshblowf.c \ @HAVE_GTK_TRUE@ sshccp.c sshcommon.c sshcrc.c sshcrcda.c sshdes.c sshdh.c \ @HAVE_GTK_TRUE@ sshdss.c sshecc.c sshgssc.c sshhmac.c sshmac.c sshmd5.c \ @HAVE_GTK_TRUE@ sshprng.c sshpubk.c sshrand.c sshrsa.c sshsh256.c sshsh512.c \ @HAVE_GTK_TRUE@ sshsha.c sshsha3.c sshshare.c sshutils.c sshverstring.c \ @HAVE_GTK_TRUE@ sshzlib.c stripctrl.c supdup.c telnet.c terminal.c time.c \ @HAVE_GTK_TRUE@ timing.c tree234.c unix/gtkapp.c unix/gtkcfg.c \ @HAVE_GTK_TRUE@ unix/gtkcols.c unix/gtkcomm.c unix/gtkdlg.c unix/gtkfont.c \ @HAVE_GTK_TRUE@ unix/gtkmisc.c unix/gtkwin.c unix/ux_x11.c unix/uxagentc.c \ @HAVE_GTK_TRUE@ unix/uxcfg.c unix/uxfdsock.c unix/uxgss.c unix/uxmisc.c \ @HAVE_GTK_TRUE@ unix/uxnet.c unix/uxnoise.c unix/uxpeer.c unix/uxpoll.c \ @HAVE_GTK_TRUE@ unix/uxprint.c unix/uxproxy.c unix/uxputty.c unix/uxsel.c \ @HAVE_GTK_TRUE@ unix/uxser.c unix/uxshare.c unix/uxsignal.c unix/uxstore.c \ @HAVE_GTK_TRUE@ unix/uxucs.c unix/uxutils.c unix/x11misc.c unix/xkeysym.c \ @HAVE_GTK_TRUE@ unix/xpmpucfg.c unix/xpmputty.c utils.c wcwidth.c wildcard.c \ @HAVE_GTK_TRUE@ x11fwd.c @HAVE_GTK_TRUE@puttyapp_LDADD = libversion.a $(GTK_LIBS) puttygen_SOURCES = cmdgen.c conf.c console.c ecc.c import.c marshal.c \ memory.c millerrabin.c misc.c mpint.c mpunsafe.c notiming.c \ pockle.c primecandidate.c smallprimes.c sshaes.c sshargon2.c \ sshauxcrypt.c sshbcrypt.c sshblake2.c sshblowf.c sshdes.c \ sshdss.c sshdssg.c sshecc.c sshecdsag.c sshhmac.c sshmd5.c \ sshprime.c sshprng.c sshpubk.c sshrand.c sshrsa.c sshrsag.c \ sshsh256.c sshsh512.c sshsha.c sshsha3.c stripctrl.c time.c \ tree234.c unix/uxcons.c unix/uxgen.c unix/uxmisc.c \ unix/uxnogtk.c unix/uxnoise.c unix/uxpoll.c unix/uxstore.c \ unix/uxutils.c utils.c wcwidth.c puttygen_LDADD = libversion.a @HAVE_GTK_TRUE@puttytel_SOURCES = be_misc.c be_nos_s.c callback.c charset/fromucs.c \ @HAVE_GTK_TRUE@ charset/localenc.c charset/macenc.c charset/mimeenc.c \ @HAVE_GTK_TRUE@ charset/sbcs.c charset/sbcsdat.c charset/slookup.c \ @HAVE_GTK_TRUE@ charset/toucs.c charset/utf8.c charset/xenc.c cmdline.c \ @HAVE_GTK_TRUE@ conf.c config.c dialog.c errsock.c ldisc.c logging.c \ @HAVE_GTK_TRUE@ marshal.c memory.c minibidi.c misc.c miscucs.c nocproxy.c \ @HAVE_GTK_TRUE@ nogss.c norand.c pinger.c proxy.c raw.c rlogin.c sessprep.c \ @HAVE_GTK_TRUE@ settings.c stripctrl.c supdup.c telnet.c terminal.c time.c \ @HAVE_GTK_TRUE@ timing.c tree234.c unix/gtkcfg.c unix/gtkcols.c \ @HAVE_GTK_TRUE@ unix/gtkcomm.c unix/gtkdlg.c unix/gtkfont.c unix/gtkmain.c \ @HAVE_GTK_TRUE@ unix/gtkmisc.c unix/gtkwin.c unix/uxcfg.c unix/uxfdsock.c \ @HAVE_GTK_TRUE@ unix/uxmisc.c unix/uxnet.c unix/uxpeer.c unix/uxpoll.c \ @HAVE_GTK_TRUE@ unix/uxprint.c unix/uxproxy.c unix/uxputty.c unix/uxsel.c \ @HAVE_GTK_TRUE@ unix/uxser.c unix/uxsignal.c unix/uxstore.c unix/uxucs.c \ @HAVE_GTK_TRUE@ unix/uxutils.c unix/x11misc.c unix/xkeysym.c unix/xpmpucfg.c \ @HAVE_GTK_TRUE@ unix/xpmputty.c utils.c wcwidth.c @HAVE_GTK_TRUE@puttytel_LDADD = libversion.a $(GTK_LIBS) testcrypt_SOURCES = ecc.c marshal.c memory.c millerrabin.c mpint.c \ mpunsafe.c pockle.c primecandidate.c smallprimes.c sshaes.c \ ssharcf.c sshargon2.c sshauxcrypt.c sshblake2.c sshblowf.c \ sshccp.c sshcrc.c sshcrcda.c sshdes.c sshdh.c sshdss.c \ sshdssg.c sshecc.c sshecdsag.c sshhmac.c sshmd5.c sshprime.c \ sshprng.c sshpubk.c sshrsa.c sshrsag.c sshsh256.c sshsh512.c \ sshsha.c sshsha3.c testcrypt.c tree234.c unix/uxutils.c \ utils.c testsc_SOURCES = ecc.c marshal.c memory.c mpint.c sshaes.c ssharcf.c \ sshargon2.c sshauxcrypt.c sshblake2.c sshblowf.c sshccp.c \ sshcrc.c sshcrcda.c sshdes.c sshdh.c sshdss.c sshecc.c \ sshhmac.c sshmac.c sshmd5.c sshpubk.c sshrsa.c sshsh256.c \ sshsh512.c sshsha.c sshsha3.c testsc.c tree234.c \ unix/uxutils.c utils.c wildcard.c testzlib_SOURCES = marshal.c memory.c sshzlib.c testzlib.c utils.c uppity_SOURCES = be_misc.c be_none.c callback.c conf.c cproxy.c ecc.c \ errsock.c logging.c marshal.c memory.c millerrabin.c misc.c \ mpint.c mpunsafe.c nullplug.c pgssapi.c pockle.c portfwd.c \ primecandidate.c proxy.c scpserver.c sesschan.c settings.c \ sftpcommon.c sftpserver.c smallprimes.c ssh1bpp.c \ ssh1censor.c ssh1connection-server.c ssh1connection.c \ ssh1login-server.c ssh2bpp-bare.c ssh2bpp.c ssh2censor.c \ ssh2connection-server.c ssh2connection.c ssh2kex-server.c \ ssh2transhk.c ssh2transport.c ssh2userauth-server.c sshaes.c \ ssharcf.c sshargon2.c sshauxcrypt.c sshblake2.c sshblowf.c \ sshccp.c sshcommon.c sshcrc.c sshcrcda.c sshdes.c sshdh.c \ sshdss.c sshecc.c sshgssc.c sshhmac.c sshmac.c sshmd5.c \ sshprime.c sshprng.c sshpubk.c sshrand.c sshrsa.c sshrsag.c \ sshserver.c sshsh256.c sshsh512.c sshsha.c sshsha3.c \ sshutils.c sshverstring.c sshzlib.c stripctrl.c time.c \ timing.c tree234.c unix/procnet.c unix/ux_x11.c \ unix/uxagentsock.c unix/uxcliloop.c unix/uxfdsock.c \ unix/uxgss.c unix/uxmisc.c unix/uxnet.c unix/uxnogtk.c \ unix/uxnoise.c unix/uxpeer.c unix/uxpoll.c unix/uxproxy.c \ unix/uxpty.c unix/uxsel.c unix/uxserver.c \ unix/uxsftpserver.c unix/uxsignal.c unix/uxstore.c \ unix/uxutils.c utils.c wcwidth.c wildcard.c x11fwd.c uppity_LDADD = libversion.a @AUTO_GIT_COMMIT_TRUE@BUILT_SOURCES = empty.h @AUTO_GIT_COMMIT_TRUE@CLEANFILES = empty.h @HAVE_GTK_FALSE@man1_MANS = doc/plink.1 doc/pscp.1 doc/psftp.1 doc/puttygen.1 doc/psusan.1 @HAVE_GTK_TRUE@man1_MANS = doc/plink.1 doc/pscp.1 doc/psftp.1 doc/puttygen.1 doc/psusan.1 \ @HAVE_GTK_TRUE@ doc/pageant.1 doc/pterm.1 doc/putty.1 doc/puttytel.1 @HAVE_QUARTZ_TRUE@noinst_SCRIPTS = unix/PuTTY.app unix/Pterm.app all: $(BUILT_SOURCES) uxconfig.h $(MAKE) $(AM_MAKEFLAGS) all-am .SUFFIXES: .SUFFIXES: .c .o .obj am--refresh: Makefile @: $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ echo ' cd $(srcdir) && $(AUTOMAKE) --foreign'; \ $(am__cd) $(srcdir) && $(AUTOMAKE) --foreign \ && exit 0; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ echo ' $(SHELL) ./config.status'; \ $(SHELL) ./config.status;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) $(SHELL) ./config.status --recheck $(top_srcdir)/configure: $(am__configure_deps) $(am__cd) $(srcdir) && $(AUTOCONF) $(ACLOCAL_M4): $(am__aclocal_m4_deps) $(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS) $(am__aclocal_m4_deps): uxconfig.h: stamp-h1 @test -f $@ || rm -f stamp-h1 @test -f $@ || $(MAKE) $(AM_MAKEFLAGS) stamp-h1 stamp-h1: $(srcdir)/uxconfig.in $(top_builddir)/config.status @rm -f stamp-h1 cd $(top_builddir) && $(SHELL) ./config.status uxconfig.h $(srcdir)/uxconfig.in: $(am__configure_deps) ($(am__cd) $(top_srcdir) && $(AUTOHEADER)) rm -f stamp-h1 touch $@ distclean-hdr: -rm -f uxconfig.h stamp-h1 install-binPROGRAMS: $(bin_PROGRAMS) @$(NORMAL_INSTALL) @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ } \ ; done uninstall-binPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(bindir)" && rm -f $$files clean-binPROGRAMS: -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS) clean-noinstPROGRAMS: -test -z "$(noinst_PROGRAMS)" || rm -f $(noinst_PROGRAMS) clean-noinstLIBRARIES: -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) libversion.a: $(libversion_a_OBJECTS) $(libversion_a_DEPENDENCIES) $(EXTRA_libversion_a_DEPENDENCIES) $(AM_V_at)-rm -f libversion.a $(AM_V_AR)$(libversion_a_AR) libversion.a $(libversion_a_OBJECTS) $(libversion_a_LIBADD) $(AM_V_at)$(RANLIB) libversion.a unix/$(am__dirstamp): @$(MKDIR_P) unix @: > unix/$(am__dirstamp) unix/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) unix/$(DEPDIR) @: > unix/$(DEPDIR)/$(am__dirstamp) unix/uxcons.$(OBJEXT): unix/$(am__dirstamp) \ unix/$(DEPDIR)/$(am__dirstamp) unix/uxgen.$(OBJEXT): unix/$(am__dirstamp) \ unix/$(DEPDIR)/$(am__dirstamp) unix/uxmisc.$(OBJEXT): unix/$(am__dirstamp) \ unix/$(DEPDIR)/$(am__dirstamp) unix/uxnogtk.$(OBJEXT): unix/$(am__dirstamp) \ unix/$(DEPDIR)/$(am__dirstamp) unix/uxnoise.$(OBJEXT): unix/$(am__dirstamp) \ unix/$(DEPDIR)/$(am__dirstamp) unix/uxpoll.$(OBJEXT): unix/$(am__dirstamp) \ unix/$(DEPDIR)/$(am__dirstamp) unix/uxstore.$(OBJEXT): unix/$(am__dirstamp) \ unix/$(DEPDIR)/$(am__dirstamp) unix/uxutils.$(OBJEXT): unix/$(am__dirstamp) \ unix/$(DEPDIR)/$(am__dirstamp) cgtest$(EXEEXT): $(cgtest_OBJECTS) $(cgtest_DEPENDENCIES) $(EXTRA_cgtest_DEPENDENCIES) @rm -f cgtest$(EXEEXT) $(AM_V_CCLD)$(LINK) $(cgtest_OBJECTS) $(cgtest_LDADD) $(LIBS) charset/$(am__dirstamp): @$(MKDIR_P) charset @: > charset/$(am__dirstamp) charset/$(DEPDIR)/$(am__dirstamp): @$(MKDIR_P) charset/$(DEPDIR) @: > charset/$(DEPDIR)/$(am__dirstamp) charset/fromucs.$(OBJEXT): charset/$(am__dirstamp) \ charset/$(DEPDIR)/$(am__dirstamp) charset/localenc.$(OBJEXT): charset/$(am__dirstamp) \ charset/$(DEPDIR)/$(am__dirstamp) charset/macenc.$(OBJEXT): charset/$(am__dirstamp) \ charset/$(DEPDIR)/$(am__dirstamp) charset/mimeenc.$(OBJEXT): charset/$(am__dirstamp) \ charset/$(DEPDIR)/$(am__dirstamp) charset/sbcs.$(OBJEXT): charset/$(am__dirstamp) \ charset/$(DEPDIR)/$(am__dirstamp) charset/sbcsdat.$(OBJEXT): charset/$(am__dirstamp) \ charset/$(DEPDIR)/$(am__dirstamp) charset/slookup.$(OBJEXT): charset/$(am__dirstamp) \ charset/$(DEPDIR)/$(am__dirstamp) charset/toucs.$(OBJEXT): charset/$(am__dirstamp) \ charset/$(DEPDIR)/$(am__dirstamp) charset/utf8.$(OBJEXT): charset/$(am__dirstamp) \ charset/$(DEPDIR)/$(am__dirstamp) charset/xenc.$(OBJEXT): charset/$(am__dirstamp) \ charset/$(DEPDIR)/$(am__dirstamp) unix/uxcfg.$(OBJEXT): unix/$(am__dirstamp) \ unix/$(DEPDIR)/$(am__dirstamp) unix/uxprint.$(OBJEXT): unix/$(am__dirstamp) \ unix/$(DEPDIR)/$(am__dirstamp) unix/uxucs.$(OBJEXT): unix/$(am__dirstamp) \ unix/$(DEPDIR)/$(am__dirstamp) fuzzterm$(EXEEXT): $(fuzzterm_OBJECTS) $(fuzzterm_DEPENDENCIES) $(EXTRA_fuzzterm_DEPENDENCIES) @rm -f fuzzterm$(EXEEXT) $(AM_V_CCLD)$(LINK) $(fuzzterm_OBJECTS) $(fuzzterm_LDADD) $(LIBS) unix/osxlaunch.$(OBJEXT): unix/$(am__dirstamp) \ unix/$(DEPDIR)/$(am__dirstamp) osxlaunch$(EXEEXT): $(osxlaunch_OBJECTS) $(osxlaunch_DEPENDENCIES) $(EXTRA_osxlaunch_DEPENDENCIES) @rm -f osxlaunch$(EXEEXT) $(AM_V_CCLD)$(LINK) $(osxlaunch_OBJECTS) $(osxlaunch_LDADD) $(LIBS) unix/gtkask.$(OBJEXT): unix/$(am__dirstamp) \ unix/$(DEPDIR)/$(am__dirstamp) unix/gtkmisc.$(OBJEXT): unix/$(am__dirstamp) \ unix/$(DEPDIR)/$(am__dirstamp) unix/ux_x11.$(OBJEXT): unix/$(am__dirstamp) \ unix/$(DEPDIR)/$(am__dirstamp) unix/uxagentc.$(OBJEXT): unix/$(am__dirstamp) \ unix/$(DEPDIR)/$(am__dirstamp) unix/uxagentsock.$(OBJEXT): unix/$(am__dirstamp) \ unix/$(DEPDIR)/$(am__dirstamp) unix/uxcliloop.$(OBJEXT): unix/$(am__dirstamp) \ unix/$(DEPDIR)/$(am__dirstamp) unix/uxfdsock.$(OBJEXT): unix/$(am__dirstamp) \ unix/$(DEPDIR)/$(am__dirstamp) unix/uxnet.$(OBJEXT): unix/$(am__dirstamp) \ unix/$(DEPDIR)/$(am__dirstamp) unix/uxpeer.$(OBJEXT): unix/$(am__dirstamp) \ unix/$(DEPDIR)/$(am__dirstamp) unix/uxpgnt.$(OBJEXT): unix/$(am__dirstamp) \ unix/$(DEPDIR)/$(am__dirstamp) unix/uxproxy.$(OBJEXT): unix/$(am__dirstamp) \ unix/$(DEPDIR)/$(am__dirstamp) unix/uxsel.$(OBJEXT): unix/$(am__dirstamp) \ unix/$(DEPDIR)/$(am__dirstamp) unix/uxsignal.$(OBJEXT): unix/$(am__dirstamp) \ unix/$(DEPDIR)/$(am__dirstamp) pageant$(EXEEXT): $(pageant_OBJECTS) $(pageant_DEPENDENCIES) $(EXTRA_pageant_DEPENDENCIES) @rm -f pageant$(EXEEXT) $(AM_V_CCLD)$(LINK) $(pageant_OBJECTS) $(pageant_LDADD) $(LIBS) unix/uxgss.$(OBJEXT): unix/$(am__dirstamp) \ unix/$(DEPDIR)/$(am__dirstamp) unix/uxplink.$(OBJEXT): unix/$(am__dirstamp) \ unix/$(DEPDIR)/$(am__dirstamp) unix/uxser.$(OBJEXT): unix/$(am__dirstamp) \ unix/$(DEPDIR)/$(am__dirstamp) unix/uxshare.$(OBJEXT): unix/$(am__dirstamp) \ unix/$(DEPDIR)/$(am__dirstamp) plink$(EXEEXT): $(plink_OBJECTS) $(plink_DEPENDENCIES) $(EXTRA_plink_DEPENDENCIES) @rm -f plink$(EXEEXT) $(AM_V_CCLD)$(LINK) $(plink_OBJECTS) $(plink_LDADD) $(LIBS) unix/uxsftp.$(OBJEXT): unix/$(am__dirstamp) \ unix/$(DEPDIR)/$(am__dirstamp) pscp$(EXEEXT): $(pscp_OBJECTS) $(pscp_DEPENDENCIES) $(EXTRA_pscp_DEPENDENCIES) @rm -f pscp$(EXEEXT) $(AM_V_CCLD)$(LINK) $(pscp_OBJECTS) $(pscp_LDADD) $(LIBS) psftp$(EXEEXT): $(psftp_OBJECTS) $(psftp_DEPENDENCIES) $(EXTRA_psftp_DEPENDENCIES) @rm -f psftp$(EXEEXT) $(AM_V_CCLD)$(LINK) $(psftp_OBJECTS) $(psftp_LDADD) $(LIBS) unix/uxsocks.$(OBJEXT): unix/$(am__dirstamp) \ unix/$(DEPDIR)/$(am__dirstamp) psocks$(EXEEXT): $(psocks_OBJECTS) $(psocks_DEPENDENCIES) $(EXTRA_psocks_DEPENDENCIES) @rm -f psocks$(EXEEXT) $(AM_V_CCLD)$(LINK) $(psocks_OBJECTS) $(psocks_LDADD) $(LIBS) unix/procnet.$(OBJEXT): unix/$(am__dirstamp) \ unix/$(DEPDIR)/$(am__dirstamp) unix/uxpsusan.$(OBJEXT): unix/$(am__dirstamp) \ unix/$(DEPDIR)/$(am__dirstamp) unix/uxpty.$(OBJEXT): unix/$(am__dirstamp) \ unix/$(DEPDIR)/$(am__dirstamp) unix/uxsftpserver.$(OBJEXT): unix/$(am__dirstamp) \ unix/$(DEPDIR)/$(am__dirstamp) psusan$(EXEEXT): $(psusan_OBJECTS) $(psusan_DEPENDENCIES) $(EXTRA_psusan_DEPENDENCIES) @rm -f psusan$(EXEEXT) $(AM_V_CCLD)$(LINK) $(psusan_OBJECTS) $(psusan_LDADD) $(LIBS) unix/gtkcfg.$(OBJEXT): unix/$(am__dirstamp) \ unix/$(DEPDIR)/$(am__dirstamp) unix/gtkcols.$(OBJEXT): unix/$(am__dirstamp) \ unix/$(DEPDIR)/$(am__dirstamp) unix/gtkcomm.$(OBJEXT): unix/$(am__dirstamp) \ unix/$(DEPDIR)/$(am__dirstamp) unix/gtkdlg.$(OBJEXT): unix/$(am__dirstamp) \ unix/$(DEPDIR)/$(am__dirstamp) unix/gtkfont.$(OBJEXT): unix/$(am__dirstamp) \ unix/$(DEPDIR)/$(am__dirstamp) unix/gtkmain.$(OBJEXT): unix/$(am__dirstamp) \ unix/$(DEPDIR)/$(am__dirstamp) unix/gtkwin.$(OBJEXT): unix/$(am__dirstamp) \ unix/$(DEPDIR)/$(am__dirstamp) unix/uxpterm.$(OBJEXT): unix/$(am__dirstamp) \ unix/$(DEPDIR)/$(am__dirstamp) unix/x11misc.$(OBJEXT): unix/$(am__dirstamp) \ unix/$(DEPDIR)/$(am__dirstamp) unix/xkeysym.$(OBJEXT): unix/$(am__dirstamp) \ unix/$(DEPDIR)/$(am__dirstamp) unix/xpmptcfg.$(OBJEXT): unix/$(am__dirstamp) \ unix/$(DEPDIR)/$(am__dirstamp) unix/xpmpterm.$(OBJEXT): unix/$(am__dirstamp) \ unix/$(DEPDIR)/$(am__dirstamp) pterm$(EXEEXT): $(pterm_OBJECTS) $(pterm_DEPENDENCIES) $(EXTRA_pterm_DEPENDENCIES) @rm -f pterm$(EXEEXT) $(AM_V_CCLD)$(LINK) $(pterm_OBJECTS) $(pterm_LDADD) $(LIBS) unix/gtkapp.$(OBJEXT): unix/$(am__dirstamp) \ unix/$(DEPDIR)/$(am__dirstamp) ptermapp$(EXEEXT): $(ptermapp_OBJECTS) $(ptermapp_DEPENDENCIES) $(EXTRA_ptermapp_DEPENDENCIES) @rm -f ptermapp$(EXEEXT) $(AM_V_CCLD)$(LINK) $(ptermapp_OBJECTS) $(ptermapp_LDADD) $(LIBS) unix/uxputty.$(OBJEXT): unix/$(am__dirstamp) \ unix/$(DEPDIR)/$(am__dirstamp) unix/xpmpucfg.$(OBJEXT): unix/$(am__dirstamp) \ unix/$(DEPDIR)/$(am__dirstamp) unix/xpmputty.$(OBJEXT): unix/$(am__dirstamp) \ unix/$(DEPDIR)/$(am__dirstamp) putty$(EXEEXT): $(putty_OBJECTS) $(putty_DEPENDENCIES) $(EXTRA_putty_DEPENDENCIES) @rm -f putty$(EXEEXT) $(AM_V_CCLD)$(LINK) $(putty_OBJECTS) $(putty_LDADD) $(LIBS) puttyapp$(EXEEXT): $(puttyapp_OBJECTS) $(puttyapp_DEPENDENCIES) $(EXTRA_puttyapp_DEPENDENCIES) @rm -f puttyapp$(EXEEXT) $(AM_V_CCLD)$(LINK) $(puttyapp_OBJECTS) $(puttyapp_LDADD) $(LIBS) puttygen$(EXEEXT): $(puttygen_OBJECTS) $(puttygen_DEPENDENCIES) $(EXTRA_puttygen_DEPENDENCIES) @rm -f puttygen$(EXEEXT) $(AM_V_CCLD)$(LINK) $(puttygen_OBJECTS) $(puttygen_LDADD) $(LIBS) puttytel$(EXEEXT): $(puttytel_OBJECTS) $(puttytel_DEPENDENCIES) $(EXTRA_puttytel_DEPENDENCIES) @rm -f puttytel$(EXEEXT) $(AM_V_CCLD)$(LINK) $(puttytel_OBJECTS) $(puttytel_LDADD) $(LIBS) testcrypt$(EXEEXT): $(testcrypt_OBJECTS) $(testcrypt_DEPENDENCIES) $(EXTRA_testcrypt_DEPENDENCIES) @rm -f testcrypt$(EXEEXT) $(AM_V_CCLD)$(LINK) $(testcrypt_OBJECTS) $(testcrypt_LDADD) $(LIBS) testsc$(EXEEXT): $(testsc_OBJECTS) $(testsc_DEPENDENCIES) $(EXTRA_testsc_DEPENDENCIES) @rm -f testsc$(EXEEXT) $(AM_V_CCLD)$(LINK) $(testsc_OBJECTS) $(testsc_LDADD) $(LIBS) testzlib$(EXEEXT): $(testzlib_OBJECTS) $(testzlib_DEPENDENCIES) $(EXTRA_testzlib_DEPENDENCIES) @rm -f testzlib$(EXEEXT) $(AM_V_CCLD)$(LINK) $(testzlib_OBJECTS) $(testzlib_LDADD) $(LIBS) unix/uxserver.$(OBJEXT): unix/$(am__dirstamp) \ unix/$(DEPDIR)/$(am__dirstamp) uppity$(EXEEXT): $(uppity_OBJECTS) $(uppity_DEPENDENCIES) $(EXTRA_uppity_DEPENDENCIES) @rm -f uppity$(EXEEXT) $(AM_V_CCLD)$(LINK) $(uppity_OBJECTS) $(uppity_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) -rm -f charset/*.$(OBJEXT) -rm -f unix/*.$(OBJEXT) distclean-compile: -rm -f *.tab.c @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/agentf.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/aqsync.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/be_all_s.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/be_misc.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/be_none.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/be_nos_s.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/be_ssh.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/callback.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cgtest.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/clicons.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmdgen.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cmdline.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/conf.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/config.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/console.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cproxy.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dialog.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ecc.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/errsock.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fuzzterm.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/import.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ldisc.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libversion_a-version.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/logging.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mainchan.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/marshal.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/memory.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/millerrabin.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/minibidi.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/misc.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/miscucs.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mpint.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mpunsafe.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nocmdline.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nocproxy.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nogss.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/norand.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/noterm.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/notiming.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nullplug.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pageant.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pgssapi.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pinger.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pockle.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/portfwd.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/primecandidate.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/proxy.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pscp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/psftp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/psftpcommon.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/psocks.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/raw.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rlogin.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/scpserver.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sesschan.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sessprep.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/settings.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sftp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sftpcommon.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sftpserver.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/smallprimes.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssh.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssh1bpp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssh1censor.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssh1connection-client.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssh1connection-server.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssh1connection.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssh1login-server.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssh1login.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssh2bpp-bare.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssh2bpp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssh2censor.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssh2connection-client.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssh2connection-server.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssh2connection.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssh2kex-client.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssh2kex-server.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssh2transhk.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssh2transport.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssh2userauth-server.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssh2userauth.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshaes.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssharcf.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshargon2.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshauxcrypt.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshbcrypt.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshblake2.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshblowf.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshccp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshcommon.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshcrc.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshcrcda.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshdes.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshdh.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshdss.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshdssg.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshecc.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshecdsag.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshgssc.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshhmac.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshmac.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshmd5.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshprime.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshprng.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshpubk.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshrand.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshrsa.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshrsag.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshserver.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshsh256.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshsh512.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshsha.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshsha3.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshshare.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshutils.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshverstring.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sshzlib.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stripctrl.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/supdup.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/telnet.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/terminal.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/testcrypt.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/testsc.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/testzlib.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/time.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/timing.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tree234.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utils.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wcwidth.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/wildcard.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/x11fwd.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@charset/$(DEPDIR)/fromucs.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@charset/$(DEPDIR)/localenc.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@charset/$(DEPDIR)/macenc.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@charset/$(DEPDIR)/mimeenc.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@charset/$(DEPDIR)/sbcs.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@charset/$(DEPDIR)/sbcsdat.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@charset/$(DEPDIR)/slookup.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@charset/$(DEPDIR)/toucs.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@charset/$(DEPDIR)/utf8.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@charset/$(DEPDIR)/xenc.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/gtkapp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/gtkask.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/gtkcfg.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/gtkcols.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/gtkcomm.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/gtkdlg.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/gtkfont.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/gtkmain.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/gtkmisc.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/gtkwin.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/osxlaunch.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/procnet.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/ux_x11.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxagentc.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxagentsock.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxcfg.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxcliloop.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxcons.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxfdsock.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxgen.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxgss.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxmisc.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxnet.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxnogtk.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxnoise.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxpeer.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxpgnt.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxplink.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxpoll.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxprint.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxproxy.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxpsusan.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxpterm.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxpty.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxputty.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxsel.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxser.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxserver.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxsftp.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxsftpserver.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxshare.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxsignal.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxsocks.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxstore.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxucs.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/uxutils.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/x11misc.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/xkeysym.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/xpmptcfg.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/xpmpterm.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/xpmpucfg.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@unix/$(DEPDIR)/xpmputty.Po@am__quote@ # am--include-marker $(am__depfiles_remade): @$(MKDIR_P) $(@D) @echo '# dummy' >$@-t && $(am__mv) $@-t $@ am--depfiles: $(am__depfiles_remade) .c.o: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: @am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ @am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ @am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` libversion_a-version.o: version.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libversion_a_CFLAGS) $(CFLAGS) -MT libversion_a-version.o -MD -MP -MF $(DEPDIR)/libversion_a-version.Tpo -c -o libversion_a-version.o `test -f 'version.c' || echo '$(srcdir)/'`version.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libversion_a-version.Tpo $(DEPDIR)/libversion_a-version.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='version.c' object='libversion_a-version.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libversion_a_CFLAGS) $(CFLAGS) -c -o libversion_a-version.o `test -f 'version.c' || echo '$(srcdir)/'`version.c libversion_a-version.obj: version.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libversion_a_CFLAGS) $(CFLAGS) -MT libversion_a-version.obj -MD -MP -MF $(DEPDIR)/libversion_a-version.Tpo -c -o libversion_a-version.obj `if test -f 'version.c'; then $(CYGPATH_W) 'version.c'; else $(CYGPATH_W) '$(srcdir)/version.c'; fi` @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libversion_a-version.Tpo $(DEPDIR)/libversion_a-version.Po @AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='version.c' object='libversion_a-version.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libversion_a_CFLAGS) $(CFLAGS) -c -o libversion_a-version.obj `if test -f 'version.c'; then $(CYGPATH_W) 'version.c'; else $(CYGPATH_W) '$(srcdir)/version.c'; fi` install-man1: $(man1_MANS) @$(NORMAL_INSTALL) @list1='$(man1_MANS)'; \ list2=''; \ test -n "$(man1dir)" \ && test -n "`echo $$list1$$list2`" \ || exit 0; \ echo " $(MKDIR_P) '$(DESTDIR)$(man1dir)'"; \ $(MKDIR_P) "$(DESTDIR)$(man1dir)" || exit 1; \ { for i in $$list1; do echo "$$i"; done; \ if test -n "$$list2"; then \ for i in $$list2; do echo "$$i"; done \ | sed -n '/\.1[a-z]*$$/p'; \ fi; \ } | while read p; do \ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; echo "$$p"; \ done | \ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \ sed 'N;N;s,\n, ,g' | { \ list=; while read file base inst; do \ if test "$$base" = "$$inst"; then list="$$list $$file"; else \ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man1dir)/$$inst'"; \ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man1dir)/$$inst" || exit $$?; \ fi; \ done; \ for i in $$list; do echo "$$i"; done | $(am__base_list) | \ while read files; do \ test -z "$$files" || { \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man1dir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(man1dir)" || exit $$?; }; \ done; } uninstall-man1: @$(NORMAL_UNINSTALL) @list='$(man1_MANS)'; test -n "$(man1dir)" || exit 0; \ files=`{ for i in $$list; do echo "$$i"; done; \ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^1][0-9a-z]*$$,1,;x' \ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \ dir='$(DESTDIR)$(man1dir)'; $(am__uninstall_files_from_dir) ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscope: cscope.files test ! -s cscope.files \ || $(CSCOPE) -b -q $(AM_CSCOPEFLAGS) $(CSCOPEFLAGS) -i cscope.files $(CSCOPE_ARGS) clean-cscope: -rm -f cscope.files cscope.files: clean-cscope cscopelist cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags -rm -f cscope.out cscope.in.out cscope.po.out cscope.files distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) $(am__remove_distdir) test -d "$(distdir)" || mkdir "$(distdir)" @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done -test -n "$(am__skip_mode_fix)" \ || find "$(distdir)" -type d ! -perm -755 \ -exec chmod u+rwx,go+rx {} \; -o \ ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \ ! -type d ! -perm -400 -exec chmod a+r {} \; -o \ ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \ || chmod -R a+r "$(distdir)" dist-gzip: distdir tardir=$(distdir) && $(am__tar) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).tar.gz $(am__post_remove_distdir) dist-bzip2: distdir tardir=$(distdir) && $(am__tar) | BZIP2=$${BZIP2--9} bzip2 -c >$(distdir).tar.bz2 $(am__post_remove_distdir) dist-lzip: distdir tardir=$(distdir) && $(am__tar) | lzip -c $${LZIP_OPT--9} >$(distdir).tar.lz $(am__post_remove_distdir) dist-xz: distdir tardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz $(am__post_remove_distdir) dist-tarZ: distdir @echo WARNING: "Support for distribution archives compressed with" \ "legacy program 'compress' is deprecated." >&2 @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z $(am__post_remove_distdir) dist-shar: distdir @echo WARNING: "Support for shar distribution archives is" \ "deprecated." >&2 @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 shar $(distdir) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).shar.gz $(am__post_remove_distdir) dist-zip: distdir -rm -f $(distdir).zip zip -rq $(distdir).zip $(distdir) $(am__post_remove_distdir) dist dist-all: $(MAKE) $(AM_MAKEFLAGS) $(DIST_TARGETS) am__post_remove_distdir='@:' $(am__post_remove_distdir) # This target untars the dist file and tries a VPATH configuration. Then # it guarantees that the distribution is self-contained by making another # tarfile. distcheck: dist case '$(DIST_ARCHIVES)' in \ *.tar.gz*) \ eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).tar.gz | $(am__untar) ;;\ *.tar.bz2*) \ bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\ *.tar.lz*) \ lzip -dc $(distdir).tar.lz | $(am__untar) ;;\ *.tar.xz*) \ xz -dc $(distdir).tar.xz | $(am__untar) ;;\ *.tar.Z*) \ uncompress -c $(distdir).tar.Z | $(am__untar) ;;\ *.shar.gz*) \ eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).shar.gz | unshar ;;\ *.zip*) \ unzip $(distdir).zip ;;\ esac chmod -R a-w $(distdir) chmod u+w $(distdir) mkdir $(distdir)/_build $(distdir)/_build/sub $(distdir)/_inst chmod a-w $(distdir) test -d $(distdir)/_build || exit 0; \ dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \ && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \ && am__cwd=`pwd` \ && $(am__cd) $(distdir)/_build/sub \ && ../../configure \ $(AM_DISTCHECK_CONFIGURE_FLAGS) \ $(DISTCHECK_CONFIGURE_FLAGS) \ --srcdir=../.. --prefix="$$dc_install_base" \ && $(MAKE) $(AM_MAKEFLAGS) \ && $(MAKE) $(AM_MAKEFLAGS) dvi \ && $(MAKE) $(AM_MAKEFLAGS) check \ && $(MAKE) $(AM_MAKEFLAGS) install \ && $(MAKE) $(AM_MAKEFLAGS) installcheck \ && $(MAKE) $(AM_MAKEFLAGS) uninstall \ && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \ distuninstallcheck \ && chmod -R a-w "$$dc_install_base" \ && ({ \ (cd ../.. && umask 077 && mkdir "$$dc_destdir") \ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \ distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \ } || { rm -rf "$$dc_destdir"; exit 1; }) \ && rm -rf "$$dc_destdir" \ && $(MAKE) $(AM_MAKEFLAGS) dist \ && rm -rf $(DIST_ARCHIVES) \ && $(MAKE) $(AM_MAKEFLAGS) distcleancheck \ && cd "$$am__cwd" \ || exit 1 $(am__post_remove_distdir) @(echo "$(distdir) archives ready for distribution: "; \ list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \ sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x' distuninstallcheck: @test -n '$(distuninstallcheck_dir)' || { \ echo 'ERROR: trying to run $@ with an empty' \ '$$(distuninstallcheck_dir)' >&2; \ exit 1; \ }; \ $(am__cd) '$(distuninstallcheck_dir)' || { \ echo 'ERROR: cannot chdir into $(distuninstallcheck_dir)' >&2; \ exit 1; \ }; \ test `$(am__distuninstallcheck_listfiles) | wc -l` -eq 0 \ || { echo "ERROR: files left after uninstall:" ; \ if test -n "$(DESTDIR)"; then \ echo " (check DESTDIR support)"; \ fi ; \ $(distuninstallcheck_listfiles) ; \ exit 1; } >&2 distcleancheck: distclean @if test '$(srcdir)' = . ; then \ echo "ERROR: distcleancheck can only run from a VPATH build" ; \ exit 1 ; \ fi @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \ || { echo "ERROR: files left in build directory after distclean:" ; \ $(distcleancheck_listfiles) ; \ exit 1; } >&2 check-am: all-am $(MAKE) $(AM_MAKEFLAGS) check-local check: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) check-am all-am: Makefile $(PROGRAMS) $(LIBRARIES) $(SCRIPTS) $(MANS) \ uxconfig.h installdirs: for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(man1dir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) -rm -f charset/$(DEPDIR)/$(am__dirstamp) -rm -f charset/$(am__dirstamp) -rm -f unix/$(DEPDIR)/$(am__dirstamp) -rm -f unix/$(am__dirstamp) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) @HAVE_SETID_CMD_FALSE@install-exec-local: clean: clean-am clean-am: clean-binPROGRAMS clean-generic clean-noinstLIBRARIES \ clean-noinstPROGRAMS mostlyclean-am distclean: distclean-am -rm -f $(am__CONFIG_DISTCLEAN_FILES) -rm -f ./$(DEPDIR)/agentf.Po -rm -f ./$(DEPDIR)/aqsync.Po -rm -f ./$(DEPDIR)/be_all_s.Po -rm -f ./$(DEPDIR)/be_misc.Po -rm -f ./$(DEPDIR)/be_none.Po -rm -f ./$(DEPDIR)/be_nos_s.Po -rm -f ./$(DEPDIR)/be_ssh.Po -rm -f ./$(DEPDIR)/callback.Po -rm -f ./$(DEPDIR)/cgtest.Po -rm -f ./$(DEPDIR)/clicons.Po -rm -f ./$(DEPDIR)/cmdgen.Po -rm -f ./$(DEPDIR)/cmdline.Po -rm -f ./$(DEPDIR)/conf.Po -rm -f ./$(DEPDIR)/config.Po -rm -f ./$(DEPDIR)/console.Po -rm -f ./$(DEPDIR)/cproxy.Po -rm -f ./$(DEPDIR)/dialog.Po -rm -f ./$(DEPDIR)/ecc.Po -rm -f ./$(DEPDIR)/errsock.Po -rm -f ./$(DEPDIR)/fuzzterm.Po -rm -f ./$(DEPDIR)/import.Po -rm -f ./$(DEPDIR)/ldisc.Po -rm -f ./$(DEPDIR)/libversion_a-version.Po -rm -f ./$(DEPDIR)/logging.Po -rm -f ./$(DEPDIR)/mainchan.Po -rm -f ./$(DEPDIR)/marshal.Po -rm -f ./$(DEPDIR)/memory.Po -rm -f ./$(DEPDIR)/millerrabin.Po -rm -f ./$(DEPDIR)/minibidi.Po -rm -f ./$(DEPDIR)/misc.Po -rm -f ./$(DEPDIR)/miscucs.Po -rm -f ./$(DEPDIR)/mpint.Po -rm -f ./$(DEPDIR)/mpunsafe.Po -rm -f ./$(DEPDIR)/nocmdline.Po -rm -f ./$(DEPDIR)/nocproxy.Po -rm -f ./$(DEPDIR)/nogss.Po -rm -f ./$(DEPDIR)/norand.Po -rm -f ./$(DEPDIR)/noterm.Po -rm -f ./$(DEPDIR)/notiming.Po -rm -f ./$(DEPDIR)/nullplug.Po -rm -f ./$(DEPDIR)/pageant.Po -rm -f ./$(DEPDIR)/pgssapi.Po -rm -f ./$(DEPDIR)/pinger.Po -rm -f ./$(DEPDIR)/pockle.Po -rm -f ./$(DEPDIR)/portfwd.Po -rm -f ./$(DEPDIR)/primecandidate.Po -rm -f ./$(DEPDIR)/proxy.Po -rm -f ./$(DEPDIR)/pscp.Po -rm -f ./$(DEPDIR)/psftp.Po -rm -f ./$(DEPDIR)/psftpcommon.Po -rm -f ./$(DEPDIR)/psocks.Po -rm -f ./$(DEPDIR)/raw.Po -rm -f ./$(DEPDIR)/rlogin.Po -rm -f ./$(DEPDIR)/scpserver.Po -rm -f ./$(DEPDIR)/sesschan.Po -rm -f ./$(DEPDIR)/sessprep.Po -rm -f ./$(DEPDIR)/settings.Po -rm -f ./$(DEPDIR)/sftp.Po -rm -f ./$(DEPDIR)/sftpcommon.Po -rm -f ./$(DEPDIR)/sftpserver.Po -rm -f ./$(DEPDIR)/smallprimes.Po -rm -f ./$(DEPDIR)/ssh.Po -rm -f ./$(DEPDIR)/ssh1bpp.Po -rm -f ./$(DEPDIR)/ssh1censor.Po -rm -f ./$(DEPDIR)/ssh1connection-client.Po -rm -f ./$(DEPDIR)/ssh1connection-server.Po -rm -f ./$(DEPDIR)/ssh1connection.Po -rm -f ./$(DEPDIR)/ssh1login-server.Po -rm -f ./$(DEPDIR)/ssh1login.Po -rm -f ./$(DEPDIR)/ssh2bpp-bare.Po -rm -f ./$(DEPDIR)/ssh2bpp.Po -rm -f ./$(DEPDIR)/ssh2censor.Po -rm -f ./$(DEPDIR)/ssh2connection-client.Po -rm -f ./$(DEPDIR)/ssh2connection-server.Po -rm -f ./$(DEPDIR)/ssh2connection.Po -rm -f ./$(DEPDIR)/ssh2kex-client.Po -rm -f ./$(DEPDIR)/ssh2kex-server.Po -rm -f ./$(DEPDIR)/ssh2transhk.Po -rm -f ./$(DEPDIR)/ssh2transport.Po -rm -f ./$(DEPDIR)/ssh2userauth-server.Po -rm -f ./$(DEPDIR)/ssh2userauth.Po -rm -f ./$(DEPDIR)/sshaes.Po -rm -f ./$(DEPDIR)/ssharcf.Po -rm -f ./$(DEPDIR)/sshargon2.Po -rm -f ./$(DEPDIR)/sshauxcrypt.Po -rm -f ./$(DEPDIR)/sshbcrypt.Po -rm -f ./$(DEPDIR)/sshblake2.Po -rm -f ./$(DEPDIR)/sshblowf.Po -rm -f ./$(DEPDIR)/sshccp.Po -rm -f ./$(DEPDIR)/sshcommon.Po -rm -f ./$(DEPDIR)/sshcrc.Po -rm -f ./$(DEPDIR)/sshcrcda.Po -rm -f ./$(DEPDIR)/sshdes.Po -rm -f ./$(DEPDIR)/sshdh.Po -rm -f ./$(DEPDIR)/sshdss.Po -rm -f ./$(DEPDIR)/sshdssg.Po -rm -f ./$(DEPDIR)/sshecc.Po -rm -f ./$(DEPDIR)/sshecdsag.Po -rm -f ./$(DEPDIR)/sshgssc.Po -rm -f ./$(DEPDIR)/sshhmac.Po -rm -f ./$(DEPDIR)/sshmac.Po -rm -f ./$(DEPDIR)/sshmd5.Po -rm -f ./$(DEPDIR)/sshprime.Po -rm -f ./$(DEPDIR)/sshprng.Po -rm -f ./$(DEPDIR)/sshpubk.Po -rm -f ./$(DEPDIR)/sshrand.Po -rm -f ./$(DEPDIR)/sshrsa.Po -rm -f ./$(DEPDIR)/sshrsag.Po -rm -f ./$(DEPDIR)/sshserver.Po -rm -f ./$(DEPDIR)/sshsh256.Po -rm -f ./$(DEPDIR)/sshsh512.Po -rm -f ./$(DEPDIR)/sshsha.Po -rm -f ./$(DEPDIR)/sshsha3.Po -rm -f ./$(DEPDIR)/sshshare.Po -rm -f ./$(DEPDIR)/sshutils.Po -rm -f ./$(DEPDIR)/sshverstring.Po -rm -f ./$(DEPDIR)/sshzlib.Po -rm -f ./$(DEPDIR)/stripctrl.Po -rm -f ./$(DEPDIR)/supdup.Po -rm -f ./$(DEPDIR)/telnet.Po -rm -f ./$(DEPDIR)/terminal.Po -rm -f ./$(DEPDIR)/testcrypt.Po -rm -f ./$(DEPDIR)/testsc.Po -rm -f ./$(DEPDIR)/testzlib.Po -rm -f ./$(DEPDIR)/time.Po -rm -f ./$(DEPDIR)/timing.Po -rm -f ./$(DEPDIR)/tree234.Po -rm -f ./$(DEPDIR)/utils.Po -rm -f ./$(DEPDIR)/wcwidth.Po -rm -f ./$(DEPDIR)/wildcard.Po -rm -f ./$(DEPDIR)/x11fwd.Po -rm -f charset/$(DEPDIR)/fromucs.Po -rm -f charset/$(DEPDIR)/localenc.Po -rm -f charset/$(DEPDIR)/macenc.Po -rm -f charset/$(DEPDIR)/mimeenc.Po -rm -f charset/$(DEPDIR)/sbcs.Po -rm -f charset/$(DEPDIR)/sbcsdat.Po -rm -f charset/$(DEPDIR)/slookup.Po -rm -f charset/$(DEPDIR)/toucs.Po -rm -f charset/$(DEPDIR)/utf8.Po -rm -f charset/$(DEPDIR)/xenc.Po -rm -f unix/$(DEPDIR)/gtkapp.Po -rm -f unix/$(DEPDIR)/gtkask.Po -rm -f unix/$(DEPDIR)/gtkcfg.Po -rm -f unix/$(DEPDIR)/gtkcols.Po -rm -f unix/$(DEPDIR)/gtkcomm.Po -rm -f unix/$(DEPDIR)/gtkdlg.Po -rm -f unix/$(DEPDIR)/gtkfont.Po -rm -f unix/$(DEPDIR)/gtkmain.Po -rm -f unix/$(DEPDIR)/gtkmisc.Po -rm -f unix/$(DEPDIR)/gtkwin.Po -rm -f unix/$(DEPDIR)/osxlaunch.Po -rm -f unix/$(DEPDIR)/procnet.Po -rm -f unix/$(DEPDIR)/ux_x11.Po -rm -f unix/$(DEPDIR)/uxagentc.Po -rm -f unix/$(DEPDIR)/uxagentsock.Po -rm -f unix/$(DEPDIR)/uxcfg.Po -rm -f unix/$(DEPDIR)/uxcliloop.Po -rm -f unix/$(DEPDIR)/uxcons.Po -rm -f unix/$(DEPDIR)/uxfdsock.Po -rm -f unix/$(DEPDIR)/uxgen.Po -rm -f unix/$(DEPDIR)/uxgss.Po -rm -f unix/$(DEPDIR)/uxmisc.Po -rm -f unix/$(DEPDIR)/uxnet.Po -rm -f unix/$(DEPDIR)/uxnogtk.Po -rm -f unix/$(DEPDIR)/uxnoise.Po -rm -f unix/$(DEPDIR)/uxpeer.Po -rm -f unix/$(DEPDIR)/uxpgnt.Po -rm -f unix/$(DEPDIR)/uxplink.Po -rm -f unix/$(DEPDIR)/uxpoll.Po -rm -f unix/$(DEPDIR)/uxprint.Po -rm -f unix/$(DEPDIR)/uxproxy.Po -rm -f unix/$(DEPDIR)/uxpsusan.Po -rm -f unix/$(DEPDIR)/uxpterm.Po -rm -f unix/$(DEPDIR)/uxpty.Po -rm -f unix/$(DEPDIR)/uxputty.Po -rm -f unix/$(DEPDIR)/uxsel.Po -rm -f unix/$(DEPDIR)/uxser.Po -rm -f unix/$(DEPDIR)/uxserver.Po -rm -f unix/$(DEPDIR)/uxsftp.Po -rm -f unix/$(DEPDIR)/uxsftpserver.Po -rm -f unix/$(DEPDIR)/uxshare.Po -rm -f unix/$(DEPDIR)/uxsignal.Po -rm -f unix/$(DEPDIR)/uxsocks.Po -rm -f unix/$(DEPDIR)/uxstore.Po -rm -f unix/$(DEPDIR)/uxucs.Po -rm -f unix/$(DEPDIR)/uxutils.Po -rm -f unix/$(DEPDIR)/x11misc.Po -rm -f unix/$(DEPDIR)/xkeysym.Po -rm -f unix/$(DEPDIR)/xpmptcfg.Po -rm -f unix/$(DEPDIR)/xpmpterm.Po -rm -f unix/$(DEPDIR)/xpmpucfg.Po -rm -f unix/$(DEPDIR)/xpmputty.Po -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-hdr distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-man install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-binPROGRAMS install-exec-local install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-man1 install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f $(am__CONFIG_DISTCLEAN_FILES) -rm -rf $(top_srcdir)/autom4te.cache -rm -f ./$(DEPDIR)/agentf.Po -rm -f ./$(DEPDIR)/aqsync.Po -rm -f ./$(DEPDIR)/be_all_s.Po -rm -f ./$(DEPDIR)/be_misc.Po -rm -f ./$(DEPDIR)/be_none.Po -rm -f ./$(DEPDIR)/be_nos_s.Po -rm -f ./$(DEPDIR)/be_ssh.Po -rm -f ./$(DEPDIR)/callback.Po -rm -f ./$(DEPDIR)/cgtest.Po -rm -f ./$(DEPDIR)/clicons.Po -rm -f ./$(DEPDIR)/cmdgen.Po -rm -f ./$(DEPDIR)/cmdline.Po -rm -f ./$(DEPDIR)/conf.Po -rm -f ./$(DEPDIR)/config.Po -rm -f ./$(DEPDIR)/console.Po -rm -f ./$(DEPDIR)/cproxy.Po -rm -f ./$(DEPDIR)/dialog.Po -rm -f ./$(DEPDIR)/ecc.Po -rm -f ./$(DEPDIR)/errsock.Po -rm -f ./$(DEPDIR)/fuzzterm.Po -rm -f ./$(DEPDIR)/import.Po -rm -f ./$(DEPDIR)/ldisc.Po -rm -f ./$(DEPDIR)/libversion_a-version.Po -rm -f ./$(DEPDIR)/logging.Po -rm -f ./$(DEPDIR)/mainchan.Po -rm -f ./$(DEPDIR)/marshal.Po -rm -f ./$(DEPDIR)/memory.Po -rm -f ./$(DEPDIR)/millerrabin.Po -rm -f ./$(DEPDIR)/minibidi.Po -rm -f ./$(DEPDIR)/misc.Po -rm -f ./$(DEPDIR)/miscucs.Po -rm -f ./$(DEPDIR)/mpint.Po -rm -f ./$(DEPDIR)/mpunsafe.Po -rm -f ./$(DEPDIR)/nocmdline.Po -rm -f ./$(DEPDIR)/nocproxy.Po -rm -f ./$(DEPDIR)/nogss.Po -rm -f ./$(DEPDIR)/norand.Po -rm -f ./$(DEPDIR)/noterm.Po -rm -f ./$(DEPDIR)/notiming.Po -rm -f ./$(DEPDIR)/nullplug.Po -rm -f ./$(DEPDIR)/pageant.Po -rm -f ./$(DEPDIR)/pgssapi.Po -rm -f ./$(DEPDIR)/pinger.Po -rm -f ./$(DEPDIR)/pockle.Po -rm -f ./$(DEPDIR)/portfwd.Po -rm -f ./$(DEPDIR)/primecandidate.Po -rm -f ./$(DEPDIR)/proxy.Po -rm -f ./$(DEPDIR)/pscp.Po -rm -f ./$(DEPDIR)/psftp.Po -rm -f ./$(DEPDIR)/psftpcommon.Po -rm -f ./$(DEPDIR)/psocks.Po -rm -f ./$(DEPDIR)/raw.Po -rm -f ./$(DEPDIR)/rlogin.Po -rm -f ./$(DEPDIR)/scpserver.Po -rm -f ./$(DEPDIR)/sesschan.Po -rm -f ./$(DEPDIR)/sessprep.Po -rm -f ./$(DEPDIR)/settings.Po -rm -f ./$(DEPDIR)/sftp.Po -rm -f ./$(DEPDIR)/sftpcommon.Po -rm -f ./$(DEPDIR)/sftpserver.Po -rm -f ./$(DEPDIR)/smallprimes.Po -rm -f ./$(DEPDIR)/ssh.Po -rm -f ./$(DEPDIR)/ssh1bpp.Po -rm -f ./$(DEPDIR)/ssh1censor.Po -rm -f ./$(DEPDIR)/ssh1connection-client.Po -rm -f ./$(DEPDIR)/ssh1connection-server.Po -rm -f ./$(DEPDIR)/ssh1connection.Po -rm -f ./$(DEPDIR)/ssh1login-server.Po -rm -f ./$(DEPDIR)/ssh1login.Po -rm -f ./$(DEPDIR)/ssh2bpp-bare.Po -rm -f ./$(DEPDIR)/ssh2bpp.Po -rm -f ./$(DEPDIR)/ssh2censor.Po -rm -f ./$(DEPDIR)/ssh2connection-client.Po -rm -f ./$(DEPDIR)/ssh2connection-server.Po -rm -f ./$(DEPDIR)/ssh2connection.Po -rm -f ./$(DEPDIR)/ssh2kex-client.Po -rm -f ./$(DEPDIR)/ssh2kex-server.Po -rm -f ./$(DEPDIR)/ssh2transhk.Po -rm -f ./$(DEPDIR)/ssh2transport.Po -rm -f ./$(DEPDIR)/ssh2userauth-server.Po -rm -f ./$(DEPDIR)/ssh2userauth.Po -rm -f ./$(DEPDIR)/sshaes.Po -rm -f ./$(DEPDIR)/ssharcf.Po -rm -f ./$(DEPDIR)/sshargon2.Po -rm -f ./$(DEPDIR)/sshauxcrypt.Po -rm -f ./$(DEPDIR)/sshbcrypt.Po -rm -f ./$(DEPDIR)/sshblake2.Po -rm -f ./$(DEPDIR)/sshblowf.Po -rm -f ./$(DEPDIR)/sshccp.Po -rm -f ./$(DEPDIR)/sshcommon.Po -rm -f ./$(DEPDIR)/sshcrc.Po -rm -f ./$(DEPDIR)/sshcrcda.Po -rm -f ./$(DEPDIR)/sshdes.Po -rm -f ./$(DEPDIR)/sshdh.Po -rm -f ./$(DEPDIR)/sshdss.Po -rm -f ./$(DEPDIR)/sshdssg.Po -rm -f ./$(DEPDIR)/sshecc.Po -rm -f ./$(DEPDIR)/sshecdsag.Po -rm -f ./$(DEPDIR)/sshgssc.Po -rm -f ./$(DEPDIR)/sshhmac.Po -rm -f ./$(DEPDIR)/sshmac.Po -rm -f ./$(DEPDIR)/sshmd5.Po -rm -f ./$(DEPDIR)/sshprime.Po -rm -f ./$(DEPDIR)/sshprng.Po -rm -f ./$(DEPDIR)/sshpubk.Po -rm -f ./$(DEPDIR)/sshrand.Po -rm -f ./$(DEPDIR)/sshrsa.Po -rm -f ./$(DEPDIR)/sshrsag.Po -rm -f ./$(DEPDIR)/sshserver.Po -rm -f ./$(DEPDIR)/sshsh256.Po -rm -f ./$(DEPDIR)/sshsh512.Po -rm -f ./$(DEPDIR)/sshsha.Po -rm -f ./$(DEPDIR)/sshsha3.Po -rm -f ./$(DEPDIR)/sshshare.Po -rm -f ./$(DEPDIR)/sshutils.Po -rm -f ./$(DEPDIR)/sshverstring.Po -rm -f ./$(DEPDIR)/sshzlib.Po -rm -f ./$(DEPDIR)/stripctrl.Po -rm -f ./$(DEPDIR)/supdup.Po -rm -f ./$(DEPDIR)/telnet.Po -rm -f ./$(DEPDIR)/terminal.Po -rm -f ./$(DEPDIR)/testcrypt.Po -rm -f ./$(DEPDIR)/testsc.Po -rm -f ./$(DEPDIR)/testzlib.Po -rm -f ./$(DEPDIR)/time.Po -rm -f ./$(DEPDIR)/timing.Po -rm -f ./$(DEPDIR)/tree234.Po -rm -f ./$(DEPDIR)/utils.Po -rm -f ./$(DEPDIR)/wcwidth.Po -rm -f ./$(DEPDIR)/wildcard.Po -rm -f ./$(DEPDIR)/x11fwd.Po -rm -f charset/$(DEPDIR)/fromucs.Po -rm -f charset/$(DEPDIR)/localenc.Po -rm -f charset/$(DEPDIR)/macenc.Po -rm -f charset/$(DEPDIR)/mimeenc.Po -rm -f charset/$(DEPDIR)/sbcs.Po -rm -f charset/$(DEPDIR)/sbcsdat.Po -rm -f charset/$(DEPDIR)/slookup.Po -rm -f charset/$(DEPDIR)/toucs.Po -rm -f charset/$(DEPDIR)/utf8.Po -rm -f charset/$(DEPDIR)/xenc.Po -rm -f unix/$(DEPDIR)/gtkapp.Po -rm -f unix/$(DEPDIR)/gtkask.Po -rm -f unix/$(DEPDIR)/gtkcfg.Po -rm -f unix/$(DEPDIR)/gtkcols.Po -rm -f unix/$(DEPDIR)/gtkcomm.Po -rm -f unix/$(DEPDIR)/gtkdlg.Po -rm -f unix/$(DEPDIR)/gtkfont.Po -rm -f unix/$(DEPDIR)/gtkmain.Po -rm -f unix/$(DEPDIR)/gtkmisc.Po -rm -f unix/$(DEPDIR)/gtkwin.Po -rm -f unix/$(DEPDIR)/osxlaunch.Po -rm -f unix/$(DEPDIR)/procnet.Po -rm -f unix/$(DEPDIR)/ux_x11.Po -rm -f unix/$(DEPDIR)/uxagentc.Po -rm -f unix/$(DEPDIR)/uxagentsock.Po -rm -f unix/$(DEPDIR)/uxcfg.Po -rm -f unix/$(DEPDIR)/uxcliloop.Po -rm -f unix/$(DEPDIR)/uxcons.Po -rm -f unix/$(DEPDIR)/uxfdsock.Po -rm -f unix/$(DEPDIR)/uxgen.Po -rm -f unix/$(DEPDIR)/uxgss.Po -rm -f unix/$(DEPDIR)/uxmisc.Po -rm -f unix/$(DEPDIR)/uxnet.Po -rm -f unix/$(DEPDIR)/uxnogtk.Po -rm -f unix/$(DEPDIR)/uxnoise.Po -rm -f unix/$(DEPDIR)/uxpeer.Po -rm -f unix/$(DEPDIR)/uxpgnt.Po -rm -f unix/$(DEPDIR)/uxplink.Po -rm -f unix/$(DEPDIR)/uxpoll.Po -rm -f unix/$(DEPDIR)/uxprint.Po -rm -f unix/$(DEPDIR)/uxproxy.Po -rm -f unix/$(DEPDIR)/uxpsusan.Po -rm -f unix/$(DEPDIR)/uxpterm.Po -rm -f unix/$(DEPDIR)/uxpty.Po -rm -f unix/$(DEPDIR)/uxputty.Po -rm -f unix/$(DEPDIR)/uxsel.Po -rm -f unix/$(DEPDIR)/uxser.Po -rm -f unix/$(DEPDIR)/uxserver.Po -rm -f unix/$(DEPDIR)/uxsftp.Po -rm -f unix/$(DEPDIR)/uxsftpserver.Po -rm -f unix/$(DEPDIR)/uxshare.Po -rm -f unix/$(DEPDIR)/uxsignal.Po -rm -f unix/$(DEPDIR)/uxsocks.Po -rm -f unix/$(DEPDIR)/uxstore.Po -rm -f unix/$(DEPDIR)/uxucs.Po -rm -f unix/$(DEPDIR)/uxutils.Po -rm -f unix/$(DEPDIR)/x11misc.Po -rm -f unix/$(DEPDIR)/xkeysym.Po -rm -f unix/$(DEPDIR)/xpmptcfg.Po -rm -f unix/$(DEPDIR)/xpmpterm.Po -rm -f unix/$(DEPDIR)/xpmpucfg.Po -rm -f unix/$(DEPDIR)/xpmputty.Po -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-binPROGRAMS uninstall-man uninstall-man: uninstall-man1 .MAKE: all check check-am install install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am am--depfiles am--refresh check \ check-am check-local clean clean-binPROGRAMS clean-cscope \ clean-generic clean-noinstLIBRARIES clean-noinstPROGRAMS \ cscope cscopelist-am ctags ctags-am dist dist-all dist-bzip2 \ dist-gzip dist-lzip dist-shar dist-tarZ dist-xz dist-zip \ distcheck distclean distclean-compile distclean-generic \ distclean-hdr distclean-tags distcleancheck distdir \ distuninstallcheck dvi dvi-am html html-am info info-am \ install install-am install-binPROGRAMS install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-exec-local install-html \ install-html-am install-info install-info-am install-man \ install-man1 install-pdf install-pdf-am install-ps \ install-ps-am install-strip installcheck installcheck-am \ installdirs maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-compile mostlyclean-generic pdf pdf-am \ ps ps-am tags tags-am uninstall uninstall-am \ uninstall-binPROGRAMS uninstall-man uninstall-man1 .PRECIOUS: Makefile @AUTO_GIT_COMMIT_TRUE@empty.h: $(allsources) @AUTO_GIT_COMMIT_TRUE@ echo '/* Empty file touched by automake makefile to force rebuild of version.o */' >$@ # Run the cryptsuite tests as part of 'make check'. Override # PUTTY_TESTCRYPT so that cryptsuite will take the testcrypt binary # from the build directory instead of the source directory, in case # this is an out-of-tree build. check-local: testcrypt PUTTY_TESTCRYPT=./testcrypt $(srcdir)/test/cryptsuite.py @HAVE_SETID_CMD_TRUE@install-exec-local: @HAVE_SETID_CMD_TRUE@ @SETID_CMD@ $(bindir)/pterm @HAVE_SETID_CMD_TRUE@ chmod @SETID_MODE@ $(bindir)/pterm @HAVE_QUARTZ_TRUE@unix/PuTTY.app: unix/putty.bundle puttyapp osxlaunch @HAVE_QUARTZ_TRUE@ rm -rf $@ && PUTTY_GTK_PREFIX_FROM_MAKEFILE=$$(pkg-config --variable=prefix gtk+-3.0) gtk-mac-bundler $< @HAVE_QUARTZ_TRUE@unix/Pterm.app: unix/pterm.bundle ptermapp osxlaunch @HAVE_QUARTZ_TRUE@ rm -rf $@ && PUTTY_GTK_PREFIX_FROM_MAKEFILE=$$(pkg-config --variable=prefix gtk+-3.0) gtk-mac-bundler $< # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: putty-0.76/depcomp0000755000175000017500000005602014072266321011137 00000000000000#! /bin/sh # depcomp - compile a program generating dependencies as side-effects scriptversion=2018-03-07.03; # UTC # Copyright (C) 1999-2018 Free Software Foundation, Inc. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # Originally written by Alexandre Oliva . case $1 in '') echo "$0: No command. Try '$0 --help' for more information." 1>&2 exit 1; ;; -h | --h*) cat <<\EOF Usage: depcomp [--help] [--version] PROGRAM [ARGS] Run PROGRAMS ARGS to compile a file, generating dependencies as side-effects. Environment variables: depmode Dependency tracking mode. source Source file read by 'PROGRAMS ARGS'. object Object file output by 'PROGRAMS ARGS'. DEPDIR directory where to store dependencies. depfile Dependency file to output. tmpdepfile Temporary file to use when outputting dependencies. libtool Whether libtool is used (yes/no). Report bugs to . EOF exit $? ;; -v | --v*) echo "depcomp $scriptversion" exit $? ;; esac # Get the directory component of the given path, and save it in the # global variables '$dir'. Note that this directory component will # be either empty or ending with a '/' character. This is deliberate. set_dir_from () { case $1 in */*) dir=`echo "$1" | sed -e 's|/[^/]*$|/|'`;; *) dir=;; esac } # Get the suffix-stripped basename of the given path, and save it the # global variable '$base'. set_base_from () { base=`echo "$1" | sed -e 's|^.*/||' -e 's/\.[^.]*$//'` } # If no dependency file was actually created by the compiler invocation, # we still have to create a dummy depfile, to avoid errors with the # Makefile "include basename.Plo" scheme. make_dummy_depfile () { echo "#dummy" > "$depfile" } # Factor out some common post-processing of the generated depfile. # Requires the auxiliary global variable '$tmpdepfile' to be set. aix_post_process_depfile () { # If the compiler actually managed to produce a dependency file, # post-process it. if test -f "$tmpdepfile"; then # Each line is of the form 'foo.o: dependency.h'. # Do two passes, one to just change these to # $object: dependency.h # and one to simply output # dependency.h: # which is needed to avoid the deleted-header problem. { sed -e "s,^.*\.[$lower]*:,$object:," < "$tmpdepfile" sed -e "s,^.*\.[$lower]*:[$tab ]*,," -e 's,$,:,' < "$tmpdepfile" } > "$depfile" rm -f "$tmpdepfile" else make_dummy_depfile fi } # A tabulation character. tab=' ' # A newline character. nl=' ' # Character ranges might be problematic outside the C locale. # These definitions help. upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ lower=abcdefghijklmnopqrstuvwxyz digits=0123456789 alpha=${upper}${lower} if test -z "$depmode" || test -z "$source" || test -z "$object"; then echo "depcomp: Variables source, object and depmode must be set" 1>&2 exit 1 fi # Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po. depfile=${depfile-`echo "$object" | sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`} tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`} rm -f "$tmpdepfile" # Avoid interferences from the environment. gccflag= dashmflag= # Some modes work just like other modes, but use different flags. We # parameterize here, but still list the modes in the big case below, # to make depend.m4 easier to write. Note that we *cannot* use a case # here, because this file can only contain one case statement. if test "$depmode" = hp; then # HP compiler uses -M and no extra arg. gccflag=-M depmode=gcc fi if test "$depmode" = dashXmstdout; then # This is just like dashmstdout with a different argument. dashmflag=-xM depmode=dashmstdout fi cygpath_u="cygpath -u -f -" if test "$depmode" = msvcmsys; then # This is just like msvisualcpp but w/o cygpath translation. # Just convert the backslash-escaped backslashes to single forward # slashes to satisfy depend.m4 cygpath_u='sed s,\\\\,/,g' depmode=msvisualcpp fi if test "$depmode" = msvc7msys; then # This is just like msvc7 but w/o cygpath translation. # Just convert the backslash-escaped backslashes to single forward # slashes to satisfy depend.m4 cygpath_u='sed s,\\\\,/,g' depmode=msvc7 fi if test "$depmode" = xlc; then # IBM C/C++ Compilers xlc/xlC can output gcc-like dependency information. gccflag=-qmakedep=gcc,-MF depmode=gcc fi case "$depmode" in gcc3) ## gcc 3 implements dependency tracking that does exactly what ## we want. Yay! Note: for some reason libtool 1.4 doesn't like ## it if -MD -MP comes after the -MF stuff. Hmm. ## Unfortunately, FreeBSD c89 acceptance of flags depends upon ## the command line argument order; so add the flags where they ## appear in depend2.am. Note that the slowdown incurred here ## affects only configure: in makefiles, %FASTDEP% shortcuts this. for arg do case $arg in -c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;; *) set fnord "$@" "$arg" ;; esac shift # fnord shift # $arg done "$@" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi mv "$tmpdepfile" "$depfile" ;; gcc) ## Note that this doesn't just cater to obsosete pre-3.x GCC compilers. ## but also to in-use compilers like IMB xlc/xlC and the HP C compiler. ## (see the conditional assignment to $gccflag above). ## There are various ways to get dependency output from gcc. Here's ## why we pick this rather obscure method: ## - Don't want to use -MD because we'd like the dependencies to end ## up in a subdir. Having to rename by hand is ugly. ## (We might end up doing this anyway to support other compilers.) ## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like ## -MM, not -M (despite what the docs say). Also, it might not be ## supported by the other compilers which use the 'gcc' depmode. ## - Using -M directly means running the compiler twice (even worse ## than renaming). if test -z "$gccflag"; then gccflag=-MD, fi "$@" -Wp,"$gccflag$tmpdepfile" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" echo "$object : \\" > "$depfile" # The second -e expression handles DOS-style file names with drive # letters. sed -e 's/^[^:]*: / /' \ -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile" ## This next piece of magic avoids the "deleted header file" problem. ## The problem is that when a header file which appears in a .P file ## is deleted, the dependency causes make to die (because there is ## typically no way to rebuild the header). We avoid this by adding ## dummy dependencies for each header file. Too bad gcc doesn't do ## this for us directly. ## Some versions of gcc put a space before the ':'. On the theory ## that the space means something, we add a space to the output as ## well. hp depmode also adds that space, but also prefixes the VPATH ## to the object. Take care to not repeat it in the output. ## Some versions of the HPUX 10.20 sed can't process this invocation ## correctly. Breaking it into two sed invocations is a workaround. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; hp) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; sgi) if test "$libtool" = yes; then "$@" "-Wp,-MDupdate,$tmpdepfile" else "$@" -MDupdate "$tmpdepfile" fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files echo "$object : \\" > "$depfile" # Clip off the initial element (the dependent). Don't try to be # clever and replace this with sed code, as IRIX sed won't handle # lines with more than a fixed number of characters (4096 in # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines; # the IRIX cc adds comments like '#:fec' to the end of the # dependency line. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' \ | tr "$nl" ' ' >> "$depfile" echo >> "$depfile" # The second pass generates a dummy entry for each header file. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ >> "$depfile" else make_dummy_depfile fi rm -f "$tmpdepfile" ;; xlc) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; aix) # The C for AIX Compiler uses -M and outputs the dependencies # in a .u file. In older versions, this file always lives in the # current directory. Also, the AIX compiler puts '$object:' at the # start of each line; $object doesn't have directory information. # Version 6 uses the directory in both cases. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then tmpdepfile1=$dir$base.u tmpdepfile2=$base.u tmpdepfile3=$dir.libs/$base.u "$@" -Wc,-M else tmpdepfile1=$dir$base.u tmpdepfile2=$dir$base.u tmpdepfile3=$dir$base.u "$@" -M fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" do test -f "$tmpdepfile" && break done aix_post_process_depfile ;; tcc) # tcc (Tiny C Compiler) understand '-MD -MF file' since version 0.9.26 # FIXME: That version still under development at the moment of writing. # Make that this statement remains true also for stable, released # versions. # It will wrap lines (doesn't matter whether long or short) with a # trailing '\', as in: # # foo.o : \ # foo.c \ # foo.h \ # # It will put a trailing '\' even on the last line, and will use leading # spaces rather than leading tabs (at least since its commit 0394caf7 # "Emit spaces for -MD"). "$@" -MD -MF "$tmpdepfile" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" # Each non-empty line is of the form 'foo.o : \' or ' dep.h \'. # We have to change lines of the first kind to '$object: \'. sed -e "s|.*:|$object :|" < "$tmpdepfile" > "$depfile" # And for each line of the second kind, we have to emit a 'dep.h:' # dummy dependency, to avoid the deleted-header problem. sed -n -e 's|^ *\(.*\) *\\$|\1:|p' < "$tmpdepfile" >> "$depfile" rm -f "$tmpdepfile" ;; ## The order of this option in the case statement is important, since the ## shell code in configure will try each of these formats in the order ## listed in this file. A plain '-MD' option would be understood by many ## compilers, so we must ensure this comes after the gcc and icc options. pgcc) # Portland's C compiler understands '-MD'. # Will always output deps to 'file.d' where file is the root name of the # source file under compilation, even if file resides in a subdirectory. # The object file name does not affect the name of the '.d' file. # pgcc 10.2 will output # foo.o: sub/foo.c sub/foo.h # and will wrap long lines using '\' : # foo.o: sub/foo.c ... \ # sub/foo.h ... \ # ... set_dir_from "$object" # Use the source, not the object, to determine the base name, since # that's sadly what pgcc will do too. set_base_from "$source" tmpdepfile=$base.d # For projects that build the same source file twice into different object # files, the pgcc approach of using the *source* file root name can cause # problems in parallel builds. Use a locking strategy to avoid stomping on # the same $tmpdepfile. lockdir=$base.d-lock trap " echo '$0: caught signal, cleaning up...' >&2 rmdir '$lockdir' exit 1 " 1 2 13 15 numtries=100 i=$numtries while test $i -gt 0; do # mkdir is a portable test-and-set. if mkdir "$lockdir" 2>/dev/null; then # This process acquired the lock. "$@" -MD stat=$? # Release the lock. rmdir "$lockdir" break else # If the lock is being held by a different process, wait # until the winning process is done or we timeout. while test -d "$lockdir" && test $i -gt 0; do sleep 1 i=`expr $i - 1` done fi i=`expr $i - 1` done trap - 1 2 13 15 if test $i -le 0; then echo "$0: failed to acquire lock after $numtries attempts" >&2 echo "$0: check lockdir '$lockdir'" >&2 exit 1 fi if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" # Each line is of the form `foo.o: dependent.h', # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'. # Do two passes, one to just change these to # `$object: dependent.h' and one to simply `dependent.h:'. sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process this invocation # correctly. Breaking it into two sed invocations is a workaround. sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; hp2) # The "hp" stanza above does not work with aCC (C++) and HP's ia64 # compilers, which have integrated preprocessors. The correct option # to use with these is +Maked; it writes dependencies to a file named # 'foo.d', which lands next to the object file, wherever that # happens to be. # Much of this is similar to the tru64 case; see comments there. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then tmpdepfile1=$dir$base.d tmpdepfile2=$dir.libs/$base.d "$@" -Wc,+Maked else tmpdepfile1=$dir$base.d tmpdepfile2=$dir$base.d "$@" +Maked fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" do test -f "$tmpdepfile" && break done if test -f "$tmpdepfile"; then sed -e "s,^.*\.[$lower]*:,$object:," "$tmpdepfile" > "$depfile" # Add 'dependent.h:' lines. sed -ne '2,${ s/^ *// s/ \\*$// s/$/:/ p }' "$tmpdepfile" >> "$depfile" else make_dummy_depfile fi rm -f "$tmpdepfile" "$tmpdepfile2" ;; tru64) # The Tru64 compiler uses -MD to generate dependencies as a side # effect. 'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'. # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put # dependencies in 'foo.d' instead, so we check for that too. # Subdirectories are respected. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then # Libtool generates 2 separate objects for the 2 libraries. These # two compilations output dependencies in $dir.libs/$base.o.d and # in $dir$base.o.d. We have to check for both files, because # one of the two compilations can be disabled. We should prefer # $dir$base.o.d over $dir.libs/$base.o.d because the latter is # automatically cleaned when .libs/ is deleted, while ignoring # the former would cause a distcleancheck panic. tmpdepfile1=$dir$base.o.d # libtool 1.5 tmpdepfile2=$dir.libs/$base.o.d # Likewise. tmpdepfile3=$dir.libs/$base.d # Compaq CCC V6.2-504 "$@" -Wc,-MD else tmpdepfile1=$dir$base.d tmpdepfile2=$dir$base.d tmpdepfile3=$dir$base.d "$@" -MD fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" do test -f "$tmpdepfile" && break done # Same post-processing that is required for AIX mode. aix_post_process_depfile ;; msvc7) if test "$libtool" = yes; then showIncludes=-Wc,-showIncludes else showIncludes=-showIncludes fi "$@" $showIncludes > "$tmpdepfile" stat=$? grep -v '^Note: including file: ' "$tmpdepfile" if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" echo "$object : \\" > "$depfile" # The first sed program below extracts the file names and escapes # backslashes for cygpath. The second sed program outputs the file # name when reading, but also accumulates all include files in the # hold buffer in order to output them again at the end. This only # works with sed implementations that can handle large buffers. sed < "$tmpdepfile" -n ' /^Note: including file: *\(.*\)/ { s//\1/ s/\\/\\\\/g p }' | $cygpath_u | sort -u | sed -n ' s/ /\\ /g s/\(.*\)/'"$tab"'\1 \\/p s/.\(.*\) \\/\1:/ H $ { s/.*/'"$tab"'/ G p }' >> "$depfile" echo >> "$depfile" # make sure the fragment doesn't end with a backslash rm -f "$tmpdepfile" ;; msvc7msys) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; #nosideeffect) # This comment above is used by automake to tell side-effect # dependency tracking mechanisms from slower ones. dashmstdout) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout, regardless of -o. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # Remove '-o $object'. IFS=" " for arg do case $arg in -o) shift ;; $object) shift ;; *) set fnord "$@" "$arg" shift # fnord shift # $arg ;; esac done test -z "$dashmflag" && dashmflag=-M # Require at least two characters before searching for ':' # in the target name. This is to cope with DOS-style filenames: # a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise. "$@" $dashmflag | sed "s|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$object: |" > "$tmpdepfile" rm -f "$depfile" cat < "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process this sed invocation # correctly. Breaking it into two sed invocations is a workaround. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; dashXmstdout) # This case only exists to satisfy depend.m4. It is never actually # run, as this mode is specially recognized in the preamble. exit 1 ;; makedepend) "$@" || exit $? # Remove any Libtool call if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # X makedepend shift cleared=no eat=no for arg do case $cleared in no) set ""; shift cleared=yes ;; esac if test $eat = yes; then eat=no continue fi case "$arg" in -D*|-I*) set fnord "$@" "$arg"; shift ;; # Strip any option that makedepend may not understand. Remove # the object too, otherwise makedepend will parse it as a source file. -arch) eat=yes ;; -*|$object) ;; *) set fnord "$@" "$arg"; shift ;; esac done obj_suffix=`echo "$object" | sed 's/^.*\././'` touch "$tmpdepfile" ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@" rm -f "$depfile" # makedepend may prepend the VPATH from the source file name to the object. # No need to regex-escape $object, excess matching of '.' is harmless. sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process the last invocation # correctly. Breaking it into two sed invocations is a workaround. sed '1,2d' "$tmpdepfile" \ | tr ' ' "$nl" \ | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" "$tmpdepfile".bak ;; cpp) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # Remove '-o $object'. IFS=" " for arg do case $arg in -o) shift ;; $object) shift ;; *) set fnord "$@" "$arg" shift # fnord shift # $arg ;; esac done "$@" -E \ | sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ | sed '$ s: \\$::' > "$tmpdepfile" rm -f "$depfile" echo "$object : \\" > "$depfile" cat < "$tmpdepfile" >> "$depfile" sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; msvisualcpp) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi IFS=" " for arg do case "$arg" in -o) shift ;; $object) shift ;; "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI") set fnord "$@" shift shift ;; *) set fnord "$@" "$arg" shift shift ;; esac done "$@" -E 2>/dev/null | sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile" rm -f "$depfile" echo "$object : \\" > "$depfile" sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile" echo "$tab" >> "$depfile" sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile" rm -f "$tmpdepfile" ;; msvcmsys) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; none) exec "$@" ;; *) echo "Unknown depmode $depmode" 1>&2 exit 1 ;; esac exit 0 # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: putty-0.76/missing0000755000175000017500000001533614072266321011166 00000000000000#! /bin/sh # Common wrapper for a few potentially missing GNU programs. scriptversion=2018-03-07.03; # UTC # Copyright (C) 1996-2018 Free Software Foundation, Inc. # Originally written by Fran,cois Pinard , 1996. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. if test $# -eq 0; then echo 1>&2 "Try '$0 --help' for more information" exit 1 fi case $1 in --is-lightweight) # Used by our autoconf macros to check whether the available missing # script is modern enough. exit 0 ;; --run) # Back-compat with the calling convention used by older automake. shift ;; -h|--h|--he|--hel|--help) echo "\ $0 [OPTION]... PROGRAM [ARGUMENT]... Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due to PROGRAM being missing or too old. Options: -h, --help display this help and exit -v, --version output version information and exit Supported PROGRAM values: aclocal autoconf autoheader autom4te automake makeinfo bison yacc flex lex help2man Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and 'g' are ignored when checking the name. Send bug reports to ." exit $? ;; -v|--v|--ve|--ver|--vers|--versi|--versio|--version) echo "missing $scriptversion (GNU Automake)" exit $? ;; -*) echo 1>&2 "$0: unknown '$1' option" echo 1>&2 "Try '$0 --help' for more information" exit 1 ;; esac # Run the given program, remember its exit status. "$@"; st=$? # If it succeeded, we are done. test $st -eq 0 && exit 0 # Also exit now if we it failed (or wasn't found), and '--version' was # passed; such an option is passed most likely to detect whether the # program is present and works. case $2 in --version|--help) exit $st;; esac # Exit code 63 means version mismatch. This often happens when the user # tries to use an ancient version of a tool on a file that requires a # minimum version. if test $st -eq 63; then msg="probably too old" elif test $st -eq 127; then # Program was missing. msg="missing on your system" else # Program was found and executed, but failed. Give up. exit $st fi perl_URL=https://www.perl.org/ flex_URL=https://github.com/westes/flex gnu_software_URL=https://www.gnu.org/software program_details () { case $1 in aclocal|automake) echo "The '$1' program is part of the GNU Automake package:" echo "<$gnu_software_URL/automake>" echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:" echo "<$gnu_software_URL/autoconf>" echo "<$gnu_software_URL/m4/>" echo "<$perl_URL>" ;; autoconf|autom4te|autoheader) echo "The '$1' program is part of the GNU Autoconf package:" echo "<$gnu_software_URL/autoconf/>" echo "It also requires GNU m4 and Perl in order to run:" echo "<$gnu_software_URL/m4/>" echo "<$perl_URL>" ;; esac } give_advice () { # Normalize program name to check for. normalized_program=`echo "$1" | sed ' s/^gnu-//; t s/^gnu//; t s/^g//; t'` printf '%s\n' "'$1' is $msg." configure_deps="'configure.ac' or m4 files included by 'configure.ac'" case $normalized_program in autoconf*) echo "You should only need it if you modified 'configure.ac'," echo "or m4 files included by it." program_details 'autoconf' ;; autoheader*) echo "You should only need it if you modified 'acconfig.h' or" echo "$configure_deps." program_details 'autoheader' ;; automake*) echo "You should only need it if you modified 'Makefile.am' or" echo "$configure_deps." program_details 'automake' ;; aclocal*) echo "You should only need it if you modified 'acinclude.m4' or" echo "$configure_deps." program_details 'aclocal' ;; autom4te*) echo "You might have modified some maintainer files that require" echo "the 'autom4te' program to be rebuilt." program_details 'autom4te' ;; bison*|yacc*) echo "You should only need it if you modified a '.y' file." echo "You may want to install the GNU Bison package:" echo "<$gnu_software_URL/bison/>" ;; lex*|flex*) echo "You should only need it if you modified a '.l' file." echo "You may want to install the Fast Lexical Analyzer package:" echo "<$flex_URL>" ;; help2man*) echo "You should only need it if you modified a dependency" \ "of a man page." echo "You may want to install the GNU Help2man package:" echo "<$gnu_software_URL/help2man/>" ;; makeinfo*) echo "You should only need it if you modified a '.texi' file, or" echo "any other file indirectly affecting the aspect of the manual." echo "You might want to install the Texinfo package:" echo "<$gnu_software_URL/texinfo/>" echo "The spurious makeinfo call might also be the consequence of" echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might" echo "want to install GNU make:" echo "<$gnu_software_URL/make/>" ;; *) echo "You might have modified some files without having the proper" echo "tools for further handling them. Check the 'README' file, it" echo "often tells you about the needed prerequisites for installing" echo "this package. You may also peek at any GNU archive site, in" echo "case some other package contains this missing '$1' program." ;; esac } give_advice "$1" | sed -e '1s/^/WARNING: /' \ -e '2,$s/^/ /' >&2 # Propagate the correct exit status (expected to be 127 for a program # not found, 63 for a program that failed due to version mismatch). exit $st # Local variables: # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: putty-0.76/install-sh0000755000175000017500000003601014072266321011563 00000000000000#!/bin/sh # install - install a program, script, or datafile scriptversion=2018-03-11.20; # UTC # This originates from X11R5 (mit/util/scripts/install.sh), which was # later released in X11R6 (xc/config/util/install.sh) with the # following copyright and license. # # Copyright (C) 1994 X Consortium # # 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: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN # AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- # TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Except as contained in this notice, the name of the X Consortium shall not # be used in advertising or otherwise to promote the sale, use or other deal- # ings in this Software without prior written authorization from the X Consor- # tium. # # # FSF changes to this file are in the public domain. # # Calling this script install-sh is preferred over install.sh, to prevent # 'make' implicit rules from creating a file called install from it # when there is no Makefile. # # This script is compatible with the BSD install script, but was written # from scratch. tab=' ' nl=' ' IFS=" $tab$nl" # Set DOITPROG to "echo" to test this script. doit=${DOITPROG-} doit_exec=${doit:-exec} # Put in absolute file names if you don't have them in your path; # or use environment vars. chgrpprog=${CHGRPPROG-chgrp} chmodprog=${CHMODPROG-chmod} chownprog=${CHOWNPROG-chown} cmpprog=${CMPPROG-cmp} cpprog=${CPPROG-cp} mkdirprog=${MKDIRPROG-mkdir} mvprog=${MVPROG-mv} rmprog=${RMPROG-rm} stripprog=${STRIPPROG-strip} posix_mkdir= # Desired mode of installed file. mode=0755 chgrpcmd= chmodcmd=$chmodprog chowncmd= mvcmd=$mvprog rmcmd="$rmprog -f" stripcmd= src= dst= dir_arg= dst_arg= copy_on_change=false is_target_a_directory=possibly usage="\ Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE or: $0 [OPTION]... SRCFILES... DIRECTORY or: $0 [OPTION]... -t DIRECTORY SRCFILES... or: $0 [OPTION]... -d DIRECTORIES... In the 1st form, copy SRCFILE to DSTFILE. In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. In the 4th, create DIRECTORIES. Options: --help display this help and exit. --version display version info and exit. -c (ignored) -C install only if different (preserve the last data modification time) -d create directories instead of installing files. -g GROUP $chgrpprog installed files to GROUP. -m MODE $chmodprog installed files to MODE. -o USER $chownprog installed files to USER. -s $stripprog installed files. -t DIRECTORY install into DIRECTORY. -T report an error if DSTFILE is a directory. Environment variables override the default commands: CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG " while test $# -ne 0; do case $1 in -c) ;; -C) copy_on_change=true;; -d) dir_arg=true;; -g) chgrpcmd="$chgrpprog $2" shift;; --help) echo "$usage"; exit $?;; -m) mode=$2 case $mode in *' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*) echo "$0: invalid mode: $mode" >&2 exit 1;; esac shift;; -o) chowncmd="$chownprog $2" shift;; -s) stripcmd=$stripprog;; -t) is_target_a_directory=always dst_arg=$2 # Protect names problematic for 'test' and other utilities. case $dst_arg in -* | [=\(\)!]) dst_arg=./$dst_arg;; esac shift;; -T) is_target_a_directory=never;; --version) echo "$0 $scriptversion"; exit $?;; --) shift break;; -*) echo "$0: invalid option: $1" >&2 exit 1;; *) break;; esac shift done # We allow the use of options -d and -T together, by making -d # take the precedence; this is for compatibility with GNU install. if test -n "$dir_arg"; then if test -n "$dst_arg"; then echo "$0: target directory not allowed when installing a directory." >&2 exit 1 fi fi if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then # When -d is used, all remaining arguments are directories to create. # When -t is used, the destination is already specified. # Otherwise, the last argument is the destination. Remove it from $@. for arg do if test -n "$dst_arg"; then # $@ is not empty: it contains at least $arg. set fnord "$@" "$dst_arg" shift # fnord fi shift # arg dst_arg=$arg # Protect names problematic for 'test' and other utilities. case $dst_arg in -* | [=\(\)!]) dst_arg=./$dst_arg;; esac done fi if test $# -eq 0; then if test -z "$dir_arg"; then echo "$0: no input file specified." >&2 exit 1 fi # It's OK to call 'install-sh -d' without argument. # This can happen when creating conditional directories. exit 0 fi if test -z "$dir_arg"; then if test $# -gt 1 || test "$is_target_a_directory" = always; then if test ! -d "$dst_arg"; then echo "$0: $dst_arg: Is not a directory." >&2 exit 1 fi fi fi if test -z "$dir_arg"; then do_exit='(exit $ret); exit $ret' trap "ret=129; $do_exit" 1 trap "ret=130; $do_exit" 2 trap "ret=141; $do_exit" 13 trap "ret=143; $do_exit" 15 # Set umask so as not to create temps with too-generous modes. # However, 'strip' requires both read and write access to temps. case $mode in # Optimize common cases. *644) cp_umask=133;; *755) cp_umask=22;; *[0-7]) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw='% 200' fi cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; *) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw=,u+rw fi cp_umask=$mode$u_plus_rw;; esac fi for src do # Protect names problematic for 'test' and other utilities. case $src in -* | [=\(\)!]) src=./$src;; esac if test -n "$dir_arg"; then dst=$src dstdir=$dst test -d "$dstdir" dstdir_status=$? else # Waiting for this to be detected by the "$cpprog $src $dsttmp" command # might cause directories to be created, which would be especially bad # if $src (and thus $dsttmp) contains '*'. if test ! -f "$src" && test ! -d "$src"; then echo "$0: $src does not exist." >&2 exit 1 fi if test -z "$dst_arg"; then echo "$0: no destination specified." >&2 exit 1 fi dst=$dst_arg # If destination is a directory, append the input filename. if test -d "$dst"; then if test "$is_target_a_directory" = never; then echo "$0: $dst_arg: Is a directory" >&2 exit 1 fi dstdir=$dst dstbase=`basename "$src"` case $dst in */) dst=$dst$dstbase;; *) dst=$dst/$dstbase;; esac dstdir_status=0 else dstdir=`dirname "$dst"` test -d "$dstdir" dstdir_status=$? fi fi case $dstdir in */) dstdirslash=$dstdir;; *) dstdirslash=$dstdir/;; esac obsolete_mkdir_used=false if test $dstdir_status != 0; then case $posix_mkdir in '') # Create intermediate dirs using mode 755 as modified by the umask. # This is like FreeBSD 'install' as of 1997-10-28. umask=`umask` case $stripcmd.$umask in # Optimize common cases. *[2367][2367]) mkdir_umask=$umask;; .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; *[0-7]) mkdir_umask=`expr $umask + 22 \ - $umask % 100 % 40 + $umask % 20 \ - $umask % 10 % 4 + $umask % 2 `;; *) mkdir_umask=$umask,go-w;; esac # With -d, create the new directory with the user-specified mode. # Otherwise, rely on $mkdir_umask. if test -n "$dir_arg"; then mkdir_mode=-m$mode else mkdir_mode= fi posix_mkdir=false case $umask in *[123567][0-7][0-7]) # POSIX mkdir -p sets u+wx bits regardless of umask, which # is incompatible with FreeBSD 'install' when (umask & 300) != 0. ;; *) # Note that $RANDOM variable is not portable (e.g. dash); Use it # here however when possible just to lower collision chance. tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ trap 'ret=$?; rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null; exit $ret' 0 # Because "mkdir -p" follows existing symlinks and we likely work # directly in world-writeable /tmp, make sure that the '$tmpdir' # directory is successfully created first before we actually test # 'mkdir -p' feature. if (umask $mkdir_umask && $mkdirprog $mkdir_mode "$tmpdir" && exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1 then if test -z "$dir_arg" || { # Check for POSIX incompatibilities with -m. # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or # other-writable bit of parent directory when it shouldn't. # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. test_tmpdir="$tmpdir/a" ls_ld_tmpdir=`ls -ld "$test_tmpdir"` case $ls_ld_tmpdir in d????-?r-*) different_mode=700;; d????-?--*) different_mode=755;; *) false;; esac && $mkdirprog -m$different_mode -p -- "$test_tmpdir" && { ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"` test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" } } then posix_mkdir=: fi rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" else # Remove any dirs left behind by ancient mkdir implementations. rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null fi trap '' 0;; esac;; esac if $posix_mkdir && ( umask $mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" ) then : else # The umask is ridiculous, or mkdir does not conform to POSIX, # or it failed possibly due to a race condition. Create the # directory the slow way, step by step, checking for races as we go. case $dstdir in /*) prefix='/';; [-=\(\)!]*) prefix='./';; *) prefix='';; esac oIFS=$IFS IFS=/ set -f set fnord $dstdir shift set +f IFS=$oIFS prefixes= for d do test X"$d" = X && continue prefix=$prefix$d if test -d "$prefix"; then prefixes= else if $posix_mkdir; then (umask=$mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break # Don't fail if two instances are running concurrently. test -d "$prefix" || exit 1 else case $prefix in *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; *) qprefix=$prefix;; esac prefixes="$prefixes '$qprefix'" fi fi prefix=$prefix/ done if test -n "$prefixes"; then # Don't fail if two instances are running concurrently. (umask $mkdir_umask && eval "\$doit_exec \$mkdirprog $prefixes") || test -d "$dstdir" || exit 1 obsolete_mkdir_used=true fi fi fi if test -n "$dir_arg"; then { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 else # Make a couple of temp file names in the proper directory. dsttmp=${dstdirslash}_inst.$$_ rmtmp=${dstdirslash}_rm.$$_ # Trap to clean up those temp files at exit. trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 # Copy the file name to the temp name. (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && # and set any options; do chmod last to preserve setuid bits. # # If any of these fail, we abort the whole thing. If we want to # ignore errors from any of these, just make sure not to ignore # errors from the above "$doit $cpprog $src $dsttmp" command. # { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && # If -C, don't bother to copy if it wouldn't change the file. if $copy_on_change && old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && set -f && set X $old && old=:$2:$4:$5:$6 && set X $new && new=:$2:$4:$5:$6 && set +f && test "$old" = "$new" && $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 then rm -f "$dsttmp" else # Rename the file to the real destination. $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || # The rename failed, perhaps because mv can't rename something else # to itself, or perhaps because mv is so ancient that it does not # support -f. { # Now remove or move aside any old file at destination location. # We try this two ways since rm can't unlink itself on some # systems and the destination file might be busy for other # reasons. In this case, the final cleanup might fail but the new # file should still install successfully. { test ! -f "$dst" || $doit $rmcmd -f "$dst" 2>/dev/null || { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } } || { echo "$0: cannot unlink or rename $dst" >&2 (exit 1); exit 1 } } && # Now rename the file to the real destination. $doit $mvcmd "$dsttmp" "$dst" } fi || exit 1 trap '' 0 fi done # Local variables: # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: putty-0.76/compile0000755000175000017500000001632714072266321011146 00000000000000#! /bin/sh # Wrapper for compilers which do not understand '-c -o'. scriptversion=2018-03-07.03; # UTC # Copyright (C) 1999-2018 Free Software Foundation, Inc. # Written by Tom Tromey . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # This file is maintained in Automake, please report # bugs to or send patches to # . nl=' ' # We need space, tab and new line, in precisely that order. Quoting is # there to prevent tools from complaining about whitespace usage. IFS=" "" $nl" file_conv= # func_file_conv build_file lazy # Convert a $build file to $host form and store it in $file # Currently only supports Windows hosts. If the determined conversion # type is listed in (the comma separated) LAZY, no conversion will # take place. func_file_conv () { file=$1 case $file in / | /[!/]*) # absolute file, and not a UNC file if test -z "$file_conv"; then # lazily determine how to convert abs files case `uname -s` in MINGW*) file_conv=mingw ;; CYGWIN*) file_conv=cygwin ;; *) file_conv=wine ;; esac fi case $file_conv/,$2, in *,$file_conv,*) ;; mingw/*) file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` ;; cygwin/*) file=`cygpath -m "$file" || echo "$file"` ;; wine/*) file=`winepath -w "$file" || echo "$file"` ;; esac ;; esac } # func_cl_dashL linkdir # Make cl look for libraries in LINKDIR func_cl_dashL () { func_file_conv "$1" if test -z "$lib_path"; then lib_path=$file else lib_path="$lib_path;$file" fi linker_opts="$linker_opts -LIBPATH:$file" } # func_cl_dashl library # Do a library search-path lookup for cl func_cl_dashl () { lib=$1 found=no save_IFS=$IFS IFS=';' for dir in $lib_path $LIB do IFS=$save_IFS if $shared && test -f "$dir/$lib.dll.lib"; then found=yes lib=$dir/$lib.dll.lib break fi if test -f "$dir/$lib.lib"; then found=yes lib=$dir/$lib.lib break fi if test -f "$dir/lib$lib.a"; then found=yes lib=$dir/lib$lib.a break fi done IFS=$save_IFS if test "$found" != yes; then lib=$lib.lib fi } # func_cl_wrapper cl arg... # Adjust compile command to suit cl func_cl_wrapper () { # Assume a capable shell lib_path= shared=: linker_opts= for arg do if test -n "$eat"; then eat= else case $1 in -o) # configure might choose to run compile as 'compile cc -o foo foo.c'. eat=1 case $2 in *.o | *.[oO][bB][jJ]) func_file_conv "$2" set x "$@" -Fo"$file" shift ;; *) func_file_conv "$2" set x "$@" -Fe"$file" shift ;; esac ;; -I) eat=1 func_file_conv "$2" mingw set x "$@" -I"$file" shift ;; -I*) func_file_conv "${1#-I}" mingw set x "$@" -I"$file" shift ;; -l) eat=1 func_cl_dashl "$2" set x "$@" "$lib" shift ;; -l*) func_cl_dashl "${1#-l}" set x "$@" "$lib" shift ;; -L) eat=1 func_cl_dashL "$2" ;; -L*) func_cl_dashL "${1#-L}" ;; -static) shared=false ;; -Wl,*) arg=${1#-Wl,} save_ifs="$IFS"; IFS=',' for flag in $arg; do IFS="$save_ifs" linker_opts="$linker_opts $flag" done IFS="$save_ifs" ;; -Xlinker) eat=1 linker_opts="$linker_opts $2" ;; -*) set x "$@" "$1" shift ;; *.cc | *.CC | *.cxx | *.CXX | *.[cC]++) func_file_conv "$1" set x "$@" -Tp"$file" shift ;; *.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO]) func_file_conv "$1" mingw set x "$@" "$file" shift ;; *) set x "$@" "$1" shift ;; esac fi shift done if test -n "$linker_opts"; then linker_opts="-link$linker_opts" fi exec "$@" $linker_opts exit 1 } eat= case $1 in '') echo "$0: No command. Try '$0 --help' for more information." 1>&2 exit 1; ;; -h | --h*) cat <<\EOF Usage: compile [--help] [--version] PROGRAM [ARGS] Wrapper for compilers which do not understand '-c -o'. Remove '-o dest.o' from ARGS, run PROGRAM with the remaining arguments, and rename the output as expected. If you are trying to build a whole package this is not the right script to run: please start by reading the file 'INSTALL'. Report bugs to . EOF exit $? ;; -v | --v*) echo "compile $scriptversion" exit $? ;; cl | *[/\\]cl | cl.exe | *[/\\]cl.exe | \ icl | *[/\\]icl | icl.exe | *[/\\]icl.exe ) func_cl_wrapper "$@" # Doesn't return... ;; esac ofile= cfile= for arg do if test -n "$eat"; then eat= else case $1 in -o) # configure might choose to run compile as 'compile cc -o foo foo.c'. # So we strip '-o arg' only if arg is an object. eat=1 case $2 in *.o | *.obj) ofile=$2 ;; *) set x "$@" -o "$2" shift ;; esac ;; *.c) cfile=$1 set x "$@" "$1" shift ;; *) set x "$@" "$1" shift ;; esac fi shift done if test -z "$ofile" || test -z "$cfile"; then # If no '-o' option was seen then we might have been invoked from a # pattern rule where we don't need one. That is ok -- this is a # normal compilation that the losing compiler can handle. If no # '.c' file was seen then we are probably linking. That is also # ok. exec "$@" fi # Name of file we expect compiler to create. cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'` # Create the lock directory. # Note: use '[/\\:.-]' here to ensure that we don't use the same name # that we are using for the .o file. Also, base the name on the expected # object file name, since that is what matters with a parallel build. lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d while true; do if mkdir "$lockdir" >/dev/null 2>&1; then break fi sleep 1 done # FIXME: race condition here if user kills between mkdir and trap. trap "rmdir '$lockdir'; exit 1" 1 2 15 # Run the compile. "$@" ret=$? if test -f "$cofile"; then test "$cofile" = "$ofile" || mv "$cofile" "$ofile" elif test -f "${cofile}bj"; then test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile" fi rmdir "$lockdir" exit $ret # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: putty-0.76/ar-lib0000755000175000017500000001330314072266321010653 00000000000000#! /bin/sh # Wrapper for Microsoft lib.exe me=ar-lib scriptversion=2012-03-01.08; # UTC # Copyright (C) 2010-2018 Free Software Foundation, Inc. # Written by Peter Rosin . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # This file is maintained in Automake, please report # bugs to or send patches to # . # func_error message func_error () { echo "$me: $1" 1>&2 exit 1 } file_conv= # func_file_conv build_file # Convert a $build file to $host form and store it in $file # Currently only supports Windows hosts. func_file_conv () { file=$1 case $file in / | /[!/]*) # absolute file, and not a UNC file if test -z "$file_conv"; then # lazily determine how to convert abs files case `uname -s` in MINGW*) file_conv=mingw ;; CYGWIN*) file_conv=cygwin ;; *) file_conv=wine ;; esac fi case $file_conv in mingw) file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` ;; cygwin) file=`cygpath -m "$file" || echo "$file"` ;; wine) file=`winepath -w "$file" || echo "$file"` ;; esac ;; esac } # func_at_file at_file operation archive # Iterate over all members in AT_FILE performing OPERATION on ARCHIVE # for each of them. # When interpreting the content of the @FILE, do NOT use func_file_conv, # since the user would need to supply preconverted file names to # binutils ar, at least for MinGW. func_at_file () { operation=$2 archive=$3 at_file_contents=`cat "$1"` eval set x "$at_file_contents" shift for member do $AR -NOLOGO $operation:"$member" "$archive" || exit $? done } case $1 in '') func_error "no command. Try '$0 --help' for more information." ;; -h | --h*) cat < header file. */ #undef HAVE_ASM_HWCAP_H /* Define if clock_gettime() is available */ #undef HAVE_CLOCK_GETTIME /* Define to 1 if you have the declaration of `CLOCK_MONOTONIC', and to 0 if you don't. */ #undef HAVE_DECL_CLOCK_MONOTONIC /* Define to 1 if you have the `dirfd' function. */ #undef HAVE_DIRFD /* Define to 1 if you have the `elf_aux_info' function. */ #undef HAVE_ELF_AUX_INFO /* Define to 1 if you have the `endpwent' function. */ #undef HAVE_ENDPWENT /* Define to 1 if you have the `fstatat' function. */ #undef HAVE_FSTATAT /* Define to 1 if you have the `futimes' function. */ #undef HAVE_FUTIMES /* Define to 1 if you have the `getaddrinfo' function. */ #undef HAVE_GETADDRINFO /* Define to 1 if you have the `getauxval' function. */ #undef HAVE_GETAUXVAL /* Define to 1 if you have the header file. */ #undef HAVE_GLOB_H /* Define to 1 if you have the header file. */ #undef HAVE_GSSAPI_GSSAPI_H /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H /* Define if libX11.a is available */ #undef HAVE_LIBX11 /* Define to 1 if you have the header file. */ #undef HAVE_MEMORY_H /* Define to 1 if you have the `pango_font_family_is_monospace' function. */ #undef HAVE_PANGO_FONT_FAMILY_IS_MONOSPACE /* Define to 1 if you have the `pango_font_map_list_families' function. */ #undef HAVE_PANGO_FONT_MAP_LIST_FAMILIES /* Define to 1 if you have the `posix_openpt' function. */ #undef HAVE_POSIX_OPENPT /* Define to 1 if you have the `ptsname' function. */ #undef HAVE_PTSNAME /* Define to 1 if you have the `setpwent' function. */ #undef HAVE_SETPWENT /* Define to 1 if you have the `setresuid' function. */ #undef HAVE_SETRESUID /* Define if SO_PEERCRED works in the Linux fashion. */ #undef HAVE_SO_PEERCRED /* Define to 1 if you have the header file. */ #undef HAVE_STDINT_H /* Define to 1 if you have the header file. */ #undef HAVE_STDLIB_H /* Define to 1 if you have the header file. */ #undef HAVE_STRINGS_H /* Define to 1 if you have the header file. */ #undef HAVE_STRING_H /* Define to 1 if you have the `strsignal' function. */ #undef HAVE_STRSIGNAL /* Define to 1 if you have the `sysctlbyname' function. */ #undef HAVE_SYSCTLBYNAME /* Define to 1 if you have the header file. */ #undef HAVE_SYS_AUXV_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_STAT_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_SYSCTL_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_TYPES_H /* Define to 1 if you have the header file. */ #undef HAVE_UNISTD_H /* Define to 1 if you have the `updwtmpx' function. */ #undef HAVE_UPDWTMPX /* Define to 1 if you have the header file. */ #undef HAVE_UTMPX_H /* Define if we could not find a gssapi library */ #undef NO_GSSAPI_LIB /* Define if we could not find libdl. */ #undef NO_LIBDL /* Define if building with GTK for MacOS. */ #undef OSX_GTK /* Name of package */ #undef PACKAGE /* Define to the address where bug reports for this package should be sent. */ #undef PACKAGE_BUGREPORT /* Define to the full name of this package. */ #undef PACKAGE_NAME /* Define to the full name and version of this package. */ #undef PACKAGE_STRING /* Define to the one symbol short name of this package. */ #undef PACKAGE_TARNAME /* Define to the home page for this package. */ #undef PACKAGE_URL /* Define to the version of this package. */ #undef PACKAGE_VERSION /* Define to 1 if you have the ANSI C header files. */ #undef STDC_HEADERS /* Version number of package */ #undef VERSION /* Define if building with GSSAPI support. */ #undef WITH_GSSAPI /* Convert autoconf definitions to ones that PuTTY wants. */ #ifndef HAVE_GETADDRINFO # define NO_IPV6 #endif #ifndef HAVE_SETRESUID # define HAVE_NO_SETRESUID #endif #ifndef HAVE_STRSIGNAL # define HAVE_NO_STRSIGNAL #endif #if !defined(HAVE_UTMPX_H) || !defined(HAVE_UPDWTMPX) # define OMIT_UTMP #endif #ifndef HAVE_PTSNAME # define BSD_PTYS #endif #ifndef HAVE_SYS_SELECT_H # define HAVE_NO_SYS_SELECT_H #endif #ifndef HAVE_PANGO_FONT_FAMILY_IS_MONOSPACE # define PANGO_PRE_1POINT4 #endif #ifndef HAVE_PANGO_FONT_MAP_LIST_FAMILIES # define PANGO_PRE_1POINT6 #endif #if !defined(WITH_GSSAPI) # define NO_GSSAPI #endif #if !defined(NO_GSSAPI) && defined(NO_LIBDL) # if !defined(HAVE_GSSAPI_GSSAPI_H) || defined(NO_GSSAPI_LIB) # define NO_GSSAPI # endif #endif putty-0.76/configure0000755000175000017500000066136014072266320011501 00000000000000#! /bin/sh # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.69 for putty 0.76. # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. # # # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # Use a proper internal environment variable to ensure we don't fall # into an infinite loop, continuously re-executing ourselves. if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then _as_can_reexec=no; export _as_can_reexec; # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 as_fn_exit 255 fi # We don't want this to propagate to other subprocesses. { _as_can_reexec=; unset _as_can_reexec;} if test "x$CONFIG_SHELL" = x; then as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else case \`(set -o) 2>/dev/null\` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi " as_required="as_fn_return () { (exit \$1); } as_fn_success () { as_fn_return 0; } as_fn_failure () { as_fn_return 1; } as_fn_ret_success () { return 0; } as_fn_ret_failure () { return 1; } exitcode=0 as_fn_success || { exitcode=1; echo as_fn_success failed.; } as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : else exitcode=1; echo positional parameters were not saved. fi test x\$exitcode = x0 || exit 1 test -x / || exit 1" as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 test \$(( 1 + 1 )) = 2 || exit 1" if (eval "$as_required") 2>/dev/null; then : as_have_required=yes else as_have_required=no fi if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. as_found=: case $as_dir in #( /*) for as_base in sh bash ksh sh5; do # Try only shells that exist, to save several forks. as_shell=$as_dir/$as_base if { test -f "$as_shell" || test -f "$as_shell.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : CONFIG_SHELL=$as_shell as_have_required=yes if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : break 2 fi fi done;; esac as_found=false done $as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : CONFIG_SHELL=$SHELL as_have_required=yes fi; } IFS=$as_save_IFS if test "x$CONFIG_SHELL" != x; then : export CONFIG_SHELL # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} # Admittedly, this is quite paranoid, since all the known shells bail # out after a failed `exec'. $as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 exit 255 fi if test x$as_have_required = xno; then : $as_echo "$0: This script requires a shell more modern than all" $as_echo "$0: the shells that I found on your system." if test x${ZSH_VERSION+set} = xset ; then $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" $as_echo "$0: be upgraded to zsh 4.3.4 or later." else $as_echo "$0: Please tell bug-autoconf@gnu.org about your system, $0: including any error possibly output before this $0: message. Then install a modern shell, or manually run $0: the script under such a shell if you do have one." fi exit 1 fi fi fi SHELL=${CONFIG_SHELL-/bin/sh} export SHELL # Unset more variables known to interfere with behavior of common tools. CLICOLOR_FORCE= GREP_OPTIONS= unset CLICOLOR_FORCE GREP_OPTIONS ## --------------------- ## ## M4sh Shell Functions. ## ## --------------------- ## # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits as_lineno_1=$LINENO as_lineno_1a=$LINENO as_lineno_2=$LINENO as_lineno_2a=$LINENO eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) sed -n ' p /[$]LINENO/= ' <$as_myself | sed ' s/[$]LINENO.*/&-/ t lineno b :lineno N :loop s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ t loop s/-\n.*// ' >$as_me.lineno && chmod +x "$as_me.lineno" || { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } # If we had to re-execute with $CONFIG_SHELL, we're ensured to have # already done that, so ensure we don't try to do so again and fall # in an infinite loop. This has already happened in practice. _as_can_reexec=no; export _as_can_reexec # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). . "./$as_me.lineno" # Exit status is that of the last command. exit } ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" test -n "$DJDIR" || exec 7<&0 &1 # Name of the host. # hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` # # Initializations. # ac_default_prefix=/usr/local ac_clean_files= ac_config_libobj_dir=. LIBOBJS= cross_compiling=no subdirs= MFLAGS= MAKEFLAGS= # Identity of this package. PACKAGE_NAME='putty' PACKAGE_TARNAME='putty' PACKAGE_VERSION='0.76' PACKAGE_STRING='putty 0.76' PACKAGE_BUGREPORT='' PACKAGE_URL='' # Factoring default headers for most tests. ac_includes_default="\ #include #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef STDC_HEADERS # include # include #else # ifdef HAVE_STDLIB_H # include # endif #endif #ifdef HAVE_STRING_H # if !defined STDC_HEADERS && defined HAVE_MEMORY_H # include # endif # include #endif #ifdef HAVE_STRINGS_H # include #endif #ifdef HAVE_INTTYPES_H # include #endif #ifdef HAVE_STDINT_H # include #endif #ifdef HAVE_UNISTD_H # include #endif" ac_subst_vars='am__EXEEXT_FALSE am__EXEEXT_TRUE LTLIBOBJS LIBOBJS WARNINGOPTS EGREP GREP CPP HAVE_GTK_FALSE HAVE_GTK_TRUE GTK1_CONFIG GTK_LIBS GTK_CFLAGS PKG_CONFIG_LIBDIR PKG_CONFIG_PATH PKG_CONFIG HAVE_QUARTZ_FALSE HAVE_QUARTZ_TRUE AUTO_GIT_COMMIT_FALSE AUTO_GIT_COMMIT_TRUE SETID_MODE SETID_CMD HAVE_SETID_CMD_FALSE HAVE_SETID_CMD_TRUE am__fastdepCC_FALSE am__fastdepCC_TRUE CCDEPMODE am__nodep AMDEPBACKSLASH AMDEP_FALSE AMDEP_TRUE am__include DEPDIR OBJEXT EXEEXT ac_ct_CC CPPFLAGS LDFLAGS CFLAGS CC ac_ct_AR AR RANLIB AM_BACKSLASH AM_DEFAULT_VERBOSITY AM_DEFAULT_V AM_V am__untar am__tar AMTAR am__leading_dot SET_MAKE AWK mkdir_p MKDIR_P INSTALL_STRIP_PROGRAM STRIP install_sh MAKEINFO AUTOHEADER AUTOMAKE AUTOCONF ACLOCAL VERSION PACKAGE CYGPATH_W am__isrc INSTALL_DATA INSTALL_SCRIPT INSTALL_PROGRAM target_alias host_alias build_alias LIBS ECHO_T ECHO_N ECHO_C DEFS mandir localedir libdir psdir pdfdir dvidir htmldir infodir docdir oldincludedir includedir runstatedir localstatedir sharedstatedir sysconfdir datadir datarootdir libexecdir sbindir bindir program_transform_name prefix exec_prefix PACKAGE_URL PACKAGE_BUGREPORT PACKAGE_STRING PACKAGE_VERSION PACKAGE_TARNAME PACKAGE_NAME PATH_SEPARATOR SHELL am__quote' ac_subst_files='' ac_user_opts=' enable_option_checking enable_silent_rules enable_dependency_tracking enable_setuid enable_setgid enable_git_commit with_gssapi with_quartz with_gtk enable_gtktest ' ac_precious_vars='build_alias host_alias target_alias CC CFLAGS LDFLAGS LIBS CPPFLAGS PKG_CONFIG PKG_CONFIG_PATH PKG_CONFIG_LIBDIR CPP' # Initialize some variables set by options. ac_init_help= ac_init_version=false ac_unrecognized_opts= ac_unrecognized_sep= # The variables have the same names as the options, with # dashes changed to underlines. cache_file=/dev/null exec_prefix=NONE no_create= no_recursion= prefix=NONE program_prefix=NONE program_suffix=NONE program_transform_name=s,x,x, silent= site= srcdir= verbose= x_includes=NONE x_libraries=NONE # Installation directory options. # These are left unexpanded so users can "make install exec_prefix=/foo" # and all the variables that are supposed to be based on exec_prefix # by default will actually change. # Use braces instead of parens because sh, perl, etc. also accept them. # (The list follows the same order as the GNU Coding Standards.) bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datarootdir='${prefix}/share' datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' runstatedir='${localstatedir}/run' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' infodir='${datarootdir}/info' htmldir='${docdir}' dvidir='${docdir}' pdfdir='${docdir}' psdir='${docdir}' libdir='${exec_prefix}/lib' localedir='${datarootdir}/locale' mandir='${datarootdir}/man' ac_prev= ac_dashdash= for ac_option do # If the previous option needs an argument, assign it. if test -n "$ac_prev"; then eval $ac_prev=\$ac_option ac_prev= continue fi case $ac_option in *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; *=) ac_optarg= ;; *) ac_optarg=yes ;; esac # Accept the important Cygnus configure options, so we can diagnose typos. case $ac_dashdash$ac_option in --) ac_dashdash=yes ;; -bindir | --bindir | --bindi | --bind | --bin | --bi) ac_prev=bindir ;; -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) bindir=$ac_optarg ;; -build | --build | --buil | --bui | --bu) ac_prev=build_alias ;; -build=* | --build=* | --buil=* | --bui=* | --bu=*) build_alias=$ac_optarg ;; -cache-file | --cache-file | --cache-fil | --cache-fi \ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ac_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) cache_file=$ac_optarg ;; --config-cache | -C) cache_file=config.cache ;; -datadir | --datadir | --datadi | --datad) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=*) datadir=$ac_optarg ;; -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ | --dataroo | --dataro | --datar) ac_prev=datarootdir ;; -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) datarootdir=$ac_optarg ;; -disable-* | --disable-*) ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=no ;; -docdir | --docdir | --docdi | --doc | --do) ac_prev=docdir ;; -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) docdir=$ac_optarg ;; -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) ac_prev=dvidir ;; -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) dvidir=$ac_optarg ;; -enable-* | --enable-*) ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=\$ac_optarg ;; -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ | --exec | --exe | --ex) ac_prev=exec_prefix ;; -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ | --exec=* | --exe=* | --ex=*) exec_prefix=$ac_optarg ;; -gas | --gas | --ga | --g) # Obsolete; use --with-gas. with_gas=yes ;; -help | --help | --hel | --he | -h) ac_init_help=long ;; -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) ac_init_help=recursive ;; -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) ac_init_help=short ;; -host | --host | --hos | --ho) ac_prev=host_alias ;; -host=* | --host=* | --hos=* | --ho=*) host_alias=$ac_optarg ;; -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) ac_prev=htmldir ;; -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ | --ht=*) htmldir=$ac_optarg ;; -includedir | --includedir | --includedi | --included | --include \ | --includ | --inclu | --incl | --inc) ac_prev=includedir ;; -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ | --includ=* | --inclu=* | --incl=* | --inc=*) includedir=$ac_optarg ;; -infodir | --infodir | --infodi | --infod | --info | --inf) ac_prev=infodir ;; -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) infodir=$ac_optarg ;; -libdir | --libdir | --libdi | --libd) ac_prev=libdir ;; -libdir=* | --libdir=* | --libdi=* | --libd=*) libdir=$ac_optarg ;; -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ | --libexe | --libex | --libe) ac_prev=libexecdir ;; -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ | --libexe=* | --libex=* | --libe=*) libexecdir=$ac_optarg ;; -localedir | --localedir | --localedi | --localed | --locale) ac_prev=localedir ;; -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) localedir=$ac_optarg ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst | --locals) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) localstatedir=$ac_optarg ;; -mandir | --mandir | --mandi | --mand | --man | --ma | --m) ac_prev=mandir ;; -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) mandir=$ac_optarg ;; -nfp | --nfp | --nf) # Obsolete; use --without-fp. with_fp=no ;; -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c | -n) no_create=yes ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) no_recursion=yes ;; -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ | --oldin | --oldi | --old | --ol | --o) ac_prev=oldincludedir ;; -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) oldincludedir=$ac_optarg ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) prefix=$ac_optarg ;; -program-prefix | --program-prefix | --program-prefi | --program-pref \ | --program-pre | --program-pr | --program-p) ac_prev=program_prefix ;; -program-prefix=* | --program-prefix=* | --program-prefi=* \ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) program_prefix=$ac_optarg ;; -program-suffix | --program-suffix | --program-suffi | --program-suff \ | --program-suf | --program-su | --program-s) ac_prev=program_suffix ;; -program-suffix=* | --program-suffix=* | --program-suffi=* \ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) program_suffix=$ac_optarg ;; -program-transform-name | --program-transform-name \ | --program-transform-nam | --program-transform-na \ | --program-transform-n | --program-transform- \ | --program-transform | --program-transfor \ | --program-transfo | --program-transf \ | --program-trans | --program-tran \ | --progr-tra | --program-tr | --program-t) ac_prev=program_transform_name ;; -program-transform-name=* | --program-transform-name=* \ | --program-transform-nam=* | --program-transform-na=* \ | --program-transform-n=* | --program-transform-=* \ | --program-transform=* | --program-transfor=* \ | --program-transfo=* | --program-transf=* \ | --program-trans=* | --program-tran=* \ | --progr-tra=* | --program-tr=* | --program-t=*) program_transform_name=$ac_optarg ;; -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) ac_prev=pdfdir ;; -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) pdfdir=$ac_optarg ;; -psdir | --psdir | --psdi | --psd | --ps) ac_prev=psdir ;; -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) psdir=$ac_optarg ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; -runstatedir | --runstatedir | --runstatedi | --runstated \ | --runstate | --runstat | --runsta | --runst | --runs \ | --run | --ru | --r) ac_prev=runstatedir ;; -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ | --run=* | --ru=* | --r=*) runstatedir=$ac_optarg ;; -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ | --sbi=* | --sb=*) sbindir=$ac_optarg ;; -sharedstatedir | --sharedstatedir | --sharedstatedi \ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ | --sharedst | --shareds | --shared | --share | --shar \ | --sha | --sh) ac_prev=sharedstatedir ;; -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ | --sha=* | --sh=*) sharedstatedir=$ac_optarg ;; -site | --site | --sit) ac_prev=site ;; -site=* | --site=* | --sit=*) site=$ac_optarg ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) srcdir=$ac_optarg ;; -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ | --syscon | --sysco | --sysc | --sys | --sy) ac_prev=sysconfdir ;; -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) sysconfdir=$ac_optarg ;; -target | --target | --targe | --targ | --tar | --ta | --t) ac_prev=target_alias ;; -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) target_alias=$ac_optarg ;; -v | -verbose | --verbose | --verbos | --verbo | --verb) verbose=yes ;; -version | --version | --versio | --versi | --vers | -V) ac_init_version=: ;; -with-* | --with-*) ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=\$ac_optarg ;; -without-* | --without-*) ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=no ;; --x) # Obsolete; use --with-x. with_x=yes ;; -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ | --x-incl | --x-inc | --x-in | --x-i) ac_prev=x_includes ;; -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) x_includes=$ac_optarg ;; -x-libraries | --x-libraries | --x-librarie | --x-librari \ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) ac_prev=x_libraries ;; -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; -*) as_fn_error $? "unrecognized option: \`$ac_option' Try \`$0 --help' for more information" ;; *=*) ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` # Reject names that are not valid shell variable names. case $ac_envvar in #( '' | [0-9]* | *[!_$as_cr_alnum]* ) as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; esac eval $ac_envvar=\$ac_optarg export $ac_envvar ;; *) # FIXME: should be removed in autoconf 3.0. $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" ;; esac done if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` as_fn_error $? "missing argument to $ac_option" fi if test -n "$ac_unrecognized_opts"; then case $enable_option_checking in no) ;; fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; esac fi # Check all directory arguments for consistency. for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ libdir localedir mandir runstatedir do eval ac_val=\$$ac_var # Remove trailing slashes. case $ac_val in */ ) ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` eval $ac_var=\$ac_val;; esac # Be sure to have absolute directory names. case $ac_val in [\\/$]* | ?:[\\/]* ) continue;; NONE | '' ) case $ac_var in *prefix ) continue;; esac;; esac as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" done # There might be people who depend on the old broken behavior: `$host' # used to hold the argument of --host etc. # FIXME: To remove some day. build=$build_alias host=$host_alias target=$target_alias # FIXME: To remove some day. if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi fi ac_tool_prefix= test -n "$host_alias" && ac_tool_prefix=$host_alias- test "$silent" = yes && exec 6>/dev/null ac_pwd=`pwd` && test -n "$ac_pwd" && ac_ls_di=`ls -di .` && ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || as_fn_error $? "working directory cannot be determined" test "X$ac_ls_di" = "X$ac_pwd_ls_di" || as_fn_error $? "pwd does not report name of working directory" # Find the source files, if location was not specified. if test -z "$srcdir"; then ac_srcdir_defaulted=yes # Try the directory containing this script, then the parent directory. ac_confdir=`$as_dirname -- "$as_myself" || $as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_myself" : 'X\(//\)[^/]' \| \ X"$as_myself" : 'X\(//\)$' \| \ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_myself" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` srcdir=$ac_confdir if test ! -r "$srcdir/$ac_unique_file"; then srcdir=.. fi else ac_srcdir_defaulted=no fi if test ! -r "$srcdir/$ac_unique_file"; then test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" fi ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" ac_abs_confdir=`( cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" pwd)` # When building in place, set srcdir=. if test "$ac_abs_confdir" = "$ac_pwd"; then srcdir=. fi # Remove unnecessary trailing slashes from srcdir. # Double slashes in file names in object file debugging info # mess up M-x gdb in Emacs. case $srcdir in */) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; esac for ac_var in $ac_precious_vars; do eval ac_env_${ac_var}_set=\${${ac_var}+set} eval ac_env_${ac_var}_value=\$${ac_var} eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} eval ac_cv_env_${ac_var}_value=\$${ac_var} done # # Report the --help message. # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF \`configure' configures putty 0.76 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. Defaults for the options are specified in brackets. Configuration: -h, --help display this help and exit --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit -q, --quiet, --silent do not print \`checking ...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for \`--cache-file=config.cache' -n, --no-create do not create output files --srcdir=DIR find the sources in DIR [configure dir or \`..'] Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX [$ac_default_prefix] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [PREFIX] By default, \`make install' will install all the files in \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify an installation prefix other than \`$ac_default_prefix' using \`--prefix', for instance \`--prefix=\$HOME'. For better control, use the options below. Fine tuning of the installation directories: --bindir=DIR user executables [EPREFIX/bin] --sbindir=DIR system admin executables [EPREFIX/sbin] --libexecdir=DIR program executables [EPREFIX/libexec] --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] --datadir=DIR read-only architecture-independent data [DATAROOTDIR] --infodir=DIR info documentation [DATAROOTDIR/info] --localedir=DIR locale-dependent data [DATAROOTDIR/locale] --mandir=DIR man documentation [DATAROOTDIR/man] --docdir=DIR documentation root [DATAROOTDIR/doc/putty] --htmldir=DIR html documentation [DOCDIR] --dvidir=DIR dvi documentation [DOCDIR] --pdfdir=DIR pdf documentation [DOCDIR] --psdir=DIR ps documentation [DOCDIR] _ACEOF cat <<\_ACEOF Program names: --program-prefix=PREFIX prepend PREFIX to installed program names --program-suffix=SUFFIX append SUFFIX to installed program names --program-transform-name=PROGRAM run sed PROGRAM on installed program names _ACEOF fi if test -n "$ac_init_help"; then case $ac_init_help in short | recursive ) echo "Configuration of putty 0.76:";; esac cat <<\_ACEOF Optional Features: --disable-option-checking ignore unrecognized --enable/--with options --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --enable-silent-rules less verbose build output (undo: "make V=1") --disable-silent-rules verbose build output (undo: "make V=0") --enable-dependency-tracking do not reject slow dependency extractors --disable-dependency-tracking speeds up one-time build --enable-setuid=USER make pterm setuid to a given user --enable-setgid=GROUP make pterm setgid to a given group --disable-git-commit disable embedding current git HEAD in binaries --disable-gtktest do not try to compile and run a test GTK+ program Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --without-gssapi disable GSSAPI support --with-quartz build for the MacOS Quartz GTK back end --with-gtk=VER specify GTK version to use (`1', `2' or `3') --without-gtk do not use GTK (build command-line tools only) Some influential environment variables: CC C compiler command CFLAGS C compiler flags LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory LIBS libraries to pass to the linker, e.g. -l CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory PKG_CONFIG path to pkg-config utility PKG_CONFIG_PATH directories to add to pkg-config's search path PKG_CONFIG_LIBDIR path overriding pkg-config's built-in search path CPP C preprocessor Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. Report bugs to the package provider. _ACEOF ac_status=$? fi if test "$ac_init_help" = "recursive"; then # If there are subdirs, report their specific --help. for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue test -d "$ac_dir" || { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || continue ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix cd "$ac_dir" || { ac_status=$?; continue; } # Check for guested configure. if test -f "$ac_srcdir/configure.gnu"; then echo && $SHELL "$ac_srcdir/configure.gnu" --help=recursive elif test -f "$ac_srcdir/configure"; then echo && $SHELL "$ac_srcdir/configure" --help=recursive else $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi || ac_status=$? cd "$ac_pwd" || { ac_status=$?; break; } done fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF putty configure 0.76 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF exit fi ## ------------------------ ## ## Autoconf initialization. ## ## ------------------------ ## # ac_fn_c_try_compile LINENO # -------------------------- # Try to compile conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_compile # ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists and can be compiled using the include files in # INCLUDES, setting the cache variable VAR accordingly. ac_fn_c_check_header_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_compile # ac_fn_c_try_run LINENO # ---------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. Assumes # that executables *can* be run. ac_fn_c_try_run () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then : ac_retval=0 else $as_echo "$as_me: program exited with status $ac_status" >&5 $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=$ac_status fi rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_run # ac_fn_c_try_link LINENO # ----------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_link () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest$ac_exeext if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || test -x conftest$ac_exeext }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would # interfere with the next link command; also delete a directory that is # left behind by Apple's compiler. We do this before executing the actions. rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_link # ac_fn_c_check_func LINENO FUNC VAR # ---------------------------------- # Tests whether FUNC exists, setting the cache variable VAR accordingly ac_fn_c_check_func () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Define $2 to an innocuous variant, in case declares $2. For example, HP-UX 11i declares gettimeofday. */ #define $2 innocuous_$2 /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $2 (); below. Prefer to if __STDC__ is defined, since exists even on freestanding compilers. */ #ifdef __STDC__ # include #else # include #endif #undef $2 /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char $2 (); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined __stub_$2 || defined __stub___$2 choke me #endif int main () { return $2 (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_func # ac_fn_c_try_cpp LINENO # ---------------------- # Try to preprocess conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_cpp () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } > conftest.i && { test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || test ! -s conftest.err }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_cpp # ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists, giving a warning if it cannot be compiled using # the include files in INCLUDES and setting the cache variable VAR # accordingly. ac_fn_c_check_header_mongrel () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if eval \${$3+:} false; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } else # Is the header compilable? { $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 $as_echo_n "checking $2 usability... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_header_compiler=yes else ac_header_compiler=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 $as_echo "$ac_header_compiler" >&6; } # Is the header present? { $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 $as_echo_n "checking $2 presence... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include <$2> _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : ac_header_preproc=yes else ac_header_preproc=no fi rm -f conftest.err conftest.i conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 $as_echo "$ac_header_preproc" >&6; } # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #(( yes:no: ) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 $as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} ;; no:yes:* ) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 $as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 $as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 $as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 $as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else eval "$3=\$ac_header_compiler" fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_mongrel # ac_fn_c_check_decl LINENO SYMBOL VAR INCLUDES # --------------------------------------------- # Tests whether SYMBOL is declared in INCLUDES, setting cache variable VAR # accordingly. ac_fn_c_check_decl () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack as_decl_name=`echo $2|sed 's/ *(.*//'` as_decl_use=`echo $2|sed -e 's/(/((/' -e 's/)/) 0&/' -e 's/,/) 0& (/g'` { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $as_decl_name is declared" >&5 $as_echo_n "checking whether $as_decl_name is declared... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main () { #ifndef $as_decl_name #ifdef __cplusplus (void) $as_decl_use; #else (void) $as_decl_name; #endif #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_decl cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by putty $as_me 0.76, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ _ACEOF exec 5>>config.log { cat <<_ASUNAME ## --------- ## ## Platform. ## ## --------- ## hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` /bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` /bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` /usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` /bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` /bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` _ASUNAME as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. $as_echo "PATH: $as_dir" done IFS=$as_save_IFS } >&5 cat >&5 <<_ACEOF ## ----------- ## ## Core tests. ## ## ----------- ## _ACEOF # Keep a trace of the command line. # Strip out --no-create and --no-recursion so they do not pile up. # Strip out --silent because we don't want to record it for future runs. # Also quote any args containing shell meta-characters. # Make two passes to allow for proper duplicate-argument suppression. ac_configure_args= ac_configure_args0= ac_configure_args1= ac_must_keep_next=false for ac_pass in 1 2 do for ac_arg do case $ac_arg in -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) continue ;; *\'*) ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac case $ac_pass in 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; 2) as_fn_append ac_configure_args1 " '$ac_arg'" if test $ac_must_keep_next = true; then ac_must_keep_next=false # Got value, back to normal. else case $ac_arg in *=* | --config-cache | -C | -disable-* | --disable-* \ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ | -with-* | --with-* | -without-* | --without-* | --x) case "$ac_configure_args0 " in "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; esac ;; -* ) ac_must_keep_next=true ;; esac fi as_fn_append ac_configure_args " '$ac_arg'" ;; esac done done { ac_configure_args0=; unset ac_configure_args0;} { ac_configure_args1=; unset ac_configure_args1;} # When interrupted or exit'd, cleanup temporary files, and complete # config.log. We remove comments because anyway the quotes in there # would cause problems or look ugly. # WARNING: Use '\'' to represent an apostrophe within the trap. # WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. trap 'exit_status=$? # Save into config.log some information that might help in debugging. { echo $as_echo "## ---------------- ## ## Cache variables. ## ## ---------------- ##" echo # The following way of writing the cache mishandles newlines in values, ( for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( *${as_nl}ac_space=\ *) sed -n \ "s/'\''/'\''\\\\'\'''\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" ;; #( *) sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) echo $as_echo "## ----------------- ## ## Output variables. ## ## ----------------- ##" echo for ac_var in $ac_subst_vars do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo if test -n "$ac_subst_files"; then $as_echo "## ------------------- ## ## File substitutions. ## ## ------------------- ##" echo for ac_var in $ac_subst_files do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo fi if test -s confdefs.h; then $as_echo "## ----------- ## ## confdefs.h. ## ## ----------- ##" echo cat confdefs.h echo fi test "$ac_signal" != 0 && $as_echo "$as_me: caught signal $ac_signal" $as_echo "$as_me: exit $exit_status" } >&5 rm -f core *.core core.conftest.* && rm -f -r conftest* confdefs* conf$$* $ac_clean_files && exit $exit_status ' 0 for ac_signal in 1 2 13 15; do trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal done ac_signal=0 # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -f -r conftest* confdefs.h $as_echo "/* confdefs.h */" > confdefs.h # Predefined preprocessor variables. cat >>confdefs.h <<_ACEOF #define PACKAGE_NAME "$PACKAGE_NAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_TARNAME "$PACKAGE_TARNAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_VERSION "$PACKAGE_VERSION" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_STRING "$PACKAGE_STRING" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_URL "$PACKAGE_URL" _ACEOF # Let the site file select an alternate cache file if it wants to. # Prefer an explicitly selected file to automatically selected ones. ac_site_file1=NONE ac_site_file2=NONE if test -n "$CONFIG_SITE"; then # We do not want a PATH search for config.site. case $CONFIG_SITE in #(( -*) ac_site_file1=./$CONFIG_SITE;; */*) ac_site_file1=$CONFIG_SITE;; *) ac_site_file1=./$CONFIG_SITE;; esac elif test "x$prefix" != xNONE; then ac_site_file1=$prefix/share/config.site ac_site_file2=$prefix/etc/config.site else ac_site_file1=$ac_default_prefix/share/config.site ac_site_file2=$ac_default_prefix/etc/config.site fi for ac_site_file in "$ac_site_file1" "$ac_site_file2" do test "x$ac_site_file" = xNONE && continue if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 $as_echo "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" \ || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "failed to load site script $ac_site_file See \`config.log' for more details" "$LINENO" 5; } fi done if test -r "$cache_file"; then # Some versions of bash will fail to source /dev/null (special files # actually), so we avoid doing that. DJGPP emulates it as a regular file. if test /dev/null != "$cache_file" && test -f "$cache_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 $as_echo "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . "$cache_file";; *) . "./$cache_file";; esac fi else { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 $as_echo "$as_me: creating cache $cache_file" >&6;} >$cache_file fi # Check that the precious variables saved in the cache have kept the same # value. ac_cache_corrupted=false for ac_var in $ac_precious_vars; do eval ac_old_set=\$ac_cv_env_${ac_var}_set eval ac_new_set=\$ac_env_${ac_var}_set eval ac_old_val=\$ac_cv_env_${ac_var}_value eval ac_new_val=\$ac_env_${ac_var}_value case $ac_old_set,$ac_new_set in set,) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} ac_cache_corrupted=: ;; ,);; *) if test "x$ac_old_val" != "x$ac_new_val"; then # differences in whitespace do not lead to failure. ac_old_val_w=`echo x $ac_old_val` ac_new_val_w=`echo x $ac_new_val` if test "$ac_old_val_w" != "$ac_new_val_w"; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 $as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} ac_cache_corrupted=: else { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 $as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} eval $ac_var=\$ac_old_val fi { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 $as_echo "$as_me: former value: \`$ac_old_val'" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 $as_echo "$as_me: current value: \`$ac_new_val'" >&2;} fi;; esac # Pass precious variables to config.status. if test "$ac_new_set" = set; then case $ac_new_val in *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; *) ac_arg=$ac_var=$ac_new_val ;; esac case " $ac_configure_args " in *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. *) as_fn_append ac_configure_args " '$ac_arg'" ;; esac fi done if $ac_cache_corrupted; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 $as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 fi ## -------------------- ## ## Main body of script. ## ## -------------------- ## ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_config_files="$ac_config_files Makefile" ac_config_headers="$ac_config_headers uxconfig.h:uxconfig.in" am__api_version='1.16' ac_aux_dir= for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do if test -f "$ac_dir/install-sh"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install-sh -c" break elif test -f "$ac_dir/install.sh"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install.sh -c" break elif test -f "$ac_dir/shtool"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/shtool install -c" break fi done if test -z "$ac_aux_dir"; then as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5 fi # These three variables are undocumented and unsupported, # and are intended to be withdrawn in a future Autoconf release. # They can cause serious problems if a builder's source tree is in a directory # whose full name contains unusual characters. ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. # Find a good install program. We prefer a C program (faster), # so one script is as good as another. But avoid the broken or # incompatible versions: # SysV /etc/install, /usr/sbin/install # SunOS /usr/etc/install # IRIX /sbin/install # AIX /bin/install # AmigaOS /C/install, which installs bootblocks on floppy discs # AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag # AFS /usr/afsws/bin/install, which mishandles nonexistent args # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" # OS/2's system install, which has a completely different semantic # ./install, which can be erroneously created by make from ./install.sh. # Reject install programs that cannot install multiple files. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 $as_echo_n "checking for a BSD-compatible install... " >&6; } if test -z "$INSTALL"; then if ${ac_cv_path_install+:} false; then : $as_echo_n "(cached) " >&6 else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. # Account for people who put trailing slashes in PATH elements. case $as_dir/ in #(( ./ | .// | /[cC]/* | \ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ /usr/ucb/* ) ;; *) # OSF1 and SCO ODT 3.0 have their own names for install. # Don't use installbsd from OSF since it installs stuff as root # by default. for ac_prog in ginstall scoinst install; do for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then if test $ac_prog = install && grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # AIX install. It has an incompatible calling convention. : elif test $ac_prog = install && grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # program-specific install script used by HP pwplus--don't use. : else rm -rf conftest.one conftest.two conftest.dir echo one > conftest.one echo two > conftest.two mkdir conftest.dir if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && test -s conftest.one && test -s conftest.two && test -s conftest.dir/conftest.one && test -s conftest.dir/conftest.two then ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" break 3 fi fi fi done done ;; esac done IFS=$as_save_IFS rm -rf conftest.one conftest.two conftest.dir fi if test "${ac_cv_path_install+set}" = set; then INSTALL=$ac_cv_path_install else # As a last resort, use the slow shell script. Don't cache a # value for INSTALL within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the value is a relative name. INSTALL=$ac_install_sh fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 $as_echo "$INSTALL" >&6; } # Use test -z because SunOS4 sh mishandles braces in ${var-val}. # It thinks the first close brace ends the variable substitution. test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5 $as_echo_n "checking whether build environment is sane... " >&6; } # Reject unsafe characters in $srcdir or the absolute working directory # name. Accept space and tab only in the latter. am_lf=' ' case `pwd` in *[\\\"\#\$\&\'\`$am_lf]*) as_fn_error $? "unsafe absolute working directory name" "$LINENO" 5;; esac case $srcdir in *[\\\"\#\$\&\'\`$am_lf\ \ ]*) as_fn_error $? "unsafe srcdir value: '$srcdir'" "$LINENO" 5;; esac # Do 'set' in a subshell so we don't clobber the current shell's # arguments. Must try -L first in case configure is actually a # symlink; some systems play weird games with the mod time of symlinks # (eg FreeBSD returns the mod time of the symlink's containing # directory). if ( am_has_slept=no for am_try in 1 2; do echo "timestamp, slept: $am_has_slept" > conftest.file set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` if test "$*" = "X"; then # -L didn't work. set X `ls -t "$srcdir/configure" conftest.file` fi if test "$*" != "X $srcdir/configure conftest.file" \ && test "$*" != "X conftest.file $srcdir/configure"; then # If neither matched, then we have a broken ls. This can happen # if, for instance, CONFIG_SHELL is bash and it inherits a # broken ls alias from the environment. This has actually # happened. Such a system could not be considered "sane". as_fn_error $? "ls -t appears to fail. Make sure there is not a broken alias in your environment" "$LINENO" 5 fi if test "$2" = conftest.file || test $am_try -eq 2; then break fi # Just in case. sleep 1 am_has_slept=yes done test "$2" = conftest.file ) then # Ok. : else as_fn_error $? "newly created file is older than distributed files! Check your system clock" "$LINENO" 5 fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } # If we didn't sleep, we still need to ensure time stamps of config.status and # generated files are strictly newer. am_sleep_pid= if grep 'slept: no' conftest.file >/dev/null 2>&1; then ( sleep 1 ) & am_sleep_pid=$! fi rm -f conftest.file test "$program_prefix" != NONE && program_transform_name="s&^&$program_prefix&;$program_transform_name" # Use a double $ so make ignores it. test "$program_suffix" != NONE && program_transform_name="s&\$&$program_suffix&;$program_transform_name" # Double any \ or $. # By default was `s,x,x', remove it if useless. ac_script='s/[\\$]/&&/g;s/;s,x,x,$//' program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"` # Expand $ac_aux_dir to an absolute path. am_aux_dir=`cd "$ac_aux_dir" && pwd` if test x"${MISSING+set}" != xset; then case $am_aux_dir in *\ * | *\ *) MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; *) MISSING="\${SHELL} $am_aux_dir/missing" ;; esac fi # Use eval to expand $SHELL if eval "$MISSING --is-lightweight"; then am_missing_run="$MISSING " else am_missing_run= { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: 'missing' script is too old or missing" >&5 $as_echo "$as_me: WARNING: 'missing' script is too old or missing" >&2;} fi if test x"${install_sh+set}" != xset; then case $am_aux_dir in *\ * | *\ *) install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; *) install_sh="\${SHELL} $am_aux_dir/install-sh" esac fi # Installed binaries are usually stripped using 'strip' when the user # run "make install-strip". However 'strip' might not be the right # tool to use in cross-compilation environments, therefore Automake # will honor the 'STRIP' environment variable to overrule this program. if test "$cross_compiling" != no; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. set dummy ${ac_tool_prefix}strip; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_STRIP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$STRIP"; then ac_cv_prog_STRIP="$STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_STRIP="${ac_tool_prefix}strip" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi STRIP=$ac_cv_prog_STRIP if test -n "$STRIP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 $as_echo "$STRIP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_STRIP"; then ac_ct_STRIP=$STRIP # Extract the first word of "strip", so it can be a program name with args. set dummy strip; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_STRIP+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_STRIP"; then ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_STRIP="strip" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP if test -n "$ac_ct_STRIP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 $as_echo "$ac_ct_STRIP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_STRIP" = x; then STRIP=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac STRIP=$ac_ct_STRIP fi else STRIP="$ac_cv_prog_STRIP" fi fi INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a thread-safe mkdir -p" >&5 $as_echo_n "checking for a thread-safe mkdir -p... " >&6; } if test -z "$MKDIR_P"; then if ${ac_cv_path_mkdir+:} false; then : $as_echo_n "(cached) " >&6 else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in mkdir gmkdir; do for ac_exec_ext in '' $ac_executable_extensions; do as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext" || continue case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #( 'mkdir (GNU coreutils) '* | \ 'mkdir (coreutils) '* | \ 'mkdir (fileutils) '4.1*) ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext break 3;; esac done done done IFS=$as_save_IFS fi test -d ./--version && rmdir ./--version if test "${ac_cv_path_mkdir+set}" = set; then MKDIR_P="$ac_cv_path_mkdir -p" else # As a last resort, use the slow shell script. Don't cache a # value for MKDIR_P within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the value is a relative name. MKDIR_P="$ac_install_sh -d" fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5 $as_echo "$MKDIR_P" >&6; } for ac_prog in gawk mawk nawk awk do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_AWK+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$AWK"; then ac_cv_prog_AWK="$AWK" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_AWK="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi AWK=$ac_cv_prog_AWK if test -n "$AWK"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5 $as_echo "$AWK" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$AWK" && break done { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 $as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } set x ${MAKE-make} ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then : $as_echo_n "(cached) " >&6 else cat >conftest.make <<\_ACEOF SHELL = /bin/sh all: @echo '@@@%%%=$(MAKE)=@@@%%%' _ACEOF # GNU make sometimes prints "make[1]: Entering ...", which would confuse us. case `${MAKE-make} -f conftest.make 2>/dev/null` in *@@@%%%=?*=@@@%%%*) eval ac_cv_prog_make_${ac_make}_set=yes;; *) eval ac_cv_prog_make_${ac_make}_set=no;; esac rm -f conftest.make fi if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } SET_MAKE= else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } SET_MAKE="MAKE=${MAKE-make}" fi rm -rf .tst 2>/dev/null mkdir .tst 2>/dev/null if test -d .tst; then am__leading_dot=. else am__leading_dot=_ fi rmdir .tst 2>/dev/null # Check whether --enable-silent-rules was given. if test "${enable_silent_rules+set}" = set; then : enableval=$enable_silent_rules; fi case $enable_silent_rules in # ((( yes) AM_DEFAULT_VERBOSITY=0;; no) AM_DEFAULT_VERBOSITY=1;; *) AM_DEFAULT_VERBOSITY=1;; esac am_make=${MAKE-make} { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5 $as_echo_n "checking whether $am_make supports nested variables... " >&6; } if ${am_cv_make_support_nested_variables+:} false; then : $as_echo_n "(cached) " >&6 else if $as_echo 'TRUE=$(BAR$(V)) BAR0=false BAR1=true V=1 am__doit: @$(TRUE) .PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then am_cv_make_support_nested_variables=yes else am_cv_make_support_nested_variables=no fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5 $as_echo "$am_cv_make_support_nested_variables" >&6; } if test $am_cv_make_support_nested_variables = yes; then AM_V='$(V)' AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' else AM_V=$AM_DEFAULT_VERBOSITY AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY fi AM_BACKSLASH='\' if test "`cd $srcdir && pwd`" != "`pwd`"; then # Use -I$(srcdir) only when $(srcdir) != ., so that make's output # is not polluted with repeated "-I." am__isrc=' -I$(srcdir)' # test to see if srcdir already configured if test -f $srcdir/config.status; then as_fn_error $? "source directory already configured; run \"make distclean\" there first" "$LINENO" 5 fi fi # test whether we have cygpath if test -z "$CYGPATH_W"; then if (cygpath --version) >/dev/null 2>/dev/null; then CYGPATH_W='cygpath -w' else CYGPATH_W=echo fi fi # Define the identity of the package. PACKAGE='putty' VERSION='0.76' cat >>confdefs.h <<_ACEOF #define PACKAGE "$PACKAGE" _ACEOF cat >>confdefs.h <<_ACEOF #define VERSION "$VERSION" _ACEOF # Some tools Automake needs. ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"} AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"} AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"} AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"} MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"} # For better backward compatibility. To be removed once Automake 1.9.x # dies out for good. For more background, see: # # mkdir_p='$(MKDIR_P)' # We need awk for the "check" target (and possibly the TAP driver). The # system "awk" is bad on some platforms. # Always define AMTAR for backward compatibility. Yes, it's still used # in the wild :-( We should find a proper way to deprecate it ... AMTAR='$${TAR-tar}' # We'll loop over all known methods to create a tar archive until one works. _am_tools='gnutar pax cpio none' am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -' # POSIX will say in a future version that running "rm -f" with no argument # is OK; and we want to be able to make that assumption in our Makefile # recipes. So use an aggressive probe to check that the usage we want is # actually supported "in the wild" to an acceptable degree. # See automake bug#10828. # To make any issue more visible, cause the running configure to be aborted # by default if the 'rm' program in use doesn't match our expectations; the # user can still override this though. if rm -f && rm -fr && rm -rf; then : OK; else cat >&2 <<'END' Oops! Your 'rm' program seems unable to run without file operands specified on the command line, even when the '-f' option is present. This is contrary to the behaviour of most rm programs out there, and not conforming with the upcoming POSIX standard: Please tell bug-automake@gnu.org about your system, including the value of your $PATH and any error possibly output before this message. This can help us improve future automake versions. END if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then echo 'Configuration will proceed anyway, since you have set the' >&2 echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 echo >&2 else cat >&2 <<'END' Aborting the configuration process, to ensure you take notice of the issue. You can download and install GNU coreutils to get an 'rm' implementation that behaves properly: . If you want to complete the configuration process using your problematic 'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM to "yes", and re-run configure. END as_fn_error $? "Your 'rm' program is bad, sorry." "$LINENO" 5 fi fi if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. set dummy ${ac_tool_prefix}ranlib; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_RANLIB+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$RANLIB"; then ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi RANLIB=$ac_cv_prog_RANLIB if test -n "$RANLIB"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5 $as_echo "$RANLIB" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_RANLIB"; then ac_ct_RANLIB=$RANLIB # Extract the first word of "ranlib", so it can be a program name with args. set dummy ranlib; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_RANLIB+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_RANLIB"; then ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_RANLIB="ranlib" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB if test -n "$ac_ct_RANLIB"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5 $as_echo "$ac_ct_RANLIB" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_RANLIB" = x; then RANLIB=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac RANLIB=$ac_ct_RANLIB fi else RANLIB="$ac_cv_prog_RANLIB" fi DEPDIR="${am__leading_dot}deps" ac_config_commands="$ac_config_commands depfiles" { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} supports the include directive" >&5 $as_echo_n "checking whether ${MAKE-make} supports the include directive... " >&6; } cat > confinc.mk << 'END' am__doit: @echo this is the am__doit target >confinc.out .PHONY: am__doit END am__include="#" am__quote= # BSD make does it like this. echo '.include "confinc.mk" # ignored' > confmf.BSD # Other make implementations (GNU, Solaris 10, AIX) do it like this. echo 'include confinc.mk # ignored' > confmf.GNU _am_result=no for s in GNU BSD; do { echo "$as_me:$LINENO: ${MAKE-make} -f confmf.$s && cat confinc.out" >&5 (${MAKE-make} -f confmf.$s && cat confinc.out) >&5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } case $?:`cat confinc.out 2>/dev/null` in #( '0:this is the am__doit target') : case $s in #( BSD) : am__include='.include' am__quote='"' ;; #( *) : am__include='include' am__quote='' ;; esac ;; #( *) : ;; esac if test "$am__include" != "#"; then _am_result="yes ($s style)" break fi done rm -f confinc.* confmf.* { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${_am_result}" >&5 $as_echo "${_am_result}" >&6; } # Check whether --enable-dependency-tracking was given. if test "${enable_dependency_tracking+set}" = set; then : enableval=$enable_dependency_tracking; fi if test "x$enable_dependency_tracking" != xno; then am_depcomp="$ac_aux_dir/depcomp" AMDEPBACKSLASH='\' am__nodep='_no' fi if test "x$enable_dependency_tracking" != xno; then AMDEP_TRUE= AMDEP_FALSE='#' else AMDEP_TRUE='#' AMDEP_FALSE= fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="${ac_tool_prefix}cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# != 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" fi fi fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then for ac_prog in cl.exe do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cl.exe do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_CC="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_CC" && break done if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi fi fi test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "no acceptable C compiler found in \$PATH See \`config.log' for more details" "$LINENO" 5; } # Provide some information about the compiler. $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" # Try to create an executable without -o first, disregard a.out. # It will help us diagnose broken compilers, and finding out an intuition # of exeext. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 $as_echo_n "checking whether the C compiler works... " >&6; } ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` # The possible output files: ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" ac_rmfiles= for ac_file in $ac_files do case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; * ) ac_rmfiles="$ac_rmfiles $ac_file";; esac done rm -f $ac_rmfiles if { { ac_try="$ac_link_default" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link_default") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. # So ignore a value of `no', otherwise this would lead to `EXEEXT = no' # in a Makefile. We should not override ac_cv_exeext if it was cached, # so that the user can short-circuit this test for compilers unknown to # Autoconf. for ac_file in $ac_files '' do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; [ab].out ) # We found the default executable, but exeext='' is most # certainly right. break;; *.* ) if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; then :; else ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` fi # We set ac_cv_exeext here because the later test for it is not # safe: cross compilers may not add the suffix if given an `-o' # argument, so we may need to know it at that point already. # Even if this section looks crufty: it has the advantage of # actually working. break;; * ) break;; esac done test "$ac_cv_exeext" = no && ac_cv_exeext= else ac_file='' fi if test -z "$ac_file"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "C compiler cannot create executables See \`config.log' for more details" "$LINENO" 5; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 $as_echo_n "checking for C compiler default output file name... " >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 $as_echo "$ac_file" >&6; } ac_exeext=$ac_cv_exeext rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 $as_echo_n "checking for suffix of executables... " >&6; } if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # If both `conftest.exe' and `conftest' are `present' (well, observable) # catch `conftest.exe'. For instance with Cygwin, `ls conftest' will # work properly (i.e., refer to `conftest.exe'), while it won't with # `rm'. for ac_file in conftest.exe conftest conftest.*; do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` break;; * ) break;; esac done else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of executables: cannot compile and link See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest conftest$ac_cv_exeext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 $as_echo "$ac_cv_exeext" >&6; } rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext ac_exeext=$EXEEXT cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { FILE *f = fopen ("conftest.out", "w"); return ferror (f) || fclose (f) != 0; ; return 0; } _ACEOF ac_clean_files="$ac_clean_files conftest.out" # Check that the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 $as_echo_n "checking whether we are cross compiling... " >&6; } if test "$cross_compiling" != yes; then { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if { ac_try='./conftest$ac_cv_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then cross_compiling=no else if test "$cross_compiling" = maybe; then cross_compiling=yes else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details" "$LINENO" 5; } fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 $as_echo "$cross_compiling" >&6; } rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 $as_echo_n "checking for suffix of object files... " >&6; } if ${ac_cv_objext+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.o conftest.obj if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : for ac_file in conftest.o conftest.obj conftest.*; do test -f "$ac_file" || continue; case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` break;; esac done else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of object files: cannot compile See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 $as_echo "$ac_cv_objext" >&6; } OBJEXT=$ac_cv_objext ac_objext=$OBJEXT { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 $as_echo_n "checking whether we are using the GNU C compiler... " >&6; } if ${ac_cv_c_compiler_gnu+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_compiler_gnu=yes else ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 $as_echo "$ac_cv_c_compiler_gnu" >&6; } if test $ac_compiler_gnu = yes; then GCC=yes else GCC= fi ac_test_CFLAGS=${CFLAGS+set} ac_save_CFLAGS=$CFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 $as_echo_n "checking whether $CC accepts -g... " >&6; } if ${ac_cv_prog_cc_g+:} false; then : $as_echo_n "(cached) " >&6 else ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes ac_cv_prog_cc_g=no CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes else CFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : else ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 $as_echo "$ac_cv_prog_cc_g" >&6; } if test "$ac_test_CFLAGS" = set; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 $as_echo_n "checking for $CC option to accept ISO C89... " >&6; } if ${ac_cv_prog_cc_c89+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_prog_cc_c89=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include struct stat; /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); static char *e (p, i) char **p; int i; { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not '\xHH' hex character constants. These don't provoke an error unfortunately, instead are silently treated as 'x'. The following induces an error, until -std is added to get proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an array size at least. It's necessary to write '\x00'==0 to get something that's true only with -std. */ int osf4_cc_array ['\x00' == 0 ? 1 : -1]; /* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters inside strings and character constants. */ #define FOO(x) 'x' int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); int argc; char **argv; int main () { return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; ; return 0; } _ACEOF for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_c89=$ac_arg fi rm -f core conftest.err conftest.$ac_objext test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi # AC_CACHE_VAL case "x$ac_cv_prog_cc_c89" in x) { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 $as_echo "none needed" >&6; } ;; xno) { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 $as_echo "unsupported" >&6; } ;; *) CC="$CC $ac_cv_prog_cc_c89" { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 $as_echo "$ac_cv_prog_cc_c89" >&6; } ;; esac if test "x$ac_cv_prog_cc_c89" != xno; then : fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5 $as_echo_n "checking whether $CC understands -c and -o together... " >&6; } if ${am_cv_prog_cc_c_o+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF # Make sure it works both with $CC and with simple cc. # Following AC_PROG_CC_C_O, we do the test twice because some # compilers refuse to overwrite an existing .o file with -o, # though they will create one. am_cv_prog_cc_c_o=yes for am_i in 1 2; do if { echo "$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext" >&5 ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } \ && test -f conftest2.$ac_objext; then : OK else am_cv_prog_cc_c_o=no break fi done rm -f core conftest* unset am_i fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5 $as_echo "$am_cv_prog_cc_c_o" >&6; } if test "$am_cv_prog_cc_c_o" != yes; then # Losing compiler, so override with the script. # FIXME: It is wrong to rewrite CC. # But if we don't then we get into trouble of one sort or another. # A longer-term fix would be to have automake use am__CC in this case, # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" CC="$am_aux_dir/compile $CC" fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu depcc="$CC" am_compiler_list= { $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 $as_echo_n "checking dependency style of $depcc... " >&6; } if ${am_cv_CC_dependencies_compiler_type+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then # We make a subdir and do the tests there. Otherwise we can end up # making bogus files that we don't know about and never remove. For # instance it was reported that on HP-UX the gcc test will end up # making a dummy file named 'D' -- because '-MD' means "put the output # in D". rm -rf conftest.dir mkdir conftest.dir # Copy depcomp to subdir because otherwise we won't find it if we're # using a relative directory. cp "$am_depcomp" conftest.dir cd conftest.dir # We will build objects and dependencies in a subdirectory because # it helps to detect inapplicable dependency modes. For instance # both Tru64's cc and ICC support -MD to output dependencies as a # side effect of compilation, but ICC will put the dependencies in # the current directory while Tru64 will put them in the object # directory. mkdir sub am_cv_CC_dependencies_compiler_type=none if test "$am_compiler_list" = ""; then am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` fi am__universal=false case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac for depmode in $am_compiler_list; do # Setup a source with many dependencies, because some compilers # like to wrap large dependency lists on column 80 (with \), and # we should not choose a depcomp mode which is confused by this. # # We need to recreate these files for each test, as the compiler may # overwrite some of them when testing with obscure command lines. # This happens at least with the AIX C compiler. : > sub/conftest.c for i in 1 2 3 4 5 6; do echo '#include "conftst'$i'.h"' >> sub/conftest.c # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with # Solaris 10 /bin/sh. echo '/* dummy */' > sub/conftst$i.h done echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf # We check with '-c' and '-o' for the sake of the "dashmstdout" # mode. It turns out that the SunPro C++ compiler does not properly # handle '-M -o', and we need to detect this. Also, some Intel # versions had trouble with output in subdirs. am__obj=sub/conftest.${OBJEXT-o} am__minus_obj="-o $am__obj" case $depmode in gcc) # This depmode causes a compiler race in universal mode. test "$am__universal" = false || continue ;; nosideeffect) # After this tag, mechanisms are not by side-effect, so they'll # only be used when explicitly requested. if test "x$enable_dependency_tracking" = xyes; then continue else break fi ;; msvc7 | msvc7msys | msvisualcpp | msvcmsys) # This compiler won't grok '-c -o', but also, the minuso test has # not run yet. These depmodes are late enough in the game, and # so weak that their functioning should not be impacted. am__obj=conftest.${OBJEXT-o} am__minus_obj= ;; none) break ;; esac if depmode=$depmode \ source=sub/conftest.c object=$am__obj \ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ >/dev/null 2>conftest.err && grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && grep $am__obj sub/conftest.Po > /dev/null 2>&1 && ${MAKE-make} -s -f confmf > /dev/null 2>&1; then # icc doesn't choke on unknown options, it will just issue warnings # or remarks (even with -Werror). So we grep stderr for any message # that says an option was ignored or not supported. # When given -MP, icc 7.0 and 7.1 complain thusly: # icc: Command line warning: ignoring option '-M'; no argument required # The diagnosis changed in icc 8.0: # icc: Command line remark: option '-MP' not supported if (grep 'ignoring option' conftest.err || grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else am_cv_CC_dependencies_compiler_type=$depmode break fi fi done cd .. rm -rf conftest.dir else am_cv_CC_dependencies_compiler_type=none fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5 $as_echo "$am_cv_CC_dependencies_compiler_type" >&6; } CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type if test "x$enable_dependency_tracking" != xno \ && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then am__fastdepCC_TRUE= am__fastdepCC_FALSE='#' else am__fastdepCC_TRUE='#' am__fastdepCC_FALSE= fi if test -n "$ac_tool_prefix"; then for ac_prog in ar lib "link -lib" do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_AR+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$AR"; then ac_cv_prog_AR="$AR" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_AR="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi AR=$ac_cv_prog_AR if test -n "$AR"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5 $as_echo "$AR" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$AR" && break done fi if test -z "$AR"; then ac_ct_AR=$AR for ac_prog in ar lib "link -lib" do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_AR+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_AR"; then ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_prog_ac_ct_AR="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_AR=$ac_cv_prog_ac_ct_AR if test -n "$ac_ct_AR"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5 $as_echo "$ac_ct_AR" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_AR" && break done if test "x$ac_ct_AR" = x; then AR="false" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac AR=$ac_ct_AR fi fi : ${AR=ar} { $as_echo "$as_me:${as_lineno-$LINENO}: checking the archiver ($AR) interface" >&5 $as_echo_n "checking the archiver ($AR) interface... " >&6; } if ${am_cv_ar_interface+:} false; then : $as_echo_n "(cached) " >&6 else ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu am_cv_ar_interface=ar cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int some_variable = 0; _ACEOF if ac_fn_c_try_compile "$LINENO"; then : am_ar_try='$AR cru libconftest.a conftest.$ac_objext >&5' { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$am_ar_try\""; } >&5 (eval $am_ar_try) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if test "$ac_status" -eq 0; then am_cv_ar_interface=ar else am_ar_try='$AR -NOLOGO -OUT:conftest.lib conftest.$ac_objext >&5' { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$am_ar_try\""; } >&5 (eval $am_ar_try) 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if test "$ac_status" -eq 0; then am_cv_ar_interface=lib else am_cv_ar_interface=unknown fi fi rm -f conftest.lib libconftest.a fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_ar_interface" >&5 $as_echo "$am_cv_ar_interface" >&6; } case $am_cv_ar_interface in ar) ;; lib) # Microsoft lib, so override with the ar-lib wrapper script. # FIXME: It is wrong to rewrite AR. # But if we don't then we get into trouble of one sort or another. # A longer-term fix would be to have automake use am__AR in this case, # and then we could set am__AR="$am_aux_dir/ar-lib \$(AR)" or something # similar. AR="$am_aux_dir/ar-lib $AR" ;; unknown) as_fn_error $? "could not determine $AR interface" "$LINENO" 5 ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C99" >&5 $as_echo_n "checking for $CC option to accept ISO C99... " >&6; } if ${ac_cv_prog_cc_c99+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_prog_cc_c99=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include #include // Check varargs macros. These examples are taken from C99 6.10.3.5. #define debug(...) fprintf (stderr, __VA_ARGS__) #define showlist(...) puts (#__VA_ARGS__) #define report(test,...) ((test) ? puts (#test) : printf (__VA_ARGS__)) static void test_varargs_macros (void) { int x = 1234; int y = 5678; debug ("Flag"); debug ("X = %d\n", x); showlist (The first, second, and third items.); report (x>y, "x is %d but y is %d", x, y); } // Check long long types. #define BIG64 18446744073709551615ull #define BIG32 4294967295ul #define BIG_OK (BIG64 / BIG32 == 4294967297ull && BIG64 % BIG32 == 0) #if !BIG_OK your preprocessor is broken; #endif #if BIG_OK #else your preprocessor is broken; #endif static long long int bignum = -9223372036854775807LL; static unsigned long long int ubignum = BIG64; struct incomplete_array { int datasize; double data[]; }; struct named_init { int number; const wchar_t *name; double average; }; typedef const char *ccp; static inline int test_restrict (ccp restrict text) { // See if C++-style comments work. // Iterate through items via the restricted pointer. // Also check for declarations in for loops. for (unsigned int i = 0; *(text+i) != '\0'; ++i) continue; return 0; } // Check varargs and va_copy. static void test_varargs (const char *format, ...) { va_list args; va_start (args, format); va_list args_copy; va_copy (args_copy, args); const char *str; int number; float fnumber; while (*format) { switch (*format++) { case 's': // string str = va_arg (args_copy, const char *); break; case 'd': // int number = va_arg (args_copy, int); break; case 'f': // float fnumber = va_arg (args_copy, double); break; default: break; } } va_end (args_copy); va_end (args); } int main () { // Check bool. _Bool success = false; // Check restrict. if (test_restrict ("String literal") == 0) success = true; char *restrict newvar = "Another string"; // Check varargs. test_varargs ("s, d' f .", "string", 65, 34.234); test_varargs_macros (); // Check flexible array members. struct incomplete_array *ia = malloc (sizeof (struct incomplete_array) + (sizeof (double) * 10)); ia->datasize = 10; for (int i = 0; i < ia->datasize; ++i) ia->data[i] = i * 1.234; // Check named initializers. struct named_init ni = { .number = 34, .name = L"Test wide string", .average = 543.34343, }; ni.number = 58; int dynamic_array[ni.number]; dynamic_array[ni.number - 1] = 543; // work around unused variable warnings return (!success || bignum == 0LL || ubignum == 0uLL || newvar[0] == 'x' || dynamic_array[ni.number - 1] != 543); ; return 0; } _ACEOF for ac_arg in '' -std=gnu99 -std=c99 -c99 -AC99 -D_STDC_C99= -qlanglvl=extc99 do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_c99=$ac_arg fi rm -f core conftest.err conftest.$ac_objext test "x$ac_cv_prog_cc_c99" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi # AC_CACHE_VAL case "x$ac_cv_prog_cc_c99" in x) { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 $as_echo "none needed" >&6; } ;; xno) { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 $as_echo "unsupported" >&6; } ;; *) CC="$CC $ac_cv_prog_cc_c99" { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5 $as_echo "$ac_cv_prog_cc_c99" >&6; } ;; esac if test "x$ac_cv_prog_cc_c99" != xno; then : fi # Mild abuse of the '--enable' option format to allow manual # specification of setuid or setgid setup in pterm. setidtype=none # Check whether --enable-setuid was given. if test "${enable_setuid+set}" = set; then : enableval=$enable_setuid; case "$enableval" in no) setidtype=none;; *) setidtype=setuid; setidval="$enableval";; esac fi # Check whether --enable-setgid was given. if test "${enable_setgid+set}" = set; then : enableval=$enable_setgid; case "$enableval" in no) setidtype=none;; *) setidtype=setgid; setidval="$enableval";; esac fi if test "$setidtype" != "none"; then HAVE_SETID_CMD_TRUE= HAVE_SETID_CMD_FALSE='#' else HAVE_SETID_CMD_TRUE='#' HAVE_SETID_CMD_FALSE= fi if test "x$setidtype" = "xsetuid"; then : SETID_CMD="chown $setidval"; SETID_MODE="4755" fi if test "x$setidtype" = "xsetgid"; then : SETID_CMD="chgrp $setidval"; SETID_MODE="2755" fi # Check whether --enable-git-commit was given. if test "${enable_git_commit+set}" = set; then : enableval=$enable_git_commit; else if test -d "$srcdir/.git"; then enable_git_commit=yes; else enable_git_commit=no; fi fi if test "x$enable_git_commit" = "xyes" -a ! -d "$srcdir/.git"; then as_fn_error $? "Cannot --enable-git-commit when source tree is not a git checkout" "$LINENO" 5 fi if test "x$enable_git_commit" = "xyes"; then AUTO_GIT_COMMIT_TRUE= AUTO_GIT_COMMIT_FALSE='#' else AUTO_GIT_COMMIT_TRUE='#' AUTO_GIT_COMMIT_FALSE= fi # Check whether --with-gssapi was given. if test "${with_gssapi+set}" = set; then : withval=$with_gssapi; else with_gssapi=yes fi # Check whether --with-quartz was given. if test "${with_quartz+set}" = set; then : withval=$with_quartz; $as_echo "#define OSX_GTK 1" >>confdefs.h with_quartz=yes else with_quartz=no fi if test "x$with_quartz" = "xyes"; then HAVE_QUARTZ_TRUE= HAVE_QUARTZ_FALSE='#' else HAVE_QUARTZ_TRUE='#' HAVE_QUARTZ_FALSE= fi WITH_GSSAPI= if test "x$with_gssapi" != xno; then : $as_echo "#define WITH_GSSAPI 1" >>confdefs.h fi # Check whether --with-gtk was given. if test "${with_gtk+set}" = set; then : withval=$with_gtk; gtk_version_desired="$withval" else gtk_version_desired="any" fi case "$gtk_version_desired" in 1 | 2 | 3 | any | no) ;; yes) gtk_version_desired="any" ;; *) as_fn_error $? "Invalid GTK version specified" "$LINENO" 5 esac for ac_header in utmpx.h do : ac_fn_c_check_header_compile "$LINENO" "utmpx.h" "ac_cv_header_utmpx_h" " #include #include " if test "x$ac_cv_header_utmpx_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_UTMPX_H 1 _ACEOF fi done # Look for GTK 3, GTK 2 and GTK 1, in descending order of preference. # If we can't find any, have the makefile only build the CLI programs. gtk=none case "$gtk_version_desired:$gtk" in 3:none | any:none) # Check whether --enable-gtktest was given. if test "${enable_gtktest+set}" = set; then : enableval=$enable_gtktest; else enable_gtktest=yes fi min_gtk_version=3.0.0 pkg_config_args="gtk+-3.0 >= $min_gtk_version" for module in . do case "$module" in gthread) pkg_config_args="$pkg_config_args gthread-2.0" ;; esac done no_gtk="" if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args. set dummy ${ac_tool_prefix}pkg-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_PKG_CONFIG+:} false; then : $as_echo_n "(cached) " >&6 else case $PKG_CONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi PKG_CONFIG=$ac_cv_path_PKG_CONFIG if test -n "$PKG_CONFIG"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5 $as_echo "$PKG_CONFIG" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_path_PKG_CONFIG"; then ac_pt_PKG_CONFIG=$PKG_CONFIG # Extract the first word of "pkg-config", so it can be a program name with args. set dummy pkg-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_ac_pt_PKG_CONFIG+:} false; then : $as_echo_n "(cached) " >&6 else case $ac_pt_PKG_CONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_ac_pt_PKG_CONFIG="$ac_pt_PKG_CONFIG" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_ac_pt_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG if test -n "$ac_pt_PKG_CONFIG"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_PKG_CONFIG" >&5 $as_echo "$ac_pt_PKG_CONFIG" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_pt_PKG_CONFIG" = x; then PKG_CONFIG="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac PKG_CONFIG=$ac_pt_PKG_CONFIG fi else PKG_CONFIG="$ac_cv_path_PKG_CONFIG" fi fi if test -n "$PKG_CONFIG"; then _pkg_min_version=0.16 { $as_echo "$as_me:${as_lineno-$LINENO}: checking pkg-config is at least version $_pkg_min_version" >&5 $as_echo_n "checking pkg-config is at least version $_pkg_min_version... " >&6; } if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } PKG_CONFIG="" fi fi if test -z "$PKG_CONFIG"; then no_gtk=yes fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GTK+ - version >= $min_gtk_version" >&5 $as_echo_n "checking for GTK+ - version >= $min_gtk_version... " >&6; } if test -n "$PKG_CONFIG"; then ## don't try to run the test against uninstalled libtool libs if $PKG_CONFIG --uninstalled $pkg_config_args; then echo "Will use uninstalled version of GTK+ found in PKG_CONFIG_PATH" enable_gtktest=no fi if $PKG_CONFIG $pkg_config_args; then : else no_gtk=yes fi fi if test x"$no_gtk" = x ; then GTK_CFLAGS=`$PKG_CONFIG $pkg_config_args --cflags` GTK_LIBS=`$PKG_CONFIG $pkg_config_args --libs` gtk_config_major_version=`$PKG_CONFIG --modversion gtk+-3.0 | \ sed 's/\([0-9]*\).\([0-9]*\).\([0-9]*\)/\1/'` gtk_config_minor_version=`$PKG_CONFIG --modversion gtk+-3.0 | \ sed 's/\([0-9]*\).\([0-9]*\).\([0-9]*\)/\2/'` gtk_config_micro_version=`$PKG_CONFIG --modversion gtk+-3.0 | \ sed 's/\([0-9]*\).\([0-9]*\).\([0-9]*\)/\3/'` if test "x$enable_gtktest" = "xyes" ; then ac_save_CFLAGS="$CFLAGS" ac_save_LIBS="$LIBS" CFLAGS="$CFLAGS $GTK_CFLAGS" LIBS="$GTK_LIBS $LIBS" rm -f conf.gtktest if test "$cross_compiling" = yes; then : echo $ac_n "cross compiling; assumed OK... $ac_c" else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include int main () { unsigned int major, minor, micro; fclose (fopen ("conf.gtktest", "w")); if (sscanf("$min_gtk_version", "%u.%u.%u", &major, &minor, µ) != 3) { printf("%s, bad version string\n", "$min_gtk_version"); exit(1); } if ((gtk_major_version != $gtk_config_major_version) || (gtk_minor_version != $gtk_config_minor_version) || (gtk_micro_version != $gtk_config_micro_version)) { printf("\n*** 'pkg-config --modversion gtk+-3.0' returned %d.%d.%d, but GTK+ (%d.%d.%d)\n", $gtk_config_major_version, $gtk_config_minor_version, $gtk_config_micro_version, gtk_major_version, gtk_minor_version, gtk_micro_version); printf ("*** was found! If pkg-config was correct, then it is best\n"); printf ("*** to remove the old version of GTK+. You may also be able to fix the error\n"); printf("*** by modifying your LD_LIBRARY_PATH enviroment variable, or by editing\n"); printf("*** /etc/ld.so.conf. Make sure you have run ldconfig if that is\n"); printf("*** required on your system.\n"); printf("*** If pkg-config was wrong, set the environment variable PKG_CONFIG_PATH\n"); printf("*** to point to the correct configuration files\n"); } else if ((gtk_major_version != GTK_MAJOR_VERSION) || (gtk_minor_version != GTK_MINOR_VERSION) || (gtk_micro_version != GTK_MICRO_VERSION)) { printf("*** GTK+ header files (version %d.%d.%d) do not match\n", GTK_MAJOR_VERSION, GTK_MINOR_VERSION, GTK_MICRO_VERSION); printf("*** library (version %d.%d.%d)\n", gtk_major_version, gtk_minor_version, gtk_micro_version); } else { if ((gtk_major_version > major) || ((gtk_major_version == major) && (gtk_minor_version > minor)) || ((gtk_major_version == major) && (gtk_minor_version == minor) && (gtk_micro_version >= micro))) { return 0; } else { printf("\n*** An old version of GTK+ (%u.%u.%u) was found.\n", gtk_major_version, gtk_minor_version, gtk_micro_version); printf("*** You need a version of GTK+ newer than %u.%u.%u. The latest version of\n", major, minor, micro); printf("*** GTK+ is always available from ftp://ftp.gtk.org.\n"); printf("***\n"); printf("*** If you have already installed a sufficiently new version, this error\n"); printf("*** probably means that the wrong copy of the pkg-config shell script is\n"); printf("*** being found. The easiest way to fix this is to remove the old version\n"); printf("*** of GTK+, but you can also set the PKG_CONFIG environment to point to the\n"); printf("*** correct copy of pkg-config. (In this case, you will have to\n"); printf("*** modify your LD_LIBRARY_PATH enviroment variable, or edit /etc/ld.so.conf\n"); printf("*** so that the correct libraries are found at run-time))\n"); } } return 1; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : else no_gtk=yes fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi CFLAGS="$ac_save_CFLAGS" LIBS="$ac_save_LIBS" fi fi if test "x$no_gtk" = x ; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes (version $gtk_config_major_version.$gtk_config_minor_version.$gtk_config_micro_version)" >&5 $as_echo "yes (version $gtk_config_major_version.$gtk_config_minor_version.$gtk_config_micro_version)" >&6; } gtk=3 else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if test -z "$PKG_CONFIG"; then echo "*** A new enough version of pkg-config was not found." echo "*** See http://pkgconfig.sourceforge.net" else if test -f conf.gtktest ; then : else echo "*** Could not run GTK+ test program, checking why..." ac_save_CFLAGS="$CFLAGS" ac_save_LIBS="$LIBS" CFLAGS="$CFLAGS $GTK_CFLAGS" LIBS="$LIBS $GTK_LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main () { return ((gtk_major_version) || (gtk_minor_version) || (gtk_micro_version)); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : echo "*** The test program compiled, but did not run. This usually means" echo "*** that the run-time linker is not finding GTK+ or finding the wrong" echo "*** version of GTK+. If it is not finding GTK+, you'll need to set your" echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point" echo "*** to the installed location Also, make sure you have run ldconfig if that" echo "*** is required on your system" echo "***" echo "*** If you have an old version installed, it is best to remove it, although" echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH" else echo "*** The test program failed to compile or link. See the file config.log for the" echo "*** exact error that occurred. This usually means GTK+ is incorrectly installed." fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext CFLAGS="$ac_save_CFLAGS" LIBS="$ac_save_LIBS" fi fi GTK_CFLAGS="" GTK_LIBS="" : fi rm -f conf.gtktest ;; esac case "$gtk_version_desired:$gtk" in 2:none | any:none) # Check whether --enable-gtktest was given. if test "${enable_gtktest+set}" = set; then : enableval=$enable_gtktest; else enable_gtktest=yes fi pkg_config_args=gtk+-2.0 for module in . do case "$module" in gthread) pkg_config_args="$pkg_config_args gthread-2.0" ;; esac done no_gtk="" if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}pkg-config", so it can be a program name with args. set dummy ${ac_tool_prefix}pkg-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_PKG_CONFIG+:} false; then : $as_echo_n "(cached) " >&6 else case $PKG_CONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi PKG_CONFIG=$ac_cv_path_PKG_CONFIG if test -n "$PKG_CONFIG"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PKG_CONFIG" >&5 $as_echo "$PKG_CONFIG" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_path_PKG_CONFIG"; then ac_pt_PKG_CONFIG=$PKG_CONFIG # Extract the first word of "pkg-config", so it can be a program name with args. set dummy pkg-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_ac_pt_PKG_CONFIG+:} false; then : $as_echo_n "(cached) " >&6 else case $ac_pt_PKG_CONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_ac_pt_PKG_CONFIG="$ac_pt_PKG_CONFIG" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_ac_pt_PKG_CONFIG="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG if test -n "$ac_pt_PKG_CONFIG"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_pt_PKG_CONFIG" >&5 $as_echo "$ac_pt_PKG_CONFIG" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_pt_PKG_CONFIG" = x; then PKG_CONFIG="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac PKG_CONFIG=$ac_pt_PKG_CONFIG fi else PKG_CONFIG="$ac_cv_path_PKG_CONFIG" fi fi if test -n "$PKG_CONFIG"; then _pkg_min_version=0.7 { $as_echo "$as_me:${as_lineno-$LINENO}: checking pkg-config is at least version $_pkg_min_version" >&5 $as_echo_n "checking pkg-config is at least version $_pkg_min_version... " >&6; } if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } PKG_CONFIG="" fi fi min_gtk_version=2.0.0 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GTK+ - version >= $min_gtk_version" >&5 $as_echo_n "checking for GTK+ - version >= $min_gtk_version... " >&6; } if test x$PKG_CONFIG != xno ; then ## don't try to run the test against uninstalled libtool libs if $PKG_CONFIG --uninstalled $pkg_config_args; then echo "Will use uninstalled version of GTK+ found in PKG_CONFIG_PATH" enable_gtktest=no fi if $PKG_CONFIG --atleast-version $min_gtk_version $pkg_config_args; then : else no_gtk=yes fi fi if test x"$no_gtk" = x ; then GTK_CFLAGS=`$PKG_CONFIG $pkg_config_args --cflags` GTK_LIBS=`$PKG_CONFIG $pkg_config_args --libs` gtk_config_major_version=`$PKG_CONFIG --modversion gtk+-2.0 | \ sed 's/\([0-9]*\).\([0-9]*\).\([0-9]*\)/\1/'` gtk_config_minor_version=`$PKG_CONFIG --modversion gtk+-2.0 | \ sed 's/\([0-9]*\).\([0-9]*\).\([0-9]*\)/\2/'` gtk_config_micro_version=`$PKG_CONFIG --modversion gtk+-2.0 | \ sed 's/\([0-9]*\).\([0-9]*\).\([0-9]*\)/\3/'` if test "x$enable_gtktest" = "xyes" ; then ac_save_CFLAGS="$CFLAGS" ac_save_LIBS="$LIBS" CFLAGS="$CFLAGS $GTK_CFLAGS" LIBS="$GTK_LIBS $LIBS" rm -f conf.gtktest if test "$cross_compiling" = yes; then : echo $ac_n "cross compiling; assumed OK... $ac_c" else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include int main () { int major, minor, micro; char *tmp_version; fclose (fopen ("conf.gtktest", "w")); /* HP/UX 9 (%@#!) writes to sscanf strings */ tmp_version = g_strdup("$min_gtk_version"); if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, µ) != 3) { printf("%s, bad version string\n", "$min_gtk_version"); exit(1); } if ((gtk_major_version != $gtk_config_major_version) || (gtk_minor_version != $gtk_config_minor_version) || (gtk_micro_version != $gtk_config_micro_version)) { printf("\n*** 'pkg-config --modversion gtk+-2.0' returned %d.%d.%d, but GTK+ (%d.%d.%d)\n", $gtk_config_major_version, $gtk_config_minor_version, $gtk_config_micro_version, gtk_major_version, gtk_minor_version, gtk_micro_version); printf ("*** was found! If pkg-config was correct, then it is best\n"); printf ("*** to remove the old version of GTK+. You may also be able to fix the error\n"); printf("*** by modifying your LD_LIBRARY_PATH enviroment variable, or by editing\n"); printf("*** /etc/ld.so.conf. Make sure you have run ldconfig if that is\n"); printf("*** required on your system.\n"); printf("*** If pkg-config was wrong, set the environment variable PKG_CONFIG_PATH\n"); printf("*** to point to the correct configuration files\n"); } else if ((gtk_major_version != GTK_MAJOR_VERSION) || (gtk_minor_version != GTK_MINOR_VERSION) || (gtk_micro_version != GTK_MICRO_VERSION)) { printf("*** GTK+ header files (version %d.%d.%d) do not match\n", GTK_MAJOR_VERSION, GTK_MINOR_VERSION, GTK_MICRO_VERSION); printf("*** library (version %d.%d.%d)\n", gtk_major_version, gtk_minor_version, gtk_micro_version); } else { if ((gtk_major_version > major) || ((gtk_major_version == major) && (gtk_minor_version > minor)) || ((gtk_major_version == major) && (gtk_minor_version == minor) && (gtk_micro_version >= micro))) { return 0; } else { printf("\n*** An old version of GTK+ (%d.%d.%d) was found.\n", gtk_major_version, gtk_minor_version, gtk_micro_version); printf("*** You need a version of GTK+ newer than %d.%d.%d. The latest version of\n", major, minor, micro); printf("*** GTK+ is always available from ftp://ftp.gtk.org.\n"); printf("***\n"); printf("*** If you have already installed a sufficiently new version, this error\n"); printf("*** probably means that the wrong copy of the pkg-config shell script is\n"); printf("*** being found. The easiest way to fix this is to remove the old version\n"); printf("*** of GTK+, but you can also set the PKG_CONFIG environment to point to the\n"); printf("*** correct copy of pkg-config. (In this case, you will have to\n"); printf("*** modify your LD_LIBRARY_PATH enviroment variable, or edit /etc/ld.so.conf\n"); printf("*** so that the correct libraries are found at run-time))\n"); } } return 1; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : else no_gtk=yes fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi CFLAGS="$ac_save_CFLAGS" LIBS="$ac_save_LIBS" fi fi if test "x$no_gtk" = x ; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes (version $gtk_config_major_version.$gtk_config_minor_version.$gtk_config_micro_version)" >&5 $as_echo "yes (version $gtk_config_major_version.$gtk_config_minor_version.$gtk_config_micro_version)" >&6; } gtk=2 else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if test "$PKG_CONFIG" = "no" ; then echo "*** A new enough version of pkg-config was not found." echo "*** See http://pkgconfig.sourceforge.net" else if test -f conf.gtktest ; then : else echo "*** Could not run GTK+ test program, checking why..." ac_save_CFLAGS="$CFLAGS" ac_save_LIBS="$LIBS" CFLAGS="$CFLAGS $GTK_CFLAGS" LIBS="$LIBS $GTK_LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include int main () { return ((gtk_major_version) || (gtk_minor_version) || (gtk_micro_version)); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : echo "*** The test program compiled, but did not run. This usually means" echo "*** that the run-time linker is not finding GTK+ or finding the wrong" echo "*** version of GTK+. If it is not finding GTK+, you'll need to set your" echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point" echo "*** to the installed location Also, make sure you have run ldconfig if that" echo "*** is required on your system" echo "***" echo "*** If you have an old version installed, it is best to remove it, although" echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH" else echo "*** The test program failed to compile or link. See the file config.log for the" echo "*** exact error that occured. This usually means GTK+ is incorrectly installed." fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext CFLAGS="$ac_save_CFLAGS" LIBS="$ac_save_LIBS" fi fi GTK_CFLAGS="" GTK_LIBS="" : fi rm -f conf.gtktest ;; esac case "$gtk_version_desired:$gtk" in 1:none | any:none) # manual check for gtk1 # Extract the first word of "gtk-config", so it can be a program name with args. set dummy gtk-config; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_GTK1_CONFIG+:} false; then : $as_echo_n "(cached) " >&6 else case $GTK1_CONFIG in [\\/]* | ?:[\\/]*) ac_cv_path_GTK1_CONFIG="$GTK1_CONFIG" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then ac_cv_path_GTK1_CONFIG="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS test -z "$ac_cv_path_GTK1_CONFIG" && ac_cv_path_GTK1_CONFIG="absent" ;; esac fi GTK1_CONFIG=$ac_cv_path_GTK1_CONFIG if test -n "$GTK1_CONFIG"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $GTK1_CONFIG" >&5 $as_echo "$GTK1_CONFIG" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "$GTK1_CONFIG" != "absent"; then GTK_CFLAGS="`"$GTK1_CONFIG" --cflags`" GTK_LIBS=`"$GTK1_CONFIG" --libs` gtk=1 fi ;; esac case "$gtk" in 1) # Add some manual #defines to make the GTK 1 headers work when # compiling in C99 mode. Left to themselves, they'll expect the # old-style pre-C99 GNU semantics of 'inline' and 'extern inline', # with the effect that they'll end up defining out-of-line # versions of the inlined functions in more than one translation # unit and cause a link failure. Override them to 'static inline', # which is safe. GTK_CFLAGS="$GTK_CFLAGS -DG_INLINE_FUNC='static inline' -DG_CAN_INLINE=1" esac if test "$gtk" != "none"; then HAVE_GTK_TRUE= HAVE_GTK_FALSE='#' else HAVE_GTK_TRUE='#' HAVE_GTK_FALSE= fi if test "$gtk" = "2" -o "$gtk" = "3"; then ac_save_CFLAGS="$CFLAGS" ac_save_LIBS="$LIBS" CFLAGS="$CFLAGS $GTK_CFLAGS" LIBS="$GTK_LIBS $LIBS" for ac_func in pango_font_family_is_monospace pango_font_map_list_families do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" if eval test \"x\$"$as_ac_var"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF fi done CFLAGS="$ac_save_CFLAGS" LIBS="$ac_save_LIBS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing socket" >&5 $as_echo_n "checking for library containing socket... " >&6; } if ${ac_cv_search_socket+:} false; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char socket (); int main () { return socket (); ; return 0; } _ACEOF for ac_lib in '' xnet; do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO"; then : ac_cv_search_socket=$ac_res fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext if ${ac_cv_search_socket+:} false; then : break fi done if ${ac_cv_search_socket+:} false; then : else ac_cv_search_socket=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_socket" >&5 $as_echo "$ac_cv_search_socket" >&6; } ac_res=$ac_cv_search_socket if test "$ac_res" != no; then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 $as_echo_n "checking how to run the C preprocessor... " >&6; } # On Suns, sometimes $CPP names a directory. if test -n "$CPP" && test -d "$CPP"; then CPP= fi if test -z "$CPP"; then if ${ac_cv_prog_CPP+:} false; then : $as_echo_n "(cached) " >&6 else # Double quotes because CPP needs to be expanded for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" do ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : break fi done ac_cv_prog_CPP=$CPP fi CPP=$ac_cv_prog_CPP else ac_cv_prog_CPP=$CPP fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 $as_echo "$CPP" >&6; } ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "C preprocessor \"$CPP\" fails sanity check See \`config.log' for more details" "$LINENO" 5; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 $as_echo_n "checking for grep that handles long lines and -e... " >&6; } if ${ac_cv_path_GREP+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$GREP"; then ac_path_GREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in grep ggrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_GREP" || continue # Check for GNU ac_path_GREP and select it if it is found. # Check for GNU $ac_path_GREP case `"$ac_path_GREP" --version 2>&1` in *GNU*) ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'GREP' >> "conftest.nl" "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_GREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_GREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_GREP"; then as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_GREP=$GREP fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 $as_echo "$ac_cv_path_GREP" >&6; } GREP="$ac_cv_path_GREP" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 $as_echo_n "checking for egrep... " >&6; } if ${ac_cv_path_EGREP+:} false; then : $as_echo_n "(cached) " >&6 else if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 then ac_cv_path_EGREP="$GREP -E" else if test -z "$EGREP"; then ac_path_EGREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in egrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" as_fn_executable_p "$ac_path_EGREP" || continue # Check for GNU ac_path_EGREP and select it if it is found. # Check for GNU $ac_path_EGREP case `"$ac_path_EGREP" --version 2>&1` in *GNU*) ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'EGREP' >> "conftest.nl" "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_EGREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_EGREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_EGREP"; then as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_EGREP=$EGREP fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 $as_echo "$ac_cv_path_EGREP" >&6; } EGREP="$ac_cv_path_EGREP" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 $as_echo_n "checking for ANSI C header files... " >&6; } if ${ac_cv_header_stdc+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_header_stdc=yes else ac_cv_header_stdc=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext if test $ac_cv_header_stdc = yes; then # SunOS 4.x string.h does not declare mem*, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "memchr" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "free" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. if test "$cross_compiling" = yes; then : : else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #if ((' ' & 0x0FF) == 0x020) # define ISLOWER(c) ('a' <= (c) && (c) <= 'z') # define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) #else # define ISLOWER(c) \ (('a' <= (c) && (c) <= 'i') \ || ('j' <= (c) && (c) <= 'r') \ || ('s' <= (c) && (c) <= 'z')) # define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) #endif #define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) int main () { int i; for (i = 0; i < 256; i++) if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) return 2; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : else ac_cv_header_stdc=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 $as_echo "$ac_cv_header_stdc" >&6; } if test $ac_cv_header_stdc = yes; then $as_echo "#define STDC_HEADERS 1" >>confdefs.h fi # On IRIX 5.3, sys/types and inttypes.h are conflicting. for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ inttypes.h stdint.h unistd.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default " if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done if test "x$with_gssapi" != xno; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing dlopen" >&5 $as_echo_n "checking for library containing dlopen... " >&6; } if ${ac_cv_search_dlopen+:} false; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char dlopen (); int main () { return dlopen (); ; return 0; } _ACEOF for ac_lib in '' dl; do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO"; then : ac_cv_search_dlopen=$ac_res fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext if ${ac_cv_search_dlopen+:} false; then : break fi done if ${ac_cv_search_dlopen+:} false; then : else ac_cv_search_dlopen=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_dlopen" >&5 $as_echo "$ac_cv_search_dlopen" >&6; } ac_res=$ac_cv_search_dlopen if test "$ac_res" != no; then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" else $as_echo "#define NO_LIBDL 1" >>confdefs.h for ac_header in gssapi/gssapi.h do : ac_fn_c_check_header_mongrel "$LINENO" "gssapi/gssapi.h" "ac_cv_header_gssapi_gssapi_h" "$ac_includes_default" if test "x$ac_cv_header_gssapi_gssapi_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_GSSAPI_GSSAPI_H 1 _ACEOF fi done { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing gss_init_sec_context" >&5 $as_echo_n "checking for library containing gss_init_sec_context... " >&6; } if ${ac_cv_search_gss_init_sec_context+:} false; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char gss_init_sec_context (); int main () { return gss_init_sec_context (); ; return 0; } _ACEOF for ac_lib in '' gssapi gssapi_krb5 gss; do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO"; then : ac_cv_search_gss_init_sec_context=$ac_res fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext if ${ac_cv_search_gss_init_sec_context+:} false; then : break fi done if ${ac_cv_search_gss_init_sec_context+:} false; then : else ac_cv_search_gss_init_sec_context=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_gss_init_sec_context" >&5 $as_echo "$ac_cv_search_gss_init_sec_context" >&6; } ac_res=$ac_cv_search_gss_init_sec_context if test "$ac_res" != no; then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" else $as_echo "#define NO_GSSAPI_LIB 1" >>confdefs.h fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for XOpenDisplay in -lX11" >&5 $as_echo_n "checking for XOpenDisplay in -lX11... " >&6; } if ${ac_cv_lib_X11_XOpenDisplay+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lX11 $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char XOpenDisplay (); int main () { return XOpenDisplay (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_X11_XOpenDisplay=yes else ac_cv_lib_X11_XOpenDisplay=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_X11_XOpenDisplay" >&5 $as_echo "$ac_cv_lib_X11_XOpenDisplay" >&6; } if test "x$ac_cv_lib_X11_XOpenDisplay" = xyes; then : GTK_LIBS="-lX11 $GTK_LIBS" $as_echo "#define HAVE_LIBX11 /**/" >>confdefs.h fi for ac_func in getaddrinfo posix_openpt ptsname setresuid strsignal updwtmpx fstatat dirfd futimes setpwent endpwent getauxval elf_aux_info sysctlbyname do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" if eval test \"x\$"$as_ac_var"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF fi done ac_fn_c_check_decl "$LINENO" "CLOCK_MONOTONIC" "ac_cv_have_decl_CLOCK_MONOTONIC" "#include " if test "x$ac_cv_have_decl_CLOCK_MONOTONIC" = xyes; then : ac_have_decl=1 else ac_have_decl=0 fi cat >>confdefs.h <<_ACEOF #define HAVE_DECL_CLOCK_MONOTONIC $ac_have_decl _ACEOF for ac_header in sys/auxv.h asm/hwcap.h sys/sysctl.h sys/types.h glob.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing clock_gettime" >&5 $as_echo_n "checking for library containing clock_gettime... " >&6; } if ${ac_cv_search_clock_gettime+:} false; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char clock_gettime (); int main () { return clock_gettime (); ; return 0; } _ACEOF for ac_lib in '' rt; do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO"; then : ac_cv_search_clock_gettime=$ac_res fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext if ${ac_cv_search_clock_gettime+:} false; then : break fi done if ${ac_cv_search_clock_gettime+:} false; then : else ac_cv_search_clock_gettime=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_clock_gettime" >&5 $as_echo "$ac_cv_search_clock_gettime" >&6; } ac_res=$ac_cv_search_clock_gettime if test "$ac_res" != no; then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" $as_echo "#define HAVE_CLOCK_GETTIME /**/" >>confdefs.h fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for SO_PEERCRED and dependencies" >&5 $as_echo_n "checking for SO_PEERCRED and dependencies... " >&6; } if ${x_cv_linux_so_peercred+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define _GNU_SOURCE #include #include int main () { struct ucred cr; socklen_t crlen = sizeof(cr); return getsockopt(0, SOL_SOCKET, SO_PEERCRED, &cr, &crlen) + cr.pid + cr.uid + cr.gid; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : x_cv_linux_so_peercred=yes else x_cv_linux_so_peercred=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $x_cv_linux_so_peercred" >&5 $as_echo "$x_cv_linux_so_peercred" >&6; } if test $x_cv_linux_so_peercred = yes; then : $as_echo "#define HAVE_SO_PEERCRED 1" >>confdefs.h fi if test "x$GCC" = "xyes"; then : WARNINGOPTS='-Wall -Wpointer-arith -Wvla' else : fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing pow" >&5 $as_echo_n "checking for library containing pow... " >&6; } if ${ac_cv_search_pow+:} false; then : $as_echo_n "(cached) " >&6 else ac_func_search_save_LIBS=$LIBS cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char pow (); int main () { return pow (); ; return 0; } _ACEOF for ac_lib in '' m; do if test -z "$ac_lib"; then ac_res="none required" else ac_res=-l$ac_lib LIBS="-l$ac_lib $ac_func_search_save_LIBS" fi if ac_fn_c_try_link "$LINENO"; then : ac_cv_search_pow=$ac_res fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext if ${ac_cv_search_pow+:} false; then : break fi done if ${ac_cv_search_pow+:} false; then : else ac_cv_search_pow=no fi rm conftest.$ac_ext LIBS=$ac_func_search_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_pow" >&5 $as_echo "$ac_cv_search_pow" >&6; } ac_res=$ac_cv_search_pow if test "$ac_res" != no; then : test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" fi cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs, see configure's option --config-cache. # It is not useful on other systems. If it contains results you don't # want to keep, you may remove or edit it. # # config.status only pays attention to the cache file if you give it # the --recheck option to rerun configure. # # `ac_cv_env_foo' variables (set or unset) will be overridden when # loading this file, other *unset* `ac_cv_foo' will be assigned the # following values. _ACEOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, we kill variables containing newlines. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. ( for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space=' '; set) 2>&1` in #( *${as_nl}ac_space=\ *) # `set' does not quote correctly, so add quotes: double-quote # substitution turns \\\\ into \\, and sed turns \\ into \. sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" ;; #( *) # `set' quotes correctly as required by POSIX, so do not add quotes. sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) | sed ' /^ac_cv_env_/b end t clear :clear s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ t end s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ :end' >>confcache if diff "$cache_file" confcache >/dev/null 2>&1; then :; else if test -w "$cache_file"; then if test "x$cache_file" != "x/dev/null"; then { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 $as_echo "$as_me: updating cache $cache_file" >&6;} if test ! -f "$cache_file" || test -h "$cache_file"; then cat confcache >"$cache_file" else case $cache_file in #( */* | ?:*) mv -f confcache "$cache_file"$$ && mv -f "$cache_file"$$ "$cache_file" ;; #( *) mv -f confcache "$cache_file" ;; esac fi fi else { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 $as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} fi fi rm -f confcache test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' DEFS=-DHAVE_CONFIG_H ac_libobjs= ac_ltlibobjs= U= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' ac_i=`$as_echo "$ac_i" | sed "$ac_script"` # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR # will be set to the directory where LIBOBJS objects are built. as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' done LIBOBJS=$ac_libobjs LTLIBOBJS=$ac_ltlibobjs { $as_echo "$as_me:${as_lineno-$LINENO}: checking that generated files are newer than configure" >&5 $as_echo_n "checking that generated files are newer than configure... " >&6; } if test -n "$am_sleep_pid"; then # Hide warnings about reused PIDs. wait $am_sleep_pid 2>/dev/null fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: done" >&5 $as_echo "done" >&6; } if test -n "$EXEEXT"; then am__EXEEXT_TRUE= am__EXEEXT_FALSE='#' else am__EXEEXT_TRUE='#' am__EXEEXT_FALSE= fi if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then as_fn_error $? "conditional \"AMDEP\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then as_fn_error $? "conditional \"am__fastdepCC\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${HAVE_SETID_CMD_TRUE}" && test -z "${HAVE_SETID_CMD_FALSE}"; then as_fn_error $? "conditional \"HAVE_SETID_CMD\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${AUTO_GIT_COMMIT_TRUE}" && test -z "${AUTO_GIT_COMMIT_FALSE}"; then as_fn_error $? "conditional \"AUTO_GIT_COMMIT\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${HAVE_QUARTZ_TRUE}" && test -z "${HAVE_QUARTZ_FALSE}"; then as_fn_error $? "conditional \"HAVE_QUARTZ\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi if test -z "${HAVE_GTK_TRUE}" && test -z "${HAVE_GTK_FALSE}"; then as_fn_error $? "conditional \"HAVE_GTK\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi : "${CONFIG_STATUS=./config.status}" ac_write_fail=0 ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 $as_echo "$as_me: creating $CONFIG_STATUS" >&6;} as_write_fail=0 cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 #! $SHELL # Generated by $as_me. # Run this file to recreate the current configuration. # Compiler output produced by configure, useful for debugging # configure, is in config.log if it exists. debug=false ac_cs_recheck=false ac_cs_silent=false SHELL=\${CONFIG_SHELL-$SHELL} export SHELL _ASEOF cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -pR'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -pR' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -pR' fi else as_ln_s='cp -pR' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi # as_fn_executable_p FILE # ----------------------- # Test if FILE is an executable regular file. as_fn_executable_p () { test -f "$1" && test -x "$1" } # as_fn_executable_p as_test_x='test -x' as_executable_p=as_fn_executable_p # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" exec 6>&1 ## ----------------------------------- ## ## Main body of $CONFIG_STATUS script. ## ## ----------------------------------- ## _ASEOF test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Save the log message, to keep $0 and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" This file was extended by putty $as_me 0.76, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ on `(hostname || uname -n) 2>/dev/null | sed 1q` " _ACEOF case $ac_config_files in *" "*) set x $ac_config_files; shift; ac_config_files=$*;; esac case $ac_config_headers in *" "*) set x $ac_config_headers; shift; ac_config_headers=$*;; esac cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # Files that config.status was made for. config_files="$ac_config_files" config_headers="$ac_config_headers" config_commands="$ac_config_commands" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 ac_cs_usage="\ \`$as_me' instantiates files and other configuration actions from templates according to the current configuration. Unless the files and actions are specified as TAGs, all are instantiated by default. Usage: $0 [OPTION]... [TAG]... -h, --help print this help, then exit -V, --version print version number and configuration settings, then exit --config print configuration, then exit -q, --quiet, --silent do not print progress messages -d, --debug don't remove temporary files --recheck update $as_me by reconfiguring in the same conditions --file=FILE[:TEMPLATE] instantiate the configuration file FILE --header=FILE[:TEMPLATE] instantiate the configuration header FILE Configuration files: $config_files Configuration headers: $config_headers Configuration commands: $config_commands Report bugs to the package provider." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ putty config.status 0.76 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" Copyright (C) 2012 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." ac_pwd='$ac_pwd' srcdir='$srcdir' INSTALL='$INSTALL' MKDIR_P='$MKDIR_P' AWK='$AWK' test -n "\$AWK" || AWK=awk _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # The default lists apply if the user does not specify any file. ac_need_defaults=: while test $# != 0 do case $1 in --*=?*) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` ac_shift=: ;; --*=) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg= ac_shift=: ;; *) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; esac case $ac_option in # Handling of the options. -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) $as_echo "$ac_cs_version"; exit ;; --config | --confi | --conf | --con | --co | --c ) $as_echo "$ac_cs_config"; exit ;; --debug | --debu | --deb | --de | --d | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; '') as_fn_error $? "missing file argument" ;; esac as_fn_append CONFIG_FILES " '$ac_optarg'" ac_need_defaults=false;; --header | --heade | --head | --hea ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; esac as_fn_append CONFIG_HEADERS " '$ac_optarg'" ac_need_defaults=false;; --he | --h) # Conflict between --help and --header as_fn_error $? "ambiguous option: \`$1' Try \`$0 --help' for more information.";; --help | --hel | -h ) $as_echo "$ac_cs_usage"; exit ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil | --si | --s) ac_cs_silent=: ;; # This is an error. -*) as_fn_error $? "unrecognized option: \`$1' Try \`$0 --help' for more information." ;; *) as_fn_append ac_config_targets " $1" ac_need_defaults=false ;; esac shift done ac_configure_extra_args= if $ac_cs_silent; then exec 6>/dev/null ac_configure_extra_args="$ac_configure_extra_args --silent" fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 if \$ac_cs_recheck; then set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion shift \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 CONFIG_SHELL='$SHELL' export CONFIG_SHELL exec "\$@" fi _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 exec 5>>config.log { echo sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX ## Running $as_me. ## _ASBOX $as_echo "$ac_log" } >&5 _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # # INIT-COMMANDS # AMDEP_TRUE="$AMDEP_TRUE" MAKE="${MAKE-make}" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Handling of arguments. for ac_config_target in $ac_config_targets do case $ac_config_target in "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; "uxconfig.h") CONFIG_HEADERS="$CONFIG_HEADERS uxconfig.h:uxconfig.in" ;; "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;; *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; esac done # If the user did not use the arguments to specify the items to instantiate, # then the envvar interface is used. Set only those that are not. # We use the long form for the default assignment because of an extremely # bizarre bug on SunOS 4.1.3. if $ac_need_defaults; then test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands fi # Have a temporary directory for convenience. Make it in the build tree # simply because there is no reason against having it here, and in addition, # creating and moving files from /tmp can sometimes cause problems. # Hook for its removal unless debugging. # Note that there is a small window in which the directory will not be cleaned: # after its creation but before its name has been assigned to `$tmp'. $debug || { tmp= ac_tmp= trap 'exit_status=$? : "${ac_tmp:=$tmp}" { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status ' 0 trap 'as_fn_exit 1' 1 2 13 15 } # Create a (secure) tmp directory for tmp files. { tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && test -d "$tmp" } || { tmp=./conf$$-$RANDOM (umask 077 && mkdir "$tmp") } || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 ac_tmp=$tmp # Set up the scripts for CONFIG_FILES section. # No need to generate them if there are no CONFIG_FILES. # This happens for instance with `./config.status config.h'. if test -n "$CONFIG_FILES"; then ac_cr=`echo X | tr X '\015'` # On cygwin, bash can eat \r inside `` if the user requested igncr. # But we know of no other shell where ac_cr would be empty at this # point, so we can use a bashism as a fallback. if test "x$ac_cr" = x; then eval ac_cr=\$\'\\r\' fi ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then ac_cs_awk_cr='\\r' else ac_cs_awk_cr=$ac_cr fi echo 'BEGIN {' >"$ac_tmp/subs1.awk" && _ACEOF { echo "cat >conf$$subs.awk <<_ACEOF" && echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && echo "_ACEOF" } >conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` ac_delim='%!_!# ' for ac_last_try in false false false false false :; do . ./conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` if test $ac_delim_n = $ac_delim_num; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done rm -f conf$$subs.sh cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && _ACEOF sed -n ' h s/^/S["/; s/!.*/"]=/ p g s/^[^!]*!// :repl t repl s/'"$ac_delim"'$// t delim :nl h s/\(.\{148\}\)..*/\1/ t more1 s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ p n b repl :more1 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t nl :delim h s/\(.\{148\}\)..*/\1/ t more2 s/["\\]/\\&/g; s/^/"/; s/$/"/ p b :more2 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t delim ' >$CONFIG_STATUS || ac_write_fail=1 rm -f conf$$subs.awk cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACAWK cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && for (key in S) S_is_set[key] = 1 FS = "" } { line = $ 0 nfields = split(line, field, "@") substed = 0 len = length(field[1]) for (i = 2; i < nfields; i++) { key = field[i] keylen = length(key) if (S_is_set[key]) { value = S[key] line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) len += length(value) + length(field[++i]) substed = 1 } else len += 1 + keylen } print line } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" else cat fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 _ACEOF # VPATH may cause trouble with some makes, so we remove sole $(srcdir), # ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ h s/// s/^/:/ s/[ ]*$/:/ s/:\$(srcdir):/:/g s/:\${srcdir}:/:/g s/:@srcdir@:/:/g s/^:*// s/:*$// x s/\(=[ ]*\).*/\1/ G s/\n// s/^[^=]*=[ ]*$// }' fi cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 fi # test -n "$CONFIG_FILES" # Set up the scripts for CONFIG_HEADERS section. # No need to generate them if there are no CONFIG_HEADERS. # This happens for instance with `./config.status Makefile'. if test -n "$CONFIG_HEADERS"; then cat >"$ac_tmp/defines.awk" <<\_ACAWK || BEGIN { _ACEOF # Transform confdefs.h into an awk script `defines.awk', embedded as # here-document in config.status, that substitutes the proper values into # config.h.in to produce config.h. # Create a delimiter string that does not exist in confdefs.h, to ease # handling of long lines. ac_delim='%!_!# ' for ac_last_try in false false :; do ac_tt=`sed -n "/$ac_delim/p" confdefs.h` if test -z "$ac_tt"; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done # For the awk script, D is an array of macro values keyed by name, # likewise P contains macro parameters if any. Preserve backslash # newline sequences. ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* sed -n ' s/.\{148\}/&'"$ac_delim"'/g t rset :rset s/^[ ]*#[ ]*define[ ][ ]*/ / t def d :def s/\\$// t bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3"/p s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p d :bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3\\\\\\n"\\/p t cont s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p t cont d :cont n s/.\{148\}/&'"$ac_delim"'/g t clear :clear s/\\$// t bsnlc s/["\\]/\\&/g; s/^/"/; s/$/"/p d :bsnlc s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p b cont ' >$CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 for (key in D) D_is_set[key] = 1 FS = "" } /^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { line = \$ 0 split(line, arg, " ") if (arg[1] == "#") { defundef = arg[2] mac1 = arg[3] } else { defundef = substr(arg[1], 2) mac1 = arg[2] } split(mac1, mac2, "(") #) macro = mac2[1] prefix = substr(line, 1, index(line, defundef) - 1) if (D_is_set[macro]) { # Preserve the white space surrounding the "#". print prefix "define", macro P[macro] D[macro] next } else { # Replace #undef with comments. This is necessary, for example, # in the case of _POSIX_SOURCE, which is predefined and required # on some systems where configure will not decide to define it. if (defundef == "undef") { print "/*", prefix defundef, macro, "*/" next } } } { print } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 fi # test -n "$CONFIG_HEADERS" eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS :C $CONFIG_COMMANDS" shift for ac_tag do case $ac_tag in :[FHLC]) ac_mode=$ac_tag; continue;; esac case $ac_mode$ac_tag in :[FHL]*:*);; :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac ac_save_IFS=$IFS IFS=: set x $ac_tag IFS=$ac_save_IFS shift ac_file=$1 shift case $ac_mode in :L) ac_source=$1;; :[FH]) ac_file_inputs= for ac_f do case $ac_f in -) ac_f="$ac_tmp/stdin";; *) # Look for the file first in the build tree, then in the source tree # (if the path is not absolute). The absolute path cannot be DOS-style, # because $ac_f cannot contain `:'. test -f "$ac_f" || case $ac_f in [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; esac case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac as_fn_append ac_file_inputs " '$ac_f'" done # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ configure_input='Generated from '` $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' `' by configure.' if test x"$ac_file" != x-; then configure_input="$ac_file. $configure_input" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 $as_echo "$as_me: creating $ac_file" >&6;} fi # Neutralize special characters interpreted by sed in replacement strings. case $configure_input in #( *\&* | *\|* | *\\* ) ac_sed_conf_input=`$as_echo "$configure_input" | sed 's/[\\\\&|]/\\\\&/g'`;; #( *) ac_sed_conf_input=$configure_input;; esac case $ac_tag in *:-:* | *:-) cat >"$ac_tmp/stdin" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; esac ;; esac ac_dir=`$as_dirname -- "$ac_file" || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` as_dir="$ac_dir"; as_fn_mkdir_p ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix case $ac_mode in :F) # # CONFIG_FILE # case $INSTALL in [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; esac ac_MKDIR_P=$MKDIR_P case $MKDIR_P in [\\/$]* | ?:[\\/]* ) ;; */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;; esac _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # If the template does not know about datarootdir, expand it. # FIXME: This hack should be removed a few years after 2.60. ac_datarootdir_hack=; ac_datarootdir_seen= ac_sed_dataroot=' /datarootdir/ { p q } /@datadir@/p /@docdir@/p /@infodir@/p /@localedir@/p /@mandir@/p' case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in *datarootdir*) ac_datarootdir_seen=yes;; *@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 $as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_datarootdir_hack=' s&@datadir@&$datadir&g s&@docdir@&$docdir&g s&@infodir@&$infodir&g s&@localedir@&$localedir&g s&@mandir@&$mandir&g s&\\\${datarootdir}&$datarootdir&g' ;; esac _ACEOF # Neutralize VPATH when `$srcdir' = `.'. # Shell code in configure.ac might set extrasub. # FIXME: do we really want to maintain this feature? cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_sed_extra="$ac_vpsub $extrasub _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 :t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b s|@configure_input@|$ac_sed_conf_input|;t t s&@top_builddir@&$ac_top_builddir_sub&;t t s&@top_build_prefix@&$ac_top_build_prefix&;t t s&@srcdir@&$ac_srcdir&;t t s&@abs_srcdir@&$ac_abs_srcdir&;t t s&@top_srcdir@&$ac_top_srcdir&;t t s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t s&@builddir@&$ac_builddir&;t t s&@abs_builddir@&$ac_abs_builddir&;t t s&@abs_top_builddir@&$ac_abs_top_builddir&;t t s&@INSTALL@&$ac_INSTALL&;t t s&@MKDIR_P@&$ac_MKDIR_P&;t t $ac_datarootdir_hack " eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ "$ac_tmp/out"`; test -z "$ac_out"; } && { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&5 $as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&2;} rm -f "$ac_tmp/stdin" case $ac_file in -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; esac \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; :H) # # CONFIG_HEADER # if test x"$ac_file" != x-; then { $as_echo "/* $configure_input */" \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" } >"$ac_tmp/config.h" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 $as_echo "$as_me: $ac_file is unchanged" >&6;} else rm -f "$ac_file" mv "$ac_tmp/config.h" "$ac_file" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 fi else $as_echo "/* $configure_input */" \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ || as_fn_error $? "could not create -" "$LINENO" 5 fi # Compute "$ac_file"'s index in $config_headers. _am_arg="$ac_file" _am_stamp_count=1 for _am_header in $config_headers :; do case $_am_header in $_am_arg | $_am_arg:* ) break ;; * ) _am_stamp_count=`expr $_am_stamp_count + 1` ;; esac done echo "timestamp for $_am_arg" >`$as_dirname -- "$_am_arg" || $as_expr X"$_am_arg" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$_am_arg" : 'X\(//\)[^/]' \| \ X"$_am_arg" : 'X\(//\)$' \| \ X"$_am_arg" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$_am_arg" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'`/stamp-h$_am_stamp_count ;; :C) { $as_echo "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5 $as_echo "$as_me: executing $ac_file commands" >&6;} ;; esac case $ac_file$ac_mode in "depfiles":C) test x"$AMDEP_TRUE" != x"" || { # Older Autoconf quotes --file arguments for eval, but not when files # are listed without --file. Let's play safe and only enable the eval # if we detect the quoting. # TODO: see whether this extra hack can be removed once we start # requiring Autoconf 2.70 or later. case $CONFIG_FILES in #( *\'*) : eval set x "$CONFIG_FILES" ;; #( *) : set x $CONFIG_FILES ;; #( *) : ;; esac shift # Used to flag and report bootstrapping failures. am_rc=0 for am_mf do # Strip MF so we end up with the name of the file. am_mf=`$as_echo "$am_mf" | sed -e 's/:.*$//'` # Check whether this is an Automake generated Makefile which includes # dependency-tracking related rules and includes. # Grep'ing the whole file directly is not great: AIX grep has a line # limit of 2048, but all sed's we know have understand at least 4000. sed -n 's,^am--depfiles:.*,X,p' "$am_mf" | grep X >/dev/null 2>&1 \ || continue am_dirpart=`$as_dirname -- "$am_mf" || $as_expr X"$am_mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$am_mf" : 'X\(//\)[^/]' \| \ X"$am_mf" : 'X\(//\)$' \| \ X"$am_mf" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$am_mf" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` am_filepart=`$as_basename -- "$am_mf" || $as_expr X/"$am_mf" : '.*/\([^/][^/]*\)/*$' \| \ X"$am_mf" : 'X\(//\)$' \| \ X"$am_mf" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$am_mf" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` { echo "$as_me:$LINENO: cd "$am_dirpart" \ && sed -e '/# am--include-marker/d' "$am_filepart" \ | $MAKE -f - am--depfiles" >&5 (cd "$am_dirpart" \ && sed -e '/# am--include-marker/d' "$am_filepart" \ | $MAKE -f - am--depfiles) >&5 2>&5 ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&5 (exit $ac_status); } || am_rc=$? done if test $am_rc -ne 0; then { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "Something went wrong bootstrapping makefile fragments for automatic dependency tracking. Try re-running configure with the '--disable-dependency-tracking' option to at least be able to build the package (albeit without support for automatic dependency tracking). See \`config.log' for more details" "$LINENO" 5; } fi { am_dirpart=; unset am_dirpart;} { am_filepart=; unset am_filepart;} { am_mf=; unset am_mf;} { am_rc=; unset am_rc;} rm -f conftest-deps.mk } ;; esac done # for ac_tag as_fn_exit 0 _ACEOF ac_clean_files=$ac_clean_files_save test $ac_write_fail = 0 || as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 # configure is writing to config.log, and then calls config.status. # config.status does its own redirection, appending to config.log. # Unfortunately, on DOS this fails, as config.log is still kept open # by configure, so config.status won't be able to write to it; its # output is simply discarded. So we exec the FD to /dev/null, # effectively closing config.log, so it can be properly (re)opened and # appended to by config.status. When coming back to configure, we # need to make the FD available again. if test "$no_create" != yes; then ac_cs_success=: ac_config_status_args= test "$silent" = yes && ac_config_status_args="$ac_config_status_args --quiet" exec 5>/dev/null $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. $ac_cs_success || as_fn_exit 1 fi if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} fi if test "$gtk_version_desired" = "no"; then cat <= $min_gtk_version) if test x$PKG_CONFIG != xno ; then ## don't try to run the test against uninstalled libtool libs if $PKG_CONFIG --uninstalled $pkg_config_args; then echo "Will use uninstalled version of GTK+ found in PKG_CONFIG_PATH" enable_gtktest=no fi if $PKG_CONFIG --atleast-version $min_gtk_version $pkg_config_args; then : else no_gtk=yes fi fi if test x"$no_gtk" = x ; then GTK_CFLAGS=`$PKG_CONFIG $pkg_config_args --cflags` GTK_LIBS=`$PKG_CONFIG $pkg_config_args --libs` gtk_config_major_version=`$PKG_CONFIG --modversion gtk+-2.0 | \ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'` gtk_config_minor_version=`$PKG_CONFIG --modversion gtk+-2.0 | \ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'` gtk_config_micro_version=`$PKG_CONFIG --modversion gtk+-2.0 | \ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'` if test "x$enable_gtktest" = "xyes" ; then ac_save_CFLAGS="$CFLAGS" ac_save_LIBS="$LIBS" CFLAGS="$CFLAGS $GTK_CFLAGS" LIBS="$GTK_LIBS $LIBS" dnl dnl Now check if the installed GTK+ is sufficiently new. (Also sanity dnl checks the results of pkg-config to some extent) dnl rm -f conf.gtktest AC_TRY_RUN([ #include #include #include int main () { int major, minor, micro; char *tmp_version; fclose (fopen ("conf.gtktest", "w")); /* HP/UX 9 (%@#!) writes to sscanf strings */ tmp_version = g_strdup("$min_gtk_version"); if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, µ) != 3) { printf("%s, bad version string\n", "$min_gtk_version"); exit(1); } if ((gtk_major_version != $gtk_config_major_version) || (gtk_minor_version != $gtk_config_minor_version) || (gtk_micro_version != $gtk_config_micro_version)) { printf("\n*** 'pkg-config --modversion gtk+-2.0' returned %d.%d.%d, but GTK+ (%d.%d.%d)\n", $gtk_config_major_version, $gtk_config_minor_version, $gtk_config_micro_version, gtk_major_version, gtk_minor_version, gtk_micro_version); printf ("*** was found! If pkg-config was correct, then it is best\n"); printf ("*** to remove the old version of GTK+. You may also be able to fix the error\n"); printf("*** by modifying your LD_LIBRARY_PATH enviroment variable, or by editing\n"); printf("*** /etc/ld.so.conf. Make sure you have run ldconfig if that is\n"); printf("*** required on your system.\n"); printf("*** If pkg-config was wrong, set the environment variable PKG_CONFIG_PATH\n"); printf("*** to point to the correct configuration files\n"); } else if ((gtk_major_version != GTK_MAJOR_VERSION) || (gtk_minor_version != GTK_MINOR_VERSION) || (gtk_micro_version != GTK_MICRO_VERSION)) { printf("*** GTK+ header files (version %d.%d.%d) do not match\n", GTK_MAJOR_VERSION, GTK_MINOR_VERSION, GTK_MICRO_VERSION); printf("*** library (version %d.%d.%d)\n", gtk_major_version, gtk_minor_version, gtk_micro_version); } else { if ((gtk_major_version > major) || ((gtk_major_version == major) && (gtk_minor_version > minor)) || ((gtk_major_version == major) && (gtk_minor_version == minor) && (gtk_micro_version >= micro))) { return 0; } else { printf("\n*** An old version of GTK+ (%d.%d.%d) was found.\n", gtk_major_version, gtk_minor_version, gtk_micro_version); printf("*** You need a version of GTK+ newer than %d.%d.%d. The latest version of\n", major, minor, micro); printf("*** GTK+ is always available from ftp://ftp.gtk.org.\n"); printf("***\n"); printf("*** If you have already installed a sufficiently new version, this error\n"); printf("*** probably means that the wrong copy of the pkg-config shell script is\n"); printf("*** being found. The easiest way to fix this is to remove the old version\n"); printf("*** of GTK+, but you can also set the PKG_CONFIG environment to point to the\n"); printf("*** correct copy of pkg-config. (In this case, you will have to\n"); printf("*** modify your LD_LIBRARY_PATH enviroment variable, or edit /etc/ld.so.conf\n"); printf("*** so that the correct libraries are found at run-time))\n"); } } return 1; } ],, no_gtk=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"]) CFLAGS="$ac_save_CFLAGS" LIBS="$ac_save_LIBS" fi fi if test "x$no_gtk" = x ; then AC_MSG_RESULT(yes (version $gtk_config_major_version.$gtk_config_minor_version.$gtk_config_micro_version)) ifelse([$2], , :, [$2]) else AC_MSG_RESULT(no) if test "$PKG_CONFIG" = "no" ; then echo "*** A new enough version of pkg-config was not found." echo "*** See http://pkgconfig.sourceforge.net" else if test -f conf.gtktest ; then : else echo "*** Could not run GTK+ test program, checking why..." ac_save_CFLAGS="$CFLAGS" ac_save_LIBS="$LIBS" CFLAGS="$CFLAGS $GTK_CFLAGS" LIBS="$LIBS $GTK_LIBS" AC_TRY_LINK([ #include #include ], [ return ((gtk_major_version) || (gtk_minor_version) || (gtk_micro_version)); ], [ echo "*** The test program compiled, but did not run. This usually means" echo "*** that the run-time linker is not finding GTK+ or finding the wrong" echo "*** version of GTK+. If it is not finding GTK+, you'll need to set your" echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point" echo "*** to the installed location Also, make sure you have run ldconfig if that" echo "*** is required on your system" echo "***" echo "*** If you have an old version installed, it is best to remove it, although" echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH" ], [ echo "*** The test program failed to compile or link. See the file config.log for the" echo "*** exact error that occured. This usually means GTK+ is incorrectly installed."]) CFLAGS="$ac_save_CFLAGS" LIBS="$ac_save_LIBS" fi fi GTK_CFLAGS="" GTK_LIBS="" ifelse([$3], , :, [$3]) fi AC_SUBST(GTK_CFLAGS) AC_SUBST(GTK_LIBS) rm -f conf.gtktest ]) # Configure paths for GTK+ # Owen Taylor 1997-2001 # Version number used by aclocal, see `info automake Serials`. # Increment on every change. #serial 1 dnl AM_PATH_GTK_3_0([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND [, MODULES]]]]) dnl Test for GTK+, and define GTK_CFLAGS and GTK_LIBS, if gthread is specified in MODULES, dnl pass to pkg-config dnl AC_DEFUN([AM_PATH_GTK_3_0], [m4_warn([obsolete], [AM_PATH_GTK_3_0 is deprecated, use PKG_CHECK_MODULES([GTK], [gtk+-3.0]) instead]) dnl Get the cflags and libraries from pkg-config dnl AC_ARG_ENABLE(gtktest, [ --disable-gtktest do not try to compile and run a test GTK+ program], , enable_gtktest=yes) min_gtk_version=ifelse([$1], [], [3.0.0], [$1]) pkg_config_args="gtk+-3.0 >= $min_gtk_version" for module in . $4 do case "$module" in gthread) pkg_config_args="$pkg_config_args gthread-2.0" ;; esac done no_gtk="" PKG_PROG_PKG_CONFIG([0.16]) if test -z "$PKG_CONFIG"; then no_gtk=yes fi AC_MSG_CHECKING(for GTK+ - version >= $min_gtk_version) if test -n "$PKG_CONFIG"; then ## don't try to run the test against uninstalled libtool libs if $PKG_CONFIG --uninstalled $pkg_config_args; then echo "Will use uninstalled version of GTK+ found in PKG_CONFIG_PATH" enable_gtktest=no fi if $PKG_CONFIG $pkg_config_args; then : else no_gtk=yes fi fi if test x"$no_gtk" = x ; then GTK_CFLAGS=`$PKG_CONFIG $pkg_config_args --cflags` GTK_LIBS=`$PKG_CONFIG $pkg_config_args --libs` gtk_config_major_version=`$PKG_CONFIG --modversion gtk+-3.0 | \ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'` gtk_config_minor_version=`$PKG_CONFIG --modversion gtk+-3.0 | \ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'` gtk_config_micro_version=`$PKG_CONFIG --modversion gtk+-3.0 | \ sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'` if test "x$enable_gtktest" = "xyes" ; then ac_save_CFLAGS="$CFLAGS" ac_save_LIBS="$LIBS" CFLAGS="$CFLAGS $GTK_CFLAGS" LIBS="$GTK_LIBS $LIBS" dnl dnl Now check if the installed GTK+ is sufficiently new. (Also sanity dnl checks the results of pkg-config to some extent) dnl rm -f conf.gtktest AC_TRY_RUN([ #include #include #include int main () { unsigned int major, minor, micro; fclose (fopen ("conf.gtktest", "w")); if (sscanf("$min_gtk_version", "%u.%u.%u", &major, &minor, µ) != 3) { printf("%s, bad version string\n", "$min_gtk_version"); exit(1); } if ((gtk_major_version != $gtk_config_major_version) || (gtk_minor_version != $gtk_config_minor_version) || (gtk_micro_version != $gtk_config_micro_version)) { printf("\n*** 'pkg-config --modversion gtk+-3.0' returned %d.%d.%d, but GTK+ (%d.%d.%d)\n", $gtk_config_major_version, $gtk_config_minor_version, $gtk_config_micro_version, gtk_major_version, gtk_minor_version, gtk_micro_version); printf ("*** was found! If pkg-config was correct, then it is best\n"); printf ("*** to remove the old version of GTK+. You may also be able to fix the error\n"); printf("*** by modifying your LD_LIBRARY_PATH enviroment variable, or by editing\n"); printf("*** /etc/ld.so.conf. Make sure you have run ldconfig if that is\n"); printf("*** required on your system.\n"); printf("*** If pkg-config was wrong, set the environment variable PKG_CONFIG_PATH\n"); printf("*** to point to the correct configuration files\n"); } else if ((gtk_major_version != GTK_MAJOR_VERSION) || (gtk_minor_version != GTK_MINOR_VERSION) || (gtk_micro_version != GTK_MICRO_VERSION)) { printf("*** GTK+ header files (version %d.%d.%d) do not match\n", GTK_MAJOR_VERSION, GTK_MINOR_VERSION, GTK_MICRO_VERSION); printf("*** library (version %d.%d.%d)\n", gtk_major_version, gtk_minor_version, gtk_micro_version); } else { if ((gtk_major_version > major) || ((gtk_major_version == major) && (gtk_minor_version > minor)) || ((gtk_major_version == major) && (gtk_minor_version == minor) && (gtk_micro_version >= micro))) { return 0; } else { printf("\n*** An old version of GTK+ (%u.%u.%u) was found.\n", gtk_major_version, gtk_minor_version, gtk_micro_version); printf("*** You need a version of GTK+ newer than %u.%u.%u. The latest version of\n", major, minor, micro); printf("*** GTK+ is always available from ftp://ftp.gtk.org.\n"); printf("***\n"); printf("*** If you have already installed a sufficiently new version, this error\n"); printf("*** probably means that the wrong copy of the pkg-config shell script is\n"); printf("*** being found. The easiest way to fix this is to remove the old version\n"); printf("*** of GTK+, but you can also set the PKG_CONFIG environment to point to the\n"); printf("*** correct copy of pkg-config. (In this case, you will have to\n"); printf("*** modify your LD_LIBRARY_PATH enviroment variable, or edit /etc/ld.so.conf\n"); printf("*** so that the correct libraries are found at run-time))\n"); } } return 1; } ],, no_gtk=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"]) CFLAGS="$ac_save_CFLAGS" LIBS="$ac_save_LIBS" fi fi if test "x$no_gtk" = x ; then AC_MSG_RESULT(yes (version $gtk_config_major_version.$gtk_config_minor_version.$gtk_config_micro_version)) ifelse([$2], , :, [$2]) else AC_MSG_RESULT(no) if test -z "$PKG_CONFIG"; then echo "*** A new enough version of pkg-config was not found." echo "*** See http://pkgconfig.sourceforge.net" else if test -f conf.gtktest ; then : else echo "*** Could not run GTK+ test program, checking why..." ac_save_CFLAGS="$CFLAGS" ac_save_LIBS="$LIBS" CFLAGS="$CFLAGS $GTK_CFLAGS" LIBS="$LIBS $GTK_LIBS" AC_TRY_LINK([ #include #include ], [ return ((gtk_major_version) || (gtk_minor_version) || (gtk_micro_version)); ], [ echo "*** The test program compiled, but did not run. This usually means" echo "*** that the run-time linker is not finding GTK+ or finding the wrong" echo "*** version of GTK+. If it is not finding GTK+, you'll need to set your" echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point" echo "*** to the installed location Also, make sure you have run ldconfig if that" echo "*** is required on your system" echo "***" echo "*** If you have an old version installed, it is best to remove it, although" echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH" ], [ echo "*** The test program failed to compile or link. See the file config.log for the" echo "*** exact error that occurred. This usually means GTK+ is incorrectly installed."]) CFLAGS="$ac_save_CFLAGS" LIBS="$ac_save_LIBS" fi fi GTK_CFLAGS="" GTK_LIBS="" ifelse([$3], , :, [$3]) fi AC_SUBST(GTK_CFLAGS) AC_SUBST(GTK_LIBS) rm -f conf.gtktest ]) dnl GTK_CHECK_BACKEND(BACKEND-NAME [, MINIMUM-VERSION [, ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]]) dnl Tests for BACKEND-NAME in the GTK targets list dnl AC_DEFUN([GTK_CHECK_BACKEND], [m4_warn([obsolete], [GTK_CHECK_BACKEND is deprecated, use PKG_CHECK_MODULES([GTK_X11], [gtk+-x11-3.0]) or similar instead]) pkg_config_args=ifelse([$1],,gtk+-3.0, gtk+-$1-3.0) min_gtk_version=ifelse([$2],,3.0.0,$2) pkg_config_args="$pkg_config_args >= $min_gtk_version" PKG_PROG_PKG_CONFIG([0.16]) AS_IF([test -z "$PKG_CONFIG"], [AC_MSG_ERROR([No pkg-config found])]) if $PKG_CONFIG $pkg_config_args ; then target_found=yes else target_found=no fi if test "x$target_found" = "xno"; then ifelse([$4],,[AC_MSG_ERROR([Backend $backend not found.])],[$4]) else ifelse([$3],,[:],[$3]) fi ]) dnl pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- dnl serial 11 (pkg-config-0.29.1) dnl dnl Copyright © 2004 Scott James Remnant . dnl Copyright © 2012-2015 Dan Nicholson dnl dnl This program is free software; you can redistribute it and/or modify dnl it under the terms of the GNU General Public License as published by dnl the Free Software Foundation; either version 2 of the License, or dnl (at your option) any later version. dnl dnl This program is distributed in the hope that it will be useful, but dnl WITHOUT ANY WARRANTY; without even the implied warranty of dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU dnl General Public License for more details. dnl dnl You should have received a copy of the GNU General Public License dnl along with this program; if not, write to the Free Software dnl Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA dnl 02111-1307, USA. dnl dnl As a special exception to the GNU General Public License, if you dnl distribute this file as part of a program that contains a dnl configuration script generated by Autoconf, you may include it under dnl the same distribution terms that you use for the rest of that dnl program. dnl PKG_PREREQ(MIN-VERSION) dnl ----------------------- dnl Since: 0.29 dnl dnl Verify that the version of the pkg-config macros are at least dnl MIN-VERSION. Unlike PKG_PROG_PKG_CONFIG, which checks the user's dnl installed version of pkg-config, this checks the developer's version dnl of pkg.m4 when generating configure. dnl dnl To ensure that this macro is defined, also add: dnl m4_ifndef([PKG_PREREQ], dnl [m4_fatal([must install pkg-config 0.29 or later before running autoconf/autogen])]) dnl dnl See the "Since" comment for each macro you use to see what version dnl of the macros you require. m4_defun([PKG_PREREQ], [m4_define([PKG_MACROS_VERSION], [0.29.1]) m4_if(m4_version_compare(PKG_MACROS_VERSION, [$1]), -1, [m4_fatal([pkg.m4 version $1 or higher is required but ]PKG_MACROS_VERSION[ found])]) ])dnl PKG_PREREQ dnl PKG_PROG_PKG_CONFIG([MIN-VERSION]) dnl ---------------------------------- dnl Since: 0.16 dnl dnl Search for the pkg-config tool and set the PKG_CONFIG variable to dnl first found in the path. Checks that the version of pkg-config found dnl is at least MIN-VERSION. If MIN-VERSION is not specified, 0.9.0 is dnl used since that's the first version where most current features of dnl pkg-config existed. AC_DEFUN([PKG_PROG_PKG_CONFIG], [m4_pattern_forbid([^_?PKG_[A-Z_]+$]) m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$]) m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$]) AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility]) AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path]) AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path]) if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) fi if test -n "$PKG_CONFIG"; then _pkg_min_version=m4_default([$1], [0.9.0]) AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) PKG_CONFIG="" fi fi[]dnl ])dnl PKG_PROG_PKG_CONFIG dnl PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) dnl ------------------------------------------------------------------- dnl Since: 0.18 dnl dnl Check to see whether a particular set of modules exists. Similar to dnl PKG_CHECK_MODULES(), but does not set variables or print errors. dnl dnl Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG]) dnl only at the first occurence in configure.ac, so if the first place dnl it's called might be skipped (such as if it is within an "if", you dnl have to call PKG_CHECK_EXISTS manually AC_DEFUN([PKG_CHECK_EXISTS], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl if test -n "$PKG_CONFIG" && \ AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then m4_default([$2], [:]) m4_ifvaln([$3], [else $3])dnl fi]) dnl _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) dnl --------------------------------------------- dnl Internal wrapper calling pkg-config via PKG_CONFIG and setting dnl pkg_failed based on the result. m4_define([_PKG_CONFIG], [if test -n "$$1"; then pkg_cv_[]$1="$$1" elif test -n "$PKG_CONFIG"; then PKG_CHECK_EXISTS([$3], [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes ], [pkg_failed=yes]) else pkg_failed=untried fi[]dnl ])dnl _PKG_CONFIG dnl _PKG_SHORT_ERRORS_SUPPORTED dnl --------------------------- dnl Internal check to see if pkg-config supports short errors. AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], [AC_REQUIRE([PKG_PROG_PKG_CONFIG]) if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi[]dnl ])dnl _PKG_SHORT_ERRORS_SUPPORTED dnl PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], dnl [ACTION-IF-NOT-FOUND]) dnl -------------------------------------------------------------- dnl Since: 0.4.0 dnl dnl Note that if there is a possibility the first call to dnl PKG_CHECK_MODULES might not happen, you should be sure to include an dnl explicit call to PKG_PROG_PKG_CONFIG in your configure.ac AC_DEFUN([PKG_CHECK_MODULES], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl pkg_failed=no AC_MSG_CHECKING([for $1]) _PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) _PKG_CONFIG([$1][_LIBS], [libs], [$2]) m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS and $1[]_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details.]) if test $pkg_failed = yes; then AC_MSG_RESULT([no]) _PKG_SHORT_ERRORS_SUPPORTED if test $_pkg_short_errors_supported = yes; then $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1` else $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD m4_default([$4], [AC_MSG_ERROR( [Package requirements ($2) were not met: $$1_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. _PKG_TEXT])[]dnl ]) elif test $pkg_failed = untried; then AC_MSG_RESULT([no]) m4_default([$4], [AC_MSG_FAILURE( [The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. _PKG_TEXT To get pkg-config, see .])[]dnl ]) else $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS $1[]_LIBS=$pkg_cv_[]$1[]_LIBS AC_MSG_RESULT([yes]) $3 fi[]dnl ])dnl PKG_CHECK_MODULES dnl PKG_CHECK_MODULES_STATIC(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], dnl [ACTION-IF-NOT-FOUND]) dnl --------------------------------------------------------------------- dnl Since: 0.29 dnl dnl Checks for existence of MODULES and gathers its build flags with dnl static libraries enabled. Sets VARIABLE-PREFIX_CFLAGS from --cflags dnl and VARIABLE-PREFIX_LIBS from --libs. dnl dnl Note that if there is a possibility the first call to dnl PKG_CHECK_MODULES_STATIC might not happen, you should be sure to dnl include an explicit call to PKG_PROG_PKG_CONFIG in your dnl configure.ac. AC_DEFUN([PKG_CHECK_MODULES_STATIC], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl _save_PKG_CONFIG=$PKG_CONFIG PKG_CONFIG="$PKG_CONFIG --static" PKG_CHECK_MODULES($@) PKG_CONFIG=$_save_PKG_CONFIG[]dnl ])dnl PKG_CHECK_MODULES_STATIC dnl PKG_INSTALLDIR([DIRECTORY]) dnl ------------------------- dnl Since: 0.27 dnl dnl Substitutes the variable pkgconfigdir as the location where a module dnl should install pkg-config .pc files. By default the directory is dnl $libdir/pkgconfig, but the default can be changed by passing dnl DIRECTORY. The user can override through the --with-pkgconfigdir dnl parameter. AC_DEFUN([PKG_INSTALLDIR], [m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])]) m4_pushdef([pkg_description], [pkg-config installation directory @<:@]pkg_default[@:>@]) AC_ARG_WITH([pkgconfigdir], [AS_HELP_STRING([--with-pkgconfigdir], pkg_description)],, [with_pkgconfigdir=]pkg_default) AC_SUBST([pkgconfigdir], [$with_pkgconfigdir]) m4_popdef([pkg_default]) m4_popdef([pkg_description]) ])dnl PKG_INSTALLDIR dnl PKG_NOARCH_INSTALLDIR([DIRECTORY]) dnl -------------------------------- dnl Since: 0.27 dnl dnl Substitutes the variable noarch_pkgconfigdir as the location where a dnl module should install arch-independent pkg-config .pc files. By dnl default the directory is $datadir/pkgconfig, but the default can be dnl changed by passing DIRECTORY. The user can override through the dnl --with-noarch-pkgconfigdir parameter. AC_DEFUN([PKG_NOARCH_INSTALLDIR], [m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])]) m4_pushdef([pkg_description], [pkg-config arch-independent installation directory @<:@]pkg_default[@:>@]) AC_ARG_WITH([noarch-pkgconfigdir], [AS_HELP_STRING([--with-noarch-pkgconfigdir], pkg_description)],, [with_noarch_pkgconfigdir=]pkg_default) AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir]) m4_popdef([pkg_default]) m4_popdef([pkg_description]) ])dnl PKG_NOARCH_INSTALLDIR dnl PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE, dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) dnl ------------------------------------------- dnl Since: 0.28 dnl dnl Retrieves the value of the pkg-config variable for the given module. AC_DEFUN([PKG_CHECK_VAR], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl _PKG_CONFIG([$1], [variable="][$3]["], [$2]) AS_VAR_COPY([$1], [pkg_cv_][$1]) AS_VAR_IF([$1], [""], [$5], [$4])dnl ])dnl PKG_CHECK_VAR # Copyright (C) 2002-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_AUTOMAKE_VERSION(VERSION) # ---------------------------- # Automake X.Y traces this macro to ensure aclocal.m4 has been # generated from the m4 files accompanying Automake X.Y. # (This private macro should not be called outside this file.) AC_DEFUN([AM_AUTOMAKE_VERSION], [am__api_version='1.16' dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to dnl require some minimum version. Point them to the right macro. m4_if([$1], [1.16.1], [], [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl ]) # _AM_AUTOCONF_VERSION(VERSION) # ----------------------------- # aclocal traces this macro to find the Autoconf version. # This is a private macro too. Using m4_define simplifies # the logic in aclocal, which can simply ignore this definition. m4_define([_AM_AUTOCONF_VERSION], []) # AM_SET_CURRENT_AUTOMAKE_VERSION # ------------------------------- # Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. # This function is AC_REQUIREd by AM_INIT_AUTOMAKE. AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], [AM_AUTOMAKE_VERSION([1.16.1])dnl m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) # Copyright (C) 2011-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_PROG_AR([ACT-IF-FAIL]) # ------------------------- # Try to determine the archiver interface, and trigger the ar-lib wrapper # if it is needed. If the detection of archiver interface fails, run # ACT-IF-FAIL (default is to abort configure with a proper error message). AC_DEFUN([AM_PROG_AR], [AC_BEFORE([$0], [LT_INIT])dnl AC_BEFORE([$0], [AC_PROG_LIBTOOL])dnl AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl AC_REQUIRE_AUX_FILE([ar-lib])dnl AC_CHECK_TOOLS([AR], [ar lib "link -lib"], [false]) : ${AR=ar} AC_CACHE_CHECK([the archiver ($AR) interface], [am_cv_ar_interface], [AC_LANG_PUSH([C]) am_cv_ar_interface=ar AC_COMPILE_IFELSE([AC_LANG_SOURCE([[int some_variable = 0;]])], [am_ar_try='$AR cru libconftest.a conftest.$ac_objext >&AS_MESSAGE_LOG_FD' AC_TRY_EVAL([am_ar_try]) if test "$ac_status" -eq 0; then am_cv_ar_interface=ar else am_ar_try='$AR -NOLOGO -OUT:conftest.lib conftest.$ac_objext >&AS_MESSAGE_LOG_FD' AC_TRY_EVAL([am_ar_try]) if test "$ac_status" -eq 0; then am_cv_ar_interface=lib else am_cv_ar_interface=unknown fi fi rm -f conftest.lib libconftest.a ]) AC_LANG_POP([C])]) case $am_cv_ar_interface in ar) ;; lib) # Microsoft lib, so override with the ar-lib wrapper script. # FIXME: It is wrong to rewrite AR. # But if we don't then we get into trouble of one sort or another. # A longer-term fix would be to have automake use am__AR in this case, # and then we could set am__AR="$am_aux_dir/ar-lib \$(AR)" or something # similar. AR="$am_aux_dir/ar-lib $AR" ;; unknown) m4_default([$1], [AC_MSG_ERROR([could not determine $AR interface])]) ;; esac AC_SUBST([AR])dnl ]) # AM_AUX_DIR_EXPAND -*- Autoconf -*- # Copyright (C) 2001-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets # $ac_aux_dir to '$srcdir/foo'. In other projects, it is set to # '$srcdir', '$srcdir/..', or '$srcdir/../..'. # # Of course, Automake must honor this variable whenever it calls a # tool from the auxiliary directory. The problem is that $srcdir (and # therefore $ac_aux_dir as well) can be either absolute or relative, # depending on how configure is run. This is pretty annoying, since # it makes $ac_aux_dir quite unusable in subdirectories: in the top # source directory, any form will work fine, but in subdirectories a # relative path needs to be adjusted first. # # $ac_aux_dir/missing # fails when called from a subdirectory if $ac_aux_dir is relative # $top_srcdir/$ac_aux_dir/missing # fails if $ac_aux_dir is absolute, # fails when called from a subdirectory in a VPATH build with # a relative $ac_aux_dir # # The reason of the latter failure is that $top_srcdir and $ac_aux_dir # are both prefixed by $srcdir. In an in-source build this is usually # harmless because $srcdir is '.', but things will broke when you # start a VPATH build or use an absolute $srcdir. # # So we could use something similar to $top_srcdir/$ac_aux_dir/missing, # iff we strip the leading $srcdir from $ac_aux_dir. That would be: # am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"` # and then we would define $MISSING as # MISSING="\${SHELL} $am_aux_dir/missing" # This will work as long as MISSING is not called from configure, because # unfortunately $(top_srcdir) has no meaning in configure. # However there are other variables, like CC, which are often used in # configure, and could therefore not use this "fixed" $ac_aux_dir. # # Another solution, used here, is to always expand $ac_aux_dir to an # absolute PATH. The drawback is that using absolute paths prevent a # configured tree to be moved without reconfiguration. AC_DEFUN([AM_AUX_DIR_EXPAND], [AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl # Expand $ac_aux_dir to an absolute path. am_aux_dir=`cd "$ac_aux_dir" && pwd` ]) # AM_CONDITIONAL -*- Autoconf -*- # Copyright (C) 1997-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_CONDITIONAL(NAME, SHELL-CONDITION) # ------------------------------------- # Define a conditional. AC_DEFUN([AM_CONDITIONAL], [AC_PREREQ([2.52])dnl m4_if([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])], [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl AC_SUBST([$1_TRUE])dnl AC_SUBST([$1_FALSE])dnl _AM_SUBST_NOTMAKE([$1_TRUE])dnl _AM_SUBST_NOTMAKE([$1_FALSE])dnl m4_define([_AM_COND_VALUE_$1], [$2])dnl if $2; then $1_TRUE= $1_FALSE='#' else $1_TRUE='#' $1_FALSE= fi AC_CONFIG_COMMANDS_PRE( [if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then AC_MSG_ERROR([[conditional "$1" was never defined. Usually this means the macro was only invoked conditionally.]]) fi])]) # Copyright (C) 1999-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # There are a few dirty hacks below to avoid letting 'AC_PROG_CC' be # written in clear, in which case automake, when reading aclocal.m4, # will think it sees a *use*, and therefore will trigger all it's # C support machinery. Also note that it means that autoscan, seeing # CC etc. in the Makefile, will ask for an AC_PROG_CC use... # _AM_DEPENDENCIES(NAME) # ---------------------- # See how the compiler implements dependency checking. # NAME is "CC", "CXX", "OBJC", "OBJCXX", "UPC", or "GJC". # We try a few techniques and use that to set a single cache variable. # # We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was # modified to invoke _AM_DEPENDENCIES(CC); we would have a circular # dependency, and given that the user is not expected to run this macro, # just rely on AC_PROG_CC. AC_DEFUN([_AM_DEPENDENCIES], [AC_REQUIRE([AM_SET_DEPDIR])dnl AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl AC_REQUIRE([AM_MAKE_INCLUDE])dnl AC_REQUIRE([AM_DEP_TRACK])dnl m4_if([$1], [CC], [depcc="$CC" am_compiler_list=], [$1], [CXX], [depcc="$CXX" am_compiler_list=], [$1], [OBJC], [depcc="$OBJC" am_compiler_list='gcc3 gcc'], [$1], [OBJCXX], [depcc="$OBJCXX" am_compiler_list='gcc3 gcc'], [$1], [UPC], [depcc="$UPC" am_compiler_list=], [$1], [GCJ], [depcc="$GCJ" am_compiler_list='gcc3 gcc'], [depcc="$$1" am_compiler_list=]) AC_CACHE_CHECK([dependency style of $depcc], [am_cv_$1_dependencies_compiler_type], [if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then # We make a subdir and do the tests there. Otherwise we can end up # making bogus files that we don't know about and never remove. For # instance it was reported that on HP-UX the gcc test will end up # making a dummy file named 'D' -- because '-MD' means "put the output # in D". rm -rf conftest.dir mkdir conftest.dir # Copy depcomp to subdir because otherwise we won't find it if we're # using a relative directory. cp "$am_depcomp" conftest.dir cd conftest.dir # We will build objects and dependencies in a subdirectory because # it helps to detect inapplicable dependency modes. For instance # both Tru64's cc and ICC support -MD to output dependencies as a # side effect of compilation, but ICC will put the dependencies in # the current directory while Tru64 will put them in the object # directory. mkdir sub am_cv_$1_dependencies_compiler_type=none if test "$am_compiler_list" = ""; then am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp` fi am__universal=false m4_case([$1], [CC], [case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac], [CXX], [case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac]) for depmode in $am_compiler_list; do # Setup a source with many dependencies, because some compilers # like to wrap large dependency lists on column 80 (with \), and # we should not choose a depcomp mode which is confused by this. # # We need to recreate these files for each test, as the compiler may # overwrite some of them when testing with obscure command lines. # This happens at least with the AIX C compiler. : > sub/conftest.c for i in 1 2 3 4 5 6; do echo '#include "conftst'$i'.h"' >> sub/conftest.c # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with # Solaris 10 /bin/sh. echo '/* dummy */' > sub/conftst$i.h done echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf # We check with '-c' and '-o' for the sake of the "dashmstdout" # mode. It turns out that the SunPro C++ compiler does not properly # handle '-M -o', and we need to detect this. Also, some Intel # versions had trouble with output in subdirs. am__obj=sub/conftest.${OBJEXT-o} am__minus_obj="-o $am__obj" case $depmode in gcc) # This depmode causes a compiler race in universal mode. test "$am__universal" = false || continue ;; nosideeffect) # After this tag, mechanisms are not by side-effect, so they'll # only be used when explicitly requested. if test "x$enable_dependency_tracking" = xyes; then continue else break fi ;; msvc7 | msvc7msys | msvisualcpp | msvcmsys) # This compiler won't grok '-c -o', but also, the minuso test has # not run yet. These depmodes are late enough in the game, and # so weak that their functioning should not be impacted. am__obj=conftest.${OBJEXT-o} am__minus_obj= ;; none) break ;; esac if depmode=$depmode \ source=sub/conftest.c object=$am__obj \ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ >/dev/null 2>conftest.err && grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && grep $am__obj sub/conftest.Po > /dev/null 2>&1 && ${MAKE-make} -s -f confmf > /dev/null 2>&1; then # icc doesn't choke on unknown options, it will just issue warnings # or remarks (even with -Werror). So we grep stderr for any message # that says an option was ignored or not supported. # When given -MP, icc 7.0 and 7.1 complain thusly: # icc: Command line warning: ignoring option '-M'; no argument required # The diagnosis changed in icc 8.0: # icc: Command line remark: option '-MP' not supported if (grep 'ignoring option' conftest.err || grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else am_cv_$1_dependencies_compiler_type=$depmode break fi fi done cd .. rm -rf conftest.dir else am_cv_$1_dependencies_compiler_type=none fi ]) AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type]) AM_CONDITIONAL([am__fastdep$1], [ test "x$enable_dependency_tracking" != xno \ && test "$am_cv_$1_dependencies_compiler_type" = gcc3]) ]) # AM_SET_DEPDIR # ------------- # Choose a directory name for dependency files. # This macro is AC_REQUIREd in _AM_DEPENDENCIES. AC_DEFUN([AM_SET_DEPDIR], [AC_REQUIRE([AM_SET_LEADING_DOT])dnl AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl ]) # AM_DEP_TRACK # ------------ AC_DEFUN([AM_DEP_TRACK], [AC_ARG_ENABLE([dependency-tracking], [dnl AS_HELP_STRING( [--enable-dependency-tracking], [do not reject slow dependency extractors]) AS_HELP_STRING( [--disable-dependency-tracking], [speeds up one-time build])]) if test "x$enable_dependency_tracking" != xno; then am_depcomp="$ac_aux_dir/depcomp" AMDEPBACKSLASH='\' am__nodep='_no' fi AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno]) AC_SUBST([AMDEPBACKSLASH])dnl _AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl AC_SUBST([am__nodep])dnl _AM_SUBST_NOTMAKE([am__nodep])dnl ]) # Generate code to set up dependency tracking. -*- Autoconf -*- # Copyright (C) 1999-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_OUTPUT_DEPENDENCY_COMMANDS # ------------------------------ AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], [{ # Older Autoconf quotes --file arguments for eval, but not when files # are listed without --file. Let's play safe and only enable the eval # if we detect the quoting. # TODO: see whether this extra hack can be removed once we start # requiring Autoconf 2.70 or later. AS_CASE([$CONFIG_FILES], [*\'*], [eval set x "$CONFIG_FILES"], [*], [set x $CONFIG_FILES]) shift # Used to flag and report bootstrapping failures. am_rc=0 for am_mf do # Strip MF so we end up with the name of the file. am_mf=`AS_ECHO(["$am_mf"]) | sed -e 's/:.*$//'` # Check whether this is an Automake generated Makefile which includes # dependency-tracking related rules and includes. # Grep'ing the whole file directly is not great: AIX grep has a line # limit of 2048, but all sed's we know have understand at least 4000. sed -n 's,^am--depfiles:.*,X,p' "$am_mf" | grep X >/dev/null 2>&1 \ || continue am_dirpart=`AS_DIRNAME(["$am_mf"])` am_filepart=`AS_BASENAME(["$am_mf"])` AM_RUN_LOG([cd "$am_dirpart" \ && sed -e '/# am--include-marker/d' "$am_filepart" \ | $MAKE -f - am--depfiles]) || am_rc=$? done if test $am_rc -ne 0; then AC_MSG_FAILURE([Something went wrong bootstrapping makefile fragments for automatic dependency tracking. Try re-running configure with the '--disable-dependency-tracking' option to at least be able to build the package (albeit without support for automatic dependency tracking).]) fi AS_UNSET([am_dirpart]) AS_UNSET([am_filepart]) AS_UNSET([am_mf]) AS_UNSET([am_rc]) rm -f conftest-deps.mk } ])# _AM_OUTPUT_DEPENDENCY_COMMANDS # AM_OUTPUT_DEPENDENCY_COMMANDS # ----------------------------- # This macro should only be invoked once -- use via AC_REQUIRE. # # This code is only required when automatic dependency tracking is enabled. # This creates each '.Po' and '.Plo' makefile fragment that we'll need in # order to bootstrap the dependency handling code. AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], [AC_CONFIG_COMMANDS([depfiles], [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS], [AMDEP_TRUE="$AMDEP_TRUE" MAKE="${MAKE-make}"])]) # Do all the work for Automake. -*- Autoconf -*- # Copyright (C) 1996-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This macro actually does too much. Some checks are only needed if # your package does certain things. But this isn't really a big deal. dnl Redefine AC_PROG_CC to automatically invoke _AM_PROG_CC_C_O. m4_define([AC_PROG_CC], m4_defn([AC_PROG_CC]) [_AM_PROG_CC_C_O ]) # AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE]) # AM_INIT_AUTOMAKE([OPTIONS]) # ----------------------------------------------- # The call with PACKAGE and VERSION arguments is the old style # call (pre autoconf-2.50), which is being phased out. PACKAGE # and VERSION should now be passed to AC_INIT and removed from # the call to AM_INIT_AUTOMAKE. # We support both call styles for the transition. After # the next Automake release, Autoconf can make the AC_INIT # arguments mandatory, and then we can depend on a new Autoconf # release and drop the old call support. AC_DEFUN([AM_INIT_AUTOMAKE], [AC_PREREQ([2.65])dnl dnl Autoconf wants to disallow AM_ names. We explicitly allow dnl the ones we care about. m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl AC_REQUIRE([AC_PROG_INSTALL])dnl if test "`cd $srcdir && pwd`" != "`pwd`"; then # Use -I$(srcdir) only when $(srcdir) != ., so that make's output # is not polluted with repeated "-I." AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl # test to see if srcdir already configured if test -f $srcdir/config.status; then AC_MSG_ERROR([source directory already configured; run "make distclean" there first]) fi fi # test whether we have cygpath if test -z "$CYGPATH_W"; then if (cygpath --version) >/dev/null 2>/dev/null; then CYGPATH_W='cygpath -w' else CYGPATH_W=echo fi fi AC_SUBST([CYGPATH_W]) # Define the identity of the package. dnl Distinguish between old-style and new-style calls. m4_ifval([$2], [AC_DIAGNOSE([obsolete], [$0: two- and three-arguments forms are deprecated.]) m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl AC_SUBST([PACKAGE], [$1])dnl AC_SUBST([VERSION], [$2])], [_AM_SET_OPTIONS([$1])dnl dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT. m4_if( m4_ifdef([AC_PACKAGE_NAME], [ok]):m4_ifdef([AC_PACKAGE_VERSION], [ok]), [ok:ok],, [m4_fatal([AC_INIT should be called with package and version arguments])])dnl AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl _AM_IF_OPTION([no-define],, [AC_DEFINE_UNQUOTED([PACKAGE], ["$PACKAGE"], [Name of package]) AC_DEFINE_UNQUOTED([VERSION], ["$VERSION"], [Version number of package])])dnl # Some tools Automake needs. AC_REQUIRE([AM_SANITY_CHECK])dnl AC_REQUIRE([AC_ARG_PROGRAM])dnl AM_MISSING_PROG([ACLOCAL], [aclocal-${am__api_version}]) AM_MISSING_PROG([AUTOCONF], [autoconf]) AM_MISSING_PROG([AUTOMAKE], [automake-${am__api_version}]) AM_MISSING_PROG([AUTOHEADER], [autoheader]) AM_MISSING_PROG([MAKEINFO], [makeinfo]) AC_REQUIRE([AM_PROG_INSTALL_SH])dnl AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl AC_REQUIRE([AC_PROG_MKDIR_P])dnl # For better backward compatibility. To be removed once Automake 1.9.x # dies out for good. For more background, see: # # AC_SUBST([mkdir_p], ['$(MKDIR_P)']) # We need awk for the "check" target (and possibly the TAP driver). The # system "awk" is bad on some platforms. AC_REQUIRE([AC_PROG_AWK])dnl AC_REQUIRE([AC_PROG_MAKE_SET])dnl AC_REQUIRE([AM_SET_LEADING_DOT])dnl _AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])], [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])], [_AM_PROG_TAR([v7])])]) _AM_IF_OPTION([no-dependencies],, [AC_PROVIDE_IFELSE([AC_PROG_CC], [_AM_DEPENDENCIES([CC])], [m4_define([AC_PROG_CC], m4_defn([AC_PROG_CC])[_AM_DEPENDENCIES([CC])])])dnl AC_PROVIDE_IFELSE([AC_PROG_CXX], [_AM_DEPENDENCIES([CXX])], [m4_define([AC_PROG_CXX], m4_defn([AC_PROG_CXX])[_AM_DEPENDENCIES([CXX])])])dnl AC_PROVIDE_IFELSE([AC_PROG_OBJC], [_AM_DEPENDENCIES([OBJC])], [m4_define([AC_PROG_OBJC], m4_defn([AC_PROG_OBJC])[_AM_DEPENDENCIES([OBJC])])])dnl AC_PROVIDE_IFELSE([AC_PROG_OBJCXX], [_AM_DEPENDENCIES([OBJCXX])], [m4_define([AC_PROG_OBJCXX], m4_defn([AC_PROG_OBJCXX])[_AM_DEPENDENCIES([OBJCXX])])])dnl ]) AC_REQUIRE([AM_SILENT_RULES])dnl dnl The testsuite driver may need to know about EXEEXT, so add the dnl 'am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This dnl macro is hooked onto _AC_COMPILER_EXEEXT early, see below. AC_CONFIG_COMMANDS_PRE(dnl [m4_provide_if([_AM_COMPILER_EXEEXT], [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl # POSIX will say in a future version that running "rm -f" with no argument # is OK; and we want to be able to make that assumption in our Makefile # recipes. So use an aggressive probe to check that the usage we want is # actually supported "in the wild" to an acceptable degree. # See automake bug#10828. # To make any issue more visible, cause the running configure to be aborted # by default if the 'rm' program in use doesn't match our expectations; the # user can still override this though. if rm -f && rm -fr && rm -rf; then : OK; else cat >&2 <<'END' Oops! Your 'rm' program seems unable to run without file operands specified on the command line, even when the '-f' option is present. This is contrary to the behaviour of most rm programs out there, and not conforming with the upcoming POSIX standard: Please tell bug-automake@gnu.org about your system, including the value of your $PATH and any error possibly output before this message. This can help us improve future automake versions. END if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then echo 'Configuration will proceed anyway, since you have set the' >&2 echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 echo >&2 else cat >&2 <<'END' Aborting the configuration process, to ensure you take notice of the issue. You can download and install GNU coreutils to get an 'rm' implementation that behaves properly: . If you want to complete the configuration process using your problematic 'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM to "yes", and re-run configure. END AC_MSG_ERROR([Your 'rm' program is bad, sorry.]) fi fi dnl The trailing newline in this macro's definition is deliberate, for dnl backward compatibility and to allow trailing 'dnl'-style comments dnl after the AM_INIT_AUTOMAKE invocation. See automake bug#16841. ]) dnl Hook into '_AC_COMPILER_EXEEXT' early to learn its expansion. Do not dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further dnl mangled by Autoconf and run in a shell conditional statement. m4_define([_AC_COMPILER_EXEEXT], m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])]) # When config.status generates a header, we must update the stamp-h file. # This file resides in the same directory as the config header # that is generated. The stamp files are numbered to have different names. # Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the # loop where config.status creates the headers, so we can generate # our stamp files there. AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK], [# Compute $1's index in $config_headers. _am_arg=$1 _am_stamp_count=1 for _am_header in $config_headers :; do case $_am_header in $_am_arg | $_am_arg:* ) break ;; * ) _am_stamp_count=`expr $_am_stamp_count + 1` ;; esac done echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) # Copyright (C) 2001-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_PROG_INSTALL_SH # ------------------ # Define $install_sh. AC_DEFUN([AM_PROG_INSTALL_SH], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl if test x"${install_sh+set}" != xset; then case $am_aux_dir in *\ * | *\ *) install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; *) install_sh="\${SHELL} $am_aux_dir/install-sh" esac fi AC_SUBST([install_sh])]) # Copyright (C) 2003-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # Check whether the underlying file-system supports filenames # with a leading dot. For instance MS-DOS doesn't. AC_DEFUN([AM_SET_LEADING_DOT], [rm -rf .tst 2>/dev/null mkdir .tst 2>/dev/null if test -d .tst; then am__leading_dot=. else am__leading_dot=_ fi rmdir .tst 2>/dev/null AC_SUBST([am__leading_dot])]) # Check to see how 'make' treats includes. -*- Autoconf -*- # Copyright (C) 2001-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_MAKE_INCLUDE() # ----------------- # Check whether make has an 'include' directive that can support all # the idioms we need for our automatic dependency tracking code. AC_DEFUN([AM_MAKE_INCLUDE], [AC_MSG_CHECKING([whether ${MAKE-make} supports the include directive]) cat > confinc.mk << 'END' am__doit: @echo this is the am__doit target >confinc.out .PHONY: am__doit END am__include="#" am__quote= # BSD make does it like this. echo '.include "confinc.mk" # ignored' > confmf.BSD # Other make implementations (GNU, Solaris 10, AIX) do it like this. echo 'include confinc.mk # ignored' > confmf.GNU _am_result=no for s in GNU BSD; do AM_RUN_LOG([${MAKE-make} -f confmf.$s && cat confinc.out]) AS_CASE([$?:`cat confinc.out 2>/dev/null`], ['0:this is the am__doit target'], [AS_CASE([$s], [BSD], [am__include='.include' am__quote='"'], [am__include='include' am__quote=''])]) if test "$am__include" != "#"; then _am_result="yes ($s style)" break fi done rm -f confinc.* confmf.* AC_MSG_RESULT([${_am_result}]) AC_SUBST([am__include])]) AC_SUBST([am__quote])]) # Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- # Copyright (C) 1997-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_MISSING_PROG(NAME, PROGRAM) # ------------------------------ AC_DEFUN([AM_MISSING_PROG], [AC_REQUIRE([AM_MISSING_HAS_RUN]) $1=${$1-"${am_missing_run}$2"} AC_SUBST($1)]) # AM_MISSING_HAS_RUN # ------------------ # Define MISSING if not defined so far and test if it is modern enough. # If it is, set am_missing_run to use it, otherwise, to nothing. AC_DEFUN([AM_MISSING_HAS_RUN], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl AC_REQUIRE_AUX_FILE([missing])dnl if test x"${MISSING+set}" != xset; then case $am_aux_dir in *\ * | *\ *) MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; *) MISSING="\${SHELL} $am_aux_dir/missing" ;; esac fi # Use eval to expand $SHELL if eval "$MISSING --is-lightweight"; then am_missing_run="$MISSING " else am_missing_run= AC_MSG_WARN(['missing' script is too old or missing]) fi ]) # Helper functions for option handling. -*- Autoconf -*- # Copyright (C) 2001-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_MANGLE_OPTION(NAME) # ----------------------- AC_DEFUN([_AM_MANGLE_OPTION], [[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])]) # _AM_SET_OPTION(NAME) # -------------------- # Set option NAME. Presently that only means defining a flag for this option. AC_DEFUN([_AM_SET_OPTION], [m4_define(_AM_MANGLE_OPTION([$1]), [1])]) # _AM_SET_OPTIONS(OPTIONS) # ------------------------ # OPTIONS is a space-separated list of Automake options. AC_DEFUN([_AM_SET_OPTIONS], [m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])]) # _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET]) # ------------------------------------------- # Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. AC_DEFUN([_AM_IF_OPTION], [m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) # Copyright (C) 1999-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_PROG_CC_C_O # --------------- # Like AC_PROG_CC_C_O, but changed for automake. We rewrite AC_PROG_CC # to automatically call this. AC_DEFUN([_AM_PROG_CC_C_O], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl AC_REQUIRE_AUX_FILE([compile])dnl AC_LANG_PUSH([C])dnl AC_CACHE_CHECK( [whether $CC understands -c and -o together], [am_cv_prog_cc_c_o], [AC_LANG_CONFTEST([AC_LANG_PROGRAM([])]) # Make sure it works both with $CC and with simple cc. # Following AC_PROG_CC_C_O, we do the test twice because some # compilers refuse to overwrite an existing .o file with -o, # though they will create one. am_cv_prog_cc_c_o=yes for am_i in 1 2; do if AM_RUN_LOG([$CC -c conftest.$ac_ext -o conftest2.$ac_objext]) \ && test -f conftest2.$ac_objext; then : OK else am_cv_prog_cc_c_o=no break fi done rm -f core conftest* unset am_i]) if test "$am_cv_prog_cc_c_o" != yes; then # Losing compiler, so override with the script. # FIXME: It is wrong to rewrite CC. # But if we don't then we get into trouble of one sort or another. # A longer-term fix would be to have automake use am__CC in this case, # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" CC="$am_aux_dir/compile $CC" fi AC_LANG_POP([C])]) # For backward compatibility. AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])]) # Copyright (C) 2001-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_RUN_LOG(COMMAND) # ------------------- # Run COMMAND, save the exit status in ac_status, and log it. # (This has been adapted from Autoconf's _AC_RUN_LOG macro.) AC_DEFUN([AM_RUN_LOG], [{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD ($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD (exit $ac_status); }]) # Check to make sure that the build environment is sane. -*- Autoconf -*- # Copyright (C) 1996-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_SANITY_CHECK # --------------- AC_DEFUN([AM_SANITY_CHECK], [AC_MSG_CHECKING([whether build environment is sane]) # Reject unsafe characters in $srcdir or the absolute working directory # name. Accept space and tab only in the latter. am_lf=' ' case `pwd` in *[[\\\"\#\$\&\'\`$am_lf]]*) AC_MSG_ERROR([unsafe absolute working directory name]);; esac case $srcdir in *[[\\\"\#\$\&\'\`$am_lf\ \ ]]*) AC_MSG_ERROR([unsafe srcdir value: '$srcdir']);; esac # Do 'set' in a subshell so we don't clobber the current shell's # arguments. Must try -L first in case configure is actually a # symlink; some systems play weird games with the mod time of symlinks # (eg FreeBSD returns the mod time of the symlink's containing # directory). if ( am_has_slept=no for am_try in 1 2; do echo "timestamp, slept: $am_has_slept" > conftest.file set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` if test "$[*]" = "X"; then # -L didn't work. set X `ls -t "$srcdir/configure" conftest.file` fi if test "$[*]" != "X $srcdir/configure conftest.file" \ && test "$[*]" != "X conftest.file $srcdir/configure"; then # If neither matched, then we have a broken ls. This can happen # if, for instance, CONFIG_SHELL is bash and it inherits a # broken ls alias from the environment. This has actually # happened. Such a system could not be considered "sane". AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken alias in your environment]) fi if test "$[2]" = conftest.file || test $am_try -eq 2; then break fi # Just in case. sleep 1 am_has_slept=yes done test "$[2]" = conftest.file ) then # Ok. : else AC_MSG_ERROR([newly created file is older than distributed files! Check your system clock]) fi AC_MSG_RESULT([yes]) # If we didn't sleep, we still need to ensure time stamps of config.status and # generated files are strictly newer. am_sleep_pid= if grep 'slept: no' conftest.file >/dev/null 2>&1; then ( sleep 1 ) & am_sleep_pid=$! fi AC_CONFIG_COMMANDS_PRE( [AC_MSG_CHECKING([that generated files are newer than configure]) if test -n "$am_sleep_pid"; then # Hide warnings about reused PIDs. wait $am_sleep_pid 2>/dev/null fi AC_MSG_RESULT([done])]) rm -f conftest.file ]) # Copyright (C) 2009-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_SILENT_RULES([DEFAULT]) # -------------------------- # Enable less verbose build rules; with the default set to DEFAULT # ("yes" being less verbose, "no" or empty being verbose). AC_DEFUN([AM_SILENT_RULES], [AC_ARG_ENABLE([silent-rules], [dnl AS_HELP_STRING( [--enable-silent-rules], [less verbose build output (undo: "make V=1")]) AS_HELP_STRING( [--disable-silent-rules], [verbose build output (undo: "make V=0")])dnl ]) case $enable_silent_rules in @%:@ ((( yes) AM_DEFAULT_VERBOSITY=0;; no) AM_DEFAULT_VERBOSITY=1;; *) AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1]);; esac dnl dnl A few 'make' implementations (e.g., NonStop OS and NextStep) dnl do not support nested variable expansions. dnl See automake bug#9928 and bug#10237. am_make=${MAKE-make} AC_CACHE_CHECK([whether $am_make supports nested variables], [am_cv_make_support_nested_variables], [if AS_ECHO([['TRUE=$(BAR$(V)) BAR0=false BAR1=true V=1 am__doit: @$(TRUE) .PHONY: am__doit']]) | $am_make -f - >/dev/null 2>&1; then am_cv_make_support_nested_variables=yes else am_cv_make_support_nested_variables=no fi]) if test $am_cv_make_support_nested_variables = yes; then dnl Using '$V' instead of '$(V)' breaks IRIX make. AM_V='$(V)' AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' else AM_V=$AM_DEFAULT_VERBOSITY AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY fi AC_SUBST([AM_V])dnl AM_SUBST_NOTMAKE([AM_V])dnl AC_SUBST([AM_DEFAULT_V])dnl AM_SUBST_NOTMAKE([AM_DEFAULT_V])dnl AC_SUBST([AM_DEFAULT_VERBOSITY])dnl AM_BACKSLASH='\' AC_SUBST([AM_BACKSLASH])dnl _AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl ]) # Copyright (C) 2001-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_PROG_INSTALL_STRIP # --------------------- # One issue with vendor 'install' (even GNU) is that you can't # specify the program used to strip binaries. This is especially # annoying in cross-compiling environments, where the build's strip # is unlikely to handle the host's binaries. # Fortunately install-sh will honor a STRIPPROG variable, so we # always use install-sh in "make install-strip", and initialize # STRIPPROG with the value of the STRIP variable (set by the user). AC_DEFUN([AM_PROG_INSTALL_STRIP], [AC_REQUIRE([AM_PROG_INSTALL_SH])dnl # Installed binaries are usually stripped using 'strip' when the user # run "make install-strip". However 'strip' might not be the right # tool to use in cross-compilation environments, therefore Automake # will honor the 'STRIP' environment variable to overrule this program. dnl Don't test for $cross_compiling = yes, because it might be 'maybe'. if test "$cross_compiling" != no; then AC_CHECK_TOOL([STRIP], [strip], :) fi INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" AC_SUBST([INSTALL_STRIP_PROGRAM])]) # Copyright (C) 2006-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_SUBST_NOTMAKE(VARIABLE) # --------------------------- # Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in. # This macro is traced by Automake. AC_DEFUN([_AM_SUBST_NOTMAKE]) # AM_SUBST_NOTMAKE(VARIABLE) # -------------------------- # Public sister of _AM_SUBST_NOTMAKE. AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) # Check how to create a tarball. -*- Autoconf -*- # Copyright (C) 2004-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_PROG_TAR(FORMAT) # -------------------- # Check how to create a tarball in format FORMAT. # FORMAT should be one of 'v7', 'ustar', or 'pax'. # # Substitute a variable $(am__tar) that is a command # writing to stdout a FORMAT-tarball containing the directory # $tardir. # tardir=directory && $(am__tar) > result.tar # # Substitute a variable $(am__untar) that extract such # a tarball read from stdin. # $(am__untar) < result.tar # AC_DEFUN([_AM_PROG_TAR], [# Always define AMTAR for backward compatibility. Yes, it's still used # in the wild :-( We should find a proper way to deprecate it ... AC_SUBST([AMTAR], ['$${TAR-tar}']) # We'll loop over all known methods to create a tar archive until one works. _am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none' m4_if([$1], [v7], [am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'], [m4_case([$1], [ustar], [# The POSIX 1988 'ustar' format is defined with fixed-size fields. # There is notably a 21 bits limit for the UID and the GID. In fact, # the 'pax' utility can hang on bigger UID/GID (see automake bug#8343 # and bug#13588). am_max_uid=2097151 # 2^21 - 1 am_max_gid=$am_max_uid # The $UID and $GID variables are not portable, so we need to resort # to the POSIX-mandated id(1) utility. Errors in the 'id' calls # below are definitely unexpected, so allow the users to see them # (that is, avoid stderr redirection). am_uid=`id -u || echo unknown` am_gid=`id -g || echo unknown` AC_MSG_CHECKING([whether UID '$am_uid' is supported by ustar format]) if test $am_uid -le $am_max_uid; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) _am_tools=none fi AC_MSG_CHECKING([whether GID '$am_gid' is supported by ustar format]) if test $am_gid -le $am_max_gid; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) _am_tools=none fi], [pax], [], [m4_fatal([Unknown tar format])]) AC_MSG_CHECKING([how to create a $1 tar archive]) # Go ahead even if we have the value already cached. We do so because we # need to set the values for the 'am__tar' and 'am__untar' variables. _am_tools=${am_cv_prog_tar_$1-$_am_tools} for _am_tool in $_am_tools; do case $_am_tool in gnutar) for _am_tar in tar gnutar gtar; do AM_RUN_LOG([$_am_tar --version]) && break done am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"' am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"' am__untar="$_am_tar -xf -" ;; plaintar) # Must skip GNU tar: if it does not support --format= it doesn't create # ustar tarball either. (tar --version) >/dev/null 2>&1 && continue am__tar='tar chf - "$$tardir"' am__tar_='tar chf - "$tardir"' am__untar='tar xf -' ;; pax) am__tar='pax -L -x $1 -w "$$tardir"' am__tar_='pax -L -x $1 -w "$tardir"' am__untar='pax -r' ;; cpio) am__tar='find "$$tardir" -print | cpio -o -H $1 -L' am__tar_='find "$tardir" -print | cpio -o -H $1 -L' am__untar='cpio -i -H $1 -d' ;; none) am__tar=false am__tar_=false am__untar=false ;; esac # If the value was cached, stop now. We just wanted to have am__tar # and am__untar set. test -n "${am_cv_prog_tar_$1}" && break # tar/untar a dummy directory, and stop if the command works. rm -rf conftest.dir mkdir conftest.dir echo GrepMe > conftest.dir/file AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar]) rm -rf conftest.dir if test -s conftest.tar; then AM_RUN_LOG([$am__untar /dev/null 2>&1 && break fi done rm -rf conftest.dir AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool]) AC_MSG_RESULT([$am_cv_prog_tar_$1])]) AC_SUBST([am__tar]) AC_SUBST([am__untar]) ]) # _AM_PROG_TAR putty-0.76/configure.ac0000644000175000017500000002007214072266316012052 00000000000000# To compile this into a configure script, you need: # * Autoconf 2.59c 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, 0.76) AC_CONFIG_FILES([Makefile]) AC_CONFIG_HEADERS([uxconfig.h:uxconfig.in]) AM_INIT_AUTOMAKE([-Wall foreign]) AC_PROG_INSTALL AC_PROG_RANLIB ifdef([AM_PROG_AR],[AM_PROG_AR]) AM_PROG_CC_C_O AC_PROG_CC_C99 # 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_ENABLE([git-commit], [AS_HELP_STRING([--disable-git-commit], [disable embedding current git HEAD in binaries])], [], [if test -d "$srcdir/.git"; then enable_git_commit=yes; else enable_git_commit=no; fi]) if test "x$enable_git_commit" = "xyes" -a ! -d "$srcdir/.git"; then AC_ERROR([Cannot --enable-git-commit when source tree is not a git checkout]) fi AM_CONDITIONAL(AUTO_GIT_COMMIT, [test "x$enable_git_commit" = "xyes"]) AC_ARG_WITH([gssapi], [AS_HELP_STRING([--without-gssapi], [disable GSSAPI support])], [], [with_gssapi=yes]) AC_ARG_WITH([quartz], [AS_HELP_STRING([--with-quartz], [build for the MacOS Quartz GTK back end])], [AC_DEFINE([OSX_GTK], [1], [Define if building with GTK for MacOS.]) with_quartz=yes], [with_quartz=no]) AM_CONDITIONAL([HAVE_QUARTZ],[test "x$with_quartz" = "xyes"]) 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', `2' or `3')]) 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 | 3 | any | no) ;; yes) gtk_version_desired="any" ;; *) AC_ERROR([Invalid GTK version specified]) esac AC_CHECK_HEADERS([utmpx.h],,,[ #include #include ]) # Look for GTK 3, GTK 2 and GTK 1, in descending order of preference. # If we can't find any, have the makefile only build the CLI programs. gtk=none case "$gtk_version_desired:$gtk" in 3:none | any:none) ifdef([AM_PATH_GTK_3_0],[AM_PATH_GTK_3_0([3.0.0], [gtk=3], [])], [AC_WARNING([generating configure script without GTK 3 autodetection])]) ;; esac 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` AC_SUBST(GTK_CFLAGS) AC_SUBST(GTK_LIBS) gtk=1 fi ]) ;; esac case "$gtk" in 1) # Add some manual #defines to make the GTK 1 headers work when # compiling in C99 mode. Left to themselves, they'll expect the # old-style pre-C99 GNU semantics of 'inline' and 'extern inline', # with the effect that they'll end up defining out-of-line # versions of the inlined functions in more than one translation # unit and cause a link failure. Override them to 'static inline', # which is safe. GTK_CFLAGS="$GTK_CFLAGS -DG_INLINE_FUNC='static inline' -DG_CAN_INLINE=1" esac AM_CONDITIONAL(HAVE_GTK, [test "$gtk" != "none"]) if test "$gtk" = "2" -o "$gtk" = "3"; 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 fstatat dirfd futimes setpwent endpwent getauxval elf_aux_info sysctlbyname]) AC_CHECK_DECLS([CLOCK_MONOTONIC], [], [], [[#include ]]) AC_CHECK_HEADERS([sys/auxv.h asm/hwcap.h sys/sysctl.h sys/types.h glob.h]) AC_SEARCH_LIBS([clock_gettime], [rt], [AC_DEFINE([HAVE_CLOCK_GETTIME],[],[Define if clock_gettime() is available])]) AC_CACHE_CHECK([for SO_PEERCRED and dependencies], [x_cv_linux_so_peercred], [ AC_COMPILE_IFELSE([ AC_LANG_PROGRAM([[ #define _GNU_SOURCE #include #include ]],[[ struct ucred cr; socklen_t crlen = sizeof(cr); return getsockopt(0, SOL_SOCKET, SO_PEERCRED, &cr, &crlen) + cr.pid + cr.uid + cr.gid; ]] )], AS_VAR_SET(x_cv_linux_so_peercred, yes), AS_VAR_SET(x_cv_linux_so_peercred, no) ) ]) AS_IF([test AS_VAR_GET(x_cv_linux_so_peercred) = yes], [AC_DEFINE([HAVE_SO_PEERCRED], [1], [Define if SO_PEERCRED works in the Linux fashion.])] ) if test "x$GCC" = "xyes"; then : AC_SUBST(WARNINGOPTS, ['-Wall -Wpointer-arith -Wvla']) else : AC_SUBST(WARNINGOPTS, []) fi AC_SEARCH_LIBS([pow], [m]) AC_OUTPUT if test "$gtk_version_desired" = "no"; then cat < date ifneq "$(Ndate)" "" read Date date set Epoch 17818 # update this at every release ifneq "$(Ndate)" "" in . do echo $(Ndate) | perl -ne 'use Time::Local; /(....)(..)(..)/ and print timegm(0,0,0,$$3,$$2-1,$$1) / 86400 - $(Epoch)' > days ifneq "$(Ndate)" "" read Days days # For any non-release, we're going to need the number of the prior # release, for putting in various places so as to get monotonic # comparisons with the surrounding actual releases. ifeq "$(RELEASE)" "" read Lastver putty/LATEST.VER # Set up the textual version strings for the docs build and installers. # We have one of these including the word 'PuTTY', and one without, # which are inconveniently capitalised differently. ifneq "$(RELEASE)" "" set Puttytextver PuTTY release $(RELEASE) ifneq "$(RELEASE)" "" set Textver Release $(RELEASE) ifneq "$(PRERELEASE)" "" set Puttytextver PuTTY pre-release $(PRERELEASE):$(Date).$(vcsid) ifneq "$(PRERELEASE)" "" set Textver Pre-release $(PRERELEASE):$(Date).$(vcsid) ifneq "$(SNAPSHOT)" "" set Puttytextver PuTTY development snapshot $(Date).$(vcsid) ifneq "$(SNAPSHOT)" "" set Textver Development snapshot $(Date).$(vcsid) ifeq "$(RELEASE)$(PRERELEASE)$(SNAPSHOT)" "" set Puttytextver PuTTY custom build $(Date).$(vcsid) ifeq "$(RELEASE)$(PRERELEASE)$(SNAPSHOT)" "" set Textver Custom build $(Date).$(vcsid) set Docmakever VERSION="$(Puttytextver)" # Set up the version string for use in the SSH connection greeting. # # We use $(Ndate) rather than $(Date) in the pre-release string to # make sure it's under 40 characters, which is a hard limit in the SSH # protocol spec (and enforced by a compile-time assertion in # version.c). ifneq "$(RELEASE)" "" set Sshver -Release-$(RELEASE) ifneq "$(PRERELEASE)" "" set Sshver -Prerelease-$(PRERELEASE):$(Ndate).$(vcsid) ifneq "$(SNAPSHOT)" "" set Sshver -Snapshot-$(Date).$(vcsid) ifeq "$(RELEASE)$(PRERELEASE)$(SNAPSHOT)" "" set Sshver -Custom-$(Date).$(vcsid) # Set up the filename suffix for the Unix source archive. ifneq "$(RELEASE)" "" set Uxarcsuffix -$(RELEASE) ifneq "$(PRERELEASE)" "" set Uxarcsuffix -$(PRERELEASE)~pre$(Ndate).$(vcsid) ifneq "$(SNAPSHOT)" "" set Uxarcsuffix -$(Lastver)-$(Date).$(vcsid) ifeq "$(RELEASE)$(PRERELEASE)$(SNAPSHOT)" "" set Uxarcsuffix -custom-$(Date).$(vcsid) # Set up the version number for the autoconf system. ifneq "$(RELEASE)" "" set Autoconfver $(RELEASE) ifneq "$(PRERELEASE)" "" set Autoconfver $(PRERELEASE)~pre$(Ndate).$(vcsid) ifneq "$(SNAPSHOT)" "" set Autoconfver $(Lastver)-$(Date).$(vcsid) ifeq "$(RELEASE)$(PRERELEASE)$(SNAPSHOT)" "" set Autoconfver Custom.$(Date).$(vcsid) # Set up the filenames for the Windows installers (minus extension, # which goes on later). ifneq "$(RELEASE)" "" set Isuffix $(RELEASE)-installer ifneq "$(PRERELEASE)" "" set Isuffix $(PRERELEASE)-pre$(Ndate)-installer ifneq "$(SNAPSHOT)" "" set Isuffix $(Date)-installer ifeq "$(RELEASE)$(PRERELEASE)$(SNAPSHOT)" "" set Isuffix custom-$(Date)-installer set Ifilename32 putty-$(Isuffix) set Ifilename64 putty-64bit-$(Isuffix) set Ifilenamea32 putty-arm32-$(Isuffix) set Ifilenamea64 putty-arm64-$(Isuffix) # Set up the Windows version resource info, for both the installers and # the individual programs. This must be a sequence of four 16-bit # integers compared lexicographically, and we define it as follows: # # For release X.YY: X.YY.0.0 # For a prerelease before the X.YY release: (X.YY-1).(DDDDD + 0x8000).0 # For a devel snapshot after the X.YY release: X.YY.DDDDD.0 # For a custom build: X.YY.DDDDD.1 # # where DDDDD is a representation of the build date, in the form of a # number of days since an epoch date. The epoch is reset at every # release (which, with 15 bits, gives us a comfortable 80-odd years # before it becomes vital to make another release to reset the count # :-). ifneq "$(RELEASE)" "" in . do echo $(RELEASE).0.0 > winver ifneq "$(PRERELEASE)" "" in . do perl -e 'printf "%s.%d.0", $$ARGV[0], 0x8000+$$ARGV[1]' $(Lastver) $(Days) > winver ifneq "$(SNAPSHOT)" "" in . do perl -e 'printf "%s.%d.0", $$ARGV[0], $$ARGV[1]' $(Lastver) $(Days) > winver ifeq "$(RELEASE)$(PRERELEASE)$(SNAPSHOT)" "" in . do perl -e 'printf "%s.%d.1", $$ARGV[0], $$ARGV[1]' $(Lastver) $(Days) > winver in . do perl -pe 'y!.!,!' winver > winvercommas read Winver winver read Winvercommas winvercommas # Write out a version.h that contains the real version number. in putty do echo '/* Generated by automated build script */' > version.h ifneq "$(RELEASE)" "" in putty do echo '$#define RELEASE $(RELEASE)' >> version.h ifneq "$(PRERELEASE)" "" in putty do echo '$#define PRERELEASE $(PRERELEASE)' >> version.h ifneq "$(SNAPSHOT)" "" in putty do echo '$#define SNAPSHOT' >> version.h in putty do echo '$#define TEXTVER "$(Textver)"' >> version.h in putty do echo '$#define SSHVER "$(Sshver)"' >> version.h in putty do echo '$#define BINARY_VERSION $(Winvercommas)' >> version.h in putty do echo '$#define SOURCE_COMMIT "$(vcsfullid)"' >> version.h # Set up the extra arguments for the main Windows nmake command. The # user can define XFLAGS and MAKEARGS on the bob command line, to pass # in extra compile and make options respectively (e.g. to do a # debugging or Minefield build). set Makeargs ifneq "$(XFLAGS)" "" set Makeargs $(Makeargs) XFLAGS="$(XFLAGS)" ifneq "$(MAKEARGS)" "" set Makeargs $(Makeargs) $(MAKEARGS) in putty do ./mksrcarc.sh in putty do ./mkunxarc.sh '$(Autoconfver)' '$(Uxarcsuffix)' $(Docmakever) in putty do perl mkfiles.pl in putty/doc do make $(Docmakever) putty.chm -j$(nproc) delegate - # Run the test suite, under self-delegation so that we don't leave any # cruft lying around. This involves doing a build of the Unix tools # (which is a useful double-check anyway to pick up any build failures) in putty do ./mkauto.sh in putty do ./configure CC=clang CFLAGS="-fsanitize=address -fsanitize=leak" in putty do make -j$(nproc) in putty do python3 test/cryptsuite.py enddelegate # Windowsify LICENCE, since it's going in the Windows installers. in putty do perl -i~ -pe 'y/\015//d;s/$$/\015/' LICENCE # Some gratuitous theming for the MSI installer UI. in putty/icons do make -j$(nproc) in putty do ./windows/make_install_images.sh mkdir putty/windows/build32 mkdir putty/windows/build64 mkdir putty/windows/buildold mkdir putty/windows/abuild32 mkdir putty/windows/abuild64 # Build the binaries to go in the installers, in both 32- and 64-bit # flavours. # # For the 32-bit ones, we set a subsystem version of 5.01, which # allows the resulting files to still run on Windows XP. in putty/windows with clangcl32 do Platform=x86 make -f Makefile.clangcl BUILDDIR=build32/ SUBSYSVER=,5.01 $(Makeargs) all -j$(nproc) in putty/windows with clangcl64 do Platform=x64 make -f Makefile.clangcl BUILDDIR=build64/ $(Makeargs) all -j$(nproc) # Build experimental Arm Windows binaries. in putty/windows with clangcl_a32 do Platform=arm make -f Makefile.clangcl BUILDDIR=abuild32/ SUBSYSVER=,5.01 $(Makeargs) all -j$(nproc) in putty/windows with clangcl_a64 do Platform=arm64 make -f Makefile.clangcl BUILDDIR=abuild64/ $(Makeargs) all -j$(nproc) # Remove Windows binaries for the test programs we don't want to ship, # like testcrypt.exe. (But we still _built_ them, to ensure the build # worked.) in putty/windows do make -f Makefile.clangcl BUILDDIR=build32/ cleantestprogs in putty/windows do make -f Makefile.clangcl BUILDDIR=build64/ cleantestprogs in putty/windows do make -f Makefile.clangcl BUILDDIR=abuild32/ cleantestprogs in putty/windows do make -f Makefile.clangcl BUILDDIR=abuild64/ cleantestprogs # Code-sign the Windows binaries, if the local bob config provides a # script to do so in a cross-compiling way. We assume here that the # script accepts an -i option to provide a 'more info' URL, an # optional -n option to provide a program name, and an -N option to # take the program name from an .exe's version resource, and that it # can accept multiple .exe or .msi filename arguments and sign them # all in place. ifneq "$(cross_winsigncode)" "" in putty/windows do $(cross_winsigncode) -N -i https://www.chiark.greenend.org.uk/~sgtatham/putty/ build*/*.exe abuild*/*.exe # Make a preliminary set of cryptographic checksums giving the hashes # of these versions of the binaries. We'll make the rest below. in putty do for hash in md5 sha1 sha256 sha512; do for dir_plat in "build32 w32" "build64 w64" "abuild32 wa32" "abuild64 wa64"; do set -- $$dir_plat; (cd windows/$$1 && $${hash}sum *.exe | sed 's!\( \+\)!\1'$$2'/!;s!$$! (installer version)!') >> $${hash}sums.installer; done; done # Build a WiX MSI installer, for each build flavour. in putty/windows with wixonlinux do candle -arch x86 -dRealPlatform=x86 -dDllOk=yes -dBuilddir=build32/ -dWinver="$(Winver)" -dPuttytextver="$(Puttytextver)" installer.wxs && light -ext WixUIExtension -ext WixUtilExtension -sval installer.wixobj -o installer32.msi -spdb in putty/windows with wixonlinux do candle -arch x64 -dRealPlatform=x64 -dDllOk=yes -dBuilddir=build64/ -dWinver="$(Winver)" -dPuttytextver="$(Puttytextver)" installer.wxs && light -ext WixUIExtension -ext WixUtilExtension -sval installer.wixobj -o installer64.msi -spdb in putty/windows with wixonlinux do candle -arch x64 -dRealPlatform=Arm -dDllOk=no -dBuilddir=abuild32/ -dWinver="$(Winver)" -dPuttytextver="$(Puttytextver)" installer.wxs && light -ext WixUIExtension -ext WixUtilExtension -sval installer.wixobj -o installera32.msi -spdb in putty/windows with wixonlinux do candle -arch x64 -dRealPlatform=Arm64 -dDllOk=no -dBuilddir=abuild64/ -dWinver="$(Winver)" -dPuttytextver="$(Puttytextver)" installer.wxs && light -ext WixUIExtension -ext WixUtilExtension -sval installer.wixobj -o installera64.msi -spdb # Change the width field for our dialog background image so that it # doesn't stretch across the whole dialog. (WiX's default one does; we # replace it with a narrow one so that the text to the right of it # shows up on system default background colour, meaning that # high-contrast mode doesn't make the text white on white. But that # means we also have to modify the width field, and there's nothing in # WiX's source syntax to make that happen.) # # Also bodge the platform fields for the Windows on Arm installers, # since WiX 3 doesn't understand Arm platform names itself. in putty/windows do ./msifixup.py installer32.msi --dialog-bmp-width=123 in putty/windows do ./msifixup.py installer64.msi --dialog-bmp-width=123 in putty/windows do ./msifixup.py installera32.msi --dialog-bmp-width=123 --platform=Arm in putty/windows do ./msifixup.py installera64.msi --dialog-bmp-width=123 --platform=Arm64 # Sign the Windows installers. ifneq "$(cross_winsigncode)" "" in putty/windows do $(cross_winsigncode) -i https://www.chiark.greenend.org.uk/~sgtatham/putty/ -n "PuTTY Installer" installer32.msi installer64.msi installera32.msi installera64.msi # Delete the binaries and resource files from the build directories, # so that we can rebuild them differently. in putty/windows/build32 do rm -f *.exe *.res *.rcpp in putty/windows/build64 do rm -f *.exe *.res *.rcpp in putty/windows/abuild32 do rm -f *.exe *.res *.rcpp in putty/windows/abuild64 do rm -f *.exe *.res *.rcpp # Build the standalone binaries, in both 32- and 64-bit flavours. # These differ from the previous set in that they embed the help file. in putty/windows with clangcl32 do Platform=x86 make -f Makefile.clangcl BUILDDIR=build32/ RCFL=-DEMBED_CHM SUBSYSVER=,5.01 $(Makeargs) all -j$(nproc) in putty/windows with clangcl64 do Platform=x64 make -f Makefile.clangcl BUILDDIR=build64/ RCFL=-DEMBED_CHM $(Makeargs) all -j$(nproc) in putty/windows with clangcl_a32 do Platform=arm make -f Makefile.clangcl BUILDDIR=abuild32/ RCFL=-DEMBED_CHM SUBSYSVER=,5.01 $(Makeargs) all -j$(nproc) in putty/windows with clangcl_a64 do Platform=arm64 make -f Makefile.clangcl BUILDDIR=abuild64/ RCFL=-DEMBED_CHM $(Makeargs) all -j$(nproc) # Build the 'old' binaries, which should still run on all 32-bit # versions of Windows back to Win95 (but not Win32s). These link # against Visual Studio 2003 libraries (the more modern versions # assume excessively modern Win32 API calls to be available), specify # a subsystem version of 4.0, and compile with /arch:IA32 to prevent # the use of modern CPU features like MMX which older machines also # might not have. # # There's no installer to go with these, so they must also embed the # help file. in putty/windows with clangcl32_2003 do Platform=x86 make -f Makefile.clangcl BUILDDIR=buildold/ RCFL=-DEMBED_CHM $(Makeargs) CCTARGET=i386-pc-windows-msvc13.0.0 SUBSYSVER=,4.0 EXTRA_windows=wincrt0.obj EXTRA_console=crt0.obj EXTRA_libs=libcpmt.lib XFLAGS="/arch:IA32 -Wno-pragma-pack" all -j$(nproc) # Remove test programs again. in putty/windows do make -f Makefile.clangcl BUILDDIR=build32/ cleantestprogs in putty/windows do make -f Makefile.clangcl BUILDDIR=build64/ cleantestprogs in putty/windows do make -f Makefile.clangcl BUILDDIR=abuild32/ cleantestprogs in putty/windows do make -f Makefile.clangcl BUILDDIR=abuild64/ cleantestprogs in putty/windows do make -f Makefile.clangcl BUILDDIR=buildold/ cleantestprogs # Code-sign the standalone versions of the binaries. ifneq "$(cross_winsigncode)" "" in putty/windows do $(cross_winsigncode) -N -i https://www.chiark.greenend.org.uk/~sgtatham/putty/ build*/*.exe abuild*/*.exe in putty/doc do make mostlyclean in putty/doc do make $(Docmakever) -j$(nproc) in putty/windows/buildold do zip -k -j putty.zip `ls *.exe | grep -v puttytel` ../../doc/putty.chm in putty/windows/build32 do zip -k -j putty.zip `ls *.exe | grep -v puttytel` ../../doc/putty.chm in putty/windows/build64 do zip -k -j putty.zip `ls *.exe | grep -v puttytel` ../../doc/putty.chm in putty/windows/abuild32 do zip -k -j putty.zip `ls *.exe | grep -v puttytel` ../../doc/putty.chm in putty/windows/abuild64 do zip -k -j putty.zip `ls *.exe | grep -v puttytel` ../../doc/putty.chm in putty/doc do zip puttydoc.zip *.html # Deliver the actual PuTTY release directory into a subdir `putty'. deliver putty/windows/buildold/*.exe putty/w32old/$@ deliver putty/windows/buildold/putty.zip putty/w32old/$@ deliver putty/windows/build32/*.exe putty/w32/$@ deliver putty/windows/build32/putty.zip putty/w32/$@ deliver putty/windows/build64/*.exe putty/w64/$@ deliver putty/windows/build64/putty.zip putty/w64/$@ deliver putty/windows/installer32.msi putty/w32/$(Ifilename32).msi deliver putty/windows/installer64.msi putty/w64/$(Ifilename64).msi deliver putty/windows/installera32.msi putty/wa32/$(Ifilenamea32).msi deliver putty/windows/installera64.msi putty/wa64/$(Ifilenamea64).msi deliver putty/windows/abuild32/*.exe putty/wa32/$@ deliver putty/windows/abuild32/putty.zip putty/wa32/$@ deliver putty/windows/abuild64/*.exe putty/wa64/$@ deliver putty/windows/abuild64/putty.zip putty/wa64/$@ deliver putty/doc/puttydoc.zip putty/$@ deliver putty/doc/putty.chm putty/$@ deliver putty/doc/puttydoc.txt putty/$@ deliver putty/doc/*.html putty/htmldoc/$@ deliver putty/putty-src.zip putty/$@ deliver putty/*.tar.gz putty/$@ # Deliver the map files alongside the `proper' release deliverables. deliver putty/windows/buildold/*.map maps/w32old/$@ deliver putty/windows/build32/*.map maps/w32/$@ deliver putty/windows/build64/*.map maps/w64/$@ deliver putty/windows/abuild32/*.map maps/wa32/$@ deliver putty/windows/abuild64/*.map maps/wa64/$@ # Deliver sign.sh, so that whoever has just built PuTTY (the # snapshot scripts or me, depending) can conveniently sign it with # whatever key they want. deliver putty/sign.sh $@ # Create files of cryptographic checksums, which will be signed along # with the files they verify. We've provided MD5 checksums for a # while, but now MD5 is looking iffy, we're expanding our selection. # # Creating these files is most easily done in the destination # directory, where all the files we're delivering are already in their # final relative layout. in . do pwd > builddir read Builddir builddir in-dest putty do a=`\find * -type f -print`; for hash in md5 sha1 sha256 sha512; do ($${hash}sum $$a; echo; cat $(Builddir)/putty/$${hash}sums.installer) > $${hash}sums; done # And construct .htaccess files. One in the top-level directory, # setting the MIME types for Windows help files and providing an # appropriate link to the source archive: in-dest putty do echo "AddType application/octet-stream .chm" >> .htaccess in-dest putty do set -- putty*.tar.gz; for k in '' .gpg; do echo RedirectMatch temp '(.*/)'putty.tar.gz$$k\$$ '$$1'"$$1$$k" >> .htaccess; done # And one in each binary directory, providing links for the installers. in-dest putty do for params in "w32 putty-installer" "w64 putty-64bit-installer" "wa32 putty-arm32-installer" "wa64 putty-arm64-installer"; do (set -- $$params; subdir=$$1; installername=$$2; cd $$subdir && for ext in msi exe; do set -- putty*installer.$$ext; if test -f $$1; then for k in '' .gpg; do echo RedirectMatch temp '(.*/)'$${installername}.$$ext$$k\$$ '$$1'"$$1$$k" >> .htaccess; done; fi; done); done putty-0.76/Buildscr.cv0000644000175000017500000000234114072266307011664 00000000000000# -*- 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.mgw 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 $(vcsfullid) > revision.txt deliver putty/revision.txt $@ deliver putty/cov-int.tar.gz $@ putty-0.76/CHECKLST.txt0000644000175000017500000002331514072266307011530 00000000000000Checklists for PuTTY administrative procedures ============================================== Going into pre-release stabilisation ------------------------------------ When we begin to work towards a release and want to enable pre-releases on the website: - Make a branch whose tip will be the current state of the pre-release. Regardless of whether the branch is from master or from a prior release branch, the name of the branch must now be in the form 'pre-X.YZ', or else the website will fail to link to it properly in gitweb and the build script will check out the wrong thing. - Edit ~/adm/puttysnap.sh on my build machine to set $prerelver correctly. - Edit ~/adm/puttysnap.sh on the master machine to enable pre-release builds, by changing the 'if false' to 'if true'. - Put the website into pre-release mode, by defining prerel_version() in components/Base.mc to return the upcoming version number. Also add a news announcement in components/news. (Previous naming convention has been to name it in the form 'X.YZ-pre.mi'.) Things to do during the branch-stabilisation period: - Go through the source (including the documentation), and the website, and review anything tagged with a comment containing the word XXX-REVIEW-BEFORE-RELEASE. (Any such comments should state clearly what needs to be done.) - Do some testing of the Windows version with Minefield (you can build a Minefield version using 'bob . XFLAGS=-DMINEFIELD'), and of the Unix version with valgrind and/or Address Sanitiser. In particular, any headline features for the release should get a workout with memory checking enabled! Making a release candidate build -------------------------------- - Make a directory to hold all the release paraphernalia. I usually call it ~/src/putty/X.YZ (where X.YZ will stand throughout for the version number). - Inside that directory, clone the PuTTY git repository to a subdirectory ~/src/putty/X.YZ/putty. Here you can make release- related commits and tags tentatively, and keep them out of the way of any 'git push' you might still be doing in other checkouts. - Double-check that we have removed anything tagged with a comment containing the words XXX-REMOVE-BEFORE-RELEASE or XXX-REVIEW-BEFORE-RELEASE. ('git grep XXX-RE' should only show up hits in this file itself.) - Now update the version numbers and the transcripts in the docs, by checking out the release branch in the release-specific checkout and running ./release.pl --version=X.YZ --setver Then check that the resulting automated git commit has updated the version number in the following places: * putty/LATEST.VER * putty/doc/plink.but * putty/doc/pscp.but and also check that it has reset the definition of 'Epoch' in Buildscr. - Make the release tag, pointing at the version-update commit we just generated. - If the release is on a branch (which I expect it generally will be), merge that branch to master. - Make a release-candidate build from the release tag, and put the build.out and build.log files somewhere safe. Normally I store these inside the ~/src/putty/X.YZ directory, alongside the git checkout at ~/src/putty/X.YZ/putty, so I'll sit in that checkout directory and run a command like bob -o ../build-X.YZ-rcN.out -l ../build-X.YZ-rcN.log -c X.YZ . RELEASE=X.YZ This should generate a basically valid release directory as `build-X.YZ-rcN.out/putty', and provide link maps and sign.sh alongside that. - Double-check in build-X.YZ-rcN.log that the release was built from the right git commit. - Make a preliminary gpg signature, but don't run the full release- signing procedure. (We use the presence of a full set of GPG signatures to distinguish _abandoned_ release candidates from the one that ended up being the release.) In the 'build.X.YZ-rcN.out' directory, run sh sign.sh -r -p putty which will generate a clearsigned file called sha512sums-preliminary.gpg _outside_ the 'putty' subdirectory. - For my own safety, make the release candidate build read-only. chmod -R a-w build-X.YZ-rcN.{out,log} - Now do some checking of the release binaries, and pass them to the rest of the team to do some as well. Do at least these things: * make sure they basically work * check they report the right version number * if there's any easily observable behaviour difference between the release branch and master, arrange to observe it * test that the Windows installer installs successfully + on x86 and Arm, and test that putty.exe runs in both cases * test that the Unix source tarball unpacks and builds + on at least a reasonably current stable Linux distro, and also try Debian sid + test-build with all of GTK 1, 2 and 3 + test-build with -DNOT_X_WINDOWS * feed the release-candidate source to Coverity and make sure it didn't turn up any last-minute problems * make sure we have a clean run of sctest * do some testing on a system with a completely clean slate (no prior saved session data) Preparing to make the release ----------------------------- - Write a release announcement (basically a summary of the changes since the last release). Check the draft version into the putty-aux repository, so the whole team can help wordsmith it if they want to. - Update the website, in a local checkout: * Write a release file in components/releases which identifies the new version, a section for the Changes page, and a news announcement for the front page. + The one thing this can't yet contain is the release date; that has to be put in at the last minute, when the release goes live. Fill in 'FIXME', for the moment. * Disable the pre-release sections of the website (if previously enabled), by editing prerel_version() in components/Base.mc to return undef. - Update the wishlist, in a local checkout: * If there are any last-minute wishlist entries (e.g. security vulnerabilities fixed in the new release), write entries for them. * If any other bug fixes have been cherry-picked to the release branch (so that the wishlist mechanism can't automatically mark them as fixed in the new release), add appropriate Fixed-in headers for those. - Sign the release in full. In the `build-X.YZ-rcN.out' directory, re-verify that the preliminary signed checksums file has a correct signature on it and also matches the files you're about to sign for real: gpg -d sha512sums-preliminary.gpg | (cd putty; grep -vF ' (installer version)' | grep . | sha512sum -c) If the combined output of that pipeline reports both a good signature (from the release key) and a successful verification of all the sha512sums, then all is well and you can do the full signing (not forgetting that the directory will have been readonly during the last-minute testing period): chmod -R u+w putty sh sign.sh -r putty # and enter the release key passphrase chmod -R a-w putty The actual release procedure ---------------------------- Once all the above preparation is done and the release has been built locally, this is the procedure for putting it up on the web. - Make a final adjustment to your local website changes, filling in the release date in components/releases/X.YZ.mi. - Upload the release itself and its link maps to everywhere it needs to be, by running this in the build-X.YZ-rcN.out directory: ../putty/release.pl --version=X.YZ --upload - Check that downloads via version-numbered URLs all work: ../putty/release.pl --version=X.YZ --precheck If this has trouble accessing chiark's ftp server, that is unfortunately normal; add --no-ftp and try again. - Switch the 'latest' links over to the new release: * Update the HTTP redirect at the:www/putty/htaccess . * Update the FTP symlink at chiark:ftp/putty-latest . - Now verify that downloads via the 'latest' URLs are all redirected correctly and work: ../putty/release.pl --version=X.YZ --postcheck - Push all the git repositories: * run 'git push' in the website checkout * run 'git push' in the wishlist checkout * push from the main PuTTY checkout. Typically this one will be pushing both the release tag and an update to the master branch, plus removing the pre-release branch, so you'll want some commands along these lines: git push origin master # update the master branch git push origin --tags # should push the new release tag git push origin :pre-X.YZ # delete the pre-release branch - Run ~/adm/puttyweb.sh on thyestes to update the website after all those git pushes. - Check that the unpublished website on thyestes looks sensible. - Run webupdate, so that all the changes on thyestes propagate to chiark. Important to do this _before_ announcing that the release 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 that the links from its latest.html point into that subdirectory. - Announce the release! + Construct a release announcement email whose message body is the announcement written above, and which includes the following headers: * Reply-To: * Subject: PuTTY X.YZ is released + Mail that release announcement to . + Post it to comp.security.ssh. + Mention it in on mono. - Edit the master ~/adm/puttysnap.sh to disable pre-release builds, if they were previously enabled. - Relax (slightly). putty-0.76/LATEST.VER0000644000175000017500000000000514072266307011130 000000000000000.76 putty-0.76/LICENCE0000644000175000017500000000270114072266307010550 00000000000000PuTTY is copyright 1997-2021 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, Christopher Staite, Lorenz Diener, Christian Brabandt, Jeff Smith, Pavel Kryukov, Maxim Kuznetsov, Svyatoslav Kuzmich, Nico Williams, Viktor Dukhovni, Josh Dersch, Lars Brinkhoff, 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: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. putty-0.76/README0000644000175000017500000001474614072266307010457 00000000000000This is the README for the source archive of PuTTY, a free Windows and Unix Telnet and SSH client. If you want to rebuild PuTTY from source, we provide a variety of Makefiles and equivalents. (If you have fetched the source from Git, you'll have to generate the Makefiles yourself -- see below.) There are various compile-time directives that you can use to disable or modify certain features; it may be necessary to do this in some environments. They are documented in `Recipe', and in comments in many of the generated Makefiles. For building on Windows: - windows/Makefile.vc is for command-line builds on MS Visual C++ systems. Change into the `windows' subdirectory and type `nmake -f Makefile.vc' to build all the PuTTY binaries. As of 2017, we successfully compile PuTTY with both Visual Studio 7 (2003) and Visual Studio 14 (2015), so our guess is that it will probably build with versions in between those as well. (The binaries from Visual Studio 14 are only compatible with Windows XP and up. Binaries from Visual Studio 7 ought to work with anything from Windows 95 onward.) - Inside the windows/MSVC subdirectory are MS Visual Studio project files for doing GUI-based builds of the various PuTTY utilities. These have been tested on Visual Studio 7 and 10. You should be able to build each PuTTY utility by loading the corresponding .dsp file in Visual Studio. For example, MSVC/putty/putty.dsp builds PuTTY itself, MSVC/plink/plink.dsp builds Plink, and so on. - windows/Makefile.mgw is for MinGW / Cygwin installations. Type `make -f Makefile.mgw' while in the `windows' subdirectory to build all the PuTTY binaries. MinGW and friends can lag behind other toolchains in their support for the Windows API. Compile-time levers are provided to exclude some features; the defaults are set appropriately for the 'mingw-w64' cross-compiler provided with Ubuntu 14.04. If you are using an older toolchain, you may need to exclude more features; alternatively, you may find that upgrading to a recent version of the 'w32api' package helps. - windows/Makefile.lcc is for lcc-win32. Type `make -f Makefile.lcc' while in the `windows' subdirectory. (You will probably need to specify COMPAT=-DNO_MULTIMON.) - Inside the windows/DEVCPP subdirectory are Dev-C++ project files for doing GUI-based builds of the various PuTTY utilities. The PuTTY team actively use Makefile.vc (with VC7/10) and Makefile.mgw (with mingw32), so we'll probably notice problems with those 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. - 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, any of Gtk+-1.2, Gtk+-2.0, and Gtk+-3.0 should be supported. If you have more than one installed, you can manually specify which one you want by giving the option '--with-gtk=N' to the configure script where N is 1, 2, or 3. (The default is the newest available, of course.) In the absence of any Gtk version, 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 Git 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.mgw 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 Git 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 . The PuTTY home web site is https://www.chiark.greenend.org.uk/~sgtatham/putty/ If you want to send bug reports or feature requests, please read the Feedback section of the web site before doing so. Sending one-line reports saying `it doesn't work' will waste your time as much as ours. See the file LICENCE for the licence conditions. putty-0.76/Recipe0000644000175000017500000004327614072266307010731 00000000000000# -*- makefile -*- # # This file describes which PuTTY programs are made up from which # object and resource files. It is processed into the various # Makefiles by means of a Perl script. Makefile changes should # really be made by editing this file and/or the Perl script, not # by editing the actual Makefiles. # ------------------------------------------------------------ # Top-level configuration. # Overall project name. !name putty # Locations and types of output Makefiles. !makefile clangcl windows/Makefile.clangcl !makefile vc windows/Makefile.vc !makefile vcproj windows/MSVC !makefile cygwin windows/Makefile.mgw !makefile lcc windows/Makefile.lcc !makefile gtk unix/Makefile.gtk !makefile unix unix/Makefile.ux !makefile am Makefile.am !makefile devcppproj windows/DEVCPP !makefile vstudio10 windows/VS2010 !makefile vstudio12 windows/VS2012 # Source directories. !srcdir charset/ !srcdir windows/ !srcdir unix/ # Help text added to the top of each Makefile, with /D converted # into -D as appropriate for the particular Makefile. !begin help # # Extra options you can set: # # - COMPAT=/DAUTO_WINSOCK (Windows only) # Causes PuTTY to assume that includes its own WinSock # header file, so that it won't try to include . # # - COMPAT=/DWINSOCK_TWO (Windows only) # Causes the PuTTY utilities to include instead of # , except Plink which _needs_ WinSock 2 so it already # does this. # # - COMPAT=/DNO_SECURITY (Windows only) # Disables use of , which is not available with some # development environments (such as very old versions of the # mingw/Cygwin GNU toolchain). This has the following effects: # - Pageant won't care about the local user ID of processes # accessing it; a version of Pageant built with this option # will therefore refuse to run under NT-series OSes on # security grounds (although it will run fine on Win95-series # OSes where there is no access control anyway). # - SSH connection sharing is disabled. # - There is no support for restriction of the process ACLs. # # - COMPAT=/DNO_MULTIMON (Windows only) # Disables PuTTY's use of , which is not available # with some development environments. This means that PuTTY's # full-screen mode (configurable to work on Alt-Enter) will # not behave usefully in a multi-monitor environment. # # - COMPAT=/DNO_HTMLHELP (Windows only) # Disables PuTTY's use of , which is not available # with some development environments. # # If you don't have this header, you may be able to use the copy # supplied with HTML Help Workshop. # # - RCFL=/DNO_MANIFESTS (Windows only) # Disables inclusion of XML application manifests in the PuTTY # binaries. This may be necessary to build for 64-bit Windows; # the manifests are only included to use the XP GUI style on # Windows XP, and the architecture tags are a lie on 64-bit. # # - COMPAT=/DNO_IPV6 # Disables PuTTY's ability to make IPv6 connections, enabling # it to compile under development environments which do not # support IPv6 in their header files. # # - COMPAT=/DNO_GSSAPI # Disables PuTTY's ability to use GSSAPI functions for # authentication and key exchange. # # - COMPAT=/DSTATIC_GSSAPI # Causes PuTTY to try to link statically against the GSSAPI # library instead of the default of doing it at run time. # # - COMPAT=/DMSVC4 (Windows only) # - RCFL=/DMSVC4 # Makes a couple of minor changes so that PuTTY compiles using # MSVC 4. You will also need /DNO_SECURITY and /DNO_MULTIMON. # # - COMPAT=/DNO_SECUREZEROMEMORY (Windows only) # Disables PuTTY's use of SecureZeroMemory(), which is missing # from some environments' header files. # # - XFLAGS=/DDEBUG # Causes PuTTY to enable internal debugging. # # - XFLAGS=/DMALLOC_LOG # Causes PuTTY to emit a file called putty_mem.log, logging every # memory allocation and free, so you can track memory leaks. # # - XFLAGS=/DMINEFIELD (Windows only) # Causes PuTTY to use a custom memory allocator, similar in # concept to Electric Fence, in place of regular malloc(). Wastes # huge amounts of RAM, but should cause heap-corruption bugs to # show up as GPFs at the point of failure rather than appearing # later on as second-level damage. # # - XFLAGS=/DFUZZING # Builds a version of PuTTY with some tweaks to make fuzz testing # easier: the SSH random number generator is replaced by one that # always returns the same thing. Note that this makes SSH # completely insecure -- a FUZZING build should never be used to # connect to a real server. !end # ------------------------------------------------------------ # Additional text added verbatim to each individual Makefile. !cflags am version !begin am if AUTO_GIT_COMMIT BUILT_SOURCES = empty.h CLEANFILES = empty.h libversion_a_CFLAGS += -DSOURCE_COMMIT=\"`git --git-dir=$(srcdir)/.git rev-parse HEAD 2>/dev/null`\" empty.h: $(allsources) echo '/* Empty file touched by automake makefile to force rebuild of version.o */' >$@ endif # Run the cryptsuite tests as part of 'make check'. Override # PUTTY_TESTCRYPT so that cryptsuite will take the testcrypt binary # from the build directory instead of the source directory, in case # this is an out-of-tree build. check-local: testcrypt PUTTY_TESTCRYPT=./testcrypt $(srcdir)/test/cryptsuite.py !end !begin >empty.h /* Empty file touched by automake makefile to force rebuild of version.o */ !end !begin vc vars CFLAGS = $(CFLAGS) /DHAS_GSSAPI !end !begin clangcl vars CFLAGS += /DHAS_GSSAPI !end # `make install' target for Unix. !begin gtk install: mkdir -p $(DESTDIR)$(bindir) $(DESTDIR)$(man1dir) $(INSTALL_PROGRAM) -m 755 pageant $(DESTDIR)$(bindir)/pageant $(INSTALL_PROGRAM) -m 755 plink $(DESTDIR)$(bindir)/plink $(INSTALL_PROGRAM) -m 755 pscp $(DESTDIR)$(bindir)/pscp $(INSTALL_PROGRAM) -m 755 psftp $(DESTDIR)$(bindir)/psftp $(INSTALL_PROGRAM) -m 755 pterm $(DESTDIR)$(bindir)/pterm if test -n "$(UTMP_GROUP)"; then \ chgrp $(UTMP_GROUP) $(DESTDIR)$(bindir)/pterm && \ chmod 2755 $(DESTDIR)$(bindir)/pterm; \ elif test -n "$(UTMP_USER)"; then \ chown $(UTMP_USER) $(DESTDIR)$(bindir)/pterm && \ chmod 4755 $(DESTDIR)$(bindir)/pterm; \ fi $(INSTALL_PROGRAM) -m 755 putty $(DESTDIR)$(bindir)/putty $(INSTALL_PROGRAM) -m 755 puttygen $(DESTDIR)$(bindir)/puttygen $(INSTALL_PROGRAM) -m 755 puttytel $(DESTDIR)$(bindir)/puttytel $(INSTALL_DATA) -m 644 ../doc/pageant.1 $(DESTDIR)$(man1dir)/pageant.1 $(INSTALL_DATA) -m 644 ../doc/plink.1 $(DESTDIR)$(man1dir)/plink.1 $(INSTALL_DATA) -m 644 ../doc/pscp.1 $(DESTDIR)$(man1dir)/pscp.1 $(INSTALL_DATA) -m 644 ../doc/psftp.1 $(DESTDIR)$(man1dir)/psftp.1 $(INSTALL_DATA) -m 644 ../doc/pterm.1 $(DESTDIR)$(man1dir)/pterm.1 $(INSTALL_DATA) -m 644 ../doc/putty.1 $(DESTDIR)$(man1dir)/putty.1 $(INSTALL_DATA) -m 644 ../doc/puttygen.1 $(DESTDIR)$(man1dir)/puttygen.1 $(INSTALL_DATA) -m 644 ../doc/puttytel.1 $(DESTDIR)$(man1dir)/puttytel.1 install-strip: $(MAKE) install INSTALL_PROGRAM="$(INSTALL_PROGRAM) -s" !end # List the man pages for the automake makefile. !begin am if HAVE_GTK man1_MANS = doc/plink.1 doc/pscp.1 doc/psftp.1 doc/puttygen.1 doc/psusan.1 \ doc/pageant.1 doc/pterm.1 doc/putty.1 doc/puttytel.1 else man1_MANS = doc/plink.1 doc/pscp.1 doc/psftp.1 doc/puttygen.1 doc/psusan.1 endif !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 # In automake makefile, build the OS X app bundle, if configured in # Quartz mode. !begin am if HAVE_QUARTZ noinst_SCRIPTS = unix/PuTTY.app unix/Pterm.app unix/PuTTY.app: unix/putty.bundle puttyapp osxlaunch rm -rf $@ && PUTTY_GTK_PREFIX_FROM_MAKEFILE=$$(pkg-config --variable=prefix gtk+-3.0) gtk-mac-bundler $< unix/Pterm.app: unix/pterm.bundle ptermapp osxlaunch rm -rf $@ && PUTTY_GTK_PREFIX_FROM_MAKEFILE=$$(pkg-config --variable=prefix gtk+-3.0) gtk-mac-bundler $< 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. # conf.c and its dependencies. CONF = conf marshal # Terminal emulator and its (platform-independent) dependencies. TERMINAL = terminal stripctrl wcwidth logging tree234 minibidi + config dialog CONF # GUI front end and terminal emulator (putty, puttytel). GUITERM = TERMINAL window windlg winctrls sizetip winprint winutils + wincfg winhelp winjump sessprep winselgui # Same thing on Unix. UXTERM = TERMINAL uxcfg uxucs uxprint timing callback miscucs GTKTERM = UXTERM gtkwin gtkcfg gtkdlg gtkfont gtkcols gtkmisc xkeysym + x11misc gtkcomm sessprep GTKMAIN = gtkmain cmdline # Non-SSH back ends (putty, puttytel, plink). NONSSH = telnet raw rlogin supdup ldisc pinger # SSH back end (putty, plink, pscp, psftp). ARITH = mpint ecc SSHCRYPTO = ARITH sshmd5 sshsha sshsh256 sshsh512 sshsha3 sshblake2 sshargon2 + sshrsa sshdss sshecc + sshdes sshblowf sshaes sshccp ssharcf + sshdh sshcrc sshcrcda sshauxcrypt + sshhmac SSHCOMMON = sshcommon sshutils sshprng sshrand SSHCRYPTO + sshverstring + sshpubk sshzlib + sshmac marshal nullplug + sshgssc pgssapi wildcard ssh1censor ssh2censor ssh2bpp + ssh2transport ssh2transhk ssh2connection portfwd x11fwd + ssh1connection ssh1bpp ssh2bpp-bare SSH = SSHCOMMON ssh + ssh1login ssh2userauth + pinger + sshshare aqsync agentf + mainchan ssh2kex-client ssh2connection-client ssh1connection-client WINSSH = SSH winnoise wincapi winpgntc wingss winshare winnps winnpc + winhsock errsock UXSSH = SSH uxnoise uxagentc uxgss uxshare # SFTP implementation (pscp, psftp). SFTP = psftpcommon sftp sftpcommon logging cmdline # Components of the prime-generation system. SSHPRIME = sshprime smallprimes primecandidate millerrabin pockle mpunsafe # Miscellaneous objects appearing in all the utilities, or all the # network ones, or the Unix or Windows subsets of those in turn. MISC = misc utils marshal memory stripctrl wcwidth MISCNETCOMMON = timing callback MISC version tree234 CONF MISCNET = MISCNETCOMMON be_misc settings proxy WINMISC = MISCNET winstore winnet winhandl cmdline windefs winmisc winproxy + wintime winhsock errsock winsecur winucs miscucs winmiscs UXMISCCOMMON = MISCNETCOMMON uxstore uxsel uxpoll uxnet uxpeer uxmisc time + uxfdsock errsock UXMISC = MISCNET UXMISCCOMMON uxproxy uxutils # SSH server. SSHSERVER = SSHCOMMON sshserver settings be_none logging ssh2kex-server + ssh2userauth-server sshrsag SSHPRIME ssh2connection-server + sesschan sftpcommon sftpserver proxy cproxy ssh1login-server + ssh1connection-server scpserver # import.c and dependencies, for PuTTYgen-like utilities that have to # load foreign key files. IMPORT = import sshbcrypt sshblowf marshal # Character set library, for use in pterm. CHARSET = sbcsdat slookup sbcs utf8 toucs fromucs xenc mimeenc macenc localenc # Standard libraries. LIBS = advapi32.lib user32.lib gdi32.lib comdlg32.lib + shell32.lib imm32.lib ole32.lib # Network backend sets. This also brings in the relevant attachment # to proxy.c depending on whether we're crypto-avoidant or not. BE_ALL = be_all cproxy BE_NOSSH = be_nossh norand nocproxy BE_SSH = be_ssh cproxy BE_NONE = be_none nocproxy # More backend sets, with the additional Windows serial-port module. W_BE_ALL = be_all_s winser cproxy W_BE_NOSSH = be_nos_s norand winser nocproxy # And with the Unix serial-port module. U_BE_ALL = be_all_s uxser cproxy U_BE_NOSSH = be_nos_s norand uxser nocproxy # Auxiliary crypto modules used by key generators. KEYGEN = sshrsag sshdssg sshecdsag # ------------------------------------------------------------ # Definitions of actual programs. The program name, followed by a # 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 console NONSSH WINSSH W_BE_ALL logging WINMISC + winx11 plink.res winnojmp sessprep noterm winnohlp winselcli + clicons wincliloop console LIBS pscp : [C] pscp winsftp wincons WINSSH BE_SSH SFTP wildcard WINMISC + pscp.res winnojmp winnohlp winselcli clicons wincliloop + console LIBS psftp : [C] psftp winsftp wincons WINSSH BE_SSH SFTP wildcard WINMISC + psftp.res winnojmp winnohlp winselcli clicons wincliloop + console LIBS pageant : [G] winpgnt pageant sshrsa sshpubk sshdes ARITH sshmd5 version + tree234 MISC sshaes sshsha winsecur winpgntc aqsync sshdss sshsh256 + sshsh512 winutils sshecc winmisc winmiscs winhelp conf pageant.res + sshauxcrypt sshhmac wincapi winnps winnpc winhsock errsock winnet + winhandl callback be_misc winselgui winhandl sshsha3 sshblake2 + sshargon2 LIBS puttygen : [G] winpgen KEYGEN SSHPRIME sshdes ARITH 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 sshecc sshprng + sshauxcrypt sshhmac winsecur winmiscs sshsha3 sshblake2 sshargon2 pterm : [X] GTKTERM uxmisc misc ldisc settings uxpty uxsel BE_NONE uxstore + uxsignal CHARSET cmdline uxpterm version time xpmpterm xpmptcfg + nogss utils memory GTKMAIN putty : [X] GTKTERM uxmisc misc ldisc settings uxsel U_BE_ALL uxstore + uxsignal CHARSET uxputty NONSSH UXSSH UXMISC ux_x11 xpmputty + xpmpucfg utils memory GTKMAIN puttytel : [X] GTKTERM uxmisc misc ldisc settings uxsel U_BE_NOSSH + uxstore uxsignal CHARSET uxputty NONSSH UXMISC xpmputty xpmpucfg + nogss utils memory GTKMAIN plink : [U] uxplink uxcons NONSSH UXSSH U_BE_ALL logging UXMISC uxsignal + ux_x11 noterm uxnogtk sessprep cmdline clicons uxcliloop console PUTTYGEN_UNIX = KEYGEN SSHPRIME sshdes ARITH sshmd5 version sshprng + sshrand uxnoise sshsha MISC sshrsa sshdss uxcons uxstore uxmisc + sshpubk sshaes sshsh256 sshsh512 IMPORT puttygen.res time tree234 + uxgen notiming CONF sshecc sshsha3 uxnogtk sshauxcrypt sshhmac + uxpoll uxutils sshblake2 sshargon2 console puttygen : [U] cmdgen PUTTYGEN_UNIX cgtest : [UT] cgtest PUTTYGEN_UNIX pscp : [U] pscp uxsftp uxcons UXSSH BE_SSH SFTP wildcard UXMISC uxnogtk + clicons uxcliloop console psftp : [U] psftp uxsftp uxcons UXSSH BE_SSH SFTP wildcard UXMISC uxnogtk + clicons uxcliloop console pageant : [X] uxpgnt uxagentc aqsync pageant sshrsa sshpubk sshdes ARITH + sshmd5 version tree234 misc sshaes sshsha sshdss sshsh256 sshsh512 + sshecc CONF uxsignal nocproxy nogss be_none x11fwd ux_x11 uxcons + gtkask gtkmisc nullplug logging UXMISC uxagentsock utils memory + sshauxcrypt sshhmac sshprng uxnoise uxcliloop sshsha3 sshblake2 + sshargon2 console ptermapp : [XT] GTKTERM uxmisc misc ldisc settings uxpty uxsel BE_NONE uxstore + uxsignal CHARSET uxpterm version time xpmpterm xpmptcfg + nogss gtkapp nocmdline utils memory puttyapp : [XT] GTKTERM uxmisc misc ldisc settings uxsel U_BE_ALL uxstore + uxsignal CHARSET uxputty NONSSH UXSSH UXMISC ux_x11 xpmputty + xpmpucfg gtkapp nocmdline utils memory osxlaunch : [UT] osxlaunch fuzzterm : [UT] UXTERM CHARSET MISC version uxmisc uxucs fuzzterm time settings + uxstore be_none uxnogtk memory testcrypt : [UT] testcrypt SSHCRYPTO sshprng SSHPRIME sshpubk marshal utils + memory tree234 uxutils KEYGEN testcrypt : [C] testcrypt SSHCRYPTO sshprng SSHPRIME sshpubk marshal utils + memory tree234 winmiscs KEYGEN testsc : [UT] testsc SSHCRYPTO marshal utils memory tree234 wildcard + sshmac uxutils sshpubk testzlib : [UT] testzlib sshzlib utils marshal memory uppity : [UT] uxserver SSHSERVER UXMISC uxsignal uxnoise uxgss uxnogtk + uxpty uxsftpserver ux_x11 uxagentsock procnet uxcliloop psusan : [U] uxpsusan SSHSERVER UXMISC uxsignal uxnoise nogss uxnogtk + uxpty uxsftpserver ux_x11 uxagentsock procnet uxcliloop PSOCKS = psocks portfwd conf sshutils logging proxy nocproxy timing callback + time tree234 version errsock be_misc norand MISC psocks : [C] PSOCKS winsocks wincons winproxy winnet winmisc winselcli + winhsock winhandl winmiscs winnohlp wincliloop console LIBS psocks : [UT] PSOCKS uxsocks uxcons uxproxy uxnet uxmisc uxpoll uxsel uxnogtk + uxpeer uxfdsock uxcliloop uxsignal console # ---------------------------------------------------------------------- # On Windows, provide a means of removing local test binaries that we # aren't going to actually ship. (I prefer this to not building them # in the first place, so that we find out about build breakage early.) !begin vc cleantestprogs: -del $(BUILDDIR)testcrypt.exe $(BUILDDIR)psocks.exe !end !begin clangcl cleantestprogs: -rm -f $(BUILDDIR)testcrypt.exe $(BUILDDIR)psocks.exe !end putty-0.76/agentf.c0000644000175000017500000001702214072266307011175 00000000000000/* * SSH agent forwarding. */ #include #include #include #include "putty.h" #include "ssh.h" #include "pageant.h" #include "sshchan.h" typedef struct agentf { SshChannel *c; bufchain inbuffer; agent_pending_query *pending; bool input_wanted; bool rcvd_eof; Channel chan; } agentf; static void agentf_got_response(agentf *af, void *reply, int replylen) { af->pending = NULL; if (!reply) { /* The real agent didn't send any kind of reply at all for * some reason, so fake an SSH_AGENT_FAILURE. */ reply = "\0\0\0\1\5"; replylen = 5; } sshfwd_write(af->c, reply, replylen); } static void agentf_callback(void *vctx, void *reply, int replylen); static void agentf_try_forward(agentf *af) { size_t datalen, length; strbuf *message; unsigned char msglen[4]; void *reply; int replylen; /* * Don't try to parallelise agent requests. Wait for each one to * return before attempting the next. */ if (af->pending) return; /* * If the outgoing side of the channel connection is currently * throttled, don't submit any new forwarded requests to the real * agent. This causes the input side of the agent forwarding not * to be emptied, exerting the required back-pressure on the * remote client, and encouraging it to read our responses before * sending too many more requests. */ if (!af->input_wanted) return; while (1) { /* * Try to extract a complete message from the input buffer. */ datalen = bufchain_size(&af->inbuffer); if (datalen < 4) break; /* not even a length field available yet */ bufchain_fetch(&af->inbuffer, msglen, 4); length = GET_32BIT_MSB_FIRST(msglen); if (length > AGENT_MAX_MSGLEN-4) { /* * If the remote has sent a message that's just _too_ * long, we should reject it in advance of seeing the rest * of the incoming message, and also close the connection * for good measure (which avoids us having to faff about * with carefully ignoring just the right number of bytes * from the overlong message). */ agentf_got_response(af, NULL, 0); sshfwd_write_eof(af->c); return; } if (length > datalen - 4) break; /* a whole message is not yet available */ bufchain_consume(&af->inbuffer, 4); message = strbuf_new_for_agent_query(); bufchain_fetch_consume( &af->inbuffer, strbuf_append(message, length), length); af->pending = agent_query( message, &reply, &replylen, agentf_callback, af); strbuf_free(message); if (af->pending) return; /* agent_query promised to reply in due course */ /* * If the agent gave us an answer immediately, pass it * straight on and go round this loop again. */ agentf_got_response(af, reply, replylen); sfree(reply); } /* * If we get here (i.e. we left the above while loop via 'break' * rather than 'return'), that means we've determined that the * input buffer for the agent forwarding connection doesn't * contain a complete request. * * So if there's potentially more data to come, we can return now, * and wait for the remote client to send it. But if the remote * has sent EOF, it would be a mistake to do that, because we'd be * waiting a long time. So this is the moment to check for EOF, * and respond appropriately. */ if (af->rcvd_eof) sshfwd_write_eof(af->c); } static void agentf_callback(void *vctx, void *reply, int replylen) { agentf *af = (agentf *)vctx; agentf_got_response(af, reply, replylen); sfree(reply); /* * Now try to extract and send further messages from the channel's * input-side buffer. */ agentf_try_forward(af); } static void agentf_free(Channel *chan); static size_t agentf_send(Channel *chan, bool is_stderr, const void *, size_t); static void agentf_send_eof(Channel *chan); static char *agentf_log_close_msg(Channel *chan); static void agentf_set_input_wanted(Channel *chan, bool wanted); static const ChannelVtable agentf_channelvt = { .free = agentf_free, .open_confirmation = chan_remotely_opened_confirmation, .open_failed = chan_remotely_opened_failure, .send = agentf_send, .send_eof = agentf_send_eof, .set_input_wanted = agentf_set_input_wanted, .log_close_msg = agentf_log_close_msg, .want_close = chan_default_want_close, .rcvd_exit_status = chan_no_exit_status, .rcvd_exit_signal = chan_no_exit_signal, .rcvd_exit_signal_numeric = chan_no_exit_signal_numeric, .run_shell = chan_no_run_shell, .run_command = chan_no_run_command, .run_subsystem = chan_no_run_subsystem, .enable_x11_forwarding = chan_no_enable_x11_forwarding, .enable_agent_forwarding = chan_no_enable_agent_forwarding, .allocate_pty = chan_no_allocate_pty, .set_env = chan_no_set_env, .send_break = chan_no_send_break, .send_signal = chan_no_send_signal, .change_window_size = chan_no_change_window_size, .request_response = chan_no_request_response, }; Channel *agentf_new(SshChannel *c) { agentf *af = snew(agentf); af->c = c; af->chan.vt = &agentf_channelvt; af->chan.initial_fixed_window_size = 0; af->rcvd_eof = false; bufchain_init(&af->inbuffer); af->pending = NULL; af->input_wanted = true; return &af->chan; } static void agentf_free(Channel *chan) { assert(chan->vt == &agentf_channelvt); agentf *af = container_of(chan, agentf, chan); if (af->pending) agent_cancel_query(af->pending); bufchain_clear(&af->inbuffer); sfree(af); } static size_t agentf_send(Channel *chan, bool is_stderr, const void *data, size_t length) { assert(chan->vt == &agentf_channelvt); agentf *af = container_of(chan, agentf, chan); bufchain_add(&af->inbuffer, data, length); agentf_try_forward(af); /* * We exert back-pressure on an agent forwarding client if and * only if we're waiting for the response to an asynchronous agent * request. This prevents the client running out of window while * receiving the _first_ message, but means that if any message * takes time to process, the client will be discouraged from * sending an endless stream of further ones after it. */ return (af->pending ? bufchain_size(&af->inbuffer) : 0); } static void agentf_send_eof(Channel *chan) { assert(chan->vt == &agentf_channelvt); agentf *af = container_of(chan, agentf, chan); af->rcvd_eof = true; /* Call try_forward, which will respond to the EOF now if * appropriate, or wait until the queue of outstanding requests is * dealt with if not. */ agentf_try_forward(af); } static char *agentf_log_close_msg(Channel *chan) { return dupstr("Agent-forwarding connection closed"); } static void agentf_set_input_wanted(Channel *chan, bool wanted) { assert(chan->vt == &agentf_channelvt); agentf *af = container_of(chan, agentf, chan); af->input_wanted = wanted; /* Agent forwarding channels are buffer-managed by not asking the * agent questions if the SSH channel isn't accepting input. So if * it's started again, we should ask a question if we have one * pending.. */ if (wanted) agentf_try_forward(af); } putty-0.76/aqsync.c0000644000175000017500000000102714072266307011225 00000000000000/* * aqsync.c: the agent_query_synchronous() wrapper function. * * This is a very small thing to have to put in its own module, but it * wants to be shared between back ends, and exist in any SSH client * program and also Pageant, and _nowhere else_ (because it pulls in * the main agent_query). */ #include #include "putty.h" void agent_query_synchronous(strbuf *query, void **out, int *outlen) { agent_pending_query *pending; pending = agent_query(query, out, outlen, NULL, 0); assert(!pending); } putty-0.76/be_all.c0000644000175000017500000000144314072266307011147 00000000000000/* * Linking module for PuTTY proper: list the available backends * including ssh. */ #include #include "putty.h" /* * This appname is not strictly in the right place, since Plink * also uses this module. However, Plink doesn't currently use any * of the dialog-box sorts of things that make use of appname, so * it shouldn't do any harm here. I'm trying to avoid having to * have tiny little source modules containing nothing but * declarations of appname, for as long as I can... */ const char *const appname = "PuTTY"; const int be_default_protocol = PROT_SSH; const struct BackendVtable *const backends[] = { &ssh_backend, &telnet_backend, &rlogin_backend, &supdup_backend, &raw_backend, &sshconn_backend, NULL }; const size_t n_ui_backends = 1; putty-0.76/be_all_s.c0000644000175000017500000000152114072266307011466 00000000000000/* * Linking module for PuTTY proper: list the available backends * including ssh, plus the serial backend. */ #include #include "putty.h" /* * This appname is not strictly in the right place, since Plink * also uses this module. However, Plink doesn't currently use any * of the dialog-box sorts of things that make use of appname, so * it shouldn't do any harm here. I'm trying to avoid having to * have tiny little source modules containing nothing but * declarations of appname, for as long as I can... */ const char *const appname = "PuTTY"; const int be_default_protocol = PROT_SSH; const struct BackendVtable *const backends[] = { &ssh_backend, &serial_backend, &telnet_backend, &rlogin_backend, &supdup_backend, &raw_backend, &sshconn_backend, NULL }; const size_t n_ui_backends = 2; putty-0.76/be_misc.c0000644000175000017500000001134714072266307011336 00000000000000/* * be_misc.c: helper functions shared between main network backends. */ #include #include #include "putty.h" #include "network.h" void backend_socket_log(Seat *seat, LogContext *logctx, PlugLogType type, SockAddr *addr, int port, const char *error_msg, int error_code, Conf *conf, bool session_started) { char addrbuf[256], *msg; switch (type) { case PLUGLOG_CONNECT_TRYING: sk_getaddr(addr, addrbuf, lenof(addrbuf)); if (sk_addr_needs_port(addr)) { msg = dupprintf("Connecting to %s port %d", addrbuf, port); } else { msg = dupprintf("Connecting to %s", addrbuf); } break; case PLUGLOG_CONNECT_FAILED: sk_getaddr(addr, addrbuf, lenof(addrbuf)); msg = dupprintf("Failed to connect to %s: %s", addrbuf, error_msg); break; case PLUGLOG_CONNECT_SUCCESS: sk_getaddr(addr, addrbuf, lenof(addrbuf)); msg = dupprintf("Connected to %s", addrbuf); break; case PLUGLOG_PROXY_MSG: { /* Proxy-related log messages have their own identifying * prefix already, put on by our caller. */ int len, log_to_term; /* Suffix \r\n temporarily, so we can log to the terminal. */ msg = dupprintf("%s\r\n", error_msg); len = strlen(msg); assert(len >= 2); log_to_term = conf_get_int(conf, CONF_proxy_log_to_term); if (log_to_term == AUTO) log_to_term = session_started ? FORCE_OFF : FORCE_ON; if (log_to_term == FORCE_ON) seat_stderr(seat, msg, len); msg[len-2] = '\0'; /* remove the \r\n again */ break; } default: msg = NULL; /* shouldn't happen, but placate optimiser */ break; } if (msg) { logevent(logctx, msg); sfree(msg); } } void psb_init(ProxyStderrBuf *psb) { psb->size = 0; } void log_proxy_stderr(Plug *plug, ProxyStderrBuf *psb, const void *vdata, size_t len) { const char *data = (const char *)vdata; /* * This helper function allows us to collect the data written to a * local proxy command's standard error in whatever size chunks we * happen to get from its pipe, and whenever we have a complete * line, we pass it to plug_log. * * (We also do this when the buffer in psb fills up, to avoid just * allocating more and more memory forever, and also to keep Event * Log lines reasonably bounded in size.) * * Prerequisites: a plug to log to, and a ProxyStderrBuf stored * somewhere to collect any not-yet-output partial line. */ while (len > 0) { /* * Copy as much data into psb->buf as will fit. */ assert(psb->size < lenof(psb->buf)); size_t to_consume = lenof(psb->buf) - psb->size; if (to_consume > len) to_consume = len; memcpy(psb->buf + psb->size, data, to_consume); data += to_consume; len -= to_consume; psb->size += to_consume; /* * Output any full lines in psb->buf. */ size_t pos = 0; while (pos < psb->size) { char *nlpos = memchr(psb->buf + pos, '\n', psb->size - pos); if (!nlpos) break; /* * Found a newline in the buffer, so we can output a line. */ size_t endpos = nlpos - psb->buf; while (endpos > pos && (psb->buf[endpos-1] == '\n' || psb->buf[endpos-1] == '\r')) endpos--; char *msg = dupprintf( "proxy: %.*s", (int)(endpos - pos), psb->buf + pos); plug_log(plug, PLUGLOG_PROXY_MSG, NULL, 0, msg, 0); sfree(msg); pos = nlpos - psb->buf + 1; assert(pos <= psb->size); } /* * If the buffer is completely full and we didn't output * anything, then output the whole thing, flagging it as a * truncated line. */ if (pos == 0 && psb->size == lenof(psb->buf)) { char *msg = dupprintf( "proxy (partial line): %.*s", (int)psb->size, psb->buf); plug_log(plug, PLUGLOG_PROXY_MSG, NULL, 0, msg, 0); sfree(msg); pos = psb->size = 0; } /* * Now move any remaining data up to the front of the buffer. */ size_t newsize = psb->size - pos; if (newsize) memmove(psb->buf, psb->buf + pos, newsize); psb->size = newsize; /* * And loop round again if there's more data to be read from * our input. */ } } putty-0.76/be_none.c0000644000175000017500000000041614072266307011335 00000000000000/* * Linking module for programs that do not support selection of backend * (such as pterm). */ #include #include "putty.h" const int be_default_protocol = -1; const struct BackendVtable *const backends[] = { NULL }; const size_t n_ui_backends = 0; putty-0.76/be_nos_s.c0000644000175000017500000000063414072266307011521 00000000000000/* * Linking module for PuTTYtel: list the available backends not * including ssh. */ #include #include "putty.h" const int be_default_protocol = PROT_TELNET; const char *const appname = "PuTTYtel"; const struct BackendVtable *const backends[] = { &telnet_backend, &serial_backend, &rlogin_backend, &supdup_backend, &raw_backend, NULL }; const size_t n_ui_backends = 2; putty-0.76/be_nossh.c0000644000175000017500000000060714072266307011532 00000000000000/* * Linking module for PuTTYtel: list the available backends not * including ssh. */ #include #include "putty.h" const int be_default_protocol = PROT_TELNET; const char *const appname = "PuTTYtel"; const struct BackendVtable *const backends[] = { &telnet_backend, &rlogin_backend, &supdup_backend, &raw_backend, NULL }; const size_t n_ui_backends = 1; putty-0.76/be_ssh.c0000644000175000017500000000072514072266307011176 00000000000000/* * Linking module for programs that are restricted to only using * SSH-type protocols (pscp and psftp). These still have a choice of * two actual backends, because they can also speak PROT_SSHCONN. */ #include #include "putty.h" const int be_default_protocol = PROT_SSH; const struct BackendVtable *const backends[] = { &ssh_backend, &sshconn_backend, NULL }; const size_t n_ui_backends = 0; /* not used in programs with a config UI */ putty-0.76/callback.c0000644000175000017500000000625314072266307011471 00000000000000/* * Facility for queueing callback functions to be run from the * top-level event loop after the current top-level activity finishes. */ #include #include "putty.h" struct callback { struct callback *next; toplevel_callback_fn_t fn; void *ctx; }; static struct callback *cbcurr = NULL, *cbhead = NULL, *cbtail = NULL; static toplevel_callback_notify_fn_t notify_frontend = NULL; static void *notify_ctx = NULL; void request_callback_notifications(toplevel_callback_notify_fn_t fn, void *ctx) { notify_frontend = fn; notify_ctx = ctx; } static void run_idempotent_callback(void *ctx) { struct IdempotentCallback *ic = (struct IdempotentCallback *)ctx; ic->queued = false; ic->fn(ic->ctx); } void queue_idempotent_callback(struct IdempotentCallback *ic) { if (ic->queued) return; ic->queued = true; queue_toplevel_callback(run_idempotent_callback, ic); } void delete_callbacks_for_context(void *ctx) { struct callback *newhead, *newtail; newhead = newtail = NULL; while (cbhead) { struct callback *cb = cbhead; cbhead = cbhead->next; if (cb->ctx == ctx || (cb->fn == run_idempotent_callback && ((struct IdempotentCallback *)cb->ctx)->ctx == ctx)) { sfree(cb); } else { if (!newhead) newhead = cb; else newtail->next = cb; newtail = cb; } } cbhead = newhead; cbtail = newtail; if (newtail) newtail->next = NULL; } 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 cbcurr is non-NULL, i.e. we are actually in the middle of * executing a callback right now, then we count that as the queue * already having been non-empty. That saves the front end getting * a constant stream of needless re-notifications if the last * callback keeps re-scheduling itself. */ if (notify_frontend && !cbhead && !cbcurr) notify_frontend(notify_ctx); if (cbtail) cbtail->next = cb; else cbhead = cb; cbtail = cb; cb->next = NULL; } bool run_toplevel_callbacks(void) { bool done_something = false; if (cbhead) { /* * Transfer the head callback into cbcurr to indicate that * it's being executed. Then operations which transform the * queue, like delete_callbacks_for_context, can proceed as if * it's not there. */ cbcurr = cbhead; cbhead = cbhead->next; if (!cbhead) cbtail = NULL; /* * Now run the callback, and then clear it out of cbcurr. */ cbcurr->fn(cbcurr->ctx); sfree(cbcurr); cbcurr = NULL; done_something = true; } return done_something; } bool toplevel_callback_pending(void) { return cbcurr != NULL || cbhead != NULL; } putty-0.76/cgtest.c0000644000175000017500000006275114072266307011233 00000000000000/* * cgtest.c: stub file to compile cmdgen.c in self-test mode */ /* * Before we #include cmdgen.c, we override some function names for * test purposes. We do this via #define, so that when we link against * modules containing the original versions, we don't get a link-time * symbol clash: * * - Calls to get_random_data() are replaced with the diagnostic * function below, in order to avoid depleting the test system's * /dev/random unnecessarily. * * - Calls to console_get_userpass_input() are replaced with the * diagnostic function below, so that I can run tests in an * automated manner and provide their interactive passphrase * inputs. * * - The main() defined by cmdgen.c is renamed to cmdgen_main(); in * this file I define another main() which calls the former * repeatedly to run tests. */ #define get_random_data get_random_data_diagnostic #define console_get_userpass_input console_get_userpass_input_diagnostic #define main cmdgen_main #define ppk_save_default_parameters ppk_save_cgtest_parameters #include "cmdgen.c" #undef get_random_data #undef console_get_userpass_input #undef main static bool cgtest_verbose = false; const struct ppk_save_parameters ppk_save_cgtest_parameters = { /* Replacement set of key derivation parameters that make this * test suite run a bit faster and also add determinism: we don't * try to auto-scale the number of passes (in case it gets * different answers twice in the test suite when we were * expecting two key files to compare equal), and we specify a * passphrase salt. */ .fmt_version = 3, .argon2_flavour = Argon2id, .argon2_mem = 16, .argon2_passes_auto = false, .argon2_passes = 2, .argon2_parallelism = 1, .salt = (const uint8_t *)"SameSaltEachTime", .saltlen = 16, }; /* * Define the special versions of get_random_data and * console_get_userpass_input that we need for this test rig. */ char *get_random_data_diagnostic(int len, const char *device) { char *buf = snewn(len, char); memset(buf, 'x', len); return buf; } static int nprompts, promptsgot; static const char *prompts[3]; int console_get_userpass_input_diagnostic(prompts_t *p) { size_t i; int ret = 1; for (i = 0; i < p->n_prompts; i++) { if (promptsgot < nprompts) { prompt_set_result(p->prompts[i], prompts[promptsgot++]); if (cgtest_verbose) printf(" prompt \"%s\": response \"%s\"\n", p->prompts[i]->prompt, p->prompts[i]->result->s); } else { promptsgot++; /* track number of requests anyway */ ret = 0; if (cgtest_verbose) printf(" prompt \"%s\": no response preloaded\n", p->prompts[i]->prompt); } } return ret; } #include static int passes, fails; void setup_passphrases(char *first, ...) { va_list ap; char *next; nprompts = 0; if (first) { prompts[nprompts++] = first; va_start(ap, first); while ((next = va_arg(ap, char *)) != NULL) { assert(nprompts < lenof(prompts)); prompts[nprompts++] = next; } va_end(ap); } } void test(int retval, ...) { va_list ap; int i, argc, ret; char **argv; argc = 0; va_start(ap, retval); while (va_arg(ap, char *) != NULL) argc++; va_end(ap); argv = snewn(argc+1, char *); va_start(ap, retval); for (i = 0; i <= argc; i++) argv[i] = va_arg(ap, char *); va_end(ap); promptsgot = 0; if (cgtest_verbose) { printf("run:"); for (int i = 0; i < argc; i++) { static const char okchars[] = "0123456789abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ%+,-./:=[]^_"; const char *arg = argv[i]; printf(" "); if (arg[strspn(arg, okchars)]) { printf("'"); for (const char *c = argv[i]; *c; c++) { if (*c == '\'') { printf("'\\''"); } else { putchar(*c); } } printf("'"); } else { fputs(arg, stdout); } } printf("\n"); } ret = cmdgen_main(argc, argv); random_clear(); if (ret != retval) { printf("FAILED retval (exp %d got %d):", retval, ret); for (i = 0; i < argc; i++) printf(" %s", argv[i]); printf("\n"); fails++; } else if (promptsgot != nprompts) { printf("FAILED nprompts (exp %d got %d):", nprompts, promptsgot); for (i = 0; i < argc; i++) printf(" %s", argv[i]); printf("\n"); fails++; } else { passes++; } sfree(argv); } PRINTF_LIKE(3, 4) void filecmp(char *file1, char *file2, char *fmt, ...) { /* * Ideally I should do file comparison myself, to maximise the * portability of this test suite once this application begins * running on non-Unix platforms. For the moment, though, * calling Unix diff is perfectly adequate. */ char *buf; int ret; buf = dupprintf("diff -q '%s' '%s'", file1, file2); ret = system(buf); sfree(buf); if (ret) { va_list ap; printf("FAILED diff (ret=%d): ", ret); va_start(ap, fmt); vprintf(fmt, ap); va_end(ap); printf("\n"); fails++; } else passes++; } /* * General-purpose flags word */ #define CGT_FLAGS(X) \ X(CGT_TYPE_KNOWN_EARLY) \ X(CGT_OPENSSH) \ X(CGT_SSHCOM) \ X(CGT_SSH_KEYGEN) \ X(CGT_ED25519) \ /* end of list */ #define FLAG_SHIFTS(name) name ## _shift, enum { CGT_FLAGS(FLAG_SHIFTS) CGT_dummy_shift }; #define FLAG_VALUES(name) name = 1 << name ## _shift, enum { CGT_FLAGS(FLAG_VALUES) CGT_dummy_flag }; char *cleanup_fp(char *s, unsigned flags) { ptrlen pl = ptrlen_from_asciz(s); static const char separators[] = " \n\t"; /* Skip initial key type word if we find one */ if (ptrlen_startswith(pl, PTRLEN_LITERAL("ssh-"), NULL) || ptrlen_startswith(pl, PTRLEN_LITERAL("ecdsa-"), NULL)) ptrlen_get_word(&pl, separators); /* Expect two words giving the key length and the hash */ ptrlen bits = ptrlen_get_word(&pl, separators); ptrlen hash = ptrlen_get_word(&pl, separators); if (flags & CGT_SSH_KEYGEN) { /* Strip "MD5:" prefix if it's present, and do nothing if it isn't */ ptrlen_startswith(hash, PTRLEN_LITERAL("MD5:"), &hash); if (flags & CGT_ED25519) { /* OpenSSH ssh-keygen lists ed25519 keys as 256 bits, not 255 */ if (ptrlen_eq_string(bits, "256")) bits = PTRLEN_LITERAL("255"); } } return dupprintf("%.*s %.*s", PTRLEN_PRINTF(bits), PTRLEN_PRINTF(hash)); } char *get_line(char *filename) { FILE *fp; char *line; fp = fopen(filename, "r"); if (!fp) return NULL; line = fgetline(fp); fclose(fp); return line; } char *get_fp(char *filename, unsigned flags) { char *orig = get_line(filename); if (!orig) return NULL; char *toret = cleanup_fp(orig, flags); sfree(orig); return toret; } PRINTF_LIKE(3, 4) void check_fp(char *filename, char *fp, char *fmt, ...) { char *newfp; if (!fp) return; newfp = get_fp(filename, 0); if (!strcmp(fp, newfp)) { passes++; } else { va_list ap; printf("FAILED check_fp ['%s' != '%s']: ", newfp, fp); va_start(ap, fmt); vprintf(fmt, ap); va_end(ap); printf("\n"); fails++; } sfree(newfp); } static const struct cgtest_keytype { const char *name; unsigned flags; } cgtest_keytypes[] = { { "rsa1", CGT_TYPE_KNOWN_EARLY }, { "dsa", CGT_OPENSSH | CGT_SSHCOM }, { "rsa", CGT_OPENSSH | CGT_SSHCOM }, { "ecdsa", CGT_OPENSSH }, { "ed25519", CGT_OPENSSH | CGT_ED25519 }, }; int main(int argc, char **argv) { int i; int active[lenof(cgtest_keytypes)], active_value; bool remove_files = true; active_value = 0; for (i = 0; i < lenof(cgtest_keytypes); i++) active[i] = active_value; while (--argc > 0) { ptrlen arg = ptrlen_from_asciz(*++argv); if (ptrlen_eq_string(arg, "-v") || ptrlen_eq_string(arg, "--verbose")) { cgtest_verbose = true; } else if (ptrlen_eq_string(arg, "--keep")) { remove_files = false; } else if (ptrlen_eq_string(arg, "--help")) { printf("usage: cgtest [options] [key types]\n"); printf("options: -v, --verbose " "print more output during tests\n"); printf(" --keep " "do not delete the temporary output files\n"); printf(" --help " "display this help text\n"); printf("key types: "); for (i = 0; i < lenof(cgtest_keytypes); i++) printf("%s%s", i ? ", " : "", cgtest_keytypes[i].name); printf("\n"); return 0; } else if (!ptrlen_startswith(arg, PTRLEN_LITERAL("-"), NULL)) { for (i = 0; i < lenof(cgtest_keytypes); i++) if (ptrlen_eq_string(arg, cgtest_keytypes[i].name)) break; if (i == lenof(cgtest_keytypes)) { fprintf(stderr, "cgtest: unrecognised key type '%.*s'\n", PTRLEN_PRINTF(arg)); return 1; } active_value = 1; /* disables all keys not explicitly enabled */ active[i] = active_value; } else { fprintf(stderr, "cgtest: unrecognised option '%.*s'\n", PTRLEN_PRINTF(arg)); return 1; } } passes = fails = 0; for (i = 0; i < lenof(cgtest_keytypes); i++) { if (active[i] != active_value) continue; const struct cgtest_keytype *keytype = &cgtest_keytypes[i]; bool supports_openssh = keytype->flags & CGT_OPENSSH; bool supports_sshcom = keytype->flags & CGT_SSHCOM; bool type_known_early = keytype->flags & CGT_TYPE_KNOWN_EARLY; char filename[128], osfilename[128], scfilename[128]; char pubfilename[128], tmpfilename1[128], tmpfilename2[128]; char *fps[SSH_N_FPTYPES]; sprintf(filename, "test-%s.ppk", keytype->name); sprintf(pubfilename, "test-%s.pub", keytype->name); sprintf(osfilename, "test-%s.os", keytype->name); sprintf(scfilename, "test-%s.sc", keytype->name); sprintf(tmpfilename1, "test-%s.tmp1", keytype->name); sprintf(tmpfilename2, "test-%s.tmp2", keytype->name); /* * Create an encrypted key. */ setup_passphrases("sponge", "sponge", NULL); test(0, "puttygen", "-t", keytype->name, "-o", filename, NULL); /* * List the public key in OpenSSH format. */ setup_passphrases(NULL); test(0, "puttygen", "-L", filename, "-o", pubfilename, NULL); for (FingerprintType fptype = 0; fptype < SSH_N_FPTYPES; fptype++) { const char *fpname = (fptype == SSH_FPTYPE_MD5 ? "md5" : "sha256"); char *cmdbuf; char *fp = NULL; cmdbuf = dupprintf("ssh-keygen -E %s -l -f '%s' > '%s'", fpname, pubfilename, tmpfilename1); if (cgtest_verbose) printf("OpenSSH %s fp check: %s\n", fpname, cmdbuf); if (system(cmdbuf) || (fp = get_fp(tmpfilename1, CGT_SSH_KEYGEN | keytype->flags)) == NULL) { printf("UNABLE to test fingerprint matching against " "OpenSSH\n"); } sfree(cmdbuf); if (fp && cgtest_verbose) { char *line = get_line(tmpfilename1); printf("OpenSSH %s fp: %s\n", fpname, line); printf("Cleaned up: %s\n", fp); sfree(line); } fps[fptype] = fp; } /* * List the public key in IETF/ssh.com format. */ setup_passphrases(NULL); test(0, "puttygen", "-p", filename, NULL); /* * List the fingerprint of the key. */ setup_passphrases(NULL); for (FingerprintType fptype = 0; fptype < SSH_N_FPTYPES; fptype++) { const char *fpname = (fptype == SSH_FPTYPE_MD5 ? "md5" : "sha256"); test(0, "puttygen", "-E", fpname, "-l", filename, "-o", tmpfilename1, NULL); if (!fps[fptype]) { /* * If we can't test fingerprints against OpenSSH, we * can at the very least test equality of all the * fingerprints we generate of this key throughout * testing. */ fps[fptype] = get_fp(tmpfilename1, 0); } else { check_fp(tmpfilename1, fps[fptype], "%s initial %s fp", keytype->name, fpname); } } /* * Change the comment of the key; this _does_ require a * passphrase owing to the tamperproofing. * * NOTE: In SSH-1, this only requires a passphrase because * of inadequacies of the loading and saving mechanisms. In * _principle_, it should be perfectly possible to modify * the comment on an SSH-1 key without requiring a * passphrase; the only reason I can't do it is because my * loading and saving mechanisms don't include a method of * loading all the key data without also trying to decrypt * the private section. * * I don't consider this to be a problem worth solving, * because (a) to fix it would probably end up bloating * PuTTY proper, and (b) SSH-1 is on the way out anyway so * it shouldn't be highly significant. If it seriously * bothers anyone then perhaps I _might_ be persuadable. */ setup_passphrases("sponge", NULL); test(0, "puttygen", "-C", "new-comment", filename, NULL); /* * Change the passphrase to nothing. */ setup_passphrases("sponge", "", "", NULL); test(0, "puttygen", "-P", filename, NULL); /* * Change the comment of the key again; this time we expect no * passphrase to be required. */ setup_passphrases(NULL); test(0, "puttygen", "-C", "new-comment-2", filename, NULL); /* * Export the private key into OpenSSH format; no passphrase * should be required since the key is currently unencrypted. */ setup_passphrases(NULL); test(supports_openssh ? 0 : 1, "puttygen", "-O", "private-openssh", "-o", osfilename, filename, NULL); if (supports_openssh) { /* * List the fingerprint of the OpenSSH-formatted key. */ setup_passphrases(NULL); test(0, "puttygen", "-l", osfilename, "-o", tmpfilename1, NULL); check_fp(tmpfilename1, fps[SSH_FPTYPE_DEFAULT], "%s openssh clear fp", keytype->name); /* * List the public half of the OpenSSH-formatted key in * OpenSSH format. */ setup_passphrases(NULL); test(0, "puttygen", "-L", osfilename, NULL); /* * List the public half of the OpenSSH-formatted key in * IETF/ssh.com format. */ setup_passphrases(NULL); test(0, "puttygen", "-p", osfilename, NULL); } /* * Export the private key into ssh.com format; no passphrase * should be required since the key is currently unencrypted. */ setup_passphrases(NULL); test(supports_sshcom ? 0 : 1, "puttygen", "-O", "private-sshcom", "-o", scfilename, filename, NULL); if (supports_sshcom) { /* * List the fingerprint of the ssh.com-formatted key. */ setup_passphrases(NULL); test(0, "puttygen", "-l", scfilename, "-o", tmpfilename1, NULL); check_fp(tmpfilename1, fps[SSH_FPTYPE_DEFAULT], "%s ssh.com clear fp", keytype->name); /* * List the public half of the ssh.com-formatted key in * OpenSSH format. */ setup_passphrases(NULL); test(0, "puttygen", "-L", scfilename, NULL); /* * List the public half of the ssh.com-formatted key in * IETF/ssh.com format. */ setup_passphrases(NULL); test(0, "puttygen", "-p", scfilename, NULL); } if (supports_openssh && supports_sshcom) { /* * Convert from OpenSSH into ssh.com. */ setup_passphrases(NULL); test(0, "puttygen", osfilename, "-o", tmpfilename1, "-O", "private-sshcom", NULL); /* * Convert from ssh.com back into a PuTTY key, * supplying the same comment as we had before we * started to ensure the comparison works. */ setup_passphrases(NULL); test(0, "puttygen", tmpfilename1, "-C", "new-comment-2", "-o", tmpfilename2, NULL); /* * See if the PuTTY key thus generated is the same as * the original. */ filecmp(filename, tmpfilename2, "p->o->s->p clear %s", keytype->name); /* * Convert from ssh.com to OpenSSH. */ setup_passphrases(NULL); test(0, "puttygen", scfilename, "-o", tmpfilename1, "-O", "private-openssh", NULL); /* * Convert from OpenSSH back into a PuTTY key, * supplying the same comment as we had before we * started to ensure the comparison works. */ setup_passphrases(NULL); test(0, "puttygen", tmpfilename1, "-C", "new-comment-2", "-o", tmpfilename2, NULL); /* * See if the PuTTY key thus generated is the same as * the original. */ filecmp(filename, tmpfilename2, "p->s->o->p clear %s", keytype->name); /* * Finally, do a round-trip conversion between PuTTY * and ssh.com without involving OpenSSH, to test that * the key comment is preserved in that case. */ setup_passphrases(NULL); test(0, "puttygen", "-O", "private-sshcom", "-o", tmpfilename1, filename, NULL); setup_passphrases(NULL); test(0, "puttygen", tmpfilename1, "-o", tmpfilename2, NULL); filecmp(filename, tmpfilename2, "p->s->p clear %s", keytype->name); } /* * Check that mismatched passphrases cause an error. */ setup_passphrases("sponge2", "sponge3", NULL); test(1, "puttygen", "-P", filename, NULL); /* * Put a passphrase back on. */ setup_passphrases("sponge2", "sponge2", NULL); test(0, "puttygen", "-P", filename, NULL); /* * Export the private key into OpenSSH format, this time * while encrypted. */ if (!supports_openssh && type_known_early) { /* We'll know far enough in advance that this combination * is going to fail that we never ask for the passphrase */ setup_passphrases(NULL); } else { setup_passphrases("sponge2", NULL); } test(supports_openssh ? 0 : 1, "puttygen", "-O", "private-openssh", "-o", osfilename, filename, NULL); if (supports_openssh) { /* * List the fingerprint of the OpenSSH-formatted key. */ setup_passphrases("sponge2", NULL); test(0, "puttygen", "-l", osfilename, "-o", tmpfilename1, NULL); check_fp(tmpfilename1, fps[SSH_FPTYPE_DEFAULT], "%s openssh encrypted fp", keytype->name); /* * List the public half of the OpenSSH-formatted key in * OpenSSH format. */ setup_passphrases("sponge2", NULL); test(0, "puttygen", "-L", osfilename, NULL); /* * List the public half of the OpenSSH-formatted key in * IETF/ssh.com format. */ setup_passphrases("sponge2", NULL); test(0, "puttygen", "-p", osfilename, NULL); } /* * Export the private key into ssh.com format, this time * while encrypted. For RSA1 keys, this should give an * error. */ if (!supports_sshcom && type_known_early) { /* We'll know far enough in advance that this combination * is going to fail that we never ask for the passphrase */ setup_passphrases(NULL); } else { setup_passphrases("sponge2", NULL); } test(supports_sshcom ? 0 : 1, "puttygen", "-O", "private-sshcom", "-o", scfilename, filename, NULL); if (supports_sshcom) { /* * List the fingerprint of the ssh.com-formatted key. */ setup_passphrases("sponge2", NULL); test(0, "puttygen", "-l", scfilename, "-o", tmpfilename1, NULL); check_fp(tmpfilename1, fps[SSH_FPTYPE_DEFAULT], "%s ssh.com encrypted fp", keytype->name); /* * List the public half of the ssh.com-formatted key in * OpenSSH format. */ setup_passphrases("sponge2", NULL); test(0, "puttygen", "-L", scfilename, NULL); /* * List the public half of the ssh.com-formatted key in * IETF/ssh.com format. */ setup_passphrases("sponge2", NULL); test(0, "puttygen", "-p", scfilename, NULL); } if (supports_openssh && supports_sshcom) { /* * Convert from OpenSSH into ssh.com. */ setup_passphrases("sponge2", NULL); test(0, "puttygen", osfilename, "-o", tmpfilename1, "-O", "private-sshcom", NULL); /* * Convert from ssh.com back into a PuTTY key, * supplying the same comment as we had before we * started to ensure the comparison works. */ setup_passphrases("sponge2", NULL); test(0, "puttygen", tmpfilename1, "-C", "new-comment-2", "-o", tmpfilename2, NULL); /* * See if the PuTTY key thus generated is the same as * the original. */ filecmp(filename, tmpfilename2, "p->o->s->p encrypted %s", keytype->name); /* * Convert from ssh.com to OpenSSH. */ setup_passphrases("sponge2", NULL); test(0, "puttygen", scfilename, "-o", tmpfilename1, "-O", "private-openssh", NULL); /* * Convert from OpenSSH back into a PuTTY key, * supplying the same comment as we had before we * started to ensure the comparison works. */ setup_passphrases("sponge2", NULL); test(0, "puttygen", tmpfilename1, "-C", "new-comment-2", "-o", tmpfilename2, NULL); /* * See if the PuTTY key thus generated is the same as * the original. */ filecmp(filename, tmpfilename2, "p->s->o->p encrypted %s", keytype->name); /* * Finally, do a round-trip conversion between PuTTY * and ssh.com without involving OpenSSH, to test that * the key comment is preserved in that case. */ setup_passphrases("sponge2", NULL); test(0, "puttygen", "-O", "private-sshcom", "-o", tmpfilename1, filename, NULL); setup_passphrases("sponge2", NULL); test(0, "puttygen", tmpfilename1, "-o", tmpfilename2, NULL); filecmp(filename, tmpfilename2, "p->s->p encrypted %s", keytype->name); } /* * Load with the wrong passphrase. */ setup_passphrases("sponge8", NULL); test(1, "puttygen", "-C", "spurious-new-comment", filename, NULL); /* * Load a totally bogus file. */ setup_passphrases(NULL); test(1, "puttygen", "-C", "spurious-new-comment", pubfilename, NULL); for (FingerprintType fptype = 0; fptype < SSH_N_FPTYPES; fptype++) sfree(fps[fptype]); if (remove_files) { remove(filename); remove(pubfilename); remove(osfilename); remove(scfilename); remove(tmpfilename1); remove(tmpfilename2); } } printf("%d passes, %d fails\n", passes, fails); return fails == 0 ? 0 : 1; } putty-0.76/clicons.c0000644000175000017500000000062214072266307011361 00000000000000/* * clicons.c: definitions limited to tools that link against both * console.c and cmdline.c. */ #include "putty.h" static const LogPolicyVtable console_cli_logpolicy_vt = { .eventlog = console_eventlog, .askappend = console_askappend, .logging_error = console_logging_error, .verbose = cmdline_lp_verbose, }; LogPolicy console_cli_logpolicy[1] = {{ &console_cli_logpolicy_vt }}; putty-0.76/cmdgen.c0000644000175000017500000013714714072266307011201 00000000000000/* * cmdgen.c - command-line form of PuTTYgen */ #include #include #include #include #include #include #include #include #include "putty.h" #include "ssh.h" #include "sshkeygen.h" #include "mpint.h" static FILE *progress_fp = NULL; static bool linear_progress_phase; static unsigned last_progress_col; static ProgressPhase cmdgen_progress_add_linear( ProgressReceiver *prog, double c) { ProgressPhase ph = { .n = 0 }; return ph; } static ProgressPhase cmdgen_progress_add_probabilistic( ProgressReceiver *prog, double c, double p) { ProgressPhase ph = { .n = 1 }; return ph; } static void cmdgen_progress_start_phase(ProgressReceiver *prog, ProgressPhase p) { linear_progress_phase = (p.n == 0); last_progress_col = 0; } static void cmdgen_progress_report(ProgressReceiver *prog, double p) { unsigned new_col = p * 64 + 0.5; for (; last_progress_col < new_col; last_progress_col++) fputc('+', progress_fp); } static void cmdgen_progress_report_attempt(ProgressReceiver *prog) { if (progress_fp) { fputc('+', progress_fp); fflush(progress_fp); } } static void cmdgen_progress_report_phase_complete(ProgressReceiver *prog) { if (linear_progress_phase) cmdgen_progress_report(prog, 1.0); if (progress_fp) { fputc('\n', progress_fp); fflush(progress_fp); } } static const ProgressReceiverVtable cmdgen_progress_vt = { .add_linear = cmdgen_progress_add_linear, .add_probabilistic = cmdgen_progress_add_probabilistic, .ready = null_progress_ready, .start_phase = cmdgen_progress_start_phase, .report = cmdgen_progress_report, .report_attempt = cmdgen_progress_report_attempt, .report_phase_complete = cmdgen_progress_report_phase_complete, }; static ProgressReceiver cmdgen_progress = { .vt = &cmdgen_progress_vt }; /* * Stubs to let everything else link sensibly. */ char *x_get_default(const char *key) { return NULL; } void sk_cleanup(void) { } void showversion(void) { char *buildinfo_text = buildinfo("\n"); printf("puttygen: %s\n%s\n", ver, buildinfo_text); sfree(buildinfo_text); } void usage(bool standalone) { fprintf(standalone ? stderr : stdout, "Usage: puttygen ( keyfile | -t type [ -b bits ] )\n" " [ -C comment ] [ -P ] [ -q ]\n" " [ -o output-keyfile ] [ -O type | -l | -L" " | -p ]\n"); if (standalone) fprintf(stderr, "Use \"puttygen --help\" for more detail.\n"); } void help(void) { /* * Help message is an extended version of the usage message. So * start with that, plus a version heading. */ printf("PuTTYgen: key generator and converter for the PuTTY tools\n" "%s\n", ver); usage(false); printf(" -t specify key type when generating:\n" " eddsa, ecdsa, rsa, dsa, rsa1 use with -b\n" " ed25519, ed448 special cases of eddsa\n" " -b specify number of bits when generating key\n" " -C change or specify key comment\n" " -P change key passphrase\n" " -q quiet: do not display progress bar\n" " -O specify output type:\n" " private output PuTTY private key format\n" " private-openssh export OpenSSH private key\n" " private-openssh-new export OpenSSH private key " "(force new format)\n" " private-sshcom export ssh.com private key\n" " public RFC 4716 / ssh.com public key\n" " public-openssh OpenSSH public key\n" " fingerprint output the key fingerprint\n" " text output the key components as " "'name=0x####'\n" " -o specify output file\n" " -l equivalent to `-O fingerprint'\n" " -L equivalent to `-O public-openssh'\n" " -p equivalent to `-O public'\n" " --dump equivalent to `-O text'\n" " --reencrypt load a key and save it with fresh " "encryption\n" " --old-passphrase file\n" " specify file containing old key passphrase\n" " --new-passphrase file\n" " specify file containing new key passphrase\n" " --random-device device\n" " specify device to read entropy from (e.g. /dev/urandom)\n" " --primes select prime-generation method:\n" " probable conventional probabilistic prime finding\n" " proven numbers that have been proven to be prime\n" " proven-even also try harder for an even distribution\n" " --strong-rsa use \"strong\" primes as RSA key factors\n" " --ppk-param =[,=,...]\n" " specify parameters when writing PuTTY private key file " "format:\n" " version PPK format version (min 2, max 3, " "default 3)\n" " kdf key derivation function (argon2id, " "argon2i, argon2d)\n" " memory Kbyte of memory to use in passphrase " "hash\n" " (default 8192)\n" " time approx milliseconds to hash for " "(default 100)\n" " passes number of hash passes to run " "(alternative to 'time')\n" " parallelism number of parallelisable threads in the " "hash function\n" " (default 1)\n" ); } static bool move(char *from, char *to) { int ret; ret = rename(from, to); if (ret) { /* * This OS may require us to remove the original file first. */ remove(to); ret = rename(from, to); } if (ret) { perror("puttygen: cannot move new file on to old one"); return false; } return true; } static char *readpassphrase(const char *filename) { FILE *fp; char *line; fp = fopen(filename, "r"); if (!fp) { fprintf(stderr, "puttygen: cannot open %s: %s\n", filename, strerror(errno)); return NULL; } line = fgetline(fp); if (line) line[strcspn(line, "\r\n")] = '\0'; else if (ferror(fp)) fprintf(stderr, "puttygen: error reading from %s: %s\n", filename, strerror(errno)); else /* empty file */ line = dupstr(""); fclose(fp); return line; } #define DEFAULT_RSADSA_BITS 2048 /* For Unix in particular, but harmless if this main() is reused elsewhere */ const bool buildinfo_gtk_relevant = false; int main(int argc, char **argv) { char *infile = NULL; Filename *infilename = NULL, *outfilename = NULL; LoadedFile *infile_lf = NULL; BinarySource *infile_bs = NULL; enum { NOKEYGEN, RSA1, RSA2, DSA, ECDSA, EDDSA } keytype = NOKEYGEN; char *outfile = NULL, *outfiletmp = NULL; enum { PRIVATE, PUBLIC, PUBLICO, FP, OPENSSH_AUTO, OPENSSH_NEW, SSHCOM, TEXT } outtype = PRIVATE; int bits = -1; const char *comment = NULL; char *origcomment = NULL; bool change_passphrase = false, reencrypt = false; bool errs = false, nogo = false; int intype = SSH_KEYTYPE_UNOPENABLE; int sshver = 0; ssh2_userkey *ssh2key = NULL; RSAKey *ssh1key = NULL; strbuf *ssh2blob = NULL; char *ssh2alg = NULL; char *old_passphrase = NULL, *new_passphrase = NULL; bool load_encrypted; const char *random_device = NULL; int exit_status = 0; const PrimeGenerationPolicy *primegen = &primegen_probabilistic; bool strong_rsa = false; ppk_save_parameters params = ppk_save_default_parameters; FingerprintType fptype = SSH_FPTYPE_DEFAULT; if (is_interactive()) progress_fp = stderr; #define RETURN(status) do { exit_status = (status); goto out; } while (0) /* ------------------------------------------------------------------ * Parse the command line to figure out what we've been asked to do. */ /* * If run with no arguments at all, print the usage message and * return success. */ if (argc <= 1) { usage(true); RETURN(0); } /* * Parse command line arguments. */ while (--argc) { char *p = *++argv; if (p[0] == '-' && p[1]) { /* * An option. */ while (p && *++p) { char c = *p; switch (c) { case '-': { /* * Long option. */ char *opt, *val; opt = p++; /* opt will have _one_ leading - */ while (*p && *p != '=') p++; /* find end of option */ if (*p == '=') { *p++ = '\0'; val = p; } else val = NULL; if (!strcmp(opt, "-help")) { if (val) { errs = true; fprintf(stderr, "puttygen: option `-%s'" " expects no argument\n", opt); } else { help(); nogo = true; } } else if (!strcmp(opt, "-version")) { if (val) { errs = true; fprintf(stderr, "puttygen: option `-%s'" " expects no argument\n", opt); } else { showversion(); nogo = true; } } else if (!strcmp(opt, "-pgpfp")) { if (val) { errs = true; fprintf(stderr, "puttygen: option `-%s'" " expects no argument\n", opt); } else { /* support --pgpfp for consistency */ pgp_fingerprints(); nogo = true; } } else if (!strcmp(opt, "-old-passphrase")) { if (!val && argc > 1) --argc, val = *++argv; if (!val) { errs = true; fprintf(stderr, "puttygen: option `-%s'" " expects an argument\n", opt); } else { old_passphrase = readpassphrase(val); if (!old_passphrase) errs = true; } } else if (!strcmp(opt, "-new-passphrase")) { if (!val && argc > 1) --argc, val = *++argv; if (!val) { errs = true; fprintf(stderr, "puttygen: option `-%s'" " expects an argument\n", opt); } else { new_passphrase = readpassphrase(val); if (!new_passphrase) errs = true; } } else if (!strcmp(opt, "-random-device")) { if (!val && argc > 1) --argc, val = *++argv; if (!val) { errs = true; fprintf(stderr, "puttygen: option `-%s'" " expects an argument\n", opt); } else { random_device = val; } } else if (!strcmp(opt, "-dump")) { outtype = TEXT; } else if (!strcmp(opt, "-primes")) { if (!val && argc > 1) --argc, val = *++argv; if (!val) { errs = true; fprintf(stderr, "puttygen: option `-%s'" " expects an argument\n", opt); } else if (!strcmp(val, "probable") || !strcmp(val, "probabilistic")) { primegen = &primegen_probabilistic; } else if (!strcmp(val, "provable") || !strcmp(val, "proven") || !strcmp(val, "simple") || !strcmp(val, "maurer-simple")) { primegen = &primegen_provable_maurer_simple; } else if (!strcmp(val, "provable-even") || !strcmp(val, "proven-even") || !strcmp(val, "even") || !strcmp(val, "complex") || !strcmp(val, "maurer-complex")) { primegen = &primegen_provable_maurer_complex; } else { errs = true; fprintf(stderr, "puttygen: unrecognised prime-" "generation mode `%s'\n", val); } } else if (!strcmp(opt, "-strong-rsa")) { strong_rsa = true; } else if (!strcmp(opt, "-reencrypt")) { reencrypt = true; } else if (!strcmp(opt, "-ppk-param") || !strcmp(opt, "-ppk-params")) { if (!val && argc > 1) --argc, val = *++argv; if (!val) { errs = true; fprintf(stderr, "puttygen: option `-%s'" " expects an argument\n", opt); } else { char *nextval; for (; val; val = nextval) { nextval = strchr(val, ','); if (nextval) *nextval++ = '\0'; char *optvalue = strchr(val, '='); if (!optvalue) { errs = true; fprintf(stderr, "puttygen: PPK parameter " "'%s' expected a value\n", val); continue; } *optvalue++ = '\0'; /* Non-numeric options */ if (!strcmp(val, "kdf")) { if (!strcmp(optvalue, "Argon2id") || !strcmp(optvalue, "argon2id")) { params.argon2_flavour = Argon2id; } else if (!strcmp(optvalue, "Argon2i") || !strcmp(optvalue, "argon2i")) { params.argon2_flavour = Argon2i; } else if (!strcmp(optvalue, "Argon2d") || !strcmp(optvalue, "argon2d")) { params.argon2_flavour = Argon2d; } else { errs = true; fprintf(stderr, "puttygen: unrecognise" "d kdf '%s'\n", optvalue); } continue; } char *end; unsigned long n = strtoul(optvalue, &end, 0); if (!*optvalue || *end) { errs = true; fprintf(stderr, "puttygen: value '%s' for " "PPK parameter '%s': expected a " "number\n", optvalue, val); continue; } if (!strcmp(val, "version")) { params.fmt_version = n; } else if (!strcmp(val, "memory") || !strcmp(val, "mem")) { params.argon2_mem = n; } else if (!strcmp(val, "time")) { params.argon2_passes_auto = true; params.argon2_milliseconds = n; } else if (!strcmp(val, "passes")) { params.argon2_passes_auto = false; params.argon2_passes = n; } else if (!strcmp(val, "parallelism") || !strcmp(val, "parallel")) { params.argon2_parallelism = n; } else { errs = true; fprintf(stderr, "puttygen: unrecognised " "PPK parameter '%s'\n", val); continue; } } } } else { errs = true; fprintf(stderr, "puttygen: no such option `-%s'\n", opt); } p = NULL; break; } case 'h': case 'V': case 'P': case 'l': case 'L': case 'p': case 'q': /* * Option requiring no parameter. */ switch (c) { case 'h': help(); nogo = true; break; case 'V': showversion(); nogo = true; break; case 'P': change_passphrase = true; break; case 'l': outtype = FP; break; case 'L': outtype = PUBLICO; break; case 'p': outtype = PUBLIC; break; case 'q': progress_fp = NULL; break; } break; case 't': case 'b': case 'C': case 'O': case 'o': case 'E': /* * Option requiring parameter. */ p++; if (!*p && argc > 1) --argc, p = *++argv; else if (!*p) { fprintf(stderr, "puttygen: option `-%c' expects a" " parameter\n", c); errs = true; } /* * Now c is the option and p is the parameter. */ switch (c) { case 't': if (!strcmp(p, "rsa") || !strcmp(p, "rsa2")) keytype = RSA2, sshver = 2; else if (!strcmp(p, "rsa1")) keytype = RSA1, sshver = 1; else if (!strcmp(p, "dsa") || !strcmp(p, "dss")) keytype = DSA, sshver = 2; else if (!strcmp(p, "ecdsa")) keytype = ECDSA, sshver = 2; else if (!strcmp(p, "eddsa")) keytype = EDDSA, sshver = 2; else if (!strcmp(p, "ed25519")) keytype = EDDSA, bits = 255, sshver = 2; else if (!strcmp(p, "ed448")) keytype = EDDSA, bits = 448, sshver = 2; else { fprintf(stderr, "puttygen: unknown key type `%s'\n", p); errs = true; } break; case 'b': bits = atoi(p); break; case 'C': comment = p; break; case 'O': if (!strcmp(p, "public")) outtype = PUBLIC; else if (!strcmp(p, "public-openssh")) outtype = PUBLICO; else if (!strcmp(p, "private")) outtype = PRIVATE; else if (!strcmp(p, "fingerprint")) outtype = FP; else if (!strcmp(p, "private-openssh")) outtype = OPENSSH_AUTO, sshver = 2; else if (!strcmp(p, "private-openssh-new")) outtype = OPENSSH_NEW, sshver = 2; else if (!strcmp(p, "private-sshcom")) outtype = SSHCOM, sshver = 2; else if (!strcmp(p, "text")) outtype = TEXT; else { fprintf(stderr, "puttygen: unknown output type `%s'\n", p); errs = true; } break; case 'o': outfile = p; break; case 'E': if (!strcmp(p, "md5")) fptype = SSH_FPTYPE_MD5; else if (!strcmp(p, "sha256")) fptype = SSH_FPTYPE_SHA256; else { fprintf(stderr, "puttygen: unknown fingerprint " "type `%s'\n", p); errs = true; } break; } p = NULL; /* prevent continued processing */ break; default: /* * Unrecognised option. */ errs = true; fprintf(stderr, "puttygen: no such option `-%c'\n", c); break; } } } else { /* * A non-option argument. */ if (!infile) infile = p; else { errs = true; fprintf(stderr, "puttygen: cannot handle more than one" " input file\n"); } } } if (bits == -1) { /* * No explicit key size was specified. Default varies * depending on key type. */ switch (keytype) { case ECDSA: bits = 384; break; case EDDSA: bits = 255; break; default: bits = DEFAULT_RSADSA_BITS; break; } } if (keytype == ECDSA || keytype == EDDSA) { const char *name = (keytype == ECDSA ? "ECDSA" : "EdDSA"); const int *valid_lengths = (keytype == ECDSA ? ec_nist_curve_lengths : ec_ed_curve_lengths); size_t n_lengths = (keytype == ECDSA ? n_ec_nist_curve_lengths : n_ec_ed_curve_lengths); bool (*alg_and_curve_by_bits)(int, const struct ec_curve **, const ssh_keyalg **) = (keytype == ECDSA ? ec_nist_alg_and_curve_by_bits : ec_ed_alg_and_curve_by_bits); const struct ec_curve *curve; const ssh_keyalg *alg; if (!alg_and_curve_by_bits(bits, &curve, &alg)) { fprintf(stderr, "puttygen: invalid bits for %s, choose", name); for (size_t i = 0; i < n_lengths; i++) fprintf(stderr, "%s%d", (i == 0 ? " " : i == n_lengths-1 ? " or " : ", "), valid_lengths[i]); fputc('\n', stderr); errs = true; } } if (keytype == RSA2 || keytype == RSA1 || keytype == DSA) { if (bits < 256) { fprintf(stderr, "puttygen: cannot generate %s keys shorter than" " 256 bits\n", (keytype == DSA ? "DSA" : "RSA")); errs = true; } else if (bits < DEFAULT_RSADSA_BITS) { fprintf(stderr, "puttygen: warning: %s keys shorter than" " %d bits are probably not secure\n", (keytype == DSA ? "DSA" : "RSA"), DEFAULT_RSADSA_BITS); /* but this is just a warning, so proceed anyway */ } } if (errs) RETURN(1); if (nogo) RETURN(0); /* * If run with at least one argument _but_ not the required * ones, print the usage message and return failure. */ if (!infile && keytype == NOKEYGEN) { usage(true); RETURN(1); } /* ------------------------------------------------------------------ * Figure out further details of exactly what we're going to do. */ /* * Bomb out if we've been asked to both load and generate a * key. */ if (keytype != NOKEYGEN && infile) { fprintf(stderr, "puttygen: cannot both load and generate a key\n"); RETURN(1); } /* * We must save the private part when generating a new key. */ if (keytype != NOKEYGEN && (outtype != PRIVATE && outtype != OPENSSH_AUTO && outtype != OPENSSH_NEW && outtype != SSHCOM && outtype != TEXT)) { fprintf(stderr, "puttygen: this would generate a new key but " "discard the private part\n"); RETURN(1); } /* * Analyse the type of the input file, in case this affects our * course of action. */ if (infile) { const char *load_error; infilename = filename_from_str(infile); if (!strcmp(infile, "-")) infile_lf = lf_load_keyfile_fp(stdin, &load_error); else infile_lf = lf_load_keyfile(infilename, &load_error); if (!infile_lf) { fprintf(stderr, "puttygen: unable to load file `%s': %s\n", infile, load_error); RETURN(1); } infile_bs = BinarySource_UPCAST(infile_lf); intype = key_type_s(infile_bs); BinarySource_REWIND(infile_bs); switch (intype) { case SSH_KEYTYPE_UNOPENABLE: case SSH_KEYTYPE_UNKNOWN: fprintf(stderr, "puttygen: unable to load file `%s': %s\n", infile, key_type_to_str(intype)); RETURN(1); case SSH_KEYTYPE_SSH1: case SSH_KEYTYPE_SSH1_PUBLIC: if (sshver == 2) { fprintf(stderr, "puttygen: conversion from SSH-1 to SSH-2 keys" " not supported\n"); RETURN(1); } sshver = 1; break; case SSH_KEYTYPE_SSH2: case SSH_KEYTYPE_SSH2_PUBLIC_RFC4716: case SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH: case SSH_KEYTYPE_OPENSSH_PEM: case SSH_KEYTYPE_OPENSSH_NEW: case SSH_KEYTYPE_SSHCOM: if (sshver == 1) { fprintf(stderr, "puttygen: conversion from SSH-2 to SSH-1 keys" " not supported\n"); RETURN(1); } sshver = 2; break; case SSH_KEYTYPE_OPENSSH_AUTO: default: unreachable("Should never see these types on an input file"); } } /* * Determine the default output file, if none is provided. * * This will usually be equal to stdout, except that if the * input and output file formats are the same then the default * output is to overwrite the input. * * Also in this code, we bomb out if the input and output file * formats are the same and no other action is performed. */ if ((intype == SSH_KEYTYPE_SSH1 && outtype == PRIVATE) || (intype == SSH_KEYTYPE_SSH2 && outtype == PRIVATE) || (intype == SSH_KEYTYPE_OPENSSH_PEM && outtype == OPENSSH_AUTO) || (intype == SSH_KEYTYPE_OPENSSH_NEW && outtype == OPENSSH_NEW) || (intype == SSH_KEYTYPE_SSHCOM && outtype == SSHCOM)) { if (!outfile) { outfile = infile; outfiletmp = dupcat(outfile, ".tmp"); } if (!change_passphrase && !comment && !reencrypt) { fprintf(stderr, "puttygen: this command would perform no useful" " action\n"); RETURN(1); } } else { if (!outfile) { /* * Bomb out rather than automatically choosing to write * a private key file to stdout. */ if (outtype == PRIVATE || outtype == OPENSSH_AUTO || outtype == OPENSSH_NEW || outtype == SSHCOM) { fprintf(stderr, "puttygen: need to specify an output file\n"); RETURN(1); } } } /* * Figure out whether we need to load the encrypted part of the * key. This will be the case if (a) we need to write out * a private key format, (b) the entire input key file is * encrypted, or (c) we're outputting TEXT, in which case we * want all of the input file including private material if it * exists. */ bool intype_entirely_encrypted = intype == SSH_KEYTYPE_OPENSSH_PEM || intype == SSH_KEYTYPE_OPENSSH_NEW || intype == SSH_KEYTYPE_SSHCOM; bool intype_has_private = !(intype == SSH_KEYTYPE_SSH1_PUBLIC || intype == SSH_KEYTYPE_SSH2_PUBLIC_RFC4716 || intype == SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH); bool outtype_has_private = outtype == PRIVATE || outtype == OPENSSH_AUTO || outtype == OPENSSH_NEW || outtype == SSHCOM; if (outtype_has_private || intype_entirely_encrypted || (outtype == TEXT && intype_has_private)) load_encrypted = true; else load_encrypted = false; if (load_encrypted && !intype_has_private) { fprintf(stderr, "puttygen: cannot perform this action on a " "public-key-only input file\n"); RETURN(1); } /* ------------------------------------------------------------------ * Now we're ready to actually do some stuff. */ /* * Either load or generate a key. */ if (keytype != NOKEYGEN) { char *entropy; char default_comment[80]; struct tm tm; tm = ltime(); if (keytype == DSA) strftime(default_comment, 30, "dsa-key-%Y%m%d", &tm); else if (keytype == ECDSA) strftime(default_comment, 30, "ecdsa-key-%Y%m%d", &tm); else if (keytype == EDDSA && bits == 255) strftime(default_comment, 30, "ed25519-key-%Y%m%d", &tm); else if (keytype == EDDSA) strftime(default_comment, 30, "eddsa-key-%Y%m%d", &tm); else strftime(default_comment, 30, "rsa-key-%Y%m%d", &tm); entropy = get_random_data(bits / 8, random_device); if (!entropy) { fprintf(stderr, "puttygen: failed to collect entropy, " "could not generate key\n"); RETURN(1); } random_setup_special(); random_reseed(make_ptrlen(entropy, bits / 8)); smemclr(entropy, bits/8); sfree(entropy); PrimeGenerationContext *pgc = primegen_new_context(primegen); if (keytype == DSA) { struct dss_key *dsskey = snew(struct dss_key); dsa_generate(dsskey, bits, pgc, &cmdgen_progress); ssh2key = snew(ssh2_userkey); ssh2key->key = &dsskey->sshk; ssh1key = NULL; } else if (keytype == ECDSA) { struct ecdsa_key *ek = snew(struct ecdsa_key); ecdsa_generate(ek, bits); ssh2key = snew(ssh2_userkey); ssh2key->key = &ek->sshk; ssh1key = NULL; } else if (keytype == EDDSA) { struct eddsa_key *ek = snew(struct eddsa_key); eddsa_generate(ek, bits); ssh2key = snew(ssh2_userkey); ssh2key->key = &ek->sshk; ssh1key = NULL; } else { RSAKey *rsakey = snew(RSAKey); rsa_generate(rsakey, bits, strong_rsa, pgc, &cmdgen_progress); rsakey->comment = NULL; if (keytype == RSA1) { ssh1key = rsakey; } else { ssh2key = snew(ssh2_userkey); ssh2key->key = &rsakey->sshk; } } primegen_free_context(pgc); if (ssh2key) ssh2key->comment = dupstr(default_comment); if (ssh1key) ssh1key->comment = dupstr(default_comment); } else { const char *error = NULL; bool encrypted; assert(infile != NULL); sfree(origcomment); origcomment = NULL; /* * Find out whether the input key is encrypted. */ if (intype == SSH_KEYTYPE_SSH1) encrypted = rsa1_encrypted_s(infile_bs, &origcomment); else if (intype == SSH_KEYTYPE_SSH2) encrypted = ppk_encrypted_s(infile_bs, &origcomment); else encrypted = import_encrypted_s(infilename, infile_bs, intype, &origcomment); BinarySource_REWIND(infile_bs); /* * If so, ask for a passphrase. */ if (encrypted && load_encrypted) { if (!old_passphrase) { prompts_t *p = new_prompts(); int ret; p->to_server = false; p->from_server = false; p->name = dupstr("SSH key passphrase"); add_prompt(p, dupstr("Enter passphrase to load key: "), false); ret = console_get_userpass_input(p); assert(ret >= 0); if (!ret) { free_prompts(p); perror("puttygen: unable to read passphrase"); RETURN(1); } else { old_passphrase = prompt_get_result(p->prompts[0]); free_prompts(p); } } } else { old_passphrase = NULL; } switch (intype) { int ret; case SSH_KEYTYPE_SSH1: case SSH_KEYTYPE_SSH1_PUBLIC: ssh1key = snew(RSAKey); memset(ssh1key, 0, sizeof(RSAKey)); if (!load_encrypted) { strbuf *blob; BinarySource src[1]; sfree(origcomment); origcomment = NULL; blob = strbuf_new(); ret = rsa1_loadpub_s(infile_bs, BinarySink_UPCAST(blob), &origcomment, &error); BinarySource_BARE_INIT(src, blob->u, blob->len); get_rsa_ssh1_pub(src, ssh1key, RSA_SSH1_EXPONENT_FIRST); strbuf_free(blob); ssh1key->comment = dupstr(origcomment); ssh1key->private_exponent = NULL; ssh1key->p = NULL; ssh1key->q = NULL; ssh1key->iqmp = NULL; } else { ret = rsa1_load_s(infile_bs, ssh1key, old_passphrase, &error); } BinarySource_REWIND(infile_bs); if (ret > 0) error = NULL; else if (!error) error = "unknown error"; break; case SSH_KEYTYPE_SSH2: case SSH_KEYTYPE_SSH2_PUBLIC_RFC4716: case SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH: if (!load_encrypted) { sfree(origcomment); origcomment = NULL; ssh2blob = strbuf_new(); if (ppk_loadpub_s(infile_bs, &ssh2alg, BinarySink_UPCAST(ssh2blob), &origcomment, &error)) { const ssh_keyalg *alg = find_pubkey_alg(ssh2alg); if (alg) bits = ssh_key_public_bits( alg, ptrlen_from_strbuf(ssh2blob)); else bits = -1; } else { strbuf_free(ssh2blob); ssh2blob = NULL; } sfree(ssh2alg); } else { ssh2key = ppk_load_s(infile_bs, old_passphrase, &error); } BinarySource_REWIND(infile_bs); 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_PEM: case SSH_KEYTYPE_OPENSSH_NEW: case SSH_KEYTYPE_SSHCOM: ssh2key = import_ssh2_s(infile_bs, intype, old_passphrase, &error); if (ssh2key) { if (ssh2key != SSH2_WRONG_PASSPHRASE) error = NULL; else error = "wrong passphrase"; } else if (!error) error = "unknown error"; break; default: unreachable("bad input key type"); } if (error) { fprintf(stderr, "puttygen: error loading `%s': %s\n", infile, error); RETURN(1); } } /* * Change the comment if asked to. */ if (comment) { if (sshver == 1) { assert(ssh1key); sfree(ssh1key->comment); ssh1key->comment = dupstr(comment); } else { assert(ssh2key); sfree(ssh2key->comment); ssh2key->comment = dupstr(comment); } } /* * Unless we're changing the passphrase, the old one (if any) is a * reasonable default. */ if (!change_passphrase && old_passphrase && !new_passphrase) new_passphrase = dupstr(old_passphrase); /* * Prompt for a new passphrase if we have been asked to, or if * we have just generated a key. * * In the latter case, an exception is if we're producing text * output, because that output format doesn't support encryption * in any case. */ if (!new_passphrase && (change_passphrase || (keytype != NOKEYGEN && outtype != TEXT))) { prompts_t *p = new_prompts(); int ret; p->to_server = false; p->from_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); assert(ret >= 0); if (!ret) { free_prompts(p); perror("puttygen: unable to read new passphrase"); RETURN(1); } else { if (strcmp(prompt_get_result_ref(p->prompts[0]), prompt_get_result_ref(p->prompts[1]))) { free_prompts(p); fprintf(stderr, "puttygen: passphrases do not match\n"); RETURN(1); } new_passphrase = prompt_get_result(p->prompts[0]); free_prompts(p); } } if (new_passphrase && !*new_passphrase) { sfree(new_passphrase); new_passphrase = NULL; } /* * Write output. * * (In the case where outfile and outfiletmp are both NULL, * there is no semantic reason to initialise outfilename at * all; but we have to write _something_ to it or some compiler * will probably complain that it might be used uninitialised.) */ if (outfiletmp) outfilename = filename_from_str(outfiletmp); else outfilename = filename_from_str(outfile ? outfile : ""); switch (outtype) { bool ret; int real_outtype; case PRIVATE: random_ref(); /* we'll need a few random bytes in the save file */ if (sshver == 1) { assert(ssh1key); ret = rsa1_save_f(outfilename, ssh1key, new_passphrase); if (!ret) { fprintf(stderr, "puttygen: unable to save SSH-1 private key\n"); RETURN(1); } } else { assert(ssh2key); ret = ppk_save_f(outfilename, ssh2key, new_passphrase, ¶ms); if (!ret) { fprintf(stderr, "puttygen: unable to save SSH-2 private key\n"); RETURN(1); } } if (outfiletmp) { if (!move(outfiletmp, outfile)) RETURN(1); /* rename failed */ } break; case PUBLIC: case PUBLICO: { FILE *fp; if (outfile) { fp = f_open(outfilename, "w", false); if (!fp) { fprintf(stderr, "unable to open output file\n"); exit(1); } } else { fp = stdout; } if (sshver == 1) { ssh1_write_pubkey(fp, ssh1key); } else { if (!ssh2blob) { assert(ssh2key); ssh2blob = strbuf_new(); ssh_key_public_blob(ssh2key->key, BinarySink_UPCAST(ssh2blob)); } ssh2_write_pubkey(fp, ssh2key ? ssh2key->comment : origcomment, ssh2blob->s, ssh2blob->len, (outtype == PUBLIC ? SSH_KEYTYPE_SSH2_PUBLIC_RFC4716 : SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH)); } if (outfile) fclose(fp); break; } case FP: { FILE *fp; char *fingerprint; if (sshver == 1) { assert(ssh1key); fingerprint = rsa_ssh1_fingerprint(ssh1key); } else { if (ssh2key) { fingerprint = ssh2_fingerprint(ssh2key->key, fptype); } else { assert(ssh2blob); fingerprint = ssh2_fingerprint_blob( ptrlen_from_strbuf(ssh2blob), fptype); } } if (outfile) { fp = f_open(outfilename, "w", false); if (!fp) { fprintf(stderr, "unable to open output file\n"); exit(1); } } else { fp = stdout; } fprintf(fp, "%s\n", fingerprint); if (outfile) fclose(fp); sfree(fingerprint); break; } case OPENSSH_AUTO: case OPENSSH_NEW: case SSHCOM: assert(sshver == 2); assert(ssh2key); random_ref(); /* both foreign key types require randomness, * for IV or padding */ switch (outtype) { case OPENSSH_AUTO: real_outtype = SSH_KEYTYPE_OPENSSH_AUTO; break; case OPENSSH_NEW: real_outtype = SSH_KEYTYPE_OPENSSH_NEW; break; case SSHCOM: real_outtype = SSH_KEYTYPE_SSHCOM; break; default: unreachable("control flow goof"); } ret = export_ssh2(outfilename, real_outtype, ssh2key, new_passphrase); if (!ret) { fprintf(stderr, "puttygen: unable to export key\n"); RETURN(1); } if (outfiletmp) { if (!move(outfiletmp, outfile)) RETURN(1); /* rename failed */ } break; case TEXT: { key_components *kc; if (sshver == 1) { assert(ssh1key); kc = rsa_components(ssh1key); } else { if (ssh2key) { kc = ssh_key_components(ssh2key->key); } else { assert(ssh2blob); BinarySource src[1]; BinarySource_BARE_INIT_PL(src, ptrlen_from_strbuf(ssh2blob)); ptrlen algname = get_string(src); const ssh_keyalg *alg = find_pubkey_alg_len(algname); if (!alg) { fprintf(stderr, "puttygen: cannot extract key components " "from public key of unknown type '%.*s'\n", PTRLEN_PRINTF(algname)); RETURN(1); } ssh_key *sk = ssh_key_new_pub( alg, ptrlen_from_strbuf(ssh2blob)); if (!sk) { fprintf(stderr, "puttygen: unable to decode public key\n"); RETURN(1); } kc = ssh_key_components(sk); ssh_key_free(sk); } } FILE *fp; if (outfile) { fp = f_open(outfilename, "w", false); if (!fp) { fprintf(stderr, "unable to open output file\n"); exit(1); } } else { fp = stdout; } for (size_t i = 0; i < kc->ncomponents; i++) { if (kc->components[i].is_mp_int) { char *hex = mp_get_hex(kc->components[i].mp); fprintf(fp, "%s=0x%s\n", kc->components[i].name, hex); smemclr(hex, strlen(hex)); sfree(hex); } else { fprintf(fp, "%s=\"", kc->components[i].name); write_c_string_literal(fp, ptrlen_from_asciz( kc->components[i].text)); fputs("\"\n", fp); } } if (outfile) fclose(fp); key_components_free(kc); break; } } out: #undef RETURN if (old_passphrase) { smemclr(old_passphrase, strlen(old_passphrase)); sfree(old_passphrase); } if (new_passphrase) { smemclr(new_passphrase, strlen(new_passphrase)); sfree(new_passphrase); } if (ssh1key) { freersakey(ssh1key); sfree(ssh1key); } if (ssh2key && ssh2key != SSH2_WRONG_PASSPHRASE) { sfree(ssh2key->comment); if (ssh2key->key) ssh_key_free(ssh2key->key); sfree(ssh2key); } if (ssh2blob) strbuf_free(ssh2blob); sfree(origcomment); if (infilename) filename_free(infilename); if (infile_lf) lf_free(infile_lf); if (outfilename) filename_free(outfilename); sfree(outfiletmp); return exit_status; } putty-0.76/cmdline.c0000644000175000017500000007566614072266307011366 00000000000000/* * cmdline.c - command-line parsing shared between many of the * PuTTY applications */ #include #include #include #include "putty.h" /* * Some command-line parameters need to be saved up until after * we've loaded the saved session which will form the basis of our * eventual running configuration. For this we use the macro * SAVEABLE, which notices if the `need_save' parameter is set and * saves the parameter and value on a list. * * We also assign priorities to saved parameters, just to slightly * ameliorate silly ordering problems. For example, if you specify * a saved session to load, it will be loaded _before_ all your * local modifications such as -L are evaluated; and if you specify * a protocol and a port, the protocol is set up first so that the * port can override its choice of port number. * * (In fact -load is not saved at all, since in at least Plink the * processing of further command-line options depends on whether or * not the loaded session contained a hostname. So it must be * executed immediately.) */ #define NPRIORITIES 2 struct cmdline_saved_param { char *p, *value; }; struct cmdline_saved_param_set { struct cmdline_saved_param *params; size_t nsaved, savesize; }; /* * C guarantees this structure will be initialised to all zero at * program start, which is exactly what we want. */ static struct cmdline_saved_param_set saves[NPRIORITIES]; static void cmdline_save_param(const char *p, const char *value, int pri) { sgrowarray(saves[pri].params, saves[pri].savesize, saves[pri].nsaved); saves[pri].params[saves[pri].nsaved].p = dupstr(p); saves[pri].params[saves[pri].nsaved].value = dupstr(value); saves[pri].nsaved++; } 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; saves[pri].savesize = 0; saves[pri].nsaved = 0; } } #define SAVEABLE(pri) do { \ if (need_save) { cmdline_save_param(p, value, pri); return ret; } \ } while (0) /* * Similar interface to seat_get_userpass_input(), except that here a * -1 return means that we aren't capable of processing the prompt and * someone else should do it. */ int cmdline_get_passwd_input(prompts_t *p) { static bool tried_once = false; /* * We only handle prompts which don't echo (which we assume to be * passwords), and (currently) we only cope with a password prompt * that comes in a prompt-set on its own. */ if (!cmdline_password || p->n_prompts != 1 || p->prompts[0]->echo) { return -1; } /* * 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 = true; return 1; } static bool cmdline_check_unavailable(int flag, const char *p) { if (cmdline_tooltype & flag) { cmdline_error("option \"%s\" not available in this tool", p); return true; } return false; } #define UNAVAILABLE_IN(flag) do { \ if (cmdline_check_unavailable(flag, p)) return ret; \ } while (0) /* * Process a standard command-line parameter. `p' is the parameter * in question; `value' is the subsequent element of argv, which * may or may not be required as an operand to the parameter. * If `need_save' is 1, arguments which need to be saved as * described at this top of this file are, for later execution; * if 0, they are processed normally. (-1 is a special value used * by pterm to count arguments for a preliminary pass through the * argument list; it causes immediate return with an appropriate * value with no action taken.) * Return value is 2 if both arguments were used; 1 if only p was * used; 0 if the parameter wasn't one we recognised; -2 if it * should have been 2 but value was NULL. */ #define RETURN(x) do { \ if ((x) == 2 && !value) return -2; \ ret = x; \ if (need_save < 0) return x; \ } while (0) static bool seen_hostname_argument = false; static bool seen_port_argument = false; static bool seen_verbose_option = false; static bool loaded_session = false; bool cmdline_verbose(void) { return seen_verbose_option; } bool cmdline_seat_verbose(Seat *seat) { return cmdline_verbose(); } bool cmdline_lp_verbose(LogPolicy *lp) { return cmdline_verbose(); } bool cmdline_loaded_session(void) { return loaded_session; } static void set_protocol(Conf *conf, int protocol) { settings_set_default_protocol(protocol); conf_set_int(conf, CONF_protocol, protocol); } static void set_port(Conf *conf, int port) { settings_set_default_port(port); conf_set_int(conf, CONF_port, port); } int cmdline_process_param(const char *p, char *value, int need_save, Conf *conf) { int ret = 0; if (p[0] != '-') { if (need_save < 0) return 0; /* * Common handling for the tools whose initial command-line * arguments specify a hostname to connect to, i.e. PuTTY and * Plink. Doesn't count the file transfer tools, because their * hostname specification appears as part of a more * complicated scheme. */ if ((cmdline_tooltype & TOOLTYPE_HOST_ARG) && !seen_hostname_argument && (!(cmdline_tooltype & TOOLTYPE_HOST_ARG_FROM_LAUNCHABLE_LOAD) || !loaded_session || !conf_launchable(conf))) { /* * Treat this argument as a host name, if we have not yet * seen a host name argument or -load. * * Exception, in some tools (Plink): if we have seen -load * but it didn't create a launchable session, then we * still accept a hostname argument following that -load. * This allows you to make saved sessions that configure * lots of other stuff (colour schemes, terminal settings * etc) and then say 'putty -load sessionname hostname'. * * Also, we carefully _don't_ test conf for launchability * if we haven't been explicitly told to load a session * (otherwise saving a host name into Default Settings * would cause 'putty' on its own to immediately launch * the default session and never be able to do anything * else). */ if (!strncmp(p, "telnet:", 7)) { /* * If the argument starts with "telnet:", set the * protocol to Telnet and process the string as a * Telnet URL. */ /* * Skip the "telnet:" or "telnet://" prefix. */ p += 7; if (p[0] == '/' && p[1] == '/') p += 2; conf_set_int(conf, CONF_protocol, PROT_TELNET); /* * The next thing we expect is a host name. */ { const char *host = p; char *buf; p += host_strcspn(p, ":/"); buf = dupprintf("%.*s", (int)(p - host), host); conf_set_str(conf, CONF_host, buf); sfree(buf); seen_hostname_argument = true; } /* * If the host name is followed by a colon, then * expect a port number after it. */ if (*p == ':') { p++; conf_set_int(conf, CONF_port, atoi(p)); /* * Set the flag that will stop us from treating * the next argument as a separate port; this one * counts as explicitly provided. */ seen_port_argument = true; } else { conf_set_int(conf, CONF_port, -1); } } else { char *user = NULL, *hostname = NULL; const char *hostname_after_user; int port_override = -1; size_t len; /* * Otherwise, treat it as a bare host name. */ if (cmdline_tooltype & TOOLTYPE_HOST_ARG_PROTOCOL_PREFIX) { /* * Here Plink checks for a comma-separated * protocol prefix, e.g. 'ssh,hostname' or * 'ssh,user@hostname'. * * I'm not entirely sure why; this behaviour dates * from 2000 and isn't explained. But I _think_ it * has to do with CVS transport or similar use * cases, in which the end user invokes the SSH * client indirectly, via some means that only * lets them pass a single string argument, and it * was occasionally useful to shoehorn the choice * of protocol into that argument. */ const char *comma = strchr(p, ','); if (comma) { char *prefix = dupprintf("%.*s", (int)(comma - p), p); const struct BackendVtable *vt = backend_vt_from_name(prefix); if (vt) { set_protocol(conf, vt->protocol); port_override = vt->default_port; } else { cmdline_error("unrecognised protocol prefix '%s'", prefix); } sfree(prefix); p = comma + 1; } } hostname_after_user = p; if (cmdline_tooltype & TOOLTYPE_HOST_ARG_CAN_BE_SESSION) { /* * If the hostname argument can also be a saved * session (see below), then here we also check * for a user@ prefix, which will override the * username from the saved session. * * (If the hostname argument _isn't_ a saved * session, we don't do this.) */ const char *at = strrchr(p, '@'); if (at) { user = dupprintf("%.*s", (int)(at - p), p); hostname_after_user = at + 1; } } /* * Write the whole hostname argument (minus only that * optional protocol prefix) into the existing Conf, * for tools that don't treat it as a saved session * and as a fallback for those that do. */ hostname = dupstr(p + strspn(p, " \t")); len = strlen(hostname); while (len > 0 && (hostname[len-1] == ' ' || hostname[len-1] == '\t')) hostname[--len] = '\0'; seen_hostname_argument = true; conf_set_str(conf, CONF_host, hostname); if ((cmdline_tooltype & TOOLTYPE_HOST_ARG_CAN_BE_SESSION) && !loaded_session) { /* * For some tools, we equivocate between a * hostname argument and an argument naming a * saved session. Here we attempt to load a * session with the specified name, and if that * session exists and is launchable, we overwrite * the entire Conf with it. * * We skip this check if a -load option has * already happened, so that * * plink -load non-launchable-session hostname * * will treat 'hostname' as a hostname _even_ if a * saved session called 'hostname' exists. (This * doesn't lose any functionality someone could * have needed, because if 'hostname' did cause a * session to be loaded, then it would overwrite * everything from the previously loaded session. * So if that was the behaviour someone wanted, * then they could get it by leaving off the * -load completely.) */ Conf *conf2 = conf_new(); if (do_defaults(hostname_after_user, conf2) && conf_launchable(conf2)) { conf_copy_into(conf, conf2); loaded_session = true; /* And override the username if one was given. */ if (user) conf_set_str(conf, CONF_username, user); } conf_free(conf2); } sfree(hostname); sfree(user); if (port_override >= 0) conf_set_int(conf, CONF_port, port_override); } return 1; } else if ((cmdline_tooltype & TOOLTYPE_PORT_ARG) && !seen_port_argument) { /* * If we've already got a host name from the command line * (either as a hostname argument or a qualifying -load), * but not a port number, then treat the next argument as * a port number. * * We handle this by calling ourself recursively to * pretend we received a -P argument, so that it will be * deferred until it's a good moment to run it. */ char *dup = dupstr(p); /* 'value' is not a const char * */ int retd = cmdline_process_param("-P", dup, 1, conf); sfree(dup); assert(retd == 2); seen_port_argument = true; return 1; } else { /* * Refuse to recognise this argument, and give it back to * the tool's own command-line processing. */ return 0; } } if (!strcmp(p, "-load")) { RETURN(2); /* This parameter must be processed immediately rather than being * saved. */ do_defaults(value, conf); loaded_session = true; return 2; } for (size_t i = 0; backends[i]; i++) { if (p[0] == '-' && !strcmp(p+1, backends[i]->id)) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); SAVEABLE(0); set_protocol(conf, backends[i]->protocol); if (backends[i]->default_port) set_port(conf, backends[i]->default_port); if (backends[i]->protocol == PROT_SERIAL) { /* Special handling: the 'where to connect to' argument will * have been placed into CONF_host, but for this protocol, it * needs to be in CONF_serline */ conf_set_str(conf, CONF_serline, conf_get_str(conf, CONF_host)); } return 1; } } if (!strcmp(p, "-v")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_NO_VERBOSE_OPTION); seen_verbose_option = true; } 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, "-hostkey")) { char *dup; RETURN(2); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); SAVEABLE(0); dup = dupstr(value); if (!validate_manual_hostkey(dup)) { cmdline_error("'%s' is not a valid format for a manual host " "key specification", value); sfree(dup); return ret; } conf_set_str_str(conf, CONF_ssh_manual_hostkeys, dup, ""); sfree(dup); } 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 = host_strchr(value, ':'); while (qq) { char *qqq = host_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 = host_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")) { const char *filename; FILE *fp; RETURN(2); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(0); filename = value; fp = fopen(filename, "r"); if (!fp) { cmdline_error("unable to open command file \"%s\"", filename); return ret; } strbuf *command = strbuf_new(); char readbuf[4096]; while (1) { size_t nread = fread(readbuf, 1, sizeof(readbuf), fp); if (nread == 0) break; put_data(command, readbuf, nread); } fclose(fp); conf_set_str(conf, CONF_remote_cmd, command->s); conf_set_str(conf, CONF_remote_cmd2, ""); conf_set_bool(conf, CONF_nopty, true); /* command => no terminal */ strbuf_free(command); } if (!strcmp(p, "-P")) { RETURN(2); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); SAVEABLE(1); /* lower priority than -ssh, -telnet, etc */ 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_bool(conf, CONF_tryagent, true); } if (!strcmp(p, "-noagent") || !strcmp(p, "-nopagent") || !strcmp(p, "-nopageant")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); SAVEABLE(0); conf_set_bool(conf, CONF_tryagent, false); } if (!strcmp(p, "-no-trivial-auth")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); SAVEABLE(0); conf_set_bool(conf, CONF_ssh_no_trivial_userauth, true); } if (!strcmp(p, "-share")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); SAVEABLE(0); conf_set_bool(conf, CONF_ssh_connection_sharing, true); } if (!strcmp(p, "-noshare")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); SAVEABLE(0); conf_set_bool(conf, CONF_ssh_connection_sharing, false); } if (!strcmp(p, "-A")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(0); conf_set_bool(conf, CONF_agentfwd, true); } if (!strcmp(p, "-a")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(0); conf_set_bool(conf, CONF_agentfwd, false); } if (!strcmp(p, "-X")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(0); conf_set_bool(conf, CONF_x11_forward, true); } if (!strcmp(p, "-x")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(0); conf_set_bool(conf, CONF_x11_forward, false); } if (!strcmp(p, "-t")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(1); /* lower priority than -m */ conf_set_bool(conf, CONF_nopty, false); } if (!strcmp(p, "-T")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(1); conf_set_bool(conf, CONF_nopty, true); } if (!strcmp(p, "-N")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER | TOOLTYPE_NONNETWORK); SAVEABLE(0); conf_set_bool(conf, CONF_ssh_no_shell, true); } if (!strcmp(p, "-C")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); SAVEABLE(0); conf_set_bool(conf, CONF_compression, true); } 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; } } if (!strcmp(p, "-sessionlog")) { Filename *fn; RETURN(2); UNAVAILABLE_IN(TOOLTYPE_FILETRANSFER); /* but available even in TOOLTYPE_NONNETWORK, cf pterm "-log" */ SAVEABLE(0); fn = filename_from_str(value); conf_set_filename(conf, CONF_logfilename, fn); conf_set_int(conf, CONF_logtype, LGTYP_DEBUG); filename_free(fn); } if (!strcmp(p, "-sshlog") || !strcmp(p, "-sshrawlog")) { Filename *fn; RETURN(2); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); SAVEABLE(0); fn = filename_from_str(value); conf_set_filename(conf, CONF_logfilename, fn); conf_set_int(conf, CONF_logtype, !strcmp(p, "-sshlog") ? LGTYP_PACKETS : /* !strcmp(p, "-sshrawlog") ? */ LGTYP_SSHRAW); filename_free(fn); } if (!strcmp(p, "-logoverwrite")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); SAVEABLE(0); conf_set_int(conf, CONF_logxfovr, LGXF_OVR); } if (!strcmp(p, "-logappend")) { RETURN(1); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); SAVEABLE(0); conf_set_int(conf, CONF_logxfovr, LGXF_APN); } if (!strcmp(p, "-proxycmd")) { RETURN(2); UNAVAILABLE_IN(TOOLTYPE_NONNETWORK); SAVEABLE(0); conf_set_int(conf, CONF_proxy_type, PROXY_CMD); conf_set_str(conf, CONF_proxy_telnet_command, value); } #ifdef _WINDOWS /* * Cross-tool options only available on Windows. */ if (!strcmp(p, "-restrict-acl") || !strcmp(p, "-restrict_acl") || !strcmp(p, "-restrictacl")) { RETURN(1); restrict_process_acl(); } #endif return ret; /* unrecognised */ } void cmdline_run_saved(Conf *conf) { for (size_t pri = 0; pri < NPRIORITIES; pri++) { for (size_t i = 0; i < saves[pri].nsaved; i++) { cmdline_process_param(saves[pri].params[i].p, saves[pri].params[i].value, 0, conf); sfree(saves[pri].params[i].p); sfree(saves[pri].params[i].value); } saves[pri].nsaved = 0; } } bool cmdline_host_ok(Conf *conf) { /* * Return true if the command-line arguments we've processed in * TOOLTYPE_HOST_ARG mode are sufficient to justify launching a * session. */ assert(cmdline_tooltype & TOOLTYPE_HOST_ARG); /* * Of course, if we _can't_ launch a session, the answer is * clearly no. */ if (!conf_launchable(conf)) return false; /* * But also, if we haven't seen either a -load option or a * hostname argument, i.e. the only saved settings we've loaded * are Default Settings plus any non-hostname-based stuff from the * command line, then the answer is still no, _even_ if this Conf * is launchable. Otherwise, if you saved your favourite hostname * into Default Settings, then just running 'putty' without * arguments would connect to it without ever offering you the * option to connect to something else or change the setting. */ if (!seen_hostname_argument && !loaded_session) return false; return true; } putty-0.76/conf.c0000644000175000017500000003633314072266307010664 00000000000000/* * conf.c: implementation of the internal storage format used for * the configuration of a PuTTY session. */ #include #include #include #include "tree234.h" #include "putty.h" /* * Enumeration of types used in keys and values. */ typedef enum { TYPE_NONE, TYPE_BOOL, 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; }; /* Variant form of struct key which doesn't contain dynamic data, used * for lookups. */ struct constkey { int primary; union { int i; const char *s; } secondary; }; struct value { union { bool boolval; 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; } } static int conf_cmp_constkey(void *av, void *bv) { struct key *a = (struct key *)av; struct constkey *b = (struct constkey *)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_BOOL: to->u.boolval = from->u.boolval; break; 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; } bool conf_get_bool(Conf *conf, int primary) { struct key key; struct conf_entry *entry; assert(subkeytypes[primary] == TYPE_NONE); assert(valuetypes[primary] == TYPE_BOOL); key.primary = primary; entry = find234(conf->tree, &key, NULL); assert(entry); return entry->value.u.boolval; } 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 constkey 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, conf_cmp_constkey, 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 constkey 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, conf_cmp_constkey, 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_bool(Conf *conf, int primary, bool value) { struct conf_entry *entry = snew(struct conf_entry); assert(subkeytypes[primary] == TYPE_NONE); assert(valuetypes[primary] == TYPE_BOOL); entry->key.primary = primary; entry->value.u.boolval = value; conf_insert(conf, entry); } 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); } void conf_serialise(BinarySink *bs, Conf *conf) { int i; struct conf_entry *entry; for (i = 0; (entry = index234(conf->tree, i)) != NULL; i++) { put_uint32(bs, entry->key.primary); switch (subkeytypes[entry->key.primary]) { case TYPE_INT: put_uint32(bs, entry->key.secondary.i); break; case TYPE_STR: put_asciz(bs, entry->key.secondary.s); break; } switch (valuetypes[entry->key.primary]) { case TYPE_BOOL: put_bool(bs, entry->value.u.boolval); break; case TYPE_INT: put_uint32(bs, entry->value.u.intval); break; case TYPE_STR: put_asciz(bs, entry->value.u.stringval); break; case TYPE_FILENAME: filename_serialise(bs, entry->value.u.fileval); break; case TYPE_FONT: fontspec_serialise(bs, entry->value.u.fontval); break; } } put_uint32(bs, 0xFFFFFFFFU); } bool conf_deserialise(Conf *conf, BinarySource *src) { struct conf_entry *entry; unsigned primary; while (1) { primary = get_uint32(src); if (get_err(src)) return false; if (primary == 0xFFFFFFFFU) return true; if (primary >= N_CONFIG_OPTIONS) return false; entry = snew(struct conf_entry); entry->key.primary = primary; switch (subkeytypes[entry->key.primary]) { case TYPE_INT: entry->key.secondary.i = toint(get_uint32(src)); break; case TYPE_STR: entry->key.secondary.s = dupstr(get_asciz(src)); break; } switch (valuetypes[entry->key.primary]) { case TYPE_BOOL: entry->value.u.boolval = get_bool(src); break; case TYPE_INT: entry->value.u.intval = toint(get_uint32(src)); break; case TYPE_STR: entry->value.u.stringval = dupstr(get_asciz(src)); break; case TYPE_FILENAME: entry->value.u.fileval = filename_deserialise(src); break; case TYPE_FONT: entry->value.u.fontval = fontspec_deserialise(src); break; } if (get_err(src)) { free_entry(entry); return false; } conf_insert(conf, entry); } } putty-0.76/config.c0000644000175000017500000040221614072266307011201 00000000000000/* * config.c - the platform-independent parts of the PuTTY * configuration box. */ #include #include #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, dlgparam *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); } } void conf_radiobutton_bool_handler(union control *ctrl, dlgparam *dlg, void *data, int event) { int button; Conf *conf = (Conf *)data; /* * Same as conf_radiobutton_handler, but using conf_set_bool in * place of conf_set_int, because it's dealing with a bool-typed * config option. */ if (event == EVENT_REFRESH) { int val = conf_get_bool(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_bool(conf, ctrl->radio.context.i, ctrl->radio.buttondata[button].i); } } #define CHECKBOX_INVERT (1<<30) void conf_checkbox_handler(union control *ctrl, dlgparam *dlg, void *data, int event) { int key; bool 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 = true; } else invert = false; /* * 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) { bool val = conf_get_bool(conf, key); dlg_checkbox_set(ctrl, dlg, (!val ^ !invert)); } else if (event == EVENT_VALCHANGE) { conf_set_bool(conf, key, !dlg_checkbox_get(ctrl,dlg) ^ !invert); } } void conf_editbox_handler(union control *ctrl, dlgparam *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, dlgparam *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, dlgparam *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, dlgparam *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, dlgparam *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, *protradio, *protlist; bool mid_refresh; }; /* * Shared handler for protocol radio-button and drop-list controls. * Handles the interaction of those two controls, and also changes * the setting of the port box to match the protocol if necessary, * and refreshes both host and port boxes when switching to/from the * serial backend. */ static void config_protocols_handler(union control *ctrl, dlgparam *dlg, void *data, int event) { Conf *conf = (Conf *)data; int curproto = conf_get_int(conf, CONF_protocol); struct hostport *hp = (struct hostport *)ctrl->generic.context.p; if (event == EVENT_REFRESH) { /* * Refresh the states of the controls from Conf. * * When refreshing these controls, we have to watch out for * re-entrancy: because there are two controls involved, the * refresh is not atomic, so the VALCHANGE and/or SELCHANGE * callbacks resulting from our updates here might cause other * settings here to change unwantedly. (E.g. setting the list * selection shouldn't trigger the SELCHANGE side effect of * selecting the Other radio button; setting the radio button * to Other here shouldn't have the side effect of selecting * whatever protocol is _currently_ selected in the list box, * if we haven't selected the right one yet.) */ hp->mid_refresh = true; if (ctrl == hp->protradio) { /* Available buttons were set up when control was created. * Just select one of them, possibly. */ for (int button = 0; button < ctrl->radio.nbuttons; button++) /* The final button is "Other:". If we reach that one, the * current protocol must be in the drop list, so we should * select the "Other:" button. */ if (curproto == ctrl->radio.buttondata[button].i || button == ctrl->radio.nbuttons-1) { dlg_radiobutton_set(ctrl, dlg, button); break; } } else if (ctrl == hp->protlist) { int curentry = -1; dlg_update_start(ctrl, dlg); dlg_listbox_clear(ctrl, dlg); assert(n_ui_backends > 0 && n_ui_backends < PROTOCOL_LIMIT); for (size_t i = n_ui_backends; i < PROTOCOL_LIMIT && backends[i]; i++) { dlg_listbox_addwithid(ctrl, dlg, backends[i]->displayname, backends[i]->protocol); if (backends[i]->protocol == curproto) curentry = i - n_ui_backends; } if (curentry > 0) { /* * The currently configured protocol is one of the * list-box ones, so select it in protlist. * * (The corresponding refresh event for protradio * should have selected the "Other:" radio button, to * keep things consistent.) */ dlg_listbox_select(ctrl, dlg, curentry); } else { /* * If the currently configured protocol is one of the * radio buttons, we must still ensure *something* is * selected in the list box. The sensible default is * the first list element, which be_*.c ought to have * arranged to be the 'runner-up' in protocol * popularity out of the ones relegated to the list * box. * * We don't make much effort to retain the state of * the list box when it doesn't correspond to an * actual protocol. So it's easy for this case to be * reached as a side effect of other actions, e.g. * loading a saved session that has a radio-button * protocol configured. */ dlg_listbox_select(ctrl, dlg, 0); } dlg_update_done(ctrl, dlg); } hp->mid_refresh = false; } else if (!hp->mid_refresh) { /* * Potentially update Conf from the states of the controls. */ int newproto = curproto; if (event == EVENT_VALCHANGE && ctrl == hp->protradio) { int button = dlg_radiobutton_get(ctrl, dlg); assert(button >= 0 && button < ctrl->radio.nbuttons); if (ctrl->radio.buttondata[button].i == -1) { /* * The 'Other' radio button was selected, which means we * have to set CONF_protocol based on the currently * selected list box entry. * * (We conditionalise this on there _being_ a selected * list box entry. I hope the case where nothing is * selected can't actually come up except during * initialisation, and I also hope that hp->mid_session * will prevent that case from getting here. But as a * last-ditch fallback, this if statement should at least * guarantee that we don't pass a nonsense value to * dlg_listbox_getid.) */ int i = dlg_listbox_index(hp->protlist, dlg); if (i >= 0) newproto = dlg_listbox_getid(hp->protlist, dlg, i); } else { newproto = ctrl->radio.buttondata[button].i; } } else if (event == EVENT_SELCHANGE && ctrl == hp->protlist) { int i = dlg_listbox_index(ctrl, dlg); if (i >= 0) { newproto = dlg_listbox_getid(ctrl, dlg, i); /* Select the "Other" radio button, too */ dlg_radiobutton_set(hp->protradio, dlg, hp->protradio->radio.nbuttons-1); } } if (newproto != curproto) { conf_set_int(conf, CONF_protocol, newproto); const struct BackendVtable *cvt = backend_vt_from_proto(curproto); const struct BackendVtable *nvt = backend_vt_from_proto(newproto); assert(cvt); assert(nvt); /* * 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. */ int port = conf_get_int(conf, CONF_port); if (port == cvt->default_port) conf_set_int(conf, CONF_port, nvt->default_port); dlg_refresh(hp->host, dlg); dlg_refresh(hp->port, dlg); } } } static void loggingbuttons_handler(union control *ctrl, dlgparam *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, dlgparam *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_bool(conf, CONF_nethack_keypad)) button = 2; else if (conf_get_bool(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_bool(conf, CONF_app_keypad, false); conf_set_bool(conf, CONF_nethack_keypad, true); } else { conf_set_bool(conf, CONF_app_keypad, (button != 0)); conf_set_bool(conf, CONF_nethack_keypad, false); } } } static void cipherlist_handler(union control *ctrl, dlgparam *dlg, void *data, int event) { Conf *conf = (Conf *)data; if (event == EVENT_REFRESH) { int i; static const struct { const char *s; int c; } ciphers[] = { { "ChaCha20 (SSH-2 only)", CIPHER_CHACHA20 }, { "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; const 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, dlgparam *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, dlgparam *dlg, void *data, int event) { Conf *conf = (Conf *)data; if (event == EVENT_REFRESH) { int i; static const struct { const 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 }, { "ECDH key exchange", KEX_ECDH }, { "-- 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; const 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 hklist_handler(union control *ctrl, dlgparam *dlg, void *data, int event) { Conf *conf = (Conf *)data; if (event == EVENT_REFRESH) { int i; static const struct { const char *s; int k; } hks[] = { { "Ed25519", HK_ED25519 }, { "Ed448", HK_ED448 }, { "ECDSA", HK_ECDSA }, { "DSA", HK_DSA }, { "RSA", HK_RSA }, { "-- warn below here --", HK_WARN } }; /* Set up the "host key preference" box. */ /* (hklist assumed to contain all algorithms) */ dlg_update_start(ctrl, dlg); dlg_listbox_clear(ctrl, dlg); for (i = 0; i < HK_MAX; i++) { int k = conf_get_int_int(conf, CONF_ssh_hklist, i); int j; const char *kstr = NULL; for (j = 0; j < lenof(hks); j++) { if (hks[j].k == k) { kstr = hks[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 < HK_MAX; i++) conf_set_int_int(conf, CONF_ssh_hklist, i, dlg_listbox_getid(ctrl, dlg, i)); } } static void printerbox_handler(union control *ctrl, dlgparam *dlg, void *data, int event) { Conf *conf = (Conf *)data; if (event == EVENT_REFRESH) { int nprinters, i; printer_enum *pe; const 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, dlgparam *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, dlgparam *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; bool midsession; char *savedsession; /* the current contents of ssd->editbox */ }; static void sessionsaver_data_free(void *ssdv) { struct sessionsaver_data *ssd = (struct sessionsaver_data *)ssdv; get_sesslist(&ssd->sesslist, false); 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 bool load_selected_session( struct sessionsaver_data *ssd, dlgparam *dlg, Conf *conf, bool *maybe_launch) { int i = dlg_listbox_index(ssd->listbox, dlg); bool isdef; if (i < 0) { dlg_beep(dlg); return false; } 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 true; } static void sessionsaver_handler(union control *ctrl, dlgparam *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) { top -= 1; } dlg_listbox_select(ssd->listbox, dlg, top); } } else if (event == EVENT_ACTION) { bool mbl = false; if (!ssd->midsession && (ctrl == ssd->listbox || (ssd->loadbutton && ctrl == ssd->loadbutton))) { /* * 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) { bool 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); dlg_refresh(ssd->editbox, dlg); dlg_refresh(ssd->listbox, dlg); } else if (!ssd->midsession && ssd->delbutton && ctrl == ssd->delbutton) { int i = dlg_listbox_index(ssd->listbox, dlg); if (i <= 0) { dlg_beep(dlg); } else { del_settings(ssd->sesslist.sessions[i]); get_sesslist(&ssd->sesslist, false); get_sesslist(&ssd->sesslist, true); dlg_refresh(ssd->listbox, dlg); } } else if (ctrl == ssd->okbutton) { if (ssd->midsession) { /* In a mid-session Change Settings, Apply is always OK. */ dlg_end(dlg, 1); return; } /* * 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) && dlg_is_visible(ssd->listbox, dlg)) { Conf *conf2 = conf_new(); bool 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, dlgparam *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 { union control *listbox, *redit, *gedit, *bedit, *button; }; /* Array of the user-visible colour names defined in the list macro in * putty.h */ static const char *const colours[] = { #define CONF_COLOUR_NAME_DECL(id,name) name, CONF_COLOUR_LIST(CONF_COLOUR_NAME_DECL) #undef CONF_COLOUR_NAME_DECL }; static void colour_handler(union control *ctrl, dlgparam *dlg, void *data, int event) { Conf *conf = (Conf *)data; struct colour_data *cd = (struct colour_data *)ctrl->generic.context.p; bool update = false, clear = false; int r, g, b; if (event == EVENT_REFRESH) { if (ctrl == cd->listbox) { int i; dlg_update_start(ctrl, dlg); dlg_listbox_clear(ctrl, dlg); for (i = 0; i < lenof(colours); i++) dlg_listbox_add(ctrl, dlg, colours[i]); dlg_update_done(ctrl, dlg); clear = true; update = true; } } else if (event == EVENT_SELCHANGE) { 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) { if (clear) { dlg_editbox_set(cd->redit, dlg, ""); dlg_editbox_set(cd->gedit, dlg, ""); dlg_editbox_set(cd->bedit, dlg, ""); } else { char buf[40]; sprintf(buf, "%d", r); dlg_editbox_set(cd->redit, dlg, buf); sprintf(buf, "%d", g); dlg_editbox_set(cd->gedit, dlg, buf); sprintf(buf, "%d", b); dlg_editbox_set(cd->bedit, dlg, buf); } } } struct ttymodes_data { union control *valradio, *valbox, *setbutton, *listbox; }; static void ttymodes_handler(union control *ctrl, dlgparam *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[0] == 'N') ? "(don't send)" : val+1)); dlg_listbox_add(ctrl, dlg, disp); sfree(disp); } dlg_update_done(ctrl, dlg); } else if (ctrl == td->valradio) { dlg_radiobutton_set(ctrl, dlg, 0); } } else if (event == EVENT_SELCHANGE) { if (ctrl == td->listbox) { int ind = dlg_listbox_index(td->listbox, dlg); char *val; if (ind < 0) { return; /* no item selected */ } val = conf_get_str_str(conf, CONF_ttymodes, conf_get_str_nthstrkey(conf, CONF_ttymodes, ind)); assert(val != NULL); /* Do this first to defuse side-effects on radio buttons: */ dlg_editbox_set(td->valbox, dlg, val+1); dlg_radiobutton_set(td->valradio, dlg, val[0] == 'A' ? 0 : (val[0] == 'N' ? 1 : 2)); } } else if (event == EVENT_VALCHANGE) { if (ctrl == td->valbox) { /* If they're editing the text box, we assume they want its * value to be used. */ dlg_radiobutton_set(td->valradio, dlg, 2); } } else if (event == EVENT_ACTION) { if (ctrl == td->setbutton) { int ind = dlg_listbox_index(td->listbox, dlg); const char *key; char *str, *val; char type; { const char types[] = {'A', 'N', 'V'}; int button = dlg_radiobutton_get(td->valradio, dlg); assert(button >= 0 && button < lenof(types)); type = types[button]; } /* Construct new entry */ if (ind >= 0) { key = conf_get_str_nthstrkey(conf, CONF_ttymodes, ind); str = (type == 'V' ? dlg_editbox_get(td->valbox, dlg) : dupstr("")); val = dupprintf("%c%s", type, str); sfree(str); conf_set_str_str(conf, CONF_ttymodes, key, val); sfree(val); dlg_refresh(td->listbox, dlg); dlg_listbox_select(td->listbox, dlg, ind); } else { /* Not a multisel listbox, so this means nothing selected */ dlg_beep(dlg); } } } } struct environ_data { union control *varbox, *valbox, *addbutton, *rembutton, *listbox; }; static void environ_handler(union control *ctrl, dlgparam *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); 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, dlgparam *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) { const char *family, *type; char *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 #endif family = ""; 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 || !host_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); 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, *p; const char *val; key = conf_get_str_nthstrkey(conf, CONF_portfwd, i); if (key) { static const char *const afs = "A46"; static const char *const dirs = "LRD"; const 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); } } } struct manual_hostkey_data { union control *addbutton, *rembutton, *listbox, *keybox; }; static void manual_hostkey_handler(union control *ctrl, dlgparam *dlg, void *data, int event) { Conf *conf = (Conf *)data; struct manual_hostkey_data *mh = (struct manual_hostkey_data *)ctrl->generic.context.p; if (event == EVENT_REFRESH) { if (ctrl == mh->listbox) { char *key, *val; dlg_update_start(ctrl, dlg); dlg_listbox_clear(ctrl, dlg); for (val = conf_get_str_strs(conf, CONF_ssh_manual_hostkeys, NULL, &key); val != NULL; val = conf_get_str_strs(conf, CONF_ssh_manual_hostkeys, key, &key)) { dlg_listbox_add(ctrl, dlg, key); } dlg_update_done(ctrl, dlg); } } else if (event == EVENT_ACTION) { if (ctrl == mh->addbutton) { char *key; key = dlg_editbox_get(mh->keybox, dlg); if (!*key) { dlg_error_msg(dlg, "You need to specify a host key or " "fingerprint"); sfree(key); return; } if (!validate_manual_hostkey(key)) { dlg_error_msg(dlg, "Host key is not in a valid format"); } else if (conf_get_str_str_opt(conf, CONF_ssh_manual_hostkeys, key)) { dlg_error_msg(dlg, "Specified host key is already listed"); } else { conf_set_str_str(conf, CONF_ssh_manual_hostkeys, key, ""); } sfree(key); dlg_refresh(mh->listbox, dlg); } else if (ctrl == mh->rembutton) { int i = dlg_listbox_index(mh->listbox, dlg); if (i < 0) { dlg_beep(dlg); } else { char *key; key = conf_get_str_nthstrkey(conf, CONF_ssh_manual_hostkeys, i); if (key) { dlg_editbox_set(mh->keybox, dlg, key); /* And delete it */ conf_del_str_str(conf, CONF_ssh_manual_hostkeys, key); } } dlg_refresh(mh->listbox, dlg); } } } static void clipboard_selector_handler(union control *ctrl, dlgparam *dlg, void *data, int event) { Conf *conf = (Conf *)data; int setting = ctrl->generic.context.i; #ifdef NAMED_CLIPBOARDS int strsetting = ctrl->editbox.context2.i; #endif static const struct { const char *name; int id; } options[] = { {"No action", CLIPUI_NONE}, {CLIPNAME_IMPLICIT, CLIPUI_IMPLICIT}, {CLIPNAME_EXPLICIT, CLIPUI_EXPLICIT}, }; if (event == EVENT_REFRESH) { int i, val = conf_get_int(conf, setting); dlg_update_start(ctrl, dlg); dlg_listbox_clear(ctrl, dlg); #ifdef NAMED_CLIPBOARDS for (i = 0; i < lenof(options); i++) dlg_listbox_add(ctrl, dlg, options[i].name); if (val == CLIPUI_CUSTOM) { const char *sval = conf_get_str(conf, strsetting); for (i = 0; i < lenof(options); i++) if (!strcmp(sval, options[i].name)) break; /* needs escaping */ if (i < lenof(options) || sval[0] == '=') { char *escaped = dupcat("=", sval); dlg_editbox_set(ctrl, dlg, escaped); sfree(escaped); } else { dlg_editbox_set(ctrl, dlg, sval); } } else { dlg_editbox_set(ctrl, dlg, options[0].name); /* fallback */ for (i = 0; i < lenof(options); i++) if (val == options[i].id) dlg_editbox_set(ctrl, dlg, options[i].name); } #else for (i = 0; i < lenof(options); i++) dlg_listbox_addwithid(ctrl, dlg, options[i].name, options[i].id); dlg_listbox_select(ctrl, dlg, 0); /* fallback */ for (i = 0; i < lenof(options); i++) if (val == options[i].id) dlg_listbox_select(ctrl, dlg, i); #endif dlg_update_done(ctrl, dlg); } else if (event == EVENT_SELCHANGE #ifdef NAMED_CLIPBOARDS || event == EVENT_VALCHANGE #endif ) { #ifdef NAMED_CLIPBOARDS char *sval = dlg_editbox_get(ctrl, dlg); int i; for (i = 0; i < lenof(options); i++) if (!strcmp(sval, options[i].name)) { conf_set_int(conf, setting, options[i].id); conf_set_str(conf, strsetting, ""); break; } if (i == lenof(options)) { conf_set_int(conf, setting, CLIPUI_CUSTOM); if (sval[0] == '=') sval++; conf_set_str(conf, strsetting, sval); } sfree(sval); #else int index = dlg_listbox_index(ctrl, dlg); if (index >= 0) { int val = dlg_listbox_getid(ctrl, dlg, index); conf_set_int(conf, setting, val); } #endif } } static void clipboard_control(struct controlset *s, const char *label, char shortcut, int percentage, intorptr helpctx, int setting, int strsetting) { #ifdef NAMED_CLIPBOARDS ctrl_combobox(s, label, shortcut, percentage, helpctx, clipboard_selector_handler, I(setting), I(strsetting)); #else /* strsetting isn't needed in this case */ ctrl_droplist(s, label, shortcut, percentage, helpctx, clipboard_selector_handler, I(setting)); #endif } static void serial_parity_handler(union control *ctrl, dlgparam *dlg, void *data, int event) { static const struct { const char *name; int val; } parities[] = { {"None", SER_PAR_NONE}, {"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 << parities[i].val)) dlg_listbox_addwithid(ctrl, dlg, parities[i].name, parities[i].val); } for (i = j = 0; i < lenof(parities); i++) { if (mask & (1 << parities[i].val)) { if (oldparity == parities[i].val) { dlg_listbox_select(ctrl, dlg, j); break; } j++; } } 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, dlgparam *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 << flows[i].val)) dlg_listbox_addwithid(ctrl, dlg, flows[i].name, flows[i].val); } for (i = j = 0; i < lenof(flows); i++) { if (mask & (1 << flows[i].val)) { 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 setup_config_box(struct controlbox *b, bool midsession, int protocol, int protcfginfo) { const struct BackendVtable *backvt; 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; struct manual_hostkey_data *mh; union control *c; bool resize_forbidden = false; 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, "", "", ""); ctrl_columns(s, 5, 20, 20, 20, 20, 20); ssd->okbutton = ctrl_pushbutton(s, (midsession ? "Apply" : "Open"), (char)(midsession ? 'a' : 'o'), HELPCTX(no_help), sessionsaver_handler, P(ssd)); ssd->okbutton->button.isdefault = true; ssd->okbutton->generic.column = 3; ssd->cancelbutton = ctrl_pushbutton(s, "Cancel", 'c', HELPCTX(no_help), sessionsaver_handler, P(ssd)); ssd->cancelbutton->button.iscancel = true; ssd->cancelbutton->generic.column = 4; /* We carefully don't close the 5-column part, so that platform- * specific add-ons can put extra buttons alongside Open and Cancel. */ /* * The Session panel. */ str = dupprintf("Basic options for your %s session", appname); ctrl_settitle(b, "Session", str); sfree(str); if (!midsession) { struct hostport *hp = (struct hostport *) ctrl_alloc(b, sizeof(struct hostport)); memset(hp, 0, sizeof(*hp)); s = ctrl_getset(b, "Session", "hostport", "Specify the destination you want to connect to"); ctrl_columns(s, 2, 75, 25); c = ctrl_editbox(s, HOST_BOX_TITLE, 'n', 100, HELPCTX(session_hostname), config_host_handler, I(0), I(0)); c->generic.column = 0; hp->host = c; c = ctrl_editbox(s, PORT_BOX_TITLE, 'p', 100, HELPCTX(session_hostname), config_port_handler, I(0), I(0)); c->generic.column = 1; hp->port = c; ctrl_columns(s, 1, 100); c = ctrl_text(s, "Connection type:", HELPCTX(session_hostname)); ctrl_columns(s, 2, 62, 38); c = ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3, HELPCTX(session_hostname), config_protocols_handler, P(hp), NULL); c->generic.column = 0; hp->protradio = c; c->radio.buttons = sresize(c->radio.buttons, PROTOCOL_LIMIT, char *); c->radio.shortcuts = sresize(c->radio.shortcuts, PROTOCOL_LIMIT, char); c->radio.buttondata = sresize(c->radio.buttondata, PROTOCOL_LIMIT, intorptr); assert(c->radio.nbuttons == 0); /* UI design assumes there exists at least one 'real' radio button */ assert(n_ui_backends > 0 && n_ui_backends < PROTOCOL_LIMIT); for (size_t i = 0; i < n_ui_backends; i++) { assert(backends[i]); c->radio.buttons[c->radio.nbuttons] = dupstr(backends[i]->displayname); c->radio.shortcuts[c->radio.nbuttons] = (backends[i]->protocol == PROT_SSH ? 's' : backends[i]->protocol == PROT_SERIAL ? 'r' : backends[i]->protocol == PROT_RAW ? 'w' : /* FIXME unused */ NO_SHORTCUT); c->radio.buttondata[c->radio.nbuttons] = I(backends[i]->protocol); c->radio.nbuttons++; } /* UI design assumes there exists at least one droplist entry */ assert(backends[c->radio.nbuttons]); c->radio.buttons[c->radio.nbuttons] = dupstr("Other:"); c->radio.shortcuts[c->radio.nbuttons] = 't'; c->radio.buttondata[c->radio.nbuttons] = I(-1); c->radio.nbuttons++; c = ctrl_droplist(s, NULL, NO_SHORTCUT, 100, HELPCTX(session_hostname), config_protocols_handler, P(hp)); hp->protlist = c; /* droplist is populated in config_protocols_handler */ c->generic.column = 1; /* Vertically centre the two protocol controls w.r.t. each other */ hp->protlist->generic.align_next_to = hp->protradio; ctrl_columns(s, 1, 100); } /* * The Load/Save panel is available even in mid-session. */ s = ctrl_getset(b, "Session", "savedsessions", midsession ? "Save the current session settings" : "Load, save or delete a stored session"); ctrl_columns(s, 2, 75, 25); get_sesslist(&ssd->sesslist, true); ssd->editbox = ctrl_editbox(s, "Saved Sessions", 'e', 100, HELPCTX(session_saved), sessionsaver_handler, P(ssd), P(NULL)); ssd->editbox->generic.column = 0; /* Reset columns so that the buttons are alongside the list, rather * than alongside that edit box. */ ctrl_columns(s, 1, 100); ctrl_columns(s, 2, 75, 25); ssd->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT, HELPCTX(session_saved), sessionsaver_handler, P(ssd)); ssd->listbox->generic.column = 0; ssd->listbox->listbox.height = 7; if (!midsession) { ssd->loadbutton = ctrl_pushbutton(s, "Load", 'l', HELPCTX(session_saved), sessionsaver_handler, P(ssd)); ssd->loadbutton->generic.column = 1; } else { /* We can't offer the Load button mid-session, as it would allow the * user to load and subsequently save settings they can't see. (And * also change otherwise immutable settings underfoot; that probably * shouldn't be a problem, but.) */ ssd->loadbutton = NULL; } /* "Save" button is permitted mid-session. */ ssd->savebutton = ctrl_pushbutton(s, "Save", 'v', HELPCTX(session_saved), sessionsaver_handler, P(ssd)); ssd->savebutton->generic.column = 1; if (!midsession) { ssd->delbutton = ctrl_pushbutton(s, "Delete", 'd', HELPCTX(session_saved), sessionsaver_handler, P(ssd)); ssd->delbutton->generic.column = 1; } 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); /* * The logging buttons change depending on whether SSH packet * logging can sensibly be available. */ { const char *sshlogname, *sshrawlogname; if ((midsession && protocol == PROT_SSH) || (!midsession && backend_vt_from_proto(PROT_SSH))) { sshlogname = "SSH packets"; sshrawlogname = "SSH packets and raw data"; } 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, &H for host name, and &P for port number)", 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)); ctrl_checkbox(s, "Include header", 'i', HELPCTX(logging_header), conf_checkbox_handler, I(CONF_logheader)); if ((midsession && protocol == PROT_SSH) || (!midsession && backend_vt_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_bool_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_bool_handler, I(CONF_rxvt_homeend), "Standard", I(false), "rxvt", I(true), 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_bool_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 remote-controlled clearing of scrollback", 'e', HELPCTX(features_clearscroll), conf_checkbox_handler, I(CONF_no_remote_clearscroll)); 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_no_arabicshaping)); ctrl_checkbox(s, "Disable bidirectional text display", 'd', HELPCTX(features_bidi), conf_checkbox_handler, I(CONF_no_bidi)); /* * The Window panel. */ str = dupprintf("Options controlling %s's window", appname); ctrl_settitle(b, "Window", str); sfree(str); backvt = backend_vt_from_proto(protocol); if (backvt) resize_forbidden = (backvt->flags & BACKEND_RESIZE_FORBIDDEN); if (!resize_forbidden || !midsession) { 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)); ctrl_checkbox(s, "Enable VT100 line drawing even in UTF-8 mode",'8', HELPCTX(translation_utf8linedraw), conf_checkbox_handler, I(CONF_utf8linedraw)); /* * 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_bool_handler, I(CONF_rect_select), "Normal", 'n', I(false), "Rectangular block", 'r', I(true), NULL); s = ctrl_getset(b, "Window/Selection", "clipboards", "Assign copy/paste actions to clipboards"); ctrl_checkbox(s, "Auto-copy selected text to " CLIPNAME_EXPLICIT_OBJECT, NO_SHORTCUT, HELPCTX(selection_autocopy), conf_checkbox_handler, I(CONF_mouseautocopy)); clipboard_control(s, "Mouse paste action:", NO_SHORTCUT, 60, HELPCTX(selection_clipactions), CONF_mousepaste, CONF_mousepaste_custom); clipboard_control(s, "{Ctrl,Shift} + Ins:", NO_SHORTCUT, 60, HELPCTX(selection_clipactions), CONF_ctrlshiftins, CONF_ctrlshiftins_custom); clipboard_control(s, "Ctrl + Shift + {C,V}:", NO_SHORTCUT, 60, HELPCTX(selection_clipactions), CONF_ctrlshiftcv, CONF_ctrlshiftcv_custom); s = ctrl_getset(b, "Window/Selection", "paste", "Control pasting of text from clipboard to terminal"); ctrl_checkbox(s, "Permit control characters in pasted text", NO_SHORTCUT, HELPCTX(selection_pastectrl), conf_checkbox_handler, I(CONF_paste_controls)); /* * The Window/Selection/Copy panel. */ ctrl_settitle(b, "Window/Selection/Copy", "Options controlling copying from terminal to clipboard"); s = ctrl_getset(b, "Window/Selection/Copy", "charclass", "Classes of character that group together"); ccd = (struct charclass_data *) ctrl_alloc(b, sizeof(struct charclass_data)); ccd->listbox = ctrl_listbox(s, "Character classes:", 'e', HELPCTX(copy_charclasses), charclass_handler, P(ccd)); ccd->listbox->listbox.multisel = 1; ccd->listbox->listbox.ncols = 4; ccd->listbox->listbox.percentages = snewn(4, int); ccd->listbox->listbox.percentages[0] = 15; ccd->listbox->listbox.percentages[1] = 25; ccd->listbox->listbox.percentages[2] = 20; ccd->listbox->listbox.percentages[3] = 40; ctrl_columns(s, 2, 67, 33); ccd->editbox = ctrl_editbox(s, "Set to class", 't', 50, HELPCTX(copy_charclasses), charclass_handler, P(ccd), P(NULL)); ccd->editbox->generic.column = 0; ccd->button = ctrl_pushbutton(s, "Set", 's', HELPCTX(copy_charclasses), charclass_handler, P(ccd)); ccd->button->generic.column = 1; ctrl_columns(s, 1, 100); /* * The Window/Colours panel. */ 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_checkbox(s, "Allow terminal to use 24-bit colours", '4', HELPCTX(colours_truecolour), conf_checkbox_handler, I(CONF_true_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)); ctrl_columns(s, 2, 67, 33); cd = (struct colour_data *)ctrl_alloc(b, sizeof(struct colour_data)); cd->listbox = ctrl_listbox(s, "Select a colour to adjust:", 'u', HELPCTX(colours_config), colour_handler, P(cd)); cd->listbox->generic.column = 0; cd->listbox->listbox.height = 7; c = ctrl_text(s, "RGB value:", HELPCTX(colours_config)); c->generic.column = 1; cd->redit = ctrl_editbox(s, "Red", 'r', 50, HELPCTX(colours_config), colour_handler, P(cd), P(NULL)); cd->redit->generic.column = 1; cd->gedit = ctrl_editbox(s, "Green", 'n', 50, HELPCTX(colours_config), colour_handler, P(cd), P(NULL)); cd->gedit->generic.column = 1; cd->bedit = ctrl_editbox(s, "Blue", 'e', 50, HELPCTX(colours_config), colour_handler, P(cd), P(NULL)); cd->bedit->generic.column = 1; cd->button = ctrl_pushbutton(s, "Modify", 'm', HELPCTX(colours_config), colour_handler, P(cd)); cd->button->generic.column = 1; ctrl_columns(s, 1, 100); /* * The Connection panel. This doesn't show up if we're in a * non-network utility such as pterm. We tell this by being * passed a protocol < 0. */ 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 { const char *label = backend_vt_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_bool_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, HELPCTX(telnet_environ), environ_handler, P(ed), P(NULL)); ed->varbox->generic.column = 0; ed->valbox = ctrl_editbox(s, "Value", 'l', 60, HELPCTX(telnet_environ), environ_handler, P(ed), P(NULL)); ed->valbox->generic.column = 0; ed->addbutton = ctrl_pushbutton(s, "Add", 'd', HELPCTX(telnet_environ), environ_handler, P(ed)); ed->addbutton->generic.column = 1; ed->rembutton = ctrl_pushbutton(s, "Remove", 'r', HELPCTX(telnet_environ), environ_handler, P(ed)); ed->rembutton->generic.column = 1; ctrl_columns(s, 1, 100); ed->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT, HELPCTX(telnet_environ), environ_handler, P(ed)); ed->listbox->listbox.height = 3; ed->listbox->listbox.ncols = 2; ed->listbox->listbox.percentages = snewn(2, int); ed->listbox->listbox.percentages[0] = 30; ed->listbox->listbox.percentages[1] = 70; } } if (!midsession) { /* * The Connection/Proxy panel. */ 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 = true; ctrl_editbox(s, "Telnet command", 'm', 100, HELPCTX(proxy_command), conf_editbox_handler, I(CONF_proxy_telnet_command), I(1)); ctrl_radiobuttons(s, "Print proxy diagnostics " "in the terminal window", 'r', 5, HELPCTX(proxy_logging), conf_radiobutton_handler, I(CONF_proxy_log_to_term), "No", I(FORCE_OFF), "Yes", I(FORCE_ON), "Only until session starts", I(AUTO), NULL); } /* * Each per-protocol configuration GUI panel is conditionally * displayed. We don't display it if this binary doesn't contain a * backend for its protocol at all; we don't display it if we're * already in mid-session with a different protocol selected; and * even if we _do_ have this protocol selected, we don't display * the panel if the protocol doesn't permit any mid-session * reconfiguration anyway. */ #define DISPLAY_RECONFIGURABLE_PROTOCOL(which_proto) \ (backend_vt_from_proto(which_proto) && \ (!midsession || protocol == (which_proto))) #define DISPLAY_NON_RECONFIGURABLE_PROTOCOL(which_proto) \ (backend_vt_from_proto(which_proto) && !midsession) if (DISPLAY_RECONFIGURABLE_PROTOCOL(PROT_SSH) || DISPLAY_RECONFIGURABLE_PROTOCOL(PROT_SSHCONN)) { /* * The Connection/SSH panel. */ ctrl_settitle(b, "Connection/SSH", "Options controlling SSH connections"); /* SSH-1 or connection-sharing downstream */ if (midsession && (protcfginfo == 1 || protcfginfo == -1)) { s = ctrl_getset(b, "Connection/SSH", "disclaimer", NULL); ctrl_text(s, "Nothing on this panel may be reconfigured in mid-" "session; it is only here so that sub-panels of it can " "exist without looking strange.", HELPCTX(no_help)); } 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 || 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) { 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, "SSH protocol version:", NO_SHORTCUT, 2, HELPCTX(ssh_protocol), conf_radiobutton_handler, I(CONF_sshprot), "2", '2', I(3), "1 (INSECURE)", '1', I(0), NULL); } /* * The Connection/SSH/Kex panel. (Owing to repeat key * exchange, much of this is meaningful in mid-session _if_ * we're using SSH-2 and are not a connection-sharing * downstream, or haven't decided yet.) */ if (protcfginfo != 1 && protcfginfo != -1) { ctrl_settitle(b, "Connection/SSH/Kex", "Options controlling SSH key exchange"); s = ctrl_getset(b, "Connection/SSH/Kex", "main", "Key exchange algorithm options"); c = ctrl_draglist(s, "Algorithm selection policy:", 's', HELPCTX(ssh_kexlist), kexlist_handler, P(NULL)); c->listbox.height = KEX_MAX; #ifndef NO_GSSAPI ctrl_checkbox(s, "Attempt GSSAPI key exchange", 'k', HELPCTX(ssh_gssapi), conf_checkbox_handler, I(CONF_try_gssapi_kex)); #endif 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)); #ifndef NO_GSSAPI ctrl_editbox(s, "Minutes between GSS checks (0 for never)", NO_SHORTCUT, 20, HELPCTX(ssh_kex_repeat), conf_editbox_handler, I(CONF_gssapirekey), I(-1)); #endif 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)); } /* * The 'Connection/SSH/Host keys' panel. */ if (protcfginfo != 1 && protcfginfo != -1) { ctrl_settitle(b, "Connection/SSH/Host keys", "Options controlling SSH host keys"); s = ctrl_getset(b, "Connection/SSH/Host keys", "main", "Host key algorithm preference"); c = ctrl_draglist(s, "Algorithm selection policy:", 's', HELPCTX(ssh_hklist), hklist_handler, P(NULL)); c->listbox.height = 5; ctrl_checkbox(s, "Prefer algorithms for which a host key is known", 'p', HELPCTX(ssh_hk_known), conf_checkbox_handler, I(CONF_ssh_prefer_known_hostkeys)); } /* * Manual host key configuration is irrelevant mid-session, * as we enforce that the host key for rekeys is the * same as that used at the start of the session. */ if (!midsession) { s = ctrl_getset(b, "Connection/SSH/Host keys", "hostkeys", "Manually configure host keys for this connection"); ctrl_columns(s, 2, 75, 25); c = ctrl_text(s, "Host keys or fingerprints to accept:", HELPCTX(ssh_kex_manual_hostkeys)); c->generic.column = 0; /* You want to select from the list, _then_ hit Remove. So * tab order should be that way round. */ mh = (struct manual_hostkey_data *) ctrl_alloc(b,sizeof(struct manual_hostkey_data)); mh->rembutton = ctrl_pushbutton(s, "Remove", 'r', HELPCTX(ssh_kex_manual_hostkeys), manual_hostkey_handler, P(mh)); mh->rembutton->generic.column = 1; mh->rembutton->generic.tabdelay = true; mh->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT, HELPCTX(ssh_kex_manual_hostkeys), manual_hostkey_handler, P(mh)); /* This list box can't be very tall, because there's not * much room in the pane on Windows at least. This makes * it become really unhelpful if a horizontal scrollbar * appears, so we suppress that. */ mh->listbox->listbox.height = 2; mh->listbox->listbox.hscroll = false; ctrl_tabdelay(s, mh->rembutton); mh->keybox = ctrl_editbox(s, "Key", 'k', 80, HELPCTX(ssh_kex_manual_hostkeys), manual_hostkey_handler, P(mh), P(NULL)); mh->keybox->generic.column = 0; mh->addbutton = ctrl_pushbutton(s, "Add key", 'y', HELPCTX(ssh_kex_manual_hostkeys), manual_hostkey_handler, P(mh)); mh->addbutton->generic.column = 1; ctrl_columns(s, 1, 100); } if (!midsession || !(protcfginfo == 1 || 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, "Display pre-authentication banner (SSH-2 only)", 'd', HELPCTX(ssh_auth_banner), conf_checkbox_handler, I(CONF_ssh_show_banner)); 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, "Disconnect if authentication succeeds trivially", 'n', HELPCTX(ssh_no_trivial_userauth), conf_checkbox_handler, I(CONF_ssh_no_trivial_userauth)); 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, "Attempt GSSAPI key exchange (SSH-2 only)", 'k', HELPCTX(ssh_gssapi), conf_checkbox_handler, I(CONF_try_gssapi_kex)); 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), gsslist_handler, P(NULL)); c->listbox.height = ngsslibs; /* * I currently assume that if more than one GSS * library option is available, then one of them is * 'user-supplied' and so we should present the * following file selector. This is at least half- * reasonable, because if we're using statically * linked GSSAPI then there will only be one option * and no way to load from a user-supplied library, * whereas if we're using dynamic libraries then * there will almost certainly be some default * option in addition to a user-supplied path. If * anyone ever ports PuTTY to a system on which * dynamic-library GSSAPI is available but there is * absolutely no consensus on where to keep the * libraries, there'll need to be a flag alongside * 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_text(s, "Terminal modes to send:", HELPCTX(ssh_ttymodes)); td->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT, HELPCTX(ssh_ttymodes), ttymodes_handler, P(td)); td->listbox->listbox.height = 8; td->listbox->listbox.ncols = 2; td->listbox->listbox.percentages = snewn(2, int); td->listbox->listbox.percentages[0] = 40; td->listbox->listbox.percentages[1] = 60; ctrl_columns(s, 2, 75, 25); c = ctrl_text(s, "For selected mode, send:", HELPCTX(ssh_ttymodes)); c->generic.column = 0; td->setbutton = ctrl_pushbutton(s, "Set", 's', HELPCTX(ssh_ttymodes), ttymodes_handler, P(td)); td->setbutton->generic.column = 1; td->setbutton->generic.tabdelay = true; ctrl_columns(s, 1, 100); /* column break */ /* Bit of a hack to get the value radio buttons and * edit-box on the same row. */ ctrl_columns(s, 2, 75, 25); td->valradio = ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3, HELPCTX(ssh_ttymodes), ttymodes_handler, P(td), "Auto", NO_SHORTCUT, P(NULL), "Nothing", NO_SHORTCUT, P(NULL), "This:", NO_SHORTCUT, P(NULL), NULL); td->valradio->generic.column = 0; td->valbox = ctrl_editbox(s, NULL, NO_SHORTCUT, 100, HELPCTX(ssh_ttymodes), ttymodes_handler, P(td), P(NULL)); td->valbox->generic.column = 1; td->valbox->generic.align_next_to = td->valradio; ctrl_tabdelay(s, td->setbutton); } if (!midsession) { /* * The Connection/SSH/X11 panel. */ 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)); pfd->rembutton = ctrl_pushbutton(s, "Remove", 'r', HELPCTX(ssh_tunnels_portfwd), portfwd_handler, P(pfd)); pfd->rembutton->generic.column = 2; pfd->rembutton->generic.tabdelay = true; pfd->listbox = ctrl_listbox(s, NULL, NO_SHORTCUT, HELPCTX(ssh_tunnels_portfwd), portfwd_handler, P(pfd)); pfd->listbox->listbox.height = 3; pfd->listbox->listbox.ncols = 2; pfd->listbox->listbox.percentages = snewn(2, int); pfd->listbox->listbox.percentages[0] = 20; pfd->listbox->listbox.percentages[1] = 80; ctrl_tabdelay(s, pfd->rembutton); ctrl_text(s, "Add new forwarded port:", HELPCTX(ssh_tunnels_portfwd)); /* You want to enter source, destination and type, _then_ hit Add. * Again, we adjust the tab order to reflect this. */ pfd->addbutton = ctrl_pushbutton(s, "Add", 'd', HELPCTX(ssh_tunnels_portfwd), portfwd_handler, P(pfd)); pfd->addbutton->generic.column = 2; pfd->addbutton->generic.tabdelay = true; pfd->sourcebox = ctrl_editbox(s, "Source port", 's', 40, HELPCTX(ssh_tunnels_portfwd), portfwd_handler, P(pfd), P(NULL)); pfd->sourcebox->generic.column = 0; pfd->destbox = ctrl_editbox(s, "Destination", 'i', 67, HELPCTX(ssh_tunnels_portfwd), portfwd_handler, P(pfd), P(NULL)); pfd->direction = ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3, HELPCTX(ssh_tunnels_portfwd), portfwd_handler, P(pfd), "Local", 'l', P(NULL), "Remote", 'm', P(NULL), "Dynamic", 'y', P(NULL), NULL); #ifndef NO_IPV6 pfd->addressfamily = ctrl_radiobuttons(s, NULL, NO_SHORTCUT, 3, HELPCTX(ssh_tunnels_portfwd_ipversion), portfwd_handler, P(pfd), "Auto", 'u', I(ADDRTYPE_UNSPEC), "IPv4", '4', I(ADDRTYPE_IPV4), "IPv6", '6', I(ADDRTYPE_IPV6), NULL); #endif ctrl_tabdelay(s, pfd->addbutton); ctrl_columns(s, 1, 100); if (!midsession) { /* * The Connection/SSH/Bugs panels. */ 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-2 ignore messages", '2', 20, HELPCTX(ssh_bugs_ignore2), sshbug_handler, I(CONF_sshbug_ignore2)); 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, "Chokes on PuTTY's SSH-2 'winadj' requests", 'j', 20, HELPCTX(ssh_bugs_winadj), sshbug_handler, I(CONF_sshbug_winadj)); ctrl_droplist(s, "Replies to requests on closed channels", 'q', 20, HELPCTX(ssh_bugs_chanreq), sshbug_handler, I(CONF_sshbug_chanreq)); ctrl_droplist(s, "Ignores SSH-2 maximum packet size", 'x', 20, HELPCTX(ssh_bugs_maxpkt2), sshbug_handler, I(CONF_sshbug_maxpkt2)); ctrl_settitle(b, "Connection/SSH/More bugs", "Further workarounds for SSH server bugs"); s = ctrl_getset(b, "Connection/SSH/More bugs", "main", "Detection of known bugs in SSH servers"); 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, "Only supports pre-RFC4419 SSH-2 DH GEX", 'd', 20, HELPCTX(ssh_bugs_oldgex2), sshbug_handler, I(CONF_sshbug_oldgex2)); ctrl_droplist(s, "Miscomputes SSH-2 HMAC keys", 'm', 20, HELPCTX(ssh_bugs_hmac2), sshbug_handler, I(CONF_sshbug_hmac2)); 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, "Miscomputes SSH-2 encryption keys", 'e', 20, HELPCTX(ssh_bugs_derivekey2), sshbug_handler, I(CONF_sshbug_derivekey2)); 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)); } } if (DISPLAY_RECONFIGURABLE_PROTOCOL(PROT_SERIAL)) { const BackendVtable *ser_vt = backend_vt_from_proto(PROT_SERIAL); /* * The Connection/Serial panel. */ ctrl_settitle(b, "Connection/Serial", "Options controlling local serial lines"); if (!midsession) { /* * We don't permit switching to a different serial port in * 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(ser_vt->serial_parity_mask)); ctrl_droplist(s, "Flow control", 'f', 40, HELPCTX(serial_flow), serial_flow_handler, I(ser_vt->serial_flow_mask)); } if (DISPLAY_RECONFIGURABLE_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_bool_handler, I(CONF_rfc_environ), "BSD (commonplace)", 'b', I(false), "RFC 1408 (unusual)", 'f', I(true), NULL); ctrl_radiobuttons(s, "Telnet negotiation mode:", 't', 2, HELPCTX(telnet_passive), conf_radiobutton_bool_handler, I(CONF_passive_telnet), "Passive", I(true), "Active", I(false), 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 (DISPLAY_NON_RECONFIGURABLE_PROTOCOL(PROT_RLOGIN)) { /* * 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)); } if (DISPLAY_NON_RECONFIGURABLE_PROTOCOL(PROT_SUPDUP)) { /* * The Connection/SUPDUP panel. */ ctrl_settitle(b, "Connection/SUPDUP", "Options controlling SUPDUP connections"); s = ctrl_getset(b, "Connection/SUPDUP", "main", NULL); ctrl_editbox(s, "Location string", 'l', 70, HELPCTX(supdup_location), conf_editbox_handler, I(CONF_supdup_location), I(1)); ctrl_radiobuttons(s, "Extended ASCII Character set:", 'e', 4, HELPCTX(supdup_ascii), conf_radiobutton_handler, I(CONF_supdup_ascii_set), "None", I(SUPDUP_CHARSET_ASCII), "ITS", I(SUPDUP_CHARSET_ITS), "WAITS", I(SUPDUP_CHARSET_WAITS), NULL); ctrl_checkbox(s, "**MORE** processing", 'm', HELPCTX(supdup_more), conf_checkbox_handler, I(CONF_supdup_more)); ctrl_checkbox(s, "Terminal scrolling", 's', HELPCTX(supdup_scroll), conf_checkbox_handler, I(CONF_supdup_scroll)); } } putty-0.76/console.c0000644000175000017500000000700514072266307011373 00000000000000/* * Common pieces between the platform console frontend modules. */ #include #include #include "putty.h" #include "misc.h" #include "console.h" const char hk_absentmsg_common_fmt[] = "The server's host key is not cached. You have no guarantee\n" "that the server is the computer you think it is.\n" "The server's %s key fingerprint is:\n" "%s\n"; const char hk_absentmsg_interactive_intro[] = "If you trust this host, enter \"y\" to add the key to\n" "PuTTY's cache and carry on connecting.\n" "If you want to carry on connecting just once, without\n" "adding the key to the cache, enter \"n\".\n" "If you do not trust this host, press Return to abandon the\n" "connection.\n"; const char hk_absentmsg_interactive_prompt[] = "Store key in cache? (y/n, Return cancels connection, " "i for more info) "; const char hk_wrongmsg_common_fmt[] = "WARNING - POTENTIAL SECURITY BREACH!\n" "The server's host key does not match the one PuTTY has\n" "cached. This means that either the server administrator\n" "has changed the host key, or you have actually connected\n" "to another computer pretending to be the server.\n" "The new %s key fingerprint is:\n" "%s\n"; const char hk_wrongmsg_interactive_intro[] = "If you were expecting this change and trust the new key,\n" "enter \"y\" to update PuTTY's cache and continue connecting.\n" "If you want to carry on connecting but without updating\n" "the cache, enter \"n\".\n" "If you want to abandon the connection completely, press\n" "Return to cancel. Pressing Return is the ONLY guaranteed\n" "safe choice.\n"; const char hk_wrongmsg_interactive_prompt[] = "Update cached key? (y/n, Return cancels connection, " "i for more info) "; const char weakcrypto_msg_common_fmt[] = "The first %s supported by the server is\n" "%s, which is below the configured warning threshold.\n"; const char weakhk_msg_common_fmt[] = "The first host key type we have stored for this server\n" "is %s, which is below the configured warning threshold.\n" "The server also provides the following types of host key\n" "above the threshold, which we do not have stored:\n" "%s\n"; const char console_continue_prompt[] = "Continue with connection? (y/n) "; const char console_abandoned_msg[] = "Connection abandoned.\n"; bool console_batch_mode = false; /* * Error message and/or fatal exit functions, all based on * console_print_error_msg which the platform front end provides. */ void console_print_error_msg_fmt_v( const char *prefix, const char *fmt, va_list ap) { char *msg = dupvprintf(fmt, ap); console_print_error_msg(prefix, msg); sfree(msg); } void console_print_error_msg_fmt(const char *prefix, const char *fmt, ...) { va_list ap; va_start(ap, fmt); console_print_error_msg_fmt_v(prefix, fmt, ap); va_end(ap); } void modalfatalbox(const char *fmt, ...) { va_list ap; va_start(ap, fmt); console_print_error_msg_fmt_v("FATAL ERROR", fmt, ap); va_end(ap); cleanup_exit(1); } void nonfatal(const char *fmt, ...) { va_list ap; va_start(ap, fmt); console_print_error_msg_fmt_v("ERROR", fmt, ap); va_end(ap); } void console_connection_fatal(Seat *seat, const char *msg) { console_print_error_msg("FATAL ERROR", msg); cleanup_exit(1); } /* * Console front ends redo their select() or equivalent every time, so * they don't need separate timer handling. */ void timer_change_notify(unsigned long next) { } putty-0.76/console.h0000644000175000017500000000104614072266307011377 00000000000000/* * Common pieces between the platform console frontend modules. */ extern const char hk_absentmsg_common_fmt[]; extern const char hk_absentmsg_interactive_intro[]; extern const char hk_absentmsg_interactive_prompt[]; extern const char hk_wrongmsg_common_fmt[]; extern const char hk_wrongmsg_interactive_intro[]; extern const char hk_wrongmsg_interactive_prompt[]; extern const char weakcrypto_msg_common_fmt[]; extern const char weakhk_msg_common_fmt[]; extern const char console_continue_prompt[]; extern const char console_abandoned_msg[]; putty-0.76/cproxy.c0000644000175000017500000001444214072266310011252 00000000000000/* * Routines to do cryptographic interaction with proxies in PuTTY. * This is in a separate module from proxy.c, so that it can be * conveniently removed in PuTTYtel by replacing this module with * the stub version nocproxy.c. */ #include #include #include #include "putty.h" #include "ssh.h" /* For MD5 support */ #include "network.h" #include "proxy.h" #include "marshal.h" static void hmacmd5_chap(const unsigned char *challenge, int challen, const char *passwd, unsigned char *response) { mac_simple(&ssh_hmac_md5, ptrlen_from_asciz(passwd), make_ptrlen(challenge, challen), response); } void proxy_socks5_offerencryptedauth(BinarySink *bs) { put_byte(bs, 0x03); /* CHAP */ } int proxy_socks5_handlechap (ProxySocket *p) { /* CHAP authentication reply format: * version number (1 bytes) = 1 * number of commands (1 byte) * * For each command: * command identifier (1 byte) * data length (1 byte) */ unsigned char data[260]; unsigned char outbuf[20]; while(p->chap_num_attributes == 0 || p->chap_num_attributes_processed < p->chap_num_attributes) { if (p->chap_num_attributes == 0 || p->chap_current_attribute == -1) { /* CHAP normally reads in two bytes, either at the * beginning or for each attribute/value pair. But if * we're waiting for the value's data, we might not want * to read 2 bytes. */ if (bufchain_size(&p->pending_input_data) < 2) return 1; /* not got anything yet */ /* get the response */ bufchain_fetch(&p->pending_input_data, data, 2); bufchain_consume(&p->pending_input_data, 2); } if (p->chap_num_attributes == 0) { /* If there are no attributes, this is our first msg * with the server, where we negotiate version and * number of attributes */ if (data[0] != 0x01) { plug_closing(p->plug, "Proxy error: SOCKS proxy wants" " a different CHAP version", PROXY_ERROR_GENERAL, 0); return 1; } if (data[1] == 0x00) { plug_closing(p->plug, "Proxy error: SOCKS proxy won't" " negotiate CHAP with us", PROXY_ERROR_GENERAL, 0); return 1; } p->chap_num_attributes = data[1]; } else { if (p->chap_current_attribute == -1) { /* We have to read in each attribute/value pair - * those we don't understand can be ignored, but * there are a few we'll need to handle. */ p->chap_current_attribute = data[0]; p->chap_current_datalen = data[1]; } if (bufchain_size(&p->pending_input_data) < p->chap_current_datalen) return 1; /* not got everything yet */ /* get the response */ bufchain_fetch(&p->pending_input_data, data, p->chap_current_datalen); bufchain_consume(&p->pending_input_data, p->chap_current_datalen); switch (p->chap_current_attribute) { case 0x00: /* Successful authentication */ if (data[0] == 0x00) p->state = 2; else { plug_closing(p->plug, "Proxy error: SOCKS proxy" " refused CHAP authentication", PROXY_ERROR_GENERAL, 0); return 1; } 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, 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 " "didn't offer it!", PROXY_ERROR_GENERAL, 0); return 1; } break; } p->chap_current_attribute = -1; p->chap_num_attributes_processed++; } if (p->state == 8 && p->chap_num_attributes_processed >= p->chap_num_attributes) { p->chap_num_attributes = 0; p->chap_num_attributes_processed = 0; p->chap_current_datalen = 0; } } return 0; } int proxy_socks5_selectchap(ProxySocket *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; p->state = 8; } else plug_closing(p->plug, "Proxy error: Server chose " "CHAP authentication but we didn't offer it!", PROXY_ERROR_GENERAL, 0); return 1; } putty-0.76/defs.h0000644000175000017500000001646314072266310010661 00000000000000/* * defs.h: initial definitions for PuTTY. * * The rule about this header file is that it can't depend on any * other header file in this code base. This is where we define * things, as much as we can, that other headers will want to refer * to, such as opaque structure types and their associated typedefs, * or macros that are used by other headers. */ #ifndef PUTTY_DEFS_H #define PUTTY_DEFS_H #include #include #include /* for __MINGW_PRINTF_FORMAT */ #include #if defined _MSC_VER && _MSC_VER < 1800 /* Work around lack of inttypes.h and strtoumax in older MSVC */ #define PRIx32 "x" #define PRIu32 "u" #define PRIu64 "I64u" #define PRIdMAX "I64d" #define PRIXMAX "I64X" #define SCNu64 "I64u" #define SIZEx "Ix" #define SIZEu "Iu" uintmax_t strtoumax(const char *nptr, char **endptr, int base); #else #include /* Because we still support older MSVC libraries which don't recognise the * standard C "z" modifier for size_t-sized integers, we must use an * inttypes.h-style macro for those */ #define SIZEx "zx" #define SIZEu "zu" #endif #if defined __GNUC__ || defined __clang__ /* * On MinGW, the correct compiler format checking for vsnprintf() etc * can depend on compile-time flags; these control whether you get * ISO C or Microsoft's non-standard format strings. * We sometimes use __attribute__ ((format)) for our own printf-like * functions, which are ultimately interpreted by the toolchain-chosen * printf, so we need to take that into account to get correct warnings. */ #ifdef __MINGW_PRINTF_FORMAT #define PRINTF_LIKE(fmt_index, ellipsis_index) \ __attribute__ ((format (__MINGW_PRINTF_FORMAT, fmt_index, ellipsis_index))) #else #define PRINTF_LIKE(fmt_index, ellipsis_index) \ __attribute__ ((format (printf, fmt_index, ellipsis_index))) #endif #else /* __GNUC__ */ #define PRINTF_LIKE(fmt_index, ellipsis_index) #endif /* __GNUC__ */ typedef struct conf_tag Conf; typedef struct terminal_tag Terminal; typedef struct term_utf8_decode term_utf8_decode; typedef struct Filename Filename; typedef struct FontSpec FontSpec; typedef struct bufchain_tag bufchain; typedef struct strbuf strbuf; typedef struct LoadedFile LoadedFile; typedef struct RSAKey RSAKey; typedef struct BinarySink BinarySink; typedef struct BinarySource BinarySource; typedef struct stdio_sink stdio_sink; typedef struct bufchain_sink bufchain_sink; typedef struct handle_sink handle_sink; typedef struct IdempotentCallback IdempotentCallback; typedef struct SockAddr SockAddr; typedef struct Socket Socket; typedef struct Plug Plug; typedef struct SocketPeerInfo SocketPeerInfo; typedef struct Backend Backend; typedef struct BackendVtable BackendVtable; typedef struct Ldisc_tag Ldisc; typedef struct LogContext LogContext; typedef struct LogPolicy LogPolicy; typedef struct LogPolicyVtable LogPolicyVtable; typedef struct Seat Seat; typedef struct SeatVtable SeatVtable; typedef struct TermWin TermWin; typedef struct TermWinVtable TermWinVtable; typedef struct Ssh Ssh; typedef struct mp_int mp_int; typedef struct MontyContext MontyContext; typedef struct WeierstrassCurve WeierstrassCurve; typedef struct WeierstrassPoint WeierstrassPoint; typedef struct MontgomeryCurve MontgomeryCurve; typedef struct MontgomeryPoint MontgomeryPoint; typedef struct EdwardsCurve EdwardsCurve; typedef struct EdwardsPoint EdwardsPoint; typedef struct SshServerConfig SshServerConfig; typedef struct SftpServer SftpServer; typedef struct SftpServerVtable SftpServerVtable; typedef struct Channel Channel; typedef struct SshChannel SshChannel; typedef struct mainchan mainchan; typedef struct ssh_sharing_state ssh_sharing_state; typedef struct ssh_sharing_connstate ssh_sharing_connstate; typedef struct share_channel share_channel; typedef struct PortFwdManager PortFwdManager; typedef struct PortFwdRecord PortFwdRecord; typedef struct ConnectionLayer ConnectionLayer; typedef struct prng prng; typedef struct ssh_hashalg ssh_hashalg; typedef struct ssh_hash ssh_hash; typedef struct ssh_kex ssh_kex; typedef struct ssh_kexes ssh_kexes; typedef struct ssh_keyalg ssh_keyalg; typedef struct ssh_key ssh_key; typedef struct ssh_compressor ssh_compressor; typedef struct ssh_decompressor ssh_decompressor; typedef struct ssh_compression_alg ssh_compression_alg; typedef struct ssh2_userkey ssh2_userkey; typedef struct ssh2_macalg ssh2_macalg; typedef struct ssh2_mac ssh2_mac; typedef struct ssh_cipheralg ssh_cipheralg; typedef struct ssh_cipher ssh_cipher; typedef struct ssh2_ciphers ssh2_ciphers; typedef struct dh_ctx dh_ctx; typedef struct ecdh_key ecdh_key; typedef struct dlgparam dlgparam; typedef struct settings_w settings_w; typedef struct settings_r settings_r; typedef struct settings_e settings_e; typedef struct SessionSpecial SessionSpecial; typedef struct StripCtrlChars StripCtrlChars; /* * A small structure wrapping up a (pointer, length) pair so that it * can be conveniently passed to or from a function. */ typedef struct ptrlen { const void *ptr; size_t len; } ptrlen; typedef struct logblank_t logblank_t; typedef struct BinaryPacketProtocol BinaryPacketProtocol; typedef struct PacketProtocolLayer PacketProtocolLayer; /* Do a compile-time type-check of 'to_check' (without evaluating it), * as a side effect of returning the value 'to_return'. Note that * although this macro double-*expands* to_return, it always * *evaluates* exactly one copy of it, so it's side-effect safe. */ #define TYPECHECK(to_check, to_return) \ (sizeof(to_check) ? (to_return) : (to_return)) /* Return a pointer to the object of structure type 'type' whose field * with name 'field' is pointed at by 'object'. */ #define container_of(object, type, field) \ TYPECHECK(object == &((type *)0)->field, \ ((type *)(((char *)(object)) - offsetof(type, field)))) #if defined __GNUC__ || defined __clang__ #define NORETURN __attribute__((__noreturn__)) #elif defined _MSC_VER #define NORETURN __declspec(noreturn) #else #define NORETURN #endif /* ---------------------------------------------------------------------- * Platform-specific definitions. * * Most of these live in the per-platform header files, of which * puttyps.h selects the appropriate one. But some of the sources * (particularly standalone test applications) would prefer not to * have to include a per-platform header at all, because that makes it * more portable to platforms not supported by the code base as a * whole (for example, compiling purely computational parts of the * code for specialist platforms for test and analysis purposes). So * any definition that has to affect even _those_ modules will have to * go here, with the key constraint being that this code has to come * to _some_ decision even if the compilation platform is not a * recognised one at all. */ /* Purely computational code uses smemclr(), so we have to make the * decision here about whether that's provided by utils.c or by a * platform implementation. We define PLATFORM_HAS_SMEMCLR to suppress * utils.c's definition. */ #ifdef _WINDOWS /* Windows provides the API function 'SecureZeroMemory', which we use * unless the user has told us not to by defining NO_SECUREZEROMEMORY. */ #ifndef NO_SECUREZEROMEMORY #define PLATFORM_HAS_SMEMCLR #endif #endif #endif /* PUTTY_DEFS_H */ putty-0.76/dialog.c0000644000175000017500000003471014072266310011165 00000000000000/* * dialog.c - a reasonably platform-independent mechanism for * describing dialog boxes. */ #include #include #include #include #define DEFINE_INTORPTR_FNS #include "putty.h" #include "dialog.h" int ctrl_path_elements(const char *path) { int i = 1; while (*path) { if (*path == '/') i++; path++; } return i; } /* Return the number of matching path elements at the starts of p1 and p2, * or INT_MAX if the paths are identical. */ int ctrl_path_compare(const char *p1, const char *p2) { int i = 0; while (*p1 || *p2) { if ((*p1 == '/' || *p1 == '\0') && (*p2 == '/' || *p2 == '\0')) i++; /* a whole element matches, ooh */ if (*p1 != *p2) return i; /* mismatch */ p1++, p2++; } return INT_MAX; /* exact match */ } struct controlbox *ctrl_new_box(void) { 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; sfree(s->pathname); sfree(s->boxname); sfree(s->boxtitle); for (i = 0; i < s->ncontrols; i++) { ctrl_free(s->ctrls[i]); } sfree(s->ctrls); sfree(s); } /* * Find the index of first controlset in a controlbox for a given * path. If that path doesn't exist, return the index where it * should be inserted. */ static int ctrl_find_set(struct controlbox *b, const char *path, bool start) { int i, last, thisone; last = 0; for (i = 0; i < b->nctrlsets; i++) { thisone = ctrl_path_compare(path, b->ctrlsets[i]->pathname); /* * If `start' is true and there exists a controlset with * exactly the path we've been given, we should return the * index of the first such controlset we find. Otherwise, * we should return the index of the first entry in which * _fewer_ path elements match than they did last time. */ if ((start && thisone == INT_MAX) || thisone < last) return i; last = thisone; } return b->nctrlsets; /* insert at end */ } /* * Find the index of next controlset in a controlbox for a given * path, or -1 if no such controlset exists. If -1 is passed as * input, finds the first. */ int ctrl_find_path(struct controlbox *b, const char *path, int index) { if (index < 0) index = ctrl_find_set(b, path, true); else index++; if (index < b->nctrlsets && !strcmp(path, b->ctrlsets[index]->pathname)) return index; else return -1; } /* Set up a panel title. */ struct controlset *ctrl_settitle(struct controlbox *b, const char *path, const char *title) { struct controlset *s = snew(struct controlset); int index = ctrl_find_set(b, path, true); s->pathname = dupstr(path); s->boxname = NULL; s->boxtitle = dupstr(title); s->ncontrols = s->ctrlsize = 0; s->ncolumns = 0; /* this is a title! */ s->ctrls = NULL; sgrowarray(b->ctrlsets, b->ctrlsetsize, b->nctrlsets); if (index < b->nctrlsets) memmove(&b->ctrlsets[index+1], &b->ctrlsets[index], (b->nctrlsets-index) * sizeof(*b->ctrlsets)); b->ctrlsets[index] = s; b->nctrlsets++; return s; } /* Retrieve a pointer to a controlset, creating it if absent. */ struct controlset *ctrl_getset(struct controlbox *b, const char *path, const char *name, const char *boxtitle) { struct controlset *s; int index = ctrl_find_set(b, path, true); while (index < b->nctrlsets && !strcmp(b->ctrlsets[index]->pathname, path)) { if (b->ctrlsets[index]->boxname && !strcmp(b->ctrlsets[index]->boxname, name)) return b->ctrlsets[index]; index++; } s = snew(struct controlset); s->pathname = dupstr(path); s->boxname = dupstr(name); s->boxtitle = boxtitle ? dupstr(boxtitle) : NULL; s->ncolumns = 1; s->ncontrols = s->ctrlsize = 0; s->ctrls = NULL; sgrowarray(b->ctrlsets, b->ctrlsetsize, b->nctrlsets); if (index < b->nctrlsets) memmove(&b->ctrlsets[index+1], &b->ctrlsets[index], (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); sgrowarray(b->frees, b->freesize, b->nfrees); 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); sgrowarray(s->ctrls, s->ctrlsize, s->ncontrols); s->ctrls[s->ncontrols++] = c; /* * Fill in the standard fields. */ c->generic.type = type; c->generic.tabdelay = false; c->generic.column = COLUMN_FIELD(0, s->ncolumns); c->generic.helpctx = helpctx; c->generic.handler = handler; c->generic.context = context; c->generic.label = NULL; c->generic.align_next_to = NULL; return c; } /* `ncolumns' is followed by that many percentages, as integers. */ union control *ctrl_columns(struct controlset *s, int ncolumns, ...) { union control *c = ctrl_new(s, CTRL_COLUMNS, P(NULL), NULL, P(NULL)); assert(s->ncolumns == 1 || ncolumns == 1); c->columns.ncols = ncolumns; s->ncolumns = ncolumns; if (ncolumns == 1) { c->columns.percentages = NULL; } else { va_list ap; int i; c->columns.percentages = snewn(ncolumns, int); va_start(ap, ncolumns); for (i = 0; i < ncolumns; i++) c->columns.percentages[i] = va_arg(ap, int); va_end(ap); } return c; } union control *ctrl_editbox(struct controlset *s, const char *label, char shortcut, int percentage, intorptr helpctx, handler_fn handler, intorptr context, intorptr context2) { union control *c = ctrl_new(s, CTRL_EDITBOX, helpctx, handler, context); c->editbox.label = label ? dupstr(label) : NULL; c->editbox.shortcut = shortcut; c->editbox.percentwidth = percentage; c->editbox.password = false; c->editbox.has_list = false; c->editbox.context2 = context2; return c; } union control *ctrl_combobox(struct controlset *s, const char *label, char shortcut, int percentage, intorptr helpctx, handler_fn handler, intorptr context, intorptr context2) { union control *c = ctrl_new(s, CTRL_EDITBOX, helpctx, handler, context); c->editbox.label = label ? dupstr(label) : NULL; c->editbox.shortcut = shortcut; c->editbox.percentwidth = percentage; c->editbox.password = false; c->editbox.has_list = true; c->editbox.context2 = context2; return c; } /* * `ncolumns' is followed by (alternately) radio button titles and * intorptrs, until a NULL in place of a title string is seen. Each * title is expected to be followed by a shortcut _iff_ `shortcut' * is NO_SHORTCUT. */ union control *ctrl_radiobuttons(struct controlset *s, const char *label, char shortcut, int ncolumns, intorptr helpctx, handler_fn handler, intorptr context, ...) { va_list ap; int i; union control *c = ctrl_new(s, CTRL_RADIO, helpctx, handler, context); c->radio.label = label ? dupstr(label) : NULL; c->radio.shortcut = shortcut; c->radio.ncolumns = ncolumns; /* * Initial pass along variable argument list to count the * buttons. */ va_start(ap, context); i = 0; while (va_arg(ap, char *) != NULL) { i++; if (c->radio.shortcut == NO_SHORTCUT) (void)va_arg(ap, int); /* char promotes to int in arg lists */ (void)va_arg(ap, intorptr); } va_end(ap); c->radio.nbuttons = i; if (c->radio.shortcut == NO_SHORTCUT) c->radio.shortcuts = snewn(c->radio.nbuttons, char); else c->radio.shortcuts = NULL; c->radio.buttons = snewn(c->radio.nbuttons, char *); c->radio.buttondata = snewn(c->radio.nbuttons, intorptr); /* * Second pass along variable argument list to actually fill in * the structure. */ va_start(ap, context); for (i = 0; i < c->radio.nbuttons; i++) { c->radio.buttons[i] = dupstr(va_arg(ap, char *)); if (c->radio.shortcut == NO_SHORTCUT) c->radio.shortcuts[i] = va_arg(ap, int); /* char promotes to int in arg lists */ c->radio.buttondata[i] = va_arg(ap, intorptr); } va_end(ap); return c; } union control *ctrl_pushbutton(struct controlset *s, const char *label, char shortcut, intorptr helpctx, handler_fn handler, intorptr context) { union control *c = ctrl_new(s, CTRL_BUTTON, helpctx, handler, context); c->button.label = label ? dupstr(label) : NULL; c->button.shortcut = shortcut; c->button.isdefault = false; c->button.iscancel = false; return c; } union control *ctrl_listbox(struct controlset *s, const char *label, char shortcut, intorptr helpctx, handler_fn handler, intorptr context) { union control *c = ctrl_new(s, CTRL_LISTBOX, helpctx, handler, context); c->listbox.label = label ? dupstr(label) : NULL; c->listbox.shortcut = shortcut; c->listbox.height = 5; /* *shrug* a plausible default */ c->listbox.draglist = false; c->listbox.multisel = 0; c->listbox.percentwidth = 100; c->listbox.ncols = 0; c->listbox.percentages = NULL; c->listbox.hscroll = true; return c; } union control *ctrl_droplist(struct controlset *s, const char *label, char shortcut, int percentage, intorptr helpctx, handler_fn handler, intorptr context) { union control *c = ctrl_new(s, CTRL_LISTBOX, helpctx, handler, context); c->listbox.label = label ? dupstr(label) : NULL; c->listbox.shortcut = shortcut; c->listbox.height = 0; /* means it's a drop-down list */ c->listbox.draglist = false; c->listbox.multisel = 0; c->listbox.percentwidth = percentage; c->listbox.ncols = 0; c->listbox.percentages = NULL; c->listbox.hscroll = false; return c; } union control *ctrl_draglist(struct controlset *s, const char *label, char shortcut, intorptr helpctx, handler_fn handler, intorptr context) { union control *c = ctrl_new(s, CTRL_LISTBOX, helpctx, handler, context); c->listbox.label = label ? dupstr(label) : NULL; c->listbox.shortcut = shortcut; c->listbox.height = 5; /* *shrug* a plausible default */ c->listbox.draglist = true; c->listbox.multisel = 0; c->listbox.percentwidth = 100; c->listbox.ncols = 0; c->listbox.percentages = NULL; c->listbox.hscroll = false; return c; } union control *ctrl_filesel(struct controlset *s, const char *label, char shortcut, const char *filter, bool write, const char *title, intorptr helpctx, handler_fn handler, intorptr context) { union control *c = ctrl_new(s, CTRL_FILESELECT, helpctx, handler, context); c->fileselect.label = label ? dupstr(label) : NULL; c->fileselect.shortcut = shortcut; c->fileselect.filter = filter; c->fileselect.for_writing = write; c->fileselect.title = dupstr(title); return c; } union control *ctrl_fontsel(struct controlset *s, const char *label, char shortcut, intorptr helpctx, handler_fn handler, intorptr context) { union control *c = ctrl_new(s, CTRL_FONTSELECT, helpctx, handler, context); c->fontselect.label = label ? dupstr(label) : NULL; c->fontselect.shortcut = shortcut; return c; } union control *ctrl_tabdelay(struct controlset *s, union control *ctrl) { union control *c = ctrl_new(s, CTRL_TABDELAY, P(NULL), NULL, P(NULL)); c->tabdelay.ctrl = ctrl; return c; } union control *ctrl_text(struct controlset *s, const char *text, intorptr helpctx) { union control *c = ctrl_new(s, CTRL_TEXT, helpctx, NULL, P(NULL)); c->text.label = dupstr(text); return c; } union control *ctrl_checkbox(struct controlset *s, const char *label, char shortcut, intorptr helpctx, handler_fn handler, intorptr context) { union control *c = ctrl_new(s, CTRL_CHECKBOX, helpctx, handler, context); c->checkbox.label = label ? dupstr(label) : NULL; c->checkbox.shortcut = shortcut; return c; } void ctrl_free(union control *ctrl) { int i; sfree(ctrl->generic.label); switch (ctrl->generic.type) { case CTRL_RADIO: for (i = 0; i < ctrl->radio.nbuttons; i++) sfree(ctrl->radio.buttons[i]); sfree(ctrl->radio.buttons); sfree(ctrl->radio.shortcuts); sfree(ctrl->radio.buttondata); break; case CTRL_COLUMNS: sfree(ctrl->columns.percentages); break; case CTRL_LISTBOX: sfree(ctrl->listbox.percentages); break; case CTRL_FILESELECT: sfree(ctrl->fileselect.title); break; } sfree(ctrl); } putty-0.76/dialog.h0000644000175000017500000006602614072266310011177 00000000000000/* * Exports and types from dialog.c. */ /* * This is the big union which defines a single control, of any * type. * * General principles: * - _All_ pointers in this structure are expected to point to * dynamically allocated things, unless otherwise indicated. * - `char' fields giving keyboard shortcuts are expected to be * NO_SHORTCUT if no shortcut is desired for a particular control. * - The `label' field can often be NULL, which will cause the * control to not have a label at all. This doesn't apply to * checkboxes and push buttons, in which the label is not * separate from the control. */ #define NO_SHORTCUT '\0' enum { CTRL_TEXT, /* just a static line of text */ CTRL_EDITBOX, /* label plus edit box */ CTRL_RADIO, /* label plus radio buttons */ CTRL_CHECKBOX, /* checkbox (contains own label) */ CTRL_BUTTON, /* simple push button (no label) */ CTRL_LISTBOX, /* label plus list box */ CTRL_COLUMNS, /* divide window into columns */ CTRL_FILESELECT, /* label plus filename selector */ CTRL_FONTSELECT, /* label plus font selector */ CTRL_TABDELAY /* see `tabdelay' below */ }; /* * Many controls have `intorptr' unions for storing user data, * since the user might reasonably want to store either an integer * or a void * pointer. Here I define a union, and two convenience * functions to create that union from actual integers or pointers. * * The convenience functions are declared as inline if possible. * Otherwise, they're declared here and defined when this header is * included with DEFINE_INTORPTR_FNS defined. This is a total pain, * but such is life. */ typedef union { void *p; int i; } intorptr; #ifndef INLINE intorptr I(int i); intorptr P(void *p); #endif #if defined DEFINE_INTORPTR_FNS || defined INLINE #ifdef INLINE #define PREFIX INLINE #else #define PREFIX #endif PREFIX intorptr I(int i) { intorptr ret; ret.i = i; return ret; } PREFIX intorptr P(void *p) { intorptr ret; ret.p = p; return ret; } #undef PREFIX #endif /* * Each control has an `int' field specifying which columns it * occupies in a multi-column part of the dialog box. These macros * pack and unpack that field. * * If a control belongs in exactly one column, just specifying the * column number is perfectly adequate. */ #define COLUMN_FIELD(start, span) ( (((span)-1) << 16) + (start) ) #define COLUMN_START(field) ( (field) & 0xFFFF ) #define COLUMN_SPAN(field) ( (((field) >> 16) & 0xFFFF) + 1 ) union control; /* * The number of event types is being deliberately kept small, on * the grounds that not all platforms might be able to report a * large number of subtle events. We have: * - the special REFRESH event, called when a control's value * needs setting * - the ACTION event, called when the user does something that * positively requests action (double-clicking a list box item, * or pushing a push-button) * - the VALCHANGE event, called when the user alters the setting * of the control in a way that is usually considered to alter * the underlying data (toggling a checkbox or radio button, * moving the items around in a drag-list, editing an edit * control) * - the SELCHANGE event, called when the user alters the setting * of the control in a more minor way (changing the selected * item in a list box). * - the CALLBACK event, which happens after the handler routine * has requested a subdialog (file selector, font selector, * colour selector) and it has come back with information. */ enum { EVENT_REFRESH, EVENT_ACTION, EVENT_VALCHANGE, EVENT_SELCHANGE, EVENT_CALLBACK }; typedef void (*handler_fn)(union control *ctrl, dlgparam *dp, void *data, int event); #define STANDARD_PREFIX \ int type; \ char *label; \ bool tabdelay; \ int column; \ handler_fn handler; \ intorptr context; \ intorptr helpctx; \ union control *align_next_to union control { /* * The first possibility in this union is the generic header * shared by all the structures, which we are therefore allowed * to access through any one of them. */ struct { int type; /* * Every control except CTRL_COLUMNS has _some_ sort of * label. By putting it in the `generic' union as well as * everywhere else, we avoid having to have an irritating * switch statement when we go through and deallocate all * the memory in a config-box structure. * * Yes, this does mean that any non-NULL value in this * field is expected to be dynamically allocated and * freeable. * * For CTRL_COLUMNS, this field MUST be NULL. */ char *label; /* * If `tabdelay' is non-zero, it indicates that this * particular control should not yet appear in the tab * order. A subsequent CTRL_TABDELAY entry will place it. */ bool tabdelay; /* * Indicate which column(s) this control occupies. This can * be unpacked into starting column and column span by the * COLUMN macros above. */ int column; /* * 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; /* * Almost all of the above functions will find it useful to * be able to store a piece of `void *' or `int' data. */ intorptr context; /* * For any control, we also allow the storage of a piece of * data for use by context-sensitive help. For example, on * Windows you can click the magic question mark and then * click a control, and help for that control should spring * up. Hence, here is a slot in which to store per-control * data that a particular platform-specific driver can use * to ensure it brings up the right piece of help text. */ intorptr helpctx; /* * Setting this to non-NULL coerces two controls to have their * y-coordinates adjusted so that they can sit alongside each * other and look nicely aligned, even if they're different * heights. * * Set this field on the _second_ control of the pair (in * terms of order in the data structure), so that when it's * instantiated, the first one is already there to be referred * to. */ union control *align_next_to; } generic; struct { STANDARD_PREFIX; union control *ctrl; } tabdelay; struct { STANDARD_PREFIX; } text; struct { STANDARD_PREFIX; char shortcut; /* keyboard shortcut */ /* * Percentage of the dialog-box width used by the edit box. * If this is set to 100, the label is on its own line; * otherwise the label is on the same line as the box * itself. */ int percentwidth; bool password; /* details of input are hidden */ /* * A special case of the edit box is the combo box, which * has a drop-down list built in. (Note that a _non_- * editable drop-down list is done as a special case of a * list box.) * * Don't try setting has_list and password on the same * control; front ends are not required to support that * combination. */ bool has_list; /* * Edit boxes tend to need two items of context, so here's * a spare. */ intorptr context2; } editbox; struct { STANDARD_PREFIX; /* * `shortcut' here is a single keyboard shortcut which is * expected to select the whole group of radio buttons. It * can be NO_SHORTCUT if required, and there is also a way * to place individual shortcuts on each button; see below. */ char shortcut; /* * There are separate fields for `ncolumns' and `nbuttons' * for several reasons. * * Firstly, we sometimes want the last of a set of buttons * to have a longer label than the rest; we achieve this by * setting `ncolumns' higher than `nbuttons', and the * layout code is expected to understand that the final * button should be given all the remaining space on the * line. This sounds like a ludicrously specific special * case (if we're doing this sort of thing, why not have * the general ability to have a particular button span * more than one column whether it's the last one or not?) * but actually it's reasonably common for the sort of * three-way control you get a lot of in PuTTY: `yes' * versus `no' versus `some more complex way to decide'. * * Secondly, setting `nbuttons' higher than `ncolumns' lets * us have more than one line of radio buttons for a single * setting. A very important special case of this is * setting `ncolumns' to 1, so that each button is on its * own line. */ int ncolumns; int nbuttons; /* * This points to a dynamically allocated array of `char *' * pointers, each of which points to a dynamically * allocated string. */ char **buttons; /* `nbuttons' button labels */ /* * This points to a dynamically allocated array of `char' * giving the individual keyboard shortcuts for each radio * button. The array may be NULL if none are required. */ char *shortcuts; /* `nbuttons' shortcuts; may be NULL */ /* * This points to a dynamically allocated array of * intorptr, giving helpful data for each button. */ intorptr *buttondata; /* `nbuttons' entries; may be NULL */ } radio; struct { STANDARD_PREFIX; char shortcut; } checkbox; struct { STANDARD_PREFIX; char shortcut; /* * At least Windows has the concept of a `default push * button', which gets implicitly pressed when you hit * Return even if it doesn't have the input focus. */ bool isdefault; /* * Also, the reverse of this: a default cancel-type button, * which is implicitly pressed when you hit Escape. */ bool iscancel; } button; struct { STANDARD_PREFIX; char shortcut; /* keyboard shortcut */ /* * Height of the list box, in approximate number of lines. * If this is zero, the list is a drop-down list. */ int height; /* height in lines */ /* * If this is set, the list elements can be reordered by * the user (by drag-and-drop or by Up and Down buttons, * whatever the per-platform implementation feels * comfortable with). This is not guaranteed to work on a * drop-down list, so don't try it! */ bool draglist; /* * If this is non-zero, the list can have more than one * element selected at a time. This is not guaranteed to * work on a drop-down list, so don't try it! * * Different non-zero values request slightly different * types of multi-selection (this may well be meaningful * only in GTK, so everyone else can ignore it if they * want). 1 means the list box expects to have individual * items selected, whereas 2 means it expects the user to * want to select a large contiguous range at a time. */ int multisel; /* * Percentage of the dialog-box width used by the list box. * If this is set to 100, the label is on its own line; * otherwise the label is on the same line as the box * itself. Setting this to anything other than 100 is not * guaranteed to work on a _non_-drop-down list, so don't * try it! */ int percentwidth; /* * Some list boxes contain strings that contain tab * characters. If `ncols' is greater than 0, then * `percentages' is expected to be non-zero and to contain * the respective widths of `ncols' columns, which together * will exactly fit the width of the list box. Otherwise * `percentages' must be NULL. * * There should never be more than one column in a * drop-down list (one with height==0), because front ends * may have to implement it as a special case of an * editable combo box. */ int ncols; /* number of columns */ int *percentages; /* % width of each column */ /* * Flag which can be set to false to suppress the horizontal * scroll bar if a list box entry goes off the right-hand * side. */ bool hscroll; } listbox; struct { STANDARD_PREFIX; char shortcut; /* * `filter' dictates what type of files will be selected by * default; for example, when selecting private key files * the file selector would do well to only show .PPK files * (on those systems where this is the chosen extension). * * The precise contents of `filter' are platform-defined, * unfortunately. The special value NULL means `all files' * and is always a valid fallback. * * Unlike almost all strings in this structure, this value * is NOT expected to require freeing (although of course * you can always use ctrl_alloc if you do need to create * one on the fly). This is because the likely mode of use * is to define string constants in a platform-specific * header file, and directly reference those. Or worse, a * particular platform might choose to cast integers into * this pointer type... */ char const *filter; /* * Some systems like to know whether a file selector is * choosing a file to read or one to write (and possibly * create). */ bool for_writing; /* * On at least some platforms, the file selector is a * separate dialog box, and contains a user-settable title. * * This value _is_ expected to require freeing. */ char *title; } fileselect; struct { /* In this variant, `label' MUST be NULL. */ STANDARD_PREFIX; int ncols; /* number of columns */ int *percentages; /* % width of each column */ /* * Every time this control type appears, exactly one of * `ncols' and the previous number of columns MUST be one. * Attempting to allow a seamless transition from a four- * to a five-column layout, for example, would be way more * trouble than it was worth. If you must lay things out * like that, define eight unevenly sized columns and use * column-spanning a lot. But better still, just don't. * * `percentages' may be NULL if ncols==1, to save space. */ } columns; struct { STANDARD_PREFIX; char shortcut; } fontselect; }; #undef STANDARD_PREFIX /* * `controlset' is a container holding an array of `union control' * structures, together with a panel name and a title for the whole * set. In Windows and any similar-looking GUI, each `controlset' * in the config will be a container box within a panel. * * Special case: if `boxname' is NULL, the control set gives an * overall title for an entire panel of controls. */ struct controlset { char *pathname; /* panel path, e.g. "SSH/Tunnels" */ char *boxname; /* internal short name of controlset */ char *boxtitle; /* title of container box */ int ncolumns; /* current no. of columns at bottom */ size_t ncontrols; /* number of `union control' in array */ size_t 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 { size_t nctrlsets; /* number of ctrlsets */ size_t ctrlsetsize; /* ctrlset size */ struct controlset **ctrlsets; /* actual array of ctrlsets */ size_t nfrees; size_t 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. */ /* Set up a panel title. */ struct controlset *ctrl_settitle(struct controlbox *, const char *path, const char *title); /* Retrieve a pointer to a controlset, creating it if absent. */ struct controlset *ctrl_getset(struct controlbox *, const char *path, const char *name, const char *boxtitle); void ctrl_free_set(struct controlset *); void ctrl_free(union control *); /* * 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 * can be made. */ /* `ncolumns' is followed by that many percentages, as integers. */ union control *ctrl_columns(struct controlset *, int ncolumns, ...); union control *ctrl_editbox(struct controlset *, const char *label, char shortcut, int percentage, intorptr helpctx, handler_fn handler, intorptr context, intorptr context2); union control *ctrl_combobox(struct controlset *, const char *label, char shortcut, int percentage, intorptr helpctx, handler_fn handler, intorptr context, intorptr context2); /* * `ncolumns' is followed by (alternately) radio button titles and * intorptrs, until a NULL in place of a title string is seen. Each * title is expected to be followed by a shortcut _iff_ `shortcut' * is NO_SHORTCUT. */ union control *ctrl_radiobuttons(struct controlset *, const char *label, char shortcut, int ncolumns, intorptr helpctx, handler_fn handler, intorptr context, ...); union control *ctrl_pushbutton(struct controlset *, const char *label, char shortcut, intorptr helpctx, handler_fn handler, intorptr context); union control *ctrl_listbox(struct controlset *, const char *label, char shortcut, intorptr helpctx, handler_fn handler, intorptr context); union control *ctrl_droplist(struct controlset *, const char *label, char shortcut, int percentage, intorptr helpctx, handler_fn handler, intorptr context); union control *ctrl_draglist(struct controlset *, const char *label, char shortcut, intorptr helpctx, handler_fn handler, intorptr context); union control *ctrl_filesel(struct controlset *, const char *label, char shortcut, const char *filter, bool write, const char *title, intorptr helpctx, handler_fn handler, intorptr context); union control *ctrl_fontsel(struct controlset *, const char *label, char shortcut, intorptr helpctx, handler_fn handler, intorptr context); union control *ctrl_text(struct controlset *, const char *text, intorptr helpctx); union control *ctrl_checkbox(struct controlset *, const 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, dlgparam *dp, int whichbutton); int dlg_radiobutton_get(union control *ctrl, dlgparam *dp); void dlg_checkbox_set(union control *ctrl, dlgparam *dp, bool checked); bool dlg_checkbox_get(union control *ctrl, dlgparam *dp); void dlg_editbox_set(union control *ctrl, dlgparam *dp, char const *text); char *dlg_editbox_get(union control *ctrl, dlgparam *dp); /* result must be freed by caller */ /* The `listbox' functions can also apply to combo boxes. */ void dlg_listbox_clear(union control *ctrl, dlgparam *dp); void dlg_listbox_del(union control *ctrl, dlgparam *dp, int index); void dlg_listbox_add(union control *ctrl, dlgparam *dp, 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, dlgparam *dp, char const *text, int id); int dlg_listbox_getid(union control *ctrl, dlgparam *dp, int index); /* dlg_listbox_index returns <0 if no single element is selected. */ int dlg_listbox_index(union control *ctrl, dlgparam *dp); bool dlg_listbox_issel(union control *ctrl, dlgparam *dp, int index); void dlg_listbox_select(union control *ctrl, dlgparam *dp, int index); void dlg_text_set(union control *ctrl, dlgparam *dp, char const *text); void dlg_filesel_set(union control *ctrl, dlgparam *dp, Filename *fn); Filename *dlg_filesel_get(union control *ctrl, dlgparam *dp); void dlg_fontsel_set(union control *ctrl, dlgparam *dp, FontSpec *fn); FontSpec *dlg_fontsel_get(union control *ctrl, dlgparam *dp); /* * 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, dlgparam *dp); void dlg_update_done(union control *ctrl, dlgparam *dp); /* * Set input focus into a particular control. */ void dlg_set_focus(union control *ctrl, dlgparam *dp); /* * Change the label text on a control. */ void dlg_label_change(union control *ctrl, dlgparam *dp, char const *text); /* * Return the `ctrl' structure for the most recent control that had * the input focus apart from the one mentioned. This is NOT * GUARANTEED to work on all platforms, so don't base any critical * functionality on it! */ union control *dlg_last_focused(union control *ctrl, dlgparam *dp); /* * Find out whether a particular control is currently visible. */ bool dlg_is_visible(union control *ctrl, dlgparam *dp); /* * During event processing, you might well want to give an error * indication to the user. dlg_beep() is a quick and easy generic * error; dlg_error() puts up a message-box or equivalent. */ void dlg_beep(dlgparam *dp); void dlg_error_msg(dlgparam *dp, const char *msg); /* * This function signals to the front end that the dialog's * processing is completed, and passes an integer value (typically * a success status). */ void dlg_end(dlgparam *dp, int value); /* * Routines to manage a (per-platform) colour selector. * dlg_coloursel_start() is called in an event handler, and * schedules the running of a colour selector after the event * handler returns. The colour selector will send EVENT_CALLBACK to * the control that spawned it, when it's finished; * dlg_coloursel_results() fetches the results, as integers from 0 * to 255; it returns nonzero on success, or zero if the colour * selector was dismissed by hitting Cancel or similar. * * dlg_coloursel_start() accepts an RGB triple which is used to * initialise the colour selector to its starting value. */ void dlg_coloursel_start(union control *ctrl, dlgparam *dp, int r, int g, int b); bool dlg_coloursel_results(union control *ctrl, dlgparam *dp, int *r, int *g, int *b); /* * This routine is used by the platform-independent code to * indicate that the value of a particular control is likely to * have changed. It triggers a call of the handler for that control * with `event' set to EVENT_REFRESH. * * If `ctrl' is NULL, _all_ controls in the dialog get refreshed * (for loading or saving entire sets of settings). */ void dlg_refresh(union control *ctrl, dlgparam *dp); /* * Standard helper functions for reading a controlbox structure. */ /* * Find the index of next controlset in a controlbox for a given * path, or -1 if no such controlset exists. If -1 is passed as * input, finds the first. Intended usage is something like * * for (index=-1; (index=ctrl_find_path(ctrlbox, index, path)) >= 0 ;) { * ... process this controlset ... * } */ int ctrl_find_path(struct controlbox *b, const char *path, int index); int ctrl_path_elements(const char *path); /* Return the number of matching path elements at the starts of p1 and p2, * or INT_MAX if the paths are identical. */ int ctrl_path_compare(const char *p1, const char *p2); putty-0.76/ecc.c0000644000175000017500000010657014072266310010464 00000000000000#include #include "ssh.h" #include "mpint.h" #include "ecc.h" /* ---------------------------------------------------------------------- * Weierstrass curves. */ struct WeierstrassPoint { /* * Internally, we represent a point using 'Jacobian coordinates', * which are three values X,Y,Z whose relation to the affine * coordinates x,y is that x = X/Z^2 and y = Y/Z^3. * * This allows us to do most of our calculations without having to * take an inverse mod p: every time the obvious affine formulae * would need you to divide by something, you instead multiply it * into the 'denominator' coordinate Z. You only have to actually * take the inverse of Z when you need to get the affine * coordinates back out, which means you do it once after your * entire computation instead of at every intermediate step. * * The point at infinity is represented by setting all three * coordinates to zero. * * These values are also stored in the Montgomery-multiplication * transformed representation. */ mp_int *X, *Y, *Z; WeierstrassCurve *wc; }; struct WeierstrassCurve { /* Prime modulus of the finite field. */ mp_int *p; /* Persistent Montgomery context for doing arithmetic mod p. */ MontyContext *mc; /* Modsqrt context for point decompression. NULL if this curve was * constructed without providing nonsquare_mod_p. */ ModsqrtContext *sc; /* Parameters of the curve, in Montgomery-multiplication * transformed form. */ mp_int *a, *b; }; WeierstrassCurve *ecc_weierstrass_curve( mp_int *p, mp_int *a, mp_int *b, mp_int *nonsquare_mod_p) { WeierstrassCurve *wc = snew(WeierstrassCurve); wc->p = mp_copy(p); wc->mc = monty_new(p); wc->a = monty_import(wc->mc, a); wc->b = monty_import(wc->mc, b); if (nonsquare_mod_p) wc->sc = modsqrt_new(p, nonsquare_mod_p); else wc->sc = NULL; return wc; } void ecc_weierstrass_curve_free(WeierstrassCurve *wc) { mp_free(wc->p); mp_free(wc->a); mp_free(wc->b); monty_free(wc->mc); if (wc->sc) modsqrt_free(wc->sc); sfree(wc); } static WeierstrassPoint *ecc_weierstrass_point_new_empty(WeierstrassCurve *wc) { WeierstrassPoint *wp = snew(WeierstrassPoint); wp->wc = wc; wp->X = wp->Y = wp->Z = NULL; return wp; } static WeierstrassPoint *ecc_weierstrass_point_new_imported( WeierstrassCurve *wc, mp_int *monty_x, mp_int *monty_y) { WeierstrassPoint *wp = ecc_weierstrass_point_new_empty(wc); wp->X = monty_x; wp->Y = monty_y; wp->Z = mp_copy(monty_identity(wc->mc)); return wp; } WeierstrassPoint *ecc_weierstrass_point_new( WeierstrassCurve *wc, mp_int *x, mp_int *y) { return ecc_weierstrass_point_new_imported( wc, monty_import(wc->mc, x), monty_import(wc->mc, y)); } WeierstrassPoint *ecc_weierstrass_point_new_identity(WeierstrassCurve *wc) { WeierstrassPoint *wp = ecc_weierstrass_point_new_empty(wc); size_t bits = mp_max_bits(wc->p); wp->X = mp_new(bits); wp->Y = mp_new(bits); wp->Z = mp_new(bits); return wp; } void ecc_weierstrass_point_copy_into( WeierstrassPoint *dest, WeierstrassPoint *src) { mp_copy_into(dest->X, src->X); mp_copy_into(dest->Y, src->Y); mp_copy_into(dest->Z, src->Z); } WeierstrassPoint *ecc_weierstrass_point_copy(WeierstrassPoint *orig) { WeierstrassPoint *wp = ecc_weierstrass_point_new_empty(orig->wc); wp->X = mp_copy(orig->X); wp->Y = mp_copy(orig->Y); wp->Z = mp_copy(orig->Z); return wp; } void ecc_weierstrass_point_free(WeierstrassPoint *wp) { mp_free(wp->X); mp_free(wp->Y); mp_free(wp->Z); smemclr(wp, sizeof(*wp)); sfree(wp); } WeierstrassPoint *ecc_weierstrass_point_new_from_x( WeierstrassCurve *wc, mp_int *xorig, unsigned desired_y_parity) { assert(wc->sc); /* * The curve equation is y^2 = x^3 + ax + b, which is already * conveniently in a form where we can compute the RHS and take * the square root of it to get y. */ unsigned success; mp_int *x = monty_import(wc->mc, xorig); /* * Compute the RHS of the curve equation. We don't need to take * account of z here, because we're constructing the point from * scratch. So it really is just x^3 + ax + b. */ mp_int *x2 = monty_mul(wc->mc, x, x); mp_int *x2_plus_a = monty_add(wc->mc, x2, wc->a); mp_int *x3_plus_ax = monty_mul(wc->mc, x2_plus_a, x); mp_int *rhs = monty_add(wc->mc, x3_plus_ax, wc->b); mp_free(x2); mp_free(x2_plus_a); mp_free(x3_plus_ax); mp_int *y = monty_modsqrt(wc->sc, rhs, &success); mp_free(rhs); if (!success) { /* Failure! x^3+ax+b worked out to be a number that has no * square root mod p. In this situation there's no point in * trying to be time-constant, since the protocol sequence is * going to diverge anyway when we complain to whoever gave us * this bogus value. */ mp_free(x); mp_free(y); return NULL; } /* * Choose whichever of y and p-y has the specified parity (of its * lowest positive residue mod p). */ mp_int *tmp = monty_export(wc->mc, y); unsigned flip = (mp_get_bit(tmp, 0) ^ desired_y_parity) & 1; mp_sub_into(tmp, wc->p, y); mp_select_into(y, y, tmp, flip); mp_free(tmp); return ecc_weierstrass_point_new_imported(wc, x, y); } static void ecc_weierstrass_cond_overwrite( WeierstrassPoint *dest, WeierstrassPoint *src, unsigned overwrite) { mp_select_into(dest->X, dest->X, src->X, overwrite); mp_select_into(dest->Y, dest->Y, src->Y, overwrite); mp_select_into(dest->Z, dest->Z, src->Z, overwrite); } static void ecc_weierstrass_cond_swap( WeierstrassPoint *P, WeierstrassPoint *Q, unsigned swap) { mp_cond_swap(P->X, Q->X, swap); mp_cond_swap(P->Y, Q->Y, swap); mp_cond_swap(P->Z, Q->Z, swap); } /* * Shared code between all three of the basic arithmetic functions: * once we've determined the slope of the line that we're intersecting * the curve with, this takes care of finding the coordinates of the * third intersection point (given the two input x-coordinates and one * of the y-coords) and negating it to generate the output. */ static inline void ecc_weierstrass_epilogue( mp_int *Px, mp_int *Qx, mp_int *Py, mp_int *common_Z, mp_int *lambda_n, mp_int *lambda_d, WeierstrassPoint *out) { WeierstrassCurve *wc = out->wc; /* Powers of the numerator and denominator of the slope lambda */ mp_int *lambda_n2 = monty_mul(wc->mc, lambda_n, lambda_n); mp_int *lambda_d2 = monty_mul(wc->mc, lambda_d, lambda_d); mp_int *lambda_d3 = monty_mul(wc->mc, lambda_d, lambda_d2); /* Make the output x-coordinate */ mp_int *xsum = monty_add(wc->mc, Px, Qx); mp_int *lambda_d2_xsum = monty_mul(wc->mc, lambda_d2, xsum); out->X = monty_sub(wc->mc, lambda_n2, lambda_d2_xsum); /* Make the output y-coordinate */ mp_int *lambda_d2_Px = monty_mul(wc->mc, lambda_d2, Px); mp_int *xdiff = monty_sub(wc->mc, lambda_d2_Px, out->X); mp_int *lambda_n_xdiff = monty_mul(wc->mc, lambda_n, xdiff); mp_int *lambda_d3_Py = monty_mul(wc->mc, lambda_d3, Py); out->Y = monty_sub(wc->mc, lambda_n_xdiff, lambda_d3_Py); /* Make the output z-coordinate */ out->Z = monty_mul(wc->mc, common_Z, lambda_d); mp_free(lambda_n2); mp_free(lambda_d2); mp_free(lambda_d3); mp_free(xsum); mp_free(xdiff); mp_free(lambda_d2_xsum); mp_free(lambda_n_xdiff); mp_free(lambda_d2_Px); mp_free(lambda_d3_Py); } /* * Shared code between add and add_general: put the two input points * over a common denominator, and determine the slope lambda of the * line through both of them. If the points have the same * x-coordinate, then the slope will be returned with a zero * denominator. */ static inline void ecc_weierstrass_add_prologue( WeierstrassPoint *P, WeierstrassPoint *Q, mp_int **Px, mp_int **Py, mp_int **Qx, mp_int **denom, mp_int **lambda_n, mp_int **lambda_d) { WeierstrassCurve *wc = P->wc; /* Powers of the points' denominators */ mp_int *Pz2 = monty_mul(wc->mc, P->Z, P->Z); mp_int *Pz3 = monty_mul(wc->mc, Pz2, P->Z); mp_int *Qz2 = monty_mul(wc->mc, Q->Z, Q->Z); mp_int *Qz3 = monty_mul(wc->mc, Qz2, Q->Z); /* Points' x,y coordinates scaled by the other one's denominator * (raised to the appropriate power) */ *Px = monty_mul(wc->mc, P->X, Qz2); *Py = monty_mul(wc->mc, P->Y, Qz3); *Qx = monty_mul(wc->mc, Q->X, Pz2); mp_int *Qy = monty_mul(wc->mc, Q->Y, Pz3); /* Common denominator */ *denom = monty_mul(wc->mc, P->Z, Q->Z); /* Slope of the line through the two points, if P != Q */ *lambda_n = monty_sub(wc->mc, Qy, *Py); *lambda_d = monty_sub(wc->mc, *Qx, *Px); mp_free(Pz2); mp_free(Pz3); mp_free(Qz2); mp_free(Qz3); mp_free(Qy); } WeierstrassPoint *ecc_weierstrass_add(WeierstrassPoint *P, WeierstrassPoint *Q) { WeierstrassCurve *wc = P->wc; assert(Q->wc == wc); WeierstrassPoint *S = ecc_weierstrass_point_new_empty(wc); mp_int *Px, *Py, *Qx, *denom, *lambda_n, *lambda_d; ecc_weierstrass_add_prologue( P, Q, &Px, &Py, &Qx, &denom, &lambda_n, &lambda_d); /* Never expect to have received two mutually inverse inputs, or * two identical ones (which would make this a doubling). In other * words, the two input x-coordinates (after putting over a common * denominator) should never have been equal. */ assert(!mp_eq_integer(lambda_n, 0)); /* Now go to the common epilogue code. */ ecc_weierstrass_epilogue(Px, Qx, Py, denom, lambda_n, lambda_d, S); mp_free(Px); mp_free(Py); mp_free(Qx); mp_free(denom); mp_free(lambda_n); mp_free(lambda_d); return S; } /* * Code to determine the slope of the line you need to intersect with * the curve in the case where you're adding a point to itself. In * this situation you can't just say "the line through both input * points" because that's under-determined; instead, you have to take * the _tangent_ to the curve at the given point, by differentiating * the curve equation y^2=x^3+ax+b to get 2y dy/dx = 3x^2+a. */ static inline void ecc_weierstrass_tangent_slope( WeierstrassPoint *P, mp_int **lambda_n, mp_int **lambda_d) { WeierstrassCurve *wc = P->wc; mp_int *X2 = monty_mul(wc->mc, P->X, P->X); mp_int *twoX2 = monty_add(wc->mc, X2, X2); mp_int *threeX2 = monty_add(wc->mc, twoX2, X2); mp_int *Z2 = monty_mul(wc->mc, P->Z, P->Z); mp_int *Z4 = monty_mul(wc->mc, Z2, Z2); mp_int *aZ4 = monty_mul(wc->mc, wc->a, Z4); *lambda_n = monty_add(wc->mc, threeX2, aZ4); *lambda_d = monty_add(wc->mc, P->Y, P->Y); mp_free(X2); mp_free(twoX2); mp_free(threeX2); mp_free(Z2); mp_free(Z4); mp_free(aZ4); } WeierstrassPoint *ecc_weierstrass_double(WeierstrassPoint *P) { WeierstrassCurve *wc = P->wc; WeierstrassPoint *D = ecc_weierstrass_point_new_empty(wc); mp_int *lambda_n, *lambda_d; ecc_weierstrass_tangent_slope(P, &lambda_n, &lambda_d); ecc_weierstrass_epilogue(P->X, P->X, P->Y, P->Z, lambda_n, lambda_d, D); mp_free(lambda_n); mp_free(lambda_d); return D; } static inline void ecc_weierstrass_select_into( WeierstrassPoint *dest, WeierstrassPoint *P, WeierstrassPoint *Q, unsigned choose_Q) { mp_select_into(dest->X, P->X, Q->X, choose_Q); mp_select_into(dest->Y, P->Y, Q->Y, choose_Q); mp_select_into(dest->Z, P->Z, Q->Z, choose_Q); } WeierstrassPoint *ecc_weierstrass_add_general( WeierstrassPoint *P, WeierstrassPoint *Q) { WeierstrassCurve *wc = P->wc; assert(Q->wc == wc); WeierstrassPoint *S = ecc_weierstrass_point_new_empty(wc); /* Parameters for the epilogue, and slope of the line if P != Q */ mp_int *Px, *Py, *Qx, *denom, *lambda_n, *lambda_d; ecc_weierstrass_add_prologue( P, Q, &Px, &Py, &Qx, &denom, &lambda_n, &lambda_d); /* Slope if P == Q */ mp_int *lambda_n_tangent, *lambda_d_tangent; ecc_weierstrass_tangent_slope(P, &lambda_n_tangent, &lambda_d_tangent); /* Select between those slopes depending on whether P == Q */ unsigned same_x_coord = mp_eq_integer(lambda_d, 0); unsigned same_y_coord = mp_eq_integer(lambda_n, 0); unsigned equality = same_x_coord & same_y_coord; mp_select_into(lambda_n, lambda_n, lambda_n_tangent, equality); mp_select_into(lambda_d, lambda_d, lambda_d_tangent, equality); /* Now go to the common code between addition and doubling */ ecc_weierstrass_epilogue(Px, Qx, Py, denom, lambda_n, lambda_d, S); /* Check for the input identity cases, and overwrite the output if * necessary. */ ecc_weierstrass_select_into(S, S, Q, mp_eq_integer(P->Z, 0)); ecc_weierstrass_select_into(S, S, P, mp_eq_integer(Q->Z, 0)); /* * In the case where P == -Q and so the output is the identity, * we'll have calculated lambda_d = 0 and so the output will have * z==0 already. Detect that and use it to normalise the other two * coordinates to zero. */ unsigned output_id = mp_eq_integer(S->Z, 0); mp_cond_clear(S->X, output_id); mp_cond_clear(S->Y, output_id); mp_free(Px); mp_free(Py); mp_free(Qx); mp_free(denom); mp_free(lambda_n); mp_free(lambda_d); mp_free(lambda_n_tangent); mp_free(lambda_d_tangent); return S; } WeierstrassPoint *ecc_weierstrass_multiply(WeierstrassPoint *B, mp_int *n) { WeierstrassPoint *two_B = ecc_weierstrass_double(B); WeierstrassPoint *k_B = ecc_weierstrass_point_copy(B); WeierstrassPoint *kplus1_B = ecc_weierstrass_point_copy(two_B); /* * This multiply routine more or less follows the shape of the * 'Montgomery ladder' technique that you have to use under the * extra constraint on addition in Montgomery curves, because it * was fresh in my mind and easier to just do it the same way. See * the comment in ecc_montgomery_multiply. */ unsigned not_started_yet = 1; for (size_t bitindex = mp_max_bits(n); bitindex-- > 0 ;) { unsigned nbit = mp_get_bit(n, bitindex); WeierstrassPoint *sum = ecc_weierstrass_add(k_B, kplus1_B); ecc_weierstrass_cond_swap(k_B, kplus1_B, nbit); WeierstrassPoint *other = ecc_weierstrass_double(k_B); ecc_weierstrass_point_free(k_B); ecc_weierstrass_point_free(kplus1_B); k_B = other; kplus1_B = sum; ecc_weierstrass_cond_swap(k_B, kplus1_B, nbit); ecc_weierstrass_cond_overwrite(k_B, B, not_started_yet); ecc_weierstrass_cond_overwrite(kplus1_B, two_B, not_started_yet); not_started_yet &= ~nbit; } ecc_weierstrass_point_free(two_B); ecc_weierstrass_point_free(kplus1_B); return k_B; } unsigned ecc_weierstrass_is_identity(WeierstrassPoint *wp) { return mp_eq_integer(wp->Z, 0); } /* * Normalise a point by scaling its Jacobian coordinates so that Z=1. * This doesn't change what point is represented by the triple, but it * means the affine x,y can now be easily recovered from X and Y. */ static void ecc_weierstrass_normalise(WeierstrassPoint *wp) { WeierstrassCurve *wc = wp->wc; mp_int *zinv = monty_invert(wc->mc, wp->Z); mp_int *zinv2 = monty_mul(wc->mc, zinv, zinv); mp_int *zinv3 = monty_mul(wc->mc, zinv2, zinv); monty_mul_into(wc->mc, wp->X, wp->X, zinv2); monty_mul_into(wc->mc, wp->Y, wp->Y, zinv3); monty_mul_into(wc->mc, wp->Z, wp->Z, zinv); mp_free(zinv); mp_free(zinv2); mp_free(zinv3); } void ecc_weierstrass_get_affine( WeierstrassPoint *wp, mp_int **x, mp_int **y) { WeierstrassCurve *wc = wp->wc; ecc_weierstrass_normalise(wp); if (x) *x = monty_export(wc->mc, wp->X); if (y) *y = monty_export(wc->mc, wp->Y); } unsigned ecc_weierstrass_point_valid(WeierstrassPoint *P) { WeierstrassCurve *wc = P->wc; /* * The projective version of the curve equation is * Y^2 = X^3 + a X Z^4 + b Z^6 */ mp_int *lhs = monty_mul(P->wc->mc, P->Y, P->Y); mp_int *x2 = monty_mul(wc->mc, P->X, P->X); mp_int *x3 = monty_mul(wc->mc, x2, P->X); mp_int *z2 = monty_mul(wc->mc, P->Z, P->Z); mp_int *z4 = monty_mul(wc->mc, z2, z2); mp_int *az4 = monty_mul(wc->mc, wc->a, z4); mp_int *axz4 = monty_mul(wc->mc, az4, P->X); mp_int *x3_plus_axz4 = monty_add(wc->mc, x3, axz4); mp_int *z6 = monty_mul(wc->mc, z2, z4); mp_int *bz6 = monty_mul(wc->mc, wc->b, z6); mp_int *rhs = monty_add(wc->mc, x3_plus_axz4, bz6); unsigned valid = mp_cmp_eq(lhs, rhs); mp_free(lhs); mp_free(x2); mp_free(x3); mp_free(z2); mp_free(z4); mp_free(az4); mp_free(axz4); mp_free(x3_plus_axz4); mp_free(z6); mp_free(bz6); mp_free(rhs); return valid; } /* ---------------------------------------------------------------------- * Montgomery curves. */ struct MontgomeryPoint { /* XZ coordinates. These represent the affine x coordinate by the * relationship x = X/Z. */ mp_int *X, *Z; MontgomeryCurve *mc; }; struct MontgomeryCurve { /* Prime modulus of the finite field. */ mp_int *p; /* Montgomery context for arithmetic mod p. */ MontyContext *mc; /* Parameters of the curve, in Montgomery-multiplication * transformed form. */ mp_int *a, *b; /* (a+2)/4, also in Montgomery-multiplication form. */ mp_int *aplus2over4; }; MontgomeryCurve *ecc_montgomery_curve( mp_int *p, mp_int *a, mp_int *b) { MontgomeryCurve *mc = snew(MontgomeryCurve); mc->p = mp_copy(p); mc->mc = monty_new(p); mc->a = monty_import(mc->mc, a); mc->b = monty_import(mc->mc, b); mp_int *four = mp_from_integer(4); mp_int *fourinverse = mp_invert(four, mc->p); mp_int *aplus2 = mp_copy(a); mp_add_integer_into(aplus2, aplus2, 2); mp_int *aplus2over4 = mp_modmul(aplus2, fourinverse, mc->p); mc->aplus2over4 = monty_import(mc->mc, aplus2over4); mp_free(four); mp_free(fourinverse); mp_free(aplus2); mp_free(aplus2over4); return mc; } void ecc_montgomery_curve_free(MontgomeryCurve *mc) { mp_free(mc->p); mp_free(mc->a); mp_free(mc->b); mp_free(mc->aplus2over4); monty_free(mc->mc); sfree(mc); } static MontgomeryPoint *ecc_montgomery_point_new_empty(MontgomeryCurve *mc) { MontgomeryPoint *mp = snew(MontgomeryPoint); mp->mc = mc; mp->X = mp->Z = NULL; return mp; } MontgomeryPoint *ecc_montgomery_point_new(MontgomeryCurve *mc, mp_int *x) { MontgomeryPoint *mp = ecc_montgomery_point_new_empty(mc); mp->X = monty_import(mc->mc, x); mp->Z = mp_copy(monty_identity(mc->mc)); return mp; } void ecc_montgomery_point_copy_into( MontgomeryPoint *dest, MontgomeryPoint *src) { mp_copy_into(dest->X, src->X); mp_copy_into(dest->Z, src->Z); } MontgomeryPoint *ecc_montgomery_point_copy(MontgomeryPoint *orig) { MontgomeryPoint *mp = ecc_montgomery_point_new_empty(orig->mc); mp->X = mp_copy(orig->X); mp->Z = mp_copy(orig->Z); return mp; } void ecc_montgomery_point_free(MontgomeryPoint *mp) { mp_free(mp->X); mp_free(mp->Z); smemclr(mp, sizeof(*mp)); sfree(mp); } static void ecc_montgomery_cond_overwrite( MontgomeryPoint *dest, MontgomeryPoint *src, unsigned overwrite) { mp_select_into(dest->X, dest->X, src->X, overwrite); mp_select_into(dest->Z, dest->Z, src->Z, overwrite); } static void ecc_montgomery_cond_swap( MontgomeryPoint *P, MontgomeryPoint *Q, unsigned swap) { mp_cond_swap(P->X, Q->X, swap); mp_cond_swap(P->Z, Q->Z, swap); } MontgomeryPoint *ecc_montgomery_diff_add( MontgomeryPoint *P, MontgomeryPoint *Q, MontgomeryPoint *PminusQ) { MontgomeryCurve *mc = P->mc; assert(Q->mc == mc); assert(PminusQ->mc == mc); /* * Differential addition is achieved using the following formula * that relates the affine x-coordinates of P, Q, P+Q and P-Q: * * x(P+Q) x(P-Q) (x(Q)-x(P))^2 = (x(P)x(Q) - 1)^2 * * As with the Weierstrass coordinates, the code below transforms * that affine relation into a projective one to avoid having to * do a division during the main arithmetic. */ MontgomeryPoint *S = ecc_montgomery_point_new_empty(mc); mp_int *Px_m_Pz = monty_sub(mc->mc, P->X, P->Z); mp_int *Px_p_Pz = monty_add(mc->mc, P->X, P->Z); mp_int *Qx_m_Qz = monty_sub(mc->mc, Q->X, Q->Z); mp_int *Qx_p_Qz = monty_add(mc->mc, Q->X, Q->Z); mp_int *PmQp = monty_mul(mc->mc, Px_m_Pz, Qx_p_Qz); mp_int *PpQm = monty_mul(mc->mc, Px_p_Pz, Qx_m_Qz); mp_int *Xpre = monty_add(mc->mc, PmQp, PpQm); mp_int *Zpre = monty_sub(mc->mc, PmQp, PpQm); mp_int *Xpre2 = monty_mul(mc->mc, Xpre, Xpre); mp_int *Zpre2 = monty_mul(mc->mc, Zpre, Zpre); S->X = monty_mul(mc->mc, Xpre2, PminusQ->Z); S->Z = monty_mul(mc->mc, Zpre2, PminusQ->X); mp_free(Px_m_Pz); mp_free(Px_p_Pz); mp_free(Qx_m_Qz); mp_free(Qx_p_Qz); mp_free(PmQp); mp_free(PpQm); mp_free(Xpre); mp_free(Zpre); mp_free(Xpre2); mp_free(Zpre2); return S; } MontgomeryPoint *ecc_montgomery_double(MontgomeryPoint *P) { MontgomeryCurve *mc = P->mc; MontgomeryPoint *D = ecc_montgomery_point_new_empty(mc); /* * To double a point in affine coordinates, in principle you can * use the same technique as for Weierstrass: differentiate the * curve equation to get the tangent line at the input point, use * that to get an expression for y which you substitute back into * the curve equation, and subtract the known two roots (in this * case both the same) from the x^2 coefficient of the resulting * cubic. * * In this case, we don't have an input y-coordinate, so you have * to do a bit of extra transformation to find a formula that can * work without it. The tangent formula is (3x^2 + 2ax + 1)/(2y), * and when that appears in the final formula it will be squared - * so we can substitute the y^2 in the denominator for the RHS of * the curve equation. Put together, that gives * * x_out = (x+1)^2 (x-1)^2 / 4(x^3+ax^2+x) * * and, as usual, the code below transforms that into projective * form to avoid the division. */ mp_int *Px_m_Pz = monty_sub(mc->mc, P->X, P->Z); mp_int *Px_p_Pz = monty_add(mc->mc, P->X, P->Z); mp_int *Px_m_Pz_2 = monty_mul(mc->mc, Px_m_Pz, Px_m_Pz); mp_int *Px_p_Pz_2 = monty_mul(mc->mc, Px_p_Pz, Px_p_Pz); D->X = monty_mul(mc->mc, Px_m_Pz_2, Px_p_Pz_2); mp_int *XZ = monty_mul(mc->mc, P->X, P->Z); mp_int *twoXZ = monty_add(mc->mc, XZ, XZ); mp_int *fourXZ = monty_add(mc->mc, twoXZ, twoXZ); mp_int *fourXZ_scaled = monty_mul(mc->mc, fourXZ, mc->aplus2over4); mp_int *Zpre = monty_add(mc->mc, Px_m_Pz_2, fourXZ_scaled); D->Z = monty_mul(mc->mc, fourXZ, Zpre); mp_free(Px_m_Pz); mp_free(Px_p_Pz); mp_free(Px_m_Pz_2); mp_free(Px_p_Pz_2); mp_free(XZ); mp_free(twoXZ); mp_free(fourXZ); mp_free(fourXZ_scaled); mp_free(Zpre); return D; } static void ecc_montgomery_normalise(MontgomeryPoint *mp) { MontgomeryCurve *mc = mp->mc; mp_int *zinv = monty_invert(mc->mc, mp->Z); monty_mul_into(mc->mc, mp->X, mp->X, zinv); monty_mul_into(mc->mc, mp->Z, mp->Z, zinv); mp_free(zinv); } MontgomeryPoint *ecc_montgomery_multiply(MontgomeryPoint *B, mp_int *n) { /* * 'Montgomery ladder' technique, to compute an arbitrary integer * multiple of B under the constraint that you can only add two * unequal points if you also know their difference. * * The setup is that you maintain two curve points one of which is * always the other one plus B. Call them kB and (k+1)B, where k * is some integer that evolves as we go along. We begin by * doubling the input B, to initialise those points to B and 2B, * so that k=1. * * At each stage, we add kB and (k+1)B together - which we can do * under the differential-addition constraint because we know * their difference is always just B - to give us (2k+1)B. Then we * double one of kB or (k+1)B, and depending on which one we * choose, we end up with (2k)B or (2k+2)B. Either way, that * differs by B from the other value we've just computed. So in * each iteration, we do one diff-add and one doubling, plus a * couple of conditional swaps to choose which value we double and * which way round we put the output points, and the effect is to * replace k with either 2k or 2k+1, which we choose based on the * appropriate bit of the desired exponent. * * This routine doesn't assume we know the exact location of the * topmost set bit of the exponent. So to maintain constant time * it does an iteration for every _potential_ bit, starting from * the top downwards; after each iteration in which we haven't * seen a set exponent bit yet, we just overwrite the two points * with B and 2B again, */ MontgomeryPoint *two_B = ecc_montgomery_double(B); MontgomeryPoint *k_B = ecc_montgomery_point_copy(B); MontgomeryPoint *kplus1_B = ecc_montgomery_point_copy(two_B); unsigned not_started_yet = 1; for (size_t bitindex = mp_max_bits(n); bitindex-- > 0 ;) { unsigned nbit = mp_get_bit(n, bitindex); MontgomeryPoint *sum = ecc_montgomery_diff_add(k_B, kplus1_B, B); ecc_montgomery_cond_swap(k_B, kplus1_B, nbit); MontgomeryPoint *other = ecc_montgomery_double(k_B); ecc_montgomery_point_free(k_B); ecc_montgomery_point_free(kplus1_B); k_B = other; kplus1_B = sum; ecc_montgomery_cond_swap(k_B, kplus1_B, nbit); ecc_montgomery_cond_overwrite(k_B, B, not_started_yet); ecc_montgomery_cond_overwrite(kplus1_B, two_B, not_started_yet); not_started_yet &= ~nbit; } ecc_montgomery_point_free(two_B); ecc_montgomery_point_free(kplus1_B); return k_B; } void ecc_montgomery_get_affine(MontgomeryPoint *mp, mp_int **x) { MontgomeryCurve *mc = mp->mc; ecc_montgomery_normalise(mp); if (x) *x = monty_export(mc->mc, mp->X); } unsigned ecc_montgomery_is_identity(MontgomeryPoint *mp) { return mp_eq_integer(mp->Z, 0); } /* ---------------------------------------------------------------------- * Twisted Edwards curves. */ struct EdwardsPoint { /* * We represent an Edwards curve point in 'extended coordinates'. * There's more than one coordinate system going by that name, * unfortunately. These ones have the semantics that X,Y,Z are * ordinary projective coordinates (so x=X/Z and y=Y/Z), but also, * we store the extra value T = xyZ = XY/Z. */ mp_int *X, *Y, *Z, *T; EdwardsCurve *ec; }; struct EdwardsCurve { /* Prime modulus of the finite field. */ mp_int *p; /* Montgomery context for arithmetic mod p. */ MontyContext *mc; /* Modsqrt context for point decompression. */ ModsqrtContext *sc; /* Parameters of the curve, in Montgomery-multiplication * transformed form. */ mp_int *d, *a; }; EdwardsCurve *ecc_edwards_curve(mp_int *p, mp_int *d, mp_int *a, mp_int *nonsquare_mod_p) { EdwardsCurve *ec = snew(EdwardsCurve); ec->p = mp_copy(p); ec->mc = monty_new(p); ec->d = monty_import(ec->mc, d); ec->a = monty_import(ec->mc, a); if (nonsquare_mod_p) ec->sc = modsqrt_new(p, nonsquare_mod_p); else ec->sc = NULL; return ec; } void ecc_edwards_curve_free(EdwardsCurve *ec) { mp_free(ec->p); mp_free(ec->d); mp_free(ec->a); monty_free(ec->mc); if (ec->sc) modsqrt_free(ec->sc); sfree(ec); } static EdwardsPoint *ecc_edwards_point_new_empty(EdwardsCurve *ec) { EdwardsPoint *ep = snew(EdwardsPoint); ep->ec = ec; ep->X = ep->Y = ep->Z = ep->T = NULL; return ep; } static EdwardsPoint *ecc_edwards_point_new_imported( EdwardsCurve *ec, mp_int *monty_x, mp_int *monty_y) { EdwardsPoint *ep = ecc_edwards_point_new_empty(ec); ep->X = monty_x; ep->Y = monty_y; ep->T = monty_mul(ec->mc, ep->X, ep->Y); ep->Z = mp_copy(monty_identity(ec->mc)); return ep; } EdwardsPoint *ecc_edwards_point_new( EdwardsCurve *ec, mp_int *x, mp_int *y) { return ecc_edwards_point_new_imported( ec, monty_import(ec->mc, x), monty_import(ec->mc, y)); } void ecc_edwards_point_copy_into(EdwardsPoint *dest, EdwardsPoint *src) { mp_copy_into(dest->X, src->X); mp_copy_into(dest->Y, src->Y); mp_copy_into(dest->Z, src->Z); mp_copy_into(dest->T, src->T); } EdwardsPoint *ecc_edwards_point_copy(EdwardsPoint *orig) { EdwardsPoint *ep = ecc_edwards_point_new_empty(orig->ec); ep->X = mp_copy(orig->X); ep->Y = mp_copy(orig->Y); ep->Z = mp_copy(orig->Z); ep->T = mp_copy(orig->T); return ep; } void ecc_edwards_point_free(EdwardsPoint *ep) { mp_free(ep->X); mp_free(ep->Y); mp_free(ep->Z); mp_free(ep->T); smemclr(ep, sizeof(*ep)); sfree(ep); } EdwardsPoint *ecc_edwards_point_new_from_y( EdwardsCurve *ec, mp_int *yorig, unsigned desired_x_parity) { assert(ec->sc); /* * The curve equation is ax^2 + y^2 = 1 + dx^2y^2, which * rearranges to x^2(dy^2-a) = y^2-1. So we compute * (y^2-1)/(dy^2-a) and take its square root. */ unsigned success; mp_int *y = monty_import(ec->mc, yorig); mp_int *y2 = monty_mul(ec->mc, y, y); mp_int *dy2 = monty_mul(ec->mc, ec->d, y2); mp_int *dy2ma = monty_sub(ec->mc, dy2, ec->a); mp_int *y2m1 = monty_sub(ec->mc, y2, monty_identity(ec->mc)); mp_int *recip_denominator = monty_invert(ec->mc, dy2ma); mp_int *radicand = monty_mul(ec->mc, y2m1, recip_denominator); mp_int *x = monty_modsqrt(ec->sc, radicand, &success); mp_free(y2); mp_free(dy2); mp_free(dy2ma); mp_free(y2m1); mp_free(recip_denominator); mp_free(radicand); if (!success) { /* Failure! x^2 worked out to be a number that has no square * root mod p. In this situation there's no point in trying to * be time-constant, since the protocol sequence is going to * diverge anyway when we complain to whoever gave us this * bogus value. */ mp_free(x); mp_free(y); return NULL; } /* * Choose whichever of x and p-x has the specified parity (of its * lowest positive residue mod p). */ mp_int *tmp = monty_export(ec->mc, x); unsigned flip = (mp_get_bit(tmp, 0) ^ desired_x_parity) & 1; mp_sub_into(tmp, ec->p, x); mp_select_into(x, x, tmp, flip); mp_free(tmp); return ecc_edwards_point_new_imported(ec, x, y); } static void ecc_edwards_cond_overwrite( EdwardsPoint *dest, EdwardsPoint *src, unsigned overwrite) { mp_select_into(dest->X, dest->X, src->X, overwrite); mp_select_into(dest->Y, dest->Y, src->Y, overwrite); mp_select_into(dest->Z, dest->Z, src->Z, overwrite); mp_select_into(dest->T, dest->T, src->T, overwrite); } static void ecc_edwards_cond_swap( EdwardsPoint *P, EdwardsPoint *Q, unsigned swap) { mp_cond_swap(P->X, Q->X, swap); mp_cond_swap(P->Y, Q->Y, swap); mp_cond_swap(P->Z, Q->Z, swap); mp_cond_swap(P->T, Q->T, swap); } EdwardsPoint *ecc_edwards_add(EdwardsPoint *P, EdwardsPoint *Q) { EdwardsCurve *ec = P->ec; assert(Q->ec == ec); EdwardsPoint *S = ecc_edwards_point_new_empty(ec); /* * The affine rule for Edwards addition of (x1,y1) and (x2,y2) is * * x_out = (x1 y2 + y1 x2) / (1 + d x1 x2 y1 y2) * y_out = (y1 y2 - a x1 x2) / (1 - d x1 x2 y1 y2) * * The formulae below are listed as 'add-2008-hwcd' in * https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html * * and if you undo the careful optimisation to find out what * they're actually computing, it comes out to * * X_out = (X1 Y2 + Y1 X2) (Z1 Z2 - d T1 T2) * Y_out = (Y1 Y2 - a X1 X2) (Z1 Z2 + d T1 T2) * Z_out = (Z1 Z2 - d T1 T2) (Z1 Z2 + d T1 T2) * T_out = (X1 Y2 + Y1 X2) (Y1 Y2 - a X1 X2) */ mp_int *PxQx = monty_mul(ec->mc, P->X, Q->X); mp_int *PyQy = monty_mul(ec->mc, P->Y, Q->Y); mp_int *PtQt = monty_mul(ec->mc, P->T, Q->T); mp_int *PzQz = monty_mul(ec->mc, P->Z, Q->Z); mp_int *Psum = monty_add(ec->mc, P->X, P->Y); mp_int *Qsum = monty_add(ec->mc, Q->X, Q->Y); mp_int *aPxQx = monty_mul(ec->mc, ec->a, PxQx); mp_int *dPtQt = monty_mul(ec->mc, ec->d, PtQt); mp_int *sumprod = monty_mul(ec->mc, Psum, Qsum); mp_int *xx_p_yy = monty_add(ec->mc, PxQx, PyQy); mp_int *E = monty_sub(ec->mc, sumprod, xx_p_yy); mp_int *F = monty_sub(ec->mc, PzQz, dPtQt); mp_int *G = monty_add(ec->mc, PzQz, dPtQt); mp_int *H = monty_sub(ec->mc, PyQy, aPxQx); S->X = monty_mul(ec->mc, E, F); S->Z = monty_mul(ec->mc, F, G); S->Y = monty_mul(ec->mc, G, H); S->T = monty_mul(ec->mc, H, E); mp_free(PxQx); mp_free(PyQy); mp_free(PtQt); mp_free(PzQz); mp_free(Psum); mp_free(Qsum); mp_free(aPxQx); mp_free(dPtQt); mp_free(sumprod); mp_free(xx_p_yy); mp_free(E); mp_free(F); mp_free(G); mp_free(H); return S; } static void ecc_edwards_normalise(EdwardsPoint *ep) { EdwardsCurve *ec = ep->ec; mp_int *zinv = monty_invert(ec->mc, ep->Z); monty_mul_into(ec->mc, ep->X, ep->X, zinv); monty_mul_into(ec->mc, ep->Y, ep->Y, zinv); monty_mul_into(ec->mc, ep->Z, ep->Z, zinv); mp_free(zinv); monty_mul_into(ec->mc, ep->T, ep->X, ep->Y); } EdwardsPoint *ecc_edwards_multiply(EdwardsPoint *B, mp_int *n) { EdwardsPoint *two_B = ecc_edwards_add(B, B); EdwardsPoint *k_B = ecc_edwards_point_copy(B); EdwardsPoint *kplus1_B = ecc_edwards_point_copy(two_B); /* * Another copy of the same exponentiation routine following the * pattern of the Montgomery ladder, because it works as well as * any other technique and this way I didn't have to debug two of * them. */ unsigned not_started_yet = 1; for (size_t bitindex = mp_max_bits(n); bitindex-- > 0 ;) { unsigned nbit = mp_get_bit(n, bitindex); EdwardsPoint *sum = ecc_edwards_add(k_B, kplus1_B); ecc_edwards_cond_swap(k_B, kplus1_B, nbit); EdwardsPoint *other = ecc_edwards_add(k_B, k_B); ecc_edwards_point_free(k_B); ecc_edwards_point_free(kplus1_B); k_B = other; kplus1_B = sum; ecc_edwards_cond_swap(k_B, kplus1_B, nbit); ecc_edwards_cond_overwrite(k_B, B, not_started_yet); ecc_edwards_cond_overwrite(kplus1_B, two_B, not_started_yet); not_started_yet &= ~nbit; } ecc_edwards_point_free(two_B); ecc_edwards_point_free(kplus1_B); return k_B; } /* * Helper routine to determine whether two values each given as a pair * of projective coordinates represent the same affine value. */ static inline unsigned projective_eq( MontyContext *mc, mp_int *An, mp_int *Ad, mp_int *Bn, mp_int *Bd) { mp_int *AnBd = monty_mul(mc, An, Bd); mp_int *BnAd = monty_mul(mc, Bn, Ad); unsigned toret = mp_cmp_eq(AnBd, BnAd); mp_free(AnBd); mp_free(BnAd); return toret; } unsigned ecc_edwards_eq(EdwardsPoint *P, EdwardsPoint *Q) { EdwardsCurve *ec = P->ec; assert(Q->ec == ec); return (projective_eq(ec->mc, P->X, P->Z, Q->X, Q->Z) & projective_eq(ec->mc, P->Y, P->Z, Q->Y, Q->Z)); } void ecc_edwards_get_affine(EdwardsPoint *ep, mp_int **x, mp_int **y) { EdwardsCurve *ec = ep->ec; ecc_edwards_normalise(ep); if (x) *x = monty_export(ec->mc, ep->X); if (y) *y = monty_export(ec->mc, ep->Y); } putty-0.76/ecc.h0000644000175000017500000002307714072266310010471 00000000000000#ifndef PUTTY_ECC_H #define PUTTY_ECC_H /* * Arithmetic functions for the various kinds of elliptic curves used * by PuTTY's public-key cryptography. * * All of these elliptic curves are over the finite field whose order * is a large prime p. (Elliptic curves over a field of order 2^n are * also known, but PuTTY currently has no need of them.) */ /* ---------------------------------------------------------------------- * Weierstrass curves (or rather, 'short form' Weierstrass curves). * * A curve in this form is defined by two parameters a,b, and the * non-identity points on the curve are represented by (x,y) (the * 'affine coordinates') such that y^2 = x^3 + ax + b. * * The identity element of the curve's group is an additional 'point * at infinity', which is considered to be the third point on the * intersection of the curve with any vertical line. Hence, the * inverse of the point (x,y) is (x,-y). */ /* * Create and destroy Weierstrass curve data structures. The mandatory * parameters to the constructor are the prime modulus p, and the * curve parameters a,b. * * 'nonsquare_mod_p' is an optional extra parameter, only needed by * ecc_edwards_point_new_from_y which has to take a modular square * root. You can pass it as NULL if you don't need that function. */ WeierstrassCurve *ecc_weierstrass_curve( mp_int *p, mp_int *a, mp_int *b, mp_int *nonsquare_mod_p); void ecc_weierstrass_curve_free(WeierstrassCurve *); /* * Create points on a Weierstrass curve, given the curve. * * point_new_identity returns the special identity point. * point_new(x,y) returns the non-identity point with the given affine * coordinates. * * point_new_from_x constructs a non-identity point given only the * x-coordinate, by using the curve equation to work out what y has to * be. Of course the equation only tells you y^2, so it only * determines y up to sign; the parameter desired_y_parity controls * which of the two values of y you get, by saying whether you'd like * its minimal non-negative residue mod p to be even or odd. (Of * course, since p itself is odd, exactly one of y and p-y is odd.) * This function has to take a modular square root, so it will only * work if you passed in a non-square mod p when constructing the * curve. */ WeierstrassPoint *ecc_weierstrass_point_new_identity(WeierstrassCurve *curve); WeierstrassPoint *ecc_weierstrass_point_new( WeierstrassCurve *curve, mp_int *x, mp_int *y); WeierstrassPoint *ecc_weierstrass_point_new_from_x( WeierstrassCurve *curve, mp_int *x, unsigned desired_y_parity); /* Memory management: copy and free points. */ void ecc_weierstrass_point_copy_into( WeierstrassPoint *dest, WeierstrassPoint *src); WeierstrassPoint *ecc_weierstrass_point_copy(WeierstrassPoint *wc); void ecc_weierstrass_point_free(WeierstrassPoint *point); /* Check whether a point is actually on the curve. */ unsigned ecc_weierstrass_point_valid(WeierstrassPoint *); /* * Add two points and return their sum. This function is fully * general: it should do the right thing if the two inputs are the * same, or if either (or both) of the input points is the identity, * or if the two input points are inverses so the output is the * identity. However, it pays for that generality by being slower than * the special-purpose functions below.. */ WeierstrassPoint *ecc_weierstrass_add_general( WeierstrassPoint *, WeierstrassPoint *); /* * Fast but less general arithmetic functions: add two points on the * condition that they are not equal and neither is the identity, and * add a point to itself. */ WeierstrassPoint *ecc_weierstrass_add(WeierstrassPoint *, WeierstrassPoint *); WeierstrassPoint *ecc_weierstrass_double(WeierstrassPoint *); /* * Compute an integer multiple of a point. Not guaranteed to work * unless the integer argument is less than the order of the point in * the group (because it won't cope if an identity element shows up in * any intermediate product). */ WeierstrassPoint *ecc_weierstrass_multiply(WeierstrassPoint *, mp_int *); /* * Query functions to get the value of a point back out. is_identity * tells you whether the point is the identity; if it isn't, then * get_affine will retrieve one or both of its affine coordinates. * (You can pass NULL as either output pointer, if you don't need that * coordinate as output.) */ unsigned ecc_weierstrass_is_identity(WeierstrassPoint *wp); void ecc_weierstrass_get_affine(WeierstrassPoint *wp, mp_int **x, mp_int **y); /* ---------------------------------------------------------------------- * Montgomery curves. * * A curve in this form is defined by two parameters a,b, and the * curve equation is by^2 = x^3 + ax^2 + x. * * As with Weierstrass curves, there's an additional point at infinity * that is the identity element, and the inverse of (x,y) is (x,-y). * * However, we don't actually work with full (x,y) pairs. We just * store the x-coordinate (so what we're really representing is not a * specific point on the curve but a two-point set {P,-P}). This means * you can't quite do point addition, because if you're given {P,-P} * and {Q,-Q} as input, you can work out a pair of x-coordinates that * are those of P-Q and P+Q, but you don't know which is which. * * Instead, the basic operation is 'differential addition', in which * you are given three parameters P, Q and P-Q and you return P+Q. (As * well as disambiguating which of the possible answers you want, that * extra input also enables a fast formulae for computing it. This * fast formula is more or less why Montgomery curves are useful in * the first place.) * * Doubling a point is still possible to do unambiguously, so you can * still compute an integer multiple of P if you start by making 2P * and then doing a series of differential additions. */ /* * Create and destroy Montgomery curve data structures. */ MontgomeryCurve *ecc_montgomery_curve(mp_int *p, mp_int *a, mp_int *b); void ecc_montgomery_curve_free(MontgomeryCurve *); /* * Create, copy and free points on the curve. We don't need to * explicitly represent the identity for this application. */ MontgomeryPoint *ecc_montgomery_point_new(MontgomeryCurve *mc, mp_int *x); void ecc_montgomery_point_copy_into( MontgomeryPoint *dest, MontgomeryPoint *src); MontgomeryPoint *ecc_montgomery_point_copy(MontgomeryPoint *orig); void ecc_montgomery_point_free(MontgomeryPoint *mp); /* * Basic arithmetic routines: differential addition and point- * doubling. Each of these assumes that no special cases come up - no * input or output point should be the identity, and in diff_add, P * and Q shouldn't be the same. */ MontgomeryPoint *ecc_montgomery_diff_add( MontgomeryPoint *P, MontgomeryPoint *Q, MontgomeryPoint *PminusQ); MontgomeryPoint *ecc_montgomery_double(MontgomeryPoint *P); /* * Compute an integer multiple of a point. */ MontgomeryPoint *ecc_montgomery_multiply(MontgomeryPoint *, mp_int *); /* * Return the affine x-coordinate of a point. */ void ecc_montgomery_get_affine(MontgomeryPoint *mp, mp_int **x); /* * Test whether a point is the curve identity. */ unsigned ecc_montgomery_is_identity(MontgomeryPoint *mp); /* ---------------------------------------------------------------------- * Twisted Edwards curves. * * A curve in this form is defined by two parameters d,a, and the * curve equation is a x^2 + y^2 = 1 + d x^2 y^2. * * Apparently if you ask a proper algebraic geometer they'll tell you * that this is technically not an actual elliptic curve. Certainly it * doesn't work quite the same way as the other kinds: in this form, * there is no need for a point at infinity, because the identity * element is represented by the affine coordinates (0,1). And you * invert a point by negating its x rather than y coordinate: the * inverse of (x,y) is (-x,y). * * The usefulness of this representation is that the addition formula * is 'strongly unified', meaning that the same formula works for any * input and output points, without needing special cases for the * identity or for doubling. */ /* * Create and destroy Edwards curve data structures. * * Similarly to ecc_weierstrass_curve, you don't have to provide * nonsquare_mod_p if you don't need ecc_edwards_point_new_from_y. */ EdwardsCurve *ecc_edwards_curve( mp_int *p, mp_int *d, mp_int *a, mp_int *nonsquare_mod_p); void ecc_edwards_curve_free(EdwardsCurve *); /* * Create points. * * There's no need to have a separate function to create the identity * point, because you can just pass x=0 and y=1 to the usual function. * * Similarly to the Weierstrass curve, ecc_edwards_point_new_from_y * creates a point given only its y-coordinate and the desired parity * of its x-coordinate, and you can only call it if you provided the * optional nonsquare_mod_p argument when creating the curve. */ EdwardsPoint *ecc_edwards_point_new( EdwardsCurve *curve, mp_int *x, mp_int *y); EdwardsPoint *ecc_edwards_point_new_from_y( EdwardsCurve *curve, mp_int *y, unsigned desired_x_parity); /* Copy and free points. */ void ecc_edwards_point_copy_into(EdwardsPoint *dest, EdwardsPoint *src); EdwardsPoint *ecc_edwards_point_copy(EdwardsPoint *ec); void ecc_edwards_point_free(EdwardsPoint *point); /* * Arithmetic: add two points, and calculate an integer multiple of a * point. */ EdwardsPoint *ecc_edwards_add(EdwardsPoint *, EdwardsPoint *); EdwardsPoint *ecc_edwards_multiply(EdwardsPoint *, mp_int *); /* * Query functions: compare two points for equality, and return the * affine coordinates of a point. */ unsigned ecc_edwards_eq(EdwardsPoint *, EdwardsPoint *); void ecc_edwards_get_affine(EdwardsPoint *wp, mp_int **x, mp_int **y); #endif /* PUTTY_ECC_H */ putty-0.76/errsock.c0000644000175000017500000000266714072266310011404 00000000000000/* * A dummy Socket implementation which just holds an error message. */ #include #include #include "tree234.h" #include "putty.h" #include "network.h" typedef struct { char *error; Plug *plug; Socket sock; } ErrorSocket; static Plug *sk_error_plug(Socket *s, Plug *p) { ErrorSocket *es = container_of(s, ErrorSocket, sock); Plug *ret = es->plug; if (p) es->plug = p; return ret; } static void sk_error_close(Socket *s) { ErrorSocket *es = container_of(s, ErrorSocket, sock); sfree(es->error); sfree(es); } static const char *sk_error_socket_error(Socket *s) { ErrorSocket *es = container_of(s, ErrorSocket, sock); return es->error; } static SocketPeerInfo *sk_error_peer_info(Socket *s) { return NULL; } static const SocketVtable ErrorSocket_sockvt = { .plug = sk_error_plug, .close = sk_error_close, .socket_error = sk_error_socket_error, .peer_info = sk_error_peer_info, /* other methods are NULL */ }; Socket *new_error_socket_consume_string(Plug *plug, char *errmsg) { ErrorSocket *es = snew(ErrorSocket); es->sock.vt = &ErrorSocket_sockvt; es->plug = plug; es->error = errmsg; return &es->sock; } Socket *new_error_socket_fmt(Plug *plug, const char *fmt, ...) { va_list ap; char *msg; va_start(ap, fmt); msg = dupvprintf(fmt, ap); va_end(ap); return new_error_socket_consume_string(plug, msg); } putty-0.76/fuzzterm.c0000644000175000017500000001721414072266310011614 00000000000000#include #include #include #include "putty.h" #include "dialog.h" #include "terminal.h" /* For Unix in particular, but harmless if this main() is reused elsewhere */ const bool buildinfo_gtk_relevant = false; static const TermWinVtable fuzz_termwin_vt; int main(int argc, char **argv) { char blk[512]; size_t len; Terminal *term; Conf *conf; struct unicode_data ucsdata; TermWin termwin; termwin.vt = &fuzz_termwin_vt; conf = conf_new(); do_defaults(NULL, conf); init_ucs(&ucsdata, conf_get_str(conf, CONF_line_codepage), conf_get_bool(conf, CONF_utf8_override), CS_NONE, conf_get_int(conf, CONF_vtmode)); term = term_init(conf, &ucsdata, &termwin); term_size(term, 24, 80, 10000); term->ldisc = NULL; /* Tell american fuzzy lop that this is a good place to fork. */ #ifdef __AFL_HAVE_MANUAL_CONTROL __AFL_INIT(); #endif while (!feof(stdin)) { len = fread(blk, 1, sizeof(blk), stdin); term_data(term, false, blk, len); } term_update(term); return 0; } /* functions required by terminal.c */ static bool fuzz_setup_draw_ctx(TermWin *tw) { return true; } static void fuzz_draw_text( TermWin *tw, int x, int y, wchar_t *text, int len, unsigned long attr, int lattr, truecolour tc) { int i; printf("TEXT[attr=%08lx,lattr=%02x]@(%d,%d):", attr, lattr, x, y); for (i = 0; i < len; i++) { printf(" %x", (unsigned)text[i]); } printf("\n"); } static void fuzz_draw_cursor( TermWin *tw, int x, int y, wchar_t *text, int len, unsigned long attr, int lattr, truecolour tc) { int i; printf("CURS[attr=%08lx,lattr=%02x]@(%d,%d):", attr, lattr, x, y); for (i = 0; i < len; i++) { printf(" %x", (unsigned)text[i]); } printf("\n"); } static void fuzz_draw_trust_sigil(TermWin *tw, int x, int y) { printf("TRUST@(%d,%d)\n", x, y); } static int fuzz_char_width(TermWin *tw, int uc) { return 1; } static void fuzz_free_draw_ctx(TermWin *tw) {} static void fuzz_set_cursor_pos(TermWin *tw, int x, int y) {} static void fuzz_set_raw_mouse_mode(TermWin *tw, bool enable) {} static void fuzz_set_scrollbar(TermWin *tw, int total, int start, int page) {} static void fuzz_bell(TermWin *tw, int mode) {} static void fuzz_clip_write( TermWin *tw, int clipboard, wchar_t *text, int *attrs, truecolour *colours, int len, bool must_deselect) {} static void fuzz_clip_request_paste(TermWin *tw, int clipboard) {} static void fuzz_refresh(TermWin *tw) {} static void fuzz_request_resize(TermWin *tw, int w, int h) {} static void fuzz_set_title(TermWin *tw, const char *title) {} static void fuzz_set_icon_title(TermWin *tw, const char *icontitle) {} static void fuzz_set_minimised(TermWin *tw, bool minimised) {} static void fuzz_set_maximised(TermWin *tw, bool maximised) {} static void fuzz_move(TermWin *tw, int x, int y) {} static void fuzz_set_zorder(TermWin *tw, bool top) {} static void fuzz_palette_set(TermWin *tw, unsigned start, unsigned ncolours, const rgb *colours) {} static void fuzz_palette_get_overrides(TermWin *tw, Terminal *term) {} static const TermWinVtable fuzz_termwin_vt = { .setup_draw_ctx = fuzz_setup_draw_ctx, .draw_text = fuzz_draw_text, .draw_cursor = fuzz_draw_cursor, .draw_trust_sigil = fuzz_draw_trust_sigil, .char_width = fuzz_char_width, .free_draw_ctx = fuzz_free_draw_ctx, .set_cursor_pos = fuzz_set_cursor_pos, .set_raw_mouse_mode = fuzz_set_raw_mouse_mode, .set_scrollbar = fuzz_set_scrollbar, .bell = fuzz_bell, .clip_write = fuzz_clip_write, .clip_request_paste = fuzz_clip_request_paste, .refresh = fuzz_refresh, .request_resize = fuzz_request_resize, .set_title = fuzz_set_title, .set_icon_title = fuzz_set_icon_title, .set_minimised = fuzz_set_minimised, .set_maximised = fuzz_set_maximised, .move = fuzz_move, .set_zorder = fuzz_set_zorder, .palette_set = fuzz_palette_set, .palette_get_overrides = fuzz_palette_get_overrides, }; void ldisc_send(Ldisc *ldisc, const void *buf, int len, bool interactive) {} void ldisc_echoedit_update(Ldisc *ldisc) {} void modalfatalbox(const char *fmt, ...) { exit(0); } void nonfatal(const char *fmt, ...) { } /* needed by timing.c */ void timer_change_notify(unsigned long next) { } /* needed by config.c and sercfg.c */ void dlg_radiobutton_set(union control *ctrl, dlgparam *dp, int whichbutton) { } int dlg_radiobutton_get(union control *ctrl, dlgparam *dp) { return 0; } void dlg_checkbox_set(union control *ctrl, dlgparam *dp, bool checked) { } bool dlg_checkbox_get(union control *ctrl, dlgparam *dp) { return false; } void dlg_editbox_set(union control *ctrl, dlgparam *dp, char const *text) { } char *dlg_editbox_get(union control *ctrl, dlgparam *dp) { return dupstr("moo"); } void dlg_listbox_clear(union control *ctrl, dlgparam *dp) { } void dlg_listbox_del(union control *ctrl, dlgparam *dp, int index) { } void dlg_listbox_add(union control *ctrl, dlgparam *dp, char const *text) { } void dlg_listbox_addwithid(union control *ctrl, dlgparam *dp, char const *text, int id) { } int dlg_listbox_getid(union control *ctrl, dlgparam *dp, int index) { return 0; } int dlg_listbox_index(union control *ctrl, dlgparam *dp) { return -1; } bool dlg_listbox_issel(union control *ctrl, dlgparam *dp, int index) { return false; } void dlg_listbox_select(union control *ctrl, dlgparam *dp, int index) { } void dlg_text_set(union control *ctrl, dlgparam *dp, char const *text) { } void dlg_filesel_set(union control *ctrl, dlgparam *dp, Filename *fn) { } Filename *dlg_filesel_get(union control *ctrl, dlgparam *dp) { return NULL; } void dlg_fontsel_set(union control *ctrl, dlgparam *dp, FontSpec *fn) { } FontSpec *dlg_fontsel_get(union control *ctrl, dlgparam *dp) { return NULL; } void dlg_update_start(union control *ctrl, dlgparam *dp) { } void dlg_update_done(union control *ctrl, dlgparam *dp) { } void dlg_set_focus(union control *ctrl, dlgparam *dp) { } void dlg_label_change(union control *ctrl, dlgparam *dp, char const *text) { } union control *dlg_last_focused(union control *ctrl, dlgparam *dp) { return NULL; } void dlg_beep(dlgparam *dp) { } void dlg_error_msg(dlgparam *dp, const char *msg) { } void dlg_end(dlgparam *dp, int value) { } void dlg_coloursel_start(union control *ctrl, dlgparam *dp, int r, int g, int b) { } bool dlg_coloursel_results(union control *ctrl, dlgparam *dp, int *r, int *g, int *b) { return false; } void dlg_refresh(union control *ctrl, dlgparam *dp) { } bool dlg_is_visible(union control *ctrl, dlgparam *dp) { return false; } const char *const appname = "FuZZterm"; const int ngsslibs = 0; const char *const gsslibnames[0] = { }; const struct keyvalwhere gsslibkeywords[0] = { }; /* * Default settings that are specific to Unix plink. */ char *platform_default_s(const char *name) { if (!strcmp(name, "TermType")) return dupstr(getenv("TERM")); if (!strcmp(name, "SerialLine")) return dupstr("/dev/ttyS0"); return NULL; } bool platform_default_b(const char *name, bool def) { return def; } 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 */ } putty-0.76/import.c0000644000175000017500000021154614072266310011244 00000000000000/* * Code for PuTTY to import and export private key files in other * SSH clients' formats. */ #include #include #include #include #include "putty.h" #include "ssh.h" #include "mpint.h" #include "misc.h" static bool openssh_pem_encrypted(BinarySource *src); static bool openssh_new_encrypted(BinarySource *src); static ssh2_userkey *openssh_pem_read( BinarySource *src, const char *passphrase, const char **errmsg_p); static ssh2_userkey *openssh_new_read( BinarySource *src, const char *passphrase, const char **errmsg_p); static bool openssh_auto_write( const Filename *file, ssh2_userkey *key, const char *passphrase); static bool openssh_pem_write( const Filename *file, ssh2_userkey *key, const char *passphrase); static bool openssh_new_write( const Filename *file, ssh2_userkey *key, const char *passphrase); static bool sshcom_encrypted(BinarySource *src, char **comment); static ssh2_userkey *sshcom_read( BinarySource *src, const char *passphrase, const char **errmsg_p); static bool sshcom_write( const Filename *file, ssh2_userkey *key, const char *passphrase); /* * Given a key type, determine whether we know how to import it. */ bool import_possible(int type) { if (type == SSH_KEYTYPE_OPENSSH_PEM) return true; if (type == SSH_KEYTYPE_OPENSSH_NEW) return true; if (type == SSH_KEYTYPE_SSHCOM) return true; return false; } /* * Given a key type, determine what native key type * (SSH_KEYTYPE_SSH1 or SSH_KEYTYPE_SSH2) it will come out as once * we've imported it. */ int import_target_type(int type) { /* * There are no known foreign SSH-1 key formats. */ return SSH_KEYTYPE_SSH2; } static inline char *bsgetline(BinarySource *src) { ptrlen line = get_chomped_line(src); if (get_err(src)) return NULL; return mkstr(line); } /* * Determine whether a foreign key is encrypted. */ bool import_encrypted_s(const Filename *filename, BinarySource *src, int type, char **comment) { if (type == SSH_KEYTYPE_OPENSSH_PEM) { /* OpenSSH PEM format doesn't contain a key comment at all */ *comment = dupstr(filename_to_str(filename)); return openssh_pem_encrypted(src); } else if (type == SSH_KEYTYPE_OPENSSH_NEW) { /* OpenSSH new format does, but it's inside the encrypted * section for some reason */ *comment = dupstr(filename_to_str(filename)); return openssh_new_encrypted(src); } else if (type == SSH_KEYTYPE_SSHCOM) { return sshcom_encrypted(src, comment); } return false; } bool import_encrypted(const Filename *filename, int type, char **comment) { LoadedFile *lf = lf_load_keyfile(filename, NULL); if (!lf) return false; /* couldn't even open the file */ bool toret = import_encrypted_s(filename, BinarySource_UPCAST(lf), type, comment); lf_free(lf); return toret; } /* * Import an SSH-1 key. */ int import_ssh1_s(BinarySource *src, int type, RSAKey *key, char *passphrase, const char **errmsg_p) { return 0; } int import_ssh1(const Filename *filename, int type, RSAKey *key, char *passphrase, const char **errmsg_p) { LoadedFile *lf = lf_load_keyfile(filename, errmsg_p); if (!lf) return false; int toret = import_ssh1_s(BinarySource_UPCAST(lf), type, key, passphrase, errmsg_p); lf_free(lf); return toret; } /* * Import an SSH-2 key. */ ssh2_userkey *import_ssh2_s(BinarySource *src, int type, char *passphrase, const char **errmsg_p) { if (type == SSH_KEYTYPE_OPENSSH_PEM) return openssh_pem_read(src, passphrase, errmsg_p); else if (type == SSH_KEYTYPE_OPENSSH_NEW) return openssh_new_read(src, passphrase, errmsg_p); if (type == SSH_KEYTYPE_SSHCOM) return sshcom_read(src, passphrase, errmsg_p); return NULL; } ssh2_userkey *import_ssh2(const Filename *filename, int type, char *passphrase, const char **errmsg_p) { LoadedFile *lf = lf_load_keyfile(filename, errmsg_p); if (!lf) return false; ssh2_userkey *toret = import_ssh2_s(BinarySource_UPCAST(lf), type, passphrase, errmsg_p); lf_free(lf); return toret; } /* * Export an SSH-1 key. */ bool export_ssh1(const Filename *filename, int type, RSAKey *key, char *passphrase) { return false; } /* * Export an SSH-2 key. */ bool export_ssh2(const Filename *filename, int type, ssh2_userkey *key, char *passphrase) { if (type == SSH_KEYTYPE_OPENSSH_AUTO) return openssh_auto_write(filename, key, passphrase); if (type == SSH_KEYTYPE_OPENSSH_NEW) return openssh_new_write(filename, key, passphrase); if (type == SSH_KEYTYPE_SSHCOM) return sshcom_write(filename, key, passphrase); return false; } /* * Strip trailing CRs and LFs at the end of a line of text. */ void strip_crlf(char *str) { char *p = str + strlen(str); while (p > str && (p[-1] == '\r' || p[-1] == '\n')) *--p = '\0'; } /* ---------------------------------------------------------------------- * Helper routines. (The base64 ones are defined in sshpubk.c.) */ #define isbase64(c) ( ((c) >= 'A' && (c) <= 'Z') || \ ((c) >= 'a' && (c) <= 'z') || \ ((c) >= '0' && (c) <= '9') || \ (c) == '+' || (c) == '/' || (c) == '=' \ ) /* * Read an ASN.1/BER identifier and length pair. * * Flags are a combination of the #defines listed below. * * Returns -1 if unsuccessful; otherwise returns the number of * bytes used out of the source data. */ /* ASN.1 tag classes. */ #define ASN1_CLASS_UNIVERSAL (0 << 6) #define ASN1_CLASS_APPLICATION (1 << 6) #define ASN1_CLASS_CONTEXT_SPECIFIC (2 << 6) #define ASN1_CLASS_PRIVATE (3 << 6) #define ASN1_CLASS_MASK (3 << 6) /* Primitive versus constructed bit. */ #define ASN1_CONSTRUCTED (1 << 5) /* * Write an ASN.1/BER identifier and length pair. Returns the * number of bytes consumed. Assumes dest contains enough space. * Will avoid writing anything if dest is NULL, but still return * amount of space required. */ static void BinarySink_put_ber_id_len(BinarySink *bs, int id, int length, int flags) { if (id <= 30) { /* * Identifier is one byte. */ put_byte(bs, id | flags); } else { int n; /* * Identifier is multiple bytes: the first byte is 11111 * plus the flags, and subsequent bytes encode the value of * the identifier, 7 bits at a time, with the top bit of * each byte 1 except the last one which is 0. */ put_byte(bs, 0x1F | flags); for (n = 1; (id >> (7*n)) > 0; n++) continue; /* count the bytes */ while (n--) put_byte(bs, (n ? 0x80 : 0) | ((id >> (7*n)) & 0x7F)); } if (length < 128) { /* * Length is one byte. */ put_byte(bs, length); } else { int n; /* * Length is multiple bytes. The first is 0x80 plus the * number of subsequent bytes, and the subsequent bytes * encode the actual length. */ for (n = 1; (length >> (8*n)) > 0; n++) continue; /* count the bytes */ put_byte(bs, 0x80 | n); while (n--) put_byte(bs, (length >> (8*n)) & 0xFF); } } #define put_ber_id_len(bs, id, len, flags) \ BinarySink_put_ber_id_len(BinarySink_UPCAST(bs), id, len, flags) typedef struct ber_item { int id; int flags; ptrlen data; } ber_item; static ber_item BinarySource_get_ber(BinarySource *src) { ber_item toret; unsigned char leadbyte, lenbyte; size_t length; leadbyte = get_byte(src); toret.flags = (leadbyte & 0xE0); if ((leadbyte & 0x1F) == 0x1F) { unsigned char idbyte; toret.id = 0; do { idbyte = get_byte(src); toret.id = (toret.id << 7) | (idbyte & 0x7F); } while (idbyte & 0x80); } else { toret.id = leadbyte & 0x1F; } lenbyte = get_byte(src); if (lenbyte & 0x80) { int nbytes = lenbyte & 0x7F; length = 0; while (nbytes-- > 0) length = (length << 8) | get_byte(src); } else { length = lenbyte; } toret.data = get_data(src, length); return toret; } #define get_ber(bs) BinarySource_get_ber(BinarySource_UPCAST(bs)) /* ---------------------------------------------------------------------- * Code to read and write OpenSSH private keys, in the old-style PEM * format. */ typedef enum { OP_DSA, OP_RSA, OP_ECDSA } openssh_pem_keytype; typedef enum { OP_E_3DES, OP_E_AES } openssh_pem_enc; struct openssh_pem_key { openssh_pem_keytype keytype; bool encrypted; openssh_pem_enc encryption; char iv[32]; strbuf *keyblob; }; void BinarySink_put_mp_ssh2_from_string(BinarySink *bs, ptrlen str) { const unsigned char *bytes = (const unsigned char *)str.ptr; size_t nbytes = str.len; while (nbytes > 0 && bytes[0] == 0) { nbytes--; bytes++; } if (nbytes > 0 && bytes[0] & 0x80) { put_uint32(bs, nbytes + 1); put_byte(bs, 0); } else { put_uint32(bs, nbytes); } put_data(bs, bytes, nbytes); } #define put_mp_ssh2_from_string(bs, str) \ BinarySink_put_mp_ssh2_from_string(BinarySink_UPCAST(bs), str) static struct openssh_pem_key *load_openssh_pem_key(BinarySource *src, const char **errmsg_p) { struct openssh_pem_key *ret; char *line = NULL; const char *errmsg; char *p; bool headers_done; char base64_bit[4]; int base64_chars = 0; ret = snew(struct openssh_pem_key); ret->keyblob = strbuf_new_nm(); if (!(line = bsgetline(src))) { errmsg = "unexpected end of file"; goto error; } if (!strstartswith(line, "-----BEGIN ") || !strendswith(line, "PRIVATE KEY-----")) { errmsg = "file does not begin with OpenSSH key header"; goto error; } /* * Parse the BEGIN line. For old-format keys, this tells us the * type of the key; for new-format keys, all it tells us is the * format, and we'll find out the key type once we parse the * base64. */ if (!strcmp(line, "-----BEGIN RSA PRIVATE KEY-----")) { ret->keytype = OP_RSA; } else if (!strcmp(line, "-----BEGIN DSA PRIVATE KEY-----")) { ret->keytype = OP_DSA; } else if (!strcmp(line, "-----BEGIN EC PRIVATE KEY-----")) { ret->keytype = OP_ECDSA; } else if (!strcmp(line, "-----BEGIN OPENSSH PRIVATE KEY-----")) { errmsg = "this is a new-style OpenSSH key"; goto error; } else { errmsg = "unrecognised key type"; goto error; } smemclr(line, strlen(line)); sfree(line); line = NULL; ret->encrypted = false; memset(ret->iv, 0, sizeof(ret->iv)); headers_done = false; while (1) { if (!(line = bsgetline(src))) { errmsg = "unexpected end of file"; goto error; } if (strstartswith(line, "-----END ") && strendswith(line, "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++; if (!strcmp(line, "Proc-Type")) { if (p[0] != '4' || p[1] != ',') { errmsg = "Proc-Type is not 4 (only 4 is supported)"; goto error; } p += 2; if (!strcmp(p, "ENCRYPTED")) ret->encrypted = true; } else if (!strcmp(line, "DEK-Info")) { int i, ivlen; if (!strncmp(p, "DES-EDE3-CBC,", 13)) { ret->encryption = OP_E_3DES; ivlen = 8; } else if (!strncmp(p, "AES-128-CBC,", 12)) { ret->encryption = OP_E_AES; ivlen = 16; } else { errmsg = "unsupported cipher"; goto error; } p = strchr(p, ',') + 1;/* always non-NULL, by above checks */ for (i = 0; i < ivlen; i++) { unsigned j; if (1 != sscanf(p, "%2x", &j)) { errmsg = "expected more iv data in DEK-Info"; goto error; } ret->iv[i] = j; p += 2; } if (*p) { errmsg = "more iv data than expected in DEK-Info"; goto error; } } } else { headers_done = true; p = line; while (isbase64(*p)) { base64_bit[base64_chars++] = *p; if (base64_chars == 4) { unsigned char out[3]; int len; base64_chars = 0; len = base64_decode_atom(base64_bit, out); if (len <= 0) { errmsg = "invalid base64 encoding"; goto error; } put_data(ret->keyblob, out, len); smemclr(out, sizeof(out)); } p++; } } smemclr(line, strlen(line)); sfree(line); line = NULL; } if (!ret->keyblob || ret->keyblob->len == 0) { 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) strbuf_free(ret->keyblob); smemclr(ret, sizeof(*ret)); sfree(ret); } if (errmsg_p) *errmsg_p = errmsg; return NULL; } static bool openssh_pem_encrypted(BinarySource *src) { struct openssh_pem_key *key = load_openssh_pem_key(src, NULL); bool ret; if (!key) return false; ret = key->encrypted; strbuf_free(key->keyblob); smemclr(key, sizeof(*key)); sfree(key); return ret; } static void openssh_pem_derivekey( ptrlen passphrase, const void *iv, uint8_t *keybuf) { /* * Derive the encryption key for a PEM key file from the * passphrase and iv/salt: * * - let block A equal MD5(passphrase || iv) * - let block B equal MD5(A || passphrase || iv) * - block C would be MD5(B || passphrase || iv) and so on * - encryption key is the first N bytes of A || B * * (Note that only 8 bytes of the iv are used for key * derivation, even when the key is encrypted with AES and * hence there are 16 bytes available.) */ ssh_hash *h; h = ssh_hash_new(&ssh_md5); put_datapl(h, passphrase); put_data(h, iv, 8); ssh_hash_digest(h, keybuf); ssh_hash_reset(h); put_data(h, keybuf, 16); put_datapl(h, passphrase); put_data(h, iv, 8); ssh_hash_final(h, keybuf + 16); } static ssh2_userkey *openssh_pem_read( BinarySource *filesrc, const char *passphrase, const char **errmsg_p) { struct openssh_pem_key *key = load_openssh_pem_key(filesrc, errmsg_p); ssh2_userkey *retkey; const ssh_keyalg *alg; BinarySource src[1]; int i, num_integers; ssh2_userkey *retval = NULL; const char *errmsg; strbuf *blob = strbuf_new_nm(); int privptr = 0, publen; if (!key) { strbuf_free(blob); return NULL; } if (key->encrypted) { unsigned char keybuf[32]; openssh_pem_derivekey(ptrlen_from_asciz(passphrase), key->iv, keybuf); /* * Decrypt the key blob. */ if (key->encryption == OP_E_3DES) des3_decrypt_pubkey_ossh(keybuf, key->iv, key->keyblob->u, key->keyblob->len); else { ssh_cipher *cipher = ssh_cipher_new(&ssh_aes128_cbc); ssh_cipher_setkey(cipher, keybuf); ssh_cipher_setiv(cipher, key->iv); ssh_cipher_decrypt(cipher, key->keyblob->u, key->keyblob->len); ssh_cipher_free(cipher); } 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 * (0x30 followed by a length code indicating that the rest of * the blob is part of the sequence). Within that SEQUENCE we * expect to see a bunch of INTEGERs. What those integers mean * depends on the key type: * * - For RSA, we expect the integers to be 0, n, e, d, p, q, * dmp1, dmq1, iqmp in that order. (The last three are d mod * (p-1), d mod (q-1), inverse of q mod p respectively.) * * - For DSA, we expect them to be 0, p, q, g, y, x in that * order. * * - In ECDSA the format is totally different: we see the * SEQUENCE, but beneath is an INTEGER 1, OCTET STRING priv * EXPLICIT [0] OID curve, EXPLICIT [1] BIT STRING pubPoint */ BinarySource_BARE_INIT(src, key->keyblob->u, key->keyblob->len); { /* Expect the SEQUENCE header. Take its absence as a failure to * decrypt, if the key was encrypted. */ ber_item seq = get_ber(src); if (get_err(src) || seq.id != 16) { errmsg = "ASN.1 decoding failure"; retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL; goto error; } /* Reinitialise our BinarySource to parse just the inside of that * SEQUENCE. */ BinarySource_BARE_INIT_PL(src, seq.data); } /* Expect a load of INTEGERs. */ if (key->keytype == OP_RSA) num_integers = 9; else if (key->keytype == OP_DSA) num_integers = 6; else num_integers = 0; /* placate compiler warnings */ if (key->keytype == OP_ECDSA) { /* And now for something completely different */ ber_item integer, privkey, sub0, sub1, oid, pubkey; const ssh_keyalg *alg; const struct ec_curve *curve; /* Parse the outer layer of things inside the containing SEQUENCE */ integer = get_ber(src); privkey = get_ber(src); sub0 = get_ber(src); sub1 = get_ber(src); /* Now look inside sub0 for the curve OID */ BinarySource_BARE_INIT_PL(src, sub0.data); oid = get_ber(src); /* And inside sub1 for the public-key BIT STRING */ BinarySource_BARE_INIT_PL(src, sub1.data); pubkey = get_ber(src); if (get_err(src) || integer.id != 2 || integer.data.len != 1 || ((const unsigned char *)integer.data.ptr)[0] != 1 || privkey.id != 4 || sub0.id != 0 || sub1.id != 1 || oid.id != 6 || pubkey.id != 3) { errmsg = "ASN.1 decoding failure"; retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL; goto error; } alg = ec_alg_by_oid(oid.data.len, oid.data.ptr, &curve); if (!alg) { errmsg = "Unsupported ECDSA curve."; retval = NULL; goto error; } if (pubkey.data.len != ((((curve->fieldBits + 7) / 8) * 2) + 2)) { errmsg = "ASN.1 decoding failure"; retval = key->encrypted ? SSH2_WRONG_PASSPHRASE : NULL; goto error; } /* Skip 0x00 before point */ pubkey.data.ptr = (const char *)pubkey.data.ptr + 1; pubkey.data.len -= 1; /* Construct the key */ retkey = snew(ssh2_userkey); put_stringz(blob, alg->ssh_id); put_stringz(blob, curve->name); put_stringpl(blob, pubkey.data); publen = blob->len; put_mp_ssh2_from_string(blob, privkey.data); retkey->key = ssh_key_new_priv( alg, make_ptrlen(blob->u, publen), make_ptrlen(blob->u + publen, blob->len - publen)); if (!retkey->key) { sfree(retkey); errmsg = "unable to create key data structure"; goto error; } } else if (key->keytype == OP_RSA || key->keytype == OP_DSA) { put_stringz(blob, key->keytype == OP_DSA ? "ssh-dss" : "ssh-rsa"); ptrlen rsa_modulus = PTRLEN_LITERAL(""); for (i = 0; i < num_integers; i++) { ber_item integer = get_ber(src); if (get_err(src) || integer.id != 2) { 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). */ if (integer.data.len != 1 || ((const unsigned char *)integer.data.ptr)[0] != 0) { errmsg = "version number mismatch"; goto error; } } else if (key->keytype == OP_RSA) { /* * Integers 1 and 2 go into the public blob but in the * opposite order; integers 3, 4, 5 and 8 go into the * private blob. The other two (6 and 7) are ignored. */ if (i == 1) { /* Save the details for after we deal with number 2. */ rsa_modulus = integer.data; } else if (i != 6 && i != 7) { put_mp_ssh2_from_string(blob, integer.data); if (i == 2) { put_mp_ssh2_from_string(blob, rsa_modulus); privptr = blob->len; } } } else if (key->keytype == OP_DSA) { /* * Integers 1-4 go into the public blob; integer 5 goes * into the private blob. */ put_mp_ssh2_from_string(blob, integer.data); if (i == 4) privptr = blob->len; } } /* * Now put together the actual key. Simplest way to do this is * to assemble our own key blobs and feed them to the createkey * functions; this is a bit faffy but it does mean we get all * the sanity checks for free. */ assert(privptr > 0); /* should have bombed by now if not */ retkey = snew(ssh2_userkey); alg = (key->keytype == OP_RSA ? &ssh_rsa : &ssh_dss); retkey->key = ssh_key_new_priv( alg, make_ptrlen(blob->u, privptr), make_ptrlen(blob->u+privptr, blob->len-privptr)); if (!retkey->key) { sfree(retkey); errmsg = "unable to create key data structure"; goto error; } } else { unreachable("Bad key type from load_openssh_pem_key"); errmsg = "Bad key type from load_openssh_pem_key"; goto error; } /* * The old key format doesn't include a comment in the private * key file. */ retkey->comment = dupstr("imported-openssh-key"); errmsg = NULL; /* no error */ retval = retkey; error: strbuf_free(blob); strbuf_free(key->keyblob); smemclr(key, sizeof(*key)); sfree(key); if (errmsg_p) *errmsg_p = errmsg; return retval; } static bool openssh_pem_write( const Filename *filename, ssh2_userkey *key, const char *passphrase) { strbuf *pubblob, *privblob, *outblob; unsigned char *spareblob; int sparelen = 0; ptrlen numbers[9]; int nnumbers, i; const char *header, *footer; char zero[1]; unsigned char iv[8]; bool ret = false; FILE *fp; BinarySource src[1]; /* * Fetch the key blobs. */ pubblob = strbuf_new(); ssh_key_public_blob(key->key, BinarySink_UPCAST(pubblob)); privblob = strbuf_new_nm(); ssh_key_private_blob(key->key, BinarySink_UPCAST(privblob)); spareblob = NULL; outblob = strbuf_new_nm(); /* * Encode the OpenSSH key blob, and also decide on the header * line. */ if (ssh_key_alg(key->key) == &ssh_rsa || ssh_key_alg(key->key) == &ssh_dss) { strbuf *seq; /* * The RSA and DSS handlers share some code because the two * key types have very similar ASN.1 representations, as a * plain SEQUENCE of big integers. So we set up a list of * bignums per key type and then construct the actual blob in * common code after that. */ if (ssh_key_alg(key->key) == &ssh_rsa) { ptrlen n, e, d, p, q, iqmp, dmp1, dmq1; mp_int *bd, *bp, *bq, *bdmp1, *bdmq1; /* * These blobs were generated from inside PuTTY, so we needn't * treat them as untrusted. */ BinarySource_BARE_INIT(src, pubblob->u, pubblob->len); get_string(src); /* skip algorithm name */ e = get_string(src); n = get_string(src); BinarySource_BARE_INIT(src, privblob->u, privblob->len); d = get_string(src); p = get_string(src); q = get_string(src); iqmp = get_string(src); assert(!get_err(src)); /* can't go wrong */ /* We also need d mod (p-1) and d mod (q-1). */ bd = mp_from_bytes_be(d); bp = mp_from_bytes_be(p); bq = mp_from_bytes_be(q); mp_sub_integer_into(bp, bp, 1); mp_sub_integer_into(bq, bq, 1); bdmp1 = mp_mod(bd, bp); bdmq1 = mp_mod(bd, bq); mp_free(bd); mp_free(bp); mp_free(bq); dmp1.len = (mp_get_nbits(bdmp1)+8)/8; dmq1.len = (mp_get_nbits(bdmq1)+8)/8; sparelen = dmp1.len + dmq1.len; spareblob = snewn(sparelen, unsigned char); dmp1.ptr = spareblob; dmq1.ptr = spareblob + dmp1.len; for (i = 0; i < dmp1.len; i++) spareblob[i] = mp_get_byte(bdmp1, dmp1.len-1 - i); for (i = 0; i < dmq1.len; i++) spareblob[i+dmp1.len] = mp_get_byte(bdmq1, dmq1.len-1 - i); mp_free(bdmp1); mp_free(bdmq1); numbers[0] = make_ptrlen(zero, 1); zero[0] = '\0'; numbers[1] = n; numbers[2] = e; numbers[3] = d; numbers[4] = p; numbers[5] = q; numbers[6] = dmp1; numbers[7] = dmq1; numbers[8] = iqmp; nnumbers = 9; header = "-----BEGIN RSA PRIVATE KEY-----\n"; footer = "-----END RSA PRIVATE KEY-----\n"; } else { /* ssh-dss */ ptrlen p, q, g, y, x; /* * These blobs were generated from inside PuTTY, so we needn't * treat them as untrusted. */ BinarySource_BARE_INIT(src, pubblob->u, pubblob->len); get_string(src); /* skip algorithm name */ p = get_string(src); q = get_string(src); g = get_string(src); y = get_string(src); BinarySource_BARE_INIT(src, privblob->u, privblob->len); x = get_string(src); assert(!get_err(src)); /* can't go wrong */ numbers[0].ptr = zero; numbers[0].len = 1; zero[0] = '\0'; numbers[1] = p; numbers[2] = q; numbers[3] = g; numbers[4] = y; numbers[5] = x; nnumbers = 6; header = "-----BEGIN DSA PRIVATE KEY-----\n"; footer = "-----END DSA PRIVATE KEY-----\n"; } seq = strbuf_new_nm(); for (i = 0; i < nnumbers; i++) { put_ber_id_len(seq, 2, numbers[i].len, 0); put_datapl(seq, numbers[i]); } put_ber_id_len(outblob, 16, seq->len, ASN1_CONSTRUCTED); put_data(outblob, seq->s, seq->len); strbuf_free(seq); } else if (ssh_key_alg(key->key) == &ssh_ecdsa_nistp256 || ssh_key_alg(key->key) == &ssh_ecdsa_nistp384 || ssh_key_alg(key->key) == &ssh_ecdsa_nistp521) { const unsigned char *oid; struct ecdsa_key *ec = container_of(key->key, struct ecdsa_key, sshk); int oidlen; int pointlen; strbuf *seq, *sub; /* * Structure of asn1: * SEQUENCE * INTEGER 1 * OCTET STRING (private key) * [0] * OID (curve) * [1] * BIT STRING (0x00 public key point) */ oid = ec_alg_oid(ssh_key_alg(key->key), &oidlen); pointlen = (ec->curve->fieldBits + 7) / 8 * 2; seq = strbuf_new_nm(); /* INTEGER 1 */ put_ber_id_len(seq, 2, 1, 0); put_byte(seq, 1); /* OCTET STRING private key */ put_ber_id_len(seq, 4, privblob->len - 4, 0); put_data(seq, privblob->s + 4, privblob->len - 4); /* Subsidiary OID */ sub = strbuf_new(); put_ber_id_len(sub, 6, oidlen, 0); put_data(sub, oid, oidlen); /* Append the OID to the sequence */ put_ber_id_len(seq, 0, sub->len, ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED); put_data(seq, sub->s, sub->len); strbuf_free(sub); /* Subsidiary BIT STRING */ sub = strbuf_new(); put_ber_id_len(sub, 3, 2 + pointlen, 0); put_byte(sub, 0); put_data(sub, pubblob->s+39, 1 + pointlen); /* Append the BIT STRING to the sequence */ put_ber_id_len(seq, 1, sub->len, ASN1_CLASS_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED); put_data(seq, sub->s, sub->len); strbuf_free(sub); /* Write the full sequence with header to the output blob. */ put_ber_id_len(outblob, 16, seq->len, ASN1_CONSTRUCTED); put_data(outblob, seq->s, seq->len); strbuf_free(seq); header = "-----BEGIN EC PRIVATE KEY-----\n"; footer = "-----END EC PRIVATE KEY-----\n"; } else { unreachable("bad key alg in openssh_pem_write"); } /* * Encrypt the key. * * For the moment, we still encrypt our OpenSSH keys using * old-style 3DES. */ if (passphrase) { unsigned char keybuf[32]; int origlen, outlen, pad; /* * Padding on OpenSSH keys is deterministic. The number of * padding bytes is always more than zero, and always at most * the cipher block length. The value of each padding byte is * equal to the number of padding bytes. So a plaintext that's * an exact multiple of the block size will be padded with 08 * 08 08 08 08 08 08 08 (assuming a 64-bit block cipher); a * plaintext one byte less than a multiple of the block size * will be padded with just 01. * * This enables the OpenSSL key decryption function to strip * off the padding algorithmically and return the unpadded * plaintext to the next layer: it looks at the final byte, and * then expects to find that many bytes at the end of the data * with the same value. Those are all removed and the rest is * returned. */ origlen = outblob->len; outlen = (origlen + 8) &~ 7; pad = outlen - origlen; put_padding(outblob, pad, pad); /* * Invent an iv, and derive the encryption key. */ random_read(iv, 8); openssh_pem_derivekey(ptrlen_from_asciz(passphrase), iv, keybuf); /* * Now encrypt the key blob. */ des3_encrypt_pubkey_ossh(keybuf, iv, outblob->u, outlen); 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->u, outblob->len, 64); fputs(footer, fp); fclose(fp); ret = true; error: if (outblob) strbuf_free(outblob); if (spareblob) { smemclr(spareblob, sparelen); sfree(spareblob); } if (privblob) strbuf_free(privblob); if (pubblob) strbuf_free(pubblob); return ret; } /* ---------------------------------------------------------------------- * Code to read and write OpenSSH private keys in the new-style format. */ typedef enum { ON_E_NONE, ON_E_AES256CBC, ON_E_AES256CTR } openssh_new_cipher; typedef enum { ON_K_NONE, ON_K_BCRYPT } openssh_new_kdf; struct openssh_new_key { openssh_new_cipher cipher; openssh_new_kdf kdf; union { struct { int rounds; /* This points to a position within keyblob, not a * separately allocated thing */ ptrlen salt; } bcrypt; } kdfopts; int nkeys, key_wanted; /* This too points to a position within keyblob */ ptrlen private; strbuf *keyblob; }; static struct openssh_new_key *load_openssh_new_key(BinarySource *filesrc, const char **errmsg_p) { struct openssh_new_key *ret; char *line = NULL; const char *errmsg; char *p; char base64_bit[4]; int base64_chars = 0; BinarySource src[1]; ptrlen str; unsigned key_index; ret = snew(struct openssh_new_key); ret->keyblob = strbuf_new_nm(); if (!(line = bsgetline(filesrc))) { errmsg = "unexpected end of file"; goto error; } if (0 != strcmp(line, "-----BEGIN OPENSSH PRIVATE KEY-----")) { errmsg = "file does not begin with OpenSSH new-style key header"; goto error; } smemclr(line, strlen(line)); sfree(line); line = NULL; while (1) { if (!(line = bsgetline(filesrc))) { errmsg = "unexpected end of file"; goto error; } if (0 == strcmp(line, "-----END OPENSSH PRIVATE KEY-----")) { sfree(line); line = NULL; break; /* done */ } p = line; while (isbase64(*p)) { base64_bit[base64_chars++] = *p; if (base64_chars == 4) { unsigned char out[3]; int len; base64_chars = 0; len = base64_decode_atom(base64_bit, out); if (len <= 0) { errmsg = "invalid base64 encoding"; goto error; } put_data(ret->keyblob, out, len); smemclr(out, sizeof(out)); } p++; } smemclr(line, strlen(line)); sfree(line); line = NULL; } if (ret->keyblob->len == 0) { errmsg = "key body not present"; goto error; } BinarySource_BARE_INIT_PL(src, ptrlen_from_strbuf(ret->keyblob)); if (strcmp(get_asciz(src), "openssh-key-v1") != 0) { errmsg = "new-style OpenSSH magic number missing\n"; goto error; } /* Cipher name */ str = get_string(src); if (ptrlen_eq_string(str, "none")) { ret->cipher = ON_E_NONE; } else if (ptrlen_eq_string(str, "aes256-cbc")) { ret->cipher = ON_E_AES256CBC; } else if (ptrlen_eq_string(str, "aes256-ctr")) { ret->cipher = ON_E_AES256CTR; } else { errmsg = get_err(src) ? "no cipher name found" : "unrecognised cipher name\n"; goto error; } /* Key derivation function name */ str = get_string(src); if (ptrlen_eq_string(str, "none")) { ret->kdf = ON_K_NONE; } else if (ptrlen_eq_string(str, "bcrypt")) { ret->kdf = ON_K_BCRYPT; } else { errmsg = get_err(src) ? "no kdf name found" : "unrecognised kdf name\n"; goto error; } /* KDF extra options */ str = get_string(src); switch (ret->kdf) { case ON_K_NONE: if (str.len != 0) { errmsg = "expected empty options string for 'none' kdf"; goto error; } break; case ON_K_BCRYPT: { BinarySource opts[1]; BinarySource_BARE_INIT_PL(opts, str); ret->kdfopts.bcrypt.salt = get_string(opts); ret->kdfopts.bcrypt.rounds = get_uint32(opts); if (get_err(opts)) { errmsg = "failed to parse bcrypt options string"; goto error; } break; } } /* * At this point we expect a uint32 saying how many keys are * stored in this file. OpenSSH new-style key files can * contain more than one. Currently we don't have any user * interface to specify which one we're trying to extract, so * we just bomb out with an error if more than one is found in * the file. However, I've put in all the mechanism here to * extract the nth one for a given n, in case we later connect * up some UI to that mechanism. Just arrange that the * 'key_wanted' field is set to a value in the range [0, * nkeys) by some mechanism. */ ret->nkeys = toint(get_uint32(src)); if (ret->nkeys != 1) { errmsg = get_err(src) ? "no key count found" : "multiple keys in new-style OpenSSH key file not supported\n"; goto error; } ret->key_wanted = 0; /* Read and ignore a string per public key. */ for (key_index = 0; key_index < ret->nkeys; key_index++) str = get_string(src); /* * Now we expect a string containing the encrypted part of the * key file. */ ret->private = get_string(src); if (get_err(src)) { errmsg = "no private key container string found\n"; goto error; } /* * And now we're done, until asked to actually decrypt. */ 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) { strbuf_free(ret->keyblob); smemclr(ret, sizeof(*ret)); sfree(ret); } if (errmsg_p) *errmsg_p = errmsg; return NULL; } static bool openssh_new_encrypted(BinarySource *src) { struct openssh_new_key *key = load_openssh_new_key(src, NULL); bool ret; if (!key) return false; ret = (key->cipher != ON_E_NONE); strbuf_free(key->keyblob); smemclr(key, sizeof(*key)); sfree(key); return ret; } static ssh2_userkey *openssh_new_read( BinarySource *filesrc, const char *passphrase, const char **errmsg_p) { struct openssh_new_key *key = load_openssh_new_key(filesrc, errmsg_p); ssh2_userkey *retkey = NULL; ssh2_userkey *retval = NULL; const char *errmsg; unsigned checkint; BinarySource src[1]; int key_index; const ssh_keyalg *alg = NULL; if (!key) return NULL; if (key->cipher != ON_E_NONE) { unsigned char keybuf[48]; int keysize; /* * Construct the decryption key, and decrypt the string. */ switch (key->cipher) { case ON_E_NONE: keysize = 0; break; case ON_E_AES256CBC: case ON_E_AES256CTR: keysize = 48; /* 32 byte key + 16 byte IV */ break; default: unreachable("Bad cipher enumeration value"); } assert(keysize <= sizeof(keybuf)); switch (key->kdf) { case ON_K_NONE: memset(keybuf, 0, keysize); break; case ON_K_BCRYPT: openssh_bcrypt(passphrase, key->kdfopts.bcrypt.salt.ptr, key->kdfopts.bcrypt.salt.len, key->kdfopts.bcrypt.rounds, keybuf, keysize); break; default: unreachable("Bad kdf enumeration value"); } switch (key->cipher) { case ON_E_NONE: break; case ON_E_AES256CBC: case ON_E_AES256CTR: if (key->private.len % 16 != 0) { errmsg = "private key container length is not a" " multiple of AES block size\n"; goto error; } { ssh_cipher *cipher = ssh_cipher_new( key->cipher == ON_E_AES256CBC ? &ssh_aes256_cbc : &ssh_aes256_sdctr); ssh_cipher_setkey(cipher, keybuf); ssh_cipher_setiv(cipher, keybuf + 32); /* Decrypt the private section in place, casting away * the const from key->private being a ptrlen */ ssh_cipher_decrypt(cipher, (char *)key->private.ptr, key->private.len); ssh_cipher_free(cipher); } break; default: unreachable("Bad cipher enumeration value"); } } /* * Now parse the entire encrypted section, and extract the key * identified by key_wanted. */ BinarySource_BARE_INIT_PL(src, key->private); checkint = get_uint32(src); if (get_uint32(src) != checkint || get_err(src)) { errmsg = "decryption check failed"; goto error; } retkey = snew(ssh2_userkey); retkey->key = NULL; retkey->comment = NULL; for (key_index = 0; key_index < key->nkeys; key_index++) { ptrlen comment; /* * Identify the key type. */ alg = find_pubkey_alg_len(get_string(src)); if (!alg) { errmsg = "private key type not recognised\n"; goto error; } /* * Read the key. We have to do this even if it's not the one * we want, because it's the only way to find out how much * data to skip past to get to the next key in the file. */ retkey->key = ssh_key_new_priv_openssh(alg, src); if (get_err(src)) { errmsg = "unable to read entire private key"; goto error; } if (!retkey->key) { errmsg = "unable to create key data structure"; goto error; } if (key_index != key->key_wanted) { /* * If this isn't the key we're looking for, throw it away. */ ssh_key_free(retkey->key); retkey->key = NULL; } /* * Read the key comment. */ comment = get_string(src); if (get_err(src)) { errmsg = "unable to read key comment"; goto error; } if (key_index == key->key_wanted) { assert(retkey); retkey->comment = mkstr(comment); } } if (!retkey->key) { errmsg = "key index out of range"; goto error; } /* * Now we expect nothing left but padding. */ { unsigned char expected_pad_byte = 1; while (get_avail(src) > 0) if (get_byte(src) != expected_pad_byte++) { errmsg = "padding at end of private string did not match"; goto error; } } errmsg = NULL; /* no error */ retval = retkey; retkey = NULL; /* prevent the free */ error: if (retkey) { sfree(retkey->comment); if (retkey->key) ssh_key_free(retkey->key); sfree(retkey); } strbuf_free(key->keyblob); smemclr(key, sizeof(*key)); sfree(key); if (errmsg_p) *errmsg_p = errmsg; return retval; } static bool openssh_new_write( const Filename *filename, ssh2_userkey *key, const char *passphrase) { strbuf *pubblob, *privblob, *cblob; int padvalue; unsigned checkint; bool ret = false; unsigned char bcrypt_salt[16]; const int bcrypt_rounds = 16; FILE *fp; /* * Fetch the key blobs and find out the lengths of things. */ pubblob = strbuf_new(); ssh_key_public_blob(key->key, BinarySink_UPCAST(pubblob)); privblob = strbuf_new_nm(); ssh_key_openssh_blob(key->key, BinarySink_UPCAST(privblob)); /* * Construct the cleartext version of the blob. */ cblob = strbuf_new_nm(); /* Magic number. */ put_asciz(cblob, "openssh-key-v1"); /* Cipher and kdf names, and kdf options. */ if (!passphrase) { memset(bcrypt_salt, 0, sizeof(bcrypt_salt)); /* prevent warnings */ put_stringz(cblob, "none"); put_stringz(cblob, "none"); put_stringz(cblob, ""); } else { strbuf *substr; random_read(bcrypt_salt, sizeof(bcrypt_salt)); put_stringz(cblob, "aes256-ctr"); put_stringz(cblob, "bcrypt"); substr = strbuf_new_nm(); put_string(substr, bcrypt_salt, sizeof(bcrypt_salt)); put_uint32(substr, bcrypt_rounds); put_stringsb(cblob, substr); } /* Number of keys. */ put_uint32(cblob, 1); /* Public blob. */ put_string(cblob, pubblob->s, pubblob->len); /* Private section. */ { strbuf *cpblob = strbuf_new_nm(); /* checkint. */ uint8_t checkint_buf[4]; random_read(checkint_buf, 4); checkint = GET_32BIT_MSB_FIRST(checkint_buf); put_uint32(cpblob, checkint); put_uint32(cpblob, checkint); /* Private key. The main private blob goes inline, with no string * wrapper. */ put_stringz(cpblob, ssh_key_ssh_id(key->key)); put_data(cpblob, privblob->s, privblob->len); /* Comment. */ put_stringz(cpblob, key->comment); /* Pad out the encrypted section. */ padvalue = 1; do { put_byte(cpblob, padvalue++); } while (cpblob->len & 15); if (passphrase) { /* * Encrypt the private section. We need 48 bytes of key * material: 32 bytes AES key + 16 bytes iv. */ unsigned char keybuf[48]; ssh_cipher *cipher; openssh_bcrypt(passphrase, bcrypt_salt, sizeof(bcrypt_salt), bcrypt_rounds, keybuf, sizeof(keybuf)); cipher = ssh_cipher_new(&ssh_aes256_sdctr); ssh_cipher_setkey(cipher, keybuf); ssh_cipher_setiv(cipher, keybuf + 32); ssh_cipher_encrypt(cipher, cpblob->u, cpblob->len); ssh_cipher_free(cipher); smemclr(keybuf, sizeof(keybuf)); } put_stringsb(cblob, cpblob); } /* * 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 OPENSSH PRIVATE KEY-----\n", fp); base64_encode(fp, cblob->u, cblob->len, 64); fputs("-----END OPENSSH PRIVATE KEY-----\n", fp); fclose(fp); ret = true; error: if (cblob) strbuf_free(cblob); if (privblob) strbuf_free(privblob); if (pubblob) strbuf_free(pubblob); return ret; } /* ---------------------------------------------------------------------- * The switch function openssh_auto_write(), which chooses one of the * concrete OpenSSH output formats based on the key type. */ static bool openssh_auto_write( const Filename *filename, ssh2_userkey *key, const char *passphrase) { /* * The old OpenSSH format supports a fixed list of key types. We * assume that anything not in that fixed list is newer, and hence * will use the new format. */ if (ssh_key_alg(key->key) == &ssh_dss || ssh_key_alg(key->key) == &ssh_rsa || ssh_key_alg(key->key) == &ssh_ecdsa_nistp256 || ssh_key_alg(key->key) == &ssh_ecdsa_nistp384 || ssh_key_alg(key->key) == &ssh_ecdsa_nistp521) return openssh_pem_write(filename, key, passphrase); else return openssh_new_write(filename, key, passphrase); } /* ---------------------------------------------------------------------- * Code to read ssh.com private keys. */ /* * The format of the base64 blob is largely SSH-2-packet-formatted, * except that mpints are a bit different: they're more like the * old SSH-1 mpint. You have a 32-bit bit count N, followed by * (N+7)/8 bytes of data. * * So. The blob contains: * * - uint32 0x3f6ff9eb (magic number) * - uint32 size (total blob size) * - string key-type (see below) * - string cipher-type (tells you if key is encrypted) * - string encrypted-blob * * (The first size field includes the size field itself and the * magic number before it. All other size fields are ordinary SSH-2 * strings, so the size field indicates how much data is to * _follow_.) * * The encrypted blob, once decrypted, contains a single string * which in turn contains the payload. (This allows padding to be * added after that string while still making it clear where the * real payload ends. Also it probably makes for a reasonable * decryption check.) * * The payload blob, for an RSA key, contains: * - mpint e * - mpint d * - mpint n (yes, the public and private stuff is intermixed) * - mpint u (presumably inverse of p mod q) * - mpint p (p is the smaller prime) * - mpint q (q is the larger) * * For a DSA key, the payload blob contains: * - uint32 0 * - mpint p * - mpint g * - mpint q * - mpint y * - mpint x * * Alternatively, if the parameters are `predefined', that * (0,p,g,q) sequence can be replaced by a uint32 1 and a string * containing some predefined parameter specification. *shudder*, * but I doubt we'll encounter this in real life. * * The key type strings are ghastly. The RSA key I looked at had a * type string of * * `if-modn{sign{rsa-pkcs1-sha1},encrypt{rsa-pkcs1v2-oaep}}' * * and the DSA key wasn't much better: * * `dl-modp{sign{dsa-nist-sha1},dh{plain}}' * * It isn't clear that these will always be the same. I think it * might be wise just to look at the `if-modn{sign{rsa' and * `dl-modp{sign{dsa' prefixes. * * Finally, the encryption. The cipher-type string appears to be * either `none' or `3des-cbc'. Looks as if this is SSH-2-style * 3des-cbc (i.e. outer cbc rather than inner). The key is created * from the passphrase by means of yet another hashing faff: * * - first 16 bytes are MD5(passphrase) * - next 16 bytes are MD5(passphrase || first 16 bytes) * - if there were more, they'd be MD5(passphrase || first 32), * and so on. */ #define SSHCOM_MAGIC_NUMBER 0x3f6ff9eb struct sshcom_key { char comment[256]; /* allowing any length is overkill */ strbuf *keyblob; }; static struct sshcom_key *load_sshcom_key(BinarySource *src, const char **errmsg_p) { struct sshcom_key *ret; char *line = NULL; int hdrstart, len; const char *errmsg; char *p; bool headers_done; char base64_bit[4]; int base64_chars = 0; ret = snew(struct sshcom_key); ret->comment[0] = '\0'; ret->keyblob = strbuf_new_nm(); if (!(line = bsgetline(src))) { errmsg = "unexpected end of file"; goto error; } 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 = false; while (1) { if (!(line = bsgetline(src))) { errmsg = "unexpected end of file"; goto error; } 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++; hdrstart = p - line; /* * Header lines can end in a trailing backslash for * continuation. */ len = hdrstart + strlen(line+hdrstart); assert(!line[len]); while (line[len-1] == '\\') { char *line2; int line2len; line2 = bsgetline(src); if (!line2) { errmsg = "unexpected end of file"; goto error; } 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; if (!strcmp(line, "Comment")) { /* Strip quotes in comment if present. */ if (p[0] == '"' && p[strlen(p)-1] == '"') { p++; p[strlen(p)-1] = '\0'; } strncpy(ret->comment, p, sizeof(ret->comment)); ret->comment[sizeof(ret->comment)-1] = '\0'; } } else { headers_done = true; p = line; while (isbase64(*p)) { base64_bit[base64_chars++] = *p; if (base64_chars == 4) { unsigned char out[3]; base64_chars = 0; len = base64_decode_atom(base64_bit, out); if (len <= 0) { errmsg = "invalid base64 encoding"; goto error; } put_data(ret->keyblob, out, len); } p++; } } smemclr(line, strlen(line)); sfree(line); line = NULL; } if (ret->keyblob->len == 0) { errmsg = "key body not present"; goto error; } if (errmsg_p) *errmsg_p = NULL; return ret; error: if (line) { smemclr(line, strlen(line)); sfree(line); line = NULL; } if (ret) { strbuf_free(ret->keyblob); smemclr(ret, sizeof(*ret)); sfree(ret); } if (errmsg_p) *errmsg_p = errmsg; return NULL; } static bool sshcom_encrypted(BinarySource *filesrc, char **comment) { struct sshcom_key *key = load_sshcom_key(filesrc, NULL); BinarySource src[1]; ptrlen str; bool answer = false; *comment = NULL; if (!key) goto done; BinarySource_BARE_INIT_PL(src, ptrlen_from_strbuf(key->keyblob)); if (get_uint32(src) != SSHCOM_MAGIC_NUMBER) goto done; /* key is invalid */ get_uint32(src); /* skip length field */ get_string(src); /* skip key type */ str = get_string(src); /* cipher type */ if (get_err(src)) goto done; /* key is invalid */ if (!ptrlen_eq_string(str, "none")) answer = true; done: if (key) { *comment = dupstr(key->comment); strbuf_free(key->keyblob); smemclr(key, sizeof(*key)); sfree(key); } else { *comment = dupstr(""); } return answer; } void BinarySink_put_mp_sshcom_from_string(BinarySink *bs, ptrlen str) { const unsigned char *bytes = (const unsigned char *)str.ptr; size_t nbytes = str.len; int bits = nbytes * 8 - 1; while (bits > 0) { if (*bytes & (1 << (bits & 7))) break; if (!(bits-- & 7)) bytes++, nbytes--; } put_uint32(bs, bits+1); put_data(bs, bytes, nbytes); } #define put_mp_sshcom_from_string(bs, str) \ BinarySink_put_mp_sshcom_from_string(BinarySink_UPCAST(bs), str) static ptrlen BinarySource_get_mp_sshcom_as_string(BinarySource *src) { unsigned bits = get_uint32(src); return get_data(src, (bits + 7) / 8); } #define get_mp_sshcom_as_string(bs) \ BinarySource_get_mp_sshcom_as_string(BinarySource_UPCAST(bs)) static void sshcom_derivekey(ptrlen passphrase, uint8_t *keybuf) { /* * Derive the encryption key for an ssh.com key file from the * passphrase and iv/salt: * * - let block A equal MD5(passphrase) * - let block B equal MD5(passphrase || A) * - block C would be MD5(passphrase || A || B) and so on * - encryption key is the first N bytes of A || B */ ssh_hash *h; h = ssh_hash_new(&ssh_md5); put_datapl(h, passphrase); ssh_hash_digest_nondestructive(h, keybuf); put_data(h, keybuf, 16); ssh_hash_final(h, keybuf + 16); } static ssh2_userkey *sshcom_read( BinarySource *filesrc, const char *passphrase, const char **errmsg_p) { struct sshcom_key *key = load_sshcom_key(filesrc, errmsg_p); const char *errmsg; BinarySource src[1]; ptrlen str, ciphertext; int publen; const char prefix_rsa[] = "if-modn{sign{rsa"; const char prefix_dsa[] = "dl-modp{sign{dsa"; enum { RSA, DSA } type; bool encrypted; ssh2_userkey *ret = NULL, *retkey; const ssh_keyalg *alg; strbuf *blob = NULL; if (!key) return NULL; BinarySource_BARE_INIT_PL(src, ptrlen_from_strbuf(key->keyblob)); if (get_uint32(src) != SSHCOM_MAGIC_NUMBER) { errmsg = "key does not begin with magic number"; goto error; } get_uint32(src); /* skip length field */ /* * Determine the key type. */ str = get_string(src); if (str.len > sizeof(prefix_rsa) - 1 && !memcmp(str.ptr, prefix_rsa, sizeof(prefix_rsa) - 1)) { type = RSA; } else if (str.len > sizeof(prefix_dsa) - 1 && !memcmp(str.ptr, prefix_dsa, sizeof(prefix_dsa) - 1)) { type = DSA; } else { errmsg = "key is of unknown type"; goto error; } /* * Determine the cipher type. */ str = get_string(src); if (ptrlen_eq_string(str, "none")) encrypted = false; else if (ptrlen_eq_string(str, "3des-cbc")) encrypted = true; else { errmsg = "key encryption is of unknown type"; goto error; } /* * Get hold of the encrypted part of the key. */ ciphertext = get_string(src); if (ciphertext.len == 0) { errmsg = "no key data found"; goto error; } /* * Decrypt it if necessary. */ if (encrypted) { /* * Derive encryption key from passphrase and iv/salt: * * - let block A equal MD5(passphrase) * - let block B equal MD5(passphrase || A) * - block C would be MD5(passphrase || A || B) and so on * - encryption key is the first N bytes of A || B */ unsigned char keybuf[32], iv[8]; if (ciphertext.len % 8 != 0) { errmsg = "encrypted part of key is not a multiple of cipher block" " size"; goto error; } sshcom_derivekey(ptrlen_from_asciz(passphrase), keybuf); /* * Now decrypt the key blob in place (casting away const from * ciphertext being a ptrlen). */ memset(iv, 0, sizeof(iv)); des3_decrypt_pubkey_ossh(keybuf, iv, (char *)ciphertext.ptr, ciphertext.len); 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; } /* * Expect the ciphertext to be formatted as a containing string, * and reinitialise src to start parsing the inside of that string. */ BinarySource_BARE_INIT_PL(src, ciphertext); str = get_string(src); if (get_err(src)) { errmsg = "containing string was ill-formed"; goto error; } BinarySource_BARE_INIT_PL(src, str); /* * Now we break down into RSA versus DSA. In either case we'll * construct public and private blobs in our own format, and * end up feeding them to ssh_key_new_priv(). */ blob = strbuf_new_nm(); if (type == RSA) { ptrlen n, e, d, u, p, q; e = get_mp_sshcom_as_string(src); d = get_mp_sshcom_as_string(src); n = get_mp_sshcom_as_string(src); u = get_mp_sshcom_as_string(src); p = get_mp_sshcom_as_string(src); q = get_mp_sshcom_as_string(src); if (get_err(src)) { errmsg = "key data did not contain six integers"; goto error; } alg = &ssh_rsa; put_stringz(blob, "ssh-rsa"); put_mp_ssh2_from_string(blob, e); put_mp_ssh2_from_string(blob, n); publen = blob->len; put_mp_ssh2_from_string(blob, d); put_mp_ssh2_from_string(blob, q); put_mp_ssh2_from_string(blob, p); put_mp_ssh2_from_string(blob, u); } else { ptrlen p, q, g, x, y; assert(type == DSA); /* the only other option from the if above */ if (get_uint32(src) != 0) { errmsg = "predefined DSA parameters not supported"; goto error; } p = get_mp_sshcom_as_string(src); g = get_mp_sshcom_as_string(src); q = get_mp_sshcom_as_string(src); y = get_mp_sshcom_as_string(src); x = get_mp_sshcom_as_string(src); if (get_err(src)) { errmsg = "key data did not contain five integers"; goto error; } alg = &ssh_dss; put_stringz(blob, "ssh-dss"); put_mp_ssh2_from_string(blob, p); put_mp_ssh2_from_string(blob, q); put_mp_ssh2_from_string(blob, g); put_mp_ssh2_from_string(blob, y); publen = blob->len; put_mp_ssh2_from_string(blob, x); } retkey = snew(ssh2_userkey); retkey->key = ssh_key_new_priv( alg, make_ptrlen(blob->u, publen), make_ptrlen(blob->u + publen, blob->len - publen)); if (!retkey->key) { 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) { strbuf_free(blob); } strbuf_free(key->keyblob); smemclr(key, sizeof(*key)); sfree(key); if (errmsg_p) *errmsg_p = errmsg; return ret; } static bool sshcom_write( const Filename *filename, ssh2_userkey *key, const char *passphrase) { strbuf *pubblob, *privblob, *outblob; ptrlen numbers[6]; int nnumbers, lenpos, i; bool initial_zero; BinarySource src[1]; const char *type; char *ciphertext; int cipherlen; bool ret = false; FILE *fp; /* * Fetch the key blobs. */ pubblob = strbuf_new(); ssh_key_public_blob(key->key, BinarySink_UPCAST(pubblob)); privblob = strbuf_new_nm(); ssh_key_private_blob(key->key, BinarySink_UPCAST(privblob)); outblob = NULL; /* * Find the sequence of integers to be encoded into the OpenSSH * key blob, and also decide on the header line. */ if (ssh_key_alg(key->key) == &ssh_rsa) { ptrlen n, e, d, p, q, iqmp; /* * These blobs were generated from inside PuTTY, so we needn't * treat them as untrusted. */ BinarySource_BARE_INIT(src, pubblob->u, pubblob->len); get_string(src); /* skip algorithm name */ e = get_string(src); n = get_string(src); BinarySource_BARE_INIT(src, privblob->u, privblob->len); d = get_string(src); p = get_string(src); q = get_string(src); iqmp = get_string(src); assert(!get_err(src)); /* can't go wrong */ numbers[0] = e; numbers[1] = d; numbers[2] = n; numbers[3] = iqmp; numbers[4] = q; numbers[5] = p; nnumbers = 6; initial_zero = false; type = "if-modn{sign{rsa-pkcs1-sha1},encrypt{rsa-pkcs1v2-oaep}}"; } else if (ssh_key_alg(key->key) == &ssh_dss) { ptrlen p, q, g, y, x; /* * These blobs were generated from inside PuTTY, so we needn't * treat them as untrusted. */ BinarySource_BARE_INIT(src, pubblob->u, pubblob->len); get_string(src); /* skip algorithm name */ p = get_string(src); q = get_string(src); g = get_string(src); y = get_string(src); BinarySource_BARE_INIT(src, privblob->u, privblob->len); x = get_string(src); assert(!get_err(src)); /* can't go wrong */ numbers[0] = p; numbers[1] = g; numbers[2] = q; numbers[3] = y; numbers[4] = x; nnumbers = 5; initial_zero = true; type = "dl-modp{sign{dsa-nist-sha1},dh{plain}}"; } else { goto error; /* unsupported key type */ } outblob = strbuf_new_nm(); /* * Create the unencrypted key blob. */ put_uint32(outblob, SSHCOM_MAGIC_NUMBER); put_uint32(outblob, 0); /* length field, fill in later */ put_stringz(outblob, type); put_stringz(outblob, passphrase ? "3des-cbc" : "none"); lenpos = outblob->len; /* remember this position */ put_uint32(outblob, 0); /* encrypted-blob size */ put_uint32(outblob, 0); /* encrypted-payload size */ if (initial_zero) put_uint32(outblob, 0); for (i = 0; i < nnumbers; i++) put_mp_sshcom_from_string(outblob, numbers[i]); /* Now wrap up the encrypted payload. */ PUT_32BIT_MSB_FIRST(outblob->s + lenpos + 4, outblob->len - (lenpos + 8)); /* Pad encrypted blob to a multiple of cipher block size. */ if (passphrase) { int padding = -(outblob->len - (lenpos+4)) & 7; uint8_t padding_buf[8]; random_read(padding_buf, padding); put_data(outblob, padding_buf, padding); } ciphertext = outblob->s + lenpos + 4; cipherlen = outblob->len - (lenpos + 4); assert(!passphrase || cipherlen % 8 == 0); /* Wrap up the encrypted blob string. */ PUT_32BIT_MSB_FIRST(outblob->s + lenpos, cipherlen); /* And finally fill in the total length field. */ PUT_32BIT_MSB_FIRST(outblob->s + 4, outblob->len); /* * Encrypt the key. */ if (passphrase) { unsigned char keybuf[32], iv[8]; sshcom_derivekey(ptrlen_from_asciz(passphrase), keybuf); /* * Now decrypt the key blob. */ memset(iv, 0, sizeof(iv)); des3_encrypt_pubkey_ossh(keybuf, iv, ciphertext, cipherlen); 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 * _doesn't_ escape backslashes or quotes within the string. * Don't ask me, I didn't design it. */ { int slen = 60; /* starts at 60 due to "Comment: " */ char *c = key->comment; while ((int)strlen(c) > slen) { fprintf(fp, "%.*s\\\n", slen, c); c += slen; slen = 70; /* allow 70 chars on subsequent lines */ } fprintf(fp, "%s\"\n", c); } base64_encode(fp, outblob->u, outblob->len, 70); fputs("---- END SSH2 ENCRYPTED PRIVATE KEY ----\n", fp); fclose(fp); ret = true; error: if (outblob) strbuf_free(outblob); if (privblob) strbuf_free(privblob); if (pubblob) strbuf_free(pubblob); return ret; } putty-0.76/ldisc.c0000644000175000017500000003062114072266310011021 00000000000000/* * ldisc.c: PuTTY line discipline. Sits between the input coming * from keypresses in the window, and the output channel leading to * the back end. Implements echo and/or local line editing, * depending on what's currently configured. */ #include #include #include #include "putty.h" #include "terminal.h" #include "ldisc.h" #define ECHOING (ldisc->localecho == FORCE_ON || \ (ldisc->localecho == AUTO && \ (backend_ldisc_option_state(ldisc->backend, LD_ECHO)))) #define EDITING (ldisc->localedit == FORCE_ON || \ (ldisc->localedit == AUTO && \ (backend_ldisc_option_state(ldisc->backend, LD_EDIT)))) static void c_write(Ldisc *ldisc, const void *buf, int len) { seat_stdout(ldisc->seat, buf, len); } static int plen(Ldisc *ldisc, unsigned char c) { if ((c >= 32 && c <= 126) || (c >= 160 && !in_utf(ldisc->term))) return 1; else if (c < 128) return 2; /* ^x for some x */ else if (in_utf(ldisc->term) && c >= 0xC0) return 1; /* UTF-8 introducer character * (FIXME: combining / wide chars) */ else if (in_utf(ldisc->term) && c >= 0x80 && c < 0xC0) return 0; /* UTF-8 followup character */ else return 4; /* hex representation */ } static void pwrite(Ldisc *ldisc, unsigned char c) { if ((c >= 32 && c <= 126) || (!in_utf(ldisc->term) && c >= 0xA0) || (in_utf(ldisc->term) && c >= 0x80)) { c_write(ldisc, &c, 1); } else if (c < 128) { char cc[2]; cc[1] = (c == 127 ? '?' : c + 0x40); cc[0] = '^'; c_write(ldisc, cc, 2); } else { char cc[5]; sprintf(cc, "<%02X>", c); c_write(ldisc, cc, 4); } } static bool char_start(Ldisc *ldisc, unsigned char c) { if (in_utf(ldisc->term)) return (c < 0x80 || c >= 0xC0); else return true; } static void bsb(Ldisc *ldisc, int n) { while (n--) c_write(ldisc, "\010 \010", 3); } #define CTRL(x) (x^'@') #define KCTRL(x) ((x^'@') | 0x100) Ldisc *ldisc_create(Conf *conf, Terminal *term, Backend *backend, Seat *seat) { Ldisc *ldisc = snew(Ldisc); ldisc->buf = NULL; ldisc->buflen = 0; ldisc->bufsiz = 0; ldisc->quotenext = false; ldisc->backend = backend; ldisc->term = term; ldisc->seat = seat; ldisc_configure(ldisc, conf); /* Link ourselves into the backend and the terminal */ if (term) term->ldisc = ldisc; if (backend) backend_provide_ldisc(backend, ldisc); return ldisc; } void ldisc_configure(Ldisc *ldisc, Conf *conf) { ldisc->telnet_keyboard = conf_get_bool(conf, CONF_telnet_keyboard); ldisc->telnet_newline = conf_get_bool(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(Ldisc *ldisc) { if (ldisc->term) ldisc->term->ldisc = NULL; if (ldisc->backend) backend_provide_ldisc(ldisc->backend, NULL); if (ldisc->buf) sfree(ldisc->buf); sfree(ldisc); } void ldisc_echoedit_update(Ldisc *ldisc) { seat_echoedit_update(ldisc->seat, ECHOING, EDITING); } void ldisc_send(Ldisc *ldisc, const void *vbuf, int len, bool interactive) { const char *buf = (const char *)vbuf; int keyflag = 0; assert(ldisc->term); if (interactive) { /* * 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('@'); } /* * Either perform local editing, or just send characters. */ if (EDITING) { while (len--) { int c; c = (unsigned char)(*buf++) + keyflag; if (!interactive && c == '\r') c += KCTRL('@'); switch (ldisc->quotenext ? ' ' : c) { /* * ^h/^?: delete, and output BSBs, to return to * last character boundary (in UTF-8 mode this may * be more than one byte) * ^w: delete, and output BSBs, to return to last * space/nonspace boundary * ^u: delete, and output BSBs, to return to BOL * ^c: Do a ^u then send a telnet IP * ^z: Do a ^u then send a telnet SUSP * ^\: Do a ^u then send a telnet ABORT * ^r: echo "^R\n" and redraw line * ^v: quote next char * ^d: if at BOL, end of file and close connection, * else send line and reset to BOL * ^m: send line-plus-\r\n and reset to BOL */ case KCTRL('H'): case KCTRL('?'): /* backspace/delete */ if (ldisc->buflen > 0) { do { if (ECHOING) bsb(ldisc, plen(ldisc, ldisc->buf[ldisc->buflen - 1])); ldisc->buflen--; } while (!char_start(ldisc, ldisc->buf[ldisc->buflen])); } break; case CTRL('W'): /* delete word */ while (ldisc->buflen > 0) { if (ECHOING) bsb(ldisc, plen(ldisc, ldisc->buf[ldisc->buflen - 1])); ldisc->buflen--; if (ldisc->buflen > 0 && isspace((unsigned char)ldisc->buf[ldisc->buflen-1]) && !isspace((unsigned char)ldisc->buf[ldisc->buflen])) break; } break; case CTRL('U'): /* delete line */ case CTRL('C'): /* Send IP */ case CTRL('\\'): /* Quit */ case CTRL('Z'): /* Suspend */ while (ldisc->buflen > 0) { if (ECHOING) bsb(ldisc, plen(ldisc, ldisc->buf[ldisc->buflen - 1])); ldisc->buflen--; } backend_special(ldisc->backend, SS_EL, 0); /* * 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')) backend_special(ldisc->backend, SS_IP, 0); if (c == CTRL('Z')) backend_special(ldisc->backend, SS_SUSP, 0); if (c == CTRL('\\')) backend_special(ldisc->backend, SS_ABORT, 0); break; case CTRL('R'): /* redraw line */ if (ECHOING) { int i; c_write(ldisc, "^R\r\n", 4); for (i = 0; i < ldisc->buflen; i++) pwrite(ldisc, ldisc->buf[i]); } break; case CTRL('V'): /* quote next char */ ldisc->quotenext = true; break; case CTRL('D'): /* logout or send */ if (ldisc->buflen == 0) { backend_special(ldisc->backend, SS_EOF, 0); } else { backend_send(ldisc->backend, ldisc->buf, ldisc->buflen); ldisc->buflen = 0; } break; /* * This particularly hideous bit of code from RDB * allows ordinary ^M^J to do the same thing as * magic-^M when in Raw protocol. The line `case * KCTRL('M'):' is _inside_ the if block. Thus: * * - receiving regular ^M goes straight to the * default clause and inserts as a literal ^M. * - receiving regular ^J _not_ directly after a * literal ^M (or not in Raw protocol) fails the * if condition, leaps to the bottom of the if, * and falls through into the default clause * again. * - receiving regular ^J just after a literal ^M * in Raw protocol passes the if condition, * deletes the literal ^M, and falls through * into the magic-^M code * - 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) backend_send(ldisc->backend, ldisc->buf, ldisc->buflen); if (ldisc->protocol == PROT_RAW) backend_send(ldisc->backend, "\r\n", 2); else if (ldisc->protocol == PROT_TELNET && ldisc->telnet_newline) backend_special(ldisc->backend, SS_EOL, 0); else backend_send(ldisc->backend, "\r", 1); if (ECHOING) c_write(ldisc, "\r\n", 2); ldisc->buflen = 0; break; } /* FALLTHROUGH */ default: /* get to this label from ^V handler */ default_case: sgrowarray(ldisc->buf, ldisc->bufsiz, ldisc->buflen); ldisc->buf[ldisc->buflen++] = c; if (ECHOING) pwrite(ldisc, (unsigned char) c); ldisc->quotenext = false; break; } } } else { if (ldisc->buflen != 0) { backend_send(ldisc->backend, ldisc->buf, ldisc->buflen); while (ldisc->buflen > 0) { 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) backend_special(ldisc->backend, SS_EOL, 0); else backend_send(ldisc->backend, "\r", 1); break; case CTRL('?'): case CTRL('H'): if (ldisc->telnet_keyboard) { backend_special(ldisc->backend, SS_EC, 0); break; } case CTRL('C'): if (ldisc->telnet_keyboard) { backend_special(ldisc->backend, SS_IP, 0); break; } case CTRL('Z'): if (ldisc->telnet_keyboard) { backend_special(ldisc->backend, SS_SUSP, 0); break; } default: backend_send(ldisc->backend, buf, len); break; } } else backend_send(ldisc->backend, buf, len); } } } putty-0.76/ldisc.h0000644000175000017500000000112114072266310011017 00000000000000/* * 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 struct Ldisc_tag { Terminal *term; Backend *backend; Seat *seat; /* * Values cached out of conf. */ bool telnet_keyboard, telnet_newline; int protocol, localecho, localedit; char *buf; size_t buflen, bufsiz; bool quotenext; }; #endif /* PUTTY_LDISC_H */ putty-0.76/licence.pl0000644000175000017500000000540414072266310011517 00000000000000#!/usr/bin/env perl -w # This script generates licence.h (containing the PuTTY licence in the # form of macros expanding to C string literals) from the LICENCE # master file. It also regenerates the licence-related Halibut input # files. use File::Basename; # Read the input file. $infile = "LICENCE"; open my $in, $infile or die "$infile: open: $!\n"; my @lines = (); while (<$in>) { chomp; push @lines, $_; } close $in; # Format into paragraphs. my @paras = (); my $para = undef; for my $line (@lines) { if ($line eq "") { $para = undef; } elsif (!defined $para) { push @paras, $line; $para = \$paras[$#paras]; } else { $$para .= " " . $line; } } # Get the copyright years and short form of copyright holder. die "bad format of first paragraph\n" unless $paras[0] =~ m!copyright ([^\.]*)\.!i; $shortdetails = $1; # Write out licence.h. $outfile = "licence.h"; open my $out, ">", $outfile or die "$outfile: open: $!\n"; select $out; print "/*\n"; print " * $outfile - macro definitions for the PuTTY licence.\n"; print " *\n"; print " * Generated by @{[basename __FILE__]} from $infile.\n"; print " * You should edit those files rather than editing this one.\n"; print " */\n"; print "\n"; print "#define LICENCE_TEXT(parsep) \\\n"; for my $i (0..$#paras) { my $lit = &stringlit($paras[$i]); print " parsep \\\n" if $i > 0; print " \"$lit\""; print " \\" if $i < $#paras; print "\n"; } print "\n"; printf "#define SHORT_COPYRIGHT_DETAILS \"%s\"\n", &stringlit($shortdetails); sub stringlit { my ($lit) = @_; $lit =~ s!\\!\\\\!g; $lit =~ s!"!\\"!g; return $lit; } close $out; # Write out doc/licence.but. $outfile = "doc/licence.but"; open $out, ">", $outfile or die "$outfile: open: $!\n"; select $out; print "\\# Generated by @{[basename __FILE__]} from $infile.\n"; print "\\# You should edit those files rather than editing this one.\n\n"; print "\\A{licence} PuTTY \\ii{Licence}\n\n"; for my $i (0..$#paras) { my $para = &halibutescape($paras[$i]); if ($i == 0) { $para =~ s!copyright!\\i{copyright}!; # index term in paragraph 1 } print "$para\n\n"; } close $out; # And write out doc/copy.but, which defines a macro used in the manual # preamble blurb. $outfile = "doc/copy.but"; open $out, ">", $outfile or die "$outfile: open: $!\n"; select $out; print "\\# Generated by @{[basename __FILE__]} from $infile.\n"; print "\\# You should edit those files rather than editing this one.\n\n"; printf "\\define{shortcopyrightdetails} %s\n\n", &halibutescape($shortdetails); close $out; sub halibutescape { my ($text) = @_; $text =~ s![\\{}]!\\$&!g; # Halibut escaping $text =~ s!"([^"]*)"!\\q{$1}!g; # convert quoted strings to \q{} return $text; } putty-0.76/logging.c0000644000175000017500000003522114072266310011352 00000000000000/* * Session logging. */ #include #include #include #include #include #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; LogPolicy *lp; Conf *conf; int logtype; /* cached out of conf */ }; static Filename *xlatlognam(Filename *s, char *hostname, int port, 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. */ static void logwrite(LogContext *ctx, ptrlen data) { /* * In state L_CLOSED, we call logfopen, which will set the state * to one of L_OPENING, L_OPEN or L_ERROR. Hence we process all of * those three _after_ processing L_CLOSED. */ if (ctx->state == L_CLOSED) logfopen(ctx); if (ctx->state == L_OPENING) { bufchain_add(&ctx->queue, data.ptr, data.len); } else if (ctx->state == L_OPEN) { assert(ctx->lgfp); if (fwrite(data.ptr, 1, data.len, ctx->lgfp) < data.len) { logfclose(ctx); ctx->state = L_ERROR; lp_eventlog(ctx->lp, "Disabled writing session log " "due to error while writing"); } } /* else L_ERROR, so ignore the write */ } /* * Convenience wrapper on logwrite() which printf-formats the * string. */ static PRINTF_LIKE(2, 3) void logprintf(LogContext *ctx, const char *fmt, ...) { va_list ap; char *data; va_start(ap, fmt); data = dupvprintf(fmt, ap); va_end(ap); logwrite(ctx, ptrlen_from_asciz(data)); sfree(data); } /* * Flush any open log file. */ void logflush(LogContext *ctx) { if (ctx->logtype > 0) if (ctx->state == L_OPEN) fflush(ctx->lgfp); } static void logfopen_callback(void *vctx, int mode) { LogContext *ctx = (LogContext *)vctx; char buf[256], *event; struct tm tm; const char *fmode; bool shout = false; if (mode == 0) { ctx->state = L_ERROR; /* disable logging */ } else { fmode = (mode == 1 ? "ab" : "wb"); ctx->lgfp = f_open(ctx->currlogfilename, fmode, false); if (ctx->lgfp) { ctx->state = L_OPEN; } else { ctx->state = L_ERROR; shout = true; } } if (ctx->state == L_OPEN && conf_get_bool(ctx->conf, CONF_logheader)) { /* Write header line into log file. */ tm = ltime(); strftime(buf, 24, "%Y.%m.%d %H:%M:%S", &tm); logprintf(ctx, "=~=~=~=~=~=~=~=~=~=~=~= PuTTY log %s" " =~=~=~=~=~=~=~=~=~=~=~=\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)); lp_eventlog(ctx->lp, event); if (shout) { /* * If we failed to open the log file due to filesystem error * (as opposed to user action such as clicking Cancel in the * askappend box), we should log it more prominently. */ lp_logging_error(ctx->lp, event); } sfree(event); /* * Having either succeeded or failed in opening the log file, * we should write any queued data out. */ assert(ctx->state != L_OPENING); /* make _sure_ it won't be requeued */ while (bufchain_size(&ctx->queue)) { ptrlen data = bufchain_prefix(&ctx->queue); logwrite(ctx, data); bufchain_consume(&ctx->queue, data.len); } logflush(ctx); } /* * Open the log file. Takes care of detecting an already-existing * file and asking the user whether they want to append, overwrite * or cancel logging. */ void logfopen(LogContext *ctx) { 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), conf_get_int(ctx->conf, CONF_port), &tm); if (open_for_write_would_lose_data(ctx->currlogfilename)) { int logxfovr = conf_get_int(ctx->conf, CONF_logxfovr); if (logxfovr != LGXF_ASK) { mode = ((logxfovr == LGXF_OVR) ? 2 : 1); } else mode = lp_askappend(ctx->lp, ctx->currlogfilename, logfopen_callback, ctx); } else mode = 2; /* create == overwrite */ if (mode < 0) ctx->state = L_OPENING; else logfopen_callback(ctx, mode); /* open the file */ } void logfclose(LogContext *ctx) { if (ctx->lgfp) { fclose(ctx->lgfp); ctx->lgfp = NULL; } ctx->state = L_CLOSED; } /* * Log session traffic. */ void logtraffic(LogContext *ctx, unsigned char c, int logmode) { if (ctx->logtype > 0) { if (ctx->logtype == logmode) logwrite(ctx, make_ptrlen(&c, 1)); } } static void logevent_internal(LogContext *ctx, const char *event) { if (ctx->logtype == LGTYP_PACKETS || ctx->logtype == LGTYP_SSHRAW) { logprintf(ctx, "Event Log: %s\r\n", event); logflush(ctx); } lp_eventlog(ctx->lp, event); } void logevent(LogContext *ctx, const char *event) { if (!ctx) return; /* * Replace newlines in Event Log messages with spaces. (Sometimes * the same message string is reused for the Event Log and a GUI * dialog box; newlines are sometimes appropriate in the latter, * but never in the former.) */ if (strchr(event, '\n') || strchr(event, '\r')) { char *dup = dupstr(event); char *p = dup, *q = dup; while (*p) { if (*p == '\r' || *p == '\n') { do { p++; } while (*p == '\r' || *p == '\n'); *q++ = ' '; } else { *q++ = *p++; } } *q = '\0'; logevent_internal(ctx, dup); sfree(dup); } else { logevent_internal(ctx, event); } } void logevent_and_free(LogContext *ctx, char *event) { logevent(ctx, event); sfree(event); } void logeventvf(LogContext *ctx, const char *fmt, va_list ap) { logevent_and_free(ctx, dupvprintf(fmt, ap)); } void logeventf(LogContext *ctx, const char *fmt, ...) { va_list ap; va_start(ap, fmt); logeventvf(ctx, fmt, ap); va_end(ap); } /* * 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(LogContext *ctx, int direction, int type, const char *texttype, const void *data, size_t len, int n_blanks, const struct logblank_t *blanks, const unsigned long *seq, unsigned downstream_id, const char *additional_log_text) { char dumpdata[128], smalldata[5]; size_t 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) { int blktype; /* Move to a current entry in the blanking array. */ while ((b < n_blanks) && (p >= blanks[b].offset + blanks[b].len)) b++; /* Work out what type of blanking to apply to * this byte. */ blktype = PKTLOG_EMIT; /* default */ if ((b < n_blanks) && (p >= blanks[b].offset) && (p < blanks[b].offset + blanks[b].len)) blktype = blanks[b].type; /* If we're about to stop omitting, it's time to say how * much we omitted. */ if ((blktype != PKTLOG_OMIT) && omitted) { logprintf(ctx, " (%"SIZEu" byte%s omitted)\r\n", omitted, (omitted==1?"":"s")); omitted = 0; } /* (Re-)initialise dumpdata as necessary * (start of row, or if we've just stopped omitting) */ if (!output_pos && !omitted) sprintf(dumpdata, " %08"SIZEx"%*s\r\n", p-(p%16), 1+3*16+2+16, ""); /* Deal with the current byte. */ if (blktype == PKTLOG_OMIT) { omitted++; } else { int c; if (blktype == PKTLOG_BLANK) { c = 'X'; sprintf(smalldata, "XX"); } else { /* PKTLOG_EMIT */ c = ((const unsigned char *)data)[p]; sprintf(smalldata, "%02x", c); } dumpdata[10+2+3*(p%16)] = smalldata[0]; dumpdata[10+2+3*(p%16)+1] = smalldata[1]; dumpdata[10+1+3*16+2+(p%16)] = (c >= 0x20 && c < 0x7F ? c : '.'); output_pos = (p%16) + 1; } p++; /* Flush row if necessary */ if (((p % 16) == 0) || (p == len) || omitted) { if (output_pos) { strcpy(dumpdata + 10+1+3*16+2+output_pos, "\r\n"); logwrite(ctx, ptrlen_from_asciz(dumpdata)); output_pos = 0; } } } /* Tidy up */ if (omitted) logprintf(ctx, " (%"SIZEu" byte%s omitted)\r\n", omitted, (omitted==1?"":"s")); logflush(ctx); } LogContext *log_init(LogPolicy *lp, Conf *conf) { LogContext *ctx = snew(LogContext); ctx->lgfp = NULL; ctx->state = L_CLOSED; ctx->lp = lp; 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(LogContext *ctx) { logfclose(ctx); bufchain_clear(&ctx->queue); if (ctx->currlogfilename) filename_free(ctx->currlogfilename); conf_free(ctx->conf); sfree(ctx); } void log_reconfig(LogContext *ctx, Conf *conf) { bool 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": "&&":& */ static Filename *xlatlognam(Filename *src, char *hostname, int port, struct tm *tm) { char buf[32], *bufp; int size; strbuf *buffer; const char *s; Filename *ret; buffer = strbuf_new(); s = filename_to_str(src); while (*s) { bool sanitise = false; /* Let (bufp, len) be the string to append. */ bufp = buf; /* don't usually override this */ if (*s == '&') { char c; s++; size = 0; if (*s) switch (c = *s++, tolower((unsigned char)c)) { case 'y': size = strftime(buf, sizeof(buf), "%Y", tm); break; case 'm': size = strftime(buf, sizeof(buf), "%m", tm); break; case 'd': size = strftime(buf, sizeof(buf), "%d", tm); break; case 't': size = strftime(buf, sizeof(buf), "%H%M%S", tm); break; case 'h': bufp = hostname; size = strlen(bufp); break; case 'p': size = sprintf(buf, "%d", port); break; default: buf[0] = '&'; size = 1; if (c != '&') buf[size++] = c; } /* Never allow path separators - or any other illegal * filename character - to come out of any of these * auto-format directives. E.g. 'hostname' can contain * colons, if it's an IPv6 address, and colons aren't * legal in filenames on Windows. */ sanitise = true; } else { buf[0] = *s++; size = 1; } while (size-- > 0) { char c = *bufp++; if (sanitise) c = filename_char_sanitise(c); put_byte(buffer, c); } } ret = filename_from_str(buffer->s); strbuf_free(buffer); return ret; } putty-0.76/mainchan.c0000644000175000017500000004134714072266310011510 00000000000000/* * SSH main session channel handling. */ #include #include #include #include "putty.h" #include "ssh.h" #include "sshppl.h" #include "sshchan.h" static void mainchan_free(Channel *chan); static void mainchan_open_confirmation(Channel *chan); static void mainchan_open_failure(Channel *chan, const char *errtext); static size_t mainchan_send( Channel *chan, bool is_stderr, const void *, size_t); static void mainchan_send_eof(Channel *chan); static void mainchan_set_input_wanted(Channel *chan, bool wanted); static char *mainchan_log_close_msg(Channel *chan); static bool mainchan_rcvd_exit_status(Channel *chan, int status); static bool mainchan_rcvd_exit_signal( Channel *chan, ptrlen signame, bool core_dumped, ptrlen msg); static bool mainchan_rcvd_exit_signal_numeric( Channel *chan, int signum, bool core_dumped, ptrlen msg); static void mainchan_request_response(Channel *chan, bool success); static const ChannelVtable mainchan_channelvt = { .free = mainchan_free, .open_confirmation = mainchan_open_confirmation, .open_failed = mainchan_open_failure, .send = mainchan_send, .send_eof = mainchan_send_eof, .set_input_wanted = mainchan_set_input_wanted, .log_close_msg = mainchan_log_close_msg, .want_close = chan_default_want_close, .rcvd_exit_status = mainchan_rcvd_exit_status, .rcvd_exit_signal = mainchan_rcvd_exit_signal, .rcvd_exit_signal_numeric = mainchan_rcvd_exit_signal_numeric, .run_shell = chan_no_run_shell, .run_command = chan_no_run_command, .run_subsystem = chan_no_run_subsystem, .enable_x11_forwarding = chan_no_enable_x11_forwarding, .enable_agent_forwarding = chan_no_enable_agent_forwarding, .allocate_pty = chan_no_allocate_pty, .set_env = chan_no_set_env, .send_break = chan_no_send_break, .send_signal = chan_no_send_signal, .change_window_size = chan_no_change_window_size, .request_response = mainchan_request_response, }; typedef enum MainChanType { MAINCHAN_SESSION, MAINCHAN_DIRECT_TCPIP } MainChanType; struct mainchan { SshChannel *sc; Conf *conf; PacketProtocolLayer *ppl; ConnectionLayer *cl; MainChanType type; bool is_simple; bool req_x11, req_agent, req_pty, req_cmd_primary, req_cmd_fallback; int n_req_env, n_env_replies, n_env_fails; bool eof_pending, eof_sent, got_pty, ready; int term_width, term_height; Channel chan; }; mainchan *mainchan_new( PacketProtocolLayer *ppl, ConnectionLayer *cl, Conf *conf, int term_width, int term_height, bool is_simple, SshChannel **sc_out) { mainchan *mc; if (conf_get_bool(conf, CONF_ssh_no_shell)) return NULL; /* no main channel at all */ mc = snew(mainchan); memset(mc, 0, sizeof(mainchan)); mc->ppl = ppl; mc->cl = cl; mc->conf = conf_copy(conf); mc->term_width = term_width; mc->term_height = term_height; mc->is_simple = is_simple; mc->sc = NULL; mc->chan.vt = &mainchan_channelvt; mc->chan.initial_fixed_window_size = 0; if (*conf_get_str(mc->conf, CONF_ssh_nc_host)) { const char *host = conf_get_str(mc->conf, CONF_ssh_nc_host); int port = conf_get_int(mc->conf, CONF_ssh_nc_port); mc->sc = ssh_lportfwd_open(cl, host, port, "main channel", NULL, &mc->chan); mc->type = MAINCHAN_DIRECT_TCPIP; } else { mc->sc = ssh_session_open(cl, &mc->chan); mc->type = MAINCHAN_SESSION; } if (sc_out) *sc_out = mc->sc; return mc; } static void mainchan_free(Channel *chan) { assert(chan->vt == &mainchan_channelvt); mainchan *mc = container_of(chan, mainchan, chan); conf_free(mc->conf); sfree(mc); } static void mainchan_try_fallback_command(mainchan *mc); static void mainchan_ready(mainchan *mc); static void mainchan_open_confirmation(Channel *chan) { mainchan *mc = container_of(chan, mainchan, chan); PacketProtocolLayer *ppl = mc->ppl; /* for ppl_logevent */ seat_update_specials_menu(mc->ppl->seat); ppl_logevent("Opened main channel"); if (mc->is_simple) sshfwd_hint_channel_is_simple(mc->sc); if (mc->type == MAINCHAN_SESSION) { /* * Send the CHANNEL_REQUESTS for the main session channel. */ char *key, *val, *cmd; struct X11Display *x11disp; struct X11FakeAuth *x11auth; bool retry_cmd_now = false; if (conf_get_bool(mc->conf, CONF_x11_forward)) { char *x11_setup_err; if ((x11disp = x11_setup_display( conf_get_str(mc->conf, CONF_x11_display), mc->conf, &x11_setup_err)) == NULL) { ppl_logevent("X11 forwarding not enabled: unable to" " initialise X display: %s", x11_setup_err); sfree(x11_setup_err); } else { x11auth = ssh_add_x11_display( mc->cl, conf_get_int(mc->conf, CONF_x11_auth), x11disp); sshfwd_request_x11_forwarding( mc->sc, true, x11auth->protoname, x11auth->datastring, x11disp->screennum, false); mc->req_x11 = true; } } if (ssh_agent_forwarding_permitted(mc->cl)) { sshfwd_request_agent_forwarding(mc->sc, true); mc->req_agent = true; } if (!conf_get_bool(mc->conf, CONF_nopty)) { sshfwd_request_pty( mc->sc, true, mc->conf, mc->term_width, mc->term_height); mc->req_pty = true; } for (val = conf_get_str_strs(mc->conf, CONF_environmt, NULL, &key); val != NULL; val = conf_get_str_strs(mc->conf, CONF_environmt, key, &key)) { sshfwd_send_env_var(mc->sc, true, key, val); mc->n_req_env++; } if (mc->n_req_env) ppl_logevent("Sent %d environment variables", mc->n_req_env); cmd = conf_get_str(mc->conf, CONF_remote_cmd); if (conf_get_bool(mc->conf, CONF_ssh_subsys)) { retry_cmd_now = !sshfwd_start_subsystem(mc->sc, true, cmd); } else if (*cmd) { sshfwd_start_command(mc->sc, true, cmd); } else { sshfwd_start_shell(mc->sc, true); } if (retry_cmd_now) mainchan_try_fallback_command(mc); else mc->req_cmd_primary = true; } else { ssh_set_ldisc_option(mc->cl, LD_ECHO, true); ssh_set_ldisc_option(mc->cl, LD_EDIT, true); mainchan_ready(mc); } } static void mainchan_try_fallback_command(mainchan *mc) { const char *cmd = conf_get_str(mc->conf, CONF_remote_cmd2); if (conf_get_bool(mc->conf, CONF_ssh_subsys2)) { sshfwd_start_subsystem(mc->sc, true, cmd); } else { sshfwd_start_command(mc->sc, true, cmd); } mc->req_cmd_fallback = true; } static void mainchan_request_response(Channel *chan, bool success) { assert(chan->vt == &mainchan_channelvt); mainchan *mc = container_of(chan, mainchan, chan); PacketProtocolLayer *ppl = mc->ppl; /* for ppl_logevent */ if (mc->req_x11) { mc->req_x11 = false; if (success) { ppl_logevent("X11 forwarding enabled"); ssh_enable_x_fwd(mc->cl); } else { ppl_logevent("X11 forwarding refused"); } return; } if (mc->req_agent) { mc->req_agent = false; if (success) { ppl_logevent("Agent forwarding enabled"); } else { ppl_logevent("Agent forwarding refused"); } return; } if (mc->req_pty) { mc->req_pty = false; if (success) { ppl_logevent("Allocated pty"); mc->got_pty = true; } else { ppl_logevent("Server refused to allocate pty"); ppl_printf("Server refused to allocate pty\r\n"); ssh_set_ldisc_option(mc->cl, LD_ECHO, true); ssh_set_ldisc_option(mc->cl, LD_EDIT, true); } return; } if (mc->n_env_replies < mc->n_req_env) { int j = mc->n_env_replies++; if (!success) { ppl_logevent("Server refused to set environment variable %s", conf_get_str_nthstrkey(mc->conf, CONF_environmt, j)); mc->n_env_fails++; } if (mc->n_env_replies == mc->n_req_env) { if (mc->n_env_fails == 0) { ppl_logevent("All environment variables successfully set"); } else if (mc->n_env_fails == mc->n_req_env) { ppl_logevent("All environment variables refused"); ppl_printf("Server refused to set environment " "variables\r\n"); } else { ppl_printf("Server refused to set all environment " "variables\r\n"); } } return; } if (mc->req_cmd_primary) { mc->req_cmd_primary = false; if (success) { ppl_logevent("Started a shell/command"); mainchan_ready(mc); } else if (*conf_get_str(mc->conf, CONF_remote_cmd2)) { ppl_logevent("Primary command failed; attempting fallback"); mainchan_try_fallback_command(mc); } else { /* * If there's no remote_cmd2 configured, then we have no * fallback command, so we've run out of options. */ ssh_sw_abort(mc->ppl->ssh, "Server refused to start a shell/command"); } return; } if (mc->req_cmd_fallback) { mc->req_cmd_fallback = false; if (success) { ppl_logevent("Started a shell/command"); ssh_got_fallback_cmd(mc->ppl->ssh); mainchan_ready(mc); } else { ssh_sw_abort(mc->ppl->ssh, "Server refused to start a shell/command"); } return; } } static void mainchan_ready(mainchan *mc) { mc->ready = true; ssh_set_wants_user_input(mc->cl, true); ssh_ppl_got_user_input(mc->ppl); /* in case any is already queued */ /* If an EOF arrived before we were ready, handle it now. */ if (mc->eof_pending) { mc->eof_pending = false; mainchan_special_cmd(mc, SS_EOF, 0); } ssh_ldisc_update(mc->ppl->ssh); queue_idempotent_callback(&mc->ppl->ic_process_queue); } static void mainchan_open_failure(Channel *chan, const char *errtext) { assert(chan->vt == &mainchan_channelvt); mainchan *mc = container_of(chan, mainchan, chan); ssh_sw_abort_deferred(mc->ppl->ssh, "Server refused to open main channel: %s", errtext); } static size_t mainchan_send(Channel *chan, bool is_stderr, const void *data, size_t length) { assert(chan->vt == &mainchan_channelvt); mainchan *mc = container_of(chan, mainchan, chan); return seat_output(mc->ppl->seat, is_stderr, data, length); } static void mainchan_send_eof(Channel *chan) { assert(chan->vt == &mainchan_channelvt); mainchan *mc = container_of(chan, mainchan, chan); PacketProtocolLayer *ppl = mc->ppl; /* for ppl_logevent */ if (!mc->eof_sent && (seat_eof(mc->ppl->seat) || mc->got_pty)) { /* * Either seat_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(mc->sc); ppl_logevent("Sent EOF message"); mc->eof_sent = true; ssh_set_wants_user_input(mc->cl, false); /* stop reading from stdin */ } } static void mainchan_set_input_wanted(Channel *chan, bool wanted) { assert(chan->vt == &mainchan_channelvt); mainchan *mc = container_of(chan, mainchan, chan); /* * This is the main channel of the SSH session, i.e. the one tied * to the standard input (or GUI) of the primary SSH client user * interface. So ssh->send_ok is how we control whether we're * reading from that input. */ ssh_set_wants_user_input(mc->cl, wanted); } static char *mainchan_log_close_msg(Channel *chan) { return dupstr("Main session channel closed"); } static bool mainchan_rcvd_exit_status(Channel *chan, int status) { assert(chan->vt == &mainchan_channelvt); mainchan *mc = container_of(chan, mainchan, chan); PacketProtocolLayer *ppl = mc->ppl; /* for ppl_logevent */ ssh_got_exitcode(mc->ppl->ssh, status); ppl_logevent("Session sent command exit status %d", status); return true; } static void mainchan_log_exit_signal_common( mainchan *mc, const char *sigdesc, bool core_dumped, ptrlen msg) { PacketProtocolLayer *ppl = mc->ppl; /* for ppl_logevent */ const char *core_msg = core_dumped ? " (core dumped)" : ""; const char *msg_pre = (msg.len ? " (" : ""); const char *msg_post = (msg.len ? ")" : ""); ppl_logevent("Session exited on %s%s%s%.*s%s", sigdesc, core_msg, msg_pre, PTRLEN_PRINTF(msg), msg_post); } static bool mainchan_rcvd_exit_signal( Channel *chan, ptrlen signame, bool core_dumped, ptrlen msg) { assert(chan->vt == &mainchan_channelvt); mainchan *mc = container_of(chan, mainchan, chan); int exitcode; char *signame_str; /* * Translate the signal description back into a locally meaningful * number, or 128 if the string didn't match any we recognise. */ exitcode = 128; #define SIGNAL_SUB(s) \ if (ptrlen_eq_string(signame, #s)) \ exitcode = 128 + SIG ## s; #define SIGNAL_MAIN(s, text) SIGNAL_SUB(s) #define SIGNALS_LOCAL_ONLY #include "sshsignals.h" #undef SIGNAL_SUB #undef SIGNAL_MAIN #undef SIGNALS_LOCAL_ONLY ssh_got_exitcode(mc->ppl->ssh, exitcode); if (exitcode == 128) signame_str = dupprintf("unrecognised signal \"%.*s\"", PTRLEN_PRINTF(signame)); else signame_str = dupprintf("signal SIG%.*s", PTRLEN_PRINTF(signame)); mainchan_log_exit_signal_common(mc, signame_str, core_dumped, msg); sfree(signame_str); return true; } static bool mainchan_rcvd_exit_signal_numeric( Channel *chan, int signum, bool core_dumped, ptrlen msg) { assert(chan->vt == &mainchan_channelvt); mainchan *mc = container_of(chan, mainchan, chan); char *signum_str; ssh_got_exitcode(mc->ppl->ssh, 128 + signum); signum_str = dupprintf("signal %d", signum); mainchan_log_exit_signal_common(mc, signum_str, core_dumped, msg); sfree(signum_str); return true; } void mainchan_get_specials( mainchan *mc, add_special_fn_t add_special, void *ctx) { /* FIXME: this _does_ depend on whether these services are supported */ add_special(ctx, "Break", SS_BRK, 0); #define SIGNAL_MAIN(name, desc) \ add_special(ctx, "SIG" #name " (" desc ")", SS_SIG ## name, 0); #define SIGNAL_SUB(name) #include "sshsignals.h" #undef SIGNAL_MAIN #undef SIGNAL_SUB add_special(ctx, "More signals", SS_SUBMENU, 0); #define SIGNAL_MAIN(name, desc) #define SIGNAL_SUB(name) \ add_special(ctx, "SIG" #name, SS_SIG ## name, 0); #include "sshsignals.h" #undef SIGNAL_MAIN #undef SIGNAL_SUB add_special(ctx, NULL, SS_EXITMENU, 0); } static const char *ssh_signal_lookup(SessionSpecialCode code) { #define SIGNAL_SUB(name) \ if (code == SS_SIG ## name) return #name; #define SIGNAL_MAIN(name, desc) SIGNAL_SUB(name) #include "sshsignals.h" #undef SIGNAL_MAIN #undef SIGNAL_SUB /* If none of those clauses matched, fail lookup. */ return NULL; } void mainchan_special_cmd(mainchan *mc, SessionSpecialCode code, int arg) { PacketProtocolLayer *ppl = mc->ppl; /* for ppl_logevent */ const char *signame; if (code == SS_EOF) { if (!mc->ready) { /* * Buffer the EOF to send as soon as the main channel is * fully set up. */ mc->eof_pending = true; } else if (!mc->eof_sent) { sshfwd_write_eof(mc->sc); mc->eof_sent = true; } } else if (code == SS_BRK) { sshfwd_send_serial_break( mc->sc, false, 0 /* default break length */); } else if ((signame = ssh_signal_lookup(code)) != NULL) { /* It's a signal. */ sshfwd_send_signal(mc->sc, false, signame); ppl_logevent("Sent signal SIG%s", signame); } } void mainchan_terminal_size(mainchan *mc, int width, int height) { mc->term_width = width; mc->term_height = height; if (mc->req_pty || mc->got_pty) sshfwd_send_terminal_size_change(mc->sc, width, height); } putty-0.76/marshal.c0000644000175000017500000001570714072266310011362 00000000000000#include #include #include #include "marshal.h" #include "misc.h" void BinarySink_put_data(BinarySink *bs, const void *data, size_t len) { bs->write(bs, data, len); } void BinarySink_put_datapl(BinarySink *bs, ptrlen pl) { BinarySink_put_data(bs, pl.ptr, pl.len); } void BinarySink_put_padding(BinarySink *bs, size_t len, unsigned char padbyte) { char buf[16]; memset(buf, padbyte, sizeof(buf)); while (len > 0) { size_t thislen = len < sizeof(buf) ? len : sizeof(buf); bs->write(bs, buf, thislen); len -= thislen; } } void BinarySink_put_byte(BinarySink *bs, unsigned char val) { bs->write(bs, &val, 1); } void BinarySink_put_bool(BinarySink *bs, bool val) { unsigned char cval = val ? 1 : 0; bs->write(bs, &cval, 1); } void BinarySink_put_uint16(BinarySink *bs, unsigned long val) { unsigned char data[2]; PUT_16BIT_MSB_FIRST(data, val); bs->write(bs, data, sizeof(data)); } void BinarySink_put_uint32(BinarySink *bs, unsigned long val) { unsigned char data[4]; PUT_32BIT_MSB_FIRST(data, val); bs->write(bs, data, sizeof(data)); } void BinarySink_put_uint64(BinarySink *bs, uint64_t val) { unsigned char data[8]; PUT_64BIT_MSB_FIRST(data, val); bs->write(bs, data, sizeof(data)); } void BinarySink_put_string(BinarySink *bs, const void *data, size_t len) { /* Check that the string length fits in a uint32, without doing a * potentially implementation-defined shift of more than 31 bits */ assert((len >> 31) < 2); BinarySink_put_uint32(bs, len); bs->write(bs, data, len); } void BinarySink_put_stringpl(BinarySink *bs, ptrlen pl) { BinarySink_put_string(bs, pl.ptr, pl.len); } void BinarySink_put_stringz(BinarySink *bs, const char *str) { BinarySink_put_string(bs, str, strlen(str)); } void BinarySink_put_stringsb(BinarySink *bs, struct strbuf *buf) { BinarySink_put_string(bs, buf->s, buf->len); strbuf_free(buf); } void BinarySink_put_asciz(BinarySink *bs, const char *str) { bs->write(bs, str, strlen(str) + 1); } bool BinarySink_put_pstring(BinarySink *bs, const char *str) { size_t len = strlen(str); if (len > 255) return false; /* can't write a Pascal-style string this long */ BinarySink_put_byte(bs, len); bs->write(bs, str, len); return true; } /* ---------------------------------------------------------------------- */ static bool BinarySource_data_avail(BinarySource *src, size_t wanted) { if (src->err) return false; if (wanted <= src->len - src->pos) return true; src->err = BSE_OUT_OF_DATA; return false; } #define avail(wanted) BinarySource_data_avail(src, wanted) #define advance(dist) (src->pos += dist) #define here ((const void *)((const unsigned char *)src->data + src->pos)) #define consume(dist) \ ((const void *)((const unsigned char *)src->data + \ ((src->pos += dist) - dist))) ptrlen BinarySource_get_data(BinarySource *src, size_t wanted) { if (!avail(wanted)) return make_ptrlen("", 0); return make_ptrlen(consume(wanted), wanted); } unsigned char BinarySource_get_byte(BinarySource *src) { const unsigned char *ucp; if (!avail(1)) return 0; ucp = consume(1); return *ucp; } bool BinarySource_get_bool(BinarySource *src) { const unsigned char *ucp; if (!avail(1)) return false; ucp = consume(1); return *ucp != 0; } unsigned BinarySource_get_uint16(BinarySource *src) { const unsigned char *ucp; if (!avail(2)) return 0; ucp = consume(2); return GET_16BIT_MSB_FIRST(ucp); } unsigned long BinarySource_get_uint32(BinarySource *src) { const unsigned char *ucp; if (!avail(4)) return 0; ucp = consume(4); return GET_32BIT_MSB_FIRST(ucp); } uint64_t BinarySource_get_uint64(BinarySource *src) { const unsigned char *ucp; if (!avail(8)) return 0; ucp = consume(8); return GET_64BIT_MSB_FIRST(ucp); } ptrlen BinarySource_get_string(BinarySource *src) { const unsigned char *ucp; size_t len; if (!avail(4)) return make_ptrlen("", 0); ucp = consume(4); len = GET_32BIT_MSB_FIRST(ucp); if (!avail(len)) return make_ptrlen("", 0); return make_ptrlen(consume(len), len); } const char *BinarySource_get_asciz(BinarySource *src) { const char *start, *end; if (src->err) return ""; start = here; end = memchr(start, '\0', src->len - src->pos); if (!end) { src->err = BSE_OUT_OF_DATA; return ""; } advance(end + 1 - start); return start; } static ptrlen BinarySource_get_chars_internal( BinarySource *src, const char *set, bool include) { const char *start = here; while (avail(1)) { bool present = NULL != strchr(set, *(const char *)consume(0)); if (present != include) break; (void) consume(1); } const char *end = here; return make_ptrlen(start, end - start); } ptrlen BinarySource_get_chars(BinarySource *src, const char *include_set) { return BinarySource_get_chars_internal(src, include_set, true); } ptrlen BinarySource_get_nonchars(BinarySource *src, const char *exclude_set) { return BinarySource_get_chars_internal(src, exclude_set, false); } ptrlen BinarySource_get_chomped_line(BinarySource *src) { const char *start, *end; if (src->err) return make_ptrlen(here, 0); start = here; end = memchr(start, '\n', src->len - src->pos); if (end) advance(end + 1 - start); else advance(src->len - src->pos); end = here; if (end > start && end[-1] == '\n') end--; if (end > start && end[-1] == '\r') end--; return make_ptrlen(start, end - start); } ptrlen BinarySource_get_pstring(BinarySource *src) { const unsigned char *ucp; size_t len; if (!avail(1)) return make_ptrlen("", 0); ucp = consume(1); len = *ucp; if (!avail(len)) return make_ptrlen("", 0); return make_ptrlen(consume(len), len); } void BinarySource_REWIND_TO__(BinarySource *src, size_t pos) { if (pos <= src->len) { src->pos = pos; src->err = BSE_NO_ERROR; /* clear any existing error */ } else { src->pos = src->len; src->err = BSE_OUT_OF_DATA; /* new error if we rewind out of range */ } } static void stdio_sink_write(BinarySink *bs, const void *data, size_t len) { stdio_sink *sink = BinarySink_DOWNCAST(bs, stdio_sink); fwrite(data, 1, len, sink->fp); } void stdio_sink_init(stdio_sink *sink, FILE *fp) { sink->fp = fp; BinarySink_INIT(sink, stdio_sink_write); } static void bufchain_sink_write(BinarySink *bs, const void *data, size_t len) { bufchain_sink *sink = BinarySink_DOWNCAST(bs, bufchain_sink); bufchain_add(sink->ch, data, len); } void bufchain_sink_init(bufchain_sink *sink, bufchain *ch) { sink->ch = ch; BinarySink_INIT(sink, bufchain_sink_write); } putty-0.76/marshal.h0000644000175000017500000003432414072266310011363 00000000000000#ifndef PUTTY_MARSHAL_H #define PUTTY_MARSHAL_H #include "defs.h" #include /* * A sort of 'abstract base class' or 'interface' or 'trait' which is * the common feature of all types that want to accept data formatted * using the SSH binary conventions of uint32, string, mpint etc. */ struct BinarySink { void (*write)(BinarySink *sink, const void *data, size_t len); BinarySink *binarysink_; }; /* * To define a structure type as a valid target for binary formatted * data, put 'BinarySink_IMPLEMENTATION' in its declaration, and when * an instance is set up, use 'BinarySink_INIT' to initialise the * 'base class' state, providing a function pointer to be the * implementation of the write() call above. */ #define BinarySink_IMPLEMENTATION BinarySink binarysink_[1] #define BinarySink_INIT(obj, writefn) \ ((obj)->binarysink_->write = (writefn), \ (obj)->binarysink_->binarysink_ = (obj)->binarysink_) /* * To define a larger structure type as a valid BinarySink in such a * way that it will delegate the write method to some other object, * put 'BinarySink_DELEGATE_IMPLEMENTATION' in its declaration, and * when an instance is set up, use 'BinarySink_DELEGATE_INIT' to point * at the object it wants to delegate to. * * In such a delegated structure, you might sometimes want to have the * delegation stop being valid (e.g. it might be delegating to an * object that only sometimes exists). You can null out the delegate * pointer using BinarySink_DELEGATE_CLEAR. */ #define BinarySink_DELEGATE_IMPLEMENTATION BinarySink *binarysink_ #define BinarySink_DELEGATE_INIT(obj, othersink) \ ((obj)->binarysink_ = BinarySink_UPCAST(othersink)) #define BinarySink_DELEGATE_CLEAR(obj) ((obj)->binarysink_ = NULL) /* * The implementing type's write function will want to downcast its * 'BinarySink *' parameter back to the more specific type. Also, * sometimes you'll want to upcast a pointer to a particular * implementing type into an abstract 'BinarySink *' to pass to * generic subroutines not defined in this file. These macros do that * job. * * Importantly, BinarySink_UPCAST can also be applied to a BinarySink * * itself (and leaves it unchanged). That's achieved by a small * piece of C trickery: implementing structures and the BinarySink * structure itself both contain a field called binarysink_, but in * implementing objects it's a BinarySink[1] whereas in the abstract * type it's a 'BinarySink *' pointing back to the same structure, * meaning that you can say 'foo->binarysink_' in either case and get * a pointer type by different methods. */ #define BinarySink_DOWNCAST(object, type) \ TYPECHECK((object) == ((type *)0)->binarysink_, \ ((type *)(((char *)(object)) - offsetof(type, binarysink_)))) #define BinarySink_UPCAST(object) \ TYPECHECK((object)->binarysink_ == (BinarySink *)0, \ (object)->binarysink_) /* * If you structure-copy an object that's implementing BinarySink, * then that tricky self-pointer in its trait subobject will point to * the wrong place. You could call BinarySink_INIT again, but this * macro is terser and does all that's needed to fix up the copied * object. */ #define BinarySink_COPIED(obj) \ ((obj)->binarysink_->binarysink_ = (obj)->binarysink_) /* * The put_* macros are the main client to this system. Any structure * which implements the BinarySink 'trait' is valid for use as the * first parameter of any of these put_* macros. */ /* Basic big-endian integer types. */ #define put_byte(bs, val) \ BinarySink_put_byte(BinarySink_UPCAST(bs), val) #define put_uint16(bs, val) \ BinarySink_put_uint16(BinarySink_UPCAST(bs), val) #define put_uint32(bs, val) \ BinarySink_put_uint32(BinarySink_UPCAST(bs), val) #define put_uint64(bs, val) \ BinarySink_put_uint64(BinarySink_UPCAST(bs), val) /* SSH booleans, encoded as a single byte storing either 0 or 1. */ #define put_bool(bs, val) \ BinarySink_put_bool(BinarySink_UPCAST(bs), val) /* SSH strings, with a leading uint32 length field. 'stringz' is a * convenience function that takes an ordinary C zero-terminated * string as input. 'stringsb' takes a strbuf * as input, and * finalises it as a side effect (handy for multi-level marshalling in * which you use these same functions to format an inner blob of data * that then gets wrapped into a string container in an outer one). */ #define put_string(bs, val, len) \ BinarySink_put_string(BinarySink_UPCAST(bs),val,len) #define put_stringpl(bs, ptrlen) \ BinarySink_put_stringpl(BinarySink_UPCAST(bs),ptrlen) #define put_stringz(bs, val) \ BinarySink_put_stringz(BinarySink_UPCAST(bs), val) #define put_stringsb(bs, val) \ BinarySink_put_stringsb(BinarySink_UPCAST(bs), val) /* Other string outputs: 'asciz' emits the string data directly into * the output including the terminating \0, and 'pstring' emits the * string in Pascal style with a leading _one_-byte length field. * pstring can fail if the string is too long. */ #define put_asciz(bs, val) \ BinarySink_put_asciz(BinarySink_UPCAST(bs), val) #define put_pstring(bs, val) \ BinarySink_put_pstring(BinarySink_UPCAST(bs), val) /* Multiprecision integers, in both the SSH-1 and SSH-2 formats. */ #define put_mp_ssh1(bs, val) \ BinarySink_put_mp_ssh1(BinarySink_UPCAST(bs), val) #define put_mp_ssh2(bs, val) \ BinarySink_put_mp_ssh2(BinarySink_UPCAST(bs), val) /* Padding with a specified byte. */ #define put_padding(bs, len, padbyte) \ BinarySink_put_padding(BinarySink_UPCAST(bs), len, padbyte) /* Fallback: just emit raw data bytes, using a syntax that matches the * rest of these macros. */ #define put_data(bs, val, len) \ BinarySink_put_data(BinarySink_UPCAST(bs), val, len) #define put_datapl(bs, pl) \ BinarySink_put_datapl(BinarySink_UPCAST(bs), pl) /* * The underlying real C functions that implement most of those * macros. Generally you won't want to call these directly, because * they have such cumbersome names; you call the wrapper macros above * instead. * * A few functions whose wrapper macros are defined above are actually * declared in other headers, so as to guarantee that the * declaration(s) of their other parameter type(s) are in scope. */ void BinarySink_put_data(BinarySink *, const void *data, size_t len); void BinarySink_put_datapl(BinarySink *, ptrlen); void BinarySink_put_padding(BinarySink *, size_t len, unsigned char padbyte); void BinarySink_put_byte(BinarySink *, unsigned char); void BinarySink_put_bool(BinarySink *, bool); void BinarySink_put_uint16(BinarySink *, unsigned long); void BinarySink_put_uint32(BinarySink *, unsigned long); void BinarySink_put_uint64(BinarySink *, uint64_t); void BinarySink_put_string(BinarySink *, const void *data, size_t len); void BinarySink_put_stringpl(BinarySink *, ptrlen); void BinarySink_put_stringz(BinarySink *, const char *str); struct strbuf; void BinarySink_put_stringsb(BinarySink *, struct strbuf *); void BinarySink_put_asciz(BinarySink *, const char *str); bool BinarySink_put_pstring(BinarySink *, const char *str); void BinarySink_put_mp_ssh1(BinarySink *bs, mp_int *x); void BinarySink_put_mp_ssh2(BinarySink *bs, mp_int *x); /* ---------------------------------------------------------------------- */ /* * A complementary trait structure for _un_-marshalling. * * This structure contains client-visible data fields rather than * methods, because that seemed more useful than leaving it totally * opaque. But it's still got the self-pointer system that will allow * the set of get_* macros to target one of these itself or any other * type that 'derives' from it. So, for example, an SSH packet * structure can act as a BinarySource while also having additional * fields like the packet type. */ typedef enum BinarySourceError { BSE_NO_ERROR, BSE_OUT_OF_DATA, BSE_INVALID } BinarySourceError; struct BinarySource { /* * (data, len) is the data block being decoded. pos is the current * position within the block. */ const void *data; size_t pos, len; /* * 'err' indicates whether a decoding error has happened at any * point. Once this has been set to something other than * BSE_NO_ERROR, it shouldn't be changed by any unmarshalling * function. So you can safely do a long sequence of get_foo() * operations and then test err just once at the end, rather than * having to conditionalise every single get. * * The unmarshalling functions should always return some value, * even if a decoding error occurs. Generally on error they'll * return zero (if numeric) or the empty string (if string-based), * or some other appropriate default value for more complicated * types. * * If the usual return value is dynamically allocated (e.g. a * bignum, or a normal C 'char *' string), then the error value is * also dynamic in the same way. So you have to free exactly the * same set of things whether or not there was a decoding error, * which simplifies exit paths - for example, you could call a big * pile of get_foo functions, then put the actual handling of the * results under 'if (!get_err(src))', and then free everything * outside that if. */ BinarySourceError err; /* * Self-pointer for the implicit derivation trick, same as * BinarySink above. */ BinarySource *binarysource_; }; /* * Implementation macros, similar to BinarySink. */ #define BinarySource_IMPLEMENTATION BinarySource binarysource_[1] static inline void BinarySource_INIT__(BinarySource *src, ptrlen data) { src->data = data.ptr; src->len = data.len; src->pos = 0; src->err = BSE_NO_ERROR; src->binarysource_ = src; } #define BinarySource_BARE_INIT_PL(obj, pl) \ TYPECHECK(&(obj)->binarysource_ == (BinarySource **)0, \ BinarySource_INIT__(obj, pl)) #define BinarySource_BARE_INIT(obj, data_, len_) \ BinarySource_BARE_INIT_PL(obj, make_ptrlen(data_, len_)) #define BinarySource_INIT_PL(obj, pl) \ TYPECHECK(&(obj)->binarysource_ == (BinarySource (*)[1])0, \ BinarySource_INIT__(BinarySource_UPCAST(obj), pl)) #define BinarySource_INIT(obj, data_, len_) \ BinarySource_INIT_PL(obj, make_ptrlen(data_, len_)) #define BinarySource_DOWNCAST(object, type) \ TYPECHECK((object) == ((type *)0)->binarysource_, \ ((type *)(((char *)(object)) - offsetof(type, binarysource_)))) #define BinarySource_UPCAST(object) \ TYPECHECK((object)->binarysource_ == (BinarySource *)0, \ (object)->binarysource_) #define BinarySource_COPIED(obj) \ ((obj)->binarysource_->binarysource_ = (obj)->binarysource_) #define BinarySource_REWIND_TO(src, pos) \ BinarySource_REWIND_TO__((src)->binarysource_, pos) #define BinarySource_REWIND(src) \ BinarySource_REWIND_TO__((src)->binarysource_, 0) #define get_data(src, len) \ BinarySource_get_data(BinarySource_UPCAST(src), len) #define get_byte(src) \ BinarySource_get_byte(BinarySource_UPCAST(src)) #define get_bool(src) \ BinarySource_get_bool(BinarySource_UPCAST(src)) #define get_uint16(src) \ BinarySource_get_uint16(BinarySource_UPCAST(src)) #define get_uint32(src) \ BinarySource_get_uint32(BinarySource_UPCAST(src)) #define get_uint64(src) \ BinarySource_get_uint64(BinarySource_UPCAST(src)) #define get_string(src) \ BinarySource_get_string(BinarySource_UPCAST(src)) #define get_asciz(src) \ BinarySource_get_asciz(BinarySource_UPCAST(src)) #define get_chars(src, include) \ BinarySource_get_chars(BinarySource_UPCAST(src), include) #define get_nonchars(src, exclude) \ BinarySource_get_nonchars(BinarySource_UPCAST(src), exclude) #define get_chomped_line(src) \ BinarySource_get_chomped_line(BinarySource_UPCAST(src)) #define get_pstring(src) \ BinarySource_get_pstring(BinarySource_UPCAST(src)) #define get_mp_ssh1(src) \ BinarySource_get_mp_ssh1(BinarySource_UPCAST(src)) #define get_mp_ssh2(src) \ BinarySource_get_mp_ssh2(BinarySource_UPCAST(src)) #define get_rsa_ssh1_pub(src, rsa, order) \ BinarySource_get_rsa_ssh1_pub(BinarySource_UPCAST(src), rsa, order) #define get_rsa_ssh1_priv(src, rsa) \ BinarySource_get_rsa_ssh1_priv(BinarySource_UPCAST(src), rsa) #define get_rsa_ssh1_priv_agent(src) \ BinarySource_get_rsa_ssh1_priv_agent(BinarySource_UPCAST(src)) #define get_err(src) (BinarySource_UPCAST(src)->err) #define get_avail(src) (BinarySource_UPCAST(src)->len - \ BinarySource_UPCAST(src)->pos) #define get_ptr(src) \ ((const void *)( \ (const unsigned char *)(BinarySource_UPCAST(src)->data) + \ BinarySource_UPCAST(src)->pos)) ptrlen BinarySource_get_data(BinarySource *, size_t); unsigned char BinarySource_get_byte(BinarySource *); bool BinarySource_get_bool(BinarySource *); unsigned BinarySource_get_uint16(BinarySource *); unsigned long BinarySource_get_uint32(BinarySource *); uint64_t BinarySource_get_uint64(BinarySource *); ptrlen BinarySource_get_string(BinarySource *); const char *BinarySource_get_asciz(BinarySource *); ptrlen BinarySource_get_chars(BinarySource *, const char *include_set); ptrlen BinarySource_get_nonchars(BinarySource *, const char *exclude_set); ptrlen BinarySource_get_chomped_line(BinarySource *); ptrlen BinarySource_get_pstring(BinarySource *); mp_int *BinarySource_get_mp_ssh1(BinarySource *src); mp_int *BinarySource_get_mp_ssh2(BinarySource *src); void BinarySource_REWIND_TO__(BinarySource *src, size_t pos); /* * A couple of useful standard BinarySink implementations, which live * as sensibly here as anywhere else: one that makes a BinarySink * whose effect is to write to a stdio stream, and one whose effect is * to append to a bufchain. */ struct stdio_sink { FILE *fp; BinarySink_IMPLEMENTATION; }; struct bufchain_sink { bufchain *ch; BinarySink_IMPLEMENTATION; }; void stdio_sink_init(stdio_sink *sink, FILE *fp); void bufchain_sink_init(bufchain_sink *sink, bufchain *ch); #endif /* PUTTY_MARSHAL_H */ putty-0.76/memory.c0000644000175000017500000000564114072266310011237 00000000000000/* * PuTTY's memory allocation wrappers. */ #include #include #include #include "defs.h" #include "puttymem.h" #include "misc.h" void *safemalloc(size_t factor1, size_t factor2, size_t addend) { if (factor1 > SIZE_MAX / factor2) goto fail; size_t product = factor1 * factor2; if (addend > SIZE_MAX) goto fail; if (product > SIZE_MAX - addend) goto fail; size_t size = product + addend; if (size == 0) size = 1; void *p; #ifdef MINEFIELD p = minefield_c_malloc(size); #else p = malloc(size); #endif if (!p) goto fail; return p; fail: out_of_memory(); } void *saferealloc(void *ptr, size_t n, size_t size) { void *p; if (n > INT_MAX / size) { p = NULL; } else { size *= n; if (!ptr) { #ifdef MINEFIELD p = minefield_c_malloc(size); #else p = malloc(size); #endif } else { #ifdef MINEFIELD p = minefield_c_realloc(ptr, size); #else p = realloc(ptr, size); #endif } } if (!p) out_of_memory(); return p; } void safefree(void *ptr) { if (ptr) { #ifdef MINEFIELD minefield_c_free(ptr); #else free(ptr); #endif } } void *safegrowarray(void *ptr, size_t *allocated, size_t eltsize, size_t oldlen, size_t extralen, bool secret) { /* The largest value we can safely multiply by eltsize */ assert(eltsize > 0); size_t maxsize = (~(size_t)0) / eltsize; size_t oldsize = *allocated; /* Range-check the input values */ assert(oldsize <= maxsize); assert(oldlen <= maxsize); assert(extralen <= maxsize - oldlen); /* If the size is already enough, don't bother doing anything! */ if (oldsize > oldlen + extralen) return ptr; /* Find out how much we need to grow the array by. */ size_t increment = (oldlen + extralen) - oldsize; /* Invent a new size. We want to grow the array by at least * 'increment' elements; by at least a fixed number of bytes (to * get things started when sizes are small); and by some constant * factor of its old size (to avoid repeated calls to this * function taking quadratic time overall). */ if (increment < 256 / eltsize) increment = 256 / eltsize; if (increment < oldsize / 16) increment = oldsize / 16; /* But we also can't grow beyond maxsize. */ size_t maxincr = maxsize - oldsize; if (increment > maxincr) increment = maxincr; size_t newsize = oldsize + increment; void *toret; if (secret) { toret = safemalloc(newsize, eltsize, 0); if (oldsize) { memcpy(toret, ptr, oldsize * eltsize); smemclr(ptr, oldsize * eltsize); sfree(ptr); } } else { toret = saferealloc(ptr, newsize, eltsize); } *allocated = newsize; return toret; } putty-0.76/millerrabin.c0000644000175000017500000001516214072266311012227 00000000000000/* * millerrabin.c: Miller-Rabin probabilistic primality testing, as * declared in sshkeygen.h. */ #include #include "ssh.h" #include "sshkeygen.h" #include "mpint.h" #include "mpunsafe.h" /* * The Miller-Rabin primality test is an extension to the Fermat * test. The Fermat test just checks that a^(p-1) == 1 mod p; this * is vulnerable to Carmichael numbers. Miller-Rabin considers how * that 1 is derived as well. * * Lemma: if a^2 == 1 (mod p), and p is prime, then either a == 1 * or a == -1 (mod p). * * Proof: p divides a^2-1, i.e. p divides (a+1)(a-1). Hence, * since p is prime, either p divides (a+1) or p divides (a-1). * But this is the same as saying that either a is congruent to * -1 mod p or a is congruent to +1 mod p. [] * * Comment: This fails when p is not prime. Consider p=mn, so * that mn divides (a+1)(a-1). Now we could have m dividing (a+1) * and n dividing (a-1), without the whole of mn dividing either. * For example, consider a=10 and p=99. 99 = 9 * 11; 9 divides * 10-1 and 11 divides 10+1, so a^2 is congruent to 1 mod p * without a having to be congruent to either 1 or -1. * * So the Miller-Rabin test, as well as considering a^(p-1), * considers a^((p-1)/2), a^((p-1)/4), and so on as far as it can * go. In other words. we write p-1 as q * 2^k, with k as large as * possible (i.e. q must be odd), and we consider the powers * * a^(q*2^0) a^(q*2^1) ... a^(q*2^(k-1)) a^(q*2^k) * i.e. a^((n-1)/2^k) a^((n-1)/2^(k-1)) ... a^((n-1)/2) a^(n-1) * * If p is to be prime, the last of these must be 1. Therefore, by * the above lemma, the one before it must be either 1 or -1. And * _if_ it's 1, then the one before that must be either 1 or -1, * and so on ... In other words, we expect to see a trailing chain * of 1s preceded by a -1. (If we're unlucky, our trailing chain of * 1s will be as long as the list so we'll never get to see what * lies before it. This doesn't count as a test failure because it * hasn't _proved_ that p is not prime.) * * For example, consider a=2 and p=1729. 1729 is a Carmichael * number: although it's not prime, it satisfies a^(p-1) == 1 mod p * for any a coprime to it. So the Fermat test wouldn't have a * problem with it at all, unless we happened to stumble on an a * which had a common factor. * * So. 1729 - 1 equals 27 * 2^6. So we look at * * 2^27 mod 1729 == 645 * 2^108 mod 1729 == 1065 * 2^216 mod 1729 == 1 * 2^432 mod 1729 == 1 * 2^864 mod 1729 == 1 * 2^1728 mod 1729 == 1 * * We do have a trailing string of 1s, so the Fermat test would * have been happy. But this trailing string of 1s is preceded by * 1065; whereas if 1729 were prime, we'd expect to see it preceded * by -1 (i.e. 1728.). Guards! Seize this impostor. * * (If we were unlucky, we might have tried a=16 instead of a=2; * now 16^27 mod 1729 == 1, so we would have seen a long string of * 1s and wouldn't have seen the thing _before_ the 1s. So, just * like the Fermat test, for a given p there may well exist values * of a which fail to show up its compositeness. So we try several, * just like the Fermat test. The difference is that Miller-Rabin * is not _in general_ fooled by Carmichael numbers.) * * Put simply, then, the Miller-Rabin test requires us to: * * 1. write p-1 as q * 2^k, with q odd * 2. compute z = (a^q) mod p. * 3. report success if z == 1 or z == -1. * 4. square z at most k-1 times, and report success if it becomes * -1 at any point. * 5. report failure otherwise. * * (We expect z to become -1 after at most k-1 squarings, because * if it became -1 after k squarings then a^(p-1) would fail to be * 1. And we don't need to investigate what happens after we see a * -1, because we _know_ that -1 squared is 1 modulo anything at * all, so after we've seen a -1 we can be sure of seeing nothing * but 1s.) */ struct MillerRabin { MontyContext *mc; size_t k; mp_int *q; mp_int *two, *pm1, *m_pm1; }; MillerRabin *miller_rabin_new(mp_int *p) { MillerRabin *mr = snew(MillerRabin); assert(mp_hs_integer(p, 2)); assert(mp_get_bit(p, 0) == 1); mr->k = 1; while (!mp_get_bit(p, mr->k)) mr->k++; mr->q = mp_rshift_safe(p, mr->k); mr->two = mp_from_integer(2); mr->pm1 = mp_unsafe_copy(p); mp_sub_integer_into(mr->pm1, mr->pm1, 1); mr->mc = monty_new(p); mr->m_pm1 = monty_import(mr->mc, mr->pm1); return mr; } void miller_rabin_free(MillerRabin *mr) { mp_free(mr->q); mp_free(mr->two); mp_free(mr->pm1); mp_free(mr->m_pm1); monty_free(mr->mc); smemclr(mr, sizeof(*mr)); sfree(mr); } struct mr_result { bool passed; bool potential_primitive_root; }; static struct mr_result miller_rabin_test_inner(MillerRabin *mr, mp_int *w) { /* * Compute w^q mod p. */ mp_int *wqp = monty_pow(mr->mc, w, mr->q); /* * See if this is 1, or if it is -1, or if it becomes -1 * when squared at most k-1 times. */ struct mr_result result; result.passed = false; result.potential_primitive_root = false; if (mp_cmp_eq(wqp, monty_identity(mr->mc))) { result.passed = true; } else { for (size_t i = 0; i < mr->k; i++) { if (mp_cmp_eq(wqp, mr->m_pm1)) { result.passed = true; result.potential_primitive_root = (i == mr->k - 1); break; } if (i == mr->k - 1) break; monty_mul_into(mr->mc, wqp, wqp, wqp); } } mp_free(wqp); return result; } bool miller_rabin_test_random(MillerRabin *mr) { mp_int *mw = mp_random_in_range(mr->two, mr->pm1); struct mr_result result = miller_rabin_test_inner(mr, mw); mp_free(mw); return result.passed; } mp_int *miller_rabin_find_potential_primitive_root(MillerRabin *mr) { while (true) { mp_int *mw = mp_unsafe_shrink(mp_random_in_range(mr->two, mr->pm1)); struct mr_result result = miller_rabin_test_inner(mr, mw); if (result.passed && result.potential_primitive_root) { mp_int *pr = monty_export(mr->mc, mw); mp_free(mw); return pr; } mp_free(mw); if (!result.passed) { return NULL; } } } unsigned miller_rabin_checks_needed(unsigned bits) { /* Table 4.4 from Handbook of Applied Cryptography */ return (bits >= 1300 ? 2 : bits >= 850 ? 3 : bits >= 650 ? 4 : bits >= 550 ? 5 : bits >= 450 ? 6 : bits >= 400 ? 7 : bits >= 350 ? 8 : bits >= 300 ? 9 : bits >= 250 ? 12 : bits >= 200 ? 15 : bits >= 150 ? 18 : 27); } putty-0.76/minibidi.c0000644000175000017500000017525714072266311011527 00000000000000/************************************************************************ * * ------------ * Description: * ------------ * This is an implementation of Unicode's Bidirectional Algorithm * (known as UAX #9). * * http://www.unicode.org/reports/tr9/ * * Author: Ahmad Khalifa * * (www.arabeyes.org - under MIT license) * ************************************************************************/ /* * TODO: * ===== * - Explicit marks need to be handled (they are not 100% now) * - Ligatures */ #include /* definition of wchar_t*/ #include "putty.h" #include "misc.h" #define LMASK 0x3F /* Embedding Level mask */ #define OMASK 0xC0 /* Override mask */ #define OISL 0x80 /* Override is L */ #define OISR 0x40 /* Override is R */ /* For standalone compilation in a testing mode. * Still depends on the PuTTY headers for snewn and sfree, but can avoid * _linking_ with any other PuTTY code. */ #ifdef TEST_GETTYPE #define safemalloc malloc #define safefree free #endif /* Shaping Helpers */ #define STYPE(xh) ((((xh) >= SHAPE_FIRST) && ((xh) <= SHAPE_LAST)) ? \ shapetypes[(xh)-SHAPE_FIRST].type : SU) /*))*/ #define SISOLATED(xh) (shapetypes[(xh)-SHAPE_FIRST].form_b) #define SFINAL(xh) ((xh)+1) #define SINITIAL(xh) ((xh)+2) #define SMEDIAL(ch) ((ch)+3) #define leastGreaterOdd(x) ( ((x)+1) | 1 ) #define leastGreaterEven(x) ( ((x)+2) &~ 1 ) /* function declarations */ static void flipThisRun( bidi_char *from, unsigned char *level, int max, int count); static int findIndexOfRun( unsigned char *level, int start, int count, int tlevel); static unsigned char getType(int ch); static unsigned char setOverrideBits( unsigned char level, unsigned char override); static int getPreviousLevel(unsigned char *level, int from); static void doMirror(unsigned int *ch); /* character types */ enum { L, LRE, LRO, R, AL, RLE, RLO, PDF, EN, ES, ET, AN, CS, NSM, BN, B, S, WS, ON }; /* Shaping Types */ enum { SL, /* Left-Joining, doesn't exist in U+0600 - U+06FF */ SR, /* Right-Joining, ie has Isolated, Final */ SD, /* Dual-Joining, ie has Isolated, Final, Initial, Medial */ SU, /* Non-Joining */ SC /* Join-Causing, like U+0640 (TATWEEL) */ }; typedef struct { char type; wchar_t form_b; } shape_node; /* Kept near the actual table, for verification. */ #define SHAPE_FIRST 0x621 #define SHAPE_LAST (SHAPE_FIRST + lenof(shapetypes) - 1) static const shape_node shapetypes[] = { /* index, Typ, Iso, Ligature Index*/ /* 621 */ {SU, 0xFE80}, /* 622 */ {SR, 0xFE81}, /* 623 */ {SR, 0xFE83}, /* 624 */ {SR, 0xFE85}, /* 625 */ {SR, 0xFE87}, /* 626 */ {SD, 0xFE89}, /* 627 */ {SR, 0xFE8D}, /* 628 */ {SD, 0xFE8F}, /* 629 */ {SR, 0xFE93}, /* 62A */ {SD, 0xFE95}, /* 62B */ {SD, 0xFE99}, /* 62C */ {SD, 0xFE9D}, /* 62D */ {SD, 0xFEA1}, /* 62E */ {SD, 0xFEA5}, /* 62F */ {SR, 0xFEA9}, /* 630 */ {SR, 0xFEAB}, /* 631 */ {SR, 0xFEAD}, /* 632 */ {SR, 0xFEAF}, /* 633 */ {SD, 0xFEB1}, /* 634 */ {SD, 0xFEB5}, /* 635 */ {SD, 0xFEB9}, /* 636 */ {SD, 0xFEBD}, /* 637 */ {SD, 0xFEC1}, /* 638 */ {SD, 0xFEC5}, /* 639 */ {SD, 0xFEC9}, /* 63A */ {SD, 0xFECD}, /* 63B */ {SU, 0x0}, /* 63C */ {SU, 0x0}, /* 63D */ {SU, 0x0}, /* 63E */ {SU, 0x0}, /* 63F */ {SU, 0x0}, /* 640 */ {SC, 0x0}, /* 641 */ {SD, 0xFED1}, /* 642 */ {SD, 0xFED5}, /* 643 */ {SD, 0xFED9}, /* 644 */ {SD, 0xFEDD}, /* 645 */ {SD, 0xFEE1}, /* 646 */ {SD, 0xFEE5}, /* 647 */ {SD, 0xFEE9}, /* 648 */ {SR, 0xFEED}, /* 649 */ {SR, 0xFEEF}, /* SD */ /* 64A */ {SD, 0xFEF1}, /* 64B */ {SU, 0x0}, /* 64C */ {SU, 0x0}, /* 64D */ {SU, 0x0}, /* 64E */ {SU, 0x0}, /* 64F */ {SU, 0x0}, /* 650 */ {SU, 0x0}, /* 651 */ {SU, 0x0}, /* 652 */ {SU, 0x0}, /* 653 */ {SU, 0x0}, /* 654 */ {SU, 0x0}, /* 655 */ {SU, 0x0}, /* 656 */ {SU, 0x0}, /* 657 */ {SU, 0x0}, /* 658 */ {SU, 0x0}, /* 659 */ {SU, 0x0}, /* 65A */ {SU, 0x0}, /* 65B */ {SU, 0x0}, /* 65C */ {SU, 0x0}, /* 65D */ {SU, 0x0}, /* 65E */ {SU, 0x0}, /* 65F */ {SU, 0x0}, /* 660 */ {SU, 0x0}, /* 661 */ {SU, 0x0}, /* 662 */ {SU, 0x0}, /* 663 */ {SU, 0x0}, /* 664 */ {SU, 0x0}, /* 665 */ {SU, 0x0}, /* 666 */ {SU, 0x0}, /* 667 */ {SU, 0x0}, /* 668 */ {SU, 0x0}, /* 669 */ {SU, 0x0}, /* 66A */ {SU, 0x0}, /* 66B */ {SU, 0x0}, /* 66C */ {SU, 0x0}, /* 66D */ {SU, 0x0}, /* 66E */ {SU, 0x0}, /* 66F */ {SU, 0x0}, /* 670 */ {SU, 0x0}, /* 671 */ {SR, 0xFB50}, /* 672 */ {SU, 0x0}, /* 673 */ {SU, 0x0}, /* 674 */ {SU, 0x0}, /* 675 */ {SU, 0x0}, /* 676 */ {SU, 0x0}, /* 677 */ {SU, 0x0}, /* 678 */ {SU, 0x0}, /* 679 */ {SD, 0xFB66}, /* 67A */ {SD, 0xFB5E}, /* 67B */ {SD, 0xFB52}, /* 67C */ {SU, 0x0}, /* 67D */ {SU, 0x0}, /* 67E */ {SD, 0xFB56}, /* 67F */ {SD, 0xFB62}, /* 680 */ {SD, 0xFB5A}, /* 681 */ {SU, 0x0}, /* 682 */ {SU, 0x0}, /* 683 */ {SD, 0xFB76}, /* 684 */ {SD, 0xFB72}, /* 685 */ {SU, 0x0}, /* 686 */ {SD, 0xFB7A}, /* 687 */ {SD, 0xFB7E}, /* 688 */ {SR, 0xFB88}, /* 689 */ {SU, 0x0}, /* 68A */ {SU, 0x0}, /* 68B */ {SU, 0x0}, /* 68C */ {SR, 0xFB84}, /* 68D */ {SR, 0xFB82}, /* 68E */ {SR, 0xFB86}, /* 68F */ {SU, 0x0}, /* 690 */ {SU, 0x0}, /* 691 */ {SR, 0xFB8C}, /* 692 */ {SU, 0x0}, /* 693 */ {SU, 0x0}, /* 694 */ {SU, 0x0}, /* 695 */ {SU, 0x0}, /* 696 */ {SU, 0x0}, /* 697 */ {SU, 0x0}, /* 698 */ {SR, 0xFB8A}, /* 699 */ {SU, 0x0}, /* 69A */ {SU, 0x0}, /* 69B */ {SU, 0x0}, /* 69C */ {SU, 0x0}, /* 69D */ {SU, 0x0}, /* 69E */ {SU, 0x0}, /* 69F */ {SU, 0x0}, /* 6A0 */ {SU, 0x0}, /* 6A1 */ {SU, 0x0}, /* 6A2 */ {SU, 0x0}, /* 6A3 */ {SU, 0x0}, /* 6A4 */ {SD, 0xFB6A}, /* 6A5 */ {SU, 0x0}, /* 6A6 */ {SD, 0xFB6E}, /* 6A7 */ {SU, 0x0}, /* 6A8 */ {SU, 0x0}, /* 6A9 */ {SD, 0xFB8E}, /* 6AA */ {SU, 0x0}, /* 6AB */ {SU, 0x0}, /* 6AC */ {SU, 0x0}, /* 6AD */ {SD, 0xFBD3}, /* 6AE */ {SU, 0x0}, /* 6AF */ {SD, 0xFB92}, /* 6B0 */ {SU, 0x0}, /* 6B1 */ {SD, 0xFB9A}, /* 6B2 */ {SU, 0x0}, /* 6B3 */ {SD, 0xFB96}, /* 6B4 */ {SU, 0x0}, /* 6B5 */ {SU, 0x0}, /* 6B6 */ {SU, 0x0}, /* 6B7 */ {SU, 0x0}, /* 6B8 */ {SU, 0x0}, /* 6B9 */ {SU, 0x0}, /* 6BA */ {SR, 0xFB9E}, /* 6BB */ {SD, 0xFBA0}, /* 6BC */ {SU, 0x0}, /* 6BD */ {SU, 0x0}, /* 6BE */ {SD, 0xFBAA}, /* 6BF */ {SU, 0x0}, /* 6C0 */ {SR, 0xFBA4}, /* 6C1 */ {SD, 0xFBA6}, /* 6C2 */ {SU, 0x0}, /* 6C3 */ {SU, 0x0}, /* 6C4 */ {SU, 0x0}, /* 6C5 */ {SR, 0xFBE0}, /* 6C6 */ {SR, 0xFBD9}, /* 6C7 */ {SR, 0xFBD7}, /* 6C8 */ {SR, 0xFBDB}, /* 6C9 */ {SR, 0xFBE2}, /* 6CA */ {SU, 0x0}, /* 6CB */ {SR, 0xFBDE}, /* 6CC */ {SD, 0xFBFC}, /* 6CD */ {SU, 0x0}, /* 6CE */ {SU, 0x0}, /* 6CF */ {SU, 0x0}, /* 6D0 */ {SU, 0x0}, /* 6D1 */ {SU, 0x0}, /* 6D2 */ {SR, 0xFBAE}, }; /* * Flips the text buffer, according to max level, and * all higher levels * * Input: * from: text buffer, on which to apply flipping * level: resolved levels buffer * max: the maximum level found in this line (should be unsigned char) * count: line size in bidi_char */ static void flipThisRun( bidi_char *from, unsigned char *level, int max, int count) { int i, j, k, tlevel; bidi_char temp; j = i = 0; while (i j; k--, j++) { temp = from[k]; from[k] = from[j]; from[j] = temp; } } } /* * Finds the index of a run with level equals tlevel */ static int findIndexOfRun( unsigned char *level , int start, int count, int tlevel) { int i; for (i=start; i 1) { k = (i + j) / 2; if (ch < lookup[k].first) j = k; else if (ch > lookup[k].last) i = k; else return lookup[k].type; } /* * If we reach here, the character was not in any of the * intervals listed in the lookup table. This means we return * ON (`Other Neutrals'). This is the appropriate code for any * character genuinely not listed in the Unicode table, and * also the table above has deliberately left out any * characters _explicitly_ listed as ON (to save space!). */ return ON; } /* * Function exported to front ends to allow them to identify * bidi-active characters (in case, for example, the platform's * text display function can't conveniently be prevented from doing * its own bidi and so special treatment is required for characters * that would cause the bidi algorithm to activate). * * This function is passed a single Unicode code point, and returns * nonzero if the presence of this code point can possibly cause * the bidi algorithm to do any reordering. Thus, any string * composed entirely of characters for which is_rtl() returns zero * should be safe to pass to a bidi-active platform display * function without fear. * * (is_rtl() must therefore also return true for any character * which would be affected by Arabic shaping, but this isn't * important because all such characters are right-to-left so it * would have flagged them anyway.) */ bool is_rtl(int c) { /* * After careful reading of the Unicode bidi algorithm (URL as * given at the top of this file) I believe that the only * character classes which can possibly cause trouble are R, * AL, RLE and RLO. I think that any string containing no * character in any of those classes will be displayed * uniformly left-to-right by the Unicode bidi algorithm. */ const int mask = (1< 0) { unsigned char current = level[--from]; while (from >= 0 && level[from] == current) from--; if (from >= 0) return level[from]; return -1; } else return -1; } /* The Main shaping function, and the only one to be used * by the outside world. * * line: buffer to apply shaping to. this must be passed by doBidi() first * to: output buffer for the shaped data * count: number of characters in line */ int do_shape(bidi_char *line, bidi_char *to, int count) { int i, tempShape; bool ligFlag = false; for (i=0; i 0) switch (line[i-1].wc) { case 0x622: ligFlag = true; if ((tempShape == SL) || (tempShape == SD) || (tempShape == SC)) to[i].wc = 0xFEF6; else to[i].wc = 0xFEF5; break; case 0x623: ligFlag = true; if ((tempShape == SL) || (tempShape == SD) || (tempShape == SC)) to[i].wc = 0xFEF8; else to[i].wc = 0xFEF7; break; case 0x625: ligFlag = true; if ((tempShape == SL) || (tempShape == SD) || (tempShape == SC)) to[i].wc = 0xFEFA; else to[i].wc = 0xFEF9; break; case 0x627: ligFlag = true; if ((tempShape == SL) || (tempShape == SD) || (tempShape == SC)) to[i].wc = 0xFEFC; else to[i].wc = 0xFEFB; break; } if (ligFlag) { to[i-1].wc = 0x20; ligFlag = false; break; } } if ((tempShape == SL) || (tempShape == SD) || (tempShape == SC)) { tempShape = (i > 0 ? STYPE(line[i-1].wc) : SU); if ((tempShape == SR) || (tempShape == SD) || (tempShape == SC)) to[i].wc = SMEDIAL((SISOLATED(line[i].wc))); else to[i].wc = SFINAL((SISOLATED(line[i].wc))); break; } tempShape = (i > 0 ? STYPE(line[i-1].wc) : SU); if ((tempShape == SR) || (tempShape == SD) || (tempShape == SC)) to[i].wc = SINITIAL((SISOLATED(line[i].wc))); else to[i].wc = SISOLATED(line[i].wc); break; } } return 1; } /* * The Main Bidi Function, and the only function that should * be used by the outside world. * * line: a buffer of size count containing text to apply * the Bidirectional algorithm to. */ int do_bidi(bidi_char *line, int count) { unsigned char* types; unsigned char* levels; unsigned char paragraphLevel; unsigned char currentEmbedding; unsigned char currentOverride; unsigned char tempType; int i, j; bool yes, bover; /* Check the presence of R or AL types as optimization */ yes = false; for (i=0; i= 0) { if (types[j] == AL) { types[i] = AN; break; } else if (types[j] == R || types[j] == L) { break; } j--; } } } /* Rule (W3) * W3. Change all ALs to R. * * Optimization: on Rule Xn, we might set a flag on AL type * to prevent this loop in L R lines only... */ for (i=0; i 0 && types[i-1] == EN) { types[i] = EN; continue; } else if (i < count-1 && types[i+1] == EN) { types[i] = EN; continue; } else if (i < count-1 && types[i+1] == ET) { j=i; while (j < count-1 && types[j] == ET) { j++; } if (types[j] == EN) types[i] = EN; } } } /* Rule (W6) * W6. Otherwise, separators and terminators change to Other Neutral: */ for (i=0; i= 0) { if (types[j] == L) { types[i] = L; break; } else if (types[j] == R || types[j] == AL) { break; } j--; } } } /* Rule (N1) * N1. A sequence of neutrals takes the direction of the surrounding * strong text if the text on both sides has the same direction. European * and Arabic numbers are treated as though they were R. */ if (count >= 2 && types[0] == ON) { if ((types[1] == R) || (types[1] == EN) || (types[1] == AN)) types[0] = R; else if (types[1] == L) types[0] = L; } for (i=1; i<(count-1); i++) { if (types[i] == ON) { if (types[i-1] == L) { j=i; while (j<(count-1) && types[j] == ON) { j++; } if (types[j] == L) { while (i= 2 && types[count-1] == ON) { if (types[count-2] == R || types[count-2] == EN || types[count-2] == AN) types[count-1] = R; else if (types[count-2] == L) types[count-1] = L; } /* Rule (N2) * N2. Any remaining neutrals take the embedding direction. */ for (i=0; i0 && (getType(line[j].wc) == WS)) { j--; } if (j < (count-1)) { for (j++; j=i ; j--) { levels[j] = paragraphLevel; } } } else if (tempType == B || tempType == S) { levels[i] = paragraphLevel; } } /* Rule (L4) NOT IMPLEMENTED * L4. A character that possesses the mirrored property as specified by * Section 4.7, Mirrored, must be depicted by a mirrored glyph if the * resolved directionality of that character is R. */ /* Note: this is implemented before L2 for efficiency */ for (i=0; i tempType) tempType = levels[i]; i++; } /* maximum level in tempType. */ while (tempType > 0) { /* loop from highest level to the least odd, */ /* which i assume is 1 */ flipThisRun(line, levels, tempType, count); tempType--; } /* Rule (L3) NOT IMPLEMENTED * L3. Combining marks applied to a right-to-left base character will at * this point precede their base character. If the rendering engine * expects them to follow the base characters in the final display * process, then the ordering of the marks and the base character must * be reversed. */ sfree(types); sfree(levels); return R; } /* * Bad, Horrible function * takes a pointer to a character that is checked for * having a mirror glyph. */ static 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; case 0x005B: *ch = 0x005D; break; case 0x005D: *ch = 0x005B; break; case 0x007B: *ch = 0x007D; break; case 0x007D: *ch = 0x007B; break; case 0x00AB: *ch = 0x00BB; break; case 0x00BB: *ch = 0x00AB; break; } } else if ((*ch & 0xFF00) == 0x2000) { switch (*ch) { case 0x2039: *ch = 0x203A; break; case 0x203A: *ch = 0x2039; break; case 0x2045: *ch = 0x2046; break; case 0x2046: *ch = 0x2045; break; case 0x207D: *ch = 0x207E; break; case 0x207E: *ch = 0x207D; break; case 0x208D: *ch = 0x208E; break; case 0x208E: *ch = 0x208D; break; } } else if ((*ch & 0xFF00) == 0x2200) { switch (*ch) { case 0x2208: *ch = 0x220B; break; case 0x2209: *ch = 0x220C; break; case 0x220A: *ch = 0x220D; break; case 0x220B: *ch = 0x2208; break; case 0x220C: *ch = 0x2209; break; case 0x220D: *ch = 0x220A; break; case 0x2215: *ch = 0x29F5; break; case 0x223C: *ch = 0x223D; break; case 0x223D: *ch = 0x223C; break; case 0x2243: *ch = 0x22CD; break; case 0x2252: *ch = 0x2253; break; case 0x2253: *ch = 0x2252; break; case 0x2254: *ch = 0x2255; break; case 0x2255: *ch = 0x2254; break; case 0x2264: *ch = 0x2265; break; case 0x2265: *ch = 0x2264; break; case 0x2266: *ch = 0x2267; break; case 0x2267: *ch = 0x2266; break; case 0x2268: *ch = 0x2269; break; case 0x2269: *ch = 0x2268; break; case 0x226A: *ch = 0x226B; break; case 0x226B: *ch = 0x226A; break; case 0x226E: *ch = 0x226F; break; case 0x226F: *ch = 0x226E; break; case 0x2270: *ch = 0x2271; break; case 0x2271: *ch = 0x2270; break; case 0x2272: *ch = 0x2273; break; case 0x2273: *ch = 0x2272; break; case 0x2274: *ch = 0x2275; break; case 0x2275: *ch = 0x2274; break; case 0x2276: *ch = 0x2277; break; case 0x2277: *ch = 0x2276; break; case 0x2278: *ch = 0x2279; break; case 0x2279: *ch = 0x2278; break; case 0x227A: *ch = 0x227B; break; case 0x227B: *ch = 0x227A; break; case 0x227C: *ch = 0x227D; break; case 0x227D: *ch = 0x227C; break; case 0x227E: *ch = 0x227F; break; case 0x227F: *ch = 0x227E; break; case 0x2280: *ch = 0x2281; break; case 0x2281: *ch = 0x2280; break; case 0x2282: *ch = 0x2283; break; case 0x2283: *ch = 0x2282; break; case 0x2284: *ch = 0x2285; break; case 0x2285: *ch = 0x2284; break; case 0x2286: *ch = 0x2287; break; case 0x2287: *ch = 0x2286; break; case 0x2288: *ch = 0x2289; break; case 0x2289: *ch = 0x2288; break; case 0x228A: *ch = 0x228B; break; case 0x228B: *ch = 0x228A; break; case 0x228F: *ch = 0x2290; break; case 0x2290: *ch = 0x228F; break; case 0x2291: *ch = 0x2292; break; case 0x2292: *ch = 0x2291; break; case 0x2298: *ch = 0x29B8; break; case 0x22A2: *ch = 0x22A3; break; case 0x22A3: *ch = 0x22A2; break; case 0x22A6: *ch = 0x2ADE; break; case 0x22A8: *ch = 0x2AE4; break; case 0x22A9: *ch = 0x2AE3; break; case 0x22AB: *ch = 0x2AE5; break; case 0x22B0: *ch = 0x22B1; break; case 0x22B1: *ch = 0x22B0; break; case 0x22B2: *ch = 0x22B3; break; case 0x22B3: *ch = 0x22B2; break; case 0x22B4: *ch = 0x22B5; break; case 0x22B5: *ch = 0x22B4; break; case 0x22B6: *ch = 0x22B7; break; case 0x22B7: *ch = 0x22B6; break; case 0x22C9: *ch = 0x22CA; break; case 0x22CA: *ch = 0x22C9; break; case 0x22CB: *ch = 0x22CC; break; case 0x22CC: *ch = 0x22CB; break; case 0x22CD: *ch = 0x2243; break; case 0x22D0: *ch = 0x22D1; break; case 0x22D1: *ch = 0x22D0; break; case 0x22D6: *ch = 0x22D7; break; case 0x22D7: *ch = 0x22D6; break; case 0x22D8: *ch = 0x22D9; break; case 0x22D9: *ch = 0x22D8; break; case 0x22DA: *ch = 0x22DB; break; case 0x22DB: *ch = 0x22DA; break; case 0x22DC: *ch = 0x22DD; break; case 0x22DD: *ch = 0x22DC; break; case 0x22DE: *ch = 0x22DF; break; case 0x22DF: *ch = 0x22DE; break; case 0x22E0: *ch = 0x22E1; break; case 0x22E1: *ch = 0x22E0; break; case 0x22E2: *ch = 0x22E3; break; case 0x22E3: *ch = 0x22E2; break; case 0x22E4: *ch = 0x22E5; break; case 0x22E5: *ch = 0x22E4; break; case 0x22E6: *ch = 0x22E7; break; case 0x22E7: *ch = 0x22E6; break; case 0x22E8: *ch = 0x22E9; break; case 0x22E9: *ch = 0x22E8; break; case 0x22EA: *ch = 0x22EB; break; case 0x22EB: *ch = 0x22EA; break; case 0x22EC: *ch = 0x22ED; break; case 0x22ED: *ch = 0x22EC; break; case 0x22F0: *ch = 0x22F1; break; case 0x22F1: *ch = 0x22F0; break; case 0x22F2: *ch = 0x22FA; break; case 0x22F3: *ch = 0x22FB; break; case 0x22F4: *ch = 0x22FC; break; case 0x22F6: *ch = 0x22FD; break; case 0x22F7: *ch = 0x22FE; break; case 0x22FA: *ch = 0x22F2; break; case 0x22FB: *ch = 0x22F3; break; case 0x22FC: *ch = 0x22F4; break; case 0x22FD: *ch = 0x22F6; break; case 0x22FE: *ch = 0x22F7; break; } } else if ((*ch & 0xFF00) == 0x2300) { switch (*ch) { case 0x2308: *ch = 0x2309; break; case 0x2309: *ch = 0x2308; break; case 0x230A: *ch = 0x230B; break; case 0x230B: *ch = 0x230A; break; case 0x2329: *ch = 0x232A; break; case 0x232A: *ch = 0x2329; break; } } else if ((*ch & 0xFF00) == 0x2700) { switch (*ch) { case 0x2768: *ch = 0x2769; break; case 0x2769: *ch = 0x2768; break; case 0x276A: *ch = 0x276B; break; case 0x276B: *ch = 0x276A; break; case 0x276C: *ch = 0x276D; break; case 0x276D: *ch = 0x276C; break; case 0x276E: *ch = 0x276F; break; case 0x276F: *ch = 0x276E; break; case 0x2770: *ch = 0x2771; break; case 0x2771: *ch = 0x2770; break; case 0x2772: *ch = 0x2773; break; case 0x2773: *ch = 0x2772; break; case 0x2774: *ch = 0x2775; break; case 0x2775: *ch = 0x2774; break; case 0x27D5: *ch = 0x27D6; break; case 0x27D6: *ch = 0x27D5; break; case 0x27DD: *ch = 0x27DE; break; case 0x27DE: *ch = 0x27DD; break; case 0x27E2: *ch = 0x27E3; break; case 0x27E3: *ch = 0x27E2; break; case 0x27E4: *ch = 0x27E5; break; case 0x27E5: *ch = 0x27E4; break; case 0x27E6: *ch = 0x27E7; break; case 0x27E7: *ch = 0x27E6; break; case 0x27E8: *ch = 0x27E9; break; case 0x27E9: *ch = 0x27E8; break; case 0x27EA: *ch = 0x27EB; break; case 0x27EB: *ch = 0x27EA; break; } } else if ((*ch & 0xFF00) == 0x2900) { switch (*ch) { case 0x2983: *ch = 0x2984; break; case 0x2984: *ch = 0x2983; break; case 0x2985: *ch = 0x2986; break; case 0x2986: *ch = 0x2985; break; case 0x2987: *ch = 0x2988; break; case 0x2988: *ch = 0x2987; break; case 0x2989: *ch = 0x298A; break; case 0x298A: *ch = 0x2989; break; case 0x298B: *ch = 0x298C; break; case 0x298C: *ch = 0x298B; break; case 0x298D: *ch = 0x2990; break; case 0x298E: *ch = 0x298F; break; case 0x298F: *ch = 0x298E; break; case 0x2990: *ch = 0x298D; break; case 0x2991: *ch = 0x2992; break; case 0x2992: *ch = 0x2991; break; case 0x2993: *ch = 0x2994; break; case 0x2994: *ch = 0x2993; break; case 0x2995: *ch = 0x2996; break; case 0x2996: *ch = 0x2995; break; case 0x2997: *ch = 0x2998; break; case 0x2998: *ch = 0x2997; break; case 0x29B8: *ch = 0x2298; break; case 0x29C0: *ch = 0x29C1; break; case 0x29C1: *ch = 0x29C0; break; case 0x29C4: *ch = 0x29C5; break; case 0x29C5: *ch = 0x29C4; break; case 0x29CF: *ch = 0x29D0; break; case 0x29D0: *ch = 0x29CF; break; case 0x29D1: *ch = 0x29D2; break; case 0x29D2: *ch = 0x29D1; break; case 0x29D4: *ch = 0x29D5; break; case 0x29D5: *ch = 0x29D4; break; case 0x29D8: *ch = 0x29D9; break; case 0x29D9: *ch = 0x29D8; break; case 0x29DA: *ch = 0x29DB; break; case 0x29DB: *ch = 0x29DA; break; case 0x29F5: *ch = 0x2215; break; case 0x29F8: *ch = 0x29F9; break; case 0x29F9: *ch = 0x29F8; break; case 0x29FC: *ch = 0x29FD; break; case 0x29FD: *ch = 0x29FC; break; } } else if ((*ch & 0xFF00) == 0x2A00) { switch (*ch) { case 0x2A2B: *ch = 0x2A2C; break; case 0x2A2C: *ch = 0x2A2B; break; case 0x2A2D: *ch = 0x2A2C; break; case 0x2A2E: *ch = 0x2A2D; break; case 0x2A34: *ch = 0x2A35; break; case 0x2A35: *ch = 0x2A34; break; case 0x2A3C: *ch = 0x2A3D; break; case 0x2A3D: *ch = 0x2A3C; break; case 0x2A64: *ch = 0x2A65; break; case 0x2A65: *ch = 0x2A64; break; case 0x2A79: *ch = 0x2A7A; break; case 0x2A7A: *ch = 0x2A79; break; case 0x2A7D: *ch = 0x2A7E; break; case 0x2A7E: *ch = 0x2A7D; break; case 0x2A7F: *ch = 0x2A80; break; case 0x2A80: *ch = 0x2A7F; break; case 0x2A81: *ch = 0x2A82; break; case 0x2A82: *ch = 0x2A81; break; case 0x2A83: *ch = 0x2A84; break; case 0x2A84: *ch = 0x2A83; break; case 0x2A8B: *ch = 0x2A8C; break; case 0x2A8C: *ch = 0x2A8B; break; case 0x2A91: *ch = 0x2A92; break; case 0x2A92: *ch = 0x2A91; break; case 0x2A93: *ch = 0x2A94; break; case 0x2A94: *ch = 0x2A93; break; case 0x2A95: *ch = 0x2A96; break; case 0x2A96: *ch = 0x2A95; break; case 0x2A97: *ch = 0x2A98; break; case 0x2A98: *ch = 0x2A97; break; case 0x2A99: *ch = 0x2A9A; break; case 0x2A9A: *ch = 0x2A99; break; case 0x2A9B: *ch = 0x2A9C; break; case 0x2A9C: *ch = 0x2A9B; break; case 0x2AA1: *ch = 0x2AA2; break; case 0x2AA2: *ch = 0x2AA1; break; case 0x2AA6: *ch = 0x2AA7; break; case 0x2AA7: *ch = 0x2AA6; break; case 0x2AA8: *ch = 0x2AA9; break; case 0x2AA9: *ch = 0x2AA8; break; case 0x2AAA: *ch = 0x2AAB; break; case 0x2AAB: *ch = 0x2AAA; break; case 0x2AAC: *ch = 0x2AAD; break; case 0x2AAD: *ch = 0x2AAC; break; case 0x2AAF: *ch = 0x2AB0; break; case 0x2AB0: *ch = 0x2AAF; break; case 0x2AB3: *ch = 0x2AB4; break; case 0x2AB4: *ch = 0x2AB3; break; case 0x2ABB: *ch = 0x2ABC; break; case 0x2ABC: *ch = 0x2ABB; break; case 0x2ABD: *ch = 0x2ABE; break; case 0x2ABE: *ch = 0x2ABD; break; case 0x2ABF: *ch = 0x2AC0; break; case 0x2AC0: *ch = 0x2ABF; break; case 0x2AC1: *ch = 0x2AC2; break; case 0x2AC2: *ch = 0x2AC1; break; case 0x2AC3: *ch = 0x2AC4; break; case 0x2AC4: *ch = 0x2AC3; break; case 0x2AC5: *ch = 0x2AC6; break; case 0x2AC6: *ch = 0x2AC5; break; case 0x2ACD: *ch = 0x2ACE; break; case 0x2ACE: *ch = 0x2ACD; break; case 0x2ACF: *ch = 0x2AD0; break; case 0x2AD0: *ch = 0x2ACF; break; case 0x2AD1: *ch = 0x2AD2; break; case 0x2AD2: *ch = 0x2AD1; break; case 0x2AD3: *ch = 0x2AD4; break; case 0x2AD4: *ch = 0x2AD3; break; case 0x2AD5: *ch = 0x2AD6; break; case 0x2AD6: *ch = 0x2AD5; break; case 0x2ADE: *ch = 0x22A6; break; case 0x2AE3: *ch = 0x22A9; break; case 0x2AE4: *ch = 0x22A8; break; case 0x2AE5: *ch = 0x22AB; break; case 0x2AEC: *ch = 0x2AED; break; case 0x2AED: *ch = 0x2AEC; break; case 0x2AF7: *ch = 0x2AF8; break; case 0x2AF8: *ch = 0x2AF7; break; case 0x2AF9: *ch = 0x2AFA; break; case 0x2AFA: *ch = 0x2AF9; break; } } else if ((*ch & 0xFF00) == 0x3000) { switch (*ch) { case 0x3008: *ch = 0x3009; break; case 0x3009: *ch = 0x3008; break; case 0x300A: *ch = 0x300B; break; case 0x300B: *ch = 0x300A; break; case 0x300C: *ch = 0x300D; break; case 0x300D: *ch = 0x300C; break; case 0x300E: *ch = 0x300F; break; case 0x300F: *ch = 0x300E; break; case 0x3010: *ch = 0x3011; break; case 0x3011: *ch = 0x3010; break; case 0x3014: *ch = 0x3015; break; case 0x3015: *ch = 0x3014; break; case 0x3016: *ch = 0x3017; break; case 0x3017: *ch = 0x3016; break; case 0x3018: *ch = 0x3019; break; case 0x3019: *ch = 0x3018; break; case 0x301A: *ch = 0x301B; break; case 0x301B: *ch = 0x301A; break; } } else if ((*ch & 0xFF00) == 0xFF00) { switch (*ch) { case 0xFF08: *ch = 0xFF09; break; case 0xFF09: *ch = 0xFF08; break; case 0xFF1C: *ch = 0xFF1E; break; case 0xFF1E: *ch = 0xFF1C; break; case 0xFF3B: *ch = 0xFF3D; break; case 0xFF3D: *ch = 0xFF3B; break; case 0xFF5B: *ch = 0xFF5D; break; case 0xFF5D: *ch = 0xFF5B; break; case 0xFF5F: *ch = 0xFF60; break; case 0xFF60: *ch = 0xFF5F; break; case 0xFF62: *ch = 0xFF63; break; case 0xFF63: *ch = 0xFF62; break; } } } #ifdef TEST_GETTYPE #include #include int main(int argc, char **argv) { static const struct { int type; char *name; } typetoname[] = { #define TYPETONAME(X) { X , #X } TYPETONAME(L), TYPETONAME(LRE), TYPETONAME(LRO), TYPETONAME(R), TYPETONAME(AL), TYPETONAME(RLE), TYPETONAME(RLO), TYPETONAME(PDF), TYPETONAME(EN), TYPETONAME(ES), TYPETONAME(ET), TYPETONAME(AN), TYPETONAME(CS), TYPETONAME(NSM), TYPETONAME(BN), TYPETONAME(B), TYPETONAME(S), TYPETONAME(WS), TYPETONAME(ON), #undef TYPETONAME }; int i; for (i = 1; i < argc; i++) { unsigned long chr = strtoul(argv[i], NULL, 0); int type = getType(chr); assert(typetoname[type].type == type); printf("U+%04x: %s\n", (unsigned)chr, typetoname[type].name); } return 0; } #endif putty-0.76/misc.c0000644000175000017500000003207514072266311010664 00000000000000/* * Platform-independent routines shared between all PuTTY programs. * * This file contains functions that use the kind of infrastructure * like conf.c that tends to only live in the main applications, or * that do things that only something like a main PuTTY application * would need. So standalone test programs should generally be able to * avoid linking against it. * * More standalone functions that depend on nothing but the C library * live in utils.c. */ #include #include #include #include #include #include #include "defs.h" #include "putty.h" #include "misc.h" #define BASE64_CHARS_NOEQ \ "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/" #define BASE64_CHARS_ALL BASE64_CHARS_NOEQ "=" void seat_connection_fatal(Seat *seat, const char *fmt, ...) { va_list ap; char *msg; va_start(ap, fmt); msg = dupvprintf(fmt, ap); va_end(ap); seat->vt->connection_fatal(seat, msg); sfree(msg); /* if we return */ } prompts_t *new_prompts(void) { prompts_t *p = snew(prompts_t); p->prompts = NULL; p->n_prompts = p->prompts_size = 0; 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, bool echo) { prompt_t *pr = snew(prompt_t); pr->prompt = promptstr; pr->echo = echo; pr->result = strbuf_new_nm(); sgrowarray(p->prompts, p->prompts_size, p->n_prompts); p->prompts[p->n_prompts++] = pr; } void prompt_set_result(prompt_t *pr, const char *newstr) { strbuf_clear(pr->result); put_datapl(pr->result, ptrlen_from_asciz(newstr)); } const char *prompt_get_result_ref(prompt_t *pr) { return pr->result->s; } char *prompt_get_result(prompt_t *pr) { return dupstr(pr->result->s); } void free_prompts(prompts_t *p) { size_t i; for (i=0; i < p->n_prompts; i++) { prompt_t *pr = p->prompts[i]; strbuf_free(pr->result); sfree(pr->prompt); sfree(pr); } sfree(p->prompts); sfree(p->name); sfree(p->instruction); sfree(p); } /* * Determine whether or not a Conf represents a session which can * sensibly be launched right now. */ bool 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); } /* * Validate a manual host key specification (either entered in the * GUI, or via -hostkey). If valid, we return true, and update 'key' * to contain a canonicalised version of the key string in 'key' * (which is guaranteed to take up at most as much space as the * original version), suitable for putting into the Conf. If not * valid, we return false. */ bool validate_manual_hostkey(char *key) { char *p, *q, *r, *s; /* * Step through the string word by word, looking for a word that's * in one of the formats we like. */ p = key; while ((p += strspn(p, " \t"))[0]) { q = p; p += strcspn(p, " \t"); if (*p) *p++ = '\0'; /* * Now q is our word. */ if (strstartswith(q, "SHA256:")) { /* Test for a valid SHA256 key fingerprint. */ r = q + 7; if (strlen(r) == 43 && r[strspn(r, BASE64_CHARS_NOEQ)] == 0) return true; } r = q; if (strstartswith(r, "MD5:")) r += 4; if (strlen(r) == 16*3 - 1 && r[strspn(r, "0123456789abcdefABCDEF:")] == 0) { /* * Test for a valid MD5 key fingerprint. Check the colons * are in the right places, and if so, return the same * fingerprint canonicalised into lowercase. */ int i; for (i = 0; i < 16; i++) if (r[3*i] == ':' || r[3*i+1] == ':') goto not_fingerprint; /* sorry */ for (i = 0; i < 15; i++) if (r[3*i+2] != ':') goto not_fingerprint; /* sorry */ for (i = 0; i < 16*3 - 1; i++) key[i] = tolower(r[i]); key[16*3 - 1] = '\0'; return true; } not_fingerprint:; /* * Before we check for a public-key blob, trim newlines out of * the middle of the word, in case someone's managed to paste * in a public-key blob _with_ them. */ for (r = s = q; *r; r++) if (*r != '\n' && *r != '\r') *s++ = *r; *s = '\0'; if (strlen(q) % 4 == 0 && strlen(q) > 2*4 && q[strspn(q, BASE64_CHARS_ALL)] == 0) { /* * Might be a base64-encoded SSH-2 public key blob. Check * that it starts with a sensible algorithm string. No * canonicalisation is necessary for this string type. * * The algorithm string must be at most 64 characters long * (RFC 4251 section 6). */ unsigned char decoded[6]; unsigned alglen; int minlen; int len = 0; len += base64_decode_atom(q, decoded+len); if (len < 3) goto not_ssh2_blob; /* sorry */ len += base64_decode_atom(q+4, decoded+len); if (len < 4) goto not_ssh2_blob; /* sorry */ alglen = GET_32BIT_MSB_FIRST(decoded); if (alglen > 64) goto not_ssh2_blob; /* sorry */ minlen = ((alglen + 4) + 2) / 3; if (strlen(q) < minlen) goto not_ssh2_blob; /* sorry */ strcpy(key, q); return true; } not_ssh2_blob:; } return false; } char *buildinfo(const char *newline) { strbuf *buf = strbuf_new(); strbuf_catf(buf, "Build platform: %d-bit %s", (int)(CHAR_BIT * sizeof(void *)), BUILDINFO_PLATFORM); #ifdef __clang_version__ #define FOUND_COMPILER strbuf_catf(buf, "%sCompiler: clang %s", newline, __clang_version__); #elif defined __GNUC__ && defined __VERSION__ #define FOUND_COMPILER strbuf_catf(buf, "%sCompiler: gcc %s", newline, __VERSION__); #endif #if defined _MSC_VER #ifndef FOUND_COMPILER #define FOUND_COMPILER strbuf_catf(buf, "%sCompiler: ", newline); #else strbuf_catf(buf, ", emulating "); #endif strbuf_catf(buf, "Visual Studio"); #if 0 /* * List of _MSC_VER values and their translations taken from * https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros * * The pointless #if 0 branch containing this comment is there so * that every real clause can start with #elif and there's no * anomalous first clause. That way the patch looks nicer when you * add extra ones. */ #elif _MSC_VER == 1928 && _MSC_FULL_VER >= 192829500 /* * 16.9 and 16.8 have the same _MSC_VER value, and have to be * distinguished by _MSC_FULL_VER. As of 2021-03-04 that is not * mentioned on the above page, but see e.g. * https://developercommunity.visualstudio.com/t/the-169-cc-compiler-still-uses-the-same-version-nu/1335194#T-N1337120 * which says that 16.9 builds will have versions starting at * 19.28.29500.* and going up. Hence, 19 28 29500 is what we * compare _MSC_FULL_VER against above. */ strbuf_catf(buf, " 2019 (16.9)"); #elif _MSC_VER == 1928 strbuf_catf(buf, " 2019 (16.8)"); #elif _MSC_VER == 1927 strbuf_catf(buf, " 2019 (16.7)"); #elif _MSC_VER == 1926 strbuf_catf(buf, " 2019 (16.6)"); #elif _MSC_VER == 1925 strbuf_catf(buf, " 2019 (16.5)"); #elif _MSC_VER == 1924 strbuf_catf(buf, " 2019 (16.4)"); #elif _MSC_VER == 1923 strbuf_catf(buf, " 2019 (16.3)"); #elif _MSC_VER == 1922 strbuf_catf(buf, " 2019 (16.2)"); #elif _MSC_VER == 1921 strbuf_catf(buf, " 2019 (16.1)"); #elif _MSC_VER == 1920 strbuf_catf(buf, " 2019 (16.0)"); #elif _MSC_VER == 1916 strbuf_catf(buf, " 2017 version 15.9"); #elif _MSC_VER == 1915 strbuf_catf(buf, " 2017 version 15.8"); #elif _MSC_VER == 1914 strbuf_catf(buf, " 2017 version 15.7"); #elif _MSC_VER == 1913 strbuf_catf(buf, " 2017 version 15.6"); #elif _MSC_VER == 1912 strbuf_catf(buf, " 2017 version 15.5"); #elif _MSC_VER == 1911 strbuf_catf(buf, " 2017 version 15.3"); #elif _MSC_VER == 1910 strbuf_catf(buf, " 2017 RTW (15.0)"); #elif _MSC_VER == 1900 strbuf_catf(buf, " 2015 (14.0)"); #elif _MSC_VER == 1800 strbuf_catf(buf, " 2013 (12.0)"); #elif _MSC_VER == 1700 strbuf_catf(buf, " 2012 (11.0)"); #elif _MSC_VER == 1600 strbuf_catf(buf, " 2010 (10.0)"); #elif _MSC_VER == 1500 strbuf_catf(buf, " 2008 (9.0)"); #elif _MSC_VER == 1400 strbuf_catf(buf, " 2005 (8.0)"); #elif _MSC_VER == 1310 strbuf_catf(buf, " .NET 2003 (7.1)"); #elif _MSC_VER == 1300 strbuf_catf(buf, " .NET 2002 (7.0)"); #elif _MSC_VER == 1200 strbuf_catf(buf, " 6.0"); #else strbuf_catf(buf, ", unrecognised version"); #endif strbuf_catf(buf, ", _MSC_VER=%d", (int)_MSC_VER); #endif #ifdef BUILDINFO_GTK { char *gtk_buildinfo = buildinfo_gtk_version(); if (gtk_buildinfo) { strbuf_catf(buf, "%sCompiled against GTK version %s", newline, gtk_buildinfo); sfree(gtk_buildinfo); } } #endif #if defined _WINDOWS { int echm = has_embedded_chm(); if (echm >= 0) strbuf_catf(buf, "%sEmbedded HTML Help file: %s", newline, echm ? "yes" : "no"); } #endif #if defined _WINDOWS && defined MINEFIELD strbuf_catf(buf, "%sBuild option: MINEFIELD", newline); #endif #ifdef NO_SECURITY strbuf_catf(buf, "%sBuild option: NO_SECURITY", newline); #endif #ifdef NO_SECUREZEROMEMORY strbuf_catf(buf, "%sBuild option: NO_SECUREZEROMEMORY", newline); #endif #ifdef NO_IPV6 strbuf_catf(buf, "%sBuild option: NO_IPV6", newline); #endif #ifdef NO_GSSAPI strbuf_catf(buf, "%sBuild option: NO_GSSAPI", newline); #endif #ifdef STATIC_GSSAPI strbuf_catf(buf, "%sBuild option: STATIC_GSSAPI", newline); #endif #ifdef UNPROTECT strbuf_catf(buf, "%sBuild option: UNPROTECT", newline); #endif #ifdef FUZZING strbuf_catf(buf, "%sBuild option: FUZZING", newline); #endif #ifdef DEBUG strbuf_catf(buf, "%sBuild option: DEBUG", newline); #endif strbuf_catf(buf, "%sSource commit: %s", newline, commitid); return strbuf_to_str(buf); } size_t nullseat_output( Seat *seat, bool is_stderr, const void *data, size_t len) { return 0; } bool nullseat_eof(Seat *seat) { return true; } int nullseat_get_userpass_input( Seat *seat, prompts_t *p, bufchain *input) { return 0; } void nullseat_notify_remote_exit(Seat *seat) {} void nullseat_connection_fatal(Seat *seat, const char *message) {} void nullseat_update_specials_menu(Seat *seat) {} char *nullseat_get_ttymode(Seat *seat, const char *mode) { return NULL; } void nullseat_set_busy_status(Seat *seat, BusyStatus status) {} int nullseat_verify_ssh_host_key( Seat *seat, const char *host, int port, const char *keytype, char *keystr, const char *keydisp, char **key_fingerprints, void (*callback)(void *ctx, int result), void *ctx) { return 0; } int nullseat_confirm_weak_crypto_primitive( Seat *seat, const char *algtype, const char *algname, void (*callback)(void *ctx, int result), void *ctx) { return 0; } int nullseat_confirm_weak_cached_hostkey( Seat *seat, const char *algname, const char *betteralgs, void (*callback)(void *ctx, int result), void *ctx) { return 0; } bool nullseat_is_never_utf8(Seat *seat) { return false; } bool nullseat_is_always_utf8(Seat *seat) { return true; } void nullseat_echoedit_update(Seat *seat, bool echoing, bool editing) {} const char *nullseat_get_x_display(Seat *seat) { return NULL; } bool nullseat_get_windowid(Seat *seat, long *id_out) { return false; } bool nullseat_get_window_pixel_size( Seat *seat, int *width, int *height) { return false; } StripCtrlChars *nullseat_stripctrl_new( Seat *seat, BinarySink *bs_out, SeatInteractionContext sic) {return NULL;} bool nullseat_set_trust_status(Seat *seat, bool tr) { return false; } bool nullseat_set_trust_status_vacuously(Seat *seat, bool tr) { return true; } bool nullseat_verbose_no(Seat *seat) { return false; } bool nullseat_verbose_yes(Seat *seat) { return true; } bool nullseat_interactive_no(Seat *seat) { return false; } bool nullseat_interactive_yes(Seat *seat) { return true; } bool nullseat_get_cursor_position(Seat *seat, int *x, int *y) { return false; } bool null_lp_verbose_no(LogPolicy *lp) { return false; } bool null_lp_verbose_yes(LogPolicy *lp) { return true; } void sk_free_peer_info(SocketPeerInfo *pi) { if (pi) { sfree((char *)pi->addr_text); sfree((char *)pi->log_text); sfree(pi); } } void out_of_memory(void) { modalfatalbox("Out of memory"); } putty-0.76/misc.h0000644000175000017500000003761214072266311010673 00000000000000/* * Header for misc.c. */ #ifndef PUTTY_MISC_H #define PUTTY_MISC_H #include "defs.h" #include "puttymem.h" #include "marshal.h" #include /* for FILE * */ #include /* for va_list */ #include /* for abort */ #include /* for struct tm */ #include /* for INT_MAX/MIN */ #include /* for assert (obviously) */ unsigned long parse_blocksize(const char *bs); char ctrlparse(char *s, char **next); size_t host_strcspn(const char *s, const char *set); char *host_strchr(const char *s, int c); char *host_strrchr(const char *s, int c); char *host_strduptrim(const char *s); char *dupstr(const char *s); char *dupcat_fn(const char *s1, ...); #define dupcat(...) dupcat_fn(__VA_ARGS__, (const char *)NULL) char *dupprintf(const char *fmt, ...) PRINTF_LIKE(1, 2); char *dupvprintf(const char *fmt, va_list ap); void burnstr(char *string); /* * The visible part of a strbuf structure. There's a surrounding * implementation struct in misc.c, which isn't exposed to client * code. */ struct strbuf { char *s; unsigned char *u; size_t len; BinarySink_IMPLEMENTATION; }; /* strbuf constructors: strbuf_new_nm and strbuf_new differ in that a * strbuf constructed using the _nm version will resize itself by * alloc/copy/smemclr/free instead of realloc. Use that version for * data sensitive enough that it's worth costing performance to * avoid copies of it lingering in process memory. */ strbuf *strbuf_new(void); strbuf *strbuf_new_nm(void); void strbuf_free(strbuf *buf); void *strbuf_append(strbuf *buf, size_t len); void strbuf_shrink_to(strbuf *buf, size_t new_len); void strbuf_shrink_by(strbuf *buf, size_t amount_to_remove); char *strbuf_to_str(strbuf *buf); /* does free buf, but you must free result */ void strbuf_catf(strbuf *buf, const char *fmt, ...) PRINTF_LIKE(2, 3); void strbuf_catfv(strbuf *buf, const char *fmt, va_list ap); static inline void strbuf_clear(strbuf *buf) { strbuf_shrink_to(buf, 0); } bool strbuf_chomp(strbuf *buf, char char_to_remove); strbuf *strbuf_new_for_agent_query(void); void strbuf_finalise_agent_query(strbuf *buf); /* String-to-Unicode converters that auto-allocate the destination and * work around the rather deficient interface of mb_to_wc. * * These actually live in miscucs.c, not misc.c (the distinction being * that the former is only linked into tools that also have the main * Unicode support). */ wchar_t *dup_mb_to_wc_c(int codepage, int flags, const char *string, int len); wchar_t *dup_mb_to_wc(int codepage, int flags, const char *string); static inline 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', and since it's * also declared inline, elide it completely in their output. */ 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 */ } char *fgetline(FILE *fp); bool read_file_into(BinarySink *bs, FILE *fp); char *chomp(char *str); bool strstartswith(const char *s, const char *t); bool strendswith(const char *s, const char *t); void base64_encode_atom(const unsigned char *data, int n, char *out); int base64_decode_atom(const char *atom, unsigned char *out); struct bufchain_granule; struct bufchain_tag { struct bufchain_granule *head, *tail; size_t buffersize; /* current amount of buffered data */ void (*queue_idempotent_callback)(IdempotentCallback *ic); IdempotentCallback *ic; }; void bufchain_init(bufchain *ch); void bufchain_clear(bufchain *ch); size_t bufchain_size(bufchain *ch); void bufchain_add(bufchain *ch, const void *data, size_t len); ptrlen bufchain_prefix(bufchain *ch); void bufchain_consume(bufchain *ch, size_t len); void bufchain_fetch(bufchain *ch, void *data, size_t len); void bufchain_fetch_consume(bufchain *ch, void *data, size_t len); bool bufchain_try_fetch_consume(bufchain *ch, void *data, size_t len); size_t bufchain_fetch_consume_up_to(bufchain *ch, void *data, size_t len); void bufchain_set_callback_inner( bufchain *ch, IdempotentCallback *ic, void (*queue_idempotent_callback)(IdempotentCallback *ic)); static inline void bufchain_set_callback(bufchain *ch, IdempotentCallback *ic) { extern void queue_idempotent_callback(struct IdempotentCallback *ic); /* Wrapper that puts in the standard queue_idempotent_callback * function. Lives here rather than in utils.c so that standalone * programs can use the bufchain facility without this optional * callback feature and not need to provide a stub of * queue_idempotent_callback. */ bufchain_set_callback_inner(ch, ic, queue_idempotent_callback); } bool validate_manual_hostkey(char *key); struct tm ltime(void); /* * Special form of strcmp which can cope with NULL inputs. NULL is * defined to sort before even the empty string. */ int nullstrcmp(const char *a, const char *b); static inline ptrlen make_ptrlen(const void *ptr, size_t len) { ptrlen pl; pl.ptr = ptr; pl.len = len; return pl; } static inline ptrlen ptrlen_from_asciz(const char *str) { return make_ptrlen(str, strlen(str)); } static inline ptrlen ptrlen_from_strbuf(strbuf *sb) { return make_ptrlen(sb->u, sb->len); } bool ptrlen_eq_string(ptrlen pl, const char *str); bool ptrlen_eq_ptrlen(ptrlen pl1, ptrlen pl2); int ptrlen_strcmp(ptrlen pl1, ptrlen pl2); /* ptrlen_startswith and ptrlen_endswith write through their 'tail' * argument if and only if it is non-NULL and they return true. Hence * you can write ptrlen_startswith(thing, prefix, &thing), writing * back to the same ptrlen it read from, to remove a prefix if present * and say whether it did so. */ bool ptrlen_startswith(ptrlen whole, ptrlen prefix, ptrlen *tail); bool ptrlen_endswith(ptrlen whole, ptrlen suffix, ptrlen *tail); ptrlen ptrlen_get_word(ptrlen *input, const char *separators); char *mkstr(ptrlen pl); int string_length_for_printf(size_t); /* Derive two printf arguments from a ptrlen, suitable for "%.*s" */ #define PTRLEN_PRINTF(pl) \ string_length_for_printf((pl).len), (const char *)(pl).ptr /* Make a ptrlen out of a compile-time string literal. We try to * enforce that it _is_ a string literal by token-pasting "" on to it, * which should provoke a compile error if it's any other kind of * string. */ #define PTRLEN_LITERAL(stringlit) \ TYPECHECK("" stringlit "", make_ptrlen(stringlit, sizeof(stringlit)-1)) /* Make a ptrlen out of a compile-time string literal in a way that * allows you to declare the ptrlen itself as a compile-time initialiser. */ #define PTRLEN_DECL_LITERAL(stringlit) \ { TYPECHECK("" stringlit "", stringlit), sizeof(stringlit)-1 } /* Make a ptrlen out of a constant byte array. */ #define PTRLEN_FROM_CONST_BYTES(a) make_ptrlen(a, sizeof(a)) /* Wipe sensitive data out of memory that's about to be freed. Simpler * than memset because we don't need the fill char parameter; also * attempts (by fiddly use of volatile) to inhibit the compiler from * over-cleverly trying to optimise the memset away because it knows * the variable is going out of scope. */ void smemclr(void *b, size_t len); /* Compare two fixed-length chunks of memory for equality, without * data-dependent control flow (so an attacker with a very accurate * stopwatch can't try to guess where the first mismatching byte was). * Returns false for mismatch or true for equality (unlike memcmp), * hinted at by the 'eq' in the name. */ bool smemeq(const void *av, const void *bv, size_t len); /* Encode a single UTF-8 character. Assumes that illegal characters * (such as things in the surrogate range, or > 0x10FFFF) have already * been removed. */ size_t encode_utf8(void *output, unsigned long ch); /* Write a string out in C string-literal format. */ void write_c_string_literal(FILE *fp, ptrlen str); char *buildinfo(const char *newline); /* * A function you can put at points in the code where execution should * never reach in the first place. Better than assert(false), or even * assert(false && "some explanatory message"), because some compilers * don't interpret assert(false) as a declaration of unreachability, * so they may still warn about pointless things like some variable * not being initialised on the unreachable code path. * * I follow the assertion with a call to abort() just in case someone * compiles with -DNDEBUG, and I wrap that abort inside my own * function labelled NORETURN just in case some unusual kind of system * header wasn't foresighted enough to label abort() itself that way. */ static inline NORETURN void unreachable_internal(void) { abort(); } #define unreachable(msg) (assert(false && msg), unreachable_internal()) /* * Debugging functions. * * Output goes to debug.log * * debug() is like printf(). * * dmemdump() and dmemdumpl() both do memory dumps. The difference * is that dmemdumpl() is more suited for when the memory address is * important (say because you'll be recording pointer values later * on). dmemdump() is more concise. */ #ifdef DEBUG void debug_printf(const char *fmt, ...) PRINTF_LIKE(1, 2); void debug_memdump(const void *buf, int len, bool L); #define debug(...) (debug_printf(__VA_ARGS__)) #define dmemdump(buf,len) (debug_memdump(buf, len, false)) #define dmemdumpl(buf,len) (debug_memdump(buf, len, true)) #else #define debug(...) ((void)0) #define dmemdump(buf,len) ((void)0) #define dmemdumpl(buf,len) ((void)0) #endif #ifndef lenof #define lenof(x) ( (sizeof((x))) / (sizeof(*(x)))) #endif #ifndef min #define min(x,y) ( (x) < (y) ? (x) : (y) ) #endif #ifndef max #define max(x,y) ( (x) > (y) ? (x) : (y) ) #endif static inline uint64_t GET_64BIT_LSB_FIRST(const void *vp) { const uint8_t *p = (const uint8_t *)vp; return (((uint64_t)p[0] ) | ((uint64_t)p[1] << 8) | ((uint64_t)p[2] << 16) | ((uint64_t)p[3] << 24) | ((uint64_t)p[4] << 32) | ((uint64_t)p[5] << 40) | ((uint64_t)p[6] << 48) | ((uint64_t)p[7] << 56)); } static inline void PUT_64BIT_LSB_FIRST(void *vp, uint64_t value) { uint8_t *p = (uint8_t *)vp; p[0] = (uint8_t)(value); p[1] = (uint8_t)(value >> 8); p[2] = (uint8_t)(value >> 16); p[3] = (uint8_t)(value >> 24); p[4] = (uint8_t)(value >> 32); p[5] = (uint8_t)(value >> 40); p[6] = (uint8_t)(value >> 48); p[7] = (uint8_t)(value >> 56); } static inline uint32_t GET_32BIT_LSB_FIRST(const void *vp) { const uint8_t *p = (const uint8_t *)vp; return (((uint32_t)p[0] ) | ((uint32_t)p[1] << 8) | ((uint32_t)p[2] << 16) | ((uint32_t)p[3] << 24)); } static inline void PUT_32BIT_LSB_FIRST(void *vp, uint32_t value) { uint8_t *p = (uint8_t *)vp; p[0] = (uint8_t)(value); p[1] = (uint8_t)(value >> 8); p[2] = (uint8_t)(value >> 16); p[3] = (uint8_t)(value >> 24); } static inline uint16_t GET_16BIT_LSB_FIRST(const void *vp) { const uint8_t *p = (const uint8_t *)vp; return (((uint16_t)p[0] ) | ((uint16_t)p[1] << 8)); } static inline void PUT_16BIT_LSB_FIRST(void *vp, uint16_t value) { uint8_t *p = (uint8_t *)vp; p[0] = (uint8_t)(value); p[1] = (uint8_t)(value >> 8); } static inline uint64_t GET_64BIT_MSB_FIRST(const void *vp) { const uint8_t *p = (const uint8_t *)vp; return (((uint64_t)p[7] ) | ((uint64_t)p[6] << 8) | ((uint64_t)p[5] << 16) | ((uint64_t)p[4] << 24) | ((uint64_t)p[3] << 32) | ((uint64_t)p[2] << 40) | ((uint64_t)p[1] << 48) | ((uint64_t)p[0] << 56)); } static inline void PUT_64BIT_MSB_FIRST(void *vp, uint64_t value) { uint8_t *p = (uint8_t *)vp; p[7] = (uint8_t)(value); p[6] = (uint8_t)(value >> 8); p[5] = (uint8_t)(value >> 16); p[4] = (uint8_t)(value >> 24); p[3] = (uint8_t)(value >> 32); p[2] = (uint8_t)(value >> 40); p[1] = (uint8_t)(value >> 48); p[0] = (uint8_t)(value >> 56); } static inline uint32_t GET_32BIT_MSB_FIRST(const void *vp) { const uint8_t *p = (const uint8_t *)vp; return (((uint32_t)p[3] ) | ((uint32_t)p[2] << 8) | ((uint32_t)p[1] << 16) | ((uint32_t)p[0] << 24)); } static inline void PUT_32BIT_MSB_FIRST(void *vp, uint32_t value) { uint8_t *p = (uint8_t *)vp; p[3] = (uint8_t)(value); p[2] = (uint8_t)(value >> 8); p[1] = (uint8_t)(value >> 16); p[0] = (uint8_t)(value >> 24); } static inline uint16_t GET_16BIT_MSB_FIRST(const void *vp) { const uint8_t *p = (const uint8_t *)vp; return (((uint16_t)p[1] ) | ((uint16_t)p[0] << 8)); } static inline void PUT_16BIT_MSB_FIRST(void *vp, uint16_t value) { uint8_t *p = (uint8_t *)vp; p[1] = (uint8_t)(value); p[0] = (uint8_t)(value >> 8); } /* Replace NULL with the empty string, permitting an idiom in which we * get a string (pointer,length) pair that might be NULL,0 and can * then safely say things like printf("%.*s", length, NULLTOEMPTY(ptr)) */ static inline const char *NULLTOEMPTY(const char *s) { return s ? s : ""; } /* StripCtrlChars, defined in stripctrl.c: an adapter you can put on * the front of one BinarySink and which functions as one in turn. * Interprets its input as a stream of multibyte characters in the * system locale, and removes any that are not either printable * characters or newlines. */ struct StripCtrlChars { BinarySink_IMPLEMENTATION; /* and this is contained in a larger structure */ }; StripCtrlChars *stripctrl_new( BinarySink *bs_out, bool permit_cr, wchar_t substitution); StripCtrlChars *stripctrl_new_term_fn( BinarySink *bs_out, bool permit_cr, wchar_t substitution, Terminal *term, unsigned long (*translate)( Terminal *, term_utf8_decode *, unsigned char)); #define stripctrl_new_term(bs, cr, sub, term) \ stripctrl_new_term_fn(bs, cr, sub, term, term_translate) void stripctrl_retarget(StripCtrlChars *sccpub, BinarySink *new_bs_out); void stripctrl_reset(StripCtrlChars *sccpub); void stripctrl_free(StripCtrlChars *sanpub); void stripctrl_enable_line_limiting(StripCtrlChars *sccpub); char *stripctrl_string_ptrlen(StripCtrlChars *sccpub, ptrlen str); static inline char *stripctrl_string(StripCtrlChars *sccpub, const char *str) { return stripctrl_string_ptrlen(sccpub, ptrlen_from_asciz(str)); } /* * A mechanism for loading a file from disk into a memory buffer where * it can be picked apart as a BinarySource. */ struct LoadedFile { char *data; size_t len, max_size; BinarySource_IMPLEMENTATION; }; typedef enum { LF_OK, /* file loaded successfully */ LF_TOO_BIG, /* file didn't fit in buffer */ LF_ERROR, /* error from stdio layer */ } LoadFileStatus; LoadedFile *lf_new(size_t max_size); void lf_free(LoadedFile *lf); LoadFileStatus lf_load_fp(LoadedFile *lf, FILE *fp); LoadFileStatus lf_load(LoadedFile *lf, const Filename *filename); static inline ptrlen ptrlen_from_lf(LoadedFile *lf) { return make_ptrlen(lf->data, lf->len); } /* Set the memory block of 'size' bytes at 'out' to the bitwise XOR of * the two blocks of the same size at 'in1' and 'in2'. * * 'out' may point to exactly the same address as one of the inputs, * but if the input and output blocks overlap in any other way, the * result of this function is not guaranteed. No memmove-style effort * is made to handle difficult overlap cases. */ void memxor(uint8_t *out, const uint8_t *in1, const uint8_t *in2, size_t size); #endif putty-0.76/miscucs.c0000644000175000017500000000135114072266311011370 00000000000000/* * Centralised Unicode-related helper functions, separate from misc.c * so that they can be omitted from tools that aren't including * Unicode handling. */ #include "putty.h" #include "misc.h" wchar_t *dup_mb_to_wc_c(int codepage, int flags, const char *string, int len) { int mult; for (mult = 1 ;; mult++) { wchar_t *ret = snewn(mult*len + 2, wchar_t); int outlen; outlen = mb_to_wc(codepage, flags, string, len, ret, mult*len + 1); if (outlen < mult*len+1) { ret[outlen] = L'\0'; return ret; } sfree(ret); } } wchar_t *dup_mb_to_wc(int codepage, int flags, const char *string) { return dup_mb_to_wc_c(codepage, flags, string, strlen(string)); } putty-0.76/mkauto.sh0000755000175000017500000000056314072266311011421 00000000000000#! /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. autoreconf -i && rm -rf autom4te.cache putty-0.76/mkfiles.pl0000755000175000017500000024563214072266311011564 00000000000000#!/usr/bin/env perl # # Cross-platform Makefile generator. # # Reads the file `Recipe' to determine the list of generated # executables and their component objects. Then reads the source # files to compute #include dependencies. Finally, writes out the # various target Makefiles. # PuTTY specifics which could still do with removing: # - Mac makefile is not portabilised at all. Include directories # are hardwired, and also the libraries are fixed. This is # 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; use Digest::SHA qw(sha512_hex); 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, and licence.h is likewise generated by licence.pl. We # need to generate those _now_, before attempting dependency analysis. eval 'chdir "charset"; require "./sbcsgen.pl"; chdir ".."; select STDOUT;'; eval 'require "./licence.pl"; 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 %makefile_extra = (); # maps makefile types to extra Makefile text %programs = (); # maps prog name + type letter to listref of objects/resources %groups = (); # maps group name to listref of objects/resources while () { chomp; @_ = split; # If we're gathering help text, keep doing so. if (defined $divert) { if ((defined $_[0]) && $_[0] eq "!end") { $divert = undef; } else { ${$divert} .= "$_\n"; } next; } # Skip comments and blank lines. next if /^\s*#/ or scalar @_ == 0; 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 if ($rest eq "") { # Make sure this file doesn't get lumped together with any # other file's cflags. $rest = "F" . $_[2]; } else { # Give this file a specific set of cflags, but permit it to # go together with other files using the same set. $rest = "C" . $rest; } $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; } # If we're gathering help/verbatim text, keep doing so. if (defined $divert) { ${$divert} .= "$_\n"; next; } # Ignore blank lines. next if scalar @_ == 0; # Now we have an ordinary line. See if it's an = line, a : line # or a + line. @objs = @_; if ($_[0] eq "+") { $listref = $lastlistref; $prog = undef; die "$.: unexpected + line\n" if !defined $lastlistref; } elsif ($#_ >= 1 && $_[1] eq "=") { $groups{$_[0]} = [] if !defined $groups{$_[0]}; $listref = $groups{$_[0]}; $prog = undef; shift @objs; # eat the group name } elsif ($#_ >= 1 && $_[1] eq ":") { $listref = []; $prog = $_[0]; shift @objs; # eat the program name } else { die "$.: unrecognised line type\n"; } shift @objs; # eat the +, the = or the : while (scalar @objs > 0) { $i = shift @objs; if ($groups{$i}) { foreach $j (@{$groups{$i}}) { unshift @objs, $j; } } elsif (($i =~ /^\[([A-Z]*)\]$/) and defined $prog) { $type = substr($i,1,(length $i)-2); die "unrecognised program type for $prog [$type]\n" if ! grep { $type eq $_ } qw(G C X U MX XT UT); } else { push @$listref, $i; } } if ($prog and $type) { die "multiple program entries for $prog [$type]\n" 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 = (); foreach $i (@prognames) { ($prog, $type) = split ",", $i; # Strip duplicate object names. $prev = ''; @list = grep { $status = ($prev ne $_); $prev=$_; $status } sort @{$programs{$i}}; $programs{$i} = [@list]; foreach $j (@list) { # Dependencies for "x" start with "x.c" or "x.m" (depending on # which one exists). # Dependencies for "x.res" start with "x.rc". # Dependencies for "x.rsrc" start with "x.r". # Both types of file are pushed on the list of files to scan. # Libraries (.lib) don't have dependencies at all. if ($j =~ /^(.*)\.res$/) { $file = "$1.rc"; $depends{$j} = [$file]; push @scanlist, $file; } elsif ($j =~ /^(.*)\.rsrc$/) { $file = "$1.r"; $depends{$j} = [$file]; push @scanlist, $file; } elsif ($j !~ /\./) { $file = "$j.c"; $file = "$j.m" unless &findfile($file); $depends{$j} = [$file]; push @scanlist, $file; } } } # Scan each file on @scanlist and find further inclusions. # Inclusions are given by lines of the form `#include "otherfile"' # (system headers are automatically ignored by this because they'll # be given in angle brackets). Files included by this method are # added back on to @scanlist to be scanned in turn (if not already # done). # # Resource scripts (.rc) can also include a file by means of: # - a line # ending `ICON "filename"'; # - a line ending `RT_MANIFEST "filename"'. # 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 () { chomp; /^\s*#include\s+\"([^\"]+)\"/ and do { push @{$further{$file}}, $1; push @scanlist, $1; next; }; /(RT_MANIFEST|ICON)\s+\"([^\"]+)\"\s*$/ and do { push @{$further{$file}}, $2; next; } } close IN; } # Now we're ready to generate the final dependencies section. For # each key in %depends, we must expand the dependencies list by # iteratively adding entries from %further. foreach $i (keys %depends) { %dep = (); @scanlist = @{$depends{$i}}; foreach $i (@scanlist) { $dep{$i} = 1; } while (scalar @scanlist > 0) { $file = shift @scanlist; foreach $j (@{$further{$file}}) { if (!$dep{$j}) { $dep{$j} = 1; push @{$depends{$i}}, $j; push @scanlist, $j; } } } # printf "%s: %s\n", $i, join ' ',@{$depends{$i}}; } # Validation of input. 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","lcc","devcppproj","gtk","unix", "am","osx","vstudio10","vstudio12","clangcl")) { return 1; } warn "$.:unknown makefile type '$type'\n"; return 0; } # Utility routines while writing out the Makefiles. sub def { my ($x) = shift @_; return (defined $x) ? $x : ""; } sub dirpfx { my ($path) = shift @_; my ($sep) = shift @_; my $ret = ""; my $i; while (($i = index $path, $sep) >= 0 || ($j = index $path, "/") >= 0) { if ($i >= 0 and ($j < 0 or $i < $j)) { $path = substr $path, ($i + length $sep); } else { $path = substr $path, ($j + 1); } $ret .= "..$sep"; } return $ret; } sub findfile { my ($name) = @_; my $dir = ''; my $i; my $outdir = undef; unless (defined $findfilecache{$name}) { $i = 0; foreach $dir (@srcdirs) { if (-f "$dir$name") { $outdir = $dir; $i++; $outdir =~ s/^\.\///; } } die "multiple instances of source file $name\n" if $i > 1; $findfilecache{$name} = (defined $outdir ? $outdir . $name : undef); } return $findfilecache{$name}; } sub objects { my ($prog, $otmpl, $rtmpl, $ltmpl, $prefix, $dirsep) = @_; my @ret; my ($i, $x, $y); ($otmpl, $rtmpl, $ltmpl) = map { defined $_ ? $_ : "" } ($otmpl, $rtmpl, $ltmpl); @ret = (); foreach $i (@{$programs{$prog}}) { $x = ""; if ($i =~ /^(.*)\.(res|rsrc)/) { $y = $1; ($x = $rtmpl) =~ s/X/$y/; } elsif ($i =~ /^(.*)\.lib/) { $y = $1; ($x = $ltmpl) =~ s/X/$y/; } elsif ($i !~ /\./) { ($x = $otmpl) =~ s/X/$i/; } push @ret, $x if $x ne ""; } return join " ", @ret; } sub special { my ($prog, $suffix) = @_; my @ret; my ($i, $x, $y); ($otmpl, $rtmpl, $ltmpl) = map { defined $_ ? $_ : "" } ($otmpl, $rtmpl, $ltmpl); @ret = (); foreach $i (@{$programs{$prog}}) { if (substr($i, (length $i) - (length $suffix)) eq $suffix) { push @ret, $i; } } return (scalar @ret) ? (join " ", @ret) : undef; } sub splitline { my ($line, $width, $splitchar) = @_; my $result = ""; my $len; $len = (defined $width ? $width : 76); $splitchar = (defined $splitchar ? $splitchar : '\\'); while (length $line > $len) { $line =~ /^(.{0,$len})\s(.*)$/ or $line =~ /^(.{$len,})?\s(.*)$/; $result .= $1; $result .= " ${splitchar}\n\t\t" if $2 ne ''; $line = $2; $len = 60; } return $result . $line; } sub deps { my ($otmpl, $rtmpl, $prefix, $dirsep, $mftyp, $depchar, $splitchar) = @_; my ($i, $x, $y); my @deps; my @ret; @ret = (); $depchar ||= ':'; foreach $i (sort keys %depends) { next if $specialobj{$mftyp}->{$i}; if ($i =~ /^(.*)\.(res|rsrc)/) { next if !defined $rtmpl; $y = $1; ($x = $rtmpl) =~ s/X/$y/; } else { ($x = $otmpl) =~ s/X/$i/; } @deps = @{$depends{$i}}; @deps = map { $_ = &findfile($_); s/\//$dirsep/g; $_ = $prefix . $_; } @deps; push @ret, {obj => $x, obj_orig => $i, deps => [@deps]}; } return @ret; } sub prognames { my ($types) = @_; my ($n, $prog, $type); my @ret; @ret = (); foreach $n (@prognames) { ($prog, $type) = split ",", $n; push @ret, $n if index(":$types:", ":$type:") >= 0; } return @ret; } sub progrealnames { my ($types) = @_; my ($n, $prog, $type); my @ret; @ret = (); foreach $n (@prognames) { ($prog, $type) = split ",", $n; push @ret, $prog if index(":$types:", ":$type:") >= 0; } return @ret; } sub manpages { my ($types,$suffix) = @_; # 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{'clangcl'}) { $dirpfx = &dirpfx($makefiles{'clangcl'}, "/"); ##-- Makefile for cross-compiling using clang-cl, lld-link, and ## MinGW's windres for resource compilation. # # This makefile allows a complete Linux-based cross-compile, but # using the real Visual Studio header files and libraries. In # order to run it, you will need: # # - clang-cl, llvm-rc and lld-link on your PATH. # * I built these from the up-to-date LLVM project trunk git # repositories, as of 2018-05-29. # - case-mashed copies of the Visual Studio include directories. # * On a real VS installation, run vcvars32.bat and look at # the resulting value of %INCLUDE%. Take a full copy of each # of those directories, and inside the copy, for each # include file that has an uppercase letter in its name, # make a lowercased symlink to it. Additionally, one of the # directories will contain files called driverspecs.h and # specstrings.h, and those will need symlinks called # DriverSpecs.h and SpecStrings.h. # * Now, on Linux, define the environment variable INCLUDE to # be a list, separated by *semicolons* (in the Windows # style), of those directories, but before all of them you # must also include lib/clang/5.0.0/include from the clang # installation area (which contains in particular a # clang-compatible stdarg.h overriding the Visual Studio # one). # - similarly case-mashed copies of the library directories. # * Again, on a real VS installation, run vcvars32 or # vcvarsx86_amd64 (as appropriate), look at %LIB%, make a # copy of each directory, and provide symlinks within that # directory so that all the files can be opened as # lowercase. # * Then set LIB to be a semicolon-separated list of those # directories (but you'll need to change which set of # directories depending on whether you want to do a 32-bit # or 64-bit build). # - for a 64-bit build, set 'Platform=x64' in the environment as # well, or else on the make command line. # * This is a variable understood only by this makefile - none # of the tools we invoke will know it - but it's consistent # with the way the VS scripts like vcvarsx86_amd64.bat set # things up, and since the environment has to change # _anyway_ between 32- and 64-bit builds (different set of # paths in $LIB) it's reasonable to have the choice of # compilation target driven by another environment variable # set in parallel with that one. # - for older versions of the VS libraries you may also have to # set EXTRA_console and/or EXTRA_windows to the name of an # object file manually extracted from one of those libraries. # * This is because old VS seems to manage its startup code by # having libcmt.lib contain lots of *crt0.obj objects, one # for each possible user entry point (main, WinMain and the # wide-char versions of both), of which the linker arranges # to include the right one by special-case code. But lld # only seems to mimic half of that code - it does include # the right crt0 object, but it doesn't also deliberately # _avoid_ including the _wrong_ ones, and since all those # objects define a common set of global symbols for other # parts of the library to use, lld may well select an # arbitrary one of them the first time it sees a reference # to one of those global symbols, and then later also select # the _right_ one for the application's entry point, causing # a multiple-definitions crash. # * So the workaround is to explicitly include the right # *crt0.obj file on the linker command line before lld even # begins searching libraries. Hence, for a console # application, you might extract crt0.obj from the library # in question and set EXTRA_console=crt0.obj, and for a GUI # application, do the same with wincrt0.obj. Then this # makefile will include the right one of those objects # alongside the matching /subsystem linker option. # - also for older versions of the VS libraries, you may also # have to set EXTRA_libs to include extra library files. open OUT, ">$makefiles{'clangcl'}"; select OUT; print "# Makefile for cross-compiling $project_name using clang-cl, lld-link,\n". "# and llvm-rc, using GNU make on Linux.\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"; print $help; print "\n". "CCCMD = clang-cl\n". "RCCMD = llvm-rc\n". "ifeq (\$(Platform),x64)\n". "CCTARGET = x86_64-pc-windows-msvc18.0.0\n". "PLATFORMCFLAGS =\n". "else ifeq (\$(Platform),arm)\n". "CCTARGET = arm-pc-windows-msvc18.0.0\n". "PLATFORMCFLAGS = /D_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE /GS-\n". "else ifeq (\$(Platform),arm64)\n". "CCTARGET = arm64-pc-windows-msvc18.0.0\n". "PLATFORMCFLAGS = /D_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE /GS-\n". "else\n". "CCTARGET = i386-pc-windows-msvc18.0.0\n". "PLATFORMCFLAGS =\n". "endif\n". "CC = \$(CCCMD)\n". "RC = \$(RCCMD) /c 1252 \n". "RCPREPROC = \$(CCCMD) /P /TC\n". "LD = lld-link\n". "\n". "# C compilation flags\n". &splitline("CFLAGS = --target=\$(CCTARGET) /nologo /W3 /O1 -Wvla " . (join " ", map {"-I$dirpfx$_"} @srcdirs) . " /D_WINDOWS /D_WIN32_WINDOWS=0x500 /DWINVER=0x500 ". "/D_CRT_SECURE_NO_WARNINGS /D_WINSOCK_DEPRECATED_NO_WARNINGS"). " \$(PLATFORMCFLAGS)\n". "LFLAGS = /incremental:no /dynamicbase /nxcompat\n". &splitline("RCPPFLAGS = ".(join " ", map {"-I$dirpfx$_"} @srcdirs). " -DWIN32 -D_WIN32 -DWINVER=0x0400")." \$(RCFL)\n". "\n". &def($makefile_extra{'clangcl'}->{'vars'}) . "\n". "\n"; print &splitline("all:" . join "", map { " \$(BUILDDIR)$_.exe" } &progrealnames("G:C")); print "\n\n"; foreach $p (&prognames("G:C")) { ($prog, $type) = split ",", $p; $objstr = &objects($p, "\$(BUILDDIR)X.obj", "\$(BUILDDIR)X.res", undef); print &splitline("\$(BUILDDIR)$prog.exe: " . $objstr), "\n"; $objstr = &objects($p, "\$(BUILDDIR)X.obj", "\$(BUILDDIR)X.res", "X.lib"); $subsys = ($type eq "G") ? "windows" : "console"; print &splitline("\t\$(LD) \$(LFLAGS) \$(XLFLAGS) ". "/out:\$(BUILDDIR)$prog.exe ". "/lldmap:\$(BUILDDIR)$prog.map ". "/subsystem:$subsys\$(SUBSYSVER) ". "\$(EXTRA_$subsys) $objstr \$(EXTRA_libs)")."\n\n"; } my $rc_pp_rules = ""; foreach $d (&deps("\$(BUILDDIR)X.obj", "\$(BUILDDIR)X.res", $dirpfx, "/", "vc")) { $extradeps = $forceobj{$d->{obj_orig}} ? ["*.c","*.h","*.rc"] : []; my $rule; my @deps = @{$d->{deps}}; my @incdeps = grep { m!\.rc2?$! } @deps; my @rcdeps = grep { ! m!\.rc2$! } @deps; if ($d->{obj} =~ /\.res$/) { my $rc = $deps[0]; my $rcpp = $rc; $rcpp =~ s!.*/!!; $rcpp =~ s/\.rc$/.rcpp/; $rcpp = "\$(BUILDDIR)" . $rcpp; $rule = "\$(RC) ".$rcpp." /FO ".$d->{obj}; $rc_pp_rules .= &splitline( sprintf("%s: %s", $rcpp, join " ", @incdeps)) ."\n" . "\t\$(RCPREPROC) \$(RCPPFLAGS) /Fi\$\@ \$<\n\n"; $rcdeps[0] = $rcpp; } else { $rule = "\$(CC) /Fo\$(BUILDDIR) \$(COMPAT) \$(CFLAGS) \$(XFLAGS) /c \$<"; } print &splitline(sprintf("%s: %s", $d->{obj}, join " ", @$extradeps, @rcdeps)), "\n"; print "\t" . $rule . "\n\n"; } print "\n" . $rc_pp_rules; print &def($makefile_extra{'clangcl'}->{'end'}); print "\nclean:\n". &splitline("\trm -f \$(BUILDDIR)*.obj \$(BUILDDIR)*.exe ". "\$(BUILDDIR)*.rcpp \$(BUILDDIR)*.res \$(BUILDDIR)*.map ". "\$(BUILDDIR)*.exe.manifest")."\n"; select STDOUT; close OUT; } if (defined $makefiles{'cygwin'}) { $dirpfx = &dirpfx($makefiles{'cygwin'}, "/"); ##-- MinGW/CygWin makefile (called 'cygwin' for historical reasons) open OUT, ">$makefiles{'cygwin'}"; select OUT; print "# Makefile for $project_name under MinGW, Cygwin, 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". "# TOOLPATH = i686-w64-mingw32-\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 = -Wall -O2 -std=gnu99 -Wvla -D_WINDOWS". " -DWIN32S_COMPAT -D_NO_OLDNAMES -D__USE_MINGW_ANSI_STDIO=1 " . (join " ", map {"-I$dirpfx$_"} @srcdirs)) . "\n". "LDFLAGS = -s\n". &splitline("RCFLAGS = \$(RCINC) --define WIN32=1 --define _WIN32=1 ". "--define WINVER=0x0400 ".(join " ", map {"-I$dirpfx$_"} @srcdirs))."\n". "\n". &def($makefile_extra{'cygwin'}->{'vars'}) . "\n". ".SUFFIXES:\n". "\n"; print &splitline("all:" . join "", map { " $_.exe" } &progrealnames("G:C")); print "\n\n"; foreach $p (&prognames("G:C")) { ($prog, $type) = split ",", $p; $objstr = &objects($p, "X.o", "X.res.o", undef); print &splitline($prog . ".exe: " . $objstr), "\n"; my $mw = $type eq "G" ? " -mwindows" : ""; $libstr = &objects($p, undef, undef, "-lX"); print &splitline("\t\$(CC)" . $mw . " \$(LDFLAGS) -o \$@ " . "-Wl,-Map,$prog.map " . $objstr . " $libstr", 69), "\n\n"; } foreach $d (&deps("X.o", "X.res.o", $dirpfx, "/", "cygwin")) { 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 &def($makefile_extra{'cygwin'}->{'end'}); print "\nclean:\n". "\trm -f *.o *.exe *.res.o *.so *.map\n". "\n". "FORCE:\n"; select STDOUT; close OUT; } if (defined $makefiles{'vc'}) { $dirpfx = &dirpfx($makefiles{'vc'}, "\\"); ##-- Visual C++ makefile open OUT, ">$makefiles{'vc'}"; select OUT; print "# Makefile for $project_name under Visual C.\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"; print $help; print "\n". "# If you rename this file to `Makefile', you should change this line,\n". "# so that the .rsp files still depend on the correct makefile.\n". "MAKEFILE = Makefile.vc\n". "\n". "# C compilation flags\n". "CFLAGS = /nologo /W3 /O1 " . (join " ", map {"-I$dirpfx$_"} @srcdirs) . " /D_WINDOWS /D_WIN32_WINDOWS=0x500 /DWINVER=0x500 /D_CRT_SECURE_NO_WARNINGS /D_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE\n". "LFLAGS = /incremental:no /dynamicbase /nxcompat\n". "RCFLAGS = ".(join " ", map {"-I$dirpfx$_"} @srcdirs). " -DWIN32 -D_WIN32 -DWINVER=0x0400\n". "\n". &def($makefile_extra{'vc'}->{'vars'}) . "\n". "\n"; print &splitline("all:" . join "", map { " \$(BUILDDIR)$_.exe" } &progrealnames("G:C")); print "\n\n"; foreach $p (&prognames("G:C")) { ($prog, $type) = split ",", $p; $objstr = &objects($p, "\$(BUILDDIR)X.obj", "\$(BUILDDIR)X.res", undef); print &splitline("\$(BUILDDIR)$prog.exe: " . $objstr), "\n"; $objstr = &objects($p, "\$(BUILDDIR)X.obj", "\$(BUILDDIR)X.res", "X.lib"); $subsys = ($type eq "G") ? "windows" : "console"; $inlinefilename = "link_$prog"; print "\ttype <<$inlinefilename\n"; @objlist = split " ", $objstr; @objlines = (""); foreach $i (@objlist) { if (length($objlines[$#objlines] . " $i") > 72) { push @objlines, ""; } $objlines[$#objlines] .= " $i"; } for ($i=0; $i<=$#objlines; $i++) { print "$objlines[$i]\n"; } print "<<\n"; print "\tlink \$(LFLAGS) \$(XLFLAGS) -out:\$(BUILDDIR)$prog.exe -map:\$(BUILDDIR)$prog.map -nologo -subsystem:$subsys\$(SUBSYSVER) \@$inlinefilename\n\n"; } foreach $d (&deps("\$(BUILDDIR)X.obj", "\$(BUILDDIR)X.res", $dirpfx, "\\", "vc")) { $extradeps = $forceobj{$d->{obj_orig}} ? ["*.c","*.h","*.rc"] : []; print &splitline(sprintf("%s: %s", $d->{obj}, join " ", @$extradeps, @{$d->{deps}})), "\n"; if ($d->{obj} =~ /.res$/) { print "\trc /Fo@{[$d->{obj}]} \$(RCFL) -r \$(RCFLAGS) ".$d->{deps}->[0],"\n\n"; } } print "\n"; foreach $real_srcdir ("", @srcdirs) { $srcdir = $real_srcdir; if ($srcdir ne "") { $srcdir =~ s!/!\\!g; $srcdir = $dirpfx . $srcdir; $srcdir =~ s!\\\.\\!\\!; $srcdir = "{$srcdir}"; } # The double colon at the end of the line makes this a # 'batch-mode inference rule', which means that nmake will # aggregate multiple invocations of the rule and issue just # one cl command with multiple source-file arguments. That # noticeably speeds up builds, since starting up the cl # process is a noticeable overhead and now has to be done far # fewer times. print "${srcdir}.c.obj::\n\tcl /Fo\$(BUILDDIR) \$(COMPAT) \$(CFLAGS) \$(XFLAGS) /c \$<\n\n"; } print &def($makefile_extra{'vc'}->{'end'}); print "\nclean: tidy\n". "\t-del \$(BUILDDIR)*.exe\n\n". "tidy:\n". "\t-del \$(BUILDDIR)*.obj\n". "\t-del \$(BUILDDIR)*.res\n". "\t-del \$(BUILDDIR)*.pch\n". "\t-del \$(BUILDDIR)*.aps\n". "\t-del \$(BUILDDIR)*.ilk\n". "\t-del \$(BUILDDIR)*.pdb\n". "\t-del \$(BUILDDIR)*.rsp\n". "\t-del \$(BUILDDIR)*.dsp\n". "\t-del \$(BUILDDIR)*.dsw\n". "\t-del \$(BUILDDIR)*.ncb\n". "\t-del \$(BUILDDIR)*.opt\n". "\t-del \$(BUILDDIR)*.plg\n". "\t-del \$(BUILDDIR)*.map\n". "\t-del \$(BUILDDIR)*.idb\n". "\t-del \$(BUILDDIR)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 # and explicitly write the CRLFs. # # Create directories if necessary mkdir $makefiles{'vcproj'} if(! -d $makefiles{'vcproj'}); chdir $makefiles{'vcproj'}; @deps = &deps("X.obj", "X.res", $dirpfx, "\\", "vcproj"); %all_object_deps = map {$_->{obj} => $_->{deps}} @deps; # Create the project files # Get names of all Windows projects (GUI and console) my @prognames = &prognames("G:C"); foreach $progname (@prognames) { create_vc_project(\%all_object_deps, $progname); } # Create the workspace file open OUT, ">$project_name.dsw"; binmode OUT; select OUT; print "Microsoft Developer Studio Workspace File, Format Version 6.00\r\n". "# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!\r\n". "\r\n". "###############################################################################\r\n". "\r\n"; # List projects foreach $progname (@prognames) { ($windows_project, $type) = split ",", $progname; print "Project: \"$windows_project\"=\".\\$windows_project\\$windows_project.dsp\" - Package Owner=<4>\r\n"; } print "\r\n". "Package=<5>\r\n". "{{{\r\n". "}}}\r\n". "\r\n". "Package=<4>\r\n". "{{{\r\n". "}}}\r\n". "\r\n". "###############################################################################\r\n". "\r\n". "Global:\r\n". "\r\n". "Package=<5>\r\n". "{{{\r\n". "}}}\r\n". "\r\n". "Package=<3>\r\n". "{{{\r\n". "}}}\r\n". "\r\n". "###############################################################################\r\n". "\r\n"; select STDOUT; close OUT; chdir $orig_dir; sub create_vc_project { my ($all_object_deps, $progname) = @_; # Construct program's dependency info %seen_objects = (); %lib_files = (); %source_files = (); %header_files = (); %resource_files = (); @object_files = split " ", &objects($progname, "X.obj", "X.res", "X.lib"); foreach $object_file (@object_files) { next if defined $seen_objects{$object_file}; $seen_objects{$object_file} = 1; if($object_file =~ /\.lib$/io) { $lib_files{$object_file} = 1; next; } $object_deps = $all_object_deps{$object_file}; foreach $object_dep (@$object_deps) { if($object_dep =~ /\.c$/io) { $source_files{$object_dep} = 1; next; } if($object_dep =~ /\.h$/io) { $header_files{$object_dep} = 1; next; } if($object_dep =~ /\.(rc|ico)$/io) { $resource_files{$object_dep} = 1; next; } } } $libs = join " ", sort keys %lib_files; @source_files = sort keys %source_files; @header_files = sort keys %header_files; @resources = sort keys %resource_files; ($windows_project, $type) = split ",", $progname; mkdir $windows_project if(! -d $windows_project); chdir $windows_project; $subsys = ($type eq "G") ? "windows" : "console"; open OUT, ">$windows_project.dsp"; binmode OUT; select OUT; print "# Microsoft Developer Studio Project File - Name=\"$windows_project\" - Package Owner=<4>\r\n". "# Microsoft Developer Studio Generated Build File, Format Version 6.00\r\n". "# ** DO NOT EDIT **\r\n". "\r\n". "# TARGTYPE \"Win32 (x86) Application\" 0x0101\r\n". "\r\n". "CFG=$windows_project - Win32 Debug\r\n". "!MESSAGE This is not a valid makefile. To build this project using NMAKE,\r\n". "!MESSAGE use the Export Makefile command and run\r\n". "!MESSAGE \r\n". "!MESSAGE NMAKE /f \"$windows_project.mak\".\r\n". "!MESSAGE \r\n". "!MESSAGE You can specify a configuration when running NMAKE\r\n". "!MESSAGE by defining the macro CFG on the command line. For example:\r\n". "!MESSAGE \r\n". "!MESSAGE NMAKE /f \"$windows_project.mak\" CFG=\"$windows_project - Win32 Debug\"\r\n". "!MESSAGE \r\n". "!MESSAGE Possible choices for configuration are:\r\n". "!MESSAGE \r\n". "!MESSAGE \"$windows_project - Win32 Release\" (based on \"Win32 (x86) Application\")\r\n". "!MESSAGE \"$windows_project - Win32 Debug\" (based on \"Win32 (x86) Application\")\r\n". "!MESSAGE \r\n". "\r\n". "# Begin Project\r\n". "# PROP AllowPerConfigDependencies 0\r\n". "# PROP Scc_ProjName \"\"\r\n". "# PROP Scc_LocalPath \"\"\r\n". "CPP=cl.exe\r\n". "MTL=midl.exe\r\n". "RSC=rc.exe\r\n". "\r\n". "!IF \"\$(CFG)\" == \"$windows_project - Win32 Release\"\r\n". "\r\n". "# PROP BASE Use_MFC 0\r\n". "# PROP BASE Use_Debug_Libraries 0\r\n". "# PROP BASE Output_Dir \"Release\"\r\n". "# PROP BASE Intermediate_Dir \"Release\"\r\n". "# PROP BASE Target_Dir \"\"\r\n". "# PROP Use_MFC 0\r\n". "# PROP Use_Debug_Libraries 0\r\n". "# PROP Output_Dir \"Release\"\r\n". "# PROP Intermediate_Dir \"Release\"\r\n". "# PROP Ignore_Export_Lib 0\r\n". "# PROP Target_Dir \"\"\r\n". "# ADD BASE CPP /nologo /W3 /GX /O2 ". (join " ", map {"/I \"..\\..\\$dirpfx$_\""} @srcdirs) . " /D \"WIN32\" /D \"NDEBUG\" /D \"_WINDOWS\" /D \"_MBCS\" /YX /FD /c\r\n". "# ADD CPP /nologo /W3 /GX /O2 ". (join " ", map {"/I \"..\\..\\$dirpfx$_\""} @srcdirs) . " /D \"WIN32\" /D \"NDEBUG\" /D \"_WINDOWS\" /D \"_MBCS\" /YX /FD /c\r\n". "# ADD BASE MTL /nologo /D \"NDEBUG\" /mktyplib203 /win32\r\n". "# ADD MTL /nologo /D \"NDEBUG\" /mktyplib203 /win32\r\n". "# ADD BASE RSC /l 0x809 /d \"NDEBUG\"\r\n". "# ADD RSC /l 0x809 /d \"NDEBUG\"\r\n". "BSC32=bscmake.exe\r\n". "# ADD BASE BSC32 /nologo\r\n". "# ADD BSC32 /nologo\r\n". "LINK32=link.exe\r\n". "# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:$subsys /machine:I386\r\n". "# ADD LINK32 $libs /nologo /subsystem:$subsys /machine:I386\r\n". "# SUBTRACT LINK32 /pdb:none\r\n". "\r\n". "!ELSEIF \"\$(CFG)\" == \"$windows_project - Win32 Debug\"\r\n". "\r\n". "# PROP BASE Use_MFC 0\r\n". "# PROP BASE Use_Debug_Libraries 1\r\n". "# PROP BASE Output_Dir \"Debug\"\r\n". "# PROP BASE Intermediate_Dir \"Debug\"\r\n". "# PROP BASE Target_Dir \"\"\r\n". "# PROP Use_MFC 0\r\n". "# PROP Use_Debug_Libraries 1\r\n". "# PROP Output_Dir \"Debug\"\r\n". "# PROP Intermediate_Dir \"Debug\"\r\n". "# PROP Ignore_Export_Lib 0\r\n". "# PROP Target_Dir \"\"\r\n". "# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od ". (join " ", map {"/I \"..\\..\\$dirpfx$_\""} @srcdirs) . " /D \"WIN32\" /D \"_DEBUG\" /D \"_WINDOWS\" /D \"_MBCS\" /YX /FD /GZ /c\r\n". "# ADD CPP /nologo /W3 /Gm /GX /ZI /Od ". (join " ", map {"/I \"..\\..\\$dirpfx$_\""} @srcdirs) . " /D \"WIN32\" /D \"_DEBUG\" /D \"_WINDOWS\" /D \"_MBCS\" /YX /FD /GZ /c\r\n". "# ADD BASE MTL /nologo /D \"_DEBUG\" /mktyplib203 /win32\r\n". "# ADD MTL /nologo /D \"_DEBUG\" /mktyplib203 /win32\r\n". "# ADD BASE RSC /l 0x809 /d \"_DEBUG\"\r\n". "# ADD RSC /l 0x809 /d \"_DEBUG\"\r\n". "BSC32=bscmake.exe\r\n". "# ADD BASE BSC32 /nologo\r\n". "# ADD BSC32 /nologo\r\n". "LINK32=link.exe\r\n". "# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:$subsys /debug /machine:I386 /pdbtype:sept\r\n". "# ADD LINK32 $libs /nologo /subsystem:$subsys /debug /machine:I386 /pdbtype:sept\r\n". "# SUBTRACT LINK32 /pdb:none\r\n". "\r\n". "!ENDIF \r\n". "\r\n". "# Begin Target\r\n". "\r\n". "# Name \"$windows_project - Win32 Release\"\r\n". "# Name \"$windows_project - Win32 Debug\"\r\n". "# Begin Group \"Source Files\"\r\n". "\r\n". "# PROP Default_Filter \"cpp;c;cxx;rc;def;r;odl;idl;hpj;bat\"\r\n"; foreach $source_file (@source_files) { print "# Begin Source File\r\n". "\r\n". "SOURCE=..\\..\\$source_file\r\n"; if($source_file =~ /ssh\.c/io) { # Disable 'Edit and continue' as Visual Studio can't handle the macros print "\r\n". "!IF \"\$(CFG)\" == \"$windows_project - Win32 Release\"\r\n". "\r\n". "!ELSEIF \"\$(CFG)\" == \"$windows_project - Win32 Debug\"\r\n". "\r\n". "# ADD CPP /Zi\r\n". "\r\n". "!ENDIF \r\n". "\r\n"; } print "# End Source File\r\n"; } print "# End Group\r\n". "# Begin Group \"Header Files\"\r\n". "\r\n". "# PROP Default_Filter \"h;hpp;hxx;hm;inl\"\r\n"; foreach $header_file (@header_files) { print "# Begin Source File\r\n". "\r\n". "SOURCE=..\\..\\$header_file\r\n". "# End Source File\r\n"; } print "# End Group\r\n". "# Begin Group \"Resource Files\"\r\n". "\r\n". "# PROP Default_Filter \"ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe\"\r\n"; foreach $resource_file (@resources) { print "# Begin Source File\r\n". "\r\n". "SOURCE=..\\..\\$resource_file\r\n". "# End Source File\r\n"; } print "# End Group\r\n". "# End Target\r\n". "# End Project\r\n"; select STDOUT; close OUT; chdir ".."; } } if (defined $makefiles{'vstudio10'} || defined $makefiles{'vstudio12'}) { ##-- Visual Studio 2010+ Solution and Projects if (defined $makefiles{'vstudio10'}) { create_vs_solution('vstudio10', "2010", "11.00", "v100"); } if (defined $makefiles{'vstudio12'}) { create_vs_solution('vstudio12', "2012", "12.00", "v110"); } sub create_vs_solution { my ($makefilename, $name, $version, $toolsver) = @_; $dirpfx = &dirpfx($makefiles{$makefilename}, "\\"); @deps = &deps("X.obj", "X.res", $dirpfx, "\\", $makefilename); %all_object_deps = map {$_->{obj} => $_->{deps}} @deps; my @prognames = &prognames("G:C"); # Create the solution file. mkdir $makefiles{$makefilename} if(! -f $makefiles{$makefilename}); chdir $makefiles{$makefilename}; open OUT, ">$project_name.sln"; select OUT; print "Microsoft Visual Studio Solution File, Format Version $version\n" . "# Visual Studio $name\n"; my %projguids = (); foreach $progname (@prognames) { ($windows_project, $type) = split ",", $progname; $projguids{$windows_project} = $guid = &invent_guid("project:$progname"); print "Project(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \"$windows_project\", \"$windows_project\\$windows_project.vcxproj\", \"{$guid}\"\n" . "EndProject\n"; } print "Global\n" . " GlobalSection(SolutionConfigurationPlatforms) = preSolution\n" . " Debug|Win32 = Debug|Win32\n" . " Release|Win32 = Release|Win32\n" . " EndGlobalSection\n" . " GlobalSection(ProjectConfigurationPlatforms) = postSolution\n" ; foreach my $projguid (values %projguids) { print " {$projguid}.Debug|Win32.ActiveCfg = Debug|Win32\n" . " {$projguid}.Debug|Win32.Build.0 = Debug|Win32\n" . " {$projguid}.Release|Win32.ActiveCfg = Release|Win32\n" . " {$projguid}.Release|Win32.Build.0 = Release|Win32\n"; } print " EndGlobalSection\n" . " GlobalSection(SolutionProperties) = preSolution\n" . " HideSolutionNode = FALSE\n" . " EndGlobalSection\n" . "EndGlobal\n"; select STDOUT; close OUT; foreach $progname (@prognames) { ($windows_project, $type) = split ",", $progname; create_vs_project(\%all_object_deps, $windows_project, $type, $projguids{$windows_project}, $toolsver); } chdir $orig_dir; } sub create_vs_project { my ($all_object_deps, $windows_project, $type, $projguid, $toolsver) = @_; # Break down the project's dependency information into the appropriate # groups. %seen_objects = (); %lib_files = (); %source_files = (); %header_files = (); %resource_files = (); %icon_files = (); @object_files = split " ", &objects($progname, "X.obj", "X.res", "X.lib"); foreach $object_file (@object_files) { next if defined $seen_objects{$object_file}; $seen_objects{$object_file} = 1; if($object_file =~ /\.lib$/io) { $lib_files{$object_file} = 1; next; } $object_deps = $all_object_deps{$object_file}; foreach $object_dep (@$object_deps) { if($object_dep eq $object_deps->[0]) { if($object_dep =~ /\.c$/io) { $source_files{$object_dep} = 1; } elsif($object_dep =~ /\.rc$/io) { $resource_files{$object_dep} = 1; } } elsif ($object_dep =~ /\.[ch]$/io) { $header_files{$object_dep} = 1; } elsif ($object_dep =~ /\.ico$/io) { $icon_files{$object_dep} = 1; } } } $libs = join ";", sort keys %lib_files; @source_files = sort keys %source_files; @header_files = sort keys %header_files; @resources = sort keys %resource_files; @icons = sort keys %icon_files; $subsystem = ($type eq "G") ? "Windows" : "Console"; mkdir $windows_project if(! -d $windows_project); chdir $windows_project; open OUT, ">$windows_project.vcxproj"; select OUT; open FILTERS, ">$windows_project.vcxproj.filters"; # The bulk of the project file is just boilerplate stuff, so we # can mostly just dump it out here. Note, buried in the ClCompile # item definition, that we use a debug information format of # ProgramDatabase, which disables the edit-and-continue support # that breaks most of the project builds. print "\n" . "\n" . " \n" . " \n" . " Debug\n" . " Win32\n" . " \n" . " \n" . " Release\n" . " Win32\n" . " \n" . " \n" . " \n" . " \n" . " \n" . " {$projguid}\n" . " \n" . " \n" . " \n" . " Application\n" . " false\n" . " MultiByte\n" . " $toolsver\n" . " \n" . " \n" . " Application\n" . " false\n" . " MultiByte\n" . " $toolsver\n" . " \n" . " \n" . " \n" . " \n" . " \n" . " \n" . " \n" . " \n" . " \n" . " \n" . " \n" . " \n" . " .\\Release\\\n" . " .\\Release\\\n" . " false\n" . " \n" . " \n" . " .\\Debug\\\n" . " .\\Debug\\\n" . " true\n" . " \n" . " \n" . " \n" . " MultiThreaded\n" . " OnlyExplicitInline\n" . " true\n" . " true\n" . " MaxSpeed\n" . " true\n" . " Level3\n" . " " . (join ";", map {"..\\..\\$dirpfx$_"} @srcdirs) . ";%(AdditionalIncludeDirectories)\n" . " WIN32;NDEBUG;_WINDOWS;POSIX;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions)\n" . " .\\Release\\\n" . " .\\Release\\$windows_project.pch\n" . " .\\Release\\\n" . " .\\Release\\\n" . " \n" . " \n" . " true\n" . " NDEBUG;%(PreprocessorDefinitions)\n" . " .\\Release\\$windows_project.tlb\n" . " true\n" . " Win32\n" . " \n" . " \n" . " 0x0809\n" . " NDEBUG;%(PreprocessorDefinitions)\n" . " \n" . " \n" . " true\n" . " .\\Release\\$windows_project.bsc\n" . " \n" . " \n" . " true\n" . " $subsystem\n" . " .\\Release\\$windows_project.exe\n" . " $libs;%(AdditionalDependencies)\n" . " \n" . " \n" . " \n" . " \n" . " MultiThreadedDebug\n" . " Default\n" . " false\n" . " Disabled\n" . " true\n" . " Level3\n" . " true\n" . " ProgramDatabase\n" . " " . (join ";", map {"..\\..\\$dirpfx$_"} @srcdirs) . ";%(AdditionalIncludeDirectories)\n" . " WIN32;_DEBUG;_WINDOWS;POSIX;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions)\n" . " .\\Debug\\\n" . " .\\Debug\\$windows_project.pch\n" . " .\\Debug\\\n" . " .\\Debug\\\n" . " EnableFastChecks\n" . " \n" . " \n" . " true\n" . " _DEBUG;%(PreprocessorDefinitions)\n" . " .\\Debug\\$windows_project.tlb\n" . " true\n" . " Win32\n" . " \n" . " \n" . " 0x0809\n" . " _DEBUG;%(PreprocessorDefinitions)\n" . " \n" . " \n" . " true\n" . " .\\Debug\\$windows_project.bsc\n" . " \n" . " \n" . " true\n" . " true\n" . " $subsystem\n" . " \$(TargetPath)\n" . " $libs;%(AdditionalDependencies)\n" . " \n" . " \n"; # The VC++ projects don't have physical structure to them, instead # the files are organized by logical "filters" that are stored in # a separate file, so different users can organize things differently. # The filters file contains a copy of the ItemGroup elements from # the main project file that list the included items, but tack # on a filter name where needed. print FILTERS "\n" . "\n"; print " \n"; print FILTERS " \n"; foreach $icon_file (@icons) { $icon_file =~ s/..\\windows\\//; print " \n"; print FILTERS " \n" . " Resource Files\n" . " \n"; } print FILTERS " \n"; print " \n"; print " \n"; print FILTERS " \n"; foreach $resource_file (@resources) { $resource_file =~ s/..\\windows\\//; print " \n" . " ..\\..;%(AdditionalIncludeDirectories)\n" . " ..\\..;%(AdditionalIncludeDirectories)\n" . " \n"; print FILTERS " \n" . " Resource Files\n" . " \n"; } print FILTERS " \n"; print " \n"; print " \n"; print FILTERS " \n"; foreach $source_file (@source_files) { $source_file =~ s/..\\windows\\//; print " \n"; print FILTERS " \n" . " Source Files\n" . " "; } print FILTERS " \n"; print " \n"; print " \n"; print FILTERS " \n"; foreach $header_file (@header_files) { $header_file =~ s/..\\windows\\//; print " \n"; print FILTERS " \n" . " Header Files\n" . " "; } print FILTERS " \n"; print " \n"; print " \n" . ""; print FILTERS " \n" . " \n" . " {" . &invent_guid("sources:$windows_project") . "}\n" . " \n" . " \n" . " {" . &invent_guid("headers:$windows_project") . "}\n" . " \n" . " \n" . " {" . &invent_guid("resources:$windows_project") . "}\n" . " \n" . " \n" . ""; select STDOUT; close OUT; close FILTERS; chdir ".."; } } if (defined $makefiles{'gtk'}) { $dirpfx = &dirpfx($makefiles{'gtk'}, "/"); ##-- X/GTK/Unix makefile open OUT, ">$makefiles{'gtk'}"; select OUT; print "# Makefile for $project_name under X/GTK and Unix.\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 = /opt/gcc/bin\n". "CC = \$(TOOLPATH)cc\n". "# If necessary set the path to krb5-config here\n". "KRB5CONFIG=krb5-config\n". "# You can manually set this to `gtk-config' or `pkg-config gtk+-1.2'\n". "# (depending on what works on your system) if you want to enforce\n". "# building with GTK 1.2, or you can set it to `pkg-config gtk+-2.0 x11'\n". "# if you want to enforce 2.0. The default is to try 2.0 and fall back\n". "# to 1.2 if it isn't found.\n". "GTK_CONFIG = sh -c 'pkg-config gtk+-3.0 x11 \$\$0 2>/dev/null || pkg-config gtk+-2.0 x11 \$\$0 2>/dev/null || gtk-config \$\$0'\n". "\n". "-include Makefile.local\n". "\n". "unexport CFLAGS # work around a weird issue with krb5-config\n". "\n". &splitline("CFLAGS = -O2 -Wall -std=gnu99 -Wvla -g " . (join " ", map {"-I$dirpfx$_"} @srcdirs) . " \$(shell \$(GTK_CONFIG) --cflags)"). " -D _FILE_OFFSET_BITS=64\n". "XLDFLAGS = \$(LDFLAGS) \$(shell \$(GTK_CONFIG) --libs)\n". "ULDFLAGS = \$(LDFLAGS)\n". "ifeq (,\$(findstring NO_GSSAPI,\$(COMPAT)))\n". "ifeq (,\$(findstring STATIC_GSSAPI,\$(COMPAT)))\n". "XLDFLAGS+= -ldl\n". "ULDFLAGS+= -ldl\n". "else\n". "CFLAGS+= -DNO_LIBDL \$(shell \$(KRB5CONFIG) --cflags gssapi)\n". "XLDFLAGS+= \$(shell \$(KRB5CONFIG) --libs gssapi)\n". "ULDFLAGS+= \$(shell \$(KRB5CONFIG) --libs gssapi)\n". "endif\n". "endif\n". "INSTALL=install\n". "INSTALL_PROGRAM=\$(INSTALL)\n". "INSTALL_DATA=\$(INSTALL)\n". "prefix=/usr/local\n". "exec_prefix=\$(prefix)\n". "bindir=\$(exec_prefix)/bin\n". "mandir=\$(prefix)/man\n". "man1dir=\$(mandir)/man1\n". "\n". &def($makefile_extra{'gtk'}->{'vars'}) . "\n". ".SUFFIXES:\n". "\n". "\n"; print &splitline("all:" . join "", map { " $_" } &progrealnames("X:XT:U:UT")); print "\n\n"; foreach $p (&prognames("X:XT:U:UT")) { ($prog, $type) = split ",", $p; ($ldflags = $type) =~ s/T$//; $objstr = &objects($p, "X.o", undef, undef); print &splitline($prog . ": " . $objstr), "\n"; $libstr = &objects($p, undef, undef, "-lX"); print &splitline("\t\$(CC) -o \$@ " . $objstr . " \$(${ldflags}LDFLAGS) $libstr", 69), "\n\n"; } foreach $d (&deps("X.o", undef, $dirpfx, "/", "gtk")) { if ($forceobj{$d->{obj_orig}}) { printf("%s: FORCE\n", $d->{obj}); } else { print &splitline(sprintf("%s: %s", $d->{obj}, join " ", @{$d->{deps}})), "\n"; } print &splitline("\t\$(CC) \$(COMPAT) \$(CFLAGS) \$(XFLAGS) -c $d->{deps}->[0]\n"); } print "\n"; print &def($makefile_extra{'gtk'}->{'end'}); print "\nclean:\n". "\trm -f *.o". (join "", map { " $_" } &progrealnames("X:XT:U:UT")) . "\n"; print "\nFORCE:\n"; select STDOUT; close OUT; } if (defined $makefiles{'unix'}) { $dirpfx = &dirpfx($makefiles{'unix'}, "/"); ##-- GTK-free pure-Unix makefile for non-GUI apps only open OUT, ">$makefiles{'unix'}"; select OUT; print "# Makefile for $project_name under Unix.\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 = /opt/gcc/bin\n". "CC = \$(TOOLPATH)cc\n". "\n". "-include Makefile.local\n". "\n". "unexport CFLAGS # work around a weird issue with krb5-config\n". "\n". &splitline("CFLAGS = -O2 -Wall -std=gnu99 -Wvla -g " . (join " ", map {"-I$dirpfx$_"} @srcdirs)). " -D _FILE_OFFSET_BITS=64\n". "ULDFLAGS = \$(LDFLAGS)\n". "INSTALL=install\n". "INSTALL_PROGRAM=\$(INSTALL)\n". "INSTALL_DATA=\$(INSTALL)\n". "prefix=/usr/local\n". "exec_prefix=\$(prefix)\n". "bindir=\$(exec_prefix)/bin\n". "mandir=\$(prefix)/man\n". "man1dir=\$(mandir)/man1\n". "\n". &def($makefile_extra{'unix'}->{'vars'}) . "\n". ".SUFFIXES:\n". "\n". "\n"; print &splitline("all:" . join "", map { " $_" } &progrealnames("U:UT")); print "\n\n"; foreach $p (&prognames("U:UT")) { ($prog, $type) = split ",", $p; ($ldflags = $type) =~ s/T$//; $objstr = &objects($p, "X.o", undef, undef); print &splitline($prog . ": " . $objstr), "\n"; $libstr = &objects($p, undef, undef, "-lX"); print &splitline("\t\$(CC) -o \$@ " . $objstr . " \$(${ldflags}LDFLAGS) $libstr", 69), "\n\n"; } foreach $d (&deps("X.o", undef, $dirpfx, "/", "unix")) { if ($forceobj{$d->{obj_orig}}) { printf("%s: FORCE\n", $d->{obj}); } else { print &splitline(sprintf("%s: %s", $d->{obj}, join " ", @{$d->{deps}})), "\n"; } print &splitline("\t\$(CC) \$(COMPAT) \$(CFLAGS) \$(XFLAGS) -c $d->{deps}->[0]\n"); } print "\n"; print &def($makefile_extra{'unix'}->{'end'}); print "\nclean:\n". "\trm -f *.o". (join "", map { " $_" } &progrealnames("U:UT")) . "\n"; print "\nFORCE:\n"; select STDOUT; close OUT; } if (defined $makefiles{'am'}) { die "Makefile.am in a subdirectory is not supported\n" if &dirpfx($makefiles{'am'}, "/") ne ""; ##-- 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"; # 2014-02-22: as of automake-1.14 we begin to get complained at if # we don't use this option print "AUTOMAKE_OPTIONS = subdir-objects\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", "=", sort grep {$_ ne "empty.h"} 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"; @noinstcliprogs = ("noinst_PROGRAMS", "="); foreach $p (&prognames("UT")) { ($prog, $type) = split ",", $p; push @noinstcliprogs, $prog; } @noinstallprogs = @noinstcliprogs; foreach $p (&prognames("XT")) { ($prog, $type) = split ",", $p; push @noinstallprogs, $prog; } print "if HAVE_GTK\n"; print &splitline(join " ", @noinstallprogs), "\n"; print "else\n"; print &splitline(join " ", @noinstcliprogs), "\n"; print "endif\n\n"; %objtosrc = (); foreach $d (&deps("X", undef, "", "/", "am")) { $objtosrc{$d->{obj}} = $d->{deps}->[0]; } print &splitline(join " ", "AM_CPPFLAGS", "=", map {"-I\$(srcdir)/$_"} @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'}}) { my $flags = $cflags{'am'}->{$obj}; $flags = "" if $flags !~ s/^C//; print "lib${obj}_a_SOURCES = ", $objtosrc{$obj}, "\n"; print &splitline(join " ", "lib${obj}_a_CFLAGS", "=", @amcflags, $flags), "\n"; $amspeciallibs{$obj} = "lib${obj}.a"; } print &splitline(join " ", "noinst_LIBRARIES", "=", sort { $a cmp $b } values %amspeciallibs), "\n\n"; foreach $p (&prognames("X:XT:U:UT")) { ($prog, $type) = split ",", $p; print "if HAVE_GTK\n" if $type eq "X" || $type eq "XT"; @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" || $type eq "XT") { push @ldadd, "\$(GTK_LIBS)"; } if (@ldadd) { print &splitline(join " ", "${prog}_LDADD", "=", @ldadd), "\n"; } print "endif\n" if $type eq "X" || $type eq "XT"; print "\n"; } print &def($makefile_extra{'am'}->{'end'}); select STDOUT; close OUT; } if (defined $makefiles{'lcc'}) { $dirpfx = &dirpfx($makefiles{'lcc'}, "\\"); ##-- lcc makefile open OUT, ">$makefiles{'lcc'}"; select OUT; print "# Makefile for $project_name under lcc.\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"; # lcc command line option is -D not /D ($_ = $help) =~ s/([=" ])\/D/$1-D/gs; print $_; print "\n". "# If you rename this file to `Makefile', you should change this line,\n". "# so that the .rsp files still depend on the correct makefile.\n". "MAKEFILE = Makefile.lcc\n". "\n". "# C compilation flags\n". "CFLAGS = -D_WINDOWS " . (join " ", map {"-I$dirpfx$_"} @srcdirs) . "\n". "# Resource compilation flags\n". "RCFLAGS = ".(join " ", map {"-I$dirpfx$_"} @srcdirs)."\n". "\n". "# Get include directory for resource compiler\n". "\n". &def($makefile_extra{'lcc'}->{'vars'}) . "\n"; print &splitline("all:" . join "", map { " $_.exe" } &progrealnames("G:C")); print "\n\n"; foreach $p (&prognames("G:C")) { ($prog, $type) = split ",", $p; $objstr = &objects($p, "X.obj", "X.res", undef); print &splitline("$prog.exe: " . $objstr ), "\n"; $subsystemtype = ''; if ($type eq "G") { $subsystemtype = "-subsystem windows"; } my $libss = "shell32.lib wsock32.lib ws2_32.lib winspool.lib winmm.lib imm32.lib"; print &splitline("\tlcclnk $subsystemtype -o $prog.exe $objstr $libss"); print "\n\n"; } foreach $d (&deps("X.obj", "X.res", $dirpfx, "\\", "lcc")) { 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} =~ /\.obj$/) { print &splitline("\tlcc -O -p6 \$(COMPAT)". " \$(CFLAGS) \$(XFLAGS) ".$d->{deps}->[0],69)."\n"; } else { print &splitline("\tlrc \$(RCFL) -r \$(RCFLAGS) ". $d->{deps}->[0],69)."\n"; } } print "\n"; print &def($makefile_extra{'lcc'}->{'end'}); print "\nclean:\n". "\t-del *.obj\n". "\t-del *.exe\n". "\t-del *.res\n". "\n". "FORCE:\n"; select STDOUT; close OUT; } if (defined $makefiles{'osx'}) { $dirpfx = &dirpfx($makefiles{'osx'}, "/"); ##-- Mac OS X makefile open OUT, ">$makefiles{'osx'}"; select OUT; print "# Makefile for $project_name under Mac OS X.\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 "CC = \$(TOOLPATH)gcc\n". "\n". &splitline("CFLAGS = -O2 -Wall -std=gnu99 -Wvla -g " . (join " ", map {"-I$dirpfx$_"} @srcdirs))."\n". "MLDFLAGS = -framework Cocoa\n". "ULDFLAGS =\n". "\n" . &def($makefile_extra{'osx'}->{'vars'}) . "\n" . &splitline("all:" . join "", map { " $_" } &progrealnames("MX:U:UT")) . "\n"; foreach $p (&prognames("MX")) { ($prog, $type) = split ",", $p; $objstr = &objects($p, "X.o", undef, undef); $icon = &special($p, ".icns"); $infoplist = &special($p, "info.plist"); print "${prog}.app:\n\tmkdir -p \$\@\n"; print "${prog}.app/Contents: ${prog}.app\n\tmkdir -p \$\@\n"; print "${prog}.app/Contents/MacOS: ${prog}.app/Contents\n\tmkdir -p \$\@\n"; $targets = "${prog}.app/Contents/MacOS/$prog"; if (defined $icon) { print "${prog}.app/Contents/Resources: ${prog}.app/Contents\n\tmkdir -p \$\@\n"; print "${prog}.app/Contents/Resources/${prog}.icns: ${prog}.app/Contents/Resources $icon\n\tcp $icon \$\@\n"; $targets .= " ${prog}.app/Contents/Resources/${prog}.icns"; } if (defined $infoplist) { print "${prog}.app/Contents/Info.plist: ${prog}.app/Contents/Resources $infoplist\n\tcp $infoplist \$\@\n"; $targets .= " ${prog}.app/Contents/Info.plist"; } $targets .= " \$(${prog}_extra)"; print &splitline("${prog}: $targets", 69) . "\n\n"; print &splitline("${prog}.app/Contents/MacOS/$prog: ". "${prog}.app/Contents/MacOS " . $objstr), "\n"; $libstr = &objects($p, undef, undef, "-lX"); print &splitline("\t\$(CC) \$(MLDFLAGS) -o \$@ " . $objstr . " $libstr", 69), "\n\n"; } foreach $p (&prognames("U:UT")) { ($prog, $type) = split ",", $p; $objstr = &objects($p, "X.o", undef, undef); print &splitline($prog . ": " . $objstr), "\n"; $libstr = &objects($p, undef, undef, "-lX"); print &splitline("\t\$(CC) \$(ULDFLAGS) -o \$@ " . $objstr . " $libstr", 69), "\n\n"; } foreach $d (&deps("X.o", undef, $dirpfx, "/", "osx")) { if ($forceobj{$d->{obj_orig}}) { printf("%s: FORCE\n", $d->{obj}); } else { print &splitline(sprintf("%s: %s", $d->{obj}, join " ", @{$d->{deps}})), "\n"; } $firstdep = $d->{deps}->[0]; if ($firstdep =~ /\.c$/) { print "\t\$(CC) \$(COMPAT) \$(FWHACK) \$(CFLAGS) \$(XFLAGS) -c \$<\n"; } elsif ($firstdep =~ /\.m$/) { print "\t\$(CC) -x objective-c \$(COMPAT) \$(FWHACK) \$(CFLAGS) \$(XFLAGS) -c \$<\n"; } } print "\n".&def($makefile_extra{'osx'}->{'end'}); print "\nclean:\n". "\trm -f *.o *.dmg". (join "", map { " $_" } &progrealnames("U:UT")) . "\n". "\trm -rf *.app\n". "\n". "FORCE:\n"; select STDOUT; close OUT; } if (defined $makefiles{'devcppproj'}) { $dirpfx = &dirpfx($makefiles{'devcppproj'}, "\\"); $orig_dir = cwd; ##-- Dev-C++ 5 projects # # Note: All files created in this section are written in binary # mode to prevent any posibility of misinterpreted line endings. # I don't know if Dev-C++ is as touchy as MSVC with LF-only line # endings. But however, CRLF line endings are the common way on # Win32 machines where Dev-C++ is running. # Hence, in order for mkfiles.pl to generate CRLF project files # even when run from Unix, I make sure all files are binary and # explicitly write the CRLFs. # # Create directories if necessary mkdir $makefiles{'devcppproj'} if(! -d $makefiles{'devcppproj'}); chdir $makefiles{'devcppproj'}; @deps = &deps("X.obj", "X.res", $dirpfx, "\\", "devcppproj"); %all_object_deps = map {$_->{obj} => $_->{deps}} @deps; # Make dir names FAT/NTFS compatible my @srcdirs = @srcdirs; for ($i=0; $i<@srcdirs; $i++) { $srcdirs[$i] =~ s/\//\\/g; $srcdirs[$i] =~ s/\\$//; } # 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 = (); %header_files = (); %resource_files = (); @object_files = split " ", &objects($progname, "X.obj", "X.res", "X.lib"); foreach $object_file (@object_files) { next if defined $seen_objects{$object_file}; $seen_objects{$object_file} = 1; if($object_file =~ /\.lib$/io) { $lib_files{$object_file} = 1; next; } $object_deps = $all_object_deps{$object_file}; foreach $object_dep (@$object_deps) { if($object_dep =~ /\.c$/io) { $source_files{$object_dep} = 1; next; } if($object_dep =~ /\.h$/io) { $header_files{$object_dep} = 1; next; } if($object_dep =~ /\.(rc|ico)$/io) { $resource_files{$object_dep} = 1; next; } } } $libs = join " ", sort keys %lib_files; @source_files = sort keys %source_files; @header_files = sort keys %header_files; @resources = sort keys %resource_files; ($windows_project, $type) = split ",", $progname; mkdir $windows_project if(! -d $windows_project); chdir $windows_project; $subsys = ($type eq "G") ? "0" : "1"; # 0 = Win32 GUI, 1 = Win32 Console open OUT, ">$windows_project.dev"; binmode OUT; select OUT; print "# DEV-C++ 5 Project File - $windows_project.dev\r\n". "# ** DO NOT EDIT **\r\n". "\r\n". # No difference between DEBUG and RELEASE here as in 'vcproj', because # Dev-C++ does not support multiple compilation profiles in one single project. # (At least I can say this for Dev-C++ 5 Beta) "[Project]\r\n". "FileName=$windows_project.dev\r\n". "Name=$windows_project\r\n". "Ver=1\r\n". "IsCpp=1\r\n". "Type=$subsys\r\n". # Multimon is disabled here, as Dev-C++ (Version 5 Beta) does not have multimon.h "Compiler=-W -D__GNUWIN32__ -DWIN32 -DNDEBUG -D_WINDOWS -DNO_MULTIMON -D_MBCS_\@\@_\r\n". "CppCompiler=-W -D__GNUWIN32__ -DWIN32 -DNDEBUG -D_WINDOWS -DNO_MULTIMON -D_MBCS_\@\@_\r\n". "Includes=" . (join ";", map {"..\\..\\$dirpfx$_"} @srcdirs) . "\r\n". "Linker=-ladvapi32 -lcomctl32 -lcomdlg32 -lgdi32 -limm32 -lshell32 -luser32 -lwinmm -lwinspool_\@\@_\r\n". "Libs=\r\n". "UnitCount=" . (@source_files + @header_files + @resources) . "\r\n". "Folders=\"Header Files\",\"Resource Files\",\"Source Files\"\r\n". "ObjFiles=\r\n". "PrivateResource=${windows_project}_private.rc\r\n". "ResourceIncludes=..\\..\\..\\WINDOWS\r\n". "MakeIncludes=\r\n". "Icon=\r\n". # It's ok to leave this blank. "ExeOutput=\r\n". "ObjectOutput=\r\n". "OverrideOutput=0\r\n". "OverrideOutputName=$windows_project.exe\r\n". "HostApplication=\r\n". "CommandLine=\r\n". "UseCustomMakefile=0\r\n". "CustomMakefile=\r\n". "IncludeVersionInfo=0\r\n". "SupportXPThemes=0\r\n". "CompilerSet=0\r\n". "CompilerSettings=0000000000000000000000\r\n". "\r\n"; $unit_count = 1; foreach $source_file (@source_files) { print "[Unit$unit_count]\r\n". "FileName=..\\..\\$source_file\r\n". "Folder=Source Files\r\n". "Compile=1\r\n". "CompileCpp=0\r\n". "Link=1\r\n". "Priority=1000\r\n". "OverrideBuildCmd=0\r\n". "BuildCmd=\r\n". "\r\n"; $unit_count++; } foreach $header_file (@header_files) { print "[Unit$unit_count]\r\n". "FileName=..\\..\\$header_file\r\n". "Folder=Header Files\r\n". "Compile=1\r\n". "CompileCpp=1\r\n". # Dev-C++ want's to compile all header files with both compilers C and C++. It does not hurt. "Link=1\r\n". "Priority=1000\r\n". "OverrideBuildCmd=0\r\n". "BuildCmd=\r\n". "\r\n"; $unit_count++; } foreach $resource_file (@resources) { if ($resource_file =~ /.*\.(ico|cur|bmp|dlg|rc2|rct|bin|rgs|gif|jpg|jpeg|jpe)/io) { # Default filter as in 'vcproj' $Compile = "0"; # Don't compile images and other binary resource files $CompileCpp = "0"; } else { $Compile = "1"; $CompileCpp = "1"; # Dev-C++ want's to compile all .rc files with both compilers C and C++. It does not hurt. } print "[Unit$unit_count]\r\n". "FileName=..\\..\\$resource_file\r\n". "Folder=Resource Files\r\n". "Compile=$Compile\r\n". "CompileCpp=$CompileCpp\r\n". "Link=0\r\n". "Priority=1000\r\n". "OverrideBuildCmd=0\r\n". "BuildCmd=\r\n". "\r\n"; $unit_count++; } #Note: By default, [VersionInfo] is not used. print "[VersionInfo]\r\n". "Major=0\r\n". "Minor=0\r\n". "Release=1\r\n". "Build=1\r\n". "LanguageID=1033\r\n". "CharsetID=1252\r\n". "CompanyName=\r\n". "FileVersion=0.1\r\n". "FileDescription=\r\n". "InternalName=\r\n". "LegalCopyright=\r\n". "LegalTrademarks=\r\n". "OriginalFilename=$windows_project.exe\r\n". "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 = "unix") or die "$targetdir: chdir: $!\n"; } system "./configure", @confargs; die "mkfiles.pl: configure returned $?\n" if $? > 0; } sub invent_guid($) { my ($name) = @_; # Invent a GUID for use in Visual Studio project files. We need # a few of these for every executable file we build. # # In order to avoid having to use the non-core Perl module # Data::GUID, and also arrange for GUIDs to be stable, we generate # our GUIDs by hashing a pile of fixed (but originally randomly # generated) data with the filename for which we need an id. # # Hashing _just_ the filenames would clearly be cheating (it's # quite conceivable that someone might hash the same string for # another reason and so generate a colliding GUID), but hashing a # whole SHA-512 data block of random gibberish as well should make # these GUIDs pseudo-random enough to not collide with anyone # else's. my $randdata = pack "N*", 0xD4AB035F,0x76998BA0,0x2DCCB0BD,0x6D3FA320,0x53638051,0xFE312F35, 0xDE1CECC0,0x784DF852,0x6C9F4589,0x54B7AC23,0x14E7A1C4,0xF9BF04DF, 0x19C08B6D,0x3FB69EF1,0xB2DA9043,0xDB5362F3,0x25718DB6,0x733560DA, 0xFEF871B0,0xFECF7A0C,0x67D19C95,0xB492E911,0xF5D562A3,0xFCE1D478, 0x02C50434,0xF7326B7E,0x93D39872,0xCF0D0269,0x9EF24C0F,0x827689AD, 0x88BD20BC,0x74EA6AFE,0x29223682,0xB9AB9287,0x7EA7CE4F,0xCF81B379, 0x9AE4A954,0x81C7AD97,0x2FF2F031,0xC51DA3C2,0xD311CCE7,0x0A31EB8B, 0x1AB04242,0xAF53B714,0xFC574D40,0x8CB4ED01,0x29FEB16F,0x4904D7ED, 0xF5C5F5E1,0xF138A4C2,0xA9D881CE,0xCEA65187,0x4421BA97,0x0EE8428E, 0x9556E384,0x6D0484C9,0x561BD84B,0xD9516A40,0x6B4FD33F,0xDDFFE4C8, 0x3D5DF8A5,0xFE6B7D99,0x3443371B,0xF4E30A3E,0xE62B9FDA,0x6BAA75DB, 0x9EF3C2C7,0x6815CA42,0xE6536076,0xF851E6E2,0x39D16E69,0xBCDF3BB6, 0x50EFFA41,0x378CDF2A,0xB5EC0D0C,0x1E94C433,0xE818241A,0x2689EB1F, 0xB649CEF9,0xD7344D46,0x59C1BB13,0x27511FDF,0x7DAD1768,0xB355E29E, 0xDFAE550C,0x2433005B,0x09DE10B0,0xAA00BA6B,0xC144ED2D,0x8513D007, 0xB0315232,0x7A10DAB6,0x1D97654E,0xF048214D,0xE3059E75,0x83C225D1, 0xFC7AB177,0x83F2B553,0x79F7A0AF,0x1C94582C,0xF5E4AF4B,0xFB39C865, 0x58ABEB27,0xAAB28058,0x52C15A89,0x0EBE9741,0x343F4D26,0xF941202A, 0xA32FD32F,0xDCC055B8,0x64281BF3,0x468BD7BA,0x0CEE09D3,0xBB5FD2B6, 0xA528D412,0xA6A6967E,0xEAAF5DAE,0xDE7B2FAE,0xCA36887B,0x0DE196EB, 0x74B95EF0,0x9EB8B7C2,0x020BFC83,0x1445086F,0xBF4B61B2,0x89AFACEC, 0x80A5CD69,0xC790F744,0x435A6998,0x8DE7AC48,0x32F31BC9,0x8F760D3D, 0xF02A74CB,0xD7B47E20,0x9EC91035,0x70FDE74D,0x9B531362,0x9D81739A, 0x59ADC2EB,0x511555B5,0xCA84B8D5,0x3EC325FF,0x2E442A4C,0x82AF30D9, 0xBFD3EC87,0x90C59E07,0x1C6DC991,0x2D16B822,0x7EA44EB5,0x3A655A39, 0xAB640886,0x09311821,0x777801D9,0x489DBE61,0xA1FFEC65,0x978B49B1, 0x7DB700CD,0x263CF3D6,0xF977E89F,0xBA0B3D01,0x6C6CED19,0x1BE6F23A, 0x19E0ED98,0x8E71A499,0x70BA3271,0x3FB7EE98,0xABA46848,0x2B797959, 0x72C6DE59,0xE08B795C,0x02936C39,0x02185CCB,0xD6F3CE18,0xD0157A40, 0x833DEC3F,0x319B00C4,0x97B59513,0x900B81FD,0x9A022379,0x16E44E1A, 0x0C4CC540,0xCA98E7F9,0xF9431A26,0x290BCFAC,0x406B82C0,0xBC1C4585, 0x55C54528,0x811EBB77,0xD4EDD4F3,0xA70DC02E,0x8AD5C0D1,0x28D64EF4, 0xBEFF5C69,0x99852C4A,0xB4BBFF7B,0x069230AC,0xA3E141FA,0x4E99FB0E, 0xBC154DAA,0x323C7F15,0x86E0247E,0x2EEA3054,0xC9CA1D32,0x8964A006, 0xC93978AC,0xF9B2C159,0x03F2079E,0xB051D284,0x4A7EA9A9,0xF001DA1F, 0xD47A0DAA,0xCF7B6B73,0xF18293B2,0x84303E34,0xF8BC76C4,0xAFBEE24F, 0xB589CA80,0x77B5BF86,0x21B9FD5B,0x1A5071DF,0xA3863110,0x0E50CA61, 0x939151A5,0xD2A59021,0x83A9CDCE,0xCEC69767,0xC906BB16,0x3EE1FF4D, 0x1321EAE4,0x0BF940D6,0x52471E61,0x8A087056,0x66E54293,0xF84AAB9B, 0x08835EF1,0x8F12B77A,0xD86935A5,0x200281D7,0xCD3C37C9,0x30ABEC05, 0x7067E8A0,0x608C4838,0xC9F51CDE,0xA6D318DE,0x41C05B2A,0x694CCE0E, 0xC7842451,0xA3194393,0xFBDC2C84,0xA6D2B577,0xC91E7924,0x01EDA708, 0x22FBB61E,0x662F9B7B,0xDE3150C3,0x2397058C; my $digest = sha512_hex($name . "\0" . $randdata); return sprintf("%s-%s-%04x-%04x-%s", substr($digest,0,8), substr($digest,8,4), 0x4000 | (0xFFF & hex(substr($digest,12,4))), 0x8000 | (0x3FFF & hex(substr($digest,16,4))), substr($digest,20,12)); } putty-0.76/mksrcarc.sh0000755000175000017500000000207414072266311011725 00000000000000#!/bin/sh set -e perl mkfiles.pl # These are text files. text=`{ find . -name CVS -prune -o \ -name .cvsignore -prune -o \ -name .svn -prune -o \ -name LATEST.VER -prune -o \ -name CHECKLST.txt -prune -o \ -name mksrcarc.sh -prune -o \ -name '*.dsp' -prune -o \ -name '*.dsw' -prune -o \ -type f -print | sed 's/^\.\///'; } | \ grep -ivE 'test/.*\.txt|MODULE|website.url' | grep -vF .ico | grep -vF .icns` # These are files which I'm _sure_ should be treated as text, but # which zip might complain about, so we direct its moans to # /dev/null! Apparently its heuristics are doubtful of UTF-8 text # files. bintext=test/*.txt # These are actual binary files which we don't want transforming. bin=`{ ls -1 windows/*.ico windows/website.url; \ find . -name '*.dsp' -print -o -name '*.dsw' -print; }` verbosely() { echo "$@" "$@" } verbosely zip -l putty-src.zip $text verbosely zip -l putty-src.zip $bintext verbosely zip putty-src.zip $bin putty-0.76/mkunxarc.sh0000755000175000017500000000216514072266311011751 00000000000000#!/bin/sh # Build a Unix source distribution from the PuTTY CVS area. # # Expects the following arguments: # - the version number to write into configure.ac # - the suffix to put on the Unix source tarball # - the options to put on the 'make' command line for the docs autoconfver="$1" arcsuffix="$2" docver="$3" 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/{} \; sed "s/^AC_INIT(putty,.*/AC_INIT(putty, $autoconfver)/" configure.ac > uxarc/$arcname/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 putty-0.76/mpint.c0000644000175000017500000025646114072266311011067 00000000000000#include #include #include #include "defs.h" #include "misc.h" #include "puttymem.h" #include "mpint.h" #include "mpint_i.h" #define SIZE_T_BITS (CHAR_BIT * sizeof(size_t)) /* * Inline helpers to take min and max of size_t values, used * throughout this code. */ static inline size_t size_t_min(size_t a, size_t b) { return a < b ? a : b; } static inline size_t size_t_max(size_t a, size_t b) { return a > b ? a : b; } /* * Helper to fetch a word of data from x with array overflow checking. * If x is too short to have that word, 0 is returned. */ static inline BignumInt mp_word(mp_int *x, size_t i) { return i < x->nw ? x->w[i] : 0; } /* * Shift an ordinary C integer by BIGNUM_INT_BITS, in a way that * avoids writing a shift operator whose RHS is greater or equal to * the size of the type, because that's undefined behaviour in C. * * In fact we must avoid even writing it in a definitely-untaken * branch of an if, because compilers will sometimes warn about * that. So you can't just write 'shift too big ? 0 : n >> shift', * because even if 'shift too big' is a constant-expression * evaluating to false, you can still get complaints about the * else clause of the ?:. * * So we have to re-check _inside_ that clause, so that the shift * count is reset to something nonsensical but safe in the case * where the clause wasn't going to be taken anyway. */ static uintmax_t shift_right_by_one_word(uintmax_t n) { bool shift_too_big = BIGNUM_INT_BYTES >= sizeof(n); return shift_too_big ? 0 : n >> (shift_too_big ? 0 : BIGNUM_INT_BITS); } static uintmax_t shift_left_by_one_word(uintmax_t n) { bool shift_too_big = BIGNUM_INT_BYTES >= sizeof(n); return shift_too_big ? 0 : n << (shift_too_big ? 0 : BIGNUM_INT_BITS); } mp_int *mp_make_sized(size_t nw) { mp_int *x = snew_plus(mp_int, nw * sizeof(BignumInt)); assert(nw); /* we outlaw the zero-word mp_int */ x->nw = nw; x->w = snew_plus_get_aux(x); mp_clear(x); return x; } mp_int *mp_new(size_t maxbits) { size_t words = (maxbits + BIGNUM_INT_BITS - 1) / BIGNUM_INT_BITS; return mp_make_sized(words); } mp_int *mp_from_integer(uintmax_t n) { mp_int *x = mp_make_sized( (sizeof(n) + BIGNUM_INT_BYTES - 1) / BIGNUM_INT_BYTES); for (size_t i = 0; i < x->nw; i++) x->w[i] = n >> (i * BIGNUM_INT_BITS); return x; } size_t mp_max_bytes(mp_int *x) { return x->nw * BIGNUM_INT_BYTES; } size_t mp_max_bits(mp_int *x) { return x->nw * BIGNUM_INT_BITS; } void mp_free(mp_int *x) { mp_clear(x); smemclr(x, sizeof(*x)); sfree(x); } void mp_dump(FILE *fp, const char *prefix, mp_int *x, const char *suffix) { fprintf(fp, "%s0x", prefix); for (size_t i = mp_max_bytes(x); i-- > 0 ;) fprintf(fp, "%02X", mp_get_byte(x, i)); fputs(suffix, fp); } void mp_copy_into(mp_int *dest, mp_int *src) { size_t copy_nw = size_t_min(dest->nw, src->nw); memmove(dest->w, src->w, copy_nw * sizeof(BignumInt)); smemclr(dest->w + copy_nw, (dest->nw - copy_nw) * sizeof(BignumInt)); } void mp_copy_integer_into(mp_int *r, uintmax_t n) { for (size_t i = 0; i < r->nw; i++) { r->w[i] = n; n = shift_right_by_one_word(n); } } /* * Conditional selection is done by negating 'which', to give a mask * word which is all 1s if which==1 and all 0s if which==0. Then you * can select between two inputs a,b without data-dependent control * flow by XORing them to get their difference; ANDing with the mask * word to replace that difference with 0 if which==0; and XORing that * into a, which will either turn it into b or leave it alone. * * This trick will be used throughout this code and taken as read the * rest of the time (or else I'd be here all week typing comments), * but I felt I ought to explain it in words _once_. */ void mp_select_into(mp_int *dest, mp_int *src0, mp_int *src1, unsigned which) { BignumInt mask = -(BignumInt)(1 & which); for (size_t i = 0; i < dest->nw; i++) { BignumInt srcword0 = mp_word(src0, i), srcword1 = mp_word(src1, i); dest->w[i] = srcword0 ^ ((srcword1 ^ srcword0) & mask); } } void mp_cond_swap(mp_int *x0, mp_int *x1, unsigned swap) { assert(x0->nw == x1->nw); volatile BignumInt mask = -(BignumInt)(1 & swap); for (size_t i = 0; i < x0->nw; i++) { BignumInt diff = (x0->w[i] ^ x1->w[i]) & mask; x0->w[i] ^= diff; x1->w[i] ^= diff; } } void mp_clear(mp_int *x) { smemclr(x->w, x->nw * sizeof(BignumInt)); } void mp_cond_clear(mp_int *x, unsigned clear) { BignumInt mask = ~-(BignumInt)(1 & clear); for (size_t i = 0; i < x->nw; i++) x->w[i] &= mask; } /* * Common code between mp_from_bytes_{le,be} which reads bytes in an * arbitrary arithmetic progression. */ static mp_int *mp_from_bytes_int(ptrlen bytes, size_t m, size_t c) { size_t nw = (bytes.len + BIGNUM_INT_BYTES - 1) / BIGNUM_INT_BYTES; nw = size_t_max(nw, 1); mp_int *n = mp_make_sized(nw); for (size_t i = 0; i < bytes.len; i++) n->w[i / BIGNUM_INT_BYTES] |= (BignumInt)(((const unsigned char *)bytes.ptr)[m*i+c]) << (8 * (i % BIGNUM_INT_BYTES)); return n; } mp_int *mp_from_bytes_le(ptrlen bytes) { return mp_from_bytes_int(bytes, 1, 0); } mp_int *mp_from_bytes_be(ptrlen bytes) { return mp_from_bytes_int(bytes, -1, bytes.len - 1); } static mp_int *mp_from_words(size_t nw, const BignumInt *w) { mp_int *x = mp_make_sized(nw); memcpy(x->w, w, x->nw * sizeof(BignumInt)); return x; } /* * Decimal-to-binary conversion: just go through the input string * adding on the decimal value of each digit, and then multiplying the * number so far by 10. */ mp_int *mp_from_decimal_pl(ptrlen decimal) { /* 196/59 is an upper bound (and also a continued-fraction * convergent) for log2(10), so this conservatively estimates the * number of bits that will be needed to store any number that can * be written in this many decimal digits. */ assert(decimal.len < (~(size_t)0) / 196); size_t bits = 196 * decimal.len / 59; /* Now round that up to words. */ size_t words = bits / BIGNUM_INT_BITS + 1; mp_int *x = mp_make_sized(words); for (size_t i = 0; i < decimal.len; i++) { mp_add_integer_into(x, x, ((const char *)decimal.ptr)[i] - '0'); if (i+1 == decimal.len) break; mp_mul_integer_into(x, x, 10); } return x; } mp_int *mp_from_decimal(const char *decimal) { return mp_from_decimal_pl(ptrlen_from_asciz(decimal)); } /* * Hex-to-binary conversion: _algorithmically_ simpler than decimal * (none of those multiplications by 10), but there's some fiddly * bit-twiddling needed to process each hex digit without diverging * control flow depending on whether it's a letter or a number. */ mp_int *mp_from_hex_pl(ptrlen hex) { assert(hex.len <= (~(size_t)0) / 4); size_t bits = hex.len * 4; size_t words = (bits + BIGNUM_INT_BITS - 1) / BIGNUM_INT_BITS; words = size_t_max(words, 1); mp_int *x = mp_make_sized(words); for (size_t nibble = 0; nibble < hex.len; nibble++) { BignumInt digit = ((const char *)hex.ptr)[hex.len-1 - nibble]; BignumInt lmask = ~-((BignumInt)((digit-'a')|('f'-digit)) >> (BIGNUM_INT_BITS-1)); BignumInt umask = ~-((BignumInt)((digit-'A')|('F'-digit)) >> (BIGNUM_INT_BITS-1)); BignumInt digitval = digit - '0'; digitval ^= (digitval ^ (digit - 'a' + 10)) & lmask; digitval ^= (digitval ^ (digit - 'A' + 10)) & umask; digitval &= 0xF; /* at least be _slightly_ nice about weird input */ size_t word_idx = nibble / (BIGNUM_INT_BYTES*2); size_t nibble_within_word = nibble % (BIGNUM_INT_BYTES*2); x->w[word_idx] |= digitval << (nibble_within_word * 4); } return x; } mp_int *mp_from_hex(const char *hex) { return mp_from_hex_pl(ptrlen_from_asciz(hex)); } mp_int *mp_copy(mp_int *x) { return mp_from_words(x->nw, x->w); } uint8_t mp_get_byte(mp_int *x, size_t byte) { return 0xFF & (mp_word(x, byte / BIGNUM_INT_BYTES) >> (8 * (byte % BIGNUM_INT_BYTES))); } unsigned mp_get_bit(mp_int *x, size_t bit) { return 1 & (mp_word(x, bit / BIGNUM_INT_BITS) >> (bit % BIGNUM_INT_BITS)); } uintmax_t mp_get_integer(mp_int *x) { uintmax_t toret = 0; for (size_t i = x->nw; i-- > 0 ;) toret = shift_left_by_one_word(toret) | x->w[i]; return toret; } void mp_set_bit(mp_int *x, size_t bit, unsigned val) { size_t word = bit / BIGNUM_INT_BITS; assert(word < x->nw); unsigned shift = (bit % BIGNUM_INT_BITS); x->w[word] &= ~((BignumInt)1 << shift); x->w[word] |= (BignumInt)(val & 1) << shift; } /* * Helper function used here and there to normalise any nonzero input * value to 1. */ static inline unsigned normalise_to_1(BignumInt n) { n = (n >> 1) | (n & 1); /* ensure top bit is clear */ n = (BignumInt)(-n) >> (BIGNUM_INT_BITS - 1); /* normalise to 0 or 1 */ return n; } static inline unsigned normalise_to_1_u64(uint64_t n) { n = (n >> 1) | (n & 1); /* ensure top bit is clear */ n = (-n) >> 63; /* normalise to 0 or 1 */ return n; } /* * Find the highest nonzero word in a number. Returns the index of the * word in x->w, and also a pair of output uint64_t in which that word * appears in the high one shifted left by 'shift_wanted' bits, the * words immediately below it occupy the space to the right, and the * words below _that_ fill up the low one. * * If there is no nonzero word at all, the passed-by-reference output * variables retain their original values. */ static inline void mp_find_highest_nonzero_word_pair( mp_int *x, size_t shift_wanted, size_t *index, uint64_t *hi, uint64_t *lo) { uint64_t curr_hi = 0, curr_lo = 0; for (size_t curr_index = 0; curr_index < x->nw; curr_index++) { BignumInt curr_word = x->w[curr_index]; unsigned indicator = normalise_to_1(curr_word); curr_lo = (BIGNUM_INT_BITS < 64 ? (curr_lo >> BIGNUM_INT_BITS) : 0) | (curr_hi << (64 - BIGNUM_INT_BITS)); curr_hi = (BIGNUM_INT_BITS < 64 ? (curr_hi >> BIGNUM_INT_BITS) : 0) | ((uint64_t)curr_word << shift_wanted); if (hi) *hi ^= (curr_hi ^ *hi ) & -(uint64_t)indicator; if (lo) *lo ^= (curr_lo ^ *lo ) & -(uint64_t)indicator; if (index) *index ^= (curr_index ^ *index) & -(size_t) indicator; } } size_t mp_get_nbits(mp_int *x) { /* Sentinel values in case there are no bits set at all: we * imagine that there's a word at position -1 (i.e. the topmost * fraction word) which is all 1s, because that way, we handle a * zero input by considering its highest set bit to be the top one * of that word, i.e. just below the units digit, i.e. at bit * index -1, i.e. so we'll return 0 on output. */ size_t hiword_index = -(size_t)1; uint64_t hiword64 = ~(BignumInt)0; /* * Find the highest nonzero word and its index. */ mp_find_highest_nonzero_word_pair(x, 0, &hiword_index, &hiword64, NULL); BignumInt hiword = hiword64; /* in case BignumInt is a narrower type */ /* * Find the index of the highest set bit within hiword. */ BignumInt hibit_index = 0; for (size_t i = (1 << (BIGNUM_INT_BITS_BITS-1)); i != 0; i >>= 1) { BignumInt shifted_word = hiword >> i; BignumInt indicator = (BignumInt)(-shifted_word) >> (BIGNUM_INT_BITS-1); hiword ^= (shifted_word ^ hiword ) & -indicator; hibit_index += i & -(size_t)indicator; } /* * Put together the result. */ return (hiword_index << BIGNUM_INT_BITS_BITS) + hibit_index + 1; } /* * Shared code between the hex and decimal output functions to get rid * of leading zeroes on the output string. The idea is that we wrote * out a fixed number of digits and a trailing \0 byte into 'buf', and * now we want to shift it all left so that the first nonzero digit * moves to buf[0] (or, if there are no nonzero digits at all, we move * up by 'maxtrim', so that we return 0 as "0" instead of ""). */ static void trim_leading_zeroes(char *buf, size_t bufsize, size_t maxtrim) { size_t trim = maxtrim; /* * Look for the first character not equal to '0', to find the * shift count. */ if (trim > 0) { for (size_t pos = trim; pos-- > 0 ;) { uint8_t diff = buf[pos] ^ '0'; size_t mask = -((((size_t)diff) - 1) >> (SIZE_T_BITS - 1)); trim ^= (trim ^ pos) & ~mask; } } /* * Now do the shift, in log n passes each of which does a * conditional shift by 2^i bytes if bit i is set in the shift * count. */ uint8_t *ubuf = (uint8_t *)buf; for (size_t logd = 0; bufsize >> logd; logd++) { uint8_t mask = -(uint8_t)((trim >> logd) & 1); size_t d = (size_t)1 << logd; for (size_t i = 0; i+d < bufsize; i++) { uint8_t diff = mask & (ubuf[i] ^ ubuf[i+d]); ubuf[i] ^= diff; ubuf[i+d] ^= diff; } } } /* * Binary to decimal conversion. Our strategy here is to extract each * decimal digit by finding the input number's residue mod 10, then * subtract that off to give an exact multiple of 10, which then means * you can safely divide by 10 by means of shifting right one bit and * then multiplying by the inverse of 5 mod 2^n. */ char *mp_get_decimal(mp_int *x_orig) { mp_int *x = mp_copy(x_orig), *y = mp_make_sized(x->nw); /* * The inverse of 5 mod 2^lots is 0xccccccccccccccccccccd, for an * appropriate number of 'c's. Manually construct an integer the * right size. */ mp_int *inv5 = mp_make_sized(x->nw); assert(BIGNUM_INT_BITS % 8 == 0); for (size_t i = 0; i < inv5->nw; i++) inv5->w[i] = BIGNUM_INT_MASK / 5 * 4; inv5->w[0]++; /* * 146/485 is an upper bound (and also a continued-fraction * convergent) of log10(2), so this is a conservative estimate of * the number of decimal digits needed to store a value that fits * in this many binary bits. */ assert(x->nw < (~(size_t)1) / (146 * BIGNUM_INT_BITS)); size_t bufsize = size_t_max(x->nw * (146 * BIGNUM_INT_BITS) / 485, 1) + 2; char *outbuf = snewn(bufsize, char); outbuf[bufsize - 1] = '\0'; /* * Loop over the number generating digits from the least * significant upwards, so that we write to outbuf in reverse * order. */ for (size_t pos = bufsize - 1; pos-- > 0 ;) { /* * Find the current residue mod 10. We do this by first * summing the bytes of the number, with all but the lowest * one multiplied by 6 (because 256^i == 6 mod 10 for all * i>0). That gives us a single word congruent mod 10 to the * input number, and then we reduce it further by manual * multiplication and shifting, just in case the compiler * target implements the C division operator in a way that has * input-dependent timing. */ uint32_t low_digit = 0, maxval = 0, mult = 1; for (size_t i = 0; i < x->nw; i++) { for (unsigned j = 0; j < BIGNUM_INT_BYTES; j++) { low_digit += mult * (0xFF & (x->w[i] >> (8*j))); maxval += mult * 0xFF; mult = 6; } /* * For _really_ big numbers, prevent overflow of t by * periodically folding the top half of the accumulator * into the bottom half, using the same rule 'multiply by * 6 when shifting down by one or more whole bytes'. */ if (maxval > UINT32_MAX - (6 * 0xFF * BIGNUM_INT_BYTES)) { low_digit = (low_digit & 0xFFFF) + 6 * (low_digit >> 16); maxval = (maxval & 0xFFFF) + 6 * (maxval >> 16); } } /* * Final reduction of low_digit. We multiply by 2^32 / 10 * (that's the constant 0x19999999) to get a 64-bit value * whose top 32 bits are the approximate quotient * low_digit/10; then we subtract off 10 times that; and * finally we do one last trial subtraction of 10 by adding 6 * (which sets bit 4 if the number was just over 10) and then * testing bit 4. */ low_digit -= 10 * ((0x19999999ULL * low_digit) >> 32); low_digit -= 10 * ((low_digit + 6) >> 4); assert(low_digit < 10); /* make sure we did reduce fully */ outbuf[pos] = '0' + low_digit; /* * Now subtract off that digit, divide by 2 (using a right * shift) and by 5 (using the modular inverse), to get the * next output digit into the units position. */ mp_sub_integer_into(x, x, low_digit); mp_rshift_fixed_into(y, x, 1); mp_mul_into(x, y, inv5); } mp_free(x); mp_free(y); mp_free(inv5); trim_leading_zeroes(outbuf, bufsize, bufsize - 2); return outbuf; } /* * Binary to hex conversion. Reasonably simple (only a spot of bit * twiddling to choose whether to output a digit or a letter for each * nibble). */ static char *mp_get_hex_internal(mp_int *x, uint8_t letter_offset) { size_t nibbles = x->nw * BIGNUM_INT_BYTES * 2; size_t bufsize = nibbles + 1; char *outbuf = snewn(bufsize, char); outbuf[nibbles] = '\0'; for (size_t nibble = 0; nibble < nibbles; nibble++) { size_t word_idx = nibble / (BIGNUM_INT_BYTES*2); size_t nibble_within_word = nibble % (BIGNUM_INT_BYTES*2); uint8_t digitval = 0xF & (x->w[word_idx] >> (nibble_within_word * 4)); uint8_t mask = -((digitval + 6) >> 4); char digit = digitval + '0' + (letter_offset & mask); outbuf[nibbles-1 - nibble] = digit; } trim_leading_zeroes(outbuf, bufsize, nibbles - 1); return outbuf; } char *mp_get_hex(mp_int *x) { return mp_get_hex_internal(x, 'a' - ('0'+10)); } char *mp_get_hex_uppercase(mp_int *x) { return mp_get_hex_internal(x, 'A' - ('0'+10)); } /* * Routines for reading and writing the SSH-1 and SSH-2 wire formats * for multiprecision integers, declared in marshal.h. * * These can't avoid having control flow dependent on the true bit * size of the number, because the wire format requires the number of * output bytes to depend on that. */ void BinarySink_put_mp_ssh1(BinarySink *bs, mp_int *x) { size_t bits = mp_get_nbits(x); size_t bytes = (bits + 7) / 8; assert(bits < 0x10000); put_uint16(bs, bits); for (size_t i = bytes; i-- > 0 ;) put_byte(bs, mp_get_byte(x, i)); } void BinarySink_put_mp_ssh2(BinarySink *bs, mp_int *x) { size_t bytes = (mp_get_nbits(x) + 8) / 8; put_uint32(bs, bytes); for (size_t i = bytes; i-- > 0 ;) put_byte(bs, mp_get_byte(x, i)); } mp_int *BinarySource_get_mp_ssh1(BinarySource *src) { unsigned bitc = get_uint16(src); ptrlen bytes = get_data(src, (bitc + 7) / 8); if (get_err(src)) { return mp_from_integer(0); } else { mp_int *toret = mp_from_bytes_be(bytes); /* SSH-1.5 spec says that it's OK for the prefix uint16 to be * _greater_ than the actual number of bits */ if (mp_get_nbits(toret) > bitc) { src->err = BSE_INVALID; mp_free(toret); toret = mp_from_integer(0); } return toret; } } mp_int *BinarySource_get_mp_ssh2(BinarySource *src) { ptrlen bytes = get_string(src); if (get_err(src)) { return mp_from_integer(0); } else { const unsigned char *p = bytes.ptr; if ((bytes.len > 0 && ((p[0] & 0x80) || (p[0] == 0 && (bytes.len <= 1 || !(p[1] & 0x80)))))) { src->err = BSE_INVALID; return mp_from_integer(0); } return mp_from_bytes_be(bytes); } } /* * Make an mp_int structure whose words array aliases a subinterval of * some other mp_int. This makes it easy to read or write just the low * or high words of a number, e.g. to add a number starting from a * high bit position, or to reduce mod 2^{n*BIGNUM_INT_BITS}. * * The convention throughout this code is that when we store an mp_int * directly by value, we always expect it to be an alias of some kind, * so its words array won't ever need freeing. Whereas an 'mp_int *' * has an owner, who knows whether it needs freeing or whether it was * created by address-taking an alias. */ static mp_int mp_make_alias(mp_int *in, size_t offset, size_t len) { /* * Bounds-check the offset and length so that we always return * something valid, even if it's not necessarily the length the * caller asked for. */ if (offset > in->nw) offset = in->nw; if (len > in->nw - offset) len = in->nw - offset; mp_int toret; toret.nw = len; toret.w = in->w + offset; return toret; } /* * A special case of mp_make_alias: in some cases we preallocate a * large mp_int to use as scratch space (to avoid pointless * malloc/free churn in recursive or iterative work). * * mp_alloc_from_scratch creates an alias of size 'len' to part of * 'pool', and adjusts 'pool' itself so that further allocations won't * overwrite that space. * * There's no free function to go with this. Typically you just copy * the pool mp_int by value, allocate from the copy, and when you're * done with those allocations, throw the copy away and go back to the * original value of pool. (A mark/release system.) */ static mp_int mp_alloc_from_scratch(mp_int *pool, size_t len) { assert(len <= pool->nw); mp_int toret = mp_make_alias(pool, 0, len); *pool = mp_make_alias(pool, len, pool->nw); return toret; } /* * Internal component common to lots of assorted add/subtract code. * Reads words from a,b; writes into w_out (which might be NULL if the * output isn't even needed). Takes an input carry flag in 'carry', * and returns the output carry. Each word read from b is ANDed with * b_and and then XORed with b_xor. * * So you can implement addition by setting b_and to all 1s and b_xor * to 0; you can subtract by making b_xor all 1s too (effectively * bit-flipping b) and also passing 1 as the input carry (to turn * one's complement into two's complement). And you can do conditional * add/subtract by choosing b_and to be all 1s or all 0s based on a * condition, because the value of b will be totally ignored if b_and * == 0. */ static BignumCarry mp_add_masked_into( BignumInt *w_out, size_t rw, mp_int *a, mp_int *b, BignumInt b_and, BignumInt b_xor, BignumCarry carry) { for (size_t i = 0; i < rw; i++) { BignumInt aword = mp_word(a, i), bword = mp_word(b, i), out; bword = (bword & b_and) ^ b_xor; BignumADC(out, carry, aword, bword, carry); if (w_out) w_out[i] = out; } return carry; } /* * Like the public mp_add_into except that it returns the output carry. */ static inline BignumCarry mp_add_into_internal(mp_int *r, mp_int *a, mp_int *b) { return mp_add_masked_into(r->w, r->nw, a, b, ~(BignumInt)0, 0, 0); } void mp_add_into(mp_int *r, mp_int *a, mp_int *b) { mp_add_into_internal(r, a, b); } void mp_sub_into(mp_int *r, mp_int *a, mp_int *b) { mp_add_masked_into(r->w, r->nw, a, b, ~(BignumInt)0, ~(BignumInt)0, 1); } void mp_and_into(mp_int *r, mp_int *a, mp_int *b) { for (size_t i = 0; i < r->nw; i++) { BignumInt aword = mp_word(a, i), bword = mp_word(b, i); r->w[i] = aword & bword; } } void mp_or_into(mp_int *r, mp_int *a, mp_int *b) { for (size_t i = 0; i < r->nw; i++) { BignumInt aword = mp_word(a, i), bword = mp_word(b, i); r->w[i] = aword | bword; } } void mp_xor_into(mp_int *r, mp_int *a, mp_int *b) { for (size_t i = 0; i < r->nw; i++) { BignumInt aword = mp_word(a, i), bword = mp_word(b, i); r->w[i] = aword ^ bword; } } void mp_bic_into(mp_int *r, mp_int *a, mp_int *b) { for (size_t i = 0; i < r->nw; i++) { BignumInt aword = mp_word(a, i), bword = mp_word(b, i); r->w[i] = aword & ~bword; } } static void mp_cond_negate(mp_int *r, mp_int *x, unsigned yes) { BignumCarry carry = yes; BignumInt flip = -(BignumInt)yes; for (size_t i = 0; i < r->nw; i++) { BignumInt xword = mp_word(x, i); xword ^= flip; BignumADC(r->w[i], carry, 0, xword, carry); } } /* * Similar to mp_add_masked_into, but takes a C integer instead of an * mp_int as the masked operand. */ static BignumCarry mp_add_masked_integer_into( BignumInt *w_out, size_t rw, mp_int *a, uintmax_t b, BignumInt b_and, BignumInt b_xor, BignumCarry carry) { for (size_t i = 0; i < rw; i++) { BignumInt aword = mp_word(a, i); BignumInt bword = b; b = shift_right_by_one_word(b); BignumInt out; bword = (bword ^ b_xor) & b_and; BignumADC(out, carry, aword, bword, carry); if (w_out) w_out[i] = out; } return carry; } void mp_add_integer_into(mp_int *r, mp_int *a, uintmax_t n) { mp_add_masked_integer_into(r->w, r->nw, a, n, ~(BignumInt)0, 0, 0); } void mp_sub_integer_into(mp_int *r, mp_int *a, uintmax_t n) { mp_add_masked_integer_into(r->w, r->nw, a, n, ~(BignumInt)0, ~(BignumInt)0, 1); } /* * Sets r to a + n << (word_index * BIGNUM_INT_BITS), treating * word_index as secret data. */ static void mp_add_integer_into_shifted_by_words( mp_int *r, mp_int *a, uintmax_t n, size_t word_index) { unsigned indicator = 0; BignumCarry carry = 0; for (size_t i = 0; i < r->nw; i++) { /* indicator becomes 1 when we reach the index that the least * significant bits of n want to be placed at, and it stays 1 * thereafter. */ indicator |= 1 ^ normalise_to_1(i ^ word_index); /* If indicator is 1, we add the low bits of n into r, and * shift n down. If it's 0, we add zero bits into r, and * leave n alone. */ BignumInt bword = n & -(BignumInt)indicator; uintmax_t new_n = shift_right_by_one_word(n); n ^= (n ^ new_n) & -(uintmax_t)indicator; BignumInt aword = mp_word(a, i); BignumInt out; BignumADC(out, carry, aword, bword, carry); r->w[i] = out; } } void mp_mul_integer_into(mp_int *r, mp_int *a, uint16_t n) { BignumInt carry = 0, mult = n; for (size_t i = 0; i < r->nw; i++) { BignumInt aword = mp_word(a, i); BignumMULADD(carry, r->w[i], aword, mult, carry); } assert(!carry); } void mp_cond_add_into(mp_int *r, mp_int *a, mp_int *b, unsigned yes) { BignumInt mask = -(BignumInt)(yes & 1); mp_add_masked_into(r->w, r->nw, a, b, mask, 0, 0); } void mp_cond_sub_into(mp_int *r, mp_int *a, mp_int *b, unsigned yes) { BignumInt mask = -(BignumInt)(yes & 1); mp_add_masked_into(r->w, r->nw, a, b, mask, mask, 1 & mask); } /* * Ordered comparison between unsigned numbers is done by subtracting * one from the other and looking at the output carry. */ unsigned mp_cmp_hs(mp_int *a, mp_int *b) { size_t rw = size_t_max(a->nw, b->nw); return mp_add_masked_into(NULL, rw, a, b, ~(BignumInt)0, ~(BignumInt)0, 1); } unsigned mp_hs_integer(mp_int *x, uintmax_t n) { BignumInt carry = 1; size_t nwords = sizeof(n)/BIGNUM_INT_BYTES; for (size_t i = 0, e = size_t_max(x->nw, nwords); i < e; i++) { BignumInt nword = n; n = shift_right_by_one_word(n); BignumInt dummy_out; BignumADC(dummy_out, carry, mp_word(x, i), ~nword, carry); (void)dummy_out; } return carry; } /* * Equality comparison is done by bitwise XOR of the input numbers, * ORing together all the output words, and normalising the result * using our careful normalise_to_1 helper function. */ unsigned mp_cmp_eq(mp_int *a, mp_int *b) { BignumInt diff = 0; for (size_t i = 0, limit = size_t_max(a->nw, b->nw); i < limit; i++) diff |= mp_word(a, i) ^ mp_word(b, i); return 1 ^ normalise_to_1(diff); /* return 1 if diff _is_ zero */ } unsigned mp_eq_integer(mp_int *x, uintmax_t n) { BignumInt diff = 0; size_t nwords = sizeof(n)/BIGNUM_INT_BYTES; for (size_t i = 0, e = size_t_max(x->nw, nwords); i < e; i++) { BignumInt nword = n; n = shift_right_by_one_word(n); diff |= mp_word(x, i) ^ nword; } return 1 ^ normalise_to_1(diff); /* return 1 if diff _is_ zero */ } static void mp_neg_into(mp_int *r, mp_int *a) { mp_int zero; zero.nw = 0; mp_sub_into(r, &zero, a); } mp_int *mp_add(mp_int *x, mp_int *y) { mp_int *r = mp_make_sized(size_t_max(x->nw, y->nw) + 1); mp_add_into(r, x, y); return r; } mp_int *mp_sub(mp_int *x, mp_int *y) { mp_int *r = mp_make_sized(size_t_max(x->nw, y->nw)); mp_sub_into(r, x, y); return r; } /* * Internal routine: multiply and accumulate in the trivial O(N^2) * way. Sets r <- r + a*b. */ static void mp_mul_add_simple(mp_int *r, mp_int *a, mp_int *b) { BignumInt *aend = a->w + a->nw, *bend = b->w + b->nw, *rend = r->w + r->nw; for (BignumInt *ap = a->w, *rp = r->w; ap < aend && rp < rend; ap++, rp++) { BignumInt adata = *ap, carry = 0, *rq = rp; for (BignumInt *bp = b->w; bp < bend && rq < rend; bp++, rq++) { BignumInt bdata = bp < bend ? *bp : 0; BignumMULADD2(carry, *rq, adata, bdata, *rq, carry); } for (; rq < rend; rq++) BignumADC(*rq, carry, carry, *rq, 0); } } #ifndef KARATSUBA_THRESHOLD /* allow redefinition via -D for testing */ #define KARATSUBA_THRESHOLD 24 #endif static inline size_t mp_mul_scratchspace_unary(size_t n) { /* * Simplistic and overcautious bound on the amount of scratch * space that the recursive multiply function will need. * * The rationale is: on the main Karatsuba branch of * mp_mul_internal, which is the most space-intensive one, we * allocate space for (a0+a1) and (b0+b1) (each just over half the * input length n) and their product (the sum of those sizes, i.e. * just over n itself). Then in order to actually compute the * product, we do a recursive multiplication of size just over n. * * If all those 'just over' weren't there, and everything was * _exactly_ half the length, you'd get the amount of space for a * size-n multiply defined by the recurrence M(n) = 2n + M(n/2), * which is satisfied by M(n) = 4n. But instead it's (2n plus a * word or two) and M(n/2 plus a word or two). On the assumption * that there's still some constant k such that M(n) <= kn, this * gives us kn = 2n + w + k(n/2 + w), where w is a small constant * (one or two words). That simplifies to kn/2 = 2n + (k+1)w, and * since we don't even _start_ needing scratch space until n is at * least 50, we can bound 2n + (k+1)w above by 3n, giving k=6. * * So I claim that 6n words of scratch space will suffice, and I * check that by assertion at every stage of the recursion. */ return n * 6; } static size_t mp_mul_scratchspace(size_t rw, size_t aw, size_t bw) { size_t inlen = size_t_min(rw, size_t_max(aw, bw)); return mp_mul_scratchspace_unary(inlen); } static void mp_mul_internal(mp_int *r, mp_int *a, mp_int *b, mp_int scratch) { size_t inlen = size_t_min(r->nw, size_t_max(a->nw, b->nw)); assert(scratch.nw >= mp_mul_scratchspace_unary(inlen)); mp_clear(r); if (inlen < KARATSUBA_THRESHOLD || a->nw == 0 || b->nw == 0) { /* * The input numbers are too small to bother optimising. Go * straight to the simple primitive approach. */ mp_mul_add_simple(r, a, b); return; } /* * Karatsuba divide-and-conquer algorithm. We cut each input in * half, so that it's expressed as two big 'digits' in a giant * base D: * * a = a_1 D + a_0 * b = b_1 D + b_0 * * Then the product is of course * * ab = a_1 b_1 D^2 + (a_1 b_0 + a_0 b_1) D + a_0 b_0 * * and we compute the three coefficients by recursively calling * ourself to do half-length multiplications. * * The clever bit that makes this worth doing is that we only need * _one_ half-length multiplication for the central coefficient * rather than the two that it obviouly looks like, because we can * use a single multiplication to compute * * (a_1 + a_0) (b_1 + b_0) = a_1 b_1 + a_1 b_0 + a_0 b_1 + a_0 b_0 * * and then we subtract the other two coefficients (a_1 b_1 and * a_0 b_0) which we were computing anyway. * * Hence we get to multiply two numbers of length N in about three * times as much work as it takes to multiply numbers of length * N/2, which is obviously better than the four times as much work * it would take if we just did a long conventional multiply. */ /* Break up the input as botlen + toplen, with botlen >= toplen. * The 'base' D is equal to 2^{botlen * BIGNUM_INT_BITS}. */ size_t toplen = inlen / 2; size_t botlen = inlen - toplen; /* Alias bignums that address the two halves of a,b, and useful * pieces of r. */ mp_int a0 = mp_make_alias(a, 0, botlen); mp_int b0 = mp_make_alias(b, 0, botlen); mp_int a1 = mp_make_alias(a, botlen, toplen); mp_int b1 = mp_make_alias(b, botlen, toplen); mp_int r0 = mp_make_alias(r, 0, botlen*2); mp_int r1 = mp_make_alias(r, botlen, r->nw); mp_int r2 = mp_make_alias(r, botlen*2, r->nw); /* Recurse to compute a0*b0 and a1*b1, in their correct positions * in the output bignum. They can't overlap. */ mp_mul_internal(&r0, &a0, &b0, scratch); mp_mul_internal(&r2, &a1, &b1, scratch); if (r->nw < inlen*2) { /* * The output buffer isn't large enough to require the whole * product, so some of a1*b1 won't have been stored. In that * case we won't try to do the full Karatsuba optimisation; * we'll just recurse again to compute a0*b1 and a1*b0 - or at * least as much of them as the output buffer size requires - * and add each one in. */ mp_int s = mp_alloc_from_scratch( &scratch, size_t_min(botlen+toplen, r1.nw)); mp_mul_internal(&s, &a0, &b1, scratch); mp_add_into(&r1, &r1, &s); mp_mul_internal(&s, &a1, &b0, scratch); mp_add_into(&r1, &r1, &s); return; } /* a0+a1 and b0+b1 */ mp_int asum = mp_alloc_from_scratch(&scratch, botlen+1); mp_int bsum = mp_alloc_from_scratch(&scratch, botlen+1); mp_add_into(&asum, &a0, &a1); mp_add_into(&bsum, &b0, &b1); /* Their product */ mp_int product = mp_alloc_from_scratch(&scratch, botlen*2+1); mp_mul_internal(&product, &asum, &bsum, scratch); /* Subtract off the outer terms we already have */ mp_sub_into(&product, &product, &r0); mp_sub_into(&product, &product, &r2); /* And add it in with the right offset. */ mp_add_into(&r1, &r1, &product); } void mp_mul_into(mp_int *r, mp_int *a, mp_int *b) { mp_int *scratch = mp_make_sized(mp_mul_scratchspace(r->nw, a->nw, b->nw)); mp_mul_internal(r, a, b, *scratch); mp_free(scratch); } mp_int *mp_mul(mp_int *x, mp_int *y) { mp_int *r = mp_make_sized(x->nw + y->nw); mp_mul_into(r, x, y); return r; } void mp_lshift_fixed_into(mp_int *r, mp_int *a, size_t bits) { size_t words = bits / BIGNUM_INT_BITS; size_t bitoff = bits % BIGNUM_INT_BITS; for (size_t i = r->nw; i-- > 0 ;) { if (i < words) { r->w[i] = 0; } else { r->w[i] = mp_word(a, i - words); if (bitoff != 0) { r->w[i] <<= bitoff; if (i > words) r->w[i] |= mp_word(a, i - words - 1) >> (BIGNUM_INT_BITS - bitoff); } } } } void mp_rshift_fixed_into(mp_int *r, mp_int *a, size_t bits) { size_t words = bits / BIGNUM_INT_BITS; size_t bitoff = bits % BIGNUM_INT_BITS; for (size_t i = 0; i < r->nw; i++) { r->w[i] = mp_word(a, i + words); if (bitoff != 0) { r->w[i] >>= bitoff; r->w[i] |= mp_word(a, i + words + 1) << (BIGNUM_INT_BITS - bitoff); } } } mp_int *mp_lshift_fixed(mp_int *x, size_t bits) { size_t words = (bits + BIGNUM_INT_BITS - 1) / BIGNUM_INT_BITS; mp_int *r = mp_make_sized(x->nw + words); mp_lshift_fixed_into(r, x, bits); return r; } mp_int *mp_rshift_fixed(mp_int *x, size_t bits) { size_t words = bits / BIGNUM_INT_BITS; size_t nw = x->nw - size_t_min(x->nw, words); mp_int *r = mp_make_sized(size_t_max(nw, 1)); mp_rshift_fixed_into(r, x, bits); return r; } /* * Safe right shift is done using the same technique as * trim_leading_zeroes above: you make an n-word left shift by * composing an appropriate subset of power-of-2-sized shifts, so it * takes log_2(n) loop iterations each of which does a different shift * by a power of 2 words, using the usual bit twiddling to make the * whole shift conditional on the appropriate bit of n. */ static void mp_rshift_safe_in_place(mp_int *r, size_t bits) { size_t wordshift = bits / BIGNUM_INT_BITS; size_t bitshift = bits % BIGNUM_INT_BITS; unsigned clear = (r->nw - wordshift) >> (CHAR_BIT * sizeof(size_t) - 1); mp_cond_clear(r, clear); for (unsigned bit = 0; r->nw >> bit; bit++) { size_t word_offset = (size_t)1 << bit; BignumInt mask = -(BignumInt)((wordshift >> bit) & 1); for (size_t i = 0; i < r->nw; i++) { BignumInt w = mp_word(r, i + word_offset); r->w[i] ^= (r->w[i] ^ w) & mask; } } /* * That's done the shifting by words; now we do the shifting by * bits. */ for (unsigned bit = 0; bit < BIGNUM_INT_BITS_BITS; bit++) { unsigned shift = 1 << bit, upshift = BIGNUM_INT_BITS - shift; BignumInt mask = -(BignumInt)((bitshift >> bit) & 1); for (size_t i = 0; i < r->nw; i++) { BignumInt w = ((r->w[i] >> shift) | (mp_word(r, i+1) << upshift)); r->w[i] ^= (r->w[i] ^ w) & mask; } } } mp_int *mp_rshift_safe(mp_int *x, size_t bits) { mp_int *r = mp_copy(x); mp_rshift_safe_in_place(r, bits); return r; } void mp_rshift_safe_into(mp_int *r, mp_int *x, size_t bits) { mp_copy_into(r, x); mp_rshift_safe_in_place(r, bits); } static void mp_lshift_safe_in_place(mp_int *r, size_t bits) { size_t wordshift = bits / BIGNUM_INT_BITS; size_t bitshift = bits % BIGNUM_INT_BITS; /* * Same strategy as mp_rshift_safe_in_place, but of course the * other way up. */ unsigned clear = (r->nw - wordshift) >> (CHAR_BIT * sizeof(size_t) - 1); mp_cond_clear(r, clear); for (unsigned bit = 0; r->nw >> bit; bit++) { size_t word_offset = (size_t)1 << bit; BignumInt mask = -(BignumInt)((wordshift >> bit) & 1); for (size_t i = r->nw; i-- > 0 ;) { BignumInt w = mp_word(r, i - word_offset); r->w[i] ^= (r->w[i] ^ w) & mask; } } size_t downshift = BIGNUM_INT_BITS - bitshift; size_t no_shift = (downshift >> BIGNUM_INT_BITS_BITS); downshift &= ~-(size_t)no_shift; BignumInt downshifted_mask = ~-(BignumInt)no_shift; for (size_t i = r->nw; i-- > 0 ;) { r->w[i] = (r->w[i] << bitshift) | ((mp_word(r, i-1) >> downshift) & downshifted_mask); } } void mp_lshift_safe_into(mp_int *r, mp_int *x, size_t bits) { mp_copy_into(r, x); mp_lshift_safe_in_place(r, bits); } void mp_reduce_mod_2to(mp_int *x, size_t p) { size_t word = p / BIGNUM_INT_BITS; size_t mask = ((size_t)1 << (p % BIGNUM_INT_BITS)) - 1; for (; word < x->nw; word++) { x->w[word] &= mask; mask = 0; } } /* * Inverse mod 2^n is computed by an iterative technique which doubles * the number of bits at each step. */ mp_int *mp_invert_mod_2to(mp_int *x, size_t p) { /* Input checks: x must be coprime to the modulus, i.e. odd, and p * can't be zero */ assert(x->nw > 0); assert(x->w[0] & 1); assert(p > 0); size_t rw = (p + BIGNUM_INT_BITS - 1) / BIGNUM_INT_BITS; rw = size_t_max(rw, 1); mp_int *r = mp_make_sized(rw); size_t mul_scratchsize = mp_mul_scratchspace(2*rw, rw, rw); mp_int *scratch_orig = mp_make_sized(6 * rw + mul_scratchsize); mp_int scratch_per_iter = *scratch_orig; mp_int mul_scratch = mp_alloc_from_scratch( &scratch_per_iter, mul_scratchsize); r->w[0] = 1; for (size_t b = 1; b < p; b <<= 1) { /* * In each step of this iteration, we have the inverse of x * mod 2^b, and we want the inverse of x mod 2^{2b}. * * Write B = 2^b for convenience, so we want x^{-1} mod B^2. * Let x = x_0 + B x_1 + k B^2, with 0 <= x_0,x_1 < B. * * We want to find r_0 and r_1 such that * (r_1 B + r_0) (x_1 B + x_0) == 1 (mod B^2) * * To begin with, we know r_0 must be the inverse mod B of * x_0, i.e. of x, i.e. it is the inverse we computed in the * previous iteration. So now all we need is r_1. * * Multiplying out, neglecting multiples of B^2, and writing * x_0 r_0 = K B + 1, we have * * r_1 x_0 B + r_0 x_1 B + K B == 0 (mod B^2) * => r_1 x_0 B == - r_0 x_1 B - K B (mod B^2) * => r_1 x_0 == - r_0 x_1 - K (mod B) * => r_1 == r_0 (- r_0 x_1 - K) (mod B) * * (the last step because we multiply through by the inverse * of x_0, which we already know is r_0). */ mp_int scratch_this_iter = scratch_per_iter; size_t Bw = (b + BIGNUM_INT_BITS - 1) / BIGNUM_INT_BITS; size_t B2w = (2*b + BIGNUM_INT_BITS - 1) / BIGNUM_INT_BITS; /* Start by finding K: multiply x_0 by r_0, and shift down. */ mp_int x0 = mp_alloc_from_scratch(&scratch_this_iter, Bw); mp_copy_into(&x0, x); mp_reduce_mod_2to(&x0, b); mp_int r0 = mp_make_alias(r, 0, Bw); mp_int Kshift = mp_alloc_from_scratch(&scratch_this_iter, B2w); mp_mul_internal(&Kshift, &x0, &r0, mul_scratch); mp_int K = mp_alloc_from_scratch(&scratch_this_iter, Bw); mp_rshift_fixed_into(&K, &Kshift, b); /* Now compute the product r_0 x_1, reusing the space of Kshift. */ mp_int x1 = mp_alloc_from_scratch(&scratch_this_iter, Bw); mp_rshift_fixed_into(&x1, x, b); mp_reduce_mod_2to(&x1, b); mp_int r0x1 = mp_make_alias(&Kshift, 0, Bw); mp_mul_internal(&r0x1, &r0, &x1, mul_scratch); /* Add K to that. */ mp_add_into(&r0x1, &r0x1, &K); /* Negate it. */ mp_neg_into(&r0x1, &r0x1); /* Multiply by r_0. */ mp_int r1 = mp_alloc_from_scratch(&scratch_this_iter, Bw); mp_mul_internal(&r1, &r0, &r0x1, mul_scratch); mp_reduce_mod_2to(&r1, b); /* That's our r_1, so add it on to r_0 to get the full inverse * output from this iteration. */ mp_lshift_fixed_into(&K, &r1, (b % BIGNUM_INT_BITS)); size_t Bpos = b / BIGNUM_INT_BITS; mp_int r1_position = mp_make_alias(r, Bpos, B2w-Bpos); mp_add_into(&r1_position, &r1_position, &K); } /* Finally, reduce mod the precise desired number of bits. */ mp_reduce_mod_2to(r, p); mp_free(scratch_orig); return r; } static size_t monty_scratch_size(MontyContext *mc) { return 3*mc->rw + mc->pw + mp_mul_scratchspace(mc->pw, mc->rw, mc->rw); } MontyContext *monty_new(mp_int *modulus) { MontyContext *mc = snew(MontyContext); mc->rw = modulus->nw; mc->rbits = mc->rw * BIGNUM_INT_BITS; mc->pw = mc->rw * 2 + 1; mc->m = mp_copy(modulus); mc->minus_minv_mod_r = mp_invert_mod_2to(mc->m, mc->rbits); mp_neg_into(mc->minus_minv_mod_r, mc->minus_minv_mod_r); mp_int *r = mp_make_sized(mc->rw + 1); r->w[mc->rw] = 1; mc->powers_of_r_mod_m[0] = mp_mod(r, mc->m); mp_free(r); for (size_t j = 1; j < lenof(mc->powers_of_r_mod_m); j++) mc->powers_of_r_mod_m[j] = mp_modmul( mc->powers_of_r_mod_m[0], mc->powers_of_r_mod_m[j-1], mc->m); mc->scratch = mp_make_sized(monty_scratch_size(mc)); return mc; } void monty_free(MontyContext *mc) { mp_free(mc->m); for (size_t j = 0; j < 3; j++) mp_free(mc->powers_of_r_mod_m[j]); mp_free(mc->minus_minv_mod_r); mp_free(mc->scratch); smemclr(mc, sizeof(*mc)); sfree(mc); } /* * The main Montgomery reduction step. */ static mp_int monty_reduce_internal(MontyContext *mc, mp_int *x, mp_int scratch) { /* * The trick with Montgomery reduction is that on the one hand we * want to reduce the size of the input by a factor of about r, * and on the other hand, the two numbers we just multiplied were * both stored with an extra factor of r multiplied in. So we * computed ar*br = ab r^2, but we want to return abr, so we need * to divide by r - and if we can do that by _actually dividing_ * by r then this also reduces the size of the number. * * But we can only do that if the number we're dividing by r is a * multiple of r. So first we must add an adjustment to it which * clears its bottom 'rbits' bits. That adjustment must be a * multiple of m in order to leave the residue mod n unchanged, so * the question is, what multiple of m can we add to x to make it * congruent to 0 mod r? And the answer is, x * (-m)^{-1} mod r. */ /* x mod r */ mp_int x_lo = mp_make_alias(x, 0, mc->rbits); /* x * (-m)^{-1}, i.e. the number we want to multiply by m */ mp_int k = mp_alloc_from_scratch(&scratch, mc->rw); mp_mul_internal(&k, &x_lo, mc->minus_minv_mod_r, scratch); /* m times that, i.e. the number we want to add to x */ mp_int mk = mp_alloc_from_scratch(&scratch, mc->pw); mp_mul_internal(&mk, mc->m, &k, scratch); /* Add it to x */ mp_add_into(&mk, x, &mk); /* Reduce mod r, by simply making an alias to the upper words of x */ mp_int toret = mp_make_alias(&mk, mc->rw, mk.nw - mc->rw); /* * We'll generally be doing this after a multiplication of two * fully reduced values. So our input could be anything up to m^2, * and then we added up to rm to it. Hence, the maximum value is * rm+m^2, and after dividing by r, that becomes r + m(m/r) < 2r. * So a single trial-subtraction will finish reducing to the * interval [0,m). */ mp_cond_sub_into(&toret, &toret, mc->m, mp_cmp_hs(&toret, mc->m)); return toret; } void monty_mul_into(MontyContext *mc, mp_int *r, mp_int *x, mp_int *y) { assert(x->nw <= mc->rw); assert(y->nw <= mc->rw); mp_int scratch = *mc->scratch; mp_int tmp = mp_alloc_from_scratch(&scratch, 2*mc->rw); mp_mul_into(&tmp, x, y); mp_int reduced = monty_reduce_internal(mc, &tmp, scratch); mp_copy_into(r, &reduced); mp_clear(mc->scratch); } mp_int *monty_mul(MontyContext *mc, mp_int *x, mp_int *y) { mp_int *toret = mp_make_sized(mc->rw); monty_mul_into(mc, toret, x, y); return toret; } mp_int *monty_modulus(MontyContext *mc) { return mc->m; } mp_int *monty_identity(MontyContext *mc) { return mc->powers_of_r_mod_m[0]; } mp_int *monty_invert(MontyContext *mc, mp_int *x) { /* Given xr, we want to return x^{-1}r = (xr)^{-1} r^2 = * monty_reduce((xr)^{-1} r^3) */ mp_int *tmp = mp_invert(x, mc->m); mp_int *toret = monty_mul(mc, tmp, mc->powers_of_r_mod_m[2]); mp_free(tmp); return toret; } /* * Importing a number into Montgomery representation involves * multiplying it by r and reducing mod m. We use the general-purpose * mp_modmul for this, in case the input number is out of range. */ mp_int *monty_import(MontyContext *mc, mp_int *x) { return mp_modmul(x, mc->powers_of_r_mod_m[0], mc->m); } void monty_import_into(MontyContext *mc, mp_int *r, mp_int *x) { mp_int *imported = monty_import(mc, x); mp_copy_into(r, imported); mp_free(imported); } /* * Exporting a number means multiplying it by r^{-1}, which is exactly * what monty_reduce does anyway, so we just do that. */ void monty_export_into(MontyContext *mc, mp_int *r, mp_int *x) { assert(x->nw <= 2*mc->rw); mp_int reduced = monty_reduce_internal(mc, x, *mc->scratch); mp_copy_into(r, &reduced); mp_clear(mc->scratch); } mp_int *monty_export(MontyContext *mc, mp_int *x) { mp_int *toret = mp_make_sized(mc->rw); monty_export_into(mc, toret, x); return toret; } static void monty_reduce(MontyContext *mc, mp_int *x) { mp_int reduced = monty_reduce_internal(mc, x, *mc->scratch); mp_copy_into(x, &reduced); mp_clear(mc->scratch); } mp_int *monty_pow(MontyContext *mc, mp_int *base, mp_int *exponent) { /* square builds up powers of the form base^{2^i}. */ mp_int *square = mp_copy(base); size_t i = 0; /* out accumulates the output value. Starts at 1 (in Montgomery * representation) and we multiply in each base^{2^i}. */ mp_int *out = mp_copy(mc->powers_of_r_mod_m[0]); /* tmp holds each product we compute and reduce. */ mp_int *tmp = mp_make_sized(mc->rw * 2); while (true) { mp_mul_into(tmp, out, square); monty_reduce(mc, tmp); mp_select_into(out, out, tmp, mp_get_bit(exponent, i)); if (++i >= exponent->nw * BIGNUM_INT_BITS) break; mp_mul_into(tmp, square, square); monty_reduce(mc, tmp); mp_copy_into(square, tmp); } mp_free(square); mp_free(tmp); mp_clear(mc->scratch); return out; } mp_int *mp_modpow(mp_int *base, mp_int *exponent, mp_int *modulus) { assert(modulus->nw > 0); assert(modulus->w[0] & 1); MontyContext *mc = monty_new(modulus); mp_int *m_base = monty_import(mc, base); mp_int *m_out = monty_pow(mc, m_base, exponent); mp_int *out = monty_export(mc, m_out); mp_free(m_base); mp_free(m_out); monty_free(mc); return out; } /* * Given two input integers a,b which are not both even, computes d = * gcd(a,b) and also two integers A,B such that A*a - B*b = d. A,B * will be the minimal non-negative pair satisfying that criterion, * which is equivalent to saying that 0 <= A < b/d and 0 <= B < a/d. * * This algorithm is an adapted form of Stein's algorithm, which * computes gcd(a,b) using only addition and bit shifts (i.e. without * needing general division), using the following rules: * * - if both of a,b are even, divide off a common factor of 2 * - if one of a,b (WLOG a) is even, then gcd(a,b) = gcd(a/2,b), so * just divide a by 2 * - if both of a,b are odd, then WLOG a>b, and gcd(a,b) = * gcd(b,(a-b)/2). * * Sometimes this function is used for modular inversion, in which * case we already know we expect the two inputs to be coprime, so to * save time the 'both even' initial case is assumed not to arise (or * to have been handled already by the caller). So this function just * performs a sequence of reductions in the following form: * * - if a,b are both odd, sort them so that a > b, and replace a with * b-a; otherwise sort them so that a is the even one * - either way, now a is even and b is odd, so divide a by 2. * * The big change to Stein's algorithm is that we need the Bezout * coefficients as output, not just the gcd. So we need to know how to * generate those in each case, based on the coefficients from the * reduced pair of numbers: * * - If a is even, and u,v are such that u*(a/2) + v*b = d: * + if u is also even, then this is just (u/2)*a + v*b = d * + otherwise, (u+b)*(a/2) + (v-a/2)*b is also equal to d, and * since u and b are both odd, (u+b)/2 is an integer, so we have * ((u+b)/2)*a + (v-a/2)*b = d. * * - If a,b are both odd, and u,v are such that u*b + v*(a-b) = d, * then v*a + (u-v)*b = d. * * In the case where we passed from (a,b) to (b,(a-b)/2), we regard it * as having first subtracted b from a and then halved a, so both of * these transformations must be done in sequence. * * The code below transforms this from a recursive to an iterative * algorithm. We first reduce a,b to 0,1, recording at each stage * whether we did the initial subtraction, and whether we had to swap * the two values; then we iterate backwards over that record of what * we did, applying the above rules for building up the Bezout * coefficients as we go. Of course, all the case analysis is done by * the usual bit-twiddling conditionalisation to avoid data-dependent * control flow. * * Also, since these mp_ints are generally treated as unsigned, we * store the coefficients by absolute value, with the semantics that * they always have opposite sign, and in the unwinding loop we keep a * bit indicating whether Aa-Bb is currently expected to be +d or -d, * so that we can do one final conditional adjustment if it's -d. * * Once the reduction rules have managed to reduce the input numbers * to (0,d), then they are stable (the next reduction will always * divide the even one by 2, which maps 0 to 0). So it doesn't matter * if we do more steps of the algorithm than necessary; hence, for * constant time, we just need to find the maximum number we could * _possibly_ require, and do that many. * * If a,b < 2^n, at most 2n iterations are required. Proof: consider * the quantity Q = log_2(a) + log_2(b). Every step halves one of the * numbers (and may also reduce one of them further by doing a * subtraction beforehand, but in the worst case, not by much or not * at all). So Q reduces by at least 1 per iteration, and it starts * off with a value at most 2n. * * The worst case inputs (I think) are where x=2^{n-1} and y=2^n-1 * (i.e. x is a power of 2 and y is all 1s). In that situation, the * first n-1 steps repeatedly halve x until it's 1, and then there are * n further steps each of which subtracts 1 from y and halves it. */ static void mp_bezout_into(mp_int *a_coeff_out, mp_int *b_coeff_out, mp_int *gcd_out, mp_int *a_in, mp_int *b_in) { size_t nw = size_t_max(1, size_t_max(a_in->nw, b_in->nw)); /* Make mutable copies of the input numbers */ mp_int *a = mp_make_sized(nw), *b = mp_make_sized(nw); mp_copy_into(a, a_in); mp_copy_into(b, b_in); /* Space to build up the output coefficients, with an extra word * so that intermediate values can overflow off the top and still * right-shift back down to the correct value */ mp_int *ac = mp_make_sized(nw + 1), *bc = mp_make_sized(nw + 1); /* And a general-purpose temp register */ mp_int *tmp = mp_make_sized(nw); /* Space to record the sequence of reduction steps to unwind. We * make it a BignumInt for no particular reason except that (a) * mp_make_sized conveniently zeroes the allocation and mp_free * wipes it, and (b) this way I can use mp_dump() if I have to * debug this code. */ size_t steps = 2 * nw * BIGNUM_INT_BITS; mp_int *record = mp_make_sized( (steps*2 + BIGNUM_INT_BITS - 1) / BIGNUM_INT_BITS); for (size_t step = 0; step < steps; step++) { /* * If a and b are both odd, we want to sort them so that a is * larger. But if one is even, we want to sort them so that a * is the even one. */ unsigned swap_if_both_odd = mp_cmp_hs(b, a); unsigned swap_if_one_even = a->w[0] & 1; unsigned both_odd = a->w[0] & b->w[0] & 1; unsigned swap = swap_if_one_even ^ ( (swap_if_both_odd ^ swap_if_one_even) & both_odd); mp_cond_swap(a, b, swap); /* * If a,b are both odd, then a is the larger number, so * subtract the smaller one from it. */ mp_cond_sub_into(a, a, b, both_odd); /* * Now a is even, so divide it by two. */ mp_rshift_fixed_into(a, a, 1); /* * Record the two 1-bit values both_odd and swap. */ mp_set_bit(record, step*2, both_odd); mp_set_bit(record, step*2+1, swap); } /* * Now we expect to have reduced the two numbers to 0 and d, * although we don't know which way round. (But we avoid checking * this by assertion; sometimes we'll need to do this computation * without giving away that we already know the inputs were bogus. * So we'd prefer to just press on and return nonsense.) */ if (gcd_out) { /* * At this point we can return the actual gcd. Since one of * a,b is it and the other is zero, the easiest way to get it * is to add them together. */ mp_add_into(gcd_out, a, b); } /* * If the caller _only_ wanted the gcd, and neither Bezout * coefficient is even required, we can skip the entire unwind * stage. */ if (a_coeff_out || b_coeff_out) { /* * The Bezout coefficients of a,b at this point are simply 0 * for whichever of a,b is zero, and 1 for whichever is * nonzero. The nonzero number equals gcd(a,b), which by * assumption is odd, so we can do this by just taking the low * bit of each one. */ ac->w[0] = mp_get_bit(a, 0); bc->w[0] = mp_get_bit(b, 0); /* * Overwrite a,b themselves with those same numbers. This has * the effect of dividing both of them by d, which will * arrange that during the unwind stage we generate the * minimal coefficients instead of a larger pair. */ mp_copy_into(a, ac); mp_copy_into(b, bc); /* * We'll maintain the invariant as we unwind that ac * a - bc * * b is either +d or -d (or rather, +1/-1 after scaling by * d), and we'll remember which. (We _could_ keep it at +d the * whole time, but it would cost more work every time round * the loop, so it's cheaper to fix that up once at the end.) * * Initially, the result is +d if a was the nonzero value after * reduction, and -d if b was. */ unsigned minus_d = b->w[0]; for (size_t step = steps; step-- > 0 ;) { /* * Recover the data from the step we're unwinding. */ unsigned both_odd = mp_get_bit(record, step*2); unsigned swap = mp_get_bit(record, step*2+1); /* * Unwind the division: if our coefficient of a is odd, we * adjust the coefficients by +b and +a respectively. */ unsigned adjust = ac->w[0] & 1; mp_cond_add_into(ac, ac, b, adjust); mp_cond_add_into(bc, bc, a, adjust); /* * Now ac is definitely even, so we divide it by two. */ mp_rshift_fixed_into(ac, ac, 1); /* * Now unwind the subtraction, if there was one, by adding * ac to bc. */ mp_cond_add_into(bc, bc, ac, both_odd); /* * Undo the transformation of the input numbers, by * multiplying a by 2 and then adding b to a (the latter * only if both_odd). */ mp_lshift_fixed_into(a, a, 1); mp_cond_add_into(a, a, b, both_odd); /* * Finally, undo the swap. If we do swap, this also * reverses the sign of the current result ac*a+bc*b. */ mp_cond_swap(a, b, swap); mp_cond_swap(ac, bc, swap); minus_d ^= swap; } /* * Now we expect to have recovered the input a,b (or rather, * the versions of them divided by d). But we might find that * our current result is -d instead of +d, that is, we have * A',B' such that A'a - B'b = -d. * * In that situation, we set A = b-A' and B = a-B', giving us * Aa-Bb = ab - A'a - ab + B'b = +1. */ mp_sub_into(tmp, b, ac); mp_select_into(ac, ac, tmp, minus_d); mp_sub_into(tmp, a, bc); mp_select_into(bc, bc, tmp, minus_d); /* * Now we really are done. Return the outputs. */ if (a_coeff_out) mp_copy_into(a_coeff_out, ac); if (b_coeff_out) mp_copy_into(b_coeff_out, bc); } mp_free(a); mp_free(b); mp_free(ac); mp_free(bc); mp_free(tmp); mp_free(record); } mp_int *mp_invert(mp_int *x, mp_int *m) { mp_int *result = mp_make_sized(m->nw); mp_bezout_into(result, NULL, NULL, x, m); return result; } void mp_gcd_into(mp_int *a, mp_int *b, mp_int *gcd, mp_int *A, mp_int *B) { /* * Identify shared factors of 2. To do this we OR the two numbers * to get something whose lowest set bit is in the right place, * remove all higher bits by ANDing it with its own negation, and * use mp_get_nbits to find the location of the single remaining * set bit. */ mp_int *tmp = mp_make_sized(size_t_max(a->nw, b->nw)); for (size_t i = 0; i < tmp->nw; i++) tmp->w[i] = mp_word(a, i) | mp_word(b, i); BignumCarry carry = 1; for (size_t i = 0; i < tmp->nw; i++) { BignumInt negw; BignumADC(negw, carry, 0, ~tmp->w[i], carry); tmp->w[i] &= negw; } size_t shift = mp_get_nbits(tmp) - 1; mp_free(tmp); /* * Make copies of a,b with those shared factors of 2 divided off, * so that at least one is odd (which is the precondition for * mp_bezout_into). Compute the gcd of those. */ mp_int *as = mp_rshift_safe(a, shift); mp_int *bs = mp_rshift_safe(b, shift); mp_bezout_into(A, B, gcd, as, bs); mp_free(as); mp_free(bs); /* * And finally shift the gcd back up (unless the caller didn't * even ask for it), to put the shared factors of 2 back in. */ if (gcd) mp_lshift_safe_in_place(gcd, shift); } mp_int *mp_gcd(mp_int *a, mp_int *b) { mp_int *gcd = mp_make_sized(size_t_min(a->nw, b->nw)); mp_gcd_into(a, b, gcd, NULL, NULL); return gcd; } unsigned mp_coprime(mp_int *a, mp_int *b) { mp_int *gcd = mp_gcd(a, b); unsigned toret = mp_eq_integer(gcd, 1); mp_free(gcd); return toret; } static uint32_t recip_approx_32(uint32_t x) { /* * Given an input x in [2^31,2^32), i.e. a uint32_t with its high * bit set, this function returns an approximation to 2^63/x, * computed using only multiplications and bit shifts just in case * the C divide operator has non-constant time (either because the * underlying machine instruction does, or because the operator * expands to a library function on a CPU without hardware * division). * * The coefficients are derived from those of the degree-9 * polynomial which is the minimax-optimal approximation to that * function on the given interval (generated using the Remez * algorithm), converted into integer arithmetic with shifts used * to maximise the number of significant bits at every state. (A * sort of 'static floating point' - the exponent is statically * known at every point in the code, so it never needs to be * stored at run time or to influence runtime decisions.) * * Exhaustive iteration over the whole input space shows the * largest possible error to be 1686.54. (The input value * attaining that bound is 4226800006 == 0xfbefd986, whose true * reciprocal is 2182116973.540... == 0x8210766d.8a6..., whereas * this function returns 2182115287 == 0x82106fd7.) */ uint64_t r = 0x92db03d6ULL; r = 0xf63e71eaULL - ((r*x) >> 34); r = 0xb63721e8ULL - ((r*x) >> 34); r = 0x9c2da00eULL - ((r*x) >> 33); r = 0xaada0bb8ULL - ((r*x) >> 32); r = 0xf75cd403ULL - ((r*x) >> 31); r = 0xecf97a41ULL - ((r*x) >> 31); r = 0x90d876cdULL - ((r*x) >> 31); r = 0x6682799a0ULL - ((r*x) >> 26); return r; } void mp_divmod_into(mp_int *n, mp_int *d, mp_int *q_out, mp_int *r_out) { assert(!mp_eq_integer(d, 0)); /* * We do division by using Newton-Raphson iteration to converge to * the reciprocal of d (or rather, R/d for R a sufficiently large * power of 2); then we multiply that reciprocal by n; and we * finish up with conditional subtraction. * * But we have to do it in a fixed number of N-R iterations, so we * need some error analysis to know how many we might need. * * The iteration is derived by defining f(r) = d - R/r. * Differentiating gives f'(r) = R/r^2, and the Newton-Raphson * formula applied to those functions gives * * r_{i+1} = r_i - f(r_i) / f'(r_i) * = r_i - (d - R/r_i) r_i^2 / R * = r_i (2 R - d r_i) / R * * Now let e_i be the error in a given iteration, in the sense * that * * d r_i = R + e_i * i.e. e_i/R = (r_i - r_true) / r_true * * so e_i is the _relative_ error in r_i. * * We must also introduce a rounding-error term, because the * division by R always gives an integer. This might make the * output off by up to 1 (in the negative direction, because * right-shifting gives floor of the true quotient). So when we * divide by R, we must imagine adding some f in [0,1). Then we * have * * d r_{i+1} = d r_i (2 R - d r_i) / R - d f * = (R + e_i) (R - e_i) / R - d f * = (R^2 - e_i^2) / R - d f * = R - (e_i^2 / R + d f) * => e_{i+1} = - (e_i^2 / R + d f) * * The sum of two positive quantities is bounded above by twice * their max, and max |f| = 1, so we can bound this as follows: * * |e_{i+1}| <= 2 max (e_i^2/R, d) * |e_{i+1}/R| <= 2 max ((e_i/R)^2, d/R) * log2 |R/e_{i+1}| <= min (2 log2 |R/e_i|, log2 |R/d|) - 1 * * which tells us that the number of 'good' bits - i.e. * log2(R/e_i) - very nearly doubles at every iteration (apart * from that subtraction of 1), until it gets to the same size as * log2(R/d). In other words, the size of R in bits has to be the * size of denominator we're putting in, _plus_ the amount of * precision we want to get back out. * * So when we multiply n (the input numerator) by our final * reciprocal approximation r, but actually r differs from R/d by * up to 2, then it follows that * * n/d - nr/R = n/d - [ n (R/d + e) ] / R * = n/d - [ (n/d) R + n e ] / R * = -ne/R * => 0 <= n/d - nr/R < 2n/R * * so our computed quotient can differ from the true n/d by up to * 2n/R. Hence, as long as we also choose R large enough that 2n/R * is bounded above by a constant, we can guarantee a bounded * number of final conditional-subtraction steps. */ /* * Get at least 32 of the most significant bits of the input * number. */ size_t hiword_index = 0; uint64_t hibits = 0, lobits = 0; mp_find_highest_nonzero_word_pair(d, 64 - BIGNUM_INT_BITS, &hiword_index, &hibits, &lobits); /* * Make a shifted combination of those two words which puts the * topmost bit of the number at bit 63. */ size_t shift_up = 0; for (size_t i = BIGNUM_INT_BITS_BITS; i-- > 0;) { size_t sl = (size_t)1 << i; /* left shift count */ size_t sr = 64 - sl; /* complementary right-shift count */ /* Should we shift up? */ unsigned indicator = 1 ^ normalise_to_1_u64(hibits >> sr); /* If we do, what will we get? */ uint64_t new_hibits = (hibits << sl) | (lobits >> sr); uint64_t new_lobits = lobits << sl; size_t new_shift_up = shift_up + sl; /* Conditionally swap those values in. */ hibits ^= (hibits ^ new_hibits ) & -(uint64_t)indicator; lobits ^= (lobits ^ new_lobits ) & -(uint64_t)indicator; shift_up ^= (shift_up ^ new_shift_up ) & -(size_t) indicator; } /* * So now we know the most significant 32 bits of d are at the top * of hibits. Approximate the reciprocal of those bits. */ lobits = (uint64_t)recip_approx_32(hibits >> 32) << 32; hibits = 0; /* * And shift that up by as many bits as the input was shifted up * just now, so that the product of this approximation and the * actual input will be close to a fixed power of two regardless * of where the MSB was. * * I do this in another log n individual passes, partly in case * the CPU's register-controlled shift operation isn't * time-constant, and also in case the compiler code-generates * uint64_t shifts out of a variable number of smaller-word shift * instructions, e.g. by splitting up into cases. */ for (size_t i = BIGNUM_INT_BITS_BITS; i-- > 0;) { size_t sl = (size_t)1 << i; /* left shift count */ size_t sr = 64 - sl; /* complementary right-shift count */ /* Should we shift up? */ unsigned indicator = 1 & (shift_up >> i); /* If we do, what will we get? */ uint64_t new_hibits = (hibits << sl) | (lobits >> sr); uint64_t new_lobits = lobits << sl; /* Conditionally swap those values in. */ hibits ^= (hibits ^ new_hibits ) & -(uint64_t)indicator; lobits ^= (lobits ^ new_lobits ) & -(uint64_t)indicator; } /* * The product of the 128-bit value now in hibits:lobits with the * 128-bit value we originally retrieved in the same variables * will be in the vicinity of 2^191. So we'll take log2(R) to be * 191, plus a multiple of BIGNUM_INT_BITS large enough to allow R * to hold the combined sizes of n and d. */ size_t log2_R; { size_t max_log2_n = (n->nw + d->nw) * BIGNUM_INT_BITS; log2_R = max_log2_n + 3; log2_R -= size_t_min(191, log2_R); log2_R = (log2_R + BIGNUM_INT_BITS - 1) & ~(BIGNUM_INT_BITS - 1); log2_R += 191; } /* Number of words in a bignum capable of holding numbers the size * of twice R. */ size_t rw = ((log2_R+2) + BIGNUM_INT_BITS - 1) / BIGNUM_INT_BITS; /* * Now construct our full-sized starting reciprocal approximation. */ mp_int *r_approx = mp_make_sized(rw); size_t output_bit_index; { /* Where in the input number did the input 128-bit value come from? */ size_t input_bit_index = (hiword_index * BIGNUM_INT_BITS) - (128 - BIGNUM_INT_BITS); /* So how far do we need to shift our 64-bit output, if the * product of those two fixed-size values is 2^191 and we want * to make it 2^log2_R instead? */ output_bit_index = log2_R - 191 - input_bit_index; /* If we've done all that right, it should be a whole number * of words. */ assert(output_bit_index % BIGNUM_INT_BITS == 0); size_t output_word_index = output_bit_index / BIGNUM_INT_BITS; mp_add_integer_into_shifted_by_words( r_approx, r_approx, lobits, output_word_index); mp_add_integer_into_shifted_by_words( r_approx, r_approx, hibits, output_word_index + 64 / BIGNUM_INT_BITS); } /* * Make the constant 2*R, which we'll need in the iteration. */ mp_int *two_R = mp_make_sized(rw); mp_add_integer_into_shifted_by_words( two_R, two_R, (BignumInt)1 << ((log2_R+1) % BIGNUM_INT_BITS), (log2_R+1) / BIGNUM_INT_BITS); /* * Scratch space. */ mp_int *dr = mp_make_sized(rw + d->nw); mp_int *diff = mp_make_sized(size_t_max(rw, dr->nw)); mp_int *product = mp_make_sized(rw + diff->nw); size_t scratchsize = size_t_max( mp_mul_scratchspace(dr->nw, r_approx->nw, d->nw), mp_mul_scratchspace(product->nw, r_approx->nw, diff->nw)); mp_int *scratch = mp_make_sized(scratchsize); mp_int product_shifted = mp_make_alias( product, log2_R / BIGNUM_INT_BITS, product->nw); /* * Initial error estimate: the 32-bit output of recip_approx_32 * differs by less than 2048 (== 2^11) from the true top 32 bits * of the reciprocal, so the relative error is at most 2^11 * divided by the 32-bit reciprocal, which at worst is 2^11/2^31 = * 2^-20. So even in the worst case, we have 20 good bits of * reciprocal to start with. */ size_t good_bits = 31 - 11; size_t good_bits_needed = BIGNUM_INT_BITS * n->nw + 4; /* add a few */ /* * Now do Newton-Raphson iterations until we have reason to think * they're not converging any more. */ while (good_bits < good_bits_needed) { /* * Compute the next iterate. */ mp_mul_internal(dr, r_approx, d, *scratch); mp_sub_into(diff, two_R, dr); mp_mul_internal(product, r_approx, diff, *scratch); mp_rshift_fixed_into(r_approx, &product_shifted, log2_R % BIGNUM_INT_BITS); /* * Adjust the error estimate. */ good_bits = good_bits * 2 - 1; } mp_free(dr); mp_free(diff); mp_free(product); mp_free(scratch); /* * Now we've got our reciprocal, we can compute the quotient, by * multiplying in n and then shifting down by log2_R bits. */ mp_int *quotient_full = mp_mul(r_approx, n); mp_int quotient_alias = mp_make_alias( quotient_full, log2_R / BIGNUM_INT_BITS, quotient_full->nw); mp_int *quotient = mp_make_sized(n->nw); mp_rshift_fixed_into(quotient, "ient_alias, log2_R % BIGNUM_INT_BITS); /* * Next, compute the remainder. */ mp_int *remainder = mp_make_sized(d->nw); mp_mul_into(remainder, quotient, d); mp_sub_into(remainder, n, remainder); /* * Finally, two conditional subtractions to fix up any remaining * rounding error. (I _think_ one should be enough, but this * routine isn't time-critical enough to take chances.) */ unsigned q_correction = 0; for (unsigned iter = 0; iter < 2; iter++) { unsigned need_correction = mp_cmp_hs(remainder, d); mp_cond_sub_into(remainder, remainder, d, need_correction); q_correction += need_correction; } mp_add_integer_into(quotient, quotient, q_correction); /* * Now we should have a perfect answer, i.e. 0 <= r < d. */ assert(!mp_cmp_hs(remainder, d)); if (q_out) mp_copy_into(q_out, quotient); if (r_out) mp_copy_into(r_out, remainder); mp_free(r_approx); mp_free(two_R); mp_free(quotient_full); mp_free(quotient); mp_free(remainder); } mp_int *mp_div(mp_int *n, mp_int *d) { mp_int *q = mp_make_sized(n->nw); mp_divmod_into(n, d, q, NULL); return q; } mp_int *mp_mod(mp_int *n, mp_int *d) { mp_int *r = mp_make_sized(d->nw); mp_divmod_into(n, d, NULL, r); return r; } mp_int *mp_nthroot(mp_int *y, unsigned n, mp_int *remainder_out) { /* * Allocate scratch space. */ mp_int **alloc, **powers, **newpowers, *scratch; size_t nalloc = 2*(n+1)+1; alloc = snewn(nalloc, mp_int *); for (size_t i = 0; i < nalloc; i++) alloc[i] = mp_make_sized(y->nw + 1); powers = alloc; newpowers = alloc + (n+1); scratch = alloc[2*n+2]; /* * We're computing the rounded-down nth root of y, i.e. the * maximal x such that x^n <= y. We try to add 2^i to it for each * possible value of i, starting from the largest one that might * fit (i.e. such that 2^{n*i} fits in the size of y) downwards to * i=0. * * We track all the smaller powers of x in the array 'powers'. In * each iteration, if we update x, we update all of those values * to match. */ mp_copy_integer_into(powers[0], 1); for (size_t s = mp_max_bits(y) / n + 1; s-- > 0 ;) { /* * Let b = 2^s. We need to compute the powers (x+b)^i for each * i, starting from our recorded values of x^i. */ for (size_t i = 0; i < n+1; i++) { /* * (x+b)^i = x^i * + (i choose 1) x^{i-1} b * + (i choose 2) x^{i-2} b^2 * + ... * + b^i */ uint16_t binom = 1; /* coefficient of b^i */ mp_copy_into(newpowers[i], powers[i]); for (size_t j = 0; j < i; j++) { /* newpowers[i] += binom * powers[j] * 2^{(i-j)*s} */ mp_mul_integer_into(scratch, powers[j], binom); mp_lshift_fixed_into(scratch, scratch, (i-j) * s); mp_add_into(newpowers[i], newpowers[i], scratch); uint32_t binom_mul = binom; binom_mul *= (i-j); binom_mul /= (j+1); assert(binom_mul < 0x10000); binom = binom_mul; } } /* * Now, is the new value of x^n still <= y? If so, update. */ unsigned newbit = mp_cmp_hs(y, newpowers[n]); for (size_t i = 0; i < n+1; i++) mp_select_into(powers[i], powers[i], newpowers[i], newbit); } if (remainder_out) mp_sub_into(remainder_out, y, powers[n]); mp_int *root = mp_new(mp_max_bits(y) / n); mp_copy_into(root, powers[1]); for (size_t i = 0; i < nalloc; i++) mp_free(alloc[i]); sfree(alloc); return root; } mp_int *mp_modmul(mp_int *x, mp_int *y, mp_int *modulus) { mp_int *product = mp_mul(x, y); mp_int *reduced = mp_mod(product, modulus); mp_free(product); return reduced; } mp_int *mp_modadd(mp_int *x, mp_int *y, mp_int *modulus) { mp_int *sum = mp_add(x, y); mp_int *reduced = mp_mod(sum, modulus); mp_free(sum); return reduced; } mp_int *mp_modsub(mp_int *x, mp_int *y, mp_int *modulus) { mp_int *diff = mp_make_sized(size_t_max(x->nw, y->nw)); mp_sub_into(diff, x, y); unsigned negate = mp_cmp_hs(y, x); mp_cond_negate(diff, diff, negate); mp_int *residue = mp_mod(diff, modulus); mp_cond_negate(residue, residue, negate); /* If we've just negated the residue, then it will be < 0 and need * the modulus adding to it to make it positive - *except* if the * residue was zero when we negated it. */ unsigned make_positive = negate & ~mp_eq_integer(residue, 0); mp_cond_add_into(residue, residue, modulus, make_positive); mp_free(diff); return residue; } static mp_int *mp_modadd_in_range(mp_int *x, mp_int *y, mp_int *modulus) { mp_int *sum = mp_make_sized(modulus->nw); unsigned carry = mp_add_into_internal(sum, x, y); mp_cond_sub_into(sum, sum, modulus, carry | mp_cmp_hs(sum, modulus)); return sum; } static mp_int *mp_modsub_in_range(mp_int *x, mp_int *y, mp_int *modulus) { mp_int *diff = mp_make_sized(modulus->nw); mp_sub_into(diff, x, y); mp_cond_add_into(diff, diff, modulus, 1 ^ mp_cmp_hs(x, y)); return diff; } mp_int *monty_add(MontyContext *mc, mp_int *x, mp_int *y) { return mp_modadd_in_range(x, y, mc->m); } mp_int *monty_sub(MontyContext *mc, mp_int *x, mp_int *y) { return mp_modsub_in_range(x, y, mc->m); } void mp_min_into(mp_int *r, mp_int *x, mp_int *y) { mp_select_into(r, x, y, mp_cmp_hs(x, y)); } void mp_max_into(mp_int *r, mp_int *x, mp_int *y) { mp_select_into(r, y, x, mp_cmp_hs(x, y)); } mp_int *mp_min(mp_int *x, mp_int *y) { mp_int *r = mp_make_sized(size_t_min(x->nw, y->nw)); mp_min_into(r, x, y); return r; } mp_int *mp_max(mp_int *x, mp_int *y) { mp_int *r = mp_make_sized(size_t_max(x->nw, y->nw)); mp_max_into(r, x, y); return r; } mp_int *mp_power_2(size_t power) { mp_int *x = mp_new(power + 1); mp_set_bit(x, power, 1); return x; } struct ModsqrtContext { mp_int *p; /* the prime */ MontyContext *mc; /* for doing arithmetic mod p */ /* Decompose p-1 as 2^e k, for positive integer e and odd k */ size_t e; mp_int *k; mp_int *km1o2; /* (k-1)/2 */ /* The user-provided value z which is not a quadratic residue mod * p, and its kth power. Both in Montgomery form. */ mp_int *z, *zk; }; ModsqrtContext *modsqrt_new(mp_int *p, mp_int *any_nonsquare_mod_p) { ModsqrtContext *sc = snew(ModsqrtContext); memset(sc, 0, sizeof(ModsqrtContext)); sc->p = mp_copy(p); sc->mc = monty_new(sc->p); sc->z = monty_import(sc->mc, any_nonsquare_mod_p); /* Find the lowest set bit in p-1. Since this routine expects p to * be non-secret (typically a well-known standard elliptic curve * parameter), for once we don't need clever bit tricks. */ for (sc->e = 1; sc->e < BIGNUM_INT_BITS * p->nw; sc->e++) if (mp_get_bit(p, sc->e)) break; sc->k = mp_rshift_fixed(p, sc->e); sc->km1o2 = mp_rshift_fixed(sc->k, 1); /* Leave zk to be filled in lazily, since it's more expensive to * compute. If this context turns out never to be needed, we can * save the bulk of the setup time this way. */ return sc; } static void modsqrt_lazy_setup(ModsqrtContext *sc) { if (!sc->zk) sc->zk = monty_pow(sc->mc, sc->z, sc->k); } void modsqrt_free(ModsqrtContext *sc) { monty_free(sc->mc); mp_free(sc->p); mp_free(sc->z); mp_free(sc->k); mp_free(sc->km1o2); if (sc->zk) mp_free(sc->zk); sfree(sc); } mp_int *mp_modsqrt(ModsqrtContext *sc, mp_int *x, unsigned *success) { mp_int *mx = monty_import(sc->mc, x); mp_int *mroot = monty_modsqrt(sc, mx, success); mp_free(mx); mp_int *root = monty_export(sc->mc, mroot); mp_free(mroot); return root; } /* * Modular square root, using an algorithm more or less similar to * Tonelli-Shanks but adapted for constant time. * * The basic idea is to write p-1 = k 2^e, where k is odd and e > 0. * Then the multiplicative group mod p (call it G) has a sequence of * e+1 nested subgroups G = G_0 > G_1 > G_2 > ... > G_e, where each * G_i is exactly half the size of G_{i-1} and consists of all the * squares of elements in G_{i-1}. So the innermost group G_e has * order k, which is odd, and hence within that group you can take a * square root by raising to the power (k+1)/2. * * Our strategy is to iterate over these groups one by one and make * sure the number x we're trying to take the square root of is inside * each one, by adjusting it if it isn't. * * Suppose g is a primitive root of p, i.e. a generator of G_0. (We * don't actually need to know what g _is_; we just imagine it for the * sake of understanding.) Then G_i consists of precisely the (2^i)th * powers of g, and hence, you can tell if a number is in G_i if * raising it to the power k 2^{e-i} gives 1. So the conceptual * algorithm goes: for each i, test whether x is in G_i by that * method. If it isn't, then the previous iteration ensured it's in * G_{i-1}, so it will be an odd power of g^{2^{i-1}}, and hence * multiplying by any other odd power of g^{2^{i-1}} will give x' in * G_i. And we have one of those, because our non-square z is an odd * power of g, so z^{2^{i-1}} is an odd power of g^{2^{i-1}}. * * (There's a special case in the very first iteration, where we don't * have a G_{i-1}. If it turns out that x is not even in G_1, that * means it's not a square, so we set *success to 0. We still run the * rest of the algorithm anyway, for the sake of constant time, but we * don't give a hoot what it returns.) * * When we get to the end and have x in G_e, then we can take its * square root by raising to (k+1)/2. But of course that's not the * square root of the original input - it's only the square root of * the adjusted version we produced during the algorithm. To get the * true output answer we also have to multiply by a power of z, * namely, z to the power of _half_ whatever we've been multiplying in * as we go along. (The power of z we multiplied in must have been * even, because the case in which we would have multiplied in an odd * power of z is the i=0 case, in which we instead set the failure * flag.) * * The code below is an optimised version of that basic idea, in which * we _start_ by computing x^k so as to be able to test membership in * G_i by only a few squarings rather than a full from-scratch modpow * every time; we also start by computing our candidate output value * x^{(k+1)/2}. So when the above description says 'adjust x by z^i' * for some i, we have to adjust our running values of x^k and * x^{(k+1)/2} by z^{ik} and z^{ik/2} respectively (the latter is safe * because, as above, i is always even). And it turns out that we * don't actually have to store the adjusted version of x itself at * all - we _only_ keep those two powers of it. */ mp_int *monty_modsqrt(ModsqrtContext *sc, mp_int *x, unsigned *success) { modsqrt_lazy_setup(sc); mp_int *scratch_to_free = mp_make_sized(3 * sc->mc->rw); mp_int scratch = *scratch_to_free; /* * Compute toret = x^{(k+1)/2}, our starting point for the output * square root, and also xk = x^k which we'll use as we go along * for knowing when to apply correction factors. We do this by * first computing x^{(k-1)/2}, then multiplying it by x, then * multiplying the two together. */ mp_int *toret = monty_pow(sc->mc, x, sc->km1o2); mp_int xk = mp_alloc_from_scratch(&scratch, sc->mc->rw); mp_copy_into(&xk, toret); monty_mul_into(sc->mc, toret, toret, x); monty_mul_into(sc->mc, &xk, toret, &xk); mp_int tmp = mp_alloc_from_scratch(&scratch, sc->mc->rw); mp_int power_of_zk = mp_alloc_from_scratch(&scratch, sc->mc->rw); mp_copy_into(&power_of_zk, sc->zk); for (size_t i = 0; i < sc->e; i++) { mp_copy_into(&tmp, &xk); for (size_t j = i+1; j < sc->e; j++) monty_mul_into(sc->mc, &tmp, &tmp, &tmp); unsigned eq1 = mp_cmp_eq(&tmp, monty_identity(sc->mc)); if (i == 0) { /* One special case: if x=0, then no power of x will ever * equal 1, but we should still report success on the * grounds that 0 does have a square root mod p. */ *success = eq1 | mp_eq_integer(x, 0); } else { monty_mul_into(sc->mc, &tmp, toret, &power_of_zk); mp_select_into(toret, &tmp, toret, eq1); monty_mul_into(sc->mc, &power_of_zk, &power_of_zk, &power_of_zk); monty_mul_into(sc->mc, &tmp, &xk, &power_of_zk); mp_select_into(&xk, &tmp, &xk, eq1); } } mp_free(scratch_to_free); return toret; } mp_int *mp_random_bits_fn(size_t bits, random_read_fn_t random_read) { size_t bytes = (bits + 7) / 8; uint8_t *randbuf = snewn(bytes, uint8_t); random_read(randbuf, bytes); if (bytes) randbuf[0] &= (2 << ((bits-1) & 7)) - 1; mp_int *toret = mp_from_bytes_be(make_ptrlen(randbuf, bytes)); smemclr(randbuf, bytes); sfree(randbuf); return toret; } mp_int *mp_random_upto_fn(mp_int *limit, random_read_fn_t rf) { /* * It would be nice to generate our random numbers in such a way * as to make every possible outcome literally equiprobable. But * we can't do that in constant time, so we have to go for a very * close approximation instead. I'm going to take the view that a * factor of (1+2^-128) between the probabilities of two outcomes * is acceptable on the grounds that you'd have to examine so many * outputs to even detect it. */ mp_int *unreduced = mp_random_bits_fn(mp_max_bits(limit) + 128, rf); mp_int *reduced = mp_mod(unreduced, limit); mp_free(unreduced); return reduced; } mp_int *mp_random_in_range_fn(mp_int *lo, mp_int *hi, random_read_fn_t rf) { mp_int *n_outcomes = mp_sub(hi, lo); mp_int *addend = mp_random_upto_fn(n_outcomes, rf); mp_int *result = mp_make_sized(hi->nw); mp_add_into(result, addend, lo); mp_free(addend); mp_free(n_outcomes); return result; } putty-0.76/mpint.h0000644000175000017500000004301514072266311011061 00000000000000#ifndef PUTTY_MPINT_H #define PUTTY_MPINT_H /* * PuTTY's multiprecision integer library. * * This library is written with the aim of avoiding leaking the input * numbers via timing and cache side channels. This means avoiding * making any control flow change, or deciding the address of any * memory access, based on the value of potentially secret input data. * * But in a library that has to handle numbers of arbitrary size, you * can't avoid your control flow depending on the _size_ of the input! * So the rule is that an mp_int has a nominal size that need not be * its mathematical size: i.e. if you call (say) mp_from_bytes_be to * turn an array of 256 bytes into an integer, and all but the last of * those bytes is zero, then you get an mp_int which has space for 256 * bytes of data but just happens to store the value 1. So the * _nominal_ sizes of input data - e.g. the size in bits of some * public-key modulus - are not considered secret, and control flow is * allowed to do what it likes based on those sizes. But the same * function, called with the same _nominally sized_ arguments * containing different values, should run in the same length of time. * * When a function returns an 'mp_int *', it is newly allocated to an * appropriate nominal size (which, again, depends only on the nominal * sizes of the inputs). Other functions have 'into' in their name, * and they instead overwrite the contents of an existing mp_int. * * Functions in this API which return values that are logically * boolean return them as 'unsigned' rather than the C99 bool type. * That's because C99 bool does an implicit test for non-zero-ness * when converting any other integer type to it, which compilers might * well implement using data-dependent control flow. */ /* * Create and destroy mp_ints. A newly created one is initialised to * zero. mp_clear also resets an existing number to zero. */ mp_int *mp_new(size_t maxbits); void mp_free(mp_int *); void mp_clear(mp_int *x); /* * Create mp_ints from various sources: little- and big-endian binary * data, an ordinary C unsigned integer type, a decimal or hex string * (given either as a ptrlen or a C NUL-terminated string), and * another mp_int. * * The decimal and hex conversion functions have running time * dependent on the length of the input data, of course. */ mp_int *mp_from_bytes_le(ptrlen bytes); mp_int *mp_from_bytes_be(ptrlen bytes); mp_int *mp_from_integer(uintmax_t n); mp_int *mp_from_decimal_pl(ptrlen decimal); mp_int *mp_from_decimal(const char *decimal); mp_int *mp_from_hex_pl(ptrlen hex); mp_int *mp_from_hex(const char *hex); mp_int *mp_copy(mp_int *x); /* * A macro for declaring large fixed numbers in source code (such as * elliptic curve parameters, or standard Diffie-Hellman moduli). The * idea is that you just write something like * * mp_int *value = MP_LITERAL(0x19284376283754638745693467245); * * and it newly allocates you an mp_int containing that number. * * Internally, the macro argument is stringified and passed to * mp_from_hex. That's not as fast as it could be if I had instead set * up some kind of mp_from_array_of_uint64_t() function, but I think * this system is valuable for the fact that the literal integers * appear in a very natural syntax that can be pasted directly out * into, say, Python if you want to cross-check a calculation. */ static inline mp_int *mp__from_string_literal(const char *lit) { /* Don't call this directly; it's not equipped to deal with * hostile data. Use only via the MP_LITERAL macro. */ if (lit[0] && (lit[1] == 'x' || lit[1] == 'X')) return mp_from_hex(lit+2); else return mp_from_decimal(lit); } #define MP_LITERAL(number) mp__from_string_literal(#number) /* * Create an mp_int with the value 2^power. */ mp_int *mp_power_2(size_t power); /* * Retrieve the value of a particular bit or byte of an mp_int. The * byte / bit index is not considered to be secret data. Out-of-range * byte/bit indices are handled cleanly and return zero. */ uint8_t mp_get_byte(mp_int *x, size_t byte); unsigned mp_get_bit(mp_int *x, size_t bit); /* * Retrieve the value of an mp_int as a uintmax_t, assuming it's small * enough to fit. */ uintmax_t mp_get_integer(mp_int *x); /* * Set an mp_int bit. Again, the bit index is not considered secret. * Do not pass an out-of-range index, on pain of assertion failure. */ void mp_set_bit(mp_int *x, size_t bit, unsigned val); /* * Return the nominal size of an mp_int, in terms of the maximum * number of bytes or bits that can fit in it. */ size_t mp_max_bytes(mp_int *x); size_t mp_max_bits(mp_int *x); /* * Return the _mathematical_ bit count of an mp_int (not its nominal * size), i.e. a value n such that 2^{n-1} <= x < 2^n. * * This function is supposed to run in constant time for a given * nominal input size. Of course it's likely that clients of this * function will promptly need to use the result as the limit of some * loop (e.g. marshalling an mp_int into an SSH packet, which doesn't * permit extra prefix zero bytes). But that's up to the caller to * decide the safety of. */ size_t mp_get_nbits(mp_int *x); /* * Return the value of an mp_int as a decimal or hex string. The * result is dynamically allocated, and the caller is responsible for * freeing it. * * These functions should run in constant time for a given nominal * input size, even though the exact number of digits returned is * variable. They always allocate enough space for the largest output * that might be needed, but they don't always fill it. */ char *mp_get_decimal(mp_int *x); char *mp_get_hex(mp_int *x); char *mp_get_hex_uppercase(mp_int *x); /* * Compare two mp_ints, or compare one mp_int against a C integer. The * 'eq' functions return 1 if the two inputs are equal, or 0 * otherwise; the 'hs' functions return 1 if the first input is >= the * second, and 0 otherwise. */ unsigned mp_cmp_hs(mp_int *a, mp_int *b); unsigned mp_cmp_eq(mp_int *a, mp_int *b); unsigned mp_hs_integer(mp_int *x, uintmax_t n); unsigned mp_eq_integer(mp_int *x, uintmax_t n); /* * Take the minimum or maximum of two mp_ints, without using a * conditional branch. */ void mp_min_into(mp_int *r, mp_int *x, mp_int *y); void mp_max_into(mp_int *r, mp_int *x, mp_int *y); mp_int *mp_min(mp_int *x, mp_int *y); mp_int *mp_max(mp_int *x, mp_int *y); /* * Diagnostic function. Writes out x in hex to the supplied stdio * stream, preceded by the string 'prefix' and followed by 'suffix'. * * This is useful to put temporarily into code, but it's also * potentially useful to call from a debugger. */ void mp_dump(FILE *fp, const char *prefix, mp_int *x, const char *suffix); /* * Overwrite one mp_int with another, or with a plain integer. */ void mp_copy_into(mp_int *dest, mp_int *src); void mp_copy_integer_into(mp_int *dest, uintmax_t n); /* * Conditional selection. Overwrites dest with either src0 or src1, * according to the value of 'choose_src1'. choose_src1 should be 0 or * 1; if it's 1, then dest is set to src1, otherwise src0. * * The value of choose_src1 is considered to be secret data, so * control flow and memory access should not depend on it. */ void mp_select_into(mp_int *dest, mp_int *src0, mp_int *src1, unsigned choose_src1); /* * Addition, subtraction and multiplication, either targeting an * existing mp_int or making a new one large enough to hold whatever * the output might be.. */ void mp_add_into(mp_int *r, mp_int *a, mp_int *b); void mp_sub_into(mp_int *r, mp_int *a, mp_int *b); void mp_mul_into(mp_int *r, mp_int *a, mp_int *b); mp_int *mp_add(mp_int *x, mp_int *y); mp_int *mp_sub(mp_int *x, mp_int *y); mp_int *mp_mul(mp_int *x, mp_int *y); /* * Bitwise operations. */ void mp_and_into(mp_int *r, mp_int *a, mp_int *b); void mp_or_into(mp_int *r, mp_int *a, mp_int *b); void mp_xor_into(mp_int *r, mp_int *a, mp_int *b); void mp_bic_into(mp_int *r, mp_int *a, mp_int *b); /* * Addition, subtraction and multiplication with one argument small * enough to fit in a C integer. For mp_mul_integer_into, it has to be * even smaller than that. */ void mp_add_integer_into(mp_int *r, mp_int *a, uintmax_t n); void mp_sub_integer_into(mp_int *r, mp_int *a, uintmax_t n); void mp_mul_integer_into(mp_int *r, mp_int *a, uint16_t n); /* * Conditional addition/subtraction. If yes == 1, sets r to a+b or a-b * (respectively). If yes == 0, sets r to just a. 'yes' is considered * secret data. */ void mp_cond_add_into(mp_int *r, mp_int *a, mp_int *b, unsigned yes); void mp_cond_sub_into(mp_int *r, mp_int *a, mp_int *b, unsigned yes); /* * Swap x0 and x1 if swap == 1, and not if swap == 0. 'swap' is * considered secret. */ void mp_cond_swap(mp_int *x0, mp_int *x1, unsigned swap); /* * Set x to 0 if clear == 1, and otherwise leave it unchanged. 'clear' * is considered secret. */ void mp_cond_clear(mp_int *x, unsigned clear); /* * Division. mp_divmod_into divides n by d, and writes the quotient * into q and the remainder into r. You can pass either of q and r as * NULL if you don't need one of the outputs. * * mp_div and mp_mod are wrappers that return one or other of those * outputs as a freshly allocated mp_int of the appropriate size. * * Division by zero gives no error, and returns a quotient of 0 and a * remainder of n (so as to still satisfy the division identity that * n=qd+r). */ void mp_divmod_into(mp_int *n, mp_int *d, mp_int *q, mp_int *r); mp_int *mp_div(mp_int *n, mp_int *d); mp_int *mp_mod(mp_int *x, mp_int *modulus); /* * Integer nth root. mp_nthroot returns the largest integer x such * that x^n <= y, and if 'remainder' is non-NULL then it fills it with * the residue (y - x^n). * * Currently, n has to be small enough that the largest binomial * coefficient (n choose k) fits in 16 bits, which works out to at * most 18. */ mp_int *mp_nthroot(mp_int *y, unsigned n, mp_int *remainder); /* * Trivially easy special case of mp_mod: reduce a number mod a power * of two. */ void mp_reduce_mod_2to(mp_int *x, size_t p); /* * Modular inverses. mp_invert computes the inverse of x mod modulus * (and will expect the two to be coprime). mp_invert_mod_2to computes * the inverse of x mod 2^p, and is a great deal faster. */ mp_int *mp_invert_mod_2to(mp_int *x, size_t p); mp_int *mp_invert(mp_int *x, mp_int *modulus); /* * Greatest common divisor. * * mp_gcd_into also returns a pair of Bezout coefficients, namely A,B * such that a*A - b*B = gcd. (The minus sign is so that both returned * coefficients can be positive.) * * You can pass any of mp_gcd_into's output pointers as NULL if you * don't need that output value. * * mp_gcd is a wrapper with a less cumbersome API, for the case where * the only output value you need is the gcd itself. mp_coprime is * even easier, if all you care about is whether or not that gcd is 1. */ mp_int *mp_gcd(mp_int *a, mp_int *b); void mp_gcd_into(mp_int *a, mp_int *b, mp_int *gcd_out, mp_int *A_out, mp_int *B_out); unsigned mp_coprime(mp_int *a, mp_int *b); /* * System for taking square roots modulo an odd prime. * * In order to do this efficiently, you need to provide an extra piece * of information at setup time, namely a number which is not * congruent mod p to any square. Given p and that non-square, you can * use modsqrt_new to make a context containing all the necessary * equipment for actually calculating the square roots, and then you * can call mp_modsqrt as many times as you like on that context * before freeing it. * * The output parameter '*success' will be filled in with 1 if the * operation was successful, or 0 if the input number doesn't have a * square root mod p at all. In the latter case, the returned mp_int * will be nonsense and you shouldn't depend on it. * * ==== WARNING ==== * * This function DOES NOT TREAT THE PRIME MODULUS AS SECRET DATA! It * will protect the number you're taking the square root _of_, but not * the number you're taking the root of it _mod_. * * (This is because the algorithm requires a number of loop iterations * equal to the number of factors of 2 in p-1. And the expected use of * this function is for elliptic-curve point decompression, in which * the modulus is always a well-known one written down in standards * documents.) */ typedef struct ModsqrtContext ModsqrtContext; ModsqrtContext *modsqrt_new(mp_int *p, mp_int *any_nonsquare_mod_p); void modsqrt_free(ModsqrtContext *); mp_int *mp_modsqrt(ModsqrtContext *sc, mp_int *x, unsigned *success); /* * Functions for Montgomery multiplication, a fast technique for doing * a long series of modular multiplications all with the same modulus * (which has to be odd). * * You start by calling monty_new to set up a context structure * containing all the precomputed bits and pieces needed by the * algorithm. Then, any numbers you want to work with must first be * transformed into the internal Montgomery representation using * monty_import; having done that, you can use monty_mul and monty_pow * to operate on them efficiently; and finally, monty_export will * convert numbers back out of Montgomery representation to give their * ordinary values. * * Addition and subtraction are not optimised by the Montgomery trick, * but monty_add and monty_sub are provided anyway for convenience. * * There are also monty_invert and monty_modsqrt, which are analogues * of mp_invert and mp_modsqrt which take their inputs in Montgomery * representation. For mp_modsqrt, the prime modulus of the * ModsqrtContext must be the same as the modulus of the MontyContext. * * The query functions monty_modulus and monty_identity return numbers * stored inside the MontyContext, without copying them. The returned * pointers are still owned by the MontyContext, so don't free them! */ MontyContext *monty_new(mp_int *modulus); void monty_free(MontyContext *mc); mp_int *monty_modulus(MontyContext *mc); /* doesn't transfer ownership */ mp_int *monty_identity(MontyContext *mc); /* doesn't transfer ownership */ void monty_import_into(MontyContext *mc, mp_int *r, mp_int *x); mp_int *monty_import(MontyContext *mc, mp_int *x); void monty_export_into(MontyContext *mc, mp_int *r, mp_int *x); mp_int *monty_export(MontyContext *mc, mp_int *x); void monty_mul_into(MontyContext *, mp_int *r, mp_int *, mp_int *); mp_int *monty_add(MontyContext *, mp_int *, mp_int *); mp_int *monty_sub(MontyContext *, mp_int *, mp_int *); mp_int *monty_mul(MontyContext *, mp_int *, mp_int *); mp_int *monty_pow(MontyContext *, mp_int *base, mp_int *exponent); mp_int *monty_invert(MontyContext *, mp_int *); mp_int *monty_modsqrt(ModsqrtContext *sc, mp_int *mx, unsigned *success); /* * Modular arithmetic functions which don't use an explicit * MontyContext. mp_modpow will use one internally (on the assumption * that the exponent is likely to be large enough to make it * worthwhile); the other three will just do ordinary non-Montgomery- * optimised modular reduction. Use mp_modmul if you only have one * product to compute; if you have a lot, consider using a * MontyContext in the client code. */ mp_int *mp_modpow(mp_int *base, mp_int *exponent, mp_int *modulus); mp_int *mp_modmul(mp_int *x, mp_int *y, mp_int *modulus); mp_int *mp_modadd(mp_int *x, mp_int *y, mp_int *modulus); mp_int *mp_modsub(mp_int *x, mp_int *y, mp_int *modulus); /* * Shift an mp_int by a given number of bits. The shift count is * considered to be secret data, and as a result, the algorithm takes * O(n log n) time instead of the obvious O(n). * * There's no mp_lshift_safe, because the size of mp_int to allocate * would not be able to avoid depending on the shift count. So if you * need to behave independently of the size of a left shift, you have * to know a bound on the space you'll need by some other means. */ void mp_lshift_safe_into(mp_int *r, mp_int *x, size_t shift); void mp_rshift_safe_into(mp_int *r, mp_int *x, size_t shift); mp_int *mp_rshift_safe(mp_int *x, size_t shift); /* * Shift an mp_int left or right by a fixed number of bits. The shift * count is NOT considered to be secret data! Use this if you're * always dividing by 2, for example, but don't use it to shift by a * variable amount derived from another secret number. * * The upside is that these functions run in sensible linear time. */ void mp_lshift_fixed_into(mp_int *r, mp_int *a, size_t shift); void mp_rshift_fixed_into(mp_int *r, mp_int *x, size_t shift); mp_int *mp_lshift_fixed(mp_int *x, size_t shift); mp_int *mp_rshift_fixed(mp_int *x, size_t shift); /* * Generate a random mp_int. * * The _function_ definitions here will expect to be given a gen_data * function that provides random data. Normally you'd use this using * random_read() from random.c, and the macro wrappers automate that. * * (This is a bit of a dodge to avoid mpint.c having a link-time * dependency on random.c, so that programs can link against one but * not the other: if a client of this header uses one of these macros * then _they_ have link-time dependencies on both modules.) * * mp_random_bits[_fn] returns an integer 0 <= n < 2^bits. * mp_random_upto[_fn](limit) returns an integer 0 <= n < limit. * mp_random_in_range[_fn](lo,hi) returns an integer lo <= n < hi. */ typedef void (*random_read_fn_t)(void *, size_t); mp_int *mp_random_bits_fn(size_t bits, random_read_fn_t randfn); mp_int *mp_random_upto_fn(mp_int *limit, random_read_fn_t randfn); mp_int *mp_random_in_range_fn( mp_int *lo_inclusive, mp_int *hi_exclusive, random_read_fn_t randfn); #define mp_random_bits(bits) mp_random_bits_fn(bits, random_read) #define mp_random_upto(limit) mp_random_upto_fn(limit, random_read) #define mp_random_in_range(lo, hi) mp_random_in_range_fn(lo, hi, random_read) #endif /* PUTTY_MPINT_H */ putty-0.76/mpint_i.h0000644000175000017500000003172214072266311011373 00000000000000/* * mpint_i.h: definitions used internally by the bignum code, and * also a few other vaguely-bignum-like places. */ /* ---------------------------------------------------------------------- * The assorted conditional definitions of BignumInt and multiply * macros used throughout the bignum code to treat numbers as arrays * of the most conveniently sized word for the target machine. * Exported so that other code (e.g. poly1305) can use it too. * * This code must export, in whatever ifdef branch it ends up in: * * - two types: 'BignumInt' and 'BignumCarry'. BignumInt is an * unsigned integer type which will be used as the base word size * for all bignum operations. BignumCarry is an unsigned integer * type used to hold the carry flag taken as input and output by * the BignumADC macro (see below). * * - five constant macros: * + BIGNUM_INT_BITS, the number of bits in BignumInt, * + BIGNUM_INT_BYTES, the number of bytes that works out to * + BIGNUM_TOP_BIT, the BignumInt value consisting of only the top bit * + BIGNUM_INT_MASK, the BignumInt value with all bits set * + BIGNUM_INT_BITS_BITS, log to the base 2 of BIGNUM_INT_BITS. * * - four statement macros: BignumADC, BignumMUL, BignumMULADD, * BignumMULADD2. These do various kinds of multi-word arithmetic, * and all produce two output values. * * BignumADC(ret,retc,a,b,c) takes input BignumInt values a,b * and a BignumCarry c, and outputs a BignumInt ret = a+b+c and * a BignumCarry retc which is the carry off the top of that * addition. * * BignumMUL(rh,rl,a,b) returns the two halves of the * double-width product a*b. * * BignumMULADD(rh,rl,a,b,addend) returns the two halves of the * double-width value a*b + addend. * * BignumMULADD2(rh,rl,a,b,addend1,addend2) returns the two * halves of the double-width value a*b + addend1 + addend2. * * Every branch of the main ifdef below defines the type BignumInt and * the value BIGNUM_INT_BITS_BITS. The other constant macros are * filled in by common code further down. * * Most branches also define a macro DEFINE_BIGNUMDBLINT containing a * typedef statement which declares a type _twice_ the length of a * BignumInt. This causes the common code further down to produce a * default implementation of the four statement macros in terms of * that double-width type, and also to defined BignumCarry to be * BignumInt. * * However, if a particular compile target does not have a type twice * the length of the BignumInt you want to use but it does provide * some alternative means of doing add-with-carry and double-word * multiply, then the ifdef branch in question can just define * BignumCarry and the four statement macros itself, and that's fine * too. */ /* You can lower the BignumInt size by defining BIGNUM_OVERRIDE on the * command line to be your chosen max value of BIGNUM_INT_BITS_BITS */ #if defined BIGNUM_OVERRIDE #define BB_OK(b) ((b) <= BIGNUM_OVERRIDE) #else #define BB_OK(b) (1) #endif #if defined __SIZEOF_INT128__ && BB_OK(6) /* * 64-bit BignumInt using gcc/clang style 128-bit BignumDblInt. * * gcc and clang both provide a __uint128_t type on 64-bit targets * (and, when they do, indicate its presence by the above macro), * using the same 'two machine registers' kind of code generation * that 32-bit targets use for 64-bit ints. */ typedef unsigned long long BignumInt; #define BIGNUM_INT_BITS_BITS 6 #define DEFINE_BIGNUMDBLINT typedef __uint128_t BignumDblInt #elif defined _MSC_VER && defined _M_AMD64 && BB_OK(6) /* * 64-bit BignumInt, using Visual Studio x86-64 compiler intrinsics. * * 64-bit Visual Studio doesn't provide very much in the way of help * here: there's no int128 type, and also no inline assembler giving * us direct access to the x86-64 MUL or ADC instructions. However, * there are compiler intrinsics giving us that access, so we can * use those - though it turns out we have to be a little careful, * since they seem to generate wrong code if their pointer-typed * output parameters alias their inputs. Hence all the internal temp * variables inside the macros. */ #include typedef unsigned char BignumCarry; /* the type _addcarry_u64 likes to use */ typedef unsigned __int64 BignumInt; #define BIGNUM_INT_BITS_BITS 6 #define BignumADC(ret, retc, a, b, c) do \ { \ BignumInt ADC_tmp; \ (retc) = _addcarry_u64(c, a, b, &ADC_tmp); \ (ret) = ADC_tmp; \ } while (0) #define BignumMUL(rh, rl, a, b) do \ { \ BignumInt MULADD_hi; \ (rl) = _umul128(a, b, &MULADD_hi); \ (rh) = MULADD_hi; \ } while (0) #define BignumMULADD(rh, rl, a, b, addend) do \ { \ BignumInt MULADD_lo, MULADD_hi; \ MULADD_lo = _umul128(a, b, &MULADD_hi); \ MULADD_hi += _addcarry_u64(0, MULADD_lo, (addend), &(rl)); \ (rh) = MULADD_hi; \ } while (0) #define BignumMULADD2(rh, rl, a, b, addend1, addend2) do \ { \ BignumInt MULADD_lo1, MULADD_lo2, MULADD_hi; \ MULADD_lo1 = _umul128(a, b, &MULADD_hi); \ MULADD_hi += _addcarry_u64(0, MULADD_lo1, (addend1), &MULADD_lo2); \ MULADD_hi += _addcarry_u64(0, MULADD_lo2, (addend2), &(rl)); \ (rh) = MULADD_hi; \ } while (0) #elif (defined __GNUC__ || defined _LLP64 || __STDC__ >= 199901L) && BB_OK(5) /* 32-bit BignumInt, using C99 unsigned long long as BignumDblInt */ typedef unsigned int BignumInt; #define BIGNUM_INT_BITS_BITS 5 #define DEFINE_BIGNUMDBLINT typedef unsigned long long BignumDblInt #elif defined _MSC_VER && BB_OK(5) /* 32-bit BignumInt, using Visual Studio __int64 as BignumDblInt */ typedef unsigned int BignumInt; #define BIGNUM_INT_BITS_BITS 5 #define DEFINE_BIGNUMDBLINT typedef unsigned __int64 BignumDblInt #elif defined _LP64 && BB_OK(5) /* * 32-bit BignumInt, using unsigned long itself as BignumDblInt. * * Only for platforms where long is 64 bits, of course. */ typedef unsigned int BignumInt; #define BIGNUM_INT_BITS_BITS 5 #define DEFINE_BIGNUMDBLINT typedef unsigned long BignumDblInt #elif BB_OK(4) /* * 16-bit BignumInt, using unsigned long as BignumDblInt. * * This is the final fallback for real emergencies: C89 guarantees * unsigned short/long to be at least the required sizes, so this * should work on any C implementation at all. But it'll be * noticeably slow, so if you find yourself in this case you * probably want to move heaven and earth to find an alternative! */ typedef unsigned short BignumInt; #define BIGNUM_INT_BITS_BITS 4 #define DEFINE_BIGNUMDBLINT typedef unsigned long BignumDblInt #else /* Should only get here if BB_OK(4) evaluated false, i.e. the * command line defined BIGNUM_OVERRIDE to an absurdly small * value. */ #error Must define BIGNUM_OVERRIDE to at least 4 #endif #undef BB_OK /* * Common code across all branches of that ifdef: define all the * easy constant macros in terms of BIGNUM_INT_BITS_BITS. */ #define BIGNUM_INT_BITS (1 << BIGNUM_INT_BITS_BITS) #define BIGNUM_INT_BYTES (BIGNUM_INT_BITS / 8) #define BIGNUM_TOP_BIT (((BignumInt)1) << (BIGNUM_INT_BITS-1)) #define BIGNUM_INT_MASK (BIGNUM_TOP_BIT | (BIGNUM_TOP_BIT-1)) /* * Just occasionally, we might need a GET_nnBIT_xSB_FIRST macro to * operate on whatever BignumInt is. */ #if BIGNUM_INT_BITS_BITS == 4 #define GET_BIGNUMINT_MSB_FIRST GET_16BIT_MSB_FIRST #define GET_BIGNUMINT_LSB_FIRST GET_16BIT_LSB_FIRST #define PUT_BIGNUMINT_MSB_FIRST PUT_16BIT_MSB_FIRST #define PUT_BIGNUMINT_LSB_FIRST PUT_16BIT_LSB_FIRST #elif BIGNUM_INT_BITS_BITS == 5 #define GET_BIGNUMINT_MSB_FIRST GET_32BIT_MSB_FIRST #define GET_BIGNUMINT_LSB_FIRST GET_32BIT_LSB_FIRST #define PUT_BIGNUMINT_MSB_FIRST PUT_32BIT_MSB_FIRST #define PUT_BIGNUMINT_LSB_FIRST PUT_32BIT_LSB_FIRST #elif BIGNUM_INT_BITS_BITS == 6 #define GET_BIGNUMINT_MSB_FIRST GET_64BIT_MSB_FIRST #define GET_BIGNUMINT_LSB_FIRST GET_64BIT_LSB_FIRST #define PUT_BIGNUMINT_MSB_FIRST PUT_64BIT_MSB_FIRST #define PUT_BIGNUMINT_LSB_FIRST PUT_64BIT_LSB_FIRST #else #error Ran out of options for GET_BIGNUMINT_xSB_FIRST #endif /* * Common code across _most_ branches of the ifdef: define a set of * statement macros in terms of the BignumDblInt type provided. In * this case, we also define BignumCarry to be the same thing as * BignumInt, for simplicity. */ #ifdef DEFINE_BIGNUMDBLINT typedef BignumInt BignumCarry; #define BignumADC(ret, retc, a, b, c) do \ { \ DEFINE_BIGNUMDBLINT; \ BignumDblInt ADC_temp = (BignumInt)(a); \ ADC_temp += (BignumInt)(b); \ ADC_temp += (c); \ (ret) = (BignumInt)ADC_temp; \ (retc) = (BignumCarry)(ADC_temp >> BIGNUM_INT_BITS); \ } while (0) #define BignumMUL(rh, rl, a, b) do \ { \ DEFINE_BIGNUMDBLINT; \ BignumDblInt MUL_temp = (BignumInt)(a); \ MUL_temp *= (BignumInt)(b); \ (rh) = (BignumInt)(MUL_temp >> BIGNUM_INT_BITS); \ (rl) = (BignumInt)(MUL_temp); \ } while (0) #define BignumMULADD(rh, rl, a, b, addend) do \ { \ DEFINE_BIGNUMDBLINT; \ BignumDblInt MUL_temp = (BignumInt)(a); \ MUL_temp *= (BignumInt)(b); \ MUL_temp += (BignumInt)(addend); \ (rh) = (BignumInt)(MUL_temp >> BIGNUM_INT_BITS); \ (rl) = (BignumInt)(MUL_temp); \ } while (0) #define BignumMULADD2(rh, rl, a, b, addend1, addend2) do \ { \ DEFINE_BIGNUMDBLINT; \ BignumDblInt MUL_temp = (BignumInt)(a); \ MUL_temp *= (BignumInt)(b); \ MUL_temp += (BignumInt)(addend1); \ MUL_temp += (BignumInt)(addend2); \ (rh) = (BignumInt)(MUL_temp >> BIGNUM_INT_BITS); \ (rl) = (BignumInt)(MUL_temp); \ } while (0) #endif /* DEFINE_BIGNUMDBLINT */ /* ---------------------------------------------------------------------- * Data structures used inside bignum.c. */ struct mp_int { size_t nw; BignumInt *w; }; struct MontyContext { /* * The actual modulus. */ mp_int *m; /* * Montgomery multiplication works by selecting a value r > m, * coprime to m, which is really easy to divide by. In binary * arithmetic, that means making it a power of 2; in fact we make * it a whole number of BignumInt. * * We don't store r directly as an mp_int (there's no need). But * its value is 2^rbits; we also store rw = rbits/BIGNUM_INT_BITS * (the corresponding word offset within an mp_int). * * pw is the number of words needed to store an mp_int you're * doing reduction on: it has to be big enough to hold the sum of * an input value up to m^2 plus an extra addend up to m*r. */ size_t rbits, rw, pw; /* * The key step in Montgomery reduction requires the inverse of -m * mod r. */ mp_int *minus_minv_mod_r; /* * r^1, r^2 and r^3 mod m, which are used for various purposes. * * (Annoyingly, this is one of the rare cases where it would have * been nicer to have a Pascal-style 1-indexed array. I couldn't * _quite_ bring myself to put a gratuitous zero element in here. * So you just have to live with getting r^k by taking the [k-1]th * element of this array.) */ mp_int *powers_of_r_mod_m[3]; /* * Persistent scratch space from which monty_* functions can * allocate storage for intermediate values. */ mp_int *scratch; }; /* Functions shared between mpint.c and mpunsafe.c */ mp_int *mp_make_sized(size_t nw); putty-0.76/mpunsafe.c0000644000175000017500000000271214072266311011542 00000000000000#include #include #include #include "defs.h" #include "misc.h" #include "puttymem.h" #include "mpint.h" #include "mpint_i.h" /* * This global symbol is also defined in ssh2kex-client.c, to ensure * that these unsafe non-constant-time mp_int functions can't end up * accidentally linked in to any PuTTY tool that actually makes an SSH * client connection. * * (Only _client_ connections, however. Uppity, being a test server * only, is exempt.) */ const int deliberate_symbol_clash = 12345; static size_t mp_unsafe_words_needed(mp_int *x) { size_t words = x->nw; while (words > 1 && !x->w[words-1]) words--; return words; } mp_int *mp_unsafe_shrink(mp_int *x) { x->nw = mp_unsafe_words_needed(x); /* This potentially leaves some allocated words between the new * and old values of x->nw, which won't be wiped by mp_free now * that x->nw doesn't mention that they exist. But we've just * checked they're all zero, so we don't need to wipe them now * either. */ return x; } mp_int *mp_unsafe_copy(mp_int *x) { mp_int *copy = mp_make_sized(mp_unsafe_words_needed(x)); mp_copy_into(copy, x); return copy; } uint32_t mp_unsafe_mod_integer(mp_int *x, uint32_t modulus) { uint64_t accumulator = 0; for (size_t i = mp_max_bytes(x); i-- > 0 ;) { accumulator = 0x100 * accumulator + mp_get_byte(x, i); accumulator %= modulus; } return accumulator; } putty-0.76/mpunsafe.h0000644000175000017500000000337714072266311011557 00000000000000/* * mpunsafe.h: functions that deal with mp_ints in ways that are *not* * expected to be constant-time. Used during key generation, in which * constant run time is a lost cause anyway. * * These functions are in a separate header, so that you can easily * check that you're not calling them in the wrong context. They're * also defined in a separate source file, which is only linked in to * the key generation tools. Furthermore, that source file also * defines a global symbol that intentionally conflicts with one * defined in the SSH client code, so that any attempt to put these * functions into the same binary as the live SSH client * implementation will cause a link-time failure. They should only be * linked into PuTTYgen and auxiliary test programs. * * Also, just in case those precautions aren't enough, all the unsafe * functions have 'unsafe' in the name. */ #ifndef PUTTY_MPINT_UNSAFE_H #define PUTTY_MPINT_UNSAFE_H /* * The most obvious unsafe thing you want to do with an mp_int is to * get rid of leading zero words in its representation, so that its * nominal size is as close as possible to its true size, and you * don't waste any time processing it. * * mp_unsafe_shrink performs this operation in place, mutating the * size field of the mp_int it's given. It returns the same pointer it * was given. * * mp_unsafe_copy leaves the original mp_int alone and makes a new one * with the minimal size. */ mp_int *mp_unsafe_shrink(mp_int *m); mp_int *mp_unsafe_copy(mp_int *m); /* * Compute the residue of x mod m. This is implemented in the most * obvious way using the C % operator, which won't be constant-time on * many C implementations. */ uint32_t mp_unsafe_mod_integer(mp_int *x, uint32_t m); #endif /* PUTTY_MPINT_UNSAFE_H */ putty-0.76/network.h0000644000175000017500000002717314072266311011432 00000000000000/* * Networking abstraction in PuTTY. * * The way this works is: a back end can choose to open any number * of sockets - including zero, which might be necessary in some. * It can register a bunch of callbacks (most notably for when * data is received) for each socket, and it can call the networking * abstraction to send data without having to worry about blocking. * The stuff behind the abstraction takes care of selects and * nonblocking writes and all that sort of painful gubbins. */ #ifndef PUTTY_NETWORK_H #define PUTTY_NETWORK_H #include "defs.h" typedef struct SocketVtable SocketVtable; typedef struct PlugVtable PlugVtable; struct Socket { const struct SocketVtable *vt; }; struct SocketVtable { 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); size_t (*write) (Socket *s, const void *data, size_t len); size_t (*write_oob) (Socket *s, const void *data, size_t len); void (*write_eof) (Socket *s); void (*set_frozen) (Socket *s, bool is_frozen); /* ignored by tcp, but vital for ssl */ const char *(*socket_error) (Socket *s); SocketPeerInfo *(*peer_info) (Socket *s); }; typedef union { void *p; int i; } accept_ctx_t; typedef Socket *(*accept_fn_t)(accept_ctx_t ctx, Plug *plug); struct Plug { const struct PlugVtable *vt; }; typedef enum PlugLogType { PLUGLOG_CONNECT_TRYING, PLUGLOG_CONNECT_FAILED, PLUGLOG_CONNECT_SUCCESS, PLUGLOG_PROXY_MSG, } PlugLogType; struct PlugVtable { void (*log)(Plug *p, PlugLogType 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. * * - PLUGLOG_CONNECT_TRYING means we are about to try to connect * to address `addr' (error_msg and error_code are ignored) * * - PLUGLOG_CONNECT_FAILED means we have failed to connect to * address `addr' (error_msg and error_code are supplied). This * is not a fatal error - we may well have other candidate * addresses to fall back to. When it _is_ fatal, the closing() * function will be called. * * - PLUGLOG_CONNECT_SUCCESS means we have succeeded in * connecting to address `addr'. * * - PLUGLOG_PROXY_MSG means that error_msg contains a line of * logging information from whatever the connection is being * proxied through. This will typically be a wodge of * standard-error output from a local proxy command, so the * receiver should probably prefix it to indicate this. */ void (*closing) (Plug *p, const char *error_msg, int error_code, bool calling_back); /* error_msg is NULL iff it is not an error (ie it closed normally) */ /* calling_back != 0 iff there is a Plug function */ /* currently running (would cure the fixme in try_send()) */ void (*receive) (Plug *p, int urgent, const char *data, size_t len); /* * - urgent==0. `data' points to `len' bytes of perfectly * ordinary data. * * - urgent==1. `data' points to `len' bytes of data, * which were read from before an Urgent pointer. * * - urgent==2. `data' points to `len' bytes of data, * the first of which was the one at the Urgent mark. */ void (*sent) (Plug *p, size_t 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, const char *hostname, int port, bool privport, bool oobinline, bool nodelay, bool keepalive, Plug *plug, Conf *conf); Socket *new_listener(const char *srcaddr, int port, Plug *plug, bool local_host_only, Conf *conf, int addressfamily); SockAddr *name_lookup(const char *host, int port, char **canonicalname, Conf *conf, int addressfamily, LogContext *logctx, const char *lookup_reason_for_logging); /* platform-dependent callback from new_connection() */ /* (same caveat about addr as new_connection()) */ Socket *platform_new_connection(SockAddr *addr, const char *hostname, int port, bool privport, bool oobinline, bool nodelay, bool 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); bool sk_addr_needs_port(SockAddr *addr); bool sk_hostname_is_local(const char *name); bool sk_address_is_local(SockAddr *addr); bool 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, bool privport, bool oobinline, bool nodelay, bool keepalive, Plug *p); Socket *sk_newlistener(const char *srcaddr, int port, Plug *plug, bool local_host_only, int address_family); static inline Plug *sk_plug(Socket *s, Plug *p) { return s->vt->plug(s, p); } static inline void sk_close(Socket *s) { s->vt->close(s); } static inline size_t sk_write(Socket *s, const void *data, size_t len) { return s->vt->write(s, data, len); } static inline size_t sk_write_oob(Socket *s, const void *data, size_t len) { return s->vt->write_oob(s, data, len); } static inline void sk_write_eof(Socket *s) { s->vt->write_eof(s); } static inline void plug_log( Plug *p, int type, SockAddr *addr, int port, const char *msg, int code) { p->vt->log(p, type, addr, port, msg, code); } static inline void plug_closing( Plug *p, const char *msg, int code, bool calling_back) { p->vt->closing(p, msg, code, calling_back); } static inline void plug_receive(Plug *p, int urg, const char *data, size_t len) { p->vt->receive(p, urg, data, len); } static inline void plug_sent (Plug *p, size_t bufsize) { p->vt->sent(p, bufsize); } static inline int plug_accepting(Plug *p, accept_fn_t cons, accept_ctx_t ctx) { return p->vt->accepting(p, cons, ctx); } /* * 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); static inline const char *sk_socket_error(Socket *s) { return s->vt->socket_error(s); } /* * Set the `frozen' flag on a socket. A frozen socket is one in * which all READABLE notifications are ignored, so that data is * not accepted from the peer until the socket is unfrozen. This * exists for two purposes: * * - Port forwarding: when a local listening port receives a * connection, we do not want to receive data from the new * socket until we have somewhere to send it. Hence, we freeze * the socket until its associated SSH channel is ready; then we * unfreeze it and pending data is delivered. * * - 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. */ static inline void sk_set_frozen(Socket *s, bool is_frozen) { s->vt->set_frozen(s, is_frozen); } /* * Return a structure giving some information about the other end of * the socket. May be NULL, if nothing is available at all. If it is * not NULL, then it is dynamically allocated, and should be freed by * a call to sk_free_peer_info(). See below for the definition. */ static inline SocketPeerInfo *sk_peer_info(Socket *s) { return s->vt->peer_info(s); } /* * The structure returned from sk_peer_info, and a function to free * one (in misc.c). */ struct SocketPeerInfo { int addressfamily; /* * Text form of the IPv4 or IPv6 address of the other end of the * socket, if available, in the standard text representation. */ const char *addr_text; /* * Binary form of the same address. Filled in if and only if * addr_text is not NULL. You can tell which branch of the union * is used by examining 'addressfamily'. */ union { unsigned char ipv6[16]; unsigned char ipv4[4]; } addr_bin; /* * Remote port number, or -1 if not available. */ int port; /* * Free-form text suitable for putting in log messages. For IP * sockets, repeats the address and port information from above. * But it can be completely different, e.g. for Unix-domain * sockets it gives information about the uid, gid and pid of the * connecting process. */ const char *log_text; }; void sk_free_peer_info(SocketPeerInfo *pi); /* * 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. * * The consume_string variant takes an already-formatted dynamically * allocated string, and takes over ownership of that string. */ Socket *new_error_socket_fmt(Plug *plug, const char *fmt, ...) PRINTF_LIKE(2, 3); Socket *new_error_socket_consume_string(Plug *plug, char *errmsg); /* * Trivial plug that does absolutely nothing. Found in nullplug.c. */ extern Plug *const nullplug; /* ---------------------------------------------------------------------- * Functions defined outside the network code, which have to be * declared in this header file rather than the main putty.h because * they use types defined here. */ /* * Exports from be_misc.c. */ void backend_socket_log(Seat *seat, LogContext *logctx, PlugLogType type, SockAddr *addr, int port, const char *error_msg, int error_code, Conf *conf, bool session_started); typedef struct ProxyStderrBuf { char buf[8192]; size_t size; } ProxyStderrBuf; void psb_init(ProxyStderrBuf *psb); void log_proxy_stderr( Plug *plug, ProxyStderrBuf *psb, const void *vdata, size_t len); #endif putty-0.76/nocmdline.c0000644000175000017500000000237714072266311011703 00000000000000/* * nocmdline.c - stubs in applications which don't do the * standard(ish) PuTTY tools' command-line parsing */ #include #include #include #include "putty.h" /* * Stub version of the function in cmdline.c which provides the * password to SSH authentication by remembering it having been passed * as a command-line option. If we're not doing normal command-line * handling, then there is no such option, so that function always * returns failure. */ int cmdline_get_passwd_input(prompts_t *p) { return -1; } /* * The main cmdline_process_param function is normally called from * applications' main(). An application linking against this stub * module shouldn't have a main() that calls it in the first place :-) * but it is just occasionally called by other supporting functions, * such as one in uxputty.c which sometimes handles a non-option * argument by making up equivalent options and passing them back to * this function. So we have to provide a link-time stub of this * function, but it had better not end up being called at run time. */ int cmdline_process_param(const char *p, char *value, int need_save, Conf *conf) { unreachable("cmdline_process_param should never be called"); } putty-0.76/nocproxy.c0000644000175000017500000000157614072266311011614 00000000000000/* * Routines to refuse to do cryptographic interaction with proxies * in PuTTY. This is a stub implementation of the same interfaces * provided by cproxy.c, for use in PuTTYtel. */ #include #include #include #include "putty.h" #include "network.h" #include "proxy.h" void proxy_socks5_offerencryptedauth(BinarySink *bs) { /* For telnet, don't add any new encrypted authentication routines */ } int proxy_socks5_handlechap (ProxySocket *p) { plug_closing(p->plug, "Proxy error: Trying to handle a SOCKS5 CHAP request" " in telnet-only build", PROXY_ERROR_GENERAL, 0); return 1; } int proxy_socks5_selectchap(ProxySocket *p) { plug_closing(p->plug, "Proxy error: Trying to handle a SOCKS5 CHAP request" " in telnet-only build", PROXY_ERROR_GENERAL, 0); return 1; } putty-0.76/nogss.c0000644000175000017500000000050414072266311011052 00000000000000/* * Stub definitions of the GSSAPI library list, for Unix pterm and * any other application that needs the symbols defined but has no * use for them. */ #include "putty.h" const int ngsslibs = 0; const char *const gsslibnames[1] = { "dummy" }; const struct keyvalwhere gsslibkeywords[1] = { { "dummy", 0, -1, -1 } }; putty-0.76/noprint.c0000644000175000017500000000116714072266311011420 00000000000000/* * Stub implementation of the printing interface for PuTTY, for the * benefit of non-printing terminal applications. */ #include #include #include "putty.h" struct printer_job_tag { int dummy; }; printer_job *printer_start_job(char *printer) { return NULL; } void printer_job_data(printer_job *pj, const void *data, size_t len) { } void printer_finish_job(printer_job *pj) { } printer_enum *printer_start_enum(int *nprinters_ptr) { *nprinters_ptr = 0; return NULL; } char *printer_get_name(printer_enum *pe, int i) { return NULL; } void printer_finish_enum(printer_enum *pe) { } putty-0.76/noproxy.c0000644000175000017500000000224114072266311011437 00000000000000/* * noproxy.c: an alternative to proxy.c, for use by auxiliary programs * that need to make network connections but don't want to include all * the full-on support for endless network proxies (and its * configuration requirements). Implements the primary APIs of * proxy.c, but maps them straight to the underlying network layer. */ #include "putty.h" #include "network.h" #include "proxy.h" SockAddr *name_lookup(const char *host, int port, char **canonicalname, Conf *conf, int addressfamily, LogContext *logctx, const char *reason) { return sk_namelookup(host, canonicalname, addressfamily); } Socket *new_connection(SockAddr *addr, const char *hostname, int port, bool privport, bool oobinline, bool nodelay, bool keepalive, Plug *plug, Conf *conf) { return sk_new(addr, port, privport, oobinline, nodelay, keepalive, plug); } Socket *new_listener(const char *srcaddr, int port, Plug *plug, bool local_host_only, Conf *conf, int addressfamily) { return sk_newlistener(srcaddr, port, plug, local_host_only, addressfamily); } putty-0.76/norand.c0000644000175000017500000000054314072266311011205 00000000000000/* * Stub implementations of RNG functions for applications without an RNG. */ #include "putty.h" void random_read(void *out, size_t size) { unreachable("Random numbers are not available in this application"); } void random_save_seed(void) { } void random_destroy_seed(void) { } void noise_ultralight(NoiseSourceId id, unsigned long data) { } putty-0.76/noshare.c0000644000175000017500000000111214072266311011354 00000000000000/* * Stub implementation of SSH connection-sharing IPC, for any * platform which can't support it at all. */ #include #include #include #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, char **ds_err, char **us_err, bool can_upstream, bool can_downstream) { return SHARE_NONE; } void platform_ssh_share_cleanup(const char *name) { } putty-0.76/noterm.c0000644000175000017500000000026014072266311011224 00000000000000/* * 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) { } putty-0.76/notiming.c0000644000175000017500000000112014072266311011540 00000000000000/* * 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) { } putty-0.76/nullplug.c0000644000175000017500000000215114072266311011563 00000000000000/* * nullplug.c: provide a null implementation of the Plug vtable which * ignores all calls. Occasionally useful in cases where we want to * make a network connection just to see if it works, but not do * anything with it afterwards except close it again. */ #include "putty.h" static void nullplug_socket_log(Plug *plug, PlugLogType type, SockAddr *addr, int port, const char *err_msg, int err_code) { } static void nullplug_closing(Plug *plug, const char *error_msg, int error_code, bool calling_back) { } static void nullplug_receive( Plug *plug, int urgent, const char *data, size_t len) { } static void nullplug_sent(Plug *plug, size_t bufsize) { } static const PlugVtable nullplug_plugvt = { .log = nullplug_socket_log, .closing = nullplug_closing, .receive = nullplug_receive, .sent = nullplug_sent, }; static Plug nullplug_plug = { &nullplug_plugvt }; /* * There's a singleton instance of nullplug, because it's not * interesting enough to worry about making more than one of them. */ Plug *const nullplug = &nullplug_plug; putty-0.76/pageant.c0000644000175000017500000022533214072266311011350 00000000000000/* * pageant.c: cross-platform code to implement Pageant. */ #include #include #include #include "putty.h" #include "mpint.h" #include "ssh.h" #include "sshcr.h" #include "pageant.h" /* * We need this to link with the RSA code, because rsa_ssh1_encrypt() * pads its data with random bytes. Since we only use rsa_ssh1_decrypt() * and the signing functions, which are deterministic, this should * never be called. * * If it _is_ called, there is a _serious_ problem, because it * won't generate true random numbers. So we must scream, panic, * and exit immediately if that should happen. */ void random_read(void *buf, size_t size) { modalfatalbox("Internal error: attempt to use random numbers in Pageant"); } static bool pageant_local = false; struct PageantClientDialogId { int dummy; }; typedef struct PageantKeySort PageantKeySort; typedef struct PageantKey PageantKey; typedef struct PageantAsyncOp PageantAsyncOp; typedef struct PageantAsyncOpVtable PageantAsyncOpVtable; typedef struct PageantClientRequestNode PageantClientRequestNode; typedef struct PageantKeyRequestNode PageantKeyRequestNode; struct PageantClientRequestNode { PageantClientRequestNode *prev, *next; }; struct PageantKeyRequestNode { PageantKeyRequestNode *prev, *next; }; struct PageantClientInfo { PageantClient *pc; /* goes to NULL when client is unregistered */ PageantClientRequestNode head; }; struct PageantAsyncOp { const PageantAsyncOpVtable *vt; PageantClientInfo *info; PageantClientRequestNode cr; PageantClientRequestId *reqid; }; struct PageantAsyncOpVtable { void (*coroutine)(PageantAsyncOp *pao); void (*free)(PageantAsyncOp *pao); }; static inline void pageant_async_op_coroutine(PageantAsyncOp *pao) { pao->vt->coroutine(pao); } static inline void pageant_async_op_free(PageantAsyncOp *pao) { delete_callbacks_for_context(pao); pao->vt->free(pao); } static inline void pageant_async_op_unlink(PageantAsyncOp *pao) { pao->cr.prev->next = pao->cr.next; pao->cr.next->prev = pao->cr.prev; } static inline void pageant_async_op_unlink_and_free(PageantAsyncOp *pao) { pageant_async_op_unlink(pao); pageant_async_op_free(pao); } static void pageant_async_op_callback(void *vctx) { pageant_async_op_coroutine((PageantAsyncOp *)vctx); } /* * Master list of all the keys we have stored, in any form at all. */ static tree234 *keytree; struct PageantKeySort { /* Prefix of the main PageantKey structure which contains all the * data that the sorting order depends on. Also simple enough that * you can construct one for lookup purposes. */ int ssh_version; /* 1 or 2; primary sort key */ ptrlen public_blob; /* secondary sort key */ }; struct PageantKey { PageantKeySort sort; strbuf *public_blob; /* the true owner of sort.public_blob */ char *comment; /* stored separately, whether or not in rkey/skey */ union { RSAKey *rkey; /* if ssh_version == 1 */ ssh2_userkey *skey; /* if ssh_version == 2 */ }; strbuf *encrypted_key_file; bool decryption_prompt_active; PageantKeyRequestNode blocked_requests; PageantClientDialogId dlgid; }; typedef struct PageantSignOp PageantSignOp; struct PageantSignOp { PageantKey *pk; strbuf *data_to_sign; unsigned flags; int crLine; unsigned char failure_type; PageantKeyRequestNode pkr; PageantAsyncOp pao; }; /* Master lock that indicates whether a GUI request is currently in * progress */ static bool gui_request_in_progress = false; static void failure(PageantClient *pc, PageantClientRequestId *reqid, strbuf *sb, unsigned char type, const char *fmt, ...); static void fail_requests_for_key(PageantKey *pk, const char *reason); static PageantKey *pageant_nth_key(int ssh_version, int i); static void pk_free(PageantKey *pk) { if (pk->public_blob) strbuf_free(pk->public_blob); sfree(pk->comment); if (pk->sort.ssh_version == 1 && pk->rkey) { freersakey(pk->rkey); sfree(pk->rkey); } if (pk->sort.ssh_version == 2 && pk->skey) { sfree(pk->skey->comment); ssh_key_free(pk->skey->key); sfree(pk->skey); } if (pk->encrypted_key_file) strbuf_free(pk->encrypted_key_file); fail_requests_for_key(pk, "key deleted from Pageant while signing " "request was pending"); sfree(pk); } static int cmpkeys(void *av, void *bv) { PageantKeySort *a = (PageantKeySort *)av, *b = (PageantKeySort *)bv; if (a->ssh_version != b->ssh_version) return a->ssh_version < b->ssh_version ? -1 : +1; else return ptrlen_strcmp(a->public_blob, b->public_blob); } static inline PageantKeySort keysort(int version, ptrlen blob) { PageantKeySort sort; sort.ssh_version = version; sort.public_blob = blob; return sort; } static strbuf *makeblob1(RSAKey *rkey) { strbuf *blob = strbuf_new(); rsa_ssh1_public_blob(BinarySink_UPCAST(blob), rkey, RSA_SSH1_EXPONENT_FIRST); return blob; } static strbuf *makeblob2(ssh2_userkey *skey) { strbuf *blob = strbuf_new(); ssh_key_public_blob(skey->key, BinarySink_UPCAST(blob)); return blob; } static PageantKey *findkey1(RSAKey *reqkey) { strbuf *blob = makeblob1(reqkey); PageantKeySort sort = keysort(1, ptrlen_from_strbuf(blob)); PageantKey *toret = find234(keytree, &sort, NULL); strbuf_free(blob); return toret; } static PageantKey *findkey2(ptrlen blob) { PageantKeySort sort = keysort(2, blob); return find234(keytree, &sort, NULL); } static int find_first_key_for_version(int ssh_version) { PageantKeySort sort = keysort(ssh_version, PTRLEN_LITERAL("")); int pos; if (findrelpos234(keytree, &sort, NULL, REL234_GE, &pos)) return pos; return count234(keytree); } static int count_keys(int ssh_version) { return (find_first_key_for_version(ssh_version + 1) - find_first_key_for_version(ssh_version)); } int pageant_count_ssh1_keys(void) { return count_keys(1); } int pageant_count_ssh2_keys(void) { return count_keys(2); } static bool pageant_add_ssh1_key(RSAKey *rkey) { PageantKey *pk = snew(PageantKey); memset(pk, 0, sizeof(PageantKey)); pk->sort.ssh_version = 1; pk->public_blob = makeblob1(rkey); pk->sort.public_blob = ptrlen_from_strbuf(pk->public_blob); pk->blocked_requests.next = pk->blocked_requests.prev = &pk->blocked_requests; if (add234(keytree, pk) == pk) { pk->rkey = rkey; if (rkey->comment) pk->comment = dupstr(rkey->comment); return true; } else { pk_free(pk); return false; } } static bool pageant_add_ssh2_key(ssh2_userkey *skey) { PageantKey *pk = snew(PageantKey); memset(pk, 0, sizeof(PageantKey)); pk->sort.ssh_version = 2; pk->public_blob = makeblob2(skey); pk->sort.public_blob = ptrlen_from_strbuf(pk->public_blob); pk->blocked_requests.next = pk->blocked_requests.prev = &pk->blocked_requests; PageantKey *pk_in_tree = add234(keytree, pk); if (pk_in_tree == pk) { /* The key wasn't in the tree at all, and we've just added it. */ pk->skey = skey; if (skey->comment) pk->comment = dupstr(skey->comment); return true; } else if (!pk_in_tree->skey) { /* The key was only stored encrypted, and now we have an * unencrypted version to add to the existing record. */ pk_in_tree->skey = skey; pk_free(pk); return true; } else { /* The key was already in the tree in full. */ pk_free(pk); return false; } } static void remove_all_keys(int ssh_version) { int start = find_first_key_for_version(ssh_version); int end = find_first_key_for_version(ssh_version + 1); while (end > start) { PageantKey *pk = delpos234(keytree, --end); assert(pk->sort.ssh_version == ssh_version); pk_free(pk); } } static void list_keys(BinarySink *bs, int ssh_version, bool extended) { int i; PageantKey *pk; put_uint32(bs, count_keys(ssh_version)); for (i = find_first_key_for_version(ssh_version); NULL != (pk = index234(keytree, i)); i++) { if (pk->sort.ssh_version != ssh_version) break; if (ssh_version > 1) put_stringpl(bs, pk->sort.public_blob); else put_datapl(bs, pk->sort.public_blob); /* no header */ put_stringpl(bs, ptrlen_from_asciz(pk->comment)); if (extended) { /* * Append to each key entry a string containing extension * data. This string begins with a flags word, and may in * future contain further data if flag bits are set saying * that it does. Hence, it's wrapped in a containing * string, so that clients that only partially understand * it can still find the parts they do understand. */ strbuf *sb = strbuf_new(); uint32_t flags = 0; if (!pk->skey) flags |= LIST_EXTENDED_FLAG_HAS_NO_CLEARTEXT_KEY; if (pk->encrypted_key_file) flags |= LIST_EXTENDED_FLAG_HAS_ENCRYPTED_KEY_FILE; put_uint32(sb, flags); put_stringsb(bs, sb); } } } void pageant_make_keylist1(BinarySink *bs) { list_keys(bs, 1, false); } void pageant_make_keylist2(BinarySink *bs) { list_keys(bs, 2, false); } void pageant_make_keylist_extended(BinarySink *bs) { list_keys(bs, 2, true); } void pageant_register_client(PageantClient *pc) { pc->info = snew(PageantClientInfo); pc->info->pc = pc; pc->info->head.prev = pc->info->head.next = &pc->info->head; } void pageant_unregister_client(PageantClient *pc) { PageantClientInfo *info = pc->info; assert(info); assert(info->pc == pc); while (pc->info->head.next != &pc->info->head) { PageantAsyncOp *pao = container_of(pc->info->head.next, PageantAsyncOp, cr); pageant_async_op_unlink_and_free(pao); } sfree(pc->info); } static PRINTF_LIKE(5, 6) void failure( PageantClient *pc, PageantClientRequestId *reqid, strbuf *sb, unsigned char type, const char *fmt, ...) { strbuf_clear(sb); put_byte(sb, type); if (!pc->suppress_logging) { va_list ap; va_start(ap, fmt); char *msg = dupvprintf(fmt, ap); va_end(ap); pageant_client_log(pc, reqid, "reply: SSH_AGENT_FAILURE (%s)", msg); sfree(msg); } } static void signop_link(PageantSignOp *so) { assert(!so->pkr.prev); assert(!so->pkr.next); so->pkr.prev = so->pk->blocked_requests.prev; so->pkr.next = &so->pk->blocked_requests; so->pkr.prev->next = &so->pkr; so->pkr.next->prev = &so->pkr; } static void signop_unlink(PageantSignOp *so) { if (so->pkr.next) { assert(so->pkr.prev); so->pkr.next->prev = so->pkr.prev; so->pkr.prev->next = so->pkr.next; } else { assert(!so->pkr.prev); } } static void signop_free(PageantAsyncOp *pao) { PageantSignOp *so = container_of(pao, PageantSignOp, pao); strbuf_free(so->data_to_sign); sfree(so); } static bool request_passphrase(PageantClient *pc, PageantKey *pk) { if (!pk->decryption_prompt_active) { assert(!gui_request_in_progress); bool created_dlg = pageant_client_ask_passphrase( pc, &pk->dlgid, pk->comment); if (!created_dlg) return false; gui_request_in_progress = true; pk->decryption_prompt_active = true; } return true; } static void signop_coroutine(PageantAsyncOp *pao) { PageantSignOp *so = container_of(pao, PageantSignOp, pao); strbuf *response; crBegin(so->crLine); while (!so->pk->skey && gui_request_in_progress) crReturnV; if (!so->pk->skey) { assert(so->pk->encrypted_key_file); if (!request_passphrase(so->pao.info->pc, so->pk)) { response = strbuf_new(); failure(so->pao.info->pc, so->pao.reqid, response, so->failure_type, "on-demand decryption could not " "prompt for a passphrase"); goto respond; } signop_link(so); crReturnV; signop_unlink(so); } uint32_t supported_flags = ssh_key_alg(so->pk->skey->key)->supported_flags; if (so->flags & ~supported_flags) { /* * We MUST reject any message containing flags we don't * understand. */ response = strbuf_new(); failure(so->pao.info->pc, so->pao.reqid, response, so->failure_type, "unsupported flag bits 0x%08"PRIx32, so->flags & ~supported_flags); goto respond; } char *invalid = ssh_key_invalid(so->pk->skey->key, so->flags); if (invalid) { response = strbuf_new(); failure(so->pao.info->pc, so->pao.reqid, response, so->failure_type, "key invalid: %s", invalid); sfree(invalid); goto respond; } strbuf *signature = strbuf_new(); ssh_key_sign(so->pk->skey->key, ptrlen_from_strbuf(so->data_to_sign), so->flags, BinarySink_UPCAST(signature)); response = strbuf_new(); put_byte(response, SSH2_AGENT_SIGN_RESPONSE); put_stringsb(response, signature); respond: pageant_client_got_response(so->pao.info->pc, so->pao.reqid, ptrlen_from_strbuf(response)); strbuf_free(response); pageant_async_op_unlink_and_free(&so->pao); crFinishFreedV; } static const PageantAsyncOpVtable signop_vtable = { .coroutine = signop_coroutine, .free = signop_free, }; static void fail_requests_for_key(PageantKey *pk, const char *reason) { while (pk->blocked_requests.next != &pk->blocked_requests) { PageantSignOp *so = container_of(pk->blocked_requests.next, PageantSignOp, pkr); signop_unlink(so); strbuf *sb = strbuf_new(); failure(so->pao.info->pc, so->pao.reqid, sb, so->failure_type, "%s", reason); pageant_client_got_response(so->pao.info->pc, so->pao.reqid, ptrlen_from_strbuf(sb)); strbuf_free(sb); pageant_async_op_unlink_and_free(&so->pao); } } static void unblock_requests_for_key(PageantKey *pk) { for (PageantKeyRequestNode *pkr = pk->blocked_requests.next; pkr != &pk->blocked_requests; pkr = pkr->next) { PageantSignOp *so = container_of(pk->blocked_requests.next, PageantSignOp, pkr); queue_toplevel_callback(pageant_async_op_callback, &so->pao); } } void pageant_passphrase_request_success(PageantClientDialogId *dlgid, ptrlen passphrase) { PageantKey *pk = container_of(dlgid, PageantKey, dlgid); assert(gui_request_in_progress); gui_request_in_progress = false; pk->decryption_prompt_active = false; if (!pk->skey) { const char *error; BinarySource src[1]; BinarySource_BARE_INIT_PL(src, ptrlen_from_strbuf( pk->encrypted_key_file)); strbuf *ppsb = strbuf_new_nm(); put_datapl(ppsb, passphrase); pk->skey = ppk_load_s(src, ppsb->s, &error); strbuf_free(ppsb); if (!pk->skey) { fail_requests_for_key(pk, "unable to decrypt key"); return; } else if (pk->skey == SSH2_WRONG_PASSPHRASE) { pk->skey = NULL; /* * Find a PageantClient to use for another attempt at * request_passphrase. */ PageantKeyRequestNode *pkr = pk->blocked_requests.next; if (pkr == &pk->blocked_requests) { /* * Special case: if all the requests have gone away at * this point, we need not bother putting up a request * at all any more. */ return; } PageantSignOp *so = container_of(pk->blocked_requests.next, PageantSignOp, pkr); pk->decryption_prompt_active = false; if (!request_passphrase(so->pao.info->pc, pk)) { fail_requests_for_key(pk, "unable to continue creating " "passphrase prompts"); } return; } else { keylist_update(); } } unblock_requests_for_key(pk); } void pageant_passphrase_request_refused(PageantClientDialogId *dlgid) { PageantKey *pk = container_of(dlgid, PageantKey, dlgid); assert(gui_request_in_progress); gui_request_in_progress = false; pk->decryption_prompt_active = false; fail_requests_for_key(pk, "user refused to supply passphrase"); } typedef struct PageantImmOp PageantImmOp; struct PageantImmOp { int crLine; strbuf *response; PageantAsyncOp pao; }; static void immop_free(PageantAsyncOp *pao) { PageantImmOp *io = container_of(pao, PageantImmOp, pao); if (io->response) strbuf_free(io->response); sfree(io); } static void immop_coroutine(PageantAsyncOp *pao) { PageantImmOp *io = container_of(pao, PageantImmOp, pao); crBegin(io->crLine); if (0) crReturnV; pageant_client_got_response(io->pao.info->pc, io->pao.reqid, ptrlen_from_strbuf(io->response)); pageant_async_op_unlink_and_free(&io->pao); crFinishFreedV; } static const PageantAsyncOpVtable immop_vtable = { .coroutine = immop_coroutine, .free = immop_free, }; static bool reencrypt_key(PageantKey *pk) { if (pk->sort.ssh_version != 2) { /* * We don't support storing SSH-1 keys in encrypted form at * all. */ return false; } if (!pk->encrypted_key_file) { /* * We can't re-encrypt a key if it doesn't have an encrypted * form. (We could make one up, of course - but with what * passphrase that we could expect the user to know later?) */ return false; } /* Only actually free pk->skey if it exists. But we return success * regardless, so that 'please ensure this key isn't stored * decrypted' is idempotent. */ if (pk->skey) { sfree(pk->skey->comment); ssh_key_free(pk->skey->key); sfree(pk->skey); pk->skey = NULL; } return true; } #define DECL_EXT_ENUM(id, name) id, enum Extension { KNOWN_EXTENSIONS(DECL_EXT_ENUM) EXT_UNKNOWN }; #define DEF_EXT_NAMES(id, name) PTRLEN_DECL_LITERAL(name), static const ptrlen extension_names[] = { KNOWN_EXTENSIONS(DEF_EXT_NAMES) }; static PageantAsyncOp *pageant_make_op( PageantClient *pc, PageantClientRequestId *reqid, ptrlen msgpl) { BinarySource msg[1]; strbuf *sb = strbuf_new_nm(); unsigned char failure_type = SSH_AGENT_FAILURE; int type; #define fail(...) failure(pc, reqid, sb, failure_type, __VA_ARGS__) BinarySource_BARE_INIT_PL(msg, msgpl); type = get_byte(msg); if (get_err(msg)) { fail("message contained no type code"); goto responded; } switch (type) { case SSH1_AGENTC_REQUEST_RSA_IDENTITIES: { /* * Reply with SSH1_AGENT_RSA_IDENTITIES_ANSWER. */ pageant_client_log(pc, reqid, "request: SSH1_AGENTC_REQUEST_RSA_IDENTITIES"); put_byte(sb, SSH1_AGENT_RSA_IDENTITIES_ANSWER); pageant_make_keylist1(BinarySink_UPCAST(sb)); pageant_client_log(pc, reqid, "reply: SSH1_AGENT_RSA_IDENTITIES_ANSWER"); if (!pc->suppress_logging) { int i; PageantKey *pk; for (i = 0; NULL != (pk = pageant_nth_key(1, i)); i++) { char *fingerprint = rsa_ssh1_fingerprint(pk->rkey); pageant_client_log(pc, reqid, "returned key: %s", fingerprint); sfree(fingerprint); } } break; } case SSH2_AGENTC_REQUEST_IDENTITIES: { /* * Reply with SSH2_AGENT_IDENTITIES_ANSWER. */ pageant_client_log(pc, reqid, "request: SSH2_AGENTC_REQUEST_IDENTITIES"); put_byte(sb, SSH2_AGENT_IDENTITIES_ANSWER); pageant_make_keylist2(BinarySink_UPCAST(sb)); pageant_client_log(pc, reqid, "reply: SSH2_AGENT_IDENTITIES_ANSWER"); if (!pc->suppress_logging) { int i; PageantKey *pk; for (i = 0; NULL != (pk = pageant_nth_key(2, i)); i++) { char *fingerprint = ssh2_fingerprint_blob( ptrlen_from_strbuf(pk->public_blob), SSH_FPTYPE_DEFAULT); pageant_client_log(pc, reqid, "returned key: %s %s", fingerprint, pk->comment); sfree(fingerprint); } } break; } case SSH1_AGENTC_RSA_CHALLENGE: { /* * Reply with either SSH1_AGENT_RSA_RESPONSE or * SSH_AGENT_FAILURE, depending on whether we have that key * or not. */ RSAKey reqkey; PageantKey *pk; mp_int *challenge, *response; ptrlen session_id; unsigned response_type; unsigned char response_md5[16]; int i; pageant_client_log(pc, reqid, "request: SSH1_AGENTC_RSA_CHALLENGE"); response = NULL; memset(&reqkey, 0, sizeof(reqkey)); get_rsa_ssh1_pub(msg, &reqkey, RSA_SSH1_EXPONENT_FIRST); challenge = get_mp_ssh1(msg); session_id = get_data(msg, 16); response_type = get_uint32(msg); if (get_err(msg)) { fail("unable to decode request"); goto challenge1_cleanup; } if (response_type != 1) { fail("response type other than 1 not supported"); goto challenge1_cleanup; } if (!pc->suppress_logging) { char *fingerprint; reqkey.comment = NULL; fingerprint = rsa_ssh1_fingerprint(&reqkey); pageant_client_log(pc, reqid, "requested key: %s", fingerprint); sfree(fingerprint); } if ((pk = findkey1(&reqkey)) == NULL) { fail("key not found"); goto challenge1_cleanup; } response = rsa_ssh1_decrypt(challenge, pk->rkey); { ssh_hash *h = ssh_hash_new(&ssh_md5); for (i = 0; i < 32; i++) put_byte(h, mp_get_byte(response, 31 - i)); put_datapl(h, session_id); ssh_hash_final(h, response_md5); } put_byte(sb, SSH1_AGENT_RSA_RESPONSE); put_data(sb, response_md5, 16); pageant_client_log(pc, reqid, "reply: SSH1_AGENT_RSA_RESPONSE"); challenge1_cleanup: if (response) mp_free(response); mp_free(challenge); freersakey(&reqkey); break; } 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. */ PageantKey *pk; ptrlen keyblob, sigdata; uint32_t flags; pageant_client_log(pc, reqid, "request: SSH2_AGENTC_SIGN_REQUEST"); keyblob = get_string(msg); sigdata = get_string(msg); if (get_err(msg)) { fail("unable to decode request"); goto responded; } /* * Later versions of the agent protocol added a flags word * on the end of the sign request. That hasn't always been * there, so we don't complain if we don't find it. * * get_uint32 will default to returning zero if no data is * available. */ bool have_flags = false; flags = get_uint32(msg); if (!get_err(msg)) have_flags = true; if (!pc->suppress_logging) { char *fingerprint = ssh2_fingerprint_blob( keyblob, SSH_FPTYPE_DEFAULT); pageant_client_log(pc, reqid, "requested key: %s", fingerprint); sfree(fingerprint); } if ((pk = findkey2(keyblob)) == NULL) { fail("key not found"); goto responded; } if (have_flags) pageant_client_log(pc, reqid, "signature flags = 0x%08"PRIx32, flags); else pageant_client_log(pc, reqid, "no signature flags"); strbuf_free(sb); /* no immediate response */ PageantSignOp *so = snew(PageantSignOp); so->pao.vt = &signop_vtable; so->pao.info = pc->info; so->pao.cr.prev = pc->info->head.prev; so->pao.cr.next = &pc->info->head; so->pao.reqid = reqid; so->pk = pk; so->pkr.prev = so->pkr.next = NULL; so->data_to_sign = strbuf_new(); put_datapl(so->data_to_sign, sigdata); so->flags = flags; so->failure_type = failure_type; so->crLine = 0; return &so->pao; break; } case SSH1_AGENTC_ADD_RSA_IDENTITY: { /* * Add to the list and return SSH_AGENT_SUCCESS, or * SSH_AGENT_FAILURE if the key was malformed. */ RSAKey *key; pageant_client_log(pc, reqid, "request: SSH1_AGENTC_ADD_RSA_IDENTITY"); key = get_rsa_ssh1_priv_agent(msg); key->comment = mkstr(get_string(msg)); if (get_err(msg)) { fail("unable to decode request"); goto add1_cleanup; } if (!rsa_verify(key)) { fail("key is invalid"); goto add1_cleanup; } if (!pc->suppress_logging) { char *fingerprint = rsa_ssh1_fingerprint(key); pageant_client_log(pc, reqid, "submitted key: %s", fingerprint); sfree(fingerprint); } if (pageant_add_ssh1_key(key)) { keylist_update(); put_byte(sb, SSH_AGENT_SUCCESS); pageant_client_log(pc, reqid, "reply: SSH_AGENT_SUCCESS"); key = NULL; /* don't free it in cleanup */ } else { fail("key already present"); } add1_cleanup: if (key) { freersakey(key); sfree(key); } break; } case SSH2_AGENTC_ADD_IDENTITY: { /* * Add to the list and return SSH_AGENT_SUCCESS, or * SSH_AGENT_FAILURE if the key was malformed. */ ssh2_userkey *key = NULL; ptrlen algpl; const ssh_keyalg *alg; pageant_client_log(pc, reqid, "request: SSH2_AGENTC_ADD_IDENTITY"); algpl = get_string(msg); key = snew(ssh2_userkey); key->key = NULL; key->comment = NULL; alg = find_pubkey_alg_len(algpl); if (!alg) { fail("algorithm unknown"); goto add2_cleanup; } key->key = ssh_key_new_priv_openssh(alg, msg); if (!key->key) { fail("key setup failed"); goto add2_cleanup; } key->comment = mkstr(get_string(msg)); if (get_err(msg)) { fail("unable to decode request"); goto add2_cleanup; } if (!pc->suppress_logging) { char *fingerprint = ssh2_fingerprint(key->key, SSH_FPTYPE_DEFAULT); pageant_client_log(pc, reqid, "submitted key: %s %s", fingerprint, key->comment); sfree(fingerprint); } if (pageant_add_ssh2_key(key)) { keylist_update(); put_byte(sb, SSH_AGENT_SUCCESS); pageant_client_log(pc, reqid, "reply: SSH_AGENT_SUCCESS"); key = NULL; /* don't clean it up */ } else { fail("key already present"); } add2_cleanup: if (key) { if (key->key) ssh_key_free(key->key); if (key->comment) sfree(key->comment); sfree(key); } break; } case SSH1_AGENTC_REMOVE_RSA_IDENTITY: { /* * Remove from the list and return SSH_AGENT_SUCCESS, or * perhaps SSH_AGENT_FAILURE if it wasn't in the list to * start with. */ RSAKey reqkey; PageantKey *pk; pageant_client_log(pc, reqid, "request: SSH1_AGENTC_REMOVE_RSA_IDENTITY"); memset(&reqkey, 0, sizeof(reqkey)); get_rsa_ssh1_pub(msg, &reqkey, RSA_SSH1_EXPONENT_FIRST); if (get_err(msg)) { fail("unable to decode request"); freersakey(&reqkey); goto responded; } if (!pc->suppress_logging) { char *fingerprint; reqkey.comment = NULL; fingerprint = rsa_ssh1_fingerprint(&reqkey); pageant_client_log(pc, reqid, "unwanted key: %s", fingerprint); sfree(fingerprint); } pk = findkey1(&reqkey); freersakey(&reqkey); if (pk) { pageant_client_log(pc, reqid, "found with comment: %s", pk->rkey->comment); del234(keytree, pk); keylist_update(); pk_free(pk); put_byte(sb, SSH_AGENT_SUCCESS); pageant_client_log(pc, reqid, "reply: SSH_AGENT_SUCCESS"); } else { fail("key not found"); } break; } case SSH2_AGENTC_REMOVE_IDENTITY: { /* * Remove from the list and return SSH_AGENT_SUCCESS, or * perhaps SSH_AGENT_FAILURE if it wasn't in the list to * start with. */ PageantKey *pk; ptrlen blob; pageant_client_log(pc, reqid, "request: SSH2_AGENTC_REMOVE_IDENTITY"); blob = get_string(msg); if (get_err(msg)) { fail("unable to decode request"); goto responded; } if (!pc->suppress_logging) { char *fingerprint = ssh2_fingerprint_blob( blob, SSH_FPTYPE_DEFAULT); pageant_client_log(pc, reqid, "unwanted key: %s", fingerprint); sfree(fingerprint); } pk = findkey2(blob); if (!pk) { fail("key not found"); goto responded; } pageant_client_log(pc, reqid, "found with comment: %s", pk->comment); del234(keytree, pk); keylist_update(); pk_free(pk); put_byte(sb, SSH_AGENT_SUCCESS); pageant_client_log(pc, reqid, "reply: SSH_AGENT_SUCCESS"); break; } case SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES: { /* * Remove all SSH-1 keys. Always returns success. */ pageant_client_log(pc, reqid, "request: SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES"); remove_all_keys(1); keylist_update(); put_byte(sb, SSH_AGENT_SUCCESS); pageant_client_log(pc, reqid, "reply: SSH_AGENT_SUCCESS"); break; } case SSH2_AGENTC_REMOVE_ALL_IDENTITIES: { /* * Remove all SSH-2 keys. Always returns success. */ pageant_client_log(pc, reqid, "request: SSH2_AGENTC_REMOVE_ALL_IDENTITIES"); remove_all_keys(2); keylist_update(); put_byte(sb, SSH_AGENT_SUCCESS); pageant_client_log(pc, reqid, "reply: SSH_AGENT_SUCCESS"); break; } case SSH2_AGENTC_EXTENSION: { enum Extension exttype = EXT_UNKNOWN; ptrlen extname = get_string(msg); pageant_client_log(pc, reqid, "request: SSH2_AGENTC_EXTENSION \"%.*s\"", PTRLEN_PRINTF(extname)); for (size_t i = 0; i < lenof(extension_names); i++) if (ptrlen_eq_ptrlen(extname, extension_names[i])) { exttype = i; /* * For SSH_AGENTC_EXTENSION requests, the message * code SSH_AGENT_FAILURE is reserved for "I don't * recognise this extension name at all". For any * other kind of failure while processing an * extension we _do_ recognise, we must switch to * returning a different failure code, with * semantics "I understood the extension name, but * something else went wrong". */ failure_type = SSH_AGENT_EXTENSION_FAILURE; break; } switch (exttype) { case EXT_UNKNOWN: fail("unrecognised extension name '%.*s'", PTRLEN_PRINTF(extname)); break; case EXT_QUERY: /* Standard request to list the supported extensions. */ put_byte(sb, SSH_AGENT_SUCCESS); for (size_t i = 0; i < lenof(extension_names); i++) put_stringpl(sb, extension_names[i]); pageant_client_log(pc, reqid, "reply: SSH_AGENT_SUCCESS + names"); break; case EXT_ADD_PPK: { ptrlen keyfile = get_string(msg); if (get_err(msg)) { fail("unable to decode request"); goto responded; } BinarySource src[1]; const char *error; strbuf *public_blob = strbuf_new(); char *comment; BinarySource_BARE_INIT_PL(src, keyfile); if (!ppk_loadpub_s(src, NULL, BinarySink_UPCAST(public_blob), &comment, &error)) { fail("failed to extract public key blob: %s", error); goto add_ppk_cleanup; } if (!pc->suppress_logging) { char *fingerprint = ssh2_fingerprint_blob( ptrlen_from_strbuf(public_blob), SSH_FPTYPE_DEFAULT); pageant_client_log(pc, reqid, "add-ppk: %s %s", fingerprint, comment); sfree(fingerprint); } BinarySource_BARE_INIT_PL(src, keyfile); bool encrypted = ppk_encrypted_s(src, NULL); if (!encrypted) { /* If the key isn't encrypted, then we should just * load and add it in the obvious way. */ BinarySource_BARE_INIT_PL(src, keyfile); ssh2_userkey *skey = ppk_load_s(src, NULL, &error); if (!skey) { fail("failed to decode private key: %s", error); } else if (pageant_add_ssh2_key(skey)) { keylist_update(); put_byte(sb, SSH_AGENT_SUCCESS); pageant_client_log(pc, reqid, "reply: SSH_AGENT_SUCCESS" " (loaded unencrypted PPK)"); } else { fail("key already present"); if (skey->key) ssh_key_free(skey->key); if (skey->comment) sfree(skey->comment); sfree(skey); } goto add_ppk_cleanup; } PageantKeySort sort = keysort(2, ptrlen_from_strbuf(public_blob)); PageantKey *pk = find234(keytree, &sort, NULL); if (pk) { /* * This public key blob already exists in the * keytree. Add the encrypted key file to the * existing record, if it doesn't have one already. */ if (!pk->encrypted_key_file) { pk->encrypted_key_file = strbuf_new_nm(); put_datapl(pk->encrypted_key_file, keyfile); keylist_update(); put_byte(sb, SSH_AGENT_SUCCESS); pageant_client_log( pc, reqid, "reply: SSH_AGENT_SUCCESS (added encrypted" " PPK to existing key record)"); } else { fail("key already present"); } } else { /* * We're adding a new key record containing only * an encrypted key file. */ PageantKey *pk = snew(PageantKey); memset(pk, 0, sizeof(PageantKey)); pk->blocked_requests.next = pk->blocked_requests.prev = &pk->blocked_requests; pk->sort.ssh_version = 2; pk->public_blob = public_blob; public_blob = NULL; pk->sort.public_blob = ptrlen_from_strbuf(pk->public_blob); pk->comment = dupstr(comment); pk->encrypted_key_file = strbuf_new_nm(); put_datapl(pk->encrypted_key_file, keyfile); PageantKey *added = add234(keytree, pk); assert(added == pk); (void)added; keylist_update(); put_byte(sb, SSH_AGENT_SUCCESS); pageant_client_log(pc, reqid, "reply: SSH_AGENT_SUCCESS (made" " new encrypted-only key record)"); } add_ppk_cleanup: if (public_blob) strbuf_free(public_blob); sfree(comment); break; } case EXT_REENCRYPT: { /* * Re-encrypt a single key, in the sense of deleting * its unencrypted copy, returning it to the state of * only having the encrypted PPK form stored, so that * the next attempt to use it will have to re-prompt * for the passphrase. */ ptrlen blob = get_string(msg); if (get_err(msg)) { fail("unable to decode request"); goto responded; } if (!pc->suppress_logging) { char *fingerprint = ssh2_fingerprint_blob( blob, SSH_FPTYPE_DEFAULT); pageant_client_log(pc, reqid, "key to re-encrypt: %s", fingerprint); sfree(fingerprint); } PageantKey *pk = findkey2(blob); if (!pk) { fail("key not found"); goto responded; } pageant_client_log(pc, reqid, "found with comment: %s", pk->comment); if (!reencrypt_key(pk)) { fail("this key couldn't be re-encrypted"); goto responded; } keylist_update(); put_byte(sb, SSH_AGENT_SUCCESS); pageant_client_log(pc, reqid, "reply: SSH_AGENT_SUCCESS"); break; } case EXT_REENCRYPT_ALL: { /* * Re-encrypt all keys that have an encrypted form * stored. Usually, returns success, but with a uint32 * appended indicating how many keys remain * unencrypted. The exception is if there is at least * one key in the agent and _no_ key was successfully * re-encrypted; in that situation we've done nothing, * and the client didn't _want_ us to do nothing, so * we return failure. * * (Rationale: the 'failure' message ought to be * atomic, that is, you shouldn't return failure * having made a state change.) */ unsigned nfailures = 0, nsuccesses = 0; PageantKey *pk; for (int i = 0; (pk = index234(keytree, i)) != NULL; i++) { if (reencrypt_key(pk)) nsuccesses++; else nfailures++; } if (nsuccesses == 0 && nfailures > 0) { fail("no key could be re-encrypted"); } else { keylist_update(); put_byte(sb, SSH_AGENT_SUCCESS); put_uint32(sb, nfailures); pageant_client_log(pc, reqid, "reply: SSH_AGENT_SUCCESS " "(%u keys re-encrypted, %u failures)", nsuccesses, nfailures); } break; } case EXT_LIST_EXTENDED: { /* * Return a key list like SSH2_AGENTC_REQUEST_IDENTITIES, * except that each key is annotated with extra * information such as whether it's currently encrypted. * * The return message type is AGENT_SUCCESS with auxiliary * data, which is more like other extension messages. I * think it would be confusing to reuse IDENTITIES_ANSWER * for a reply message with an incompatible format. */ put_byte(sb, SSH_AGENT_SUCCESS); pageant_make_keylist_extended(BinarySink_UPCAST(sb)); pageant_client_log(pc, reqid, "reply: SSH2_AGENT_SUCCESS + key list"); if (!pc->suppress_logging) { int i; PageantKey *pk; for (i = 0; NULL != (pk = pageant_nth_key(2, i)); i++) { char *fingerprint = ssh2_fingerprint_blob( ptrlen_from_strbuf(pk->public_blob), SSH_FPTYPE_DEFAULT); pageant_client_log(pc, reqid, "returned key: %s %s", fingerprint, pk->comment); sfree(fingerprint); } } break; } } break; } default: pageant_client_log(pc, reqid, "request: unknown message type %d", type); fail("unrecognised message"); break; } #undef fail responded:; PageantImmOp *io = snew(PageantImmOp); io->pao.vt = &immop_vtable; io->pao.info = pc->info; io->pao.cr.prev = pc->info->head.prev; io->pao.cr.next = &pc->info->head; io->pao.reqid = reqid; io->response = sb; io->crLine = 0; return &io->pao; } void pageant_handle_msg(PageantClient *pc, PageantClientRequestId *reqid, ptrlen msgpl) { PageantAsyncOp *pao = pageant_make_op(pc, reqid, msgpl); queue_toplevel_callback(pageant_async_op_callback, pao); } void pageant_init(void) { pageant_local = true; keytree = newtree234(cmpkeys); } static PageantKey *pageant_nth_key(int ssh_version, int i) { PageantKey *pk = index234( keytree, find_first_key_for_version(ssh_version) + i); if (pk && pk->sort.ssh_version == ssh_version) return pk; else return NULL; } bool pageant_delete_nth_ssh1_key(int i) { PageantKey *pk = delpos234(keytree, find_first_key_for_version(1) + i); if (!pk) return false; pk_free(pk); return true; } bool pageant_delete_nth_ssh2_key(int i) { PageantKey *pk = delpos234(keytree, find_first_key_for_version(2) + i); if (!pk) return false; pk_free(pk); return true; } bool pageant_reencrypt_nth_ssh2_key(int i) { PageantKey *pk = index234(keytree, find_first_key_for_version(2) + i); if (!pk) return false; return reencrypt_key(pk); } void pageant_delete_all(void) { remove_all_keys(1); remove_all_keys(2); } void pageant_reencrypt_all(void) { PageantKey *pk; for (int i = 0; (pk = index234(keytree, i)) != NULL; i++) reencrypt_key(pk); } /* ---------------------------------------------------------------------- * The agent plug. */ /* * An extra coroutine macro, specific to this code which is consuming * 'const char *data'. */ #define crGetChar(c) do \ { \ while (len == 0) { \ *crLine =__LINE__; return; case __LINE__:; \ } \ len--; \ (c) = (unsigned char)*data++; \ } while (0) struct pageant_conn_queued_response { struct pageant_conn_queued_response *next, *prev; size_t req_index; /* for indexing requests in log messages */ strbuf *sb; PageantClientRequestId reqid; }; struct pageant_conn_state { Socket *connsock; PageantListenerClient *plc; unsigned char lenbuf[4], pktbuf[AGENT_MAX_MSGLEN]; unsigned len, got; bool real_packet; size_t conn_index; /* for indexing connections in log messages */ size_t req_index; /* for indexing requests in log messages */ int crLine; /* for coroutine in pageant_conn_receive */ struct pageant_conn_queued_response response_queue; PageantClient pc; Plug plug; }; static void pageant_conn_closing(Plug *plug, const char *error_msg, int error_code, bool calling_back) { struct pageant_conn_state *pc = container_of( plug, struct pageant_conn_state, plug); if (error_msg) pageant_listener_client_log(pc->plc, "c#%"SIZEu": error: %s", pc->conn_index, error_msg); else pageant_listener_client_log(pc->plc, "c#%"SIZEu": connection closed", pc->conn_index); sk_close(pc->connsock); pageant_unregister_client(&pc->pc); sfree(pc); } static void pageant_conn_sent(Plug *plug, size_t bufsize) { /* struct pageant_conn_state *pc = container_of( plug, struct pageant_conn_state, plug); */ /* * We do nothing here, because we expect that there won't be a * need to throttle and unthrottle the connection to an agent - * clients will typically not send many requests, and will wait * until they receive each reply before sending a new request. */ } static void pageant_conn_log(PageantClient *pc, PageantClientRequestId *reqid, const char *fmt, va_list ap) { struct pageant_conn_state *pcs = container_of(pc, struct pageant_conn_state, pc); struct pageant_conn_queued_response *qr = container_of(reqid, struct pageant_conn_queued_response, reqid); char *formatted = dupvprintf(fmt, ap); pageant_listener_client_log(pcs->plc, "c#%"SIZEu",r#%"SIZEu": %s", pcs->conn_index, qr->req_index, formatted); sfree(formatted); } static void pageant_conn_got_response( PageantClient *pc, PageantClientRequestId *reqid, ptrlen response) { struct pageant_conn_state *pcs = container_of(pc, struct pageant_conn_state, pc); struct pageant_conn_queued_response *qr = container_of(reqid, struct pageant_conn_queued_response, reqid); qr->sb = strbuf_new_nm(); put_stringpl(qr->sb, response); while (pcs->response_queue.next != &pcs->response_queue && pcs->response_queue.next->sb) { qr = pcs->response_queue.next; sk_write(pcs->connsock, qr->sb->u, qr->sb->len); qr->next->prev = qr->prev; qr->prev->next = qr->next; strbuf_free(qr->sb); sfree(qr); } } static bool pageant_conn_ask_passphrase( PageantClient *pc, PageantClientDialogId *dlgid, const char *comment) { struct pageant_conn_state *pcs = container_of(pc, struct pageant_conn_state, pc); return pageant_listener_client_ask_passphrase(pcs->plc, dlgid, comment); } static const PageantClientVtable pageant_connection_clientvt = { .log = pageant_conn_log, .got_response = pageant_conn_got_response, .ask_passphrase = pageant_conn_ask_passphrase, }; static void pageant_conn_receive( Plug *plug, int urgent, const char *data, size_t len) { struct pageant_conn_state *pc = container_of( plug, struct pageant_conn_state, plug); char c; crBegin(pc->crLine); while (len > 0) { pc->got = 0; while (pc->got < 4) { crGetChar(c); pc->lenbuf[pc->got++] = c; } pc->len = GET_32BIT_MSB_FIRST(pc->lenbuf); pc->got = 0; pc->real_packet = (pc->len < AGENT_MAX_MSGLEN-4); { struct pageant_conn_queued_response *qr = snew(struct pageant_conn_queued_response); qr->prev = pc->response_queue.prev; qr->next = &pc->response_queue; qr->prev->next = qr->next->prev = qr; qr->sb = NULL; qr->req_index = pc->req_index++; } if (!pc->real_packet) { /* * Send failure immediately, before consuming the packet * data. That way we notify the client reasonably early * even if the data channel has just started spewing * nonsense. */ pageant_client_log(&pc->pc, &pc->response_queue.prev->reqid, "early reply: SSH_AGENT_FAILURE " "(overlong message, length %u)", pc->len); static const unsigned char failure[] = { SSH_AGENT_FAILURE }; pageant_conn_got_response(&pc->pc, &pc->response_queue.prev->reqid, make_ptrlen(failure, lenof(failure))); } while (pc->got < pc->len) { crGetChar(c); if (pc->real_packet) pc->pktbuf[pc->got] = c; pc->got++; } if (pc->real_packet) pageant_handle_msg(&pc->pc, &pc->response_queue.prev->reqid, make_ptrlen(pc->pktbuf, pc->len)); } crFinishV; } struct pageant_listen_state { Socket *listensock; PageantListenerClient *plc; size_t conn_index; /* for indexing connections in log messages */ Plug plug; }; static void pageant_listen_closing(Plug *plug, const char *error_msg, int error_code, bool calling_back) { struct pageant_listen_state *pl = container_of( plug, struct pageant_listen_state, plug); if (error_msg) pageant_listener_client_log(pl->plc, "listening socket: error: %s", error_msg); sk_close(pl->listensock); pl->listensock = NULL; } static const PlugVtable pageant_connection_plugvt = { .closing = pageant_conn_closing, .receive = pageant_conn_receive, .sent = pageant_conn_sent, }; static int pageant_listen_accepting(Plug *plug, accept_fn_t constructor, accept_ctx_t ctx) { struct pageant_listen_state *pl = container_of( plug, struct pageant_listen_state, plug); struct pageant_conn_state *pc; const char *err; SocketPeerInfo *peerinfo; pc = snew(struct pageant_conn_state); pc->plug.vt = &pageant_connection_plugvt; pc->pc.vt = &pageant_connection_clientvt; pc->plc = pl->plc; pc->response_queue.next = pc->response_queue.prev = &pc->response_queue; pc->conn_index = pl->conn_index++; pc->req_index = 0; pc->crLine = 0; pc->connsock = constructor(ctx, &pc->plug); if ((err = sk_socket_error(pc->connsock)) != NULL) { sk_close(pc->connsock); sfree(pc); return 1; } sk_set_frozen(pc->connsock, false); peerinfo = sk_peer_info(pc->connsock); if (peerinfo && peerinfo->log_text) { pageant_listener_client_log(pl->plc, "c#%"SIZEu": new connection from %s", pc->conn_index, peerinfo->log_text); } else { pageant_listener_client_log(pl->plc, "c#%"SIZEu": new connection", pc->conn_index); } sk_free_peer_info(peerinfo); pageant_register_client(&pc->pc); return 0; } static const PlugVtable pageant_listener_plugvt = { .closing = pageant_listen_closing, .accepting = pageant_listen_accepting, }; struct pageant_listen_state *pageant_listener_new( Plug **plug, PageantListenerClient *plc) { struct pageant_listen_state *pl = snew(struct pageant_listen_state); pl->plug.vt = &pageant_listener_plugvt; pl->plc = plc; pl->listensock = NULL; pl->conn_index = 0; *plug = &pl->plug; return pl; } void pageant_listener_got_socket(struct pageant_listen_state *pl, Socket *sock) { pl->listensock = sock; } void pageant_listener_free(struct pageant_listen_state *pl) { if (pl->listensock) sk_close(pl->listensock); sfree(pl); } /* ---------------------------------------------------------------------- * Code to perform agent operations either as a client, or within the * same process as the running agent. */ static tree234 *passphrases = NULL; typedef struct PageantInternalClient { strbuf *response; bool got_response; PageantClient pc; } PageantInternalClient; static void internal_client_got_response( PageantClient *pc, PageantClientRequestId *reqid, ptrlen response) { PageantInternalClient *pic = container_of(pc, PageantInternalClient, pc); strbuf_clear(pic->response); put_datapl(pic->response, response); pic->got_response = true; } static bool internal_client_ask_passphrase( PageantClient *pc, PageantClientDialogId *dlgid, const char *comment) { /* No delaying operations are permitted in this mode */ return false; } static const PageantClientVtable internal_clientvt = { .log = NULL, .got_response = internal_client_got_response, .ask_passphrase = internal_client_ask_passphrase, }; typedef struct PageantClientOp { strbuf *buf; bool request_made; BinarySink_DELEGATE_IMPLEMENTATION; BinarySource_IMPLEMENTATION; } PageantClientOp; static PageantClientOp *pageant_client_op_new(void) { PageantClientOp *pco = snew(PageantClientOp); pco->buf = strbuf_new_for_agent_query(); pco->request_made = false; BinarySink_DELEGATE_INIT(pco, pco->buf); BinarySource_INIT(pco, "", 0); return pco; } static void pageant_client_op_free(PageantClientOp *pco) { if (pco->buf) strbuf_free(pco->buf); sfree(pco); } static unsigned pageant_client_op_query(PageantClientOp *pco) { /* Since we use the same strbuf for the request and the response, * check by assertion that we aren't embarrassingly sending a * previous response back to the agent */ assert(!pco->request_made); pco->request_made = true; if (!pageant_local) { void *response_raw; int resplen_raw; agent_query_synchronous(pco->buf, &response_raw, &resplen_raw); strbuf_clear(pco->buf); put_data(pco->buf, response_raw, resplen_raw); sfree(response_raw); /* The data coming back from agent_query_synchronous will have * its length field prepended. So we start by parsing it as an * SSH-formatted string, and then reinitialise our * BinarySource with the interior of that string. */ BinarySource_INIT_PL(pco, ptrlen_from_strbuf(pco->buf)); BinarySource_INIT_PL(pco, get_string(pco)); } else { PageantInternalClient pic; PageantClientRequestId reqid; pic.pc.vt = &internal_clientvt; pic.pc.suppress_logging = true; pic.response = pco->buf; pic.got_response = false; pageant_register_client(&pic.pc); assert(pco->buf->len > 4); PageantAsyncOp *pao = pageant_make_op( &pic.pc, &reqid, make_ptrlen(pco->buf->s + 4, pco->buf->len - 4)); while (!pic.got_response) pageant_async_op_coroutine(pao); pageant_unregister_client(&pic.pc); BinarySource_INIT_PL(pco, ptrlen_from_strbuf(pco->buf)); } /* Strip off and directly return the type byte, which every client * will need, to save a boilerplate get_byte at each call site */ unsigned reply_type = get_byte(pco); if (get_err(pco)) reply_type = 256; /* out-of-range code */ return reply_type; } /* * After processing a list of filenames, we want to forget the * passphrases. */ void pageant_forget_passphrases(void) { if (!passphrases) /* in case we never set it up at all */ return; while (count234(passphrases) > 0) { char *pp = index234(passphrases, 0); smemclr(pp, strlen(pp)); delpos234(passphrases, 0); sfree(pp); } } typedef struct KeyListEntry { ptrlen blob, comment; uint32_t flags; } KeyListEntry; typedef struct KeyList { strbuf *raw_data; KeyListEntry *keys; size_t nkeys; bool broken; } KeyList; static void keylist_free(KeyList *kl) { sfree(kl->keys); strbuf_free(kl->raw_data); sfree(kl); } static PageantClientOp *pageant_request_keylist_1(void) { PageantClientOp *pco = pageant_client_op_new(); put_byte(pco, SSH1_AGENTC_REQUEST_RSA_IDENTITIES); if (pageant_client_op_query(pco) == SSH1_AGENT_RSA_IDENTITIES_ANSWER) return pco; pageant_client_op_free(pco); return NULL; } static PageantClientOp *pageant_request_keylist_2(void) { PageantClientOp *pco = pageant_client_op_new(); put_byte(pco, SSH2_AGENTC_REQUEST_IDENTITIES); if (pageant_client_op_query(pco) == SSH2_AGENT_IDENTITIES_ANSWER) return pco; pageant_client_op_free(pco); return NULL; } static PageantClientOp *pageant_request_keylist_extended(void) { PageantClientOp *pco = pageant_client_op_new(); put_byte(pco, SSH2_AGENTC_EXTENSION); put_stringpl(pco, extension_names[EXT_LIST_EXTENDED]); if (pageant_client_op_query(pco) == SSH_AGENT_SUCCESS) return pco; pageant_client_op_free(pco); return NULL; } static KeyList *pageant_get_keylist(unsigned ssh_version) { PageantClientOp *pco; bool list_is_extended = false; if (ssh_version == 1) { pco = pageant_request_keylist_1(); } else { if ((pco = pageant_request_keylist_extended()) != NULL) list_is_extended = true; else pco = pageant_request_keylist_2(); } if (!pco) return NULL; KeyList *kl = snew(KeyList); kl->nkeys = get_uint32(pco); kl->keys = snewn(kl->nkeys, struct KeyListEntry); kl->broken = false; for (size_t i = 0; i < kl->nkeys && !get_err(pco); i++) { if (ssh_version == 1) { int bloblen = rsa_ssh1_public_blob_len( make_ptrlen(get_ptr(pco), get_avail(pco))); if (bloblen < 0) { kl->broken = true; bloblen = 0; } kl->keys[i].blob = get_data(pco, bloblen); } else { kl->keys[i].blob = get_string(pco); } kl->keys[i].comment = get_string(pco); if (list_is_extended) { ptrlen key_ext_info = get_string(pco); BinarySource src[1]; BinarySource_BARE_INIT_PL(src, key_ext_info); kl->keys[i].flags = get_uint32(src); } else { kl->keys[i].flags = 0; } } if (get_err(pco)) kl->broken = true; kl->raw_data = pco->buf; pco->buf = NULL; pageant_client_op_free(pco); return kl; } int pageant_add_keyfile(Filename *filename, const char *passphrase, char **retstr, bool add_encrypted) { RSAKey *rkey = NULL; ssh2_userkey *skey = NULL; bool needs_pass; int ret; int attempts; char *comment; const char *this_passphrase; const char *error = NULL; int type; if (!passphrases) { passphrases = newtree234(NULL); } *retstr = NULL; type = key_type(filename); if (type != SSH_KEYTYPE_SSH1 && type != SSH_KEYTYPE_SSH2) { *retstr = dupprintf("Couldn't load this key (%s)", key_type_to_str(type)); return PAGEANT_ACTION_FAILURE; } if (add_encrypted && type == SSH_KEYTYPE_SSH1) { *retstr = dupprintf("Can't add SSH-1 keys in encrypted form"); return PAGEANT_ACTION_FAILURE; } /* * See if the key is already loaded (in the primary Pageant, * which may or may not be us). */ { strbuf *blob = strbuf_new(); KeyList *kl; if (type == SSH_KEYTYPE_SSH1) { if (!rsa1_loadpub_f(filename, BinarySink_UPCAST(blob), NULL, &error)) { *retstr = dupprintf("Couldn't load private key (%s)", error); strbuf_free(blob); return PAGEANT_ACTION_FAILURE; } kl = pageant_get_keylist(1); } else { if (!ppk_loadpub_f(filename, NULL, BinarySink_UPCAST(blob), NULL, &error)) { *retstr = dupprintf("Couldn't load private key (%s)", error); strbuf_free(blob); return PAGEANT_ACTION_FAILURE; } kl = pageant_get_keylist(2); } if (kl) { if (kl->broken) { *retstr = dupstr("Received broken key list from agent"); keylist_free(kl); strbuf_free(blob); return PAGEANT_ACTION_FAILURE; } for (size_t i = 0; i < kl->nkeys; i++) { /* * If the key already exists in the agent, we're done, * except in the following special cases: * * It's encrypted in the agent, and we're being asked * to add it unencrypted, in which case we still want * to upload the unencrypted version to cause the key * to become decrypted. * (Rationale: if you know in advance you're going to * want it, and don't want to be interrupted at an * unpredictable moment to be asked for the * passphrase.) * * The agent only has cleartext, and we're being asked * to add it encrypted, in which case we'll add the * encrypted form. * (Rationale: if you might want to re-encrypt the key * at some future point, but it happened to have been * initially added in cleartext, perhaps by something * other than Pageant.) */ if (ptrlen_eq_ptrlen(ptrlen_from_strbuf(blob), kl->keys[i].blob)) { bool have_unencrypted = !(kl->keys[i].flags & LIST_EXTENDED_FLAG_HAS_NO_CLEARTEXT_KEY); bool have_encrypted = (kl->keys[i].flags & LIST_EXTENDED_FLAG_HAS_ENCRYPTED_KEY_FILE); if ((have_unencrypted && !add_encrypted) || (have_encrypted && add_encrypted)) { /* Key is already present in the desired form; * we can now leave. */ keylist_free(kl); strbuf_free(blob); return PAGEANT_ACTION_OK; } } } keylist_free(kl); } strbuf_free(blob); } if (add_encrypted) { const char *load_error; LoadedFile *lf = lf_load_keyfile(filename, &load_error); if (!lf) { *retstr = dupstr(load_error); return PAGEANT_ACTION_FAILURE; } PageantClientOp *pco = pageant_client_op_new(); put_byte(pco, SSH2_AGENTC_EXTENSION); put_stringpl(pco, extension_names[EXT_ADD_PPK]); put_string(pco, lf->data, lf->len); lf_free(lf); unsigned reply = pageant_client_op_query(pco); pageant_client_op_free(pco); if (reply != SSH_AGENT_SUCCESS) { if (reply == SSH_AGENT_FAILURE) { /* The agent didn't understand the protocol extension * at all. */ *retstr = dupstr("Agent doesn't support adding " "encrypted keys"); } else { *retstr = dupstr("The already running agent " "refused to add the key."); } return PAGEANT_ACTION_FAILURE; } return PAGEANT_ACTION_OK; } error = NULL; if (type == SSH_KEYTYPE_SSH1) needs_pass = rsa1_encrypted_f(filename, &comment); else needs_pass = ppk_encrypted_f(filename, &comment); attempts = 0; if (type == SSH_KEYTYPE_SSH1) rkey = snew(RSAKey); /* * Loop round repeatedly trying to load the key, until we either * succeed, fail for some serious reason, or run out of * passphrases to try. */ while (1) { if (needs_pass) { /* * If we've been given a passphrase on input, try using * it. Otherwise, try one from our tree234 of previously * useful passphrases. */ if (passphrase) { this_passphrase = (attempts == 0 ? passphrase : NULL); } else { this_passphrase = (const char *)index234(passphrases, attempts); } if (!this_passphrase) { /* * Run out of passphrases to try. */ *retstr = comment; sfree(rkey); return PAGEANT_ACTION_NEED_PP; } } else this_passphrase = ""; if (type == SSH_KEYTYPE_SSH1) ret = rsa1_load_f(filename, rkey, this_passphrase, &error); else { skey = ppk_load_f(filename, this_passphrase, &error); if (skey == SSH2_WRONG_PASSPHRASE) ret = -1; else if (!skey) ret = 0; else ret = 1; } if (ret == 0) { /* * Failed to load the key file, for some reason other than * a bad passphrase. */ *retstr = dupstr(error); sfree(rkey); if (comment) sfree(comment); return PAGEANT_ACTION_FAILURE; } else if (ret == 1) { /* * Successfully loaded the key file. */ break; } else { /* * Passphrase wasn't right; go round again. */ attempts++; } } /* * If we get here, we've successfully loaded the key into * rkey/skey, but not yet added it to the agent. */ /* * If the key was successfully decrypted, save the passphrase for * use with other keys we try to load. */ { char *pp_copy = dupstr(this_passphrase); if (addpos234(passphrases, pp_copy, 0) != pp_copy) { /* No need; it was already there. */ smemclr(pp_copy, strlen(pp_copy)); sfree(pp_copy); } } if (comment) sfree(comment); if (type == SSH_KEYTYPE_SSH1) { PageantClientOp *pco = pageant_client_op_new(); put_byte(pco, SSH1_AGENTC_ADD_RSA_IDENTITY); rsa_ssh1_private_blob_agent(BinarySink_UPCAST(pco), rkey); put_stringz(pco, rkey->comment); unsigned reply = pageant_client_op_query(pco); pageant_client_op_free(pco); freersakey(rkey); sfree(rkey); if (reply != SSH_AGENT_SUCCESS) { *retstr = dupstr("The already running agent " "refused to add the key."); return PAGEANT_ACTION_FAILURE; } } else { PageantClientOp *pco = pageant_client_op_new(); put_byte(pco, SSH2_AGENTC_ADD_IDENTITY); put_stringz(pco, ssh_key_ssh_id(skey->key)); ssh_key_openssh_blob(skey->key, BinarySink_UPCAST(pco)); put_stringz(pco, skey->comment); unsigned reply = pageant_client_op_query(pco); pageant_client_op_free(pco); sfree(skey->comment); ssh_key_free(skey->key); sfree(skey); if (reply != SSH_AGENT_SUCCESS) { *retstr = dupstr("The already running agent " "refused to add the key."); return PAGEANT_ACTION_FAILURE; } } return PAGEANT_ACTION_OK; } int pageant_enum_keys(pageant_key_enum_fn_t callback, void *callback_ctx, char **retstr) { KeyList *kl1 = NULL, *kl2 = NULL; struct pageant_pubkey cbkey; int toret = PAGEANT_ACTION_FAILURE; kl1 = pageant_get_keylist(1); if (kl1 && kl1->broken) { *retstr = dupstr("Received broken SSH-1 key list from agent"); goto out; } kl2 = pageant_get_keylist(2); if (kl2 && kl2->broken) { *retstr = dupstr("Received broken SSH-2 key list from agent"); goto out; } if (kl1) { for (size_t i = 0; i < kl1->nkeys; i++) { cbkey.blob = strbuf_new(); put_datapl(cbkey.blob, kl1->keys[i].blob); cbkey.comment = mkstr(kl1->keys[i].comment); cbkey.ssh_version = 1; /* Decode public blob into a key in order to fingerprint it */ RSAKey rkey; memset(&rkey, 0, sizeof(rkey)); { BinarySource src[1]; BinarySource_BARE_INIT_PL(src, kl1->keys[i].blob); get_rsa_ssh1_pub(src, &rkey, RSA_SSH1_EXPONENT_FIRST); if (get_err(src)) { *retstr = dupstr( "Received an invalid SSH-1 key from agent"); goto out; } } char **fingerprints = rsa_ssh1_fake_all_fingerprints(&rkey); freersakey(&rkey); callback(callback_ctx, fingerprints, cbkey.comment, kl1->keys[i].flags, &cbkey); strbuf_free(cbkey.blob); sfree(cbkey.comment); ssh2_free_all_fingerprints(fingerprints); } } if (kl2) { for (size_t i = 0; i < kl2->nkeys; i++) { cbkey.blob = strbuf_new(); put_datapl(cbkey.blob, kl2->keys[i].blob); cbkey.comment = mkstr(kl2->keys[i].comment); cbkey.ssh_version = 2; char **fingerprints = ssh2_all_fingerprints_for_blob(kl2->keys[i].blob); callback(callback_ctx, fingerprints, cbkey.comment, kl2->keys[i].flags, &cbkey); ssh2_free_all_fingerprints(fingerprints); sfree(cbkey.comment); strbuf_free(cbkey.blob); } } *retstr = NULL; toret = PAGEANT_ACTION_OK; out: if (kl1) keylist_free(kl1); if (kl2) keylist_free(kl2); return toret; } int pageant_delete_key(struct pageant_pubkey *key, char **retstr) { PageantClientOp *pco = pageant_client_op_new(); if (key->ssh_version == 1) { put_byte(pco, SSH1_AGENTC_REMOVE_RSA_IDENTITY); put_data(pco, key->blob->s, key->blob->len); } else { put_byte(pco, SSH2_AGENTC_REMOVE_IDENTITY); put_string(pco, key->blob->s, key->blob->len); } unsigned reply = pageant_client_op_query(pco); pageant_client_op_free(pco); if (reply != SSH_AGENT_SUCCESS) { *retstr = dupstr("Agent failed to delete key"); return PAGEANT_ACTION_FAILURE; } else { *retstr = NULL; return PAGEANT_ACTION_OK; } } int pageant_delete_all_keys(char **retstr) { PageantClientOp *pco; unsigned reply; pco = pageant_client_op_new(); put_byte(pco, SSH2_AGENTC_REMOVE_ALL_IDENTITIES); reply = pageant_client_op_query(pco); pageant_client_op_free(pco); if (reply != SSH_AGENT_SUCCESS) { *retstr = dupstr("Agent failed to delete SSH-2 keys"); return PAGEANT_ACTION_FAILURE; } pco = pageant_client_op_new(); put_byte(pco, SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES); reply = pageant_client_op_query(pco); pageant_client_op_free(pco); if (reply != SSH_AGENT_SUCCESS) { *retstr = dupstr("Agent failed to delete SSH-1 keys"); return PAGEANT_ACTION_FAILURE; } *retstr = NULL; return PAGEANT_ACTION_OK; } int pageant_reencrypt_key(struct pageant_pubkey *key, char **retstr) { PageantClientOp *pco = pageant_client_op_new(); if (key->ssh_version == 1) { *retstr = dupstr("Can't re-encrypt an SSH-1 key"); pageant_client_op_free(pco); return PAGEANT_ACTION_FAILURE; } else { put_byte(pco, SSH2_AGENTC_EXTENSION); put_stringpl(pco, extension_names[EXT_REENCRYPT]); put_string(pco, key->blob->s, key->blob->len); } unsigned reply = pageant_client_op_query(pco); pageant_client_op_free(pco); if (reply != SSH_AGENT_SUCCESS) { if (reply == SSH_AGENT_FAILURE) { /* The agent didn't understand the protocol extension at all. */ *retstr = dupstr("Agent doesn't support encrypted keys"); } else { *retstr = dupstr("Agent failed to re-encrypt key"); } return PAGEANT_ACTION_FAILURE; } else { *retstr = NULL; return PAGEANT_ACTION_OK; } } int pageant_reencrypt_all_keys(char **retstr) { PageantClientOp *pco = pageant_client_op_new(); put_byte(pco, SSH2_AGENTC_EXTENSION); put_stringpl(pco, extension_names[EXT_REENCRYPT_ALL]); unsigned reply = pageant_client_op_query(pco); uint32_t failures = get_uint32(pco); pageant_client_op_free(pco); if (reply != SSH_AGENT_SUCCESS) { if (reply == SSH_AGENT_FAILURE) { /* The agent didn't understand the protocol extension at all. */ *retstr = dupstr("Agent doesn't support encrypted keys"); } else { *retstr = dupstr("Agent failed to re-encrypt any keys"); } return PAGEANT_ACTION_FAILURE; } else if (failures == 1) { /* special case for English grammar */ *retstr = dupstr("1 key remains unencrypted"); return PAGEANT_ACTION_WARNING; } else if (failures > 0) { *retstr = dupprintf("%"PRIu32" keys remain unencrypted", failures); return PAGEANT_ACTION_WARNING; } else { *retstr = NULL; return PAGEANT_ACTION_OK; } } int pageant_sign(struct pageant_pubkey *key, ptrlen message, strbuf *out, uint32_t flags, char **retstr) { PageantClientOp *pco = pageant_client_op_new(); put_byte(pco, SSH2_AGENTC_SIGN_REQUEST); put_string(pco, key->blob->s, key->blob->len); put_stringpl(pco, message); put_uint32(pco, flags); unsigned reply = pageant_client_op_query(pco); ptrlen signature = get_string(pco); if (reply == SSH2_AGENT_SIGN_RESPONSE && !get_err(pco)) { *retstr = NULL; put_datapl(out, signature); pageant_client_op_free(pco); return PAGEANT_ACTION_OK; } else { *retstr = dupstr("Agent failed to create signature"); pageant_client_op_free(pco); return PAGEANT_ACTION_FAILURE; } } struct pageant_pubkey *pageant_pubkey_copy(struct pageant_pubkey *key) { struct pageant_pubkey *ret = snew(struct pageant_pubkey); ret->blob = strbuf_new(); put_data(ret->blob, key->blob->s, key->blob->len); ret->comment = key->comment ? dupstr(key->comment) : NULL; ret->ssh_version = key->ssh_version; return ret; } void pageant_pubkey_free(struct pageant_pubkey *key) { sfree(key->comment); strbuf_free(key->blob); sfree(key); } putty-0.76/pageant.h0000644000175000017500000002353014072266311011351 00000000000000/* * pageant.h: header for pageant.c. */ #include /* * Upper limit on length of any agent message. Used as a basic sanity * check on messages' length fields, and used by the Windows Pageant * client IPC to decide how large a file mapping to allocate. */ #define AGENT_MAX_MSGLEN 262144 typedef struct PageantClientVtable PageantClientVtable; typedef struct PageantClient PageantClient; typedef struct PageantClientInfo PageantClientInfo; typedef struct PageantClientRequestId PageantClientRequestId; typedef struct PageantClientDialogId PageantClientDialogId; struct PageantClient { const struct PageantClientVtable *vt; PageantClientInfo *info; /* used by the central Pageant code */ /* Setting this flag prevents the 'log' vtable entry from ever * being called, so that it's safe to make it NULL. This also * allows optimisations in the core code (it can avoid entire * loops that are only used for logging purposes). So you can also * set it dynamically if you find out at run time that you're not * doing logging. */ bool suppress_logging; }; struct PageantClientVtable { void (*log)(PageantClient *pc, PageantClientRequestId *reqid, const char *fmt, va_list ap); void (*got_response)(PageantClient *pc, PageantClientRequestId *reqid, ptrlen response); bool (*ask_passphrase)(PageantClient *pc, PageantClientDialogId *dlgid, const char *key_comment); }; static inline void pageant_client_log_v( PageantClient *pc, PageantClientRequestId *reqid, const char *fmt, va_list ap) { if (!pc->suppress_logging) pc->vt->log(pc, reqid, fmt, ap); } static inline PRINTF_LIKE(3, 4) void pageant_client_log( PageantClient *pc, PageantClientRequestId *reqid, const char *fmt, ...) { if (!pc->suppress_logging) { va_list ap; va_start(ap, fmt); pc->vt->log(pc, reqid, fmt, ap); va_end(ap); } } static inline void pageant_client_got_response( PageantClient *pc, PageantClientRequestId *reqid, ptrlen response) { pc->vt->got_response(pc, reqid, response); } static inline bool pageant_client_ask_passphrase( PageantClient *pc, PageantClientDialogId *dlgid, const char *comment) { return pc->vt->ask_passphrase(pc, dlgid, comment); } /* PageantClientRequestId is used to match up responses to the agent * requests they refer to. A client may allocate one of these for each * call to pageant_handle_request, (probably as a subfield of some * larger struct on the client side) and expect the same pointer to be * passed back in pageant_client_got_response. */ struct PageantClientRequestId { int unused_; }; /* * Initial setup. */ void pageant_init(void); /* * Register and unregister PageantClients. This is necessary so that * when a PageantClient goes away, any unfinished asynchronous * requests can be cleaned up. * * pageant_register_client will fill in pc->id. The client itself * should not touch that field. */ void pageant_register_client(PageantClient *pc); void pageant_unregister_client(PageantClient *pc); /* * The main agent function that answers messages. * * Expects a message/length pair as input, minus its initial length * field but still with its type code on the front. * * When a response is ready, the got_response method in the * PageantClient vtable will be passed it in the form of a ptrlen, * again minus its length field. */ void pageant_handle_msg(PageantClient *pc, PageantClientRequestId *reqid, ptrlen msg); /* * Send the core Pageant code a response to a passphrase request. */ void pageant_passphrase_request_success(PageantClientDialogId *dlgid, ptrlen passphrase); void pageant_passphrase_request_refused(PageantClientDialogId *dlgid); /* * Construct a list of public keys, just as the two LIST_IDENTITIES * requests would have returned them. */ void pageant_make_keylist1(BinarySink *); void pageant_make_keylist2(BinarySink *); /* * Accessor functions for Pageant's internal key lists, used by GUI * Pageant, to count the keys, to delete a key, or to re-encrypt a * decrypted-on-demand key (SSH-2 only). */ int pageant_count_ssh1_keys(void); int pageant_count_ssh2_keys(void); bool pageant_delete_nth_ssh1_key(int i); bool pageant_delete_nth_ssh2_key(int i); bool pageant_reencrypt_nth_ssh2_key(int i); void pageant_delete_all(void); void pageant_reencrypt_all(void); /* * This callback must be provided by the Pageant front end code. * pageant_handle_msg calls it to indicate that the message it's just * handled has changed the list of keys held by the agent. Front ends * which expose that key list through dedicated UI may need to refresh * that UI's state in this function; other front ends can leave it * empty. */ void keylist_update(void); /* * Functions to establish a listening socket speaking the SSH agent * protocol. Call pageant_listener_new() to set up a state; then * create a socket using the returned Plug; then call * pageant_listener_got_socket() to give the listening state its own * socket pointer. Also, provide a logging function later if you want * to. */ typedef struct PageantListenerClientVtable PageantListenerClientVtable; typedef struct PageantListenerClient PageantListenerClient; struct PageantListenerClient { const PageantListenerClientVtable *vt; /* suppress_logging flag works similarly to the one in * PageantClient, but it is only read when a new connection comes * in. So if you do need to change it in mid-run, expect existing * agent connections to still use the old value. */ bool suppress_logging; }; struct PageantListenerClientVtable { void (*log)(PageantListenerClient *, const char *fmt, va_list ap); bool (*ask_passphrase)(PageantListenerClient *pc, PageantClientDialogId *dlgid, const char *key_comment); }; static inline void pageant_listener_client_log_v( PageantListenerClient *plc, const char *fmt, va_list ap) { if (!plc->suppress_logging) plc->vt->log(plc, fmt, ap); } static inline PRINTF_LIKE(2, 3) void pageant_listener_client_log( PageantListenerClient *plc, const char *fmt, ...) { if (!plc->suppress_logging) { va_list ap; va_start(ap, fmt); plc->vt->log(plc, fmt, ap); va_end(ap); } } static inline bool pageant_listener_client_ask_passphrase( PageantListenerClient *plc, PageantClientDialogId *dlgid, const char *comment) { return plc->vt->ask_passphrase(plc, dlgid, comment); } struct pageant_listen_state; struct pageant_listen_state *pageant_listener_new( Plug **plug, PageantListenerClient *plc); void pageant_listener_got_socket(struct pageant_listen_state *pl, Socket *); void pageant_listener_free(struct pageant_listen_state *pl); /* * Functions to perform specific key actions, either as a client of an * ssh-agent running elsewhere, or directly on the agent state in this * process. (On at least one platform we want to do this in an * agnostic way between the two situations.) * * pageant_add_keyfile() is used to load a private key from a file and * add it to the agent. Initially, you should call it with passphrase * NULL, and it will check if the key is already in the agent, and * whether a passphrase is required. Return values are given in the * enum below. On return, *retstr will either be NULL, or a * dynamically allocated string containing a key comment or an error * message. * * pageant_add_keyfile() also remembers passphrases with which it's * successfully decrypted keys (because if you try to add multiple * keys in one go, you might very well have used the same passphrase * for keys that have the same trust properties). Call * pageant_forget_passphrases() to get rid of them all. */ enum { PAGEANT_ACTION_OK, /* success; no further action needed */ PAGEANT_ACTION_FAILURE, /* failure; *retstr is error message */ PAGEANT_ACTION_NEED_PP, /* need passphrase: *retstr is key comment */ PAGEANT_ACTION_WARNING, /* success but with a warning message; * *retstr is warning message */ }; int pageant_add_keyfile(Filename *filename, const char *passphrase, char **retstr, bool add_encrypted); void pageant_forget_passphrases(void); struct pageant_pubkey { /* Everything needed to identify a public key found by * pageant_enum_keys and pass it back to the agent or other code * later */ strbuf *blob; char *comment; int ssh_version; }; struct pageant_pubkey *pageant_pubkey_copy(struct pageant_pubkey *key); void pageant_pubkey_free(struct pageant_pubkey *key); typedef void (*pageant_key_enum_fn_t)(void *ctx, char **fingerprints, const char *comment, uint32_t ext_flags, struct pageant_pubkey *key); int pageant_enum_keys(pageant_key_enum_fn_t callback, void *callback_ctx, char **retstr); int pageant_delete_key(struct pageant_pubkey *key, char **retstr); int pageant_delete_all_keys(char **retstr); int pageant_reencrypt_key(struct pageant_pubkey *key, char **retstr); int pageant_reencrypt_all_keys(char **retstr); int pageant_sign(struct pageant_pubkey *key, ptrlen message, strbuf *out, uint32_t flags, char **retstr); /* * Definitions for agent protocol extensions. */ #define PUTTYEXT(base) base "@putty.projects.tartarus.org" #define KNOWN_EXTENSIONS(X) \ X(EXT_QUERY, "query") \ X(EXT_ADD_PPK, PUTTYEXT("add-ppk")) \ X(EXT_REENCRYPT, PUTTYEXT("reencrypt")) \ X(EXT_REENCRYPT_ALL, PUTTYEXT("reencrypt-all")) \ X(EXT_LIST_EXTENDED, PUTTYEXT("list-extended")) \ /* end of list */ #define LIST_EXTENDED_FLAG_HAS_ENCRYPTED_KEY_FILE 1 #define LIST_EXTENDED_FLAG_HAS_NO_CLEARTEXT_KEY 2 putty-0.76/pgssapi.c0000644000175000017500000001103014072266311011363 00000000000000/* This file actually defines the GSSAPI function pointers for * functions we plan to import from a GSSAPI library. */ #include "putty.h" #ifndef NO_GSSAPI #include "pgssapi.h" #ifndef NO_LIBDL /* Reserved static storage for GSS_oids. Comments are quotes from RFC 2744. */ static const gss_OID_desc oids[] = { /* The implementation must reserve static storage for a * gss_OID_desc object containing the value */ {10, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x01"}, /* corresponding to an object-identifier value of * {iso(1) member-body(2) United States(840) mit(113554) * infosys(1) gssapi(2) generic(1) user_name(1)}. The constant * GSS_C_NT_USER_NAME should be initialized to point * to that gss_OID_desc. * The implementation must reserve static storage for a * gss_OID_desc object containing the value */ {10, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x02"}, /* corresponding to an object-identifier value of * {iso(1) member-body(2) United States(840) mit(113554) * infosys(1) gssapi(2) generic(1) machine_uid_name(2)}. * The constant GSS_C_NT_MACHINE_UID_NAME should be * initialized to point to that gss_OID_desc. * The implementation must reserve static storage for a * gss_OID_desc object containing the value */ {10, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x03"}, /* corresponding to an object-identifier value of * {iso(1) member-body(2) United States(840) mit(113554) * infosys(1) gssapi(2) generic(1) string_uid_name(3)}. * The constant GSS_C_NT_STRING_UID_NAME should be * initialized to point to that gss_OID_desc. * * The implementation must reserve static storage for a * gss_OID_desc object containing the value */ {6, (void *)"\x2b\x06\x01\x05\x06\x02"}, /* corresponding to an object-identifier value of * {iso(1) org(3) dod(6) internet(1) security(5) * nametypes(6) gss-host-based-services(2))}. The constant * GSS_C_NT_HOSTBASED_SERVICE_X should be initialized to point * to that gss_OID_desc. This is a deprecated OID value, and * implementations wishing to support hostbased-service names * should instead use the GSS_C_NT_HOSTBASED_SERVICE OID, * defined below, to identify such names; * GSS_C_NT_HOSTBASED_SERVICE_X should be accepted a synonym * for GSS_C_NT_HOSTBASED_SERVICE when presented as an input * parameter, but should not be emitted by GSS-API * implementations * * The implementation must reserve static storage for a * gss_OID_desc object containing the value */ {10, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x01\x04"}, /* corresponding to an object-identifier value of {iso(1) * member-body(2) Unites States(840) mit(113554) infosys(1) * gssapi(2) generic(1) service_name(4)}. The constant * GSS_C_NT_HOSTBASED_SERVICE should be initialized * to point to that gss_OID_desc. * * The implementation must reserve static storage for a * gss_OID_desc object containing the value */ {6, (void *)"\x2b\x06\01\x05\x06\x03"}, /* corresponding to an object identifier value of * {1(iso), 3(org), 6(dod), 1(internet), 5(security), * 6(nametypes), 3(gss-anonymous-name)}. The constant * and GSS_C_NT_ANONYMOUS should be initialized to point * to that gss_OID_desc. * * The implementation must reserve static storage for a * gss_OID_desc object containing the value */ {6, (void *)"\x2b\x06\x01\x05\x06\x04"}, /* corresponding to an object-identifier value of * {1(iso), 3(org), 6(dod), 1(internet), 5(security), * 6(nametypes), 4(gss-api-exported-name)}. The constant * GSS_C_NT_EXPORT_NAME should be initialized to point * to that gss_OID_desc. */ }; /* Here are the constants which point to the static structure above. * * Constants of the form GSS_C_NT_* are specified by rfc 2744. */ const_gss_OID GSS_C_NT_USER_NAME = oids+0; const_gss_OID GSS_C_NT_MACHINE_UID_NAME = oids+1; const_gss_OID GSS_C_NT_STRING_UID_NAME = oids+2; const_gss_OID GSS_C_NT_HOSTBASED_SERVICE_X = oids+3; const_gss_OID GSS_C_NT_HOSTBASED_SERVICE = oids+4; const_gss_OID GSS_C_NT_ANONYMOUS = oids+5; const_gss_OID GSS_C_NT_EXPORT_NAME = oids+6; #endif /* NO_LIBDL */ static gss_OID_desc gss_mech_krb5_desc = { 9, (void *)"\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" }; /* iso(1) member-body(2) United States(840) mit(113554) infosys(1) gssapi(2) krb5(2)*/ const gss_OID GSS_MECH_KRB5 = &gss_mech_krb5_desc; #endif /* NO_GSSAPI */ putty-0.76/pgssapi.h0000644000175000017500000003221114072266311011374 00000000000000#ifndef PUTTY_PGSSAPI_H #define PUTTY_PGSSAPI_H #include "putty.h" #ifndef NO_GSSAPI /* * On Unix, if we're statically linking against GSSAPI, we leave the * declaration of all this lot to the official header. If we're * dynamically linking, we declare it ourselves, because that avoids * us needing the official header at compile time. * * However, we still need the function pointer types, because even * with statically linked GSSAPI we use the ssh_gss_library wrapper. */ #ifdef STATIC_GSSAPI #include typedef gss_OID const_gss_OID; /* for our prototypes below */ #else /* STATIC_GSSAPI */ /******************************************************************************* * GSSAPI Definitions, taken from RFC 2744 ******************************************************************************/ /* GSSAPI Type Definitions */ typedef uint32_t OM_uint32; typedef struct gss_OID_desc_struct { OM_uint32 length; void *elements; } gss_OID_desc; typedef const gss_OID_desc *const_gss_OID; typedef gss_OID_desc *gss_OID; typedef struct gss_OID_set_desc_struct { size_t count; gss_OID elements; } gss_OID_set_desc; typedef const gss_OID_set_desc *const_gss_OID_set; typedef gss_OID_set_desc *gss_OID_set; typedef struct gss_buffer_desc_struct { size_t length; void *value; } gss_buffer_desc, *gss_buffer_t; typedef struct gss_channel_bindings_struct { OM_uint32 initiator_addrtype; gss_buffer_desc initiator_address; OM_uint32 acceptor_addrtype; gss_buffer_desc acceptor_address; gss_buffer_desc application_data; } *gss_channel_bindings_t; typedef void * gss_ctx_id_t; typedef void * gss_name_t; typedef void * gss_cred_id_t; typedef OM_uint32 gss_qop_t; typedef int gss_cred_usage_t; /* Flag bits for context-level services. */ #define GSS_C_DELEG_FLAG 1 #define GSS_C_MUTUAL_FLAG 2 #define GSS_C_REPLAY_FLAG 4 #define GSS_C_SEQUENCE_FLAG 8 #define GSS_C_CONF_FLAG 16 #define GSS_C_INTEG_FLAG 32 #define GSS_C_ANON_FLAG 64 #define GSS_C_PROT_READY_FLAG 128 #define GSS_C_TRANS_FLAG 256 /* Credential usage options */ #define GSS_C_BOTH 0 #define GSS_C_INITIATE 1 #define GSS_C_ACCEPT 2 /*- * RFC 2744 Page 86 * Expiration time of 2^32-1 seconds means infinite lifetime for a * credential or security context */ #define GSS_C_INDEFINITE 0xfffffffful /* Status code types for gss_display_status */ #define GSS_C_GSS_CODE 1 #define GSS_C_MECH_CODE 2 /* The constant definitions for channel-bindings address families */ #define GSS_C_AF_UNSPEC 0 #define GSS_C_AF_LOCAL 1 #define GSS_C_AF_INET 2 #define GSS_C_AF_IMPLINK 3 #define GSS_C_AF_PUP 4 #define GSS_C_AF_CHAOS 5 #define GSS_C_AF_NS 6 #define GSS_C_AF_NBS 7 #define GSS_C_AF_ECMA 8 #define GSS_C_AF_DATAKIT 9 #define GSS_C_AF_CCITT 10 #define GSS_C_AF_SNA 11 #define GSS_C_AF_DECnet 12 #define GSS_C_AF_DLI 13 #define GSS_C_AF_LAT 14 #define GSS_C_AF_HYLINK 15 #define GSS_C_AF_APPLETALK 16 #define GSS_C_AF_BSC 17 #define GSS_C_AF_DSS 18 #define GSS_C_AF_OSI 19 #define GSS_C_AF_X25 21 #define GSS_C_AF_NULLADDR 255 /* Various Null values */ #define GSS_C_NO_NAME ((gss_name_t) 0) #define GSS_C_NO_BUFFER ((gss_buffer_t) 0) #define GSS_C_NO_OID ((gss_OID) 0) #define GSS_C_NO_OID_SET ((gss_OID_set) 0) #define GSS_C_NO_CONTEXT ((gss_ctx_id_t) 0) #define GSS_C_NO_CREDENTIAL ((gss_cred_id_t) 0) #define GSS_C_NO_CHANNEL_BINDINGS ((gss_channel_bindings_t) 0) #define GSS_C_EMPTY_BUFFER {0, NULL} /* Major status codes */ #define GSS_S_COMPLETE 0 /* Some "helper" definitions to make the status code macros obvious. */ #define GSS_C_CALLING_ERROR_OFFSET 24 #define GSS_C_ROUTINE_ERROR_OFFSET 16 #define GSS_C_SUPPLEMENTARY_OFFSET 0 #define GSS_C_CALLING_ERROR_MASK 0377ul #define GSS_C_ROUTINE_ERROR_MASK 0377ul #define GSS_C_SUPPLEMENTARY_MASK 0177777ul /* * The macros that test status codes for error conditions. * Note that the GSS_ERROR() macro has changed slightly from * the V1 GSS-API so that it now evaluates its argument * only once. */ #define GSS_CALLING_ERROR(x) \ (x & (GSS_C_CALLING_ERROR_MASK << GSS_C_CALLING_ERROR_OFFSET)) #define GSS_ROUTINE_ERROR(x) \ (x & (GSS_C_ROUTINE_ERROR_MASK << GSS_C_ROUTINE_ERROR_OFFSET)) #define GSS_SUPPLEMENTARY_INFO(x) \ (x & (GSS_C_SUPPLEMENTARY_MASK << GSS_C_SUPPLEMENTARY_OFFSET)) #define GSS_ERROR(x) \ (x & ((GSS_C_CALLING_ERROR_MASK << GSS_C_CALLING_ERROR_OFFSET) | \ (GSS_C_ROUTINE_ERROR_MASK << GSS_C_ROUTINE_ERROR_OFFSET))) /* Now the actual status code definitions */ /* Calling errors: */ #define GSS_S_CALL_INACCESSIBLE_READ \ (1ul << GSS_C_CALLING_ERROR_OFFSET) #define GSS_S_CALL_INACCESSIBLE_WRITE \ (2ul << GSS_C_CALLING_ERROR_OFFSET) #define GSS_S_CALL_BAD_STRUCTURE \ (3ul << GSS_C_CALLING_ERROR_OFFSET) /* Routine errors: */ #define GSS_S_BAD_MECH (1ul << \ GSS_C_ROUTINE_ERROR_OFFSET) #define GSS_S_BAD_NAME (2ul << \ GSS_C_ROUTINE_ERROR_OFFSET) #define GSS_S_BAD_NAMETYPE (3ul << \ GSS_C_ROUTINE_ERROR_OFFSET) #define GSS_S_BAD_BINDINGS (4ul << \ GSS_C_ROUTINE_ERROR_OFFSET) #define GSS_S_BAD_STATUS (5ul << \ GSS_C_ROUTINE_ERROR_OFFSET) #define GSS_S_BAD_SIG (6ul << \ GSS_C_ROUTINE_ERROR_OFFSET) #define GSS_S_BAD_MIC GSS_S_BAD_SIG #define GSS_S_NO_CRED (7ul << \ GSS_C_ROUTINE_ERROR_OFFSET) #define GSS_S_NO_CONTEXT (8ul << \ GSS_C_ROUTINE_ERROR_OFFSET) #define GSS_S_DEFECTIVE_TOKEN (9ul << \ GSS_C_ROUTINE_ERROR_OFFSET) #define GSS_S_DEFECTIVE_CREDENTIAL (10ul << \ GSS_C_ROUTINE_ERROR_OFFSET) #define GSS_S_CREDENTIALS_EXPIRED (11ul << \ GSS_C_ROUTINE_ERROR_OFFSET) #define GSS_S_CONTEXT_EXPIRED (12ul << \ GSS_C_ROUTINE_ERROR_OFFSET) #define GSS_S_FAILURE (13ul << \ GSS_C_ROUTINE_ERROR_OFFSET) #define GSS_S_BAD_QOP (14ul << \ GSS_C_ROUTINE_ERROR_OFFSET) #define GSS_S_UNAUTHORIZED (15ul << \ GSS_C_ROUTINE_ERROR_OFFSET) #define GSS_S_UNAVAILABLE (16ul << \ GSS_C_ROUTINE_ERROR_OFFSET) #define GSS_S_DUPLICATE_ELEMENT (17ul << \ GSS_C_ROUTINE_ERROR_OFFSET) #define GSS_S_NAME_NOT_MN (18ul << \ GSS_C_ROUTINE_ERROR_OFFSET) /* Supplementary info bits: */ #define GSS_S_CONTINUE_NEEDED \ (1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 0)) #define GSS_S_DUPLICATE_TOKEN \ (1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 1)) #define GSS_S_OLD_TOKEN \ (1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 2)) #define GSS_S_UNSEQ_TOKEN \ (1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 3)) #define GSS_S_GAP_TOKEN \ (1ul << (GSS_C_SUPPLEMENTARY_OFFSET + 4)) extern const_gss_OID GSS_C_NT_USER_NAME; extern const_gss_OID GSS_C_NT_MACHINE_UID_NAME; extern const_gss_OID GSS_C_NT_STRING_UID_NAME; extern const_gss_OID GSS_C_NT_HOSTBASED_SERVICE_X; extern const_gss_OID GSS_C_NT_HOSTBASED_SERVICE; extern const_gss_OID GSS_C_NT_ANONYMOUS; extern const_gss_OID GSS_C_NT_EXPORT_NAME; #endif /* STATIC_GSSAPI */ extern const gss_OID GSS_MECH_KRB5; /* GSSAPI functions we use. * TODO: Replace with all GSSAPI functions from RFC? */ /* Calling convention, just in case we need one. */ #ifndef GSS_CC #define GSS_CC #endif /*GSS_CC*/ typedef OM_uint32 (GSS_CC *t_gss_release_cred) (OM_uint32 * /*minor_status*/, gss_cred_id_t * /*cred_handle*/); typedef OM_uint32 (GSS_CC *t_gss_init_sec_context) (OM_uint32 * /*minor_status*/, const gss_cred_id_t /*initiator_cred_handle*/, gss_ctx_id_t * /*context_handle*/, const gss_name_t /*target_name*/, const gss_OID /*mech_type*/, OM_uint32 /*req_flags*/, OM_uint32 /*time_req*/, const gss_channel_bindings_t /*input_chan_bindings*/, const gss_buffer_t /*input_token*/, gss_OID * /*actual_mech_type*/, gss_buffer_t /*output_token*/, OM_uint32 * /*ret_flags*/, OM_uint32 * /*time_rec*/); typedef OM_uint32 (GSS_CC *t_gss_delete_sec_context) (OM_uint32 * /*minor_status*/, gss_ctx_id_t * /*context_handle*/, gss_buffer_t /*output_token*/); typedef OM_uint32 (GSS_CC *t_gss_get_mic) (OM_uint32 * /*minor_status*/, const gss_ctx_id_t /*context_handle*/, gss_qop_t /*qop_req*/, const gss_buffer_t /*message_buffer*/, gss_buffer_t /*msg_token*/); typedef OM_uint32 (GSS_CC *t_gss_verify_mic) (OM_uint32 * /*minor_status*/, const gss_ctx_id_t /*context_handle*/, const gss_buffer_t /*message_buffer*/, const gss_buffer_t /*msg_token*/, gss_qop_t * /*qop_state*/); typedef OM_uint32 (GSS_CC *t_gss_display_status) (OM_uint32 * /*minor_status*/, OM_uint32 /*status_value*/, int /*status_type*/, const gss_OID /*mech_type*/, OM_uint32 * /*message_context*/, gss_buffer_t /*status_string*/); typedef OM_uint32 (GSS_CC *t_gss_import_name) (OM_uint32 * /*minor_status*/, const gss_buffer_t /*input_name_buffer*/, const_gss_OID /*input_name_type*/, gss_name_t * /*output_name*/); typedef OM_uint32 (GSS_CC *t_gss_release_name) (OM_uint32 * /*minor_status*/, gss_name_t * /*name*/); typedef OM_uint32 (GSS_CC *t_gss_release_buffer) (OM_uint32 * /*minor_status*/, gss_buffer_t /*buffer*/); typedef OM_uint32 (GSS_CC *t_gss_acquire_cred) (OM_uint32 * /*minor_status*/, const gss_name_t /*desired_name*/, OM_uint32 /*time_req*/, const gss_OID_set /*desired_mechs*/, gss_cred_usage_t /*cred_usage*/, gss_cred_id_t * /*output_cred_handle*/, gss_OID_set * /*actual_mechs*/, OM_uint32 * /*time_rec*/); typedef OM_uint32 (GSS_CC *t_gss_inquire_cred_by_mech) (OM_uint32 * /*minor_status*/, const gss_cred_id_t /*cred_handle*/, const gss_OID /*mech_type*/, gss_name_t * /*name*/, OM_uint32 * /*initiator_lifetime*/, OM_uint32 * /*acceptor_lifetime*/, gss_cred_usage_t * /*cred_usage*/); struct gssapi_functions { t_gss_delete_sec_context delete_sec_context; t_gss_display_status display_status; t_gss_get_mic get_mic; t_gss_verify_mic verify_mic; t_gss_import_name import_name; t_gss_init_sec_context init_sec_context; t_gss_release_buffer release_buffer; t_gss_release_cred release_cred; t_gss_release_name release_name; t_gss_acquire_cred acquire_cred; t_gss_inquire_cred_by_mech inquire_cred_by_mech; }; #endif /* NO_GSSAPI */ #endif /* PUTTY_PGSSAPI_H */ putty-0.76/pinger.c0000644000175000017500000000332114072266311011205 00000000000000/* * pinger.c: centralised module that deals with sending SS_PING * keepalives, to avoid replicating this code in multiple backends. */ #include "putty.h" struct Pinger { int interval; bool pending; unsigned long when_set, next; Backend *backend; }; 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) { backend_special(pinger->backend, SS_PING, 0); pinger->pending = false; pinger_schedule(pinger); } } static void pinger_schedule(Pinger *pinger) { unsigned long next; if (!pinger->interval) { pinger->pending = false; /* cancel any pending ping */ return; } next = schedule_timer(pinger->interval * TICKSPERSEC, pinger_timer, pinger); if (!pinger->pending || (next - pinger->when_set) < (pinger->next - pinger->when_set)) { pinger->next = next; pinger->when_set = timing_last_clock(); pinger->pending = true; } } Pinger *pinger_new(Conf *conf, Backend *backend) { Pinger *pinger = snew(Pinger); pinger->interval = conf_get_int(conf, CONF_ping_interval); pinger->pending = false; pinger->backend = backend; 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); } putty-0.76/pockle.c0000644000175000017500000003416614072266311011211 00000000000000#include #include "ssh.h" #include "sshkeygen.h" #include "mpint.h" #include "mpunsafe.h" #include "tree234.h" typedef struct PocklePrimeRecord PocklePrimeRecord; struct Pockle { tree234 *tree; PocklePrimeRecord **list; size_t nlist, listsize; }; struct PocklePrimeRecord { mp_int *prime; PocklePrimeRecord **factors; size_t nfactors; mp_int *witness; size_t index; /* index in pockle->list */ }; static int ppr_cmp(void *av, void *bv) { PocklePrimeRecord *a = (PocklePrimeRecord *)av; PocklePrimeRecord *b = (PocklePrimeRecord *)bv; return mp_cmp_hs(a->prime, b->prime) - mp_cmp_hs(b->prime, a->prime); } static int ppr_find(void *av, void *bv) { mp_int *a = (mp_int *)av; PocklePrimeRecord *b = (PocklePrimeRecord *)bv; return mp_cmp_hs(a, b->prime) - mp_cmp_hs(b->prime, a); } Pockle *pockle_new(void) { Pockle *pockle = snew(Pockle); pockle->tree = newtree234(ppr_cmp); pockle->list = NULL; pockle->nlist = pockle->listsize = 0; return pockle; } void pockle_free(Pockle *pockle) { pockle_release(pockle, 0); assert(count234(pockle->tree) == 0); freetree234(pockle->tree); sfree(pockle->list); sfree(pockle); } static PockleStatus pockle_insert(Pockle *pockle, mp_int *p, mp_int **factors, size_t nfactors, mp_int *w) { PocklePrimeRecord *pr = snew(PocklePrimeRecord); pr->prime = mp_copy(p); PocklePrimeRecord *found = add234(pockle->tree, pr); if (pr != found) { /* it was already in there */ mp_free(pr->prime); sfree(pr); return POCKLE_OK; } if (w) { pr->factors = snewn(nfactors, PocklePrimeRecord *); for (size_t i = 0; i < nfactors; i++) { pr->factors[i] = find234(pockle->tree, factors[i], ppr_find); assert(pr->factors[i]); } pr->nfactors = nfactors; pr->witness = mp_copy(w); } else { pr->factors = NULL; pr->nfactors = 0; pr->witness = NULL; } pr->index = pockle->nlist; sgrowarray(pockle->list, pockle->listsize, pockle->nlist); pockle->list[pockle->nlist++] = pr; return POCKLE_OK; } size_t pockle_mark(Pockle *pockle) { return pockle->nlist; } void pockle_release(Pockle *pockle, size_t mark) { while (pockle->nlist > mark) { PocklePrimeRecord *pr = pockle->list[--pockle->nlist]; del234(pockle->tree, pr); mp_free(pr->prime); if (pr->witness) mp_free(pr->witness); sfree(pr->factors); sfree(pr); } } PockleStatus pockle_add_small_prime(Pockle *pockle, mp_int *p) { if (mp_hs_integer(p, (1ULL << 32))) return POCKLE_SMALL_PRIME_NOT_SMALL; uint32_t val = mp_get_integer(p); if (val < 2) return POCKLE_PRIME_SMALLER_THAN_2; init_smallprimes(); for (size_t i = 0; i < NSMALLPRIMES; i++) { if (val == smallprimes[i]) break; /* success */ if (val % smallprimes[i] == 0) return POCKLE_SMALL_PRIME_NOT_PRIME; } return pockle_insert(pockle, p, NULL, 0, NULL); } PockleStatus pockle_add_prime(Pockle *pockle, mp_int *p, mp_int **factors, size_t nfactors, mp_int *witness) { MontyContext *mc = NULL; mp_int *x = NULL, *f = NULL, *w = NULL; PockleStatus status; /* * We're going to try to verify that p is prime by using * Pocklington's theorem. The idea is that we're given w such that * w^{p-1} == 1 (mod p) (1) * and for a collection of primes q | p-1, * w^{(p-1)/q} - 1 is coprime to p. (2) * * Suppose r is a prime factor of p itself. Consider the * multiplicative order of w mod r. By (1), r | w^{p-1}-1. But by * (2), r does not divide w^{(p-1)/q}-1. So the order of w mod r * is a factor of p-1, but not a factor of (p-1)/q. Hence, the * largest power of q that divides p-1 must also divide ord w. * * Repeating this reasoning for all q, we find that the product of * all the q (which we'll denote f) must divide ord w, which in * turn divides r-1. So f | r-1 for any r | p. * * In particular, this means f < r. That is, all primes r | p are * bigger than f. So if f > sqrt(p), then we've shown p is prime, * because otherwise it would have to be the product of at least * two factors bigger than its own square root. * * With an extra check, we can also show p to be prime even if * we're only given enough factors to make f > cbrt(p). See below * for that part, when we come to it. */ /* * Start by checking p > 1. It certainly can't be prime otherwise! * (And since we're going to prove it prime by showing all its * prime factors are large, we do also have to know it _has_ at * least one prime factor for that to tell us anything.) */ if (!mp_hs_integer(p, 2)) return POCKLE_PRIME_SMALLER_THAN_2; /* * Check that all the factors we've been given really are primes * (in the sense that we already had them in our index). Make the * product f, and check it really does divide p-1. */ x = mp_copy(p); mp_sub_integer_into(x, x, 1); f = mp_from_integer(1); for (size_t i = 0; i < nfactors; i++) { mp_int *q = factors[i]; if (!find234(pockle->tree, q, ppr_find)) { status = POCKLE_FACTOR_NOT_KNOWN_PRIME; goto out; } mp_int *quotient = mp_new(mp_max_bits(x)); mp_int *residue = mp_new(mp_max_bits(q)); mp_divmod_into(x, q, quotient, residue); unsigned exact = mp_eq_integer(residue, 0); mp_free(residue); mp_free(x); x = quotient; if (!exact) { status = POCKLE_FACTOR_NOT_A_FACTOR; goto out; } mp_int *tmp = f; f = mp_unsafe_shrink(mp_mul(tmp, q)); mp_free(tmp); } /* * Check that f > cbrt(p). */ mp_int *f2 = mp_mul(f, f); mp_int *f3 = mp_mul(f2, f); bool too_big = mp_cmp_hs(p, f3); mp_free(f3); mp_free(f2); if (too_big) { status = POCKLE_PRODUCT_OF_FACTORS_TOO_SMALL; goto out; } /* * Now do the extra check that allows us to get away with only * having f > cbrt(p) instead of f > sqrt(p). * * If we can show that f | r-1 for any r | p, then we've ruled out * p being a product of _more_ than two primes (because then it * would be the product of at least three things bigger than its * own cube root). But we still have to rule out it being a * product of exactly two. * * Suppose for the sake of contradiction that p is the product of * two prime factors. We know both of those factors would have to * be congruent to 1 mod f. So we'd have to have * * p = (uf+1)(vf+1) = (uv)f^2 + (u+v)f + 1 (3) * * We can't have uv >= f, or else that expression would come to at * least f^3, i.e. it would exceed p. So uv < f. Hence, u,v < f as * well. * * Can we have u+v >= f? If we did, then we could write v >= f-u, * and hence f > uv >= u(f-u). That can be rearranged to show that * u^2 > (u-1)f; decrementing the LHS makes the inequality no * longer necessarily strict, so we have u^2-1 >= (u-1)f, and * dividing off u-1 gives u+1 >= f. But we know u < f, so the only * way this could happen would be if u=f-1, which makes v=1. But * _then_ (3) gives us p = (f-1)f^2 + f^2 + 1 = f^3+1. But that * can't be true if f^3 > p. So we can't have u+v >= f either, by * contradiction. * * After all that, what have we shown? We've shown that we can * write p = (uv)f^2 + (u+v)f + 1, with both uv and u+v strictly * less than f. In other words, if you write down p in base f, it * has exactly three digits, and they are uv, u+v and 1. * * But that means we can _find_ u and v: we know p and f, so we * can just extract those digits of p's base-f representation. * Once we've done so, they give the sum and product of the * potential u,v. And given the sum and product of two numbers, * you can make a quadratic which has those numbers as roots. * * We don't actually have to _solve_ the quadratic: all we have to * do is check if its discriminant is a perfect square. If not, * we'll know that no integers u,v can match this description. */ { /* We already have x = (p-1)/f. So we just need to write x in * the form aF + b, and then we have a=uv and b=u+v. */ mp_int *a = mp_new(mp_max_bits(x)); mp_int *b = mp_new(mp_max_bits(f)); mp_divmod_into(x, f, a, b); assert(!mp_cmp_hs(a, f)); assert(!mp_cmp_hs(b, f)); /* If a=0, then that means p < f^2, so we don't need to do * this check at all: the straightforward Pocklington theorem * is all we need. */ if (!mp_eq_integer(a, 0)) { unsigned perfect_square = 0; mp_int *bsq = mp_mul(b, b); mp_lshift_fixed_into(a, a, 2); if (mp_cmp_hs(bsq, a)) { /* b^2-4a is non-negative, so it might be a square. * Check it. */ mp_int *discriminant = mp_sub(bsq, a); mp_int *remainder = mp_new(mp_max_bits(discriminant)); mp_int *root = mp_nthroot(discriminant, 2, remainder); perfect_square = mp_eq_integer(remainder, 0); mp_free(discriminant); mp_free(root); mp_free(remainder); } mp_free(bsq); if (perfect_square) { mp_free(b); mp_free(a); status = POCKLE_DISCRIMINANT_IS_SQUARE; goto out; } } mp_free(b); mp_free(a); } /* * Now we've done all the checks that are cheaper than a modpow, * so we've ruled out as many things as possible before having to * do any hard work. But there's nothing for it now: make a * MontyContext. */ mc = monty_new(p); w = monty_import(mc, witness); /* * The initial Fermat check: is w^{p-1} itself congruent to 1 mod * p? */ { mp_int *pm1 = mp_copy(p); mp_sub_integer_into(pm1, pm1, 1); mp_int *power = monty_pow(mc, w, pm1); unsigned fermat_pass = mp_cmp_eq(power, monty_identity(mc)); mp_free(power); mp_free(pm1); if (!fermat_pass) { status = POCKLE_FERMAT_TEST_FAILED; goto out; } } /* * And now, for each factor q, is w^{(p-1)/q}-1 coprime to p? */ for (size_t i = 0; i < nfactors; i++) { mp_int *q = factors[i]; mp_int *exponent = mp_unsafe_shrink(mp_div(p, q)); mp_int *power = monty_pow(mc, w, exponent); mp_int *power_extracted = monty_export(mc, power); mp_sub_integer_into(power_extracted, power_extracted, 1); unsigned coprime = mp_coprime(power_extracted, p); if (!coprime) { /* * If w^{(p-1)/q}-1 is not coprime to p, the test has * failed. But it makes a difference why. If the power of * w turned out to be 1, so that we took gcd(1-1,p) = * gcd(0,p) = p, that's like an inconclusive Fermat or M-R * test: it might just mean you picked a witness integer * that wasn't a primitive root. But if the power is any * _other_ value mod p that is not coprime to p, it means * we've detected that the number is *actually not prime*! */ if (mp_eq_integer(power_extracted, 0)) status = POCKLE_WITNESS_POWER_IS_1; else status = POCKLE_WITNESS_POWER_NOT_COPRIME; } mp_free(exponent); mp_free(power); mp_free(power_extracted); if (!coprime) goto out; /* with the status we set up above */ } /* * Success! p is prime. Insert it into our tree234 of known * primes, so that future calls to this function can cite it in * evidence of larger numbers' primality. */ status = pockle_insert(pockle, p, factors, nfactors, witness); out: if (x) mp_free(x); if (f) mp_free(f); if (w) mp_free(w); if (mc) monty_free(mc); return status; } static void mp_write_decimal(strbuf *sb, mp_int *x) { char *s = mp_get_decimal(x); ptrlen pl = ptrlen_from_asciz(s); put_datapl(sb, pl); smemclr(s, pl.len); sfree(s); } strbuf *pockle_mpu(Pockle *pockle, mp_int *p) { strbuf *sb = strbuf_new_nm(); PocklePrimeRecord *pr = find234(pockle->tree, p, ppr_find); assert(pr); bool *needed = snewn(pockle->nlist, bool); memset(needed, 0, pockle->nlist * sizeof(bool)); needed[pr->index] = true; strbuf_catf(sb, "[MPU - Primality Certificate]\nVersion 1.0\nBase 10\n\n" "Proof for:\nN "); mp_write_decimal(sb, p); strbuf_catf(sb, "\n"); for (size_t index = pockle->nlist; index-- > 0 ;) { if (!needed[index]) continue; pr = pockle->list[index]; if (mp_get_nbits(pr->prime) <= 64) { strbuf_catf(sb, "\nType Small\nN "); mp_write_decimal(sb, pr->prime); strbuf_catf(sb, "\n"); } else { assert(pr->witness); strbuf_catf(sb, "\nType BLS5\nN "); mp_write_decimal(sb, pr->prime); strbuf_catf(sb, "\n"); for (size_t i = 0; i < pr->nfactors; i++) { strbuf_catf(sb, "Q[%"SIZEu"] ", i+1); mp_write_decimal(sb, pr->factors[i]->prime); assert(pr->factors[i]->index < index); needed[pr->factors[i]->index] = true; strbuf_catf(sb, "\n"); } for (size_t i = 0; i < pr->nfactors + 1; i++) { strbuf_catf(sb, "A[%"SIZEu"] ", i); mp_write_decimal(sb, pr->witness); strbuf_catf(sb, "\n"); } strbuf_catf(sb, "----\n"); } } sfree(needed); return sb; } putty-0.76/portfwd.c0000644000175000017500000010737114072266311011420 00000000000000/* * SSH port forwarding. */ #include #include #include #include "putty.h" #include "ssh.h" #include "sshchan.h" /* * Enumeration of values that live in the 'socks_state' field of * struct PortForwarding. */ typedef enum { SOCKS_NONE, /* direct connection (no SOCKS, or SOCKS already done) */ SOCKS_INITIAL, /* don't know if we're SOCKS 4 or 5 yet */ SOCKS_4, /* expect a SOCKS 4 (or 4A) connection message */ SOCKS_5_INITIAL, /* expect a SOCKS 5 preliminary message */ SOCKS_5_CONNECT /* expect a SOCKS 5 connection message */ } SocksState; typedef struct PortForwarding { SshChannel *c; /* channel structure held by SSH connection layer */ ConnectionLayer *cl; /* the connection layer itself */ /* Note that ssh need not be filled in if c is non-NULL */ Socket *s; bool input_wanted; bool ready; SocksState socks_state; /* * `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 the initial SOCKS * segment of the incoming data, plus anything after that that we * receive before we're ready to send data to the SSH server. */ strbuf *socksbuf; size_t socksbuf_consumed; Plug plug; Channel chan; } PortForwarding; struct PortListener { ConnectionLayer *cl; Socket *s; bool is_dynamic; /* * `hostname' and `port' are the real hostname and port, for * ordinary forwardings. */ char *hostname; int port; Plug plug; }; static struct PortForwarding *new_portfwd_state(void) { struct PortForwarding *pf = snew(struct PortForwarding); pf->hostname = NULL; pf->socksbuf = NULL; return pf; } static void free_portfwd_state(struct PortForwarding *pf) { if (!pf) return; sfree(pf->hostname); if (pf->socksbuf) strbuf_free(pf->socksbuf); 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, PlugLogType 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, PlugLogType 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 pfd_close(struct PortForwarding *pf); static void pfd_closing(Plug *plug, const char *error_msg, int error_code, bool calling_back) { struct PortForwarding *pf = container_of(plug, struct PortForwarding, plug); if (error_msg) { /* * Socket error. Slam the connection instantly shut. */ if (pf->c) { sshfwd_initiate_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_initiate_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); } } static void pfl_terminate(struct PortListener *pl); static void pfl_closing(Plug *plug, const char *error_msg, int error_code, bool calling_back) { struct PortListener *pl = (struct PortListener *) plug; pfl_terminate(pl); } static SshChannel *wrap_lportfwd_open( ConnectionLayer *cl, const char *hostname, int port, Socket *s, Channel *chan) { SocketPeerInfo *pi; char *description; SshChannel *toret; pi = sk_peer_info(s); if (pi && pi->log_text) { description = dupprintf("forwarding from %s", pi->log_text); } else { description = dupstr("forwarding"); } toret = ssh_lportfwd_open(cl, hostname, port, description, pi, chan); sk_free_peer_info(pi); sfree(description); return toret; } static char *ipv4_to_string(unsigned ipv4) { return dupprintf("%u.%u.%u.%u", (ipv4 >> 24) & 0xFF, (ipv4 >> 16) & 0xFF, (ipv4 >> 8) & 0xFF, (ipv4 ) & 0xFF); } static char *ipv6_to_string(ptrlen ipv6) { const unsigned char *addr = ipv6.ptr; assert(ipv6.len == 16); return dupprintf("%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x", (unsigned)GET_16BIT_MSB_FIRST(addr + 0), (unsigned)GET_16BIT_MSB_FIRST(addr + 2), (unsigned)GET_16BIT_MSB_FIRST(addr + 4), (unsigned)GET_16BIT_MSB_FIRST(addr + 6), (unsigned)GET_16BIT_MSB_FIRST(addr + 8), (unsigned)GET_16BIT_MSB_FIRST(addr + 10), (unsigned)GET_16BIT_MSB_FIRST(addr + 12), (unsigned)GET_16BIT_MSB_FIRST(addr + 14)); } static void pfd_receive(Plug *plug, int urgent, const char *data, size_t len) { struct PortForwarding *pf = container_of(plug, struct PortForwarding, plug); if (len == 0) return; if (pf->socks_state != SOCKS_NONE) { BinarySource src[1]; /* * Store all the data we've got in socksbuf. */ put_data(pf->socksbuf, data, len); /* * Check the start of socksbuf to see if it's a valid and * complete message in the SOCKS exchange. */ if (pf->socks_state == SOCKS_INITIAL) { /* Preliminary: check the first byte of the data (which we * _must_ have by now) to find out which SOCKS major * version we're speaking. */ switch (pf->socksbuf->u[0]) { case 4: pf->socks_state = SOCKS_4; break; case 5: pf->socks_state = SOCKS_5_INITIAL; break; default: pfd_close(pf); /* unrecognised version */ return; } } BinarySource_BARE_INIT(src, pf->socksbuf->u, pf->socksbuf->len); get_data(src, pf->socksbuf_consumed); while (pf->socks_state != SOCKS_NONE) { unsigned socks_version, message_type, reserved_byte; unsigned reply_code, port, ipv4, method; ptrlen methods; const char *socks4_hostname; strbuf *output; switch (pf->socks_state) { case SOCKS_INITIAL: case SOCKS_NONE: unreachable("These case values cannot appear"); case SOCKS_4: /* SOCKS 4/4A connect message */ socks_version = get_byte(src); message_type = get_byte(src); if (get_err(src) == BSE_OUT_OF_DATA) return; if (socks_version == 4 && message_type == 1) { /* CONNECT message */ bool name_based = false; port = get_uint16(src); ipv4 = get_uint32(src); if (ipv4 > 0x00000000 && ipv4 < 0x00000100) { /* * Addresses in this range indicate the SOCKS 4A * extension to specify a hostname, which comes * after the username. */ name_based = true; } get_asciz(src); /* skip username */ socks4_hostname = name_based ? get_asciz(src) : NULL; if (get_err(src) == BSE_OUT_OF_DATA) return; if (get_err(src)) goto socks4_reject; pf->port = port; if (name_based) { pf->hostname = dupstr(socks4_hostname); } else { pf->hostname = ipv4_to_string(ipv4); } output = strbuf_new(); put_byte(output, 0); /* reply version */ put_byte(output, 90); /* SOCKS 4 'request granted' */ put_uint16(output, 0); /* null port field */ put_uint32(output, 0); /* null address field */ sk_write(pf->s, output->u, output->len); strbuf_free(output); pf->socks_state = SOCKS_NONE; pf->socksbuf_consumed = src->pos; break; } socks4_reject: output = strbuf_new(); put_byte(output, 0); /* reply version */ put_byte(output, 91); /* SOCKS 4 'request rejected' */ put_uint16(output, 0); /* null port field */ put_uint32(output, 0); /* null address field */ sk_write(pf->s, output->u, output->len); strbuf_free(output); pfd_close(pf); return; case SOCKS_5_INITIAL: /* SOCKS 5 initial method list */ socks_version = get_byte(src); methods = get_pstring(src); method = 0xFF; /* means 'no usable method found' */ { int i; for (i = 0; i < methods.len; i++) { if (((const unsigned char *)methods.ptr)[i] == 0 ) { method = 0; /* no auth */ break; } } } if (get_err(src) == BSE_OUT_OF_DATA) return; if (get_err(src)) method = 0xFF; output = strbuf_new(); put_byte(output, 5); /* SOCKS version */ put_byte(output, method); /* selected auth method */ sk_write(pf->s, output->u, output->len); strbuf_free(output); if (method == 0xFF) { pfd_close(pf); return; } pf->socks_state = SOCKS_5_CONNECT; pf->socksbuf_consumed = src->pos; break; case SOCKS_5_CONNECT: /* SOCKS 5 connect message */ socks_version = get_byte(src); message_type = get_byte(src); reserved_byte = get_byte(src); if (socks_version == 5 && message_type == 1 && reserved_byte == 0) { reply_code = 0; /* success */ switch (get_byte(src)) { case 1: /* IPv4 */ pf->hostname = ipv4_to_string(get_uint32(src)); break; case 4: /* IPv6 */ pf->hostname = ipv6_to_string(get_data(src, 16)); break; case 3: /* unresolved domain name */ pf->hostname = mkstr(get_pstring(src)); break; default: pf->hostname = NULL; reply_code = 8; /* address type not supported */ break; } pf->port = get_uint16(src); } else { reply_code = 7; /* command not supported */ } if (get_err(src) == BSE_OUT_OF_DATA) return; if (get_err(src)) reply_code = 1; /* general server failure */ output = strbuf_new(); put_byte(output, 5); /* SOCKS version */ put_byte(output, reply_code); put_byte(output, 0); /* reserved */ put_byte(output, 1); /* IPv4 address follows */ put_uint32(output, 0); /* bound IPv4 address (unused) */ put_uint16(output, 0); /* bound port number (unused) */ sk_write(pf->s, output->u, output->len); strbuf_free(output); if (reply_code != 0) { pfd_close(pf); return; } pf->socks_state = SOCKS_NONE; pf->socksbuf_consumed = src->pos; break; } } /* * We come here when we're ready to make an actual * connection. */ /* * Freeze the socket until the SSH server confirms the * connection. */ sk_set_frozen(pf->s, true); pf->c = wrap_lportfwd_open(pf->cl, pf->hostname, pf->port, pf->s, &pf->chan); } if (pf->ready) sshfwd_write(pf->c, data, len); } static void pfd_sent(Plug *plug, size_t bufsize) { struct PortForwarding *pf = container_of(plug, struct PortForwarding, plug); if (pf->c) sshfwd_unthrottle(pf->c, bufsize); } static const PlugVtable PortForwarding_plugvt = { .log = pfd_log, .closing = pfd_closing, .receive = pfd_receive, .sent = pfd_sent, }; static void pfd_chan_free(Channel *chan); static void pfd_open_confirmation(Channel *chan); static void pfd_open_failure(Channel *chan, const char *errtext); static size_t pfd_send( Channel *chan, bool is_stderr, const void *data, size_t len); static void pfd_send_eof(Channel *chan); static void pfd_set_input_wanted(Channel *chan, bool wanted); static char *pfd_log_close_msg(Channel *chan); static const ChannelVtable PortForwarding_channelvt = { .free = pfd_chan_free, .open_confirmation = pfd_open_confirmation, .open_failed = pfd_open_failure, .send = pfd_send, .send_eof = pfd_send_eof, .set_input_wanted = pfd_set_input_wanted, .log_close_msg = pfd_log_close_msg, .want_close = chan_default_want_close, .rcvd_exit_status = chan_no_exit_status, .rcvd_exit_signal = chan_no_exit_signal, .rcvd_exit_signal_numeric = chan_no_exit_signal_numeric, .run_shell = chan_no_run_shell, .run_command = chan_no_run_command, .run_subsystem = chan_no_run_subsystem, .enable_x11_forwarding = chan_no_enable_x11_forwarding, .enable_agent_forwarding = chan_no_enable_agent_forwarding, .allocate_pty = chan_no_allocate_pty, .set_env = chan_no_set_env, .send_break = chan_no_send_break, .send_signal = chan_no_send_signal, .change_window_size = chan_no_change_window_size, .request_response = chan_no_request_response, }; Channel *portfwd_raw_new(ConnectionLayer *cl, Plug **plug, bool start_ready) { struct PortForwarding *pf; pf = new_portfwd_state(); pf->plug.vt = &PortForwarding_plugvt; pf->chan.initial_fixed_window_size = 0; pf->chan.vt = &PortForwarding_channelvt; pf->input_wanted = true; pf->c = NULL; pf->cl = cl; pf->input_wanted = true; pf->ready = start_ready; pf->socks_state = SOCKS_NONE; pf->hostname = NULL; pf->port = 0; *plug = &pf->plug; return &pf->chan; } void portfwd_raw_free(Channel *pfchan) { struct PortForwarding *pf; assert(pfchan->vt == &PortForwarding_channelvt); pf = container_of(pfchan, struct PortForwarding, chan); free_portfwd_state(pf); } void portfwd_raw_setup(Channel *pfchan, Socket *s, SshChannel *sc) { struct PortForwarding *pf; assert(pfchan->vt == &PortForwarding_channelvt); pf = container_of(pfchan, struct PortForwarding, chan); pf->s = s; pf->c = sc; } /* called when someone connects to the local port */ static int pfl_accepting(Plug *p, accept_fn_t constructor, accept_ctx_t ctx) { struct PortListener *pl = container_of(p, struct PortListener, plug); struct PortForwarding *pf; Channel *chan; Plug *plug; Socket *s; const char *err; chan = portfwd_raw_new(pl->cl, &plug, false); s = constructor(ctx, plug); if ((err = sk_socket_error(s)) != NULL) { portfwd_raw_free(chan); return 1; } pf = container_of(chan, struct PortForwarding, chan); if (pl->is_dynamic) { pf->s = s; pf->socks_state = SOCKS_INITIAL; pf->socksbuf = strbuf_new(); pf->socksbuf_consumed = 0; pf->port = 0; /* "hostname" buffer is so far empty */ sk_set_frozen(s, false); /* we want to receive SOCKS _now_! */ } else { pf->hostname = dupstr(pl->hostname); pf->port = pl->port; portfwd_raw_setup( chan, s, wrap_lportfwd_open(pl->cl, pf->hostname, pf->port, s, &pf->chan)); } return 0; } static const PlugVtable PortListener_plugvt = { .log = pfl_log, .closing = pfl_closing, .accepting = pfl_accepting, }; /* * Add a new port-forwarding listener from srcaddr:port -> desthost:destport. * * desthost == NULL indicates dynamic SOCKS port forwarding. * * On success, returns NULL and fills in *pl_ret. On error, returns a * dynamically allocated error message string. */ static char *pfl_listen(const char *desthost, int destport, const char *srcaddr, int port, ConnectionLayer *cl, Conf *conf, struct PortListener **pl_ret, int address_family) { const char *err; struct PortListener *pl; /* * Open socket. */ pl = *pl_ret = new_portlistener_state(); pl->plug.vt = &PortListener_plugvt; if (desthost) { pl->hostname = dupstr(desthost); pl->port = destport; pl->is_dynamic = false; } else pl->is_dynamic = true; pl->cl = cl; pl->s = new_listener(srcaddr, port, &pl->plug, !conf_get_bool(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; } static char *pfd_log_close_msg(Channel *chan) { return dupstr("Forwarded port closed"); } static void pfd_close(struct PortForwarding *pf) { if (!pf) return; sk_close(pf->s); free_portfwd_state(pf); } /* * Terminate a listener. */ static void pfl_terminate(struct PortListener *pl) { if (!pl) return; sk_close(pl->s); free_portlistener_state(pl); } static void pfd_set_input_wanted(Channel *chan, bool wanted) { assert(chan->vt == &PortForwarding_channelvt); PortForwarding *pf = container_of(chan, PortForwarding, chan); pf->input_wanted = wanted; sk_set_frozen(pf->s, !pf->input_wanted); } static void pfd_chan_free(Channel *chan) { assert(chan->vt == &PortForwarding_channelvt); PortForwarding *pf = container_of(chan, PortForwarding, chan); pfd_close(pf); } /* * Called to send data down the raw connection. */ static size_t pfd_send( Channel *chan, bool is_stderr, const void *data, size_t len) { assert(chan->vt == &PortForwarding_channelvt); PortForwarding *pf = container_of(chan, PortForwarding, chan); return sk_write(pf->s, data, len); } static void pfd_send_eof(Channel *chan) { assert(chan->vt == &PortForwarding_channelvt); PortForwarding *pf = container_of(chan, PortForwarding, chan); sk_write_eof(pf->s); } static void pfd_open_confirmation(Channel *chan) { assert(chan->vt == &PortForwarding_channelvt); PortForwarding *pf = container_of(chan, PortForwarding, chan); pf->ready = true; sk_set_frozen(pf->s, false); sk_write(pf->s, NULL, 0); if (pf->socksbuf) { sshfwd_write(pf->c, pf->socksbuf->u + pf->socksbuf_consumed, pf->socksbuf->len - pf->socksbuf_consumed); strbuf_free(pf->socksbuf); pf->socksbuf = NULL; } } static void pfd_open_failure(Channel *chan, const char *errtext) { assert(chan->vt == &PortForwarding_channelvt); PortForwarding *pf = container_of(chan, PortForwarding, chan); logeventf(pf->cl->logctx, "Forwarded connection refused by remote%s%s", errtext ? ": " : "", errtext ? errtext : ""); } /* ---------------------------------------------------------------------- * Code to manage the complete set of currently active port * forwardings, and update it from Conf. */ struct PortFwdRecord { 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; }; static int pfr_cmp(void *av, void *bv) { PortFwdRecord *a = (PortFwdRecord *) av; PortFwdRecord *b = (PortFwdRecord *) bv; int i; if (a->type > b->type) return +1; if (a->type < b->type) return -1; if (a->addressfamily > b->addressfamily) return +1; if (a->addressfamily < b->addressfamily) return -1; if ( (i = nullstrcmp(a->saddr, b->saddr)) != 0) return i < 0 ? -1 : +1; if (a->sport > b->sport) return +1; if (a->sport < b->sport) return -1; if (a->type != 'D') { if ( (i = nullstrcmp(a->daddr, b->daddr)) != 0) return i < 0 ? -1 : +1; if (a->dport > b->dport) return +1; if (a->dport < b->dport) return -1; } return 0; } static void pfr_free(PortFwdRecord *pfr) { /* Dispose of any listening socket. */ if (pfr->local) pfl_terminate(pfr->local); sfree(pfr->saddr); sfree(pfr->daddr); sfree(pfr->sserv); sfree(pfr->dserv); sfree(pfr); } struct PortFwdManager { ConnectionLayer *cl; Conf *conf; tree234 *forwardings; }; PortFwdManager *portfwdmgr_new(ConnectionLayer *cl) { PortFwdManager *mgr = snew(PortFwdManager); mgr->cl = cl; mgr->conf = NULL; mgr->forwardings = newtree234(pfr_cmp); return mgr; } void portfwdmgr_close(PortFwdManager *mgr, PortFwdRecord *pfr) { PortFwdRecord *realpfr = del234(mgr->forwardings, pfr); if (realpfr == pfr) pfr_free(pfr); } void portfwdmgr_close_all(PortFwdManager *mgr) { PortFwdRecord *pfr; while ((pfr = delpos234(mgr->forwardings, 0)) != NULL) pfr_free(pfr); } void portfwdmgr_free(PortFwdManager *mgr) { portfwdmgr_close_all(mgr); freetree234(mgr->forwardings); if (mgr->conf) conf_free(mgr->conf); sfree(mgr); } void portfwdmgr_config(PortFwdManager *mgr, Conf *conf) { PortFwdRecord *pfr; int i; char *key, *val; if (mgr->conf) conf_free(mgr->conf); mgr->conf = conf_copy(conf); /* * 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. */ for (i = 0; (pfr = index234(mgr->forwardings, i)) != NULL; i++) pfr->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 = host_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. */ char *saddr_tmp = dupprintf("%.*s", (int)(kp2 - kp), kp); saddr = host_strduptrim(saddr_tmp); sfree(saddr_tmp); 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(mgr->cl->logctx, "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 + host_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(mgr->cl->logctx, "Service lookup failed for destination" " port \"%s\"", dports); } } } if (sport && dport) { /* Set up a description of the source port. */ pfr = snew(PortFwdRecord); pfr->type = type; pfr->saddr = saddr; pfr->sserv = sserv ? dupstr(sports) : NULL; pfr->sport = sport; pfr->daddr = host; pfr->dserv = dserv ? dupstr(dports) : NULL; pfr->dport = dport; pfr->local = NULL; pfr->remote = NULL; pfr->addressfamily = (address_family == '4' ? ADDRTYPE_IPV4 : address_family == '6' ? ADDRTYPE_IPV6 : ADDRTYPE_UNSPEC); PortFwdRecord *existing = add234(mgr->forwardings, pfr); if (existing != pfr) { if (existing->status == DESTROY) { /* * We already have a port forwarding up and running * with precisely these parameters. Hence, no need * to do anything; simply re-tag the existing one * as KEEP. */ existing->status = KEEP; } /* * Anything else indicates that there was a duplicate * in our input, which we'll silently ignore. */ pfr_free(pfr); } else { pfr->status = CREATE; } } else { sfree(saddr); sfree(host); } } /* * Now go through and destroy any port forwardings which were * not re-enabled. */ for (i = 0; (pfr = index234(mgr->forwardings, i)) != NULL; i++) { if (pfr->status == DESTROY) { char *message; message = dupprintf("%s port forwarding from %s%s%d", pfr->type == 'L' ? "local" : pfr->type == 'R' ? "remote" : "dynamic", pfr->saddr ? pfr->saddr : "", pfr->saddr ? ":" : "", pfr->sport); if (pfr->type != 'D') { char *msg2 = dupprintf("%s to %s:%d", message, pfr->daddr, pfr->dport); sfree(message); message = msg2; } logeventf(mgr->cl->logctx, "Cancelling %s", message); sfree(message); /* pfr->remote or pfr->local may be NULL if setting up a * forwarding failed. */ if (pfr->remote) { /* * Cancel the port forwarding at the server * end. * * Actually closing the listening port on the server * side may fail - because in SSH-1 there's no message * in the protocol to request it! * * Instead, we simply remove the record of the * forwarding from our local end, so that any * connections the server tries to make on it are * rejected. */ ssh_rportfwd_remove(mgr->cl, pfr->remote); pfr->remote = NULL; } else if (pfr->local) { pfl_terminate(pfr->local); pfr->local = NULL; } delpos234(mgr->forwardings, i); pfr_free(pfr); i--; /* so we don't skip one in the list */ } } /* * And finally, set up any new port forwardings (status==CREATE). */ for (i = 0; (pfr = index234(mgr->forwardings, i)) != NULL; i++) { if (pfr->status == CREATE) { char *sportdesc, *dportdesc; sportdesc = dupprintf("%s%s%s%s%d%s", pfr->saddr ? pfr->saddr : "", pfr->saddr ? ":" : "", pfr->sserv ? pfr->sserv : "", pfr->sserv ? "(" : "", pfr->sport, pfr->sserv ? ")" : ""); if (pfr->type == 'D') { dportdesc = NULL; } else { dportdesc = dupprintf("%s:%s%s%d%s", pfr->daddr, pfr->dserv ? pfr->dserv : "", pfr->dserv ? "(" : "", pfr->dport, pfr->dserv ? ")" : ""); } if (pfr->type == 'L') { char *err = pfl_listen(pfr->daddr, pfr->dport, pfr->saddr, pfr->sport, mgr->cl, conf, &pfr->local, pfr->addressfamily); logeventf(mgr->cl->logctx, "Local %sport %s forwarding to %s%s%s", pfr->addressfamily == ADDRTYPE_IPV4 ? "IPv4 " : pfr->addressfamily == ADDRTYPE_IPV6 ? "IPv6 " : "", sportdesc, dportdesc, err ? " failed: " : "", err ? err : ""); if (err) sfree(err); } else if (pfr->type == 'D') { char *err = pfl_listen(NULL, -1, pfr->saddr, pfr->sport, mgr->cl, conf, &pfr->local, pfr->addressfamily); logeventf(mgr->cl->logctx, "Local %sport %s SOCKS dynamic forwarding%s%s", pfr->addressfamily == ADDRTYPE_IPV4 ? "IPv4 " : pfr->addressfamily == ADDRTYPE_IPV6 ? "IPv6 " : "", sportdesc, err ? " failed: " : "", err ? err : ""); if (err) sfree(err); } else { const char *shost; if (pfr->saddr) { shost = pfr->saddr; } else if (conf_get_bool(conf, CONF_rport_acceptall)) { shost = ""; } else { shost = "localhost"; } pfr->remote = ssh_rportfwd_alloc( mgr->cl, shost, pfr->sport, pfr->daddr, pfr->dport, pfr->addressfamily, sportdesc, pfr, NULL); if (!pfr->remote) { logeventf(mgr->cl->logctx, "Duplicate remote port forwarding to %s:%d", pfr->daddr, pfr->dport); pfr_free(pfr); } else { logeventf(mgr->cl->logctx, "Requesting remote port %s" " forward to %s", sportdesc, dportdesc); } } sfree(sportdesc); sfree(dportdesc); } } } bool portfwdmgr_listen(PortFwdManager *mgr, const char *host, int port, const char *keyhost, int keyport, Conf *conf) { PortFwdRecord *pfr; pfr = snew(PortFwdRecord); pfr->type = 'L'; pfr->saddr = host ? dupstr(host) : NULL; pfr->daddr = keyhost ? dupstr(keyhost) : NULL; pfr->sserv = pfr->dserv = NULL; pfr->sport = port; pfr->dport = keyport; pfr->local = NULL; pfr->remote = NULL; pfr->addressfamily = ADDRTYPE_UNSPEC; PortFwdRecord *existing = add234(mgr->forwardings, pfr); if (existing != pfr) { /* * We had this record already. Return failure. */ pfr_free(pfr); return false; } char *err = pfl_listen(keyhost, keyport, host, port, mgr->cl, conf, &pfr->local, pfr->addressfamily); logeventf(mgr->cl->logctx, "%s on port %s:%d to forward to client%s%s", err ? "Failed to listen" : "Listening", host, port, err ? ": " : "", err ? err : ""); if (err) { sfree(err); del234(mgr->forwardings, pfr); pfr_free(pfr); return false; } return true; } bool portfwdmgr_unlisten(PortFwdManager *mgr, const char *host, int port) { PortFwdRecord pfr_key; pfr_key.type = 'L'; /* Safe to cast the const away here, because it will only be used * by pfr_cmp, which won't write to the string */ pfr_key.saddr = pfr_key.daddr = (char *)host; pfr_key.sserv = pfr_key.dserv = NULL; pfr_key.sport = pfr_key.dport = port; pfr_key.local = NULL; pfr_key.remote = NULL; pfr_key.addressfamily = ADDRTYPE_UNSPEC; PortFwdRecord *pfr = del234(mgr->forwardings, &pfr_key); if (!pfr) return false; logeventf(mgr->cl->logctx, "Closing listening port %s:%d", host, port); pfr_free(pfr); return true; } /* * 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 *portfwdmgr_connect(PortFwdManager *mgr, Channel **chan_ret, char *hostname, int port, SshChannel *c, int addressfamily) { SockAddr *addr; const char *err; char *dummy_realhost = NULL; struct PortForwarding *pf; /* * Try to find host. */ addr = name_lookup(hostname, port, &dummy_realhost, mgr->conf, addressfamily, NULL, NULL); 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 = new_portfwd_state(); *chan_ret = &pf->chan; pf->plug.vt = &PortForwarding_plugvt; pf->chan.initial_fixed_window_size = 0; pf->chan.vt = &PortForwarding_channelvt; pf->input_wanted = true; pf->ready = true; pf->c = c; pf->cl = mgr->cl; pf->socks_state = SOCKS_NONE; pf->s = new_connection(addr, dummy_realhost, port, false, true, false, false, &pf->plug, mgr->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); *chan_ret = NULL; return err_ret; } return NULL; } putty-0.76/pproxy.c0000644000175000017500000000074714072266311011273 00000000000000/* * 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, const char *hostname, int port, int privport, int oobinline, int nodelay, int keepalive, Plug *plug, Conf *conf) { return NULL; } putty-0.76/primecandidate.c0000644000175000017500000003220714072266311012677 00000000000000/* * primecandidate.c: implementation of the PrimeCandidateSource * abstraction declared in sshkeygen.h. */ #include #include "ssh.h" #include "mpint.h" #include "mpunsafe.h" #include "sshkeygen.h" struct avoid { unsigned mod, res; }; struct PrimeCandidateSource { unsigned bits; bool ready, try_sophie_germain; bool one_shot, thrown_away_my_shot; /* We'll start by making up a random number strictly less than this ... */ mp_int *limit; /* ... then we'll multiply by 'factor', and add 'addend'. */ mp_int *factor, *addend; /* Then we'll try to add a small multiple of 'factor' to it to * avoid it being a multiple of any small prime. Also, for RSA, we * may need to avoid it being _this_ multiple of _this_: */ unsigned avoid_residue, avoid_modulus; /* Once we're actually running, this will be the complete list of * (modulus, residue) pairs we want to avoid. */ struct avoid *avoids; size_t navoids, avoidsize; /* List of known primes that our number will be congruent to 1 modulo */ mp_int **kps; size_t nkps, kpsize; }; PrimeCandidateSource *pcs_new_with_firstbits(unsigned bits, unsigned first, unsigned nfirst) { PrimeCandidateSource *s = snew(PrimeCandidateSource); assert(first >> (nfirst-1) == 1); s->bits = bits; s->ready = false; s->try_sophie_germain = false; s->one_shot = false; s->thrown_away_my_shot = false; s->kps = NULL; s->nkps = s->kpsize = 0; s->avoids = NULL; s->navoids = s->avoidsize = 0; /* Make the number that's the lower limit of our range */ mp_int *firstmp = mp_from_integer(first); mp_int *base = mp_lshift_fixed(firstmp, bits - nfirst); mp_free(firstmp); /* Set the low bit of that, because all (nontrivial) primes are odd */ mp_set_bit(base, 0, 1); /* That's our addend. Now initialise factor to 2, to ensure we * only generate odd numbers */ s->factor = mp_from_integer(2); s->addend = base; /* And that means the limit of our random numbers must be one * factor of two _less_ than the position of the low bit of * 'first', because we'll be multiplying the random number by * 2 immediately afterwards. */ s->limit = mp_power_2(bits - nfirst - 1); /* avoid_modulus == 0 signals that there's no extra residue to avoid */ s->avoid_residue = 1; s->avoid_modulus = 0; return s; } PrimeCandidateSource *pcs_new(unsigned bits) { return pcs_new_with_firstbits(bits, 1, 1); } void pcs_free(PrimeCandidateSource *s) { mp_free(s->limit); mp_free(s->factor); mp_free(s->addend); for (size_t i = 0; i < s->nkps; i++) mp_free(s->kps[i]); sfree(s->avoids); sfree(s->kps); sfree(s); } void pcs_try_sophie_germain(PrimeCandidateSource *s) { s->try_sophie_germain = true; } void pcs_set_oneshot(PrimeCandidateSource *s) { s->one_shot = true; } static void pcs_require_residue_inner(PrimeCandidateSource *s, mp_int *mod, mp_int *res) { /* * We already have a factor and addend. Ensure this one doesn't * contradict it. */ mp_int *gcd = mp_gcd(mod, s->factor); mp_int *test1 = mp_mod(s->addend, gcd); mp_int *test2 = mp_mod(res, gcd); assert(mp_cmp_eq(test1, test2)); mp_free(test1); mp_free(test2); /* * Reduce our input factor and addend, which are constraints on * the ultimate output number, so that they're constraints on the * initial cofactor we're going to make up. * * If we're generating x and we want to ensure ax+b == r (mod m), * how does that work? We've already checked that b == r modulo g * = gcd(a,m), i.e. r-b is a multiple of g, and so are a and m. So * let's write a=gA, m=gM, (r-b)=gR, and then we can start by * dividing that off: * * ax == r-b (mod m ) * => gAx == gR (mod gM) * => Ax == R (mod M) * * Now the moduli A,M are coprime, which makes things easier. * * We're going to need to generate the x in this equation by * generating a new smaller value y, multiplying it by M, and * adding some constant K. So we have x = My + K, and we need to * work out what K will satisfy the above equation. In other * words, we need A(My+K) == R (mod M), and the AMy term vanishes, * so we just need AK == R (mod M). So our congruence is solved by * setting K to be R * A^{-1} mod M. */ mp_int *A = mp_div(s->factor, gcd); mp_int *M = mp_div(mod, gcd); mp_int *Rpre = mp_modsub(res, s->addend, mod); mp_int *R = mp_div(Rpre, gcd); mp_int *Ainv = mp_invert(A, M); mp_int *K = mp_modmul(R, Ainv, M); mp_free(gcd); mp_free(Rpre); mp_free(Ainv); mp_free(A); mp_free(R); /* * So we know we have to transform our existing (factor, addend) * pair into (factor * M, addend * factor * K). Now we just need * to work out what the limit should be on the random value we're * generating. * * If we need My+K < old_limit, then y < (old_limit-K)/M. But the * RHS is a fraction, so in integers, we need y < ceil of it. */ assert(!mp_cmp_hs(K, s->limit)); mp_int *dividend = mp_add(s->limit, M); mp_sub_integer_into(dividend, dividend, 1); mp_sub_into(dividend, dividend, K); mp_free(s->limit); s->limit = mp_div(dividend, M); mp_free(dividend); /* * Now just update the real factor and addend, and we're done. */ mp_int *addend_old = s->addend; mp_int *tmp = mp_mul(s->factor, K); /* use the _old_ value of factor */ s->addend = mp_add(s->addend, tmp); mp_free(tmp); mp_free(addend_old); mp_int *factor_old = s->factor; s->factor = mp_mul(s->factor, M); mp_free(factor_old); mp_free(M); mp_free(K); s->factor = mp_unsafe_shrink(s->factor); s->addend = mp_unsafe_shrink(s->addend); s->limit = mp_unsafe_shrink(s->limit); } void pcs_require_residue(PrimeCandidateSource *s, mp_int *mod, mp_int *res_orig) { /* * Reduce the input residue to its least non-negative value, in * case it was given as a larger equivalent value. */ mp_int *res_reduced = mp_mod(res_orig, mod); pcs_require_residue_inner(s, mod, res_reduced); mp_free(res_reduced); } void pcs_require_residue_1(PrimeCandidateSource *s, mp_int *mod) { mp_int *res = mp_from_integer(1); pcs_require_residue(s, mod, res); mp_free(res); } void pcs_require_residue_1_mod_prime(PrimeCandidateSource *s, mp_int *mod) { pcs_require_residue_1(s, mod); sgrowarray(s->kps, s->kpsize, s->nkps); s->kps[s->nkps++] = mp_copy(mod); } void pcs_avoid_residue_small(PrimeCandidateSource *s, unsigned mod, unsigned res) { assert(!s->avoid_modulus); /* can't cope with more than one */ s->avoid_modulus = mod; s->avoid_residue = res % mod; /* reduce, just in case */ } static int avoid_cmp(const void *av, const void *bv) { const struct avoid *a = (const struct avoid *)av; const struct avoid *b = (const struct avoid *)bv; return a->mod < b->mod ? -1 : a->mod > b->mod ? +1 : 0; } static uint64_t invert(uint64_t a, uint64_t m) { int64_t v0 = a, i0 = 1; int64_t v1 = m, i1 = 0; while (v0) { int64_t tmp, q = v1 / v0; tmp = v0; v0 = v1 - q*v0; v1 = tmp; tmp = i0; i0 = i1 - q*i0; i1 = tmp; } assert(v1 == 1 || v1 == -1); return i1 * v1; } void pcs_ready(PrimeCandidateSource *s) { /* * List all the small (modulus, residue) pairs we want to avoid. */ init_smallprimes(); #define ADD_AVOID(newmod, newres) do { \ sgrowarray(s->avoids, s->avoidsize, s->navoids); \ s->avoids[s->navoids].mod = (newmod); \ s->avoids[s->navoids].res = (newres); \ s->navoids++; \ } while (0) unsigned limit = (mp_hs_integer(s->addend, 65536) ? 65536 : mp_get_integer(s->addend)); /* * Don't be divisible by any small prime, or at least, any prime * smaller than our output number might actually manage to be. (If * asked to generate a really small prime, it would be * embarrassing to rule out legitimate answers on the grounds that * they were divisible by themselves.) */ for (size_t i = 0; i < NSMALLPRIMES && smallprimes[i] < limit; i++) ADD_AVOID(smallprimes[i], 0); if (s->try_sophie_germain) { /* * If we're aiming to generate a Sophie Germain prime (i.e. p * such that 2p+1 is also prime), then we also want to ensure * 2p+1 is not congruent to 0 mod any small prime, because if * it is, we'll waste a lot of time generating a p for which * 2p+1 can't possibly work. So we have to avoid an extra * residue mod each odd q. * * We can simplify: 2p+1 == 0 (mod q) * => 2p == -1 (mod q) * => p == -2^{-1} (mod q) * * There's no need to do Euclid's algorithm to compute those * inverses, because for any odd q, the modular inverse of -2 * mod q is just (q-1)/2. (Proof: multiplying it by -2 gives * 1-q, which is congruent to 1 mod q.) */ for (size_t i = 0; i < NSMALLPRIMES && smallprimes[i] < limit; i++) if (smallprimes[i] != 2) ADD_AVOID(smallprimes[i], (smallprimes[i] - 1) / 2); } /* * Finally, if there's a particular modulus and residue we've been * told to avoid, put it on the list. */ if (s->avoid_modulus) ADD_AVOID(s->avoid_modulus, s->avoid_residue); #undef ADD_AVOID /* * Sort our to-avoid list by modulus. Partly this is so that we'll * check the smaller moduli first during the live runs, which lets * us spot most failing cases earlier rather than later. Also, it * brings equal moduli together, so that we can reuse the residue * we computed from a previous one. */ qsort(s->avoids, s->navoids, sizeof(*s->avoids), avoid_cmp); /* * Next, adjust each of these moduli to take account of our factor * and addend. If we want factor*x+addend to avoid being congruent * to 'res' modulo 'mod', then x itself must avoid being congruent * to (res - addend) * factor^{-1}. * * If factor == 0 modulo mod, then the answer will have a fixed * residue anyway, so we can discard it from our list to test. */ int64_t factor_m = 0, addend_m = 0, last_mod = 0; size_t out = 0; for (size_t i = 0; i < s->navoids; i++) { int64_t mod = s->avoids[i].mod, res = s->avoids[i].res; if (mod != last_mod) { last_mod = mod; addend_m = mp_unsafe_mod_integer(s->addend, mod); factor_m = mp_unsafe_mod_integer(s->factor, mod); } if (factor_m == 0) { assert(res != addend_m); continue; } res = (res - addend_m) * invert(factor_m, mod); res %= mod; if (res < 0) res += mod; s->avoids[out].mod = mod; s->avoids[out].res = res; out++; } s->navoids = out; s->ready = true; } mp_int *pcs_generate(PrimeCandidateSource *s) { assert(s->ready); if (s->one_shot) { if (s->thrown_away_my_shot) return NULL; s->thrown_away_my_shot = true; } while (true) { mp_int *x = mp_random_upto(s->limit); int64_t x_res = 0, last_mod = 0; bool ok = true; for (size_t i = 0; i < s->navoids; i++) { int64_t mod = s->avoids[i].mod, avoid_res = s->avoids[i].res; if (mod != last_mod) { last_mod = mod; x_res = mp_unsafe_mod_integer(x, mod); } if (x_res == avoid_res) { ok = false; break; } } if (!ok) { mp_free(x); continue; /* try a new x */ } /* * We've found a viable x. Make the final output value. */ mp_int *toret = mp_new(s->bits); mp_mul_into(toret, x, s->factor); mp_add_into(toret, toret, s->addend); mp_free(x); return toret; } } void pcs_inspect(PrimeCandidateSource *pcs, mp_int **limit_out, mp_int **factor_out, mp_int **addend_out) { *limit_out = mp_copy(pcs->limit); *factor_out = mp_copy(pcs->factor); *addend_out = mp_copy(pcs->addend); } unsigned pcs_get_bits(PrimeCandidateSource *pcs) { return pcs->bits; } unsigned pcs_get_bits_remaining(PrimeCandidateSource *pcs) { return mp_get_nbits(pcs->limit); } mp_int *pcs_get_upper_bound(PrimeCandidateSource *pcs) { /* Compute (limit-1) * factor + addend */ mp_int *tmp = mp_mul(pcs->limit, pcs->factor); mp_int *bound = mp_add(tmp, pcs->addend); mp_free(tmp); mp_sub_into(bound, bound, pcs->factor); return bound; } mp_int **pcs_get_known_prime_factors(PrimeCandidateSource *pcs, size_t *nout) { *nout = pcs->nkps; return pcs->kps; } putty-0.76/proxy.c0000644000175000017500000014507114072266311011113 00000000000000/* * Network proxy abstraction in PuTTY * * A proxy layer, if necessary, wedges itself between the network * code and the higher level backend. */ #include #include #include #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 (ProxySocket *p) { size_t output_before, output_after; p->state = PROXY_STATE_ACTIVE; /* we want to ignore new receive events until we have sent * all of our buffered receive data. */ sk_set_frozen(p->sub_socket, true); /* how many bytes of output have we buffered? */ output_before = bufchain_size(&p->pending_oob_output_data) + bufchain_size(&p->pending_output_data); /* and keep track of how many bytes do not get sent. */ output_after = 0; /* send buffered OOB writes */ while (bufchain_size(&p->pending_oob_output_data) > 0) { ptrlen data = bufchain_prefix(&p->pending_oob_output_data); output_after += sk_write_oob(p->sub_socket, data.ptr, data.len); bufchain_consume(&p->pending_oob_output_data, data.len); } /* send buffered normal writes */ while (bufchain_size(&p->pending_output_data) > 0) { ptrlen data = bufchain_prefix(&p->pending_output_data); output_after += sk_write(p->sub_socket, data.ptr, data.len); bufchain_consume(&p->pending_output_data, data.len); } /* if we managed to send any data, let the higher levels know. */ if (output_after < output_before) plug_sent(p->plug, output_after); /* 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(&p->sock, false); } /* basic proxy socket functions */ static Plug *sk_proxy_plug (Socket *s, Plug *p) { ProxySocket *ps = container_of(s, ProxySocket, sock); Plug *ret = ps->plug; if (p) ps->plug = p; return ret; } static void sk_proxy_close (Socket *s) { ProxySocket *ps = container_of(s, ProxySocket, sock); sk_close(ps->sub_socket); sk_addr_free(ps->remote_addr); sfree(ps); } static size_t sk_proxy_write (Socket *s, const void *data, size_t len) { ProxySocket *ps = container_of(s, ProxySocket, sock); if (ps->state != PROXY_STATE_ACTIVE) { bufchain_add(&ps->pending_output_data, data, len); return bufchain_size(&ps->pending_output_data); } return sk_write(ps->sub_socket, data, len); } static size_t sk_proxy_write_oob (Socket *s, const void *data, size_t len) { ProxySocket *ps = container_of(s, ProxySocket, sock); if (ps->state != PROXY_STATE_ACTIVE) { 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) { ProxySocket *ps = container_of(s, ProxySocket, sock); if (ps->state != PROXY_STATE_ACTIVE) { ps->pending_eof = true; return; } sk_write_eof(ps->sub_socket); } static void sk_proxy_set_frozen (Socket *s, bool is_frozen) { ProxySocket *ps = container_of(s, ProxySocket, sock); if (ps->state != PROXY_STATE_ACTIVE) { ps->freeze = is_frozen; return; } /* handle any remaining buffered recv data first */ if (bufchain_size(&ps->pending_input_data) > 0) { ps->freeze = is_frozen; /* loop while we still have buffered data, and while we are * unfrozen. the plug_receive call in the loop could result * in a call back into this function refreezing the socket, * so we have to check each time. */ while (!ps->freeze && bufchain_size(&ps->pending_input_data) > 0) { char databuf[512]; ptrlen data = bufchain_prefix(&ps->pending_input_data); if (data.len > lenof(databuf)) data.len = lenof(databuf); memcpy(databuf, data.ptr, data.len); bufchain_consume(&ps->pending_input_data, data.len); plug_receive(ps->plug, 0, databuf, data.len); } /* if we're still frozen, we'll have to wait for another * call from the backend to finish unbuffering the data. */ if (ps->freeze) return; } sk_set_frozen(ps->sub_socket, is_frozen); } static const char * sk_proxy_socket_error (Socket *s) { ProxySocket *ps = container_of(s, ProxySocket, sock); if (ps->error != NULL || ps->sub_socket == NULL) { return ps->error; } return sk_socket_error(ps->sub_socket); } /* basic proxy plug functions */ static void plug_proxy_log(Plug *plug, PlugLogType type, SockAddr *addr, int port, const char *error_msg, int error_code) { ProxySocket *ps = container_of(plug, ProxySocket, plugimpl); plug_log(ps->plug, type, addr, port, error_msg, error_code); } static void plug_proxy_closing (Plug *p, const char *error_msg, int error_code, bool calling_back) { ProxySocket *ps = container_of(p, ProxySocket, plugimpl); if (ps->state != PROXY_STATE_ACTIVE) { ps->closing_error_msg = error_msg; ps->closing_error_code = error_code; ps->closing_calling_back = calling_back; ps->negotiate(ps, PROXY_CHANGE_CLOSING); } else { plug_closing(ps->plug, error_msg, error_code, calling_back); } } static void plug_proxy_receive( Plug *p, int urgent, const char *data, size_t len) { ProxySocket *ps = container_of(p, ProxySocket, plugimpl); if (ps->state != PROXY_STATE_ACTIVE) { /* we will lose the urgentness of this data, but since most, * if not all, of this data will be consumed by the negotiation * process, hopefully it won't affect the protocol above us */ bufchain_add(&ps->pending_input_data, data, len); ps->receive_urgent = (urgent != 0); ps->receive_data = data; ps->receive_len = len; ps->negotiate(ps, PROXY_CHANGE_RECEIVE); } else { plug_receive(ps->plug, urgent, data, len); } } static void plug_proxy_sent (Plug *p, size_t bufsize) { ProxySocket *ps = container_of(p, ProxySocket, plugimpl); if (ps->state != PROXY_STATE_ACTIVE) { 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) { ProxySocket *ps = container_of(p, ProxySocket, plugimpl); 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. */ static bool 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 false; /* do not proxy */ /* * Check the host name and IP against the hard-coded * representations of `localhost'. */ if (!conf_get_bool(conf, CONF_even_proxy_localhost) && (sk_hostname_is_local(hostname) || (addr && sk_address_is_local(addr)))) return false; /* 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] && (isspace((unsigned char)exclude_list[s]) || exclude_list[s] == ',')) s++; if (!exclude_list[s]) break; e = s; while (exclude_list[e] && (isalnum((unsigned char)exclude_list[e]) || exclude_list[e] == '-' || exclude_list[e] == '.' || exclude_list[e] == '*')) e++; if (exclude_list[s] == '*') { /* wildcard at beginning of entry */ if ((addr && strnicmp(hostip + hostip_len - (e - s - 1), exclude_list + s + 1, e - s - 1) == 0) || strnicmp(hostname + hostname_len - (e - s - 1), exclude_list + s + 1, e - s - 1) == 0) { /* IP/hostname range excluded. do not use proxy. */ return false; } } else if (exclude_list[e-1] == '*') { /* wildcard at end of entry */ if ((addr && strnicmp(hostip, exclude_list + s, e - s - 1) == 0) || strnicmp(hostname, exclude_list + s, e - s - 1) == 0) { /* IP/hostname range excluded. do not use proxy. */ return false; } } else { /* no wildcard at either end, so let's try an absolute * match (ie. a specific IP) */ if (addr && strnicmp(hostip, exclude_list + s, e - s) == 0) return false; /* IP/hostname excluded. do not use proxy. */ if (strnicmp(hostname, exclude_list + s, e - s) == 0) return false; /* IP/hostname excluded. do not use proxy. */ } s = e; /* Make sure we really have reached the next comma or end-of-string */ while (exclude_list[s] && !isspace((unsigned char)exclude_list[s]) && exclude_list[s] != ',') s++; } /* no matches in the exclude list, so use the proxy */ return true; } static char *dns_log_msg(const char *host, int addressfamily, const char *reason) { return dupprintf("Looking up host \"%s\"%s for %s", host, (addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" : addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" : ""), reason); } SockAddr *name_lookup(const char *host, int port, char **canonicalname, Conf *conf, int addressfamily, LogContext *logctx, const char *reason) { if (conf_get_int(conf, CONF_proxy_type) != PROXY_NONE && do_proxy_dns(conf) && proxy_for_destination(NULL, host, port, conf)) { if (logctx) logeventf(logctx, "Leaving host lookup to proxy of \"%s\"" " (for %s)", host, reason); *canonicalname = dupstr(host); return sk_nonamelookup(host); } else { if (logctx) logevent_and_free( logctx, dns_log_msg(host, addressfamily, reason)); return sk_namelookup(host, canonicalname, addressfamily); } } static const SocketVtable ProxySocket_sockvt = { .plug = sk_proxy_plug, .close = sk_proxy_close, .write = sk_proxy_write, .write_oob = sk_proxy_write_oob, .write_eof = sk_proxy_write_eof, .set_frozen = sk_proxy_set_frozen, .socket_error = sk_proxy_socket_error, .peer_info = NULL, }; static const PlugVtable ProxySocket_plugvt = { .log = plug_proxy_log, .closing = plug_proxy_closing, .receive = plug_proxy_receive, .sent = plug_proxy_sent, .accepting = plug_proxy_accepting }; Socket *new_connection(SockAddr *addr, const char *hostname, int port, bool privport, bool oobinline, bool nodelay, bool keepalive, Plug *plug, Conf *conf) { if (conf_get_int(conf, CONF_proxy_type) != PROXY_NONE && proxy_for_destination(addr, hostname, port, conf)) { ProxySocket *ret; SockAddr *proxy_addr; char *proxy_canonical_name; const char *proxy_type; Socket *sret; int type; if ((sret = platform_new_connection(addr, hostname, port, privport, oobinline, nodelay, keepalive, plug, conf)) != NULL) return sret; ret = snew(ProxySocket); ret->sock.vt = &ProxySocket_sockvt; ret->plugimpl.vt = &ProxySocket_plugvt; 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_eof = false; ret->freeze = false; 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; proxy_type = "HTTP"; } else if (type == PROXY_SOCKS4) { ret->negotiate = proxy_socks4_negotiate; proxy_type = "SOCKS 4"; } else if (type == PROXY_SOCKS5) { ret->negotiate = proxy_socks5_negotiate; proxy_type = "SOCKS 5"; } else if (type == PROXY_TELNET) { ret->negotiate = proxy_telnet_negotiate; proxy_type = "Telnet"; } else { ret->error = "Proxy error: Unknown proxy method"; return &ret->sock; } { char *logmsg = dupprintf("Will use %s proxy at %s:%d to connect" " to %s:%d", proxy_type, conf_get_str(conf, CONF_proxy_host), conf_get_int(conf, CONF_proxy_port), hostname, port); plug_log(plug, PLUGLOG_PROXY_MSG, NULL, 0, logmsg, 0); sfree(logmsg); } { char *logmsg = dns_log_msg(conf_get_str(conf, CONF_proxy_host), conf_get_int(conf, CONF_addressfamily), "proxy"); plug_log(plug, PLUGLOG_PROXY_MSG, NULL, 0, logmsg, 0); sfree(logmsg); } /* 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"; sk_addr_free(proxy_addr); return &ret->sock; } sfree(proxy_canonical_name); { char addrbuf[256], *logmsg; sk_getaddr(proxy_addr, addrbuf, lenof(addrbuf)); logmsg = dupprintf("Connecting to %s proxy at %s port %d", proxy_type, addrbuf, conf_get_int(conf, CONF_proxy_port)); plug_log(plug, PLUGLOG_PROXY_MSG, NULL, 0, logmsg, 0); sfree(logmsg); } /* 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, &ret->plugimpl); if (sk_socket_error(ret->sub_socket) != NULL) return &ret->sock; /* start the proxy negotiation process... */ sk_set_frozen(ret->sub_socket, false); ret->negotiate(ret, PROXY_CHANGE_NEW); return &ret->sock; } /* no proxy, so just return the direct socket */ return sk_new(addr, port, privport, oobinline, nodelay, keepalive, plug); } Socket *new_listener(const char *srcaddr, int port, Plug *plug, bool 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); } /* ---------------------------------------------------------------------- * HTTP CONNECT proxy type. */ static bool get_line_end(char *data, size_t len, size_t *out) { size_t off = 0; while (off < len) { if (data[off] == '\n') { /* we have a newline */ off++; /* is that the only thing on this line? */ if (off <= 2) { *out = off; return true; } /* if not, then there is the possibility that this header * continues onto the next line, if it starts with a space * or a tab. */ if (off + 1 < len && data[off+1] != ' ' && data[off+1] != '\t') { *out = off; return true; } /* the line does continue, so we have to keep going * until we see an the header's "real" end of line. */ off++; } off++; } return false; } int proxy_http_negotiate (ProxySocket *p, int change) { 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; } if (change == PROXY_CHANGE_CLOSING) { /* if our proxy negotiation process involves closing and opening * new sockets, then we would want to intercept this closing * callback when we were expecting it. if we aren't anticipating * a socket close, then some error must have occurred. we'll * just pass those errors up to the backend. */ plug_closing(p->plug, p->closing_error_msg, p->closing_error_code, p->closing_calling_back); return 0; /* ignored */ } if (change == PROXY_CHANGE_SENT) { /* some (or all) of what we wrote to the proxy was sent. * we don't do anything new, however, until we receive the * proxy's response. we might want to set a timer so we can * timeout the proxy negotiation after a while... */ return 0; } 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. */ char *data, *datap; size_t len, eol; if (p->state == 1) { int min_ver, maj_ver, status; /* get the status line */ len = bufchain_size(&p->pending_input_data); assert(len > 0); /* or we wouldn't be here */ data = snewn(len+1, char); bufchain_fetch(&p->pending_input_data, data, len); /* * We must NUL-terminate this data, because Windows * sscanf appears to require a NUL at the end of the * string because it strlens it _first_. Sigh. */ data[len] = '\0'; if (!get_line_end(data, len, &eol)) { sfree(data); return 1; } status = -1; /* We can't rely on whether the %n incremented the sscanf return */ if (sscanf((char *)data, "HTTP/%i.%i %n", &maj_ver, &min_ver, &status) < 2 || status == -1) { plug_closing(p->plug, "Proxy error: HTTP response was absent", PROXY_ERROR_GENERAL, 0); sfree(data); return 1; } /* remove the status line from the input buffer. */ bufchain_consume(&p->pending_input_data, eol); if (data[status] != '2') { /* error */ char *buf; data[eol] = '\0'; while (eol > status && (data[eol-1] == '\r' || data[eol-1] == '\n')) data[--eol] = '\0'; buf = dupprintf("Proxy error: %s", data+status); plug_closing(p->plug, buf, PROXY_ERROR_GENERAL, 0); sfree(buf); sfree(data); return 1; } sfree(data); p->state = 2; } if (p->state == 2) { /* get headers. we're done when we get a * header of length 2, (ie. just "\r\n") */ len = bufchain_size(&p->pending_input_data); assert(len > 0); /* or we wouldn't be here */ data = snewn(len, char); datap = data; bufchain_fetch(&p->pending_input_data, data, len); if (!get_line_end(datap, len, &eol)) { sfree(data); return 1; } while (eol > 2) { bufchain_consume(&p->pending_input_data, eol); datap += eol; len -= eol; if (!get_line_end(datap, len, &eol)) eol = 0; /* terminate the loop */ } if (eol == 2) { /* we're done */ bufchain_consume(&p->pending_input_data, 2); proxy_activate(p); /* proxy activate will have dealt with * whatever is left of the buffer */ sfree(data); return 1; } sfree(data); return 1; } } plug_closing(p->plug, "Proxy error: unexpected proxy error", PROXY_ERROR_UNEXPECTED, 0); return 1; } /* ---------------------------------------------------------------------- * SOCKS proxy type. */ /* SOCKS version 4 */ int proxy_socks4_negotiate (ProxySocket *p, int change) { if (p->state == PROXY_CHANGE_NEW) { /* request format: * version number (1 byte) = 4 * command code (1 byte) * 1 = CONNECT * 2 = BIND * dest. port (2 bytes) [network order] * dest. address (4 bytes) * user ID (variable length, null terminated string) */ strbuf *command = strbuf_new(); char hostname[512]; bool write_hostname = false; put_byte(command, 4); /* SOCKS version 4 */ put_byte(command, 1); /* CONNECT command */ put_uint16(command, p->remote_port); switch (sk_addrtype(p->remote_addr)) { case ADDRTYPE_IPV4: { char addr[4]; sk_addrcopy(p->remote_addr, addr); put_data(command, addr, 4); break; } case ADDRTYPE_NAME: sk_getaddr(p->remote_addr, hostname, lenof(hostname)); put_uint32(command, 1); write_hostname = true; break; case ADDRTYPE_IPV6: p->error = "Proxy error: SOCKS version 4 does not support IPv6"; strbuf_free(command); return 1; } put_asciz(command, conf_get_str(p->conf, CONF_proxy_username)); if (write_hostname) put_asciz(command, hostname); sk_write(p->sub_socket, command->s, command->len); strbuf_free(command); p->state = 1; return 0; } if (change == PROXY_CHANGE_CLOSING) { /* if our proxy negotiation process involves closing and opening * new sockets, then we would want to intercept this closing * callback when we were expecting it. if we aren't anticipating * a socket close, then some error must have occurred. we'll * just pass those errors up to the backend. */ plug_closing(p->plug, p->closing_error_msg, p->closing_error_code, p->closing_calling_back); return 0; /* ignored */ } if (change == PROXY_CHANGE_SENT) { /* some (or all) of what we wrote to the proxy was sent. * we don't do anything new, however, until we receive the * proxy's response. we might want to set a timer so we can * timeout the proxy negotiation after a while... */ return 0; } 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. */ if (p->state == 1) { /* response format: * version number (1 byte) = 4 * reply code (1 byte) * 90 = request granted * 91 = request rejected or failed * 92 = request rejected due to lack of IDENTD on client * 93 = request rejected due to difference in user ID * (what we sent vs. what IDENTD said) * dest. port (2 bytes) * dest. address (4 bytes) */ char data[8]; if (bufchain_size(&p->pending_input_data) < 8) return 1; /* not got anything yet */ /* get the response */ bufchain_fetch(&p->pending_input_data, data, 8); if (data[0] != 0) { plug_closing(p->plug, "Proxy error: SOCKS proxy responded with " "unexpected reply code version", PROXY_ERROR_GENERAL, 0); return 1; } if (data[1] != 90) { switch (data[1]) { case 92: plug_closing(p->plug, "Proxy error: SOCKS server wanted IDENTD on client", PROXY_ERROR_GENERAL, 0); break; case 93: plug_closing(p->plug, "Proxy error: Username and IDENTD on client don't agree", PROXY_ERROR_GENERAL, 0); break; case 91: default: plug_closing(p->plug, "Proxy error: Error while communicating with proxy", PROXY_ERROR_GENERAL, 0); break; } return 1; } bufchain_consume(&p->pending_input_data, 8); /* we're done */ proxy_activate(p); /* proxy activate will have dealt with * whatever is left of the buffer */ return 1; } } plug_closing(p->plug, "Proxy error: unexpected proxy error", PROXY_ERROR_UNEXPECTED, 0); return 1; } /* SOCKS version 5 */ int proxy_socks5_negotiate (ProxySocket *p, int change) { if (p->state == PROXY_CHANGE_NEW) { /* initial command: * version number (1 byte) = 5 * number of available authentication methods (1 byte) * available authentication methods (1 byte * previous value) * authentication methods: * 0x00 = no authentication * 0x01 = GSSAPI * 0x02 = username/password * 0x03 = CHAP */ strbuf *command; char *username, *password; int method_count_offset, methods_start; command = strbuf_new(); put_byte(command, 5); /* SOCKS version 5 */ username = conf_get_str(p->conf, CONF_proxy_username); password = conf_get_str(p->conf, CONF_proxy_password); method_count_offset = command->len; put_byte(command, 0); methods_start = command->len; put_byte(command, 0x00); /* no authentication */ if (username[0] || password[0]) { proxy_socks5_offerencryptedauth(BinarySink_UPCAST(command)); put_byte(command, 0x02); /* username/password */ } command->u[method_count_offset] = command->len - methods_start; sk_write(p->sub_socket, command->s, command->len); strbuf_free(command); p->state = 1; return 0; } if (change == PROXY_CHANGE_CLOSING) { /* if our proxy negotiation process involves closing and opening * new sockets, then we would want to intercept this closing * callback when we were expecting it. if we aren't anticipating * a socket close, then some error must have occurred. we'll * just pass those errors up to the backend. */ plug_closing(p->plug, p->closing_error_msg, p->closing_error_code, p->closing_calling_back); return 0; /* ignored */ } if (change == PROXY_CHANGE_SENT) { /* some (or all) of what we wrote to the proxy was sent. * we don't do anything new, however, until we receive the * proxy's response. we might want to set a timer so we can * timeout the proxy negotiation after a while... */ return 0; } 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. */ if (p->state == 1) { /* initial response: * version number (1 byte) = 5 * authentication method (1 byte) * authentication methods: * 0x00 = no authentication * 0x01 = GSSAPI * 0x02 = username/password * 0x03 = CHAP * 0xff = no acceptable methods */ char data[2]; if (bufchain_size(&p->pending_input_data) < 2) return 1; /* not got anything yet */ /* get the response */ bufchain_fetch(&p->pending_input_data, data, 2); if (data[0] != 5) { plug_closing(p->plug, "Proxy error: SOCKS proxy returned unexpected version", PROXY_ERROR_GENERAL, 0); return 1; } if (data[1] == 0x00) p->state = 2; /* no authentication needed */ else if (data[1] == 0x01) p->state = 4; /* GSSAPI authentication */ else if (data[1] == 0x02) p->state = 5; /* username/password authentication */ else if (data[1] == 0x03) p->state = 6; /* CHAP authentication */ else { plug_closing(p->plug, "Proxy error: SOCKS proxy did not accept our authentication", PROXY_ERROR_GENERAL, 0); return 1; } bufchain_consume(&p->pending_input_data, 2); } if (p->state == 7) { /* password authentication reply format: * version number (1 bytes) = 1 * reply code (1 byte) * 0 = succeeded * >0 = failed */ char data[2]; if (bufchain_size(&p->pending_input_data) < 2) return 1; /* not got anything yet */ /* get the response */ bufchain_fetch(&p->pending_input_data, data, 2); if (data[0] != 1) { plug_closing(p->plug, "Proxy error: SOCKS password " "subnegotiation contained wrong version number", PROXY_ERROR_GENERAL, 0); return 1; } if (data[1] != 0) { plug_closing(p->plug, "Proxy error: SOCKS proxy refused" " password authentication", PROXY_ERROR_GENERAL, 0); return 1; } bufchain_consume(&p->pending_input_data, 2); p->state = 2; /* now proceed as authenticated */ } if (p->state == 8) { int ret; ret = proxy_socks5_handlechap(p); if (ret) return ret; } if (p->state == 2) { /* request format: * version number (1 byte) = 5 * command code (1 byte) * 1 = CONNECT * 2 = BIND * 3 = UDP ASSOCIATE * reserved (1 byte) = 0x00 * address type (1 byte) * 1 = IPv4 * 3 = domainname (first byte has length, no terminating null) * 4 = IPv6 * dest. address (variable) * dest. port (2 bytes) [network order] */ strbuf *command = strbuf_new(); put_byte(command, 5); /* SOCKS version 5 */ put_byte(command, 1); /* CONNECT command */ put_byte(command, 0x00); /* reserved byte */ switch (sk_addrtype(p->remote_addr)) { case ADDRTYPE_IPV4: put_byte(command, 1); /* IPv4 */ sk_addrcopy(p->remote_addr, strbuf_append(command, 4)); break; case ADDRTYPE_IPV6: put_byte(command, 4); /* IPv6 */ sk_addrcopy(p->remote_addr, strbuf_append(command, 16)); break; case ADDRTYPE_NAME: { char hostname[512]; put_byte(command, 3); /* domain name */ sk_getaddr(p->remote_addr, hostname, lenof(hostname)); if (!put_pstring(command, hostname)) { p->error = "Proxy error: SOCKS 5 cannot " "support host names longer than 255 chars"; strbuf_free(command); return 1; } break; } } put_uint16(command, p->remote_port); sk_write(p->sub_socket, command->s, command->len); strbuf_free(command); p->state = 3; return 1; } if (p->state == 3) { /* reply format: * version number (1 bytes) = 5 * reply code (1 byte) * 0 = succeeded * 1 = general SOCKS server failure * 2 = connection not allowed by ruleset * 3 = network unreachable * 4 = host unreachable * 5 = connection refused * 6 = TTL expired * 7 = command not supported * 8 = address type not supported * reserved (1 byte) = x00 * address type (1 byte) * 1 = IPv4 * 3 = domainname (first byte has length, no terminating null) * 4 = IPv6 * server bound address (variable) * server bound port (2 bytes) [network order] */ char data[5]; int len; /* First 5 bytes of packet are enough to tell its length. */ if (bufchain_size(&p->pending_input_data) < 5) return 1; /* not got anything yet */ /* get the response */ bufchain_fetch(&p->pending_input_data, data, 5); if (data[0] != 5) { plug_closing(p->plug, "Proxy error: SOCKS proxy returned wrong version number", PROXY_ERROR_GENERAL, 0); return 1; } if (data[1] != 0) { char buf[256]; strcpy(buf, "Proxy error: "); switch (data[1]) { case 1: strcat(buf, "General SOCKS server failure"); break; case 2: strcat(buf, "Connection not allowed by ruleset"); break; case 3: strcat(buf, "Network unreachable"); break; case 4: strcat(buf, "Host unreachable"); break; case 5: strcat(buf, "Connection refused"); break; case 6: strcat(buf, "TTL expired"); break; case 7: strcat(buf, "Command not supported"); break; case 8: strcat(buf, "Address type not supported"); break; default: sprintf(buf+strlen(buf), "Unrecognised SOCKS error code %d", data[1]); break; } plug_closing(p->plug, buf, PROXY_ERROR_GENERAL, 0); return 1; } /* * Eat the rest of the reply packet. */ len = 6; /* first 4 bytes, last 2 */ switch (data[3]) { case 1: len += 4; break; /* IPv4 address */ case 4: len += 16; break;/* IPv6 address */ case 3: len += 1+(unsigned char)data[4]; break; /* domain name */ default: plug_closing(p->plug, "Proxy error: SOCKS proxy returned " "unrecognised address format", PROXY_ERROR_GENERAL, 0); return 1; } if (bufchain_size(&p->pending_input_data) < len) return 1; /* not got whole reply yet */ bufchain_consume(&p->pending_input_data, len); /* we're done */ proxy_activate(p); return 1; } if (p->state == 4) { /* 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) { const char *username = conf_get_str(p->conf, CONF_proxy_username); const char *password = conf_get_str(p->conf, CONF_proxy_password); if (username[0] || password[0]) { strbuf *auth = strbuf_new_nm(); put_byte(auth, 1); /* version number of subnegotiation */ if (!put_pstring(auth, username)) { p->error = "Proxy error: SOCKS 5 authentication cannot " "support usernames longer than 255 chars"; strbuf_free(auth); return 1; } if (!put_pstring(auth, password)) { p->error = "Proxy error: SOCKS 5 authentication cannot " "support passwords longer than 255 chars"; strbuf_free(auth); return 1; } sk_write(p->sub_socket, auth->s, auth->len); strbuf_free(auth); 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); return 1; } if (p->state == 6) { int ret; ret = proxy_socks5_selectchap(p); if (ret) return ret; } } plug_closing(p->plug, "Proxy error: Unexpected proxy error", PROXY_ERROR_UNEXPECTED, 0); return 1; } /* ---------------------------------------------------------------------- * `Telnet' proxy type. * * (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); int so = 0, eo = 0; strbuf *buf = strbuf_new(); /* 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) put_data(buf, fmt + so, 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 '\\': put_byte(buf, '\\'); eo++; break; case '%': put_byte(buf, '%'); eo++; break; case 'r': put_byte(buf, '\r'); eo++; break; case 'n': put_byte(buf, '\n'); eo++; break; case 't': put_byte(buf, '\t'); eo++; break; case 'x': case 'X': { /* 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) */ put_byte(buf, '\\'); eo = so + 1; break; } /* we only extract two hex characters */ if (i == 1) { put_byte(buf, v); eo++; break; } i++; v <<= 4; } break; } default: put_data(buf, fmt + so, 2); eo++; break; } } else { /* % escape. we recognize %%, %host, %port, %user, %pass. * %proxyhost, %proxyport. Anything else we just send * unescaped (including the %). */ if (fmt[eo] == '%') { put_byte(buf, '%'); eo++; } else if (strnicmp(fmt + eo, "host", 4) == 0) { char dest[512]; sk_getaddr(addr, dest, lenof(dest)); put_data(buf, dest, strlen(dest)); eo += 4; } else if (strnicmp(fmt + eo, "port", 4) == 0) { strbuf_catf(buf, "%d", port); eo += 4; } else if (strnicmp(fmt + eo, "user", 4) == 0) { const char *username = conf_get_str(conf, CONF_proxy_username); put_data(buf, username, strlen(username)); eo += 4; } else if (strnicmp(fmt + eo, "pass", 4) == 0) { const char *password = conf_get_str(conf, CONF_proxy_password); put_data(buf, password, strlen(password)); eo += 4; } else if (strnicmp(fmt + eo, "proxyhost", 9) == 0) { const char *host = conf_get_str(conf, CONF_proxy_host); put_data(buf, host, strlen(host)); eo += 9; } else if (strnicmp(fmt + eo, "proxyport", 9) == 0) { int port = conf_get_int(conf, CONF_proxy_port); strbuf_catf(buf, "%d", port); eo += 9; } else { /* we don't escape this, so send the % now, and * don't advance eo, so that we'll consider the * text immediately following the % as unescaped. */ put_byte(buf, '%'); } } /* 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) { put_data(buf, fmt + so, eo - so); } return strbuf_to_str(buf); } int proxy_telnet_negotiate (ProxySocket *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); { /* * Re-escape control chars in the command, for logging. */ char *reescaped = snewn(4*strlen(formatted_cmd) + 1, char); const char *in; char *out; char *logmsg; for (in = formatted_cmd, out = reescaped; *in; in++) { if (*in == '\n') { *out++ = '\\'; *out++ = 'n'; } else if (*in == '\r') { *out++ = '\\'; *out++ = 'r'; } else if (*in == '\t') { *out++ = '\\'; *out++ = 't'; } else if (*in == '\\') { *out++ = '\\'; *out++ = '\\'; } else if ((unsigned)(((unsigned char)*in) - 0x20) < (0x7F-0x20)) { *out++ = *in; } else { out += sprintf(out, "\\x%02X", (unsigned)*in & 0xFF); } } *out = '\0'; logmsg = dupprintf("Sending Telnet proxy command: %s", reescaped); plug_log(p->plug, PLUGLOG_PROXY_MSG, NULL, 0, logmsg, 0); sfree(logmsg); sfree(reescaped); } sk_write(p->sub_socket, formatted_cmd, strlen(formatted_cmd)); sfree(formatted_cmd); p->state = 1; return 0; } if (change == PROXY_CHANGE_CLOSING) { /* if our proxy negotiation process involves closing and opening * new sockets, then we would want to intercept this closing * callback when we were expecting it. if we aren't anticipating * a socket close, then some error must have occurred. we'll * just pass those errors up to the backend. */ plug_closing(p->plug, p->closing_error_msg, p->closing_error_code, p->closing_calling_back); return 0; /* ignored */ } if (change == PROXY_CHANGE_SENT) { /* some (or all) of what we wrote to the proxy was sent. * we don't do anything new, however, until we receive the * proxy's response. we might want to set a timer so we can * timeout the proxy negotiation after a while... */ return 0; } 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. */ /* we're done */ proxy_activate(p); /* proxy activate will have dealt with * whatever is left of the buffer */ return 1; } plug_closing(p->plug, "Proxy error: Unexpected proxy error", PROXY_ERROR_UNEXPECTED, 0); return 1; } putty-0.76/proxy.h0000644000175000017500000000601414072266311011111 00000000000000/* * Network proxy abstraction in PuTTY * * A proxy layer, if necessary, wedges itself between the * network code and the higher level backend. * * Supported proxies: HTTP CONNECT, generic telnet, SOCKS 4 & 5 */ #ifndef PUTTY_PROXY_H #define PUTTY_PROXY_H #define PROXY_ERROR_GENERAL 8000 #define PROXY_ERROR_UNEXPECTED 8001 typedef struct ProxySocket ProxySocket; struct ProxySocket { const char *error; Socket *sub_socket; Plug *plug; SockAddr *remote_addr; int remote_port; bufchain pending_output_data; bufchain pending_oob_output_data; bufchain pending_input_data; bool 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 * proxy server. */ bool freeze; /* should we freeze the underlying socket when * we are done with the proxy negotiation? this * simply caches the value of sk_set_frozen calls. */ #define PROXY_CHANGE_NEW -1 #define PROXY_CHANGE_CLOSING 0 #define PROXY_CHANGE_SENT 1 #define PROXY_CHANGE_RECEIVE 2 #define PROXY_CHANGE_ACCEPTING 3 /* something has changed (a call from the sub socket * layer into our Proxy Plug layer, or we were just * created, etc), so the proxy layer needs to handle * this change (the type of which is the second argument) * and further the proxy negotiation process. */ int (*negotiate) (ProxySocket * /* this */, int /* change type */); /* current arguments of plug handlers * (for use by proxy's negotiate function) */ /* closing */ const char *closing_error_msg; int closing_error_code; bool closing_calling_back; /* receive */ bool receive_urgent; const char *receive_data; int receive_len; /* 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; Socket sock; Plug plugimpl; }; extern void proxy_activate (ProxySocket *); extern int proxy_http_negotiate (ProxySocket *, int); extern int proxy_telnet_negotiate (ProxySocket *, int); extern int proxy_socks4_negotiate (ProxySocket *, int); extern int proxy_socks5_negotiate (ProxySocket *, 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(BinarySink *); extern int proxy_socks5_handlechap (ProxySocket *); extern int proxy_socks5_selectchap(ProxySocket *); #endif putty-0.76/pscp.c0000644000175000017500000021655514072266311010705 00000000000000/* * scp.c - Scp (Secure Copy) client for PuTTY. * Joris van Rantwijk, Simon Tatham * * This is mainly based on ssh-1.2.26/scp.c by Timo Rinne & Tatu Ylonen. * They, in turn, used stuff from BSD rcp. * * (SGT, 2001-09-10: Joris van Rantwijk assures me that although * this file as originally submitted was inspired by, and * _structurally_ based on, ssh-1.2.26's scp.c, there wasn't any * actual code duplicated, so the above comment shouldn't give rise * to licensing issues.) */ #include #include #include #include #include #include #include "putty.h" #include "psftp.h" #include "ssh.h" #include "sftp.h" #include "storage.h" static bool list = false; static bool verbose = false; static bool recursive = false; static bool preserve = false; static bool targetshouldbedirectory = false; static bool statistics = true; static int prev_stats_len = 0; static bool scp_unsafe_mode = false; static int errs = 0; static bool try_scp = true; static bool try_sftp = true; static bool main_cmd_is_sftp = false; static bool fallback_cmd_is_sftp = false; static bool using_sftp = false; static bool uploading = false; static Backend *backend; static Conf *conf; static bool sent_eof = false; static void source(const char *src); static void rsource(const char *src); static void sink(const char *targ, const char *src); const char *const appname = "PSCP"; /* * The maximum amount of queued data we accept before we stop and * wait for the server to process some. */ #define MAX_SCP_BUFSIZE 16384 void ldisc_echoedit_update(Ldisc *ldisc) { } static size_t pscp_output(Seat *, bool is_stderr, const void *, size_t); static bool pscp_eof(Seat *); static const SeatVtable pscp_seat_vt = { .output = pscp_output, .eof = pscp_eof, .get_userpass_input = filexfer_get_userpass_input, .notify_remote_exit = nullseat_notify_remote_exit, .connection_fatal = console_connection_fatal, .update_specials_menu = nullseat_update_specials_menu, .get_ttymode = nullseat_get_ttymode, .set_busy_status = nullseat_set_busy_status, .verify_ssh_host_key = console_verify_ssh_host_key, .confirm_weak_crypto_primitive = console_confirm_weak_crypto_primitive, .confirm_weak_cached_hostkey = console_confirm_weak_cached_hostkey, .is_utf8 = nullseat_is_never_utf8, .echoedit_update = nullseat_echoedit_update, .get_x_display = nullseat_get_x_display, .get_windowid = nullseat_get_windowid, .get_window_pixel_size = nullseat_get_window_pixel_size, .stripctrl_new = console_stripctrl_new, .set_trust_status = nullseat_set_trust_status_vacuously, .verbose = cmdline_seat_verbose, .interactive = nullseat_interactive_no, .get_cursor_position = nullseat_get_cursor_position, }; static Seat pscp_seat[1] = {{ &pscp_seat_vt }}; static void tell_char(FILE *stream, char c) { fputc(c, stream); } static void tell_str(FILE *stream, const char *str) { unsigned int i; for (i = 0; i < strlen(str); ++i) tell_char(stream, str[i]); } static void abandon_stats(void) { /* * Output a \n to stdout (which is where we've been sending * transfer statistics) so that the cursor will move to the next * line. We should do this before displaying any other kind of * output like an error message. */ if (prev_stats_len) { putchar('\n'); fflush(stdout); prev_stats_len = 0; } } static PRINTF_LIKE(2, 3) void tell_user(FILE *stream, const char *fmt, ...) { char *str, *str2; va_list ap; va_start(ap, fmt); str = dupvprintf(fmt, ap); va_end(ap); str2 = dupcat(str, "\n"); sfree(str); abandon_stats(); tell_str(stream, str2); sfree(str2); } /* * Receive a block of data from the SSH link. Block until all data * is available. * * To do this, we repeatedly call the SSH protocol module, with our * own pscp_output() function to catch the data that comes back. We do * this until we have enough data. */ static bufchain received_data; static BinarySink *stderr_bs; static size_t pscp_output( Seat *seat, bool is_stderr, const void *data, size_t len) { /* * stderr data is just spouted to local stderr (optionally via a * sanitiser) and otherwise ignored. */ if (is_stderr) { put_data(stderr_bs, data, len); return 0; } bufchain_add(&received_data, data, len); return 0; } static bool pscp_eof(Seat *seat) { /* * 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) { seat_connection_fatal( pscp_seat, "Received unexpected end-of-file from server"); } return false; } static bool ssh_scp_recv(void *vbuf, size_t len) { char *buf = (char *)vbuf; while (len > 0) { while (bufchain_size(&received_data) == 0) { if (backend_exitcode(backend) >= 0 || ssh_sftp_loop_iteration() < 0) return false; /* doom */ } size_t got = bufchain_fetch_consume_up_to(&received_data, buf, len); buf += got; len -= got; } return true; } /* * Loop through the ssh connection and authentication process. */ static void ssh_scp_init(void) { while (!backend_sendok(backend)) { if (backend_exitcode(backend) >= 0) { errs++; return; } if (ssh_sftp_loop_iteration() < 0) { errs++; return; /* doom */ } } /* Work out which backend we ended up using. */ if (!ssh_fallback_cmd(backend)) using_sftp = main_cmd_is_sftp; else using_sftp = fallback_cmd_is_sftp; if (verbose) { if (using_sftp) tell_user(stderr, "Using SFTP"); else tell_user(stderr, "Using SCP1"); } } /* * Print an error message and exit after closing the SSH link. */ static NORETURN PRINTF_LIKE(1, 2) void bump(const char *fmt, ...) { char *str, *str2; va_list ap; va_start(ap, fmt); str = dupvprintf(fmt, ap); va_end(ap); str2 = dupcat(str, "\n"); sfree(str); abandon_stats(); tell_str(stderr, str2); sfree(str2); errs++; if (backend && backend_connected(backend)) { char ch; backend_special(backend, SS_EOF, 0); sent_eof = true; ssh_scp_recv(&ch, 1); } cleanup_exit(1); } /* * A nasty loop macro that lets me get an escape-sequence sanitised * version of a string for display, and free it automatically * afterwards. */ static StripCtrlChars *string_scc; #define with_stripctrl(varname, input) \ for (char *varname = stripctrl_string(string_scc, input); varname; \ sfree(varname), varname = NULL) /* * 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 seat_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) { seat_connection_fatal( pscp_seat, "did not receive SFTP response packet from server"); } rreq = sftp_find_request(pktin); if (rreq != req) { seat_connection_fatal( pscp_seat, "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; char *realhost; LogContext *logctx; if (host == NULL || host[0] == '\0') bump("Empty host name"); /* * Remove a colon suffix. */ host[host_strcspn(host, ":")] = '\0'; /* * If we haven't loaded session details already (e.g., from -load), * try looking for a session called "host". */ if (!cmdline_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 protocol to SSH if the user has somehow contrived to * select one we don't support (e.g. by loading an inappropriate * saved session). In that situation we assume the port number is * useless too.) */ if (!backend_vt_from_proto(conf_get_int(conf, CONF_protocol))) { 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); } } /* * Force protocol to SSH if the user has somehow contrived to * select one we don't support (e.g. by loading an inappropriate * saved session). In that situation we assume the port number is * useless too.) */ if (!backend_vt_from_proto(conf_get_int(conf, CONF_protocol))) { conf_set_int(conf, CONF_protocol, PROT_SSH); conf_set_int(conf, CONF_port, 22); } /* * Disable scary things which shouldn't be enabled for simple * things like SCP and SFTP: agent forwarding, port forwarding, * X forwarding. */ conf_set_bool(conf, CONF_x11_forward, false); conf_set_bool(conf, CONF_agentfwd, false); conf_set_bool(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 = true; conf_set_str(conf, CONF_remote_cmd, "sftp"); conf_set_bool(conf, CONF_ssh_subsys, true); if (try_scp) { /* Fallback is to use the provided scp command. */ fallback_cmd_is_sftp = false; conf_set_str(conf, CONF_remote_cmd2, cmd); conf_set_bool(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 = true; /* 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_bool(conf, CONF_ssh_subsys2, false); } } else { /* Don't try SFTP at all; just try the scp command. */ main_cmd_is_sftp = false; conf_set_str(conf, CONF_remote_cmd, cmd); conf_set_bool(conf, CONF_ssh_subsys, false); } conf_set_bool(conf, CONF_nopty, true); logctx = log_init(console_cli_logpolicy, conf); platform_psftp_pre_conn_setup(console_cli_logpolicy); err = backend_init(backend_vt_from_proto( conf_get_int(conf, CONF_protocol)), pscp_seat, &backend, logctx, conf, conf_get_str(conf, CONF_host), conf_get_int(conf, CONF_port), &realhost, 0, conf_get_bool(conf, CONF_tcp_keepalives)); if (err != NULL) bump("ssh_init: %s", err); 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(const char *name, uint64_t size, uint64_t done, time_t start, time_t now) { float ratebs; unsigned long eta; char *etastr; int pct; int len; int elap; elap = (unsigned long) difftime(now, start); if (now > start) ratebs = (float)done / elap; else ratebs = (float)done; if (ratebs < 1.0) eta = size - done; else eta = (unsigned long)((size - done) / ratebs); etastr = dupprintf("%02ld:%02ld:%02ld", eta / 3600, (eta % 3600) / 60, eta % 60); pct = (int) (100.0 * done / size); { /* divide by 1024 to provide kB */ len = printf("\r%-25.25s | %"PRIu64" kB | %5.1f kB/s | " "ETA: %8s | %3d%%", name, done >> 10, ratebs / 1024.0, etastr, pct); if (len < prev_stats_len) printf("%*s", prev_stats_len - len, ""); prev_stats_len = len; if (done == size) abandon_stats(); fflush(stdout); } free(etastr); } /* * Find a colon in str and return a pointer to the colon. * This is used to separate hostname from filename. */ static char *colon(char *str) { /* We ignore a leading colon, since the hostname cannot be empty. We also ignore a colon as second character because of filenames like f:myfile.txt. */ if (str[0] == '\0' || str[0] == ':' || (str[0] != '[' && str[1] == ':')) return (NULL); str += host_strcspn(str, ":/\\"); if (*str == ':') return (str); else return (NULL); } /* * Determine whether a string is entirely composed of dots. */ static bool is_dots(char *str) { return str[strspn(str, ".")] == '\0'; } /* * Wait for a response from the other side. * Return 0 if ok, -1 if error. */ static int response(void) { char ch, resp, rbuf[2048]; int p; if (!ssh_scp_recv(&resp, 1)) bump("Lost connection"); p = 0; switch (resp) { case 0: /* ok */ return (0); default: rbuf[p++] = resp; /* fallthrough */ case 1: /* error */ case 2: /* fatal error */ do { if (!ssh_scp_recv(&ch, 1)) 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); } } bool sftp_recvdata(char *buf, size_t len) { return ssh_scp_recv(buf, len); } bool sftp_senddata(const char *buf, size_t len) { backend_send(backend, buf, len); return true; } size_t sftp_sendbuffer(void) { return backend_sendbuffer(backend); } /* ---------------------------------------------------------------------- * sftp-based replacement for the hacky `pscp -ls'. */ void list_directory_from_sftp_warn_unsorted(void) { fprintf(stderr, "Directory is too large to sort; writing file names unsorted\n"); } void list_directory_from_sftp_print(struct fxp_name *name) { with_stripctrl(san, name->longname) printf("%s\n", san); } void scp_sftp_listdir(const char *dirname) { struct fxp_handle *dirh; struct fxp_names *names; struct sftp_packet *pktin; struct sftp_request *req; 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) { tell_user(stderr, "Unable to open %s: %s\n", dirname, fxp_error()); errs++; } else { struct list_directory_from_sftp_ctx *ctx = list_directory_from_sftp_new(); 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; } if (names->nnames == 0) { fxp_free_names(names); break; } for (size_t i = 0; i < names->nnames; i++) list_directory_from_sftp_feed(ctx, &names->names[i]); fxp_free_names(names); } req = fxp_close_send(dirh); pktin = sftp_wait_for_reply(req); fxp_close_recv(pktin, req); list_directory_from_sftp_finish(ctx); list_directory_from_sftp_free(ctx); } } /* ---------------------------------------------------------------------- * Helper routines that contain the actual SCP protocol elements, * implemented both as SCP1 and SFTP. */ static struct scp_sftp_dirstack { struct scp_sftp_dirstack *next; struct fxp_name *names; int namepos, namelen; char *dirpath; char *wildcard; bool matched_something; /* wildcard match set was non-empty */ } *scp_sftp_dirstack_head; static char *scp_sftp_remotepath, *scp_sftp_currentname; static char *scp_sftp_wildcard; static bool scp_sftp_targetisdir, scp_sftp_donethistarget; static bool scp_sftp_preserve, scp_sftp_recursive; static unsigned long scp_sftp_mtime, scp_sftp_atime; static bool scp_has_times; static struct fxp_handle *scp_sftp_filehandle; static struct fxp_xfer *scp_sftp_xfer; static uint64_t scp_sftp_fileoffset; int scp_source_setup(const char *target, bool shouldbedir) { 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; bool 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 = false; else scp_sftp_targetisdir = (attrs.permissions & 0040000) != 0; if (shouldbedir && !scp_sftp_targetisdir) { bump("pscp: remote filespec %s: not a directory\n", target); } scp_sftp_remotepath = dupstr(target); scp_has_times = false; } else { (void) response(); } return 0; } int scp_send_errmsg(char *str) { if (using_sftp) { /* do nothing; we never need to send our errors to the server */ } else { backend_send(backend, "\001", 1);/* scp protocol error prefix */ backend_send(backend, str, strlen(str)); } return 0; /* can't fail */ } int scp_send_filetimes(unsigned long mtime, unsigned long atime) { if (using_sftp) { scp_sftp_mtime = mtime; scp_sftp_atime = atime; scp_has_times = true; return 0; } else { char buf[80]; sprintf(buf, "T%lu 0 %lu 0\n", mtime, atime); backend_send(backend, buf, strlen(buf)); return response(); } } int scp_send_filename(const char *name, uint64_t 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); } 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 = 0; scp_sftp_xfer = xfer_upload_init(scp_sftp_filehandle, scp_sftp_fileoffset); sfree(fullname); return 0; } else { char *buf; if (permissions < 0) permissions = 0644; buf = dupprintf("C%04o %"PRIu64" ", (int)(permissions & 07777), size); backend_send(backend, buf, strlen(buf)); sfree(buf); backend_send(backend, name, strlen(name)); backend_send(backend, "\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)) { if (toplevel_callback_pending()) { /* If we have pending callbacks, they might make * xfer_upload_ready start to return true. So we should * run them and then re-check xfer_upload_ready, before * we go as far as waiting for an entire packet to * arrive. */ run_toplevel_callbacks(); continue; } 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); scp_sftp_fileoffset += len; return 0; } else { int bufsize = backend_send(backend, data, len); /* * If the network transfer is backing up - that is, the * remote site is not accepting data as fast as we can * produce it - then we must loop on network events until * we have space in the buffer again. */ while (bufsize > MAX_SCP_BUFSIZE) { if (ssh_sftp_loop_iteration() < 0) return 1; bufsize = backend_sendbuffer(backend); } return 0; } } int scp_send_finish(void) { if (using_sftp) { struct fxp_attrs attrs; struct sftp_packet *pktin; struct sftp_request *req; while (!xfer_done(scp_sftp_xfer)) { pktin = sftp_recv(); int 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); bool 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 = false; return 0; } else { backend_send(backend, "", 1); return response(); } } char *scp_save_remotepath(void) { if (using_sftp) return scp_sftp_remotepath; else return NULL; } void scp_restore_remotepath(char *data) { if (using_sftp) scp_sftp_remotepath = data; } int scp_send_dirname(const char *name, int modes) { if (using_sftp) { char *fullname; char const *err; struct fxp_attrs attrs; struct sftp_packet *pktin; struct sftp_request *req; bool ret; if (scp_sftp_targetisdir) { fullname = dupcat(scp_sftp_remotepath, "/", name); } 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, NULL); 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; } else { char buf[40]; sprintf(buf, "D%04o 0 ", modes); backend_send(backend, buf, strlen(buf)); backend_send(backend, name, strlen(name)); backend_send(backend, "\n", 1); return response(); } } int scp_send_enddir(void) { if (using_sftp) { sfree(scp_sftp_remotepath); return 0; } else { backend_send(backend, "E\n", 2); return response(); } } /* * Yes, I know; I have an scp_sink_setup _and_ an scp_sink_init. * That's bad. The difference is that scp_sink_setup is called once * right at the start, whereas scp_sink_init is called to * initialise every level of recursion in the protocol. */ int scp_sink_setup(const char *source, bool preserve, bool recursive) { if (using_sftp) { char *newsource; if (!fxp_init()) { tell_user(stderr, "unable to initialise SFTP: %s", fxp_error()); errs++; return 1; } /* * It's possible that the source string we've been given * contains a wildcard. If so, we must split the directory * away from the wildcard itself (throwing an error if any * 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, false); wildcard = dupstr(lastpart); *lastpart = '\0'; if (*dupsource && dupsource[1]) { /* * The remains of dupsource are at least two * characters long, meaning the pathname wasn't * empty or just `/'. Hence, we remove the trailing * slash. */ lastpart[-1] = '\0'; } else if (!*dupsource) { /* * The remains of dupsource are _empty_ - the whole * pathname was a wildcard. Hence we need to * replace it with ".". */ sfree(dupsource); dupsource = dupstr("."); } /* * Now we have separated our string into dupsource (the * directory part) and wildcard. Both of these will * need freeing at some point. Next step is to remove * wildcard escapes from the directory part, throwing * an error if it contains a real wildcard. */ dirpart = snewn(1+strlen(dupsource), char); if (!wc_unescape(dirpart, dupsource)) { tell_user(stderr, "%s: multiple-level wildcards unsupported", source); errs++; sfree(dirpart); sfree(wildcard); sfree(dupsource); return 1; } /* * Now we have dirpart (unescaped, ie a valid remote * path), and wildcard (a wildcard). This will be * sufficient to arrange a dirstack entry. */ scp_sftp_remotepath = dirpart; scp_sftp_wildcard = wildcard; sfree(dupsource); } else { scp_sftp_remotepath = newsource; scp_sftp_wildcard = NULL; } scp_sftp_preserve = preserve; scp_sftp_recursive = recursive; scp_sftp_donethistarget = false; scp_sftp_dirstack_head = NULL; } return 0; } int scp_sink_init(void) { if (!using_sftp) { backend_send(backend, "", 1); } return 0; } #define SCP_SINK_FILE 1 #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 */ strbuf *buf; /* will need freeing after use */ char *name; /* filename or dirname (not ENDDIR) */ long permissions; /* access permissions (not ENDDIR) */ uint64_t size; /* file size (not ENDDIR) */ bool settime; /* true 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; bool must_free_fname; struct fxp_attrs attrs; struct sftp_packet *pktin; struct sftp_request *req; bool ret; if (!scp_sftp_dirstack_head) { if (!scp_sftp_donethistarget) { /* * Simple case: we are only dealing with one file. */ fname = scp_sftp_remotepath; must_free_fname = false; scp_sftp_donethistarget = true; } else { /* * Even simpler case: one file _which we've done_. * Return 1 (finished). */ return 1; } } else { /* * We're now in the middle of stepping through a list * of names returned from fxp_readdir(); so let's carry * on. */ struct scp_sftp_dirstack *head = scp_sftp_dirstack_head; while (head->namepos < head->namelen && (is_dots(head->names[head->namepos].filename) || (head->wildcard && !wc_match(head->wildcard, head->names[head->namepos].filename)))) head->namepos++; /* skip . and .. */ if (head->namepos < head->namelen) { head->matched_something = true; fname = dupcat(head->dirpath, "/", head->names[head->namepos++].filename); must_free_fname = true; } else { /* * We've come to the end of the list; pop it off * the stack and return an ENDDIR action (or RETRY * if this was a wildcard match). */ if (head->wildcard) { act->action = SCP_SINK_RETRY; if (!head->matched_something) { tell_user(stderr, "pscp: wildcard '%s' matched " "no files", head->wildcard); errs++; } sfree(head->wildcard); } else { act->action = SCP_SINK_ENDDIR; } sfree(head->dirpath); sfree(head->names); scp_sftp_dirstack_head = head->next; sfree(head); return 0; } } /* * 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)) { with_stripctrl(san, fname) tell_user(stderr, "unable to identify %s: %s", san, 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; size_t nnames, namesize; struct fxp_name *ournames; struct fxp_names *names; /* * It's a directory. If we're not in recursive mode, * this merits a complaint (which is fatal if the name * was specified directly, but not if it was matched by * a wildcard). * * We skip this complaint completely if * scp_sftp_wildcard is set, because that's an * indication that we're not actually supposed to * _recursively_ transfer the dir, just scan it for * things matching the wildcard. */ if (!scp_sftp_recursive && !scp_sftp_wildcard) { with_stripctrl(san, fname) tell_user(stderr, "pscp: %s: is a directory", san); errs++; if (must_free_fname) sfree(fname); if (scp_sftp_dirstack_head) { act->action = SCP_SINK_RETRY; return 0; } else { return 1; } } /* * Otherwise, the fun begins. We must fxp_opendir() the * directory, slurp the filenames into memory, return * SCP_SINK_DIR (unless this is a wildcard match), and * set targetisdir. The next time we're called, we will * run through the list of filenames one by one, * 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) { with_stripctrl(san, fname) tell_user(stderr, "pscp: unable to open directory %s: %s", san, 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; with_stripctrl(san, fname) tell_user(stderr, "pscp: reading directory %s: %s", san, 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); break; } sgrowarrayn(ournames, namesize, nnames, names->nnames); for (i = 0; i < names->nnames; i++) { if (!strcmp(names->names[i].filename, ".") || !strcmp(names->names[i].filename, "..")) { /* * . and .. are normal consequences of * reading a directory, and aren't worth * complaining about. */ } else if (!vet_filename(names->names[i].filename)) { with_stripctrl(san, names->names[i].filename) tell_user(stderr, "ignoring potentially dangerous " "server-supplied filename '%s'", san); } 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) newitem->dirpath = fname; else newitem->dirpath = dupstr(fname); if (scp_sftp_wildcard) { newitem->wildcard = scp_sftp_wildcard; newitem->matched_something = false; scp_sftp_wildcard = NULL; } else { newitem->wildcard = NULL; } scp_sftp_dirstack_head = newitem; if (newitem->wildcard) { act->action = SCP_SINK_RETRY; } else { act->action = SCP_SINK_DIR; strbuf_clear(act->buf); put_asciz(act->buf, stripslashes(fname, false)); act->name = act->buf->s; act->size = 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 = true; } else act->settime = false; } return 0; } else { /* * It's a file. Return SCP_SINK_FILE. */ act->action = SCP_SINK_FILE; strbuf_clear(act->buf); put_asciz(act->buf, stripslashes(fname, false)); act->name = act->buf->s; if (attrs.flags & SSH_FILEXFER_ATTR_SIZE) { act->size = attrs.size; } else act->size = UINT64_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 = true; } else act->settime = false; if (must_free_fname) scp_sftp_currentname = fname; else scp_sftp_currentname = dupstr(fname); return 0; } } else { bool done = false; int action; char ch; act->settime = false; strbuf_clear(act->buf); while (!done) { if (!ssh_scp_recv(&ch, 1)) return 1; if (ch == '\n') bump("Protocol error: Unexpected newline"); action = ch; while (1) { if (!ssh_scp_recv(&ch, 1)) bump("Lost connection"); if (ch == '\n') break; put_byte(act->buf, ch); } switch (action) { case '\01': /* error */ with_stripctrl(san, act->buf->s) tell_user(stderr, "%s", san); errs++; continue; /* go round again */ case '\02': /* fatal error */ with_stripctrl(san, act->buf->s) bump("%s", san); case 'E': backend_send(backend, "", 1); act->action = SCP_SINK_ENDDIR; return 0; case 'T': if (sscanf(act->buf->s, "%lu %*d %lu %*d", &act->mtime, &act->atime) == 2) { act->settime = true; backend_send(backend, "", 1); strbuf_clear(act->buf); continue; /* go round again */ } bump("Protocol error: Illegal time format"); case 'C': case 'D': act->action = (action == 'C' ? SCP_SINK_FILE : SCP_SINK_DIR); if (act->action == SCP_SINK_DIR && !recursive) { bump("security violation: remote host attempted to create " "a subdirectory in a non-recursive copy!"); } break; default: bump("Protocol error: Expected control record"); } /* * We will go round this loop only once, unless we hit * `continue' above. */ done = true; } /* * If we get here, we must have seen SCP_SINK_FILE or * SCP_SINK_DIR. */ { int i; if (sscanf(act->buf->s, "%lo %"SCNu64" %n", &act->permissions, &act->size, &i) != 2) bump("Protocol error: Illegal file descriptor format"); act->name = act->buf->s + 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) { with_stripctrl(san, scp_sftp_currentname) tell_user(stderr, "pscp: unable to open %s: %s", san, fxp_error()); errs++; return 1; } scp_sftp_fileoffset = 0; scp_sftp_xfer = xfer_download_init(scp_sftp_filehandle, scp_sftp_fileoffset); sfree(scp_sftp_currentname); return 0; } else { backend_send(backend, "", 1); return 0; /* can't fail */ } } int scp_recv_filedata(char *data, int len) { if (using_sftp) { 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)) { if (actuallen <= 0) { tell_user(stderr, "pscp: end of file while reading"); errs++; sfree(vbuf); return -1; } /* * This assertion relies on the fact that the natural * block size used in the xfer manager is at most that * used in this module. I don't like crossing layers in * this way, but it'll do for now. */ assert(actuallen <= len); memcpy(data, vbuf, actuallen); sfree(vbuf); } else actuallen = 0; scp_sftp_fileoffset += actuallen; return actuallen; } else { return ssh_scp_recv(data, len) ? len : 0; } } 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 { backend_send(backend, "", 1); return response(); } } /* ---------------------------------------------------------------------- * Send an error message to the other side and to the screen. * Increment error counter. */ static PRINTF_LIKE(1, 2) 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"); sfree(str); scp_send_errmsg(str2); abandon_stats(); tell_user(stderr, "%s", str2); va_end(ap); sfree(str2); } /* * Execute the source part of the SCP protocol. */ static void source(const char *src) { uint64_t size; unsigned long mtime, atime; long permissions; const char *last; RFile *f; int attr; uint64_t i; uint64_t stat_bytes; time_t stat_starttime, stat_lasttime; attr = file_type(src); if (attr == FILE_TYPE_NONEXISTENT || attr == FILE_TYPE_WEIRD) { run_err("%s: %s file or directory", src, (attr == FILE_TYPE_WEIRD ? "Not a" : "No such")); return; } if (attr == FILE_TYPE_DIRECTORY) { if (recursive) { /* * Avoid . and .. directories. */ const char *p; p = strrchr(src, '/'); if (!p) p = strrchr(src, '\\'); if (!p) p = src; else p++; if (!strcmp(p, ".") || !strcmp(p, "..")) /* skip . and .. */ ; else rsource(src); } else { run_err("%s: not a regular file", src); } return; } if ((last = strrchr(src, '/')) == NULL) last = src; 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) { tell_user(stderr, "Sending file %s, size=%"PRIu64, last, size); } if (scp_send_filename(last, size, permissions)) { close_rfile(f); return; } stat_bytes = 0; stat_starttime = time(NULL); stat_lasttime = 0; #define PSCP_SEND_BLOCK 4096 for (i = 0; i < size; i += PSCP_SEND_BLOCK) { char transbuf[PSCP_SEND_BLOCK]; int j, k = PSCP_SEND_BLOCK; if (i + k > size) k = size - i; if ((j = read_from_file(f, transbuf, k)) != k) { bump("%s: Read error", src); } if (scp_send_filedata(transbuf, k)) bump("%s: Network error occurred", src); if (statistics) { stat_bytes += k; if (time(NULL) != stat_lasttime || i + k == size) { stat_lasttime = time(NULL); print_stats(last, size, stat_bytes, stat_starttime, stat_lasttime); } } } close_rfile(f); (void) scp_send_finish(); } /* * Recursively send the contents of a directory. */ static void rsource(const char *src) { const char *last; char *save_target; DirHandle *dir; if ((last = strrchr(src, '/')) == NULL) last = src; else last++; if (strrchr(last, '\\') != NULL) last = strrchr(last, '\\') + 1; if (last == src && strchr(src, ':') != NULL) last = strchr(src, ':') + 1; /* maybe send filetime */ save_target = scp_save_remotepath(); if (verbose) tell_user(stderr, "Entering directory: %s", last); if (scp_send_dirname(last, 0755)) return; const char *opendir_err; dir = open_directory(src, &opendir_err); if (dir != NULL) { char *filename; while ((filename = read_filename(dir)) != NULL) { char *foundfile = dupcat(src, "/", filename); source(foundfile); sfree(foundfile); sfree(filename); } close_directory(dir); } else { tell_user(stderr, "Error opening directory %s: %s", src, opendir_err); } (void) scp_send_enddir(); scp_restore_remotepath(save_target); } /* * Execute the sink part of the SCP protocol. */ static void sink(const char *targ, const char *src) { char *destfname; bool targisdir = false; bool exists; int attr; WFile *f; uint64_t received; bool wrerror = false; uint64_t stat_bytes; time_t stat_starttime, stat_lasttime; char *stat_name; attr = file_type(targ); if (attr == FILE_TYPE_DIRECTORY) targisdir = true; if (targetshouldbedirectory && !targisdir) bump("%s: Not a directory", targ); scp_sink_init(); struct scp_sink_action act; act.buf = strbuf_new(); while (1) { if (scp_get_sink_action(&act)) goto out; if (act.action == SCP_SINK_ENDDIR) goto out; if (act.action == SCP_SINK_RETRY) continue; if (targisdir) { /* * Prevent the remote side from maliciously writing to * files outside the target area by sending a filename * containing `../'. In fact, it shouldn't be sending * filenames with any slashes or colons in at all; so * we'll find the last slash, backslash or colon in the * filename and use only the part after that. (And * warn!) * * In addition, we also ensure here that if we're * copying a single file and the target is a directory * (common usage: `pscp host:filename .') the remote * can't send us a _different_ file name. We can * distinguish this case because `src' will be non-NULL * and the last component of that will fail to match * (the last component of) the name sent. * * Well, not always; if `src' is a wildcard, we do * expect to get back filenames that don't correspond * exactly to it. Ideally in this case, we would like * to ensure that the returned filename actually * matches the wildcard pattern - but one of SCP's * protocol infelicities is that wildcard matching is * done at the server end _by the server's rules_ and * so in general this is infeasible. Hence, we only * accept filenames that don't correspond to `src' if * unsafe mode is enabled or we are using SFTP (which * resolves remote wildcards on the client side and can * be trusted). */ char *striptarget, *stripsrc; striptarget = stripslashes(act.name, true); if (striptarget != act.name) { with_stripctrl(sanname, act.name) { with_stripctrl(santarg, act.name) { tell_user(stderr, "warning: remote host sent a" " compound pathname '%s'", sanname); tell_user(stderr, " renaming local" " file to '%s'", santarg); } } } /* * Also check to see if the target filename is '.' or * '..', or indeed '...' and so on because Windows * appears to interpret those like '..'. */ if (is_dots(striptarget)) { bump("security violation: remote host attempted to write to" " a '.' or '..' path!"); } if (src) { stripsrc = stripslashes(src, true); if (strcmp(striptarget, stripsrc) && !using_sftp && !scp_unsafe_mode) { with_stripctrl(san, striptarget) tell_user(stderr, "warning: remote host tried to " "write to a file called '%s'", san); tell_user(stderr, " when we requested a file " "called '%s'.", stripsrc); tell_user(stderr, " If this is a wildcard, " "consider upgrading to SSH-2 or using"); tell_user(stderr, " the '-unsafe' option. Renaming" " of this file has been disallowed."); /* Override the name the server provided with our own. */ striptarget = stripsrc; } } if (targ[0] != '\0') destfname = dir_file_cat(targ, striptarget); else destfname = dupstr(striptarget); } else { /* * In this branch of the if, the target area is a * single file with an explicitly specified name in any * case, so there's no danger. */ destfname = dupstr(targ); } attr = file_type(destfname); exists = (attr != FILE_TYPE_NONEXISTENT); if (act.action == SCP_SINK_DIR) { if (exists && attr != FILE_TYPE_DIRECTORY) { with_stripctrl(san, destfname) run_err("%s: Not a directory", san); sfree(destfname); continue; } if (!exists) { if (!create_directory(destfname)) { with_stripctrl(san, destfname) run_err("%s: Cannot create directory", san); 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) { with_stripctrl(san, destfname) run_err("%s: Cannot create file", san); sfree(destfname); continue; } if (scp_accept_filexfer()) { sfree(destfname); close_wfile(f); goto out; } stat_bytes = 0; stat_starttime = time(NULL); stat_lasttime = 0; stat_name = stripctrl_string( string_scc, stripslashes(destfname, true)); received = 0; while (received < act.size) { char transbuf[32768]; uint64_t blksize; int read; blksize = 32768; if (blksize > act.size - received) blksize = act.size - received; read = scp_recv_filedata(transbuf, (int)blksize); if (read <= 0) bump("Lost connection"); if (wrerror) { received += read; continue; } if (write_to_file(f, transbuf, read) != (int)read) { wrerror = true; /* FIXME: in sftp we can actually abort the transfer */ if (statistics) printf("\r%-25.25s | %50s\n", stat_name, "Write error.. waiting for end of file"); received += read; continue; } if (statistics) { stat_bytes += read; if (time(NULL) > stat_lasttime || received + read == act.size) { stat_lasttime = time(NULL); print_stats(stat_name, act.size, stat_bytes, stat_starttime, stat_lasttime); } } received += read; } if (act.settime) { set_file_times(f, act.mtime, act.atime); } close_wfile(f); if (wrerror) { with_stripctrl(san, destfname) run_err("%s: Write error", san); sfree(destfname); continue; } (void) scp_finish_filerecv(); sfree(stat_name); sfree(destfname); } out: strbuf_free(act.buf); } /* * We will copy local files to a remote server. */ static void toremote(int argc, char *argv[]) { char *src, *wtarg, *host, *user; const char *targ; char *cmd; int i, wc_type; uploading = true; wtarg = argv[argc - 1]; /* Separate host from filename */ host = wtarg; wtarg = colon(wtarg); if (wtarg == NULL) bump("wtarg == NULL in toremote()"); *wtarg++ = '\0'; /* Substitute "." for empty target */ if (*wtarg == '\0') targ = "."; else targ = wtarg; /* Separate host and username */ user = host; host = strrchr(host, '@'); if (host == NULL) { host = user; user = NULL; } else { *host++ = '\0'; if (*user == '\0') user = NULL; } if (argc == 2) { if (colon(argv[0]) != NULL) bump("%s: Remote to remote not supported", argv[0]); wc_type = test_wildcard(argv[0], true); if (wc_type == WCTYPE_NONEXISTENT) bump("%s: No such file or directory\n", argv[0]); else if (wc_type == WCTYPE_WILDCARD) targetshouldbedirectory = true; } cmd = dupprintf("scp%s%s%s%s -t %s", verbose ? " -v" : "", recursive ? " -r" : "", preserve ? " -p" : "", targetshouldbedirectory ? " -d" : "", targ); do_cmd(host, user, cmd); sfree(cmd); if (scp_source_setup(targ, targetshouldbedirectory)) return; for (i = 0; i < argc - 1; i++) { src = argv[i]; if (colon(src) != NULL) { tell_user(stderr, "%s: Remote to remote not supported\n", src); errs++; continue; } wc_type = test_wildcard(src, true); if (wc_type == WCTYPE_NONEXISTENT) { run_err("%s: No such file or directory", src); continue; } else if (wc_type == WCTYPE_FILENAME) { source(src); continue; } else { WildcardMatcher *wc; char *filename; wc = begin_wildcard_matching(src); if (wc == NULL) { run_err("%s: No such file or directory", src); continue; } while ((filename = wildcard_get_filename(wc)) != NULL) { source(filename); sfree(filename); } finish_wildcard_matching(wc); } } } /* * We will copy files from a remote server to the local machine. */ static void tolocal(int argc, char *argv[]) { char *wsrc, *host, *user; const char *src, *targ; char *cmd; uploading = false; if (argc != 2) bump("More than one remote source not supported"); wsrc = argv[0]; targ = argv[1]; /* Separate host from filename */ host = wsrc; wsrc = colon(wsrc); if (wsrc == NULL) bump("Local to local copy not supported"); *wsrc++ = '\0'; /* Substitute "." for empty filename */ if (*wsrc == '\0') src = "."; else src = wsrc; /* Separate username and hostname */ user = host; host = strrchr(host, '@'); if (host == NULL) { host = user; user = NULL; } else { *host++ = '\0'; if (*user == '\0') user = NULL; } cmd = dupprintf("scp%s%s%s%s -f %s", verbose ? " -v" : "", recursive ? " -r" : "", preserve ? " -p" : "", targetshouldbedirectory ? " -d" : "", src); do_cmd(host, user, cmd); sfree(cmd); if (scp_sink_setup(src, preserve, recursive)) return; sink(targ, src); } /* * We will issue a list command to get a remote directory. */ static void get_dir_list(int argc, char *argv[]) { char *wsrc, *host, *user; const char *src; char *cmd, *p; const char *q; char c; wsrc = argv[0]; /* Separate host from filename */ host = wsrc; wsrc = colon(wsrc); if (wsrc == NULL) bump("Local file listing not supported"); *wsrc++ = '\0'; /* Substitute "." for empty filename */ if (*wsrc == '\0') src = "."; else src = wsrc; /* Separate username and hostname */ user = host; host = strrchr(host, '@'); if (host == NULL) { host = user; user = NULL; } else { *host++ = '\0'; if (*user == '\0') user = NULL; } cmd = snewn(4 * strlen(src) + 100, char); strcpy(cmd, "ls -la '"); p = cmd + strlen(cmd); for (q = src; *q; q++) { if (*q == '\'') { *p++ = '\''; *p++ = '\\'; *p++ = '\''; *p++ = '\''; } else { *p++ = *q; } } *p++ = '\''; *p = '\0'; do_cmd(host, user, cmd); sfree(cmd); if (using_sftp) { scp_sftp_listdir(src); } else { stdio_sink ss; stdio_sink_init(&ss, stdout); StripCtrlChars *scc = stripctrl_new( BinarySink_UPCAST(&ss), false, L'\0'); while (ssh_scp_recv(&c, 1)) put_byte(scc, c); stripctrl_free(scc); } } /* * Short description of parameters. */ static void usage(void) { printf("PuTTY Secure Copy client\n"); printf("%s\n", ver); printf("Usage: pscp [options] [user@]host:source target\n"); printf (" pscp [options] source [source...] [user@]host:target\n"); printf(" pscp [options] -ls [user@]host:filespec\n"); printf("Options:\n"); printf(" -V print version information and exit\n"); printf(" -pgpfp print PGP key fingerprints and exit\n"); printf(" -p preserve file attributes\n"); printf(" -q quiet, don't show statistics\n"); printf(" -r copy directories recursively\n"); printf(" -v show verbose messages\n"); printf(" -load sessname Load settings from saved session\n"); printf(" -P port connect to specified port\n"); printf(" -l user connect with specified username\n"); printf(" -pw passw login with specified password\n"); printf(" -1 -2 force use of particular SSH protocol version\n"); printf(" -ssh -ssh-connection\n"); printf(" force use of particular SSH protocol variant\n"); printf(" -4 -6 force use of IPv4 or IPv6\n"); printf(" -C enable compression\n"); printf(" -i key private key file for user authentication\n"); printf(" -noagent disable use of Pageant\n"); printf(" -agent enable use of Pageant\n"); printf(" -no-trivial-auth\n"); printf(" disconnect if SSH authentication succeeds trivially\n"); printf(" -hostkey keyid\n"); printf(" manually specify a host key (may be repeated)\n"); printf(" -batch disable all interactive prompts\n"); printf(" -no-sanitise-stderr don't strip control chars from" " standard error\n"); printf(" -proxycmd command\n"); printf(" use 'command' as local proxy\n"); printf(" -unsafe allow server-side wildcards (DANGEROUS)\n"); printf(" -sftp force use of SFTP protocol\n"); printf(" -scp force use of SCP protocol\n"); printf(" -sshlog file\n"); printf(" -sshrawlog file\n"); printf(" log protocol details to a file\n"); printf(" -logoverwrite\n"); printf(" -logappend\n"); printf(" control what happens when a log file already exists\n"); cleanup_exit(1); } void version(void) { char *buildinfo_text = buildinfo("\n"); printf("pscp: %s\n%s\n", ver, buildinfo_text); sfree(buildinfo_text); exit(0); } void cmdline_error(const char *p, ...) { va_list ap; fprintf(stderr, "pscp: "); va_start(ap, p); vfprintf(stderr, p, ap); va_end(ap); fprintf(stderr, "\n try typing just \"pscp\" for help\n"); exit(1); } const bool share_can_be_downstream = true; const bool share_can_be_upstream = false; static stdio_sink stderr_ss; static StripCtrlChars *stderr_scc; const unsigned cmdline_tooltype = TOOLTYPE_FILETRANSFER; /* * 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; bool sanitise_stderr = true; sk_init(); /* Load Default Settings before doing anything else. */ conf = conf_new(); do_defaults(NULL, conf); for (i = 1; i < argc; i++) { int ret; if (argv[i][0] != '-') break; ret = cmdline_process_param(argv[i], i+1 2) targetshouldbedirectory = true; if (colon(argv[argc - 1]) != NULL) toremote(argc, argv); else tolocal(argc, argv); } if (backend && backend_connected(backend)) { char ch; backend_special(backend, SS_EOF, 0); sent_eof = true; ssh_scp_recv(&ch, 1); } random_save_seed(); cmdline_cleanup(); if (backend) { backend_free(backend); backend = NULL; } sk_cleanup(); return (errs == 0 ? 0 : 1); } /* end */ putty-0.76/psftp.c0000644000175000017500000025243614072266311011072 00000000000000/* * psftp.c: (platform-independent) front end for PSFTP. */ #include #include #include #include #include #include "putty.h" #include "psftp.h" #include "storage.h" #include "ssh.h" #include "sftp.h" const char *const appname = "PSFTP"; /* * Since SFTP is a request-response oriented protocol, it requires * no buffer management: when we send data, we stop and wait for an * acknowledgement _anyway_, and so we can't possibly overfill our * send buffer. */ static int psftp_connect(char *userhost, char *user, int portnumber); static int do_sftp_init(void); static void do_sftp_cleanup(void); /* ---------------------------------------------------------------------- * sftp client state. */ static char *pwd, *homedir; static LogContext *psftp_logctx = NULL; static Backend *backend; static Conf *conf; static bool sent_eof = false; /* ------------------------------------------------------------ * Seat vtable. */ static size_t psftp_output(Seat *, bool is_stderr, const void *, size_t); static bool psftp_eof(Seat *); static const SeatVtable psftp_seat_vt = { .output = psftp_output, .eof = psftp_eof, .get_userpass_input = filexfer_get_userpass_input, .notify_remote_exit = nullseat_notify_remote_exit, .connection_fatal = console_connection_fatal, .update_specials_menu = nullseat_update_specials_menu, .get_ttymode = nullseat_get_ttymode, .set_busy_status = nullseat_set_busy_status, .verify_ssh_host_key = console_verify_ssh_host_key, .confirm_weak_crypto_primitive = console_confirm_weak_crypto_primitive, .confirm_weak_cached_hostkey = console_confirm_weak_cached_hostkey, .is_utf8 = nullseat_is_never_utf8, .echoedit_update = nullseat_echoedit_update, .get_x_display = nullseat_get_x_display, .get_windowid = nullseat_get_windowid, .get_window_pixel_size = nullseat_get_window_pixel_size, .stripctrl_new = console_stripctrl_new, .set_trust_status = nullseat_set_trust_status_vacuously, .verbose = cmdline_seat_verbose, .interactive = nullseat_interactive_yes, .get_cursor_position = nullseat_get_cursor_position, }; static Seat psftp_seat[1] = {{ &psftp_seat_vt }}; /* ---------------------------------------------------------------------- * A nasty loop macro that lets me get an escape-sequence sanitised * version of a string for display, and free it automatically * afterwards. */ static StripCtrlChars *string_scc; #define with_stripctrl(varname, input) \ for (char *varname = stripctrl_string(string_scc, input); varname; \ sfree(varname), varname = NULL) /* ---------------------------------------------------------------------- * 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) { seat_connection_fatal( psftp_seat, "did not receive SFTP response packet from server"); } rreq = sftp_find_request(pktin); if (rreq != req) { seat_connection_fatal( psftp_seat, "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(const char *name) { char *fullname, *canonname; struct sftp_packet *pktin; struct sftp_request *req; if (name[0] == '/') { fullname = dupstr(name); } else { const char *slash; if (pwd[strlen(pwd) - 1] == '/') slash = ""; else slash = "/"; fullname = dupcat(pwd, slash, name); } 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 * (glibc-based ones, in particular) require the _whole_ * path to point to something that exists, whereas others * (BSD-based) only require all but the last component to * exist. So if the first call failed, we should strip off * everything from the last slash onwards and try again, * then put the final component back on. * * Special cases: * * - if the last component is "/." or "/..", then we don't * bother trying this because there's no way it can work. * * - if the thing actually ends with a "/", we remove it * before we start. Except if the string is "/" itself * (although I can't see why we'd have got here if so, * because surely "/" would have worked the first * time?), in which case we don't bother. * * - if there's no slash in the string at all, give up in * confusion (we expect at least one because of the way * we constructed the string). */ int i; char *returnname; i = strlen(fullname); if (i > 2 && fullname[i - 1] == '/') fullname[--i] = '\0'; /* strip trailing / unless at pos 0 */ while (i > 0 && fullname[--i] != '/'); /* * Give up on special cases. */ if (fullname[i] != '/' || /* no slash at all */ !strcmp(fullname + i, "/.") || /* ends in /. */ !strcmp(fullname + i, "/..") || /* ends in /.. */ !strcmp(fullname, "/")) { return fullname; } /* * 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; } /* * We have a canonical name for all but the last path * component. Concatenate the last component and return. */ returnname = dupcat(canonname, (strendswith(canonname, "/") ? "" : "/"), fullname + i + 1); sfree(fullname); sfree(canonname); return returnname; } } static int bare_name_compare(const void *av, const void *bv) { const char **a = (const char **) av; const char **b = (const char **) bv; return strcmp(*a, *b); } static void not_connected(void) { printf("psftp: not connected to a host; use \"open host.name\"\n"); } /* ---------------------------------------------------------------------- * The meat of the `get' and `put' commands. */ bool sftp_get_file(char *fname, char *outfname, bool recurse, bool restart) { struct fxp_handle *fh; struct sftp_packet *pktin; struct sftp_request *req; struct fxp_xfer *xfer; uint64_t offset; WFile *file; bool toret, 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) { bool 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; size_t nnames, namesize; struct fxp_name **ournames; struct fxp_names *names; int i; /* * First, attempt to create the destination directory, * unless it already exists. */ if (file_type(outfname) != FILE_TYPE_DIRECTORY && !create_directory(outfname)) { with_stripctrl(san, outfname) printf("%s: Cannot create directory\n", san); return false; } /* * 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) { with_stripctrl(san, fname) printf("%s: unable to open directory: %s\n", san, fxp_error()); return false; } 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; with_stripctrl(san, fname) printf("%s: reading directory: %s\n", san, fxp_error()); req = fxp_close_send(dirhandle); pktin = sftp_wait_for_reply(req); fxp_close_recv(pktin, req); sfree(ournames); return false; } if (names->nnames == 0) { fxp_free_names(names); break; } sgrowarrayn(ournames, namesize, nnames, names->nnames); for (i = 0; i < names->nnames; i++) if (strcmp(names->names[i].filename, ".") && strcmp(names->names[i].filename, "..")) { if (!vet_filename(names->names[i].filename)) { with_stripctrl(san, names->names[i].filename) printf("ignoring potentially dangerous server-" "supplied filename '%s'\n", san); } 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; bool nonexistent; nextoutfname = dir_file_cat(outfname, ournames[i]->filename); nonexistent = (file_type(nextoutfname) == FILE_TYPE_NONEXISTENT); sfree(nextoutfname); if (nonexistent) 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_get_file again. */ for (; i < nnames; i++) { char *nextfname, *nextoutfname; bool retd; nextfname = dupcat(fname, "/", ournames[i]->filename); nextoutfname = dir_file_cat(outfname, ournames[i]->filename); retd = sftp_get_file( nextfname, nextoutfname, recurse, restart); restart = false; /* after first partial file, do full */ sfree(nextoutfname); sfree(nextfname); if (!retd) { for (i = 0; i < nnames; i++) { fxp_free_name(ournames[i]); } sfree(ournames); return false; } } /* * Done this recursion level. Free everything. */ for (i = 0; i < nnames; i++) { fxp_free_name(ournames[i]); } sfree(ournames); return true; } } 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) { with_stripctrl(san, fname) printf("%s: open for read: %s\n", san, fxp_error()); return false; } if (restart) { file = open_existing_wfile(outfname, NULL); } else { file = open_new_file(outfname, GET_PERMISSIONS(attrs, -1)); } if (!file) { with_stripctrl(san, outfname) printf("local: unable to open %s\n", san); req = fxp_close_send(fh); pktin = sftp_wait_for_reply(req); fxp_close_recv(pktin, req); return false; } if (restart) { if (seek_file(file, 0, FROM_END) == -1) { close_wfile(file); with_stripctrl(san, outfname) printf("reget: cannot restart %s - file too large\n", san); req = fxp_close_send(fh); pktin = sftp_wait_for_reply(req); fxp_close_recv(pktin, req); return false; } offset = get_file_posn(file); printf("reget: restarting at file position %"PRIu64"\n", offset); } else { offset = 0; } with_stripctrl(san, fname) { with_stripctrl(sano, outfname) printf("remote:%s => local:%s\n", san, sano); } /* * FIXME: we can use FXP_FSTAT here to get the file size, and * thus put up a progress bar. */ toret = true; xfer = xfer_download_init(fh, offset); while (!xfer_done(xfer)) { void *vbuf; int retd, len; int wpos, wlen; xfer_download_queue(xfer); pktin = sftp_recv(); retd = xfer_download_gotpkt(xfer, pktin); if (retd <= 0) { if (!shown_err) { printf("error while reading: %s\n", fxp_error()); shown_err = true; } if (retd == INT_MIN) /* pktin not even freed */ sfree(pktin); toret = false; } while (xfer_download_data(xfer, &vbuf, &len)) { unsigned char *buf = (unsigned char *)vbuf; wpos = 0; while (wpos < len) { wlen = write_to_file(file, buf + wpos, len - wpos); if (wlen <= 0) { printf("error while writing local file\n"); toret = false; xfer_set_error(xfer); break; } wpos += wlen; } if (wpos < len) { /* we had an error */ toret = false; xfer_set_error(xfer); } sfree(vbuf); } } xfer_cleanup(xfer); close_wfile(file); req = fxp_close_send(fh); pktin = sftp_wait_for_reply(req); fxp_close_recv(pktin, req); return toret; } bool sftp_put_file(char *fname, char *outfname, bool recurse, bool restart) { struct fxp_handle *fh; struct fxp_xfer *xfer; struct sftp_packet *pktin; struct sftp_request *req; uint64_t offset; RFile *file; bool err = false, 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) { bool result; size_t nnames, namesize; char *name, **ournames; const char *opendir_err; DirHandle *dh; size_t 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, NULL); pktin = sftp_wait_for_reply(req); result = fxp_mkdir_recv(pktin, req); if (!result) { printf("%s: create directory: %s\n", outfname, fxp_error()); return false; } } /* * Now get the list of filenames in the local directory. */ nnames = namesize = 0; ournames = NULL; dh = open_directory(fname, &opendir_err); if (!dh) { printf("%s: unable to open directory: %s\n", fname, opendir_err); return false; } while ((name = read_filename(dh)) != NULL) { sgrowarray(ournames, namesize, nnames); ournames[nnames++] = name; } close_directory(dh); /* * 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]); 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; bool retd; nextfname = dir_file_cat(fname, ournames[i]); nextoutfname = dupcat(outfname, "/", ournames[i]); retd = sftp_put_file(nextfname, nextoutfname, recurse, restart); restart = false; /* after first partial file, do full */ sfree(nextoutfname); sfree(nextfname); if (!retd) { for (size_t i = 0; i < nnames; i++) { sfree(ournames[i]); } sfree(ournames); return false; } } /* * Done this recursion level. Free everything. */ for (size_t i = 0; i < nnames; i++) { sfree(ournames[i]); } sfree(ournames); return true; } file = open_existing_file(fname, NULL, NULL, NULL, &permissions); if (!file) { printf("local: unable to open %s\n", fname); return false; } 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 false; } if (restart) { struct fxp_attrs attrs; bool retd; req = fxp_fstat_send(fh); pktin = sftp_wait_for_reply(req); retd = fxp_fstat_recv(pktin, req, &attrs); if (!retd) { printf("read size of %s: %s\n", outfname, fxp_error()); err = true; goto cleanup; } if (!(attrs.flags & SSH_FILEXFER_ATTR_SIZE)) { printf("read size of %s: size was not given\n", outfname); err = true; goto cleanup; } offset = attrs.size; printf("reput: restarting at file position %"PRIu64"\n", offset); if (seek_file((WFile *)file, offset, FROM_START) != 0) seek_file((WFile *)file, 0, FROM_END); /* *shrug* */ } else { offset = 0; } printf("local:%s => remote:%s\n", fname, outfname); /* * FIXME: we can use FXP_FSTAT here to get the file size, and * thus put up a progress bar. */ xfer = xfer_upload_init(fh, offset); eof = false; while ((!err && !eof) || !xfer_done(xfer)) { char buffer[4096]; int len, ret; while (xfer_upload_ready(xfer) && !err && !eof) { len = read_from_file(file, buffer, sizeof(buffer)); if (len == -1) { printf("error while reading local file\n"); err = true; } else if (len == 0) { eof = true; } else { xfer_upload_data(xfer, buffer, len); } } if (toplevel_callback_pending() && !err && !eof) { /* If we have pending callbacks, they might make * xfer_upload_ready start to return true. So we should * run them and then re-check xfer_upload_ready, before * we go as far as waiting for an entire packet to * arrive. */ run_toplevel_callbacks(); continue; } 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 = true; } } } } xfer_cleanup(xfer); cleanup: req = fxp_close_send(fh); pktin = sftp_wait_for_reply(req); if (!fxp_close_recv(pktin, req)) { if (!err) { printf("error while closing: %s", fxp_error()); err = true; } } close_rfile(file); return !err; } /* ---------------------------------------------------------------------- * A remote wildcard matcher, providing a similar interface to the * local one in psftp.h. */ typedef struct SftpWildcardMatcher { struct fxp_handle *dirh; struct fxp_names *names; 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; bool check; SftpWildcardMatcher *swcm; struct fxp_handle *dirh; /* * We don't handle multi-level wildcards; so we expect to find * a fully specified directory part, followed by a wildcard * after that. */ wildcard = stripslashes(name, false); unwcdir = dupstr(name); len = wildcard - name; unwcdir[len] = '\0'; if (len > 0 && unwcdir[len-1] == '/') unwcdir[len-1] = '\0'; tmpdir = snewn(1 + len, char); check = wc_unescape(tmpdir, unwcdir); sfree(tmpdir); if (!check) { 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; } else { printf("Unable to open %s: %s\n", cdir, fxp_error()); swcm = NULL; sfree(unwcdir); } sfree(cdir); 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) { with_stripctrl(san, swcm->prefix) printf("%s: reading directory: %s\n", san, 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++]; if (!strcmp(name->filename, ".") || !strcmp(name->filename, "..")) continue; /* expected bad filenames */ if (!vet_filename(name->filename)) { with_stripctrl(san, name->filename) printf("ignoring potentially dangerous server-" "supplied filename '%s'\n", san); continue; /* unexpected bad filename */ } if (!wc_match(swcm->wildcard, name->filename)) continue; /* doesn't match the wildcard */ /* * We have a working filename. Return it. */ return dupprintf("%s%s%s", swcm->prefix, (!swcm->prefix[0] || swcm->prefix[strlen(swcm->prefix)-1]=='/' ? "" : "/"), 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); sfree(swcm); } /* * General function to match a potential wildcard in a filename * argument and iterate over every matching file. Used in several * PSFTP commands (rmdir, rm, chmod, mv). */ bool wildcard_iterate(char *filename, bool (*func)(void *, char *), void *ctx) { char *unwcfname, *newname, *cname; bool is_wc, toret; unwcfname = snewn(strlen(filename)+1, char); is_wc = !wc_unescape(unwcfname, filename); if (is_wc) { SftpWildcardMatcher *swcm = sftp_begin_wildcard_matching(filename); bool matched = false; sfree(unwcfname); if (!swcm) return false; toret = true; while ( (newname = sftp_wildcard_get_filename(swcm)) != NULL ) { cname = canonify(newname); sfree(newname); matched = true; if (!func(ctx, cname)) toret = false; sfree(cname); } if (!matched) { /* Politely warn the user that nothing matched. */ printf("%s: nothing matched\n", filename); } sftp_finish_wildcard_matching(swcm); } else { cname = canonify(unwcfname); toret = func(ctx, cname); sfree(cname); sfree(unwcfname); } return toret; } /* * Handy helper function. */ bool is_wildcard(char *name) { char *unwcfname = snewn(strlen(name)+1, char); bool is_wc = !wc_unescape(unwcfname, name); sfree(unwcfname); return is_wc; } /* ---------------------------------------------------------------------- * Actual sftp commands. */ struct sftp_command { char **words; size_t nwords, wordssize; int (*obey) (struct sftp_command *); /* returns <0 to quit */ }; int sftp_cmd_null(struct sftp_command *cmd) { return 1; /* success */ } int sftp_cmd_unknown(struct sftp_command *cmd) { printf("psftp: unknown command \"%s\"\n", cmd->words[0]); return 0; /* failure */ } int sftp_cmd_quit(struct sftp_command *cmd) { return -1; } int sftp_cmd_close(struct sftp_command *cmd) { if (!backend) { not_connected(); return 0; } if (backend_connected(backend)) { char ch; backend_special(backend, SS_EOF, 0); sent_eof = true; sftp_recvdata(&ch, 1); } do_sftp_cleanup(); return 0; } void list_directory_from_sftp_warn_unsorted(void) { printf("Directory is too large to sort; writing file names unsorted\n"); } void list_directory_from_sftp_print(struct fxp_name *name) { with_stripctrl(san, name->longname) printf("%s\n", san); } /* * 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; const char *dir; char *cdir, *unwcdir, *wildcard; struct sftp_packet *pktin; struct sftp_request *req; if (!backend) { 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; bool check; sfree(unwcdir); wildcard = stripslashes(dir, false); unwcdir = dupstr(dir); len = wildcard - dir; unwcdir[len] = '\0'; if (len > 0 && unwcdir[len-1] == '/') unwcdir[len-1] = '\0'; tmpdir = snewn(1 + len, char); check = wc_unescape(tmpdir, unwcdir); sfree(tmpdir); if (!check) { printf("Multiple-level wildcards are not supported\n"); sfree(unwcdir); return 0; } dir = unwcdir; } cdir = canonify(dir); with_stripctrl(san, cdir) printf("Listing directory %s\n", san); 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()); sfree(cdir); sfree(unwcdir); return 0; } else { struct list_directory_from_sftp_ctx *ctx = list_directory_from_sftp_new(); 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; } if (names->nnames == 0) { fxp_free_names(names); break; } for (size_t i = 0; i < names->nnames; i++) if (!wildcard || wc_match(wildcard, names->names[i].filename)) list_directory_from_sftp_feed(ctx, &names->names[i]); fxp_free_names(names); } req = fxp_close_send(dirh); pktin = sftp_wait_for_reply(req); fxp_close_recv(pktin, req); list_directory_from_sftp_finish(ctx); list_directory_from_sftp_free(ctx); } sfree(cdir); sfree(unwcdir); return 1; } /* * 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 (!backend) { not_connected(); return 0; } if (cmd->nwords < 2) dir = dupstr(homedir); else { dir = canonify(cmd->words[1]); } req = fxp_opendir_send(dir); pktin = sftp_wait_for_reply(req); dirh = fxp_opendir_recv(pktin, req); if (!dirh) { with_stripctrl(san, dir) printf("Directory %s: %s\n", san, 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; with_stripctrl(san, pwd) printf("Remote directory is now %s\n", san); return 1; } /* * Print current directory. Easy as pie. */ int sftp_cmd_pwd(struct sftp_command *cmd) { if (!backend) { not_connected(); return 0; } with_stripctrl(san, pwd) printf("Remote directory is %s\n", san); return 1; } /* * Get a file and save it at the local end. We have three very * similar commands here. The basic one is `get'; `reget' differs * in that it checks for the existence of the destination file and * starts from where a previous aborted transfer left off; `mget' * differs in that it interprets all its arguments as files to * transfer (never as a different local name for a remote file) and * can handle wildcards. */ int sftp_general_get(struct sftp_command *cmd, bool restart, bool multiple) { char *fname, *unwcfname, *origfname, *origwfname, *outfname; int i, toret; bool recurse = false; if (!backend) { not_connected(); return 0; } i = 1; while (i < cmd->nwords && cmd->words[i][0] == '-') { if (!strcmp(cmd->words[i], "--")) { /* finish processing options */ i++; break; } else if (!strcmp(cmd->words[i], "-r")) { recurse = true; } else { printf("%s: unrecognised option '%s'\n", cmd->words[0], cmd->words[i]); return 0; } i++; } if (i >= cmd->nwords) { printf("%s: expects a filename\n", cmd->words[0]); return 0; } toret = 1; do { SftpWildcardMatcher *swcm; origfname = cmd->words[i++]; unwcfname = snewn(strlen(origfname)+1, char); if (multiple && !wc_unescape(unwcfname, origfname)) { swcm = sftp_begin_wildcard_matching(origfname); if (!swcm) { sfree(unwcfname); continue; } origwfname = sftp_wildcard_get_filename(swcm); if (!origwfname) { /* Politely warn the user that nothing matched. */ printf("%s: nothing matched\n", origfname); sftp_finish_wildcard_matching(swcm); sfree(unwcfname); continue; } } else { origwfname = origfname; swcm = NULL; } while (origwfname) { fname = canonify(origwfname); if (!multiple && i < cmd->nwords) outfname = cmd->words[i++]; else outfname = stripslashes(origwfname, false); toret = sftp_get_file(fname, outfname, recurse, restart); sfree(fname); if (swcm) { sfree(origwfname); origwfname = sftp_wildcard_get_filename(swcm); } else { origwfname = NULL; } } sfree(unwcfname); if (swcm) sftp_finish_wildcard_matching(swcm); if (!toret) return toret; } while (multiple && i < cmd->nwords); return toret; } int sftp_cmd_get(struct sftp_command *cmd) { return sftp_general_get(cmd, false, false); } int sftp_cmd_mget(struct sftp_command *cmd) { return sftp_general_get(cmd, false, true); } int sftp_cmd_reget(struct sftp_command *cmd) { return sftp_general_get(cmd, true, false); } /* * Send a file and store it at the remote end. We have three very * similar commands here. The basic one is `put'; `reput' differs * in that it checks for the existence of the destination file and * starts from where a previous aborted transfer left off; `mput' * differs in that it interprets all its arguments as files to * transfer (never as a different remote name for a local file) and * can handle wildcards. */ int sftp_general_put(struct sftp_command *cmd, bool restart, bool multiple) { char *fname, *wfname, *origoutfname, *outfname; int i; int toret; bool recurse = false; if (!backend) { not_connected(); return 0; } i = 1; while (i < cmd->nwords && cmd->words[i][0] == '-') { if (!strcmp(cmd->words[i], "--")) { /* finish processing options */ i++; break; } else if (!strcmp(cmd->words[i], "-r")) { recurse = true; } else { printf("%s: unrecognised option '%s'\n", cmd->words[0], cmd->words[i]); return 0; } i++; } if (i >= cmd->nwords) { printf("%s: expects a filename\n", cmd->words[0]); return 0; } toret = 1; do { WildcardMatcher *wcm; fname = cmd->words[i++]; if (multiple && test_wildcard(fname, false) == WCTYPE_WILDCARD) { wcm = begin_wildcard_matching(fname); wfname = wildcard_get_filename(wcm); if (!wfname) { /* Politely warn the user that nothing matched. */ printf("%s: nothing matched\n", fname); finish_wildcard_matching(wcm); continue; } } else { wfname = fname; wcm = NULL; } while (wfname) { if (!multiple && i < cmd->nwords) origoutfname = cmd->words[i++]; else origoutfname = stripslashes(wfname, true); outfname = canonify(origoutfname); toret = sftp_put_file(wfname, outfname, recurse, restart); sfree(outfname); if (wcm) { sfree(wfname); wfname = wildcard_get_filename(wcm); } else { wfname = NULL; } } if (wcm) finish_wildcard_matching(wcm); if (!toret) return toret; } while (multiple && i < cmd->nwords); return toret; } int sftp_cmd_put(struct sftp_command *cmd) { return sftp_general_put(cmd, false, false); } int sftp_cmd_mput(struct sftp_command *cmd) { return sftp_general_put(cmd, false, true); } int sftp_cmd_reput(struct sftp_command *cmd) { return sftp_general_put(cmd, true, false); } int sftp_cmd_mkdir(struct sftp_command *cmd) { char *dir; struct sftp_packet *pktin; struct sftp_request *req; bool result; int i, ret; if (!backend) { not_connected(); return 0; } if (cmd->nwords < 2) { printf("mkdir: expects a directory\n"); return 0; } ret = 1; for (i = 1; i < cmd->nwords; i++) { dir = canonify(cmd->words[i]); req = fxp_mkdir_send(dir, NULL); pktin = sftp_wait_for_reply(req); result = fxp_mkdir_recv(pktin, req); if (!result) { with_stripctrl(san, dir) printf("mkdir %s: %s\n", san, fxp_error()); ret = 0; } else with_stripctrl(san, dir) printf("mkdir %s: OK\n", san); sfree(dir); } return ret; } static bool sftp_action_rmdir(void *vctx, char *dir) { struct sftp_packet *pktin; struct sftp_request *req; bool 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 false; } printf("rmdir %s: OK\n", dir); return true; } int sftp_cmd_rmdir(struct sftp_command *cmd) { int i, ret; if (!backend) { not_connected(); return 0; } if (cmd->nwords < 2) { printf("rmdir: expects a directory\n"); return 0; } ret = 1; for (i = 1; i < cmd->nwords; i++) ret &= wildcard_iterate(cmd->words[i], sftp_action_rmdir, NULL); return ret; } static bool sftp_action_rm(void *vctx, char *fname) { struct sftp_packet *pktin; struct sftp_request *req; bool 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 false; } printf("rm %s: OK\n", fname); return true; } int sftp_cmd_rm(struct sftp_command *cmd) { int i, ret; if (!backend) { not_connected(); return 0; } if (cmd->nwords < 2) { printf("rm: expects a filename\n"); return 0; } ret = 1; for (i = 1; i < cmd->nwords; i++) ret &= wildcard_iterate(cmd->words[i], sftp_action_rm, NULL); return ret; } static bool check_is_dir(char *dstfname) { struct sftp_packet *pktin; struct sftp_request *req; struct fxp_attrs attrs; bool 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; bool dest_is_dir; }; static bool 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; bool toret, result; if (ctx->dest_is_dir) { char *p; char *newname; p = srcfname + strlen(srcfname); while (p > srcfname && p[-1] != '/') p--; newname = dupcat(ctx->dstfname, "/", p); newcanon = canonify(newname); 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) { with_stripctrl(san, finalfname) printf("mv %s %s: %s\n", srcfname, san, error); toret = false; } else { with_stripctrl(san, finalfname) printf("%s -> %s\n", srcfname, san); toret = true; } sfree(newcanon); return toret; } int sftp_cmd_mv(struct sftp_command *cmd) { struct sftp_context_mv ctx[1]; int i, ret; if (!backend) { not_connected(); return 0; } if (cmd->nwords < 3) { printf("mv: expects two filenames\n"); return 0; } ctx->dstfname = canonify(cmd->words[cmd->nwords-1]); /* * If there's more than one source argument, or one source * argument which is a wildcard, we _require_ that the * destination is a directory. */ ctx->dest_is_dir = check_is_dir(ctx->dstfname); if ((cmd->nwords > 3 || is_wildcard(cmd->words[1])) && !ctx->dest_is_dir) { printf("mv: multiple or wildcard arguments require the destination" " to be a directory\n"); sfree(ctx->dstfname); return 0; } /* * Now iterate over the source arguments. */ ret = 1; for (i = 1; i < cmd->nwords-1; i++) ret &= wildcard_iterate(cmd->words[i], sftp_action_mv, ctx); sfree(ctx->dstfname); return ret; } struct sftp_context_chmod { unsigned attrs_clr, attrs_xor; }; static bool sftp_action_chmod(void *vctx, char *fname) { struct fxp_attrs attrs; struct sftp_packet *pktin; struct sftp_request *req; bool 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 false; } 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 true; /* 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 false; } printf("%s: %04o -> %04o\n", fname, oldperms, newperms); return true; } int sftp_cmd_chmod(struct sftp_command *cmd) { char *mode; int i, ret; struct sftp_context_chmod ctx[1]; if (!backend) { not_connected(); return 0; } if (cmd->nwords < 3) { printf("chmod: expects a mode specifier and a filename\n"); return 0; } /* * Attempt to parse the mode specifier in cmd->words[1]. We * don't support the full horror of Unix chmod; instead we * support a much simpler syntax in which the user can either * specify an octal number, or a comma-separated sequence of * [ugoa]*[-+=][rwxst]+. (The initial [ugoa] sequence may * _only_ be omitted if the only attribute mentioned is t, * since all others require a user/group/other specification. * Additionally, the s attribute may not be specified for any * [ugoa] specifications other than exactly u or exactly g. */ ctx->attrs_clr = ctx->attrs_xor = 0; mode = cmd->words[1]; if (mode[0] >= '0' && mode[0] <= '9') { if (mode[strspn(mode, "01234567")]) { printf("chmod: numeric file modes should" " contain digits 0-7 only\n"); return 0; } ctx->attrs_clr = 07777; sscanf(mode, "%o", &ctx->attrs_xor); ctx->attrs_xor &= ctx->attrs_clr; } else { while (*mode) { char *modebegin = mode; unsigned subset, perms; int action; subset = 0; while (*mode && *mode != ',' && *mode != '+' && *mode != '-' && *mode != '=') { switch (*mode) { case 'u': subset |= 04700; break; /* setuid, user perms */ case 'g': subset |= 02070; break; /* setgid, group perms */ case 'o': subset |= 00007; break; /* just other perms */ case 'a': subset |= 06777; break; /* all of the above */ default: printf("chmod: file mode '%.*s' contains unrecognised" " user/group/other specifier '%c'\n", (int)strcspn(modebegin, ","), modebegin, *mode); return 0; } mode++; } if (!*mode || *mode == ',') { printf("chmod: file mode '%.*s' is incomplete\n", (int)strcspn(modebegin, ","), modebegin); return 0; } action = *mode++; if (!*mode || *mode == ',') { printf("chmod: file mode '%.*s' is incomplete\n", (int)strcspn(modebegin, ","), modebegin); return 0; } perms = 0; while (*mode && *mode != ',') { switch (*mode) { case 'r': perms |= 00444; break; case 'w': perms |= 00222; break; case 'x': perms |= 00111; break; case 't': perms |= 01000; subset |= 01000; break; case 's': if ((subset & 06777) != 04700 && (subset & 06777) != 02070) { printf("chmod: file mode '%.*s': set[ug]id bit should" " be used with exactly one of u or g only\n", (int)strcspn(modebegin, ","), modebegin); return 0; } perms |= 06000; break; default: printf("chmod: file mode '%.*s' contains unrecognised" " permission specifier '%c'\n", (int)strcspn(modebegin, ","), modebegin, *mode); return 0; } mode++; } if (!(subset & 06777) && (perms &~ subset)) { printf("chmod: file mode '%.*s' contains no user/group/other" " specifier and permissions other than 't' \n", (int)strcspn(modebegin, ","), modebegin); return 0; } perms &= subset; switch (action) { case '+': ctx->attrs_clr |= perms; ctx->attrs_xor |= perms; break; case '-': ctx->attrs_clr |= perms; ctx->attrs_xor &= ~perms; break; case '=': ctx->attrs_clr |= subset; ctx->attrs_xor |= perms; break; } if (*mode) mode++; /* eat comma */ } } ret = 1; for (i = 2; i < cmd->nwords; i++) ret &= wildcard_iterate(cmd->words[i], sftp_action_chmod, ctx); return ret; } static int sftp_cmd_open(struct sftp_command *cmd) { int portnumber; if (backend) { printf("psftp: already connected\n"); return 0; } if (cmd->nwords < 2) { printf("open: expects a host name\n"); return 0; } if (cmd->nwords > 2) { portnumber = atoi(cmd->words[2]); if (portnumber == 0) { printf("open: invalid port number\n"); return 0; } } else portnumber = 0; if (psftp_connect(cmd->words[1], NULL, portnumber)) { backend = NULL; /* connection is already closed */ return -1; /* this is fatal */ } do_sftp_init(); return 1; } static int sftp_cmd_lcd(struct sftp_command *cmd) { char *currdir, *errmsg; if (cmd->nwords < 2) { printf("lcd: expects a local directory name\n"); return 0; } errmsg = psftp_lcd(cmd->words[1]); if (errmsg) { printf("lcd: unable to change directory: %s\n", errmsg); sfree(errmsg); return 0; } currdir = psftp_getcwd(); printf("New local directory is %s\n", currdir); sfree(currdir); return 1; } static int sftp_cmd_lpwd(struct sftp_command *cmd) { char *currdir; currdir = psftp_getcwd(); printf("Current local directory is %s\n", currdir); sfree(currdir); return 1; } static int sftp_cmd_pling(struct sftp_command *cmd) { int exitcode; exitcode = system(cmd->words[1]); return (exitcode == 0); } static int sftp_cmd_help(struct sftp_command *cmd); static struct sftp_cmd_lookup { const char *name; /* * For help purposes, there are two kinds of command: * * - primary commands, in which `longhelp' is non-NULL. In * this case `shorthelp' is descriptive text, and `longhelp' * is longer descriptive text intended to be printed after * the command name. * * - alias commands, in which `longhelp' is NULL. In this case * `shorthelp' is the name of a primary command, which * contains the help that should double up for this command. */ bool listed; /* do we list this in primary help? */ const char *shorthelp; const char *longhelp; int (*obey) (struct sftp_command *); } sftp_lookup[] = { /* * List of sftp commands. This is binary-searched so it MUST be * in ASCII order. */ { "!", true, "run a local command", "\n" /* FIXME: this example is crap for non-Windows. */ " Runs a local command. For example, \"!del myfile\".\n", sftp_cmd_pling }, { "bye", true, "finish your SFTP session", "\n" " Terminates your SFTP session and quits the PSFTP program.\n", sftp_cmd_quit }, { "cd", true, "change your remote working directory", " [ ]\n" " Change the remote working directory for your SFTP session.\n" " If a new working directory is not supplied, you will be\n" " returned to your home directory.\n", sftp_cmd_cd }, { "chmod", true, "change file permissions and modes", " [ ... ]\n" " Change the file permissions on one or more remote files or\n" " directories.\n" " can be any octal Unix permission specifier.\n" " Alternatively, can include the following modifiers:\n" " u+r make file readable by owning user\n" " u+w make file writable by owning user\n" " u+x make file executable by owning user\n" " u-r make file not readable by owning user\n" " [also u-w, u-x]\n" " g+r make file readable by members of owning group\n" " [also g+w, g+x, g-r, g-w, g-x]\n" " o+r make file readable by all other users\n" " [also o+w, o+x, o-r, o-w, o-x]\n" " a+r make file readable by absolutely everybody\n" " [also a+w, a+x, a-r, a-w, a-x]\n" " u+s enable the Unix set-user-ID bit\n" " u-s disable the Unix set-user-ID bit\n" " g+s enable the Unix set-group-ID bit\n" " g-s disable the Unix set-group-ID bit\n" " +t enable the Unix \"sticky bit\"\n" " You can give more than one modifier for the same user (\"g-rwx\"), and\n" " more than one user for the same modifier (\"ug+w\"). You can\n" " use commas to separate different modifiers (\"u+rwx,g+s\").\n", sftp_cmd_chmod }, { "close", true, "finish your SFTP session but do not quit PSFTP", "\n" " Terminates your SFTP session, but does not quit the PSFTP\n" " program. You can then use \"open\" to start another SFTP\n" " session, to the same server or to a different one.\n", sftp_cmd_close }, { "del", true, "delete files on the remote server", " [ ... ]\n" " Delete a file or files from the server.\n", sftp_cmd_rm }, { "delete", false, "del", NULL, sftp_cmd_rm }, { "dir", true, "list remote files", " [ ]/[ ]\n" " List the contents of a specified directory on the server.\n" " If is not given, the current working directory\n" " is assumed.\n" " If is given, it is treated as a set of files to\n" " list; otherwise, all files are listed.\n", sftp_cmd_ls }, { "exit", true, "bye", NULL, sftp_cmd_quit }, { "get", true, "download a file from the server to your local machine", " [ -r ] [ -- ] [ ]\n" " Downloads a file on the server and stores it locally under\n" " the same name, or under a different one if you supply the\n" " argument .\n" " If -r specified, recursively fetch a directory.\n", sftp_cmd_get }, { "help", true, "give help", " [ [ ... ] ]\n" " Give general help if no commands are specified.\n" " If one or more commands are specified, give specific help on\n" " those particular commands.\n", sftp_cmd_help }, { "lcd", true, "change local working directory", " \n" " Change the local working directory of the PSFTP program (the\n" " default location where the \"get\" command will save files).\n", sftp_cmd_lcd }, { "lpwd", true, "print local working directory", "\n" " Print the local working directory of the PSFTP program (the\n" " default location where the \"get\" command will save files).\n", sftp_cmd_lpwd }, { "ls", true, "dir", NULL, sftp_cmd_ls }, { "mget", true, "download multiple files at once", " [ -r ] [ -- ] [ ... ]\n" " Downloads many files from the server, storing each one under\n" " the same name it has on the server side. You can use wildcards\n" " such as \"*.c\" to specify lots of files at once.\n" " If -r specified, recursively fetch files and directories.\n", sftp_cmd_mget }, { "mkdir", true, "create directories on the remote server", " [ ... ]\n" " Creates directories with the given names on the server.\n", sftp_cmd_mkdir }, { "mput", true, "upload multiple files at once", " [ -r ] [ -- ] [ ... ]\n" " Uploads many files to the server, storing each one under the\n" " same name it has on the client side. You can use wildcards\n" " such as \"*.c\" to specify lots of files at once.\n" " If -r specified, recursively store files and directories.\n", sftp_cmd_mput }, { "mv", true, "move or rename file(s) on the remote server", " [ ... ] \n" " Moves or renames (s) on the server to ,\n" " also on the server.\n" " If specifies an existing directory, then \n" " may be a wildcard, and multiple s may be given; all\n" " source files are moved into .\n" " Otherwise, must specify a single file, which is moved\n" " or renamed so that it is accessible under the name .\n", sftp_cmd_mv }, { "open", true, "connect to a host", " [@] []\n" " Establishes an SFTP connection to a given host. Only usable\n" " when you are not already connected to a server.\n", sftp_cmd_open }, { "put", true, "upload a file from your local machine to the server", " [ -r ] [ -- ] [ ]\n" " Uploads a file to the server and stores it there under\n" " the same name, or under a different one if you supply the\n" " argument .\n" " If -r specified, recursively store a directory.\n", sftp_cmd_put }, { "pwd", true, "print your remote working directory", "\n" " Print the current remote working directory for your SFTP session.\n", sftp_cmd_pwd }, { "quit", true, "bye", NULL, sftp_cmd_quit }, { "reget", true, "continue downloading files", " [ -r ] [ -- ] [ ]\n" " Works exactly like the \"get\" command, but the local file\n" " must already exist. The download will begin at the end of the\n" " file. This is for resuming a download that was interrupted.\n" " If -r specified, resume interrupted \"get -r\".\n", sftp_cmd_reget }, { "ren", true, "mv", NULL, sftp_cmd_mv }, { "rename", false, "mv", NULL, sftp_cmd_mv }, { "reput", true, "continue uploading files", " [ -r ] [ -- ] [ ]\n" " Works exactly like the \"put\" command, but the remote file\n" " must already exist. The upload will begin at the end of the\n" " file. This is for resuming an upload that was interrupted.\n" " If -r specified, resume interrupted \"put -r\".\n", sftp_cmd_reput }, { "rm", true, "del", NULL, sftp_cmd_rm }, { "rmdir", true, "remove directories on the remote server", " [ ... ]\n" " Removes the directory with the given name on the server.\n" " The directory will not be removed unless it is empty.\n" " Wildcards may be used to specify multiple directories.\n", sftp_cmd_rmdir } }; const struct sftp_cmd_lookup *lookup_command(const char *name) { int i, j, k, cmp; i = -1; j = lenof(sftp_lookup); while (j - i > 1) { k = (j + i) / 2; cmp = strcmp(name, sftp_lookup[k].name); if (cmp < 0) j = k; else if (cmp > 0) i = k; else { return &sftp_lookup[k]; } } return NULL; } static int sftp_cmd_help(struct sftp_command *cmd) { int i; if (cmd->nwords == 1) { /* * Give short help on each command. */ int maxlen; maxlen = 0; for (i = 0; i < lenof(sftp_lookup); i++) { int len; if (!sftp_lookup[i].listed) continue; len = strlen(sftp_lookup[i].name); if (maxlen < len) maxlen = len; } for (i = 0; i < lenof(sftp_lookup); i++) { const struct sftp_cmd_lookup *lookup; if (!sftp_lookup[i].listed) continue; lookup = &sftp_lookup[i]; printf("%-*s", maxlen+2, lookup->name); if (lookup->longhelp == NULL) lookup = lookup_command(lookup->shorthelp); printf("%s\n", lookup->shorthelp); } } else { /* * Give long help on specific commands. */ for (i = 1; i < cmd->nwords; i++) { const struct sftp_cmd_lookup *lookup; lookup = lookup_command(cmd->words[i]); if (!lookup) { printf("help: %s: command not found\n", cmd->words[i]); } else { printf("%s", lookup->name); if (lookup->longhelp == NULL) lookup = lookup_command(lookup->shorthelp); printf("%s", lookup->longhelp); } } } return 1; } /* ---------------------------------------------------------------------- * Command line reading and parsing. */ struct sftp_command *sftp_getcmd(FILE *fp, int mode, int modeflags) { char *line; struct sftp_command *cmd; char *p, *q, *r; bool quoting; cmd = snew(struct sftp_command); cmd->words = NULL; cmd->nwords = 0; cmd->wordssize = 0; line = NULL; if (fp) { if (modeflags & 1) printf("psftp> "); line = fgetline(fp); } else { line = ssh_sftp_get_cmdline("psftp> ", !backend); } 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); } p = line; while (*p && (*p == ' ' || *p == '\t')) p++; if (*p == '!') { /* * Special case: the ! command. This is always parsed as * exactly two words: one containing the !, and the second * containing everything else on the line. */ cmd->nwords = 2; sgrowarrayn(cmd->words, cmd->wordssize, cmd->nwords, 0); cmd->words[0] = dupstr("!"); cmd->words[1] = dupstr(p+1); } else if (*p == '#') { /* * Special case: comment. Entire line is ignored. */ cmd->nwords = cmd->wordssize = 0; } else { /* * Parse the command line into words. The syntax is: * - double quotes are removed, but cause spaces within to be * treated as non-separating. * - a double-doublequote pair is a literal double quote, inside * _or_ outside quotes. Like this: * * firstword "second word" "this has ""quotes"" in" and""this"" * * 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 = false; while (*p) { if (!quoting && (*p == ' ' || *p == '\t')) break; /* reached end of word */ else if (*p == '"' && p[1] == '"') p += 2, *r++ = '"'; /* a literal quote */ else if (*p == '"') p++, quoting = !quoting; else *r++ = *p++; } if (*p) p++; /* skip over the whitespace */ *r = '\0'; sgrowarray(cmd->words, cmd->wordssize, cmd->nwords); cmd->words[cmd->nwords++] = dupstr(q); } } sfree(line); /* * Now parse the first word and assign a function. */ if (cmd->nwords == 0) cmd->obey = sftp_cmd_null; else { const struct sftp_cmd_lookup *lookup; lookup = lookup_command(cmd->words[0]); if (!lookup) cmd->obey = sftp_cmd_unknown; else cmd->obey = lookup->obey; } return cmd; } static void sftp_cmd_free(struct sftp_command *cmd) { if (cmd->words) { for (size_t i = 0; i < cmd->nwords; i++) sfree(cmd->words[i]); sfree(cmd->words); } sfree(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 { with_stripctrl(san, homedir) printf("Remote working directory is %s\n", san); } pwd = dupstr(homedir); return 0; } static void do_sftp_cleanup(void) { char ch; if (backend) { backend_special(backend, SS_EOF, 0); sent_eof = true; sftp_recvdata(&ch, 1); backend_free(backend); sftp_cleanup_request(); backend = NULL; } if (pwd) { sfree(pwd); pwd = NULL; } if (homedir) { sfree(homedir); homedir = NULL; } } int do_sftp(int mode, int modeflags, char *batchfile) { FILE *fp; int ret; /* * Batch mode? */ if (mode == 0) { /* ------------------------------------------------------------------ * Now we're ready to do Real Stuff. */ while (1) { struct sftp_command *cmd; cmd = sftp_getcmd(NULL, 0, 0); if (!cmd) break; ret = cmd->obey(cmd); sftp_cmd_free(cmd); if (ret < 0) break; } } else { fp = fopen(batchfile, "r"); if (!fp) { printf("Fatal: unable to open %s\n", batchfile); return 1; } ret = 0; while (1) { struct sftp_command *cmd; cmd = sftp_getcmd(fp, mode, modeflags); if (!cmd) break; ret = cmd->obey(cmd); sftp_cmd_free(cmd); if (ret < 0) break; if (ret == 0) { if (!(modeflags & 2)) break; } } fclose(fp); /* * In batch mode, and if exit on command failure is enabled, * any command failure causes the whole of PSFTP to fail. */ if (ret == 0 && !(modeflags & 2)) return 2; } return 0; } /* ---------------------------------------------------------------------- * Dirty bits: integration with PuTTY. */ static bool verbose = false; void ldisc_echoedit_update(Ldisc *ldisc) { } /* * Receive a block of data from the SSH link. Block until all data * is available. * * To do this, we repeatedly call the SSH protocol module, with our * own psftp_output() function to catch the data that comes back. We * do this until we have enough data. */ static bufchain received_data; static BinarySink *stderr_bs; static size_t psftp_output( Seat *seat, bool is_stderr, const void *data, size_t len) { /* * stderr data is just spouted to local stderr (optionally via a * sanitiser) and otherwise ignored. */ if (is_stderr) { put_data(stderr_bs, data, len); return 0; } bufchain_add(&received_data, data, len); return 0; } static bool psftp_eof(Seat *seat) { /* * 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) { seat_connection_fatal( psftp_seat, "Received unexpected end-of-file from SFTP server"); } return false; } bool sftp_recvdata(char *buf, size_t len) { while (len > 0) { while (bufchain_size(&received_data) == 0) { if (backend_exitcode(backend) >= 0 || ssh_sftp_loop_iteration() < 0) return false; /* doom */ } size_t got = bufchain_fetch_consume_up_to(&received_data, buf, len); buf += got; len -= got; } return true; } bool sftp_senddata(const char *buf, size_t len) { backend_send(backend, buf, len); return true; } size_t sftp_sendbuffer(void) { return backend_sendbuffer(backend); } /* * Short description of parameters. */ static void usage(void) { printf("PuTTY Secure File Transfer (SFTP) client\n"); printf("%s\n", ver); printf("Usage: psftp [options] [user@]host\n"); printf("Options:\n"); printf(" -V print version information and exit\n"); printf(" -pgpfp print PGP key fingerprints and exit\n"); printf(" -b file use specified batchfile\n"); printf(" -bc output batchfile commands\n"); printf(" -be don't stop batchfile processing if errors\n"); printf(" -v show verbose messages\n"); printf(" -load sessname Load settings from saved session\n"); printf(" -l user connect with specified username\n"); printf(" -P port connect to specified port\n"); printf(" -pw passw login with specified password\n"); printf(" -1 -2 force use of particular SSH protocol version\n"); printf(" -ssh -ssh-connection\n"); printf(" force use of particular SSH protocol variant\n"); printf(" -4 -6 force use of IPv4 or IPv6\n"); printf(" -C enable compression\n"); printf(" -i key private key file for user authentication\n"); printf(" -noagent disable use of Pageant\n"); printf(" -agent enable use of Pageant\n"); printf(" -no-trivial-auth\n"); printf(" disconnect if SSH authentication succeeds trivially\n"); printf(" -hostkey keyid\n"); printf(" manually specify a host key (may be repeated)\n"); printf(" -batch disable all interactive prompts\n"); printf(" -no-sanitise-stderr don't strip control chars from" " standard error\n"); printf(" -proxycmd command\n"); printf(" use 'command' as local proxy\n"); printf(" -sshlog file\n"); printf(" -sshrawlog file\n"); printf(" log protocol details to a file\n"); printf(" -logoverwrite\n"); printf(" -logappend\n"); printf(" control what happens when a log file already exists\n"); cleanup_exit(1); } static void version(void) { char *buildinfo_text = buildinfo("\n"); printf("psftp: %s\n%s\n", ver, buildinfo_text); sfree(buildinfo_text); exit(0); } /* * Connect to a host. */ static int psftp_connect(char *userhost, char *user, int portnumber) { char *host, *realhost; const char *err; /* Separate host and username */ host = userhost; host = strrchr(host, '@'); if (host == NULL) { host = userhost; } else { *host++ = '\0'; if (user) { printf("psftp: multiple usernames specified; using \"%s\"\n", user); } else user = userhost; } /* * If we haven't loaded session details already (e.g., from -load), * try looking for a session called "host". */ if (!cmdline_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 protocol to SSH if the user has somehow contrived to * select one we don't support (e.g. by loading an inappropriate * saved session). In that situation we assume the port number is * useless too.) */ if (!backend_vt_from_proto(conf_get_int(conf, CONF_protocol))) { 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_bool(conf, CONF_x11_forward, false); conf_set_bool(conf, CONF_agentfwd, false); conf_set_bool(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_bool(conf, CONF_ssh_subsys, true); conf_set_bool(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_bool(conf, CONF_ssh_subsys2, false); psftp_logctx = log_init(console_cli_logpolicy, conf); platform_psftp_pre_conn_setup(console_cli_logpolicy); err = backend_init(backend_vt_from_proto( conf_get_int(conf, CONF_protocol)), psftp_seat, &backend, psftp_logctx, conf, conf_get_str(conf, CONF_host), conf_get_int(conf, CONF_port), &realhost, 0, conf_get_bool(conf, CONF_tcp_keepalives)); if (err != NULL) { fprintf(stderr, "ssh_init: %s\n", err); return 1; } while (!backend_sendok(backend)) { if (backend_exitcode(backend) >= 0) return 1; if (ssh_sftp_loop_iteration() < 0) { fprintf(stderr, "ssh_init: error during SSH connection setup\n"); return 1; } } if (verbose && realhost != NULL) printf("Connected to %s\n", realhost); if (realhost != NULL) sfree(realhost); return 0; } void cmdline_error(const char *p, ...) { va_list ap; fprintf(stderr, "psftp: "); va_start(ap, p); vfprintf(stderr, p, ap); va_end(ap); fprintf(stderr, "\n try typing \"psftp -h\" for help\n"); exit(1); } const bool share_can_be_downstream = true; const bool share_can_be_upstream = false; static stdio_sink stderr_ss; static StripCtrlChars *stderr_scc; const unsigned cmdline_tooltype = TOOLTYPE_FILETRANSFER; /* * Main program. Parse arguments etc. */ int psftp_main(int argc, char *argv[]) { int i, ret; int portnumber = 0; char *userhost, *user; int mode = 0; int modeflags = 0; bool sanitise_stderr = true; char *batchfile = NULL; sk_init(); userhost = user = NULL; /* Load Default Settings before doing anything else. */ conf = conf_new(); do_defaults(NULL, conf); 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+1mutable and the * other const->const :-( */ char *stripslashes(const char *str, bool local); /* ---------------------------------------------------------------------- * In psftpcommon.c */ /* * qsort comparison routine for fxp_name structures. Sorts by real * file name. */ int sftp_name_compare(const void *av, const void *bv); /* * Shared code for outputting a directory listing in response to a * stream of name structures from FXP_READDIR operations. Used by * psftp's ls command and pscp -ls. */ struct list_directory_from_sftp_ctx; struct fxp_name; /* in sftp.h */ struct list_directory_from_sftp_ctx *list_directory_from_sftp_new(void); void list_directory_from_sftp_feed(struct list_directory_from_sftp_ctx *ctx, struct fxp_name *name); void list_directory_from_sftp_finish(struct list_directory_from_sftp_ctx *ctx); void list_directory_from_sftp_free(struct list_directory_from_sftp_ctx *ctx); /* Callbacks provided by the tool front end */ void list_directory_from_sftp_warn_unsorted(void); void list_directory_from_sftp_print(struct fxp_name *name); #endif /* PUTTY_PSFTP_H */ putty-0.76/psftpcommon.c0000644000175000017500000000620514072266311012272 00000000000000/* * psftpcommon.c: front-end functionality shared between both file * transfer tools across platforms. (As opposed to sftpcommon.c, which * has *protocol*-level common code.) */ #include #include #include "putty.h" #include "sftp.h" #include "psftp.h" #define MAX_NAMES_MEMORY ((size_t)8 << 20) /* * qsort comparison routine for fxp_name structures. Sorts by real * file name. */ int sftp_name_compare(const void *av, const void *bv) { const struct fxp_name *const *a = (const struct fxp_name *const *) av; const struct fxp_name *const *b = (const struct fxp_name *const *) bv; return strcmp((*a)->filename, (*b)->filename); } struct list_directory_from_sftp_ctx { size_t nnames, namesize, total_memory; struct fxp_name **names; bool sorting; }; struct list_directory_from_sftp_ctx *list_directory_from_sftp_new(void) { struct list_directory_from_sftp_ctx *ctx = snew(struct list_directory_from_sftp_ctx); memset(ctx, 0, sizeof(*ctx)); ctx->sorting = true; return ctx; } void list_directory_from_sftp_free(struct list_directory_from_sftp_ctx *ctx) { for (size_t i = 0; i < ctx->nnames; i++) fxp_free_name(ctx->names[i]); sfree(ctx->names); sfree(ctx); } void list_directory_from_sftp_feed(struct list_directory_from_sftp_ctx *ctx, struct fxp_name *name) { if (ctx->sorting) { /* * Accumulate these filenames into an array that we'll sort - * unless the array gets _really_ big, in which case, to avoid * consuming all the client's memory, we fall back to * outputting the directory listing unsorted. */ size_t this_name_memory = sizeof(*ctx->names) + sizeof(**ctx->names) + strlen(name->filename) + strlen(name->longname); if (MAX_NAMES_MEMORY - ctx->total_memory < this_name_memory) { list_directory_from_sftp_warn_unsorted(); /* Output all the previously stored names. */ for (size_t i = 0; i < ctx->nnames; i++) { list_directory_from_sftp_print(ctx->names[i]); fxp_free_name(ctx->names[i]); } /* Don't store further names in that array. */ sfree(ctx->names); ctx->names = NULL; ctx->nnames = 0; ctx->namesize = 0; ctx->sorting = false; /* And don't forget to output the name passed in this * actual function call. */ list_directory_from_sftp_print(name); } else { sgrowarray(ctx->names, ctx->namesize, ctx->nnames); ctx->names[ctx->nnames++] = fxp_dup_name(name); ctx->total_memory += this_name_memory; } } else { list_directory_from_sftp_print(name); } } void list_directory_from_sftp_finish(struct list_directory_from_sftp_ctx *ctx) { if (ctx->nnames > 0) { assert(ctx->sorting); qsort(ctx->names, ctx->nnames, sizeof(*ctx->names), sftp_name_compare); for (size_t i = 0; i < ctx->nnames; i++) list_directory_from_sftp_print(ctx->names[i]); } } putty-0.76/psocks.c0000644000175000017500000004217614072266311011236 00000000000000/* * Platform-independent parts of a standalone SOCKS server program * based on the PuTTY SOCKS code. */ #include #include #include "putty.h" #include "misc.h" #include "ssh.h" #include "sshchan.h" #include "psocks.h" /* * Possible later TODOs: * * - verbosity setting for log messages * * - could import proxy.c and use name_lookup rather than * sk_namelookup, to allow forwarding via some other proxy type */ #define BUFLIMIT 16384 #define LOGBITS(X) \ X(CONNSTATUS) \ X(DIALOGUE) \ /* end of list */ #define BITINDEX_ENUM(x) LOG_##x##_bitindex, enum { LOGBITS(BITINDEX_ENUM) }; #define BITFLAG_ENUM(x) LOG_##x = 1 << LOG_##x##_bitindex, enum { LOGBITS(BITFLAG_ENUM) }; typedef struct psocks_connection psocks_connection; typedef enum RecordDestination { REC_NONE, REC_FILE, REC_PIPE } RecordDestination; struct psocks_state { const PsocksPlatform *platform; int listen_port; bool acceptall; PortFwdManager *portfwdmgr; uint64_t next_conn_index; FILE *logging_fp; unsigned log_flags; RecordDestination rec_dest; char *rec_cmd; strbuf *subcmd; ConnectionLayer cl; }; struct psocks_connection { psocks_state *ps; Channel *chan; char *host, *realhost; int port; SockAddr *addr; Socket *socket; bool connecting, eof_pfmgr_to_socket, eof_socket_to_pfmgr; uint64_t index; PsocksDataSink *rec_sink; Plug plug; SshChannel sc; }; static SshChannel *psocks_lportfwd_open( ConnectionLayer *cl, const char *hostname, int port, const char *description, const SocketPeerInfo *pi, Channel *chan); static const ConnectionLayerVtable psocks_clvt = { .lportfwd_open = psocks_lportfwd_open, /* everything else is NULL */ }; static size_t psocks_sc_write(SshChannel *sc, bool is_stderr, const void *, size_t); static void psocks_sc_write_eof(SshChannel *sc); static void psocks_sc_initiate_close(SshChannel *sc, const char *err); static void psocks_sc_unthrottle(SshChannel *sc, size_t bufsize); static const SshChannelVtable psocks_scvt = { .write = psocks_sc_write, .write_eof = psocks_sc_write_eof, .initiate_close = psocks_sc_initiate_close, .unthrottle = psocks_sc_unthrottle, /* all the rest are NULL */ }; static void psocks_plug_log(Plug *p, PlugLogType type, SockAddr *addr, int port, const char *error_msg, int error_code); static void psocks_plug_closing(Plug *p, const char *error_msg, int error_code, bool calling_back); static void psocks_plug_receive(Plug *p, int urgent, const char *data, size_t len); static void psocks_plug_sent(Plug *p, size_t bufsize); static const PlugVtable psocks_plugvt = { .log = psocks_plug_log, .closing = psocks_plug_closing, .receive = psocks_plug_receive, .sent = psocks_plug_sent, }; static void psocks_conn_log(psocks_connection *conn, const char *fmt, ...) { if (!conn->ps->logging_fp) return; va_list ap; va_start(ap, fmt); char *msg = dupvprintf(fmt, ap); va_end(ap); fprintf(conn->ps->logging_fp, "c#%"PRIu64": %s\n", conn->index, msg); sfree(msg); fflush(conn->ps->logging_fp); } static void psocks_conn_log_data(psocks_connection *conn, PsocksDirection dir, const void *vdata, size_t len) { if ((conn->ps->log_flags & LOG_DIALOGUE) && conn->ps->logging_fp) { const char *data = vdata; while (len > 0) { const char *nl = memchr(data, '\n', len); size_t thislen = nl ? (nl+1) - data : len; const char *thisdata = data; data += thislen; len -= thislen; static const char *const direction_names[2] = { [UP] = "send", [DN] = "recv" }; fprintf(conn->ps->logging_fp, "c#%"PRIu64": %s \"", conn->index, direction_names[dir]); write_c_string_literal(conn->ps->logging_fp, make_ptrlen(thisdata, thislen)); fprintf(conn->ps->logging_fp, "\"\n"); } fflush(conn->ps->logging_fp); } if (conn->rec_sink) put_data(conn->rec_sink->s[dir], vdata, len); } static void psocks_connection_establish(void *vctx); static SshChannel *psocks_lportfwd_open( ConnectionLayer *cl, const char *hostname, int port, const char *description, const SocketPeerInfo *pi, Channel *chan) { psocks_state *ps = container_of(cl, psocks_state, cl); psocks_connection *conn = snew(psocks_connection); memset(conn, 0, sizeof(*conn)); conn->ps = ps; conn->sc.vt = &psocks_scvt; conn->plug.vt = &psocks_plugvt; conn->chan = chan; conn->host = dupstr(hostname); conn->port = port; conn->index = ps->next_conn_index++; if (conn->ps->log_flags & LOG_CONNSTATUS) psocks_conn_log(conn, "request from %s for %s port %d", pi->log_text, hostname, port); switch (conn->ps->rec_dest) { case REC_FILE: { char *fnames[2]; FILE *fp[2]; bool ok = true; static const char *const direction_names[2] = { [UP] = "sockout", [DN] = "sockin" }; for (size_t i = 0; i < 2; i++) { fnames[i] = dupprintf("%s.%"PRIu64, direction_names[i], conn->index); fp[i] = fopen(fnames[i], "wb"); if (!fp[i]) { psocks_conn_log(conn, "cannot log this connection: " "creating file '%s': %s", fnames[i], strerror(errno)); ok = false; } } if (ok) { if (conn->ps->log_flags & LOG_CONNSTATUS) psocks_conn_log(conn, "logging to '%s' / '%s'", fnames[0], fnames[1]); conn->rec_sink = pds_stdio(fp); } else { for (size_t i = 0; i < 2; i++) { if (fp[i]) { remove(fnames[i]); fclose(fp[i]); } } } for (size_t i = 0; i < 2; i++) sfree(fnames[i]); } break; case REC_PIPE: { static const char *const direction_args[2] = { [UP] = "out", [DN] = "in" }; char *index_arg = dupprintf("%"PRIu64, conn->index); char *err; conn->rec_sink = conn->ps->platform->open_pipes( conn->ps->rec_cmd, direction_args, index_arg, &err); if (!conn->rec_sink) { psocks_conn_log(conn, "cannot log this connection: " "creating pipes: %s", err); sfree(err); } sfree(index_arg); } break; default: break; } queue_toplevel_callback(psocks_connection_establish, conn); return &conn->sc; } static void psocks_conn_free(psocks_connection *conn) { if (conn->ps->log_flags & LOG_CONNSTATUS) psocks_conn_log(conn, "closed"); sfree(conn->host); sfree(conn->realhost); if (conn->socket) sk_close(conn->socket); if (conn->chan) chan_free(conn->chan); if (conn->rec_sink) pds_free(conn->rec_sink); delete_callbacks_for_context(conn); sfree(conn); } static void psocks_connection_establish(void *vctx) { psocks_connection *conn = (psocks_connection *)vctx; /* * Look up destination host name. */ conn->addr = sk_namelookup(conn->host, &conn->realhost, ADDRTYPE_UNSPEC); const char *err = sk_addr_error(conn->addr); if (err) { char *msg = dupprintf("name lookup failed: %s", err); chan_open_failed(conn->chan, msg); sfree(msg); psocks_conn_free(conn); return; } /* * Make the connection. */ conn->connecting = true; conn->socket = sk_new(conn->addr, conn->port, false, false, false, false, &conn->plug); } static size_t psocks_sc_write(SshChannel *sc, bool is_stderr, const void *data, size_t len) { psocks_connection *conn = container_of(sc, psocks_connection, sc); if (!conn->socket) return 0; psocks_conn_log_data(conn, UP, data, len); return sk_write(conn->socket, data, len); } static void psocks_check_close(void *vctx) { psocks_connection *conn = (psocks_connection *)vctx; if (chan_want_close(conn->chan, conn->eof_pfmgr_to_socket, conn->eof_socket_to_pfmgr)) psocks_conn_free(conn); } static void psocks_sc_write_eof(SshChannel *sc) { psocks_connection *conn = container_of(sc, psocks_connection, sc); if (!conn->socket) return; sk_write_eof(conn->socket); conn->eof_pfmgr_to_socket = true; if (conn->ps->log_flags & LOG_DIALOGUE) psocks_conn_log(conn, "send eof"); queue_toplevel_callback(psocks_check_close, conn); } static void psocks_sc_initiate_close(SshChannel *sc, const char *err) { psocks_connection *conn = container_of(sc, psocks_connection, sc); sk_close(conn->socket); conn->socket = NULL; } static void psocks_sc_unthrottle(SshChannel *sc, size_t bufsize) { psocks_connection *conn = container_of(sc, psocks_connection, sc); if (bufsize < BUFLIMIT) sk_set_frozen(conn->socket, false); } static void psocks_plug_log(Plug *plug, PlugLogType type, SockAddr *addr, int port, const char *error_msg, int error_code) { psocks_connection *conn = container_of(plug, psocks_connection, plug); char addrbuf[256]; if (!(conn->ps->log_flags & LOG_CONNSTATUS)) return; switch (type) { case PLUGLOG_CONNECT_TRYING: sk_getaddr(addr, addrbuf, sizeof(addrbuf)); if (sk_addr_needs_port(addr)) psocks_conn_log(conn, "trying to connect to %s port %d", addrbuf, port); else psocks_conn_log(conn, "trying to connect to %s", addrbuf); break; case PLUGLOG_CONNECT_FAILED: psocks_conn_log(conn, "connection attempt failed: %s", error_msg); break; case PLUGLOG_CONNECT_SUCCESS: psocks_conn_log(conn, "connection established", error_msg); if (conn->connecting) { chan_open_confirmation(conn->chan); conn->connecting = false; } break; case PLUGLOG_PROXY_MSG: psocks_conn_log(conn, "connection setup: %s", error_msg); break; }; } static void psocks_plug_closing(Plug *plug, const char *error_msg, int error_code, bool calling_back) { psocks_connection *conn = container_of(plug, psocks_connection, plug); if (conn->connecting) { if (conn->ps->log_flags & LOG_CONNSTATUS) psocks_conn_log(conn, "unable to connect: %s", error_msg); chan_open_failed(conn->chan, error_msg); conn->eof_socket_to_pfmgr = true; conn->eof_pfmgr_to_socket = true; conn->connecting = false; } else { if (conn->ps->log_flags & LOG_DIALOGUE) psocks_conn_log(conn, "recv eof"); chan_send_eof(conn->chan); conn->eof_socket_to_pfmgr = true; } queue_toplevel_callback(psocks_check_close, conn); } static void psocks_plug_receive(Plug *plug, int urgent, const char *data, size_t len) { psocks_connection *conn = container_of(plug, psocks_connection, plug); size_t bufsize = chan_send(conn->chan, false, data, len); sk_set_frozen(conn->socket, bufsize > BUFLIMIT); psocks_conn_log_data(conn, DN, data, len); } static void psocks_plug_sent(Plug *plug, size_t bufsize) { psocks_connection *conn = container_of(plug, psocks_connection, plug); sk_set_frozen(conn->socket, bufsize > BUFLIMIT); } psocks_state *psocks_new(const PsocksPlatform *platform) { psocks_state *ps = snew(psocks_state); memset(ps, 0, sizeof(*ps)); ps->listen_port = 1080; ps->acceptall = false; ps->cl.vt = &psocks_clvt; ps->portfwdmgr = portfwdmgr_new(&ps->cl); ps->logging_fp = stderr; /* could make this configurable later */ ps->log_flags = LOG_CONNSTATUS; ps->rec_dest = REC_NONE; ps->platform = platform; ps->subcmd = strbuf_new(); return ps; } void psocks_free(psocks_state *ps) { portfwdmgr_free(ps->portfwdmgr); strbuf_free(ps->subcmd); sfree(ps->rec_cmd); sfree(ps); } void psocks_cmdline(psocks_state *ps, int argc, char **argv) { bool doing_opts = true; bool accumulating_exec_args = false; size_t args_seen = 0; while (--argc > 0) { const char *p = *++argv; if (doing_opts && p[0] == '-' && p[1]) { if (!strcmp(p, "--")) { doing_opts = false; } else if (!strcmp(p, "-g")) { ps->acceptall = true; } else if (!strcmp(p, "-d")) { ps->log_flags |= LOG_DIALOGUE; } else if (!strcmp(p, "-f")) { ps->rec_dest = REC_FILE; } else if (!strcmp(p, "-p")) { if (!ps->platform->open_pipes) { fprintf(stderr, "psocks: '-p' is not supported on this " "platform\n"); exit(1); } if (--argc > 0) { ps->rec_cmd = dupstr(*++argv); } else { fprintf(stderr, "psocks: expected an argument to '-p'\n"); exit(1); } ps->rec_dest = REC_PIPE; } else if (!strcmp(p, "--exec")) { if (!ps->platform->start_subcommand) { fprintf(stderr, "psocks: running a subcommand is not " "supported on this platform\n"); exit(1); } accumulating_exec_args = true; /* Now consume all further argv words for the * subcommand, even if they look like options */ doing_opts = false; } else if (!strcmp(p, "--help")) { printf("usage: psocks [ -d ] [ -f"); if (ps->platform->open_pipes) printf(" | -p pipe-cmd"); printf(" ] [ -g ] port-number"); printf("\n"); printf("where: -d log all connection contents to" " standard output\n"); printf(" -f record each half-connection to " "a file sockin.N/sockout.N\n"); if (ps->platform->open_pipes) printf(" -p pipe-cmd pipe each half-connection" " to 'pipe-cmd [in|out] N'\n"); printf(" -g accept connections from anywhere," " not just localhost\n"); if (ps->platform->start_subcommand) printf(" --exec subcmd [args...] run command, and " "terminate when it exits\n"); printf(" port-number listen on this port" " (default 1080)\n"); printf("also: psocks --help display this help text\n"); exit(0); } else { fprintf(stderr, "psocks: unrecognised option '%s'\n", p); exit(1); } } else { if (accumulating_exec_args) { put_asciz(ps->subcmd, p); } else switch (args_seen++) { case 0: ps->listen_port = atoi(p); break; default: fprintf(stderr, "psocks: unexpected extra argument '%s'\n", p); exit(1); break; } } } } void psocks_start(psocks_state *ps) { Conf *conf = conf_new(); conf_set_bool(conf, CONF_lport_acceptall, ps->acceptall); char *key = dupprintf("AL%d", ps->listen_port); conf_set_str_str(conf, CONF_portfwd, key, "D"); sfree(key); portfwdmgr_config(ps->portfwdmgr, conf); if (ps->subcmd->len) ps->platform->start_subcommand(ps->subcmd); conf_free(conf); } /* * Some stubs that are needed to link against PuTTY modules. */ int verify_host_key(const char *hostname, int port, const char *keytype, const char *key) { unreachable("host keys not handled in this tool"); } void store_host_key(const char *hostname, int port, const char *keytype, const char *key) { unreachable("host keys not handled in this tool"); } /* * stdio-targeted PsocksDataSink. */ typedef struct PsocksDataSinkStdio { stdio_sink sink[2]; PsocksDataSink pds; } PsocksDataSinkStdio; static void stdio_free(PsocksDataSink *pds) { PsocksDataSinkStdio *pdss = container_of(pds, PsocksDataSinkStdio, pds); for (size_t i = 0; i < 2; i++) fclose(pdss->sink[i].fp); sfree(pdss); } PsocksDataSink *pds_stdio(FILE *fp[2]) { PsocksDataSinkStdio *pdss = snew(PsocksDataSinkStdio); for (size_t i = 0; i < 2; i++) { setvbuf(fp[i], NULL, _IONBF, 0); stdio_sink_init(&pdss->sink[i], fp[i]); pdss->pds.s[i] = BinarySink_UPCAST(&pdss->sink[i]); } pdss->pds.free = stdio_free; return &pdss->pds; } putty-0.76/psocks.h0000644000175000017500000000152214072266311011231 00000000000000typedef struct psocks_state psocks_state; typedef struct PsocksPlatform PsocksPlatform; typedef struct PsocksDataSink PsocksDataSink; /* indices into PsocksDataSink arrays */ typedef enum PsocksDirection { UP, DN } PsocksDirection; typedef struct PsocksDataSink { void (*free)(PsocksDataSink *); BinarySink *s[2]; } PsocksDataSink; static inline void pds_free(PsocksDataSink *pds) { pds->free(pds); } PsocksDataSink *pds_stdio(FILE *fp[2]); struct PsocksPlatform { PsocksDataSink *(*open_pipes)( const char *cmd, const char *const *direction_args, const char *index_arg, char **err); void (*start_subcommand)(strbuf *args); }; psocks_state *psocks_new(const PsocksPlatform *); void psocks_free(psocks_state *ps); void psocks_cmdline(psocks_state *ps, int argc, char **argv); void psocks_start(psocks_state *ps); putty-0.76/putty.h0000644000175000017500000027755314072266311011137 00000000000000#ifndef PUTTY_PUTTY_H #define PUTTY_PUTTY_H #include /* for wchar_t */ #include /* for INT_MAX */ #include "defs.h" #include "puttyps.h" #include "network.h" #include "misc.h" #include "marshal.h" /* * We express various time intervals in unsigned long minutes, but may need to * clip some values so that the resulting number of ticks does not overflow an * integer value. */ #define MAX_TICK_MINS (INT_MAX / (60 * TICKSPERSEC)) /* * Fingerprints of the current and previous PGP master keys, to * establish a trust path between an executable and other files. */ #define PGP_MASTER_KEY_YEAR "2018" #define PGP_MASTER_KEY_DETAILS "RSA, 4096-bit" #define PGP_MASTER_KEY_FP \ "24E1 B1C5 75EA 3C9F F752 A922 76BC 7FE4 EBFD 2D9E" #define PGP_PREV_MASTER_KEY_YEAR "2015" #define PGP_PREV_MASTER_KEY_DETAILS "RSA, 4096-bit" #define PGP_PREV_MASTER_KEY_FP \ "440D E3B5 B7A1 CA85 B3CC 1718 AB58 5DC6 0467 6F7C" /* * Definitions of three separate indexing schemes for colour palette * entries. * * Why three? Because history, sorry. * * Two of the colour indexings are used in escape sequences. The * Linux-console style OSC P sequences for setting the palette use an * indexing in which the eight standard ANSI SGR colours come first, * then their bold versions, and then six extra colours for default * fg/bg and the terminal cursor. And the xterm OSC 4 sequences for * querying the palette use a related indexing in which the six extra * colours are pushed up to indices 256 and onwards, with the previous * 16 being the first part of the xterm 256-colour space, and 240 * additional terminal-accessible colours inserted in the middle. * * The third indexing is the order that the colours appear in the * PuTTY configuration panel, and also the order in which they're * described in the saved session files. This order specifies the same * set of colours as the OSC P encoding, but in a different order, * with the default fg/bg colours (which users are most likely to want * to reconfigure) at the start, and the ANSI SGR colours coming * later. * * So all three indices really are needed, because all three appear in * protocols or file formats outside the PuTTY binary. (Changing the * saved-session encoding would have a backwards-compatibility impact; * also, if we ever do, it would be better to replace the numeric * indices with descriptive keywords.) * * Since the OSC 4 encoding contains the full set of colours used in * the terminal display, that's the encoding used by front ends to * store any actual data associated with their palette entries. So the * TermWin palette_set and palette_get_overrides methods use that * encoding, and so does the bitwise encoding of attribute words used * in terminal redraw operations. * * The Conf encoding, of course, is used by config.c and settings.c. * * The aim is that those two sections of the code should never need to * come directly into contact, and the only module that should have to * deal directly with the mapping between these colour encodings - or * to deal _at all_ with the intermediate OSC P encoding - is * terminal.c itself. */ #define CONF_NCOLOURS 22 /* 16 + 6 special ones */ #define OSCP_NCOLOURS 22 /* same as CONF, but different order */ #define OSC4_NCOLOURS 262 /* 256 + the same 6 special ones */ /* The list macro for the conf colours also gives the textual names * used in the GUI configurer */ #define CONF_COLOUR_LIST(X) \ X(fg, "Default Foreground") \ X(fg_bold, "Default Bold Foreground") \ X(bg, "Default Background") \ X(bg_bold, "Default Bold Background") \ X(cursor_fg, "Cursor Text") \ X(cursor_bg, "Cursor Colour") \ X(black, "ANSI Black") \ X(black_bold, "ANSI Black Bold") \ X(red, "ANSI Red") \ X(red_bold, "ANSI Red Bold") \ X(green, "ANSI Green") \ X(green_bold, "ANSI Green Bold") \ X(yellow, "ANSI Yellow") \ X(yellow_bold, "ANSI Yellow Bold") \ X(blue, "ANSI Blue") \ X(blue_bold, "ANSI Blue Bold") \ X(magenta, "ANSI Magenta") \ X(magenta_bold, "ANSI Magenta Bold") \ X(cyan, "ANSI Cyan") \ X(cyan_bold, "ANSI Cyan Bold") \ X(white, "ANSI White") \ X(white_bold, "ANSI White Bold") \ /* end of list */ #define OSCP_COLOUR_LIST(X) \ X(black) \ X(red) \ X(green) \ X(yellow) \ X(blue) \ X(magenta) \ X(cyan) \ X(white) \ X(black_bold) \ X(red_bold) \ X(green_bold) \ X(yellow_bold) \ X(blue_bold) \ X(magenta_bold) \ X(cyan_bold) \ X(white_bold) \ /* * In the OSC 4 indexing, this is where the extra 240 colours go. * They consist of: * * - 216 colours forming a 6x6x6 cube, with R the most * significant colour and G the least. In other words, these * occupy the space of indices 16 <= i < 232, with each * individual colour found as i = 16 + 36*r + 6*g + b, for all * 0 <= r,g,b <= 5. * * - The remaining indices, 232 <= i < 256, consist of a uniform * series of grey shades running between black and white (but * not including either, since actual black and white are * already provided in the previous colour cube). * * After that, we have the remaining 6 special colours: */ \ X(fg) \ X(fg_bold) \ X(bg) \ X(bg_bold) \ X(cursor_fg) \ X(cursor_bg) \ /* end of list */ /* Enumerations of the colour lists. These are available everywhere in * the code. The OSC P encoding shouldn't be used outside terminal.c, * but the easiest way to define the OSC 4 enum is to have the OSC P * one available to compute with. */ enum { #define ENUM_DECL(id,name) CONF_COLOUR_##id, CONF_COLOUR_LIST(ENUM_DECL) #undef ENUM_DECL }; enum { #define ENUM_DECL(id) OSCP_COLOUR_##id, OSCP_COLOUR_LIST(ENUM_DECL) #undef ENUM_DECL }; enum { #define ENUM_DECL(id) OSC4_COLOUR_##id = \ OSCP_COLOUR_##id + (OSCP_COLOUR_##id >= 16 ? 240 : 0), OSCP_COLOUR_LIST(ENUM_DECL) #undef ENUM_DECL }; /* Mapping tables defined in terminal.c */ extern const int colour_indices_conf_to_oscp[CONF_NCOLOURS]; extern const int colour_indices_conf_to_osc4[CONF_NCOLOURS]; extern const int colour_indices_oscp_to_osc4[OSCP_NCOLOURS]; /* Three attribute types: * The ATTRs (normal attributes) are stored with the characters in * the main display arrays * * The TATTRs (temporary attributes) are generated on the fly, they * can overlap with characters but not with normal attributes. * * The LATTRs (line attributes) are an entirely disjoint space of * flags. * * The DATTRs (display attributes) are internal to terminal.c (but * defined here because their values have to match the others * here); they reuse the TATTR_* space but are always masked off * before sending to the front end. * * ATTR_INVALID is an illegal colour combination. */ #define TATTR_ACTCURS 0x40000000UL /* active cursor (block) */ #define TATTR_PASCURS 0x20000000UL /* passive cursor (box) */ #define TATTR_RIGHTCURS 0x10000000UL /* cursor-on-RHS */ #define TATTR_COMBINING 0x80000000UL /* combining characters */ #define DATTR_STARTRUN 0x80000000UL /* start of redraw run */ #define TDATTR_MASK 0xF0000000UL #define TATTR_MASK (TDATTR_MASK) #define DATTR_MASK (TDATTR_MASK) #define LATTR_NORM 0x00000000UL #define LATTR_WIDE 0x00000001UL #define LATTR_TOP 0x00000002UL #define LATTR_BOT 0x00000003UL #define LATTR_MODE 0x00000003UL #define LATTR_WRAPPED 0x00000010UL /* this line wraps to next */ #define LATTR_WRAPPED2 0x00000020UL /* with WRAPPED: CJK wide character wrapped to next line, so last single-width cell is empty */ #define ATTR_INVALID 0x03FFFFU /* Use the DC00 page for direct to font. */ #define CSET_OEMCP 0x0000DC00UL /* OEM Codepage DTF */ #define CSET_ACP 0x0000DD00UL /* Ansi Codepage DTF */ /* These are internal use overlapping with the UTF-16 surrogates */ #define CSET_ASCII 0x0000D800UL /* normal ASCII charset ESC ( B */ #define CSET_LINEDRW 0x0000D900UL /* line drawing charset ESC ( 0 */ #define CSET_SCOACS 0x0000DA00UL /* SCO Alternate charset */ #define CSET_GBCHR 0x0000DB00UL /* UK variant charset ESC ( A */ #define CSET_MASK 0xFFFFFF00UL /* Character set mask */ #define DIRECT_CHAR(c) ((c&0xFFFFFC00)==0xD800) #define DIRECT_FONT(c) ((c&0xFFFFFE00)==0xDC00) #define UCSERR (CSET_LINEDRW|'a') /* UCS Format error character. */ /* * UCSWIDE is a special value used in the terminal data to signify * the character cell containing the right-hand half of a CJK wide * character. We use 0xDFFF because it's part of the surrogate * range and hence won't be used for anything else (it's impossible * to input it via UTF-8 because our UTF-8 decoder correctly * rejects surrogates). */ #define UCSWIDE 0xDFFF #define ATTR_NARROW 0x0800000U #define ATTR_WIDE 0x0400000U #define ATTR_BOLD 0x0040000U #define ATTR_UNDER 0x0080000U #define ATTR_REVERSE 0x0100000U #define ATTR_BLINK 0x0200000U #define ATTR_FGMASK 0x00001FFU /* stores a colour in OSC 4 indexing */ #define ATTR_BGMASK 0x003FE00U /* stores a colour in OSC 4 indexing */ #define ATTR_COLOURS 0x003FFFFU #define ATTR_DIM 0x1000000U #define ATTR_STRIKE 0x2000000U #define ATTR_FGSHIFT 0 #define ATTR_BGSHIFT 9 #define ATTR_DEFFG (OSC4_COLOUR_fg << ATTR_FGSHIFT) #define ATTR_DEFBG (OSC4_COLOUR_bg << ATTR_BGSHIFT) #define ATTR_DEFAULT (ATTR_DEFFG | ATTR_DEFBG) struct sesslist { int nsessions; const char **sessions; char *buffer; /* so memory can be freed later */ }; struct unicode_data { char **uni_tbl; bool dbcs_screenfont; int font_codepage; int line_codepage; wchar_t unitab_scoacs[256]; wchar_t unitab_line[256]; wchar_t unitab_font[256]; wchar_t unitab_xterm[256]; wchar_t unitab_oemcp[256]; unsigned char unitab_ctrl[256]; }; #define LGXF_OVR 1 /* existing logfile overwrite */ #define LGXF_APN 0 /* existing logfile append */ #define LGXF_ASK -1 /* existing logfile ask */ #define LGTYP_NONE 0 /* logmode: no logging */ #define LGTYP_ASCII 1 /* logmode: pure ascii */ #define LGTYP_DEBUG 2 /* logmode: all chars of traffic */ #define LGTYP_PACKETS 3 /* logmode: SSH data packets */ #define LGTYP_SSHRAW 4 /* logmode: SSH raw data */ /* * Enumeration of 'special commands' that can be sent during a * session, separately from the byte stream of ordinary session data. */ typedef enum { /* * Commands that are generally useful in multiple backends. */ SS_BRK, /* serial-line break */ SS_EOF, /* end-of-file on session input */ SS_NOP, /* transmit data with no effect */ SS_PING, /* try to keep the session alive (probably, but not * necessarily, implemented as SS_NOP) */ /* * Commands specific to Telnet. */ SS_AYT, /* Are You There */ SS_SYNCH, /* Synch */ SS_EC, /* Erase Character */ SS_EL, /* Erase Line */ SS_GA, /* Go Ahead */ SS_ABORT, /* Abort Process */ SS_AO, /* Abort Output */ SS_IP, /* Interrupt Process */ SS_SUSP, /* Suspend Process */ SS_EOR, /* End Of Record */ SS_EOL, /* Telnet end-of-line sequence (CRLF, as opposed to CR * NUL that escapes a literal CR) */ /* * Commands specific to SSH. */ SS_REKEY, /* trigger an immediate repeat key exchange */ SS_XCERT, /* cross-certify another host key ('arg' indicates which) */ /* * Send a POSIX-style signal. (Useful in SSH and also pterm.) * * We use the master list in sshsignals.h to define these enum * values, which will come out looking like names of the form * SS_SIGABRT, SS_SIGINT etc. */ #define SIGNAL_MAIN(name, text) SS_SIG ## name, #define SIGNAL_SUB(name) SS_SIG ## name, #include "sshsignals.h" #undef SIGNAL_MAIN #undef SIGNAL_SUB /* * These aren't really special commands, but they appear in the * enumeration because the list returned from * backend_get_specials() will use them to specify the structure * of the GUI specials menu. */ SS_SEP, /* Separator */ SS_SUBMENU, /* Start a new submenu with specified name */ SS_EXITMENU, /* Exit current submenu, or end of entire specials list */ } SessionSpecialCode; /* * The structure type returned from backend_get_specials. */ struct SessionSpecial { const char *name; SessionSpecialCode code; int arg; }; /* Needed by both sshchan.h and sshppl.h */ typedef void (*add_special_fn_t)( void *ctx, const char *text, SessionSpecialCode code, int arg); typedef enum { MBT_NOTHING, MBT_LEFT, MBT_MIDDLE, MBT_RIGHT, /* `raw' button designations */ MBT_SELECT, MBT_EXTEND, MBT_PASTE, /* `cooked' button designations */ MBT_WHEEL_UP, MBT_WHEEL_DOWN /* mouse wheel */ } Mouse_Button; typedef enum { MA_NOTHING, MA_CLICK, MA_2CLK, MA_3CLK, MA_DRAG, MA_RELEASE } Mouse_Action; /* Keyboard modifiers -- keys the user is actually holding down */ #define PKM_SHIFT 0x01 #define PKM_CONTROL 0x02 #define PKM_META 0x04 #define PKM_ALT 0x08 /* Keyboard flags that aren't really modifiers */ #define PKF_CAPSLOCK 0x10 #define PKF_NUMLOCK 0x20 #define PKF_REPEAT 0x40 /* Stand-alone keysyms for function keys */ typedef enum { PK_NULL, /* No symbol for this key */ /* Main keypad keys */ PK_ESCAPE, PK_TAB, PK_BACKSPACE, PK_RETURN, PK_COMPOSE, /* Editing keys */ PK_HOME, PK_INSERT, PK_DELETE, PK_END, PK_PAGEUP, PK_PAGEDOWN, /* Cursor keys */ PK_UP, PK_DOWN, PK_RIGHT, PK_LEFT, PK_REST, /* Numeric keypad */ /* Real one looks like: */ PK_PF1, PK_PF2, PK_PF3, PK_PF4, /* PF1 PF2 PF3 PF4 */ PK_KPCOMMA, PK_KPMINUS, PK_KPDECIMAL, /* 7 8 9 - */ PK_KP0, PK_KP1, PK_KP2, PK_KP3, PK_KP4, /* 4 5 6 , */ PK_KP5, PK_KP6, PK_KP7, PK_KP8, PK_KP9, /* 1 2 3 en- */ PK_KPBIGPLUS, PK_KPENTER, /* 0 . ter */ /* Top row */ PK_F1, PK_F2, PK_F3, PK_F4, PK_F5, PK_F6, PK_F7, PK_F8, PK_F9, PK_F10, PK_F11, PK_F12, PK_F13, PK_F14, PK_F15, PK_F16, PK_F17, PK_F18, PK_F19, PK_F20, PK_PAUSE } Key_Sym; #define PK_ISEDITING(k) ((k) >= PK_HOME && (k) <= PK_PAGEDOWN) #define PK_ISCURSOR(k) ((k) >= PK_UP && (k) <= PK_REST) #define PK_ISKEYPAD(k) ((k) >= PK_PF1 && (k) <= PK_KPENTER) #define PK_ISFKEY(k) ((k) >= PK_F1 && (k) <= PK_F20) enum { VT_XWINDOWS, VT_OEMANSI, VT_OEMONLY, VT_POORMAN, VT_UNICODE }; enum { /* * SSH-2 key exchange algorithms */ KEX_WARN, KEX_DHGROUP1, KEX_DHGROUP14, KEX_DHGEX, KEX_RSA, KEX_ECDH, KEX_MAX }; enum { /* * SSH-2 host key algorithms */ HK_WARN, HK_RSA, HK_DSA, HK_ECDSA, HK_ED25519, HK_ED448, HK_MAX }; enum { /* * SSH ciphers (both SSH-1 and SSH-2) */ CIPHER_WARN, /* pseudo 'cipher' */ CIPHER_3DES, CIPHER_BLOWFISH, CIPHER_AES, /* (SSH-2 only) */ CIPHER_DES, CIPHER_ARCFOUR, CIPHER_CHACHA20, CIPHER_MAX /* no. ciphers (inc warn) */ }; enum TriState { /* * Several different bits of the PuTTY configuration seem to be * three-way settings whose values are `always yes', `always * no', and `decide by some more complex automated means'. This * is true of line discipline options (local echo and line * editing), proxy DNS, proxy terminal logging, Close On Exit, and * SSH server bug workarounds. Accordingly I supply a single enum * here to deal with them all. */ FORCE_ON, FORCE_OFF, AUTO }; enum { /* * Proxy types. */ PROXY_NONE, PROXY_SOCKS4, PROXY_SOCKS5, PROXY_HTTP, PROXY_TELNET, PROXY_CMD, PROXY_FUZZ }; enum { /* * Line discipline options which the backend might try to control. */ LD_EDIT, /* local line editing */ LD_ECHO, /* local echo */ LD_N_OPTIONS }; enum { /* Actions on remote window title query */ TITLE_NONE, TITLE_EMPTY, TITLE_REAL }; enum { /* SUPDUP character set options */ SUPDUP_CHARSET_ASCII, SUPDUP_CHARSET_ITS, SUPDUP_CHARSET_WAITS }; enum { /* Protocol back ends. (CONF_protocol) */ PROT_RAW, PROT_TELNET, PROT_RLOGIN, PROT_SSH, PROT_SSHCONN, /* PROT_SERIAL is supported on a subset of platforms, but it doesn't * hurt to define it globally. */ PROT_SERIAL, /* PROT_SUPDUP is the historical RFC 734 protocol. */ PROT_SUPDUP, PROTOCOL_LIMIT, /* upper bound on number of protocols */ }; 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 }; enum { FQ_DEFAULT, FQ_ANTIALIASED, FQ_NONANTIALIASED, FQ_CLEARTYPE }; enum { SER_PAR_NONE, SER_PAR_ODD, SER_PAR_EVEN, SER_PAR_MARK, SER_PAR_SPACE }; enum { SER_FLOW_NONE, SER_FLOW_XONXOFF, SER_FLOW_RTSCTS, SER_FLOW_DSRDTR }; /* * Tables of string <-> enum value mappings used in settings.c. * Defined here so that backends can export their GSS library tables * to the cross-platform settings code. */ struct keyvalwhere { /* * Two fields which define a string and enum value to be * equivalent to each other. */ const char *s; int v; /* * The next pair of fields are used by gprefs() in settings.c to * arrange that when it reads a list of strings representing a * preference list and translates it into the corresponding list * of integers, strings not appearing in the list are entered in a * configurable position rather than uniformly at the end. */ /* * 'vrel' indicates which other value in the list to place this * element relative to. It should be a value that has occurred in * a 'v' field of some other element of the array, or -1 to * indicate that we simply place relative to one or other end of * the list. * * gprefs will try to process the elements in an order which makes * this field work (i.e. so that the element referenced has been * added before processing this one). */ int vrel; /* * 'where' indicates whether to place the new value before or * after the one referred to by vrel. -1 means before; +1 means * after. * * When vrel is -1, this also implicitly indicates which end of * the array to use. So vrel=-1, where=-1 means to place _before_ * some end of the list (hence, at the last element); vrel=-1, * where=+1 means to place _after_ an end (hence, at the first). */ int where; }; #ifndef NO_GSSAPI extern const int ngsslibs; extern const char *const gsslibnames[]; /* for displaying in configuration */ extern const struct keyvalwhere gsslibkeywords[]; /* for settings.c */ #endif extern const char *const ttymodes[]; enum { /* * Network address types. Used for specifying choice of IPv4/v6 * in config; also used in proxy.c to indicate whether a given * host name has already been resolved or will be resolved at * the proxy end. */ ADDRTYPE_UNSPEC, ADDRTYPE_IPV4, ADDRTYPE_IPV6, ADDRTYPE_LOCAL, /* e.g. Unix domain socket, or Windows named pipe */ ADDRTYPE_NAME /* SockAddr storing an unresolved host name */ }; /* Backend flags */ #define BACKEND_RESIZE_FORBIDDEN 0x01 /* Backend does not allow resizing terminal */ #define BACKEND_NEEDS_TERMINAL 0x02 /* Backend must have terminal */ struct Backend { const BackendVtable *vt; }; struct BackendVtable { char *(*init) (const BackendVtable *vt, Seat *seat, Backend **backend_out, LogContext *logctx, Conf *conf, const char *host, int port, char **realhost, bool nodelay, bool keepalive); void (*free) (Backend *be); /* Pass in a replacement configuration. */ void (*reconfig) (Backend *be, Conf *conf); /* send() returns the current amount of buffered data. */ size_t (*send) (Backend *be, const char *buf, size_t len); /* sendbuffer() does the same thing but without attempting a send */ size_t (*sendbuffer) (Backend *be); void (*size) (Backend *be, int width, int height); void (*special) (Backend *be, SessionSpecialCode code, int arg); const SessionSpecial *(*get_specials) (Backend *be); bool (*connected) (Backend *be); int (*exitcode) (Backend *be); /* If back->sendok() returns false, the backend doesn't currently * want input data, so the frontend should avoid acquiring any if * possible (passing back-pressure on to its sender). */ bool (*sendok) (Backend *be); bool (*ldisc_option_state) (Backend *be, int); void (*provide_ldisc) (Backend *be, Ldisc *ldisc); /* Tells the back end that the front end buffer is clearing. */ void (*unthrottle) (Backend *be, size_t bufsize); int (*cfg_info) (Backend *be); /* Only implemented in the SSH protocol: check whether a * connection-sharing upstream exists for a given configuration. */ bool (*test_for_upstream)(const char *host, int port, Conf *conf); /* Special-purpose function to return additional information to put * in a "are you sure you want to close this session" dialog; * return NULL if no such info, otherwise caller must free. * Only implemented in the SSH protocol, to warn about downstream * connections that would be lost if this one were terminated. */ char *(*close_warn_text)(Backend *be); /* 'id' is a machine-readable name for the backend, used in * saved-session storage. 'displayname' is a human-readable name * for error messages. */ const char *id, *displayname; int protocol; int default_port; unsigned flags; /* Only relevant for the serial protocol: bit masks of which * parity and flow control settings are supported. */ unsigned serial_parity_mask, serial_flow_mask; }; static inline char *backend_init( const BackendVtable *vt, Seat *seat, Backend **out, LogContext *logctx, Conf *conf, const char *host, int port, char **rhost, bool nd, bool ka) { return vt->init(vt, seat, out, logctx, conf, host, port, rhost, nd, ka); } static inline void backend_free(Backend *be) { be->vt->free(be); } static inline void backend_reconfig(Backend *be, Conf *conf) { be->vt->reconfig(be, conf); } static inline size_t backend_send(Backend *be, const char *buf, size_t len) { return be->vt->send(be, buf, len); } static inline size_t backend_sendbuffer(Backend *be) { return be->vt->sendbuffer(be); } static inline void backend_size(Backend *be, int width, int height) { be->vt->size(be, width, height); } static inline void backend_special( Backend *be, SessionSpecialCode code, int arg) { be->vt->special(be, code, arg); } static inline const SessionSpecial *backend_get_specials(Backend *be) { return be->vt->get_specials(be); } static inline bool backend_connected(Backend *be) { return be->vt->connected(be); } static inline int backend_exitcode(Backend *be) { return be->vt->exitcode(be); } static inline bool backend_sendok(Backend *be) { return be->vt->sendok(be); } static inline bool backend_ldisc_option_state(Backend *be, int state) { return be->vt->ldisc_option_state(be, state); } static inline void backend_provide_ldisc(Backend *be, Ldisc *ldisc) { be->vt->provide_ldisc(be, ldisc); } static inline void backend_unthrottle(Backend *be, size_t bufsize) { be->vt->unthrottle(be, bufsize); } static inline int backend_cfg_info(Backend *be) { return be->vt->cfg_info(be); } extern const struct BackendVtable *const backends[]; /* * In programs with a config UI, only the first few members of * backends[] will be displayed at the top-level; the others will be * relegated to a drop-down. */ extern const size_t n_ui_backends; /* * Suggested default protocol provided by the backend link module. * The application is free to ignore this. */ extern const int be_default_protocol; /* * Name of this particular application, for use in the config box * and other pieces of text. */ extern const char *const appname; /* * Mechanism for getting text strings such as usernames and passwords * from the front-end. * The fields are mostly modelled after SSH's keyboard-interactive auth. * FIXME We should probably mandate a character set/encoding (probably UTF-8). * * Since many of the pieces of text involved may be chosen by the server, * the caller must take care to ensure that the server can't spoof locally- * generated prompts such as key passphrase prompts. Some ground rules: * - If the front-end needs to truncate a string, it should lop off the * end. * - The front-end should filter out any dangerous characters and * 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; bool echo; strbuf *result; } 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 * sufficient). */ bool to_server; /* * Indicates whether the prompts originated _at_ the server, so * that the front end can display some kind of trust sigil that * distinguishes (say) a legit private-key passphrase prompt from * a fake one sent by a malicious server. */ bool from_server; char *name; /* Short description, perhaps for dialog box title */ bool name_reqd; /* Display of `name' required or optional? */ char *instruction; /* Long description, maybe with embedded newlines */ bool instr_reqd; /* Display of `instruction' required or optional? */ size_t n_prompts; /* May be zero (in which case display the foregoing, * if any, and return success) */ size_t prompts_size; /* allocated storage capacity for prompts[] */ prompt_t **prompts; void *data; /* slot for housekeeping data, managed by * seat_get_userpass_input(); initially NULL */ } prompts_t; prompts_t *new_prompts(void); void add_prompt(prompts_t *p, char *promptstr, bool echo); void prompt_set_result(prompt_t *pr, const char *newstr); char *prompt_get_result(prompt_t *pr); const char *prompt_get_result_ref(prompt_t *pr); void free_prompts(prompts_t *p); /* * Data type definitions for true-colour terminal display. * 'optionalrgb' describes a single RGB colour, which overrides the * other colour settings if 'enabled' is nonzero, and is ignored * otherwise. 'truecolour' contains a pair of those for foreground and * background. */ typedef struct optionalrgb { bool enabled; unsigned char r, g, b; } optionalrgb; extern const optionalrgb optionalrgb_none; typedef struct truecolour { optionalrgb fg, bg; } truecolour; #define optionalrgb_equal(r1,r2) ( \ (r1).enabled==(r2).enabled && \ (r1).r==(r2).r && (r1).g==(r2).g && (r1).b==(r2).b) #define truecolour_equal(c1,c2) ( \ optionalrgb_equal((c1).fg, (c2).fg) && \ optionalrgb_equal((c1).bg, (c2).bg)) /* * Enumeration of clipboards. We provide some standard ones cross- * platform, and then permit each platform to extend this enumeration * further by defining PLATFORM_CLIPBOARDS in its own header file. * * CLIP_NULL is a non-clipboard, writes to which are ignored and reads * from which return no data. * * CLIP_LOCAL refers to a buffer within terminal.c, which * unconditionally saves the last data selected in the terminal. In * configurations where a system clipboard is not written * automatically on selection but instead by an explicit UI action, * this is where the code responding to that action can find the data * to write to the clipboard in question. */ #define CROSS_PLATFORM_CLIPBOARDS(X) \ X(CLIP_NULL, "null clipboard") \ X(CLIP_LOCAL, "last text selected in terminal") \ /* end of list */ #define ALL_CLIPBOARDS(X) \ CROSS_PLATFORM_CLIPBOARDS(X) \ PLATFORM_CLIPBOARDS(X) \ /* end of list */ #define CLIP_ID(id,name) id, enum { ALL_CLIPBOARDS(CLIP_ID) N_CLIPBOARDS }; #undef CLIP_ID /* Hint from backend to frontend about time-consuming operations, used * by seat_set_busy_status. Initial state is assumed to be * BUSY_NOT. */ typedef enum BusyStatus { BUSY_NOT, /* Not busy, all user interaction OK */ BUSY_WAITING, /* Waiting for something; local event loops still running so some local interaction (e.g. menus) OK, but network stuff is suspended */ BUSY_CPU /* Locally busy (e.g. crypto); user interaction * suspended */ } BusyStatus; typedef enum SeatInteractionContext { SIC_BANNER, SIC_KI_PROMPTS } SeatInteractionContext; /* * Data type 'Seat', which is an API intended to contain essentially * everything that a back end might need to talk to its client for: * session output, password prompts, SSH warnings about host keys and * weak cryptography, notifications of events like the remote process * exiting or the GUI specials menu needing an update. */ struct Seat { const struct SeatVtable *vt; }; struct SeatVtable { /* * Provide output from the remote session. 'is_stderr' indicates * that the output should be sent to a separate error message * channel, if the seat has one. But combining both channels into * one is OK too; that's what terminal-window based seats do. * * The return value is the current size of the output backlog. */ size_t (*output)(Seat *seat, bool is_stderr, const void *data, size_t 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. */ bool (*eof)(Seat *seat); /* * Try to get answers from a set of interactive login prompts. The * prompts are provided in 'p'; the bufchain 'input' holds the * data currently outstanding in the session's normal standard- * input channel. Seats may implement this function by consuming * data from 'input' (e.g. password prompts in GUI PuTTY, * displayed in the same terminal as the subsequent session), or * by doing something entirely different (e.g. directly * interacting with standard I/O, or putting up a dialog box). * * A positive return value means that all prompts have had answers * filled in. A zero return means that the user performed a * deliberate 'cancel' UI action. A negative return means that no * answer can be given yet but please try again later. * * (FIXME: it would be nice to distinguish two classes of cancel * action, so the user could specify 'I want to abandon this * entire attempt to start a session' or the milder 'I want to * abandon this particular form of authentication and fall back to * a different one' - e.g. if you turn out not to be able to * remember your private key passphrase then perhaps you'd rather * fall back to password auth rather than aborting the whole * session.) * * (Also FIXME: currently, backends' only response to the 'try * again later' is to try again when more input data becomes * available, because they assume that a seat is returning that * value because it's consuming keyboard input. But a seat that * handled this function by putting up a dialog box might want to * put it up non-modally, and therefore would want to proactively * notify the backend to retry once the dialog went away. So if I * ever do want to move password prompts into a dialog box, I'll * want a backend method for sending that notification.) */ int (*get_userpass_input)(Seat *seat, prompts_t *p, bufchain *input); /* * Notify the seat that the process running at the other end of * the connection has finished. */ void (*notify_remote_exit)(Seat *seat); /* * Notify the seat that the connection has suffered a fatal error. */ void (*connection_fatal)(Seat *seat, const char *message); /* * Notify the seat that the list of special commands available * from backend_get_specials() has changed, so that it might want * to call that function to repopulate its menu. * * Seats are not expected to call backend_get_specials() * proactively; they may start by assuming that the backend * provides no special commands at all, so if the backend does * provide any, then it should use this notification at startup * time. Of course 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)(Seat *seat); /* * Get the seat's preferred value for an SSH terminal mode * setting. Returning NULL indicates no preference (i.e. the SSH * connection will not attempt to set the mode at all). * * The returned value is dynamically allocated, and the caller * should free it. */ char *(*get_ttymode)(Seat *seat, const char *mode); /* * Tell the seat whether the backend is currently doing anything * CPU-intensive (typically a cryptographic key exchange). See * BusyStatus enumeration above. */ void (*set_busy_status)(Seat *seat, BusyStatus status); /* * Ask the seat whether a given SSH host key should be accepted. * This may return immediately after checking saved configuration * or command-line options, or it may have to present a prompt to * the user and return asynchronously later. * * Return values: * * - +1 means `key was OK' (either already known or the user just * approved it) `so continue with the connection' * * - 0 means `key was not OK, abandon the connection' * * - -1 means `I've initiated enquiries, please wait to be called * back via the provided function with a result that's either 0 * or +1'. */ int (*verify_ssh_host_key)( Seat *seat, const char *host, int port, const char *keytype, char *keystr, const char *keydisp, char **key_fingerprints, void (*callback)(void *ctx, int result), void *ctx); /* * Check with the seat whether it's OK to use a cryptographic * primitive from below the 'warn below this line' threshold in * the input Conf. Return values are the same as * verify_ssh_host_key above. */ int (*confirm_weak_crypto_primitive)( Seat *seat, const char *algtype, const char *algname, void (*callback)(void *ctx, int result), void *ctx); /* * Variant form of confirm_weak_crypto_primitive, which prints a * slightly different message but otherwise has the same * semantics. * * This form is used in the case where we're using a host key * below the warning threshold because that's the best one we have * cached, but at least one host key algorithm *above* the * threshold is available that we don't have cached. 'betteralgs' * lists the better algorithm(s). */ int (*confirm_weak_cached_hostkey)( Seat *seat, const char *algname, const char *betteralgs, void (*callback)(void *ctx, int result), void *ctx); /* * Indicates whether the seat is expecting to interact with the * user in the UTF-8 character set. (Affects e.g. visual erase * handling in local line editing.) */ bool (*is_utf8)(Seat *seat); /* * Notify the seat that the back end, and/or the ldisc between * them, have changed their idea of whether they currently want * local echo and/or local line editing enabled. */ void (*echoedit_update)(Seat *seat, bool echoing, bool editing); /* * Return the local X display string relevant to a seat, or NULL * if there isn't one or if the concept is meaningless. */ const char *(*get_x_display)(Seat *seat); /* * Return the X11 id of the X terminal window relevant to a seat, * by returning true and filling in the output pointer. Return * false if there isn't one or if the concept is meaningless. */ bool (*get_windowid)(Seat *seat, long *id_out); /* * Return the size of the terminal window in pixels. If the * concept is meaningless or the information is unavailable, * return false; otherwise fill in the output pointers and return * true. */ bool (*get_window_pixel_size)(Seat *seat, int *width, int *height); /* * Return a StripCtrlChars appropriate for sanitising untrusted * terminal data (e.g. SSH banners, prompts) being sent to the * user of this seat. May return NULL if no sanitisation is * needed. */ StripCtrlChars *(*stripctrl_new)( Seat *seat, BinarySink *bs_out, SeatInteractionContext sic); /* * Set the seat's current idea of where output is coming from. * True means that output is being generated by our own code base * (and hence, can be trusted if it's asking you for secrets such * as your passphrase); false means output is coming from the * server. * * Returns true if the seat has a way to indicate this * distinction. Returns false if not, in which case the backend * should use a fallback defence against spoofing of PuTTY's local * prompts by malicious servers. */ bool (*set_trust_status)(Seat *seat, bool trusted); /* * Ask the seat whether it would like verbose messages. */ bool (*verbose)(Seat *seat); /* * Ask the seat whether it's an interactive program. */ bool (*interactive)(Seat *seat); /* * Return the seat's current idea of where the output cursor is. * * Returns true if the seat has a cursor. Returns false if not. */ bool (*get_cursor_position)(Seat *seat, int *x, int *y); }; static inline size_t seat_output( Seat *seat, bool err, const void *data, size_t len) { return seat->vt->output(seat, err, data, len); } static inline bool seat_eof(Seat *seat) { return seat->vt->eof(seat); } static inline int seat_get_userpass_input( Seat *seat, prompts_t *p, bufchain *input) { return seat->vt->get_userpass_input(seat, p, input); } static inline void seat_notify_remote_exit(Seat *seat) { seat->vt->notify_remote_exit(seat); } static inline void seat_update_specials_menu(Seat *seat) { seat->vt->update_specials_menu(seat); } static inline char *seat_get_ttymode(Seat *seat, const char *mode) { return seat->vt->get_ttymode(seat, mode); } static inline void seat_set_busy_status(Seat *seat, BusyStatus status) { seat->vt->set_busy_status(seat, status); } static inline int seat_verify_ssh_host_key( Seat *seat, const char *h, int p, const char *ktyp, char *kstr, const char *kdsp, char **fps, void (*cb)(void *ctx, int result), void *ctx) { return seat->vt->verify_ssh_host_key(seat, h, p, ktyp, kstr, kdsp, fps, cb, ctx); } static inline int seat_confirm_weak_crypto_primitive( Seat *seat, const char *atyp, const char *aname, void (*cb)(void *ctx, int result), void *ctx) { return seat->vt->confirm_weak_crypto_primitive(seat, atyp, aname, cb, ctx); } static inline int seat_confirm_weak_cached_hostkey( Seat *seat, const char *aname, const char *better, void (*cb)(void *ctx, int result), void *ctx) { return seat->vt->confirm_weak_cached_hostkey(seat, aname, better, cb, ctx); } static inline bool seat_is_utf8(Seat *seat) { return seat->vt->is_utf8(seat); } static inline void seat_echoedit_update(Seat *seat, bool ec, bool ed) { seat->vt->echoedit_update(seat, ec, ed); } static inline const char *seat_get_x_display(Seat *seat) { return seat->vt->get_x_display(seat); } static inline bool seat_get_windowid(Seat *seat, long *id_out) { return seat->vt->get_windowid(seat, id_out); } static inline bool seat_get_window_pixel_size(Seat *seat, int *w, int *h) { return seat->vt->get_window_pixel_size(seat, w, h); } static inline StripCtrlChars *seat_stripctrl_new( Seat *seat, BinarySink *bs, SeatInteractionContext sic) { return seat->vt->stripctrl_new(seat, bs, sic); } static inline bool seat_set_trust_status(Seat *seat, bool trusted) { return seat->vt->set_trust_status(seat, trusted); } static inline bool seat_verbose(Seat *seat) { return seat->vt->verbose(seat); } static inline bool seat_interactive(Seat *seat) { return seat->vt->interactive(seat); } static inline bool seat_get_cursor_position(Seat *seat, int *x, int *y) { return seat->vt->get_cursor_position(seat, x, y); } /* Unlike the seat's actual method, the public entry point * seat_connection_fatal is a wrapper function with a printf-like API, * defined in misc.c. */ void seat_connection_fatal(Seat *seat, const char *fmt, ...) PRINTF_LIKE(2, 3); /* Handy aliases for seat_output which set is_stderr to a fixed value. */ static inline size_t seat_stdout(Seat *seat, const void *data, size_t len) { return seat_output(seat, false, data, len); } static inline size_t seat_stdout_pl(Seat *seat, ptrlen data) { return seat_output(seat, false, data.ptr, data.len); } static inline size_t seat_stderr(Seat *seat, const void *data, size_t len) { return seat_output(seat, true, data, len); } static inline size_t seat_stderr_pl(Seat *seat, ptrlen data) { return seat_output(seat, true, data.ptr, data.len); } /* * Stub methods for seat implementations that want to use the obvious * null handling for a given method. * * These are generally obvious, except for is_utf8, where you might * plausibly want to return either fixed answer 'no' or 'yes'. */ size_t nullseat_output( Seat *seat, bool is_stderr, const void *data, size_t len); bool nullseat_eof(Seat *seat); int nullseat_get_userpass_input(Seat *seat, prompts_t *p, bufchain *input); void nullseat_notify_remote_exit(Seat *seat); void nullseat_connection_fatal(Seat *seat, const char *message); void nullseat_update_specials_menu(Seat *seat); char *nullseat_get_ttymode(Seat *seat, const char *mode); void nullseat_set_busy_status(Seat *seat, BusyStatus status); int nullseat_verify_ssh_host_key( Seat *seat, const char *host, int port, const char *keytype, char *keystr, const char *keydisp, char **key_fingerprints, void (*callback)(void *ctx, int result), void *ctx); int nullseat_confirm_weak_crypto_primitive( Seat *seat, const char *algtype, const char *algname, void (*callback)(void *ctx, int result), void *ctx); int nullseat_confirm_weak_cached_hostkey( Seat *seat, const char *algname, const char *betteralgs, void (*callback)(void *ctx, int result), void *ctx); bool nullseat_is_never_utf8(Seat *seat); bool nullseat_is_always_utf8(Seat *seat); void nullseat_echoedit_update(Seat *seat, bool echoing, bool editing); const char *nullseat_get_x_display(Seat *seat); bool nullseat_get_windowid(Seat *seat, long *id_out); bool nullseat_get_window_pixel_size(Seat *seat, int *width, int *height); StripCtrlChars *nullseat_stripctrl_new( Seat *seat, BinarySink *bs_out, SeatInteractionContext sic); bool nullseat_set_trust_status(Seat *seat, bool trusted); bool nullseat_set_trust_status_vacuously(Seat *seat, bool trusted); bool nullseat_verbose_no(Seat *seat); bool nullseat_verbose_yes(Seat *seat); bool nullseat_interactive_no(Seat *seat); bool nullseat_interactive_yes(Seat *seat); bool nullseat_get_cursor_position(Seat *seat, int *x, int *y); /* * Seat functions provided by the platform's console-application * support module (wincons.c, uxcons.c). */ void console_connection_fatal(Seat *seat, const char *message); int console_verify_ssh_host_key( Seat *seat, const char *host, int port, const char *keytype, char *keystr, const char *keydisp, char **key_fingerprints, void (*callback)(void *ctx, int result), void *ctx); int console_confirm_weak_crypto_primitive( Seat *seat, const char *algtype, const char *algname, void (*callback)(void *ctx, int result), void *ctx); int console_confirm_weak_cached_hostkey( Seat *seat, const char *algname, const char *betteralgs, void (*callback)(void *ctx, int result), void *ctx); StripCtrlChars *console_stripctrl_new( Seat *seat, BinarySink *bs_out, SeatInteractionContext sic); bool console_set_trust_status(Seat *seat, bool trusted); /* * Other centralised seat functions. */ int filexfer_get_userpass_input(Seat *seat, prompts_t *p, bufchain *input); bool cmdline_seat_verbose(Seat *seat); typedef struct rgb { uint8_t r, g, b; } rgb; /* * Data type 'TermWin', which is a vtable encapsulating all the * functionality that Terminal expects from its containing terminal * window. */ struct TermWin { const struct TermWinVtable *vt; }; struct TermWinVtable { /* * All functions listed here between setup_draw_ctx and * free_draw_ctx expect to be _called_ between them too, so that * the TermWin has a drawing context currently available. * * (Yes, even char_width, because e.g. the Windows implementation * of TermWin handles it by loading the currently configured font * into the HDC and doing a GDI query.) */ bool (*setup_draw_ctx)(TermWin *); /* Draw text in the window, during a painting operation */ void (*draw_text)(TermWin *, int x, int y, wchar_t *text, int len, unsigned long attrs, int line_attrs, truecolour tc); /* Draw the visible cursor. Expects you to have called do_text * first (because it might just draw an underline over a character * presumed to exist already), but also expects you to pass in all * the details of the character under the cursor (because it might * redraw it in different colours). */ void (*draw_cursor)(TermWin *, int x, int y, wchar_t *text, int len, unsigned long attrs, int line_attrs, truecolour tc); /* Draw the sigil indicating that a line of text has come from * PuTTY itself rather than the far end (defence against end-of- * authentication spoofing) */ void (*draw_trust_sigil)(TermWin *, int x, int y); int (*char_width)(TermWin *, int uc); void (*free_draw_ctx)(TermWin *); void (*set_cursor_pos)(TermWin *, int x, int y); /* set_raw_mouse_mode instructs the front end to start sending mouse events * in raw mode suitable for translating into mouse-tracking terminal data * (e.g. include scroll-wheel events and don't bother to identify double- * and triple-clicks). set_raw_mouse_mode_pointer instructs the front end * to change the mouse pointer shape to *indicate* raw mouse mode. */ void (*set_raw_mouse_mode)(TermWin *, bool enable); void (*set_raw_mouse_mode_pointer)(TermWin *, bool enable); void (*set_scrollbar)(TermWin *, int total, int start, int page); void (*bell)(TermWin *, int mode); void (*clip_write)(TermWin *, int clipboard, wchar_t *text, int *attrs, truecolour *colours, int len, bool must_deselect); void (*clip_request_paste)(TermWin *, int clipboard); void (*refresh)(TermWin *); void (*request_resize)(TermWin *, int w, int h); void (*set_title)(TermWin *, const char *title); void (*set_icon_title)(TermWin *, const char *icontitle); /* set_minimised and set_maximised are assumed to set two * independent settings, rather than a single three-way * {min,normal,max} switch. The idea is that when you un-minimise * the window it remembers whether to go back to normal or * maximised. */ void (*set_minimised)(TermWin *, bool minimised); void (*set_maximised)(TermWin *, bool maximised); void (*move)(TermWin *, int x, int y); void (*set_zorder)(TermWin *, bool top); /* Set the colour palette that the TermWin will use to display * text. One call to this function sets 'ncolours' consecutive * colours in the OSC 4 sequence, starting at 'start'. */ void (*palette_set)(TermWin *, unsigned start, unsigned ncolours, const rgb *colours); /* Query the front end for any OS-local overrides to the default * colours stored in Conf. The front end should set any it cares * about by calling term_palette_override. * * The Terminal object is passed in as a parameter, because this * can be called as a callback from term_init(). So the TermWin * itself won't yet have been told where to find its Terminal * object, because that doesn't happen until term_init * returns. */ void (*palette_get_overrides)(TermWin *, Terminal *); }; static inline bool win_setup_draw_ctx(TermWin *win) { return win->vt->setup_draw_ctx(win); } static inline void win_draw_text( TermWin *win, int x, int y, wchar_t *text, int len, unsigned long attrs, int line_attrs, truecolour tc) { win->vt->draw_text(win, x, y, text, len, attrs, line_attrs, tc); } static inline void win_draw_cursor( TermWin *win, int x, int y, wchar_t *text, int len, unsigned long attrs, int line_attrs, truecolour tc) { win->vt->draw_cursor(win, x, y, text, len, attrs, line_attrs, tc); } static inline void win_draw_trust_sigil(TermWin *win, int x, int y) { win->vt->draw_trust_sigil(win, x, y); } static inline int win_char_width(TermWin *win, int uc) { return win->vt->char_width(win, uc); } static inline void win_free_draw_ctx(TermWin *win) { win->vt->free_draw_ctx(win); } static inline void win_set_cursor_pos(TermWin *win, int x, int y) { win->vt->set_cursor_pos(win, x, y); } static inline void win_set_raw_mouse_mode(TermWin *win, bool enable) { win->vt->set_raw_mouse_mode(win, enable); } static inline void win_set_raw_mouse_mode_pointer(TermWin *win, bool enable) { win->vt->set_raw_mouse_mode_pointer(win, enable); } static inline void win_set_scrollbar(TermWin *win, int t, int s, int p) { win->vt->set_scrollbar(win, t, s, p); } static inline void win_bell(TermWin *win, int mode) { win->vt->bell(win, mode); } static inline void win_clip_write( TermWin *win, int clipboard, wchar_t *text, int *attrs, truecolour *colours, int len, bool deselect) { win->vt->clip_write(win, clipboard, text, attrs, colours, len, deselect); } static inline void win_clip_request_paste(TermWin *win, int clipboard) { win->vt->clip_request_paste(win, clipboard); } static inline void win_refresh(TermWin *win) { win->vt->refresh(win); } static inline void win_request_resize(TermWin *win, int w, int h) { win->vt->request_resize(win, w, h); } static inline void win_set_title(TermWin *win, const char *title) { win->vt->set_title(win, title); } static inline void win_set_icon_title(TermWin *win, const char *icontitle) { win->vt->set_icon_title(win, icontitle); } static inline void win_set_minimised(TermWin *win, bool minimised) { win->vt->set_minimised(win, minimised); } static inline void win_set_maximised(TermWin *win, bool maximised) { win->vt->set_maximised(win, maximised); } static inline void win_move(TermWin *win, int x, int y) { win->vt->move(win, x, y); } static inline void win_set_zorder(TermWin *win, bool top) { win->vt->set_zorder(win, top); } static inline void win_palette_set( TermWin *win, unsigned start, unsigned ncolours, const rgb *colours) { win->vt->palette_set(win, start, ncolours, colours); } static inline void win_palette_get_overrides(TermWin *win, Terminal *term) { win->vt->palette_get_overrides(win, term); } /* * Global functions not specific to a connection instance. */ void nonfatal(const char *, ...) PRINTF_LIKE(1, 2); NORETURN void modalfatalbox(const char *, ...) PRINTF_LIKE(1, 2); NORETURN 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) /* PROT_SSH, PROT_TELNET etc */ \ X(INT, NONE, addressfamily) /* ADDRTYPE_IPV[46] or ADDRTYPE_UNSPEC */ \ X(INT, NONE, close_on_exit) /* FORCE_ON, FORCE_OFF, AUTO */ \ X(BOOL, NONE, warn_on_close) \ X(INT, NONE, ping_interval) /* in seconds */ \ X(BOOL, NONE, tcp_nodelay) \ X(BOOL, 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) /* FORCE_ON, FORCE_OFF, AUTO */ \ X(BOOL, NONE, even_proxy_localhost) \ X(INT, NONE, proxy_type) /* PROXY_NONE, PROXY_SOCKS4, ... */ \ 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) \ X(INT, NONE, proxy_log_to_term) /* FORCE_ON, FORCE_OFF, AUTO */ \ /* SSH options */ \ X(STR, NONE, remote_cmd) \ X(STR, NONE, remote_cmd2) /* fallback if remote_cmd fails; never loaded or saved */ \ X(BOOL, NONE, nopty) \ X(BOOL, NONE, compression) \ X(INT, INT, ssh_kexlist) \ X(INT, INT, ssh_hklist) \ X(BOOL, NONE, ssh_prefer_known_hostkeys) \ X(INT, NONE, ssh_rekey_time) /* in minutes */ \ X(STR, NONE, ssh_rekey_data) /* string encoding e.g. "100K", "2M", "1G" */ \ X(BOOL, NONE, tryagent) \ X(BOOL, NONE, agentfwd) \ X(BOOL, NONE, change_username) /* allow username switching in SSH-2 */ \ X(INT, INT, ssh_cipherlist) \ X(FILENAME, NONE, keyfile) \ /* \ * Which SSH protocol to use. \ * For historical reasons, the current legal values for CONF_sshprot \ * are: \ * 0 = SSH-1 only \ * 3 = SSH-2 only \ * We used to also support \ * 1 = SSH-1 with fallback to SSH-2 \ * 2 = SSH-2 with fallback to SSH-1 \ * and we continue to use 0/3 in storage formats rather than the more \ * obvious 1/2 to avoid surprises if someone saves a session and later \ * downgrades PuTTY. So it's easier to use these numbers internally too. \ */ \ X(INT, NONE, sshprot) \ X(BOOL, NONE, ssh2_des_cbc) /* "des-cbc" unrecommended SSH-2 cipher */ \ X(BOOL, NONE, ssh_no_userauth) /* bypass "ssh-userauth" (SSH-2 only) */ \ X(BOOL, NONE, ssh_no_trivial_userauth) /* disable trivial types of auth */ \ X(BOOL, NONE, ssh_show_banner) /* show USERAUTH_BANNERs (SSH-2 only) */ \ X(BOOL, NONE, try_tis_auth) \ X(BOOL, NONE, try_ki_auth) \ X(BOOL, NONE, try_gssapi_auth) /* attempt gssapi auth via ssh userauth */ \ X(BOOL, NONE, try_gssapi_kex) /* attempt gssapi auth via ssh kex */ \ X(BOOL, NONE, gssapifwd) /* forward tgt via gss */ \ X(INT, NONE, gssapirekey) /* KEXGSS refresh interval (mins) */ \ X(INT, INT, ssh_gsslist) /* preference order for local GSS libs */ \ X(FILENAME, NONE, ssh_gss_custom) \ X(BOOL, NONE, ssh_subsys) /* run a subsystem rather than a command */ \ X(BOOL, NONE, ssh_subsys2) /* fallback to go with remote_cmd_ptr2 */ \ X(BOOL, 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(BOOL, NONE, username_from_env) \ X(STR, NONE, localusername) \ X(BOOL, NONE, rfc_environ) \ X(BOOL, 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) /* SER_PAR_NONE, SER_PAR_ODD, ... */ \ X(INT, NONE, serflow) /* SER_FLOW_NONE, SER_FLOW_XONXOFF, ... */ \ /* Supdup options */ \ X(STR, NONE, supdup_location) \ X(INT, NONE, supdup_ascii_set) \ X(BOOL, NONE, supdup_more) \ X(BOOL, NONE, supdup_scroll) \ /* Keyboard options */ \ X(BOOL, NONE, bksp_is_delete) \ X(BOOL, NONE, rxvt_homeend) \ X(INT, NONE, funky_type) /* FUNKY_XTERM, FUNKY_LINUX, ... */ \ X(BOOL, NONE, no_applic_c) /* totally disable app cursor keys */ \ X(BOOL, NONE, no_applic_k) /* totally disable app keypad */ \ X(BOOL, NONE, no_mouse_rep) /* totally disable mouse reporting */ \ X(BOOL, NONE, no_remote_resize) /* disable remote resizing */ \ X(BOOL, NONE, no_alt_screen) /* disable alternate screen */ \ X(BOOL, NONE, no_remote_wintitle) /* disable remote retitling */ \ X(BOOL, NONE, no_remote_clearscroll) /* disable ESC[3J */ \ X(BOOL, NONE, no_dbackspace) /* disable destructive backspace */ \ X(BOOL, NONE, no_remote_charset) /* disable remote charset config */ \ X(INT, NONE, remote_qtitle_action) /* remote win title query action * (TITLE_NONE, TITLE_EMPTY, ...) */ \ X(BOOL, NONE, app_cursor) \ X(BOOL, NONE, app_keypad) \ X(BOOL, NONE, nethack_keypad) \ X(BOOL, NONE, telnet_keyboard) \ X(BOOL, NONE, telnet_newline) \ X(BOOL, NONE, alt_f4) /* is it special? */ \ X(BOOL, NONE, alt_space) /* is it special? */ \ X(BOOL, NONE, alt_only) /* is it special? */ \ X(INT, NONE, localecho) /* FORCE_ON, FORCE_OFF, AUTO */ \ X(INT, NONE, localedit) /* FORCE_ON, FORCE_OFF, AUTO */ \ X(BOOL, NONE, alwaysontop) \ X(BOOL, NONE, fullscreenonaltenter) \ X(BOOL, NONE, scroll_on_key) \ X(BOOL, NONE, scroll_on_disp) \ X(BOOL, NONE, erase_to_scrollback) \ X(BOOL, NONE, compose_key) \ X(BOOL, NONE, ctrlaltkeys) \ X(BOOL, NONE, osx_option_meta) \ X(BOOL, NONE, osx_command_meta) \ X(STR, NONE, wintitle) /* initial window title */ \ /* Terminal options */ \ X(INT, NONE, savelines) \ X(BOOL, NONE, dec_om) \ X(BOOL, NONE, wrap_mode) \ X(BOOL, NONE, lfhascr) \ X(INT, NONE, cursor_type) /* 0=block 1=underline 2=vertical */ \ X(BOOL, NONE, blink_cur) \ X(INT, NONE, beep) /* BELL_DISABLED, BELL_DEFAULT, ... */ \ X(INT, NONE, beep_ind) /* B_IND_DISABLED, B_IND_FLASH, ... */ \ X(BOOL, 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(BOOL, NONE, scrollbar) \ X(BOOL, NONE, scrollbar_in_fullscreen) \ X(INT, NONE, resize_action) /* RESIZE_TERM, RESIZE_DISABLED, ... */ \ X(BOOL, NONE, bce) \ X(BOOL, NONE, blinktext) \ X(BOOL, NONE, win_name_always) \ X(INT, NONE, width) \ X(INT, NONE, height) \ X(FONT, NONE, font) \ X(INT, NONE, font_quality) /* FQ_DEFAULT, FQ_ANTIALIASED, ... */ \ X(FILENAME, NONE, logfilename) \ X(INT, NONE, logtype) /* LGTYP_NONE, LGTYPE_ASCII, ... */ \ X(INT, NONE, logxfovr) /* LGXF_OVR, LGXF_APN, LGXF_ASK */ \ X(BOOL, NONE, logflush) \ X(BOOL, NONE, logheader) \ X(BOOL, NONE, logomitpass) \ X(BOOL, NONE, logomitdata) \ X(BOOL, NONE, hide_mouseptr) \ X(BOOL, NONE, sunken_edge) \ X(INT, NONE, window_border) /* in pixels */ \ X(STR, NONE, answerback) \ X(STR, NONE, printer) \ X(BOOL, NONE, no_arabicshaping) \ X(BOOL, NONE, no_bidi) \ /* Colour options */ \ X(BOOL, NONE, ansi_colour) \ X(BOOL, NONE, xterm_256_colour) \ X(BOOL, NONE, true_colour) \ X(BOOL, NONE, system_colour) \ X(BOOL, NONE, try_palette) \ X(INT, NONE, bold_style) /* 1=font 2=colour (3=both) */ \ X(INT, INT, colours) /* indexed by the CONF_COLOUR_* enum encoding */ \ /* Selection options */ \ X(INT, NONE, mouse_is_xterm) /* 0=compromise 1=xterm 2=Windows */ \ X(BOOL, NONE, rect_select) \ X(BOOL, NONE, paste_controls) \ X(BOOL, NONE, rawcnp) \ X(BOOL, NONE, utf8linedraw) \ X(BOOL, NONE, rtf_paste) \ X(BOOL, NONE, mouse_override) \ X(INT, INT, wordness) \ X(BOOL, NONE, mouseautocopy) \ X(INT, NONE, mousepaste) /* CLIPUI_IMPLICIT, CLIPUI_EXPLICIT, ... */ \ X(INT, NONE, ctrlshiftins) /* CLIPUI_IMPLICIT, CLIPUI_EXPLICIT, ... */ \ X(INT, NONE, ctrlshiftcv) /* CLIPUI_IMPLICIT, CLIPUI_EXPLICIT, ... */ \ X(STR, NONE, mousepaste_custom) \ X(STR, NONE, ctrlshiftins_custom) \ X(STR, NONE, ctrlshiftcv_custom) \ /* translations */ \ X(INT, NONE, vtmode) /* VT_XWINDOWS, VT_OEMANSI, ... */ \ X(STR, NONE, line_codepage) \ X(BOOL, NONE, cjk_ambig_wide) \ X(BOOL, NONE, utf8_override) \ X(BOOL, NONE, xlat_capslockcyr) \ /* X11 forwarding */ \ X(BOOL, NONE, x11_forward) \ X(STR, NONE, x11_display) \ X(INT, NONE, x11_auth) /* X11_NO_AUTH, X11_MIT, X11_XDM */ \ X(FILENAME, NONE, xauthfile) \ /* port forwarding */ \ X(BOOL, NONE, lport_acceptall) /* accept conns from hosts other than localhost */ \ X(BOOL, 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. All FORCE_ON/FORCE_OFF/AUTO */ \ 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_oldgex2) \ X(INT, NONE, sshbug_winadj) \ X(INT, NONE, sshbug_chanreq) \ /* \ * 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(BOOL, NONE, ssh_simple) \ X(BOOL, NONE, ssh_connection_sharing) \ X(BOOL, NONE, ssh_connection_sharing_upstream) \ X(BOOL, NONE, ssh_connection_sharing_downstream) \ /* * ssh_manual_hostkeys is conceptually a set rather than a * dictionary: the string subkeys are the important thing, and the * actual values to which those subkeys map are all "". */ \ X(STR, STR, ssh_manual_hostkeys) \ /* Options for pterm. Should split out into platform-dependent part. */ \ X(BOOL, NONE, stamp_utmp) \ X(BOOL, NONE, login_shell) \ X(BOOL, NONE, scrollbar_on_left) \ X(BOOL, NONE, shadowbold) \ X(FONT, NONE, boldfont) \ X(FONT, NONE, widefont) \ X(FONT, NONE, wideboldfont) \ X(INT, NONE, shadowboldoffset) /* in pixels */ \ X(BOOL, NONE, crhaslf) \ X(STR, NONE, winclass) \ /* end of list */ /* 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 /* 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. */ bool conf_get_bool(Conf *conf, int key); 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_bool(Conf *conf, int key, bool value); 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 */ void conf_serialise(BinarySink *bs, Conf *conf); bool conf_deserialise(Conf *conf, BinarySource *src);/*returns true on success*/ /* * Functions to copy, free, serialise and deserialise FontSpecs. * Provided per-platform, to go with the platform's idea of a * FontSpec's contents. */ FontSpec *fontspec_copy(const FontSpec *f); void fontspec_free(FontSpec *f); void fontspec_serialise(BinarySink *bs, FontSpec *f); FontSpec *fontspec_deserialise(BinarySource *src); /* * Exports from noise.c. */ typedef enum NoiseSourceId { NOISE_SOURCE_TIME, NOISE_SOURCE_IOID, NOISE_SOURCE_IOLEN, NOISE_SOURCE_KEY, NOISE_SOURCE_MOUSEBUTTON, NOISE_SOURCE_MOUSEPOS, NOISE_SOURCE_MEMINFO, NOISE_SOURCE_STAT, NOISE_SOURCE_RUSAGE, NOISE_SOURCE_FGWINDOW, NOISE_SOURCE_CAPTURE, NOISE_SOURCE_CLIPBOARD, NOISE_SOURCE_QUEUE, NOISE_SOURCE_CURSORPOS, NOISE_SOURCE_THREADTIME, NOISE_SOURCE_PROCTIME, NOISE_SOURCE_PERFCOUNT, NOISE_MAX_SOURCES } NoiseSourceId; void noise_get_heavy(void (*func) (void *, int)); void noise_get_light(void (*func) (void *, int)); void noise_regular(void); void noise_ultralight(NoiseSourceId id, unsigned long data); void random_save_seed(void); void random_destroy_seed(void); /* * Exports from settings.c. * * load_settings() and do_defaults() return false if the provided * session name didn't actually exist. But they still fill in the * provided Conf with _something_. */ const struct BackendVtable *backend_vt_from_name(const char *name); const struct BackendVtable *backend_vt_from_proto(int proto); char *get_remote_username(Conf *conf); /* dynamically allocated */ char *save_settings(const char *section, Conf *conf); void save_open_settings(settings_w *sesskey, Conf *conf); bool load_settings(const char *section, Conf *conf); void load_open_settings(settings_r *sesskey, Conf *conf); void get_sesslist(struct sesslist *, bool allocate); bool do_defaults(const char *, Conf *); void registry_cleanup(void); void settings_set_default_protocol(int); void settings_set_default_port(int); /* * 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); bool platform_default_b(const char *name, bool def); 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 *, TermWin *); void term_free(Terminal *); void term_size(Terminal *, int, int, int); void term_paint(Terminal *, int, int, int, int, bool); void term_scroll(Terminal *, int, int); void term_scroll_to_selection(Terminal *, int); void term_pwron(Terminal *, bool); void term_clrsb(Terminal *); void term_mouse(Terminal *, Mouse_Button, Mouse_Button, Mouse_Action, int, int, bool, bool, bool); void term_key(Terminal *, Key_Sym, wchar_t *, size_t, unsigned int, unsigned int); void term_lost_clipboard_ownership(Terminal *, int clipboard); void term_update(Terminal *); void term_invalidate(Terminal *); void term_blink(Terminal *, bool set_cursor); void term_do_paste(Terminal *, const wchar_t *, int); void term_nopaste(Terminal *); void term_copyall(Terminal *, const int *, int); void term_pre_reconfig(Terminal *, Conf *); void term_reconfig(Terminal *, Conf *); void term_request_copy(Terminal *, const int *clipboards, int n_clipboards); void term_request_paste(Terminal *, int clipboard); void term_seen_key_event(Terminal *); size_t term_data(Terminal *, bool is_stderr, const void *data, size_t len); void term_provide_backend(Terminal *term, Backend *backend); void term_provide_logctx(Terminal *term, LogContext *logctx); void term_set_focus(Terminal *term, bool has_focus); char *term_get_ttymode(Terminal *term, const char *mode); int term_get_userpass_input(Terminal *term, prompts_t *p, bufchain *input); void term_set_trust_status(Terminal *term, bool trusted); void term_keyinput(Terminal *, int codepage, const void *buf, int len); void term_keyinputw(Terminal *, const wchar_t * widebuf, int len); void term_get_cursor_position(Terminal *term, int *x, int *y); void term_setup_window_titles(Terminal *term, const char *title_hostname); void term_notify_minimised(Terminal *term, bool minimised); void term_notify_palette_changed(Terminal *term); void term_notify_window_pos(Terminal *term, int x, int y); void term_notify_window_size_pixels(Terminal *term, int x, int y); void term_palette_override(Terminal *term, unsigned osc4_index, rgb rgb); typedef enum SmallKeypadKey { SKK_HOME, SKK_END, SKK_INSERT, SKK_DELETE, SKK_PGUP, SKK_PGDN, } SmallKeypadKey; int format_arrow_key(char *buf, Terminal *term, int xkey, bool ctrl); int format_function_key(char *buf, Terminal *term, int key_number, bool shift, bool ctrl); int format_small_keypad_key(char *buf, Terminal *term, SmallKeypadKey key); int format_numeric_keypad_key(char *buf, Terminal *term, char key, bool shift, bool ctrl); /* * Exports from logging.c. */ struct LogPolicyVtable { /* * Pass Event Log entries on from LogContext to the front end, * which might write them to standard error or save them for a GUI * list box or other things. */ void (*eventlog)(LogPolicy *lp, const char *event); /* * Ask what to do about the specified output log file already * existing. 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, and callback() will be called with one * of those options. */ int (*askappend)(LogPolicy *lp, Filename *filename, void (*callback)(void *ctx, int result), void *ctx); /* * Emergency logging when the log file itself can't be opened, * which typically means we want to shout about it more loudly * than a mere Event Log entry. * * One reasonable option is to send it to the same place that * stderr output from the main session goes (so, either a console * tool's actual stderr, or a terminal window). In many cases this * is unlikely to cause this error message to turn up * embarrassingly in a log file of real server output, because the * whole point is that we haven't managed to open any such log * file :-) */ void (*logging_error)(LogPolicy *lp, const char *event); /* * Ask whether extra verbose log messages are required. */ bool (*verbose)(LogPolicy *lp); }; struct LogPolicy { const LogPolicyVtable *vt; }; static inline void lp_eventlog(LogPolicy *lp, const char *event) { lp->vt->eventlog(lp, event); } static inline int lp_askappend( LogPolicy *lp, Filename *filename, void (*callback)(void *ctx, int result), void *ctx) { return lp->vt->askappend(lp, filename, callback, ctx); } static inline void lp_logging_error(LogPolicy *lp, const char *event) { lp->vt->logging_error(lp, event); } static inline bool lp_verbose(LogPolicy *lp) { return lp->vt->verbose(lp); } /* Defined in conscli.c, used in several console command-line tools */ extern LogPolicy console_cli_logpolicy[]; int console_askappend(LogPolicy *lp, Filename *filename, void (*callback)(void *ctx, int result), void *ctx); void console_logging_error(LogPolicy *lp, const char *string); void console_eventlog(LogPolicy *lp, const char *string); bool null_lp_verbose_yes(LogPolicy *lp); bool null_lp_verbose_no(LogPolicy *lp); bool cmdline_lp_verbose(LogPolicy *lp); LogContext *log_init(LogPolicy *lp, Conf *conf); void log_free(LogContext *logctx); void log_reconfig(LogContext *logctx, Conf *conf); void logfopen(LogContext *logctx); void logfclose(LogContext *logctx); void logtraffic(LogContext *logctx, unsigned char c, int logmode); void logflush(LogContext *logctx); void logevent(LogContext *logctx, const char *event); void logeventf(LogContext *logctx, const char *fmt, ...) PRINTF_LIKE(2, 3); void logeventvf(LogContext *logctx, const char *fmt, va_list ap); /* * Pass a dynamically allocated string to logevent and immediately * free it. Intended for use by wrapper macros which pass the return * value of dupprintf straight to this. */ void logevent_and_free(LogContext *logctx, char *event); enum { PKT_INCOMING, PKT_OUTGOING }; enum { PKTLOG_EMIT, PKTLOG_BLANK, PKTLOG_OMIT }; struct logblank_t { int offset; int len; int type; }; void log_packet(LogContext *logctx, int direction, int type, const char *texttype, const void *data, size_t 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 const struct BackendVtable null_backend; extern const struct BackendVtable loop_backend; /* * Exports from raw.c. */ extern const struct BackendVtable raw_backend; /* * Exports from rlogin.c. */ extern const struct BackendVtable rlogin_backend; /* * Exports from telnet.c. */ extern const struct BackendVtable telnet_backend; /* * Exports from ssh.c. */ extern const struct BackendVtable ssh_backend; extern const struct BackendVtable sshconn_backend; /* * Exports from supdup.c. */ extern const struct BackendVtable supdup_backend; /* * Exports from ldisc.c. */ Ldisc *ldisc_create(Conf *, Terminal *, Backend *, Seat *); void ldisc_configure(Ldisc *, Conf *); void ldisc_free(Ldisc *); void ldisc_send(Ldisc *, const void *buf, int len, bool interactive); void ldisc_echoedit_update(Ldisc *); /* * Exports from sshrand.c. */ void random_add_noise(NoiseSourceId source, const void *noise, int length); void random_read(void *buf, size_t size); void random_get_savedata(void **data, int *len); extern int random_active; /* The random number subsystem is activated if at least one other entity * within the program expresses an interest in it. So each SSH session * calls random_ref on startup and random_unref on shutdown. */ void random_ref(void); void random_unref(void); /* random_clear is equivalent to calling random_unref as many times as * necessary to shut down the global PRNG instance completely. It's * not needed in normal applications, but the command-line PuTTYgen * test finds it useful to clean up after each invocation of the * logical main() no matter whether it needed random numbers or * not. */ void random_clear(void); /* random_setup_custom sets up the process-global random number * generator specially, with a hash function of your choice. */ void random_setup_custom(const ssh_hashalg *hash); /* random_setup_special() is a macro wrapper on that, which makes an * extra-big one based on the largest hash function we have. It's * defined this way to avoid what would otherwise be an unnecessary * module dependency from sshrand.c to a hash function implementation. */ #define random_setup_special() random_setup_custom(&ssh_shake256_114bytes) /* Manually drop a random seed into the random number generator, e.g. * just before generating a key. */ void random_reseed(ptrlen seed); /* Limit on how much entropy is worth putting into the generator (bits). */ size_t random_seed_bits(void); /* * Exports from pinger.c. */ typedef struct Pinger Pinger; Pinger *pinger_new(Conf *conf, Backend *backend); void pinger_reconfig(Pinger *, Conf *oldconf, Conf *newconf); void pinger_free(Pinger *); /* * Exports from misc.c. */ #include "misc.h" bool conf_launchable(Conf *conf); char const *conf_dest(Conf *conf); /* * Exports from sessprep.c. */ void prepare_session(Conf *conf); /* * Exports from sercfg.c. */ void ser_setup_config_box(struct controlbox *b, bool midsession, int parity_mask, int flow_mask); /* * Exports from version.c. */ extern const char ver[]; extern const char commitid[]; /* * Exports from unicode.c. */ #ifndef CP_UTF8 #define CP_UTF8 65001 #endif /* void init_ucs(void); -- this is now in platform-specific headers */ bool 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, const char *defchr, 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 pageantc.c. * * agent_query returns NULL for here's-a-response, and non-NULL for * query-in- progress. In the latter case there will be a call to * `callback' at some future point, passing callback_ctx as the first * parameter and the actual reply data as the second and third. * * The response may be a NULL pointer (in either of the synchronous * or asynchronous cases), which indicates failure to receive a * response. * * When the return from agent_query is not NULL, it identifies the * in-progress query in case it needs to be cancelled. If * agent_cancel_query is called, then the pending query is destroyed * and the callback will not be called. (E.g. if you're going to throw * away the thing you were using as callback_ctx.) * * Passing a null pointer as callback forces agent_query to behave * synchronously, i.e. it will block if necessary, and guarantee to * return NULL. The wrapper function agent_query_synchronous() makes * this easier. */ typedef struct agent_pending_query agent_pending_query; agent_pending_query *agent_query( strbuf *in, void **out, int *outlen, void (*callback)(void *, void *, int), void *callback_ctx); void agent_cancel_query(agent_pending_query *); void agent_query_synchronous(strbuf *in, void **out, int *outlen); bool agent_exists(void); /* For stream-oriented agent connections, if available. */ Socket *agent_connect(Plug *plug); /* * Exports from wildcard.c */ const char *wc_error(int value); int wc_match_pl(const char *wildcard, ptrlen target); int wc_match(const char *wildcard, const char *target); bool wc_unescape(char *output, const char *wildcard); /* * Exports from frontend (windlg.c etc) */ void pgp_fingerprints(void); /* * have_ssh_host_key() just returns true if a key of that type is * already cached and false otherwise. */ bool have_ssh_host_key(const char *host, int port, const char *keytype); /* * Exports from console frontends (wincons.c, uxcons.c) * that aren't equivalents to things in windlg.c et al. */ extern bool console_batch_mode, console_antispoof_prompt; int console_get_userpass_input(prompts_t *p); bool is_interactive(void); void console_print_error_msg(const char *prefix, const char *msg); void console_print_error_msg_fmt_v( const char *prefix, const char *fmt, va_list ap); void console_print_error_msg_fmt(const char *prefix, const char *fmt, ...) PRINTF_LIKE(2, 3); /* * Exports from printing.c. */ typedef struct printer_enum_tag printer_enum; typedef struct printer_job_tag printer_job; printer_enum *printer_start_enum(int *nprinters); char *printer_get_name(printer_enum *, int); void printer_finish_enum(printer_enum *); printer_job *printer_start_job(char *printer); void printer_job_data(printer_job *, const void *, size_t); 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). * * Note that cmdline_process_param takes a const option string, but a * writable argument string. That's not a mistake - that's so it can * zero out password arguments in the hope of not having them show up * avoidably in Unix 'ps'. */ int cmdline_process_param(const char *, char *, int, Conf *); void cmdline_run_saved(Conf *); void cmdline_cleanup(void); int cmdline_get_passwd_input(prompts_t *p); bool cmdline_host_ok(Conf *); bool cmdline_verbose(void); bool cmdline_loaded_session(void); /* * Here we have a flags word provided by each tool, which describes * the capabilities of that tool that cmdline.c needs to know about. * It will refuse certain command-line options if a particular tool * inherently can't do anything sensible. For example, the file * transfer tools (psftp, pscp) can't do a great deal with protocol * selections (ever tried running scp over telnet?) or with port * forwarding (even if it wasn't a hideously bad idea, they don't have * the select/poll infrastructure to make them work). */ extern const unsigned cmdline_tooltype; /* Bit flags for the above */ #define TOOLTYPE_LIST(X) \ X(TOOLTYPE_FILETRANSFER) \ X(TOOLTYPE_NONNETWORK) \ X(TOOLTYPE_HOST_ARG) \ X(TOOLTYPE_HOST_ARG_CAN_BE_SESSION) \ X(TOOLTYPE_HOST_ARG_PROTOCOL_PREFIX) \ X(TOOLTYPE_HOST_ARG_FROM_LAUNCHABLE_LOAD) \ X(TOOLTYPE_PORT_ARG) \ X(TOOLTYPE_NO_VERBOSE_OPTION) \ /* end of list */ #define BITFLAG_INDEX(val) val ## _bitflag_index, enum { TOOLTYPE_LIST(BITFLAG_INDEX) }; #define BITFLAG_DEF(val) val = 1U << (val ## _bitflag_index), enum { TOOLTYPE_LIST(BITFLAG_DEF) }; void cmdline_error(const char *, ...) PRINTF_LIKE(1, 2); /* * Exports from config.c. */ struct controlbox; union control; void conf_radiobutton_handler(union control *ctrl, dlgparam *dlg, void *data, int event); #define CHECKBOX_INVERT (1<<30) void conf_checkbox_handler(union control *ctrl, dlgparam *dlg, void *data, int event); void conf_editbox_handler(union control *ctrl, dlgparam *dlg, void *data, int event); void conf_filesel_handler(union control *ctrl, dlgparam *dlg, void *data, int event); void conf_fontsel_handler(union control *ctrl, dlgparam *dlg, void *data, int event); void setup_config_box(struct controlbox *b, bool midsession, int protocol, int protcfginfo); /* * Exports from minibidi.c. */ #define BIDI_CHAR_INDEX_NONE ((unsigned short)-1) typedef struct bidi_char { unsigned int origwc, wc; unsigned short index, nchars; } bidi_char; int do_bidi(bidi_char *line, int count); int do_shape(bidi_char *line, bidi_char *to, int count); bool 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 */ /* * An enum for the copy-paste UI action configuration. */ enum { CLIPUI_NONE, /* UI action has no copy/paste effect */ CLIPUI_IMPLICIT, /* use the default clipboard implicit in mouse actions */ CLIPUI_EXPLICIT, /* use the default clipboard for explicit Copy/Paste */ CLIPUI_CUSTOM, /* use a named clipboard (on systems that support it) */ }; /* * 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); bool filename_equal(const Filename *f1, const Filename *f2); bool filename_is_null(const Filename *fn); Filename *filename_copy(const Filename *fn); void filename_free(Filename *fn); void filename_serialise(BinarySink *bs, const Filename *f); Filename *filename_deserialise(BinarySource *src); char *get_username(void); /* return value needs freeing */ char *get_random_data(int bytes, const char *device); /* used in cmdgen.c */ char filename_char_sanitise(char c); /* rewrite special pathname chars */ bool open_for_write_would_lose_data(const Filename *fn); /* * Exports and imports from timing.c. * * schedule_timer() asks the front end to schedule a callback to a * timer function in a given number of ticks. The returned value is * the time (in ticks since an arbitrary offset) at which the * callback can be expected. This value will also be passed as the * `now' parameter to the callback function. Hence, you can (for * example) schedule an event at a particular time by calling * schedule_timer() and storing the return value in your context * structure as the time when that event is due. The first time a * callback function gives you that value or more as `now', you do * the thing. * * expire_timer_context() drops all current timers associated with * a given value of ctx (for when you're about to free ctx). * * run_timers() is called from the front end when it has reason to * think some timers have reached their moment, or when it simply * needs to know how long to wait next. We pass it the time we * think it is. It returns true and places the time when the next * timer needs to go off in `next', or alternatively it returns * false if there are no timers at all pending. * * timer_change_notify() must be supplied by the front end; it * notifies the front end that a new timer has been added to the * list which is sooner than any existing ones. It provides the * time when that timer needs to go off. * * *** FRONT END IMPLEMENTORS NOTE: * * There's an important subtlety in the front-end implementation of * the timer interface. When a front end is given a `next' value, * either returned from run_timers() or via timer_change_notify(), * it should ensure that it really passes _that value_ as the `now' * parameter to its next run_timers call. It should _not_ simply * call GETTICKCOUNT() to get the `now' parameter when invoking * run_timers(). * * The reason for this is that an OS's system clock might not agree * exactly with the timing mechanisms it supplies to wait for a * given interval. I'll illustrate this by the simple example of * Unix Plink, which uses timeouts to poll() in a way which for * these purposes can simply be considered to be a wait() function. * Suppose, for the sake of argument, that this wait() function * tends to return early by 1%. Then a possible sequence of actions * is: * * - run_timers() tells the front end that the next timer firing * is 10000ms from now. * - Front end calls wait(10000ms), but according to * GETTICKCOUNT() it has only waited for 9900ms. * - Front end calls run_timers() again, passing time T-100ms as * `now'. * - run_timers() does nothing, and says the next timer firing is * still 100ms from now. * - Front end calls wait(100ms), which only waits for 99ms. * - Front end calls run_timers() yet again, passing time T-1ms. * - run_timers() says there's still 1ms to wait. * - Front end calls wait(1ms). * * If you're _lucky_ at this point, wait(1ms) will actually wait * for 1ms and you'll only have woken the program up three times. * If you're unlucky, wait(1ms) might do nothing at all due to * being below some minimum threshold, and you might find your * program spends the whole of the last millisecond tight-looping * between wait() and run_timers(). * * Instead, what you should do is to _save_ the precise `next' * value provided by run_timers() or via timer_change_notify(), and * use that precise value as the input to the next run_timers() * call. So: * * - run_timers() tells the front end that the next timer firing * is at time T, 10000ms from now. * - Front end calls wait(10000ms). * - Front end then immediately calls run_timers() and passes it * time T, without stopping to check GETTICKCOUNT() at all. * * This guarantees that the program wakes up only as many times as * there are actual timer actions to be taken, and that the timing * mechanism will never send it into a tight loop. * * (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); bool run_timers(unsigned long now, unsigned long *next); void timer_change_notify(unsigned long next); unsigned long timing_last_clock(void); /* * 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 poll() * call) then it can call toplevel_callback_pending(), which will * return true if at least one callback is in the queue. * * run_toplevel_callbacks() returns true if it ran any actual code. * This can be used as a means of speculatively terminating a poll * loop, as in PSFTP, for example - if a callback has run then perhaps * it might have done whatever the loop's caller was waiting for. */ typedef void (*toplevel_callback_fn_t)(void *ctx); void queue_toplevel_callback(toplevel_callback_fn_t fn, void *ctx); bool run_toplevel_callbacks(void); bool toplevel_callback_pending(void); void delete_callbacks_for_context(void *ctx); /* * Another facility in callback.c deals with 'idempotent' callbacks, * defined as those which never need to be scheduled again if they are * already scheduled and have not yet run. (An example would be one * which, when called, empties a queue of data completely: when data * is added to the queue, you must ensure a run of the queue-consuming * function has been scheduled, but if one is already pending, you * don't need to schedule a second one.) */ struct IdempotentCallback { toplevel_callback_fn_t fn; void *ctx; bool queued; }; void queue_idempotent_callback(struct IdempotentCallback *ic); typedef void (*toplevel_callback_notify_fn_t)(void *ctx); void request_callback_notifications(toplevel_callback_notify_fn_t notify, void *ctx); /* * 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 HIGH_SURROGATE_START /* in some toolchains defines these */ #define HIGH_SURROGATE_START 0xd800 #define HIGH_SURROGATE_END 0xdbff #define LOW_SURROGATE_START 0xdc00 #define LOW_SURROGATE_END 0xdfff #endif /* These macros exist in the Windows API, so the environment may * provide them. If not, define them in terms of the above. */ #ifndef IS_HIGH_SURROGATE #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 putty-0.76/puttymem.h0000644000175000017500000001251714072266311011621 00000000000000/* * PuTTY memory-handling header. */ #ifndef PUTTY_PUTTYMEM_H #define PUTTY_PUTTYMEM_H #include /* for size_t */ #include /* for memcpy() */ #include "defs.h" #define smalloc(z) safemalloc(z,1,0) #define snmalloc safemalloc #define srealloc(y,z) saferealloc(y,z,1) #define snrealloc saferealloc #define sfree safefree void *safemalloc(size_t factor1, size_t factor2, size_t addend); void *saferealloc(void *, size_t, size_t); 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. sresize also uses * TYPECHECK to verify that the _input_ pointer is a pointer to the * correct type. */ #define snew(type) ((type *)snmalloc(1, sizeof(type), 0)) #define snewn(n, type) ((type *)snmalloc((n), sizeof(type), 0)) #define sresize(ptr, n, type) TYPECHECK((type *)0 == (ptr), \ ((type *)snrealloc((ptr), (n), sizeof(type)))) /* * For cases where you want to allocate a struct plus a subsidiary * data buffer in one step, this macro lets you add a constant to the * amount malloced. * * Since the return value is already cast to the struct type, a * pointer to that many bytes of extra data can be conveniently * obtained by simply adding 1 to the returned pointer! * snew_plus_get_aux is a handy macro that does that and casts the * result to void *, so you can assign it straight to wherever you * wanted it. */ #define snew_plus(type, extra) ((type *)snmalloc(1, sizeof(type), (extra))) #define snew_plus_get_aux(ptr) ((void *)((ptr) + 1)) /* * Helper macros to deal with the common use case of growing an array. * * The common setup is that 'array' is a pointer to the first element * of a dynamic array of some type, and 'size' represents the current * allocated size of that array (in elements). Both of those macro * parameters are implicitly written back to. * * Then sgrowarray(array, size, n) means: make sure the nth element of * the array exists (i.e. the size is at least n+1). You call that * before writing to the nth element, if you're looping round * appending to the array. * * If you need to grow the array by more than one element, you can * instead call sgrowarrayn(array, size, n, m), which will ensure the * size of the array is at least n+m. (So sgrowarray is just the * special case of that in which m == 1.) * * It's common to call sgrowarrayn with one of n,m equal to the * previous logical length of the array, and the other equal to the * new number of logical entries you want to add, so that n <= size on * entry. But that's not actually a mandatory precondition: the two * length parameters are just arbitrary integers that get added * together with an initial check for overflow, and the semantics are * simply 'make sure the array is big enough to take their sum, no * matter how big it was to start with'.) * * Another occasionally useful idiom is to call sgrowarray with n == * size, i.e. sgrowarray(array, size, size). That just means: make * array bigger by _some_ amount, I don't particularly mind how much. * You might use that style if you were repeatedly calling an API * function outside your control, which would either fill your buffer * and return success, or else return a 'too big' error without * telling you how much bigger it needed to be. * * The _nm variants of the macro set the 'private' flag in the * underlying function, which forces array resizes to be done by a * manual allocate/copy/free instead of realloc, with careful clearing * of the previous memory block before we free it. This costs * performance, but if the block contains important secrets such as * private keys or passwords, it avoids the risk that a realloc that * moves the memory block might leave a copy of the data visible in * the freed memory at the previous location. */ void *safegrowarray(void *array, size_t *size, size_t eltsize, size_t oldlen, size_t extralen, bool private); /* The master macro wrapper, of which all others are special cases */ #define sgrowarray_general(array, size, n, m, priv) \ ((array) = safegrowarray(array, &(size), sizeof(*array), n, m, priv)) /* The special-case macros that are easier to use in most situations */ #define sgrowarrayn( a, s, n, m) sgrowarray_general(a, s, n, m, false) #define sgrowarray( a, s, n ) sgrowarray_general(a, s, n, 1, false) #define sgrowarrayn_nm(a, s, n, m) sgrowarray_general(a, s, n, m, true ) #define sgrowarray_nm( a, s, n ) sgrowarray_general(a, s, n, 1, true ) /* * This function is called by the innermost safemalloc/saferealloc * functions when allocation fails. Usually it's provided by misc.c * which ties it into an application's existing modalfatalbox() * system, but standalone test applications can reimplement it some * other way if they prefer. */ NORETURN void out_of_memory(void); #ifdef MINEFIELD /* * Definitions for Minefield, PuTTY's own Windows-specific malloc * debugger in the style of Electric Fence. Implemented in winmisc.c, * and referred to by the main malloc wrappers in memory.c. */ void *minefield_c_malloc(size_t size); void minefield_c_free(void *p); void *minefield_c_realloc(void *p, size_t size); #endif #endif putty-0.76/puttyps.h0000644000175000017500000000030214072266311011452 00000000000000/* * Find the platform-specific header for this platform. */ #ifndef PUTTY_PUTTYPS_H #define PUTTY_PUTTYPS_H #ifdef _WINDOWS #include "winstuff.h" #else #include "unix.h" #endif #endif putty-0.76/raw.c0000644000175000017500000001757414072266311010531 00000000000000/* * "Raw" backend. */ #include #include #include #include "putty.h" #define RAW_MAX_BACKLOG 4096 typedef struct Raw Raw; struct Raw { Socket *s; bool closed_on_socket_error; size_t bufsize; Seat *seat; LogContext *logctx; bool sent_console_eof, sent_socket_eof, session_started; Conf *conf; Plug plug; Backend backend; }; static void raw_size(Backend *be, int width, int height); static void c_write(Raw *raw, const void *buf, size_t len) { size_t backlog = seat_stdout(raw->seat, buf, len); sk_set_frozen(raw->s, backlog > RAW_MAX_BACKLOG); } static void raw_log(Plug *plug, PlugLogType type, SockAddr *addr, int port, const char *error_msg, int error_code) { Raw *raw = container_of(plug, Raw, plug); backend_socket_log(raw->seat, raw->logctx, type, addr, port, error_msg, error_code, raw->conf, raw->session_started); } 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; seat_notify_remote_exit(raw->seat); } } } static void raw_closing(Plug *plug, const char *error_msg, int error_code, bool calling_back) { Raw *raw = container_of(plug, 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; seat_notify_remote_exit(raw->seat); } logevent(raw->logctx, error_msg); seat_connection_fatal(raw->seat, "%s", error_msg); } else { /* Otherwise, the remote side closed the connection normally. */ if (!raw->sent_console_eof && seat_eof(raw->seat)) { /* * 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); } } static void raw_receive(Plug *plug, int urgent, const char *data, size_t len) { Raw *raw = container_of(plug, Raw, plug); c_write(raw, data, len); /* We count 'session start', for proxy logging purposes, as being * when data is received from the network and printed. */ raw->session_started = true; } static void raw_sent(Plug *plug, size_t bufsize) { Raw *raw = container_of(plug, Raw, plug); raw->bufsize = bufsize; } static const PlugVtable Raw_plugvt = { .log = raw_log, .closing = raw_closing, .receive = raw_receive, .sent = raw_sent, }; /* * Called to set up the raw 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 char *raw_init(const BackendVtable *vt, Seat *seat, Backend **backend_handle, LogContext *logctx, Conf *conf, const char *host, int port, char **realhost, bool nodelay, bool keepalive) { SockAddr *addr; const char *err; Raw *raw; int addressfamily; char *loghost; /* No local authentication phase in this protocol */ seat_set_trust_status(seat, false); raw = snew(Raw); raw->plug.vt = &Raw_plugvt; raw->backend.vt = vt; raw->s = NULL; raw->closed_on_socket_error = false; *backend_handle = &raw->backend; raw->sent_console_eof = raw->sent_socket_eof = false; raw->bufsize = 0; raw->session_started = false; raw->conf = conf_copy(conf); raw->seat = seat; raw->logctx = logctx; addressfamily = conf_get_int(conf, CONF_addressfamily); /* * Try to find host. */ addr = name_lookup(host, port, realhost, conf, addressfamily, raw->logctx, "main connection"); if ((err = sk_addr_error(addr)) != NULL) { sk_addr_free(addr); return dupstr(err); } if (port < 0) port = 23; /* default telnet port */ /* * Open socket. */ raw->s = new_connection(addr, *realhost, port, false, true, nodelay, keepalive, &raw->plug, conf); if ((err = sk_socket_error(raw->s)) != NULL) return dupstr(err); loghost = conf_get_str(conf, CONF_loghost); if (*loghost) { char *colon; sfree(*realhost); *realhost = dupstr(loghost); colon = host_strrchr(*realhost, ':'); if (colon) *colon++ = '\0'; } return NULL; } static void raw_free(Backend *be) { Raw *raw = container_of(be, Raw, backend); if (raw->s) sk_close(raw->s); conf_free(raw->conf); sfree(raw); } /* * Stub routine (we don't have any need to reconfigure this backend). */ static void raw_reconfig(Backend *be, Conf *conf) { } /* * Called to send data down the raw connection. */ static size_t raw_send(Backend *be, const char *buf, size_t len) { Raw *raw = container_of(be, Raw, backend); if (raw->s == NULL) return 0; raw->bufsize = sk_write(raw->s, buf, len); return raw->bufsize; } /* * Called to query the current socket sendability status. */ static size_t raw_sendbuffer(Backend *be) { Raw *raw = container_of(be, Raw, backend); return raw->bufsize; } /* * Called to set the size of the window */ static void raw_size(Backend *be, int width, int height) { /* Do nothing! */ return; } /* * Send raw special codes. We only handle outgoing EOF here. */ static void raw_special(Backend *be, SessionSpecialCode code, int arg) { Raw *raw = container_of(be, Raw, backend); if (code == SS_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. */ static const SessionSpecial *raw_get_specials(Backend *be) { return NULL; } static bool raw_connected(Backend *be) { Raw *raw = container_of(be, Raw, backend); return raw->s != NULL; } static bool raw_sendok(Backend *be) { return true; } static void raw_unthrottle(Backend *be, size_t backlog) { Raw *raw = container_of(be, Raw, backend); sk_set_frozen(raw->s, backlog > RAW_MAX_BACKLOG); } static bool raw_ldisc(Backend *be, int option) { if (option == LD_EDIT || option == LD_ECHO) return true; return false; } static void raw_provide_ldisc(Backend *be, Ldisc *ldisc) { /* This is a stub. */ } static int raw_exitcode(Backend *be) { Raw *raw = container_of(be, Raw, backend); 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. */ static int raw_cfg_info(Backend *be) { return 0; } const BackendVtable raw_backend = { .init = raw_init, .free = raw_free, .reconfig = raw_reconfig, .send = raw_send, .sendbuffer = raw_sendbuffer, .size = raw_size, .special = raw_special, .get_specials = raw_get_specials, .connected = raw_connected, .exitcode = raw_exitcode, .sendok = raw_sendok, .ldisc_option_state = raw_ldisc, .provide_ldisc = raw_provide_ldisc, .unthrottle = raw_unthrottle, .cfg_info = raw_cfg_info, .id = "raw", .displayname = "Raw", .protocol = PROT_RAW, .default_port = 0, }; putty-0.76/release.pl0000755000175000017500000002204414072266311011540 00000000000000#!/usr/bin/perl # Script to automate some easy-to-mess-up parts of the PuTTY release # procedure. use strict; use warnings; use Getopt::Long; use File::Find; use File::Temp qw/tempdir/; use LWP::UserAgent; my $version = undef; my $setver = 0; my $upload = 0; my $precheck = 0; my $postcheck = 0; my $skip_ftp = 0; GetOptions("version=s" => \$version, "setver" => \$setver, "upload" => \$upload, "precheck" => \$precheck, "postcheck" => \$postcheck, "no-ftp" => \$skip_ftp) or &usage(); # --setver: construct a local commit which updates the version # number, and the command-line help transcripts in the docs. if ($setver) { defined $version or die "use --version"; 0 == system "git", "diff-index", "--quiet", "--cached", "HEAD" or die "index is dirty"; 0 == system "git", "diff-files", "--quiet" or die "working tree is dirty"; my $builddir = tempdir(DIR => ".", CLEANUP => 1); 0 == system "git archive --format=tar HEAD | ( cd $builddir && tar xf - )" or die; 0 == system "cd $builddir && ./mkfiles.pl" or die; 0 == system "cd $builddir && ./mkauto.sh" or die; 0 == system "cd $builddir && ./configure" or die; 0 == system "cd $builddir && make pscp plink RELEASE=${version}" or die; our $pscp_transcript = `cd $builddir && ./pscp --help`; $pscp_transcript =~ s/^Unidentified build/Release ${version}/m or die; $pscp_transcript =~ s/^/\\c /mg; our $plink_transcript = `cd $builddir && ./plink --help`; $plink_transcript =~ s/^Unidentified build/Release ${version}/m or die; $plink_transcript =~ s/^/\\c /mg; &transform("LATEST.VER", sub { s/^\d+\.\d+$/$version/ }); our $transforming = 0; &transform("doc/pscp.but", sub { if (/^\\c.*>pscp$/) { $transforming = 1; $_ .= $pscp_transcript; } elsif (!/^\\c/) { $transforming = 0; } elsif ($transforming) { $_=""; } }); $transforming = 0; &transform("doc/plink.but", sub { if (/^\\c.*>plink$/) { $transforming = 1; $_ .= $plink_transcript; } elsif (!/^\\c/) { $transforming = 0; } elsif ($transforming) { $_=""; } }); &transform("Buildscr", sub { s!^(set Epoch )\d+!$1 . sprintf "%d", time/86400 - 1000!e }); 0 == system ("git", "commit", "-a", "-m", "Update version number for ${version} release.") or die; exit 0; } # --upload: upload the release to all the places it should live, and # check all signatures and md5sums once it arrives there. if ($upload) { defined $version or die "use --version"; # Run this inside the build.out directory. -d "maps" or die "no maps directory in cwd"; -d "putty" or die "no putty directory in cwd"; 0 == system("rsync", "-av", "maps/", "thyestes:src/putty-local/maps-$version") or die "could not upload link maps"; for my $location (["thyestes", "www/putty/$version"], ["the", "www/putty/$version"], ["chiark", "ftp/putty-$version"]) { my ($host, $path) = @$location; 0 == system("rsync", "-av", "putty/", "$host:$path") or die "could not upload release to $host"; open my $pipe, "|-", "ssh", $host, "cd $path && sh"; print $pipe "set -e\n"; print $pipe "pwd\n"; find({ wanted => sub { if (m!^putty/(.*).gpg!) { my $file = $1; print $pipe "echo verifying $file\n"; if ($file =~ /sums$/) { print $pipe "gpg --verify $file.gpg\n"; } else { print $pipe "gpg --verify $file.gpg $file\n"; } } elsif (m!^putty/(.*sum)s!) { print $pipe "echo checking ${1}s\n"; print $pipe "grep -vF ' (installer version)' ${1}s | grep . | $1 -c\n"; } }, no_chdir => 1}, "putty"); print $pipe "echo all verified ok\n"; close $pipe; die "VERIFICATION FAILED on $host" if $? != 0; } print "Uploaded $version OK!\n"; exit 0; } # --precheck and --postcheck: attempt to download the release from its # various web and FTP locations. if ($precheck || $postcheck) { defined $version or die "use --version"; # Run this inside the build.out directory, so we can check the # downloaded files against the exact contents they should have. -d "putty" or die "no putty directory in cwd"; my $httpprefix = "https://the.earth.li/~sgtatham/putty/"; my $ftpprefix = "ftp://ftp.chiark.greenend.org.uk/users/sgtatham/putty-"; # Go through all the files in build.out. find({ wanted => sub { if (-f $_) { die unless (m!^putty/(.*)$!); my $path = $1; # Don't try to check .htaccess - web servers will # treat it weirdly. return if $path =~ m!^(.*/)?.htaccess$!; print "Checking $path\n"; my $real_content = ""; open my $fh, "<", $_ or die "$_: open local file: $!"; $real_content .= $_ while <$fh>; close $fh; my $http_numbered = "${httpprefix}$version/$path"; my $http_latest = "${httpprefix}latest/$path"; my $ftp_numbered = "${ftpprefix}$version/$path"; my $ftp_latest = "${ftpprefix}latest/$path"; my ($http_uri, $ftp_uri); if ($precheck) { # Before the 'latest' links/redirects update, # we just download from explicitly version- # numbered URLs. $http_uri = $http_numbered; $ftp_uri = $ftp_numbered; } if ($postcheck) { # After 'latest' is updated, we're testing that # the redirects work, so we download from the # URLs with 'latest' in them. $http_uri = $http_latest; $ftp_uri = $ftp_latest; } # Now test-download the files themselves. unless ($skip_ftp) { my $ftpdata = `curl -s $ftp_uri`; printf " got %d bytes via FTP", length $ftpdata; die "FTP download for $ftp_uri did not match" if $ftpdata ne $real_content; print ", ok\n"; } my $ua = LWP::UserAgent->new; my $httpresponse = $ua->get($http_uri); my $httpdata = $httpresponse->{_content}; printf " got %d bytes via HTTP", length $httpdata; die "HTTP download for $http_uri did not match" if $httpdata ne $real_content; print ", ok\n"; # Check content types on any files likely to go # wrong. my $ct = $httpresponse->{_headers}->{"content-type"}; if (defined $ct) { printf " got content-type %s", $ct; } else { printf " got no content-type"; } my $right_ct = undef; if ($path =~ m/\.(hlp|cnt|chm)$/) { $right_ct = "application/octet-stream"; } elsif ($path =~ /\.gpg$/) { $right_ct = "application/pgp-signature"; } if (defined $right_ct) { if ($ct ne $right_ct) { die "content-type $ct should be $right_ct"; } else { print ", ok\n"; } } else { print "\n"; } if ($postcheck) { # Finally, if we're testing the 'latest' URL, # also check that the HTTP redirect header was # present and correct. my $redirected = $httpresponse->{_request}->{_uri}; printf " redirect -> %s\n", $redirected; die "redirect header wrong for $http_uri" if $redirected ne $http_numbered; } } }, no_chdir => 1}, "putty"); print "Check OK\n"; exit 0; } &usage(); sub transform { my ($filename, $proc) = @_; my $file; open $file, "<", $filename or die "$file: open for read: $!\n"; my $data = ""; while (<$file>) { $proc->(); $data .= $_; } close $file; open $file, ">", $filename or die "$file: open for write: $!\n"; print $file $data; close $file or die "$file: close after write: $!\n";; } sub usage { die "usage: release.pl --set-version=X.YZ\n"; } putty-0.76/resource.h0000644000175000017500000000061314072266311011556 00000000000000//{{NO_DEPENDENCIES}} // Microsoft Developer Studio generated include file. // Used by win_res.rc // // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 101 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1000 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif putty-0.76/rlogin.c0000644000175000017500000002702514072266311011222 00000000000000/* * Rlogin backend. */ #include #include #include #include #include "putty.h" #define RLOGIN_MAX_BACKLOG 4096 typedef struct Rlogin Rlogin; struct Rlogin { Socket *s; bool closed_on_socket_error; int bufsize; bool firstbyte; bool cansize; int term_width, term_height; Seat *seat; LogContext *logctx; Conf *conf; /* In case we need to read a username from the terminal before starting */ prompts_t *prompt; Plug plug; Backend backend; }; static void c_write(Rlogin *rlogin, const void *buf, size_t len) { size_t backlog = seat_stdout(rlogin->seat, buf, len); sk_set_frozen(rlogin->s, backlog > RLOGIN_MAX_BACKLOG); } static void rlogin_log(Plug *plug, PlugLogType type, SockAddr *addr, int port, const char *error_msg, int error_code) { Rlogin *rlogin = container_of(plug, Rlogin, plug); backend_socket_log(rlogin->seat, rlogin->logctx, type, addr, port, error_msg, error_code, rlogin->conf, !rlogin->firstbyte); } static void rlogin_closing(Plug *plug, const char *error_msg, int error_code, bool calling_back) { Rlogin *rlogin = container_of(plug, 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; seat_notify_remote_exit(rlogin->seat); } if (error_msg) { /* A socket error has occurred. */ logevent(rlogin->logctx, error_msg); seat_connection_fatal(rlogin->seat, "%s", error_msg); } /* Otherwise, the remote side closed the connection normally. */ } static void rlogin_receive( Plug *plug, int urgent, const char *data, size_t len) { Rlogin *rlogin = container_of(plug, Rlogin, plug); if (len == 0) return; if (urgent == 2) { char c; c = *data++; len--; if (c == '\x80') { rlogin->cansize = true; backend_size(&rlogin->backend, rlogin->term_width, rlogin->term_height); } /* * We should flush everything (aka Telnet SYNCH) if we see * 0x02, and we should turn off and on _local_ flow control * on 0x10 and 0x20 respectively. I'm not convinced it's * worth it... */ } else { /* * Main rlogin protocol. This is really simple: the first * byte is expected to be NULL and is ignored, and the rest * is printed. */ if (rlogin->firstbyte) { if (data[0] == '\0') { data++; len--; } rlogin->firstbyte = false; } if (len > 0) c_write(rlogin, data, len); } } static void rlogin_sent(Plug *plug, size_t bufsize) { Rlogin *rlogin = container_of(plug, Rlogin, plug); 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; } static const PlugVtable Rlogin_plugvt = { .log = rlogin_log, .closing = rlogin_closing, .receive = rlogin_receive, .sent = rlogin_sent, }; /* * 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 char *rlogin_init(const BackendVtable *vt, Seat *seat, Backend **backend_handle, LogContext *logctx, Conf *conf, const char *host, int port, char **realhost, bool nodelay, bool keepalive) { SockAddr *addr; const char *err; Rlogin *rlogin; char *ruser; int addressfamily; char *loghost; rlogin = snew(Rlogin); rlogin->plug.vt = &Rlogin_plugvt; rlogin->backend.vt = vt; rlogin->s = NULL; rlogin->closed_on_socket_error = false; rlogin->seat = seat; rlogin->logctx = logctx; rlogin->term_width = conf_get_int(conf, CONF_width); rlogin->term_height = conf_get_int(conf, CONF_height); rlogin->firstbyte = true; rlogin->cansize = false; rlogin->prompt = NULL; rlogin->conf = conf_copy(conf); *backend_handle = &rlogin->backend; addressfamily = conf_get_int(conf, CONF_addressfamily); /* * Try to find host. */ addr = name_lookup(host, port, realhost, conf, addressfamily, rlogin->logctx, "rlogin connection"); if ((err = sk_addr_error(addr)) != NULL) { sk_addr_free(addr); return dupstr(err); } if (port < 0) port = 513; /* default rlogin port */ /* * Open socket. */ rlogin->s = new_connection(addr, *realhost, port, true, false, nodelay, keepalive, &rlogin->plug, conf); if ((err = sk_socket_error(rlogin->s)) != NULL) return dupstr(err); loghost = conf_get_str(conf, CONF_loghost); if (*loghost) { char *colon; sfree(*realhost); *realhost = dupstr(loghost); colon = host_strrchr(*realhost, ':'); if (colon) *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) { /* Next terminal output will come from server */ seat_set_trust_status(rlogin->seat, false); rlogin_startup(rlogin, ruser); sfree(ruser); } else { int ret; rlogin->prompt = new_prompts(); rlogin->prompt->to_server = true; rlogin->prompt->from_server = false; rlogin->prompt->name = dupstr("Rlogin login name"); add_prompt(rlogin->prompt, dupstr("rlogin username: "), true); ret = seat_get_userpass_input(rlogin->seat, rlogin->prompt, NULL); if (ret >= 0) { /* Next terminal output will come from server */ seat_set_trust_status(rlogin->seat, false); rlogin_startup(rlogin, prompt_get_result_ref( rlogin->prompt->prompts[0])); } } return NULL; } static void rlogin_free(Backend *be) { Rlogin *rlogin = container_of(be, Rlogin, backend); 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(Backend *be, Conf *conf) { } /* * Called to send data down the rlogin connection. */ static size_t rlogin_send(Backend *be, const char *buf, size_t len) { Rlogin *rlogin = container_of(be, Rlogin, backend); bufchain bc; if (rlogin->s == NULL) return 0; bufchain_init(&bc); bufchain_add(&bc, buf, len); if (rlogin->prompt) { /* * We're still prompting for a username, and aren't talking * directly to the network connection yet. */ int ret = seat_get_userpass_input(rlogin->seat, rlogin->prompt, &bc); if (ret >= 0) { /* Next terminal output will come from server */ seat_set_trust_status(rlogin->seat, false); rlogin_startup(rlogin, prompt_get_result_ref( rlogin->prompt->prompts[0])); /* that nulls out rlogin->prompt, so then we'll start sending * data down the wire in the obvious way */ } } if (!rlogin->prompt) { while (bufchain_size(&bc) > 0) { ptrlen data = bufchain_prefix(&bc); rlogin->bufsize = sk_write(rlogin->s, data.ptr, data.len); bufchain_consume(&bc, len); } } bufchain_clear(&bc); return rlogin->bufsize; } /* * Called to query the current socket sendability status. */ static size_t rlogin_sendbuffer(Backend *be) { Rlogin *rlogin = container_of(be, Rlogin, backend); return rlogin->bufsize; } /* * Called to set the size of the window */ static void rlogin_size(Backend *be, int width, int height) { Rlogin *rlogin = container_of(be, Rlogin, backend); char b[12] = { '\xFF', '\xFF', 0x73, 0x73, 0, 0, 0, 0, 0, 0, 0, 0 }; rlogin->term_width = width; rlogin->term_height = height; if (rlogin->s == NULL || !rlogin->cansize) return; b[6] = rlogin->term_width >> 8; b[7] = rlogin->term_width & 0xFF; b[4] = rlogin->term_height >> 8; b[5] = rlogin->term_height & 0xFF; rlogin->bufsize = sk_write(rlogin->s, b, 12); return; } /* * Send rlogin special codes. */ static void rlogin_special(Backend *be, SessionSpecialCode code, int arg) { /* Do nothing! */ return; } /* * Return a list of the special codes that make sense in this * protocol. */ static const SessionSpecial *rlogin_get_specials(Backend *be) { return NULL; } static bool rlogin_connected(Backend *be) { Rlogin *rlogin = container_of(be, Rlogin, backend); return rlogin->s != NULL; } static bool rlogin_sendok(Backend *be) { /* Rlogin *rlogin = container_of(be, Rlogin, backend); */ return true; } static void rlogin_unthrottle(Backend *be, size_t backlog) { Rlogin *rlogin = container_of(be, Rlogin, backend); sk_set_frozen(rlogin->s, backlog > RLOGIN_MAX_BACKLOG); } static bool rlogin_ldisc(Backend *be, int option) { /* Rlogin *rlogin = container_of(be, Rlogin, backend); */ return false; } static void rlogin_provide_ldisc(Backend *be, Ldisc *ldisc) { /* This is a stub. */ } static int rlogin_exitcode(Backend *be) { Rlogin *rlogin = container_of(be, Rlogin, backend); 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. */ static int rlogin_cfg_info(Backend *be) { return 0; } const BackendVtable rlogin_backend = { .init = rlogin_init, .free = rlogin_free, .reconfig = rlogin_reconfig, .send = rlogin_send, .sendbuffer = rlogin_sendbuffer, .size = rlogin_size, .special = rlogin_special, .get_specials = rlogin_get_specials, .connected = rlogin_connected, .exitcode = rlogin_exitcode, .sendok = rlogin_sendok, .ldisc_option_state = rlogin_ldisc, .provide_ldisc = rlogin_provide_ldisc, .unthrottle = rlogin_unthrottle, .cfg_info = rlogin_cfg_info, .id = "rlogin", .displayname = "Rlogin", .protocol = PROT_RLOGIN, .default_port = 513, }; putty-0.76/scpserver.c0000644000175000017500000013345614072266311011752 00000000000000/* * Server side of the old-school SCP protocol. */ #include #include #include #include "putty.h" #include "ssh.h" #include "sshcr.h" #include "sshchan.h" #include "sftp.h" /* * I think it's worth actually documenting my understanding of what * this protocol _is_, since I don't know of any other documentation * of it anywhere. * * Format of data stream * --------------------- * * The sending side of an SCP connection - the client, if you're * uploading files, or the server if you're downloading - sends a data * stream consisting of a sequence of 'commands', or header records, * or whatever you want to call them, interleaved with file data. * * Each command starts with a letter indicating what type it is, and * ends with a \n. * * The 'C' command introduces an actual file. It is followed by an * octal file-permissions mask, then a space, then a decimal file * size, then a space, then the file name up to the termating newline. * For example, "C0644 12345 filename.txt\n" would be a plausible C * command. * * After the 'C' command, the sending side will transmit exactly as * many bytes of file data as specified by the size field in the * header line, followed by a single zero byte. * * The 'D' command introduces a subdirectory. Its format is identical * to 'C', including the size field, but the size field is sent as * zero. * * After the 'D' command, all subsequent C and D commands are taken to * indicate files that should be placed inside that subdirectory, * until a terminating 'E' command. * * The 'E' command indicates the end of a subdirectory. It has no * arguments at all (its format is always just "E\n"). After the E * command, the receiver should revert to placing further downloaded * files in whatever directory it was placing them before the * subdirectory opened by the just-closed D. * * D and E commands match like parentheses: if you send, say, * * C0644 123 foo.txt ( followed by data ) * D0755 0 subdir * C0644 123 bar.txt ( followed by data ) * D0755 0 subsubdir * C0644 123 baz.txt ( followed by data ) * E * C0644 123 quux.txt ( followed by data ) * E * C0644 123 wibble.txt ( followed by data ) * * then foo.txt, subdir and wibble.txt go in the top-level destination * directory; bar.txt, subsubdir and quux.txt go in 'subdir'; and * baz.txt goes in 'subdir/subsubdir'. * * The sender terminates the data stream with EOF when it has no more * files to send. I believe it is not _required_ for all D to be * closed by an E before this happens - you can elide a trailing * sequence of E commands without provoking an error message from the * receiver. * * Finally, the 'T' command is sent immediately before a C or D. It is * followed by four space-separated decimal integers giving an mtime * and atime to be applied to the file or directory created by the * following C or D command. The first two integers give the mtime, * encoded as seconds and microseconds (respectively) since the Unix * epoch; the next two give the atime, encoded similarly. So * "T1540373455 0 1540373457 0\n" is an example of a valid T command. * * Acknowledgments * --------------- * * The sending side waits for an ack from the receiving side before * sending each command; before beginning to send the file data * following a C command; and before sending the final EOF. * * (In particular, the receiving side is expected to send an initial * ack before _anything_ is sent.) * * Normally an ack consists of a single zero byte. It's also allowable * to send a byte with value 1 or 2 followed by a \n-terminated error * message (where 1 means a non-fatal error and 2 means a fatal one). * I have to suppose that sending an error message from client to * server is of limited use, but apparently it's allowed. * * Initiation * ---------- * * The protocol is begun by the client sending a command string to the * server via the SSH-2 "exec" request (or the analogous * SSH1_CMSG_EXEC_CMD), which indicates that this is an scp session * rather than any other thing; specifies the direction of transfer; * says what file(s) are to be sent by the server, or where the server * should put files that the client is about to send; and a couple of * other options. * * The command string takes the following form: * * Start with prefix "scp ", indicating that this is an SCP command at * all. Otherwise it's a request to run some completely different * command in the SSH session. * * Next the command can contain zero or more of the following options, * each followed by a space: * * "-v" turns on verbose server diagnostics. Of course a server is not * required to actually produce any, but this is an invitation for it * to send any it might have available. Diagnostics are free-form, and * sent as SSH standard-error extended data, so that they are separate * from the actual data stream as described above. * * (Servers can send standard-error output anyway if they like, and in * case of an actual error, they probably will with or without -v.) * * "-r" indicates recursive file transfer, i.e. potentially including * subdirectories. For a download, this indicates that the client is * willing to receive subdirectories (a D/E command pair bracketing * further files and subdirs); without it, the server should only send * C commands for individual files, followed by EOF. * * This flag must also be specified for a recursive upload, because I * believe at least one server will reject D/E pairs sent by the * client if the command didn't have -r in it. (Probably a consequence * of sharing code between download and upload.) * * "-p" means preserve file times. In a download, this requests the * server to send a T command before each C or D. I don't know whether * any server will insist on having seen this option from the client * before accepting T commands in an upload, but it is probably * sensible to send it anyway. * * "-d", in an upload, means that the destination pathname (see below) * is expected to be a directory, and that uploaded files (and * subdirs) should be put inside it. Without -d, the semantics are * that _if_ the destination exists and is a directory, then files * will be put in it, whereas if it is not, then just a single file * (or subdir) upload is expected, which will be placed at that exact * pathname. * * In a download, I observe that clients tend to send -d if they are * requesting multiple files or a wildcard, but as far as I know, * servers ignore it. * * After all those optional options, there is a single mandatory * option indicating the direction of transfer, which is either "-f" * or "-t". "-f" indicates a download; "-t" indicates an upload. * * After that mandatory option, there is a single space, followed by * the name(s) of files to transfer. * * This file name field is transmitted with NO QUOTING, in spite of * the fact that a server will typically interpret it as a shell * command. You'd think this couldn't possibly work, in the face of * almost any filename with an interesting character in it - and you'd * be right. Or rather (you might argue), it works 'as designed', but * it's designed in a weird way, in that it's the user's * responsibility to apply quoting on the client command line to get * the filename through the shell that will decode things on the * server side. * * But one effect of this is that if you issue a download command * including a wildcard, say "scp -f somedir/foo*.txt", then the shell * will expand the wildcard, and actually run the server-side scp * program with multiple arguments, say "somedir/foo.txt * somedir/quux.txt", leading to the download sending multiple C * commands. This clearly _is_ intended: it's how a command such as * 'scp server:somedir/foo*.txt destdir' can work at all. * * (You would think, given that, that it might also be legal to send * multiple space-separated filenames in order to trigger a download * of exactly those files. Given how scp is invoked in practice on a * typical server, this would surely actually work, but my observation * is that scp clients don't in fact try this - if you run OpenSSH's * scp by saying 'scp server:foo server:bar destdir' then it will make * two separate connections to the server for the two files, rather * than sending a single space-separated remote command. PSCP won't * even do that, and will make you do it in two separate runs.) * * So, some examples: * * - "scp -f filename.txt" * * Server should send a single C command (plus data) for that file. * Client ought to ignore the filename in the C command, in favour * of saving the file under the name implied by the user's command * line. * * - "scp -f file*.txt" * * Server sends zero or more C commands, then EOF. Client will have * been given a target directory to put them all in, and will name * each one according to the name in the C command. * * (You'd like the client to validate the filenames against the * wildcard it sent, to ensure a malicious server didn't try to * overwrite some path like ".bashrc" when you thought you were * downloading only normal text files. But wildcard semantics are * chosen by the server, so this is essentially hopeless to do * rigorously.) * * - "scp -f -r somedir" * * Assuming somedir is actually a directory, server sends a D/E * pair, in between which are the contents of the directory * (perhaps including further nested D/E pairs). Client probably * ignores the name field of the outermost D * * - "scp -f -r some*wild*card*" * * Server sends multiple C or D-stuff-E, one for each top-level * thing matching the wildcard, whether it's a file or a directory. * * - "scp -t -d some_dir" * * Client sends stuff, and server deposits each file at * some_dir/. * * - "scp -t some_path_name" * * Client sends one C command, and server deposits it at * some_path_name itself, or in some_path_name/, depending whether some_path_name was already a * directory or not. */ /* * Here's a useful debugging aid: run over a binary file containing * the complete contents of the sender's data stream (e.g. extracted * by contrib/logparse.pl -d), it removes the file contents, leaving * only the list of commands, so you can see what the server sent. * * perl -pe 'read ARGV,$x,1+$1 if/^C\S+ (\d+)/' */ /* ---------------------------------------------------------------------- * Shared system for receiving replies from the SftpServer, and * putting them into a set of ordinary variables rather than * marshalling them into actual SFTP reply packets that we'd only have * to unmarshal again. */ typedef struct ScpReplyReceiver ScpReplyReceiver; struct ScpReplyReceiver { bool err; unsigned code; char *errmsg; struct fxp_attrs attrs; ptrlen name, handle, data; SftpReplyBuilder srb; }; static void scp_reply_ok(SftpReplyBuilder *srb) { ScpReplyReceiver *reply = container_of(srb, ScpReplyReceiver, srb); reply->err = false; } static void scp_reply_error( SftpReplyBuilder *srb, unsigned code, const char *msg) { ScpReplyReceiver *reply = container_of(srb, ScpReplyReceiver, srb); reply->err = true; reply->code = code; sfree(reply->errmsg); reply->errmsg = dupstr(msg); } static void scp_reply_name_count(SftpReplyBuilder *srb, unsigned count) { ScpReplyReceiver *reply = container_of(srb, ScpReplyReceiver, srb); reply->err = false; } static void scp_reply_full_name( SftpReplyBuilder *srb, ptrlen name, ptrlen longname, struct fxp_attrs attrs) { ScpReplyReceiver *reply = container_of(srb, ScpReplyReceiver, srb); char *p; reply->err = false; sfree((void *)reply->name.ptr); reply->name.ptr = p = mkstr(name); reply->name.len = name.len; reply->attrs = attrs; } static void scp_reply_simple_name(SftpReplyBuilder *srb, ptrlen name) { ScpReplyReceiver *reply = container_of(srb, ScpReplyReceiver, srb); reply->err = false; } static void scp_reply_handle(SftpReplyBuilder *srb, ptrlen handle) { ScpReplyReceiver *reply = container_of(srb, ScpReplyReceiver, srb); char *p; reply->err = false; sfree((void *)reply->handle.ptr); reply->handle.ptr = p = mkstr(handle); reply->handle.len = handle.len; } static void scp_reply_data(SftpReplyBuilder *srb, ptrlen data) { ScpReplyReceiver *reply = container_of(srb, ScpReplyReceiver, srb); char *p; reply->err = false; sfree((void *)reply->data.ptr); reply->data.ptr = p = mkstr(data); reply->data.len = data.len; } static void scp_reply_attrs( SftpReplyBuilder *srb, struct fxp_attrs attrs) { ScpReplyReceiver *reply = container_of(srb, ScpReplyReceiver, srb); reply->err = false; reply->attrs = attrs; } static const SftpReplyBuilderVtable ScpReplyReceiver_vt = { .reply_ok = scp_reply_ok, .reply_error = scp_reply_error, .reply_simple_name = scp_reply_simple_name, .reply_name_count = scp_reply_name_count, .reply_full_name = scp_reply_full_name, .reply_handle = scp_reply_handle, .reply_data = scp_reply_data, .reply_attrs = scp_reply_attrs, }; static void scp_reply_setup(ScpReplyReceiver *reply) { memset(reply, 0, sizeof(*reply)); reply->srb.vt = &ScpReplyReceiver_vt; } static void scp_reply_cleanup(ScpReplyReceiver *reply) { sfree(reply->errmsg); sfree((void *)reply->name.ptr); sfree((void *)reply->handle.ptr); sfree((void *)reply->data.ptr); } /* ---------------------------------------------------------------------- * Source end of the SCP protocol. */ #define SCP_MAX_BACKLOG 65536 typedef struct ScpSource ScpSource; typedef struct ScpSourceStackEntry ScpSourceStackEntry; struct ScpSource { SftpServer *sf; int acks; bool expect_newline, eof, throttled, finished; SshChannel *sc; ScpSourceStackEntry *head; bool recursive; bool send_file_times; strbuf *pending_commands[3]; int n_pending_commands; uint64_t file_offset, file_size; ScpReplyReceiver reply; ScpServer scpserver; }; typedef enum ScpSourceNodeType ScpSourceNodeType; enum ScpSourceNodeType { SCP_ROOTPATH, SCP_NAME, SCP_READDIR, SCP_READFILE }; struct ScpSourceStackEntry { ScpSourceStackEntry *next; ScpSourceNodeType type; ptrlen pathname, handle; const char *wildcard; struct fxp_attrs attrs; }; static void scp_source_push(ScpSource *scp, ScpSourceNodeType type, ptrlen pathname, ptrlen handle, const struct fxp_attrs *attrs, const char *wc) { size_t wc_len = wc ? strlen(wc)+1 : 0; ScpSourceStackEntry *node = snew_plus( ScpSourceStackEntry, pathname.len + handle.len + wc_len); char *namebuf = snew_plus_get_aux(node); memcpy(namebuf, pathname.ptr, pathname.len); node->pathname = make_ptrlen(namebuf, pathname.len); memcpy(namebuf + pathname.len, handle.ptr, handle.len); node->handle = make_ptrlen(namebuf + pathname.len, handle.len); if (wc) { strcpy(namebuf + pathname.len + handle.len, wc); node->wildcard = namebuf + pathname.len + handle.len; } else { node->wildcard = NULL; } node->attrs = attrs ? *attrs : no_attrs; node->type = type; node->next = scp->head; scp->head = node; } static char *scp_source_err_base(ScpSource *scp, const char *fmt, va_list ap) { char *msg = dupvprintf(fmt, ap); sshfwd_write_ext(scp->sc, true, msg, strlen(msg)); sshfwd_write_ext(scp->sc, true, "\012", 1); return msg; } static PRINTF_LIKE(2, 3) void scp_source_err( ScpSource *scp, const char *fmt, ...) { va_list ap; va_start(ap, fmt); sfree(scp_source_err_base(scp, fmt, ap)); va_end(ap); } static PRINTF_LIKE(2, 3) void scp_source_abort( ScpSource *scp, const char *fmt, ...) { va_list ap; char *msg; va_start(ap, fmt); msg = scp_source_err_base(scp, fmt, ap); va_end(ap); sshfwd_send_exit_status(scp->sc, 1); sshfwd_write_eof(scp->sc); sshfwd_initiate_close(scp->sc, msg); scp->finished = true; } static void scp_source_push_name( ScpSource *scp, ptrlen pathname, struct fxp_attrs attrs, const char *wc) { if (!(attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS)) { scp_source_err(scp, "unable to read file permissions for %.*s", PTRLEN_PRINTF(pathname)); return; } if (attrs.permissions & PERMS_DIRECTORY) { if (!scp->recursive && !wc) { scp_source_err(scp, "%.*s: is a directory", PTRLEN_PRINTF(pathname)); return; } } else { if (!(attrs.flags & SSH_FILEXFER_ATTR_SIZE)) { scp_source_err(scp, "unable to read file size for %.*s", PTRLEN_PRINTF(pathname)); return; } } scp_source_push(scp, SCP_NAME, pathname, PTRLEN_LITERAL(""), &attrs, wc); } static void scp_source_free(ScpServer *s); static size_t scp_source_send(ScpServer *s, const void *data, size_t length); static void scp_source_eof(ScpServer *s); static void scp_source_throttle(ScpServer *s, bool throttled); static const ScpServerVtable ScpSource_ScpServer_vt = { .free = scp_source_free, .send = scp_source_send, .throttle = scp_source_throttle, .eof = scp_source_eof, }; static ScpSource *scp_source_new( SshChannel *sc, const SftpServerVtable *sftpserver_vt, ptrlen pathname) { ScpSource *scp = snew(ScpSource); memset(scp, 0, sizeof(*scp)); scp->scpserver.vt = &ScpSource_ScpServer_vt; scp_reply_setup(&scp->reply); scp->sc = sc; scp->sf = sftpsrv_new(sftpserver_vt); scp->n_pending_commands = 0; scp_source_push(scp, SCP_ROOTPATH, pathname, PTRLEN_LITERAL(""), NULL, NULL); return scp; } static void scp_source_free(ScpServer *s) { ScpSource *scp = container_of(s, ScpSource, scpserver); scp_reply_cleanup(&scp->reply); while (scp->n_pending_commands > 0) strbuf_free(scp->pending_commands[--scp->n_pending_commands]); while (scp->head) { ScpSourceStackEntry *node = scp->head; scp->head = node->next; sfree(node); } delete_callbacks_for_context(scp); sfree(scp); } static void scp_source_send_E(ScpSource *scp) { strbuf *cmd; assert(scp->n_pending_commands == 0); scp->pending_commands[scp->n_pending_commands++] = cmd = strbuf_new(); strbuf_catf(cmd, "E\012"); } static void scp_source_send_CD( ScpSource *scp, char cmdchar, struct fxp_attrs attrs, uint64_t size, ptrlen name) { strbuf *cmd; assert(scp->n_pending_commands == 0); if (scp->send_file_times && (attrs.flags & SSH_FILEXFER_ATTR_ACMODTIME)) { scp->pending_commands[scp->n_pending_commands++] = cmd = strbuf_new(); /* Our SFTP-based filesystem API doesn't support microsecond times */ strbuf_catf(cmd, "T%lu 0 %lu 0\012", attrs.mtime, attrs.atime); } const char *slash; while ((slash = memchr(name.ptr, '/', name.len)) != NULL) name = make_ptrlen( slash+1, name.len - (slash+1 - (const char *)name.ptr)); scp->pending_commands[scp->n_pending_commands++] = cmd = strbuf_new(); strbuf_catf(cmd, "%c%04o %"PRIu64" %.*s\012", cmdchar, (unsigned)(attrs.permissions & 07777), size, PTRLEN_PRINTF(name)); if (cmdchar == 'C') { /* We'll also wait for an ack before sending the file data, * which we record by saving a zero-length 'command' to be * sent after the C. */ scp->pending_commands[scp->n_pending_commands++] = cmd = strbuf_new(); } } static void scp_source_process_stack(ScpSource *scp); static void scp_source_process_stack_cb(void *vscp) { ScpSource *scp = (ScpSource *)vscp; if (scp->finished) return; /* this callback is out of date */ scp_source_process_stack(scp); } static void scp_requeue(ScpSource *scp) { queue_toplevel_callback(scp_source_process_stack_cb, scp); } static void scp_source_process_stack(ScpSource *scp) { if (scp->throttled) return; while (scp->n_pending_commands > 0) { /* Expect an ack, and consume it */ if (scp->eof) { scp_source_abort( scp, "scp: received client EOF, abandoning transfer"); return; } if (scp->acks == 0) return; scp->acks--; /* * Now send the actual command (unless it was the phony * zero-length one that indicates our need for an ack before * beginning to send file data). */ if (scp->pending_commands[0]->len) sshfwd_write(scp->sc, scp->pending_commands[0]->s, scp->pending_commands[0]->len); strbuf_free(scp->pending_commands[0]); scp->n_pending_commands--; if (scp->n_pending_commands > 0) { /* * We still have at least one pending command to send, so * move up the queue. * * (We do that with a bodgy memmove, because there are at * most a bounded number of commands ever pending at once, * so no need to worry about quadratic time.) */ memmove(scp->pending_commands, scp->pending_commands+1, scp->n_pending_commands * sizeof(*scp->pending_commands)); } } /* * Mostly, we start by waiting for an ack byte from the receiver. */ if (scp->head && scp->head->type == SCP_READFILE && scp->file_offset) { /* * Exception: if we're already in the middle of transferring a * file, we'll be called back here because the channel backlog * has cleared; we don't need to wait for an ack. */ } else if (scp->head && scp->head->type == SCP_ROOTPATH) { /* * Another exception: the initial action node that makes us * stat the root path. We'll translate it into an SCP_NAME, * and _that_ will require an ack. */ ScpSourceStackEntry *node = scp->head; scp->head = node->next; /* * Start by checking if there's a wildcard involved in the * root path. */ char *rootpath_str = mkstr(node->pathname); char *rootpath_unesc = snewn(1+node->pathname.len, char); ptrlen pathname; const char *wildcard; if (wc_unescape(rootpath_unesc, rootpath_str)) { /* * We successfully removed instances of the escape * character used in our wildcard syntax, without * encountering any actual wildcard chars - i.e. this is * not a wildcard, just a single file. The simple case. */ pathname = ptrlen_from_asciz(rootpath_str); wildcard = NULL; } else { /* * This is a wildcard. Separate it into a directory name * (which we enforce mustn't contain wc characters, for * simplicity) and a wildcard to match leaf names. */ char *last_slash = strrchr(rootpath_str, '/'); if (last_slash) { wildcard = last_slash + 1; *last_slash = '\0'; if (!wc_unescape(rootpath_unesc, rootpath_str)) { scp_source_abort(scp, "scp: wildcards in path components " "before the file name not supported"); sfree(rootpath_str); sfree(rootpath_unesc); return; } pathname = ptrlen_from_asciz(rootpath_unesc); } else { pathname = PTRLEN_LITERAL("."); wildcard = rootpath_str; } } /* * Now we know what directory we're scanning, and what * wildcard (if any) we're using to match the filenames we get * back. */ sftpsrv_stat(scp->sf, &scp->reply.srb, pathname, true); if (scp->reply.err) { scp_source_abort( scp, "%.*s: unable to access: %s", PTRLEN_PRINTF(pathname), scp->reply.errmsg); sfree(rootpath_str); sfree(rootpath_unesc); sfree(node); return; } scp_source_push_name(scp, pathname, scp->reply.attrs, wildcard); sfree(rootpath_str); sfree(rootpath_unesc); sfree(node); scp_requeue(scp); return; } else { } if (scp->head && scp->head->type == SCP_READFILE) { /* * Transfer file data if our backlog hasn't filled up. */ int backlog; uint64_t limit = scp->file_size - scp->file_offset; if (limit > 4096) limit = 4096; if (limit > 0) { sftpsrv_read(scp->sf, &scp->reply.srb, scp->head->handle, scp->file_offset, limit); if (scp->reply.err) { scp_source_abort( scp, "%.*s: unable to read: %s", PTRLEN_PRINTF(scp->head->pathname), scp->reply.errmsg); return; } backlog = sshfwd_write( scp->sc, scp->reply.data.ptr, scp->reply.data.len); scp->file_offset += scp->reply.data.len; if (backlog < SCP_MAX_BACKLOG) scp_requeue(scp); return; } /* * If we're done, send a terminating zero byte, close our file * handle, and pop the stack. */ sshfwd_write(scp->sc, "\0", 1); sftpsrv_close(scp->sf, &scp->reply.srb, scp->head->handle); ScpSourceStackEntry *node = scp->head; scp->head = node->next; sfree(node); scp_requeue(scp); return; } /* * If our queue is actually empty, send outgoing EOF. */ if (!scp->head) { sshfwd_send_exit_status(scp->sc, 0); sshfwd_write_eof(scp->sc); sshfwd_initiate_close(scp->sc, NULL); scp->finished = true; return; } /* * Otherwise, handle a command. */ ScpSourceStackEntry *node = scp->head; scp->head = node->next; if (node->type == SCP_READDIR) { sftpsrv_readdir(scp->sf, &scp->reply.srb, node->handle, 1, true); if (scp->reply.err) { if (scp->reply.code != SSH_FX_EOF) scp_source_err(scp, "%.*s: unable to list directory: %s", PTRLEN_PRINTF(node->pathname), scp->reply.errmsg); sftpsrv_close(scp->sf, &scp->reply.srb, node->handle); if (!node->wildcard) { /* * Send 'pop stack' or 'end of directory' command, * unless this was the topmost READDIR in a * wildcard-based retrieval (in which case we didn't * send a D command to start, so an E now would have * no stack entry to pop). */ scp_source_send_E(scp); } } else if (ptrlen_eq_string(scp->reply.name, ".") || ptrlen_eq_string(scp->reply.name, "..") || (node->wildcard && !wc_match_pl(node->wildcard, scp->reply.name))) { /* Skip special directory names . and .., and anything * that doesn't match our wildcard (if we have one). */ scp->head = node; /* put back the unfinished READDIR */ node = NULL; /* and prevent it being freed */ } else { ptrlen subpath; subpath.len = node->pathname.len + 1 + scp->reply.name.len; char *subpath_space = snewn(subpath.len, char); subpath.ptr = subpath_space; memcpy(subpath_space, node->pathname.ptr, node->pathname.len); subpath_space[node->pathname.len] = '/'; memcpy(subpath_space + node->pathname.len + 1, scp->reply.name.ptr, scp->reply.name.len); scp->head = node; /* put back the unfinished READDIR */ node = NULL; /* and prevent it being freed */ scp_source_push_name(scp, subpath, scp->reply.attrs, NULL); sfree(subpath_space); } } else if (node->attrs.permissions & PERMS_DIRECTORY) { assert(scp->recursive || node->wildcard); if (!node->wildcard) scp_source_send_CD(scp, 'D', node->attrs, 0, node->pathname); sftpsrv_opendir(scp->sf, &scp->reply.srb, node->pathname); if (scp->reply.err) { scp_source_err( scp, "%.*s: unable to access: %s", PTRLEN_PRINTF(node->pathname), scp->reply.errmsg); if (!node->wildcard) { /* Send 'pop stack' or 'end of directory' command. */ scp_source_send_E(scp); } } else { scp_source_push( scp, SCP_READDIR, node->pathname, scp->reply.handle, NULL, node->wildcard); } } else { sftpsrv_open(scp->sf, &scp->reply.srb, node->pathname, SSH_FXF_READ, no_attrs); if (scp->reply.err) { scp_source_err( scp, "%.*s: unable to open: %s", PTRLEN_PRINTF(node->pathname), scp->reply.errmsg); scp_requeue(scp); return; } sftpsrv_fstat(scp->sf, &scp->reply.srb, scp->reply.handle); if (scp->reply.err) { scp_source_err( scp, "%.*s: unable to stat: %s", PTRLEN_PRINTF(node->pathname), scp->reply.errmsg); sftpsrv_close(scp->sf, &scp->reply.srb, scp->reply.handle); scp_requeue(scp); return; } scp->file_offset = 0; scp->file_size = scp->reply.attrs.size; scp_source_send_CD(scp, 'C', node->attrs, scp->file_size, node->pathname); scp_source_push( scp, SCP_READFILE, node->pathname, scp->reply.handle, NULL, NULL); } sfree(node); scp_requeue(scp); } static size_t scp_source_send(ScpServer *s, const void *vdata, size_t length) { ScpSource *scp = container_of(s, ScpSource, scpserver); const char *data = (const char *)vdata; size_t i; if (scp->finished) return 0; for (i = 0; i < length; i++) { if (scp->expect_newline) { if (data[i] == '\012') { /* End of an error message following a 1 byte */ scp->expect_newline = false; scp->acks++; } } else { switch (data[i]) { case 0: /* ordinary ack */ scp->acks++; break; case 1: /* non-fatal error; consume it */ scp->expect_newline = true; break; case 2: scp_source_abort( scp, "terminating on fatal error from client"); return 0; default: scp_source_abort( scp, "unrecognised response code from client"); return 0; } } } scp_source_process_stack(scp); return 0; } static void scp_source_throttle(ScpServer *s, bool throttled) { ScpSource *scp = container_of(s, ScpSource, scpserver); if (scp->finished) return; scp->throttled = throttled; if (!throttled) scp_source_process_stack(scp); } static void scp_source_eof(ScpServer *s) { ScpSource *scp = container_of(s, ScpSource, scpserver); if (scp->finished) return; scp->eof = true; scp_source_process_stack(scp); } /* ---------------------------------------------------------------------- * Sink end of the SCP protocol. */ typedef struct ScpSink ScpSink; typedef struct ScpSinkStackEntry ScpSinkStackEntry; struct ScpSink { SftpServer *sf; SshChannel *sc; ScpSinkStackEntry *head; uint64_t file_offset, file_size; unsigned long atime, mtime; bool got_file_times; bufchain data; bool input_eof; strbuf *command; char command_chr; strbuf *filename_sb; ptrlen filename; struct fxp_attrs attrs; char *errmsg; int crState; ScpReplyReceiver reply; ScpServer scpserver; }; struct ScpSinkStackEntry { ScpSinkStackEntry *next; ptrlen destpath; /* * If isdir is true, then destpath identifies a directory that the * files we receive should be created inside. If it's false, then * it identifies the exact pathname the next file we receive * should be created _as_ - regardless of the filename in the 'C' * command. */ bool isdir; }; static void scp_sink_push(ScpSink *scp, ptrlen pathname, bool isdir) { ScpSinkStackEntry *node = snew_plus(ScpSinkStackEntry, pathname.len); char *p = snew_plus_get_aux(node); node->destpath.ptr = p; node->destpath.len = pathname.len; memcpy(p, pathname.ptr, pathname.len); node->isdir = isdir; node->next = scp->head; scp->head = node; } static void scp_sink_pop(ScpSink *scp) { ScpSinkStackEntry *node = scp->head; scp->head = node->next; sfree(node); } static void scp_sink_free(ScpServer *s); static size_t scp_sink_send(ScpServer *s, const void *data, size_t length); static void scp_sink_eof(ScpServer *s); static void scp_sink_throttle(ScpServer *s, bool throttled) {} static const ScpServerVtable ScpSink_ScpServer_vt = { .free = scp_sink_free, .send = scp_sink_send, .throttle = scp_sink_throttle, .eof = scp_sink_eof, }; static void scp_sink_coroutine(ScpSink *scp); static void scp_sink_start_callback(void *vscp) { scp_sink_coroutine((ScpSink *)vscp); } static ScpSink *scp_sink_new( SshChannel *sc, const SftpServerVtable *sftpserver_vt, ptrlen pathname, bool pathname_is_definitely_dir) { ScpSink *scp = snew(ScpSink); memset(scp, 0, sizeof(*scp)); scp->scpserver.vt = &ScpSink_ScpServer_vt; scp_reply_setup(&scp->reply); scp->sc = sc; scp->sf = sftpsrv_new(sftpserver_vt); bufchain_init(&scp->data); scp->command = strbuf_new(); scp->filename_sb = strbuf_new(); if (!pathname_is_definitely_dir) { /* * If our root pathname is not already expected to be a * directory because of the -d option in the command line, * test it ourself to see whether it is or not. */ sftpsrv_stat(scp->sf, &scp->reply.srb, pathname, true); if (!scp->reply.err && (scp->reply.attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) && (scp->reply.attrs.permissions & PERMS_DIRECTORY)) pathname_is_definitely_dir = true; } scp_sink_push(scp, pathname, pathname_is_definitely_dir); queue_toplevel_callback(scp_sink_start_callback, scp); return scp; } static void scp_sink_free(ScpServer *s) { ScpSink *scp = container_of(s, ScpSink, scpserver); scp_reply_cleanup(&scp->reply); bufchain_clear(&scp->data); strbuf_free(scp->command); strbuf_free(scp->filename_sb); while (scp->head) scp_sink_pop(scp); sfree(scp->errmsg); delete_callbacks_for_context(scp); sfree(scp); } static void scp_sink_coroutine(ScpSink *scp) { crBegin(scp->crState); while (1) { /* * Send an ack, and read a command. */ sshfwd_write(scp->sc, "\0", 1); strbuf_clear(scp->command); while (1) { crMaybeWaitUntilV(scp->input_eof || bufchain_size(&scp->data) > 0); if (scp->input_eof) goto done; ptrlen data = bufchain_prefix(&scp->data); const char *cdata = data.ptr; const char *newline = memchr(cdata, '\012', data.len); if (newline) data.len = (int)(newline+1 - cdata); put_data(scp->command, cdata, data.len); bufchain_consume(&scp->data, data.len); if (newline) break; } /* * Parse the command. */ strbuf_chomp(scp->command, '\n'); scp->command_chr = scp->command->len > 0 ? scp->command->s[0] : '\0'; if (scp->command_chr == 'T') { unsigned long dummy1, dummy2; if (sscanf(scp->command->s, "T%lu %lu %lu %lu", &scp->mtime, &dummy1, &scp->atime, &dummy2) != 4) goto parse_error; scp->got_file_times = true; } else if (scp->command_chr == 'C' || scp->command_chr == 'D') { /* * Common handling of the start of this case, because the * messages are parsed similarly. We diverge later. */ const char *q, *p = scp->command->s + 1; /* skip the 'C' */ scp->attrs.flags = SSH_FILEXFER_ATTR_PERMISSIONS; scp->attrs.permissions = 0; while (*p >= '0' && *p <= '7') { scp->attrs.permissions = scp->attrs.permissions * 8 + (*p - '0'); p++; } if (*p != ' ') goto parse_error; p++; q = p; while (*p >= '0' && *p <= '9') p++; if (*p != ' ') goto parse_error; p++; scp->file_size = strtoull(q, NULL, 10); ptrlen leafname = make_ptrlen( p, scp->command->len - (p - scp->command->s)); strbuf_clear(scp->filename_sb); put_datapl(scp->filename_sb, scp->head->destpath); if (scp->head->isdir) { if (scp->filename_sb->len > 0 && scp->filename_sb->s[scp->filename_sb->len-1] != '/') put_byte(scp->filename_sb, '/'); put_datapl(scp->filename_sb, leafname); } scp->filename = ptrlen_from_strbuf(scp->filename_sb); if (scp->got_file_times) { scp->attrs.mtime = scp->mtime; scp->attrs.atime = scp->atime; scp->attrs.flags |= SSH_FILEXFER_ATTR_ACMODTIME; } scp->got_file_times = false; if (scp->command_chr == 'D') { sftpsrv_mkdir(scp->sf, &scp->reply.srb, scp->filename, scp->attrs); if (scp->reply.err) { scp->errmsg = dupprintf( "'%.*s': unable to create directory: %s", PTRLEN_PRINTF(scp->filename), scp->reply.errmsg); goto done; } scp_sink_push(scp, scp->filename, true); } else { sftpsrv_open(scp->sf, &scp->reply.srb, scp->filename, SSH_FXF_WRITE | SSH_FXF_CREAT | SSH_FXF_TRUNC, scp->attrs); if (scp->reply.err) { scp->errmsg = dupprintf( "'%.*s': unable to open file: %s", PTRLEN_PRINTF(scp->filename), scp->reply.errmsg); goto done; } /* * Now send an ack, and read the file data. */ sshfwd_write(scp->sc, "\0", 1); scp->file_offset = 0; while (scp->file_offset < scp->file_size) { ptrlen data; uint64_t this_len, remaining; crMaybeWaitUntilV( scp->input_eof || bufchain_size(&scp->data) > 0); if (scp->input_eof) { sftpsrv_close(scp->sf, &scp->reply.srb, scp->reply.handle); goto done; } data = bufchain_prefix(&scp->data); this_len = data.len; remaining = scp->file_size - scp->file_offset; if (this_len > remaining) this_len = remaining; sftpsrv_write(scp->sf, &scp->reply.srb, scp->reply.handle, scp->file_offset, make_ptrlen(data.ptr, this_len)); if (scp->reply.err) { scp->errmsg = dupprintf( "'%.*s': unable to write to file: %s", PTRLEN_PRINTF(scp->filename), scp->reply.errmsg); goto done; } bufchain_consume(&scp->data, this_len); scp->file_offset += this_len; } /* * Wait for the trailing NUL byte. */ crMaybeWaitUntilV( scp->input_eof || bufchain_size(&scp->data) > 0); if (scp->input_eof) { sftpsrv_close(scp->sf, &scp->reply.srb, scp->reply.handle); goto done; } bufchain_consume(&scp->data, 1); } } else if (scp->command_chr == 'E') { if (!scp->head) { scp->errmsg = dupstr("received E command without matching D"); goto done; } scp_sink_pop(scp); scp->got_file_times = false; } else { ptrlen cmd_pl; /* * Also come here if any of the above cases run into * parsing difficulties. */ parse_error: cmd_pl = ptrlen_from_strbuf(scp->command); scp->errmsg = dupprintf("unrecognised scp command '%.*s'", PTRLEN_PRINTF(cmd_pl)); goto done; } } done: if (scp->errmsg) { sshfwd_write_ext(scp->sc, true, scp->errmsg, strlen(scp->errmsg)); sshfwd_write_ext(scp->sc, true, "\012", 1); sshfwd_send_exit_status(scp->sc, 1); } else { sshfwd_send_exit_status(scp->sc, 0); } sshfwd_write_eof(scp->sc); sshfwd_initiate_close(scp->sc, scp->errmsg); while (1) crReturnV; crFinishV; } static size_t scp_sink_send(ScpServer *s, const void *data, size_t length) { ScpSink *scp = container_of(s, ScpSink, scpserver); if (!scp->input_eof) { bufchain_add(&scp->data, data, length); scp_sink_coroutine(scp); } return 0; } static void scp_sink_eof(ScpServer *s) { ScpSink *scp = container_of(s, ScpSink, scpserver); scp->input_eof = true; scp_sink_coroutine(scp); } /* ---------------------------------------------------------------------- * Top-level error handler, instantiated in the case where the user * sent a command starting with "scp " that we couldn't make sense of. */ typedef struct ScpError ScpError; struct ScpError { SshChannel *sc; char *message; ScpServer scpserver; }; static void scp_error_free(ScpServer *s); static size_t scp_error_send(ScpServer *s, const void *data, size_t length) { return 0; } static void scp_error_eof(ScpServer *s) {} static void scp_error_throttle(ScpServer *s, bool throttled) {} static const ScpServerVtable ScpError_ScpServer_vt = { .free = scp_error_free, .send = scp_error_send, .throttle = scp_error_throttle, .eof = scp_error_eof, }; static void scp_error_send_message_cb(void *vscp) { ScpError *scp = (ScpError *)vscp; sshfwd_write_ext(scp->sc, true, scp->message, strlen(scp->message)); sshfwd_write_ext(scp->sc, true, "\n", 1); sshfwd_send_exit_status(scp->sc, 1); sshfwd_write_eof(scp->sc); sshfwd_initiate_close(scp->sc, scp->message); } static PRINTF_LIKE(2, 3) ScpError *scp_error_new( SshChannel *sc, const char *fmt, ...) { va_list ap; ScpError *scp = snew(ScpError); memset(scp, 0, sizeof(*scp)); scp->scpserver.vt = &ScpError_ScpServer_vt; scp->sc = sc; va_start(ap, fmt); scp->message = dupvprintf(fmt, ap); va_end(ap); queue_toplevel_callback(scp_error_send_message_cb, scp); return scp; } static void scp_error_free(ScpServer *s) { ScpError *scp = container_of(s, ScpError, scpserver); sfree(scp->message); delete_callbacks_for_context(scp); sfree(scp); } /* ---------------------------------------------------------------------- * Top-level entry point, which parses a command sent from the SSH * client, and if it recognises it as an scp command, instantiates an * appropriate ScpServer implementation and returns it. */ ScpServer *scp_recognise_exec( SshChannel *sc, const SftpServerVtable *sftpserver_vt, ptrlen command) { bool recursive = false, preserve = false; bool targetshouldbedirectory = false; ptrlen command_orig = command; if (!ptrlen_startswith(command, PTRLEN_LITERAL("scp "), &command)) return NULL; while (1) { if (ptrlen_startswith(command, PTRLEN_LITERAL("-v "), &command)) { /* Enable verbose mode in the server, which we ignore */ continue; } if (ptrlen_startswith(command, PTRLEN_LITERAL("-r "), &command)) { recursive = true; continue; } if (ptrlen_startswith(command, PTRLEN_LITERAL("-p "), &command)) { preserve = true; continue; } if (ptrlen_startswith(command, PTRLEN_LITERAL("-d "), &command)) { targetshouldbedirectory = true; continue; } break; } if (ptrlen_startswith(command, PTRLEN_LITERAL("-t "), &command)) { ScpSink *scp = scp_sink_new(sc, sftpserver_vt, command, targetshouldbedirectory); return &scp->scpserver; } else if (ptrlen_startswith(command, PTRLEN_LITERAL("-f "), &command)) { ScpSource *scp = scp_source_new(sc, sftpserver_vt, command); scp->recursive = recursive; scp->send_file_times = preserve; return &scp->scpserver; } else { ScpError *scp = scp_error_new( sc, "Unable to parse scp command: '%.*s'", PTRLEN_PRINTF(command_orig)); return &scp->scpserver; } } putty-0.76/sesschan.c0000644000175000017500000005610014072266311011533 00000000000000/* * Implement the "session" channel type for the SSH server. */ #include #include #include #include #include "putty.h" #include "ssh.h" #include "sshchan.h" #include "sshserver.h" #include "sftp.h" struct agentfwd { ConnectionLayer *cl; Socket *socket; Plug plug; }; typedef struct sesschan { SshChannel *c; LogContext *parent_logctx, *child_logctx; Conf *conf; const SftpServerVtable *sftpserver_vt; LogPolicy logpolicy; Seat seat; bool want_pty; struct ssh_ttymodes ttymodes; int wc, hc, wp, hp; strbuf *termtype; bool ignoring_input; bool seen_eof, seen_exit; Plug xfwd_plug; int n_x11_sockets; Socket *x11_sockets[MAX_X11_SOCKETS]; agentfwd *agent; Backend *backend; bufchain subsys_input; SftpServer *sftpsrv; ScpServer *scpsrv; const SshServerConfig *ssc; Channel chan; } sesschan; static void sesschan_free(Channel *chan); static size_t sesschan_send( Channel *chan, bool is_stderr, const void *, size_t); static void sesschan_send_eof(Channel *chan); static char *sesschan_log_close_msg(Channel *chan); static bool sesschan_want_close(Channel *, bool, bool); static void sesschan_set_input_wanted(Channel *chan, bool wanted); static bool sesschan_run_shell(Channel *chan); static bool sesschan_run_command(Channel *chan, ptrlen command); static bool sesschan_run_subsystem(Channel *chan, ptrlen subsys); static bool sesschan_enable_x11_forwarding( Channel *chan, bool oneshot, ptrlen authproto, ptrlen authdata, unsigned screen_number); static bool sesschan_enable_agent_forwarding(Channel *chan); static bool sesschan_allocate_pty( Channel *chan, ptrlen termtype, unsigned width, unsigned height, unsigned pixwidth, unsigned pixheight, struct ssh_ttymodes modes); static bool sesschan_set_env(Channel *chan, ptrlen var, ptrlen value); static bool sesschan_send_break(Channel *chan, unsigned length); static bool sesschan_send_signal(Channel *chan, ptrlen signame); static bool sesschan_change_window_size( Channel *chan, unsigned width, unsigned height, unsigned pixwidth, unsigned pixheight); static const ChannelVtable sesschan_channelvt = { .free = sesschan_free, .open_confirmation = chan_remotely_opened_confirmation, .open_failed = chan_remotely_opened_failure, .send = sesschan_send, .send_eof = sesschan_send_eof, .set_input_wanted = sesschan_set_input_wanted, .log_close_msg = sesschan_log_close_msg, .want_close = sesschan_want_close, .rcvd_exit_status = chan_no_exit_status, .rcvd_exit_signal = chan_no_exit_signal, .rcvd_exit_signal_numeric = chan_no_exit_signal_numeric, .run_shell = sesschan_run_shell, .run_command = sesschan_run_command, .run_subsystem = sesschan_run_subsystem, .enable_x11_forwarding = sesschan_enable_x11_forwarding, .enable_agent_forwarding = sesschan_enable_agent_forwarding, .allocate_pty = sesschan_allocate_pty, .set_env = sesschan_set_env, .send_break = sesschan_send_break, .send_signal = sesschan_send_signal, .change_window_size = sesschan_change_window_size, .request_response = chan_no_request_response, }; static size_t sftp_chan_send( Channel *chan, bool is_stderr, const void *, size_t); static void sftp_chan_send_eof(Channel *chan); static char *sftp_log_close_msg(Channel *chan); static const ChannelVtable sftp_channelvt = { .free = sesschan_free, .open_confirmation = chan_remotely_opened_confirmation, .open_failed = chan_remotely_opened_failure, .send = sftp_chan_send, .send_eof = sftp_chan_send_eof, .set_input_wanted = sesschan_set_input_wanted, .log_close_msg = sftp_log_close_msg, .want_close = chan_default_want_close, .rcvd_exit_status = chan_no_exit_status, .rcvd_exit_signal = chan_no_exit_signal, .rcvd_exit_signal_numeric = chan_no_exit_signal_numeric, .run_shell = chan_no_run_shell, .run_command = chan_no_run_command, .run_subsystem = chan_no_run_subsystem, .enable_x11_forwarding = chan_no_enable_x11_forwarding, .enable_agent_forwarding = chan_no_enable_agent_forwarding, .allocate_pty = chan_no_allocate_pty, .set_env = chan_no_set_env, .send_break = chan_no_send_break, .send_signal = chan_no_send_signal, .change_window_size = chan_no_change_window_size, .request_response = chan_no_request_response, }; static size_t scp_chan_send( Channel *chan, bool is_stderr, const void *, size_t); static void scp_chan_send_eof(Channel *chan); static void scp_set_input_wanted(Channel *chan, bool wanted); static char *scp_log_close_msg(Channel *chan); static const ChannelVtable scp_channelvt = { .free = sesschan_free, .open_confirmation = chan_remotely_opened_confirmation, .open_failed = chan_remotely_opened_failure, .send = scp_chan_send, .send_eof = scp_chan_send_eof, .set_input_wanted = scp_set_input_wanted, .log_close_msg = scp_log_close_msg, .want_close = chan_default_want_close, .rcvd_exit_status = chan_no_exit_status, .rcvd_exit_signal = chan_no_exit_signal, .rcvd_exit_signal_numeric = chan_no_exit_signal_numeric, .run_shell = chan_no_run_shell, .run_command = chan_no_run_command, .run_subsystem = chan_no_run_subsystem, .enable_x11_forwarding = chan_no_enable_x11_forwarding, .enable_agent_forwarding = chan_no_enable_agent_forwarding, .allocate_pty = chan_no_allocate_pty, .set_env = chan_no_set_env, .send_break = chan_no_send_break, .send_signal = chan_no_send_signal, .change_window_size = chan_no_change_window_size, .request_response = chan_no_request_response, }; static void sesschan_eventlog(LogPolicy *lp, const char *event) {} static void sesschan_logging_error(LogPolicy *lp, const char *event) {} static int sesschan_askappend( LogPolicy *lp, Filename *filename, void (*callback)(void *ctx, int result), void *ctx) { return 2; } static const LogPolicyVtable sesschan_logpolicy_vt = { .eventlog = sesschan_eventlog, .askappend = sesschan_askappend, .logging_error = sesschan_logging_error, .verbose = null_lp_verbose_no, }; static size_t sesschan_seat_output( Seat *, bool is_stderr, const void *, size_t); static bool sesschan_seat_eof(Seat *); static void sesschan_notify_remote_exit(Seat *seat); static void sesschan_connection_fatal(Seat *seat, const char *message); static bool sesschan_get_window_pixel_size(Seat *seat, int *w, int *h); static const SeatVtable sesschan_seat_vt = { .output = sesschan_seat_output, .eof = sesschan_seat_eof, .get_userpass_input = nullseat_get_userpass_input, .notify_remote_exit = sesschan_notify_remote_exit, .connection_fatal = sesschan_connection_fatal, .update_specials_menu = nullseat_update_specials_menu, .get_ttymode = nullseat_get_ttymode, .set_busy_status = nullseat_set_busy_status, .verify_ssh_host_key = nullseat_verify_ssh_host_key, .confirm_weak_crypto_primitive = nullseat_confirm_weak_crypto_primitive, .confirm_weak_cached_hostkey = nullseat_confirm_weak_cached_hostkey, .is_utf8 = nullseat_is_never_utf8, .echoedit_update = nullseat_echoedit_update, .get_x_display = nullseat_get_x_display, .get_windowid = nullseat_get_windowid, .get_window_pixel_size = sesschan_get_window_pixel_size, .stripctrl_new = nullseat_stripctrl_new, .set_trust_status = nullseat_set_trust_status, .verbose = nullseat_verbose_no, .interactive = nullseat_interactive_no, .get_cursor_position = nullseat_get_cursor_position, }; Channel *sesschan_new(SshChannel *c, LogContext *logctx, const SftpServerVtable *sftpserver_vt, const SshServerConfig *ssc) { sesschan *sess = snew(sesschan); memset(sess, 0, sizeof(sesschan)); sess->c = c; sess->chan.vt = &sesschan_channelvt; sess->chan.initial_fixed_window_size = 0; sess->parent_logctx = logctx; sess->ssc = ssc; /* Start with a completely default Conf */ sess->conf = conf_new(); load_open_settings(NULL, sess->conf); /* Set close-on-exit = true to suppress uxpty.c's "[pterm: process * terminated with status x]" message */ conf_set_int(sess->conf, CONF_close_on_exit, FORCE_ON); sess->seat.vt = &sesschan_seat_vt; sess->logpolicy.vt = &sesschan_logpolicy_vt; sess->child_logctx = log_init(&sess->logpolicy, sess->conf); sess->sftpserver_vt = sftpserver_vt; bufchain_init(&sess->subsys_input); return &sess->chan; } static void sesschan_free(Channel *chan) { sesschan *sess = container_of(chan, sesschan, chan); int i; delete_callbacks_for_context(sess); conf_free(sess->conf); if (sess->backend) backend_free(sess->backend); bufchain_clear(&sess->subsys_input); if (sess->sftpsrv) sftpsrv_free(sess->sftpsrv); for (i = 0; i < sess->n_x11_sockets; i++) sk_close(sess->x11_sockets[i]); if (sess->agent) agentfwd_free(sess->agent); sfree(sess); } static size_t sesschan_send(Channel *chan, bool is_stderr, const void *data, size_t length) { sesschan *sess = container_of(chan, sesschan, chan); if (!sess->backend || sess->ignoring_input) return 0; return backend_send(sess->backend, data, length); } static void sesschan_send_eof(Channel *chan) { sesschan *sess = container_of(chan, sesschan, chan); if (sess->backend) backend_special(sess->backend, SS_EOF, 0); } static char *sesschan_log_close_msg(Channel *chan) { return dupstr("Session channel closed"); } static void sesschan_set_input_wanted(Channel *chan, bool wanted) { /* I don't think we need to do anything here */ } static void sesschan_start_backend(sesschan *sess, const char *cmd) { /* * List of environment variables that we should not pass through * from the login session Uppity was run in (which, it being a * test server, there will usually be one of). These variables * will be set as part of X or agent forwarding, and shouldn't be * confusingly set in the absence of that. * * (DISPLAY must also be cleared, but uxpty.c will do that anyway * when our get_x_display method returns NULL.) */ static const char *const env_to_unset[] = { "XAUTHORITY", "SSH_AUTH_SOCK", "SSH_AGENT_PID", NULL /* terminator */ }; sess->backend = pty_backend_create( &sess->seat, sess->child_logctx, sess->conf, NULL, cmd, sess->ttymodes, !sess->want_pty, sess->ssc->session_starting_dir, env_to_unset); backend_size(sess->backend, sess->wc, sess->hc); } bool sesschan_run_shell(Channel *chan) { sesschan *sess = container_of(chan, sesschan, chan); if (sess->backend) return false; sesschan_start_backend(sess, NULL); return true; } bool sesschan_run_command(Channel *chan, ptrlen command) { sesschan *sess = container_of(chan, sesschan, chan); if (sess->backend) return false; /* FIXME: make this possible to configure off */ if ((sess->scpsrv = scp_recognise_exec(sess->c, sess->sftpserver_vt, command)) != NULL) { sess->chan.vt = &scp_channelvt; logevent(sess->parent_logctx, "Starting built-in SCP server"); return true; } char *command_str = mkstr(command); sesschan_start_backend(sess, command_str); sfree(command_str); return true; } bool sesschan_run_subsystem(Channel *chan, ptrlen subsys) { sesschan *sess = container_of(chan, sesschan, chan); if (ptrlen_eq_string(subsys, "sftp") && sess->sftpserver_vt) { sess->sftpsrv = sftpsrv_new(sess->sftpserver_vt); sess->chan.vt = &sftp_channelvt; logevent(sess->parent_logctx, "Starting built-in SFTP subsystem"); return true; } return false; } static void fwd_log(Plug *plug, PlugLogType type, SockAddr *addr, int port, const char *error_msg, int error_code) { /* don't expect any weirdnesses from a listening socket */ } static void fwd_closing(Plug *plug, const char *error_msg, int error_code, bool calling_back) { /* not here, either */ } static int xfwd_accepting(Plug *p, accept_fn_t constructor, accept_ctx_t ctx) { sesschan *sess = container_of(p, sesschan, xfwd_plug); Plug *plug; Channel *chan; Socket *s; SocketPeerInfo *pi; const char *err; chan = portfwd_raw_new(sess->c->cl, &plug, false); s = constructor(ctx, plug); if ((err = sk_socket_error(s)) != NULL) { portfwd_raw_free(chan); return 1; } pi = sk_peer_info(s); portfwd_raw_setup(chan, s, ssh_serverside_x11_open(sess->c->cl, chan, pi)); sk_free_peer_info(pi); return 0; } static const PlugVtable xfwd_plugvt = { .log = fwd_log, .closing = fwd_closing, .accepting = xfwd_accepting, }; bool sesschan_enable_x11_forwarding( Channel *chan, bool oneshot, ptrlen authproto, ptrlen authdata_hex, unsigned screen_number) { sesschan *sess = container_of(chan, sesschan, chan); strbuf *authdata_bin; size_t i; char screensuffix[32]; if (oneshot) return false; /* not supported */ snprintf(screensuffix, sizeof(screensuffix), ".%u", screen_number); /* * Decode the authorisation data from ASCII hex into binary. */ if (authdata_hex.len % 2) return false; /* expected an even number of digits */ authdata_bin = strbuf_new_nm(); for (i = 0; i < authdata_hex.len; i += 2) { const unsigned char *hex = authdata_hex.ptr; char hexbuf[3]; if (!isxdigit(hex[i]) || !isxdigit(hex[i+1])) { strbuf_free(authdata_bin); return false; /* not hex */ } hexbuf[0] = hex[i]; hexbuf[1] = hex[i+1]; hexbuf[2] = '\0'; put_byte(authdata_bin, strtoul(hexbuf, NULL, 16)); } sess->xfwd_plug.vt = &xfwd_plugvt; sess->n_x11_sockets = platform_make_x11_server( &sess->xfwd_plug, appname, 10, screensuffix, authproto, ptrlen_from_strbuf(authdata_bin), sess->x11_sockets, sess->conf); strbuf_free(authdata_bin); return sess->n_x11_sockets != 0; } static int agentfwd_accepting( Plug *p, accept_fn_t constructor, accept_ctx_t ctx) { agentfwd *agent = container_of(p, agentfwd, plug); Plug *plug; Channel *chan; Socket *s; const char *err; chan = portfwd_raw_new(agent->cl, &plug, false); s = constructor(ctx, plug); if ((err = sk_socket_error(s)) != NULL) { portfwd_raw_free(chan); return 1; } portfwd_raw_setup(chan, s, ssh_serverside_agent_open(agent->cl, chan)); return 0; } static const PlugVtable agentfwd_plugvt = { .log = fwd_log, .closing = fwd_closing, .accepting = agentfwd_accepting, }; agentfwd *agentfwd_new(ConnectionLayer *cl, char **socketname_out) { agentfwd *agent = snew(agentfwd); agent->cl = cl; agent->plug.vt = &agentfwd_plugvt; char *dir_prefix = dupprintf("/tmp/%s-agentfwd", appname); char *error = NULL, *socketname = NULL; agent->socket = platform_make_agent_socket( &agent->plug, dir_prefix, &error, &socketname); sfree(dir_prefix); sfree(error); if (!agent->socket) { sfree(agent); sfree(socketname); return NULL; } *socketname_out = socketname; return agent; } void agentfwd_free(agentfwd *agent) { if (agent->socket) sk_close(agent->socket); sfree(agent); } bool sesschan_enable_agent_forwarding(Channel *chan) { sesschan *sess = container_of(chan, sesschan, chan); char *socketname; assert(!sess->agent); sess->agent = agentfwd_new(sess->c->cl, &socketname); if (!sess->agent) return false; conf_set_str_str(sess->conf, CONF_environmt, "SSH_AUTH_SOCK", socketname); sfree(socketname); return true; } bool sesschan_allocate_pty( Channel *chan, ptrlen termtype, unsigned width, unsigned height, unsigned pixwidth, unsigned pixheight, struct ssh_ttymodes modes) { sesschan *sess = container_of(chan, sesschan, chan); char *s; if (sess->want_pty) return false; s = mkstr(termtype); conf_set_str(sess->conf, CONF_termtype, s); sfree(s); sess->want_pty = true; sess->ttymodes = modes; sess->wc = width; sess->hc = height; sess->wp = pixwidth; sess->hp = pixheight; return true; } bool sesschan_set_env(Channel *chan, ptrlen var, ptrlen value) { sesschan *sess = container_of(chan, sesschan, chan); char *svar = mkstr(var), *svalue = mkstr(value); conf_set_str_str(sess->conf, CONF_environmt, svar, svalue); sfree(svar); sfree(svalue); return true; } bool sesschan_send_break(Channel *chan, unsigned length) { sesschan *sess = container_of(chan, sesschan, chan); if (sess->backend) { /* We ignore the break length. We could pass it through as the * 'arg' parameter, and have uxpty.c collect it and pass it on * to tcsendbreak, but since tcsendbreak in turn assigns * implementation-defined semantics to _its_ duration * parameter, this all just sounds too difficult. */ backend_special(sess->backend, SS_BRK, 0); return true; } return false; } bool sesschan_send_signal(Channel *chan, ptrlen signame) { sesschan *sess = container_of(chan, sesschan, chan); /* Start with a code that definitely isn't a signal (or indeed a * special command at all), to indicate 'nothing matched'. */ SessionSpecialCode code = SS_EXITMENU; #define SIGNAL_SUB(name) \ if (ptrlen_eq_string(signame, #name)) code = SS_SIG ## name; #define SIGNAL_MAIN(name, text) SIGNAL_SUB(name) #include "sshsignals.h" #undef SIGNAL_MAIN #undef SIGNAL_SUB if (code == SS_EXITMENU) return false; backend_special(sess->backend, code, 0); return true; } bool sesschan_change_window_size( Channel *chan, unsigned width, unsigned height, unsigned pixwidth, unsigned pixheight) { sesschan *sess = container_of(chan, sesschan, chan); if (!sess->want_pty) return false; sess->wc = width; sess->hc = height; sess->wp = pixwidth; sess->hp = pixheight; if (sess->backend) backend_size(sess->backend, sess->wc, sess->hc); return true; } static size_t sesschan_seat_output( Seat *seat, bool is_stderr, const void *data, size_t len) { sesschan *sess = container_of(seat, sesschan, seat); return sshfwd_write_ext(sess->c, is_stderr, data, len); } static void sesschan_check_close_callback(void *vctx) { sesschan *sess = (sesschan *)vctx; /* * Once we've seen incoming EOF from the backend (aka EIO from the * pty master) and also passed on the process's exit status, we * should proactively initiate closure of the session channel. */ if (sess->seen_eof && sess->seen_exit) sshfwd_initiate_close(sess->c, NULL); } static bool sesschan_want_close(Channel *chan, bool seen_eof, bool rcvd_eof) { sesschan *sess = container_of(chan, sesschan, chan); /* * Similarly to above, we don't want to initiate channel closure * until we've sent the process's exit status, _even_ if EOF of * the actual data stream has happened in both directions. */ return (sess->seen_eof && sess->seen_exit); } static bool sesschan_seat_eof(Seat *seat) { sesschan *sess = container_of(seat, sesschan, seat); sshfwd_write_eof(sess->c); sess->seen_eof = true; queue_toplevel_callback(sesschan_check_close_callback, sess); return true; } static void sesschan_notify_remote_exit(Seat *seat) { sesschan *sess = container_of(seat, sesschan, seat); if (!sess->backend) return; bool got_signal = false; if (!sess->ssc->exit_signal_numeric) { char *sigmsg; ptrlen signame = pty_backend_exit_signame(sess->backend, &sigmsg); if (signame.len) { if (!sigmsg) sigmsg = dupstr(""); sshfwd_send_exit_signal( sess->c, signame, false, ptrlen_from_asciz(sigmsg)); got_signal = true; } sfree(sigmsg); } else { int signum = pty_backend_exit_signum(sess->backend); if (signum >= 0) { sshfwd_send_exit_signal_numeric(sess->c, signum, false, PTRLEN_LITERAL("")); got_signal = true; } } if (!got_signal) sshfwd_send_exit_status(sess->c, backend_exitcode(sess->backend)); sess->seen_exit = true; queue_toplevel_callback(sesschan_check_close_callback, sess); } static void sesschan_connection_fatal(Seat *seat, const char *message) { sesschan *sess = container_of(seat, sesschan, seat); /* Closest translation I can think of */ sshfwd_send_exit_signal( sess->c, PTRLEN_LITERAL("HUP"), false, ptrlen_from_asciz(message)); sess->ignoring_input = true; } static bool sesschan_get_window_pixel_size(Seat *seat, int *width, int *height) { sesschan *sess = container_of(seat, sesschan, seat); *width = sess->wp; *height = sess->hp; return true; } /* ---------------------------------------------------------------------- * Built-in SFTP subsystem. */ static size_t sftp_chan_send(Channel *chan, bool is_stderr, const void *data, size_t length) { sesschan *sess = container_of(chan, sesschan, chan); bufchain_add(&sess->subsys_input, data, length); while (bufchain_size(&sess->subsys_input) >= 4) { char lenbuf[4]; unsigned pktlen; struct sftp_packet *pkt, *reply; bufchain_fetch(&sess->subsys_input, lenbuf, 4); pktlen = GET_32BIT_MSB_FIRST(lenbuf); if (bufchain_size(&sess->subsys_input) - 4 < pktlen) break; /* wait for more data */ bufchain_consume(&sess->subsys_input, 4); pkt = sftp_recv_prepare(pktlen); bufchain_fetch_consume(&sess->subsys_input, pkt->data, pktlen); sftp_recv_finish(pkt); reply = sftp_handle_request(sess->sftpsrv, pkt); sftp_pkt_free(pkt); sftp_send_prepare(reply); sshfwd_write(sess->c, reply->data, reply->length); sftp_pkt_free(reply); } return 0; } static void sftp_chan_send_eof(Channel *chan) { sesschan *sess = container_of(chan, sesschan, chan); sshfwd_write_eof(sess->c); } static char *sftp_log_close_msg(Channel *chan) { return dupstr("Session channel (SFTP) closed"); } /* ---------------------------------------------------------------------- * Built-in SCP subsystem. */ static size_t scp_chan_send(Channel *chan, bool is_stderr, const void *data, size_t length) { sesschan *sess = container_of(chan, sesschan, chan); return scp_send(sess->scpsrv, data, length); } static void scp_chan_send_eof(Channel *chan) { sesschan *sess = container_of(chan, sesschan, chan); scp_eof(sess->scpsrv); } static char *scp_log_close_msg(Channel *chan) { return dupstr("Session channel (SCP) closed"); } static void scp_set_input_wanted(Channel *chan, bool wanted) { sesschan *sess = container_of(chan, sesschan, chan); scp_throttle(sess->scpsrv, !wanted); } putty-0.76/sessprep.c0000644000175000017500000000520714072266311011572 00000000000000/* * sessprep.c: centralise some preprocessing done on Conf objects * before launching them. */ #include "putty.h" void prepare_session(Conf *conf) { char *hostbuf = dupstr(conf_get_str(conf, CONF_host)); char *host = hostbuf; char *p, *q; /* * Trim leading whitespace from the hostname. */ host += strspn(host, " \t"); /* * See if host is of the form user@host, and separate out the * username if so. */ if (host[0] != '\0') { /* * Use strrchr, in case the _username_ in turn is of the form * user@host, which has been known. */ 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, and discard * the text after it. * * The exact reason why we _ignore_ this text, rather than * treating it as a port number, is unfortunately lost in the * mists of history: the commit which originally introduced this * change on 2001-05-06 was clear on _what_ it was doing but * didn't bother to explain _why_. But I [SGT, 2017-12-03] suspect * it has to do with priority order: what should a saved session * do if its CONF_host contains 'server.example.com:123' and its * CONF_port contains 456? If CONF_port contained the _default_ * port number then it might be a good guess that the colon suffix * on the host name was intended to override that, but you don't * really want to get into making heuristic judgments on that * basis. * * (Then again, you could just as easily make the same argument * about whether a 'user@' prefix on the host name should override * CONF_username, which this code _does_ do. I don't have a good * answer, sadly. Both these pieces of behaviour have been around * for years and it would probably cause subtle breakage in all * sorts of long-forgotten scripting to go changing things around * now.) * * In order to protect unbracketed IPv6 address literals against * this treatment, we do not make this change at all if there's * _more_ than one (un-IPv6-bracketed) colon. */ p = host_strchr(host, ':'); if (p && p == host_strrchr(host, ':')) { *p = '\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); } putty-0.76/settings.c0000644000175000017500000016721514072266311011576 00000000000000/* * settings.c: read and write saved sessions. (platform-independent) */ #include #include #include #include "putty.h" #include "storage.h" #ifndef NO_GSSAPI #include "sshgssc.h" #include "sshgss.h" #endif /* The cipher order given here is the default order. */ static const struct keyvalwhere ciphernames[] = { { "aes", CIPHER_AES, -1, -1 }, { "chacha20", CIPHER_CHACHA20, CIPHER_AES, +1 }, { "3des", CIPHER_3DES, -1, -1 }, { "WARN", CIPHER_WARN, -1, -1 }, { "des", CIPHER_DES, -1, -1 }, { "blowfish", CIPHER_BLOWFISH, -1, -1 }, { "arcfour", CIPHER_ARCFOUR, -1, -1 }, }; /* The default order here is sometimes overridden by the backward- * compatibility warts in load_open_settings(), and should be kept * in sync with those. */ static const struct keyvalwhere kexnames[] = { { "ecdh", KEX_ECDH, -1, +1 }, /* This name is misleading: it covers both SHA-256 and SHA-1 variants */ { "dh-gex-sha1", KEX_DHGEX, -1, -1 }, { "dh-group14-sha1", KEX_DHGROUP14, -1, -1 }, { "dh-group1-sha1", KEX_DHGROUP1, KEX_WARN, +1 }, { "rsa", KEX_RSA, KEX_WARN, -1 }, { "WARN", KEX_WARN, -1, -1 } }; static const struct keyvalwhere hknames[] = { { "ed25519", HK_ED25519, -1, +1 }, { "ed448", HK_ED448, -1, +1 }, { "ecdsa", HK_ECDSA, -1, -1 }, { "dsa", HK_DSA, -1, -1 }, { "rsa", HK_RSA, -1, -1 }, { "WARN", HK_WARN, -1, -1 }, }; /* * All the terminal modes that we know about for the "TerminalModes" * setting. (Also used by config.c for the drop-down list.) * This is currently precisely the same as the set in ssh.c, but could * in principle differ if other backends started to support tty modes * (e.g., the pty backend). * The set of modes in in this array is currently significant for * settings migration from old versions; if they change, review the * gppmap() invocation for "TerminalModes". */ const char *const ttymodes[] = { "INTR", "QUIT", "ERASE", "KILL", "EOF", "EOL", "EOL2", "START", "STOP", "SUSP", "DSUSP", "REPRINT", "WERASE", "LNEXT", "FLUSH", "SWTCH", "STATUS", "DISCARD", "IGNPAR", "PARMRK", "INPCK", "ISTRIP", "INLCR", "IGNCR", "ICRNL", "IUCLC", "IXON", "IXANY", "IXOFF", "IMAXBEL", "IUTF8", "ISIG", "ICANON", "XCASE", "ECHO", "ECHOE", "ECHOK", "ECHONL", "NOFLSH", "TOSTOP", "IEXTEN", "ECHOCTL", "ECHOKE", "PENDIN", "OPOST", "OLCUC", "ONLCR", "OCRNL", "ONOCR", "ONLRET", "CS7", "CS8", "PARENB", "PARODD", NULL }; static int default_protocol, default_port; void settings_set_default_protocol(int newval) { default_protocol = newval; } void settings_set_default_port(int newval) { default_port = newval; } /* * Convenience functions to access the backends[] array * (which is only present in tools that manage settings). */ const struct BackendVtable *backend_vt_from_name(const char *name) { const struct BackendVtable *const *p; for (p = backends; *p != NULL; p++) if (!strcmp((*p)->id, name)) return *p; return NULL; } const struct BackendVtable *backend_vt_from_proto(int proto) { const struct BackendVtable *const *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_bool(conf, CONF_username_from_env)) { /* Use local username. */ return get_username(); /* might still be NULL */ } else { return NULL; } } static char *gpps_raw(settings_r *sesskey, const char *name, const char *def) { char *ret = read_setting_s(sesskey, 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(settings_r *sesskey, const char *name, const char *def, Conf *conf, int primary) { char *val = gpps_raw(sesskey, 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(settings_r *sesskey, char *name, Conf *conf, int primary) { FontSpec *result = read_setting_fontspec(sesskey, name); if (!result) result = platform_default_fontspec(name); conf_set_fontspec(conf, primary, result); fontspec_free(result); } static void gppfile(settings_r *sesskey, const char *name, Conf *conf, int primary) { Filename *result = read_setting_filename(sesskey, name); if (!result) result = platform_default_filename(name); conf_set_filename(conf, primary, result); filename_free(result); } static bool gppb_raw(settings_r *sesskey, const char *name, bool def) { def = platform_default_b(name, def); return sesskey ? read_setting_i(sesskey, name, def) != 0 : def; } static void gppb(settings_r *sesskey, const char *name, bool def, Conf *conf, int primary) { conf_set_bool(conf, primary, gppb_raw(sesskey, name, def)); } static int gppi_raw(settings_r *sesskey, const char *name, int def) { def = platform_default_i(name, def); return read_setting_i(sesskey, name, def); } static void gppi(settings_r *sesskey, const char *name, int def, Conf *conf, int primary) { conf_set_int(conf, primary, gppi_raw(sesskey, 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 * If there's no "=VALUE" (e.g. just NAME,NAME,NAME) then those keys * are mapped to the empty string. */ static bool gppmap(settings_r *sesskey, const 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(sesskey, 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, or just the * names if include_values is false. */ static void wmap(settings_w *sesskey, char const *outkey, Conf *conf, int primary, bool include_values) { char *buf, *p, *key, *realkey; const char *val, *q; 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", value "D") to the * conceptually incoherent legacy storage format (key * "D", 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; } if (include_values) { *p++ = '='; for (q = val; *q; q++) { if (*q == '=' || *q == ',' || *q == '\\') *p++ = '\\'; *p++ = *q; } } if (realkey) { free(key); key = realkey; } } *p = '\0'; write_setting_s(sesskey, outkey, buf); sfree(buf); } static int key2val(const struct keyvalwhere *mapping, int nmaps, char *key) { int i; for (i = 0; i < nmaps; i++) if (!strcmp(mapping[i].s, key)) return mapping[i].v; return -1; } static const char *val2key(const struct keyvalwhere *mapping, int nmaps, int val) { int i; for (i = 0; i < nmaps; i++) if (mapping[i].v == val) return mapping[i].s; return NULL; } /* * 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_from_str(const char *str, const struct keyvalwhere *mapping, int nvals, Conf *conf, int primary) { char *commalist = dupstr(str); char *p, *q; int i, j, n, v, pos; unsigned long seen = 0; /* bitmap for weeding dups etc */ /* * 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. */ while (n < nvals) { for (i = 0; i < nvals; i++) { assert(mapping[i].v >= 0); assert(mapping[i].v < 32); if (!(seen & (1 << mapping[i].v))) { /* * This element needs adding. But can we add it yet? */ if (mapping[i].vrel != -1 && !(seen & (1 << mapping[i].vrel))) continue; /* nope */ /* * 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<= 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); seen |= (1 << mapping[i].v); n++; } } } } /* * Read a preference list. */ static void gprefs(settings_r *sesskey, const char *name, const char *def, const struct keyvalwhere *mapping, int nvals, Conf *conf, int primary) { /* * Fetch the string which we'll parse as a comma-separated list. */ char *value = gpps_raw(sesskey, name, def); gprefs_from_str(value, mapping, nvals, conf, primary); sfree(value); } /* * Write out a preference list. */ static void wprefs(settings_w *sesskey, const 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); } static void write_setting_b(settings_w *handle, const char *key, bool value) { write_setting_i(handle, key, value ? 1 : 0); } static void write_clip_setting(settings_w *sesskey, const char *savekey, Conf *conf, int confkey, int strconfkey) { int val = conf_get_int(conf, confkey); switch (val) { case CLIPUI_NONE: default: write_setting_s(sesskey, savekey, "none"); break; case CLIPUI_IMPLICIT: write_setting_s(sesskey, savekey, "implicit"); break; case CLIPUI_EXPLICIT: write_setting_s(sesskey, savekey, "explicit"); break; case CLIPUI_CUSTOM: { char *sval = dupcat("custom:", conf_get_str(conf, strconfkey)); write_setting_s(sesskey, savekey, sval); sfree(sval); break; } } } static void read_clip_setting(settings_r *sesskey, char *savekey, int def, Conf *conf, int confkey, int strconfkey) { char *setting = read_setting_s(sesskey, savekey); int val; conf_set_str(conf, strconfkey, ""); if (!setting) { val = def; } else if (!strcmp(setting, "implicit")) { val = CLIPUI_IMPLICIT; } else if (!strcmp(setting, "explicit")) { val = CLIPUI_EXPLICIT; } else if (!strncmp(setting, "custom:", 7)) { val = CLIPUI_CUSTOM; conf_set_str(conf, strconfkey, setting + 7); } else { val = CLIPUI_NONE; } conf_set_int(conf, confkey, val); sfree(setting); } char *save_settings(const char *section, Conf *conf) { struct settings_w *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(settings_w *sesskey, Conf *conf) { int i; const 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_b(sesskey, "LogFlush", conf_get_bool(conf, CONF_logflush)); write_setting_b(sesskey, "LogHeader", conf_get_bool(conf, CONF_logheader)); write_setting_b(sesskey, "SSHLogOmitPasswords", conf_get_bool(conf, CONF_logomitpass)); write_setting_b(sesskey, "SSHLogOmitData", conf_get_bool(conf, CONF_logomitdata)); p = "raw"; { const struct BackendVtable *vt = backend_vt_from_proto(conf_get_int(conf, CONF_protocol)); if (vt) p = vt->id; } 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_b(sesskey, "WarnOnClose", !!conf_get_bool(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_b(sesskey, "TCPNoDelay", conf_get_bool(conf, CONF_tcp_nodelay)); write_setting_b(sesskey, "TCPKeepalives", conf_get_bool(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, true); /* 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_b(sesskey, "ProxyLocalhost", conf_get_bool(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)); write_setting_i(sesskey, "ProxyLogToTerm", conf_get_int(conf, CONF_proxy_log_to_term)); wmap(sesskey, "Environment", conf, CONF_environmt, true); write_setting_s(sesskey, "UserName", conf_get_str(conf, CONF_username)); write_setting_b(sesskey, "UserNameFromEnvironment", conf_get_bool(conf, CONF_username_from_env)); write_setting_s(sesskey, "LocalUserName", conf_get_str(conf, CONF_localusername)); write_setting_b(sesskey, "NoPTY", conf_get_bool(conf, CONF_nopty)); write_setting_b(sesskey, "Compression", conf_get_bool(conf, CONF_compression)); write_setting_b(sesskey, "TryAgent", conf_get_bool(conf, CONF_tryagent)); write_setting_b(sesskey, "AgentFwd", conf_get_bool(conf, CONF_agentfwd)); #ifndef NO_GSSAPI write_setting_b(sesskey, "GssapiFwd", conf_get_bool(conf, CONF_gssapifwd)); #endif write_setting_b(sesskey, "ChangeUsername", conf_get_bool(conf, CONF_change_username)); wprefs(sesskey, "Cipher", ciphernames, CIPHER_MAX, conf, CONF_ssh_cipherlist); wprefs(sesskey, "KEX", kexnames, KEX_MAX, conf, CONF_ssh_kexlist); wprefs(sesskey, "HostKey", hknames, HK_MAX, conf, CONF_ssh_hklist); write_setting_b(sesskey, "PreferKnownHostKeys", conf_get_bool(conf, CONF_ssh_prefer_known_hostkeys)); write_setting_i(sesskey, "RekeyTime", conf_get_int(conf, CONF_ssh_rekey_time)); #ifndef NO_GSSAPI write_setting_i(sesskey, "GssapiRekey", conf_get_int(conf, CONF_gssapirekey)); #endif write_setting_s(sesskey, "RekeyBytes", conf_get_str(conf, CONF_ssh_rekey_data)); write_setting_b(sesskey, "SshNoAuth", conf_get_bool(conf, CONF_ssh_no_userauth)); write_setting_b(sesskey, "SshNoTrivialAuth", conf_get_bool(conf, CONF_ssh_no_trivial_userauth)); write_setting_b(sesskey, "SshBanner", conf_get_bool(conf, CONF_ssh_show_banner)); write_setting_b(sesskey, "AuthTIS", conf_get_bool(conf, CONF_try_tis_auth)); write_setting_b(sesskey, "AuthKI", conf_get_bool(conf, CONF_try_ki_auth)); #ifndef NO_GSSAPI write_setting_b(sesskey, "AuthGSSAPI", conf_get_bool(conf, CONF_try_gssapi_auth)); write_setting_b(sesskey, "AuthGSSAPIKEX", conf_get_bool(conf, CONF_try_gssapi_kex)); 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_b(sesskey, "SshNoShell", conf_get_bool(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_b(sesskey, "SSH2DES", conf_get_bool(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_b(sesskey, "RFCEnviron", conf_get_bool(conf, CONF_rfc_environ)); write_setting_b(sesskey, "PassiveTelnet", conf_get_bool(conf, CONF_passive_telnet)); write_setting_b(sesskey, "BackspaceIsDelete", conf_get_bool(conf, CONF_bksp_is_delete)); write_setting_b(sesskey, "RXVTHomeEnd", conf_get_bool(conf, CONF_rxvt_homeend)); write_setting_i(sesskey, "LinuxFunctionKeys", conf_get_int(conf, CONF_funky_type)); write_setting_b(sesskey, "NoApplicationKeys", conf_get_bool(conf, CONF_no_applic_k)); write_setting_b(sesskey, "NoApplicationCursors", conf_get_bool(conf, CONF_no_applic_c)); write_setting_b(sesskey, "NoMouseReporting", conf_get_bool(conf, CONF_no_mouse_rep)); write_setting_b(sesskey, "NoRemoteResize", conf_get_bool(conf, CONF_no_remote_resize)); write_setting_b(sesskey, "NoAltScreen", conf_get_bool(conf, CONF_no_alt_screen)); write_setting_b(sesskey, "NoRemoteWinTitle", conf_get_bool(conf, CONF_no_remote_wintitle)); write_setting_b(sesskey, "NoRemoteClearScroll", conf_get_bool(conf, CONF_no_remote_clearscroll)); write_setting_i(sesskey, "RemoteQTitleAction", conf_get_int(conf, CONF_remote_qtitle_action)); write_setting_b(sesskey, "NoDBackspace", conf_get_bool(conf, CONF_no_dbackspace)); write_setting_b(sesskey, "NoRemoteCharset", conf_get_bool(conf, CONF_no_remote_charset)); write_setting_b(sesskey, "ApplicationCursorKeys", conf_get_bool(conf, CONF_app_cursor)); write_setting_b(sesskey, "ApplicationKeypad", conf_get_bool(conf, CONF_app_keypad)); write_setting_b(sesskey, "NetHackKeypad", conf_get_bool(conf, CONF_nethack_keypad)); write_setting_b(sesskey, "AltF4", conf_get_bool(conf, CONF_alt_f4)); write_setting_b(sesskey, "AltSpace", conf_get_bool(conf, CONF_alt_space)); write_setting_b(sesskey, "AltOnly", conf_get_bool(conf, CONF_alt_only)); write_setting_b(sesskey, "ComposeKey", conf_get_bool(conf, CONF_compose_key)); write_setting_b(sesskey, "CtrlAltKeys", conf_get_bool(conf, CONF_ctrlaltkeys)); #ifdef OSX_META_KEY_CONFIG write_setting_b(sesskey, "OSXOptionMeta", conf_get_bool(conf, CONF_osx_option_meta)); write_setting_b(sesskey, "OSXCommandMeta", conf_get_bool(conf, CONF_osx_command_meta)); #endif write_setting_b(sesskey, "TelnetKey", conf_get_bool(conf, CONF_telnet_keyboard)); write_setting_b(sesskey, "TelnetRet", conf_get_bool(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_b(sesskey, "AlwaysOnTop", conf_get_bool(conf, CONF_alwaysontop)); write_setting_b(sesskey, "FullScreenOnAltEnter", conf_get_bool(conf, CONF_fullscreenonaltenter)); write_setting_b(sesskey, "HideMousePtr", conf_get_bool(conf, CONF_hide_mouseptr)); write_setting_b(sesskey, "SunkenEdge", conf_get_bool(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_b(sesskey, "BlinkCur", conf_get_bool(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_b(sesskey, "BellOverload", conf_get_bool(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_b(sesskey, "DECOriginMode", conf_get_bool(conf, CONF_dec_om)); write_setting_b(sesskey, "AutoWrapMode", conf_get_bool(conf, CONF_wrap_mode)); write_setting_b(sesskey, "LFImpliesCR", conf_get_bool(conf, CONF_lfhascr)); write_setting_b(sesskey, "CRImpliesLF", conf_get_bool(conf, CONF_crhaslf)); write_setting_b(sesskey, "DisableArabicShaping", conf_get_bool(conf, CONF_no_arabicshaping)); write_setting_b(sesskey, "DisableBidi", conf_get_bool(conf, CONF_no_bidi)); write_setting_b(sesskey, "WinNameAlways", conf_get_bool(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_b(sesskey, "UseSystemColours", conf_get_bool(conf, CONF_system_colour)); write_setting_b(sesskey, "TryPalette", conf_get_bool(conf, CONF_try_palette)); write_setting_b(sesskey, "ANSIColour", conf_get_bool(conf, CONF_ansi_colour)); write_setting_b(sesskey, "Xterm256Colour", conf_get_bool(conf, CONF_xterm_256_colour)); write_setting_b(sesskey, "TrueColour", conf_get_bool(conf, CONF_true_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_b(sesskey, "RawCNP", conf_get_bool(conf, CONF_rawcnp)); write_setting_b(sesskey, "UTF8linedraw", conf_get_bool(conf, CONF_utf8linedraw)); write_setting_b(sesskey, "PasteRTF", conf_get_bool(conf, CONF_rtf_paste)); write_setting_i(sesskey, "MouseIsXterm", conf_get_int(conf, CONF_mouse_is_xterm)); write_setting_b(sesskey, "RectSelect", conf_get_bool(conf, CONF_rect_select)); write_setting_b(sesskey, "PasteControls", conf_get_bool(conf, CONF_paste_controls)); write_setting_b(sesskey, "MouseOverride", conf_get_bool(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_b(sesskey, "MouseAutocopy", conf_get_bool(conf, CONF_mouseautocopy)); write_clip_setting(sesskey, "MousePaste", conf, CONF_mousepaste, CONF_mousepaste_custom); write_clip_setting(sesskey, "CtrlShiftIns", conf, CONF_ctrlshiftins, CONF_ctrlshiftins_custom); write_clip_setting(sesskey, "CtrlShiftCV", conf, CONF_ctrlshiftcv, CONF_ctrlshiftcv_custom); write_setting_s(sesskey, "LineCodePage", conf_get_str(conf, CONF_line_codepage)); write_setting_b(sesskey, "CJKAmbigWide", conf_get_bool(conf, CONF_cjk_ambig_wide)); write_setting_b(sesskey, "UTF8Override", conf_get_bool(conf, CONF_utf8_override)); write_setting_s(sesskey, "Printer", conf_get_str(conf, CONF_printer)); write_setting_b(sesskey, "CapsLockCyr", conf_get_bool(conf, CONF_xlat_capslockcyr)); write_setting_b(sesskey, "ScrollBar", conf_get_bool(conf, CONF_scrollbar)); write_setting_b(sesskey, "ScrollBarFullScreen", conf_get_bool(conf, CONF_scrollbar_in_fullscreen)); write_setting_b(sesskey, "ScrollOnKey", conf_get_bool(conf, CONF_scroll_on_key)); write_setting_b(sesskey, "ScrollOnDisp", conf_get_bool(conf, CONF_scroll_on_disp)); write_setting_b(sesskey, "EraseToScrollback", conf_get_bool(conf, CONF_erase_to_scrollback)); write_setting_i(sesskey, "LockSize", conf_get_int(conf, CONF_resize_action)); write_setting_b(sesskey, "BCE", conf_get_bool(conf, CONF_bce)); write_setting_b(sesskey, "BlinkText", conf_get_bool(conf, CONF_blinktext)); write_setting_b(sesskey, "X11Forward", conf_get_bool(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_b(sesskey, "LocalPortAcceptAll", conf_get_bool(conf, CONF_lport_acceptall)); write_setting_b(sesskey, "RemotePortAcceptAll", conf_get_bool(conf, CONF_rport_acceptall)); wmap(sesskey, "PortForwardings", conf, CONF_portfwd, true); 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, "BugOldGex2", 2-conf_get_int(conf, CONF_sshbug_oldgex2)); write_setting_i(sesskey, "BugWinadj", 2-conf_get_int(conf, CONF_sshbug_winadj)); write_setting_i(sesskey, "BugChanReq", 2-conf_get_int(conf, CONF_sshbug_chanreq)); write_setting_b(sesskey, "StampUtmp", conf_get_bool(conf, CONF_stamp_utmp)); write_setting_b(sesskey, "LoginShell", conf_get_bool(conf, CONF_login_shell)); write_setting_b(sesskey, "ScrollbarOnLeft", conf_get_bool(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_b(sesskey, "ShadowBold", conf_get_bool(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_b(sesskey, "ConnectionSharing", conf_get_bool(conf, CONF_ssh_connection_sharing)); write_setting_b(sesskey, "ConnectionSharingUpstream", conf_get_bool(conf, CONF_ssh_connection_sharing_upstream)); write_setting_b(sesskey, "ConnectionSharingDownstream", conf_get_bool(conf, CONF_ssh_connection_sharing_downstream)); wmap(sesskey, "SSHManualHostKeys", conf, CONF_ssh_manual_hostkeys, false); /* * SUPDUP settings */ write_setting_s(sesskey, "SUPDUPLocation", conf_get_str(conf, CONF_supdup_location)); write_setting_i(sesskey, "SUPDUPCharset", conf_get_int(conf, CONF_supdup_ascii_set)); write_setting_b(sesskey, "SUPDUPMoreProcessing", conf_get_bool(conf, CONF_supdup_more)); write_setting_b(sesskey, "SUPDUPScrolling", conf_get_bool(conf, CONF_supdup_scroll)); } bool load_settings(const char *section, Conf *conf) { settings_r *sesskey; sesskey = open_settings_r(section); bool exists = (sesskey != NULL); load_open_settings(sesskey, conf); close_settings_r(sesskey); if (exists && conf_launchable(conf)) add_session_to_jumplist(section); return exists; } void load_open_settings(settings_r *sesskey, Conf *conf) { int i; char *prot; conf_set_bool(conf, CONF_ssh_subsys, false); /* 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); gppb(sesskey, "LogFlush", true, conf, CONF_logflush); gppb(sesskey, "LogHeader", true, conf, CONF_logheader); gppb(sesskey, "SSHLogOmitPasswords", true, conf, CONF_logomitpass); gppb(sesskey, "SSHLogOmitData", false, 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 struct BackendVtable *vt = backend_vt_from_name(prot); if (vt) { conf_set_int(conf, CONF_protocol, vt->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); gppb(sesskey, "WarnOnClose", true, 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); } gppb(sesskey, "TCPNoDelay", true, conf, CONF_tcp_nodelay); gppb(sesskey, "TCPKeepalives", false, 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)) { /* * Backwards compatibility with old saved settings. * * From the invention of this setting through 0.67, the set of * terminal modes was fixed, and absence of a mode from this * setting meant the user had explicitly removed it from the * UI and we shouldn't send it. * * In 0.68, the IUTF8 mode was added, and in handling old * settings we inadvertently removed the ability to not send * a mode. Any mode not mentioned was treated as if it was * set to 'auto' (A). * * After 0.68, we added explicit notation to the setting format * when the user removes a known terminal mode from the list. * * So: if any of the modes from the original set is missing, we * assume this was an intentional removal by the user and add * an explicit removal ('N'); but if IUTF8 (or any other mode * added after 0.67) is missing, we assume that its absence is * due to the setting being old rather than intentional, and * add it with its default setting. * * (This does mean that if a 0.68 user explicitly removed IUTF8, * we add it back; but removing IUTF8 had no effect in 0.68, so * we're preserving behaviour, which is the best we can do.) */ for (i = 0; ttymodes[i]; i++) { if (!conf_get_str_str_opt(conf, CONF_ttymodes, ttymodes[i])) { /* Mode not mentioned in setting. */ const char *def; if (!strcmp(ttymodes[i], "IUTF8")) { /* Any new modes we add in future should be treated * this way too. */ def = "A"; /* same as new-setting default below */ } else { /* One of the original modes. Absence is probably * deliberate. */ def = "N"; /* don't send */ } conf_set_str_str(conf, CONF_ttymodes, ttymodes[i], def); } } } else { /* 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); gppb(sesskey, "ProxyLocalhost", false, 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); gppi(sesskey, "ProxyLogToTerm", FORCE_OFF, conf, CONF_proxy_log_to_term); gppmap(sesskey, "Environment", conf, CONF_environmt); gpps(sesskey, "UserName", "", conf, CONF_username); gppb(sesskey, "UserNameFromEnvironment", false, conf, CONF_username_from_env); gpps(sesskey, "LocalUserName", "", conf, CONF_localusername); gppb(sesskey, "NoPTY", false, conf, CONF_nopty); gppb(sesskey, "Compression", false, conf, CONF_compression); gppb(sesskey, "TryAgent", true, conf, CONF_tryagent); gppb(sesskey, "AgentFwd", false, conf, CONF_agentfwd); gppb(sesskey, "ChangeUsername", false, conf, CONF_change_username); #ifndef NO_GSSAPI gppb(sesskey, "GssapiFwd", false, conf, CONF_gssapifwd); #endif gprefs(sesskey, "Cipher", "\0", ciphernames, CIPHER_MAX, conf, CONF_ssh_cipherlist); { /* Backward-compatibility: before 0.58 (when the "KEX" * preference was first added), we had 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. */ const char *default_kexes, *normal_default = "ecdh,dh-gex-sha1,dh-group14-sha1,rsa," "WARN,dh-group1-sha1", *bugdhgex2_default = "ecdh,dh-group14-sha1,rsa," "WARN,dh-group1-sha1,dh-gex-sha1"; char *raw; i = 2 - gppi_raw(sesskey, "BugDHGEx2", 0); if (i == FORCE_ON) default_kexes = bugdhgex2_default; else default_kexes = normal_default; /* Migration: after 0.67 we decided we didn't like * dh-group1-sha1. If it looks like the user never changed * the defaults, quietly upgrade their settings to demote it. * (If they did, they're on their own.) */ raw = gpps_raw(sesskey, "KEX", default_kexes); assert(raw != NULL); /* Lack of 'ecdh' tells us this was saved by 0.58-0.67 * inclusive. If it was saved by a later version, we need * to leave it alone. */ if (strcmp(raw, "dh-group14-sha1,dh-group1-sha1,rsa," "WARN,dh-gex-sha1") == 0) { /* Previously migrated from BugDHGEx2. */ sfree(raw); raw = dupstr(bugdhgex2_default); } else if (strcmp(raw, "dh-gex-sha1,dh-group14-sha1," "dh-group1-sha1,rsa,WARN") == 0) { /* Untouched old default setting. */ sfree(raw); raw = dupstr(normal_default); } /* (For the record: after 0.70, the default algorithm list * very briefly contained the string 'gss-sha1-krb5'; this was * never used in any committed version of code, but was left * over from a pre-commit version of GSS key exchange. * Mentioned here as it is remotely possible that it will turn * up in someone's saved settings in future.) */ gprefs_from_str(raw, kexnames, KEX_MAX, conf, CONF_ssh_kexlist); sfree(raw); } gprefs(sesskey, "HostKey", "ed25519,ecdsa,rsa,dsa,WARN", hknames, HK_MAX, conf, CONF_ssh_hklist); gppb(sesskey, "PreferKnownHostKeys", true, conf, CONF_ssh_prefer_known_hostkeys); gppi(sesskey, "RekeyTime", 60, conf, CONF_ssh_rekey_time); #ifndef NO_GSSAPI gppi(sesskey, "GssapiRekey", GSS_DEF_REKEY_MINS, conf, CONF_gssapirekey); #endif gpps(sesskey, "RekeyBytes", "1G", conf, CONF_ssh_rekey_data); { /* SSH-2 only by default */ int sshprot = gppi_raw(sesskey, "SshProt", 3); /* Old sessions may contain the values corresponding to the fallbacks * we used to allow; migrate them */ if (sshprot == 1) sshprot = 0; /* => "SSH-1 only" */ else if (sshprot == 2) sshprot = 3; /* => "SSH-2 only" */ conf_set_int(conf, CONF_sshprot, sshprot); } gpps(sesskey, "LogHost", "", conf, CONF_loghost); gppb(sesskey, "SSH2DES", false, conf, CONF_ssh2_des_cbc); gppb(sesskey, "SshNoAuth", false, conf, CONF_ssh_no_userauth); gppb(sesskey, "SshNoTrivialAuth", false, conf, CONF_ssh_no_trivial_userauth); gppb(sesskey, "SshBanner", true, conf, CONF_ssh_show_banner); gppb(sesskey, "AuthTIS", false, conf, CONF_try_tis_auth); gppb(sesskey, "AuthKI", true, conf, CONF_try_ki_auth); #ifndef NO_GSSAPI gppb(sesskey, "AuthGSSAPI", true, conf, CONF_try_gssapi_auth); gppb(sesskey, "AuthGSSAPIKEX", true, conf, CONF_try_gssapi_kex); gprefs(sesskey, "GSSLibs", "\0", gsslibkeywords, ngsslibs, conf, CONF_ssh_gsslist); gppfile(sesskey, "GSSCustom", conf, CONF_ssh_gss_custom); #endif gppb(sesskey, "SshNoShell", false, conf, CONF_ssh_no_shell); gppfile(sesskey, "PublicKeyFile", conf, CONF_keyfile); gpps(sesskey, "RemoteCommand", "", conf, CONF_remote_cmd); gppb(sesskey, "RFCEnviron", false, conf, CONF_rfc_environ); gppb(sesskey, "PassiveTelnet", false, conf, CONF_passive_telnet); gppb(sesskey, "BackspaceIsDelete", true, conf, CONF_bksp_is_delete); gppb(sesskey, "RXVTHomeEnd", false, conf, CONF_rxvt_homeend); gppi(sesskey, "LinuxFunctionKeys", 0, conf, CONF_funky_type); gppb(sesskey, "NoApplicationKeys", false, conf, CONF_no_applic_k); gppb(sesskey, "NoApplicationCursors", false, conf, CONF_no_applic_c); gppb(sesskey, "NoMouseReporting", false, conf, CONF_no_mouse_rep); gppb(sesskey, "NoRemoteResize", false, conf, CONF_no_remote_resize); gppb(sesskey, "NoAltScreen", false, conf, CONF_no_alt_screen); gppb(sesskey, "NoRemoteWinTitle", false, conf, CONF_no_remote_wintitle); gppb(sesskey, "NoRemoteClearScroll", false, conf, CONF_no_remote_clearscroll); { /* 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); } gppb(sesskey, "NoDBackspace", false, conf, CONF_no_dbackspace); gppb(sesskey, "NoRemoteCharset", false, conf, CONF_no_remote_charset); gppb(sesskey, "ApplicationCursorKeys", false, conf, CONF_app_cursor); gppb(sesskey, "ApplicationKeypad", false, conf, CONF_app_keypad); gppb(sesskey, "NetHackKeypad", false, conf, CONF_nethack_keypad); gppb(sesskey, "AltF4", true, conf, CONF_alt_f4); gppb(sesskey, "AltSpace", false, conf, CONF_alt_space); gppb(sesskey, "AltOnly", false, conf, CONF_alt_only); gppb(sesskey, "ComposeKey", false, conf, CONF_compose_key); gppb(sesskey, "CtrlAltKeys", true, conf, CONF_ctrlaltkeys); #ifdef OSX_META_KEY_CONFIG gppb(sesskey, "OSXOptionMeta", true, conf, CONF_osx_option_meta); gppb(sesskey, "OSXCommandMeta", false, conf, CONF_osx_command_meta); #endif gppb(sesskey, "TelnetKey", false, conf, CONF_telnet_keyboard); gppb(sesskey, "TelnetRet", true, 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); gppb(sesskey, "AlwaysOnTop", false, conf, CONF_alwaysontop); gppb(sesskey, "FullScreenOnAltEnter", false, conf, CONF_fullscreenonaltenter); gppb(sesskey, "HideMousePtr", false, conf, CONF_hide_mouseptr); gppb(sesskey, "SunkenEdge", false, conf, CONF_sunken_edge); gppi(sesskey, "WindowBorder", 1, conf, CONF_window_border); gppi(sesskey, "CurType", 0, conf, CONF_cursor_type); gppb(sesskey, "BlinkCur", false, 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); gppb(sesskey, "BellOverload", true, 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); gppb(sesskey, "DECOriginMode", false, conf, CONF_dec_om); gppb(sesskey, "AutoWrapMode", true, conf, CONF_wrap_mode); gppb(sesskey, "LFImpliesCR", false, conf, CONF_lfhascr); gppb(sesskey, "CRImpliesLF", false, conf, CONF_crhaslf); gppb(sesskey, "DisableArabicShaping", false, conf, CONF_no_arabicshaping); gppb(sesskey, "DisableBidi", false, conf, CONF_no_bidi); gppb(sesskey, "WinNameAlways", true, 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); gppb(sesskey, "UseSystemColours", false, conf, CONF_system_colour); gppb(sesskey, "TryPalette", false, conf, CONF_try_palette); gppb(sesskey, "ANSIColour", true, conf, CONF_ansi_colour); gppb(sesskey, "Xterm256Colour", true, conf, CONF_xterm_256_colour); gppb(sesskey, "TrueColour", true, conf, CONF_true_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); } gppb(sesskey, "RawCNP", false, conf, CONF_rawcnp); gppb(sesskey, "UTF8linedraw", false, conf, CONF_utf8linedraw); gppb(sesskey, "PasteRTF", false, conf, CONF_rtf_paste); gppi(sesskey, "MouseIsXterm", 0, conf, CONF_mouse_is_xterm); gppb(sesskey, "RectSelect", false, conf, CONF_rect_select); gppb(sesskey, "PasteControls", false, conf, CONF_paste_controls); gppb(sesskey, "MouseOverride", true, 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); } gppb(sesskey, "MouseAutocopy", CLIPUI_DEFAULT_AUTOCOPY, conf, CONF_mouseautocopy); read_clip_setting(sesskey, "MousePaste", CLIPUI_DEFAULT_MOUSE, conf, CONF_mousepaste, CONF_mousepaste_custom); read_clip_setting(sesskey, "CtrlShiftIns", CLIPUI_DEFAULT_INS, conf, CONF_ctrlshiftins, CONF_ctrlshiftins_custom); read_clip_setting(sesskey, "CtrlShiftCV", CLIPUI_NONE, conf, CONF_ctrlshiftcv, CONF_ctrlshiftcv_custom); /* * The empty default for LineCodePage will be converted later * into a plausible default for the locale. */ gpps(sesskey, "LineCodePage", "", conf, CONF_line_codepage); gppb(sesskey, "CJKAmbigWide", false, conf, CONF_cjk_ambig_wide); gppb(sesskey, "UTF8Override", true, conf, CONF_utf8_override); gpps(sesskey, "Printer", "", conf, CONF_printer); gppb(sesskey, "CapsLockCyr", false, conf, CONF_xlat_capslockcyr); gppb(sesskey, "ScrollBar", true, conf, CONF_scrollbar); gppb(sesskey, "ScrollBarFullScreen", false, conf, CONF_scrollbar_in_fullscreen); gppb(sesskey, "ScrollOnKey", false, conf, CONF_scroll_on_key); gppb(sesskey, "ScrollOnDisp", true, conf, CONF_scroll_on_disp); gppb(sesskey, "EraseToScrollback", true, conf, CONF_erase_to_scrollback); gppi(sesskey, "LockSize", 0, conf, CONF_resize_action); gppb(sesskey, "BCE", true, conf, CONF_bce); gppb(sesskey, "BlinkText", false, conf, CONF_blinktext); gppb(sesskey, "X11Forward", false, 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); gppb(sesskey, "LocalPortAcceptAll", false, conf, CONF_lport_acceptall); gppb(sesskey, "RemotePortAcceptAll", false, 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, "BugOldGex2", 0); conf_set_int(conf, CONF_sshbug_oldgex2, 2-i); i = gppi_raw(sesskey, "BugWinadj", 0); conf_set_int(conf, CONF_sshbug_winadj, 2-i); i = gppi_raw(sesskey, "BugChanReq", 0); conf_set_int(conf, CONF_sshbug_chanreq, 2-i); conf_set_bool(conf, CONF_ssh_simple, false); gppb(sesskey, "StampUtmp", true, conf, CONF_stamp_utmp); gppb(sesskey, "LoginShell", true, conf, CONF_login_shell); gppb(sesskey, "ScrollbarOnLeft", false, conf, CONF_scrollbar_on_left); gppb(sesskey, "ShadowBold", false, 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); gppb(sesskey, "ConnectionSharing", false, conf, CONF_ssh_connection_sharing); gppb(sesskey, "ConnectionSharingUpstream", true, conf, CONF_ssh_connection_sharing_upstream); gppb(sesskey, "ConnectionSharingDownstream", true, conf, CONF_ssh_connection_sharing_downstream); gppmap(sesskey, "SSHManualHostKeys", conf, CONF_ssh_manual_hostkeys); /* * SUPDUP settings */ gpps(sesskey, "SUPDUPLocation", "The Internet", conf, CONF_supdup_location); gppi(sesskey, "SUPDUPCharset", false, conf, CONF_supdup_ascii_set); gppb(sesskey, "SUPDUPMoreProcessing", false, conf, CONF_supdup_more); gppb(sesskey, "SUPDUPScrolling", false, conf, CONF_supdup_scroll); } bool do_defaults(const char *session, Conf *conf) { return 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; /* * Alphabetical order, except that "Default Settings" is a * special case and comes first. */ if (!strcmp(a, "Default Settings")) return -1; /* a comes first */ if (!strcmp(b, "Default Settings")) return +1; /* b comes first */ /* * FIXME: perhaps we should ignore the first & in determining * sort order. */ return strcmp(a, b); /* otherwise, compare normally */ } void get_sesslist(struct sesslist *list, bool allocate) { int i; char *p; settings_e *handle; if (allocate) { strbuf *sb = strbuf_new(); if ((handle = enum_settings_start()) != NULL) { while (enum_settings_next(handle, sb)) put_byte(sb, '\0'); enum_settings_finish(handle); } put_byte(sb, '\0'); list->buffer = strbuf_to_str(sb); /* * Now set up the list of sessions. Note that "Default * Settings" must always be claimed to exist, even if it * doesn't really. */ p = list->buffer; list->nsessions = 1; /* "Default Settings" counts as one */ while (*p) { if (strcmp(p, "Default Settings")) list->nsessions++; while (*p) p++; p++; } list->sessions = snewn(list->nsessions + 1, const char *); list->sessions[0] = "Default Settings"; p = list->buffer; i = 1; while (*p) { if (strcmp(p, "Default Settings")) list->sessions[i++] = p; while (*p) p++; p++; } qsort(list->sessions, i, sizeof(const char *), sessioncmp); } else { sfree(list->buffer); sfree(list->sessions); list->buffer = NULL; list->sessions = NULL; } } putty-0.76/sftp.c0000644000175000017500000007331214072266311010704 00000000000000/* * sftp.c: SFTP generic client code. */ #include #include #include #include #include #include "misc.h" #include "tree234.h" #include "sftp.h" static const char *fxp_error_message; static int fxp_errtype; static void fxp_internal_error(const char *msg); /* ---------------------------------------------------------------------- * Client-specific parts of the send- and receive-packet system. */ bool sftp_send(struct sftp_packet *pkt) { bool ret; sftp_send_prepare(pkt); 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]; if (!sftp_recvdata(x, 4)) return NULL; /* Impose _some_ upper bound on packet size. We never expect to * receive more than 32K of data in response to an FXP_READ, * because we decide how much data to ask for. FXP_READDIR and * pathname-returning things like FXP_REALPATH don't have an * explicit bound, so I suppose we just have to trust the server * to be sensible. */ unsigned pktlen = GET_32BIT_MSB_FIRST(x); if (pktlen > (1<<20)) return NULL; pkt = sftp_recv_prepare(pktlen); if (!sftp_recvdata(pkt->data, pkt->length)) { sftp_pkt_free(pkt); return NULL; } if (!sftp_recv_finish(pkt)) { sftp_pkt_free(pkt); return NULL; } return pkt; } /* ---------------------------------------------------------------------- * Request ID allocation and temporary dispatch routines. */ #define REQUEST_ID_OFFSET 256 struct sftp_request { unsigned id; bool registered; void *userdata; }; static int sftp_reqcmp(void *av, void *bv) { struct sftp_request *a = (struct sftp_request *)av; struct sftp_request *b = (struct sftp_request *)bv; if (a->id < b->id) return -1; if (a->id > b->id) return +1; return 0; } static int sftp_reqfind(void *av, void *bv) { unsigned *a = (unsigned *) av; struct sftp_request *b = (struct sftp_request *)bv; if (*a < b->id) return -1; if (*a > b->id) return +1; return 0; } static tree234 *sftp_requests; static struct sftp_request *sftp_alloc_request(void) { unsigned low, high, mid; int tsize; struct sftp_request *r; if (sftp_requests == NULL) sftp_requests = newtree234(sftp_reqcmp); /* * First-fit allocation of request IDs: always pick the lowest * unused one. To do this, binary-search using the counted * B-tree to find the largest ID which is in a contiguous * sequence from the beginning. (Precisely everything in that * sequence must have ID equal to its tree index plus * REQUEST_ID_OFFSET.) */ tsize = count234(sftp_requests); low = -1; high = tsize; while (high - low > 1) { mid = (high + low) / 2; r = index234(sftp_requests, mid); if (r->id == mid + REQUEST_ID_OFFSET) low = mid; /* this one is fine */ else high = mid; /* this one is past it */ } /* * Now low points to either -1, or the tree index of the * largest ID in the initial sequence. */ { unsigned i = low + 1 + REQUEST_ID_OFFSET; assert(NULL == find234(sftp_requests, &i, sftp_reqfind)); } /* * So the request ID we need to create is * low + 1 + REQUEST_ID_OFFSET. */ r = snew(struct sftp_request); r->id = low + 1 + REQUEST_ID_OFFSET; r->registered = false; r->userdata = NULL; add234(sftp_requests, r); return r; } void sftp_cleanup_request(void) { if (sftp_requests != NULL) { freetree234(sftp_requests); sftp_requests = NULL; } } void sftp_register(struct sftp_request *req) { req->registered = true; } struct sftp_request *sftp_find_request(struct sftp_packet *pktin) { unsigned id; struct sftp_request *req; if (!pktin) { fxp_internal_error("did not receive a valid SFTP packet\n"); return NULL; } id = get_uint32(pktin); if (get_err(pktin)) { 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; } /* ---------------------------------------------------------------------- * SFTP primitives. */ /* * Deal with (and free) an FXP_STATUS packet. Return 1 if * SSH_FX_OK, 0 if SSH_FX_EOF, and -1 for anything else (error). * Also place the status into fxp_errtype. */ static int fxp_got_status(struct sftp_packet *pktin) { static const char *const messages[] = { /* SSH_FX_OK. The only time we will display a _message_ for this * is if we were expecting something other than FXP_STATUS on * success, so this is actually an error message! */ "unexpected OK response", "end of file", "no such file or directory", "permission denied", "failure", "bad message", "no connection", "connection lost", "operation unsupported", }; if (pktin->type != SSH_FXP_STATUS) { fxp_error_message = "expected FXP_STATUS packet"; fxp_errtype = -1; } else { fxp_errtype = get_uint32(pktin); if (get_err(pktin)) { fxp_error_message = "malformed FXP_STATUS packet"; fxp_errtype = -1; } else { if (fxp_errtype < 0 || fxp_errtype >= lenof(messages)) fxp_error_message = "unknown error code"; else fxp_error_message = messages[fxp_errtype]; } } if (fxp_errtype == SSH_FX_OK) return 1; else if (fxp_errtype == SSH_FX_EOF) return 0; else return -1; } static void fxp_internal_error(const char *msg) { fxp_error_message = msg; fxp_errtype = -1; } const char *fxp_error(void) { return fxp_error_message; } int fxp_error_type(void) { return fxp_errtype; } /* * Perform exchange of init/version packets. Return 0 on failure. */ bool fxp_init(void) { struct sftp_packet *pktout, *pktin; unsigned long remotever; pktout = sftp_pkt_init(SSH_FXP_INIT); put_uint32(pktout, SFTP_PROTO_VERSION); sftp_send(pktout); pktin = sftp_recv(); if (!pktin) { fxp_internal_error("could not connect"); return false; } if (pktin->type != SSH_FXP_VERSION) { fxp_internal_error("did not receive FXP_VERSION"); sftp_pkt_free(pktin); return false; } remotever = get_uint32(pktin); if (get_err(pktin)) { fxp_internal_error("malformed FXP_VERSION packet"); sftp_pkt_free(pktin); return false; } if (remotever > SFTP_PROTO_VERSION) { fxp_internal_error ("remote protocol is more advanced than we support"); sftp_pkt_free(pktin); return false; } /* * In principle, this packet might also contain extension- * string pairs. We should work through them and look for any * we recognise. In practice we don't currently do so because * we know we don't recognise _any_. */ sftp_pkt_free(pktin); return true; } /* * Canonify a pathname. */ struct sftp_request *fxp_realpath_send(const char *path) { struct sftp_request *req = sftp_alloc_request(); struct sftp_packet *pktout; pktout = sftp_pkt_init(SSH_FXP_REALPATH); put_uint32(pktout, req->id); put_stringz(pktout, path); sftp_send(pktout); return req; } char *fxp_realpath_recv(struct sftp_packet *pktin, struct sftp_request *req) { sfree(req); if (pktin->type == SSH_FXP_NAME) { unsigned long count; char *path; ptrlen name; count = get_uint32(pktin); if (get_err(pktin) || count != 1) { fxp_internal_error("REALPATH did not return name count of 1\n"); sftp_pkt_free(pktin); return NULL; } name = get_string(pktin); if (get_err(pktin)) { fxp_internal_error("REALPATH returned malformed FXP_NAME\n"); sftp_pkt_free(pktin); return NULL; } path = mkstr(name); sftp_pkt_free(pktin); return path; } else { fxp_got_status(pktin); sftp_pkt_free(pktin); return NULL; } } /* * Open a file. */ struct sftp_request *fxp_open_send(const char *path, int type, const struct fxp_attrs *attrs) { struct sftp_request *req = sftp_alloc_request(); struct sftp_packet *pktout; pktout = sftp_pkt_init(SSH_FXP_OPEN); put_uint32(pktout, req->id); put_stringz(pktout, path); put_uint32(pktout, type); put_fxp_attrs(pktout, attrs ? *attrs : no_attrs); sftp_send(pktout); return req; } static struct fxp_handle *fxp_got_handle(struct sftp_packet *pktin) { ptrlen id; struct fxp_handle *handle; id = get_string(pktin); if (get_err(pktin)) { fxp_internal_error("received malformed FXP_HANDLE"); sftp_pkt_free(pktin); return NULL; } handle = snew(struct fxp_handle); handle->hstring = mkstr(id); handle->hlen = id.len; sftp_pkt_free(pktin); return handle; } struct fxp_handle *fxp_open_recv(struct sftp_packet *pktin, struct sftp_request *req) { sfree(req); if (pktin->type == SSH_FXP_HANDLE) { return fxp_got_handle(pktin); } else { fxp_got_status(pktin); sftp_pkt_free(pktin); return NULL; } } /* * Open a directory. */ struct sftp_request *fxp_opendir_send(const char *path) { struct sftp_request *req = sftp_alloc_request(); struct sftp_packet *pktout; pktout = sftp_pkt_init(SSH_FXP_OPENDIR); put_uint32(pktout, req->id); put_stringz(pktout, path); sftp_send(pktout); return req; } struct fxp_handle *fxp_opendir_recv(struct sftp_packet *pktin, struct sftp_request *req) { sfree(req); if (pktin->type == SSH_FXP_HANDLE) { return fxp_got_handle(pktin); } else { fxp_got_status(pktin); sftp_pkt_free(pktin); return NULL; } } /* * Close a file/dir. */ struct sftp_request *fxp_close_send(struct fxp_handle *handle) { struct sftp_request *req = sftp_alloc_request(); struct sftp_packet *pktout; pktout = sftp_pkt_init(SSH_FXP_CLOSE); put_uint32(pktout, req->id); put_string(pktout, handle->hstring, handle->hlen); sftp_send(pktout); sfree(handle->hstring); sfree(handle); return req; } bool fxp_close_recv(struct sftp_packet *pktin, struct sftp_request *req) { sfree(req); fxp_got_status(pktin); sftp_pkt_free(pktin); return fxp_errtype == SSH_FX_OK; } struct sftp_request *fxp_mkdir_send(const char *path, const struct fxp_attrs *attrs) { struct sftp_request *req = sftp_alloc_request(); struct sftp_packet *pktout; pktout = sftp_pkt_init(SSH_FXP_MKDIR); put_uint32(pktout, req->id); put_stringz(pktout, path); put_fxp_attrs(pktout, attrs ? *attrs : no_attrs); sftp_send(pktout); return req; } bool fxp_mkdir_recv(struct sftp_packet *pktin, struct sftp_request *req) { int id; sfree(req); id = fxp_got_status(pktin); sftp_pkt_free(pktin); return id == 1; } struct sftp_request *fxp_rmdir_send(const char *path) { struct sftp_request *req = sftp_alloc_request(); struct sftp_packet *pktout; pktout = sftp_pkt_init(SSH_FXP_RMDIR); put_uint32(pktout, req->id); put_stringz(pktout, path); sftp_send(pktout); return req; } bool fxp_rmdir_recv(struct sftp_packet *pktin, struct sftp_request *req) { int id; sfree(req); id = fxp_got_status(pktin); sftp_pkt_free(pktin); return id == 1; } struct sftp_request *fxp_remove_send(const char *fname) { struct sftp_request *req = sftp_alloc_request(); struct sftp_packet *pktout; pktout = sftp_pkt_init(SSH_FXP_REMOVE); put_uint32(pktout, req->id); put_stringz(pktout, fname); sftp_send(pktout); return req; } bool fxp_remove_recv(struct sftp_packet *pktin, struct sftp_request *req) { int id; sfree(req); id = fxp_got_status(pktin); sftp_pkt_free(pktin); return id == 1; } struct sftp_request *fxp_rename_send(const char *srcfname, const char *dstfname) { struct sftp_request *req = sftp_alloc_request(); struct sftp_packet *pktout; pktout = sftp_pkt_init(SSH_FXP_RENAME); put_uint32(pktout, req->id); put_stringz(pktout, srcfname); put_stringz(pktout, dstfname); sftp_send(pktout); return req; } bool fxp_rename_recv(struct sftp_packet *pktin, struct sftp_request *req) { int id; sfree(req); id = fxp_got_status(pktin); sftp_pkt_free(pktin); return id == 1; } /* * Retrieve the attributes of a file. We have fxp_stat which works * on filenames, and fxp_fstat which works on open file handles. */ struct sftp_request *fxp_stat_send(const char *fname) { struct sftp_request *req = sftp_alloc_request(); struct sftp_packet *pktout; pktout = sftp_pkt_init(SSH_FXP_STAT); put_uint32(pktout, req->id); put_stringz(pktout, fname); sftp_send(pktout); return req; } static bool fxp_got_attrs(struct sftp_packet *pktin, struct fxp_attrs *attrs) { get_fxp_attrs(pktin, attrs); if (get_err(pktin)) { fxp_internal_error("malformed SSH_FXP_ATTRS packet"); sftp_pkt_free(pktin); return false; } sftp_pkt_free(pktin); return true; } bool fxp_stat_recv(struct sftp_packet *pktin, struct sftp_request *req, struct fxp_attrs *attrs) { sfree(req); if (pktin->type == SSH_FXP_ATTRS) { return fxp_got_attrs(pktin, attrs); } else { fxp_got_status(pktin); sftp_pkt_free(pktin); return false; } } struct sftp_request *fxp_fstat_send(struct fxp_handle *handle) { struct sftp_request *req = sftp_alloc_request(); struct sftp_packet *pktout; pktout = sftp_pkt_init(SSH_FXP_FSTAT); put_uint32(pktout, req->id); put_string(pktout, handle->hstring, handle->hlen); sftp_send(pktout); return req; } bool fxp_fstat_recv(struct sftp_packet *pktin, struct sftp_request *req, struct fxp_attrs *attrs) { sfree(req); if (pktin->type == SSH_FXP_ATTRS) { return fxp_got_attrs(pktin, attrs); } else { fxp_got_status(pktin); sftp_pkt_free(pktin); return false; } } /* * Set the attributes of a file. */ struct sftp_request *fxp_setstat_send(const char *fname, struct fxp_attrs attrs) { struct sftp_request *req = sftp_alloc_request(); struct sftp_packet *pktout; pktout = sftp_pkt_init(SSH_FXP_SETSTAT); put_uint32(pktout, req->id); put_stringz(pktout, fname); put_fxp_attrs(pktout, attrs); sftp_send(pktout); return req; } bool fxp_setstat_recv(struct sftp_packet *pktin, struct sftp_request *req) { int id; sfree(req); id = fxp_got_status(pktin); sftp_pkt_free(pktin); return id == 1; } struct sftp_request *fxp_fsetstat_send(struct fxp_handle *handle, struct fxp_attrs attrs) { struct sftp_request *req = sftp_alloc_request(); struct sftp_packet *pktout; pktout = sftp_pkt_init(SSH_FXP_FSETSTAT); put_uint32(pktout, req->id); put_string(pktout, handle->hstring, handle->hlen); put_fxp_attrs(pktout, attrs); sftp_send(pktout); return req; } bool fxp_fsetstat_recv(struct sftp_packet *pktin, struct sftp_request *req) { int id; sfree(req); id = fxp_got_status(pktin); sftp_pkt_free(pktin); return id == 1; } /* * Read from a file. Returns the number of bytes read, or -1 on an * error, or possibly 0 if EOF. (I'm not entirely sure whether it * will return 0 on EOF, or return -1 and store SSH_FX_EOF in the * error indicator. It might even depend on the SFTP server.) */ struct sftp_request *fxp_read_send(struct fxp_handle *handle, uint64_t offset, int len) { struct sftp_request *req = sftp_alloc_request(); struct sftp_packet *pktout; pktout = sftp_pkt_init(SSH_FXP_READ); put_uint32(pktout, req->id); put_string(pktout, handle->hstring, handle->hlen); put_uint64(pktout, offset); put_uint32(pktout, len); sftp_send(pktout); return req; } int fxp_read_recv(struct sftp_packet *pktin, struct sftp_request *req, char *buffer, int len) { sfree(req); if (pktin->type == SSH_FXP_DATA) { ptrlen data; data = get_string(pktin); if (get_err(pktin)) { fxp_internal_error("READ returned malformed SSH_FXP_DATA packet"); sftp_pkt_free(pktin); return -1; } if (data.len > len) { fxp_internal_error("READ returned more bytes than requested"); sftp_pkt_free(pktin); return -1; } memcpy(buffer, data.ptr, data.len); sftp_pkt_free(pktin); return data.len; } else { fxp_got_status(pktin); sftp_pkt_free(pktin); return -1; } } /* * Read from a directory. */ struct sftp_request *fxp_readdir_send(struct fxp_handle *handle) { struct sftp_request *req = sftp_alloc_request(); struct sftp_packet *pktout; pktout = sftp_pkt_init(SSH_FXP_READDIR); put_uint32(pktout, req->id); put_string(pktout, handle->hstring, handle->hlen); sftp_send(pktout); return req; } struct fxp_names *fxp_readdir_recv(struct sftp_packet *pktin, struct sftp_request *req) { sfree(req); if (pktin->type == SSH_FXP_NAME) { struct fxp_names *ret; unsigned long i; i = get_uint32(pktin); /* * Sanity-check the number of names. Minimum is obviously * zero. Maximum is the remaining space in the packet * divided by the very minimum length of a name, which is * 12 bytes (4 for an empty filename, 4 for an empty * longname, 4 for a set of attribute flags indicating that * no other attributes are supplied). */ if (get_err(pktin) || i > get_avail(pktin) / 12) { fxp_internal_error("malformed FXP_NAME packet"); sftp_pkt_free(pktin); return NULL; } /* * Ensure the implicit multiplication in the snewn() call * doesn't suffer integer overflow and cause us to malloc * too little space. */ if (i > INT_MAX / sizeof(struct fxp_name)) { fxp_internal_error("unreasonably large FXP_NAME packet"); sftp_pkt_free(pktin); return NULL; } ret = snew(struct fxp_names); ret->nnames = i; ret->names = snewn(ret->nnames, struct fxp_name); for (i = 0; i < (unsigned long)ret->nnames; i++) { ret->names[i].filename = mkstr(get_string(pktin)); ret->names[i].longname = mkstr(get_string(pktin)); get_fxp_attrs(pktin, &ret->names[i].attrs); } if (get_err(pktin)) { fxp_internal_error("malformed FXP_NAME packet"); for (i = 0; i < (unsigned long)ret->nnames; i++) { sfree(ret->names[i].filename); sfree(ret->names[i].longname); } sfree(ret->names); sfree(ret); sfree(pktin); return NULL; } sftp_pkt_free(pktin); return ret; } else { fxp_got_status(pktin); sftp_pkt_free(pktin); return NULL; } } /* * Write to a file. Returns 0 on error, 1 on OK. */ struct sftp_request *fxp_write_send(struct fxp_handle *handle, void *buffer, uint64_t offset, int len) { struct sftp_request *req = sftp_alloc_request(); struct sftp_packet *pktout; pktout = sftp_pkt_init(SSH_FXP_WRITE); put_uint32(pktout, req->id); put_string(pktout, handle->hstring, handle->hlen); put_uint64(pktout, offset); put_string(pktout, buffer, len); sftp_send(pktout); return req; } bool fxp_write_recv(struct sftp_packet *pktin, struct sftp_request *req) { sfree(req); fxp_got_status(pktin); sftp_pkt_free(pktin); return fxp_errtype == SSH_FX_OK; } /* * Free up an fxp_names structure. */ void fxp_free_names(struct fxp_names *names) { int i; for (i = 0; i < names->nnames; i++) { sfree(names->names[i].filename); sfree(names->names[i].longname); } sfree(names->names); sfree(names); } /* * Duplicate an fxp_name structure. */ struct fxp_name *fxp_dup_name(struct fxp_name *name) { struct fxp_name *ret; ret = snew(struct fxp_name); ret->filename = dupstr(name->filename); ret->longname = dupstr(name->longname); ret->attrs = name->attrs; /* structure copy */ return ret; } /* * Free up an fxp_name structure. */ void fxp_free_name(struct fxp_name *name) { sfree(name->filename); sfree(name->longname); sfree(name); } /* * Store user data in an sftp_request structure. */ void *fxp_get_userdata(struct sftp_request *req) { return req->userdata; } void fxp_set_userdata(struct sftp_request *req, void *data) { req->userdata = data; } /* * A wrapper to go round fxp_read_* and fxp_write_*, which manages * the queueing of multiple read/write requests. */ struct req { char *buffer; int len, retlen, complete; uint64_t offset; struct req *next, *prev; }; struct fxp_xfer { uint64_t offset, furthestdata, filesize; int req_totalsize, req_maxsize; bool eof, err; struct fxp_handle *fh; struct req *head, *tail; }; static struct fxp_xfer *xfer_init(struct fxp_handle *fh, uint64_t offset) { struct fxp_xfer *xfer = snew(struct fxp_xfer); xfer->fh = fh; xfer->offset = offset; xfer->head = xfer->tail = NULL; xfer->req_totalsize = 0; xfer->req_maxsize = 1048576; xfer->err = false; xfer->filesize = UINT64_MAX; xfer->furthestdata = 0; return xfer; } bool xfer_done(struct fxp_xfer *xfer) { /* * We're finished if we've seen EOF _and_ there are no * outstanding requests. */ return (xfer->eof || xfer->err) && !xfer->head; } void xfer_download_queue(struct fxp_xfer *xfer) { while (xfer->req_totalsize < xfer->req_maxsize && !xfer->eof && !xfer->err) { /* * Queue a new read request. */ struct req *rr; struct sftp_request *req; rr = snew(struct req); rr->offset = xfer->offset; rr->complete = 0; if (xfer->tail) { xfer->tail->next = rr; rr->prev = xfer->tail; } else { xfer->head = rr; rr->prev = NULL; } xfer->tail = rr; rr->next = NULL; rr->len = 32768; rr->buffer = snewn(rr->len, char); sftp_register(req = fxp_read_send(xfer->fh, rr->offset, rr->len)); fxp_set_userdata(req, rr); xfer->offset += rr->len; xfer->req_totalsize += rr->len; #ifdef DEBUG_DOWNLOAD printf("queueing read request %p at %"PRIu64"\n", rr, rr->offset); #endif } } struct fxp_xfer *xfer_download_init(struct fxp_handle *fh, uint64_t offset) { struct fxp_xfer *xfer = xfer_init(fh, offset); 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; rr->retlen = 0; rr->complete = -1; #ifdef DEBUG_DOWNLOAD printf("setting eof\n"); #endif } else if (rr->retlen < 0) { /* some error other than EOF; signal it back to caller */ xfer_set_error(xfer); rr->complete = -1; return -1; } rr->complete = 1; /* * Special case: if we have received fewer bytes than we * actually read, we should do something. For the moment I'll * just throw an ersatz FXP error to signal this; the SFTP * draft I've got says that it can't happen except on special * files, in which case seeking probably has very little * meaning and so queueing an additional read request to fill * up the gap sounds like the wrong answer. I'm not sure what I * should be doing here - if it _was_ a special file, I suspect * I simply shouldn't have been queueing multiple requests in * the first place... */ if (rr->retlen > 0 && xfer->furthestdata < rr->offset) { xfer->furthestdata = rr->offset; #ifdef DEBUG_DOWNLOAD printf("setting furthestdata = %"PRIu64"\n", xfer->furthestdata); #endif } if (rr->retlen < rr->len) { uint64_t filesize = rr->offset + (rr->retlen < 0 ? 0 : rr->retlen); #ifdef DEBUG_DOWNLOAD printf("short block! trying filesize = %"PRIu64"\n", filesize); #endif if (xfer->filesize > filesize) { xfer->filesize = filesize; #ifdef DEBUG_DOWNLOAD printf("actually changing filesize\n"); #endif } } if (xfer->furthestdata > xfer->filesize) { fxp_error_message = "received a short buffer from FXP_READ, but not" " at EOF"; fxp_errtype = -1; xfer_set_error(xfer); return -1; } return 1; } void xfer_set_error(struct fxp_xfer *xfer) { xfer->err = true; } bool xfer_download_data(struct fxp_xfer *xfer, void **buf, int *len) { void *retbuf = NULL; int retlen = 0; /* * Discard anything at the head of the rr queue with complete < * 0; return the first thing with complete > 0. */ while (xfer->head && xfer->head->complete && !retbuf) { struct req *rr = xfer->head; if (rr->complete > 0) { retbuf = rr->buffer; retlen = rr->retlen; #ifdef DEBUG_DOWNLOAD printf("handing back data from read request %p\n", rr); #endif } #ifdef DEBUG_DOWNLOAD else printf("skipping failed read request %p\n", rr); #endif xfer->head = xfer->head->next; if (xfer->head) xfer->head->prev = NULL; else xfer->tail = NULL; xfer->req_totalsize -= rr->len; sfree(rr); } if (retbuf) { *buf = retbuf; *len = retlen; return true; } else return false; } struct fxp_xfer *xfer_upload_init(struct fxp_handle *fh, uint64_t offset) { struct fxp_xfer *xfer = xfer_init(fh, offset); /* * We set `eof' to 1 because this will cause xfer_done() to * return true iff there are no outstanding requests. During an * upload, our caller will be responsible for working out * whether all the data has been sent, so all it needs to know * from us is whether the outstanding requests have been * handled once that's done. */ xfer->eof = true; return xfer; } bool xfer_upload_ready(struct fxp_xfer *xfer) { return sftp_sendbuffer() == 0; } void xfer_upload_data(struct fxp_xfer *xfer, char *buffer, int len) { struct req *rr; struct sftp_request *req; rr = snew(struct req); rr->offset = xfer->offset; rr->complete = 0; if (xfer->tail) { xfer->tail->next = rr; rr->prev = xfer->tail; } else { xfer->head = rr; rr->prev = NULL; } xfer->tail = rr; rr->next = NULL; rr->len = len; rr->buffer = NULL; sftp_register(req = fxp_write_send(xfer->fh, buffer, rr->offset, len)); fxp_set_userdata(req, rr); xfer->offset += rr->len; xfer->req_totalsize += rr->len; #ifdef DEBUG_UPLOAD printf("queueing write request %p at %"PRIu64" [len %d]\n", rr, rr->offset, 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; bool 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 ? 1 : 0); #endif /* * Remove this one from the queue. */ prev = rr->prev; next = rr->next; if (prev) prev->next = next; else xfer->head = next; if (next) next->prev = prev; else xfer->tail = prev; xfer->req_totalsize -= rr->len; sfree(rr); if (!ret) return -1; return 1; } void xfer_cleanup(struct fxp_xfer *xfer) { struct req *rr; while (xfer->head) { rr = xfer->head; xfer->head = xfer->head->next; sfree(rr->buffer); sfree(rr); } sfree(xfer); } putty-0.76/sftp.h0000644000175000017500000005111314072266311010704 00000000000000/* * sftp.h: definitions for SFTP and the sftp.c routines. */ #include "defs.h" #define SSH_FXP_INIT 1 /* 0x1 */ #define SSH_FXP_VERSION 2 /* 0x2 */ #define SSH_FXP_OPEN 3 /* 0x3 */ #define SSH_FXP_CLOSE 4 /* 0x4 */ #define SSH_FXP_READ 5 /* 0x5 */ #define SSH_FXP_WRITE 6 /* 0x6 */ #define SSH_FXP_LSTAT 7 /* 0x7 */ #define SSH_FXP_FSTAT 8 /* 0x8 */ #define SSH_FXP_SETSTAT 9 /* 0x9 */ #define SSH_FXP_FSETSTAT 10 /* 0xa */ #define SSH_FXP_OPENDIR 11 /* 0xb */ #define SSH_FXP_READDIR 12 /* 0xc */ #define SSH_FXP_REMOVE 13 /* 0xd */ #define SSH_FXP_MKDIR 14 /* 0xe */ #define SSH_FXP_RMDIR 15 /* 0xf */ #define SSH_FXP_REALPATH 16 /* 0x10 */ #define SSH_FXP_STAT 17 /* 0x11 */ #define SSH_FXP_RENAME 18 /* 0x12 */ #define SSH_FXP_STATUS 101 /* 0x65 */ #define SSH_FXP_HANDLE 102 /* 0x66 */ #define SSH_FXP_DATA 103 /* 0x67 */ #define SSH_FXP_NAME 104 /* 0x68 */ #define SSH_FXP_ATTRS 105 /* 0x69 */ #define SSH_FXP_EXTENDED 200 /* 0xc8 */ #define SSH_FXP_EXTENDED_REPLY 201 /* 0xc9 */ #define SSH_FX_OK 0 #define SSH_FX_EOF 1 #define SSH_FX_NO_SUCH_FILE 2 #define SSH_FX_PERMISSION_DENIED 3 #define SSH_FX_FAILURE 4 #define SSH_FX_BAD_MESSAGE 5 #define SSH_FX_NO_CONNECTION 6 #define SSH_FX_CONNECTION_LOST 7 #define SSH_FX_OP_UNSUPPORTED 8 #define SSH_FILEXFER_ATTR_SIZE 0x00000001 #define SSH_FILEXFER_ATTR_UIDGID 0x00000002 #define SSH_FILEXFER_ATTR_PERMISSIONS 0x00000004 #define SSH_FILEXFER_ATTR_ACMODTIME 0x00000008 #define SSH_FILEXFER_ATTR_EXTENDED 0x80000000 #define SSH_FXF_READ 0x00000001 #define SSH_FXF_WRITE 0x00000002 #define SSH_FXF_APPEND 0x00000004 #define SSH_FXF_CREAT 0x00000008 #define SSH_FXF_TRUNC 0x00000010 #define SSH_FXF_EXCL 0x00000020 #define SFTP_PROTO_VERSION 3 #define PERMS_DIRECTORY 040000 /* * External references. The sftp client module sftp.c expects to be * able to get at these functions. * * sftp_recvdata must never return less than len. It either blocks * until len is available and then returns true, or it returns false * for failure. * * sftp_senddata returns true on success, false on failure. * * sftp_sendbuffer returns the size of the backlog of data in the * transmit queue. */ bool sftp_senddata(const char *data, size_t len); size_t sftp_sendbuffer(void); bool sftp_recvdata(char *data, size_t len); /* * Free sftp_requests */ void sftp_cleanup_request(void); struct fxp_attrs { unsigned long flags; uint64_t size; unsigned long uid; unsigned long gid; unsigned long permissions; unsigned long atime; unsigned long mtime; }; extern const struct fxp_attrs no_attrs; /* * 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, defaultperms) \ ((attrs).flags & SSH_FILEXFER_ATTR_PERMISSIONS ? \ (attrs).permissions : defaultperms) struct fxp_handle { char *hstring; int hlen; }; struct fxp_name { char *filename, *longname; struct fxp_attrs attrs; }; struct fxp_names { int nnames; struct fxp_name *names; }; struct sftp_request; /* * Packet-manipulation functions. */ struct sftp_packet { char *data; size_t length, maxlen, savedpos; int type; BinarySink_IMPLEMENTATION; BinarySource_IMPLEMENTATION; }; /* When sending a packet, create it with sftp_pkt_init, then add * things to it by treating it as a BinarySink. When it's done, call * sftp_send_prepare, and then pkt->data and pkt->length describe its * wire format. */ struct sftp_packet *sftp_pkt_init(int pkt_type); void sftp_send_prepare(struct sftp_packet *pkt); /* When receiving a packet, create it with sftp_recv_prepare once you * decode its length from the first 4 bytes of wire data. Then write * that many bytes into pkt->data, and call sftp_recv_finish to set up * the type code and BinarySource. */ struct sftp_packet *sftp_recv_prepare(unsigned length); bool sftp_recv_finish(struct sftp_packet *pkt); /* Either kind of packet can be freed afterwards with sftp_pkt_free. */ void sftp_pkt_free(struct sftp_packet *pkt); void BinarySink_put_fxp_attrs(BinarySink *bs, struct fxp_attrs attrs); bool BinarySource_get_fxp_attrs(BinarySource *src, struct fxp_attrs *attrs); #define put_fxp_attrs(bs, attrs) \ BinarySink_put_fxp_attrs(BinarySink_UPCAST(bs), attrs) #define get_fxp_attrs(bs, attrs) \ BinarySource_get_fxp_attrs(BinarySource_UPCAST(bs), attrs) /* * Error handling. */ const char *fxp_error(void); int fxp_error_type(void); /* * Perform exchange of init/version packets. Return false on failure. */ bool fxp_init(void); /* * Canonify a pathname. Concatenate the two given path elements * with a separating slash, unless the second is NULL. */ struct sftp_request *fxp_realpath_send(const 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(const char *path, int type, const 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(const char *path); struct fxp_handle *fxp_opendir_recv(struct sftp_packet *pktin, struct sftp_request *req); /* * Close a file/dir. Returns true on success, false on error. */ struct sftp_request *fxp_close_send(struct fxp_handle *handle); bool fxp_close_recv(struct sftp_packet *pktin, struct sftp_request *req); /* * Make a directory. */ struct sftp_request *fxp_mkdir_send(const char *path, const struct fxp_attrs *attrs); bool fxp_mkdir_recv(struct sftp_packet *pktin, struct sftp_request *req); /* * Remove a directory. */ struct sftp_request *fxp_rmdir_send(const char *path); bool fxp_rmdir_recv(struct sftp_packet *pktin, struct sftp_request *req); /* * Remove a file. */ struct sftp_request *fxp_remove_send(const char *fname); bool fxp_remove_recv(struct sftp_packet *pktin, struct sftp_request *req); /* * Rename a file. */ struct sftp_request *fxp_rename_send(const char *srcfname, const char *dstfname); bool fxp_rename_recv(struct sftp_packet *pktin, struct sftp_request *req); /* * Return file attributes. */ struct sftp_request *fxp_stat_send(const char *fname); bool fxp_stat_recv(struct sftp_packet *pktin, struct sftp_request *req, struct fxp_attrs *attrs); struct sftp_request *fxp_fstat_send(struct fxp_handle *handle); bool fxp_fstat_recv(struct sftp_packet *pktin, struct sftp_request *req, struct fxp_attrs *attrs); /* * Set file attributes. */ struct sftp_request *fxp_setstat_send(const char *fname, struct fxp_attrs attrs); bool fxp_setstat_recv(struct sftp_packet *pktin, struct sftp_request *req); struct sftp_request *fxp_fsetstat_send(struct fxp_handle *handle, struct fxp_attrs attrs); bool fxp_fsetstat_recv(struct sftp_packet *pktin, struct sftp_request *req); /* * Read from a file. */ struct sftp_request *fxp_read_send(struct fxp_handle *handle, uint64_t offset, int len); int fxp_read_recv(struct sftp_packet *pktin, struct sftp_request *req, char *buffer, int len); /* * Write to a file. */ struct sftp_request *fxp_write_send(struct fxp_handle *handle, void *buffer, uint64_t offset, int len); bool fxp_write_recv(struct sftp_packet *pktin, struct sftp_request *req); /* * Read from a directory. */ struct sftp_request *fxp_readdir_send(struct fxp_handle *handle); struct fxp_names *fxp_readdir_recv(struct sftp_packet *pktin, struct sftp_request *req); /* * Free up an fxp_names structure. */ void fxp_free_names(struct fxp_names *names); /* * Duplicate and free fxp_name structures. */ struct fxp_name *fxp_dup_name(struct fxp_name *name); void fxp_free_name(struct fxp_name *name); /* * Store user data in an sftp_request structure. */ void *fxp_get_userdata(struct sftp_request *req); void fxp_set_userdata(struct sftp_request *req, void *data); /* * These functions might well be temporary placeholders to be * replaced with more useful similar functions later. They form the * main dispatch loop for processing incoming SFTP responses. */ void sftp_register(struct sftp_request *req); struct sftp_request *sftp_find_request(struct sftp_packet *pktin); struct sftp_packet *sftp_recv(void); /* * A wrapper to go round fxp_read_* and fxp_write_*, which manages * the queueing of multiple read/write requests. */ struct fxp_xfer; struct fxp_xfer *xfer_download_init(struct fxp_handle *fh, uint64_t offset); void xfer_download_queue(struct fxp_xfer *xfer); int xfer_download_gotpkt(struct fxp_xfer *xfer, struct sftp_packet *pktin); bool xfer_download_data(struct fxp_xfer *xfer, void **buf, int *len); struct fxp_xfer *xfer_upload_init(struct fxp_handle *fh, uint64_t offset); bool xfer_upload_ready(struct fxp_xfer *xfer); void xfer_upload_data(struct fxp_xfer *xfer, char *buffer, int len); int xfer_upload_gotpkt(struct fxp_xfer *xfer, struct sftp_packet *pktin); bool xfer_done(struct fxp_xfer *xfer); void xfer_set_error(struct fxp_xfer *xfer); void xfer_cleanup(struct fxp_xfer *xfer); /* * Vtable for the platform-specific filesystem implementation that * answers requests in an SFTP server. */ typedef struct SftpReplyBuilder SftpReplyBuilder; struct SftpServer { const SftpServerVtable *vt; }; struct SftpServerVtable { SftpServer *(*new)(const SftpServerVtable *vt); void (*free)(SftpServer *srv); /* * Handle actual filesystem requests. * * Each of these functions replies by calling an appropiate * sftp_reply_foo() function on the given reply packet. */ /* Should call fxp_reply_error or fxp_reply_simple_name */ void (*realpath)(SftpServer *srv, SftpReplyBuilder *reply, ptrlen path); /* Should call fxp_reply_error or fxp_reply_handle */ void (*open)(SftpServer *srv, SftpReplyBuilder *reply, ptrlen path, unsigned flags, struct fxp_attrs attrs); /* Should call fxp_reply_error or fxp_reply_handle */ void (*opendir)(SftpServer *srv, SftpReplyBuilder *reply, ptrlen path); /* Should call fxp_reply_error or fxp_reply_ok */ void (*close)(SftpServer *srv, SftpReplyBuilder *reply, ptrlen handle); /* Should call fxp_reply_error or fxp_reply_ok */ void (*mkdir)(SftpServer *srv, SftpReplyBuilder *reply, ptrlen path, struct fxp_attrs attrs); /* Should call fxp_reply_error or fxp_reply_ok */ void (*rmdir)(SftpServer *srv, SftpReplyBuilder *reply, ptrlen path); /* Should call fxp_reply_error or fxp_reply_ok */ void (*remove)(SftpServer *srv, SftpReplyBuilder *reply, ptrlen path); /* Should call fxp_reply_error or fxp_reply_ok */ void (*rename)(SftpServer *srv, SftpReplyBuilder *reply, ptrlen srcpath, ptrlen dstpath); /* Should call fxp_reply_error or fxp_reply_attrs */ void (*stat)(SftpServer *srv, SftpReplyBuilder *reply, ptrlen path, bool follow_symlinks); /* Should call fxp_reply_error or fxp_reply_attrs */ void (*fstat)(SftpServer *srv, SftpReplyBuilder *reply, ptrlen handle); /* Should call fxp_reply_error or fxp_reply_ok */ void (*setstat)(SftpServer *srv, SftpReplyBuilder *reply, ptrlen path, struct fxp_attrs attrs); /* Should call fxp_reply_error or fxp_reply_ok */ void (*fsetstat)(SftpServer *srv, SftpReplyBuilder *reply, ptrlen handle, struct fxp_attrs attrs); /* Should call fxp_reply_error or fxp_reply_data */ void (*read)(SftpServer *srv, SftpReplyBuilder *reply, ptrlen handle, uint64_t offset, unsigned length); /* Should call fxp_reply_error or fxp_reply_ok */ void (*write)(SftpServer *srv, SftpReplyBuilder *reply, ptrlen handle, uint64_t offset, ptrlen data); /* Should call fxp_reply_error, or fxp_reply_name_count once and * then fxp_reply_full_name that many times */ void (*readdir)(SftpServer *srv, SftpReplyBuilder *reply, ptrlen handle, int max_entries, bool omit_longname); }; static inline SftpServer *sftpsrv_new(const SftpServerVtable *vt) { return vt->new(vt); } static inline void sftpsrv_free(SftpServer *srv) { srv->vt->free(srv); } static inline void sftpsrv_realpath(SftpServer *srv, SftpReplyBuilder *reply, ptrlen path) { srv->vt->realpath(srv, reply, path); } static inline void sftpsrv_open( SftpServer *srv, SftpReplyBuilder *reply, ptrlen path, unsigned flags, struct fxp_attrs attrs) { srv->vt->open(srv, reply, path, flags, attrs); } static inline void sftpsrv_opendir( SftpServer *srv, SftpReplyBuilder *reply, ptrlen path) { srv->vt->opendir(srv, reply, path); } static inline void sftpsrv_close( SftpServer *srv, SftpReplyBuilder *reply, ptrlen handle) { srv->vt->close(srv, reply, handle); } static inline void sftpsrv_mkdir(SftpServer *srv, SftpReplyBuilder *reply, ptrlen path, struct fxp_attrs attrs) { srv->vt->mkdir(srv, reply, path, attrs); } static inline void sftpsrv_rmdir( SftpServer *srv, SftpReplyBuilder *reply, ptrlen path) { srv->vt->rmdir(srv, reply, path); } static inline void sftpsrv_remove( SftpServer *srv, SftpReplyBuilder *reply, ptrlen path) { srv->vt->remove(srv, reply, path); } static inline void sftpsrv_rename(SftpServer *srv, SftpReplyBuilder *reply, ptrlen srcpath, ptrlen dstpath) { srv->vt->rename(srv, reply, srcpath, dstpath); } static inline void sftpsrv_stat( SftpServer *srv, SftpReplyBuilder *reply, ptrlen path, bool follow) { srv->vt->stat(srv, reply, path, follow); } static inline void sftpsrv_fstat( SftpServer *srv, SftpReplyBuilder *reply, ptrlen handle) { srv->vt->fstat(srv, reply, handle); } static inline void sftpsrv_setstat(SftpServer *srv, SftpReplyBuilder *reply, ptrlen path, struct fxp_attrs attrs) { srv->vt->setstat(srv, reply, path, attrs); } static inline void sftpsrv_fsetstat(SftpServer *srv, SftpReplyBuilder *reply, ptrlen handle, struct fxp_attrs attrs) { srv->vt->fsetstat(srv, reply, handle, attrs); } static inline void sftpsrv_read( SftpServer *srv, SftpReplyBuilder *reply, ptrlen handle, uint64_t offset, unsigned length) { srv->vt->read(srv, reply, handle, offset, length); } static inline void sftpsrv_write(SftpServer *srv, SftpReplyBuilder *reply, ptrlen handle, uint64_t offset, ptrlen data) { srv->vt->write(srv, reply, handle, offset, data); } static inline void sftpsrv_readdir( SftpServer *srv, SftpReplyBuilder *reply, ptrlen handle, int max_entries, bool omit_longname) { srv->vt->readdir(srv, reply, handle, max_entries, omit_longname); } typedef struct SftpReplyBuilderVtable SftpReplyBuilderVtable; struct SftpReplyBuilder { const SftpReplyBuilderVtable *vt; }; struct SftpReplyBuilderVtable { void (*reply_ok)(SftpReplyBuilder *reply); void (*reply_error)(SftpReplyBuilder *reply, unsigned code, const char *msg); void (*reply_simple_name)(SftpReplyBuilder *reply, ptrlen name); void (*reply_name_count)(SftpReplyBuilder *reply, unsigned count); void (*reply_full_name)(SftpReplyBuilder *reply, ptrlen name, ptrlen longname, struct fxp_attrs attrs); void (*reply_handle)(SftpReplyBuilder *reply, ptrlen handle); void (*reply_data)(SftpReplyBuilder *reply, ptrlen data); void (*reply_attrs)(SftpReplyBuilder *reply, struct fxp_attrs attrs); }; static inline void fxp_reply_ok(SftpReplyBuilder *reply) { reply->vt->reply_ok(reply); } static inline void fxp_reply_error(SftpReplyBuilder *reply, unsigned code, const char *msg) { reply->vt->reply_error(reply, code, msg); } static inline void fxp_reply_simple_name(SftpReplyBuilder *reply, ptrlen name) { reply->vt->reply_simple_name(reply, name); } static inline void fxp_reply_name_count( SftpReplyBuilder *reply, unsigned count) { reply->vt->reply_name_count(reply, count); } static inline void fxp_reply_full_name(SftpReplyBuilder *reply, ptrlen name, ptrlen longname, struct fxp_attrs attrs) { reply->vt->reply_full_name(reply, name, longname, attrs); } static inline void fxp_reply_handle(SftpReplyBuilder *reply, ptrlen handle) { reply->vt->reply_handle(reply, handle); } static inline void fxp_reply_data(SftpReplyBuilder *reply, ptrlen data) { reply->vt->reply_data(reply, data); } static inline void fxp_reply_attrs( SftpReplyBuilder *reply, struct fxp_attrs attrs) { reply->vt->reply_attrs(reply, attrs); } /* * The usual implementation of an SftpReplyBuilder, containing a * 'struct sftp_packet' which is assumed to be already initialised * before one of the above request methods is called. */ extern const struct SftpReplyBuilderVtable DefaultSftpReplyBuilder_vt; typedef struct DefaultSftpReplyBuilder DefaultSftpReplyBuilder; struct DefaultSftpReplyBuilder { SftpReplyBuilder rb; struct sftp_packet *pkt; }; /* * The top-level function that handles an SFTP request, given an * implementation of the above SftpServer abstraction to do the actual * filesystem work. It handles all the marshalling and unmarshalling * of packets, and the copying of request ids into the responses. */ struct sftp_packet *sftp_handle_request( SftpServer *srv, struct sftp_packet *request); /* ---------------------------------------------------------------------- * Not exactly SFTP-related, but here's a system that implements an * old-fashioned SCP server module, given an SftpServer vtable to use * as its underlying filesystem access. */ typedef struct ScpServer ScpServer; typedef struct ScpServerVtable ScpServerVtable; struct ScpServer { const struct ScpServerVtable *vt; }; struct ScpServerVtable { void (*free)(ScpServer *s); size_t (*send)(ScpServer *s, const void *data, size_t length); void (*throttle)(ScpServer *s, bool throttled); void (*eof)(ScpServer *s); }; static inline void scp_free(ScpServer *s) { s->vt->free(s); } static inline size_t scp_send(ScpServer *s, const void *data, size_t length) { return s->vt->send(s, data, length); } static inline void scp_throttle(ScpServer *s, bool throttled) { s->vt->throttle(s, throttled); } static inline void scp_eof(ScpServer *s) { s->vt->eof(s); } /* * Create an ScpServer by calling this function, giving it the command * you received from the SSH client to execute. If that command is * recognised as an scp command, it will construct an ScpServer object * and return it; otherwise, it will return NULL, and you should * execute the command in whatever way you normally would. * * The ScpServer will generate output for the client by writing it to * the provided SshChannel using sshfwd_write; you pass it input using * the send method in its own vtable. */ ScpServer *scp_recognise_exec( SshChannel *sc, const SftpServerVtable *sftpserver_vt, ptrlen command); putty-0.76/sftpcommon.c0000644000175000017500000000735114072266311012115 00000000000000/* * sftpcommon.c: SFTP code shared between client and server. */ #include #include #include #include #include #include "misc.h" #include "sftp.h" static void sftp_pkt_BinarySink_write( BinarySink *bs, const void *data, size_t length) { struct sftp_packet *pkt = BinarySink_DOWNCAST(bs, struct sftp_packet); assert(length <= 0xFFFFFFFFU - pkt->length); sgrowarrayn_nm(pkt->data, pkt->maxlen, pkt->length, length); memcpy(pkt->data + pkt->length, data, length); pkt->length += length; } struct sftp_packet *sftp_pkt_init(int type) { struct sftp_packet *pkt; pkt = snew(struct sftp_packet); pkt->data = NULL; pkt->savedpos = -1; pkt->length = 0; pkt->maxlen = 0; pkt->type = type; BinarySink_INIT(pkt, sftp_pkt_BinarySink_write); put_uint32(pkt, 0); /* length field will be filled in later */ put_byte(pkt, 0); /* so will the type field */ return pkt; } void BinarySink_put_fxp_attrs(BinarySink *bs, struct fxp_attrs attrs) { put_uint32(bs, attrs.flags); if (attrs.flags & SSH_FILEXFER_ATTR_SIZE) put_uint64(bs, attrs.size); if (attrs.flags & SSH_FILEXFER_ATTR_UIDGID) { put_uint32(bs, attrs.uid); put_uint32(bs, attrs.gid); } if (attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) { put_uint32(bs, attrs.permissions); } if (attrs.flags & SSH_FILEXFER_ATTR_ACMODTIME) { put_uint32(bs, attrs.atime); put_uint32(bs, attrs.mtime); } if (attrs.flags & SSH_FILEXFER_ATTR_EXTENDED) { /* * We currently don't support sending any extended * attributes. */ } } const struct fxp_attrs no_attrs = { 0 }; #define put_fxp_attrs(bs, attrs) \ BinarySink_put_fxp_attrs(BinarySink_UPCAST(bs), attrs) bool BinarySource_get_fxp_attrs(BinarySource *src, struct fxp_attrs *attrs) { attrs->flags = get_uint32(src); if (attrs->flags & SSH_FILEXFER_ATTR_SIZE) attrs->size = get_uint64(src); if (attrs->flags & SSH_FILEXFER_ATTR_UIDGID) { attrs->uid = get_uint32(src); attrs->gid = get_uint32(src); } if (attrs->flags & SSH_FILEXFER_ATTR_PERMISSIONS) attrs->permissions = get_uint32(src); if (attrs->flags & SSH_FILEXFER_ATTR_ACMODTIME) { attrs->atime = get_uint32(src); attrs->mtime = get_uint32(src); } if (attrs->flags & SSH_FILEXFER_ATTR_EXTENDED) { unsigned long count = get_uint32(src); while (count--) { if (get_err(src)) { /* Truncated packet. Don't waste time looking for * attributes that aren't there. Caller should spot * the truncation. */ break; } /* * We should try to analyse these, if we ever find one * we recognise. */ get_string(src); get_string(src); } } return true; } void sftp_pkt_free(struct sftp_packet *pkt) { if (pkt->data) sfree(pkt->data); sfree(pkt); } void sftp_send_prepare(struct sftp_packet *pkt) { PUT_32BIT_MSB_FIRST(pkt->data, pkt->length - 4); if (pkt->length >= 5) { /* Rewrite the type code, in case the caller changed its mind * about pkt->type since calling sftp_pkt_init */ pkt->data[4] = pkt->type; } } struct sftp_packet *sftp_recv_prepare(unsigned length) { struct sftp_packet *pkt; pkt = snew(struct sftp_packet); pkt->savedpos = 0; pkt->length = pkt->maxlen = length; pkt->data = snewn(pkt->length, char); return pkt; } bool sftp_recv_finish(struct sftp_packet *pkt) { BinarySource_INIT(pkt, pkt->data, pkt->length); pkt->type = get_byte(pkt); return !get_err(pkt); } putty-0.76/sftpserver.c0000644000175000017500000001735414072266311012137 00000000000000/* * Implement the centralised parts of the server side of SFTP. */ #include #include #include #include "putty.h" #include "ssh.h" #include "sftp.h" struct sftp_packet *sftp_handle_request( SftpServer *srv, struct sftp_packet *req) { struct sftp_packet *reply; unsigned id; uint32_t flags; ptrlen path, dstpath, handle, data; uint64_t offset; unsigned length; struct fxp_attrs attrs; DefaultSftpReplyBuilder dsrb; SftpReplyBuilder *rb; if (req->type == SSH_FXP_INIT) { /* * Special case which doesn't have a request id at the start. */ reply = sftp_pkt_init(SSH_FXP_VERSION); /* * Since we support only the lowest protocol version, we don't * need to take the min of this and the client's version, or * even to bother reading the client version number out of the * input packet. */ put_uint32(reply, SFTP_PROTO_VERSION); return reply; } /* * Centralise the request id handling. We'll overwrite the type * code of the output packet later. */ id = get_uint32(req); reply = sftp_pkt_init(0); put_uint32(reply, id); dsrb.rb.vt = &DefaultSftpReplyBuilder_vt; dsrb.pkt = reply; rb = &dsrb.rb; switch (req->type) { case SSH_FXP_REALPATH: path = get_string(req); if (get_err(req)) goto decode_error; sftpsrv_realpath(srv, rb, path); break; case SSH_FXP_OPEN: path = get_string(req); flags = get_uint32(req); get_fxp_attrs(req, &attrs); if (get_err(req)) goto decode_error; if ((flags & (SSH_FXF_READ|SSH_FXF_WRITE)) == 0) { fxp_reply_error(rb, SSH_FX_BAD_MESSAGE, "open without READ or WRITE flag"); } else if ((flags & (SSH_FXF_CREAT|SSH_FXF_TRUNC)) == SSH_FXF_TRUNC) { fxp_reply_error(rb, SSH_FX_BAD_MESSAGE, "open with TRUNC but not CREAT"); } else if ((flags & (SSH_FXF_CREAT|SSH_FXF_EXCL)) == SSH_FXF_EXCL) { fxp_reply_error(rb, SSH_FX_BAD_MESSAGE, "open with EXCL but not CREAT"); } else { sftpsrv_open(srv, rb, path, flags, attrs); } break; case SSH_FXP_OPENDIR: path = get_string(req); if (get_err(req)) goto decode_error; sftpsrv_opendir(srv, rb, path); break; case SSH_FXP_CLOSE: handle = get_string(req); if (get_err(req)) goto decode_error; sftpsrv_close(srv, rb, handle); break; case SSH_FXP_MKDIR: path = get_string(req); get_fxp_attrs(req, &attrs); if (get_err(req)) goto decode_error; sftpsrv_mkdir(srv, rb, path, attrs); break; case SSH_FXP_RMDIR: path = get_string(req); if (get_err(req)) goto decode_error; sftpsrv_rmdir(srv, rb, path); break; case SSH_FXP_REMOVE: path = get_string(req); if (get_err(req)) goto decode_error; sftpsrv_remove(srv, rb, path); break; case SSH_FXP_RENAME: path = get_string(req); dstpath = get_string(req); if (get_err(req)) goto decode_error; sftpsrv_rename(srv, rb, path, dstpath); break; case SSH_FXP_STAT: path = get_string(req); if (get_err(req)) goto decode_error; sftpsrv_stat(srv, rb, path, true); break; case SSH_FXP_LSTAT: path = get_string(req); if (get_err(req)) goto decode_error; sftpsrv_stat(srv, rb, path, false); break; case SSH_FXP_FSTAT: handle = get_string(req); if (get_err(req)) goto decode_error; sftpsrv_fstat(srv, rb, handle); break; case SSH_FXP_SETSTAT: path = get_string(req); get_fxp_attrs(req, &attrs); if (get_err(req)) goto decode_error; sftpsrv_setstat(srv, rb, path, attrs); break; case SSH_FXP_FSETSTAT: handle = get_string(req); get_fxp_attrs(req, &attrs); if (get_err(req)) goto decode_error; sftpsrv_fsetstat(srv, rb, handle, attrs); break; case SSH_FXP_READ: handle = get_string(req); offset = get_uint64(req); length = get_uint32(req); if (get_err(req)) goto decode_error; sftpsrv_read(srv, rb, handle, offset, length); break; case SSH_FXP_READDIR: handle = get_string(req); if (get_err(req)) goto decode_error; sftpsrv_readdir(srv, rb, handle, INT_MAX, false); break; case SSH_FXP_WRITE: handle = get_string(req); offset = get_uint64(req); data = get_string(req); if (get_err(req)) goto decode_error; sftpsrv_write(srv, rb, handle, offset, data); break; default: if (get_err(req)) goto decode_error; fxp_reply_error(rb, SSH_FX_OP_UNSUPPORTED, "Unrecognised request type"); break; decode_error: fxp_reply_error(rb, SSH_FX_BAD_MESSAGE, "Unable to decode request"); } return reply; } static void default_reply_ok(SftpReplyBuilder *reply) { DefaultSftpReplyBuilder *d = container_of(reply, DefaultSftpReplyBuilder, rb); d->pkt->type = SSH_FXP_STATUS; put_uint32(d->pkt, SSH_FX_OK); put_stringz(d->pkt, ""); } static void default_reply_error( SftpReplyBuilder *reply, unsigned code, const char *msg) { DefaultSftpReplyBuilder *d = container_of(reply, DefaultSftpReplyBuilder, rb); d->pkt->type = SSH_FXP_STATUS; put_uint32(d->pkt, code); put_stringz(d->pkt, msg); } static void default_reply_name_count(SftpReplyBuilder *reply, unsigned count) { DefaultSftpReplyBuilder *d = container_of(reply, DefaultSftpReplyBuilder, rb); d->pkt->type = SSH_FXP_NAME; put_uint32(d->pkt, count); } static void default_reply_full_name(SftpReplyBuilder *reply, ptrlen name, ptrlen longname, struct fxp_attrs attrs) { DefaultSftpReplyBuilder *d = container_of(reply, DefaultSftpReplyBuilder, rb); d->pkt->type = SSH_FXP_NAME; put_stringpl(d->pkt, name); put_stringpl(d->pkt, longname); put_fxp_attrs(d->pkt, attrs); } static void default_reply_simple_name(SftpReplyBuilder *reply, ptrlen name) { fxp_reply_name_count(reply, 1); fxp_reply_full_name(reply, name, PTRLEN_LITERAL(""), no_attrs); } static void default_reply_handle(SftpReplyBuilder *reply, ptrlen handle) { DefaultSftpReplyBuilder *d = container_of(reply, DefaultSftpReplyBuilder, rb); d->pkt->type = SSH_FXP_HANDLE; put_stringpl(d->pkt, handle); } static void default_reply_data(SftpReplyBuilder *reply, ptrlen data) { DefaultSftpReplyBuilder *d = container_of(reply, DefaultSftpReplyBuilder, rb); d->pkt->type = SSH_FXP_DATA; put_stringpl(d->pkt, data); } static void default_reply_attrs( SftpReplyBuilder *reply, struct fxp_attrs attrs) { DefaultSftpReplyBuilder *d = container_of(reply, DefaultSftpReplyBuilder, rb); d->pkt->type = SSH_FXP_ATTRS; put_fxp_attrs(d->pkt, attrs); } const SftpReplyBuilderVtable DefaultSftpReplyBuilder_vt = { .reply_ok = default_reply_ok, .reply_error = default_reply_error, .reply_simple_name = default_reply_simple_name, .reply_name_count = default_reply_name_count, .reply_full_name = default_reply_full_name, .reply_handle = default_reply_handle, .reply_data = default_reply_data, .reply_attrs = default_reply_attrs, }; putty-0.76/sign.sh0000755000175000017500000000275214072266311011063 00000000000000#!/bin/sh # Generate GPG signatures on a PuTTY release/snapshot directory as # delivered by Buildscr. # Usage: sh sign.sh [-r] # e.g. sh sign.sh putty (probably in the build.out directory) # or sh sign.sh -r 0.60 (-r means use the release keys) set -e keyname=38BA7229B7588FD1 preliminary=false while :; do case "$1" in -r) shift keyname=6289A25F4AE8DA82 ;; -p) shift preliminary=true ;; -*) echo "Unknown option '$1'" >&2 exit 1 ;; *) break ;; esac done sign() { # Check for the prior existence of the signature, so we can # re-run this script if it encounters an error part way # through. echo "----- Signing $2 with key '$keyname'" test -f "$3" || \ gpg --load-extension=idea "$1" -u "$keyname" -o "$3" "$2" } cd "$1" echo "===== Signing with key '$keyname'" if $preliminary; then sign --clearsign sha512sums ../sha512sums-preliminary.gpg else for i in putty*src.zip putty*.tar.gz \ w32/*.exe w32/*.zip w32/*.msi \ w64/*.exe w64/*.zip w64/*.msi \ wa32/*.exe wa32/*.zip wa32/*.msi \ wa64/*.exe wa64/*.zip wa64/*.msi \ w32old/*.exe w32old/*.zip; do sign --detach-sign "$i" "$i.gpg" done for i in md5sums sha1sums sha256sums sha512sums; do sign --clearsign "$i" "$i.gpg" done fi putty-0.76/smallprimes.c0000644000175000017500000000210314072266311012246 00000000000000/* * smallprimes.c: implementation of the array of small primes defined * in sshkeygen.h. */ #include #include "ssh.h" #include "sshkeygen.h" /* The real array that stores the primes. It has to be writable in * this module, but outside this module, we only expose the * const-qualified pointer 'smallprimes' so that nobody else can * accidentally overwrite it. */ static unsigned short smallprimes_array[NSMALLPRIMES]; const unsigned short *const smallprimes = smallprimes_array; void init_smallprimes(void) { if (smallprimes_array[0]) return; /* already done */ bool A[65536]; for (size_t i = 2; i < lenof(A); i++) A[i] = true; for (size_t i = 2; i < lenof(A); i++) { if (!A[i]) continue; for (size_t j = 2*i; j < lenof(A); j += i) A[j] = false; } size_t pos = 0; for (size_t i = 2; i < lenof(A); i++) { if (A[i]) { assert(pos < NSMALLPRIMES); smallprimes_array[pos++] = i; } } assert(pos == NSMALLPRIMES); } putty-0.76/ssh.c0000644000175000017500000011014014072266311010514 00000000000000/* * SSH backend. */ #include #include #include #include #include #include #include "putty.h" #include "pageant.h" /* for AGENT_MAX_MSGLEN */ #include "tree234.h" #include "storage.h" #include "marshal.h" #include "ssh.h" #include "sshcr.h" #include "sshbpp.h" #include "sshppl.h" #include "sshchan.h" #ifndef NO_GSSAPI #include "sshgssc.h" #include "sshgss.h" #define MIN_CTXT_LIFETIME 5 /* Avoid rekey with short lifetime (seconds) */ #define GSS_KEX_CAPABLE (1<<0) /* Can do GSS KEX */ #define GSS_CRED_UPDATED (1<<1) /* Cred updated since previous delegation */ #define GSS_CTXT_EXPIRES (1<<2) /* Context expires before next timer */ #define GSS_CTXT_MAYFAIL (1<<3) /* Context may expire during handshake */ #endif struct Ssh { Socket *s; Seat *seat; Conf *conf; struct ssh_version_receiver version_receiver; int remote_bugs; Plug plug; Backend backend; Ldisc *ldisc; LogContext *logctx; /* The last list returned from get_specials. */ SessionSpecial *specials; bool bare_connection; ssh_sharing_state *connshare; bool attempting_connshare; #ifndef NO_GSSAPI struct ssh_connection_shared_gss_state gss_state; #endif char *savedhost; int savedport; char *fullhostname; bool fallback_cmd; int exitcode; int version; int conn_throttle_count; size_t overall_bufsize; bool throttled_all; /* * logically_frozen is true if we're not currently _processing_ * data from the SSH socket (e.g. because a higher layer has asked * us not to due to ssh_throttle_conn). socket_frozen is true if * we're not even _reading_ data from the socket (i.e. it should * always match the value we last passed to sk_set_frozen). * * The two differ in that socket_frozen can also become * temporarily true because of a large backlog in the in_raw * bufchain, to force no further plug_receive events until the BPP * input function has had a chance to run. (Some front ends, like * GTK, can persistently call the network and never get round to * the toplevel callbacks.) If we've stopped reading from the * socket for that reason, we absolutely _do_ want to carry on * processing our input bufchain, because that's the only way * it'll ever get cleared! * * ssh_check_frozen() resets socket_frozen, and should be called * whenever either of logically_frozen and the bufchain size * changes. */ bool logically_frozen, socket_frozen; /* in case we find these out before we have a ConnectionLayer to tell */ int term_width, term_height; bufchain in_raw, out_raw, user_input; bool pending_close; IdempotentCallback ic_out_raw; PacketLogSettings pls; struct DataTransferStats stats; BinaryPacketProtocol *bpp; /* * base_layer identifies the bottommost packet protocol layer, the * one connected directly to the BPP's packet queues. Any * operation that needs to talk to all layers (e.g. free, or * get_specials) will do it by talking to this, which will * recursively propagate it if necessary. */ PacketProtocolLayer *base_layer; /* * The ConnectionLayer vtable from our connection layer. */ ConnectionLayer *cl; /* * A dummy ConnectionLayer that can be used for logging sharing * downstreams that connect before the real one is ready. */ ConnectionLayer cl_dummy; /* * session_started is false until we initialise the main protocol * layers. So it distinguishes between base_layer==NULL meaning * that the SSH protocol hasn't been set up _yet_, and * base_layer==NULL meaning the SSH protocol has run and finished. * It's also used to mark the point where we stop counting proxy * command diagnostics as pre-session-startup. */ bool session_started; Pinger *pinger; char *deferred_abort_message; bool need_random_unref; }; #define ssh_logevent(params) ( \ logevent_and_free((ssh)->logctx, dupprintf params)) static void ssh_shutdown(Ssh *ssh); static void ssh_throttle_all(Ssh *ssh, bool enable, size_t bufsize); static void ssh_bpp_output_raw_data_callback(void *vctx); LogContext *ssh_get_logctx(Ssh *ssh) { return ssh->logctx; } static void ssh_connect_bpp(Ssh *ssh) { ssh->bpp->ssh = ssh; ssh->bpp->in_raw = &ssh->in_raw; ssh->bpp->out_raw = &ssh->out_raw; bufchain_set_callback(ssh->bpp->out_raw, &ssh->ic_out_raw); ssh->bpp->pls = &ssh->pls; ssh->bpp->logctx = ssh->logctx; ssh->bpp->remote_bugs = ssh->remote_bugs; } static void ssh_connect_ppl(Ssh *ssh, PacketProtocolLayer *ppl) { ppl->bpp = ssh->bpp; ppl->user_input = &ssh->user_input; ppl->seat = ssh->seat; ppl->ssh = ssh; ppl->logctx = ssh->logctx; ppl->remote_bugs = ssh->remote_bugs; } static void ssh_got_ssh_version(struct ssh_version_receiver *rcv, int major_version) { Ssh *ssh = container_of(rcv, Ssh, version_receiver); BinaryPacketProtocol *old_bpp; PacketProtocolLayer *connection_layer; ssh->session_started = true; /* * We don't support choosing a major protocol version dynamically, * so this should always be the same value we set up in * connect_to_host(). */ assert(ssh->version == major_version); old_bpp = ssh->bpp; ssh->remote_bugs = ssh_verstring_get_bugs(old_bpp); if (!ssh->bare_connection) { if (ssh->version == 2) { PacketProtocolLayer *userauth_layer, *transport_child_layer; /* * 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. */ bool is_simple = (conf_get_bool(ssh->conf, CONF_ssh_simple) && !ssh->connshare); ssh->bpp = ssh2_bpp_new(ssh->logctx, &ssh->stats, false); ssh_connect_bpp(ssh); #ifndef NO_GSSAPI /* Load and pick the highest GSS library on the preference * list. */ if (!ssh->gss_state.libs) ssh->gss_state.libs = ssh_gss_setup(ssh->conf); ssh->gss_state.lib = NULL; if (ssh->gss_state.libs->nlibraries > 0) { int i, j; for (i = 0; i < ngsslibs; i++) { int want_id = conf_get_int_int(ssh->conf, CONF_ssh_gsslist, i); for (j = 0; j < ssh->gss_state.libs->nlibraries; j++) if (ssh->gss_state.libs->libraries[j].id == want_id) { ssh->gss_state.lib = &ssh->gss_state.libs->libraries[j]; goto got_gsslib; /* double break */ } } got_gsslib: /* * We always expect to have found something in * the above loop: we only came here if there * was at least one viable GSS library, and the * preference list should always mention * everything and only change the order. */ assert(ssh->gss_state.lib); } #endif connection_layer = ssh2_connection_new( ssh, ssh->connshare, is_simple, ssh->conf, ssh_verstring_get_remote(old_bpp), &ssh->cl); ssh_connect_ppl(ssh, connection_layer); if (conf_get_bool(ssh->conf, CONF_ssh_no_userauth)) { userauth_layer = NULL; transport_child_layer = connection_layer; } else { char *username = get_remote_username(ssh->conf); userauth_layer = ssh2_userauth_new( connection_layer, ssh->savedhost, ssh->fullhostname, conf_get_filename(ssh->conf, CONF_keyfile), conf_get_bool(ssh->conf, CONF_ssh_show_banner), conf_get_bool(ssh->conf, CONF_tryagent), conf_get_bool(ssh->conf, CONF_ssh_no_trivial_userauth), username, conf_get_bool(ssh->conf, CONF_change_username), conf_get_bool(ssh->conf, CONF_try_ki_auth), #ifndef NO_GSSAPI conf_get_bool(ssh->conf, CONF_try_gssapi_auth), conf_get_bool(ssh->conf, CONF_try_gssapi_kex), conf_get_bool(ssh->conf, CONF_gssapifwd), &ssh->gss_state #else false, false, false, NULL #endif ); ssh_connect_ppl(ssh, userauth_layer); transport_child_layer = userauth_layer; sfree(username); } ssh->base_layer = ssh2_transport_new( ssh->conf, ssh->savedhost, ssh->savedport, ssh->fullhostname, ssh_verstring_get_local(old_bpp), ssh_verstring_get_remote(old_bpp), #ifndef NO_GSSAPI &ssh->gss_state, #else NULL, #endif &ssh->stats, transport_child_layer, NULL); ssh_connect_ppl(ssh, ssh->base_layer); if (userauth_layer) ssh2_userauth_set_transport_layer(userauth_layer, ssh->base_layer); } else { ssh->bpp = ssh1_bpp_new(ssh->logctx); ssh_connect_bpp(ssh); connection_layer = ssh1_connection_new(ssh, ssh->conf, &ssh->cl); ssh_connect_ppl(ssh, connection_layer); ssh->base_layer = ssh1_login_new( ssh->conf, ssh->savedhost, ssh->savedport, connection_layer); ssh_connect_ppl(ssh, ssh->base_layer); } } else { ssh->bpp = ssh2_bare_bpp_new(ssh->logctx); ssh_connect_bpp(ssh); connection_layer = ssh2_connection_new( ssh, ssh->connshare, false, ssh->conf, ssh_verstring_get_remote(old_bpp), &ssh->cl); ssh_connect_ppl(ssh, connection_layer); ssh->base_layer = connection_layer; } /* Connect the base layer - whichever it is - to the BPP, and set * up its selfptr. */ ssh->base_layer->selfptr = &ssh->base_layer; ssh_ppl_setup_queues(ssh->base_layer, &ssh->bpp->in_pq, &ssh->bpp->out_pq); seat_update_specials_menu(ssh->seat); ssh->pinger = pinger_new(ssh->conf, &ssh->backend); queue_idempotent_callback(&ssh->bpp->ic_in_raw); ssh_ppl_process_queue(ssh->base_layer); /* Pass in the initial terminal size, if we knew it already. */ ssh_terminal_size(ssh->cl, ssh->term_width, ssh->term_height); ssh_bpp_free(old_bpp); } void ssh_check_frozen(Ssh *ssh) { if (!ssh->s) return; bool prev_frozen = ssh->socket_frozen; ssh->socket_frozen = (ssh->logically_frozen || bufchain_size(&ssh->in_raw) > SSH_MAX_BACKLOG); sk_set_frozen(ssh->s, ssh->socket_frozen); if (prev_frozen && !ssh->socket_frozen && ssh->bpp) { /* * If we've just unfrozen, process any SSH connection data * that was stashed in our queue while we were frozen. */ queue_idempotent_callback(&ssh->bpp->ic_in_raw); } } void ssh_conn_processed_data(Ssh *ssh) { ssh_check_frozen(ssh); } static void ssh_bpp_output_raw_data_callback(void *vctx) { Ssh *ssh = (Ssh *)vctx; if (!ssh->s) return; while (bufchain_size(&ssh->out_raw) > 0) { size_t backlog; ptrlen data = bufchain_prefix(&ssh->out_raw); if (ssh->logctx) log_packet(ssh->logctx, PKT_OUTGOING, -1, NULL, data.ptr, data.len, 0, NULL, NULL, 0, NULL); backlog = sk_write(ssh->s, data.ptr, data.len); bufchain_consume(&ssh->out_raw, data.len); if (backlog > SSH_MAX_BACKLOG) { ssh_throttle_all(ssh, true, backlog); return; } } ssh_check_frozen(ssh); if (ssh->pending_close) { sk_close(ssh->s); ssh->s = NULL; } } static void ssh_shutdown_internal(Ssh *ssh) { expire_timer_context(ssh); if (ssh->connshare) { sharestate_free(ssh->connshare); ssh->connshare = NULL; } if (ssh->pinger) { pinger_free(ssh->pinger); ssh->pinger = NULL; } /* * We only need to free the base PPL, which will free the others * (if any) transitively. */ if (ssh->base_layer) { ssh_ppl_free(ssh->base_layer); ssh->base_layer = NULL; } ssh->cl = NULL; } static void ssh_shutdown(Ssh *ssh) { ssh_shutdown_internal(ssh); if (ssh->bpp) { ssh_bpp_free(ssh->bpp); ssh->bpp = NULL; } if (ssh->s) { sk_close(ssh->s); ssh->s = NULL; } bufchain_clear(&ssh->in_raw); bufchain_clear(&ssh->out_raw); bufchain_clear(&ssh->user_input); } static void ssh_initiate_connection_close(Ssh *ssh) { /* Wind up everything above the BPP. */ ssh_shutdown_internal(ssh); /* Force any remaining queued SSH packets through the BPP, and * schedule closing the network socket after they go out. */ ssh_bpp_handle_output(ssh->bpp); ssh->pending_close = true; queue_idempotent_callback(&ssh->ic_out_raw); /* Now we expect the other end to close the connection too in * response, so arrange that we'll receive notification of that * via ssh_remote_eof. */ ssh->bpp->expect_close = true; } #define GET_FORMATTED_MSG \ char *msg; \ va_list ap; \ va_start(ap, fmt); \ msg = dupvprintf(fmt, ap); \ va_end(ap); \ ((void)0) /* eat trailing semicolon */ void ssh_remote_error(Ssh *ssh, const char *fmt, ...) { if (ssh->base_layer || !ssh->session_started) { GET_FORMATTED_MSG; /* Error messages sent by the remote don't count as clean exits */ ssh->exitcode = 128; /* Close the socket immediately, since the server has already * closed its end (or is about to). */ ssh_shutdown(ssh); logevent(ssh->logctx, msg); seat_connection_fatal(ssh->seat, "%s", msg); sfree(msg); } } void ssh_remote_eof(Ssh *ssh, const char *fmt, ...) { if (ssh->base_layer || !ssh->session_started) { GET_FORMATTED_MSG; /* EOF from the remote, if we were expecting it, does count as * a clean exit */ ssh->exitcode = 0; /* Close the socket immediately, since the server has already * closed its end. */ ssh_shutdown(ssh); logevent(ssh->logctx, msg); sfree(msg); seat_notify_remote_exit(ssh->seat); } else { /* This is responding to EOF after we've already seen some * other reason for terminating the session. */ ssh_shutdown(ssh); } } void ssh_proto_error(Ssh *ssh, const char *fmt, ...) { if (ssh->base_layer || !ssh->session_started) { GET_FORMATTED_MSG; ssh->exitcode = 128; ssh_bpp_queue_disconnect(ssh->bpp, msg, SSH2_DISCONNECT_PROTOCOL_ERROR); ssh_initiate_connection_close(ssh); logevent(ssh->logctx, msg); seat_connection_fatal(ssh->seat, "%s", msg); sfree(msg); } } void ssh_sw_abort(Ssh *ssh, const char *fmt, ...) { if (ssh->base_layer || !ssh->session_started) { GET_FORMATTED_MSG; ssh->exitcode = 128; ssh_initiate_connection_close(ssh); logevent(ssh->logctx, msg); seat_connection_fatal(ssh->seat, "%s", msg); sfree(msg); seat_notify_remote_exit(ssh->seat); } } void ssh_user_close(Ssh *ssh, const char *fmt, ...) { if (ssh->base_layer || !ssh->session_started) { GET_FORMATTED_MSG; /* Closing the connection due to user action, even if the * action is the user aborting during authentication prompts, * does count as a clean exit - except that this is also how * we signal ordinary session termination, in which case we * should use the exit status already sent from the main * session (if any). */ if (ssh->exitcode < 0) ssh->exitcode = 0; ssh_initiate_connection_close(ssh); logevent(ssh->logctx, msg); sfree(msg); seat_notify_remote_exit(ssh->seat); } } static void ssh_deferred_abort_callback(void *vctx) { Ssh *ssh = (Ssh *)vctx; char *msg = ssh->deferred_abort_message; ssh->deferred_abort_message = NULL; ssh_sw_abort(ssh, "%s", msg); sfree(msg); } void ssh_sw_abort_deferred(Ssh *ssh, const char *fmt, ...) { if (!ssh->deferred_abort_message) { GET_FORMATTED_MSG; ssh->deferred_abort_message = msg; queue_toplevel_callback(ssh_deferred_abort_callback, ssh); } } static void ssh_socket_log(Plug *plug, PlugLogType type, SockAddr *addr, int port, const char *error_msg, int error_code) { Ssh *ssh = container_of(plug, Ssh, plug); /* * 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. */ if (!ssh->attempting_connshare) backend_socket_log(ssh->seat, ssh->logctx, type, addr, port, error_msg, error_code, ssh->conf, ssh->session_started); } static void ssh_closing(Plug *plug, const char *error_msg, int error_code, bool calling_back) { Ssh *ssh = container_of(plug, Ssh, plug); if (error_msg) { ssh_remote_error(ssh, "%s", error_msg); } else if (ssh->bpp) { ssh->bpp->input_eof = true; queue_idempotent_callback(&ssh->bpp->ic_in_raw); } } static void ssh_receive(Plug *plug, int urgent, const char *data, size_t len) { Ssh *ssh = container_of(plug, Ssh, plug); /* Log raw data, if we're in that mode. */ if (ssh->logctx) log_packet(ssh->logctx, PKT_INCOMING, -1, NULL, data, len, 0, NULL, NULL, 0, NULL); bufchain_add(&ssh->in_raw, data, len); if (!ssh->logically_frozen && ssh->bpp) queue_idempotent_callback(&ssh->bpp->ic_in_raw); ssh_check_frozen(ssh); } static void ssh_sent(Plug *plug, size_t bufsize) { Ssh *ssh = container_of(plug, Ssh, plug); /* * If the send backlog on the SSH socket itself clears, we should * unthrottle the whole world if it was throttled. Also trigger an * extra call to the consumer of the BPP's output, to try to send * some more data off its bufchain. */ if (bufsize < SSH_MAX_BACKLOG) { ssh_throttle_all(ssh, false, bufsize); queue_idempotent_callback(&ssh->ic_out_raw); } } static void ssh_hostport_setup(const char *host, int port, Conf *conf, char **savedhost, int *savedport, char **loghost_ret) { char *loghost = conf_get_str(conf, CONF_loghost); if (loghost_ret) *loghost_ret = loghost; if (*loghost) { char *tmphost; char *colon; tmphost = dupstr(loghost); *savedport = 22; /* default ssh port */ /* * A colon suffix on the hostname string also lets us affect * savedport. (Unless there are multiple colons, in which case * we assume this is an unbracketed IPv6 literal.) */ colon = host_strrchr(tmphost, ':'); if (colon && colon == host_strchr(tmphost, ':')) { *colon++ = '\0'; if (*colon) *savedport = atoi(colon); } *savedhost = host_strduptrim(tmphost); sfree(tmphost); } else { *savedhost = host_strduptrim(host); if (port < 0) port = 22; /* default ssh port */ *savedport = port; } } static bool ssh_test_for_upstream(const char *host, int port, Conf *conf) { char *savedhost; int savedport; bool ret; random_ref(); /* platform may need this to determine share socket name */ ssh_hostport_setup(host, port, conf, &savedhost, &savedport, NULL); ret = ssh_share_test_for_upstream(savedhost, savedport, conf); sfree(savedhost); random_unref(); return ret; } static char *ssh_close_warn_text(Backend *be) { Ssh *ssh = container_of(be, Ssh, backend); if (!ssh->connshare) return NULL; int ndowns = share_ndownstreams(ssh->connshare); if (ndowns == 0) return NULL; char *msg = dupprintf("This will also close %d downstream connection%s.", ndowns, ndowns==1 ? "" : "s"); return msg; } static const PlugVtable Ssh_plugvt = { .log = ssh_socket_log, .closing = ssh_closing, .receive = ssh_receive, .sent = ssh_sent, }; /* * Connect to specified host and port. * Returns an error message, or NULL on success. * Also places the canonical host name into `realhost'. It must be * freed by the caller. */ static char *connect_to_host( Ssh *ssh, const char *host, int port, char **realhost, bool nodelay, bool keepalive) { SockAddr *addr; const char *err; char *loghost; int addressfamily, sshprot; ssh_hostport_setup(host, port, ssh->conf, &ssh->savedhost, &ssh->savedport, &loghost); ssh->plug.vt = &Ssh_plugvt; /* * 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->logctx, &ssh->plug, &ssh->connshare); if (ssh->connshare) ssh_connshare_provide_connlayer(ssh->connshare, &ssh->cl_dummy); ssh->attempting_connshare = false; if (ssh->s != NULL) { /* * We are a downstream. */ ssh->bare_connection = true; ssh->fullhostname = NULL; *realhost = dupstr(host); /* best we can do */ if (seat_verbose(ssh->seat) || seat_interactive(ssh->seat)) { /* In an interactive session, or in verbose mode, announce * in the console window that we're a sharing downstream, * to avoid confusing users as to why this session doesn't * behave in quite the usual way. */ const char *msg = "Reusing a shared connection to this server.\r\n"; seat_stderr_pl(ssh->seat, ptrlen_from_asciz(msg)); } } else { /* * We're not a downstream, so open a normal socket. */ /* * Try to find host. */ addressfamily = conf_get_int(ssh->conf, CONF_addressfamily); addr = name_lookup(host, port, realhost, ssh->conf, addressfamily, ssh->logctx, "SSH connection"); if ((err = sk_addr_error(addr)) != NULL) { sk_addr_free(addr); return dupstr(err); } ssh->fullhostname = dupstr(*realhost); /* save in case of GSSAPI */ ssh->s = new_connection(addr, *realhost, port, false, true, nodelay, keepalive, &ssh->plug, ssh->conf); if ((err = sk_socket_error(ssh->s)) != NULL) { ssh->s = NULL; seat_notify_remote_exit(ssh->seat); return dupstr(err); } } /* * The SSH version number is always fixed (since we no longer support * fallback between versions), so set it now. */ sshprot = conf_get_int(ssh->conf, CONF_sshprot); assert(sshprot == 0 || sshprot == 3); if (sshprot == 0) /* SSH-1 only */ ssh->version = 1; if (sshprot == 3 || ssh->bare_connection) { /* SSH-2 only */ ssh->version = 2; } /* * Set up the initial BPP that will do the version string * exchange, and get it started so that it can send the outgoing * version string early if it wants to. */ ssh->version_receiver.got_ssh_version = ssh_got_ssh_version; ssh->bpp = ssh_verstring_new( ssh->conf, ssh->logctx, ssh->bare_connection, ssh->version == 1 ? "1.5" : "2.0", &ssh->version_receiver, false, "PuTTY"); ssh_connect_bpp(ssh); queue_idempotent_callback(&ssh->bpp->ic_in_raw); /* * loghost, if configured, overrides realhost. */ if (*loghost) { sfree(*realhost); *realhost = dupstr(loghost); } return NULL; } /* * Throttle or unthrottle the SSH connection. */ void ssh_throttle_conn(Ssh *ssh, int adjust) { int old_count = ssh->conn_throttle_count; bool frozen; ssh->conn_throttle_count += adjust; assert(ssh->conn_throttle_count >= 0); if (ssh->conn_throttle_count && !old_count) { frozen = true; } else if (!ssh->conn_throttle_count && old_count) { frozen = false; } else { return; /* don't change current frozen state */ } ssh->logically_frozen = frozen; ssh_check_frozen(ssh); } /* * Throttle or unthrottle _all_ local data streams (for when sends * on the SSH connection itself back up). */ static void ssh_throttle_all(Ssh *ssh, bool enable, size_t bufsize) { if (enable == ssh->throttled_all) return; ssh->throttled_all = enable; ssh->overall_bufsize = bufsize; ssh_throttle_all_channels(ssh->cl, enable); } static void ssh_cache_conf_values(Ssh *ssh) { ssh->pls.omit_passwords = conf_get_bool(ssh->conf, CONF_logomitpass); ssh->pls.omit_data = conf_get_bool(ssh->conf, CONF_logomitdata); } bool ssh_is_bare(Ssh *ssh) { return ssh->backend.vt->protocol == PROT_SSHCONN; } /* Dummy connlayer must provide ssh_sharing_no_more_downstreams, * because it might be called early due to plink -shareexists */ static void dummy_sharing_no_more_downstreams(ConnectionLayer *cl) {} static const ConnectionLayerVtable dummy_connlayer_vtable = { .sharing_no_more_downstreams = dummy_sharing_no_more_downstreams, }; /* * Called to set up the connection. * * Returns an error message, or NULL on success. */ static char *ssh_init(const BackendVtable *vt, Seat *seat, Backend **backend_handle, LogContext *logctx, Conf *conf, const char *host, int port, char **realhost, bool nodelay, bool keepalive) { Ssh *ssh; ssh = snew(Ssh); memset(ssh, 0, sizeof(Ssh)); ssh->conf = conf_copy(conf); ssh_cache_conf_values(ssh); ssh->exitcode = -1; ssh->pls.kctx = SSH2_PKTCTX_NOKEX; ssh->pls.actx = SSH2_PKTCTX_NOAUTH; bufchain_init(&ssh->in_raw); bufchain_init(&ssh->out_raw); bufchain_init(&ssh->user_input); ssh->ic_out_raw.fn = ssh_bpp_output_raw_data_callback; ssh->ic_out_raw.ctx = ssh; ssh->term_width = conf_get_int(ssh->conf, CONF_width); ssh->term_height = conf_get_int(ssh->conf, CONF_height); ssh->backend.vt = vt; *backend_handle = &ssh->backend; ssh->bare_connection = (vt->protocol == PROT_SSHCONN); ssh->seat = seat; ssh->cl_dummy.vt = &dummy_connlayer_vtable; ssh->cl_dummy.logctx = ssh->logctx = logctx; random_ref(); /* do this now - may be needed by sharing setup code */ ssh->need_random_unref = true; char *conn_err = connect_to_host( ssh, host, port, realhost, nodelay, keepalive); if (conn_err) { /* Call random_unref now instead of waiting until the caller * frees this useless Ssh object, in case the caller is * impatient and just exits without bothering, in which case * the random seed won't be re-saved. */ ssh->need_random_unref = false; random_unref(); return conn_err; } return NULL; } static void ssh_free(Backend *be) { Ssh *ssh = container_of(be, Ssh, backend); bool need_random_unref; ssh_shutdown(ssh); conf_free(ssh->conf); if (ssh->connshare) sharestate_free(ssh->connshare); sfree(ssh->savedhost); sfree(ssh->fullhostname); sfree(ssh->specials); #ifndef NO_GSSAPI if (ssh->gss_state.srv_name) ssh->gss_state.lib->release_name( ssh->gss_state.lib, &ssh->gss_state.srv_name); if (ssh->gss_state.ctx != NULL) ssh->gss_state.lib->release_cred( ssh->gss_state.lib, &ssh->gss_state.ctx); if (ssh->gss_state.libs) ssh_gss_cleanup(ssh->gss_state.libs); #endif sfree(ssh->deferred_abort_message); delete_callbacks_for_context(ssh); /* likely to catch ic_out_raw */ need_random_unref = ssh->need_random_unref; sfree(ssh); if (need_random_unref) random_unref(); } /* * Reconfigure the SSH backend. */ static void ssh_reconfig(Backend *be, Conf *conf) { Ssh *ssh = container_of(be, Ssh, backend); if (ssh->pinger) pinger_reconfig(ssh->pinger, ssh->conf, conf); ssh_ppl_reconfigure(ssh->base_layer, conf); conf_free(ssh->conf); ssh->conf = conf_copy(conf); ssh_cache_conf_values(ssh); } /* * Called to send data down the SSH connection. */ static size_t ssh_send(Backend *be, const char *buf, size_t len) { Ssh *ssh = container_of(be, Ssh, backend); if (ssh == NULL || ssh->s == NULL) return 0; bufchain_add(&ssh->user_input, buf, len); if (ssh->base_layer) ssh_ppl_got_user_input(ssh->base_layer); return backend_sendbuffer(&ssh->backend); } /* * Called to query the current amount of buffered stdin data. */ static size_t ssh_sendbuffer(Backend *be) { Ssh *ssh = container_of(be, Ssh, backend); size_t backlog; if (!ssh || !ssh->s || !ssh->cl) return 0; backlog = ssh_stdin_backlog(ssh->cl); if (ssh->base_layer) backlog += ssh_ppl_queued_data_size(ssh->base_layer); /* * If the SSH socket itself has backed up, add the total backup * size on that to any individual buffer on the stdin channel. */ if (ssh->throttled_all) backlog += ssh->overall_bufsize; return backlog; } /* * Called to set the size of the window from SSH's POV. */ static void ssh_size(Backend *be, int width, int height) { Ssh *ssh = container_of(be, Ssh, backend); ssh->term_width = width; ssh->term_height = height; if (ssh->cl) ssh_terminal_size(ssh->cl, ssh->term_width, ssh->term_height); } struct ssh_add_special_ctx { SessionSpecial *specials; size_t nspecials, specials_size; }; static void ssh_add_special(void *vctx, const char *text, SessionSpecialCode code, int arg) { struct ssh_add_special_ctx *ctx = (struct ssh_add_special_ctx *)vctx; SessionSpecial *spec; sgrowarray(ctx->specials, ctx->specials_size, ctx->nspecials); spec = &ctx->specials[ctx->nspecials++]; spec->name = text; spec->code = code; spec->arg = arg; } /* * Return a list of the special codes that make sense in this * protocol. */ static const SessionSpecial *ssh_get_specials(Backend *be) { Ssh *ssh = container_of(be, Ssh, backend); /* * Ask all our active protocol layers what specials they've got, * and amalgamate the list into one combined one. */ struct ssh_add_special_ctx ctx[1]; ctx->specials = NULL; ctx->nspecials = ctx->specials_size = 0; if (ssh->base_layer) ssh_ppl_get_specials(ssh->base_layer, ssh_add_special, ctx); if (ctx->specials) { /* If the list is non-empty, terminate it with a SS_EXITMENU. */ ssh_add_special(ctx, NULL, SS_EXITMENU, 0); } sfree(ssh->specials); ssh->specials = ctx->specials; return ssh->specials; } /* * Send special codes. */ static void ssh_special(Backend *be, SessionSpecialCode code, int arg) { Ssh *ssh = container_of(be, Ssh, backend); if (ssh->base_layer) ssh_ppl_special_cmd(ssh->base_layer, code, arg); } /* * This is called when the seat's output channel manages to clear some * backlog. */ static void ssh_unthrottle(Backend *be, size_t bufsize) { Ssh *ssh = container_of(be, Ssh, backend); if (ssh->cl) ssh_stdout_unthrottle(ssh->cl, bufsize); } static bool ssh_connected(Backend *be) { Ssh *ssh = container_of(be, Ssh, backend); return ssh->s != NULL; } static bool ssh_sendok(Backend *be) { Ssh *ssh = container_of(be, Ssh, backend); return ssh->base_layer && ssh_ppl_want_user_input(ssh->base_layer); } void ssh_ldisc_update(Ssh *ssh) { /* Called when the connection layer wants to propagate an update * to the line discipline options */ if (ssh->ldisc) ldisc_echoedit_update(ssh->ldisc); } static bool ssh_ldisc(Backend *be, int option) { Ssh *ssh = container_of(be, Ssh, backend); return ssh->cl ? ssh_ldisc_option(ssh->cl, option) : false; } static void ssh_provide_ldisc(Backend *be, Ldisc *ldisc) { Ssh *ssh = container_of(be, Ssh, backend); ssh->ldisc = ldisc; } void ssh_got_exitcode(Ssh *ssh, int exitcode) { ssh->exitcode = exitcode; } static int ssh_return_exitcode(Backend *be) { Ssh *ssh = container_of(be, Ssh, backend); if (ssh->s && (!ssh->session_started || ssh->base_layer)) return -1; else return (ssh->exitcode >= 0 ? ssh->exitcode : INT_MAX); } /* * cfg_info for SSH is the protocol running in this session. * (1 or 2 for the full SSH-1 or SSH-2 protocol; -1 for the bare * SSH-2 connection protocol, i.e. a downstream; 0 for not-decided-yet.) */ static int ssh_cfg_info(Backend *be) { Ssh *ssh = container_of(be, Ssh, backend); if (ssh->version == 0) return 0; /* don't know yet */ else if (ssh->bare_connection) return -1; else return ssh->version; } /* * Gross hack: pscp will try to start SFTP but fall back to scp1 if * that fails. This variable is the means by which scp.c can reach * into the SSH code and find out which one it got. */ extern bool ssh_fallback_cmd(Backend *be) { Ssh *ssh = container_of(be, Ssh, backend); return ssh->fallback_cmd; } void ssh_got_fallback_cmd(Ssh *ssh) { ssh->fallback_cmd = true; } const BackendVtable ssh_backend = { .init = ssh_init, .free = ssh_free, .reconfig = ssh_reconfig, .send = ssh_send, .sendbuffer = ssh_sendbuffer, .size = ssh_size, .special = ssh_special, .get_specials = ssh_get_specials, .connected = ssh_connected, .exitcode = ssh_return_exitcode, .sendok = ssh_sendok, .ldisc_option_state = ssh_ldisc, .provide_ldisc = ssh_provide_ldisc, .unthrottle = ssh_unthrottle, .cfg_info = ssh_cfg_info, .test_for_upstream = ssh_test_for_upstream, .close_warn_text = ssh_close_warn_text, .id = "ssh", .displayname = "SSH", .protocol = PROT_SSH, .default_port = 22, }; const BackendVtable sshconn_backend = { .init = ssh_init, .free = ssh_free, .reconfig = ssh_reconfig, .send = ssh_send, .sendbuffer = ssh_sendbuffer, .size = ssh_size, .special = ssh_special, .get_specials = ssh_get_specials, .connected = ssh_connected, .exitcode = ssh_return_exitcode, .sendok = ssh_sendok, .ldisc_option_state = ssh_ldisc, .provide_ldisc = ssh_provide_ldisc, .unthrottle = ssh_unthrottle, .cfg_info = ssh_cfg_info, .test_for_upstream = ssh_test_for_upstream, .close_warn_text = ssh_close_warn_text, .id = "ssh-connection", .displayname = "Bare ssh-connection", .protocol = PROT_SSHCONN, }; putty-0.76/ssh.h0000644000175000017500000021336514072266311010536 00000000000000#include #include #include "puttymem.h" #include "tree234.h" #include "network.h" #include "misc.h" struct ssh_channel; /* * 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 * connection (in SSH-1 only). Throttling the whole connection is * pretty drastic so we set this high in the hope it won't * happen very often. * * - SSH_MAX_BACKLOG is the amount of backlog that must build up * on the SSH connection itself before we defensively throttle * _all_ local data streams. This is pretty drastic too (though * thankfully unlikely in SSH-2 since the window mechanism should * ensure that the server never has any need to throttle its end * of the connection), so we set this high as well. * * - OUR_V2_WINSIZE is the default window size we present on SSH-2 * channels. * * - OUR_V2_BIGWIN is the window size we advertise for the only * channel in a simple connection. It must be <= INT_MAX. * * - OUR_V2_MAXPKT is the official "maximum packet size" we send * to the remote side. This actually has nothing to do with the * size of the _packet_, but is instead a limit on the amount * of data we're willing to receive in a single SSH2 channel * data message. * * - OUR_V2_PACKETLIMIT is actually the maximum size of SSH * _packet_ we're prepared to cope with. It must be a multiple * of the cipher block size, and must be at least 35000. */ #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 typedef struct PacketQueueNode PacketQueueNode; struct PacketQueueNode { PacketQueueNode *next, *prev; size_t formal_size; /* contribution to PacketQueueBase's total_size */ bool on_free_queue; /* is this packet scheduled for freeing? */ }; typedef struct PktIn { int type; unsigned long sequence; /* SSH-2 incoming sequence number */ PacketQueueNode qnode; /* for linking this packet on to a queue */ BinarySource_IMPLEMENTATION; } PktIn; typedef struct PktOut { size_t prefix; /* bytes up to and including type field */ size_t length; /* total bytes, including prefix */ int type; size_t minlen; /* SSH-2: ensure wire length is at least this */ unsigned char *data; /* allocated storage */ size_t maxlen; /* amount of storage allocated for `data' */ /* 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; PacketQueueNode qnode; /* for linking this packet on to a queue */ BinarySink_IMPLEMENTATION; } PktOut; typedef struct PacketQueueBase { PacketQueueNode end; size_t total_size; /* sum of all formal_size fields on the queue */ struct IdempotentCallback *ic; } PacketQueueBase; typedef struct PktInQueue { PacketQueueBase pqb; PktIn *(*after)(PacketQueueBase *, PacketQueueNode *prev, bool pop); } PktInQueue; typedef struct PktOutQueue { PacketQueueBase pqb; PktOut *(*after)(PacketQueueBase *, PacketQueueNode *prev, bool pop); } PktOutQueue; void pq_base_push(PacketQueueBase *pqb, PacketQueueNode *node); void pq_base_push_front(PacketQueueBase *pqb, PacketQueueNode *node); void pq_base_concatenate(PacketQueueBase *dest, PacketQueueBase *q1, PacketQueueBase *q2); void pq_in_init(PktInQueue *pq); void pq_out_init(PktOutQueue *pq); void pq_in_clear(PktInQueue *pq); void pq_out_clear(PktOutQueue *pq); #define pq_push(pq, pkt) \ TYPECHECK((pq)->after(&(pq)->pqb, NULL, false) == pkt, \ pq_base_push(&(pq)->pqb, &(pkt)->qnode)) #define pq_push_front(pq, pkt) \ TYPECHECK((pq)->after(&(pq)->pqb, NULL, false) == pkt, \ pq_base_push_front(&(pq)->pqb, &(pkt)->qnode)) #define pq_peek(pq) ((pq)->after(&(pq)->pqb, &(pq)->pqb.end, false)) #define pq_pop(pq) ((pq)->after(&(pq)->pqb, &(pq)->pqb.end, true)) #define pq_concatenate(dst, q1, q2) \ TYPECHECK((q1)->after(&(q1)->pqb, NULL, false) == \ (dst)->after(&(dst)->pqb, NULL, false) && \ (q2)->after(&(q2)->pqb, NULL, false) == \ (dst)->after(&(dst)->pqb, NULL, false), \ pq_base_concatenate(&(dst)->pqb, &(q1)->pqb, &(q2)->pqb)) #define pq_first(pq) pq_peek(pq) #define pq_next(pq, pkt) ((pq)->after(&(pq)->pqb, &(pkt)->qnode, false)) /* * 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_ECDHKEX, SSH2_PKTCTX_GSSKEX, SSH2_PKTCTX_RSAKEX } Pkt_KCtx; typedef enum { SSH2_PKTCTX_NOAUTH, SSH2_PKTCTX_PUBLICKEY, SSH2_PKTCTX_PASSWORD, SSH2_PKTCTX_GSSAPI, SSH2_PKTCTX_KBDINTER } Pkt_ACtx; typedef struct PacketLogSettings { bool omit_passwords, omit_data; Pkt_KCtx kctx; Pkt_ACtx actx; } PacketLogSettings; #define MAX_BLANKS 4 /* no packet needs more censored sections than this */ int ssh1_censor_packet( const PacketLogSettings *pls, int type, bool sender_is_client, ptrlen pkt, logblank_t *blanks); int ssh2_censor_packet( const PacketLogSettings *pls, int type, bool sender_is_client, ptrlen pkt, logblank_t *blanks); PktOut *ssh_new_packet(void); void ssh_free_pktout(PktOut *pkt); Socket *ssh_connection_sharing_init( const char *host, int port, Conf *conf, LogContext *logctx, Plug *sshplug, ssh_sharing_state **state); void ssh_connshare_provide_connlayer(ssh_sharing_state *sharestate, ConnectionLayer *cl); bool ssh_share_test_for_upstream(const char *host, int port, Conf *conf); void share_got_pkt_from_server(ssh_sharing_connstate *ctx, int type, const void *pkt, int pktlen); void share_activate(ssh_sharing_state *sharestate, const char *server_verstring); void sharestate_free(ssh_sharing_state *state); int share_ndownstreams(ssh_sharing_state *state); void ssh_connshare_log(Ssh *ssh, int event, const char *logtext, const char *ds_err, const char *us_err); void share_setup_x11_channel(ssh_sharing_connstate *cs, share_channel *chan, 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); /* Per-application overrides for what roles we can take in connection * sharing, regardless of user configuration (e.g. pscp will never be * an upstream) */ extern const bool share_can_be_downstream; extern const bool share_can_be_upstream; struct X11Display; struct X11FakeAuth; /* Structure definition centralised here because the SSH-1 and SSH-2 * connection layers both use it. But the client module (portfwd.c) * should not try to look inside here. */ struct ssh_rportfwd { unsigned sport, dport; char *shost, *dhost; int addressfamily; char *log_description; /* name of remote listening port, for logging */ ssh_sharing_connstate *share_ctx; PortFwdRecord *pfr; }; void free_rportfwd(struct ssh_rportfwd *rpf); typedef struct ConnectionLayerVtable ConnectionLayerVtable; struct ConnectionLayerVtable { /* Allocate and free remote-to-local port forwardings, called by * PortFwdManager or by connection sharing */ struct ssh_rportfwd *(*rportfwd_alloc)( ConnectionLayer *cl, const char *shost, int sport, const char *dhost, int dport, int addressfamily, const char *log_description, PortFwdRecord *pfr, ssh_sharing_connstate *share_ctx); void (*rportfwd_remove)(ConnectionLayer *cl, struct ssh_rportfwd *rpf); /* Open a local-to-remote port forwarding channel, called by * PortFwdManager */ SshChannel *(*lportfwd_open)( ConnectionLayer *cl, const char *hostname, int port, const char *description, const SocketPeerInfo *peerinfo, Channel *chan); /* Initiate opening of a 'session'-type channel */ SshChannel *(*session_open)(ConnectionLayer *cl, Channel *chan); /* Open outgoing channels for X and agent forwarding. (Used in the * SSH server.) */ SshChannel *(*serverside_x11_open)(ConnectionLayer *cl, Channel *chan, const SocketPeerInfo *pi); SshChannel *(*serverside_agent_open)(ConnectionLayer *cl, Channel *chan); /* Add an X11 display for ordinary X forwarding */ struct X11FakeAuth *(*add_x11_display)( ConnectionLayer *cl, int authtype, struct X11Display *x11disp); /* Add and remove X11 displays for connection sharing downstreams */ struct X11FakeAuth *(*add_sharing_x11_display)( ConnectionLayer *cl, int authtype, ssh_sharing_connstate *share_cs, share_channel *share_chan); void (*remove_sharing_x11_display)( ConnectionLayer *cl, struct X11FakeAuth *auth); /* Pass through an outgoing SSH packet from a downstream */ void (*send_packet_from_downstream)( ConnectionLayer *cl, unsigned id, int type, const void *pkt, int pktlen, const char *additional_log_text); /* Allocate/free an upstream channel number associated with a * sharing downstream */ unsigned (*alloc_sharing_channel)(ConnectionLayer *cl, ssh_sharing_connstate *connstate); void (*delete_sharing_channel)(ConnectionLayer *cl, unsigned localid); /* Indicate that a downstream has sent a global request with the * want-reply flag, so that when a reply arrives it will be passed * back to that downstrean */ void (*sharing_queue_global_request)( ConnectionLayer *cl, ssh_sharing_connstate *connstate); /* Indicate that the last downstream has disconnected */ void (*sharing_no_more_downstreams)(ConnectionLayer *cl); /* Query whether the connection layer is doing agent forwarding */ bool (*agent_forwarding_permitted)(ConnectionLayer *cl); /* Set the size of the main terminal window (if any) */ void (*terminal_size)(ConnectionLayer *cl, int width, int height); /* Indicate that the backlog on standard output has cleared */ void (*stdout_unthrottle)(ConnectionLayer *cl, size_t bufsize); /* Query the size of the backlog on standard _input_ */ size_t (*stdin_backlog)(ConnectionLayer *cl); /* Tell the connection layer that the SSH connection itself has * backed up, so it should tell all currently open channels to * cease reading from their local input sources if they can. (Or * tell it that that state of affairs has gone away again.) */ void (*throttle_all_channels)(ConnectionLayer *cl, bool throttled); /* Ask the connection layer about its current preference for * line-discipline options. */ bool (*ldisc_option)(ConnectionLayer *cl, int option); /* Communicate _to_ the connection layer (from the main session * channel) what its preference for line-discipline options is. */ void (*set_ldisc_option)(ConnectionLayer *cl, int option, bool value); /* Communicate to the connection layer whether X forwarding was * successfully enabled (for purposes of knowing whether to accept * subsequent channel-opens). */ void (*enable_x_fwd)(ConnectionLayer *cl); /* Communicate to the connection layer whether the main session * channel currently wants user input. */ void (*set_wants_user_input)(ConnectionLayer *cl, bool wanted); }; struct ConnectionLayer { LogContext *logctx; const struct ConnectionLayerVtable *vt; }; static inline struct ssh_rportfwd *ssh_rportfwd_alloc( ConnectionLayer *cl, const char *sh, int sp, const char *dh, int dp, int af, const char *log, PortFwdRecord *pfr, ssh_sharing_connstate *cs) { return cl->vt->rportfwd_alloc(cl, sh, sp, dh, dp, af, log, pfr, cs); } static inline void ssh_rportfwd_remove( ConnectionLayer *cl, struct ssh_rportfwd *rpf) { cl->vt->rportfwd_remove(cl, rpf); } static inline SshChannel *ssh_lportfwd_open( ConnectionLayer *cl, const char *host, int port, const char *desc, const SocketPeerInfo *pi, Channel *chan) { return cl->vt->lportfwd_open(cl, host, port, desc, pi, chan); } static inline SshChannel *ssh_session_open(ConnectionLayer *cl, Channel *chan) { return cl->vt->session_open(cl, chan); } static inline SshChannel *ssh_serverside_x11_open( ConnectionLayer *cl, Channel *chan, const SocketPeerInfo *pi) { return cl->vt->serverside_x11_open(cl, chan, pi); } static inline SshChannel *ssh_serverside_agent_open( ConnectionLayer *cl, Channel *chan) { return cl->vt->serverside_agent_open(cl, chan); } static inline struct X11FakeAuth *ssh_add_x11_display( ConnectionLayer *cl, int authtype, struct X11Display *x11disp) { return cl->vt->add_x11_display(cl, authtype, x11disp); } static inline struct X11FakeAuth *ssh_add_sharing_x11_display( ConnectionLayer *cl, int authtype, ssh_sharing_connstate *share_cs, share_channel *share_chan) { return cl->vt->add_sharing_x11_display(cl, authtype, share_cs, share_chan); } static inline void ssh_remove_sharing_x11_display( ConnectionLayer *cl, struct X11FakeAuth *auth) { cl->vt->remove_sharing_x11_display(cl, auth); } static inline void ssh_send_packet_from_downstream( ConnectionLayer *cl, unsigned id, int type, const void *pkt, int len, const char *log) { cl->vt->send_packet_from_downstream(cl, id, type, pkt, len, log); } static inline unsigned ssh_alloc_sharing_channel( ConnectionLayer *cl, ssh_sharing_connstate *connstate) { return cl->vt->alloc_sharing_channel(cl, connstate); } static inline void ssh_delete_sharing_channel( ConnectionLayer *cl, unsigned localid) { cl->vt->delete_sharing_channel(cl, localid); } static inline void ssh_sharing_queue_global_request( ConnectionLayer *cl, ssh_sharing_connstate *connstate) { cl->vt->sharing_queue_global_request(cl, connstate); } static inline void ssh_sharing_no_more_downstreams(ConnectionLayer *cl) { cl->vt->sharing_no_more_downstreams(cl); } static inline bool ssh_agent_forwarding_permitted(ConnectionLayer *cl) { return cl->vt->agent_forwarding_permitted(cl); } static inline void ssh_terminal_size(ConnectionLayer *cl, int w, int h) { cl->vt->terminal_size(cl, w, h); } static inline void ssh_stdout_unthrottle(ConnectionLayer *cl, size_t bufsize) { cl->vt->stdout_unthrottle(cl, bufsize); } static inline size_t ssh_stdin_backlog(ConnectionLayer *cl) { return cl->vt->stdin_backlog(cl); } static inline void ssh_throttle_all_channels(ConnectionLayer *cl, bool thr) { cl->vt->throttle_all_channels(cl, thr); } static inline bool ssh_ldisc_option(ConnectionLayer *cl, int option) { return cl->vt->ldisc_option(cl, option); } static inline void ssh_set_ldisc_option(ConnectionLayer *cl, int opt, bool val) { cl->vt->set_ldisc_option(cl, opt, val); } static inline void ssh_enable_x_fwd(ConnectionLayer *cl) { cl->vt->enable_x_fwd(cl); } static inline void ssh_set_wants_user_input(ConnectionLayer *cl, bool wanted) { cl->vt->set_wants_user_input(cl, wanted); } /* Exports from portfwd.c */ PortFwdManager *portfwdmgr_new(ConnectionLayer *cl); void portfwdmgr_free(PortFwdManager *mgr); void portfwdmgr_config(PortFwdManager *mgr, Conf *conf); void portfwdmgr_close(PortFwdManager *mgr, PortFwdRecord *pfr); void portfwdmgr_close_all(PortFwdManager *mgr); char *portfwdmgr_connect(PortFwdManager *mgr, Channel **chan_ret, char *hostname, int port, SshChannel *c, int addressfamily); bool portfwdmgr_listen(PortFwdManager *mgr, const char *host, int port, const char *keyhost, int keyport, Conf *conf); bool portfwdmgr_unlisten(PortFwdManager *mgr, const char *host, int port); Channel *portfwd_raw_new(ConnectionLayer *cl, Plug **plug, bool start_ready); void portfwd_raw_free(Channel *pfchan); void portfwd_raw_setup(Channel *pfchan, Socket *s, SshChannel *sc); Socket *platform_make_agent_socket(Plug *plug, const char *dirprefix, char **error, char **name); LogContext *ssh_get_logctx(Ssh *ssh); /* Communications back to ssh.c from connection layers */ void ssh_throttle_conn(Ssh *ssh, int adjust); void ssh_got_exitcode(Ssh *ssh, int status); void ssh_ldisc_update(Ssh *ssh); void ssh_got_fallback_cmd(Ssh *ssh); bool ssh_is_bare(Ssh *ssh); /* Communications back to ssh.c from the BPP */ void ssh_conn_processed_data(Ssh *ssh); void ssh_check_frozen(Ssh *ssh); /* Functions to abort the connection, for various reasons. */ void ssh_remote_error(Ssh *ssh, const char *fmt, ...) PRINTF_LIKE(2, 3); void ssh_remote_eof(Ssh *ssh, const char *fmt, ...) PRINTF_LIKE(2, 3); void ssh_proto_error(Ssh *ssh, const char *fmt, ...) PRINTF_LIKE(2, 3); void ssh_sw_abort(Ssh *ssh, const char *fmt, ...) PRINTF_LIKE(2, 3); void ssh_sw_abort_deferred(Ssh *ssh, const char *fmt, ...) PRINTF_LIKE(2, 3); void ssh_user_close(Ssh *ssh, const char *fmt, ...) PRINTF_LIKE(2, 3); /* Bit positions in the SSH-1 cipher protocol word */ #define SSH1_CIPHER_IDEA 1 #define SSH1_CIPHER_DES 2 #define SSH1_CIPHER_3DES 3 #define SSH1_CIPHER_BLOWFISH 6 /* The subset of those that we support, with names for selecting them * on Uppity's command line */ #define SSH1_SUPPORTED_CIPHER_LIST(X) \ X(SSH1_CIPHER_3DES, "3des") \ X(SSH1_CIPHER_BLOWFISH, "blowfish") \ X(SSH1_CIPHER_DES, "des") \ /* end of list */ #define SSH1_CIPHER_LIST_MAKE_MASK(bitpos, name) | (1U << bitpos) #define SSH1_SUPPORTED_CIPHER_MASK \ (0 SSH1_SUPPORTED_CIPHER_LIST(SSH1_CIPHER_LIST_MAKE_MASK)) struct ssh_key { const ssh_keyalg *vt; }; struct RSAKey { int bits; int bytes; mp_int *modulus; mp_int *exponent; mp_int *private_exponent; mp_int *p; mp_int *q; mp_int *iqmp; char *comment; ssh_key sshk; }; struct dss_key { mp_int *p, *q, *g, *y, *x; ssh_key sshk; }; struct ec_curve; /* Weierstrass form curve */ struct ec_wcurve { WeierstrassCurve *wc; WeierstrassPoint *G; mp_int *G_order; }; /* Montgomery form curve */ struct ec_mcurve { MontgomeryCurve *mc; MontgomeryPoint *G; unsigned log2_cofactor; }; /* Edwards form curve */ struct ec_ecurve { EdwardsCurve *ec; EdwardsPoint *G; mp_int *G_order; unsigned log2_cofactor; }; typedef enum EllipticCurveType { EC_WEIERSTRASS, EC_MONTGOMERY, EC_EDWARDS } EllipticCurveType; struct ec_curve { EllipticCurveType type; /* 'name' is the identifier of the curve when it has to appear in * wire protocol encodings, as it does in e.g. the public key and * signature formats for NIST curves. Curves which do not format * their keys or signatures in this way just have name==NULL. * * 'textname' is non-NULL for all curves, and is a human-readable * identification suitable for putting in log messages. */ const char *name, *textname; size_t fieldBits, fieldBytes; mp_int *p; union { struct ec_wcurve w; struct ec_mcurve m; struct ec_ecurve e; }; }; const ssh_keyalg *ec_alg_by_oid(int len, const void *oid, const struct ec_curve **curve); const unsigned char *ec_alg_oid(const ssh_keyalg *alg, int *oidlen); extern const int ec_nist_curve_lengths[], n_ec_nist_curve_lengths; extern const int ec_ed_curve_lengths[], n_ec_ed_curve_lengths; bool ec_nist_alg_and_curve_by_bits(int bits, const struct ec_curve **curve, const ssh_keyalg **alg); bool ec_ed_alg_and_curve_by_bits(int bits, const struct ec_curve **curve, const ssh_keyalg **alg); struct ecdsa_key { const struct ec_curve *curve; WeierstrassPoint *publicKey; mp_int *privateKey; ssh_key sshk; }; struct eddsa_key { const struct ec_curve *curve; EdwardsPoint *publicKey; mp_int *privateKey; ssh_key sshk; }; WeierstrassPoint *ecdsa_public(mp_int *private_key, const ssh_keyalg *alg); EdwardsPoint *eddsa_public(mp_int *private_key, const ssh_keyalg *alg); typedef struct key_components { size_t ncomponents, componentsize; struct { char *name; bool is_mp_int; union { char *text; mp_int *mp; }; } *components; } key_components; key_components *key_components_new(void); void key_components_add_text(key_components *kc, const char *name, const char *value); void key_components_add_mp(key_components *kc, const char *name, mp_int *value); void key_components_free(key_components *kc); /* * SSH-1 never quite decided which order to store the two components * of an RSA key. During connection setup, the server sends its host * and server keys with the exponent first; private key files store * the modulus first. The agent protocol is even more confusing, * because the client specifies a key to the server in one order and * the server lists the keys it knows about in the other order! */ typedef enum { RSA_SSH1_EXPONENT_FIRST, RSA_SSH1_MODULUS_FIRST } RsaSsh1Order; void BinarySource_get_rsa_ssh1_pub( BinarySource *src, RSAKey *result, RsaSsh1Order order); void BinarySource_get_rsa_ssh1_priv( BinarySource *src, RSAKey *rsa); RSAKey *BinarySource_get_rsa_ssh1_priv_agent(BinarySource *src); bool rsa_ssh1_encrypt(unsigned char *data, int length, RSAKey *key); mp_int *rsa_ssh1_decrypt(mp_int *input, RSAKey *key); bool rsa_ssh1_decrypt_pkcs1(mp_int *input, RSAKey *key, strbuf *outbuf); char *rsastr_fmt(RSAKey *key); char *rsa_ssh1_fingerprint(RSAKey *key); char **rsa_ssh1_fake_all_fingerprints(RSAKey *key); bool rsa_verify(RSAKey *key); void rsa_ssh1_public_blob(BinarySink *bs, RSAKey *key, RsaSsh1Order order); int rsa_ssh1_public_blob_len(ptrlen data); void rsa_ssh1_private_blob_agent(BinarySink *bs, RSAKey *key); void freersapriv(RSAKey *key); void freersakey(RSAKey *key); key_components *rsa_components(RSAKey *key); uint32_t crc32_rfc1662(ptrlen data); uint32_t crc32_ssh1(ptrlen data); uint32_t crc32_update(uint32_t crc_input, ptrlen data); /* SSH CRC compensation attack detector */ struct crcda_ctx; struct crcda_ctx *crcda_make_context(void); void crcda_free_context(struct crcda_ctx *ctx); bool detect_attack(struct crcda_ctx *ctx, const unsigned char *buf, uint32_t len, const unsigned char *IV); /* * SSH2 RSA key exchange functions */ struct ssh_rsa_kex_extra { int minklen; }; RSAKey *ssh_rsakex_newkey(ptrlen data); void ssh_rsakex_freekey(RSAKey *key); int ssh_rsakex_klen(RSAKey *key); strbuf *ssh_rsakex_encrypt( RSAKey *key, const ssh_hashalg *h, ptrlen plaintext); mp_int *ssh_rsakex_decrypt( RSAKey *key, const ssh_hashalg *h, ptrlen ciphertext); /* * SSH2 ECDH key exchange functions */ const char *ssh_ecdhkex_curve_textname(const ssh_kex *kex); ecdh_key *ssh_ecdhkex_newkey(const ssh_kex *kex); void ssh_ecdhkex_freekey(ecdh_key *key); void ssh_ecdhkex_getpublic(ecdh_key *key, BinarySink *bs); mp_int *ssh_ecdhkex_getkey(ecdh_key *key, ptrlen remoteKey); /* * Helper function for k generation in DSA, reused in ECDSA */ mp_int *dss_gen_k(const char *id_string, mp_int *modulus, mp_int *private_key, unsigned char *digest, int digest_len); struct ssh_cipher { const ssh_cipheralg *vt; }; struct ssh_cipheralg { ssh_cipher *(*new)(const ssh_cipheralg *alg); void (*free)(ssh_cipher *); void (*setiv)(ssh_cipher *, const void *iv); void (*setkey)(ssh_cipher *, const void *key); void (*encrypt)(ssh_cipher *, void *blk, int len); void (*decrypt)(ssh_cipher *, void *blk, int len); /* Ignored unless SSH_CIPHER_SEPARATE_LENGTH flag set */ void (*encrypt_length)(ssh_cipher *, void *blk, int len, unsigned long seq); void (*decrypt_length)(ssh_cipher *, void *blk, int len, unsigned long seq); const char *ssh2_id; int blksize; /* real_keybits is the number of bits of entropy genuinely used by * the cipher scheme; it's used for deciding how big a * Diffie-Hellman group is needed to exchange a key for the * cipher. */ int real_keybits; /* padded_keybytes is the number of bytes of key data expected as * input to the setkey function; it's used for deciding how much * data needs to be generated from the post-kex generation of key * material. In a sensible cipher which uses all its key bytes for * real work, this will just be real_keybits/8, but in DES-type * ciphers which ignore one bit in each byte, it'll be slightly * different. */ int padded_keybytes; unsigned int flags; #define SSH_CIPHER_IS_CBC 1 #define SSH_CIPHER_SEPARATE_LENGTH 2 const char *text_name; /* If set, this takes priority over other MAC. */ const ssh2_macalg *required_mac; /* Pointer to any extra data used by a particular implementation. */ const void *extra; }; static inline ssh_cipher *ssh_cipher_new(const ssh_cipheralg *alg) { return alg->new(alg); } static inline void ssh_cipher_free(ssh_cipher *c) { c->vt->free(c); } static inline void ssh_cipher_setiv(ssh_cipher *c, const void *iv) { c->vt->setiv(c, iv); } static inline void ssh_cipher_setkey(ssh_cipher *c, const void *key) { c->vt->setkey(c, key); } static inline void ssh_cipher_encrypt(ssh_cipher *c, void *blk, int len) { c->vt->encrypt(c, blk, len); } static inline void ssh_cipher_decrypt(ssh_cipher *c, void *blk, int len) { c->vt->decrypt(c, blk, len); } static inline void ssh_cipher_encrypt_length( ssh_cipher *c, void *blk, int len, unsigned long seq) { c->vt->encrypt_length(c, blk, len, seq); } static inline void ssh_cipher_decrypt_length( ssh_cipher *c, void *blk, int len, unsigned long seq) { c->vt->decrypt_length(c, blk, len, seq); } static inline const struct ssh_cipheralg *ssh_cipher_alg(ssh_cipher *c) { return c->vt; } struct ssh2_ciphers { int nciphers; const ssh_cipheralg *const *list; }; struct ssh2_mac { const ssh2_macalg *vt; BinarySink_DELEGATE_IMPLEMENTATION; }; struct ssh2_macalg { /* Passes in the cipher context */ ssh2_mac *(*new)(const ssh2_macalg *alg, ssh_cipher *cipher); void (*free)(ssh2_mac *); void (*setkey)(ssh2_mac *, ptrlen key); void (*start)(ssh2_mac *); void (*genresult)(ssh2_mac *, unsigned char *); const char *(*text_name)(ssh2_mac *); const char *name, *etm_name; int len, keylen; /* Pointer to any extra data used by a particular implementation. */ const void *extra; }; static inline ssh2_mac *ssh2_mac_new( const ssh2_macalg *alg, ssh_cipher *cipher) { return alg->new(alg, cipher); } static inline void ssh2_mac_free(ssh2_mac *m) { m->vt->free(m); } static inline void ssh2_mac_setkey(ssh2_mac *m, ptrlen key) { m->vt->setkey(m, key); } static inline void ssh2_mac_start(ssh2_mac *m) { m->vt->start(m); } static inline void ssh2_mac_genresult(ssh2_mac *m, unsigned char *out) { m->vt->genresult(m, out); } static inline const char *ssh2_mac_text_name(ssh2_mac *m) { return m->vt->text_name(m); } static inline const ssh2_macalg *ssh2_mac_alg(ssh2_mac *m) { return m->vt; } /* Centralised 'methods' for ssh2_mac, defined in sshmac.c. These run * the MAC in a specifically SSH-2 style, i.e. taking account of a * packet sequence number as well as the data to be authenticated. */ bool ssh2_mac_verresult(ssh2_mac *, const void *); void ssh2_mac_generate(ssh2_mac *, void *, int, unsigned long seq); bool ssh2_mac_verify(ssh2_mac *, const void *, int, unsigned long seq); /* Use a MAC in its raw form, outside SSH-2 context, to MAC a given * string with a given key in the most obvious way. */ void mac_simple(const ssh2_macalg *alg, ptrlen key, ptrlen data, void *output); struct ssh_hash { const ssh_hashalg *vt; BinarySink_DELEGATE_IMPLEMENTATION; }; struct ssh_hashalg { ssh_hash *(*new)(const ssh_hashalg *alg); void (*reset)(ssh_hash *); void (*copyfrom)(ssh_hash *dest, ssh_hash *src); void (*digest)(ssh_hash *, unsigned char *); void (*free)(ssh_hash *); size_t hlen; /* output length in bytes */ size_t blocklen; /* length of the hash's input block, or 0 for N/A */ const char *text_basename; /* the semantic name of the hash */ const char *annotation; /* extra info, e.g. which of multiple impls */ const char *text_name; /* both combined, e.g. "SHA-n (unaccelerated)" */ const void *extra; /* private to the hash implementation */ }; static inline ssh_hash *ssh_hash_new(const ssh_hashalg *alg) { ssh_hash *h = alg->new(alg); if (h) h->vt->reset(h); return h; } static inline ssh_hash *ssh_hash_copy(ssh_hash *orig) { ssh_hash *h = orig->vt->new(orig->vt); h->vt->copyfrom(h, orig); return h; } static inline void ssh_hash_digest(ssh_hash *h, unsigned char *out) { h->vt->digest(h, out); } static inline void ssh_hash_free(ssh_hash *h) { h->vt->free(h); } static inline const ssh_hashalg *ssh_hash_alg(ssh_hash *h) { return h->vt; } /* The reset and copyfrom vtable methods return void. But for call-site * convenience, these wrappers return their input pointer. */ static inline ssh_hash *ssh_hash_reset(ssh_hash *h) { h->vt->reset(h); return h; } static inline ssh_hash *ssh_hash_copyfrom(ssh_hash *dest, ssh_hash *src) { dest->vt->copyfrom(dest, src); return dest; } /* ssh_hash_final emits the digest _and_ frees the ssh_hash */ static inline void ssh_hash_final(ssh_hash *h, unsigned char *out) { h->vt->digest(h, out); h->vt->free(h); } /* ssh_hash_digest_nondestructive generates a finalised hash from the * given object without changing its state, so you can continue * appending data to get a hash of an extended string. */ static inline void ssh_hash_digest_nondestructive(ssh_hash *h, unsigned char *out) { ssh_hash_final(ssh_hash_copy(h), out); } /* Handy macros for defining all those text-name fields at once */ #define HASHALG_NAMES_BARE(base) \ .text_basename = base, .annotation = NULL, .text_name = base #define HASHALG_NAMES_ANNOTATED(base, ann) \ .text_basename = base, .annotation = ann, .text_name = base " (" ann ")" void hash_simple(const ssh_hashalg *alg, ptrlen data, void *output); struct ssh_kex { const char *name, *groupname; enum { KEXTYPE_DH, KEXTYPE_RSA, KEXTYPE_ECDH, KEXTYPE_GSS } main_type; const ssh_hashalg *hash; const void *extra; /* private to the kex methods */ }; struct ssh_kexes { int nkexes; const ssh_kex *const *list; }; /* Indices of the negotiation strings in the KEXINIT packet */ enum kexlist { KEXLIST_KEX, KEXLIST_HOSTKEY, KEXLIST_CSCIPHER, KEXLIST_SCCIPHER, KEXLIST_CSMAC, KEXLIST_SCMAC, KEXLIST_CSCOMP, KEXLIST_SCCOMP, NKEXLIST }; struct ssh_keyalg { /* Constructors that create an ssh_key */ ssh_key *(*new_pub) (const ssh_keyalg *self, ptrlen pub); ssh_key *(*new_priv) (const ssh_keyalg *self, ptrlen pub, ptrlen priv); ssh_key *(*new_priv_openssh) (const ssh_keyalg *self, BinarySource *); /* Methods that operate on an existing ssh_key */ void (*freekey) (ssh_key *key); char *(*invalid) (ssh_key *key, unsigned flags); void (*sign) (ssh_key *key, ptrlen data, unsigned flags, BinarySink *); bool (*verify) (ssh_key *key, ptrlen sig, ptrlen data); void (*public_blob)(ssh_key *key, BinarySink *); void (*private_blob)(ssh_key *key, BinarySink *); void (*openssh_blob) (ssh_key *key, BinarySink *); char *(*cache_str) (ssh_key *key); key_components *(*components) (ssh_key *key); /* 'Class methods' that don't deal with an ssh_key at all */ int (*pubkey_bits) (const ssh_keyalg *self, ptrlen blob); /* Constant data fields giving information about the key type */ const char *ssh_id; /* string identifier in the SSH protocol */ const char *cache_id; /* identifier used in PuTTY's host key cache */ const void *extra; /* private to the public key methods */ const unsigned supported_flags; /* signature-type flags we understand */ }; static inline ssh_key *ssh_key_new_pub(const ssh_keyalg *self, ptrlen pub) { return self->new_pub(self, pub); } static inline ssh_key *ssh_key_new_priv( const ssh_keyalg *self, ptrlen pub, ptrlen priv) { return self->new_priv(self, pub, priv); } static inline ssh_key *ssh_key_new_priv_openssh( const ssh_keyalg *self, BinarySource *src) { return self->new_priv_openssh(self, src); } static inline void ssh_key_free(ssh_key *key) { key->vt->freekey(key); } static inline char *ssh_key_invalid(ssh_key *key, unsigned flags) { return key->vt->invalid(key, flags); } static inline void ssh_key_sign( ssh_key *key, ptrlen data, unsigned flags, BinarySink *bs) { key->vt->sign(key, data, flags, bs); } static inline bool ssh_key_verify(ssh_key *key, ptrlen sig, ptrlen data) { return key->vt->verify(key, sig, data); } static inline void ssh_key_public_blob(ssh_key *key, BinarySink *bs) { key->vt->public_blob(key, bs); } static inline void ssh_key_private_blob(ssh_key *key, BinarySink *bs) { key->vt->private_blob(key, bs); } static inline void ssh_key_openssh_blob(ssh_key *key, BinarySink *bs) { key->vt->openssh_blob(key, bs); } static inline char *ssh_key_cache_str(ssh_key *key) { return key->vt->cache_str(key); } static inline key_components *ssh_key_components(ssh_key *key) { return key->vt->components(key); } static inline int ssh_key_public_bits(const ssh_keyalg *self, ptrlen blob) { return self->pubkey_bits(self, blob); } static inline const ssh_keyalg *ssh_key_alg(ssh_key *key) { return key->vt; } static inline const char *ssh_key_ssh_id(ssh_key *key) { return key->vt->ssh_id; } static inline const char *ssh_key_cache_id(ssh_key *key) { return key->vt->cache_id; } /* * Enumeration of signature flags from draft-miller-ssh-agent-02 */ #define SSH_AGENT_RSA_SHA2_256 2 #define SSH_AGENT_RSA_SHA2_512 4 struct ssh_compressor { const ssh_compression_alg *vt; }; struct ssh_decompressor { const ssh_compression_alg *vt; }; struct ssh_compression_alg { const char *name; /* For zlib@openssh.com: if non-NULL, this name will be considered once * userauth has completed successfully. */ const char *delayed_name; ssh_compressor *(*compress_new)(void); void (*compress_free)(ssh_compressor *); void (*compress)(ssh_compressor *, const unsigned char *block, int len, unsigned char **outblock, int *outlen, int minlen); ssh_decompressor *(*decompress_new)(void); void (*decompress_free)(ssh_decompressor *); bool (*decompress)(ssh_decompressor *, const unsigned char *block, int len, unsigned char **outblock, int *outlen); const char *text_name; }; static inline ssh_compressor *ssh_compressor_new( const ssh_compression_alg *alg) { return alg->compress_new(); } static inline ssh_decompressor *ssh_decompressor_new( const ssh_compression_alg *alg) { return alg->decompress_new(); } static inline void ssh_compressor_free(ssh_compressor *c) { c->vt->compress_free(c); } static inline void ssh_decompressor_free(ssh_decompressor *d) { d->vt->decompress_free(d); } static inline void ssh_compressor_compress( ssh_compressor *c, const unsigned char *block, int len, unsigned char **outblock, int *outlen, int minlen) { c->vt->compress(c, block, len, outblock, outlen, minlen); } static inline bool ssh_decompressor_decompress( ssh_decompressor *d, const unsigned char *block, int len, unsigned char **outblock, int *outlen) { return d->vt->decompress(d, block, len, outblock, outlen); } static inline const ssh_compression_alg *ssh_compressor_alg( ssh_compressor *c) { return c->vt; } static inline const ssh_compression_alg *ssh_decompressor_alg( ssh_decompressor *d) { return d->vt; } struct ssh2_userkey { ssh_key *key; /* the key itself */ char *comment; /* the key comment */ }; /* Argon2 password hashing function */ typedef enum { Argon2d = 0, Argon2i = 1, Argon2id = 2 } Argon2Flavour; void argon2(Argon2Flavour, uint32_t mem, uint32_t passes, uint32_t parallel, uint32_t taglen, ptrlen P, ptrlen S, ptrlen K, ptrlen X, strbuf *out); void argon2_choose_passes( Argon2Flavour, uint32_t mem, uint32_t milliseconds, uint32_t *passes, uint32_t parallel, uint32_t taglen, ptrlen P, ptrlen S, ptrlen K, ptrlen X, strbuf *out); /* The H' hash defined in Argon2, exposed just for testcrypt */ strbuf *argon2_long_hash(unsigned length, ptrlen data); /* The maximum length of any hash algorithm. (bytes) */ #define MAX_HASH_LEN (114) /* longest is SHAKE256 with 114-byte output */ extern const ssh_cipheralg ssh_3des_ssh1; extern const ssh_cipheralg ssh_blowfish_ssh1; extern const ssh_cipheralg ssh_3des_ssh2_ctr; extern const ssh_cipheralg ssh_3des_ssh2; extern const ssh_cipheralg ssh_des; extern const ssh_cipheralg ssh_des_sshcom_ssh2; extern const ssh_cipheralg ssh_aes256_sdctr; extern const ssh_cipheralg ssh_aes256_sdctr_hw; extern const ssh_cipheralg ssh_aes256_sdctr_sw; extern const ssh_cipheralg ssh_aes256_cbc; extern const ssh_cipheralg ssh_aes256_cbc_hw; extern const ssh_cipheralg ssh_aes256_cbc_sw; extern const ssh_cipheralg ssh_aes192_sdctr; extern const ssh_cipheralg ssh_aes192_sdctr_hw; extern const ssh_cipheralg ssh_aes192_sdctr_sw; extern const ssh_cipheralg ssh_aes192_cbc; extern const ssh_cipheralg ssh_aes192_cbc_hw; extern const ssh_cipheralg ssh_aes192_cbc_sw; extern const ssh_cipheralg ssh_aes128_sdctr; extern const ssh_cipheralg ssh_aes128_sdctr_hw; extern const ssh_cipheralg ssh_aes128_sdctr_sw; extern const ssh_cipheralg ssh_aes128_cbc; extern const ssh_cipheralg ssh_aes128_cbc_hw; extern const ssh_cipheralg ssh_aes128_cbc_sw; extern const ssh_cipheralg ssh_blowfish_ssh2_ctr; extern const ssh_cipheralg ssh_blowfish_ssh2; extern const ssh_cipheralg ssh_arcfour256_ssh2; extern const ssh_cipheralg ssh_arcfour128_ssh2; extern const ssh_cipheralg ssh2_chacha20_poly1305; extern const ssh2_ciphers ssh2_3des; extern const ssh2_ciphers ssh2_des; extern const ssh2_ciphers ssh2_aes; extern const ssh2_ciphers ssh2_blowfish; extern const ssh2_ciphers ssh2_arcfour; extern const ssh2_ciphers ssh2_ccp; extern const ssh_hashalg ssh_md5; extern const ssh_hashalg ssh_sha1; extern const ssh_hashalg ssh_sha1_hw; extern const ssh_hashalg ssh_sha1_sw; extern const ssh_hashalg ssh_sha256; extern const ssh_hashalg ssh_sha256_hw; extern const ssh_hashalg ssh_sha256_sw; extern const ssh_hashalg ssh_sha384; extern const ssh_hashalg ssh_sha384_hw; extern const ssh_hashalg ssh_sha384_sw; extern const ssh_hashalg ssh_sha512; extern const ssh_hashalg ssh_sha512_hw; extern const ssh_hashalg ssh_sha512_sw; extern const ssh_hashalg ssh_sha3_224; extern const ssh_hashalg ssh_sha3_256; extern const ssh_hashalg ssh_sha3_384; extern const ssh_hashalg ssh_sha3_512; extern const ssh_hashalg ssh_shake256_114bytes; extern const ssh_hashalg ssh_blake2b; extern const ssh_kexes ssh_diffiehellman_group1; extern const ssh_kexes ssh_diffiehellman_group14; extern const ssh_kexes ssh_diffiehellman_gex; extern const ssh_kexes ssh_gssk5_sha1_kex; extern const ssh_kexes ssh_rsa_kex; extern const ssh_kex ssh_ec_kex_curve25519; extern const ssh_kex ssh_ec_kex_curve448; extern const ssh_kex ssh_ec_kex_nistp256; extern const ssh_kex ssh_ec_kex_nistp384; extern const ssh_kex ssh_ec_kex_nistp521; extern const ssh_kexes ssh_ecdh_kex; extern const ssh_keyalg ssh_dss; extern const ssh_keyalg ssh_rsa; extern const ssh_keyalg ssh_rsa_sha256; extern const ssh_keyalg ssh_rsa_sha512; extern const ssh_keyalg ssh_ecdsa_ed25519; extern const ssh_keyalg ssh_ecdsa_ed448; extern const ssh_keyalg ssh_ecdsa_nistp256; extern const ssh_keyalg ssh_ecdsa_nistp384; extern const ssh_keyalg ssh_ecdsa_nistp521; extern const ssh2_macalg ssh_hmac_md5; extern const ssh2_macalg ssh_hmac_sha1; extern const ssh2_macalg ssh_hmac_sha1_buggy; extern const ssh2_macalg ssh_hmac_sha1_96; extern const ssh2_macalg ssh_hmac_sha1_96_buggy; extern const ssh2_macalg ssh_hmac_sha256; extern const ssh2_macalg ssh2_poly1305; extern const ssh_compression_alg ssh_zlib; /* Special constructor: BLAKE2b can be instantiated with any hash * length up to 128 bytes */ ssh_hash *blake2b_new_general(unsigned hashlen); /* * On some systems, you have to detect hardware crypto acceleration by * asking the local OS API rather than OS-agnostically asking the CPU * itself. If so, then this function should be implemented in each * platform subdirectory. */ bool platform_aes_hw_available(void); bool platform_sha256_hw_available(void); bool platform_sha1_hw_available(void); bool platform_sha512_hw_available(void); /* * PuTTY version number formatted as an SSH version string. */ extern const char sshver[]; /* * Gross hack: pscp will try to start SFTP but fall back to scp1 if * that fails. This variable is the means by which scp.c can reach * into the SSH code and find out which one it got. */ extern bool ssh_fallback_cmd(Backend *backend); /* * The PRNG type, defined in sshprng.c. Visible data fields are * 'savesize', which suggests how many random bytes you should request * from a particular PRNG instance to write to putty.rnd, and a * BinarySink implementation which you can use to write seed data in * between calling prng_seed_{begin,finish}. */ struct prng { size_t savesize; BinarySink_IMPLEMENTATION; /* (also there's a surrounding implementation struct in sshprng.c) */ }; prng *prng_new(const ssh_hashalg *hashalg); void prng_free(prng *p); void prng_seed_begin(prng *p); void prng_seed_finish(prng *p); void prng_read(prng *p, void *vout, size_t size); void prng_add_entropy(prng *p, unsigned source_id, ptrlen data); size_t prng_seed_bits(prng *p); /* This function must be implemented by the platform, and returns a * timer in milliseconds that the PRNG can use to know whether it's * been reseeded too recently to do it again. * * The PRNG system has its own special timing function not because its * timing needs are unusual in the real applications, but simply so * that testcrypt can mock it to keep the tests deterministic. */ uint64_t prng_reseed_time_ms(void); void random_read(void *out, size_t size); /* 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 */ bool 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; ssh_sharing_connstate *share_cs; share_channel *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. * * If the returned pointer is NULL, then *error_msg will contain a * dynamically allocated error message string. */ extern struct X11Display *x11_setup_display(const char *display, Conf *, char **error_msg); 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); Channel *x11_new_channel(tree234 *authtree, SshChannel *c, const char *peeraddr, int peerport, bool connection_sharing_possible); 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 bool 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 */ /* Callbacks in x11.c usable _by_ platform X11 functions */ /* * This function does the job of platform_get_x11_auth, provided * it is told where to find a normally formatted .Xauthority file: * it opens that file, parses it to find an auth record which * matches the display details in "display", and fills in the * localauth fields. * * It is expected that most implementations of * 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); void x11_format_auth_for_authfile( BinarySink *bs, SockAddr *addr, int display_no, ptrlen authproto, ptrlen authdata); int x11_identify_auth_proto(ptrlen protoname); void *x11_dehexify(ptrlen hex, int *outlen); Channel *agentf_new(SshChannel *c); bool dh_is_gex(const ssh_kex *kex); dh_ctx *dh_setup_group(const ssh_kex *kex); dh_ctx *dh_setup_gex(mp_int *pval, mp_int *gval); int dh_modulus_bit_size(const dh_ctx *ctx); void dh_cleanup(dh_ctx *); mp_int *dh_create_e(dh_ctx *, int nbits); const char *dh_validate_f(dh_ctx *, mp_int *f); mp_int *dh_find_K(dh_ctx *, mp_int *f); static inline bool is_base64_char(char c) { return ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '+' || c == '/' || c == '='); } extern int base64_decode_atom(const char *atom, unsigned char *out); extern int base64_lines(int datalen); extern void base64_encode_atom(const unsigned char *data, int n, char *out); extern void base64_encode(FILE *fp, const unsigned char *data, int datalen, int cpl); /* ppk_load_* can return this as an error */ extern ssh2_userkey ssh2_wrong_passphrase; #define SSH2_WRONG_PASSPHRASE (&ssh2_wrong_passphrase) bool ppk_encrypted_s(BinarySource *src, char **comment); bool ppk_encrypted_f(const Filename *filename, char **comment); bool rsa1_encrypted_s(BinarySource *src, char **comment); bool rsa1_encrypted_f(const Filename *filename, char **comment); ssh2_userkey *ppk_load_s(BinarySource *src, const char *passphrase, const char **errorstr); ssh2_userkey *ppk_load_f(const Filename *filename, const char *passphrase, const char **errorstr); int rsa1_load_s(BinarySource *src, RSAKey *key, const char *passphrase, const char **errorstr); int rsa1_load_f(const Filename *filename, RSAKey *key, const char *passphrase, const char **errorstr); typedef struct ppk_save_parameters { unsigned fmt_version; /* currently 2 or 3 */ /* * Parameters for fmt_version == 3 */ Argon2Flavour argon2_flavour; uint32_t argon2_mem; /* in Kbyte */ bool argon2_passes_auto; union { uint32_t argon2_passes; /* if auto == false */ uint32_t argon2_milliseconds; /* if auto == true */ }; uint32_t argon2_parallelism; /* The ability to choose a specific salt is only intended for the * use of the automated test of PuTTYgen. It's a (mild) security * risk to do it with any passphrase you actually care about, * because it invalidates the entire point of having a salt in the * first place. */ const uint8_t *salt; size_t saltlen; } ppk_save_parameters; extern const ppk_save_parameters ppk_save_default_parameters; strbuf *ppk_save_sb(ssh2_userkey *key, const char *passphrase, const ppk_save_parameters *params); bool ppk_save_f(const Filename *filename, ssh2_userkey *key, const char *passphrase, const ppk_save_parameters *params); strbuf *rsa1_save_sb(RSAKey *key, const char *passphrase); bool rsa1_save_f(const Filename *filename, RSAKey *key, const char *passphrase); bool ppk_loadpub_s(BinarySource *src, char **algorithm, BinarySink *bs, char **commentptr, const char **errorstr); bool ppk_loadpub_f(const Filename *filename, char **algorithm, BinarySink *bs, char **commentptr, const char **errorstr); int rsa1_loadpub_s(BinarySource *src, BinarySink *bs, char **commentptr, const char **errorstr); int rsa1_loadpub_f(const Filename *filename, BinarySink *bs, char **commentptr, const char **errorstr); extern const ssh_keyalg *const all_keyalgs[]; extern const size_t n_keyalgs; const ssh_keyalg *find_pubkey_alg(const char *name); const ssh_keyalg *find_pubkey_alg_len(ptrlen name); /* Convenient wrappers on the LoadedFile mechanism suitable for key files */ LoadedFile *lf_load_keyfile(const Filename *filename, const char **errptr); LoadedFile *lf_load_keyfile_fp(FILE *fp, const char **errptr); enum { SSH_KEYTYPE_UNOPENABLE, SSH_KEYTYPE_UNKNOWN, SSH_KEYTYPE_SSH1, SSH_KEYTYPE_SSH2, /* * The OpenSSH key types deserve a little explanation. OpenSSH has * two physical formats for private key storage: an old PEM-based * one largely dictated by their use of OpenSSL and full of ASN.1, * and a new one using the same private key formats used over the * wire for talking to ssh-agent. The old format can only support * a subset of the key types, because it needs redesign for each * key type, and after a while they decided to move to the new * format so as not to have to do that. * * On input, key files are identified as either * SSH_KEYTYPE_OPENSSH_PEM or SSH_KEYTYPE_OPENSSH_NEW, describing * accurately which actual format the keys are stored in. * * On output, however, we default to following OpenSSH's own * policy of writing out PEM-style keys for maximum backwards * compatibility if the key type supports it, and otherwise * switching to the new format. So the formats you can select for * output are SSH_KEYTYPE_OPENSSH_NEW (forcing the new format for * any key type), and SSH_KEYTYPE_OPENSSH_AUTO to use the oldest * format supported by whatever key type you're writing out. * * So we have three type codes, but only two of them usable in any * given circumstance. An input key file will never be identified * as AUTO, only PEM or NEW; key export UIs should not be able to * select PEM, only AUTO or NEW. */ SSH_KEYTYPE_OPENSSH_AUTO, SSH_KEYTYPE_OPENSSH_PEM, SSH_KEYTYPE_OPENSSH_NEW, SSH_KEYTYPE_SSHCOM, /* * Public-key-only formats, which we still want to be able to read * for various purposes. */ SSH_KEYTYPE_SSH1_PUBLIC, SSH_KEYTYPE_SSH2_PUBLIC_RFC4716, SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH }; typedef enum { SSH_FPTYPE_MD5, SSH_FPTYPE_SHA256, } FingerprintType; #define SSH_FPTYPE_DEFAULT SSH_FPTYPE_SHA256 #define SSH_N_FPTYPES (SSH_FPTYPE_SHA256 + 1) FingerprintType ssh2_pick_fingerprint(char **fingerprints, FingerprintType preferred_type); FingerprintType ssh2_pick_default_fingerprint(char **fingerprints); char *ssh1_pubkey_str(RSAKey *ssh1key); void ssh1_write_pubkey(FILE *fp, RSAKey *ssh1key); char *ssh2_pubkey_openssh_str(ssh2_userkey *key); void ssh2_write_pubkey(FILE *fp, const char *comment, const void *v_pub_blob, int pub_len, int keytype); char *ssh2_fingerprint_blob(ptrlen, FingerprintType); char *ssh2_fingerprint(ssh_key *key, FingerprintType); char **ssh2_all_fingerprints_for_blob(ptrlen); char **ssh2_all_fingerprints(ssh_key *key); void ssh2_free_all_fingerprints(char **); int key_type(const Filename *filename); int key_type_s(BinarySource *src); const char *key_type_to_str(int type); bool import_possible(int type); int import_target_type(int type); bool import_encrypted(const Filename *filename, int type, char **comment); bool import_encrypted_s(const Filename *filename, BinarySource *src, int type, char **comment); int import_ssh1(const Filename *filename, int type, RSAKey *key, char *passphrase, const char **errmsg_p); int import_ssh1_s(BinarySource *src, int type, RSAKey *key, char *passphrase, const char **errmsg_p); ssh2_userkey *import_ssh2(const Filename *filename, int type, char *passphrase, const char **errmsg_p); ssh2_userkey *import_ssh2_s(BinarySource *src, int type, char *passphrase, const char **errmsg_p); bool export_ssh1(const Filename *filename, int type, RSAKey *key, char *passphrase); bool export_ssh2(const Filename *filename, int type, ssh2_userkey *key, char *passphrase); void des3_decrypt_pubkey(const void *key, void *blk, int len); void des3_encrypt_pubkey(const void *key, void *blk, int len); void des3_decrypt_pubkey_ossh(const void *key, const void *iv, void *blk, int len); void des3_encrypt_pubkey_ossh(const void *key, const void *iv, void *blk, int len); void aes256_encrypt_pubkey(const void *key, const void *iv, void *blk, int len); void aes256_decrypt_pubkey(const void *key, const void *iv, void *blk, int len); void des_encrypt_xdmauth(const void *key, void *blk, int len); void des_decrypt_xdmauth(const void *key, void *blk, int len); void openssh_bcrypt(const char *passphrase, const unsigned char *salt, int saltbytes, int rounds, unsigned char *out, int outbytes); /* * 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, bool can_upstream, bool can_downstream); void platform_ssh_share_cleanup(const char *name); /* * List macro defining the SSH-1 message type codes. */ #define SSH1_MESSAGE_TYPES(X, y) \ X(y, SSH1_MSG_DISCONNECT, 1) \ X(y, SSH1_SMSG_PUBLIC_KEY, 2) \ X(y, SSH1_CMSG_SESSION_KEY, 3) \ X(y, SSH1_CMSG_USER, 4) \ X(y, SSH1_CMSG_AUTH_RSA, 6) \ X(y, SSH1_SMSG_AUTH_RSA_CHALLENGE, 7) \ X(y, SSH1_CMSG_AUTH_RSA_RESPONSE, 8) \ X(y, SSH1_CMSG_AUTH_PASSWORD, 9) \ X(y, SSH1_CMSG_REQUEST_PTY, 10) \ X(y, SSH1_CMSG_WINDOW_SIZE, 11) \ X(y, SSH1_CMSG_EXEC_SHELL, 12) \ X(y, SSH1_CMSG_EXEC_CMD, 13) \ X(y, SSH1_SMSG_SUCCESS, 14) \ X(y, SSH1_SMSG_FAILURE, 15) \ X(y, SSH1_CMSG_STDIN_DATA, 16) \ X(y, SSH1_SMSG_STDOUT_DATA, 17) \ X(y, SSH1_SMSG_STDERR_DATA, 18) \ X(y, SSH1_CMSG_EOF, 19) \ X(y, SSH1_SMSG_EXIT_STATUS, 20) \ X(y, SSH1_MSG_CHANNEL_OPEN_CONFIRMATION, 21) \ X(y, SSH1_MSG_CHANNEL_OPEN_FAILURE, 22) \ X(y, SSH1_MSG_CHANNEL_DATA, 23) \ X(y, SSH1_MSG_CHANNEL_CLOSE, 24) \ X(y, SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION, 25) \ X(y, SSH1_SMSG_X11_OPEN, 27) \ X(y, SSH1_CMSG_PORT_FORWARD_REQUEST, 28) \ X(y, SSH1_MSG_PORT_OPEN, 29) \ X(y, SSH1_CMSG_AGENT_REQUEST_FORWARDING, 30) \ X(y, SSH1_SMSG_AGENT_OPEN, 31) \ X(y, SSH1_MSG_IGNORE, 32) \ X(y, SSH1_CMSG_EXIT_CONFIRMATION, 33) \ X(y, SSH1_CMSG_X11_REQUEST_FORWARDING, 34) \ X(y, SSH1_CMSG_AUTH_RHOSTS_RSA, 35) \ X(y, SSH1_MSG_DEBUG, 36) \ X(y, SSH1_CMSG_REQUEST_COMPRESSION, 37) \ X(y, SSH1_CMSG_AUTH_TIS, 39) \ X(y, SSH1_SMSG_AUTH_TIS_CHALLENGE, 40) \ X(y, SSH1_CMSG_AUTH_TIS_RESPONSE, 41) \ X(y, SSH1_CMSG_AUTH_CCARD, 70) \ X(y, SSH1_SMSG_AUTH_CCARD_CHALLENGE, 71) \ X(y, SSH1_CMSG_AUTH_CCARD_RESPONSE, 72) \ /* end of list */ #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 */ /* * List macro defining SSH-2 message type codes. Some of these depend * on particular contexts (i.e. a previously negotiated kex or auth * method) */ #define SSH2_MESSAGE_TYPES(X, K, A, y) \ X(y, SSH2_MSG_DISCONNECT, 1) \ X(y, SSH2_MSG_IGNORE, 2) \ X(y, SSH2_MSG_UNIMPLEMENTED, 3) \ X(y, SSH2_MSG_DEBUG, 4) \ X(y, SSH2_MSG_SERVICE_REQUEST, 5) \ X(y, SSH2_MSG_SERVICE_ACCEPT, 6) \ X(y, SSH2_MSG_EXT_INFO, 7) \ X(y, SSH2_MSG_KEXINIT, 20) \ X(y, SSH2_MSG_NEWKEYS, 21) \ K(y, SSH2_MSG_KEXDH_INIT, 30, SSH2_PKTCTX_DHGROUP) \ K(y, SSH2_MSG_KEXDH_REPLY, 31, SSH2_PKTCTX_DHGROUP) \ K(y, SSH2_MSG_KEX_DH_GEX_REQUEST_OLD, 30, SSH2_PKTCTX_DHGEX) \ K(y, SSH2_MSG_KEX_DH_GEX_REQUEST, 34, SSH2_PKTCTX_DHGEX) \ K(y, SSH2_MSG_KEX_DH_GEX_GROUP, 31, SSH2_PKTCTX_DHGEX) \ K(y, SSH2_MSG_KEX_DH_GEX_INIT, 32, SSH2_PKTCTX_DHGEX) \ K(y, SSH2_MSG_KEX_DH_GEX_REPLY, 33, SSH2_PKTCTX_DHGEX) \ K(y, SSH2_MSG_KEXGSS_INIT, 30, SSH2_PKTCTX_GSSKEX) \ K(y, SSH2_MSG_KEXGSS_CONTINUE, 31, SSH2_PKTCTX_GSSKEX) \ K(y, SSH2_MSG_KEXGSS_COMPLETE, 32, SSH2_PKTCTX_GSSKEX) \ K(y, SSH2_MSG_KEXGSS_HOSTKEY, 33, SSH2_PKTCTX_GSSKEX) \ K(y, SSH2_MSG_KEXGSS_ERROR, 34, SSH2_PKTCTX_GSSKEX) \ K(y, SSH2_MSG_KEXGSS_GROUPREQ, 40, SSH2_PKTCTX_GSSKEX) \ K(y, SSH2_MSG_KEXGSS_GROUP, 41, SSH2_PKTCTX_GSSKEX) \ K(y, SSH2_MSG_KEXRSA_PUBKEY, 30, SSH2_PKTCTX_RSAKEX) \ K(y, SSH2_MSG_KEXRSA_SECRET, 31, SSH2_PKTCTX_RSAKEX) \ K(y, SSH2_MSG_KEXRSA_DONE, 32, SSH2_PKTCTX_RSAKEX) \ K(y, SSH2_MSG_KEX_ECDH_INIT, 30, SSH2_PKTCTX_ECDHKEX) \ K(y, SSH2_MSG_KEX_ECDH_REPLY, 31, SSH2_PKTCTX_ECDHKEX) \ X(y, SSH2_MSG_USERAUTH_REQUEST, 50) \ X(y, SSH2_MSG_USERAUTH_FAILURE, 51) \ X(y, SSH2_MSG_USERAUTH_SUCCESS, 52) \ X(y, SSH2_MSG_USERAUTH_BANNER, 53) \ A(y, SSH2_MSG_USERAUTH_PK_OK, 60, SSH2_PKTCTX_PUBLICKEY) \ A(y, SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ, 60, SSH2_PKTCTX_PASSWORD) \ A(y, SSH2_MSG_USERAUTH_INFO_REQUEST, 60, SSH2_PKTCTX_KBDINTER) \ A(y, SSH2_MSG_USERAUTH_INFO_RESPONSE, 61, SSH2_PKTCTX_KBDINTER) \ A(y, SSH2_MSG_USERAUTH_GSSAPI_RESPONSE, 60, SSH2_PKTCTX_GSSAPI) \ A(y, SSH2_MSG_USERAUTH_GSSAPI_TOKEN, 61, SSH2_PKTCTX_GSSAPI) \ A(y, SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, 63, SSH2_PKTCTX_GSSAPI) \ A(y, SSH2_MSG_USERAUTH_GSSAPI_ERROR, 64, SSH2_PKTCTX_GSSAPI) \ A(y, SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, 65, SSH2_PKTCTX_GSSAPI) \ A(y, SSH2_MSG_USERAUTH_GSSAPI_MIC, 66, SSH2_PKTCTX_GSSAPI) \ X(y, SSH2_MSG_GLOBAL_REQUEST, 80) \ X(y, SSH2_MSG_REQUEST_SUCCESS, 81) \ X(y, SSH2_MSG_REQUEST_FAILURE, 82) \ X(y, SSH2_MSG_CHANNEL_OPEN, 90) \ X(y, SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, 91) \ X(y, SSH2_MSG_CHANNEL_OPEN_FAILURE, 92) \ X(y, SSH2_MSG_CHANNEL_WINDOW_ADJUST, 93) \ X(y, SSH2_MSG_CHANNEL_DATA, 94) \ X(y, SSH2_MSG_CHANNEL_EXTENDED_DATA, 95) \ X(y, SSH2_MSG_CHANNEL_EOF, 96) \ X(y, SSH2_MSG_CHANNEL_CLOSE, 97) \ X(y, SSH2_MSG_CHANNEL_REQUEST, 98) \ X(y, SSH2_MSG_CHANNEL_SUCCESS, 99) \ X(y, SSH2_MSG_CHANNEL_FAILURE, 100) \ /* end of list */ #define DEF_ENUM_UNIVERSAL(y, name, value) name = value, #define DEF_ENUM_CONTEXTUAL(y, name, value, context) name = value, enum { SSH1_MESSAGE_TYPES(DEF_ENUM_UNIVERSAL, y) SSH2_MESSAGE_TYPES(DEF_ENUM_UNIVERSAL, DEF_ENUM_CONTEXTUAL, DEF_ENUM_CONTEXTUAL, y) /* Virtual packet type, for packets too short to even have a type */ SSH_MSG_NO_TYPE_CODE = 256 }; #undef DEF_ENUM_UNIVERSAL #undef DEF_ENUM_CONTEXTUAL /* * 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 #define SSH1_AGENTC_ADD_RSA_IDENTITY 7 #define SSH1_AGENTC_REMOVE_RSA_IDENTITY 8 #define SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES 9 /* openssh private? */ /* * Messages common to SSH-1 and OpenSSH's SSH-2. */ #define SSH_AGENT_FAILURE 5 #define SSH_AGENT_SUCCESS 6 /* * OpenSSH's SSH-2 agent messages. */ #define SSH2_AGENTC_REQUEST_IDENTITIES 11 #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 #define SSH2_AGENTC_EXTENSION 27 #define SSH_AGENT_EXTENSION_FAILURE 28 /* * 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 */ enum { /* TTY modes with opcodes defined consistently in the SSH specs. */ #define TTYMODE_CHAR(name, val, index) SSH_TTYMODE_##name = val, #define TTYMODE_FLAG(name, val, field, mask) SSH_TTYMODE_##name = val, #include "sshttymodes.h" #undef TTYMODE_CHAR #undef TTYMODE_FLAG /* Modes encoded differently between SSH-1 and SSH-2, for which we * make up our own dummy opcodes to avoid confusion. */ TTYMODE_dummy = 255, TTYMODE_ISPEED, TTYMODE_OSPEED, /* Limiting value that we can use as an array bound below */ TTYMODE_LIMIT, /* The real opcodes for terminal speeds. */ TTYMODE_ISPEED_SSH1 = 192, TTYMODE_OSPEED_SSH1 = 193, TTYMODE_ISPEED_SSH2 = 128, TTYMODE_OSPEED_SSH2 = 129, /* And the opcode that ends a list. */ TTYMODE_END_OF_LIST = 0 }; struct ssh_ttymodes { /* A boolean per mode, indicating whether it's set. */ bool have_mode[TTYMODE_LIMIT]; /* The actual value for each mode. */ unsigned mode_val[TTYMODE_LIMIT]; }; struct ssh_ttymodes get_ttymodes_from_conf(Seat *seat, Conf *conf); struct ssh_ttymodes read_ttymodes_from_packet( BinarySource *bs, int ssh_version); void write_ttymodes_to_packet(BinarySink *bs, int ssh_version, struct ssh_ttymodes modes); const char *ssh1_pkt_type(int type); const char *ssh2_pkt_type(Pkt_KCtx pkt_kctx, Pkt_ACtx pkt_actx, int type); bool ssh2_pkt_type_code_valid(unsigned type); /* * Need this to warn about support for the original SSH-2 keyfile * format. */ void old_keyfile_warning(void); /* * Flags indicating implementation bugs that we know how to mitigate * if we think the other end has them. */ #define SSH_IMPL_BUG_LIST(X) \ X(BUG_CHOKES_ON_SSH1_IGNORE) \ X(BUG_SSH2_HMAC) \ X(BUG_NEEDS_SSH1_PLAIN_PASSWORD) \ X(BUG_CHOKES_ON_RSA) \ X(BUG_SSH2_RSA_PADDING) \ X(BUG_SSH2_DERIVEKEY) \ X(BUG_SSH2_REKEY) \ X(BUG_SSH2_PK_SESSIONID) \ X(BUG_SSH2_MAXPKT) \ X(BUG_CHOKES_ON_SSH2_IGNORE) \ X(BUG_CHOKES_ON_WINADJ) \ X(BUG_SENDS_LATE_REQUEST_REPLY) \ X(BUG_SSH2_OLDGEX) \ /* end of list */ #define TMP_DECLARE_LOG2_ENUM(thing) log2_##thing, enum { SSH_IMPL_BUG_LIST(TMP_DECLARE_LOG2_ENUM) }; #undef TMP_DECLARE_LOG2_ENUM #define TMP_DECLARE_REAL_ENUM(thing) thing = 1 << log2_##thing, enum { SSH_IMPL_BUG_LIST(TMP_DECLARE_REAL_ENUM) }; #undef TMP_DECLARE_REAL_ENUM /* Shared system for allocating local SSH channel ids. Expects to be * passed a tree full of structs that have a field called 'localid' of * type unsigned, and will check that! */ unsigned alloc_channel_id_general(tree234 *channels, size_t localid_offset); #define alloc_channel_id(tree, type) \ TYPECHECK(&((type *)0)->localid == (unsigned *)0, \ alloc_channel_id_general(tree, offsetof(type, localid))) void add_to_commasep(strbuf *buf, const char *data); bool get_commasep_word(ptrlen *list, ptrlen *word); int verify_ssh_manual_host_key(Conf *conf, char **fingerprints, ssh_key *key); typedef struct ssh_transient_hostkey_cache ssh_transient_hostkey_cache; ssh_transient_hostkey_cache *ssh_transient_hostkey_cache_new(void); void ssh_transient_hostkey_cache_free(ssh_transient_hostkey_cache *thc); void ssh_transient_hostkey_cache_add( ssh_transient_hostkey_cache *thc, ssh_key *key); bool ssh_transient_hostkey_cache_verify( ssh_transient_hostkey_cache *thc, ssh_key *key); bool ssh_transient_hostkey_cache_has( ssh_transient_hostkey_cache *thc, const ssh_keyalg *alg); bool ssh_transient_hostkey_cache_non_empty(ssh_transient_hostkey_cache *thc); putty-0.76/ssh1bpp.c0000644000175000017500000003054514072266311011311 00000000000000/* * Binary packet protocol for SSH-1. */ #include #include "putty.h" #include "ssh.h" #include "sshbpp.h" #include "sshcr.h" struct ssh1_bpp_state { int crState; long len, pad, biglen, length, maxlen; unsigned char *data; uint32_t realcrc, gotcrc; int chunk; PktIn *pktin; ssh_cipher *cipher_in, *cipher_out; struct crcda_ctx *crcda_ctx; uint8_t iv[8]; /* for crcda */ bool pending_compression_request; ssh_compressor *compctx; ssh_decompressor *decompctx; BinaryPacketProtocol bpp; }; static void ssh1_bpp_free(BinaryPacketProtocol *bpp); static void ssh1_bpp_handle_input(BinaryPacketProtocol *bpp); static void ssh1_bpp_handle_output(BinaryPacketProtocol *bpp); static void ssh1_bpp_queue_disconnect(BinaryPacketProtocol *bpp, const char *msg, int category); static PktOut *ssh1_bpp_new_pktout(int type); static const BinaryPacketProtocolVtable ssh1_bpp_vtable = { .free = ssh1_bpp_free, .handle_input = ssh1_bpp_handle_input, .handle_output = ssh1_bpp_handle_output, .new_pktout = ssh1_bpp_new_pktout, .queue_disconnect = ssh1_bpp_queue_disconnect, .packet_size_limit = 0xFFFFFFFF, /* no special limit for this bpp */ }; BinaryPacketProtocol *ssh1_bpp_new(LogContext *logctx) { struct ssh1_bpp_state *s = snew(struct ssh1_bpp_state); memset(s, 0, sizeof(*s)); s->bpp.vt = &ssh1_bpp_vtable; s->bpp.logctx = logctx; ssh_bpp_common_setup(&s->bpp); return &s->bpp; } static void ssh1_bpp_free(BinaryPacketProtocol *bpp) { struct ssh1_bpp_state *s = container_of(bpp, struct ssh1_bpp_state, bpp); if (s->cipher_in) ssh_cipher_free(s->cipher_in); if (s->cipher_out) ssh_cipher_free(s->cipher_out); if (s->compctx) ssh_compressor_free(s->compctx); if (s->decompctx) ssh_decompressor_free(s->decompctx); if (s->crcda_ctx) crcda_free_context(s->crcda_ctx); sfree(s->pktin); sfree(s); } void ssh1_bpp_new_cipher(BinaryPacketProtocol *bpp, const ssh_cipheralg *cipher, const void *session_key) { struct ssh1_bpp_state *s; assert(bpp->vt == &ssh1_bpp_vtable); s = container_of(bpp, struct ssh1_bpp_state, bpp); assert(!s->cipher_in); assert(!s->cipher_out); if (cipher) { s->cipher_in = ssh_cipher_new(cipher); s->cipher_out = ssh_cipher_new(cipher); ssh_cipher_setkey(s->cipher_in, session_key); ssh_cipher_setkey(s->cipher_out, session_key); assert(!s->crcda_ctx); s->crcda_ctx = crcda_make_context(); bpp_logevent("Initialised %s encryption", cipher->text_name); memset(s->iv, 0, sizeof(s->iv)); assert(cipher->blksize <= sizeof(s->iv)); ssh_cipher_setiv(s->cipher_in, s->iv); ssh_cipher_setiv(s->cipher_out, s->iv); } } void ssh1_bpp_start_compression(BinaryPacketProtocol *bpp) { struct ssh1_bpp_state *s; assert(bpp->vt == &ssh1_bpp_vtable); s = container_of(bpp, struct ssh1_bpp_state, bpp); assert(!s->compctx); assert(!s->decompctx); s->compctx = ssh_compressor_new(&ssh_zlib); s->decompctx = ssh_decompressor_new(&ssh_zlib); bpp_logevent("Started zlib (RFC1950) compression"); } #define BPP_READ(ptr, len) do \ { \ bool success; \ crMaybeWaitUntilV((success = bufchain_try_fetch_consume( \ s->bpp.in_raw, ptr, len)) || \ s->bpp.input_eof); \ if (!success) \ goto eof; \ ssh_check_frozen(s->bpp.ssh); \ } while (0) static void ssh1_bpp_handle_input(BinaryPacketProtocol *bpp) { struct ssh1_bpp_state *s = container_of(bpp, struct ssh1_bpp_state, bpp); crBegin(s->crState); while (1) { s->maxlen = 0; s->length = 0; { unsigned char lenbuf[4]; BPP_READ(lenbuf, 4); s->len = toint(GET_32BIT_MSB_FIRST(lenbuf)); } if (s->len < 5 || s->len > 262144) { /* SSH1.5-mandated max size */ ssh_sw_abort(s->bpp.ssh, "Out-of-range packet length from remote suggests" " data stream corruption"); crStopV; } s->pad = 8 - (s->len % 8); s->biglen = s->len + s->pad; s->length = s->len - 5; /* * Allocate the packet to return, now we know its length. */ s->pktin = snew_plus(PktIn, s->biglen); s->pktin->qnode.prev = s->pktin->qnode.next = NULL; s->pktin->qnode.on_free_queue = false; s->pktin->type = 0; s->maxlen = s->biglen; s->data = snew_plus_get_aux(s->pktin); BPP_READ(s->data, s->biglen); if (s->cipher_in && detect_attack(s->crcda_ctx, s->data, s->biglen, s->iv)) { ssh_sw_abort(s->bpp.ssh, "Network attack (CRC compensation) detected!"); crStopV; } /* Save the last cipher block, to be passed to the next call * to detect_attack */ assert(s->biglen >= 8); memcpy(s->iv, s->data + s->biglen - 8, sizeof(s->iv)); if (s->cipher_in) ssh_cipher_decrypt(s->cipher_in, s->data, s->biglen); s->realcrc = crc32_ssh1(make_ptrlen(s->data, s->biglen - 4)); s->gotcrc = GET_32BIT_MSB_FIRST(s->data + s->biglen - 4); if (s->gotcrc != s->realcrc) { ssh_sw_abort(s->bpp.ssh, "Incorrect CRC received on packet"); crStopV; } if (s->decompctx) { unsigned char *decompblk; int decomplen; if (!ssh_decompressor_decompress( s->decompctx, s->data + s->pad, s->length + 1, &decompblk, &decomplen)) { ssh_sw_abort(s->bpp.ssh, "Zlib decompression encountered invalid data"); crStopV; } if (s->maxlen < s->pad + decomplen) { PktIn *old_pktin = s->pktin; s->maxlen = s->pad + decomplen; s->pktin = snew_plus(PktIn, s->maxlen); *s->pktin = *old_pktin; /* structure copy */ s->data = snew_plus_get_aux(s->pktin); smemclr(old_pktin, s->biglen); sfree(old_pktin); } memcpy(s->data + s->pad, decompblk, decomplen); sfree(decompblk); s->length = decomplen - 1; } /* * Now we can find the bounds of the semantic content of the * packet, and the initial type byte. */ s->data += s->pad; s->pktin->type = *s->data++; BinarySource_INIT(s->pktin, s->data, s->length); if (s->bpp.logctx) { logblank_t blanks[MAX_BLANKS]; int nblanks = ssh1_censor_packet( s->bpp.pls, s->pktin->type, false, make_ptrlen(s->data, s->length), blanks); log_packet(s->bpp.logctx, PKT_INCOMING, s->pktin->type, ssh1_pkt_type(s->pktin->type), get_ptr(s->pktin), get_avail(s->pktin), nblanks, blanks, NULL, 0, NULL); } s->pktin->qnode.formal_size = get_avail(s->pktin); pq_push(&s->bpp.in_pq, s->pktin); { int type = s->pktin->type; s->pktin = NULL; switch (type) { case SSH1_SMSG_SUCCESS: case SSH1_SMSG_FAILURE: if (s->pending_compression_request) { /* * This is the response to * SSH1_CMSG_REQUEST_COMPRESSION. */ if (type == SSH1_SMSG_SUCCESS) { /* * If the response was positive, start * compression. */ ssh1_bpp_start_compression(&s->bpp); } /* * Either way, cancel the pending flag, and * schedule a run of our output side in case we * had any packets queued up in the meantime. */ s->pending_compression_request = false; queue_idempotent_callback(&s->bpp.ic_out_pq); } break; } } } eof: if (!s->bpp.expect_close) { ssh_remote_error(s->bpp.ssh, "Remote side unexpectedly closed network connection"); } else { ssh_remote_eof(s->bpp.ssh, "Remote side closed network connection"); } return; /* avoid touching s now it's been freed */ crFinishV; } static PktOut *ssh1_bpp_new_pktout(int pkt_type) { PktOut *pkt = ssh_new_packet(); pkt->length = 4 + 8; /* space for length + max padding */ put_byte(pkt, pkt_type); pkt->prefix = pkt->length; pkt->type = pkt_type; pkt->downstream_id = 0; pkt->additional_log_text = NULL; return pkt; } static void ssh1_bpp_format_packet(struct ssh1_bpp_state *s, PktOut *pkt) { int pad, biglen, pktoffs; uint32_t crc; int len; if (s->bpp.logctx) { ptrlen pktdata = make_ptrlen(pkt->data + pkt->prefix, pkt->length - pkt->prefix); logblank_t blanks[MAX_BLANKS]; int nblanks = ssh1_censor_packet( s->bpp.pls, pkt->type, true, pktdata, blanks); log_packet(s->bpp.logctx, PKT_OUTGOING, pkt->type, ssh1_pkt_type(pkt->type), pktdata.ptr, pktdata.len, nblanks, blanks, NULL, 0, NULL); } if (s->compctx) { unsigned char *compblk; int complen; ssh_compressor_compress(s->compctx, pkt->data + 12, pkt->length - 12, &compblk, &complen, 0); /* Replace the uncompressed packet data with the compressed * version. */ pkt->length = 12; put_data(pkt, compblk, complen); sfree(compblk); } put_uint32(pkt, 0); /* space for CRC */ len = pkt->length - 4 - 8; /* len(type+data+CRC) */ pad = 8 - (len % 8); pktoffs = 8 - pad; biglen = len + pad; /* len(padding+type+data+CRC) */ random_read(pkt->data + pktoffs, 4+8 - pktoffs); crc = crc32_ssh1( make_ptrlen(pkt->data + pktoffs + 4, biglen - 4)); /* all ex len */ PUT_32BIT_MSB_FIRST(pkt->data + pktoffs + 4 + biglen - 4, crc); PUT_32BIT_MSB_FIRST(pkt->data + pktoffs, len); if (s->cipher_out) ssh_cipher_encrypt(s->cipher_out, pkt->data + pktoffs + 4, biglen); bufchain_add(s->bpp.out_raw, pkt->data + pktoffs, biglen + 4); /* len(length+padding+type+data+CRC) */ } static void ssh1_bpp_handle_output(BinaryPacketProtocol *bpp) { struct ssh1_bpp_state *s = container_of(bpp, struct ssh1_bpp_state, bpp); PktOut *pkt; if (s->pending_compression_request) { /* * Don't send any output packets while we're awaiting a * response to SSH1_CMSG_REQUEST_COMPRESSION, because if they * cross over in transit with the responding SSH1_CMSG_SUCCESS * then the other end could decode them with the wrong * compression settings. */ return; } while ((pkt = pq_pop(&s->bpp.out_pq)) != NULL) { int type = pkt->type; ssh1_bpp_format_packet(s, pkt); ssh_free_pktout(pkt); if (type == SSH1_CMSG_REQUEST_COMPRESSION) { /* * When we see the actual compression request go past, set * the pending flag, and stop processing packets this * time. */ s->pending_compression_request = true; break; } } } static void ssh1_bpp_queue_disconnect(BinaryPacketProtocol *bpp, const char *msg, int category) { PktOut *pkt = ssh_bpp_new_pktout(bpp, SSH1_MSG_DISCONNECT); put_stringz(pkt, msg); pq_push(&bpp->out_pq, pkt); } putty-0.76/ssh1censor.c0000644000175000017500000000504714072266311012020 00000000000000/* * Packet-censoring code for SSH-1, used to identify sensitive fields * like passwords so that the logging system can avoid writing them * into log files. */ #include #include "putty.h" #include "ssh.h" int ssh1_censor_packet( const PacketLogSettings *pls, int type, bool sender_is_client, ptrlen pkt, logblank_t *blanks) { int nblanks = 0; ptrlen str; BinarySource src[1]; BinarySource_BARE_INIT_PL(src, pkt); if (pls->omit_data && (type == SSH1_SMSG_STDOUT_DATA || type == SSH1_SMSG_STDERR_DATA || type == SSH1_CMSG_STDIN_DATA || type == SSH1_MSG_CHANNEL_DATA)) { /* "Session data" packets - omit the data string. */ if (type == SSH1_MSG_CHANNEL_DATA) get_uint32(src); /* skip channel id */ str = get_string(src); if (!get_err(src)) { assert(nblanks < MAX_BLANKS); blanks[nblanks].offset = src->pos - str.len; blanks[nblanks].type = PKTLOG_OMIT; blanks[nblanks].len = str.len; nblanks++; } } if (sender_is_client && pls->omit_passwords) { if (type == SSH1_CMSG_AUTH_PASSWORD || type == SSH1_CMSG_AUTH_TIS_RESPONSE || type == SSH1_CMSG_AUTH_CCARD_RESPONSE) { /* If this is a password or similar packet, blank the * password(s). */ assert(nblanks < MAX_BLANKS); blanks[nblanks].offset = 0; blanks[nblanks].len = pkt.len; blanks[nblanks].type = PKTLOG_BLANK; nblanks++; } else if (type == SSH1_CMSG_X11_REQUEST_FORWARDING) { /* * 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. */ get_string(src); /* skip protocol name */ str = get_string(src); if (!get_err(src)) { assert(nblanks < MAX_BLANKS); blanks[nblanks].offset = src->pos - str.len; blanks[nblanks].type = PKTLOG_BLANK; blanks[nblanks].len = str.len; nblanks++; } } } return nblanks; } putty-0.76/ssh1connection-client.c0000644000175000017500000004243114072266311014140 00000000000000/* * Client-specific parts of the SSH-1 connection layer. */ #include #include "putty.h" #include "ssh.h" #include "sshbpp.h" #include "sshppl.h" #include "sshchan.h" #include "sshcr.h" #include "ssh1connection.h" void ssh1_connection_direction_specific_setup( struct ssh1_connection_state *s) { if (!s->mainchan) { /* * Start up the main session, by telling mainchan.c to do it * all just as it would in SSH-2, and translating those * concepts to SSH-1's non-channel-shaped idea of the main * session. */ s->mainchan = mainchan_new( &s->ppl, &s->cl, s->conf, s->term_width, s->term_height, false /* is_simple */, NULL); } } typedef void (*sf_handler_fn_t)(struct ssh1_connection_state *s, bool success, void *ctx); struct outstanding_succfail { sf_handler_fn_t handler; void *ctx; struct outstanding_succfail *next; /* * The 'trivial' flag is set if this handler is in response to a * request for which the SSH-1 protocol doesn't actually specify a * response packet. The client of this system (mainchan.c) will * expect to get an acknowledgment regardless, so we arrange to * send that ack immediately after the rest of the queue empties. */ bool trivial; }; static void ssh1_connection_process_trivial_succfails(void *vs); static void ssh1_queue_succfail_handler( struct ssh1_connection_state *s, sf_handler_fn_t handler, void *ctx, bool trivial) { struct outstanding_succfail *osf = snew(struct outstanding_succfail); osf->handler = handler; osf->ctx = ctx; osf->trivial = trivial; osf->next = NULL; if (s->succfail_tail) s->succfail_tail->next = osf; else s->succfail_head = osf; s->succfail_tail = osf; /* In case this one was trivial and the queue was already empty, * we should make sure we run the handler promptly, and the * easiest way is to queue it anyway and then run a trivials pass * by callback. */ queue_toplevel_callback(ssh1_connection_process_trivial_succfails, s); } static void ssh1_connection_process_succfail( struct ssh1_connection_state *s, bool success) { struct outstanding_succfail *prevhead = s->succfail_head; s->succfail_head = s->succfail_head->next; if (!s->succfail_head) s->succfail_tail = NULL; prevhead->handler(s, success, prevhead->ctx); sfree(prevhead); } static void ssh1_connection_process_trivial_succfails(void *vs) { struct ssh1_connection_state *s = (struct ssh1_connection_state *)vs; while (s->succfail_head && s->succfail_head->trivial) ssh1_connection_process_succfail(s, true); } bool ssh1_handle_direction_specific_packet( struct ssh1_connection_state *s, PktIn *pktin) { PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */ PktOut *pktout; struct ssh1_channel *c; unsigned remid; struct ssh_rportfwd pf, *pfp; ptrlen host, data; int port; switch (pktin->type) { case SSH1_SMSG_SUCCESS: case SSH1_SMSG_FAILURE: if (!s->succfail_head) { ssh_remote_error(s->ppl.ssh, "Received %s with no outstanding request", ssh1_pkt_type(pktin->type)); return true; } ssh1_connection_process_succfail( s, pktin->type == SSH1_SMSG_SUCCESS); queue_toplevel_callback( ssh1_connection_process_trivial_succfails, s); return true; case SSH1_SMSG_X11_OPEN: remid = get_uint32(pktin); /* Refuse if X11 forwarding is disabled. */ if (!s->X11_fwd_enabled) { pktout = ssh_bpp_new_pktout( s->ppl.bpp, SSH1_MSG_CHANNEL_OPEN_FAILURE); put_uint32(pktout, remid); pq_push(s->ppl.out_pq, pktout); ppl_logevent("Rejected X11 connect request"); } else { c = snew(struct ssh1_channel); c->connlayer = s; ssh1_channel_init(c); c->remoteid = remid; c->chan = x11_new_channel(s->x11authtree, &c->sc, NULL, -1, false); c->remoteid = remid; c->halfopen = false; pktout = ssh_bpp_new_pktout( s->ppl.bpp, SSH1_MSG_CHANNEL_OPEN_CONFIRMATION); put_uint32(pktout, c->remoteid); put_uint32(pktout, c->localid); pq_push(s->ppl.out_pq, pktout); ppl_logevent("Opened X11 forward channel"); } return true; case SSH1_SMSG_AGENT_OPEN: remid = get_uint32(pktin); /* Refuse if agent forwarding is disabled. */ if (!ssh_agent_forwarding_permitted(&s->cl)) { pktout = ssh_bpp_new_pktout( s->ppl.bpp, SSH1_MSG_CHANNEL_OPEN_FAILURE); put_uint32(pktout, remid); pq_push(s->ppl.out_pq, pktout); } else { c = snew(struct ssh1_channel); c->connlayer = s; ssh1_channel_init(c); c->remoteid = remid; c->halfopen = false; /* * If possible, make a stream-oriented connection to the * agent and set up an ordinary port-forwarding type * channel over it. */ Plug *plug; Channel *ch = portfwd_raw_new(&s->cl, &plug, true); Socket *skt = agent_connect(plug); if (!sk_socket_error(skt)) { portfwd_raw_setup(ch, skt, &c->sc); c->chan = ch; } else { portfwd_raw_free(ch); /* * Otherwise, fall back to the old-fashioned system of * parsing the forwarded data stream ourselves for * message boundaries, and passing each individual * message to the one-off agent_query(). */ c->chan = agentf_new(&c->sc); } pktout = ssh_bpp_new_pktout( s->ppl.bpp, SSH1_MSG_CHANNEL_OPEN_CONFIRMATION); put_uint32(pktout, c->remoteid); put_uint32(pktout, c->localid); pq_push(s->ppl.out_pq, pktout); } return true; case SSH1_MSG_PORT_OPEN: remid = get_uint32(pktin); host = get_string(pktin); port = toint(get_uint32(pktin)); pf.dhost = mkstr(host); pf.dport = port; pfp = find234(s->rportfwds, &pf, NULL); if (!pfp) { ppl_logevent("Rejected remote port open request for %s:%d", pf.dhost, port); pktout = ssh_bpp_new_pktout( s->ppl.bpp, SSH1_MSG_CHANNEL_OPEN_FAILURE); put_uint32(pktout, remid); pq_push(s->ppl.out_pq, pktout); } else { char *err; c = snew(struct ssh1_channel); c->connlayer = s; ppl_logevent("Received remote port open request for %s:%d", pf.dhost, port); err = portfwdmgr_connect( s->portfwdmgr, &c->chan, pf.dhost, port, &c->sc, pfp->addressfamily); if (err) { ppl_logevent("Port open failed: %s", err); sfree(err); ssh1_channel_free(c); pktout = ssh_bpp_new_pktout( s->ppl.bpp, SSH1_MSG_CHANNEL_OPEN_FAILURE); put_uint32(pktout, remid); pq_push(s->ppl.out_pq, pktout); } else { ssh1_channel_init(c); c->remoteid = remid; c->halfopen = false; pktout = ssh_bpp_new_pktout( s->ppl.bpp, SSH1_MSG_CHANNEL_OPEN_CONFIRMATION); put_uint32(pktout, c->remoteid); put_uint32(pktout, c->localid); pq_push(s->ppl.out_pq, pktout); ppl_logevent("Forwarded port opened successfully"); } } sfree(pf.dhost); return true; case SSH1_SMSG_STDOUT_DATA: case SSH1_SMSG_STDERR_DATA: data = get_string(pktin); if (!get_err(pktin)) { int bufsize = seat_output( s->ppl.seat, pktin->type == SSH1_SMSG_STDERR_DATA, data.ptr, data.len); if (!s->stdout_throttling && bufsize > SSH1_BUFFER_LIMIT) { s->stdout_throttling = true; ssh_throttle_conn(s->ppl.ssh, +1); } } return true; case SSH1_SMSG_EXIT_STATUS: { int exitcode = get_uint32(pktin); ppl_logevent("Server sent command exit status %d", exitcode); ssh_got_exitcode(s->ppl.ssh, exitcode); s->session_terminated = true; return true; } default: return false; } } static void ssh1mainchan_succfail_wantreply(struct ssh1_connection_state *s, bool success, void *ctx) { chan_request_response(s->mainchan_chan, success); } static void ssh1mainchan_succfail_nowantreply(struct ssh1_connection_state *s, bool success, void *ctx) { } static void ssh1mainchan_queue_response(struct ssh1_connection_state *s, bool want_reply, bool trivial) { sf_handler_fn_t handler = (want_reply ? ssh1mainchan_succfail_wantreply : ssh1mainchan_succfail_nowantreply); ssh1_queue_succfail_handler(s, handler, NULL, trivial); } static void ssh1mainchan_request_x11_forwarding( SshChannel *sc, bool want_reply, const char *authproto, const char *authdata, int screen_number, bool oneshot) { struct ssh1_connection_state *s = container_of(sc, struct ssh1_connection_state, mainchan_sc); PktOut *pktout; pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_CMSG_X11_REQUEST_FORWARDING); put_stringz(pktout, authproto); put_stringz(pktout, authdata); if (s->local_protoflags & SSH1_PROTOFLAG_SCREEN_NUMBER) put_uint32(pktout, screen_number); pq_push(s->ppl.out_pq, pktout); ssh1mainchan_queue_response(s, want_reply, false); } static void ssh1mainchan_request_agent_forwarding( SshChannel *sc, bool want_reply) { struct ssh1_connection_state *s = container_of(sc, struct ssh1_connection_state, mainchan_sc); PktOut *pktout; pktout = ssh_bpp_new_pktout( s->ppl.bpp, SSH1_CMSG_AGENT_REQUEST_FORWARDING); pq_push(s->ppl.out_pq, pktout); ssh1mainchan_queue_response(s, want_reply, false); } static void ssh1mainchan_request_pty( SshChannel *sc, bool want_reply, Conf *conf, int w, int h) { struct ssh1_connection_state *s = container_of(sc, struct ssh1_connection_state, mainchan_sc); PktOut *pktout; pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_CMSG_REQUEST_PTY); put_stringz(pktout, conf_get_str(s->conf, CONF_termtype)); put_uint32(pktout, h); put_uint32(pktout, w); put_uint32(pktout, 0); /* width in pixels */ put_uint32(pktout, 0); /* height in pixels */ write_ttymodes_to_packet( BinarySink_UPCAST(pktout), 1, get_ttymodes_from_conf(s->ppl.seat, conf)); pq_push(s->ppl.out_pq, pktout); ssh1mainchan_queue_response(s, want_reply, false); } static bool ssh1mainchan_send_env_var( SshChannel *sc, bool want_reply, const char *var, const char *value) { return false; /* SSH-1 doesn't support this at all */ } static void ssh1mainchan_start_shell(SshChannel *sc, bool want_reply) { struct ssh1_connection_state *s = container_of(sc, struct ssh1_connection_state, mainchan_sc); PktOut *pktout; pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_CMSG_EXEC_SHELL); pq_push(s->ppl.out_pq, pktout); ssh1mainchan_queue_response(s, want_reply, true); } static void ssh1mainchan_start_command( SshChannel *sc, bool want_reply, const char *command) { struct ssh1_connection_state *s = container_of(sc, struct ssh1_connection_state, mainchan_sc); PktOut *pktout; pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_CMSG_EXEC_CMD); put_stringz(pktout, command); pq_push(s->ppl.out_pq, pktout); ssh1mainchan_queue_response(s, want_reply, true); } static bool ssh1mainchan_start_subsystem( SshChannel *sc, bool want_reply, const char *subsystem) { return false; /* SSH-1 doesn't support this at all */ } static bool ssh1mainchan_send_serial_break( SshChannel *sc, bool want_reply, int length) { return false; /* SSH-1 doesn't support this at all */ } static bool ssh1mainchan_send_signal( SshChannel *sc, bool want_reply, const char *signame) { return false; /* SSH-1 doesn't support this at all */ } static void ssh1mainchan_send_terminal_size_change( SshChannel *sc, int w, int h) { struct ssh1_connection_state *s = container_of(sc, struct ssh1_connection_state, mainchan_sc); PktOut *pktout; pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_CMSG_WINDOW_SIZE); put_uint32(pktout, h); put_uint32(pktout, w); put_uint32(pktout, 0); /* width in pixels */ put_uint32(pktout, 0); /* height in pixels */ pq_push(s->ppl.out_pq, pktout); } static void ssh1mainchan_hint_channel_is_simple(SshChannel *sc) { } static size_t ssh1mainchan_write( SshChannel *sc, bool is_stderr, const void *data, size_t len) { struct ssh1_connection_state *s = container_of(sc, struct ssh1_connection_state, mainchan_sc); PktOut *pktout; pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_CMSG_STDIN_DATA); put_string(pktout, data, len); pq_push(s->ppl.out_pq, pktout); return 0; } static void ssh1mainchan_write_eof(SshChannel *sc) { struct ssh1_connection_state *s = container_of(sc, struct ssh1_connection_state, mainchan_sc); PktOut *pktout; pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_CMSG_EOF); pq_push(s->ppl.out_pq, pktout); } static const SshChannelVtable ssh1mainchan_vtable = { .write = ssh1mainchan_write, .write_eof = ssh1mainchan_write_eof, .request_x11_forwarding = ssh1mainchan_request_x11_forwarding, .request_agent_forwarding = ssh1mainchan_request_agent_forwarding, .request_pty = ssh1mainchan_request_pty, .send_env_var = ssh1mainchan_send_env_var, .start_shell = ssh1mainchan_start_shell, .start_command = ssh1mainchan_start_command, .start_subsystem = ssh1mainchan_start_subsystem, .send_serial_break = ssh1mainchan_send_serial_break, .send_signal = ssh1mainchan_send_signal, .send_terminal_size_change = ssh1mainchan_send_terminal_size_change, .hint_channel_is_simple = ssh1mainchan_hint_channel_is_simple, /* other methods are NULL */ }; static void ssh1_session_confirm_callback(void *vctx) { struct ssh1_connection_state *s = (struct ssh1_connection_state *)vctx; chan_open_confirmation(s->mainchan_chan); } SshChannel *ssh1_session_open(ConnectionLayer *cl, Channel *chan) { struct ssh1_connection_state *s = container_of(cl, struct ssh1_connection_state, cl); s->mainchan_sc.vt = &ssh1mainchan_vtable; s->mainchan_sc.cl = &s->cl; s->mainchan_chan = chan; queue_toplevel_callback(ssh1_session_confirm_callback, s); return &s->mainchan_sc; } static void ssh1_rportfwd_response(struct ssh1_connection_state *s, bool success, void *ctx) { PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */ struct ssh_rportfwd *rpf = (struct ssh_rportfwd *)ctx; if (success) { ppl_logevent("Remote port forwarding from %s enabled", rpf->log_description); } else { ppl_logevent("Remote port forwarding from %s refused", rpf->log_description); struct ssh_rportfwd *realpf = del234(s->rportfwds, rpf); assert(realpf == rpf); portfwdmgr_close(s->portfwdmgr, rpf->pfr); free_rportfwd(rpf); } } struct ssh_rportfwd *ssh1_rportfwd_alloc( ConnectionLayer *cl, const char *shost, int sport, const char *dhost, int dport, int addressfamily, const char *log_description, PortFwdRecord *pfr, ssh_sharing_connstate *share_ctx) { struct ssh1_connection_state *s = container_of(cl, struct ssh1_connection_state, cl); struct ssh_rportfwd *rpf = snew(struct ssh_rportfwd); rpf->shost = dupstr(shost); rpf->sport = sport; rpf->dhost = dupstr(dhost); rpf->dport = dport; rpf->addressfamily = addressfamily; rpf->log_description = dupstr(log_description); rpf->pfr = pfr; if (add234(s->rportfwds, rpf) != rpf) { free_rportfwd(rpf); return NULL; } PktOut *pktout = ssh_bpp_new_pktout( s->ppl.bpp, SSH1_CMSG_PORT_FORWARD_REQUEST); put_uint32(pktout, rpf->sport); put_stringz(pktout, rpf->dhost); put_uint32(pktout, rpf->dport); pq_push(s->ppl.out_pq, pktout); ssh1_queue_succfail_handler(s, ssh1_rportfwd_response, rpf, false); return rpf; } SshChannel *ssh1_serverside_x11_open( ConnectionLayer *cl, Channel *chan, const SocketPeerInfo *pi) { unreachable("Should never be called in the client"); } SshChannel *ssh1_serverside_agent_open(ConnectionLayer *cl, Channel *chan) { unreachable("Should never be called in the client"); } bool ssh1_connection_need_antispoof_prompt(struct ssh1_connection_state *s) { return !seat_set_trust_status(s->ppl.seat, false); } putty-0.76/ssh1connection-server.c0000644000175000017500000002655714072266311014203 00000000000000/* * Server-specific parts of the SSH-1 connection layer. */ #include #include "putty.h" #include "ssh.h" #include "sshbpp.h" #include "sshppl.h" #include "sshchan.h" #include "sshcr.h" #include "ssh1connection.h" #include "sshserver.h" static size_t ssh1sesschan_write(SshChannel *c, bool is_stderr, const void *, size_t); static void ssh1sesschan_write_eof(SshChannel *c); static void ssh1sesschan_initiate_close(SshChannel *c, const char *err); static void ssh1sesschan_send_exit_status(SshChannel *c, int status); static void ssh1sesschan_send_exit_signal( SshChannel *c, ptrlen signame, bool core_dumped, ptrlen msg); static const SshChannelVtable ssh1sesschan_vtable = { .write = ssh1sesschan_write, .write_eof = ssh1sesschan_write_eof, .initiate_close = ssh1sesschan_initiate_close, .send_exit_status = ssh1sesschan_send_exit_status, .send_exit_signal = ssh1sesschan_send_exit_signal, /* everything else is NULL */ }; void ssh1connection_server_configure( PacketProtocolLayer *ppl, const SshServerConfig *ssc) { struct ssh1_connection_state *s = container_of(ppl, struct ssh1_connection_state, ppl); s->ssc = ssc; } void ssh1_connection_direction_specific_setup( struct ssh1_connection_state *s) { if (!s->mainchan_chan) { s->mainchan_sc.vt = &ssh1sesschan_vtable; s->mainchan_sc.cl = &s->cl; s->mainchan_chan = sesschan_new( &s->mainchan_sc, s->ppl.logctx, NULL, s->ssc); } } bool ssh1_handle_direction_specific_packet( struct ssh1_connection_state *s, PktIn *pktin) { PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */ PktOut *pktout; struct ssh1_channel *c; unsigned remid; ptrlen host, cmd, data; char *host_str, *err; int port, listenport; bool success; switch (pktin->type) { case SSH1_CMSG_EXEC_SHELL: if (s->finished_setup) goto unexpected_setup_packet; ppl_logevent("Client requested a shell"); chan_run_shell(s->mainchan_chan); s->finished_setup = true; return true; case SSH1_CMSG_EXEC_CMD: if (s->finished_setup) goto unexpected_setup_packet; cmd = get_string(pktin); ppl_logevent("Client sent command '%.*s'", PTRLEN_PRINTF(cmd)); chan_run_command(s->mainchan_chan, cmd); s->finished_setup = true; return true; case SSH1_CMSG_REQUEST_COMPRESSION: if (s->compressing || !s->ssc->ssh1_allow_compression) { pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_SMSG_FAILURE); pq_push(s->ppl.out_pq, pktout); } else { pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_SMSG_SUCCESS); pq_push(s->ppl.out_pq, pktout); /* Synchronous run of output formatting, to ensure that * success packet is converted into wire format before we * start compressing */ ssh_bpp_handle_output(s->ppl.bpp); /* And now ensure that the _next_ packet will be the first * compressed one. */ ssh1_bpp_start_compression(s->ppl.bpp); s->compressing = true; } return true; case SSH1_CMSG_REQUEST_PTY: { if (s->finished_setup) goto unexpected_setup_packet; ptrlen termtype = get_string(pktin); unsigned height = get_uint32(pktin); unsigned width = get_uint32(pktin); unsigned pixwidth = get_uint32(pktin); unsigned pixheight = get_uint32(pktin); struct ssh_ttymodes modes = read_ttymodes_from_packet( BinarySource_UPCAST(pktin), 1); if (get_err(pktin)) { ppl_logevent("Unable to decode pty request packet"); success = false; } else if (!chan_allocate_pty( s->mainchan_chan, termtype, width, height, pixwidth, pixheight, modes)) { ppl_logevent("Unable to allocate a pty"); success = false; } else { success = true; } pktout = ssh_bpp_new_pktout( s->ppl.bpp, (success ? SSH1_SMSG_SUCCESS : SSH1_SMSG_FAILURE)); pq_push(s->ppl.out_pq, pktout); return true; } case SSH1_CMSG_PORT_FORWARD_REQUEST: if (s->finished_setup) goto unexpected_setup_packet; listenport = toint(get_uint32(pktin)); host = get_string(pktin); port = toint(get_uint32(pktin)); ppl_logevent("Client requested port %d forward to %.*s:%d", listenport, PTRLEN_PRINTF(host), port); host_str = mkstr(host); success = portfwdmgr_listen( s->portfwdmgr, NULL, listenport, host_str, port, s->conf); sfree(host_str); pktout = ssh_bpp_new_pktout( s->ppl.bpp, (success ? SSH1_SMSG_SUCCESS : SSH1_SMSG_FAILURE)); pq_push(s->ppl.out_pq, pktout); return true; case SSH1_CMSG_X11_REQUEST_FORWARDING: { if (s->finished_setup) goto unexpected_setup_packet; ptrlen authproto = get_string(pktin); ptrlen authdata = get_string(pktin); unsigned screen_number = 0; if (s->remote_protoflags & SSH1_PROTOFLAG_SCREEN_NUMBER) screen_number = get_uint32(pktin); success = chan_enable_x11_forwarding( s->mainchan_chan, false, authproto, authdata, screen_number); pktout = ssh_bpp_new_pktout( s->ppl.bpp, (success ? SSH1_SMSG_SUCCESS : SSH1_SMSG_FAILURE)); pq_push(s->ppl.out_pq, pktout); return true; } case SSH1_CMSG_AGENT_REQUEST_FORWARDING: if (s->finished_setup) goto unexpected_setup_packet; success = chan_enable_agent_forwarding(s->mainchan_chan); pktout = ssh_bpp_new_pktout( s->ppl.bpp, (success ? SSH1_SMSG_SUCCESS : SSH1_SMSG_FAILURE)); pq_push(s->ppl.out_pq, pktout); return true; case SSH1_CMSG_STDIN_DATA: data = get_string(pktin); chan_send(s->mainchan_chan, false, data.ptr, data.len); return true; case SSH1_CMSG_EOF: chan_send_eof(s->mainchan_chan); return true; case SSH1_CMSG_WINDOW_SIZE: return true; case SSH1_MSG_PORT_OPEN: remid = get_uint32(pktin); host = get_string(pktin); port = toint(get_uint32(pktin)); host_str = mkstr(host); ppl_logevent("Received request to connect to port %s:%d", host_str, port); c = snew(struct ssh1_channel); c->connlayer = s; err = portfwdmgr_connect( s->portfwdmgr, &c->chan, host_str, port, &c->sc, ADDRTYPE_UNSPEC); sfree(host_str); if (err) { ppl_logevent("Port open failed: %s", err); sfree(err); ssh1_channel_free(c); pktout = ssh_bpp_new_pktout( s->ppl.bpp, SSH1_MSG_CHANNEL_OPEN_FAILURE); put_uint32(pktout, remid); pq_push(s->ppl.out_pq, pktout); } else { ssh1_channel_init(c); c->remoteid = remid; c->halfopen = false; pktout = ssh_bpp_new_pktout( s->ppl.bpp, SSH1_MSG_CHANNEL_OPEN_CONFIRMATION); put_uint32(pktout, c->remoteid); put_uint32(pktout, c->localid); pq_push(s->ppl.out_pq, pktout); ppl_logevent("Forwarded port opened successfully"); } return true; case SSH1_CMSG_EXIT_CONFIRMATION: if (!s->sent_exit_status) { ssh_proto_error(s->ppl.ssh, "Received SSH1_CMSG_EXIT_CONFIRMATION" " without having sent SSH1_SMSG_EXIT_STATUS"); return true; } ppl_logevent("Client sent exit confirmation"); return true; default: return false; } unexpected_setup_packet: ssh_proto_error(s->ppl.ssh, "Received unexpected setup packet after the " "setup phase, type %d (%s)", pktin->type, ssh1_pkt_type(pktin->type)); /* FIXME: ensure caller copes with us just having freed the whole layer */ return true; } SshChannel *ssh1_session_open(ConnectionLayer *cl, Channel *chan) { unreachable("Should never be called in the server"); } struct ssh_rportfwd *ssh1_rportfwd_alloc( ConnectionLayer *cl, const char *shost, int sport, const char *dhost, int dport, int addressfamily, const char *log_description, PortFwdRecord *pfr, ssh_sharing_connstate *share_ctx) { unreachable("Should never be called in the server"); } static size_t ssh1sesschan_write(SshChannel *sc, bool is_stderr, const void *data, size_t len) { struct ssh1_connection_state *s = container_of(sc, struct ssh1_connection_state, mainchan_sc); PktOut *pktout; pktout = ssh_bpp_new_pktout( s->ppl.bpp, (is_stderr ? SSH1_SMSG_STDERR_DATA : SSH1_SMSG_STDOUT_DATA)); put_string(pktout, data, len); pq_push(s->ppl.out_pq, pktout); return 0; } static void ssh1sesschan_write_eof(SshChannel *sc) { /* SSH-1 can't represent server-side EOF */ /* FIXME: some kind of check-termination system, whereby once this has been called _and_ we've had an exit status _and_ we've got no other channels open, we send the actual EXIT_STATUS message */ } static void ssh1sesschan_initiate_close(SshChannel *sc, const char *err) { /* SSH-1 relies on the client to close the connection in the end */ } static void ssh1sesschan_send_exit_status(SshChannel *sc, int status) { struct ssh1_connection_state *s = container_of(sc, struct ssh1_connection_state, mainchan_sc); PktOut *pktout; pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_SMSG_EXIT_STATUS); put_uint32(pktout, status); pq_push(s->ppl.out_pq, pktout); s->sent_exit_status = true; } static void ssh1sesschan_send_exit_signal( SshChannel *sc, ptrlen signame, bool core_dumped, ptrlen msg) { /* SSH-1 has no separate representation for signals */ ssh1sesschan_send_exit_status(sc, 128); } SshChannel *ssh1_serverside_x11_open( ConnectionLayer *cl, Channel *chan, const SocketPeerInfo *pi) { struct ssh1_connection_state *s = container_of(cl, struct ssh1_connection_state, cl); PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */ struct ssh1_channel *c = snew(struct ssh1_channel); PktOut *pktout; c->connlayer = s; ssh1_channel_init(c); c->halfopen = true; c->chan = chan; ppl_logevent("Forwarding X11 connection to client"); pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_SMSG_X11_OPEN); put_uint32(pktout, c->localid); pq_push(s->ppl.out_pq, pktout); return &c->sc; } SshChannel *ssh1_serverside_agent_open(ConnectionLayer *cl, Channel *chan) { struct ssh1_connection_state *s = container_of(cl, struct ssh1_connection_state, cl); PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */ struct ssh1_channel *c = snew(struct ssh1_channel); PktOut *pktout; c->connlayer = s; ssh1_channel_init(c); c->halfopen = true; c->chan = chan; ppl_logevent("Forwarding agent connection to client"); pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_SMSG_AGENT_OPEN); put_uint32(pktout, c->localid); pq_push(s->ppl.out_pq, pktout); return &c->sc; } bool ssh1_connection_need_antispoof_prompt(struct ssh1_connection_state *s) { return false; } putty-0.76/ssh1connection.c0000644000175000017500000006412714072266311012672 00000000000000/* * Packet protocol layer for the SSH-1 'connection protocol', i.e. * everything after authentication finishes. */ #include #include "putty.h" #include "ssh.h" #include "sshbpp.h" #include "sshppl.h" #include "sshchan.h" #include "sshcr.h" #include "ssh1connection.h" static int ssh1_rportfwd_cmp(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->dhost, b->dhost)) != 0) return i < 0 ? -1 : +1; if (a->dport > b->dport) return +1; if (a->dport < b->dport) return -1; return 0; } static void ssh1_connection_free(PacketProtocolLayer *); static void ssh1_connection_process_queue(PacketProtocolLayer *); static void ssh1_connection_special_cmd(PacketProtocolLayer *ppl, SessionSpecialCode code, int arg); static bool ssh1_connection_want_user_input(PacketProtocolLayer *ppl); static void ssh1_connection_got_user_input(PacketProtocolLayer *ppl); static void ssh1_connection_reconfigure(PacketProtocolLayer *ppl, Conf *conf); static const PacketProtocolLayerVtable ssh1_connection_vtable = { .free = ssh1_connection_free, .process_queue = ssh1_connection_process_queue, .get_specials = ssh1_common_get_specials, .special_cmd = ssh1_connection_special_cmd, .want_user_input = ssh1_connection_want_user_input, .got_user_input = ssh1_connection_got_user_input, .reconfigure = ssh1_connection_reconfigure, .queued_data_size = ssh_ppl_default_queued_data_size, .name = NULL, /* no layer names in SSH-1 */ }; static void ssh1_rportfwd_remove( ConnectionLayer *cl, struct ssh_rportfwd *rpf); static SshChannel *ssh1_lportfwd_open( ConnectionLayer *cl, const char *hostname, int port, const char *description, const SocketPeerInfo *pi, Channel *chan); static struct X11FakeAuth *ssh1_add_x11_display( ConnectionLayer *cl, int authtype, struct X11Display *disp); static bool ssh1_agent_forwarding_permitted(ConnectionLayer *cl); static void ssh1_terminal_size(ConnectionLayer *cl, int width, int height); static void ssh1_stdout_unthrottle(ConnectionLayer *cl, size_t bufsize); static size_t ssh1_stdin_backlog(ConnectionLayer *cl); static void ssh1_throttle_all_channels(ConnectionLayer *cl, bool throttled); static bool ssh1_ldisc_option(ConnectionLayer *cl, int option); static void ssh1_set_ldisc_option(ConnectionLayer *cl, int option, bool value); static void ssh1_enable_x_fwd(ConnectionLayer *cl); static void ssh1_set_wants_user_input(ConnectionLayer *cl, bool wanted); static const ConnectionLayerVtable ssh1_connlayer_vtable = { .rportfwd_alloc = ssh1_rportfwd_alloc, .rportfwd_remove = ssh1_rportfwd_remove, .lportfwd_open = ssh1_lportfwd_open, .session_open = ssh1_session_open, .serverside_x11_open = ssh1_serverside_x11_open, .serverside_agent_open = ssh1_serverside_agent_open, .add_x11_display = ssh1_add_x11_display, .agent_forwarding_permitted = ssh1_agent_forwarding_permitted, .terminal_size = ssh1_terminal_size, .stdout_unthrottle = ssh1_stdout_unthrottle, .stdin_backlog = ssh1_stdin_backlog, .throttle_all_channels = ssh1_throttle_all_channels, .ldisc_option = ssh1_ldisc_option, .set_ldisc_option = ssh1_set_ldisc_option, .enable_x_fwd = ssh1_enable_x_fwd, .set_wants_user_input = ssh1_set_wants_user_input, /* other methods are NULL */ }; static size_t ssh1channel_write( SshChannel *c, bool is_stderr, const void *buf, size_t len); static void ssh1channel_write_eof(SshChannel *c); static void ssh1channel_initiate_close(SshChannel *c, const char *err); static void ssh1channel_unthrottle(SshChannel *c, size_t bufsize); static Conf *ssh1channel_get_conf(SshChannel *c); static void ssh1channel_window_override_removed(SshChannel *c) { /* ignore */ } static const SshChannelVtable ssh1channel_vtable = { .write = ssh1channel_write, .write_eof = ssh1channel_write_eof, .initiate_close = ssh1channel_initiate_close, .unthrottle = ssh1channel_unthrottle, .get_conf = ssh1channel_get_conf, .window_override_removed = ssh1channel_window_override_removed, /* everything else is NULL */ }; static void ssh1_channel_try_eof(struct ssh1_channel *c); static void ssh1_channel_close_local(struct ssh1_channel *c, const char *reason); static void ssh1_channel_destroy(struct ssh1_channel *c); static void ssh1_channel_check_close(struct ssh1_channel *c); static int ssh1_channelcmp(void *av, void *bv) { const struct ssh1_channel *a = (const struct ssh1_channel *) av; const struct ssh1_channel *b = (const struct ssh1_channel *) bv; if (a->localid < b->localid) return -1; if (a->localid > b->localid) return +1; return 0; } static int ssh1_channelfind(void *av, void *bv) { const unsigned *a = (const unsigned *) av; const struct ssh1_channel *b = (const struct ssh1_channel *) bv; if (*a < b->localid) return -1; if (*a > b->localid) return +1; return 0; } void ssh1_channel_free(struct ssh1_channel *c) { if (c->chan) chan_free(c->chan); sfree(c); } PacketProtocolLayer *ssh1_connection_new( Ssh *ssh, Conf *conf, ConnectionLayer **cl_out) { struct ssh1_connection_state *s = snew(struct ssh1_connection_state); memset(s, 0, sizeof(*s)); s->ppl.vt = &ssh1_connection_vtable; s->conf = conf_copy(conf); s->channels = newtree234(ssh1_channelcmp); s->x11authtree = newtree234(x11_authcmp); /* Need to get the log context for s->cl now, because we won't be * helpfully notified when a copy is written into s->ppl by our * owner. */ s->cl.vt = &ssh1_connlayer_vtable; s->cl.logctx = ssh_get_logctx(ssh); s->portfwdmgr = portfwdmgr_new(&s->cl); s->rportfwds = newtree234(ssh1_rportfwd_cmp); *cl_out = &s->cl; return &s->ppl; } static void ssh1_connection_free(PacketProtocolLayer *ppl) { struct ssh1_connection_state *s = container_of(ppl, struct ssh1_connection_state, ppl); struct X11FakeAuth *auth; struct ssh1_channel *c; struct ssh_rportfwd *rpf; conf_free(s->conf); while ((c = delpos234(s->channels, 0)) != NULL) ssh1_channel_free(c); freetree234(s->channels); if (s->mainchan_chan) chan_free(s->mainchan_chan); if (s->x11disp) x11_free_display(s->x11disp); while ((auth = delpos234(s->x11authtree, 0)) != NULL) x11_free_fake_auth(auth); freetree234(s->x11authtree); while ((rpf = delpos234(s->rportfwds, 0)) != NULL) free_rportfwd(rpf); freetree234(s->rportfwds); portfwdmgr_free(s->portfwdmgr); if (s->antispoof_prompt) free_prompts(s->antispoof_prompt); delete_callbacks_for_context(s); sfree(s); } void ssh1_connection_set_protoflags(PacketProtocolLayer *ppl, int local, int remote) { assert(ppl->vt == &ssh1_connection_vtable); struct ssh1_connection_state *s = container_of(ppl, struct ssh1_connection_state, ppl); s->local_protoflags = local; s->remote_protoflags = remote; } static bool ssh1_connection_filter_queue(struct ssh1_connection_state *s) { PktIn *pktin; ptrlen data; struct ssh1_channel *c; unsigned localid; bool expect_halfopen; while (1) { if (ssh1_common_filter_queue(&s->ppl)) return true; if ((pktin = pq_peek(s->ppl.in_pq)) == NULL) return false; switch (pktin->type) { case SSH1_MSG_CHANNEL_DATA: case SSH1_MSG_CHANNEL_OPEN_CONFIRMATION: case SSH1_MSG_CHANNEL_OPEN_FAILURE: case SSH1_MSG_CHANNEL_CLOSE: case SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION: /* * Common preliminary code for all the messages from the * server that cite one of our channel ids: look up that * channel id, check it exists, and if it's for a sharing * downstream, pass it on. */ localid = get_uint32(pktin); c = find234(s->channels, &localid, ssh1_channelfind); expect_halfopen = ( pktin->type == SSH1_MSG_CHANNEL_OPEN_CONFIRMATION || pktin->type == SSH1_MSG_CHANNEL_OPEN_FAILURE); if (!c || c->halfopen != expect_halfopen) { ssh_remote_error( s->ppl.ssh, "Received %s for %s channel %u", ssh1_pkt_type(pktin->type), !c ? "nonexistent" : c->halfopen ? "half-open" : "open", localid); return true; } switch (pktin->type) { case SSH1_MSG_CHANNEL_OPEN_CONFIRMATION: assert(c->halfopen); c->remoteid = get_uint32(pktin); c->halfopen = false; c->throttling_conn = false; chan_open_confirmation(c->chan); /* * Now that the channel is fully open, it's possible * in principle to immediately close it. Check whether * it wants us to! * * This can occur if a local socket error occurred * between us sending out CHANNEL_OPEN and receiving * OPEN_CONFIRMATION. If that happens, all we can do * is immediately initiate close proceedings now that * we know the server's id to put in the close * message. We'll have handled that in this code by * having already turned c->chan into a zombie, so its * want_close method (which ssh1_channel_check_close * will consult) will already be returning true. */ ssh1_channel_check_close(c); if (c->pending_eof) ssh1_channel_try_eof(c); /* in case we had a pending EOF */ break; case SSH1_MSG_CHANNEL_OPEN_FAILURE: assert(c->halfopen); chan_open_failed(c->chan, NULL); chan_free(c->chan); del234(s->channels, c); ssh1_channel_free(c); break; case SSH1_MSG_CHANNEL_DATA: data = get_string(pktin); if (!get_err(pktin)) { int bufsize = chan_send( c->chan, false, data.ptr, data.len); if (!c->throttling_conn && bufsize > SSH1_BUFFER_LIMIT) { c->throttling_conn = true; ssh_throttle_conn(s->ppl.ssh, +1); } } break; case SSH1_MSG_CHANNEL_CLOSE: if (!(c->closes & CLOSES_RCVD_CLOSE)) { c->closes |= CLOSES_RCVD_CLOSE; chan_send_eof(c->chan); ssh1_channel_check_close(c); } break; case SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION: if (!(c->closes & CLOSES_RCVD_CLOSECONF)) { if (!(c->closes & CLOSES_SENT_CLOSE)) { ssh_remote_error( s->ppl.ssh, "Received CHANNEL_CLOSE_CONFIRMATION for channel" " %u for which we never sent CHANNEL_CLOSE\n", c->localid); return true; } c->closes |= CLOSES_RCVD_CLOSECONF; ssh1_channel_check_close(c); } break; } pq_pop(s->ppl.in_pq); break; default: if (ssh1_handle_direction_specific_packet(s, pktin)) { pq_pop(s->ppl.in_pq); if (ssh1_check_termination(s)) return true; } else { return false; } } } } static PktIn *ssh1_connection_pop(struct ssh1_connection_state *s) { ssh1_connection_filter_queue(s); return pq_pop(s->ppl.in_pq); } static void ssh1_connection_process_queue(PacketProtocolLayer *ppl) { struct ssh1_connection_state *s = container_of(ppl, struct ssh1_connection_state, ppl); PktIn *pktin; if (ssh1_connection_filter_queue(s)) /* no matter why we were called */ return; crBegin(s->crState); /* * Signal the seat that authentication is done, so that it can * deploy spoofing defences. If it doesn't have any, deploy our * own fallback one. * * We do this here rather than at the end of userauth, because we * might not have gone through userauth at all (if we're a * connection-sharing downstream). */ if (ssh1_connection_need_antispoof_prompt(s)) { s->antispoof_prompt = new_prompts(); s->antispoof_prompt->to_server = true; s->antispoof_prompt->from_server = false; s->antispoof_prompt->name = dupstr("Authentication successful"); add_prompt( s->antispoof_prompt, dupstr("Access granted. Press Return to begin session. "), false); s->antispoof_ret = seat_get_userpass_input( s->ppl.seat, s->antispoof_prompt, NULL); while (1) { while (s->antispoof_ret < 0 && bufchain_size(s->ppl.user_input) > 0) s->antispoof_ret = seat_get_userpass_input( s->ppl.seat, s->antispoof_prompt, s->ppl.user_input); if (s->antispoof_ret >= 0) break; s->want_user_input = true; crReturnV; s->want_user_input = false; } free_prompts(s->antispoof_prompt); s->antispoof_prompt = NULL; } portfwdmgr_config(s->portfwdmgr, s->conf); s->portfwdmgr_configured = true; while (!s->finished_setup) { ssh1_connection_direction_specific_setup(s); crReturnV; } while (1) { /* * By this point, most incoming packets are already being * handled by filter_queue, and we need only pay attention to * the unusual ones. */ if ((pktin = ssh1_connection_pop(s)) != NULL) { ssh_proto_error(s->ppl.ssh, "Unexpected packet received, " "type %d (%s)", pktin->type, ssh1_pkt_type(pktin->type)); return; } crReturnV; } crFinishV; } static void ssh1_channel_check_close(struct ssh1_channel *c) { struct ssh1_connection_state *s = c->connlayer; PktOut *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_CLOSE | CLOSES_RCVD_CLOSE) & ~c->closes) || chan_want_close(c->chan, (c->closes & CLOSES_SENT_CLOSE), (c->closes & CLOSES_RCVD_CLOSE))) && !(c->closes & CLOSES_SENT_CLOSECONF)) { /* * We have both sent and received CLOSE (or the channel type * doesn't need us to), which means the channel is in final * wind-up. Send CLOSE and/or CLOSE_CONFIRMATION, whichever we * haven't sent yet. */ if (!(c->closes & CLOSES_SENT_CLOSE)) { pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_MSG_CHANNEL_CLOSE); put_uint32(pktout, c->remoteid); pq_push(s->ppl.out_pq, pktout); c->closes |= CLOSES_SENT_CLOSE; } if (c->closes & CLOSES_RCVD_CLOSE) { pktout = ssh_bpp_new_pktout( s->ppl.bpp, SSH1_MSG_CHANNEL_CLOSE_CONFIRMATION); put_uint32(pktout, c->remoteid); pq_push(s->ppl.out_pq, pktout); c->closes |= CLOSES_SENT_CLOSECONF; } } if (!((CLOSES_SENT_CLOSECONF | CLOSES_RCVD_CLOSECONF) & ~c->closes)) { /* * We have both sent and received CLOSE_CONFIRMATION, which * means we're completely done with the channel. */ ssh1_channel_destroy(c); } } static void ssh1_channel_try_eof(struct ssh1_channel *c) { struct ssh1_connection_state *s = c->connlayer; PktOut *pktout; assert(c->pending_eof); /* precondition for calling us */ if (c->halfopen) return; /* can't close: not even opened yet */ c->pending_eof = false; /* we're about to send it */ pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_MSG_CHANNEL_CLOSE); put_uint32(pktout, c->remoteid); pq_push(s->ppl.out_pq, pktout); c->closes |= CLOSES_SENT_CLOSE; ssh1_channel_check_close(c); } /* * Close any local socket and free any local resources associated with * a channel. This converts the channel into a zombie. */ static void ssh1_channel_close_local(struct ssh1_channel *c, const char *reason) { struct ssh1_connection_state *s = c->connlayer; PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */ char *msg = chan_log_close_msg(c->chan); if (msg != NULL) { ppl_logevent("%s%s%s", msg, reason ? " " : "", reason ? reason : ""); sfree(msg); } chan_free(c->chan); c->chan = zombiechan_new(); } static void ssh1_check_termination_callback(void *vctx) { struct ssh1_connection_state *s = (struct ssh1_connection_state *)vctx; ssh1_check_termination(s); } static void ssh1_channel_destroy(struct ssh1_channel *c) { struct ssh1_connection_state *s = c->connlayer; ssh1_channel_close_local(c, NULL); del234(s->channels, c); ssh1_channel_free(c); /* * If that was the last channel left open, we might need to * terminate. But we'll be a bit cautious, by doing that in a * toplevel callback, just in case anything on the current call * stack objects to this entire PPL being freed. */ queue_toplevel_callback(ssh1_check_termination_callback, s); } bool ssh1_check_termination(struct ssh1_connection_state *s) { /* * Decide whether we should terminate the SSH connection now. * Called after a channel goes away, or when the main session * returns SSH1_SMSG_EXIT_STATUS; we terminate when none of either * is left. */ if (s->session_terminated && count234(s->channels) == 0) { PktOut *pktout = ssh_bpp_new_pktout( s->ppl.bpp, SSH1_CMSG_EXIT_CONFIRMATION); pq_push(s->ppl.out_pq, pktout); ssh_user_close(s->ppl.ssh, "Session finished"); return true; } return false; } /* * Set up most of a new ssh1_channel. Leaves chan untouched (since it * will sometimes have been filled in before calling this). */ void ssh1_channel_init(struct ssh1_channel *c) { struct ssh1_connection_state *s = c->connlayer; c->closes = 0; c->pending_eof = false; c->throttling_conn = false; c->sc.vt = &ssh1channel_vtable; c->sc.cl = &s->cl; c->localid = alloc_channel_id(s->channels, struct ssh1_channel); add234(s->channels, c); } static Conf *ssh1channel_get_conf(SshChannel *sc) { struct ssh1_channel *c = container_of(sc, struct ssh1_channel, sc); struct ssh1_connection_state *s = c->connlayer; return s->conf; } static void ssh1channel_write_eof(SshChannel *sc) { struct ssh1_channel *c = container_of(sc, struct ssh1_channel, sc); if (c->closes & CLOSES_SENT_CLOSE) return; c->pending_eof = true; ssh1_channel_try_eof(c); } static void ssh1channel_initiate_close(SshChannel *sc, const char *err) { struct ssh1_channel *c = container_of(sc, struct ssh1_channel, sc); char *reason; reason = err ? dupprintf("due to local error: %s", err) : NULL; ssh1_channel_close_local(c, reason); sfree(reason); c->pending_eof = false; /* this will confuse a zombie channel */ ssh1_channel_check_close(c); } static void ssh1channel_unthrottle(SshChannel *sc, size_t bufsize) { struct ssh1_channel *c = container_of(sc, struct ssh1_channel, sc); struct ssh1_connection_state *s = c->connlayer; if (c->throttling_conn && bufsize <= SSH1_BUFFER_LIMIT) { c->throttling_conn = false; ssh_throttle_conn(s->ppl.ssh, -1); } } static size_t ssh1channel_write( SshChannel *sc, bool is_stderr, const void *buf, size_t len) { struct ssh1_channel *c = container_of(sc, struct ssh1_channel, sc); struct ssh1_connection_state *s = c->connlayer; assert(!(c->closes & CLOSES_SENT_CLOSE)); PktOut *pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_MSG_CHANNEL_DATA); put_uint32(pktout, c->remoteid); put_string(pktout, buf, len); pq_push(s->ppl.out_pq, pktout); /* * In SSH-1 we can return 0 here - implying that channels 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. */ return 0; } static struct X11FakeAuth *ssh1_add_x11_display( ConnectionLayer *cl, int authtype, struct X11Display *disp) { struct ssh1_connection_state *s = container_of(cl, struct ssh1_connection_state, cl); struct X11FakeAuth *auth = x11_invent_fake_auth(s->x11authtree, authtype); auth->disp = disp; return auth; } static SshChannel *ssh1_lportfwd_open( ConnectionLayer *cl, const char *hostname, int port, const char *description, const SocketPeerInfo *pi, Channel *chan) { struct ssh1_connection_state *s = container_of(cl, struct ssh1_connection_state, cl); PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */ struct ssh1_channel *c = snew(struct ssh1_channel); PktOut *pktout; c->connlayer = s; ssh1_channel_init(c); c->halfopen = true; c->chan = chan; ppl_logevent("Opening connection to %s:%d for %s", hostname, port, description); pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_MSG_PORT_OPEN); put_uint32(pktout, c->localid); put_stringz(pktout, hostname); put_uint32(pktout, port); /* originator string would go here, but we didn't specify * SSH_PROTOFLAG_HOST_IN_FWD_OPEN */ pq_push(s->ppl.out_pq, pktout); return &c->sc; } static void ssh1_rportfwd_remove(ConnectionLayer *cl, struct ssh_rportfwd *rpf) { /* * We cannot cancel listening ports on the server side in SSH-1! * There's no message to support it. */ } static bool ssh1_agent_forwarding_permitted(ConnectionLayer *cl) { struct ssh1_connection_state *s = container_of(cl, struct ssh1_connection_state, cl); return conf_get_bool(s->conf, CONF_agentfwd) && agent_exists(); } static void ssh1_connection_special_cmd(PacketProtocolLayer *ppl, SessionSpecialCode code, int arg) { struct ssh1_connection_state *s = container_of(ppl, struct ssh1_connection_state, ppl); PktOut *pktout; if (code == SS_PING || code == SS_NOP) { if (!(s->ppl.remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE)) { pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_MSG_IGNORE); put_stringz(pktout, ""); pq_push(s->ppl.out_pq, pktout); } } else if (s->mainchan) { mainchan_special_cmd(s->mainchan, code, arg); } } static void ssh1_terminal_size(ConnectionLayer *cl, int width, int height) { struct ssh1_connection_state *s = container_of(cl, struct ssh1_connection_state, cl); s->term_width = width; s->term_height = height; if (s->mainchan) mainchan_terminal_size(s->mainchan, width, height); } static void ssh1_stdout_unthrottle(ConnectionLayer *cl, size_t bufsize) { struct ssh1_connection_state *s = container_of(cl, struct ssh1_connection_state, cl); if (s->stdout_throttling && bufsize < SSH1_BUFFER_LIMIT) { s->stdout_throttling = false; ssh_throttle_conn(s->ppl.ssh, -1); } } static size_t ssh1_stdin_backlog(ConnectionLayer *cl) { return 0; } static void ssh1_throttle_all_channels(ConnectionLayer *cl, bool throttled) { struct ssh1_connection_state *s = container_of(cl, struct ssh1_connection_state, cl); struct ssh1_channel *c; int i; for (i = 0; NULL != (c = index234(s->channels, i)); i++) chan_set_input_wanted(c->chan, !throttled); } static bool ssh1_ldisc_option(ConnectionLayer *cl, int option) { struct ssh1_connection_state *s = container_of(cl, struct ssh1_connection_state, cl); return s->ldisc_opts[option]; } static void ssh1_set_ldisc_option(ConnectionLayer *cl, int option, bool value) { struct ssh1_connection_state *s = container_of(cl, struct ssh1_connection_state, cl); s->ldisc_opts[option] = value; } static void ssh1_enable_x_fwd(ConnectionLayer *cl) { struct ssh1_connection_state *s = container_of(cl, struct ssh1_connection_state, cl); s->X11_fwd_enabled = true; } static void ssh1_set_wants_user_input(ConnectionLayer *cl, bool wanted) { struct ssh1_connection_state *s = container_of(cl, struct ssh1_connection_state, cl); s->want_user_input = wanted; s->finished_setup = true; } static bool ssh1_connection_want_user_input(PacketProtocolLayer *ppl) { struct ssh1_connection_state *s = container_of(ppl, struct ssh1_connection_state, ppl); return s->want_user_input; } static void ssh1_connection_got_user_input(PacketProtocolLayer *ppl) { struct ssh1_connection_state *s = container_of(ppl, struct ssh1_connection_state, ppl); while (s->mainchan && bufchain_size(s->ppl.user_input) > 0) { /* * Add user input to the main channel's buffer. */ ptrlen data = bufchain_prefix(s->ppl.user_input); if (data.len > 512) data.len = 512; sshfwd_write(&s->mainchan_sc, data.ptr, data.len); bufchain_consume(s->ppl.user_input, data.len); } } static void ssh1_connection_reconfigure(PacketProtocolLayer *ppl, Conf *conf) { struct ssh1_connection_state *s = container_of(ppl, struct ssh1_connection_state, ppl); conf_free(s->conf); s->conf = conf_copy(conf); if (s->portfwdmgr_configured) portfwdmgr_config(s->portfwdmgr, s->conf); } putty-0.76/ssh1connection.h0000644000175000017500000001001314072266311012660 00000000000000struct ssh1_channel; struct outstanding_succfail; struct ssh1_connection_state { int crState; Conf *conf; int local_protoflags, remote_protoflags; tree234 *channels; /* indexed by local id */ /* In SSH-1, the main session doesn't take the form of a 'channel' * according to the wire protocol. But we want to use the same API * for it, so we define an SshChannel here - but one that uses a * separate vtable from the usual one, so it doesn't map to a * struct ssh1_channel as all the others do. */ SshChannel mainchan_sc; Channel *mainchan_chan; /* the other end of mainchan_sc */ mainchan *mainchan; /* and its subtype */ bool got_pty; bool ldisc_opts[LD_N_OPTIONS]; bool stdout_throttling; bool want_user_input; bool session_terminated; int term_width, term_height, term_width_orig, term_height_orig; bool X11_fwd_enabled; struct X11Display *x11disp; struct X11FakeAuth *x11auth; tree234 *x11authtree; tree234 *rportfwds; PortFwdManager *portfwdmgr; bool portfwdmgr_configured; bool finished_setup; /* * These store the list of requests that we're waiting for * SSH_SMSG_{SUCCESS,FAILURE} replies to. (Those messages don't * come with any indication of what they're in response to, so we * have to keep track of the queue ourselves.) */ struct outstanding_succfail *succfail_head, *succfail_tail; bool compressing; /* used in server mode only */ bool sent_exit_status; /* also for server mode */ prompts_t *antispoof_prompt; int antispoof_ret; const SshServerConfig *ssc; ConnectionLayer cl; PacketProtocolLayer ppl; }; struct ssh1_channel { struct ssh1_connection_state *connlayer; unsigned remoteid, localid; int type; /* True if we opened this channel but server hasn't confirmed. */ bool halfopen; /* Bitmap of whether we've sent/received CHANNEL_CLOSE and * CHANNEL_CLOSE_CONFIRMATION. */ #define CLOSES_SENT_CLOSE 1 #define CLOSES_SENT_CLOSECONF 2 #define CLOSES_RCVD_CLOSE 4 #define CLOSES_RCVD_CLOSECONF 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. */ bool pending_eof; /* * True if this channel is causing the underlying connection to be * throttled. */ bool throttling_conn; /* * True if we currently have backed-up data on the direction of * this channel pointing out of the SSH connection, and therefore * would prefer the 'Channel' implementation not to read further * local input if possible. */ bool throttled_by_backlog; Channel *chan; /* handle the client side of this channel, if not */ SshChannel sc; /* entry point for chan to talk back to */ }; SshChannel *ssh1_session_open(ConnectionLayer *cl, Channel *chan); void ssh1_channel_init(struct ssh1_channel *c); void ssh1_channel_free(struct ssh1_channel *c); struct ssh_rportfwd *ssh1_rportfwd_alloc( ConnectionLayer *cl, const char *shost, int sport, const char *dhost, int dport, int addressfamily, const char *log_description, PortFwdRecord *pfr, ssh_sharing_connstate *share_ctx); SshChannel *ssh1_serverside_x11_open( ConnectionLayer *cl, Channel *chan, const SocketPeerInfo *pi); SshChannel *ssh1_serverside_agent_open(ConnectionLayer *cl, Channel *chan); void ssh1_connection_direction_specific_setup( struct ssh1_connection_state *s); bool ssh1_handle_direction_specific_packet( struct ssh1_connection_state *s, PktIn *pktin); bool ssh1_check_termination(struct ssh1_connection_state *s); bool ssh1_connection_need_antispoof_prompt(struct ssh1_connection_state *s); putty-0.76/ssh1login-server.c0000644000175000017500000003641714072266311013150 00000000000000/* * Packet protocol layer for the SSH-1 login phase, from the server side. */ #include #include "putty.h" #include "mpint.h" #include "ssh.h" #include "sshbpp.h" #include "sshppl.h" #include "sshcr.h" #include "sshserver.h" #include "sshkeygen.h" struct ssh1_login_server_state { int crState; PacketProtocolLayer *successor_layer; const SshServerConfig *ssc; int remote_protoflags; int local_protoflags; unsigned long supported_ciphers_mask, supported_auths_mask; unsigned cipher_type; unsigned char cookie[8]; unsigned char session_key[32]; unsigned char session_id[16]; char *username_str; ptrlen username; RSAKey *servkey, *hostkey; bool servkey_generated_here; mp_int *sesskey; AuthPolicy *authpolicy; unsigned ap_methods, current_method; unsigned char auth_rsa_expected_response[16]; RSAKey *authkey; bool auth_successful; PacketProtocolLayer ppl; }; static void ssh1_login_server_free(PacketProtocolLayer *); static void ssh1_login_server_process_queue(PacketProtocolLayer *); static bool ssh1_login_server_get_specials( PacketProtocolLayer *ppl, add_special_fn_t add_special, void *ctx) { return false; } static void ssh1_login_server_special_cmd(PacketProtocolLayer *ppl, SessionSpecialCode code, int arg) {} static bool ssh1_login_server_want_user_input( PacketProtocolLayer *ppl) { return false; } static void ssh1_login_server_got_user_input(PacketProtocolLayer *ppl) {} static void ssh1_login_server_reconfigure( PacketProtocolLayer *ppl, Conf *conf) {} static const PacketProtocolLayerVtable ssh1_login_server_vtable = { .free = ssh1_login_server_free, .process_queue = ssh1_login_server_process_queue, .get_specials = ssh1_login_server_get_specials, .special_cmd = ssh1_login_server_special_cmd, .want_user_input = ssh1_login_server_want_user_input, .got_user_input = ssh1_login_server_got_user_input, .reconfigure = ssh1_login_server_reconfigure, .queued_data_size = ssh_ppl_default_queued_data_size, .name = NULL, /* no layer names in SSH-1 */ }; PacketProtocolLayer *ssh1_login_server_new( PacketProtocolLayer *successor_layer, RSAKey *hostkey, AuthPolicy *authpolicy, const SshServerConfig *ssc) { struct ssh1_login_server_state *s = snew(struct ssh1_login_server_state); memset(s, 0, sizeof(*s)); s->ppl.vt = &ssh1_login_server_vtable; s->ssc = ssc; s->hostkey = hostkey; s->authpolicy = authpolicy; s->successor_layer = successor_layer; return &s->ppl; } static void ssh1_login_server_free(PacketProtocolLayer *ppl) { struct ssh1_login_server_state *s = container_of(ppl, struct ssh1_login_server_state, ppl); if (s->successor_layer) ssh_ppl_free(s->successor_layer); if (s->servkey_generated_here && s->servkey) { freersakey(s->servkey); sfree(s->servkey); } smemclr(s->session_key, sizeof(s->session_key)); sfree(s->username_str); sfree(s); } static bool ssh1_login_server_filter_queue(struct ssh1_login_server_state *s) { return ssh1_common_filter_queue(&s->ppl); } static PktIn *ssh1_login_server_pop(struct ssh1_login_server_state *s) { if (ssh1_login_server_filter_queue(s)) return NULL; return pq_pop(s->ppl.in_pq); } static void ssh1_login_server_process_queue(PacketProtocolLayer *ppl) { struct ssh1_login_server_state *s = container_of(ppl, struct ssh1_login_server_state, ppl); PktIn *pktin; PktOut *pktout; int i; /* Filter centrally handled messages off the front of the queue on * every entry to this coroutine, no matter where we're resuming * from, even if we're _not_ looping on pq_pop. That way we can * still proactively handle those messages even if we're waiting * for a user response. */ if (ssh1_login_server_filter_queue(s)) return; crBegin(s->crState); if (!s->servkey) { int server_key_bits = s->hostkey->bytes - 256; if (server_key_bits < 512) server_key_bits = s->hostkey->bytes + 256; s->servkey = snew(RSAKey); PrimeGenerationContext *pgc = primegen_new_context( &primegen_probabilistic); ProgressReceiver null_progress; null_progress.vt = &null_progress_vt; rsa_generate(s->servkey, server_key_bits, false, pgc, &null_progress); primegen_free_context(pgc); s->servkey->comment = NULL; s->servkey_generated_here = true; } s->local_protoflags = SSH1_PROTOFLAGS_SUPPORTED; s->supported_ciphers_mask = s->ssc->ssh1_cipher_mask; s->supported_auths_mask = 0; s->ap_methods = auth_methods(s->authpolicy); if (s->ap_methods & AUTHMETHOD_PASSWORD) s->supported_auths_mask |= (1U << SSH1_AUTH_PASSWORD); if (s->ap_methods & AUTHMETHOD_PUBLICKEY) s->supported_auths_mask |= (1U << SSH1_AUTH_RSA); if (s->ap_methods & AUTHMETHOD_TIS) s->supported_auths_mask |= (1U << SSH1_AUTH_TIS); if (s->ap_methods & AUTHMETHOD_CRYPTOCARD) s->supported_auths_mask |= (1U << SSH1_AUTH_CCARD); random_read(s->cookie, 8); pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_SMSG_PUBLIC_KEY); put_data(pktout, s->cookie, 8); rsa_ssh1_public_blob(BinarySink_UPCAST(pktout), s->servkey, RSA_SSH1_EXPONENT_FIRST); rsa_ssh1_public_blob(BinarySink_UPCAST(pktout), s->hostkey, RSA_SSH1_EXPONENT_FIRST); put_uint32(pktout, s->local_protoflags); put_uint32(pktout, s->supported_ciphers_mask); put_uint32(pktout, s->supported_auths_mask); pq_push(s->ppl.out_pq, pktout); crMaybeWaitUntilV((pktin = ssh1_login_server_pop(s)) != NULL); if (pktin->type != SSH1_CMSG_SESSION_KEY) { ssh_proto_error(s->ppl.ssh, "Received unexpected packet in response" " to initial public key packet, type %d (%s)", pktin->type, ssh1_pkt_type(pktin->type)); return; } { ptrlen client_cookie; s->cipher_type = get_byte(pktin); client_cookie = get_data(pktin, 8); s->sesskey = get_mp_ssh1(pktin); s->remote_protoflags = get_uint32(pktin); if (get_err(pktin)) { ssh_proto_error(s->ppl.ssh, "Unable to parse session key packet"); return; } if (!ptrlen_eq_ptrlen(client_cookie, make_ptrlen(s->cookie, 8))) { ssh_proto_error(s->ppl.ssh, "Client sent incorrect anti-spoofing cookie"); return; } } if (s->cipher_type >= 32 || !((s->supported_ciphers_mask >> s->cipher_type) & 1)) { ssh_proto_error(s->ppl.ssh, "Client selected an unsupported cipher"); return; } { RSAKey *smaller, *larger; strbuf *data = strbuf_new_nm(); if (mp_get_nbits(s->hostkey->modulus) > mp_get_nbits(s->servkey->modulus)) { larger = s->hostkey; smaller = s->servkey; } else { smaller = s->hostkey; larger = s->servkey; } if (rsa_ssh1_decrypt_pkcs1(s->sesskey, larger, data)) { mp_free(s->sesskey); s->sesskey = mp_from_bytes_be(ptrlen_from_strbuf(data)); strbuf_clear(data); if (rsa_ssh1_decrypt_pkcs1(s->sesskey, smaller, data) && data->len == sizeof(s->session_key)) { memcpy(s->session_key, data->u, sizeof(s->session_key)); mp_free(s->sesskey); s->sesskey = NULL; /* indicates success */ } } strbuf_free(data); } if (s->sesskey) { ssh_proto_error(s->ppl.ssh, "Failed to decrypt session key"); return; } ssh1_compute_session_id(s->session_id, s->cookie, s->hostkey, s->servkey); for (i = 0; i < 16; i++) s->session_key[i] ^= s->session_id[i]; { const ssh_cipheralg *cipher = (s->cipher_type == SSH1_CIPHER_BLOWFISH ? &ssh_blowfish_ssh1 : s->cipher_type == SSH1_CIPHER_DES ? &ssh_des : &ssh_3des_ssh1); ssh1_bpp_new_cipher(s->ppl.bpp, cipher, s->session_key); } pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_SMSG_SUCCESS); pq_push(s->ppl.out_pq, pktout); crMaybeWaitUntilV((pktin = ssh1_login_server_pop(s)) != NULL); if (pktin->type != SSH1_CMSG_USER) { ssh_proto_error(s->ppl.ssh, "Received unexpected packet while " "expecting username, type %d (%s)", pktin->type, ssh1_pkt_type(pktin->type)); return; } s->username = get_string(pktin); s->username.ptr = s->username_str = mkstr(s->username); ppl_logevent("Received username '%.*s'", PTRLEN_PRINTF(s->username)); s->auth_successful = auth_none(s->authpolicy, s->username); while (1) { /* Signal failed authentication */ pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_SMSG_FAILURE); pq_push(s->ppl.out_pq, pktout); crMaybeWaitUntilV((pktin = ssh1_login_server_pop(s)) != NULL); if (pktin->type == SSH1_CMSG_AUTH_PASSWORD) { s->current_method = AUTHMETHOD_PASSWORD; if (!(s->ap_methods & s->current_method)) continue; ptrlen password = get_string(pktin); /* Tolerate historic traffic-analysis defence of NUL + * garbage on the end of the binary password string */ char *nul = memchr(password.ptr, '\0', password.len); if (nul) password.len = (const char *)nul - (const char *)password.ptr; if (auth_password(s->authpolicy, s->username, password, NULL)) goto auth_success; } else if (pktin->type == SSH1_CMSG_AUTH_RSA) { s->current_method = AUTHMETHOD_PUBLICKEY; if (!(s->ap_methods & s->current_method)) continue; { mp_int *modulus = get_mp_ssh1(pktin); s->authkey = auth_publickey_ssh1( s->authpolicy, s->username, modulus); if (!s->authkey && s->ssc->stunt_pretend_to_accept_any_pubkey) { mp_int *zero = mp_from_integer(0); mp_int *fake_challenge = mp_random_in_range(zero, modulus); pktout = ssh_bpp_new_pktout( s->ppl.bpp, SSH1_SMSG_AUTH_RSA_CHALLENGE); put_mp_ssh1(pktout, fake_challenge); pq_push(s->ppl.out_pq, pktout); mp_free(zero); mp_free(fake_challenge); } mp_free(modulus); } if (!s->authkey && !s->ssc->stunt_pretend_to_accept_any_pubkey) continue; if (s->authkey && s->authkey->bytes < 32) { ppl_logevent("Auth key far too small"); continue; } if (s->authkey) { unsigned char *rsabuf = snewn(s->authkey->bytes, unsigned char); random_read(rsabuf, 32); { ssh_hash *h = ssh_hash_new(&ssh_md5); put_data(h, rsabuf, 32); put_data(h, s->session_id, 16); ssh_hash_final(h, s->auth_rsa_expected_response); } if (!rsa_ssh1_encrypt(rsabuf, 32, s->authkey)) { sfree(rsabuf); ppl_logevent("Failed to encrypt auth challenge"); continue; } mp_int *bn = mp_from_bytes_be( make_ptrlen(rsabuf, s->authkey->bytes)); smemclr(rsabuf, s->authkey->bytes); sfree(rsabuf); pktout = ssh_bpp_new_pktout( s->ppl.bpp, SSH1_SMSG_AUTH_RSA_CHALLENGE); put_mp_ssh1(pktout, bn); pq_push(s->ppl.out_pq, pktout); mp_free(bn); } crMaybeWaitUntilV((pktin = ssh1_login_server_pop(s)) != NULL); if (pktin->type != SSH1_CMSG_AUTH_RSA_RESPONSE) { ssh_proto_error(s->ppl.ssh, "Received unexpected packet in " "response to RSA auth challenge, type %d (%s)", pktin->type, ssh1_pkt_type(pktin->type)); return; } if (!s->authkey) continue; { ptrlen response = get_data(pktin, 16); ptrlen expected = make_ptrlen( s->auth_rsa_expected_response, 16); if (!ptrlen_eq_ptrlen(response, expected)) { ppl_logevent("Wrong response to auth challenge"); continue; } } goto auth_success; } else if (pktin->type == SSH1_CMSG_AUTH_TIS || pktin->type == SSH1_CMSG_AUTH_CCARD) { char *challenge; unsigned response_type; ptrlen response; s->current_method = (pktin->type == SSH1_CMSG_AUTH_TIS ? AUTHMETHOD_TIS : AUTHMETHOD_CRYPTOCARD); if (!(s->ap_methods & s->current_method)) continue; challenge = auth_ssh1int_challenge( s->authpolicy, s->current_method, s->username); if (!challenge) continue; pktout = ssh_bpp_new_pktout( s->ppl.bpp, (s->current_method == AUTHMETHOD_TIS ? SSH1_SMSG_AUTH_TIS_CHALLENGE : SSH1_SMSG_AUTH_CCARD_CHALLENGE)); put_stringz(pktout, challenge); pq_push(s->ppl.out_pq, pktout); sfree(challenge); crMaybeWaitUntilV((pktin = ssh1_login_server_pop(s)) != NULL); response_type = (s->current_method == AUTHMETHOD_TIS ? SSH1_CMSG_AUTH_TIS_RESPONSE : SSH1_CMSG_AUTH_CCARD_RESPONSE); if (pktin->type != response_type) { ssh_proto_error(s->ppl.ssh, "Received unexpected packet in " "response to %s challenge, type %d (%s)", (s->current_method == AUTHMETHOD_TIS ? "TIS" : "CryptoCard"), pktin->type, ssh1_pkt_type(pktin->type)); return; } response = get_string(pktin); if (auth_ssh1int_response(s->authpolicy, response)) goto auth_success; } } auth_success: if (!auth_successful(s->authpolicy, s->username, s->current_method)) { ssh_sw_abort(s->ppl.ssh, "Multiple authentications required but SSH-1" " cannot perform them"); return; } /* Signal successful authentication */ pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_SMSG_SUCCESS); pq_push(s->ppl.out_pq, pktout); ssh1_connection_set_protoflags( s->successor_layer, s->local_protoflags, s->remote_protoflags); { PacketProtocolLayer *successor = s->successor_layer; s->successor_layer = NULL; /* avoid freeing it ourself */ ssh_ppl_replace(&s->ppl, successor); return; /* we've just freed s, so avoid even touching s->crState */ } crFinishV; } putty-0.76/ssh1login.c0000644000175000017500000013741414072266311011643 00000000000000/* * Packet protocol layer for the SSH-1 login phase (combining what * SSH-2 would think of as key exchange and user authentication). */ #include #include "putty.h" #include "ssh.h" #include "mpint.h" #include "sshbpp.h" #include "sshppl.h" #include "sshcr.h" typedef struct agent_key { RSAKey key; strbuf *comment; ptrlen blob; /* only used during initial parsing of agent response */ } agent_key; struct ssh1_login_state { int crState; PacketProtocolLayer *successor_layer; Conf *conf; char *savedhost; int savedport; bool try_agent_auth, is_trivial_auth; int remote_protoflags; int local_protoflags; unsigned char session_key[32]; char *username; agent_pending_query *auth_agent_query; int len; unsigned char *rsabuf; unsigned long supported_ciphers_mask, supported_auths_mask; bool tried_publickey, tried_agent; bool tis_auth_refused, ccard_auth_refused; unsigned char cookie[8]; unsigned char session_id[16]; int cipher_type; strbuf *publickey_blob; char *publickey_comment; bool privatekey_available, privatekey_encrypted; prompts_t *cur_prompt; int userpass_ret; char c; int pwpkt_type; void *agent_response_to_free; ptrlen agent_response; BinarySource asrc[1]; /* response from SSH agent */ size_t agent_keys_len; agent_key *agent_keys; size_t agent_key_index, agent_key_limit; bool authed; RSAKey key; int dlgret; Filename *keyfile; RSAKey servkey, hostkey; bool want_user_input; StripCtrlChars *tis_scc; bool tis_scc_initialised; PacketProtocolLayer ppl; }; static void ssh1_login_free(PacketProtocolLayer *); static void ssh1_login_process_queue(PacketProtocolLayer *); static void ssh1_login_dialog_callback(void *, int); static void ssh1_login_special_cmd(PacketProtocolLayer *ppl, SessionSpecialCode code, int arg); static bool ssh1_login_want_user_input(PacketProtocolLayer *ppl); static void ssh1_login_got_user_input(PacketProtocolLayer *ppl); static void ssh1_login_reconfigure(PacketProtocolLayer *ppl, Conf *conf); static const PacketProtocolLayerVtable ssh1_login_vtable = { .free = ssh1_login_free, .process_queue = ssh1_login_process_queue, .get_specials = ssh1_common_get_specials, .special_cmd = ssh1_login_special_cmd, .want_user_input = ssh1_login_want_user_input, .got_user_input = ssh1_login_got_user_input, .reconfigure = ssh1_login_reconfigure, .queued_data_size = ssh_ppl_default_queued_data_size, .name = NULL, /* no layer names in SSH-1 */ }; static void ssh1_login_agent_query(struct ssh1_login_state *s, strbuf *req); static void ssh1_login_agent_callback(void *loginv, void *reply, int replylen); PacketProtocolLayer *ssh1_login_new( Conf *conf, const char *host, int port, PacketProtocolLayer *successor_layer) { struct ssh1_login_state *s = snew(struct ssh1_login_state); memset(s, 0, sizeof(*s)); s->ppl.vt = &ssh1_login_vtable; s->conf = conf_copy(conf); s->savedhost = dupstr(host); s->savedport = port; s->successor_layer = successor_layer; s->is_trivial_auth = true; return &s->ppl; } static void ssh1_login_free(PacketProtocolLayer *ppl) { struct ssh1_login_state *s = container_of(ppl, struct ssh1_login_state, ppl); if (s->successor_layer) ssh_ppl_free(s->successor_layer); conf_free(s->conf); sfree(s->savedhost); sfree(s->rsabuf); sfree(s->username); if (s->publickey_blob) strbuf_free(s->publickey_blob); sfree(s->publickey_comment); if (s->cur_prompt) free_prompts(s->cur_prompt); if (s->agent_keys) { for (size_t i = 0; i < s->agent_keys_len; i++) { freersakey(&s->agent_keys[i].key); strbuf_free(s->agent_keys[i].comment); } sfree(s->agent_keys); } sfree(s->agent_response_to_free); if (s->auth_agent_query) agent_cancel_query(s->auth_agent_query); sfree(s); } static bool ssh1_login_filter_queue(struct ssh1_login_state *s) { return ssh1_common_filter_queue(&s->ppl); } static PktIn *ssh1_login_pop(struct ssh1_login_state *s) { if (ssh1_login_filter_queue(s)) return NULL; return pq_pop(s->ppl.in_pq); } static void ssh1_login_setup_tis_scc(struct ssh1_login_state *s); static void ssh1_login_process_queue(PacketProtocolLayer *ppl) { struct ssh1_login_state *s = container_of(ppl, struct ssh1_login_state, ppl); PktIn *pktin; PktOut *pkt; int i; /* Filter centrally handled messages off the front of the queue on * every entry to this coroutine, no matter where we're resuming * from, even if we're _not_ looping on pq_pop. That way we can * still proactively handle those messages even if we're waiting * for a user response. */ if (ssh1_login_filter_queue(s)) return; crBegin(s->crState); crMaybeWaitUntilV((pktin = ssh1_login_pop(s)) != NULL); if (pktin->type != SSH1_SMSG_PUBLIC_KEY) { ssh_proto_error(s->ppl.ssh, "Public key packet not received"); return; } ppl_logevent("Received public keys"); { ptrlen pl = get_data(pktin, 8); memcpy(s->cookie, pl.ptr, pl.len); } get_rsa_ssh1_pub(pktin, &s->servkey, RSA_SSH1_EXPONENT_FIRST); get_rsa_ssh1_pub(pktin, &s->hostkey, RSA_SSH1_EXPONENT_FIRST); s->hostkey.comment = NULL; /* avoid confusing rsa_ssh1_fingerprint */ /* * Log the host key fingerprint. */ if (!get_err(pktin)) { char *fingerprint = rsa_ssh1_fingerprint(&s->hostkey); ppl_logevent("Host key fingerprint is:"); ppl_logevent(" %s", fingerprint); sfree(fingerprint); } s->remote_protoflags = get_uint32(pktin); s->supported_ciphers_mask = get_uint32(pktin); s->supported_auths_mask = get_uint32(pktin); if (get_err(pktin)) { ssh_proto_error(s->ppl.ssh, "Bad SSH-1 public key packet"); return; } if ((s->ppl.remote_bugs & BUG_CHOKES_ON_RSA)) s->supported_auths_mask &= ~(1 << SSH1_AUTH_RSA); s->local_protoflags = s->remote_protoflags & SSH1_PROTOFLAGS_SUPPORTED; s->local_protoflags |= SSH1_PROTOFLAG_SCREEN_NUMBER; ssh1_compute_session_id(s->session_id, s->cookie, &s->hostkey, &s->servkey); random_read(s->session_key, 32); /* * Verify that the `bits' and `bytes' parameters match. */ if (s->hostkey.bits > s->hostkey.bytes * 8 || s->servkey.bits > s->servkey.bytes * 8) { ssh_proto_error(s->ppl.ssh, "SSH-1 public keys were badly formatted"); return; } s->len = 32; if (s->len < s->hostkey.bytes) s->len = s->hostkey.bytes; if (s->len < s->servkey.bytes) s->len = s->servkey.bytes; s->rsabuf = snewn(s->len, unsigned char); /* * Verify the host key. */ { /* * First format the key into a string. */ char *keystr = rsastr_fmt(&s->hostkey); char **fingerprints = rsa_ssh1_fake_all_fingerprints(&s->hostkey); /* First check against manually configured host keys. */ s->dlgret = verify_ssh_manual_host_key(s->conf, fingerprints, NULL); if (s->dlgret == 0) { /* did not match */ ssh2_free_all_fingerprints(fingerprints); sfree(keystr); ssh_proto_error(s->ppl.ssh, "Host key did not appear in manually " "configured list"); return; } else if (s->dlgret < 0) { /* none configured; use standard handling */ char *keydisp = ssh1_pubkey_str(&s->hostkey); s->dlgret = seat_verify_ssh_host_key( s->ppl.seat, s->savedhost, s->savedport, "rsa", keystr, keydisp, fingerprints, ssh1_login_dialog_callback, s); sfree(keydisp); ssh2_free_all_fingerprints(fingerprints); sfree(keystr); #ifdef FUZZING s->dlgret = 1; #endif crMaybeWaitUntilV(s->dlgret >= 0); if (s->dlgret == 0) { ssh_user_close(s->ppl.ssh, "User aborted at host key verification"); return; } } else { ssh2_free_all_fingerprints(fingerprints); sfree(keystr); } } for (i = 0; i < 32; i++) { s->rsabuf[i] = s->session_key[i]; if (i < 16) s->rsabuf[i] ^= s->session_id[i]; } { RSAKey *smaller = (s->hostkey.bytes > s->servkey.bytes ? &s->servkey : &s->hostkey); RSAKey *larger = (s->hostkey.bytes > s->servkey.bytes ? &s->hostkey : &s->servkey); if (!rsa_ssh1_encrypt(s->rsabuf, 32, smaller) || !rsa_ssh1_encrypt(s->rsabuf, smaller->bytes, larger)) { ssh_proto_error(s->ppl.ssh, "SSH-1 public key encryptions failed " "due to bad formatting"); return; } } ppl_logevent("Encrypted session key"); { bool cipher_chosen = false, warn = false; const char *cipher_string = NULL; int i; for (i = 0; !cipher_chosen && i < CIPHER_MAX; i++) { int next_cipher = conf_get_int_int( s->conf, CONF_ssh_cipherlist, i); if (next_cipher == CIPHER_WARN) { /* If/when we choose a cipher, warn about it */ warn = true; } else if (next_cipher == CIPHER_AES) { /* XXX Probably don't need to mention this. */ ppl_logevent("AES not supported in SSH-1, skipping"); } else { switch (next_cipher) { case CIPHER_3DES: s->cipher_type = SSH1_CIPHER_3DES; cipher_string = "3DES"; break; case CIPHER_BLOWFISH: s->cipher_type = SSH1_CIPHER_BLOWFISH; cipher_string = "Blowfish"; break; case CIPHER_DES: s->cipher_type = SSH1_CIPHER_DES; cipher_string = "single-DES"; break; } if (s->supported_ciphers_mask & (1 << s->cipher_type)) cipher_chosen = true; } } if (!cipher_chosen) { if ((s->supported_ciphers_mask & (1 << SSH1_CIPHER_3DES)) == 0) { ssh_proto_error(s->ppl.ssh, "Server violates SSH-1 protocol " "by not supporting 3DES encryption"); } else { /* shouldn't happen */ ssh_sw_abort(s->ppl.ssh, "No supported ciphers found"); } return; } /* Warn about chosen cipher if necessary. */ if (warn) { s->dlgret = seat_confirm_weak_crypto_primitive( s->ppl.seat, "cipher", cipher_string, ssh1_login_dialog_callback, s); crMaybeWaitUntilV(s->dlgret >= 0); if (s->dlgret == 0) { ssh_user_close(s->ppl.ssh, "User aborted at cipher warning"); return; } } } switch (s->cipher_type) { case SSH1_CIPHER_3DES: ppl_logevent("Using 3DES encryption"); break; case SSH1_CIPHER_DES: ppl_logevent("Using single-DES encryption"); break; case SSH1_CIPHER_BLOWFISH: ppl_logevent("Using Blowfish encryption"); break; } pkt = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_CMSG_SESSION_KEY); put_byte(pkt, s->cipher_type); put_data(pkt, s->cookie, 8); put_uint16(pkt, s->len * 8); put_data(pkt, s->rsabuf, s->len); put_uint32(pkt, s->local_protoflags); pq_push(s->ppl.out_pq, pkt); ppl_logevent("Trying to enable encryption..."); sfree(s->rsabuf); s->rsabuf = NULL; /* * Force the BPP to synchronously marshal all packets up to and * including the SESSION_KEY into wire format, before we turn on * crypto. */ ssh_bpp_handle_output(s->ppl.bpp); { const ssh_cipheralg *cipher = (s->cipher_type == SSH1_CIPHER_BLOWFISH ? &ssh_blowfish_ssh1 : s->cipher_type == SSH1_CIPHER_DES ? &ssh_des : &ssh_3des_ssh1); ssh1_bpp_new_cipher(s->ppl.bpp, cipher, s->session_key); } freersakey(&s->servkey); freersakey(&s->hostkey); crMaybeWaitUntilV((pktin = ssh1_login_pop(s)) != NULL); if (pktin->type != SSH1_SMSG_SUCCESS) { ssh_proto_error(s->ppl.ssh, "Encryption not successfully enabled"); return; } ppl_logevent("Successfully started encryption"); if ((s->username = get_remote_username(s->conf)) == NULL) { s->cur_prompt = new_prompts(); s->cur_prompt->to_server = true; s->cur_prompt->from_server = false; s->cur_prompt->name = dupstr("SSH login name"); add_prompt(s->cur_prompt, dupstr("login as: "), true); s->userpass_ret = seat_get_userpass_input( s->ppl.seat, s->cur_prompt, NULL); while (1) { while (s->userpass_ret < 0 && bufchain_size(s->ppl.user_input) > 0) s->userpass_ret = seat_get_userpass_input( s->ppl.seat, s->cur_prompt, s->ppl.user_input); if (s->userpass_ret >= 0) break; s->want_user_input = true; crReturnV; s->want_user_input = false; } if (!s->userpass_ret) { /* * Failed to get a username. Terminate. */ ssh_user_close(s->ppl.ssh, "No username provided"); return; } s->username = prompt_get_result(s->cur_prompt->prompts[0]); free_prompts(s->cur_prompt); s->cur_prompt = NULL; } pkt = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_CMSG_USER); put_stringz(pkt, s->username); pq_push(s->ppl.out_pq, pkt); ppl_logevent("Sent username \"%s\"", s->username); if (seat_verbose(s->ppl.seat) || seat_interactive(s->ppl.seat)) ppl_printf("Sent username \"%s\"\r\n", s->username); crMaybeWaitUntilV((pktin = ssh1_login_pop(s)) != NULL); if (!(s->supported_auths_mask & (1 << SSH1_AUTH_RSA))) { /* We must not attempt PK auth. Pretend we've already tried it. */ s->tried_publickey = s->tried_agent = true; } else { s->tried_publickey = s->tried_agent = false; } s->tis_auth_refused = s->ccard_auth_refused = false; /* * Load the public half of any configured keyfile for later use. */ s->keyfile = conf_get_filename(s->conf, CONF_keyfile); if (!filename_is_null(s->keyfile)) { int keytype; ppl_logevent("Reading key file \"%s\"", filename_to_str(s->keyfile)); keytype = key_type(s->keyfile); if (keytype == SSH_KEYTYPE_SSH1 || keytype == SSH_KEYTYPE_SSH1_PUBLIC) { const char *error; s->publickey_blob = strbuf_new(); if (rsa1_loadpub_f(s->keyfile, BinarySink_UPCAST(s->publickey_blob), &s->publickey_comment, &error)) { s->privatekey_available = (keytype == SSH_KEYTYPE_SSH1); if (!s->privatekey_available) ppl_logevent("Key file contains public key only"); s->privatekey_encrypted = rsa1_encrypted_f(s->keyfile, NULL); } else { ppl_logevent("Unable to load key (%s)", error); ppl_printf("Unable to load key file \"%s\" (%s)\r\n", filename_to_str(s->keyfile), error); strbuf_free(s->publickey_blob); s->publickey_blob = NULL; } } else { ppl_logevent("Unable to use this key file (%s)", key_type_to_str(keytype)); ppl_printf("Unable to use key file \"%s\" (%s)\r\n", filename_to_str(s->keyfile), key_type_to_str(keytype)); } } /* Check whether we're configured to try Pageant, and also whether * it's available. */ s->try_agent_auth = (conf_get_bool(s->conf, CONF_tryagent) && agent_exists()); while (pktin->type == SSH1_SMSG_FAILURE) { s->pwpkt_type = SSH1_CMSG_AUTH_PASSWORD; if (s->try_agent_auth && !s->tried_agent) { /* * Attempt RSA authentication using Pageant. */ s->authed = false; s->tried_agent = true; ppl_logevent("Pageant is running. Requesting keys."); /* Request the keys held by the agent. */ { strbuf *request = strbuf_new_for_agent_query(); put_byte(request, SSH1_AGENTC_REQUEST_RSA_IDENTITIES); ssh1_login_agent_query(s, request); strbuf_free(request); crMaybeWaitUntilV(!s->auth_agent_query); } BinarySource_BARE_INIT_PL(s->asrc, s->agent_response); get_uint32(s->asrc); /* skip length field */ if (get_byte(s->asrc) == SSH1_AGENT_RSA_IDENTITIES_ANSWER) { size_t nkeys = get_uint32(s->asrc); size_t origpos = s->asrc->pos; /* * Check that the agent response is well formed. */ for (size_t i = 0; i < nkeys; i++) { get_rsa_ssh1_pub(s->asrc, NULL, RSA_SSH1_EXPONENT_FIRST); get_string(s->asrc); /* comment */ if (get_err(s->asrc)) { ppl_logevent("Pageant's response was truncated"); goto parsed_agent_query; } } /* * Copy the list of public-key blobs out of the Pageant * response. */ BinarySource_REWIND_TO(s->asrc, origpos); s->agent_keys_len = nkeys; s->agent_keys = snewn(s->agent_keys_len, agent_key); for (size_t i = 0; i < nkeys; i++) { memset(&s->agent_keys[i].key, 0, sizeof(s->agent_keys[i].key)); const char *blobstart = get_ptr(s->asrc); get_rsa_ssh1_pub(s->asrc, &s->agent_keys[i].key, RSA_SSH1_EXPONENT_FIRST); const char *blobend = get_ptr(s->asrc); s->agent_keys[i].comment = strbuf_new(); put_datapl(s->agent_keys[i].comment, get_string(s->asrc)); s->agent_keys[i].blob = make_ptrlen( blobstart, blobend - blobstart); } ppl_logevent("Pageant has %"SIZEu" SSH-1 keys", nkeys); if (s->publickey_blob) { /* * If we've been given a specific public key blob, * filter the list of keys to try from the agent * down to only that one, or none if it's not * there. */ ptrlen our_blob = ptrlen_from_strbuf(s->publickey_blob); size_t i; for (i = 0; i < nkeys; i++) { if (ptrlen_eq_ptrlen(our_blob, s->agent_keys[i].blob)) break; } if (i < nkeys) { ppl_logevent("Pageant key #%"SIZEu" matches " "configured key file", i); s->agent_key_index = i; s->agent_key_limit = i+1; } else { ppl_logevent("Configured key file not in Pageant"); s->agent_key_index = 0; s->agent_key_limit = 0; } } else { /* * Otherwise, try them all. */ s->agent_key_index = 0; s->agent_key_limit = nkeys; } } else { ppl_logevent("Failed to get reply from Pageant"); } parsed_agent_query:; for (; s->agent_key_index < s->agent_key_limit; s->agent_key_index++) { ppl_logevent("Trying Pageant key #%"SIZEu, s->agent_key_index); pkt = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_CMSG_AUTH_RSA); put_mp_ssh1(pkt, s->agent_keys[s->agent_key_index].key.modulus); pq_push(s->ppl.out_pq, pkt); crMaybeWaitUntilV((pktin = ssh1_login_pop(s)) != NULL); if (pktin->type != SSH1_SMSG_AUTH_RSA_CHALLENGE) { ppl_logevent("Key refused"); continue; } ppl_logevent("Received RSA challenge"); { mp_int *challenge = get_mp_ssh1(pktin); if (get_err(pktin)) { mp_free(challenge); ssh_proto_error(s->ppl.ssh, "Server's RSA challenge " "was badly formatted"); return; } strbuf *agentreq = strbuf_new_for_agent_query(); put_byte(agentreq, SSH1_AGENTC_RSA_CHALLENGE); rsa_ssh1_public_blob( BinarySink_UPCAST(agentreq), &s->agent_keys[s->agent_key_index].key, RSA_SSH1_EXPONENT_FIRST); put_mp_ssh1(agentreq, challenge); mp_free(challenge); put_data(agentreq, s->session_id, 16); put_uint32(agentreq, 1); /* response format */ ssh1_login_agent_query(s, agentreq); strbuf_free(agentreq); crMaybeWaitUntilV(!s->auth_agent_query); } { const unsigned char *ret = s->agent_response.ptr; if (ret) { if (s->agent_response.len >= 5+16 && ret[4] == SSH1_AGENT_RSA_RESPONSE) { ppl_logevent("Sending Pageant's response"); pkt = ssh_bpp_new_pktout( s->ppl.bpp, SSH1_CMSG_AUTH_RSA_RESPONSE); put_data(pkt, ret + 5, 16); pq_push(s->ppl.out_pq, pkt); s->is_trivial_auth = false; crMaybeWaitUntilV( (pktin = ssh1_login_pop(s)) != NULL); if (pktin->type == SSH1_SMSG_SUCCESS) { ppl_logevent("Pageant's response " "accepted"); if (seat_verbose(s->ppl.seat)) { ptrlen comment = ptrlen_from_strbuf( s->agent_keys[s->agent_key_index]. comment); ppl_printf("Authenticated using RSA " "key \"%.*s\" from " "agent\r\n", PTRLEN_PRINTF(comment)); } s->authed = true; } else ppl_logevent("Pageant's response not " "accepted"); } else { ppl_logevent("Pageant failed to answer " "challenge"); sfree((char *)ret); } } else { ppl_logevent("No reply received from Pageant"); } } if (s->authed) break; } if (s->authed) break; } if (s->publickey_blob && s->privatekey_available && !s->tried_publickey) { /* * Try public key authentication with the specified * key file. */ bool got_passphrase; /* need not be kept over crReturn */ if (seat_verbose(s->ppl.seat)) ppl_printf("Trying public key authentication.\r\n"); ppl_logevent("Trying public key \"%s\"", filename_to_str(s->keyfile)); s->tried_publickey = true; got_passphrase = false; while (!got_passphrase) { /* * Get a passphrase, if necessary. */ int retd; char *passphrase = NULL; /* only written after crReturn */ const char *error; if (!s->privatekey_encrypted) { if (seat_verbose(s->ppl.seat)) ppl_printf("No passphrase required.\r\n"); passphrase = NULL; } else { s->cur_prompt = new_prompts(); s->cur_prompt->to_server = false; s->cur_prompt->from_server = false; s->cur_prompt->name = dupstr("SSH key passphrase"); add_prompt(s->cur_prompt, dupprintf("Passphrase for key \"%s\": ", s->publickey_comment), false); s->userpass_ret = seat_get_userpass_input( s->ppl.seat, s->cur_prompt, NULL); while (1) { while (s->userpass_ret < 0 && bufchain_size(s->ppl.user_input) > 0) s->userpass_ret = seat_get_userpass_input( s->ppl.seat, s->cur_prompt, s->ppl.user_input); if (s->userpass_ret >= 0) break; s->want_user_input = true; crReturnV; s->want_user_input = false; } if (!s->userpass_ret) { /* Failed to get a passphrase. Terminate. */ ssh_user_close(s->ppl.ssh, "User aborted at passphrase prompt"); return; } passphrase = prompt_get_result(s->cur_prompt->prompts[0]); free_prompts(s->cur_prompt); s->cur_prompt = NULL; } /* * Try decrypting key with passphrase. */ retd = rsa1_load_f(s->keyfile, &s->key, passphrase, &error); if (passphrase) { smemclr(passphrase, strlen(passphrase)); sfree(passphrase); } if (retd == 1) { /* Correct passphrase. */ got_passphrase = true; } else if (retd == 0) { ppl_printf("Couldn't load private key from %s (%s).\r\n", filename_to_str(s->keyfile), error); got_passphrase = false; break; /* go and try something else */ } else if (retd == -1) { ppl_printf("Wrong passphrase.\r\n"); got_passphrase = false; /* and try again */ } else { unreachable("unexpected return from rsa1_load_f()"); } } if (got_passphrase) { /* * Send a public key attempt. */ pkt = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_CMSG_AUTH_RSA); put_mp_ssh1(pkt, s->key.modulus); pq_push(s->ppl.out_pq, pkt); crMaybeWaitUntilV((pktin = ssh1_login_pop(s)) != NULL); if (pktin->type == SSH1_SMSG_FAILURE) { ppl_printf("Server refused our public key.\r\n"); continue; /* go and try something else */ } if (pktin->type != SSH1_SMSG_AUTH_RSA_CHALLENGE) { ssh_proto_error(s->ppl.ssh, "Received unexpected packet" " in response to offer of public key, " "type %d (%s)", pktin->type, ssh1_pkt_type(pktin->type)); return; } { int i; unsigned char buffer[32]; mp_int *challenge, *response; challenge = get_mp_ssh1(pktin); if (get_err(pktin)) { mp_free(challenge); ssh_proto_error(s->ppl.ssh, "Server's RSA challenge " "was badly formatted"); return; } response = rsa_ssh1_decrypt(challenge, &s->key); freersapriv(&s->key); /* burn the evidence */ for (i = 0; i < 32; i++) { buffer[i] = mp_get_byte(response, 31 - i); } { ssh_hash *h = ssh_hash_new(&ssh_md5); put_data(h, buffer, 32); put_data(h, s->session_id, 16); ssh_hash_final(h, buffer); } pkt = ssh_bpp_new_pktout( s->ppl.bpp, SSH1_CMSG_AUTH_RSA_RESPONSE); put_data(pkt, buffer, 16); pq_push(s->ppl.out_pq, pkt); s->is_trivial_auth = false; mp_free(challenge); mp_free(response); } crMaybeWaitUntilV((pktin = ssh1_login_pop(s)) != NULL); if (pktin->type == SSH1_SMSG_FAILURE) { if (seat_verbose(s->ppl.seat)) ppl_printf("Failed to authenticate with" " our public key.\r\n"); continue; /* go and try something else */ } else if (pktin->type != SSH1_SMSG_SUCCESS) { ssh_proto_error(s->ppl.ssh, "Received unexpected packet" " in response to RSA authentication, " "type %d (%s)", pktin->type, ssh1_pkt_type(pktin->type)); return; } break; /* we're through! */ } } /* * Otherwise, try various forms of password-like authentication. */ s->cur_prompt = new_prompts(); if (conf_get_bool(s->conf, CONF_try_tis_auth) && (s->supported_auths_mask & (1 << SSH1_AUTH_TIS)) && !s->tis_auth_refused) { ssh1_login_setup_tis_scc(s); s->pwpkt_type = SSH1_CMSG_AUTH_TIS_RESPONSE; ppl_logevent("Requested TIS authentication"); pkt = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_CMSG_AUTH_TIS); pq_push(s->ppl.out_pq, pkt); crMaybeWaitUntilV((pktin = ssh1_login_pop(s)) != NULL); if (pktin->type == SSH1_SMSG_FAILURE) { ppl_logevent("TIS authentication declined"); if (seat_interactive(s->ppl.seat)) ppl_printf("TIS authentication refused.\r\n"); s->tis_auth_refused = true; continue; } else if (pktin->type == SSH1_SMSG_AUTH_TIS_CHALLENGE) { ptrlen challenge = get_string(pktin); if (get_err(pktin)) { ssh_proto_error(s->ppl.ssh, "TIS challenge packet was " "badly formed"); return; } ppl_logevent("Received TIS challenge"); s->cur_prompt->to_server = true; s->cur_prompt->from_server = true; s->cur_prompt->name = dupstr("SSH TIS authentication"); strbuf *sb = strbuf_new(); put_datapl(sb, PTRLEN_LITERAL("\ -- TIS authentication challenge from server: ---------------------------------\ \r\n")); if (s->tis_scc) { stripctrl_retarget(s->tis_scc, BinarySink_UPCAST(sb)); put_datapl(s->tis_scc, challenge); stripctrl_retarget(s->tis_scc, NULL); } else { put_datapl(sb, challenge); } if (!ptrlen_endswith(challenge, PTRLEN_LITERAL("\n"), NULL)) put_datapl(sb, PTRLEN_LITERAL("\r\n")); put_datapl(sb, PTRLEN_LITERAL("\ -- End of TIS authentication challenge from server: --------------------------\ \r\n")); s->cur_prompt->instruction = strbuf_to_str(sb); s->cur_prompt->instr_reqd = true; add_prompt(s->cur_prompt, dupstr( "TIS authentication response: "), false); } else { ssh_proto_error(s->ppl.ssh, "Received unexpected packet" " in response to TIS authentication, " "type %d (%s)", pktin->type, ssh1_pkt_type(pktin->type)); return; } } else if (conf_get_bool(s->conf, CONF_try_tis_auth) && (s->supported_auths_mask & (1 << SSH1_AUTH_CCARD)) && !s->ccard_auth_refused) { ssh1_login_setup_tis_scc(s); s->pwpkt_type = SSH1_CMSG_AUTH_CCARD_RESPONSE; ppl_logevent("Requested CryptoCard authentication"); pkt = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_CMSG_AUTH_CCARD); pq_push(s->ppl.out_pq, pkt); crMaybeWaitUntilV((pktin = ssh1_login_pop(s)) != NULL); if (pktin->type == SSH1_SMSG_FAILURE) { ppl_logevent("CryptoCard authentication declined"); ppl_printf("CryptoCard authentication refused.\r\n"); s->ccard_auth_refused = true; continue; } else if (pktin->type == SSH1_SMSG_AUTH_CCARD_CHALLENGE) { ptrlen challenge = get_string(pktin); if (get_err(pktin)) { ssh_proto_error(s->ppl.ssh, "CryptoCard challenge packet " "was badly formed"); return; } ppl_logevent("Received CryptoCard challenge"); s->cur_prompt->to_server = true; s->cur_prompt->from_server = true; s->cur_prompt->name = dupstr("SSH CryptoCard authentication"); strbuf *sb = strbuf_new(); put_datapl(sb, PTRLEN_LITERAL("\ -- CryptoCard authentication challenge from server: --------------------------\ \r\n")); if (s->tis_scc) { stripctrl_retarget(s->tis_scc, BinarySink_UPCAST(sb)); put_datapl(s->tis_scc, challenge); stripctrl_retarget(s->tis_scc, NULL); } else { put_datapl(sb, challenge); } if (!ptrlen_endswith(challenge, PTRLEN_LITERAL("\n"), NULL)) put_datapl(sb, PTRLEN_LITERAL("\r\n")); put_datapl(sb, PTRLEN_LITERAL("\ -- End of CryptoCard authentication challenge from server: -------------------\ \r\n")); s->cur_prompt->instruction = strbuf_to_str(sb); s->cur_prompt->instr_reqd = true; add_prompt(s->cur_prompt, dupstr( "CryptoCard authentication response: "), false); } else { ssh_proto_error(s->ppl.ssh, "Received unexpected packet" " in response to TIS authentication, " "type %d (%s)", pktin->type, ssh1_pkt_type(pktin->type)); return; } } if (s->pwpkt_type == SSH1_CMSG_AUTH_PASSWORD) { if ((s->supported_auths_mask & (1 << SSH1_AUTH_PASSWORD)) == 0) { ssh_sw_abort(s->ppl.ssh, "No supported authentication methods " "available"); return; } s->cur_prompt->to_server = true; s->cur_prompt->from_server = false; s->cur_prompt->name = dupstr("SSH password"); add_prompt(s->cur_prompt, dupprintf("%s@%s's password: ", s->username, s->savedhost), false); } /* * Show password prompt, having first obtained it via a TIS * or CryptoCard exchange if we're doing TIS or CryptoCard * authentication. */ s->userpass_ret = seat_get_userpass_input( s->ppl.seat, s->cur_prompt, NULL); while (1) { while (s->userpass_ret < 0 && bufchain_size(s->ppl.user_input) > 0) s->userpass_ret = seat_get_userpass_input( s->ppl.seat, s->cur_prompt, s->ppl.user_input); if (s->userpass_ret >= 0) break; s->want_user_input = true; crReturnV; s->want_user_input = false; } if (!s->userpass_ret) { /* * Failed to get a password (for example * because one was supplied on the command line * which has already failed to work). Terminate. */ ssh_user_close(s->ppl.ssh, "User aborted at password prompt"); return; } if (s->pwpkt_type == SSH1_CMSG_AUTH_PASSWORD) { /* * Defence against traffic analysis: we send a * whole bunch of packets containing strings of * different lengths. One of these strings is the * password, in a SSH1_CMSG_AUTH_PASSWORD packet. * The others are all random data in * SSH1_MSG_IGNORE packets. This way a passive * listener can't tell which is the password, and * hence can't deduce the password length. * * Anybody with a password length greater than 16 * bytes is going to have enough entropy in their * password that a listener won't find it _that_ * much help to know how long it is. So what we'll * do is: * * - if password length < 16, we send 15 packets * containing string lengths 1 through 15 * * - otherwise, we let N be the nearest multiple * of 8 below the password length, and send 8 * packets containing string lengths N through * N+7. This won't obscure the order of * magnitude of the password length, but it will * introduce a bit of extra uncertainty. * * A few servers can't deal with SSH1_MSG_IGNORE, at * least in this context. For these servers, we need * an alternative defence. We make use of the fact * that the password is interpreted as a C string: * so we can append a NUL, then some random data. * * A few servers can deal with neither SSH1_MSG_IGNORE * here _nor_ a padded password string. * For these servers we are left with no defences * against password length sniffing. */ if (!(s->ppl.remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE) && !(s->ppl.remote_bugs & BUG_NEEDS_SSH1_PLAIN_PASSWORD)) { /* * The server can deal with SSH1_MSG_IGNORE, so * we can use the primary defence. */ int bottom, top, pwlen, i; const char *pw = prompt_get_result_ref( s->cur_prompt->prompts[0]); pwlen = strlen(pw); if (pwlen < 16) { bottom = 0; /* zero length passwords are OK! :-) */ top = 15; } else { bottom = pwlen & ~7; top = bottom + 7; } assert(pwlen >= bottom && pwlen <= top); for (i = bottom; i <= top; i++) { if (i == pwlen) { pkt = ssh_bpp_new_pktout(s->ppl.bpp, s->pwpkt_type); put_stringz(pkt, pw); pq_push(s->ppl.out_pq, pkt); } else { strbuf *random_data = strbuf_new_nm(); random_read(strbuf_append(random_data, i), i); pkt = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_MSG_IGNORE); put_stringsb(pkt, random_data); pq_push(s->ppl.out_pq, pkt); } } ppl_logevent("Sending password with camouflage packets"); } else if (!(s->ppl.remote_bugs & BUG_NEEDS_SSH1_PLAIN_PASSWORD)) { /* * The server can't deal with SSH1_MSG_IGNORE * but can deal with padded passwords, so we * can use the secondary defence. */ strbuf *padded_pw = strbuf_new_nm(); ppl_logevent("Sending length-padded password"); pkt = ssh_bpp_new_pktout(s->ppl.bpp, s->pwpkt_type); put_asciz(padded_pw, prompt_get_result_ref( s->cur_prompt->prompts[0])); size_t pad = 63 & -padded_pw->len; random_read(strbuf_append(padded_pw, pad), pad); put_stringsb(pkt, padded_pw); pq_push(s->ppl.out_pq, pkt); } else { /* * The server is believed unable to cope with * any of our password camouflage methods. */ ppl_logevent("Sending unpadded password"); pkt = ssh_bpp_new_pktout(s->ppl.bpp, s->pwpkt_type); put_stringz(pkt, prompt_get_result_ref( s->cur_prompt->prompts[0])); pq_push(s->ppl.out_pq, pkt); } } else { pkt = ssh_bpp_new_pktout(s->ppl.bpp, s->pwpkt_type); put_stringz(pkt, prompt_get_result_ref(s->cur_prompt->prompts[0])); pq_push(s->ppl.out_pq, pkt); } s->is_trivial_auth = false; ppl_logevent("Sent password"); free_prompts(s->cur_prompt); s->cur_prompt = NULL; crMaybeWaitUntilV((pktin = ssh1_login_pop(s)) != NULL); if (pktin->type == SSH1_SMSG_FAILURE) { if (seat_verbose(s->ppl.seat)) ppl_printf("Access denied\r\n"); ppl_logevent("Authentication refused"); } else if (pktin->type != SSH1_SMSG_SUCCESS) { ssh_proto_error(s->ppl.ssh, "Received unexpected packet" " in response to password authentication, type %d " "(%s)", pktin->type, ssh1_pkt_type(pktin->type)); return; } } if (conf_get_bool(s->conf, CONF_ssh_no_trivial_userauth) && s->is_trivial_auth) { ssh_proto_error(s->ppl.ssh, "Authentication was trivial! " "Abandoning session as specified in configuration."); return; } ppl_logevent("Authentication successful"); if (conf_get_bool(s->conf, CONF_compression)) { ppl_logevent("Requesting compression"); pkt = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_CMSG_REQUEST_COMPRESSION); put_uint32(pkt, 6); /* gzip compression level */ pq_push(s->ppl.out_pq, pkt); crMaybeWaitUntilV((pktin = ssh1_login_pop(s)) != NULL); if (pktin->type == SSH1_SMSG_SUCCESS) { /* * We don't have to actually do anything here: the SSH-1 * BPP will take care of automatically starting the * compression, by recognising our outgoing request packet * and the success response. (Horrible, but it's the * easiest way to avoid race conditions if other packets * cross in transit.) */ } else if (pktin->type == SSH1_SMSG_FAILURE) { ppl_logevent("Server refused to enable compression"); ppl_printf("Server refused to compress\r\n"); } else { ssh_proto_error(s->ppl.ssh, "Received unexpected packet" " in response to compression request, type %d " "(%s)", pktin->type, ssh1_pkt_type(pktin->type)); return; } } ssh1_connection_set_protoflags( s->successor_layer, s->local_protoflags, s->remote_protoflags); { PacketProtocolLayer *successor = s->successor_layer; s->successor_layer = NULL; /* avoid freeing it ourself */ ssh_ppl_replace(&s->ppl, successor); return; /* we've just freed s, so avoid even touching s->crState */ } crFinishV; } static void ssh1_login_setup_tis_scc(struct ssh1_login_state *s) { if (s->tis_scc_initialised) return; s->tis_scc = seat_stripctrl_new(s->ppl.seat, NULL, SIC_KI_PROMPTS); if (s->tis_scc) stripctrl_enable_line_limiting(s->tis_scc); s->tis_scc_initialised = true; } static void ssh1_login_dialog_callback(void *loginv, int ret) { struct ssh1_login_state *s = (struct ssh1_login_state *)loginv; s->dlgret = ret; ssh_ppl_process_queue(&s->ppl); } static void ssh1_login_agent_query(struct ssh1_login_state *s, strbuf *req) { void *response; int response_len; sfree(s->agent_response_to_free); s->agent_response_to_free = NULL; s->auth_agent_query = agent_query(req, &response, &response_len, ssh1_login_agent_callback, s); if (!s->auth_agent_query) ssh1_login_agent_callback(s, response, response_len); } static void ssh1_login_agent_callback(void *loginv, void *reply, int replylen) { struct ssh1_login_state *s = (struct ssh1_login_state *)loginv; s->auth_agent_query = NULL; s->agent_response_to_free = reply; s->agent_response = make_ptrlen(reply, replylen); queue_idempotent_callback(&s->ppl.ic_process_queue); } static void ssh1_login_special_cmd(PacketProtocolLayer *ppl, SessionSpecialCode code, int arg) { struct ssh1_login_state *s = container_of(ppl, struct ssh1_login_state, ppl); PktOut *pktout; if (code == SS_PING || code == SS_NOP) { if (!(s->ppl.remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE)) { pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH1_MSG_IGNORE); put_stringz(pktout, ""); pq_push(s->ppl.out_pq, pktout); } } } static bool ssh1_login_want_user_input(PacketProtocolLayer *ppl) { struct ssh1_login_state *s = container_of(ppl, struct ssh1_login_state, ppl); return s->want_user_input; } static void ssh1_login_got_user_input(PacketProtocolLayer *ppl) { struct ssh1_login_state *s = container_of(ppl, struct ssh1_login_state, ppl); if (s->want_user_input) queue_idempotent_callback(&s->ppl.ic_process_queue); } static void ssh1_login_reconfigure(PacketProtocolLayer *ppl, Conf *conf) { struct ssh1_login_state *s = container_of(ppl, struct ssh1_login_state, ppl); ssh_ppl_reconfigure(s->successor_layer, conf); } putty-0.76/ssh2bpp-bare.c0000644000175000017500000001525714072266311012224 00000000000000/* * Trivial binary packet protocol for the 'bare' ssh-connection * protocol used in PuTTY's SSH-2 connection sharing system. */ #include #include "putty.h" #include "ssh.h" #include "sshbpp.h" #include "sshcr.h" struct ssh2_bare_bpp_state { int crState; long packetlen, maxlen; unsigned char *data; unsigned long incoming_sequence, outgoing_sequence; PktIn *pktin; BinaryPacketProtocol bpp; }; static void ssh2_bare_bpp_free(BinaryPacketProtocol *bpp); static void ssh2_bare_bpp_handle_input(BinaryPacketProtocol *bpp); static void ssh2_bare_bpp_handle_output(BinaryPacketProtocol *bpp); static PktOut *ssh2_bare_bpp_new_pktout(int type); static const BinaryPacketProtocolVtable ssh2_bare_bpp_vtable = { .free = ssh2_bare_bpp_free, .handle_input = ssh2_bare_bpp_handle_input, .handle_output = ssh2_bare_bpp_handle_output, .new_pktout = ssh2_bare_bpp_new_pktout, .queue_disconnect = ssh2_bpp_queue_disconnect, /* in sshcommon.c */ /* packet size limit, per protocol spec in sshshare.c comment */ .packet_size_limit = 0x4000, }; BinaryPacketProtocol *ssh2_bare_bpp_new(LogContext *logctx) { struct ssh2_bare_bpp_state *s = snew(struct ssh2_bare_bpp_state); memset(s, 0, sizeof(*s)); s->bpp.vt = &ssh2_bare_bpp_vtable; s->bpp.logctx = logctx; ssh_bpp_common_setup(&s->bpp); return &s->bpp; } static void ssh2_bare_bpp_free(BinaryPacketProtocol *bpp) { struct ssh2_bare_bpp_state *s = container_of(bpp, struct ssh2_bare_bpp_state, bpp); sfree(s->pktin); sfree(s); } #define BPP_READ(ptr, len) do \ { \ bool success; \ crMaybeWaitUntilV((success = bufchain_try_fetch_consume( \ s->bpp.in_raw, ptr, len)) || \ s->bpp.input_eof); \ if (!success) \ goto eof; \ ssh_check_frozen(s->bpp.ssh); \ } while (0) static void ssh2_bare_bpp_handle_input(BinaryPacketProtocol *bpp) { struct ssh2_bare_bpp_state *s = container_of(bpp, struct ssh2_bare_bpp_state, bpp); crBegin(s->crState); while (1) { /* Read the length field. */ { unsigned char lenbuf[4]; BPP_READ(lenbuf, 4); s->packetlen = toint(GET_32BIT_MSB_FIRST(lenbuf)); } if (s->packetlen <= 0 || s->packetlen >= (long)OUR_V2_PACKETLIMIT) { ssh_sw_abort(s->bpp.ssh, "Invalid packet length received"); crStopV; } /* * Allocate the packet to return, now we know its length. */ s->pktin = snew_plus(PktIn, s->packetlen); s->pktin->qnode.prev = s->pktin->qnode.next = NULL; s->pktin->qnode.on_free_queue = false; s->maxlen = 0; s->data = snew_plus_get_aux(s->pktin); s->pktin->sequence = s->incoming_sequence++; /* * Read the remainder of the packet. */ BPP_READ(s->data, s->packetlen); /* * The data we just read is precisely the initial type byte * followed by the packet payload. */ s->pktin->type = s->data[0]; s->data++; s->packetlen--; BinarySource_INIT(s->pktin, s->data, s->packetlen); if (s->pktin->type == SSH2_MSG_EXT_INFO) { /* * Mild layer violation: EXT_INFO is not permitted in the * bare ssh-connection protocol. Faulting it here means * that ssh2_common_filter_queue doesn't receive it in the * first place unless it's legal to have sent it. */ ssh_proto_error(s->bpp.ssh, "Remote side sent SSH2_MSG_EXT_INFO " "in bare connection protocol"); return; } /* * Log incoming packet, possibly omitting sensitive fields. */ if (s->bpp.logctx) { logblank_t blanks[MAX_BLANKS]; int nblanks = ssh2_censor_packet( s->bpp.pls, s->pktin->type, false, make_ptrlen(s->data, s->packetlen), blanks); log_packet(s->bpp.logctx, PKT_INCOMING, s->pktin->type, ssh2_pkt_type(s->bpp.pls->kctx, s->bpp.pls->actx, s->pktin->type), get_ptr(s->pktin), get_avail(s->pktin), nblanks, blanks, &s->pktin->sequence, 0, NULL); } if (ssh2_bpp_check_unimplemented(&s->bpp, s->pktin)) { sfree(s->pktin); s->pktin = NULL; continue; } s->pktin->qnode.formal_size = get_avail(s->pktin); pq_push(&s->bpp.in_pq, s->pktin); s->pktin = NULL; } eof: if (!s->bpp.expect_close) { ssh_remote_error(s->bpp.ssh, "Remote side unexpectedly closed network connection"); } else { ssh_remote_eof(s->bpp.ssh, "Remote side closed network connection"); } return; /* avoid touching s now it's been freed */ crFinishV; } static PktOut *ssh2_bare_bpp_new_pktout(int pkt_type) { PktOut *pkt = ssh_new_packet(); pkt->length = 4; /* space for packet length */ pkt->type = pkt_type; put_byte(pkt, pkt_type); return pkt; } static void ssh2_bare_bpp_format_packet(struct ssh2_bare_bpp_state *s, PktOut *pkt) { if (s->bpp.logctx) { ptrlen pktdata = make_ptrlen(pkt->data + 5, pkt->length - 5); logblank_t blanks[MAX_BLANKS]; int nblanks = ssh2_censor_packet( s->bpp.pls, pkt->type, true, pktdata, blanks); log_packet(s->bpp.logctx, PKT_OUTGOING, pkt->type, ssh2_pkt_type(s->bpp.pls->kctx, s->bpp.pls->actx, pkt->type), pktdata.ptr, pktdata.len, nblanks, blanks, &s->outgoing_sequence, pkt->downstream_id, pkt->additional_log_text); } s->outgoing_sequence++; /* only for diagnostics, really */ PUT_32BIT_MSB_FIRST(pkt->data, pkt->length - 4); bufchain_add(s->bpp.out_raw, pkt->data, pkt->length); } static void ssh2_bare_bpp_handle_output(BinaryPacketProtocol *bpp) { struct ssh2_bare_bpp_state *s = container_of(bpp, struct ssh2_bare_bpp_state, bpp); PktOut *pkt; while ((pkt = pq_pop(&s->bpp.out_pq)) != NULL) { ssh2_bare_bpp_format_packet(s, pkt); ssh_free_pktout(pkt); } } putty-0.76/ssh2bpp.c0000644000175000017500000010754114072266311011313 00000000000000/* * Binary packet protocol for SSH-2. */ #include #include "putty.h" #include "ssh.h" #include "sshbpp.h" #include "sshcr.h" struct ssh2_bpp_direction { unsigned long sequence; ssh_cipher *cipher; ssh2_mac *mac; bool etm_mode; const ssh_compression_alg *pending_compression; }; struct ssh2_bpp_state { int crState; long len, pad, payload, packetlen, maclen, length, maxlen; unsigned char *buf; size_t bufsize; unsigned char *data; unsigned cipherblk; PktIn *pktin; struct DataTransferStats *stats; bool cbc_ignore_workaround; struct ssh2_bpp_direction in, out; /* comp and decomp logically belong in the per-direction * substructure, except that they have different types */ ssh_decompressor *in_decomp; ssh_compressor *out_comp; bool is_server; bool pending_newkeys; bool pending_compression, seen_userauth_success; bool enforce_next_packet_is_userauth_success; unsigned nnewkeys; int prev_type; BinaryPacketProtocol bpp; }; static void ssh2_bpp_free(BinaryPacketProtocol *bpp); static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp); static void ssh2_bpp_handle_output(BinaryPacketProtocol *bpp); static PktOut *ssh2_bpp_new_pktout(int type); static const BinaryPacketProtocolVtable ssh2_bpp_vtable = { .free = ssh2_bpp_free, .handle_input = ssh2_bpp_handle_input, .handle_output = ssh2_bpp_handle_output, .new_pktout = ssh2_bpp_new_pktout, .queue_disconnect = ssh2_bpp_queue_disconnect, /* in sshcommon.c */ .packet_size_limit = 0xFFFFFFFF, /* no special limit for this bpp */ }; BinaryPacketProtocol *ssh2_bpp_new( LogContext *logctx, struct DataTransferStats *stats, bool is_server) { struct ssh2_bpp_state *s = snew(struct ssh2_bpp_state); memset(s, 0, sizeof(*s)); s->bpp.vt = &ssh2_bpp_vtable; s->bpp.logctx = logctx; s->stats = stats; s->is_server = is_server; ssh_bpp_common_setup(&s->bpp); return &s->bpp; } static void ssh2_bpp_free_outgoing_crypto(struct ssh2_bpp_state *s) { /* * We must free the MAC before the cipher, because sometimes the * MAC is not actually separately allocated but just a different * facet of the same object as the cipher, in which case * ssh2_mac_free does nothing and ssh_cipher_free does the actual * freeing. So if we freed the cipher first and then tried to * dereference the MAC's vtable pointer to find out how to free * that too, we'd be accessing freed memory. */ if (s->out.mac) ssh2_mac_free(s->out.mac); if (s->out.cipher) ssh_cipher_free(s->out.cipher); if (s->out_comp) ssh_compressor_free(s->out_comp); } static void ssh2_bpp_free_incoming_crypto(struct ssh2_bpp_state *s) { /* As above, take care to free in.mac before in.cipher */ if (s->in.mac) ssh2_mac_free(s->in.mac); if (s->in.cipher) ssh_cipher_free(s->in.cipher); if (s->in_decomp) ssh_decompressor_free(s->in_decomp); } static void ssh2_bpp_free(BinaryPacketProtocol *bpp) { struct ssh2_bpp_state *s = container_of(bpp, struct ssh2_bpp_state, bpp); sfree(s->buf); ssh2_bpp_free_outgoing_crypto(s); ssh2_bpp_free_incoming_crypto(s); sfree(s->pktin); sfree(s); } void ssh2_bpp_new_outgoing_crypto( BinaryPacketProtocol *bpp, const ssh_cipheralg *cipher, const void *ckey, const void *iv, const ssh2_macalg *mac, bool etm_mode, const void *mac_key, const ssh_compression_alg *compression, bool delayed_compression) { struct ssh2_bpp_state *s; assert(bpp->vt == &ssh2_bpp_vtable); s = container_of(bpp, struct ssh2_bpp_state, bpp); ssh2_bpp_free_outgoing_crypto(s); if (cipher) { s->out.cipher = ssh_cipher_new(cipher); ssh_cipher_setkey(s->out.cipher, ckey); ssh_cipher_setiv(s->out.cipher, iv); s->cbc_ignore_workaround = ( (ssh_cipher_alg(s->out.cipher)->flags & SSH_CIPHER_IS_CBC) && !(s->bpp.remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE)); bpp_logevent("Initialised %s outbound encryption", ssh_cipher_alg(s->out.cipher)->text_name); } else { s->out.cipher = NULL; s->cbc_ignore_workaround = false; } s->out.etm_mode = etm_mode; if (mac) { s->out.mac = ssh2_mac_new(mac, s->out.cipher); ssh2_mac_setkey(s->out.mac, make_ptrlen(mac_key, mac->keylen)); bpp_logevent("Initialised %s outbound MAC algorithm%s%s", ssh2_mac_text_name(s->out.mac), etm_mode ? " (in ETM mode)" : "", (s->out.cipher && ssh_cipher_alg(s->out.cipher)->required_mac ? " (required by cipher)" : "")); } else { s->out.mac = NULL; } if (delayed_compression && !s->seen_userauth_success) { s->out.pending_compression = compression; s->out_comp = NULL; bpp_logevent("Will enable %s compression after user authentication", s->out.pending_compression->text_name); } else { s->out.pending_compression = NULL; /* 'compression' is always non-NULL, because no compression is * indicated by ssh_comp_none. But this setup call may return a * null out_comp. */ s->out_comp = ssh_compressor_new(compression); if (s->out_comp) bpp_logevent("Initialised %s compression", ssh_compressor_alg(s->out_comp)->text_name); } } void ssh2_bpp_new_incoming_crypto( BinaryPacketProtocol *bpp, const ssh_cipheralg *cipher, const void *ckey, const void *iv, const ssh2_macalg *mac, bool etm_mode, const void *mac_key, const ssh_compression_alg *compression, bool delayed_compression) { struct ssh2_bpp_state *s; assert(bpp->vt == &ssh2_bpp_vtable); s = container_of(bpp, struct ssh2_bpp_state, bpp); ssh2_bpp_free_incoming_crypto(s); if (cipher) { s->in.cipher = ssh_cipher_new(cipher); ssh_cipher_setkey(s->in.cipher, ckey); ssh_cipher_setiv(s->in.cipher, iv); bpp_logevent("Initialised %s inbound encryption", ssh_cipher_alg(s->in.cipher)->text_name); } else { s->in.cipher = NULL; } s->in.etm_mode = etm_mode; if (mac) { s->in.mac = ssh2_mac_new(mac, s->in.cipher); ssh2_mac_setkey(s->in.mac, make_ptrlen(mac_key, mac->keylen)); bpp_logevent("Initialised %s inbound MAC algorithm%s%s", ssh2_mac_text_name(s->in.mac), etm_mode ? " (in ETM mode)" : "", (s->in.cipher && ssh_cipher_alg(s->in.cipher)->required_mac ? " (required by cipher)" : "")); } else { s->in.mac = NULL; } if (delayed_compression && !s->seen_userauth_success) { s->in.pending_compression = compression; s->in_decomp = NULL; bpp_logevent("Will enable %s decompression after user authentication", s->in.pending_compression->text_name); } else { s->in.pending_compression = NULL; /* 'compression' is always non-NULL, because no compression is * indicated by ssh_comp_none. But this setup call may return a * null in_decomp. */ s->in_decomp = ssh_decompressor_new(compression); if (s->in_decomp) bpp_logevent("Initialised %s decompression", ssh_decompressor_alg(s->in_decomp)->text_name); } /* Clear the pending_newkeys flag, so that handle_input below will * start consuming the input data again. */ s->pending_newkeys = false; /* And schedule a run of handle_input, in case there's already * input data in the queue. */ queue_idempotent_callback(&s->bpp.ic_in_raw); } bool ssh2_bpp_rekey_inadvisable(BinaryPacketProtocol *bpp) { struct ssh2_bpp_state *s; assert(bpp->vt == &ssh2_bpp_vtable); s = container_of(bpp, struct ssh2_bpp_state, bpp); return s->pending_compression; } static void ssh2_bpp_enable_pending_compression(struct ssh2_bpp_state *s) { BinaryPacketProtocol *bpp = &s->bpp; /* for bpp_logevent */ if (s->in.pending_compression) { s->in_decomp = ssh_decompressor_new(s->in.pending_compression); bpp_logevent("Initialised delayed %s decompression", ssh_decompressor_alg(s->in_decomp)->text_name); s->in.pending_compression = NULL; } if (s->out.pending_compression) { s->out_comp = ssh_compressor_new(s->out.pending_compression); bpp_logevent("Initialised delayed %s compression", ssh_compressor_alg(s->out_comp)->text_name); s->out.pending_compression = NULL; } } #define BPP_READ(ptr, len) do \ { \ bool success; \ crMaybeWaitUntilV((success = bufchain_try_fetch_consume( \ s->bpp.in_raw, ptr, len)) || \ s->bpp.input_eof); \ if (!success) \ goto eof; \ ssh_check_frozen(s->bpp.ssh); \ } while (0) #define userauth_range(pkttype) ((unsigned)((pkttype) - 50) < 20) static void ssh2_bpp_handle_input(BinaryPacketProtocol *bpp) { struct ssh2_bpp_state *s = container_of(bpp, struct ssh2_bpp_state, bpp); crBegin(s->crState); while (1) { s->maxlen = 0; s->length = 0; if (s->in.cipher) s->cipherblk = ssh_cipher_alg(s->in.cipher)->blksize; else s->cipherblk = 8; if (s->cipherblk < 8) s->cipherblk = 8; s->maclen = s->in.mac ? ssh2_mac_alg(s->in.mac)->len : 0; if (s->in.cipher && (ssh_cipher_alg(s->in.cipher)->flags & SSH_CIPHER_IS_CBC) && s->in.mac && !s->in.etm_mode) { /* * When dealing with a CBC-mode cipher, we want to avoid the * possibility of an attacker's tweaking the ciphertext stream * so as to cause us to feed the same block to the block * cipher more than once and thus leak information * (VU#958563). The way we do this is not to take any * decisions on the basis of anything we've decrypted until * we've verified it with a MAC. That includes the packet * length, so we just read data and check the MAC repeatedly, * and when the MAC passes, see if the length we've got is * plausible. * * This defence is unnecessary in OpenSSH ETM mode, because * the whole point of ETM mode is that the attacker can't * tweak the ciphertext stream at all without the MAC * detecting it before we decrypt anything. */ /* * Make sure we have buffer space for a maximum-size packet. */ unsigned buflimit = OUR_V2_PACKETLIMIT + s->maclen; if (s->bufsize < buflimit) { s->bufsize = buflimit; s->buf = sresize(s->buf, s->bufsize, unsigned char); } /* Read an amount corresponding to the MAC. */ BPP_READ(s->buf, s->maclen); s->packetlen = 0; ssh2_mac_start(s->in.mac); put_uint32(s->in.mac, s->in.sequence); for (;;) { /* Once around this loop per cipher block. */ /* Read another cipher-block's worth, and tack it on to * the end. */ BPP_READ(s->buf + (s->packetlen + s->maclen), s->cipherblk); /* Decrypt one more block (a little further back in * the stream). */ ssh_cipher_decrypt(s->in.cipher, s->buf + s->packetlen, s->cipherblk); /* Feed that block to the MAC. */ put_data(s->in.mac, s->buf + s->packetlen, s->cipherblk); s->packetlen += s->cipherblk; /* See if that gives us a valid packet. */ if (ssh2_mac_verresult(s->in.mac, s->buf + s->packetlen) && ((s->len = toint(GET_32BIT_MSB_FIRST(s->buf))) == s->packetlen-4)) break; if (s->packetlen >= (long)OUR_V2_PACKETLIMIT) { ssh_sw_abort(s->bpp.ssh, "No valid incoming packet found"); crStopV; } } s->maxlen = s->packetlen + s->maclen; /* * Now transfer the data into an output packet. */ s->pktin = snew_plus(PktIn, s->maxlen); s->pktin->qnode.prev = s->pktin->qnode.next = NULL; s->pktin->type = 0; s->pktin->qnode.on_free_queue = false; s->data = snew_plus_get_aux(s->pktin); memcpy(s->data, s->buf, s->maxlen); } else if (s->in.mac && s->in.etm_mode) { if (s->bufsize < 4) { s->bufsize = 4; s->buf = sresize(s->buf, s->bufsize, unsigned char); } /* * OpenSSH encrypt-then-MAC mode: the packet length is * unencrypted, unless the cipher supports length encryption. */ BPP_READ(s->buf, 4); /* Cipher supports length decryption, so do it */ if (s->in.cipher && (ssh_cipher_alg(s->in.cipher)->flags & SSH_CIPHER_SEPARATE_LENGTH)) { /* Keep the packet the same though, so the MAC passes */ unsigned char len[4]; memcpy(len, s->buf, 4); ssh_cipher_decrypt_length( s->in.cipher, len, 4, s->in.sequence); s->len = toint(GET_32BIT_MSB_FIRST(len)); } else { s->len = toint(GET_32BIT_MSB_FIRST(s->buf)); } /* * _Completely_ silly lengths should be stomped on before they * do us any more damage. */ if (s->len < 0 || s->len > (long)OUR_V2_PACKETLIMIT || s->len % s->cipherblk != 0) { ssh_sw_abort(s->bpp.ssh, "Incoming packet length field was garbled"); crStopV; } /* * So now we can work out the total packet length. */ s->packetlen = s->len + 4; /* * Allocate the packet to return, now we know its length. */ s->pktin = snew_plus(PktIn, OUR_V2_PACKETLIMIT + s->maclen); s->pktin->qnode.prev = s->pktin->qnode.next = NULL; s->pktin->type = 0; s->pktin->qnode.on_free_queue = false; s->data = snew_plus_get_aux(s->pktin); memcpy(s->data, s->buf, 4); /* * Read the remainder of the packet. */ BPP_READ(s->data + 4, s->packetlen + s->maclen - 4); /* * Check the MAC. */ if (s->in.mac && !ssh2_mac_verify( s->in.mac, s->data, s->len + 4, s->in.sequence)) { ssh_sw_abort(s->bpp.ssh, "Incorrect MAC received on packet"); crStopV; } /* Decrypt everything between the length field and the MAC. */ if (s->in.cipher) ssh_cipher_decrypt( s->in.cipher, s->data + 4, s->packetlen - 4); } else { if (s->bufsize < s->cipherblk) { s->bufsize = s->cipherblk; s->buf = sresize(s->buf, s->bufsize, unsigned char); } /* * Acquire and decrypt the first block of the packet. This will * contain the length and padding details. */ BPP_READ(s->buf, s->cipherblk); if (s->in.cipher) ssh_cipher_decrypt(s->in.cipher, s->buf, s->cipherblk); /* * Now get the length figure. */ s->len = toint(GET_32BIT_MSB_FIRST(s->buf)); /* * _Completely_ silly lengths should be stomped on before they * do us any more damage. */ if (s->len < 0 || s->len > (long)OUR_V2_PACKETLIMIT || (s->len + 4) % s->cipherblk != 0) { ssh_sw_abort(s->bpp.ssh, "Incoming packet was garbled on decryption"); crStopV; } /* * So now we can work out the total packet length. */ s->packetlen = s->len + 4; /* * Allocate the packet to return, now we know its length. */ s->maxlen = s->packetlen + s->maclen; s->pktin = snew_plus(PktIn, s->maxlen); s->pktin->qnode.prev = s->pktin->qnode.next = NULL; s->pktin->type = 0; s->pktin->qnode.on_free_queue = false; s->data = snew_plus_get_aux(s->pktin); memcpy(s->data, s->buf, s->cipherblk); /* * Read and decrypt the remainder of the packet. */ BPP_READ(s->data + s->cipherblk, s->packetlen + s->maclen - s->cipherblk); /* Decrypt everything _except_ the MAC. */ if (s->in.cipher) ssh_cipher_decrypt( s->in.cipher, s->data + s->cipherblk, s->packetlen - s->cipherblk); /* * Check the MAC. */ if (s->in.mac && !ssh2_mac_verify( s->in.mac, s->data, s->len + 4, s->in.sequence)) { ssh_sw_abort(s->bpp.ssh, "Incorrect MAC received on packet"); crStopV; } } /* Get and sanity-check the amount of random padding. */ s->pad = s->data[4]; if (s->pad < 4 || s->len - s->pad < 1) { ssh_sw_abort(s->bpp.ssh, "Invalid padding length on received packet"); crStopV; } /* * This enables us to deduce the payload length. */ s->payload = s->len - s->pad - 1; s->length = s->payload + 5; dts_consume(&s->stats->in, s->packetlen); s->pktin->sequence = s->in.sequence++; s->length = s->packetlen - s->pad; assert(s->length >= 0); /* * Decompress packet payload. */ { unsigned char *newpayload; int newlen; if (s->in_decomp && ssh_decompressor_decompress( s->in_decomp, s->data + 5, s->length - 5, &newpayload, &newlen)) { if (s->maxlen < newlen + 5) { PktIn *old_pktin = s->pktin; s->maxlen = newlen + 5; s->pktin = snew_plus(PktIn, s->maxlen); *s->pktin = *old_pktin; /* structure copy */ s->data = snew_plus_get_aux(s->pktin); smemclr(old_pktin, s->packetlen + s->maclen); sfree(old_pktin); } s->length = 5 + newlen; memcpy(s->data + 5, newpayload, newlen); sfree(newpayload); } } /* * Now we can identify the semantic content of the packet, * and also the initial type byte. */ if (s->length <= 5) { /* == 5 we hope, but robustness */ /* * RFC 4253 doesn't explicitly say that completely empty * packets with no type byte are forbidden. We handle them * here by giving them a type code larger than 0xFF, which * will be picked up at the next layer and trigger * SSH_MSG_UNIMPLEMENTED. */ s->pktin->type = SSH_MSG_NO_TYPE_CODE; s->data += 5; s->length = 0; } else { s->pktin->type = s->data[5]; s->data += 6; s->length -= 6; } BinarySource_INIT(s->pktin, s->data, s->length); if (s->bpp.logctx) { logblank_t blanks[MAX_BLANKS]; int nblanks = ssh2_censor_packet( s->bpp.pls, s->pktin->type, false, make_ptrlen(s->data, s->length), blanks); log_packet(s->bpp.logctx, PKT_INCOMING, s->pktin->type, ssh2_pkt_type(s->bpp.pls->kctx, s->bpp.pls->actx, s->pktin->type), s->data, s->length, nblanks, blanks, &s->pktin->sequence, 0, NULL); } if (ssh2_bpp_check_unimplemented(&s->bpp, s->pktin)) { sfree(s->pktin); s->pktin = NULL; continue; } s->pktin->qnode.formal_size = get_avail(s->pktin); pq_push(&s->bpp.in_pq, s->pktin); { int type = s->pktin->type; int prev_type = s->prev_type; s->prev_type = type; s->pktin = NULL; if (s->enforce_next_packet_is_userauth_success) { /* See EXT_INFO handler below */ if (type != SSH2_MSG_USERAUTH_SUCCESS) { ssh_proto_error(s->bpp.ssh, "Remote side sent SSH2_MSG_EXT_INFO " "not either preceded by NEWKEYS or " "followed by USERAUTH_SUCCESS"); return; } s->enforce_next_packet_is_userauth_success = false; } if (type == SSH2_MSG_NEWKEYS) { if (s->nnewkeys < 2) s->nnewkeys++; /* * Mild layer violation: in this situation we must * suspend processing of the input byte stream until * the transport layer has initialised the new keys by * calling ssh2_bpp_new_incoming_crypto above. */ s->pending_newkeys = true; crWaitUntilV(!s->pending_newkeys); continue; } if (type == SSH2_MSG_USERAUTH_SUCCESS && !s->is_server) { /* * Another one: if we were configured with OpenSSH's * deferred compression which is triggered on receipt * of USERAUTH_SUCCESS, then this is the moment to * turn on compression. */ ssh2_bpp_enable_pending_compression(s); /* * Whether or not we were doing delayed compression in * _this_ set of crypto parameters, we should set a * flag indicating that we're now authenticated, so * that a delayed compression method enabled in any * future rekey will be treated as un-delayed. */ s->seen_userauth_success = true; } if (type == SSH2_MSG_EXT_INFO) { /* * And another: enforce that an incoming EXT_INFO is * either the message immediately after the initial * NEWKEYS, or (if we're the client) the one * immediately before USERAUTH_SUCCESS. */ if (prev_type == SSH2_MSG_NEWKEYS && s->nnewkeys == 1) { /* OK - this is right after the first NEWKEYS. */ } else if (s->is_server) { /* We're the server, so they're the client. * Clients may not send EXT_INFO at _any_ other * time. */ ssh_proto_error(s->bpp.ssh, "Remote side sent SSH2_MSG_EXT_INFO " "that was not immediately after the " "initial NEWKEYS"); return; } else if (s->nnewkeys > 0 && s->seen_userauth_success) { /* We're the client, so they're the server. In * that case they may also send EXT_INFO * immediately before USERAUTH_SUCCESS. Error out * immediately if this can't _possibly_ be that * moment (because we haven't even seen NEWKEYS * yet, or because we've already seen * USERAUTH_SUCCESS). */ ssh_proto_error(s->bpp.ssh, "Remote side sent SSH2_MSG_EXT_INFO " "after USERAUTH_SUCCESS"); return; } else { /* This _could_ be OK, provided the next packet is * USERAUTH_SUCCESS. Set a flag to remember to * fault it if not. */ s->enforce_next_packet_is_userauth_success = true; } } if (s->pending_compression && userauth_range(type)) { /* * Receiving any userauth message at all indicates * that we're not about to turn on delayed compression * - either because we just _have_ done, or because * this message is a USERAUTH_FAILURE or some kind of * intermediate 'please send more data' continuation * message. Either way, we turn off the outgoing * packet blockage for now, and release any queued * output packets, so that we can make another attempt * to authenticate. The next userauth packet we send * will re-block the output direction. */ s->pending_compression = false; queue_idempotent_callback(&s->bpp.ic_out_pq); } } } eof: /* * We've seen EOF. But we might have pushed stuff on the outgoing * packet queue first, and that stuff _might_ include a DISCONNECT * message, in which case we'd like to use that as the diagnostic. * So first wait for the queue to have been processed. */ crMaybeWaitUntilV(!pq_peek(&s->bpp.in_pq)); if (!s->bpp.expect_close) { ssh_remote_error(s->bpp.ssh, "Remote side unexpectedly closed network connection"); } else { ssh_remote_eof(s->bpp.ssh, "Remote side closed network connection"); } return; /* avoid touching s now it's been freed */ crFinishV; } static PktOut *ssh2_bpp_new_pktout(int pkt_type) { PktOut *pkt = ssh_new_packet(); pkt->length = 5; /* space for packet length + padding length */ pkt->minlen = 0; pkt->type = pkt_type; put_byte(pkt, pkt_type); pkt->prefix = pkt->length; return pkt; } static void ssh2_bpp_format_packet_inner(struct ssh2_bpp_state *s, PktOut *pkt) { int origlen, cipherblk, maclen, padding, unencrypted_prefix, i; if (s->bpp.logctx) { ptrlen pktdata = make_ptrlen(pkt->data + pkt->prefix, pkt->length - pkt->prefix); logblank_t blanks[MAX_BLANKS]; int nblanks = ssh2_censor_packet( s->bpp.pls, pkt->type, true, pktdata, blanks); log_packet(s->bpp.logctx, PKT_OUTGOING, pkt->type, ssh2_pkt_type(s->bpp.pls->kctx, s->bpp.pls->actx, pkt->type), pktdata.ptr, pktdata.len, nblanks, blanks, &s->out.sequence, pkt->downstream_id, pkt->additional_log_text); } cipherblk = s->out.cipher ? ssh_cipher_alg(s->out.cipher)->blksize : 8; cipherblk = cipherblk < 8 ? 8 : cipherblk; /* or 8 if blksize < 8 */ if (s->out_comp) { unsigned char *newpayload; int minlen, newlen; /* * Compress packet payload. */ minlen = pkt->minlen; if (minlen) { /* * Work out how much compressed data we need (at least) to * make the overall packet length come to pkt->minlen. */ if (s->out.mac) minlen -= ssh2_mac_alg(s->out.mac)->len; minlen -= 8; /* length field + min padding */ } ssh_compressor_compress(s->out_comp, pkt->data + 5, pkt->length - 5, &newpayload, &newlen, minlen); pkt->length = 5; put_data(pkt, newpayload, newlen); sfree(newpayload); } /* * Add padding. At least four bytes, and must also bring total * length (minus MAC) up to a multiple of the block size. * If pkt->forcepad is set, make sure the packet is at least that size * after padding. */ padding = 4; unencrypted_prefix = (s->out.mac && s->out.etm_mode) ? 4 : 0; padding += (cipherblk - (pkt->length - unencrypted_prefix + padding) % cipherblk) % cipherblk; assert(padding <= 255); maclen = s->out.mac ? ssh2_mac_alg(s->out.mac)->len : 0; origlen = pkt->length; for (i = 0; i < padding; i++) put_byte(pkt, 0); /* make space for random padding */ random_read(pkt->data + origlen, padding); pkt->data[4] = padding; PUT_32BIT_MSB_FIRST(pkt->data, origlen + padding - 4); /* Encrypt length if the scheme requires it */ if (s->out.cipher && (ssh_cipher_alg(s->out.cipher)->flags & SSH_CIPHER_SEPARATE_LENGTH)) { ssh_cipher_encrypt_length(s->out.cipher, pkt->data, 4, s->out.sequence); } put_padding(pkt, maclen, 0); if (s->out.mac && s->out.etm_mode) { /* * OpenSSH-defined encrypt-then-MAC protocol. */ if (s->out.cipher) ssh_cipher_encrypt(s->out.cipher, pkt->data + 4, origlen + padding - 4); ssh2_mac_generate(s->out.mac, pkt->data, origlen + padding, s->out.sequence); } else { /* * SSH-2 standard protocol. */ if (s->out.mac) ssh2_mac_generate(s->out.mac, pkt->data, origlen + padding, s->out.sequence); if (s->out.cipher) ssh_cipher_encrypt(s->out.cipher, pkt->data, origlen + padding); } s->out.sequence++; /* whether or not we MACed */ dts_consume(&s->stats->out, origlen + padding); } static void ssh2_bpp_format_packet(struct ssh2_bpp_state *s, PktOut *pkt) { if (pkt->minlen > 0 && !s->out_comp) { /* * If we've been told to pad the packet out to a given minimum * length, but we're not compressing (and hence can't get the * compression to do the padding by pointlessly opening and * closing zlib blocks), then our other strategy is to precede * this message with an SSH_MSG_IGNORE that makes it up to the * right length. * * A third option in principle, and the most obviously * sensible, would be to set the explicit padding field in the * packet to more than its minimum value. Sadly, that turns * out to break some servers (our institutional memory thinks * Cisco in particular) and so we abandoned that idea shortly * after trying it. */ /* * Calculate the length we expect the real packet to have. */ int block, length; PktOut *ignore_pkt; block = s->out.cipher ? ssh_cipher_alg(s->out.cipher)->blksize : 0; if (block < 8) block = 8; length = pkt->length; length += 4; /* minimum 4 byte padding */ length += block-1; length -= (length % block); if (s->out.mac) length += ssh2_mac_alg(s->out.mac)->len; if (length < pkt->minlen) { /* * We need an ignore message. Calculate its length. */ length = pkt->minlen - length; /* * And work backwards from that to the length of the * contained string. */ if (s->out.mac) length -= ssh2_mac_alg(s->out.mac)->len; length -= 8; /* length field + min padding */ length -= 5; /* type code + string length prefix */ if (length < 0) length = 0; ignore_pkt = ssh2_bpp_new_pktout(SSH2_MSG_IGNORE); put_uint32(ignore_pkt, length); size_t origlen = ignore_pkt->length; for (size_t i = 0; i < length; i++) put_byte(ignore_pkt, 0); /* make space for random padding */ random_read(ignore_pkt->data + origlen, length); ssh2_bpp_format_packet_inner(s, ignore_pkt); bufchain_add(s->bpp.out_raw, ignore_pkt->data, ignore_pkt->length); ssh_free_pktout(ignore_pkt); } } ssh2_bpp_format_packet_inner(s, pkt); bufchain_add(s->bpp.out_raw, pkt->data, pkt->length); } static void ssh2_bpp_handle_output(BinaryPacketProtocol *bpp) { struct ssh2_bpp_state *s = container_of(bpp, struct ssh2_bpp_state, bpp); PktOut *pkt; int n_userauth; /* * Count the userauth packets in the queue. */ n_userauth = 0; for (pkt = pq_first(&s->bpp.out_pq); pkt != NULL; pkt = pq_next(&s->bpp.out_pq, pkt)) if (userauth_range(pkt->type)) n_userauth++; if (s->pending_compression && !n_userauth) { /* * We're currently blocked from sending any outgoing packets * until the other end tells us whether we're going to have to * enable compression or not. * * If our end has pushed a userauth packet on the queue, that * must mean it knows that a USERAUTH_SUCCESS is not * immediately forthcoming, so we unblock ourselves and send * up to and including that packet. But in this if statement, * there aren't any, so we're still blocked. */ return; } if (s->cbc_ignore_workaround) { /* * When using a CBC-mode cipher in SSH-2, it's necessary to * ensure that an attacker can't provide data to be encrypted * using an IV that they know. We ensure this by inserting an * SSH_MSG_IGNORE if the last cipher block of the previous * packet has already been sent to the network (which we * approximate conservatively by checking if it's vanished * from out_raw). */ if (bufchain_size(s->bpp.out_raw) < (ssh_cipher_alg(s->out.cipher)->blksize + ssh2_mac_alg(s->out.mac)->len)) { /* * There's less data in out_raw than the MAC size plus the * cipher block size, which means at least one byte of * that cipher block must already have left. Add an * IGNORE. */ pkt = ssh_bpp_new_pktout(&s->bpp, SSH2_MSG_IGNORE); put_stringz(pkt, ""); ssh2_bpp_format_packet(s, pkt); } } while ((pkt = pq_pop(&s->bpp.out_pq)) != NULL) { int type = pkt->type; if (userauth_range(type)) n_userauth--; ssh2_bpp_format_packet(s, pkt); ssh_free_pktout(pkt); if (n_userauth == 0 && s->out.pending_compression && !s->is_server) { /* * This is the last userauth packet in the queue, so * unless our side decides to send another one in future, * we have to assume will potentially provoke * USERAUTH_SUCCESS. Block (non-userauth) outgoing packets * until we see the reply. */ s->pending_compression = true; return; } else if (type == SSH2_MSG_USERAUTH_SUCCESS && s->is_server) { ssh2_bpp_enable_pending_compression(s); } } } putty-0.76/ssh2censor.c0000644000175000017500000000767514072266311012032 00000000000000/* * Packet-censoring code for SSH-2, used to identify sensitive fields * like passwords so that the logging system can avoid writing them * into log files. */ #include #include "putty.h" #include "ssh.h" int ssh2_censor_packet( const PacketLogSettings *pls, int type, bool sender_is_client, ptrlen pkt, logblank_t *blanks) { int nblanks = 0; ptrlen str; BinarySource src[1]; BinarySource_BARE_INIT_PL(src, pkt); if (pls->omit_data && (type == SSH2_MSG_CHANNEL_DATA || type == SSH2_MSG_CHANNEL_EXTENDED_DATA)) { /* "Session data" packets - omit the data string. */ get_uint32(src); /* skip channel id */ if (type == SSH2_MSG_CHANNEL_EXTENDED_DATA) get_uint32(src); /* skip extended data type */ str = get_string(src); if (!get_err(src)) { assert(nblanks < MAX_BLANKS); blanks[nblanks].offset = src->pos - str.len; blanks[nblanks].type = PKTLOG_OMIT; blanks[nblanks].len = str.len; nblanks++; } } if (sender_is_client && pls->omit_passwords) { if (type == SSH2_MSG_USERAUTH_REQUEST) { /* If this is a password packet, blank the password(s). */ get_string(src); /* username */ get_string(src); /* service name */ str = get_string(src); /* auth method */ if (ptrlen_eq_string(str, "password")) { get_bool(src); /* Blank the password field. */ str = get_string(src); if (!get_err(src)) { assert(nblanks < MAX_BLANKS); blanks[nblanks].offset = src->pos - str.len; blanks[nblanks].type = PKTLOG_BLANK; blanks[nblanks].len = str.len; nblanks++; /* If there's another password field beyond it * (change of password), blank that too. */ str = get_string(src); if (!get_err(src)) blanks[nblanks-1].len = src->pos - blanks[nblanks].offset; } } } else if (pls->actx == SSH2_PKTCTX_KBDINTER && type == SSH2_MSG_USERAUTH_INFO_RESPONSE) { /* If this is a keyboard-interactive response packet, * blank the responses. */ get_uint32(src); assert(nblanks < MAX_BLANKS); blanks[nblanks].offset = src->pos; blanks[nblanks].type = PKTLOG_BLANK; do { str = get_string(src); } while (!get_err(src)); blanks[nblanks].len = src->pos - blanks[nblanks].offset; nblanks++; } else if (type == SSH2_MSG_CHANNEL_REQUEST) { /* * 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. */ get_uint32(src); str = get_string(src); if (ptrlen_eq_string(str, "x11-req")) { get_bool(src); get_bool(src); get_string(src); str = get_string(src); if (!get_err(src)) { assert(nblanks < MAX_BLANKS); blanks[nblanks].offset = src->pos - str.len; blanks[nblanks].type = PKTLOG_BLANK; blanks[nblanks].len = str.len; nblanks++; } } } } return nblanks; } putty-0.76/ssh2connection-client.c0000644000175000017500000003723014072266311014142 00000000000000/* * Client-specific parts of the SSH-2 connection layer. */ #include #include "putty.h" #include "ssh.h" #include "sshbpp.h" #include "sshppl.h" #include "sshchan.h" #include "sshcr.h" #include "ssh2connection.h" static ChanopenResult chan_open_x11( struct ssh2_connection_state *s, SshChannel *sc, ptrlen peeraddr, int peerport) { PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */ char *peeraddr_str; Channel *ch; ppl_logevent("Received X11 connect request from %.*s:%d", PTRLEN_PRINTF(peeraddr), peerport); if (!s->X11_fwd_enabled && !s->connshare) { CHANOPEN_RETURN_FAILURE( SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED, ("X11 forwarding is not enabled")); } peeraddr_str = peeraddr.ptr ? mkstr(peeraddr) : NULL; ch = x11_new_channel( s->x11authtree, sc, peeraddr_str, peerport, s->connshare != NULL); sfree(peeraddr_str); ppl_logevent("Opened X11 forward channel"); CHANOPEN_RETURN_SUCCESS(ch); } static ChanopenResult chan_open_forwarded_tcpip( struct ssh2_connection_state *s, SshChannel *sc, ptrlen fwdaddr, int fwdport, ptrlen peeraddr, int peerport) { PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */ struct ssh_rportfwd pf, *realpf; Channel *ch; char *err; ppl_logevent("Received remote port %.*s:%d open request from %.*s:%d", PTRLEN_PRINTF(fwdaddr), fwdport, PTRLEN_PRINTF(peeraddr), peerport); pf.shost = mkstr(fwdaddr); pf.sport = fwdport; realpf = find234(s->rportfwds, &pf, NULL); sfree(pf.shost); if (realpf == NULL) { CHANOPEN_RETURN_FAILURE( SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED, ("Remote port is not recognised")); } if (realpf->share_ctx) { /* * This port forwarding is on behalf of a connection-sharing * downstream. */ CHANOPEN_RETURN_DOWNSTREAM(realpf->share_ctx); } err = portfwdmgr_connect( s->portfwdmgr, &ch, realpf->dhost, realpf->dport, sc, realpf->addressfamily); ppl_logevent("Attempting to forward remote port to %s:%d", realpf->dhost, realpf->dport); if (err != NULL) { ppl_logevent("Port open failed: %s", err); sfree(err); CHANOPEN_RETURN_FAILURE( SSH2_OPEN_CONNECT_FAILED, ("Port open failed")); } ppl_logevent("Forwarded port opened successfully"); CHANOPEN_RETURN_SUCCESS(ch); } static ChanopenResult chan_open_auth_agent( struct ssh2_connection_state *s, SshChannel *sc) { if (!ssh_agent_forwarding_permitted(&s->cl)) { CHANOPEN_RETURN_FAILURE( SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED, ("Agent forwarding is not enabled")); } /* * If possible, make a stream-oriented connection to the agent and * set up an ordinary port-forwarding type channel over it. */ Plug *plug; Channel *ch = portfwd_raw_new(&s->cl, &plug, true); Socket *skt = agent_connect(plug); if (!sk_socket_error(skt)) { portfwd_raw_setup(ch, skt, sc); CHANOPEN_RETURN_SUCCESS(ch); } else { portfwd_raw_free(ch); /* * Otherwise, fall back to the old-fashioned system of parsing the * forwarded data stream ourselves for message boundaries, and * passing each individual message to the one-off agent_query(). */ CHANOPEN_RETURN_SUCCESS(agentf_new(sc)); } } ChanopenResult ssh2_connection_parse_channel_open( struct ssh2_connection_state *s, ptrlen type, PktIn *pktin, SshChannel *sc) { if (ptrlen_eq_string(type, "x11")) { ptrlen peeraddr = get_string(pktin); int peerport = get_uint32(pktin); return chan_open_x11(s, sc, peeraddr, peerport); } else if (ptrlen_eq_string(type, "forwarded-tcpip")) { ptrlen fwdaddr = get_string(pktin); int fwdport = toint(get_uint32(pktin)); ptrlen peeraddr = get_string(pktin); int peerport = toint(get_uint32(pktin)); return chan_open_forwarded_tcpip( s, sc, fwdaddr, fwdport, peeraddr, peerport); } else if (ptrlen_eq_string(type, "auth-agent@openssh.com")) { return chan_open_auth_agent(s, sc); } else { CHANOPEN_RETURN_FAILURE( SSH2_OPEN_UNKNOWN_CHANNEL_TYPE, ("Unsupported channel type requested")); } } bool ssh2_connection_parse_global_request( struct ssh2_connection_state *s, ptrlen type, PktIn *pktin) { /* * We don't know of any global requests that an SSH client needs * to honour. */ return false; } PktOut *ssh2_portfwd_chanopen( struct ssh2_connection_state *s, struct ssh2_channel *c, const char *hostname, int port, const char *description, const SocketPeerInfo *peerinfo) { PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */ PktOut *pktout; /* * In client mode, this function is called by portfwdmgr in * response to PortListeners that were set up in * portfwdmgr_config, which means that the hostname and port * parameters will indicate the host we want to tell the server to * connect _to_. */ ppl_logevent("Opening connection to %s:%d for %s", hostname, port, description); pktout = ssh2_chanopen_init(c, "direct-tcpip"); { char *trimmed_host = host_strduptrim(hostname); put_stringz(pktout, trimmed_host); sfree(trimmed_host); } put_uint32(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. The "originator IP address" is syntactically a * numeric IP address, and some servers (e.g., Tectia) get upset * if it doesn't match this syntax. */ put_stringz(pktout, "0.0.0.0"); put_uint32(pktout, 0); return pktout; } static int ssh2_rportfwd_cmp(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; } static void ssh2_rportfwd_globreq_response(struct ssh2_connection_state *s, PktIn *pktin, void *ctx) { PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */ struct ssh_rportfwd *rpf = (struct ssh_rportfwd *)ctx; if (pktin->type == SSH2_MSG_REQUEST_SUCCESS) { ppl_logevent("Remote port forwarding from %s enabled", rpf->log_description); } else { ppl_logevent("Remote port forwarding from %s refused", rpf->log_description); struct ssh_rportfwd *realpf = del234(s->rportfwds, rpf); assert(realpf == rpf); portfwdmgr_close(s->portfwdmgr, rpf->pfr); free_rportfwd(rpf); } } struct ssh_rportfwd *ssh2_rportfwd_alloc( ConnectionLayer *cl, const char *shost, int sport, const char *dhost, int dport, int addressfamily, const char *log_description, PortFwdRecord *pfr, ssh_sharing_connstate *share_ctx) { struct ssh2_connection_state *s = container_of(cl, struct ssh2_connection_state, cl); struct ssh_rportfwd *rpf = snew(struct ssh_rportfwd); if (!s->rportfwds) s->rportfwds = newtree234(ssh2_rportfwd_cmp); rpf->shost = dupstr(shost); rpf->sport = sport; rpf->dhost = dupstr(dhost); rpf->dport = dport; rpf->addressfamily = addressfamily; rpf->log_description = dupstr(log_description); rpf->pfr = pfr; rpf->share_ctx = share_ctx; if (add234(s->rportfwds, rpf) != rpf) { free_rportfwd(rpf); return NULL; } if (!rpf->share_ctx) { PktOut *pktout = ssh_bpp_new_pktout( s->ppl.bpp, SSH2_MSG_GLOBAL_REQUEST); put_stringz(pktout, "tcpip-forward"); put_bool(pktout, true); /* want reply */ put_stringz(pktout, rpf->shost); put_uint32(pktout, rpf->sport); pq_push(s->ppl.out_pq, pktout); ssh2_queue_global_request_handler( s, ssh2_rportfwd_globreq_response, rpf); } return rpf; } void ssh2_rportfwd_remove(ConnectionLayer *cl, struct ssh_rportfwd *rpf) { struct ssh2_connection_state *s = container_of(cl, struct ssh2_connection_state, cl); if (rpf->share_ctx) { /* * We don't manufacture a cancel-tcpip-forward message for * remote port forwardings being removed on behalf of a * downstream; we just pass through the one the downstream * sent to us. */ } else { PktOut *pktout = ssh_bpp_new_pktout( s->ppl.bpp, SSH2_MSG_GLOBAL_REQUEST); put_stringz(pktout, "cancel-tcpip-forward"); put_bool(pktout, false); /* _don't_ want reply */ put_stringz(pktout, rpf->shost); put_uint32(pktout, rpf->sport); pq_push(s->ppl.out_pq, pktout); } assert(s->rportfwds); struct ssh_rportfwd *realpf = del234(s->rportfwds, rpf); assert(realpf == rpf); free_rportfwd(rpf); } SshChannel *ssh2_session_open(ConnectionLayer *cl, Channel *chan) { struct ssh2_connection_state *s = container_of(cl, struct ssh2_connection_state, cl); PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */ struct ssh2_channel *c = snew(struct ssh2_channel); PktOut *pktout; c->connlayer = s; ssh2_channel_init(c); c->halfopen = true; c->chan = chan; ppl_logevent("Opening main session channel"); pktout = ssh2_chanopen_init(c, "session"); pq_push(s->ppl.out_pq, pktout); return &c->sc; } SshChannel *ssh2_serverside_x11_open( ConnectionLayer *cl, Channel *chan, const SocketPeerInfo *pi) { unreachable("Should never be called in the client"); } SshChannel *ssh2_serverside_agent_open(ConnectionLayer *cl, Channel *chan) { unreachable("Should never be called in the client"); } static void ssh2_channel_response( struct ssh2_channel *c, PktIn *pkt, void *ctx) { /* If pkt==NULL (because this handler has been called in response * to CHANNEL_CLOSE arriving while the request was still * outstanding), we treat that the same as CHANNEL_FAILURE. */ chan_request_response(c->chan, pkt && pkt->type == SSH2_MSG_CHANNEL_SUCCESS); } void ssh2channel_start_shell(SshChannel *sc, bool want_reply) { struct ssh2_channel *c = container_of(sc, struct ssh2_channel, sc); struct ssh2_connection_state *s = c->connlayer; PktOut *pktout = ssh2_chanreq_init( c, "shell", want_reply ? ssh2_channel_response : NULL, NULL); pq_push(s->ppl.out_pq, pktout); } void ssh2channel_start_command( SshChannel *sc, bool want_reply, const char *command) { struct ssh2_channel *c = container_of(sc, struct ssh2_channel, sc); struct ssh2_connection_state *s = c->connlayer; PktOut *pktout = ssh2_chanreq_init( c, "exec", want_reply ? ssh2_channel_response : NULL, NULL); put_stringz(pktout, command); pq_push(s->ppl.out_pq, pktout); } bool ssh2channel_start_subsystem( SshChannel *sc, bool want_reply, const char *subsystem) { struct ssh2_channel *c = container_of(sc, struct ssh2_channel, sc); struct ssh2_connection_state *s = c->connlayer; PktOut *pktout = ssh2_chanreq_init( c, "subsystem", want_reply ? ssh2_channel_response : NULL, NULL); put_stringz(pktout, subsystem); pq_push(s->ppl.out_pq, pktout); return true; } void ssh2channel_send_exit_status(SshChannel *sc, int status) { unreachable("Should never be called in the client"); } void ssh2channel_send_exit_signal( SshChannel *sc, ptrlen signame, bool core_dumped, ptrlen msg) { unreachable("Should never be called in the client"); } void ssh2channel_send_exit_signal_numeric( SshChannel *sc, int signum, bool core_dumped, ptrlen msg) { unreachable("Should never be called in the client"); } void ssh2channel_request_x11_forwarding( SshChannel *sc, bool want_reply, const char *authproto, const char *authdata, int screen_number, bool oneshot) { struct ssh2_channel *c = container_of(sc, struct ssh2_channel, sc); struct ssh2_connection_state *s = c->connlayer; PktOut *pktout = ssh2_chanreq_init( c, "x11-req", want_reply ? ssh2_channel_response : NULL, NULL); put_bool(pktout, oneshot); put_stringz(pktout, authproto); put_stringz(pktout, authdata); put_uint32(pktout, screen_number); pq_push(s->ppl.out_pq, pktout); } void ssh2channel_request_agent_forwarding(SshChannel *sc, bool want_reply) { struct ssh2_channel *c = container_of(sc, struct ssh2_channel, sc); struct ssh2_connection_state *s = c->connlayer; PktOut *pktout = ssh2_chanreq_init( c, "auth-agent-req@openssh.com", want_reply ? ssh2_channel_response : NULL, NULL); pq_push(s->ppl.out_pq, pktout); } void ssh2channel_request_pty( SshChannel *sc, bool want_reply, Conf *conf, int w, int h) { struct ssh2_channel *c = container_of(sc, struct ssh2_channel, sc); struct ssh2_connection_state *s = c->connlayer; strbuf *modebuf; PktOut *pktout = ssh2_chanreq_init( c, "pty-req", want_reply ? ssh2_channel_response : NULL, NULL); put_stringz(pktout, conf_get_str(conf, CONF_termtype)); put_uint32(pktout, w); put_uint32(pktout, h); put_uint32(pktout, 0); /* pixel width */ put_uint32(pktout, 0); /* pixel height */ modebuf = strbuf_new(); write_ttymodes_to_packet( BinarySink_UPCAST(modebuf), 2, get_ttymodes_from_conf(s->ppl.seat, conf)); put_stringsb(pktout, modebuf); pq_push(s->ppl.out_pq, pktout); } bool ssh2channel_send_env_var( SshChannel *sc, bool want_reply, const char *var, const char *value) { struct ssh2_channel *c = container_of(sc, struct ssh2_channel, sc); struct ssh2_connection_state *s = c->connlayer; PktOut *pktout = ssh2_chanreq_init( c, "env", want_reply ? ssh2_channel_response : NULL, NULL); put_stringz(pktout, var); put_stringz(pktout, value); pq_push(s->ppl.out_pq, pktout); return true; } bool ssh2channel_send_serial_break(SshChannel *sc, bool want_reply, int length) { struct ssh2_channel *c = container_of(sc, struct ssh2_channel, sc); struct ssh2_connection_state *s = c->connlayer; PktOut *pktout = ssh2_chanreq_init( c, "break", want_reply ? ssh2_channel_response : NULL, NULL); put_uint32(pktout, length); pq_push(s->ppl.out_pq, pktout); return true; } bool ssh2channel_send_signal( SshChannel *sc, bool want_reply, const char *signame) { struct ssh2_channel *c = container_of(sc, struct ssh2_channel, sc); struct ssh2_connection_state *s = c->connlayer; PktOut *pktout = ssh2_chanreq_init( c, "signal", want_reply ? ssh2_channel_response : NULL, NULL); put_stringz(pktout, signame); pq_push(s->ppl.out_pq, pktout); return true; } void ssh2channel_send_terminal_size_change(SshChannel *sc, int w, int h) { struct ssh2_channel *c = container_of(sc, struct ssh2_channel, sc); struct ssh2_connection_state *s = c->connlayer; PktOut *pktout = ssh2_chanreq_init(c, "window-change", NULL, NULL); put_uint32(pktout, w); put_uint32(pktout, h); put_uint32(pktout, 0); /* pixel width */ put_uint32(pktout, 0); /* pixel height */ pq_push(s->ppl.out_pq, pktout); } bool ssh2_connection_need_antispoof_prompt(struct ssh2_connection_state *s) { bool success = seat_set_trust_status(s->ppl.seat, false); return (!success && !ssh_is_bare(s->ppl.ssh)); } putty-0.76/ssh2connection-server.c0000644000175000017500000002223714072266312014174 00000000000000/* * Server-specific parts of the SSH-2 connection layer. */ #include #include "putty.h" #include "ssh.h" #include "sshbpp.h" #include "sshppl.h" #include "sshchan.h" #include "sshcr.h" #include "ssh2connection.h" #include "sshserver.h" void ssh2connection_server_configure( PacketProtocolLayer *ppl, const SftpServerVtable *sftpserver_vt, const SshServerConfig *ssc) { struct ssh2_connection_state *s = container_of(ppl, struct ssh2_connection_state, ppl); s->sftpserver_vt = sftpserver_vt; s->ssc = ssc; } static ChanopenResult chan_open_session( struct ssh2_connection_state *s, SshChannel *sc) { PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */ ppl_logevent("Opened session channel"); CHANOPEN_RETURN_SUCCESS(sesschan_new(sc, s->ppl.logctx, s->sftpserver_vt, s->ssc)); } static ChanopenResult chan_open_direct_tcpip( struct ssh2_connection_state *s, SshChannel *sc, ptrlen dstaddr, int dstport, ptrlen peeraddr, int peerport) { PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */ Channel *ch; char *dstaddr_str, *err; dstaddr_str = mkstr(dstaddr); ppl_logevent("Received request to connect to port %s:%d (from %.*s:%d)", dstaddr_str, dstport, PTRLEN_PRINTF(peeraddr), peerport); err = portfwdmgr_connect( s->portfwdmgr, &ch, dstaddr_str, dstport, sc, ADDRTYPE_UNSPEC); sfree(dstaddr_str); if (err != NULL) { ppl_logevent("Port open failed: %s", err); sfree(err); CHANOPEN_RETURN_FAILURE( SSH2_OPEN_CONNECT_FAILED, ("Connection failed")); } ppl_logevent("Port opened successfully"); CHANOPEN_RETURN_SUCCESS(ch); } ChanopenResult ssh2_connection_parse_channel_open( struct ssh2_connection_state *s, ptrlen type, PktIn *pktin, SshChannel *sc) { if (ptrlen_eq_string(type, "session")) { return chan_open_session(s, sc); } else if (ptrlen_eq_string(type, "direct-tcpip")) { ptrlen dstaddr = get_string(pktin); int dstport = toint(get_uint32(pktin)); ptrlen peeraddr = get_string(pktin); int peerport = toint(get_uint32(pktin)); return chan_open_direct_tcpip( s, sc, dstaddr, dstport, peeraddr, peerport); } else { CHANOPEN_RETURN_FAILURE( SSH2_OPEN_UNKNOWN_CHANNEL_TYPE, ("Unsupported channel type requested")); } } bool ssh2_connection_parse_global_request( struct ssh2_connection_state *s, ptrlen type, PktIn *pktin) { if (ptrlen_eq_string(type, "tcpip-forward")) { char *host = mkstr(get_string(pktin)); unsigned port = get_uint32(pktin); /* In SSH-2, the host/port we listen on are the same host/port * we want reported back to us when a connection comes in, * because that's what we tell the client */ bool toret = portfwdmgr_listen( s->portfwdmgr, host, port, host, port, s->conf); sfree(host); return toret; } else if (ptrlen_eq_string(type, "cancel-tcpip-forward")) { char *host = mkstr(get_string(pktin)); unsigned port = get_uint32(pktin); bool toret = portfwdmgr_unlisten(s->portfwdmgr, host, port); sfree(host); return toret; } else { /* Unrecognised request. */ return false; } } PktOut *ssh2_portfwd_chanopen( struct ssh2_connection_state *s, struct ssh2_channel *c, const char *hostname, int port, const char *description, const SocketPeerInfo *pi) { PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */ PktOut *pktout; /* * In server mode, this function is called by portfwdmgr in * response to PortListeners that were set up by calling * portfwdmgr_listen, which means that the hostname and port * parameters will identify the listening socket on which a * connection just came in. */ if (pi && pi->log_text) ppl_logevent("Forwarding connection to listening port %s:%d from %s", hostname, port, pi->log_text); else ppl_logevent("Forwarding connection to listening port %s:%d", hostname, port); pktout = ssh2_chanopen_init(c, "forwarded-tcpip"); put_stringz(pktout, hostname); put_uint32(pktout, port); put_stringz(pktout, (pi && pi->addr_text ? pi->addr_text : "0.0.0.0")); put_uint32(pktout, (pi && pi->port >= 0 ? pi->port : 0)); return pktout; } struct ssh_rportfwd *ssh2_rportfwd_alloc( ConnectionLayer *cl, const char *shost, int sport, const char *dhost, int dport, int addressfamily, const char *log_description, PortFwdRecord *pfr, ssh_sharing_connstate *share_ctx) { unreachable("Should never be called in the server"); } void ssh2_rportfwd_remove(ConnectionLayer *cl, struct ssh_rportfwd *rpf) { unreachable("Should never be called in the server"); } SshChannel *ssh2_session_open(ConnectionLayer *cl, Channel *chan) { unreachable("Should never be called in the server"); } SshChannel *ssh2_serverside_x11_open( ConnectionLayer *cl, Channel *chan, const SocketPeerInfo *pi) { struct ssh2_connection_state *s = container_of(cl, struct ssh2_connection_state, cl); PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */ struct ssh2_channel *c = snew(struct ssh2_channel); PktOut *pktout; c->connlayer = s; ssh2_channel_init(c); c->halfopen = true; c->chan = chan; ppl_logevent("Forwarding X11 channel to client"); pktout = ssh2_chanopen_init(c, "x11"); put_stringz(pktout, (pi && pi->addr_text ? pi->addr_text : "0.0.0.0")); put_uint32(pktout, (pi && pi->port >= 0 ? pi->port : 0)); pq_push(s->ppl.out_pq, pktout); return &c->sc; } SshChannel *ssh2_serverside_agent_open(ConnectionLayer *cl, Channel *chan) { struct ssh2_connection_state *s = container_of(cl, struct ssh2_connection_state, cl); PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */ struct ssh2_channel *c = snew(struct ssh2_channel); PktOut *pktout; c->connlayer = s; ssh2_channel_init(c); c->halfopen = true; c->chan = chan; ppl_logevent("Forwarding SSH agent to client"); pktout = ssh2_chanopen_init(c, "auth-agent@openssh.com"); pq_push(s->ppl.out_pq, pktout); return &c->sc; } void ssh2channel_start_shell(SshChannel *sc, bool want_reply) { unreachable("Should never be called in the server"); } void ssh2channel_start_command( SshChannel *sc, bool want_reply, const char *command) { unreachable("Should never be called in the server"); } bool ssh2channel_start_subsystem( SshChannel *sc, bool want_reply, const char *subsystem) { unreachable("Should never be called in the server"); } void ssh2channel_send_exit_status(SshChannel *sc, int status) { struct ssh2_channel *c = container_of(sc, struct ssh2_channel, sc); struct ssh2_connection_state *s = c->connlayer; PktOut *pktout = ssh2_chanreq_init(c, "exit-status", NULL, NULL); put_uint32(pktout, status); pq_push(s->ppl.out_pq, pktout); } void ssh2channel_send_exit_signal( SshChannel *sc, ptrlen signame, bool core_dumped, ptrlen msg) { struct ssh2_channel *c = container_of(sc, struct ssh2_channel, sc); struct ssh2_connection_state *s = c->connlayer; PktOut *pktout = ssh2_chanreq_init(c, "exit-signal", NULL, NULL); put_stringpl(pktout, signame); put_bool(pktout, core_dumped); put_stringpl(pktout, msg); put_stringz(pktout, ""); /* language tag */ pq_push(s->ppl.out_pq, pktout); } void ssh2channel_send_exit_signal_numeric( SshChannel *sc, int signum, bool core_dumped, ptrlen msg) { struct ssh2_channel *c = container_of(sc, struct ssh2_channel, sc); struct ssh2_connection_state *s = c->connlayer; PktOut *pktout = ssh2_chanreq_init(c, "exit-signal", NULL, NULL); put_uint32(pktout, signum); put_bool(pktout, core_dumped); put_stringpl(pktout, msg); put_stringz(pktout, ""); /* language tag */ pq_push(s->ppl.out_pq, pktout); } void ssh2channel_request_x11_forwarding( SshChannel *sc, bool want_reply, const char *authproto, const char *authdata, int screen_number, bool oneshot) { unreachable("Should never be called in the server"); } void ssh2channel_request_agent_forwarding(SshChannel *sc, bool want_reply) { unreachable("Should never be called in the server"); } void ssh2channel_request_pty( SshChannel *sc, bool want_reply, Conf *conf, int w, int h) { unreachable("Should never be called in the server"); } bool ssh2channel_send_env_var( SshChannel *sc, bool want_reply, const char *var, const char *value) { unreachable("Should never be called in the server"); } bool ssh2channel_send_serial_break(SshChannel *sc, bool want_reply, int length) { unreachable("Should never be called in the server"); } bool ssh2channel_send_signal( SshChannel *sc, bool want_reply, const char *signame) { unreachable("Should never be called in the server"); } void ssh2channel_send_terminal_size_change(SshChannel *sc, int w, int h) { unreachable("Should never be called in the server"); } bool ssh2_connection_need_antispoof_prompt(struct ssh2_connection_state *s) { return false; } putty-0.76/ssh2connection.c0000644000175000017500000017470214072266312012675 00000000000000/* * Packet protocol layer for the SSH-2 connection protocol (RFC 4254). */ #include #include "putty.h" #include "ssh.h" #include "sshbpp.h" #include "sshppl.h" #include "sshchan.h" #include "sshcr.h" #include "ssh2connection.h" static void ssh2_connection_free(PacketProtocolLayer *); static void ssh2_connection_process_queue(PacketProtocolLayer *); static bool ssh2_connection_get_specials( PacketProtocolLayer *ppl, add_special_fn_t add_special, void *ctx); static void ssh2_connection_special_cmd(PacketProtocolLayer *ppl, SessionSpecialCode code, int arg); static bool ssh2_connection_want_user_input(PacketProtocolLayer *ppl); static void ssh2_connection_got_user_input(PacketProtocolLayer *ppl); static void ssh2_connection_reconfigure(PacketProtocolLayer *ppl, Conf *conf); static const PacketProtocolLayerVtable ssh2_connection_vtable = { .free = ssh2_connection_free, .process_queue = ssh2_connection_process_queue, .get_specials = ssh2_connection_get_specials, .special_cmd = ssh2_connection_special_cmd, .want_user_input = ssh2_connection_want_user_input, .got_user_input = ssh2_connection_got_user_input, .reconfigure = ssh2_connection_reconfigure, .queued_data_size = ssh_ppl_default_queued_data_size, .name = "ssh-connection", }; static SshChannel *ssh2_lportfwd_open( ConnectionLayer *cl, const char *hostname, int port, const char *description, const SocketPeerInfo *pi, Channel *chan); static struct X11FakeAuth *ssh2_add_x11_display( ConnectionLayer *cl, int authtype, struct X11Display *x11disp); static struct X11FakeAuth *ssh2_add_sharing_x11_display( ConnectionLayer *cl, int authtype, ssh_sharing_connstate *share_cs, share_channel *share_chan); static void ssh2_remove_sharing_x11_display(ConnectionLayer *cl, struct X11FakeAuth *auth); static void ssh2_send_packet_from_downstream( ConnectionLayer *cl, unsigned id, int type, const void *pkt, int pktlen, const char *additional_log_text); static unsigned ssh2_alloc_sharing_channel( ConnectionLayer *cl, ssh_sharing_connstate *connstate); static void ssh2_delete_sharing_channel( ConnectionLayer *cl, unsigned localid); static void ssh2_sharing_queue_global_request( ConnectionLayer *cl, ssh_sharing_connstate *share_ctx); static void ssh2_sharing_no_more_downstreams(ConnectionLayer *cl); static bool ssh2_agent_forwarding_permitted(ConnectionLayer *cl); static void ssh2_terminal_size(ConnectionLayer *cl, int width, int height); static void ssh2_stdout_unthrottle(ConnectionLayer *cl, size_t bufsize); static size_t ssh2_stdin_backlog(ConnectionLayer *cl); static void ssh2_throttle_all_channels(ConnectionLayer *cl, bool throttled); static bool ssh2_ldisc_option(ConnectionLayer *cl, int option); static void ssh2_set_ldisc_option(ConnectionLayer *cl, int option, bool value); static void ssh2_enable_x_fwd(ConnectionLayer *cl); static void ssh2_set_wants_user_input(ConnectionLayer *cl, bool wanted); static const ConnectionLayerVtable ssh2_connlayer_vtable = { .rportfwd_alloc = ssh2_rportfwd_alloc, .rportfwd_remove = ssh2_rportfwd_remove, .lportfwd_open = ssh2_lportfwd_open, .session_open = ssh2_session_open, .serverside_x11_open = ssh2_serverside_x11_open, .serverside_agent_open = ssh2_serverside_agent_open, .add_x11_display = ssh2_add_x11_display, .add_sharing_x11_display = ssh2_add_sharing_x11_display, .remove_sharing_x11_display = ssh2_remove_sharing_x11_display, .send_packet_from_downstream = ssh2_send_packet_from_downstream, .alloc_sharing_channel = ssh2_alloc_sharing_channel, .delete_sharing_channel = ssh2_delete_sharing_channel, .sharing_queue_global_request = ssh2_sharing_queue_global_request, .sharing_no_more_downstreams = ssh2_sharing_no_more_downstreams, .agent_forwarding_permitted = ssh2_agent_forwarding_permitted, .terminal_size = ssh2_terminal_size, .stdout_unthrottle = ssh2_stdout_unthrottle, .stdin_backlog = ssh2_stdin_backlog, .throttle_all_channels = ssh2_throttle_all_channels, .ldisc_option = ssh2_ldisc_option, .set_ldisc_option = ssh2_set_ldisc_option, .enable_x_fwd = ssh2_enable_x_fwd, .set_wants_user_input = ssh2_set_wants_user_input, }; static char *ssh2_channel_open_failure_error_text(PktIn *pktin) { static const char *const reasons[] = { NULL, "Administratively prohibited", "Connect failed", "Unknown channel type", "Resource shortage", }; unsigned reason_code; const char *reason_code_string; char reason_code_buf[256]; ptrlen reason; reason_code = get_uint32(pktin); if (reason_code < lenof(reasons) && reasons[reason_code]) { reason_code_string = reasons[reason_code]; } else { reason_code_string = reason_code_buf; sprintf(reason_code_buf, "unknown reason code %#x", reason_code); } reason = get_string(pktin); return dupprintf("%s [%.*s]", reason_code_string, PTRLEN_PRINTF(reason)); } static size_t ssh2channel_write( SshChannel *c, bool is_stderr, const void *buf, size_t len); static void ssh2channel_write_eof(SshChannel *c); static void ssh2channel_initiate_close(SshChannel *c, const char *err); static void ssh2channel_unthrottle(SshChannel *c, size_t bufsize); static Conf *ssh2channel_get_conf(SshChannel *c); static void ssh2channel_window_override_removed(SshChannel *c); static void ssh2channel_x11_sharing_handover( SshChannel *c, ssh_sharing_connstate *share_cs, share_channel *share_chan, const char *peer_addr, int peer_port, int endian, int protomajor, int protominor, const void *initial_data, int initial_len); static void ssh2channel_hint_channel_is_simple(SshChannel *c); static const SshChannelVtable ssh2channel_vtable = { .write = ssh2channel_write, .write_eof = ssh2channel_write_eof, .initiate_close = ssh2channel_initiate_close, .unthrottle = ssh2channel_unthrottle, .get_conf = ssh2channel_get_conf, .window_override_removed = ssh2channel_window_override_removed, .x11_sharing_handover = ssh2channel_x11_sharing_handover, .send_exit_status = ssh2channel_send_exit_status, .send_exit_signal = ssh2channel_send_exit_signal, .send_exit_signal_numeric = ssh2channel_send_exit_signal_numeric, .request_x11_forwarding = ssh2channel_request_x11_forwarding, .request_agent_forwarding = ssh2channel_request_agent_forwarding, .request_pty = ssh2channel_request_pty, .send_env_var = ssh2channel_send_env_var, .start_shell = ssh2channel_start_shell, .start_command = ssh2channel_start_command, .start_subsystem = ssh2channel_start_subsystem, .send_serial_break = ssh2channel_send_serial_break, .send_signal = ssh2channel_send_signal, .send_terminal_size_change = ssh2channel_send_terminal_size_change, .hint_channel_is_simple = ssh2channel_hint_channel_is_simple, }; static void ssh2_channel_check_close(struct ssh2_channel *c); static void ssh2_channel_try_eof(struct ssh2_channel *c); static void ssh2_set_window(struct ssh2_channel *c, int newwin); static size_t ssh2_try_send(struct ssh2_channel *c); static void ssh2_try_send_and_unthrottle(struct ssh2_channel *c); static void ssh2_channel_check_throttle(struct ssh2_channel *c); static void ssh2_channel_close_local(struct ssh2_channel *c, const char *reason); static void ssh2_channel_destroy(struct ssh2_channel *c); static void ssh2_check_termination(struct ssh2_connection_state *s); struct outstanding_global_request { gr_handler_fn_t handler; void *ctx; struct outstanding_global_request *next; }; void ssh2_queue_global_request_handler( struct ssh2_connection_state *s, gr_handler_fn_t handler, void *ctx) { struct outstanding_global_request *ogr = snew(struct outstanding_global_request); ogr->handler = handler; ogr->ctx = ctx; if (s->globreq_tail) s->globreq_tail->next = ogr; else s->globreq_head = ogr; s->globreq_tail = ogr; } static int ssh2_channelcmp(void *av, void *bv) { const struct ssh2_channel *a = (const struct ssh2_channel *) av; const struct ssh2_channel *b = (const struct ssh2_channel *) bv; if (a->localid < b->localid) return -1; if (a->localid > b->localid) return +1; return 0; } static int ssh2_channelfind(void *av, void *bv) { const unsigned *a = (const unsigned *) av; const struct ssh2_channel *b = (const struct ssh2_channel *) bv; if (*a < b->localid) return -1; if (*a > b->localid) return +1; return 0; } /* * Each channel has a queue of outstanding CHANNEL_REQUESTS and their * handlers. */ struct outstanding_channel_request { cr_handler_fn_t handler; void *ctx; struct outstanding_channel_request *next; }; static void ssh2_channel_free(struct ssh2_channel *c) { bufchain_clear(&c->outbuffer); bufchain_clear(&c->errbuffer); while (c->chanreq_head) { struct outstanding_channel_request *chanreq = c->chanreq_head; c->chanreq_head = c->chanreq_head->next; sfree(chanreq); } if (c->chan) { struct ssh2_connection_state *s = c->connlayer; if (s->mainchan_sc == &c->sc) { s->mainchan = NULL; s->mainchan_sc = NULL; } chan_free(c->chan); } sfree(c); } PacketProtocolLayer *ssh2_connection_new( Ssh *ssh, ssh_sharing_state *connshare, bool is_simple, Conf *conf, const char *peer_verstring, ConnectionLayer **cl_out) { struct ssh2_connection_state *s = snew(struct ssh2_connection_state); memset(s, 0, sizeof(*s)); s->ppl.vt = &ssh2_connection_vtable; s->conf = conf_copy(conf); s->ssh_is_simple = is_simple; /* * If the ssh_no_shell option is enabled, we disable the usual * termination check, so that we persist even in the absence of * any at all channels (because our purpose is probably to be a * background port forwarder). */ s->persistent = conf_get_bool(s->conf, CONF_ssh_no_shell); s->connshare = connshare; s->peer_verstring = dupstr(peer_verstring); s->channels = newtree234(ssh2_channelcmp); s->x11authtree = newtree234(x11_authcmp); /* Need to get the log context for s->cl now, because we won't be * helpfully notified when a copy is written into s->ppl by our * owner. */ s->cl.vt = &ssh2_connlayer_vtable; s->cl.logctx = ssh_get_logctx(ssh); s->portfwdmgr = portfwdmgr_new(&s->cl); *cl_out = &s->cl; if (s->connshare) ssh_connshare_provide_connlayer(s->connshare, &s->cl); return &s->ppl; } static void ssh2_connection_free(PacketProtocolLayer *ppl) { struct ssh2_connection_state *s = container_of(ppl, struct ssh2_connection_state, ppl); struct X11FakeAuth *auth; struct ssh2_channel *c; struct ssh_rportfwd *rpf; sfree(s->peer_verstring); conf_free(s->conf); while ((c = delpos234(s->channels, 0)) != NULL) ssh2_channel_free(c); freetree234(s->channels); while ((auth = delpos234(s->x11authtree, 0)) != NULL) { if (auth->disp) x11_free_display(auth->disp); x11_free_fake_auth(auth); } freetree234(s->x11authtree); if (s->rportfwds) { while ((rpf = delpos234(s->rportfwds, 0)) != NULL) free_rportfwd(rpf); freetree234(s->rportfwds); } portfwdmgr_free(s->portfwdmgr); if (s->antispoof_prompt) free_prompts(s->antispoof_prompt); delete_callbacks_for_context(s); sfree(s); } static bool ssh2_connection_filter_queue(struct ssh2_connection_state *s) { PktIn *pktin; PktOut *pktout; ptrlen type, data; struct ssh2_channel *c; struct outstanding_channel_request *ocr; unsigned localid, remid, winsize, pktsize, ext_type; bool want_reply, reply_success, expect_halfopen; ChanopenResult chanopen_result; PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */ while (1) { if (ssh2_common_filter_queue(&s->ppl)) return true; if ((pktin = pq_peek(s->ppl.in_pq)) == NULL) return false; switch (pktin->type) { case SSH2_MSG_GLOBAL_REQUEST: type = get_string(pktin); want_reply = get_bool(pktin); reply_success = ssh2_connection_parse_global_request( s, type, pktin); if (want_reply) { int type = (reply_success ? SSH2_MSG_REQUEST_SUCCESS : SSH2_MSG_REQUEST_FAILURE); pktout = ssh_bpp_new_pktout(s->ppl.bpp, type); pq_push(s->ppl.out_pq, pktout); } pq_pop(s->ppl.in_pq); break; case SSH2_MSG_REQUEST_SUCCESS: case SSH2_MSG_REQUEST_FAILURE: if (!s->globreq_head) { ssh_proto_error( s->ppl.ssh, "Received %s with no outstanding global request", ssh2_pkt_type(s->ppl.bpp->pls->kctx, s->ppl.bpp->pls->actx, pktin->type)); return true; } s->globreq_head->handler(s, pktin, s->globreq_head->ctx); { struct outstanding_global_request *tmp = s->globreq_head; s->globreq_head = s->globreq_head->next; sfree(tmp); } pq_pop(s->ppl.in_pq); break; case SSH2_MSG_CHANNEL_OPEN: type = get_string(pktin); c = snew(struct ssh2_channel); c->connlayer = s; c->chan = NULL; remid = get_uint32(pktin); winsize = get_uint32(pktin); pktsize = get_uint32(pktin); chanopen_result = ssh2_connection_parse_channel_open( s, type, pktin, &c->sc); if (chanopen_result.outcome == CHANOPEN_RESULT_DOWNSTREAM) { /* * This channel-open request needs to go to 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( chanopen_result.u.downstream.share_ctx, pktin->type, BinarySource_UPCAST(pktin)->data, BinarySource_UPCAST(pktin)->len); sfree(c); break; } c->remoteid = remid; c->halfopen = false; if (chanopen_result.outcome == CHANOPEN_RESULT_FAILURE) { pktout = ssh_bpp_new_pktout( s->ppl.bpp, SSH2_MSG_CHANNEL_OPEN_FAILURE); put_uint32(pktout, c->remoteid); put_uint32(pktout, chanopen_result.u.failure.reason_code); put_stringz(pktout, chanopen_result.u.failure.wire_message); put_stringz(pktout, "en"); /* language tag */ pq_push(s->ppl.out_pq, pktout); ppl_logevent("Rejected channel open: %s", chanopen_result.u.failure.wire_message); sfree(chanopen_result.u.failure.wire_message); sfree(c); } else { c->chan = chanopen_result.u.success.channel; ssh2_channel_init(c); c->remwindow = winsize; c->remmaxpkt = pktsize; if (c->remmaxpkt > s->ppl.bpp->vt->packet_size_limit) c->remmaxpkt = s->ppl.bpp->vt->packet_size_limit; if (c->chan->initial_fixed_window_size) { c->locwindow = c->locmaxwin = c->remlocwin = c->chan->initial_fixed_window_size; } pktout = ssh_bpp_new_pktout( s->ppl.bpp, SSH2_MSG_CHANNEL_OPEN_CONFIRMATION); put_uint32(pktout, c->remoteid); put_uint32(pktout, c->localid); put_uint32(pktout, c->locwindow); put_uint32(pktout, OUR_V2_MAXPKT); /* our max pkt size */ pq_push(s->ppl.out_pq, pktout); } pq_pop(s->ppl.in_pq); break; case SSH2_MSG_CHANNEL_DATA: case SSH2_MSG_CHANNEL_EXTENDED_DATA: case SSH2_MSG_CHANNEL_WINDOW_ADJUST: case SSH2_MSG_CHANNEL_REQUEST: case SSH2_MSG_CHANNEL_EOF: case SSH2_MSG_CHANNEL_CLOSE: case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION: case SSH2_MSG_CHANNEL_OPEN_FAILURE: case SSH2_MSG_CHANNEL_SUCCESS: case SSH2_MSG_CHANNEL_FAILURE: /* * Common preliminary code for all the messages from the * server that cite one of our channel ids: look up that * channel id, check it exists, and if it's for a sharing * downstream, pass it on. */ localid = get_uint32(pktin); c = find234(s->channels, &localid, ssh2_channelfind); if (c && c->sharectx) { share_got_pkt_from_server(c->sharectx, pktin->type, BinarySource_UPCAST(pktin)->data, BinarySource_UPCAST(pktin)->len); pq_pop(s->ppl.in_pq); break; } expect_halfopen = ( pktin->type == SSH2_MSG_CHANNEL_OPEN_CONFIRMATION || pktin->type == SSH2_MSG_CHANNEL_OPEN_FAILURE); if (!c || c->halfopen != expect_halfopen) { ssh_proto_error(s->ppl.ssh, "Received %s for %s channel %u", ssh2_pkt_type(s->ppl.bpp->pls->kctx, s->ppl.bpp->pls->actx, pktin->type), (!c ? "nonexistent" : c->halfopen ? "half-open" : "open"), localid); return true; } switch (pktin->type) { case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION: assert(c->halfopen); c->remoteid = get_uint32(pktin); c->halfopen = false; c->remwindow = get_uint32(pktin); c->remmaxpkt = get_uint32(pktin); if (c->remmaxpkt > s->ppl.bpp->vt->packet_size_limit) c->remmaxpkt = s->ppl.bpp->vt->packet_size_limit; chan_open_confirmation(c->chan); /* * Now that the channel is fully open, it's possible * in principle to immediately close it. Check whether * it wants us to! * * This can occur if a local socket error occurred * between us sending out CHANNEL_OPEN and receiving * OPEN_CONFIRMATION. If that happens, all we can do * is immediately initiate close proceedings now that * we know the server's id to put in the close * message. We'll have handled that in this code by * having already turned c->chan into a zombie, so its * want_close method (which ssh2_channel_check_close * will consult) will already be returning true. */ ssh2_channel_check_close(c); if (c->pending_eof) ssh2_channel_try_eof(c); /* in case we had a pending EOF */ break; case SSH2_MSG_CHANNEL_OPEN_FAILURE: { assert(c->halfopen); char *err = ssh2_channel_open_failure_error_text(pktin); chan_open_failed(c->chan, err); sfree(err); del234(s->channels, c); ssh2_channel_free(c); break; } case SSH2_MSG_CHANNEL_DATA: case SSH2_MSG_CHANNEL_EXTENDED_DATA: ext_type = (pktin->type == SSH2_MSG_CHANNEL_DATA ? 0 : get_uint32(pktin)); data = get_string(pktin); if (!get_err(pktin)) { int bufsize; c->locwindow -= data.len; c->remlocwin -= data.len; if (ext_type != 0 && ext_type != SSH2_EXTENDED_DATA_STDERR) data.len = 0; /* ignore unknown extended data */ bufsize = chan_send( c->chan, ext_type == SSH2_EXTENDED_DATA_STDERR, data.ptr, data.len); /* * The channel may have turned into a connection- * shared one as a result of that chan_send, e.g. * if the data we just provided completed the X11 * auth phase and caused a callback to * x11_sharing_handover. If so, do nothing * further. */ if (c->sharectx) break; /* * If it looks like the remote end hit the end of * its window, and we didn't want it to do that, * think about using a larger window. */ if (c->remlocwin <= 0 && c->throttle_state == UNTHROTTLED && c->locmaxwin < 0x40000000) c->locmaxwin += OUR_V2_WINSIZE; /* * If we are not buffering too much data, enlarge * the window again at the remote side. If we are * buffering too much, we may still need to adjust * the window if the server's sent excess data. */ if (bufsize < c->locmaxwin) ssh2_set_window(c, c->locmaxwin - bufsize); /* * 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->locmaxwin || (s->ssh_is_simple && bufsize>0)) && !c->throttling_conn) { c->throttling_conn = true; ssh_throttle_conn(s->ppl.ssh, +1); } } break; case SSH2_MSG_CHANNEL_WINDOW_ADJUST: if (!(c->closes & CLOSES_SENT_EOF)) { c->remwindow += get_uint32(pktin); ssh2_try_send_and_unthrottle(c); } break; case SSH2_MSG_CHANNEL_REQUEST: type = get_string(pktin); want_reply = get_bool(pktin); reply_success = false; if (c->closes & CLOSES_SENT_CLOSE) { /* * We don't reply to channel requests after we've * sent CHANNEL_CLOSE for the channel, because our * reply might cross in the network with the other * side's CHANNEL_CLOSE and arrive after they have * wound the channel up completely. */ want_reply = false; } /* * Try every channel request name we recognise, no * matter what the channel, and see if the Channel * instance will accept it. */ if (ptrlen_eq_string(type, "exit-status")) { int exitcode = toint(get_uint32(pktin)); reply_success = chan_rcvd_exit_status(c->chan, exitcode); } else if (ptrlen_eq_string(type, "exit-signal")) { ptrlen signame; int signum; bool core = false; ptrlen errmsg; int format; /* * ICK: older versions of OpenSSH (e.g. 3.4p1) * provide an `int' for the signal, despite its * having been a `string' in the drafts of RFC * 4254 since at least 2001. (Fixed in session.c * 1.147.) Try to infer which we can safely parse * it as. */ size_t startpos = BinarySource_UPCAST(pktin)->pos; for (format = 0; format < 2; format++) { BinarySource_UPCAST(pktin)->pos = startpos; BinarySource_UPCAST(pktin)->err = BSE_NO_ERROR; /* placate compiler warnings about unin */ signame = make_ptrlen(NULL, 0); signum = 0; if (format == 0) /* standard string-based format */ signame = get_string(pktin); else /* nonstandard integer format */ signum = toint(get_uint32(pktin)); core = get_bool(pktin); errmsg = get_string(pktin); /* error message */ get_string(pktin); /* language tag */ if (!get_err(pktin) && get_avail(pktin) == 0) break; /* successful parse */ } switch (format) { case 0: reply_success = chan_rcvd_exit_signal( c->chan, signame, core, errmsg); break; case 1: reply_success = chan_rcvd_exit_signal_numeric( c->chan, signum, core, errmsg); break; default: /* Couldn't parse this message in either format */ reply_success = false; break; } } else if (ptrlen_eq_string(type, "shell")) { reply_success = chan_run_shell(c->chan); } else if (ptrlen_eq_string(type, "exec")) { ptrlen command = get_string(pktin); reply_success = chan_run_command(c->chan, command); } else if (ptrlen_eq_string(type, "subsystem")) { ptrlen subsys = get_string(pktin); reply_success = chan_run_subsystem(c->chan, subsys); } else if (ptrlen_eq_string(type, "x11-req")) { bool oneshot = get_bool(pktin); ptrlen authproto = get_string(pktin); ptrlen authdata = get_string(pktin); unsigned screen_number = get_uint32(pktin); reply_success = chan_enable_x11_forwarding( c->chan, oneshot, authproto, authdata, screen_number); } else if (ptrlen_eq_string(type, "auth-agent-req@openssh.com")) { reply_success = chan_enable_agent_forwarding(c->chan); } else if (ptrlen_eq_string(type, "pty-req")) { ptrlen termtype = get_string(pktin); unsigned width = get_uint32(pktin); unsigned height = get_uint32(pktin); unsigned pixwidth = get_uint32(pktin); unsigned pixheight = get_uint32(pktin); ptrlen encoded_modes = get_string(pktin); BinarySource bs_modes[1]; struct ssh_ttymodes modes; BinarySource_BARE_INIT_PL(bs_modes, encoded_modes); modes = read_ttymodes_from_packet(bs_modes, 2); if (get_err(bs_modes) || get_avail(bs_modes) > 0) { ppl_logevent("Unable to decode terminal mode string"); reply_success = false; } else { reply_success = chan_allocate_pty( c->chan, termtype, width, height, pixwidth, pixheight, modes); } } else if (ptrlen_eq_string(type, "env")) { ptrlen var = get_string(pktin); ptrlen value = get_string(pktin); reply_success = chan_set_env(c->chan, var, value); } else if (ptrlen_eq_string(type, "break")) { unsigned length = get_uint32(pktin); reply_success = chan_send_break(c->chan, length); } else if (ptrlen_eq_string(type, "signal")) { ptrlen signame = get_string(pktin); reply_success = chan_send_signal(c->chan, signame); } else if (ptrlen_eq_string(type, "window-change")) { unsigned width = get_uint32(pktin); unsigned height = get_uint32(pktin); unsigned pixwidth = get_uint32(pktin); unsigned pixheight = get_uint32(pktin); reply_success = chan_change_window_size( c->chan, width, height, pixwidth, pixheight); } if (want_reply) { int type = (reply_success ? SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE); pktout = ssh_bpp_new_pktout(s->ppl.bpp, type); put_uint32(pktout, c->remoteid); pq_push(s->ppl.out_pq, pktout); } break; case SSH2_MSG_CHANNEL_SUCCESS: case SSH2_MSG_CHANNEL_FAILURE: ocr = c->chanreq_head; if (!ocr) { ssh_proto_error( s->ppl.ssh, "Received %s for channel %d with no outstanding " "channel request", ssh2_pkt_type(s->ppl.bpp->pls->kctx, s->ppl.bpp->pls->actx, pktin->type), c->localid); return true; } ocr->handler(c, pktin, ocr->ctx); c->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); break; case SSH2_MSG_CHANNEL_EOF: if (!(c->closes & CLOSES_RCVD_EOF)) { c->closes |= CLOSES_RCVD_EOF; chan_send_eof(c->chan); ssh2_channel_check_close(c); } break; case SSH2_MSG_CHANNEL_CLOSE: /* * When we receive CLOSE on a channel, we assume it * comes with an implied EOF if we haven't seen EOF * yet. */ if (!(c->closes & CLOSES_RCVD_EOF)) { c->closes |= CLOSES_RCVD_EOF; chan_send_eof(c->chan); } if (!(s->ppl.remote_bugs & BUG_SENDS_LATE_REQUEST_REPLY)) { /* * It also means we stop expecting to see replies * to any outstanding channel requests, so clean * those up too. (ssh_chanreq_init will enforce by * assertion that we don't subsequently put * anything back on this list.) */ while (c->chanreq_head) { struct outstanding_channel_request *ocr = c->chanreq_head; ocr->handler(c, NULL, ocr->ctx); c->chanreq_head = ocr->next; sfree(ocr); } } /* * 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)) { /* * 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->outbuffer); bufchain_clear(&c->errbuffer); /* * Send outgoing EOF. */ sshfwd_write_eof(&c->sc); /* * Make sure we don't read any more from whatever * our local data source is for this channel. * (This will pick up on the changes made by * sshfwd_write_eof.) */ ssh2_channel_check_throttle(c); } /* * Now process the actual close. */ if (!(c->closes & CLOSES_RCVD_CLOSE)) { c->closes |= CLOSES_RCVD_CLOSE; ssh2_channel_check_close(c); } break; } pq_pop(s->ppl.in_pq); break; default: return false; } } } static void ssh2_handle_winadj_response(struct ssh2_channel *c, PktIn *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->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->throttle_state == UNTHROTTLING) c->throttle_state = UNTHROTTLED; } static void ssh2_set_window(struct ssh2_channel *c, int newwin) { struct ssh2_connection_state *s = c->connlayer; /* * 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; /* * If the client-side Channel is in an initial setup phase with a * fixed window size, e.g. for an X11 channel when we're still * waiting to see its initial auth and may yet hand it off to a * downstream, don't send any WINDOW_ADJUST either. */ if (c->chan->initial_fixed_window_size) 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 ((s->ppl.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->locwindow) { PktOut *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->locmaxwin && !(s->ppl.remote_bugs & BUG_CHOKES_ON_WINADJ)) { up = snew(unsigned); *up = newwin - c->locwindow; pktout = ssh2_chanreq_init(c, "winadj@putty.projects.tartarus.org", ssh2_handle_winadj_response, up); pq_push(s->ppl.out_pq, pktout); if (c->throttle_state != UNTHROTTLED) c->throttle_state = UNTHROTTLING; } else { /* Pretend the WINDOW_ADJUST was acked immediately. */ c->remlocwin = newwin; c->throttle_state = THROTTLED; } pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_CHANNEL_WINDOW_ADJUST); put_uint32(pktout, c->remoteid); put_uint32(pktout, newwin - c->locwindow); pq_push(s->ppl.out_pq, pktout); c->locwindow = newwin; } } static PktIn *ssh2_connection_pop(struct ssh2_connection_state *s) { ssh2_connection_filter_queue(s); return pq_pop(s->ppl.in_pq); } static void ssh2_connection_process_queue(PacketProtocolLayer *ppl) { struct ssh2_connection_state *s = container_of(ppl, struct ssh2_connection_state, ppl); PktIn *pktin; if (ssh2_connection_filter_queue(s)) /* no matter why we were called */ return; crBegin(s->crState); if (s->connshare) share_activate(s->connshare, s->peer_verstring); /* * Signal the seat that authentication is done, so that it can * deploy spoofing defences. If it doesn't have any, deploy our * own fallback one. * * We do this here rather than at the end of userauth, because we * might not have gone through userauth at all (if we're a * connection-sharing downstream). */ if (ssh2_connection_need_antispoof_prompt(s)) { s->antispoof_prompt = new_prompts(); s->antispoof_prompt->to_server = true; s->antispoof_prompt->from_server = false; s->antispoof_prompt->name = dupstr("Authentication successful"); add_prompt( s->antispoof_prompt, dupstr("Access granted. Press Return to begin session. "), false); s->antispoof_ret = seat_get_userpass_input( s->ppl.seat, s->antispoof_prompt, NULL); while (1) { while (s->antispoof_ret < 0 && bufchain_size(s->ppl.user_input) > 0) s->antispoof_ret = seat_get_userpass_input( s->ppl.seat, s->antispoof_prompt, s->ppl.user_input); if (s->antispoof_ret >= 0) break; s->want_user_input = true; crReturnV; s->want_user_input = false; } free_prompts(s->antispoof_prompt); s->antispoof_prompt = NULL; } /* * Enable port forwardings. */ portfwdmgr_config(s->portfwdmgr, s->conf); s->portfwdmgr_configured = true; /* * Create the main session channel, if any. */ s->mainchan = mainchan_new( &s->ppl, &s->cl, s->conf, s->term_width, s->term_height, s->ssh_is_simple, &s->mainchan_sc); s->started = true; /* * Transfer data! */ while (1) { if ((pktin = ssh2_connection_pop(s)) != NULL) { /* * _All_ the connection-layer packets we expect to * receive are now handled by the dispatch table. * Anything that reaches here must be bogus. */ ssh_proto_error(s->ppl.ssh, "Received unexpected connection-layer " "packet, type %d (%s)", pktin->type, ssh2_pkt_type(s->ppl.bpp->pls->kctx, s->ppl.bpp->pls->actx, pktin->type)); return; } crReturnV; } crFinishV; } static void ssh2_channel_check_close(struct ssh2_channel *c) { struct ssh2_connection_state *s = c->connlayer; PktOut *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 (chan_want_close(c->chan, (c->closes & CLOSES_SENT_EOF), (c->closes & CLOSES_RCVD_EOF)) && !c->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 = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_CHANNEL_CLOSE); put_uint32(pktout, c->remoteid); pq_push(s->ppl.out_pq, pktout); c->closes |= CLOSES_SENT_EOF | CLOSES_SENT_CLOSE; } if (!((CLOSES_SENT_CLOSE | CLOSES_RCVD_CLOSE) & ~c->closes)) { assert(c->chanreq_head == NULL); /* * We have both sent and received CLOSE, which means we're * completely done with the channel. */ ssh2_channel_destroy(c); } } static void ssh2_channel_try_eof(struct ssh2_channel *c) { struct ssh2_connection_state *s = c->connlayer; PktOut *pktout; assert(c->pending_eof); /* precondition for calling us */ if (c->halfopen) return; /* can't close: not even opened yet */ if (bufchain_size(&c->outbuffer) > 0 || bufchain_size(&c->errbuffer) > 0) return; /* can't send EOF: pending outgoing data */ c->pending_eof = false; /* we're about to send it */ pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_CHANNEL_EOF); put_uint32(pktout, c->remoteid); pq_push(s->ppl.out_pq, pktout); c->closes |= CLOSES_SENT_EOF; ssh2_channel_check_close(c); } /* * Attempt to send data on an SSH-2 channel. */ static size_t ssh2_try_send(struct ssh2_channel *c) { struct ssh2_connection_state *s = c->connlayer; PktOut *pktout; size_t bufsize; if (!c->halfopen) { while (c->remwindow > 0 && (bufchain_size(&c->outbuffer) > 0 || bufchain_size(&c->errbuffer) > 0)) { bufchain *buf = (bufchain_size(&c->errbuffer) > 0 ? &c->errbuffer : &c->outbuffer); ptrlen data = bufchain_prefix(buf); if (data.len > c->remwindow) data.len = c->remwindow; if (data.len > c->remmaxpkt) data.len = c->remmaxpkt; if (buf == &c->errbuffer) { pktout = ssh_bpp_new_pktout( s->ppl.bpp, SSH2_MSG_CHANNEL_EXTENDED_DATA); put_uint32(pktout, c->remoteid); put_uint32(pktout, SSH2_EXTENDED_DATA_STDERR); } else { pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_CHANNEL_DATA); put_uint32(pktout, c->remoteid); } put_stringpl(pktout, data); pq_push(s->ppl.out_pq, pktout); bufchain_consume(buf, data.len); c->remwindow -= data.len; } } /* * After having sent as much data as we can, return the amount * still buffered. */ bufsize = bufchain_size(&c->outbuffer) + bufchain_size(&c->errbuffer); /* * And if there's no data pending but we need to send an EOF, send * it. */ if (!bufsize && c->pending_eof) ssh2_channel_try_eof(c); return bufsize; } static void ssh2_try_send_and_unthrottle(struct ssh2_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) { c->throttled_by_backlog = false; ssh2_channel_check_throttle(c); } } static void ssh2_channel_check_throttle(struct ssh2_channel *c) { /* * We don't want this channel to read further input if this * particular channel has a backed-up SSH window, or if the * outgoing side of the whole SSH connection is currently * throttled, or if this channel already has an outgoing EOF * either sent or pending. */ chan_set_input_wanted(c->chan, !c->throttled_by_backlog && !c->connlayer->all_channels_throttled && !c->pending_eof && !(c->closes & CLOSES_SENT_EOF)); } /* * Close any local socket and free any local resources associated with * a channel. This converts the channel into a zombie. */ static void ssh2_channel_close_local(struct ssh2_channel *c, const char *reason) { struct ssh2_connection_state *s = c->connlayer; PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */ char *msg = NULL; if (c->sharectx) return; msg = chan_log_close_msg(c->chan); if (msg) ppl_logevent("%s%s%s", msg, reason ? " " : "", reason ? reason : ""); sfree(msg); chan_free(c->chan); c->chan = zombiechan_new(); } static void ssh2_check_termination_callback(void *vctx) { struct ssh2_connection_state *s = (struct ssh2_connection_state *)vctx; ssh2_check_termination(s); } static void ssh2_channel_destroy(struct ssh2_channel *c) { struct ssh2_connection_state *s = c->connlayer; assert(c->chanreq_head == NULL); ssh2_channel_close_local(c, NULL); del234(s->channels, c); ssh2_channel_free(c); /* * If that was the last channel left open, we might need to * terminate. But we'll be a bit cautious, by doing that in a * toplevel callback, just in case anything on the current call * stack objects to this entire PPL being freed. */ queue_toplevel_callback(ssh2_check_termination_callback, s); } static void ssh2_check_termination(struct ssh2_connection_state *s) { /* * Decide whether we should terminate the SSH connection now. * Called after a channel or a downstream goes away. The general * policy is that we terminate when none of either is left. */ if (s->persistent) return; /* persistent mode: never proactively terminate */ if (!s->started) { /* At startup, we don't have any channels open because we * haven't got round to opening the main one yet. In that * situation, we don't want to terminate, even if a sharing * connection opens and closes and causes a call to this * function. */ return; } if (count234(s->channels) == 0 && !(s->connshare && share_ndownstreams(s->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_user_close(s->ppl.ssh, "All channels closed"); return; } } /* * Set up most of a new ssh2_channel. Nulls out sharectx, but leaves * chan untouched (since it will sometimes have been filled in before * calling this). */ void ssh2_channel_init(struct ssh2_channel *c) { struct ssh2_connection_state *s = c->connlayer; c->closes = 0; c->pending_eof = false; c->throttling_conn = false; c->throttled_by_backlog = false; c->sharectx = NULL; c->locwindow = c->locmaxwin = c->remlocwin = s->ssh_is_simple ? OUR_V2_BIGWIN : OUR_V2_WINSIZE; c->chanreq_head = NULL; c->throttle_state = UNTHROTTLED; bufchain_init(&c->outbuffer); bufchain_init(&c->errbuffer); c->sc.vt = &ssh2channel_vtable; c->sc.cl = &s->cl; c->localid = alloc_channel_id(s->channels, struct ssh2_channel); add234(s->channels, c); } /* * Construct the common parts of a CHANNEL_OPEN. */ PktOut *ssh2_chanopen_init(struct ssh2_channel *c, const char *type) { struct ssh2_connection_state *s = c->connlayer; PktOut *pktout; pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_CHANNEL_OPEN); put_stringz(pktout, type); put_uint32(pktout, c->localid); put_uint32(pktout, c->locwindow); /* our window size */ put_uint32(pktout, OUR_V2_MAXPKT); /* our max pkt size */ return pktout; } /* * 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, or because * the server initiated channel closure before we saw the response) * and the handler should free any storage it's holding. */ PktOut *ssh2_chanreq_init(struct ssh2_channel *c, const char *type, cr_handler_fn_t handler, void *ctx) { struct ssh2_connection_state *s = c->connlayer; PktOut *pktout; assert(!(c->closes & (CLOSES_SENT_CLOSE | CLOSES_RCVD_CLOSE))); pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_CHANNEL_REQUEST); put_uint32(pktout, c->remoteid); put_stringz(pktout, type); put_bool(pktout, handler != NULL); if (handler != NULL) { struct outstanding_channel_request *ocr = snew(struct outstanding_channel_request); ocr->handler = handler; ocr->ctx = ctx; ocr->next = NULL; if (!c->chanreq_head) c->chanreq_head = ocr; else c->chanreq_tail->next = ocr; c->chanreq_tail = ocr; } return pktout; } static Conf *ssh2channel_get_conf(SshChannel *sc) { struct ssh2_channel *c = container_of(sc, struct ssh2_channel, sc); struct ssh2_connection_state *s = c->connlayer; return s->conf; } static void ssh2channel_write_eof(SshChannel *sc) { struct ssh2_channel *c = container_of(sc, struct ssh2_channel, sc); if (c->closes & CLOSES_SENT_EOF) return; c->pending_eof = true; ssh2_channel_try_eof(c); } static void ssh2channel_initiate_close(SshChannel *sc, const char *err) { struct ssh2_channel *c = container_of(sc, struct ssh2_channel, sc); char *reason; reason = err ? dupprintf("due to local error: %s", err) : NULL; ssh2_channel_close_local(c, reason); sfree(reason); c->pending_eof = false; /* this will confuse a zombie channel */ ssh2_channel_check_close(c); } static void ssh2channel_unthrottle(SshChannel *sc, size_t bufsize) { struct ssh2_channel *c = container_of(sc, struct ssh2_channel, sc); struct ssh2_connection_state *s = c->connlayer; size_t buflimit; buflimit = s->ssh_is_simple ? 0 : c->locmaxwin; if (bufsize < buflimit) ssh2_set_window(c, buflimit - bufsize); if (c->throttling_conn && bufsize <= buflimit) { c->throttling_conn = false; ssh_throttle_conn(s->ppl.ssh, -1); } } static size_t ssh2channel_write( SshChannel *sc, bool is_stderr, const void *buf, size_t len) { struct ssh2_channel *c = container_of(sc, struct ssh2_channel, sc); assert(!(c->closes & CLOSES_SENT_EOF)); bufchain_add(is_stderr ? &c->errbuffer : &c->outbuffer, buf, len); return ssh2_try_send(c); } static void ssh2channel_x11_sharing_handover( SshChannel *sc, ssh_sharing_connstate *share_cs, share_channel *share_chan, const char *peer_addr, int peer_port, int endian, int protomajor, int protominor, const void *initial_data, int initial_len) { struct ssh2_channel *c = container_of(sc, struct ssh2_channel, sc); /* * 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 sharing one, meaning * that we completely stop tracking windows and buffering data and * just pass more or less unmodified SSH messages back and forth. */ c->sharectx = share_cs; share_setup_x11_channel(share_cs, share_chan, c->localid, c->remoteid, c->remwindow, c->remmaxpkt, c->locwindow, peer_addr, peer_port, endian, protomajor, protominor, initial_data, initial_len); chan_free(c->chan); c->chan = NULL; } static void ssh2channel_window_override_removed(SshChannel *sc) { struct ssh2_channel *c = container_of(sc, struct ssh2_channel, sc); struct ssh2_connection_state *s = c->connlayer; /* * This function is called when a client-side Channel has just * stopped requiring an initial fixed-size window. */ assert(!c->chan->initial_fixed_window_size); ssh2_set_window(c, s->ssh_is_simple ? OUR_V2_BIGWIN : OUR_V2_WINSIZE); } static void ssh2channel_hint_channel_is_simple(SshChannel *sc) { struct ssh2_channel *c = container_of(sc, struct ssh2_channel, sc); struct ssh2_connection_state *s = c->connlayer; PktOut *pktout = ssh2_chanreq_init( c, "simple@putty.projects.tartarus.org", NULL, NULL); pq_push(s->ppl.out_pq, pktout); } static SshChannel *ssh2_lportfwd_open( ConnectionLayer *cl, const char *hostname, int port, const char *description, const SocketPeerInfo *pi, Channel *chan) { struct ssh2_connection_state *s = container_of(cl, struct ssh2_connection_state, cl); struct ssh2_channel *c = snew(struct ssh2_channel); PktOut *pktout; c->connlayer = s; ssh2_channel_init(c); c->halfopen = true; c->chan = chan; pktout = ssh2_portfwd_chanopen(s, c, hostname, port, description, pi); pq_push(s->ppl.out_pq, pktout); return &c->sc; } static void ssh2_sharing_globreq_response( struct ssh2_connection_state *s, PktIn *pktin, void *ctx) { ssh_sharing_connstate *cs = (ssh_sharing_connstate *)ctx; share_got_pkt_from_server(cs, pktin->type, BinarySource_UPCAST(pktin)->data, BinarySource_UPCAST(pktin)->len); } static void ssh2_sharing_queue_global_request( ConnectionLayer *cl, ssh_sharing_connstate *cs) { struct ssh2_connection_state *s = container_of(cl, struct ssh2_connection_state, cl); ssh2_queue_global_request_handler(s, ssh2_sharing_globreq_response, cs); } static void ssh2_sharing_no_more_downstreams(ConnectionLayer *cl) { struct ssh2_connection_state *s = container_of(cl, struct ssh2_connection_state, cl); queue_toplevel_callback(ssh2_check_termination_callback, s); } static struct X11FakeAuth *ssh2_add_x11_display( ConnectionLayer *cl, int authtype, struct X11Display *disp) { struct ssh2_connection_state *s = container_of(cl, struct ssh2_connection_state, cl); struct X11FakeAuth *auth = x11_invent_fake_auth(s->x11authtree, authtype); auth->disp = disp; return auth; } static struct X11FakeAuth *ssh2_add_sharing_x11_display( ConnectionLayer *cl, int authtype, ssh_sharing_connstate *share_cs, share_channel *share_chan) { struct ssh2_connection_state *s = container_of(cl, struct ssh2_connection_state, cl); 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(s->x11authtree, authtype); auth->share_cs = share_cs; auth->share_chan = share_chan; return auth; } static void ssh2_remove_sharing_x11_display( ConnectionLayer *cl, struct X11FakeAuth *auth) { struct ssh2_connection_state *s = container_of(cl, struct ssh2_connection_state, cl); del234(s->x11authtree, auth); x11_free_fake_auth(auth); } static unsigned ssh2_alloc_sharing_channel( ConnectionLayer *cl, ssh_sharing_connstate *connstate) { struct ssh2_connection_state *s = container_of(cl, struct ssh2_connection_state, cl); struct ssh2_channel *c = snew(struct ssh2_channel); c->connlayer = s; ssh2_channel_init(c); c->chan = NULL; c->sharectx = connstate; return c->localid; } static void ssh2_delete_sharing_channel(ConnectionLayer *cl, unsigned localid) { struct ssh2_connection_state *s = container_of(cl, struct ssh2_connection_state, cl); struct ssh2_channel *c = find234(s->channels, &localid, ssh2_channelfind); if (c) ssh2_channel_destroy(c); } static void ssh2_send_packet_from_downstream( ConnectionLayer *cl, unsigned id, int type, const void *data, int datalen, const char *additional_log_text) { struct ssh2_connection_state *s = container_of(cl, struct ssh2_connection_state, cl); PktOut *pkt = ssh_bpp_new_pktout(s->ppl.bpp, type); pkt->downstream_id = id; pkt->additional_log_text = additional_log_text; put_data(pkt, data, datalen); pq_push(s->ppl.out_pq, pkt); } static bool ssh2_agent_forwarding_permitted(ConnectionLayer *cl) { struct ssh2_connection_state *s = container_of(cl, struct ssh2_connection_state, cl); return conf_get_bool(s->conf, CONF_agentfwd) && agent_exists(); } static bool ssh2_connection_get_specials( PacketProtocolLayer *ppl, add_special_fn_t add_special, void *ctx) { struct ssh2_connection_state *s = container_of(ppl, struct ssh2_connection_state, ppl); bool toret = false; if (s->mainchan) { mainchan_get_specials(s->mainchan, add_special, ctx); toret = true; } /* * Don't bother offering IGNORE if we've decided the remote * won't cope with it, since we wouldn't bother sending it if * asked anyway. */ if (!(s->ppl.remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE)) { if (toret) add_special(ctx, NULL, SS_SEP, 0); add_special(ctx, "IGNORE message", SS_NOP, 0); toret = true; } return toret; } static void ssh2_connection_special_cmd(PacketProtocolLayer *ppl, SessionSpecialCode code, int arg) { struct ssh2_connection_state *s = container_of(ppl, struct ssh2_connection_state, ppl); PktOut *pktout; if (code == SS_PING || code == SS_NOP) { if (!(s->ppl.remote_bugs & BUG_CHOKES_ON_SSH2_IGNORE)) { pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_IGNORE); put_stringz(pktout, ""); pq_push(s->ppl.out_pq, pktout); } } else if (s->mainchan) { mainchan_special_cmd(s->mainchan, code, arg); } } static void ssh2_terminal_size(ConnectionLayer *cl, int width, int height) { struct ssh2_connection_state *s = container_of(cl, struct ssh2_connection_state, cl); s->term_width = width; s->term_height = height; if (s->mainchan) mainchan_terminal_size(s->mainchan, width, height); } static void ssh2_stdout_unthrottle(ConnectionLayer *cl, size_t bufsize) { struct ssh2_connection_state *s = container_of(cl, struct ssh2_connection_state, cl); if (s->mainchan) sshfwd_unthrottle(s->mainchan_sc, bufsize); } static size_t ssh2_stdin_backlog(ConnectionLayer *cl) { struct ssh2_connection_state *s = container_of(cl, struct ssh2_connection_state, cl); struct ssh2_channel *c; if (!s->mainchan) return 0; c = container_of(s->mainchan_sc, struct ssh2_channel, sc); return s->mainchan ? bufchain_size(&c->outbuffer) + bufchain_size(&c->errbuffer) : 0; } static void ssh2_throttle_all_channels(ConnectionLayer *cl, bool throttled) { struct ssh2_connection_state *s = container_of(cl, struct ssh2_connection_state, cl); struct ssh2_channel *c; int i; s->all_channels_throttled = throttled; for (i = 0; NULL != (c = index234(s->channels, i)); i++) if (!c->sharectx) ssh2_channel_check_throttle(c); } static bool ssh2_ldisc_option(ConnectionLayer *cl, int option) { struct ssh2_connection_state *s = container_of(cl, struct ssh2_connection_state, cl); return s->ldisc_opts[option]; } static void ssh2_set_ldisc_option(ConnectionLayer *cl, int option, bool value) { struct ssh2_connection_state *s = container_of(cl, struct ssh2_connection_state, cl); s->ldisc_opts[option] = value; } static void ssh2_enable_x_fwd(ConnectionLayer *cl) { struct ssh2_connection_state *s = container_of(cl, struct ssh2_connection_state, cl); s->X11_fwd_enabled = true; } static void ssh2_set_wants_user_input(ConnectionLayer *cl, bool wanted) { struct ssh2_connection_state *s = container_of(cl, struct ssh2_connection_state, cl); s->want_user_input = wanted; } static bool ssh2_connection_want_user_input(PacketProtocolLayer *ppl) { struct ssh2_connection_state *s = container_of(ppl, struct ssh2_connection_state, ppl); return s->want_user_input; } static void ssh2_connection_got_user_input(PacketProtocolLayer *ppl) { struct ssh2_connection_state *s = container_of(ppl, struct ssh2_connection_state, ppl); while (s->mainchan && bufchain_size(s->ppl.user_input) > 0) { /* * Add user input to the main channel's buffer. */ ptrlen data = bufchain_prefix(s->ppl.user_input); sshfwd_write(s->mainchan_sc, data.ptr, data.len); bufchain_consume(s->ppl.user_input, data.len); } } static void ssh2_connection_reconfigure(PacketProtocolLayer *ppl, Conf *conf) { struct ssh2_connection_state *s = container_of(ppl, struct ssh2_connection_state, ppl); conf_free(s->conf); s->conf = conf_copy(conf); if (s->portfwdmgr_configured) portfwdmgr_config(s->portfwdmgr, s->conf); } putty-0.76/ssh2connection.h0000644000175000017500000002012314072266312012665 00000000000000#ifndef PUTTY_SSH2CONNECTION_H #define PUTTY_SSH2CONNECTION_H struct outstanding_channel_request; struct outstanding_global_request; struct ssh2_connection_state { int crState; ssh_sharing_state *connshare; char *peer_verstring; mainchan *mainchan; SshChannel *mainchan_sc; bool ldisc_opts[LD_N_OPTIONS]; int session_attempt, session_status; int term_width, term_height; bool want_user_input; bool ssh_is_simple; bool persistent; bool started; Conf *conf; tree234 *channels; /* indexed by local id */ bool all_channels_throttled; bool X11_fwd_enabled; tree234 *x11authtree; bool got_pty; tree234 *rportfwds; PortFwdManager *portfwdmgr; bool portfwdmgr_configured; prompts_t *antispoof_prompt; int antispoof_ret; const SftpServerVtable *sftpserver_vt; const SshServerConfig *ssc; /* * These store the list of global requests that we're waiting for * replies to. (REQUEST_FAILURE doesn't come with any indication * of what message caused it, so we have to keep track of the * queue ourselves.) */ struct outstanding_global_request *globreq_head, *globreq_tail; ConnectionLayer cl; PacketProtocolLayer ppl; }; typedef void (*gr_handler_fn_t)(struct ssh2_connection_state *s, PktIn *pktin, void *ctx); void ssh2_queue_global_request_handler( struct ssh2_connection_state *s, gr_handler_fn_t handler, void *ctx); struct ssh2_channel { struct ssh2_connection_state *connlayer; unsigned remoteid, localid; int type; /* True if we opened this channel but server hasn't confirmed. */ bool halfopen; /* Bitmap of whether we've sent/received CHANNEL_EOF and * CHANNEL_CLOSE. */ #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. */ bool pending_eof; /* * True if this channel is causing the underlying connection to be * throttled. */ bool throttling_conn; /* * True if we currently have backed-up data on the direction of * this channel pointing out of the SSH connection, and therefore * would prefer the 'Channel' implementation not to read further * local input if possible. */ bool throttled_by_backlog; bufchain outbuffer, errbuffer; 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 we're waiting for * replies to. (CHANNEL_FAILURE doesn't come with any indication * of what message caused it, so we have to keep track of the * queue ourselves.) */ struct outstanding_channel_request *chanreq_head, *chanreq_tail; enum { THROTTLED, UNTHROTTLING, UNTHROTTLED } throttle_state; ssh_sharing_connstate *sharectx; /* sharing context, if this is a * downstream channel */ Channel *chan; /* handle the client side of this channel, if not */ SshChannel sc; /* entry point for chan to talk back to */ }; typedef void (*cr_handler_fn_t)(struct ssh2_channel *, PktIn *, void *); void ssh2_channel_init(struct ssh2_channel *c); PktOut *ssh2_chanreq_init(struct ssh2_channel *c, const char *type, cr_handler_fn_t handler, void *ctx); typedef enum ChanopenOutcome { CHANOPEN_RESULT_FAILURE, CHANOPEN_RESULT_SUCCESS, CHANOPEN_RESULT_DOWNSTREAM, } ChanopenOutcome; typedef struct ChanopenResult { ChanopenOutcome outcome; union { struct { char *wire_message; /* must be freed by recipient */ unsigned reason_code; } failure; struct { Channel *channel; } success; struct { ssh_sharing_connstate *share_ctx; } downstream; } u; } ChanopenResult; PktOut *ssh2_chanopen_init(struct ssh2_channel *c, const char *type); PktOut *ssh2_portfwd_chanopen( struct ssh2_connection_state *s, struct ssh2_channel *c, const char *hostname, int port, const char *description, const SocketPeerInfo *peerinfo); struct ssh_rportfwd *ssh2_rportfwd_alloc( ConnectionLayer *cl, const char *shost, int sport, const char *dhost, int dport, int addressfamily, const char *log_description, PortFwdRecord *pfr, ssh_sharing_connstate *share_ctx); void ssh2_rportfwd_remove( ConnectionLayer *cl, struct ssh_rportfwd *rpf); SshChannel *ssh2_session_open(ConnectionLayer *cl, Channel *chan); SshChannel *ssh2_serverside_x11_open( ConnectionLayer *cl, Channel *chan, const SocketPeerInfo *pi); SshChannel *ssh2_serverside_agent_open(ConnectionLayer *cl, Channel *chan); void ssh2channel_send_exit_status(SshChannel *c, int status); void ssh2channel_send_exit_signal( SshChannel *c, ptrlen signame, bool core_dumped, ptrlen msg); void ssh2channel_send_exit_signal_numeric( SshChannel *c, int signum, bool core_dumped, ptrlen msg); void ssh2channel_request_x11_forwarding( SshChannel *c, bool want_reply, const char *authproto, const char *authdata, int screen_number, bool oneshot); void ssh2channel_request_agent_forwarding(SshChannel *c, bool want_reply); void ssh2channel_request_pty( SshChannel *c, bool want_reply, Conf *conf, int w, int h); bool ssh2channel_send_env_var( SshChannel *c, bool want_reply, const char *var, const char *value); void ssh2channel_start_shell(SshChannel *c, bool want_reply); void ssh2channel_start_command( SshChannel *c, bool want_reply, const char *command); bool ssh2channel_start_subsystem( SshChannel *c, bool want_reply, const char *subsystem); bool ssh2channel_send_env_var( SshChannel *c, bool want_reply, const char *var, const char *value); bool ssh2channel_send_serial_break( SshChannel *c, bool want_reply, int length); bool ssh2channel_send_signal( SshChannel *c, bool want_reply, const char *signame); void ssh2channel_send_terminal_size_change(SshChannel *c, int w, int h); #define CHANOPEN_RETURN_FAILURE(code, msgparams) do \ { \ ChanopenResult toret; \ toret.outcome = CHANOPEN_RESULT_FAILURE; \ toret.u.failure.reason_code = code; \ toret.u.failure.wire_message = dupprintf msgparams; \ return toret; \ } while (0) #define CHANOPEN_RETURN_SUCCESS(chan) do \ { \ ChanopenResult toret; \ toret.outcome = CHANOPEN_RESULT_SUCCESS; \ toret.u.success.channel = chan; \ return toret; \ } while (0) #define CHANOPEN_RETURN_DOWNSTREAM(shctx) do \ { \ ChanopenResult toret; \ toret.outcome = CHANOPEN_RESULT_DOWNSTREAM; \ toret.u.downstream.share_ctx = shctx; \ return toret; \ } while (0) ChanopenResult ssh2_connection_parse_channel_open( struct ssh2_connection_state *s, ptrlen type, PktIn *pktin, SshChannel *sc); bool ssh2_connection_parse_global_request( struct ssh2_connection_state *s, ptrlen type, PktIn *pktin); bool ssh2_connection_need_antispoof_prompt(struct ssh2_connection_state *s); #endif /* PUTTY_SSH2CONNECTION_H */ putty-0.76/ssh2kex-client.c0000644000175000017500000010774014072266312012577 00000000000000/* * Client side of key exchange for the SSH-2 transport protocol (RFC 4253). */ #include #include "putty.h" #include "ssh.h" #include "sshbpp.h" #include "sshppl.h" #include "sshcr.h" #include "storage.h" #include "ssh2transport.h" #include "mpint.h" /* * Another copy of the symbol defined in mpunsafe.c. See the comment * there. */ const int deliberate_symbol_clash = 12345; void ssh2kex_coroutine(struct ssh2_transport_state *s, bool *aborted) { PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */ PktIn *pktin; PktOut *pktout; crBegin(s->crStateKex); if (s->kex_alg->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... */ { int csbits, scbits; csbits = s->out.cipher ? s->out.cipher->real_keybits : 0; scbits = s->in.cipher ? s->in.cipher->real_keybits : 0; s->nbits = (csbits > scbits ? csbits : scbits); } /* The keys only have hlen-bit entropy, since they're based on * a hash. So cap the key size at hlen bits. */ if (s->nbits > s->kex_alg->hash->hlen * 8) s->nbits = s->kex_alg->hash->hlen * 8; /* * If we're doing Diffie-Hellman group exchange, start by * requesting a group. */ if (dh_is_gex(s->kex_alg)) { ppl_logevent("Doing Diffie-Hellman group exchange"); s->ppl.bpp->pls->kctx = SSH2_PKTCTX_DHGEX; /* * Work out how big a DH group we will need to allow that * much data. */ s->pbits = 512 << ((s->nbits - 1) / 64); if (s->pbits < DH_MIN_SIZE) s->pbits = DH_MIN_SIZE; if (s->pbits > DH_MAX_SIZE) s->pbits = DH_MAX_SIZE; if ((s->ppl.remote_bugs & BUG_SSH2_OLDGEX)) { pktout = ssh_bpp_new_pktout( s->ppl.bpp, SSH2_MSG_KEX_DH_GEX_REQUEST_OLD); put_uint32(pktout, s->pbits); } else { pktout = ssh_bpp_new_pktout( s->ppl.bpp, SSH2_MSG_KEX_DH_GEX_REQUEST); put_uint32(pktout, DH_MIN_SIZE); put_uint32(pktout, s->pbits); put_uint32(pktout, DH_MAX_SIZE); } pq_push(s->ppl.out_pq, pktout); crMaybeWaitUntilV((pktin = ssh2_transport_pop(s)) != NULL); if (pktin->type != SSH2_MSG_KEX_DH_GEX_GROUP) { ssh_proto_error(s->ppl.ssh, "Received unexpected packet when " "expecting Diffie-Hellman group, type %d (%s)", pktin->type, ssh2_pkt_type(s->ppl.bpp->pls->kctx, s->ppl.bpp->pls->actx, pktin->type)); *aborted = true; return; } s->p = get_mp_ssh2(pktin); s->g = get_mp_ssh2(pktin); if (get_err(pktin)) { ssh_proto_error(s->ppl.ssh, "Unable to parse Diffie-Hellman group packet"); *aborted = true; return; } s->dh_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; ppl_logevent("Doing Diffie-Hellman key exchange using %d-bit " "modulus and hash %s with a server-supplied group", dh_modulus_bit_size(s->dh_ctx), ssh_hash_alg(s->exhash)->text_name); } else { s->ppl.bpp->pls->kctx = SSH2_PKTCTX_DHGROUP; s->dh_ctx = dh_setup_group(s->kex_alg); s->kex_init_value = SSH2_MSG_KEXDH_INIT; s->kex_reply_value = SSH2_MSG_KEXDH_REPLY; ppl_logevent("Doing Diffie-Hellman key exchange using %d-bit " "modulus and hash %s with standard group \"%s\"", dh_modulus_bit_size(s->dh_ctx), ssh_hash_alg(s->exhash)->text_name, s->kex_alg->groupname); } /* * Now generate and send e for Diffie-Hellman. */ seat_set_busy_status(s->ppl.seat, BUSY_CPU); s->e = dh_create_e(s->dh_ctx, s->nbits * 2); pktout = ssh_bpp_new_pktout(s->ppl.bpp, s->kex_init_value); put_mp_ssh2(pktout, s->e); pq_push(s->ppl.out_pq, pktout); seat_set_busy_status(s->ppl.seat, BUSY_WAITING); crMaybeWaitUntilV((pktin = ssh2_transport_pop(s)) != NULL); if (pktin->type != s->kex_reply_value) { ssh_proto_error(s->ppl.ssh, "Received unexpected packet when " "expecting Diffie-Hellman reply, type %d (%s)", pktin->type, ssh2_pkt_type(s->ppl.bpp->pls->kctx, s->ppl.bpp->pls->actx, pktin->type)); *aborted = true; return; } seat_set_busy_status(s->ppl.seat, BUSY_CPU); s->hostkeydata = get_string(pktin); s->hkey = ssh_key_new_pub(s->hostkey_alg, s->hostkeydata); s->f = get_mp_ssh2(pktin); s->sigdata = get_string(pktin); if (get_err(pktin)) { ssh_proto_error(s->ppl.ssh, "Unable to parse Diffie-Hellman reply packet"); *aborted = true; return; } { const char *err = dh_validate_f(s->dh_ctx, s->f); if (err) { ssh_proto_error(s->ppl.ssh, "Diffie-Hellman reply failed " "validation: %s", err); *aborted = true; return; } } s->K = dh_find_K(s->dh_ctx, s->f); /* We assume everything from now on will be quick, and it might * involve user interaction. */ seat_set_busy_status(s->ppl.seat, BUSY_NOT); put_stringpl(s->exhash, s->hostkeydata); if (dh_is_gex(s->kex_alg)) { if (!(s->ppl.remote_bugs & BUG_SSH2_OLDGEX)) put_uint32(s->exhash, DH_MIN_SIZE); put_uint32(s->exhash, s->pbits); if (!(s->ppl.remote_bugs & BUG_SSH2_OLDGEX)) put_uint32(s->exhash, DH_MAX_SIZE); put_mp_ssh2(s->exhash, s->p); put_mp_ssh2(s->exhash, s->g); } put_mp_ssh2(s->exhash, s->e); put_mp_ssh2(s->exhash, s->f); dh_cleanup(s->dh_ctx); s->dh_ctx = NULL; mp_free(s->f); s->f = NULL; if (dh_is_gex(s->kex_alg)) { mp_free(s->g); s->g = NULL; mp_free(s->p); s->p = NULL; } } else if (s->kex_alg->main_type == KEXTYPE_ECDH) { ppl_logevent("Doing ECDH key exchange with curve %s and hash %s", ssh_ecdhkex_curve_textname(s->kex_alg), ssh_hash_alg(s->exhash)->text_name); s->ppl.bpp->pls->kctx = SSH2_PKTCTX_ECDHKEX; s->ecdh_key = ssh_ecdhkex_newkey(s->kex_alg); if (!s->ecdh_key) { ssh_sw_abort(s->ppl.ssh, "Unable to generate key for ECDH"); *aborted = true; return; } pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_KEX_ECDH_INIT); { strbuf *pubpoint = strbuf_new(); ssh_ecdhkex_getpublic(s->ecdh_key, BinarySink_UPCAST(pubpoint)); put_stringsb(pktout, pubpoint); } pq_push(s->ppl.out_pq, pktout); crMaybeWaitUntilV((pktin = ssh2_transport_pop(s)) != NULL); if (pktin->type != SSH2_MSG_KEX_ECDH_REPLY) { ssh_proto_error(s->ppl.ssh, "Received unexpected packet when " "expecting ECDH reply, type %d (%s)", pktin->type, ssh2_pkt_type(s->ppl.bpp->pls->kctx, s->ppl.bpp->pls->actx, pktin->type)); *aborted = true; return; } s->hostkeydata = get_string(pktin); put_stringpl(s->exhash, s->hostkeydata); s->hkey = ssh_key_new_pub(s->hostkey_alg, s->hostkeydata); { strbuf *pubpoint = strbuf_new(); ssh_ecdhkex_getpublic(s->ecdh_key, BinarySink_UPCAST(pubpoint)); put_string(s->exhash, pubpoint->u, pubpoint->len); strbuf_free(pubpoint); } { ptrlen keydata = get_string(pktin); put_stringpl(s->exhash, keydata); s->K = ssh_ecdhkex_getkey(s->ecdh_key, keydata); if (!get_err(pktin) && !s->K) { ssh_proto_error(s->ppl.ssh, "Received invalid elliptic curve " "point in ECDH reply"); *aborted = true; return; } } s->sigdata = get_string(pktin); if (get_err(pktin)) { ssh_proto_error(s->ppl.ssh, "Unable to parse ECDH reply packet"); *aborted = true; return; } ssh_ecdhkex_freekey(s->ecdh_key); s->ecdh_key = NULL; #ifndef NO_GSSAPI } else if (s->kex_alg->main_type == KEXTYPE_GSS) { ptrlen data; s->ppl.bpp->pls->kctx = SSH2_PKTCTX_GSSKEX; s->init_token_sent = false; s->complete_rcvd = false; s->hkey = NULL; s->keystr = NULL; /* * 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... * * This is rote from the KEXTYPE_DH section above. */ { int csbits, scbits; csbits = s->out.cipher->real_keybits; scbits = s->in.cipher->real_keybits; s->nbits = (csbits > scbits ? csbits : scbits); } /* The keys only have hlen-bit entropy, since they're based on * a hash. So cap the key size at hlen bits. */ if (s->nbits > s->kex_alg->hash->hlen * 8) s->nbits = s->kex_alg->hash->hlen * 8; if (dh_is_gex(s->kex_alg)) { /* * Work out how big a DH group we will need to allow that * much data. */ s->pbits = 512 << ((s->nbits - 1) / 64); ppl_logevent("Doing GSSAPI (with Kerberos V5) Diffie-Hellman " "group exchange, with minimum %d bits", s->pbits); pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_KEXGSS_GROUPREQ); put_uint32(pktout, s->pbits); /* min */ put_uint32(pktout, s->pbits); /* preferred */ put_uint32(pktout, s->pbits * 2); /* max */ pq_push(s->ppl.out_pq, pktout); crMaybeWaitUntilV( (pktin = ssh2_transport_pop(s)) != NULL); if (pktin->type != SSH2_MSG_KEXGSS_GROUP) { ssh_proto_error(s->ppl.ssh, "Received unexpected packet when " "expecting Diffie-Hellman group, type %d (%s)", pktin->type, ssh2_pkt_type(s->ppl.bpp->pls->kctx, s->ppl.bpp->pls->actx, pktin->type)); *aborted = true; return; } s->p = get_mp_ssh2(pktin); s->g = get_mp_ssh2(pktin); if (get_err(pktin)) { ssh_proto_error(s->ppl.ssh, "Unable to parse Diffie-Hellman group packet"); *aborted = true; return; } s->dh_ctx = dh_setup_gex(s->p, s->g); } else { s->dh_ctx = dh_setup_group(s->kex_alg); ppl_logevent("Using GSSAPI (with Kerberos V5) Diffie-Hellman with" " standard group \"%s\"", s->kex_alg->groupname); } ppl_logevent("Doing GSSAPI (with Kerberos V5) Diffie-Hellman key " "exchange with hash %s", ssh_hash_alg(s->exhash)->text_name); /* Now generate e for Diffie-Hellman. */ seat_set_busy_status(s->ppl.seat, BUSY_CPU); s->e = dh_create_e(s->dh_ctx, s->nbits * 2); if (s->shgss->lib->gsslogmsg) ppl_logevent("%s", s->shgss->lib->gsslogmsg); /* initial tokens are empty */ SSH_GSS_CLEAR_BUF(&s->gss_rcvtok); SSH_GSS_CLEAR_BUF(&s->gss_sndtok); SSH_GSS_CLEAR_BUF(&s->mic); s->gss_stat = s->shgss->lib->acquire_cred( s->shgss->lib, &s->shgss->ctx, &s->gss_cred_expiry); if (s->gss_stat != SSH_GSS_OK) { ssh_sw_abort(s->ppl.ssh, "GSSAPI key exchange failed to initialise"); *aborted = true; return; } /* now enter the loop */ assert(s->shgss->srv_name); do { /* * When acquire_cred yields no useful expiration, go with the * service ticket expiration. */ s->gss_stat = s->shgss->lib->init_sec_context( s->shgss->lib, &s->shgss->ctx, s->shgss->srv_name, s->gss_delegate, &s->gss_rcvtok, &s->gss_sndtok, (s->gss_cred_expiry == GSS_NO_EXPIRATION ? &s->gss_cred_expiry : NULL), NULL); SSH_GSS_CLEAR_BUF(&s->gss_rcvtok); if (s->gss_stat == SSH_GSS_S_COMPLETE && s->complete_rcvd) break; /* MIC is verified after the loop */ if (s->gss_stat != SSH_GSS_S_COMPLETE && s->gss_stat != SSH_GSS_S_CONTINUE_NEEDED) { if (s->shgss->lib->display_status( s->shgss->lib, s->shgss->ctx, &s->gss_buf) == SSH_GSS_OK) { char *err = s->gss_buf.value; ssh_sw_abort(s->ppl.ssh, "GSSAPI key exchange failed to initialise " "context: %s", err); sfree(err); *aborted = true; return; } } assert(s->gss_stat == SSH_GSS_S_COMPLETE || s->gss_stat == SSH_GSS_S_CONTINUE_NEEDED); if (!s->init_token_sent) { s->init_token_sent = true; pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_KEXGSS_INIT); if (s->gss_sndtok.length == 0) { ssh_sw_abort(s->ppl.ssh, "GSSAPI key exchange failed: " "no initial context token"); *aborted = true; return; } put_string(pktout, s->gss_sndtok.value, s->gss_sndtok.length); put_mp_ssh2(pktout, s->e); pq_push(s->ppl.out_pq, pktout); s->shgss->lib->free_tok(s->shgss->lib, &s->gss_sndtok); ppl_logevent("GSSAPI key exchange initialised"); } else if (s->gss_sndtok.length != 0) { pktout = ssh_bpp_new_pktout( s->ppl.bpp, SSH2_MSG_KEXGSS_CONTINUE); put_string(pktout, s->gss_sndtok.value, s->gss_sndtok.length); pq_push(s->ppl.out_pq, pktout); s->shgss->lib->free_tok(s->shgss->lib, &s->gss_sndtok); } if (s->gss_stat == SSH_GSS_S_COMPLETE && s->complete_rcvd) break; wait_for_gss_token: crMaybeWaitUntilV( (pktin = ssh2_transport_pop(s)) != NULL); switch (pktin->type) { case SSH2_MSG_KEXGSS_CONTINUE: data = get_string(pktin); s->gss_rcvtok.value = (char *)data.ptr; s->gss_rcvtok.length = data.len; continue; case SSH2_MSG_KEXGSS_COMPLETE: s->complete_rcvd = true; s->f = get_mp_ssh2(pktin); data = get_string(pktin); s->mic.value = (char *)data.ptr; s->mic.length = data.len; /* If there's a final token we loop to consume it */ if (get_bool(pktin)) { data = get_string(pktin); s->gss_rcvtok.value = (char *)data.ptr; s->gss_rcvtok.length = data.len; continue; } break; case SSH2_MSG_KEXGSS_HOSTKEY: s->hostkeydata = get_string(pktin); if (s->hostkey_alg) { s->hkey = ssh_key_new_pub(s->hostkey_alg, s->hostkeydata); put_stringpl(s->exhash, s->hostkeydata); } /* * Can't loop as we have no token to pass to * init_sec_context. */ goto wait_for_gss_token; case SSH2_MSG_KEXGSS_ERROR: /* * We have no use for the server's major and minor * status. The minor status is really only * meaningful to the server, and with luck the major * status means something to us (but not really all * that much). The string is more meaningful, and * hopefully the server sends any error tokens, as * that will produce the most useful information for * us. */ get_uint32(pktin); /* server's major status */ get_uint32(pktin); /* server's minor status */ data = get_string(pktin); ppl_logevent("GSSAPI key exchange failed; " "server's message: %.*s", PTRLEN_PRINTF(data)); /* Language tag, but we have no use for it */ get_string(pktin); /* * Wait for an error token, if there is one, or the * server's disconnect. The error token, if there * is one, must follow the SSH2_MSG_KEXGSS_ERROR * message, per the RFC. */ goto wait_for_gss_token; default: ssh_proto_error(s->ppl.ssh, "Received unexpected packet " "during GSSAPI key exchange, type %d (%s)", pktin->type, ssh2_pkt_type(s->ppl.bpp->pls->kctx, s->ppl.bpp->pls->actx, pktin->type)); *aborted = true; return; } } while (s->gss_rcvtok.length || s->gss_stat == SSH_GSS_S_CONTINUE_NEEDED || !s->complete_rcvd); { const char *err = dh_validate_f(s->dh_ctx, s->f); if (err) { ssh_proto_error(s->ppl.ssh, "GSSAPI reply failed " "validation: %s", err); *aborted = true; return; } } s->K = dh_find_K(s->dh_ctx, s->f); /* We assume everything from now on will be quick, and it might * involve user interaction. */ seat_set_busy_status(s->ppl.seat, BUSY_NOT); if (!s->hkey) put_stringz(s->exhash, ""); if (dh_is_gex(s->kex_alg)) { /* min, preferred, max */ put_uint32(s->exhash, s->pbits); put_uint32(s->exhash, s->pbits); put_uint32(s->exhash, s->pbits * 2); put_mp_ssh2(s->exhash, s->p); put_mp_ssh2(s->exhash, s->g); } put_mp_ssh2(s->exhash, s->e); put_mp_ssh2(s->exhash, s->f); /* * MIC verification is done below, after we compute the hash * used as the MIC input. */ dh_cleanup(s->dh_ctx); s->dh_ctx = NULL; mp_free(s->f); s->f = NULL; if (dh_is_gex(s->kex_alg)) { mp_free(s->g); s->g = NULL; mp_free(s->p); s->p = NULL; } #endif } else { ptrlen rsakeydata; assert(s->kex_alg->main_type == KEXTYPE_RSA); ppl_logevent("Doing RSA key exchange with hash %s", ssh_hash_alg(s->exhash)->text_name); s->ppl.bpp->pls->kctx = SSH2_PKTCTX_RSAKEX; /* * RSA key exchange. First expect a KEXRSA_PUBKEY packet * from the server. */ crMaybeWaitUntilV((pktin = ssh2_transport_pop(s)) != NULL); if (pktin->type != SSH2_MSG_KEXRSA_PUBKEY) { ssh_proto_error(s->ppl.ssh, "Received unexpected packet when " "expecting RSA public key, type %d (%s)", pktin->type, ssh2_pkt_type(s->ppl.bpp->pls->kctx, s->ppl.bpp->pls->actx, pktin->type)); *aborted = true; return; } s->hostkeydata = get_string(pktin); put_stringpl(s->exhash, s->hostkeydata); s->hkey = ssh_key_new_pub(s->hostkey_alg, s->hostkeydata); rsakeydata = get_string(pktin); s->rsa_kex_key = ssh_rsakex_newkey(rsakeydata); if (!s->rsa_kex_key) { ssh_proto_error(s->ppl.ssh, "Unable to parse RSA public key packet"); *aborted = true; return; } s->rsa_kex_key_needs_freeing = true; put_stringpl(s->exhash, rsakeydata); /* * Next, set up a shared secret K, of precisely KLEN - * 2*HLEN - 49 bits, where KLEN is the bit length of the * RSA key modulus and HLEN is the bit length of the hash * we're using. */ { int klen = ssh_rsakex_klen(s->rsa_kex_key); const struct ssh_rsa_kex_extra *extra = (const struct ssh_rsa_kex_extra *)s->kex_alg->extra; if (klen < extra->minklen) { ssh_proto_error(s->ppl.ssh, "Server sent %d-bit RSA key, " "less than the minimum size %d for %s " "key exchange", klen, extra->minklen, s->kex_alg->name); *aborted = true; return; } int nbits = klen - (2*s->kex_alg->hash->hlen*8 + 49); assert(nbits > 0); strbuf *buf, *outstr; mp_int *tmp = mp_random_bits(nbits - 1); s->K = mp_power_2(nbits - 1); mp_add_into(s->K, s->K, tmp); mp_free(tmp); /* * Encode this as an mpint. */ buf = strbuf_new_nm(); put_mp_ssh2(buf, s->K); /* * Encrypt it with the given RSA key. */ outstr = ssh_rsakex_encrypt(s->rsa_kex_key, s->kex_alg->hash, ptrlen_from_strbuf(buf)); /* * And send it off in a return packet. */ pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_KEXRSA_SECRET); put_stringpl(pktout, ptrlen_from_strbuf(outstr)); pq_push(s->ppl.out_pq, pktout); put_stringsb(s->exhash, outstr); /* frees outstr */ strbuf_free(buf); } ssh_rsakex_freekey(s->rsa_kex_key); s->rsa_kex_key = NULL; s->rsa_kex_key_needs_freeing = false; crMaybeWaitUntilV((pktin = ssh2_transport_pop(s)) != NULL); if (pktin->type != SSH2_MSG_KEXRSA_DONE) { ssh_proto_error(s->ppl.ssh, "Received unexpected packet when " "expecting RSA kex signature, type %d (%s)", pktin->type, ssh2_pkt_type(s->ppl.bpp->pls->kctx, s->ppl.bpp->pls->actx, pktin->type)); *aborted = true; return; } s->sigdata = get_string(pktin); if (get_err(pktin)) { ssh_proto_error(s->ppl.ssh, "Unable to parse RSA kex signature"); *aborted = true; return; } } ssh2transport_finalise_exhash(s); #ifndef NO_GSSAPI if (s->kex_alg->main_type == KEXTYPE_GSS) { Ssh_gss_buf gss_buf; SSH_GSS_CLEAR_BUF(&s->gss_buf); gss_buf.value = s->exchange_hash; gss_buf.length = s->kex_alg->hash->hlen; s->gss_stat = s->shgss->lib->verify_mic( s->shgss->lib, s->shgss->ctx, &gss_buf, &s->mic); if (s->gss_stat != SSH_GSS_OK) { if (s->shgss->lib->display_status( s->shgss->lib, s->shgss->ctx, &s->gss_buf) == SSH_GSS_OK) { char *err = s->gss_buf.value; ssh_sw_abort(s->ppl.ssh, "GSSAPI key exchange MIC was " "not valid: %s", err); sfree(err); } else { ssh_sw_abort(s->ppl.ssh, "GSSAPI key exchange MIC was " "not valid"); } *aborted = true; return; } s->gss_kex_used = true; /*- * If this the first KEX, save the GSS context for "gssapi-keyex" * authentication. * * http://tools.ietf.org/html/rfc4462#section-4 * * This method may be used only if the initial key exchange was * performed using a GSS-API-based key exchange method defined in * accordance with Section 2. The GSS-API context used with this * method is always that established during an initial GSS-API-based * key exchange. Any context established during key exchange for the * purpose of rekeying MUST NOT be used with this method. */ if (s->got_session_id) { s->shgss->lib->release_cred(s->shgss->lib, &s->shgss->ctx); } ppl_logevent("GSSAPI Key Exchange complete!"); } #endif s->dh_ctx = NULL; /* In GSS keyex there's no hostkey signature to verify */ if (s->kex_alg->main_type != KEXTYPE_GSS) { if (!s->hkey) { ssh_proto_error(s->ppl.ssh, "Server's host key is invalid"); *aborted = true; return; } if (!ssh_key_verify( s->hkey, s->sigdata, make_ptrlen(s->exchange_hash, s->kex_alg->hash->hlen))) { #ifndef FUZZING ssh_proto_error(s->ppl.ssh, "Signature from server's host key " "is invalid"); *aborted = true; return; #endif } } s->keystr = (s->hkey ? ssh_key_cache_str(s->hkey) : NULL); #ifndef NO_GSSAPI if (s->gss_kex_used) { /* * In a GSS-based session, check the host key (if any) against * the transient host key cache. */ if (s->kex_alg->main_type == KEXTYPE_GSS) { /* * We've just done a GSS key exchange. If it gave us a * host key, store it. */ if (s->hkey) { char *fingerprint = ssh2_fingerprint( s->hkey, SSH_FPTYPE_DEFAULT); ppl_logevent("GSS kex provided fallback host key:"); ppl_logevent("%s", fingerprint); sfree(fingerprint); ssh_transient_hostkey_cache_add(s->thc, s->hkey); } else if (!ssh_transient_hostkey_cache_non_empty(s->thc)) { /* * But if it didn't, then we currently have no * fallback host key to use in subsequent non-GSS * rekeys. So we should immediately trigger a non-GSS * rekey of our own, to set one up, before the session * keys have been used for anything else. * * This is similar to the cross-certification done at * user request in the permanent host key cache, but * here we do it automatically, once, at session * startup, and only add the key to the transient * cache. */ if (s->hostkey_alg) { s->need_gss_transient_hostkey = true; } else { /* * If we negotiated the "null" host key algorithm * in the key exchange, that's an indication that * no host key at all is available from the server * (both because we listed "null" last, and * because RFC 4462 section 5 says that a server * MUST NOT offer "null" as a host key algorithm * unless that is the only algorithm it provides * at all). * * In that case we actually _can't_ perform a * non-GSSAPI key exchange, so it's pointless to * attempt one proactively. This is also likely to * cause trouble later if a rekey is required at a * moment whne GSS credentials are not available, * but someone setting up a server in this * configuration presumably accepts that as a * consequence. */ if (!s->warned_about_no_gss_transient_hostkey) { ppl_logevent("No fallback host key available"); s->warned_about_no_gss_transient_hostkey = true; } } } } else { /* * We've just done a fallback key exchange, so make * sure the host key it used is in the cache of keys * we previously received in GSS kexes. * * An exception is if this was the non-GSS key exchange we * triggered on purpose to populate the transient cache. */ assert(s->hkey); /* only KEXTYPE_GSS lets this be null */ char *fingerprint = ssh2_fingerprint(s->hkey, SSH_FPTYPE_DEFAULT); if (s->need_gss_transient_hostkey) { ppl_logevent("Post-GSS rekey provided fallback host key:"); ppl_logevent("%s", fingerprint); ssh_transient_hostkey_cache_add(s->thc, s->hkey); s->need_gss_transient_hostkey = false; } else if (!ssh_transient_hostkey_cache_verify(s->thc, s->hkey)) { ppl_logevent("Non-GSS rekey after initial GSS kex " "used host key:"); ppl_logevent("%s", fingerprint); sfree(fingerprint); ssh_sw_abort(s->ppl.ssh, "Server's host key did not match any " "used in previous GSS kex"); *aborted = true; return; } sfree(fingerprint); } } else #endif /* NO_GSSAPI */ if (!s->got_session_id) { /* * Make a note of any other host key formats that are available. */ { int i, j, nkeys = 0; char *list = NULL; for (i = 0; i < lenof(ssh2_hostkey_algs); i++) { if (ssh2_hostkey_algs[i].alg == s->hostkey_alg) continue; for (j = 0; j < s->n_uncert_hostkeys; j++) if (s->uncert_hostkeys[j] == i) break; if (j < s->n_uncert_hostkeys) { char *newlist; if (list) newlist = dupprintf( "%s/%s", list, ssh2_hostkey_algs[i].alg->ssh_id); else newlist = dupprintf( "%s", ssh2_hostkey_algs[i].alg->ssh_id); sfree(list); list = newlist; nkeys++; } } if (list) { ppl_logevent("Server also has %s host key%s, but we " "don't know %s", list, nkeys > 1 ? "s" : "", nkeys > 1 ? "any of them" : "it"); sfree(list); } } /* * Authenticate remote host: verify host key. (We've already * checked the signature of the exchange hash.) */ char **fingerprints = ssh2_all_fingerprints(s->hkey); FingerprintType fptype_default = ssh2_pick_default_fingerprint(fingerprints); ppl_logevent("Host key fingerprint is:"); ppl_logevent("%s", fingerprints[fptype_default]); /* First check against manually configured host keys. */ s->dlgret = verify_ssh_manual_host_key( s->conf, fingerprints, s->hkey); if (s->dlgret == 0) { /* did not match */ ssh2_free_all_fingerprints(fingerprints); ssh_sw_abort(s->ppl.ssh, "Host key did not appear in manually " "configured list"); *aborted = true; return; } else if (s->dlgret < 0) { /* none configured; use standard handling */ ssh2_userkey uk = { .key = s->hkey, .comment = NULL }; char *keydisp = ssh2_pubkey_openssh_str(&uk); s->dlgret = seat_verify_ssh_host_key( s->ppl.seat, s->savedhost, s->savedport, ssh_key_cache_id(s->hkey), s->keystr, keydisp, fingerprints, ssh2_transport_dialog_callback, s); sfree(keydisp); ssh2_free_all_fingerprints(fingerprints); #ifdef FUZZING s->dlgret = 1; #endif crMaybeWaitUntilV(s->dlgret >= 0); if (s->dlgret == 0) { ssh_user_close(s->ppl.ssh, "User aborted at host key verification"); *aborted = true; return; } } /* * Save this host key, to check against the one presented in * subsequent rekeys. */ s->hostkey_str = s->keystr; s->keystr = NULL; } else if (s->cross_certifying) { assert(s->hkey); assert(ssh_key_alg(s->hkey) == s->cross_certifying); char *fingerprint = ssh2_fingerprint(s->hkey, SSH_FPTYPE_DEFAULT); ppl_logevent("Storing additional host key for this host:"); ppl_logevent("%s", fingerprint); sfree(fingerprint); store_host_key(s->savedhost, s->savedport, ssh_key_cache_id(s->hkey), s->keystr); /* * Don't forget to store the new key as the one we'll be * re-checking in future normal rekeys. */ s->hostkey_str = s->keystr; s->keystr = NULL; } 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. */ assert(s->keystr); /* filled in by prior key exchange */ if (strcmp(s->hostkey_str, s->keystr)) { #ifndef FUZZING ssh_sw_abort(s->ppl.ssh, "Host key was different in repeat key exchange"); *aborted = true; return; #endif } } sfree(s->keystr); s->keystr = NULL; if (s->hkey) { ssh_key_free(s->hkey); s->hkey = NULL; } crFinishV; } putty-0.76/ssh2kex-server.c0000644000175000017500000002745614072266312012634 00000000000000/* * Server side of key exchange for the SSH-2 transport protocol (RFC 4253). */ #include #include "putty.h" #include "ssh.h" #include "sshbpp.h" #include "sshppl.h" #include "sshcr.h" #include "sshserver.h" #include "sshkeygen.h" #include "storage.h" #include "ssh2transport.h" #include "mpint.h" void ssh2_transport_provide_hostkeys(PacketProtocolLayer *ppl, ssh_key *const *hostkeys, int nhostkeys) { struct ssh2_transport_state *s = container_of(ppl, struct ssh2_transport_state, ppl); s->hostkeys = hostkeys; s->nhostkeys = nhostkeys; } static strbuf *finalise_and_sign_exhash(struct ssh2_transport_state *s) { strbuf *sb; ssh2transport_finalise_exhash(s); sb = strbuf_new(); ssh_key_sign( s->hkey, make_ptrlen(s->exchange_hash, s->kex_alg->hash->hlen), s->hkflags, BinarySink_UPCAST(sb)); return sb; } void ssh2kex_coroutine(struct ssh2_transport_state *s, bool *aborted) { PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */ PktIn *pktin; PktOut *pktout; crBegin(s->crStateKex); { int i; for (i = 0; i < s->nhostkeys; i++) if (ssh_key_alg(s->hostkeys[i]) == s->hostkey_alg) { s->hkey = s->hostkeys[i]; break; } assert(s->hkey); } strbuf_clear(s->hostkeyblob); ssh_key_public_blob(s->hkey, BinarySink_UPCAST(s->hostkeyblob)); s->hostkeydata = ptrlen_from_strbuf(s->hostkeyblob); put_stringpl(s->exhash, s->hostkeydata); if (s->kex_alg->main_type == KEXTYPE_DH) { /* * If we're doing Diffie-Hellman group exchange, start by * waiting for the group request. */ if (dh_is_gex(s->kex_alg)) { ppl_logevent("Doing Diffie-Hellman group exchange"); s->ppl.bpp->pls->kctx = SSH2_PKTCTX_DHGEX; crMaybeWaitUntilV((pktin = ssh2_transport_pop(s)) != NULL); if (pktin->type != SSH2_MSG_KEX_DH_GEX_REQUEST && pktin->type != SSH2_MSG_KEX_DH_GEX_REQUEST_OLD) { ssh_proto_error(s->ppl.ssh, "Received unexpected packet when " "expecting Diffie-Hellman group exchange " "request, type %d (%s)", pktin->type, ssh2_pkt_type(s->ppl.bpp->pls->kctx, s->ppl.bpp->pls->actx, pktin->type)); *aborted = true; return; } if (pktin->type != SSH2_MSG_KEX_DH_GEX_REQUEST_OLD) { s->dh_got_size_bounds = true; s->dh_min_size = get_uint32(pktin); s->pbits = get_uint32(pktin); s->dh_max_size = get_uint32(pktin); } else { s->dh_got_size_bounds = false; s->pbits = get_uint32(pktin); } /* * This is a hopeless strategy for making a secure DH * group! It's good enough for testing a client against, * but not for serious use. */ PrimeGenerationContext *pgc = primegen_new_context( &primegen_probabilistic); ProgressReceiver null_progress; null_progress.vt = &null_progress_vt; s->p = primegen_generate(pgc, pcs_new(s->pbits), &null_progress); primegen_free_context(pgc); s->g = mp_from_integer(2); s->dh_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; pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_KEX_DH_GEX_GROUP); put_mp_ssh2(pktout, s->p); put_mp_ssh2(pktout, s->g); pq_push(s->ppl.out_pq, pktout); } else { s->ppl.bpp->pls->kctx = SSH2_PKTCTX_DHGROUP; s->dh_ctx = dh_setup_group(s->kex_alg); s->kex_init_value = SSH2_MSG_KEXDH_INIT; s->kex_reply_value = SSH2_MSG_KEXDH_REPLY; ppl_logevent("Using Diffie-Hellman with standard group \"%s\"", s->kex_alg->groupname); } ppl_logevent("Doing Diffie-Hellman key exchange with hash %s", ssh_hash_alg(s->exhash)->text_name); /* * Generate e for Diffie-Hellman. */ s->e = dh_create_e(s->dh_ctx, s->nbits * 2); /* * Wait to receive f. */ crMaybeWaitUntilV((pktin = ssh2_transport_pop(s)) != NULL); if (pktin->type != s->kex_init_value) { ssh_proto_error(s->ppl.ssh, "Received unexpected packet when " "expecting Diffie-Hellman initial packet, " "type %d (%s)", pktin->type, ssh2_pkt_type(s->ppl.bpp->pls->kctx, s->ppl.bpp->pls->actx, pktin->type)); *aborted = true; return; } s->f = get_mp_ssh2(pktin); if (get_err(pktin)) { ssh_proto_error(s->ppl.ssh, "Unable to parse Diffie-Hellman initial packet"); *aborted = true; return; } { const char *err = dh_validate_f(s->dh_ctx, s->f); if (err) { ssh_proto_error(s->ppl.ssh, "Diffie-Hellman initial packet " "failed validation: %s", err); *aborted = true; return; } } s->K = dh_find_K(s->dh_ctx, s->f); if (dh_is_gex(s->kex_alg)) { if (s->dh_got_size_bounds) put_uint32(s->exhash, s->dh_min_size); put_uint32(s->exhash, s->pbits); if (s->dh_got_size_bounds) put_uint32(s->exhash, s->dh_max_size); put_mp_ssh2(s->exhash, s->p); put_mp_ssh2(s->exhash, s->g); } put_mp_ssh2(s->exhash, s->f); put_mp_ssh2(s->exhash, s->e); pktout = ssh_bpp_new_pktout(s->ppl.bpp, s->kex_reply_value); put_stringpl(pktout, s->hostkeydata); put_mp_ssh2(pktout, s->e); put_stringsb(pktout, finalise_and_sign_exhash(s)); pq_push(s->ppl.out_pq, pktout); dh_cleanup(s->dh_ctx); s->dh_ctx = NULL; mp_free(s->f); s->f = NULL; if (dh_is_gex(s->kex_alg)) { mp_free(s->g); s->g = NULL; mp_free(s->p); s->p = NULL; } } else if (s->kex_alg->main_type == KEXTYPE_ECDH) { ppl_logevent("Doing ECDH key exchange with curve %s and hash %s", ssh_ecdhkex_curve_textname(s->kex_alg), ssh_hash_alg(s->exhash)->text_name); s->ppl.bpp->pls->kctx = SSH2_PKTCTX_ECDHKEX; s->ecdh_key = ssh_ecdhkex_newkey(s->kex_alg); if (!s->ecdh_key) { ssh_sw_abort(s->ppl.ssh, "Unable to generate key for ECDH"); *aborted = true; return; } crMaybeWaitUntilV((pktin = ssh2_transport_pop(s)) != NULL); if (pktin->type != SSH2_MSG_KEX_ECDH_INIT) { ssh_proto_error(s->ppl.ssh, "Received unexpected packet when " "expecting ECDH initial packet, type %d (%s)", pktin->type, ssh2_pkt_type(s->ppl.bpp->pls->kctx, s->ppl.bpp->pls->actx, pktin->type)); *aborted = true; return; } { ptrlen keydata = get_string(pktin); put_stringpl(s->exhash, keydata); s->K = ssh_ecdhkex_getkey(s->ecdh_key, keydata); if (!get_err(pktin) && !s->K) { ssh_proto_error(s->ppl.ssh, "Received invalid elliptic curve " "point in ECDH initial packet"); *aborted = true; return; } } pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_KEX_ECDH_REPLY); put_stringpl(pktout, s->hostkeydata); { strbuf *pubpoint = strbuf_new(); ssh_ecdhkex_getpublic(s->ecdh_key, BinarySink_UPCAST(pubpoint)); put_string(s->exhash, pubpoint->u, pubpoint->len); put_stringsb(pktout, pubpoint); } put_stringsb(pktout, finalise_and_sign_exhash(s)); pq_push(s->ppl.out_pq, pktout); ssh_ecdhkex_freekey(s->ecdh_key); s->ecdh_key = NULL; } else if (s->kex_alg->main_type == KEXTYPE_GSS) { ssh_sw_abort(s->ppl.ssh, "GSS key exchange not supported in server"); } else { assert(s->kex_alg->main_type == KEXTYPE_RSA); ppl_logevent("Doing RSA key exchange with hash %s", ssh_hash_alg(s->exhash)->text_name); s->ppl.bpp->pls->kctx = SSH2_PKTCTX_RSAKEX; const struct ssh_rsa_kex_extra *extra = (const struct ssh_rsa_kex_extra *)s->kex_alg->extra; if (s->ssc && s->ssc->rsa_kex_key) { int klen = ssh_rsakex_klen(s->ssc->rsa_kex_key); if (klen >= extra->minklen) { ppl_logevent("Using configured %d-bit RSA key", klen); s->rsa_kex_key = s->ssc->rsa_kex_key; } else { ppl_logevent("Configured %d-bit RSA key is too short (min %d)", klen, extra->minklen); } } if (!s->rsa_kex_key) { ppl_logevent("Generating a %d-bit RSA key", extra->minklen); s->rsa_kex_key = snew(RSAKey); PrimeGenerationContext *pgc = primegen_new_context( &primegen_probabilistic); ProgressReceiver null_progress; null_progress.vt = &null_progress_vt; rsa_generate(s->rsa_kex_key, extra->minklen, false, pgc, &null_progress); primegen_free_context(pgc); s->rsa_kex_key->comment = NULL; s->rsa_kex_key_needs_freeing = true; } pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_KEXRSA_PUBKEY); put_stringpl(pktout, s->hostkeydata); { strbuf *pubblob = strbuf_new(); ssh_key_public_blob(&s->rsa_kex_key->sshk, BinarySink_UPCAST(pubblob)); put_string(s->exhash, pubblob->u, pubblob->len); put_stringsb(pktout, pubblob); } pq_push(s->ppl.out_pq, pktout); crMaybeWaitUntilV((pktin = ssh2_transport_pop(s)) != NULL); if (pktin->type != SSH2_MSG_KEXRSA_SECRET) { ssh_proto_error(s->ppl.ssh, "Received unexpected packet when " "expecting RSA kex secret, type %d (%s)", pktin->type, ssh2_pkt_type(s->ppl.bpp->pls->kctx, s->ppl.bpp->pls->actx, pktin->type)); *aborted = true; return; } { ptrlen encrypted_secret = get_string(pktin); put_stringpl(s->exhash, encrypted_secret); s->K = ssh_rsakex_decrypt( s->rsa_kex_key, s->kex_alg->hash, encrypted_secret); } if (!s->K) { ssh_proto_error(s->ppl.ssh, "Unable to decrypt RSA kex secret"); *aborted = true; return; } if (s->rsa_kex_key_needs_freeing) { ssh_rsakex_freekey(s->rsa_kex_key); sfree(s->rsa_kex_key); } s->rsa_kex_key = NULL; s->rsa_kex_key_needs_freeing = false; pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_KEXRSA_DONE); put_stringsb(pktout, finalise_and_sign_exhash(s)); pq_push(s->ppl.out_pq, pktout); } crFinishV; } putty-0.76/ssh2transhk.c0000644000175000017500000001002714072266312012175 00000000000000/* * Data structure managing host keys in sessions based on GSSAPI KEX. * * In a session we started with a GSSAPI key exchange, the concept of * 'host key' has completely different lifetime and security semantics * from the usual ones. Per RFC 4462 section 2.1, we assume that any * host key delivered to us in the course of a GSSAPI key exchange is * _solely_ there to use as a transient fallback within the same * session, if at the time of a subsequent rekey the GSS credentials * are temporarily invalid and so a non-GSS KEX method has to be used. * * In particular, in a GSS-based SSH deployment, host keys may not * even _be_ persistent identities for the server; it would be * legitimate for a server to generate a fresh one routinely if it * wanted to, like SSH-1 server keys. * * So, in this mode, we never touch the persistent host key cache at * all, either to check keys against it _or_ to store keys in it. * Instead, we maintain an in-memory cache of host keys that have been * mentioned in GSS key exchanges within this particular session, and * we permit precisely those host keys in non-GSS rekeys. */ #include #include "putty.h" #include "ssh.h" struct ssh_transient_hostkey_cache { tree234 *cache; }; struct ssh_transient_hostkey_cache_entry { const ssh_keyalg *alg; strbuf *pub_blob; }; static int ssh_transient_hostkey_cache_cmp(void *av, void *bv) { const struct ssh_transient_hostkey_cache_entry *a = (const struct ssh_transient_hostkey_cache_entry *)av, *b = (const struct ssh_transient_hostkey_cache_entry *)bv; return strcmp(a->alg->ssh_id, b->alg->ssh_id); } static int ssh_transient_hostkey_cache_find(void *av, void *bv) { const ssh_keyalg *aalg = (const ssh_keyalg *)av; const struct ssh_transient_hostkey_cache_entry *b = (const struct ssh_transient_hostkey_cache_entry *)bv; return strcmp(aalg->ssh_id, b->alg->ssh_id); } ssh_transient_hostkey_cache *ssh_transient_hostkey_cache_new(void) { ssh_transient_hostkey_cache *thc = snew(ssh_transient_hostkey_cache); thc->cache = newtree234(ssh_transient_hostkey_cache_cmp); return thc; } void ssh_transient_hostkey_cache_free(ssh_transient_hostkey_cache *thc) { struct ssh_transient_hostkey_cache_entry *ent; while ((ent = delpos234(thc->cache, 0)) != NULL) { strbuf_free(ent->pub_blob); sfree(ent); } freetree234(thc->cache); sfree(thc); } void ssh_transient_hostkey_cache_add( ssh_transient_hostkey_cache *thc, ssh_key *key) { struct ssh_transient_hostkey_cache_entry *ent, *retd; if ((ent = find234(thc->cache, (void *)ssh_key_alg(key), ssh_transient_hostkey_cache_find)) != NULL) { del234(thc->cache, ent); strbuf_free(ent->pub_blob); sfree(ent); } ent = snew(struct ssh_transient_hostkey_cache_entry); ent->alg = ssh_key_alg(key); ent->pub_blob = strbuf_new(); ssh_key_public_blob(key, BinarySink_UPCAST(ent->pub_blob)); retd = add234(thc->cache, ent); assert(retd == ent); } bool ssh_transient_hostkey_cache_verify( ssh_transient_hostkey_cache *thc, ssh_key *key) { struct ssh_transient_hostkey_cache_entry *ent; bool toret = false; if ((ent = find234(thc->cache, (void *)ssh_key_alg(key), ssh_transient_hostkey_cache_find)) != NULL) { strbuf *this_blob = strbuf_new(); ssh_key_public_blob(key, BinarySink_UPCAST(this_blob)); if (this_blob->len == ent->pub_blob->len && !memcmp(this_blob->s, ent->pub_blob->s, this_blob->len)) toret = true; strbuf_free(this_blob); } return toret; } bool ssh_transient_hostkey_cache_has( ssh_transient_hostkey_cache *thc, const ssh_keyalg *alg) { struct ssh_transient_hostkey_cache_entry *ent = find234(thc->cache, (void *)alg, ssh_transient_hostkey_cache_find); return ent != NULL; } bool ssh_transient_hostkey_cache_non_empty(ssh_transient_hostkey_cache *thc) { return count234(thc->cache) > 0; } putty-0.76/ssh2transport.c0000644000175000017500000023072414072266312012567 00000000000000/* * Packet protocol layer for the SSH-2 transport protocol (RFC 4253). */ #include #include "putty.h" #include "ssh.h" #include "sshbpp.h" #include "sshppl.h" #include "sshcr.h" #include "sshserver.h" #include "storage.h" #include "ssh2transport.h" #include "mpint.h" const struct ssh_signkey_with_user_pref_id ssh2_hostkey_algs[] = { #define ARRAYENT_HOSTKEY_ALGORITHM(type, alg) { &alg, type }, HOSTKEY_ALGORITHMS(ARRAYENT_HOSTKEY_ALGORITHM) }; const static ssh2_macalg *const macs[] = { &ssh_hmac_sha256, &ssh_hmac_sha1, &ssh_hmac_sha1_96, &ssh_hmac_md5 }; const static ssh2_macalg *const buggymacs[] = { &ssh_hmac_sha1_buggy, &ssh_hmac_sha1_96_buggy, &ssh_hmac_md5 }; static ssh_compressor *ssh_comp_none_init(void) { return NULL; } static void ssh_comp_none_cleanup(ssh_compressor *handle) { } static ssh_decompressor *ssh_decomp_none_init(void) { return NULL; } static void ssh_decomp_none_cleanup(ssh_decompressor *handle) { } static void ssh_comp_none_block(ssh_compressor *handle, const unsigned char *block, int len, unsigned char **outblock, int *outlen, int minlen) { } static bool ssh_decomp_none_block(ssh_decompressor *handle, const unsigned char *block, int len, unsigned char **outblock, int *outlen) { return false; } static const ssh_compression_alg ssh_comp_none = { .name = "none", .delayed_name = NULL, .compress_new = ssh_comp_none_init, .compress_free = ssh_comp_none_cleanup, .compress = ssh_comp_none_block, .decompress_new = ssh_decomp_none_init, .decompress_free = ssh_decomp_none_cleanup, .decompress = ssh_decomp_none_block, .text_name = NULL, }; const static ssh_compression_alg *const compressions[] = { &ssh_zlib, &ssh_comp_none }; static void ssh2_transport_free(PacketProtocolLayer *); static void ssh2_transport_process_queue(PacketProtocolLayer *); static bool ssh2_transport_get_specials( PacketProtocolLayer *ppl, add_special_fn_t add_special, void *ctx); static void ssh2_transport_special_cmd(PacketProtocolLayer *ppl, SessionSpecialCode code, int arg); static bool ssh2_transport_want_user_input(PacketProtocolLayer *ppl); static void ssh2_transport_got_user_input(PacketProtocolLayer *ppl); static void ssh2_transport_reconfigure(PacketProtocolLayer *ppl, Conf *conf); static size_t ssh2_transport_queued_data_size(PacketProtocolLayer *ppl); static void ssh2_transport_set_max_data_size(struct ssh2_transport_state *s); static unsigned long sanitise_rekey_time(int rekey_time, unsigned long def); static void ssh2_transport_higher_layer_packet_callback(void *context); static const PacketProtocolLayerVtable ssh2_transport_vtable = { .free = ssh2_transport_free, .process_queue = ssh2_transport_process_queue, .get_specials = ssh2_transport_get_specials, .special_cmd = ssh2_transport_special_cmd, .want_user_input = ssh2_transport_want_user_input, .got_user_input = ssh2_transport_got_user_input, .reconfigure = ssh2_transport_reconfigure, .queued_data_size = ssh2_transport_queued_data_size, .name = NULL, /* no protocol name for this layer */ }; #ifndef NO_GSSAPI static void ssh2_transport_gss_update(struct ssh2_transport_state *s, bool definitely_rekeying); #endif static bool ssh2_transport_timer_update(struct ssh2_transport_state *s, unsigned long rekey_time); static int ssh2_transport_confirm_weak_crypto_primitive( struct ssh2_transport_state *s, const char *type, const char *name, const void *alg); static const char *const kexlist_descr[NKEXLIST] = { "key exchange algorithm", "host key algorithm", "client-to-server cipher", "server-to-client cipher", "client-to-server MAC", "server-to-client MAC", "client-to-server compression method", "server-to-client compression method" }; static int weak_algorithm_compare(void *av, void *bv); PacketProtocolLayer *ssh2_transport_new( Conf *conf, const char *host, int port, const char *fullhostname, const char *client_greeting, const char *server_greeting, struct ssh_connection_shared_gss_state *shgss, struct DataTransferStats *stats, PacketProtocolLayer *higher_layer, const SshServerConfig *ssc) { struct ssh2_transport_state *s = snew(struct ssh2_transport_state); memset(s, 0, sizeof(*s)); s->ppl.vt = &ssh2_transport_vtable; s->conf = conf_copy(conf); s->savedhost = dupstr(host); s->savedport = port; s->fullhostname = dupstr(fullhostname); s->shgss = shgss; s->client_greeting = dupstr(client_greeting); s->server_greeting = dupstr(server_greeting); s->stats = stats; s->hostkeyblob = strbuf_new(); pq_in_init(&s->pq_in_higher); pq_out_init(&s->pq_out_higher); s->pq_out_higher.pqb.ic = &s->ic_pq_out_higher; s->ic_pq_out_higher.fn = ssh2_transport_higher_layer_packet_callback; s->ic_pq_out_higher.ctx = &s->ppl; s->higher_layer = higher_layer; s->higher_layer->selfptr = &s->higher_layer; ssh_ppl_setup_queues(s->higher_layer, &s->pq_in_higher, &s->pq_out_higher); #ifndef NO_GSSAPI s->gss_cred_expiry = GSS_NO_EXPIRATION; s->shgss->srv_name = GSS_C_NO_NAME; s->shgss->ctx = NULL; #endif s->thc = ssh_transient_hostkey_cache_new(); s->gss_kex_used = false; s->outgoing_kexinit = strbuf_new(); s->incoming_kexinit = strbuf_new(); if (ssc) { s->ssc = ssc; s->client_kexinit = s->incoming_kexinit; s->server_kexinit = s->outgoing_kexinit; s->cstrans = &s->in; s->sctrans = &s->out; s->out.mkkey_adjust = 1; } else { s->client_kexinit = s->outgoing_kexinit; s->server_kexinit = s->incoming_kexinit; s->cstrans = &s->out; s->sctrans = &s->in; s->in.mkkey_adjust = 1; } s->weak_algorithms_consented_to = newtree234(weak_algorithm_compare); ssh2_transport_set_max_data_size(s); return &s->ppl; } static void ssh2_transport_free(PacketProtocolLayer *ppl) { struct ssh2_transport_state *s = container_of(ppl, struct ssh2_transport_state, ppl); /* * As our last act before being freed, move any outgoing packets * off our higher layer's output queue on to our own output queue. * We might be being freed while the SSH connection is still alive * (because we're initiating shutdown from our end), in which case * we don't want those last few packets to get lost. * * (If our owner were to have already destroyed our output pq * before wanting to free us, then it would have to reset our * publicly visible out_pq field to NULL to inhibit this attempt. * But that's not how I expect the shutdown sequence to go in * practice.) */ if (s->ppl.out_pq) pq_concatenate(s->ppl.out_pq, s->ppl.out_pq, &s->pq_out_higher); conf_free(s->conf); ssh_ppl_free(s->higher_layer); pq_in_clear(&s->pq_in_higher); pq_out_clear(&s->pq_out_higher); sfree(s->savedhost); sfree(s->fullhostname); sfree(s->client_greeting); sfree(s->server_greeting); sfree(s->keystr); sfree(s->hostkey_str); strbuf_free(s->hostkeyblob); if (s->hkey && !s->hostkeys) { ssh_key_free(s->hkey); s->hkey = NULL; } if (s->f) mp_free(s->f); if (s->p) mp_free(s->p); if (s->g) mp_free(s->g); if (s->K) mp_free(s->K); if (s->dh_ctx) dh_cleanup(s->dh_ctx); if (s->rsa_kex_key_needs_freeing) { ssh_rsakex_freekey(s->rsa_kex_key); sfree(s->rsa_kex_key); } if (s->ecdh_key) ssh_ecdhkex_freekey(s->ecdh_key); if (s->exhash) ssh_hash_free(s->exhash); strbuf_free(s->outgoing_kexinit); strbuf_free(s->incoming_kexinit); ssh_transient_hostkey_cache_free(s->thc); freetree234(s->weak_algorithms_consented_to); expire_timer_context(s); sfree(s); } /* * SSH-2 key derivation (RFC 4253 section 7.2). */ static void ssh2_mkkey( struct ssh2_transport_state *s, strbuf *out, mp_int *K, unsigned char *H, char chr, int keylen) { int hlen = s->kex_alg->hash->hlen; int keylen_padded; unsigned char *key; ssh_hash *h; if (keylen == 0) return; /* * Round the requested amount of key material up to a multiple of * the length of the hash we're using to make it. This makes life * simpler because then we can just write each hash output block * straight into the output buffer without fiddling about * truncating the last one. Since it's going into a strbuf, and * strbufs are always smemclr()ed on free, there's no need to * worry about leaving extra potentially-sensitive data in memory * that the caller didn't ask for. */ keylen_padded = ((keylen + hlen - 1) / hlen) * hlen; strbuf_clear(out); key = strbuf_append(out, keylen_padded); /* First hlen bytes. */ h = ssh_hash_new(s->kex_alg->hash); if (!(s->ppl.remote_bugs & BUG_SSH2_DERIVEKEY)) put_mp_ssh2(h, K); put_data(h, H, hlen); put_byte(h, chr); put_data(h, s->session_id, s->session_id_len); ssh_hash_digest(h, key); /* Subsequent blocks of hlen bytes. */ if (keylen_padded > hlen) { int offset; ssh_hash_reset(h); if (!(s->ppl.remote_bugs & BUG_SSH2_DERIVEKEY)) put_mp_ssh2(h, K); put_data(h, H, hlen); for (offset = hlen; offset < keylen_padded; offset += hlen) { put_data(h, key + offset - hlen, hlen); ssh_hash_digest_nondestructive(h, key + offset); } } ssh_hash_free(h); } /* * Find a slot in a KEXINIT algorithm list to use for a new algorithm. * If the algorithm is already in the list, return a pointer to its * entry, otherwise return an entry from the end of the list. * This assumes that every time a particular name is passed in, it * comes from the same string constant. If this isn't true, this * function may need to be rewritten to use strcmp() instead. */ static struct kexinit_algorithm *ssh2_kexinit_addalg(struct kexinit_algorithm *list, const char *name) { int i; for (i = 0; i < MAXKEXLIST; i++) if (list[i].name == NULL || list[i].name == name) { list[i].name = name; return &list[i]; } unreachable("Should never run out of space in KEXINIT list"); } bool ssh2_common_filter_queue(PacketProtocolLayer *ppl) { 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", }; PktIn *pktin; ptrlen msg; int reason; while ((pktin = pq_peek(ppl->in_pq)) != NULL) { switch (pktin->type) { case SSH2_MSG_DISCONNECT: reason = get_uint32(pktin); msg = get_string(pktin); ssh_remote_error( ppl->ssh, "Remote side sent disconnect message\n" "type %d (%s):\n\"%.*s\"", reason, ((reason > 0 && reason < lenof(ssh2_disconnect_reasons)) ? ssh2_disconnect_reasons[reason] : "unknown"), PTRLEN_PRINTF(msg)); /* don't try to pop the queue, because we've been freed! */ return true; /* indicate that we've been freed */ case SSH2_MSG_DEBUG: /* XXX maybe we should actually take notice of the return value */ get_bool(pktin); msg = get_string(pktin); ppl_logevent("Remote debug message: %.*s", PTRLEN_PRINTF(msg)); pq_pop(ppl->in_pq); break; case SSH2_MSG_IGNORE: /* Do nothing, because we're ignoring it! Duhh. */ pq_pop(ppl->in_pq); break; case SSH2_MSG_EXT_INFO: { /* * The BPP enforces that these turn up only at legal * points in the protocol. In particular, it will not pass * an EXT_INFO on to us if it arrives before encryption is * enabled (which is when a MITM could inject one * maliciously). * * However, one of the criteria for legality is that a * server is permitted to send this message immediately * _before_ USERAUTH_SUCCESS. So we may receive this * message not yet knowing whether it's legal to have sent * it - we won't know until the BPP processes the next * packet. * * But that should be OK, because firstly, an * out-of-sequence EXT_INFO that's still within the * encrypted session is only a _protocol_ violation, not * an attack; secondly, any data we set in response to * such an illegal EXT_INFO won't have a chance to affect * the session before the BPP aborts it anyway. */ uint32_t nexts = get_uint32(pktin); for (uint32_t i = 0; i < nexts && !get_err(pktin); i++) { ptrlen extname = get_string(pktin); ptrlen extvalue = get_string(pktin); if (ptrlen_eq_string(extname, "server-sig-algs")) { /* * Server has sent a list of signature algorithms * it will potentially accept for user * authentication keys. Check in particular * whether the RFC 8332 improved versions of * ssh-rsa are in the list, and set flags in the * BPP if so. * * TODO: another thing we _could_ do here is to * record a full list of the algorithm identifiers * we've seen, whether we understand them * ourselves or not. Then we could use that as a * pre-filter during userauth, to skip keys in the * SSH agent if we already know the server can't * possibly accept them. (Even if the key * algorithm is one that the agent and the server * both understand but we do not.) */ ptrlen algname; while (get_commasep_word(&extvalue, &algname)) { if (ptrlen_eq_string(algname, "rsa-sha2-256")) ppl->bpp->ext_info_rsa_sha256_ok = true; if (ptrlen_eq_string(algname, "rsa-sha2-512")) ppl->bpp->ext_info_rsa_sha512_ok = true; } } } pq_pop(ppl->in_pq); break; } default: return false; } } return false; } static bool ssh2_transport_filter_queue(struct ssh2_transport_state *s) { PktIn *pktin; while (1) { if (ssh2_common_filter_queue(&s->ppl)) return true; if ((pktin = pq_peek(s->ppl.in_pq)) == NULL) return false; /* Pass on packets to the next layer if they're outside * the range reserved for the transport protocol. */ if (pktin->type >= 50) { /* ... except that we shouldn't tolerate higher-layer * packets coming from the server before we've seen * the first NEWKEYS. */ if (!s->higher_layer_ok) { ssh_proto_error(s->ppl.ssh, "Received premature higher-" "layer packet, type %d (%s)", pktin->type, ssh2_pkt_type(s->ppl.bpp->pls->kctx, s->ppl.bpp->pls->actx, pktin->type)); return true; } pq_pop(s->ppl.in_pq); pq_push(&s->pq_in_higher, pktin); } else { /* Anything else is a transport-layer packet that the main * process_queue coroutine should handle. */ return false; } } } PktIn *ssh2_transport_pop(struct ssh2_transport_state *s) { if (ssh2_transport_filter_queue(s)) return NULL; /* we've been freed */ return pq_pop(s->ppl.in_pq); } static void ssh2_write_kexinit_lists( BinarySink *pktout, struct kexinit_algorithm kexlists[NKEXLIST][MAXKEXLIST], Conf *conf, const SshServerConfig *ssc, int remote_bugs, const char *hk_host, int hk_port, const ssh_keyalg *hk_prev, ssh_transient_hostkey_cache *thc, ssh_key *const *our_hostkeys, int our_nhostkeys, bool first_time, bool can_gssapi_keyex, bool transient_hostkey_mode) { int i, j, k; bool warn; int n_preferred_kex; const ssh_kexes *preferred_kex[KEX_MAX + 1]; /* +1 for GSSAPI */ int n_preferred_hk; int preferred_hk[HK_MAX]; int n_preferred_ciphers; const ssh2_ciphers *preferred_ciphers[CIPHER_MAX]; const ssh_compression_alg *preferred_comp; const ssh2_macalg *const *maclist; int nmacs; struct kexinit_algorithm *alg; /* * Set up the preferred key exchange. (NULL => warn below here) */ n_preferred_kex = 0; if (can_gssapi_keyex) preferred_kex[n_preferred_kex++] = &ssh_gssk5_sha1_kex; for (i = 0; i < KEX_MAX; i++) { switch (conf_get_int_int(conf, CONF_ssh_kexlist, i)) { case KEX_DHGEX: preferred_kex[n_preferred_kex++] = &ssh_diffiehellman_gex; break; case KEX_DHGROUP14: preferred_kex[n_preferred_kex++] = &ssh_diffiehellman_group14; break; case KEX_DHGROUP1: preferred_kex[n_preferred_kex++] = &ssh_diffiehellman_group1; break; case KEX_RSA: preferred_kex[n_preferred_kex++] = &ssh_rsa_kex; break; case KEX_ECDH: preferred_kex[n_preferred_kex++] = &ssh_ecdh_kex; break; case KEX_WARN: /* Flag for later. Don't bother if it's the last in * the list. */ if (i < KEX_MAX - 1) { preferred_kex[n_preferred_kex++] = NULL; } break; } } /* * Set up the preferred host key types. These are just the ids * in the enum in putty.h, so 'warn below here' is indicated * by HK_WARN. */ n_preferred_hk = 0; for (i = 0; i < HK_MAX; i++) { int id = conf_get_int_int(conf, CONF_ssh_hklist, i); /* As above, don't bother with HK_WARN if it's last in the * list */ if (id != HK_WARN || i < HK_MAX - 1) preferred_hk[n_preferred_hk++] = id; } /* * Set up the preferred ciphers. (NULL => warn below here) */ n_preferred_ciphers = 0; for (i = 0; i < CIPHER_MAX; i++) { switch (conf_get_int_int(conf, CONF_ssh_cipherlist, i)) { case CIPHER_BLOWFISH: preferred_ciphers[n_preferred_ciphers++] = &ssh2_blowfish; break; case CIPHER_DES: if (conf_get_bool(conf, CONF_ssh2_des_cbc)) preferred_ciphers[n_preferred_ciphers++] = &ssh2_des; break; case CIPHER_3DES: preferred_ciphers[n_preferred_ciphers++] = &ssh2_3des; break; case CIPHER_AES: preferred_ciphers[n_preferred_ciphers++] = &ssh2_aes; break; case CIPHER_ARCFOUR: preferred_ciphers[n_preferred_ciphers++] = &ssh2_arcfour; break; case CIPHER_CHACHA20: preferred_ciphers[n_preferred_ciphers++] = &ssh2_ccp; break; case CIPHER_WARN: /* Flag for later. Don't bother if it's the last in * the list. */ if (i < CIPHER_MAX - 1) { preferred_ciphers[n_preferred_ciphers++] = NULL; } break; } } /* * Set up preferred compression. */ if (conf_get_bool(conf, CONF_compression)) preferred_comp = &ssh_zlib; else preferred_comp = &ssh_comp_none; for (i = 0; i < NKEXLIST; i++) for (j = 0; j < MAXKEXLIST; j++) kexlists[i][j].name = NULL; /* List key exchange algorithms. */ warn = false; for (i = 0; i < n_preferred_kex; i++) { const ssh_kexes *k = preferred_kex[i]; if (!k) warn = true; else for (j = 0; j < k->nkexes; j++) { alg = ssh2_kexinit_addalg(kexlists[KEXLIST_KEX], k->list[j]->name); alg->u.kex.kex = k->list[j]; alg->u.kex.warn = warn; } } /* List server host key algorithms. */ if (our_hostkeys) { /* * In server mode, we just list the algorithms that match the * host keys we actually have. */ for (i = 0; i < our_nhostkeys; i++) { const ssh_keyalg *keyalg = ssh_key_alg(our_hostkeys[i]); alg = ssh2_kexinit_addalg(kexlists[KEXLIST_HOSTKEY], keyalg->ssh_id); alg->u.hk.hostkey = keyalg; alg->u.hk.hkflags = 0; alg->u.hk.warn = false; if (keyalg == &ssh_rsa) { alg = ssh2_kexinit_addalg(kexlists[KEXLIST_HOSTKEY], "rsa-sha2-256"); alg->u.hk.hostkey = keyalg; alg->u.hk.hkflags = SSH_AGENT_RSA_SHA2_256; alg->u.hk.warn = false; alg = ssh2_kexinit_addalg(kexlists[KEXLIST_HOSTKEY], "rsa-sha2-512"); alg->u.hk.hostkey = keyalg; alg->u.hk.hkflags = SSH_AGENT_RSA_SHA2_512; alg->u.hk.warn = false; } } } else if (first_time) { /* * In the first key exchange, we list all the algorithms we're * prepared to cope with, but (if configured to) we prefer * those algorithms for which we have a host key for this * host. * * If the host key algorithm is below the warning * threshold, we warn even if we did already have a key * for it, on the basis that if the user has just * reconfigured that host key type to be warned about, * they surely _do_ want to be alerted that a server * they're actually connecting to is using it. */ warn = false; for (i = 0; i < n_preferred_hk; i++) { if (preferred_hk[i] == HK_WARN) warn = true; for (j = 0; j < lenof(ssh2_hostkey_algs); j++) { if (ssh2_hostkey_algs[j].id != preferred_hk[i]) continue; if (conf_get_bool(conf, CONF_ssh_prefer_known_hostkeys) && have_ssh_host_key(hk_host, hk_port, ssh2_hostkey_algs[j].alg->cache_id)) { alg = ssh2_kexinit_addalg(kexlists[KEXLIST_HOSTKEY], ssh2_hostkey_algs[j].alg->ssh_id); alg->u.hk.hostkey = ssh2_hostkey_algs[j].alg; alg->u.hk.warn = warn; } } } warn = false; for (i = 0; i < n_preferred_hk; i++) { if (preferred_hk[i] == HK_WARN) warn = true; for (j = 0; j < lenof(ssh2_hostkey_algs); j++) { if (ssh2_hostkey_algs[j].id != preferred_hk[i]) continue; alg = ssh2_kexinit_addalg(kexlists[KEXLIST_HOSTKEY], ssh2_hostkey_algs[j].alg->ssh_id); alg->u.hk.hostkey = ssh2_hostkey_algs[j].alg; alg->u.hk.warn = warn; } } #ifndef NO_GSSAPI } else if (transient_hostkey_mode) { /* * If we've previously done a GSSAPI KEX, then we list * precisely the algorithms for which a previous GSS key * exchange has delivered us a host key, because we expect * one of exactly those keys to be used in any subsequent * non-GSS-based rekey. * * An exception is if this is the key exchange we * triggered for the purposes of populating that cache - * in which case the cache will currently be empty, which * isn't helpful! */ warn = false; for (i = 0; i < n_preferred_hk; i++) { if (preferred_hk[i] == HK_WARN) warn = true; for (j = 0; j < lenof(ssh2_hostkey_algs); j++) { if (ssh2_hostkey_algs[j].id != preferred_hk[i]) continue; if (ssh_transient_hostkey_cache_has( thc, ssh2_hostkey_algs[j].alg)) { alg = ssh2_kexinit_addalg(kexlists[KEXLIST_HOSTKEY], ssh2_hostkey_algs[j].alg->ssh_id); alg->u.hk.hostkey = ssh2_hostkey_algs[j].alg; alg->u.hk.warn = warn; } } } #endif } else { /* * In subsequent key exchanges, we list only the host key * 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(hk_prev); alg = ssh2_kexinit_addalg(kexlists[KEXLIST_HOSTKEY], hk_prev->ssh_id); alg->u.hk.hostkey = hk_prev; alg->u.hk.warn = false; } if (can_gssapi_keyex) { alg = ssh2_kexinit_addalg(kexlists[KEXLIST_HOSTKEY], "null"); alg->u.hk.hostkey = NULL; } /* List encryption algorithms (client->server then server->client). */ for (k = KEXLIST_CSCIPHER; k <= KEXLIST_SCCIPHER; k++) { warn = false; #ifdef FUZZING alg = ssh2_kexinit_addalg(kexlists[k], "none"); alg->u.cipher.cipher = NULL; alg->u.cipher.warn = warn; #endif /* FUZZING */ for (i = 0; i < n_preferred_ciphers; i++) { const ssh2_ciphers *c = preferred_ciphers[i]; if (!c) warn = true; else for (j = 0; j < c->nciphers; j++) { alg = ssh2_kexinit_addalg(kexlists[k], c->list[j]->ssh2_id); alg->u.cipher.cipher = c->list[j]; alg->u.cipher.warn = warn; } } } /* * Be prepared to work around the buggy MAC problem. */ if (remote_bugs & BUG_SSH2_HMAC) { maclist = buggymacs; nmacs = lenof(buggymacs); } else { maclist = macs; nmacs = lenof(macs); } /* List MAC algorithms (client->server then server->client). */ for (j = KEXLIST_CSMAC; j <= KEXLIST_SCMAC; j++) { #ifdef FUZZING alg = ssh2_kexinit_addalg(kexlists[j], "none"); alg->u.mac.mac = NULL; alg->u.mac.etm = false; #endif /* FUZZING */ for (i = 0; i < nmacs; i++) { alg = ssh2_kexinit_addalg(kexlists[j], maclist[i]->name); alg->u.mac.mac = maclist[i]; alg->u.mac.etm = false; } for (i = 0; i < nmacs; i++) { /* For each MAC, there may also be an ETM version, * which we list second. */ if (maclist[i]->etm_name) { alg = ssh2_kexinit_addalg(kexlists[j], maclist[i]->etm_name); alg->u.mac.mac = maclist[i]; alg->u.mac.etm = true; } } } /* List client->server compression algorithms, * then server->client compression algorithms. (We use the * same set twice.) */ for (j = KEXLIST_CSCOMP; j <= KEXLIST_SCCOMP; j++) { assert(lenof(compressions) > 1); /* Prefer non-delayed versions */ alg = ssh2_kexinit_addalg(kexlists[j], preferred_comp->name); alg->u.comp.comp = preferred_comp; alg->u.comp.delayed = false; if (preferred_comp->delayed_name) { alg = ssh2_kexinit_addalg(kexlists[j], preferred_comp->delayed_name); alg->u.comp.comp = preferred_comp; alg->u.comp.delayed = true; } for (i = 0; i < lenof(compressions); i++) { const ssh_compression_alg *c = compressions[i]; alg = ssh2_kexinit_addalg(kexlists[j], c->name); alg->u.comp.comp = c; alg->u.comp.delayed = false; if (c->delayed_name) { alg = ssh2_kexinit_addalg(kexlists[j], c->delayed_name); alg->u.comp.comp = c; alg->u.comp.delayed = true; } } } /* * Finally, format the lists into text and write them into the * outgoing KEXINIT packet. */ for (i = 0; i < NKEXLIST; i++) { strbuf *list = strbuf_new(); if (ssc && ssc->kex_override[i].ptr) { put_datapl(list, ssc->kex_override[i]); } else { for (j = 0; j < MAXKEXLIST; j++) { if (kexlists[i][j].name == NULL) break; add_to_commasep(list, kexlists[i][j].name); } } if (i == KEXLIST_KEX && first_time) { if (our_hostkeys) /* we're the server */ add_to_commasep(list, "ext-info-s"); else /* we're the client */ add_to_commasep(list, "ext-info-c"); } put_stringsb(pktout, list); } /* List client->server languages. Empty list. */ put_stringz(pktout, ""); /* List server->client languages. Empty list. */ put_stringz(pktout, ""); } static bool ssh2_scan_kexinits( ptrlen client_kexinit, ptrlen server_kexinit, struct kexinit_algorithm kexlists[NKEXLIST][MAXKEXLIST], const ssh_kex **kex_alg, const ssh_keyalg **hostkey_alg, transport_direction *cs, transport_direction *sc, bool *warn_kex, bool *warn_hk, bool *warn_cscipher, bool *warn_sccipher, Ssh *ssh, bool *ignore_guess_cs_packet, bool *ignore_guess_sc_packet, int *n_server_hostkeys, int server_hostkeys[MAXKEXLIST], unsigned *hkflags, bool *can_send_ext_info) { BinarySource client[1], server[1]; int i; bool guess_correct; ptrlen clists[NKEXLIST], slists[NKEXLIST]; const struct kexinit_algorithm *selected[NKEXLIST]; BinarySource_BARE_INIT_PL(client, client_kexinit); BinarySource_BARE_INIT_PL(server, server_kexinit); /* Skip packet type bytes and random cookies. */ get_data(client, 1 + 16); get_data(server, 1 + 16); guess_correct = true; /* Find the matching string in each list, and map it to its * kexinit_algorithm structure. */ for (i = 0; i < NKEXLIST; i++) { ptrlen clist, slist, cword, sword, found; bool cfirst, sfirst; int j; clists[i] = get_string(client); slists[i] = get_string(server); if (get_err(client) || get_err(server)) { /* Report a better error than the spurious "Couldn't * agree" that we'd generate if we pressed on regardless * and treated the empty get_string() result as genuine */ ssh_proto_error(ssh, "KEXINIT packet was incomplete"); return false; } for (cfirst = true, clist = clists[i]; get_commasep_word(&clist, &cword); cfirst = false) for (sfirst = true, slist = slists[i]; get_commasep_word(&slist, &sword); sfirst = false) if (ptrlen_eq_ptrlen(cword, sword)) { found = cword; goto found_match; } /* No matching string found in the two lists. Delay reporting * a fatal error until below, because sometimes it turns out * not to be fatal. */ selected[i] = NULL; /* * However, even if a failure to agree on any algorithm at all * is not completely fatal (e.g. because it's the MAC * negotiation for a cipher that comes with a built-in MAC), * it still invalidates the guessed key exchange packet. (RFC * 4253 section 7, not contradicted by OpenSSH's * PROTOCOL.chacha20poly1305 or as far as I can see by their * code.) */ guess_correct = false; continue; found_match: selected[i] = NULL; for (j = 0; j < MAXKEXLIST; j++) { if (kexlists[i][j].name && ptrlen_eq_string(found, kexlists[i][j].name)) { selected[i] = &kexlists[i][j]; break; } } if (!selected[i]) { /* * In the client, this should never happen! But in the * server, where we allow manual override on the command * line of the exact KEXINIT strings, it can happen * because the command line contained a typo. So we * produce a reasonably useful message instead of an * assertion failure. */ ssh_sw_abort(ssh, "Selected %s \"%.*s\" does not correspond to " "any supported algorithm", kexlist_descr[i], PTRLEN_PRINTF(found)); return false; } /* * If the kex or host key algorithm is not the first one in * both sides' lists, that means the guessed key exchange * packet (if any) is officially wrong. */ if ((i == KEXLIST_KEX || i == KEXLIST_HOSTKEY) && !(cfirst || sfirst)) guess_correct = false; } /* * Skip language strings in both KEXINITs, and read the flags * saying whether a guessed KEX packet follows. */ get_string(client); get_string(client); get_string(server); get_string(server); if (ignore_guess_cs_packet) *ignore_guess_cs_packet = get_bool(client) && !guess_correct; if (ignore_guess_sc_packet) *ignore_guess_sc_packet = get_bool(server) && !guess_correct; /* * Now transcribe the selected algorithm set into the output data. */ for (i = 0; i < NKEXLIST; i++) { const struct kexinit_algorithm *alg; /* * If we've already selected a cipher which requires a * particular MAC, then just select that. This is the case in * which it's not a fatal error if the actual MAC string lists * didn't include any matching error. */ if (i == KEXLIST_CSMAC && cs->cipher && cs->cipher->required_mac) { cs->mac = cs->cipher->required_mac; cs->etm_mode = !!(cs->mac->etm_name); continue; } if (i == KEXLIST_SCMAC && sc->cipher && sc->cipher->required_mac) { sc->mac = sc->cipher->required_mac; sc->etm_mode = !!(sc->mac->etm_name); continue; } alg = selected[i]; if (!alg) { /* * Otherwise, any match failure _is_ a fatal error. */ ssh_sw_abort(ssh, "Couldn't agree a %s (available: %.*s)", kexlist_descr[i], PTRLEN_PRINTF(slists[i])); return false; } switch (i) { case KEXLIST_KEX: *kex_alg = alg->u.kex.kex; *warn_kex = alg->u.kex.warn; break; case KEXLIST_HOSTKEY: /* * Ignore an unexpected/inappropriate offer of "null", * we offer "null" when we're willing to use GSS KEX, * but it is only acceptable when GSSKEX is actually * selected. */ if (alg->u.hk.hostkey == NULL && (*kex_alg)->main_type != KEXTYPE_GSS) continue; *hostkey_alg = alg->u.hk.hostkey; *hkflags = alg->u.hk.hkflags; *warn_hk = alg->u.hk.warn; break; case KEXLIST_CSCIPHER: cs->cipher = alg->u.cipher.cipher; *warn_cscipher = alg->u.cipher.warn; break; case KEXLIST_SCCIPHER: sc->cipher = alg->u.cipher.cipher; *warn_sccipher = alg->u.cipher.warn; break; case KEXLIST_CSMAC: cs->mac = alg->u.mac.mac; cs->etm_mode = alg->u.mac.etm; break; case KEXLIST_SCMAC: sc->mac = alg->u.mac.mac; sc->etm_mode = alg->u.mac.etm; break; case KEXLIST_CSCOMP: cs->comp = alg->u.comp.comp; cs->comp_delayed = alg->u.comp.delayed; break; case KEXLIST_SCCOMP: sc->comp = alg->u.comp.comp; sc->comp_delayed = alg->u.comp.delayed; break; default: unreachable("Bad list index in scan_kexinits"); } } /* * Check whether the other side advertised support for EXT_INFO. */ { ptrlen extinfo_advert = (server_hostkeys ? PTRLEN_LITERAL("ext-info-c") : PTRLEN_LITERAL("ext-info-s")); ptrlen list = (server_hostkeys ? clists[KEXLIST_KEX] : slists[KEXLIST_KEX]); for (ptrlen word; get_commasep_word(&list, &word) ;) if (ptrlen_eq_ptrlen(word, extinfo_advert)) *can_send_ext_info = true; } if (server_hostkeys) { /* * Finally, make an auxiliary pass over the server's host key * list to find all the host key algorithms offered by the * server which we know about at all, whether we selected each * one or not. We return these as a list of indices into the * constant ssh2_hostkey_algs[] array. */ *n_server_hostkeys = 0; ptrlen list = slists[KEXLIST_HOSTKEY]; for (ptrlen word; get_commasep_word(&list, &word) ;) { for (i = 0; i < lenof(ssh2_hostkey_algs); i++) if (ptrlen_eq_string(word, ssh2_hostkey_algs[i].alg->ssh_id)) { server_hostkeys[(*n_server_hostkeys)++] = i; break; } } } return true; } void ssh2transport_finalise_exhash(struct ssh2_transport_state *s) { put_mp_ssh2(s->exhash, s->K); assert(ssh_hash_alg(s->exhash)->hlen <= sizeof(s->exchange_hash)); ssh_hash_final(s->exhash, s->exchange_hash); s->exhash = NULL; #if 0 debug("Exchange hash is:\n"); dmemdump(s->exchange_hash, s->kex_alg->hash->hlen); #endif } static void ssh2_transport_process_queue(PacketProtocolLayer *ppl) { struct ssh2_transport_state *s = container_of(ppl, struct ssh2_transport_state, ppl); PktIn *pktin; PktOut *pktout; /* Filter centrally handled messages off the front of the queue on * every entry to this coroutine, no matter where we're resuming * from, even if we're _not_ looping on pq_pop. That way we can * still proactively handle those messages even if we're waiting * for a user response. */ if (ssh2_transport_filter_queue(s)) return; /* we've been freed */ crBegin(s->crState); s->in.cipher = s->out.cipher = NULL; s->in.mac = s->out.mac = NULL; s->in.comp = s->out.comp = NULL; s->got_session_id = false; s->need_gss_transient_hostkey = false; s->warned_about_no_gss_transient_hostkey = false; begin_key_exchange: #ifndef NO_GSSAPI if (s->need_gss_transient_hostkey) { /* * This flag indicates a special case in which we must not do * GSS key exchange even if we could. (See comments below, * where the flag was set on the previous key exchange.) */ s->can_gssapi_keyex = false; } else if (conf_get_bool(s->conf, CONF_try_gssapi_kex)) { /* * We always check if we have GSS creds before we come up with * the kex algorithm list, otherwise future rekeys will fail * when creds expire. To make this so, this code section must * follow the begin_key_exchange label above, otherwise this * section would execute just once per-connection. * * Update GSS state unless the reason we're here is that a * timer just checked the GSS state and decided that we should * rekey to update delegated credentials. In that case, the * state is "fresh". */ if (s->rekey_class != RK_GSS_UPDATE) ssh2_transport_gss_update(s, true); /* Do GSSAPI KEX when capable */ s->can_gssapi_keyex = s->gss_status & GSS_KEX_CAPABLE; /* * But not when failure is likely. [ GSS implementations may * attempt (and fail) to use a ticket that is almost expired * when retrieved from the ccache that actually expires by the * time the server receives it. ] * * Note: The first time always try KEXGSS if we can, failures * will be very rare, and disabling the initial GSS KEX is * worse. Some day GSS libraries will ignore cached tickets * whose lifetime is critically short, and will instead use * fresh ones. */ if (!s->got_session_id && (s->gss_status & GSS_CTXT_MAYFAIL) != 0) s->can_gssapi_keyex = false; s->gss_delegate = conf_get_bool(s->conf, CONF_gssapifwd); } else { s->can_gssapi_keyex = false; } #endif s->ppl.bpp->pls->kctx = SSH2_PKTCTX_NOKEX; /* * Construct our KEXINIT packet, in a strbuf so we can refer to it * later. */ strbuf_clear(s->client_kexinit); put_byte(s->outgoing_kexinit, SSH2_MSG_KEXINIT); random_read(strbuf_append(s->outgoing_kexinit, 16), 16); ssh2_write_kexinit_lists( BinarySink_UPCAST(s->outgoing_kexinit), s->kexlists, s->conf, s->ssc, s->ppl.remote_bugs, s->savedhost, s->savedport, s->hostkey_alg, s->thc, s->hostkeys, s->nhostkeys, !s->got_session_id, s->can_gssapi_keyex, s->gss_kex_used && !s->need_gss_transient_hostkey); /* First KEX packet does _not_ follow, because we're not that brave. */ put_bool(s->outgoing_kexinit, false); put_uint32(s->outgoing_kexinit, 0); /* reserved */ /* * Send our KEXINIT. */ pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_KEXINIT); put_data(pktout, s->outgoing_kexinit->u + 1, s->outgoing_kexinit->len - 1); /* omit initial packet type byte */ pq_push(s->ppl.out_pq, pktout); /* * Flag that KEX is in progress. */ s->kex_in_progress = true; /* * Wait for the other side's KEXINIT, and save it. */ crMaybeWaitUntilV((pktin = ssh2_transport_pop(s)) != NULL); if (pktin->type != SSH2_MSG_KEXINIT) { ssh_proto_error(s->ppl.ssh, "Received unexpected packet when " "expecting KEXINIT, type %d (%s)", pktin->type, ssh2_pkt_type(s->ppl.bpp->pls->kctx, s->ppl.bpp->pls->actx, pktin->type)); return; } strbuf_clear(s->incoming_kexinit); put_byte(s->incoming_kexinit, SSH2_MSG_KEXINIT); put_data(s->incoming_kexinit, get_ptr(pktin), get_avail(pktin)); /* * Work through the two KEXINIT packets in parallel to find the * selected algorithm identifiers. */ { int nhk, hks[MAXKEXLIST], i, j; if (!ssh2_scan_kexinits( ptrlen_from_strbuf(s->client_kexinit), ptrlen_from_strbuf(s->server_kexinit), s->kexlists, &s->kex_alg, &s->hostkey_alg, s->cstrans, s->sctrans, &s->warn_kex, &s->warn_hk, &s->warn_cscipher, &s->warn_sccipher, s->ppl.ssh, NULL, &s->ignorepkt, &nhk, hks, &s->hkflags, &s->can_send_ext_info)) return; /* false means a fatal error function was called */ /* * In addition to deciding which host key we're actually going * to use, we should make a list of the host keys offered by * the server which we _don't_ have cached. These will be * offered as cross-certification options by ssh_get_specials. * * We also count the key we're currently using for KEX as one * we've already got, because by the time this menu becomes * visible, it will be. */ s->n_uncert_hostkeys = 0; for (i = 0; i < nhk; i++) { j = hks[i]; if (ssh2_hostkey_algs[j].alg != s->hostkey_alg && !have_ssh_host_key(s->savedhost, s->savedport, ssh2_hostkey_algs[j].alg->cache_id)) { s->uncert_hostkeys[s->n_uncert_hostkeys++] = j; } } } if (s->warn_kex) { s->dlgret = ssh2_transport_confirm_weak_crypto_primitive( s, "key-exchange algorithm", s->kex_alg->name, s->kex_alg); crMaybeWaitUntilV(s->dlgret >= 0); if (s->dlgret == 0) { ssh_user_close(s->ppl.ssh, "User aborted at kex warning"); return; } } if (s->warn_hk) { int j, k; char *betteralgs; /* * Change warning box wording depending on why we chose a * warning-level host key algorithm. If it's because * that's all we have *cached*, list the host keys we * could usefully cross-certify. Otherwise, use the same * standard wording as any other weak crypto primitive. */ betteralgs = NULL; for (j = 0; j < s->n_uncert_hostkeys; j++) { const struct ssh_signkey_with_user_pref_id *hktype = &ssh2_hostkey_algs[s->uncert_hostkeys[j]]; bool better = false; for (k = 0; k < HK_MAX; k++) { int id = conf_get_int_int(s->conf, CONF_ssh_hklist, k); if (id == HK_WARN) { break; } else if (id == hktype->id) { better = true; break; } } if (better) { if (betteralgs) { char *old_ba = betteralgs; betteralgs = dupcat(betteralgs, ",", hktype->alg->ssh_id); sfree(old_ba); } else { betteralgs = dupstr(hktype->alg->ssh_id); } } } if (betteralgs) { /* Use the special warning prompt that lets us provide * a list of better algorithms */ s->dlgret = seat_confirm_weak_cached_hostkey( s->ppl.seat, s->hostkey_alg->ssh_id, betteralgs, ssh2_transport_dialog_callback, s); sfree(betteralgs); } else { /* If none exist, use the more general 'weak crypto' * warning prompt */ s->dlgret = ssh2_transport_confirm_weak_crypto_primitive( s, "host key type", s->hostkey_alg->ssh_id, s->hostkey_alg); } crMaybeWaitUntilV(s->dlgret >= 0); if (s->dlgret == 0) { ssh_user_close(s->ppl.ssh, "User aborted at host key warning"); return; } } if (s->warn_cscipher) { s->dlgret = ssh2_transport_confirm_weak_crypto_primitive( s, "client-to-server cipher", s->out.cipher->ssh2_id, s->out.cipher); crMaybeWaitUntilV(s->dlgret >= 0); if (s->dlgret == 0) { ssh_user_close(s->ppl.ssh, "User aborted at cipher warning"); return; } } if (s->warn_sccipher) { s->dlgret = ssh2_transport_confirm_weak_crypto_primitive( s, "server-to-client cipher", s->in.cipher->ssh2_id, s->in.cipher); crMaybeWaitUntilV(s->dlgret >= 0); if (s->dlgret == 0) { ssh_user_close(s->ppl.ssh, "User aborted at cipher warning"); return; } } /* * If the other side has sent an initial key exchange packet that * we must treat as a wrong guess, wait for it, and discard it. */ if (s->ignorepkt) crMaybeWaitUntilV((pktin = ssh2_transport_pop(s)) != NULL); /* * Actually perform the key exchange. */ s->exhash = ssh_hash_new(s->kex_alg->hash); put_stringz(s->exhash, s->client_greeting); put_stringz(s->exhash, s->server_greeting); put_string(s->exhash, s->client_kexinit->u, s->client_kexinit->len); put_string(s->exhash, s->server_kexinit->u, s->server_kexinit->len); s->crStateKex = 0; while (1) { bool aborted = false; ssh2kex_coroutine(s, &aborted); if (aborted) return; /* disaster: our entire state has been freed */ if (!s->crStateKex) break; /* kex phase has terminated normally */ crReturnV; } /* * The exchange hash from the very first key exchange is also * the session id, used in session key construction and * authentication. */ if (!s->got_session_id) { assert(sizeof(s->exchange_hash) <= sizeof(s->session_id)); memcpy(s->session_id, s->exchange_hash, sizeof(s->exchange_hash)); s->session_id_len = s->kex_alg->hash->hlen; assert(s->session_id_len <= sizeof(s->session_id)); s->got_session_id = true; } /* * Send SSH2_MSG_NEWKEYS. */ pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_NEWKEYS); pq_push(s->ppl.out_pq, pktout); /* Start counting down the outgoing-data limit for these cipher keys. */ dts_reset(&s->stats->out, s->max_data_size); /* * Force the BPP to synchronously marshal all packets up to and * including that NEWKEYS into wire format, before we switch over * to new crypto. */ ssh_bpp_handle_output(s->ppl.bpp); /* * We've sent outgoing NEWKEYS, so create and initialise outgoing * session keys. */ { strbuf *cipher_key = strbuf_new_nm(); strbuf *cipher_iv = strbuf_new_nm(); strbuf *mac_key = strbuf_new_nm(); if (s->out.cipher) { ssh2_mkkey(s, cipher_iv, s->K, s->exchange_hash, 'A' + s->out.mkkey_adjust, s->out.cipher->blksize); ssh2_mkkey(s, cipher_key, s->K, s->exchange_hash, 'C' + s->out.mkkey_adjust, s->out.cipher->padded_keybytes); } if (s->out.mac) { ssh2_mkkey(s, mac_key, s->K, s->exchange_hash, 'E' + s->out.mkkey_adjust, s->out.mac->keylen); } ssh2_bpp_new_outgoing_crypto( s->ppl.bpp, s->out.cipher, cipher_key->u, cipher_iv->u, s->out.mac, s->out.etm_mode, mac_key->u, s->out.comp, s->out.comp_delayed); strbuf_free(cipher_key); strbuf_free(cipher_iv); strbuf_free(mac_key); } /* * If that was our first key exchange, this is the moment to send * our EXT_INFO, if we're sending one. */ if (!s->post_newkeys_ext_info) { s->post_newkeys_ext_info = true; /* never do this again */ if (s->can_send_ext_info) { strbuf *extinfo = strbuf_new(); uint32_t n_exts = 0; if (s->ssc) { /* Server->client EXT_INFO lists our supported user * key algorithms. */ n_exts++; put_stringz(extinfo, "server-sig-algs"); strbuf *list = strbuf_new(); for (size_t i = 0; i < n_keyalgs; i++) add_to_commasep(list, all_keyalgs[i]->ssh_id); put_stringsb(extinfo, list); } else { /* Client->server EXT_INFO is currently not sent, but here's * where we should put things in it if we ever want to. */ } /* Only send EXT_INFO if it's non-empty */ if (n_exts) { pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_EXT_INFO); put_uint32(pktout, n_exts); put_datapl(pktout, ptrlen_from_strbuf(extinfo)); pq_push(s->ppl.out_pq, pktout); } strbuf_free(extinfo); } } /* * Now our end of the key exchange is complete, we can send all * our queued higher-layer packets. Transfer the whole of the next * layer's outgoing queue on to our own. */ pq_concatenate(s->ppl.out_pq, s->ppl.out_pq, &s->pq_out_higher); /* * Expect SSH2_MSG_NEWKEYS from server. */ crMaybeWaitUntilV((pktin = ssh2_transport_pop(s)) != NULL); if (pktin->type != SSH2_MSG_NEWKEYS) { ssh_proto_error(s->ppl.ssh, "Received unexpected packet when " "expecting SSH_MSG_NEWKEYS, type %d (%s)", pktin->type, ssh2_pkt_type(s->ppl.bpp->pls->kctx, s->ppl.bpp->pls->actx, pktin->type)); return; } /* Start counting down the incoming-data limit for these cipher keys. */ dts_reset(&s->stats->in, s->max_data_size); /* * We've seen incoming NEWKEYS, so create and initialise * incoming session keys. */ { strbuf *cipher_key = strbuf_new_nm(); strbuf *cipher_iv = strbuf_new_nm(); strbuf *mac_key = strbuf_new_nm(); if (s->in.cipher) { ssh2_mkkey(s, cipher_iv, s->K, s->exchange_hash, 'A' + s->in.mkkey_adjust, s->in.cipher->blksize); ssh2_mkkey(s, cipher_key, s->K, s->exchange_hash, 'C' + s->in.mkkey_adjust, s->in.cipher->padded_keybytes); } if (s->in.mac) { ssh2_mkkey(s, mac_key, s->K, s->exchange_hash, 'E' + s->in.mkkey_adjust, s->in.mac->keylen); } ssh2_bpp_new_incoming_crypto( s->ppl.bpp, s->in.cipher, cipher_key->u, cipher_iv->u, s->in.mac, s->in.etm_mode, mac_key->u, s->in.comp, s->in.comp_delayed); strbuf_free(cipher_key); strbuf_free(cipher_iv); strbuf_free(mac_key); } /* * Free shared secret. */ mp_free(s->K); s->K = NULL; /* * Update the specials menu to list the remaining uncertified host * keys. */ seat_update_specials_menu(s->ppl.seat); /* * Key exchange is over. Loop straight back round if we have a * deferred rekey reason. */ if (s->deferred_rekey_reason) { ppl_logevent("%s", s->deferred_rekey_reason); pktin = NULL; s->deferred_rekey_reason = NULL; goto begin_key_exchange; } /* * Otherwise, schedule a timer for our next rekey. */ s->kex_in_progress = false; s->last_rekey = GETTICKCOUNT(); (void) ssh2_transport_timer_update(s, 0); /* * Now we're encrypting. Get the next-layer protocol started if it * hasn't already, and then sit here waiting for reasons to go * back to the start and do a repeat key exchange. One of those * reasons is that we receive KEXINIT from the other end; the * other is if we find rekey_reason is non-NULL, i.e. we've * decided to initiate a rekey ourselves for some reason. */ if (!s->higher_layer_ok) { if (!s->hostkeys) { /* We're the client, so send SERVICE_REQUEST. */ pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_SERVICE_REQUEST); put_stringz(pktout, s->higher_layer->vt->name); pq_push(s->ppl.out_pq, pktout); crMaybeWaitUntilV((pktin = ssh2_transport_pop(s)) != NULL); if (pktin->type != SSH2_MSG_SERVICE_ACCEPT) { ssh_sw_abort(s->ppl.ssh, "Server refused request to start " "'%s' protocol", s->higher_layer->vt->name); return; } } else { ptrlen service_name; /* We're the server, so expect SERVICE_REQUEST. */ crMaybeWaitUntilV((pktin = ssh2_transport_pop(s)) != NULL); if (pktin->type != SSH2_MSG_SERVICE_REQUEST) { ssh_proto_error(s->ppl.ssh, "Received unexpected packet when " "expecting SERVICE_REQUEST, type %d (%s)", pktin->type, ssh2_pkt_type(s->ppl.bpp->pls->kctx, s->ppl.bpp->pls->actx, pktin->type)); return; } service_name = get_string(pktin); if (!ptrlen_eq_string(service_name, s->higher_layer->vt->name)) { ssh_proto_error(s->ppl.ssh, "Client requested service " "'%.*s' when we only support '%s'", PTRLEN_PRINTF(service_name), s->higher_layer->vt->name); return; } pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_SERVICE_ACCEPT); put_stringz(pktout, s->higher_layer->vt->name); pq_push(s->ppl.out_pq, pktout); } s->higher_layer_ok = true; queue_idempotent_callback(&s->higher_layer->ic_process_queue); } s->rekey_class = RK_NONE; do { crReturnV; /* Pass through outgoing packets from the higher layer. */ pq_concatenate(s->ppl.out_pq, s->ppl.out_pq, &s->pq_out_higher); /* Wait for either a KEXINIT, or something setting * s->rekey_class. This call to ssh2_transport_pop also has * the side effect of transferring incoming packets _to_ the * higher layer (via filter_queue). */ if ((pktin = ssh2_transport_pop(s)) != NULL) { if (pktin->type != SSH2_MSG_KEXINIT) { ssh_proto_error(s->ppl.ssh, "Received unexpected transport-" "layer packet outside a key exchange, " "type %d (%s)", pktin->type, ssh2_pkt_type(s->ppl.bpp->pls->kctx, s->ppl.bpp->pls->actx, pktin->type)); return; } pq_push_front(s->ppl.in_pq, pktin); ppl_logevent("Remote side initiated key re-exchange"); s->rekey_class = RK_SERVER; } if (s->rekey_class == RK_POST_USERAUTH) { /* * userauth has seen a USERAUTH_SUCCESS. This may be the * moment to do an immediate rekey with different * parameters. But it may not; so here we turn that rekey * class into either RK_NONE or RK_NORMAL. * * Currently the only reason for this is if we've done a * GSS key exchange and don't have anything in our * transient hostkey cache, in which case we should make * an attempt to populate the cache now. */ if (s->need_gss_transient_hostkey) { s->rekey_reason = "populating transient host key cache"; s->rekey_class = RK_NORMAL; } else { /* No need to rekey at this time. */ s->rekey_class = RK_NONE; } } if (!s->rekey_class) { /* If we don't yet have any other reason to rekey, check * if we've hit our data limit in either direction. */ if (s->stats->in.expired) { s->rekey_reason = "too much data received"; s->rekey_class = RK_NORMAL; } else if (s->stats->out.expired) { s->rekey_reason = "too much data sent"; s->rekey_class = RK_NORMAL; } } if (s->rekey_class != RK_NONE && s->rekey_class != RK_SERVER) { /* * Special case: if the server bug is set that doesn't * allow rekeying, we give a different log message and * continue waiting. (If such a server _initiates_ a * rekey, we process it anyway!) */ if ((s->ppl.remote_bugs & BUG_SSH2_REKEY)) { ppl_logevent("Remote bug prevents key re-exchange (%s)", s->rekey_reason); /* Reset the counters, so that at least this message doesn't * hit the event log _too_ often. */ dts_reset(&s->stats->in, s->max_data_size); dts_reset(&s->stats->out, s->max_data_size); (void) ssh2_transport_timer_update(s, 0); s->rekey_class = RK_NONE; } else { ppl_logevent("Initiating key re-exchange (%s)", s->rekey_reason); } } } while (s->rekey_class == RK_NONE); /* Once we exit the above loop, we really are rekeying. */ goto begin_key_exchange; crFinishV; } static void ssh2_transport_higher_layer_packet_callback(void *context) { PacketProtocolLayer *ppl = (PacketProtocolLayer *)context; ssh_ppl_process_queue(ppl); } static void ssh2_transport_timer(void *ctx, unsigned long now) { struct ssh2_transport_state *s = (struct ssh2_transport_state *)ctx; unsigned long mins; unsigned long ticks; if (s->kex_in_progress || now != s->next_rekey) return; mins = sanitise_rekey_time(conf_get_int(s->conf, CONF_ssh_rekey_time), 60); if (mins == 0) return; /* Rekey if enough time has elapsed */ ticks = mins * 60 * TICKSPERSEC; if (now - s->last_rekey > ticks - 30*TICKSPERSEC) { s->rekey_reason = "timeout"; s->rekey_class = RK_NORMAL; queue_idempotent_callback(&s->ppl.ic_process_queue); return; } #ifndef NO_GSSAPI /* * Rekey now if we have a new cred or context expires this cycle, * but not if this is unsafe. */ if (conf_get_int(s->conf, CONF_gssapirekey)) { ssh2_transport_gss_update(s, false); if ((s->gss_status & GSS_KEX_CAPABLE) != 0 && (s->gss_status & GSS_CTXT_MAYFAIL) == 0 && (s->gss_status & (GSS_CRED_UPDATED|GSS_CTXT_EXPIRES)) != 0) { s->rekey_reason = "GSS credentials updated"; s->rekey_class = RK_GSS_UPDATE; queue_idempotent_callback(&s->ppl.ic_process_queue); return; } } #endif /* Try again later. */ (void) ssh2_transport_timer_update(s, 0); } /* * The rekey_time is zero except when re-configuring. * * We either schedule the next timer and return false, or return true * to run the callback now, which will call us again to re-schedule on * completion. */ static bool ssh2_transport_timer_update(struct ssh2_transport_state *s, unsigned long rekey_time) { unsigned long mins; unsigned long ticks; mins = sanitise_rekey_time(conf_get_int(s->conf, CONF_ssh_rekey_time), 60); ticks = mins * 60 * TICKSPERSEC; /* Handle change from previous setting */ if (rekey_time != 0 && rekey_time != mins) { unsigned long next; unsigned long now = GETTICKCOUNT(); mins = rekey_time; ticks = mins * 60 * TICKSPERSEC; next = s->last_rekey + ticks; /* If overdue, caller will rekey synchronously now */ if (now - s->last_rekey > ticks) return true; ticks = next - now; } #ifndef NO_GSSAPI if (s->gss_kex_used) { /* * If we've used GSSAPI key exchange, then we should * periodically check whether we need to do another one to * pass new credentials to the server. */ unsigned long gssmins; /* Check cascade conditions more frequently if configured */ gssmins = sanitise_rekey_time( conf_get_int(s->conf, CONF_gssapirekey), GSS_DEF_REKEY_MINS); if (gssmins > 0) { if (gssmins < mins) ticks = (mins = gssmins) * 60 * TICKSPERSEC; if ((s->gss_status & GSS_KEX_CAPABLE) != 0) { /* * Run next timer even sooner if it would otherwise be * too close to the context expiration time */ if ((s->gss_status & GSS_CTXT_EXPIRES) == 0 && s->gss_ctxt_lifetime - mins * 60 < 2 * MIN_CTXT_LIFETIME) ticks -= 2 * MIN_CTXT_LIFETIME * TICKSPERSEC; } } } #endif /* Schedule the next timer */ s->next_rekey = schedule_timer(ticks, ssh2_transport_timer, s); return false; } void ssh2_transport_dialog_callback(void *loginv, int ret) { struct ssh2_transport_state *s = (struct ssh2_transport_state *)loginv; s->dlgret = ret; ssh_ppl_process_queue(&s->ppl); } #ifndef NO_GSSAPI /* * This is called at the beginning of each SSH rekey to determine * whether we are GSS capable, and if we did GSS key exchange, and are * delegating credentials, it is also called periodically to determine * whether we should rekey in order to delegate (more) fresh * credentials. This is called "credential cascading". * * On Windows, with SSPI, we may not get the credential expiration, as * Windows automatically renews from cached passwords, so the * credential effectively never expires. Since we still want to * cascade when the local TGT is updated, we use the expiration of a * newly obtained context as a proxy for the expiration of the TGT. */ static void ssh2_transport_gss_update(struct ssh2_transport_state *s, bool definitely_rekeying) { PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */ int gss_stat; time_t gss_cred_expiry; unsigned long mins; Ssh_gss_buf gss_sndtok; Ssh_gss_buf gss_rcvtok; Ssh_gss_ctx gss_ctx; s->gss_status = 0; /* * Nothing to do if no GSSAPI libraries are configured or GSSAPI * auth is not enabled. */ if (s->shgss->libs->nlibraries == 0) return; if (!conf_get_bool(s->conf, CONF_try_gssapi_auth) && !conf_get_bool(s->conf, CONF_try_gssapi_kex)) return; /* Import server name and cache it */ if (s->shgss->srv_name == GSS_C_NO_NAME) { gss_stat = s->shgss->lib->import_name( s->shgss->lib, s->fullhostname, &s->shgss->srv_name); if (gss_stat != SSH_GSS_OK) { if (gss_stat == SSH_GSS_BAD_HOST_NAME) ppl_logevent("GSSAPI import name failed - Bad service name;" " won't use GSS key exchange"); else ppl_logevent("GSSAPI import name failed;" " won't use GSS key exchange"); return; } } /* * Do we (still) have credentials? Capture the credential * expiration when available */ gss_stat = s->shgss->lib->acquire_cred( s->shgss->lib, &gss_ctx, &gss_cred_expiry); if (gss_stat != SSH_GSS_OK) return; SSH_GSS_CLEAR_BUF(&gss_sndtok); SSH_GSS_CLEAR_BUF(&gss_rcvtok); /* * When acquire_cred yields no useful expiration, get a proxy for * the cred expiration from the context expiration. */ gss_stat = s->shgss->lib->init_sec_context( s->shgss->lib, &gss_ctx, s->shgss->srv_name, 0 /* don't delegate */, &gss_rcvtok, &gss_sndtok, (gss_cred_expiry == GSS_NO_EXPIRATION ? &gss_cred_expiry : NULL), &s->gss_ctxt_lifetime); /* This context was for testing only. */ if (gss_ctx) s->shgss->lib->release_cred(s->shgss->lib, &gss_ctx); if (gss_stat != SSH_GSS_OK && gss_stat != SSH_GSS_S_CONTINUE_NEEDED) { /* * No point in verbosely interrupting the user to tell them we * couldn't get GSS credentials, if this was only a check * between key exchanges to see if fresh ones were available. * When we do do a rekey, this message (if displayed) will * appear among the standard rekey blurb, but when we're not, * it shouldn't pop up all the time regardless. */ if (definitely_rekeying) ppl_logevent("No GSSAPI security context available"); return; } if (gss_sndtok.length) s->shgss->lib->free_tok(s->shgss->lib, &gss_sndtok); s->gss_status |= GSS_KEX_CAPABLE; /* * When rekeying to cascade, avoding doing this too close to the * context expiration time, since the key exchange might fail. */ if (s->gss_ctxt_lifetime < MIN_CTXT_LIFETIME) s->gss_status |= GSS_CTXT_MAYFAIL; /* * If we're not delegating credentials, rekeying is not used to * refresh them. We must avoid setting GSS_CRED_UPDATED or * GSS_CTXT_EXPIRES when credential delegation is disabled. */ if (!conf_get_bool(s->conf, CONF_gssapifwd)) return; if (s->gss_cred_expiry != GSS_NO_EXPIRATION && difftime(gss_cred_expiry, s->gss_cred_expiry) > 0) s->gss_status |= GSS_CRED_UPDATED; mins = sanitise_rekey_time( conf_get_int(s->conf, CONF_gssapirekey), GSS_DEF_REKEY_MINS); if (mins > 0 && s->gss_ctxt_lifetime <= mins * 60) s->gss_status |= GSS_CTXT_EXPIRES; } #endif /* NO_GSSAPI */ ptrlen ssh2_transport_get_session_id(PacketProtocolLayer *ppl) { struct ssh2_transport_state *s; assert(ppl->vt == &ssh2_transport_vtable); s = container_of(ppl, struct ssh2_transport_state, ppl); assert(s->got_session_id); return make_ptrlen(s->session_id, s->session_id_len); } void ssh2_transport_notify_auth_done(PacketProtocolLayer *ppl) { struct ssh2_transport_state *s; assert(ppl->vt == &ssh2_transport_vtable); s = container_of(ppl, struct ssh2_transport_state, ppl); s->rekey_reason = NULL; /* will be filled in later */ s->rekey_class = RK_POST_USERAUTH; queue_idempotent_callback(&s->ppl.ic_process_queue); } static bool ssh2_transport_get_specials( PacketProtocolLayer *ppl, add_special_fn_t add_special, void *ctx) { struct ssh2_transport_state *s = container_of(ppl, struct ssh2_transport_state, ppl); bool need_separator = false; bool toret = false; if (ssh_ppl_get_specials(s->higher_layer, add_special, ctx)) { need_separator = true; toret = true; } /* * Don't bother offering rekey-based specials if we've decided the * remote won't cope with it, since we wouldn't bother sending it * if asked anyway. */ if (!(s->ppl.remote_bugs & BUG_SSH2_REKEY)) { if (need_separator) { add_special(ctx, NULL, SS_SEP, 0); need_separator = false; } add_special(ctx, "Repeat key exchange", SS_REKEY, 0); toret = true; if (s->n_uncert_hostkeys) { int i; add_special(ctx, NULL, SS_SEP, 0); add_special(ctx, "Cache new host key type", SS_SUBMENU, 0); for (i = 0; i < s->n_uncert_hostkeys; i++) { const ssh_keyalg *alg = ssh2_hostkey_algs[s->uncert_hostkeys[i]].alg; add_special(ctx, alg->ssh_id, SS_XCERT, s->uncert_hostkeys[i]); } add_special(ctx, NULL, SS_EXITMENU, 0); } } return toret; } static void ssh2_transport_special_cmd(PacketProtocolLayer *ppl, SessionSpecialCode code, int arg) { struct ssh2_transport_state *s = container_of(ppl, struct ssh2_transport_state, ppl); if (code == SS_REKEY) { if (!s->kex_in_progress) { s->rekey_reason = "at user request"; s->rekey_class = RK_NORMAL; queue_idempotent_callback(&s->ppl.ic_process_queue); } } else if (code == SS_XCERT) { if (!s->kex_in_progress) { s->cross_certifying = s->hostkey_alg = ssh2_hostkey_algs[arg].alg; s->rekey_reason = "cross-certifying new host key"; s->rekey_class = RK_NORMAL; queue_idempotent_callback(&s->ppl.ic_process_queue); } } else { /* Send everything else to the next layer up. This includes * SS_PING/SS_NOP, which we _could_ handle here - but it's * better to put them in the connection layer, so they'll * still work in bare connection mode. */ ssh_ppl_special_cmd(s->higher_layer, code, arg); } } /* Safely convert rekey_time to unsigned long minutes */ static unsigned long sanitise_rekey_time(int rekey_time, unsigned long def) { if (rekey_time < 0 || rekey_time > MAX_TICK_MINS) rekey_time = def; return (unsigned long)rekey_time; } static void ssh2_transport_set_max_data_size(struct ssh2_transport_state *s) { s->max_data_size = parse_blocksize( conf_get_str(s->conf, CONF_ssh_rekey_data)); } static void ssh2_transport_reconfigure(PacketProtocolLayer *ppl, Conf *conf) { struct ssh2_transport_state *s; const char *rekey_reason = NULL; bool rekey_mandatory = false; unsigned long old_max_data_size, rekey_time; int i; assert(ppl->vt == &ssh2_transport_vtable); s = container_of(ppl, struct ssh2_transport_state, ppl); rekey_time = sanitise_rekey_time( conf_get_int(conf, CONF_ssh_rekey_time), 60); if (ssh2_transport_timer_update(s, rekey_time)) rekey_reason = "timeout shortened"; old_max_data_size = s->max_data_size; ssh2_transport_set_max_data_size(s); if (old_max_data_size != s->max_data_size && s->max_data_size != 0) { if (s->max_data_size < old_max_data_size) { unsigned long diff = old_max_data_size - s->max_data_size; dts_consume(&s->stats->out, diff); dts_consume(&s->stats->in, diff); if (s->stats->out.expired || s->stats->in.expired) rekey_reason = "data limit lowered"; } else { unsigned long diff = s->max_data_size - old_max_data_size; if (s->stats->out.running) s->stats->out.remaining += diff; if (s->stats->in.running) s->stats->in.remaining += diff; } } if (conf_get_bool(s->conf, CONF_compression) != conf_get_bool(conf, CONF_compression)) { rekey_reason = "compression setting changed"; rekey_mandatory = true; } for (i = 0; i < CIPHER_MAX; i++) if (conf_get_int_int(s->conf, CONF_ssh_cipherlist, i) != conf_get_int_int(conf, CONF_ssh_cipherlist, i)) { rekey_reason = "cipher settings changed"; rekey_mandatory = true; } if (conf_get_bool(s->conf, CONF_ssh2_des_cbc) != conf_get_bool(conf, CONF_ssh2_des_cbc)) { rekey_reason = "cipher settings changed"; rekey_mandatory = true; } conf_free(s->conf); s->conf = conf_copy(conf); if (rekey_reason) { if (!s->kex_in_progress && !ssh2_bpp_rekey_inadvisable(s->ppl.bpp)) { s->rekey_reason = rekey_reason; s->rekey_class = RK_NORMAL; queue_idempotent_callback(&s->ppl.ic_process_queue); } else if (rekey_mandatory) { s->deferred_rekey_reason = rekey_reason; } } /* Also pass the configuration along to our higher layer */ ssh_ppl_reconfigure(s->higher_layer, conf); } static bool ssh2_transport_want_user_input(PacketProtocolLayer *ppl) { struct ssh2_transport_state *s = container_of(ppl, struct ssh2_transport_state, ppl); /* Just delegate this to the higher layer */ return ssh_ppl_want_user_input(s->higher_layer); } static void ssh2_transport_got_user_input(PacketProtocolLayer *ppl) { struct ssh2_transport_state *s = container_of(ppl, struct ssh2_transport_state, ppl); /* Just delegate this to the higher layer */ ssh_ppl_got_user_input(s->higher_layer); } static int weak_algorithm_compare(void *av, void *bv) { uintptr_t a = (uintptr_t)av, b = (uintptr_t)bv; return a < b ? -1 : a > b ? +1 : 0; } /* * Wrapper on seat_confirm_weak_crypto_primitive(), which uses the * tree234 s->weak_algorithms_consented_to to ensure we ask at most * once about any given crypto primitive. */ static int ssh2_transport_confirm_weak_crypto_primitive( struct ssh2_transport_state *s, const char *type, const char *name, const void *alg) { if (find234(s->weak_algorithms_consented_to, (void *)alg, NULL)) return 1; add234(s->weak_algorithms_consented_to, (void *)alg); return seat_confirm_weak_crypto_primitive( s->ppl.seat, type, name, ssh2_transport_dialog_callback, s); } static size_t ssh2_transport_queued_data_size(PacketProtocolLayer *ppl) { struct ssh2_transport_state *s = container_of(ppl, struct ssh2_transport_state, ppl); return (ssh_ppl_default_queued_data_size(ppl) + ssh_ppl_queued_data_size(s->higher_layer)); } putty-0.76/ssh2transport.h0000644000175000017500000001774514072266312012602 00000000000000/* * Header connecting the pieces of the SSH-2 transport layer. */ #ifndef PUTTY_SSH2TRANSPORT_H #define PUTTY_SSH2TRANSPORT_H #ifndef NO_GSSAPI #include "sshgssc.h" #include "sshgss.h" #define MIN_CTXT_LIFETIME 5 /* Avoid rekey with short lifetime (seconds) */ #define GSS_KEX_CAPABLE (1<<0) /* Can do GSS KEX */ #define GSS_CRED_UPDATED (1<<1) /* Cred updated since previous delegation */ #define GSS_CTXT_EXPIRES (1<<2) /* Context expires before next timer */ #define GSS_CTXT_MAYFAIL (1<<3) /* Context may expire during handshake */ #endif #define DH_MIN_SIZE 1024 #define DH_MAX_SIZE 8192 #define MAXKEXLIST 16 struct kexinit_algorithm { const char *name; union { struct { const ssh_kex *kex; bool warn; } kex; struct { const ssh_keyalg *hostkey; unsigned hkflags; bool warn; } hk; struct { const ssh_cipheralg *cipher; bool warn; } cipher; struct { const ssh2_macalg *mac; bool etm; } mac; struct { const ssh_compression_alg *comp; bool delayed; } comp; } u; }; #define HOSTKEY_ALGORITHMS(X) \ X(HK_ED25519, ssh_ecdsa_ed25519) \ X(HK_ED448, ssh_ecdsa_ed448) \ X(HK_ECDSA, ssh_ecdsa_nistp256) \ X(HK_ECDSA, ssh_ecdsa_nistp384) \ X(HK_ECDSA, ssh_ecdsa_nistp521) \ X(HK_DSA, ssh_dss) \ X(HK_RSA, ssh_rsa_sha512) \ X(HK_RSA, ssh_rsa_sha256) \ X(HK_RSA, ssh_rsa) \ /* end of list */ #define COUNT_HOSTKEY_ALGORITHM(type, alg) +1 #define N_HOSTKEY_ALGORITHMS (0 HOSTKEY_ALGORITHMS(COUNT_HOSTKEY_ALGORITHM)) struct ssh_signkey_with_user_pref_id { const ssh_keyalg *alg; int id; }; extern const struct ssh_signkey_with_user_pref_id ssh2_hostkey_algs[N_HOSTKEY_ALGORITHMS]; /* * Enumeration of high-level classes of reason why we might need to do * a repeat key exchange. A full detailed reason in human-readable * string form for the Event Log is also provided, but this enum type * is used to discriminate between classes of reason that the code * needs to treat differently. * * RK_NONE == 0 is the value indicating that no rekey is currently * needed at all. RK_INITIAL indicates that we haven't even done the * _first_ key exchange yet. RK_SERVER indicates that we're rekeying * because the server asked for it, not because we decided it * ourselves. RK_NORMAL is the usual case. RK_GSS_UPDATE indicates * that we're rekeying because we've just got new GSSAPI credentials * (hence there's no point in doing a preliminary check for new GSS * creds, because we already know the answer); RK_POST_USERAUTH * indicates that _if_ we're going to need a post-userauth immediate * rekey for any reason, this is the moment to do it. * * So RK_POST_USERAUTH only tells the transport layer to _consider_ * rekeying, not to definitely do it. Also, that one enum value is * special in that the user-readable reason text is passed in to the * transport layer as NULL, whereas fills in the reason text after it * decides whether it needs a rekey at all. In the other cases, * rekey_reason is passed in to the at the same time as rekey_class. */ typedef enum RekeyClass { RK_NONE = 0, RK_INITIAL, RK_SERVER, RK_NORMAL, RK_POST_USERAUTH, RK_GSS_UPDATE } RekeyClass; typedef struct transport_direction { const ssh_cipheralg *cipher; const ssh2_macalg *mac; bool etm_mode; const ssh_compression_alg *comp; bool comp_delayed; int mkkey_adjust; } transport_direction; struct ssh2_transport_state { int crState, crStateKex; PacketProtocolLayer *higher_layer; PktInQueue pq_in_higher; PktOutQueue pq_out_higher; IdempotentCallback ic_pq_out_higher; Conf *conf; char *savedhost; int savedport; const char *rekey_reason; enum RekeyClass rekey_class; unsigned long max_data_size; const ssh_kex *kex_alg; const ssh_keyalg *hostkey_alg; char *hostkey_str; /* string representation, for easy checking in rekeys */ unsigned char session_id[MAX_HASH_LEN]; int session_id_len; int dh_min_size, dh_max_size; bool dh_got_size_bounds; dh_ctx *dh_ctx; ssh_hash *exhash; struct DataTransferStats *stats; const SshServerConfig *ssc; char *client_greeting, *server_greeting; bool kex_in_progress; unsigned long next_rekey, last_rekey; const char *deferred_rekey_reason; bool higher_layer_ok; /* * Fully qualified host name, which we need if doing GSSAPI. */ char *fullhostname; /* shgss is outside the ifdef on purpose to keep APIs simple. If * NO_GSSAPI is not defined, then it's just an opaque structure * tag and the pointer will be NULL. */ struct ssh_connection_shared_gss_state *shgss; #ifndef NO_GSSAPI int gss_status; time_t gss_cred_expiry; /* Re-delegate if newer */ unsigned long gss_ctxt_lifetime; /* Re-delegate when short */ #endif ssh_transient_hostkey_cache *thc; bool gss_kex_used; int nbits, pbits; bool warn_kex, warn_hk, warn_cscipher, warn_sccipher; mp_int *p, *g, *e, *f, *K; strbuf *outgoing_kexinit, *incoming_kexinit; strbuf *client_kexinit, *server_kexinit; /* aliases to the above */ int kex_init_value, kex_reply_value; transport_direction in, out, *cstrans, *sctrans; ptrlen hostkeydata, sigdata; strbuf *hostkeyblob; char *keystr; ssh_key *hkey; /* actual host key */ unsigned hkflags; /* signing flags, used in server */ RSAKey *rsa_kex_key; /* for RSA kex */ bool rsa_kex_key_needs_freeing; ecdh_key *ecdh_key; /* for ECDH kex */ unsigned char exchange_hash[MAX_HASH_LEN]; bool can_gssapi_keyex; bool need_gss_transient_hostkey; bool warned_about_no_gss_transient_hostkey; bool got_session_id; bool can_send_ext_info, post_newkeys_ext_info; int dlgret; bool guessok; bool ignorepkt; struct kexinit_algorithm kexlists[NKEXLIST][MAXKEXLIST]; #ifndef NO_GSSAPI Ssh_gss_buf gss_buf; Ssh_gss_buf gss_rcvtok, gss_sndtok; Ssh_gss_stat gss_stat; Ssh_gss_buf mic; bool init_token_sent; bool complete_rcvd; bool gss_delegate; #endif /* List of crypto primitives below the warning threshold that the * user has already clicked OK to, so that we don't keep asking * about them again during rekeys. This directly stores pointers * to the algorithm vtables, compared by pointer value (which is * not a determinism hazard, because we're only using it as a * set). */ tree234 *weak_algorithms_consented_to; /* * List of host key algorithms for which we _don't_ have a stored * host key. These are indices into the main hostkey_algs[] array */ int uncert_hostkeys[N_HOSTKEY_ALGORITHMS]; int n_uncert_hostkeys; /* * Indicate that the current rekey is intended to finish with a * newly cross-certified host key. To double-check that we * certified the right one, we set this to point to the host key * algorithm we expect it to be. */ const ssh_keyalg *cross_certifying; ssh_key *const *hostkeys; int nhostkeys; PacketProtocolLayer ppl; }; /* Helpers shared between transport and kex */ PktIn *ssh2_transport_pop(struct ssh2_transport_state *s); void ssh2_transport_dialog_callback(void *, int); /* Provided by transport for use in kex */ void ssh2transport_finalise_exhash(struct ssh2_transport_state *s); /* Provided by kex for use in transport. Must set the 'aborted' flag * if it throws a connection-terminating error, so that the caller * won't have to check that by looking inside its state parameter * which might already have been freed. */ void ssh2kex_coroutine(struct ssh2_transport_state *s, bool *aborted); #endif /* PUTTY_SSH2TRANSPORT_H */ putty-0.76/ssh2userauth-server.c0000644000175000017500000003043414072266312013673 00000000000000/* * Packet protocol layer for the server side of the SSH-2 userauth * protocol (RFC 4252). */ #include #include "putty.h" #include "ssh.h" #include "sshbpp.h" #include "sshppl.h" #include "sshcr.h" #include "sshserver.h" #ifndef NO_GSSAPI #include "sshgssc.h" #include "sshgss.h" #endif struct ssh2_userauth_server_state { int crState; PacketProtocolLayer *transport_layer, *successor_layer; ptrlen session_id; AuthPolicy *authpolicy; const SshServerConfig *ssc; ptrlen username, service, method; unsigned methods, this_method; bool partial_success; AuthKbdInt *aki; PacketProtocolLayer ppl; }; static void ssh2_userauth_server_free(PacketProtocolLayer *); static void ssh2_userauth_server_process_queue(PacketProtocolLayer *); static const PacketProtocolLayerVtable ssh2_userauth_server_vtable = { .free = ssh2_userauth_server_free, .process_queue = ssh2_userauth_server_process_queue, .queued_data_size = ssh_ppl_default_queued_data_size, .name = "ssh-userauth", /* other methods are NULL */ }; static void free_auth_kbdint(AuthKbdInt *aki) { int i; if (!aki) return; sfree(aki->title); sfree(aki->instruction); for (i = 0; i < aki->nprompts; i++) sfree(aki->prompts[i].prompt); sfree(aki->prompts); sfree(aki); } PacketProtocolLayer *ssh2_userauth_server_new( PacketProtocolLayer *successor_layer, AuthPolicy *authpolicy, const SshServerConfig *ssc) { struct ssh2_userauth_server_state *s = snew(struct ssh2_userauth_server_state); memset(s, 0, sizeof(*s)); s->ppl.vt = &ssh2_userauth_server_vtable; s->successor_layer = successor_layer; s->authpolicy = authpolicy; s->ssc = ssc; return &s->ppl; } void ssh2_userauth_server_set_transport_layer(PacketProtocolLayer *userauth, PacketProtocolLayer *transport) { struct ssh2_userauth_server_state *s = container_of(userauth, struct ssh2_userauth_server_state, ppl); s->transport_layer = transport; } static void ssh2_userauth_server_free(PacketProtocolLayer *ppl) { struct ssh2_userauth_server_state *s = container_of(ppl, struct ssh2_userauth_server_state, ppl); if (s->successor_layer) ssh_ppl_free(s->successor_layer); free_auth_kbdint(s->aki); sfree(s); } static PktIn *ssh2_userauth_server_pop(struct ssh2_userauth_server_state *s) { return pq_pop(s->ppl.in_pq); } static void ssh2_userauth_server_add_session_id( struct ssh2_userauth_server_state *s, strbuf *sigdata) { if (s->ppl.remote_bugs & BUG_SSH2_PK_SESSIONID) { put_datapl(sigdata, s->session_id); } else { put_stringpl(sigdata, s->session_id); } } static void ssh2_userauth_server_process_queue(PacketProtocolLayer *ppl) { struct ssh2_userauth_server_state *s = container_of(ppl, struct ssh2_userauth_server_state, ppl); PktIn *pktin; PktOut *pktout; crBegin(s->crState); s->session_id = ssh2_transport_get_session_id(s->transport_layer); if (s->ssc->banner.ptr) { pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_USERAUTH_BANNER); put_stringpl(pktout, s->ssc->banner); put_stringz(pktout, ""); /* language tag */ pq_push(s->ppl.out_pq, pktout); } while (1) { crMaybeWaitUntilV((pktin = ssh2_userauth_server_pop(s)) != NULL); if (pktin->type != SSH2_MSG_USERAUTH_REQUEST) { ssh_proto_error(s->ppl.ssh, "Received unexpected packet when " "expecting USERAUTH_REQUEST, type %d (%s)", pktin->type, ssh2_pkt_type(s->ppl.bpp->pls->kctx, s->ppl.bpp->pls->actx, pktin->type)); return; } s->username = get_string(pktin); s->service = get_string(pktin); s->method = get_string(pktin); if (!ptrlen_eq_string(s->service, s->successor_layer->vt->name)) { /* * Unconditionally reject authentication for any service * other than the one we're going to hand over to. */ pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_USERAUTH_FAILURE); put_stringz(pktout, ""); put_bool(pktout, false); pq_push(s->ppl.out_pq, pktout); continue; } s->methods = auth_methods(s->authpolicy); s->partial_success = false; if (ptrlen_eq_string(s->method, "none")) { s->this_method = AUTHMETHOD_NONE; if (!(s->methods & s->this_method)) goto failure; if (!auth_none(s->authpolicy, s->username)) goto failure; } else if (ptrlen_eq_string(s->method, "password")) { bool changing; ptrlen password, new_password, *new_password_ptr; s->this_method = AUTHMETHOD_PASSWORD; if (!(s->methods & s->this_method)) goto failure; changing = get_bool(pktin); password = get_string(pktin); if (changing) { new_password = get_string(pktin); new_password_ptr = &new_password; } else { new_password_ptr = NULL; } int result = auth_password(s->authpolicy, s->username, password, new_password_ptr); if (result == 2) { pktout = ssh_bpp_new_pktout( s->ppl.bpp, SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ); put_stringz(pktout, "Please change your password"); put_stringz(pktout, ""); /* language tag */ pq_push(s->ppl.out_pq, pktout); continue; /* skip USERAUTH_{SUCCESS,FAILURE} epilogue */ } else if (result != 1) { goto failure; } } else if (ptrlen_eq_string(s->method, "publickey")) { bool has_signature, success, send_pk_ok, key_really_ok; ptrlen algorithm, blob, signature; const ssh_keyalg *keyalg; ssh_key *key; strbuf *sigdata; s->this_method = AUTHMETHOD_PUBLICKEY; if (!(s->methods & s->this_method)) goto failure; has_signature = get_bool(pktin); algorithm = get_string(pktin); blob = get_string(pktin); key_really_ok = auth_publickey(s->authpolicy, s->username, blob); send_pk_ok = key_really_ok || s->ssc->stunt_pretend_to_accept_any_pubkey; if (!has_signature) { if (!send_pk_ok) goto failure; pktout = ssh_bpp_new_pktout( s->ppl.bpp, SSH2_MSG_USERAUTH_PK_OK); put_stringpl(pktout, algorithm); put_stringpl(pktout, blob); pq_push(s->ppl.out_pq, pktout); continue; /* skip USERAUTH_{SUCCESS,FAILURE} epilogue */ } if (!key_really_ok) goto failure; keyalg = find_pubkey_alg_len(algorithm); if (!keyalg) goto failure; key = ssh_key_new_pub(keyalg, blob); if (!key) goto failure; sigdata = strbuf_new(); ssh2_userauth_server_add_session_id(s, sigdata); put_byte(sigdata, SSH2_MSG_USERAUTH_REQUEST); put_stringpl(sigdata, s->username); put_stringpl(sigdata, s->service); put_stringpl(sigdata, s->method); put_bool(sigdata, has_signature); put_stringpl(sigdata, algorithm); put_stringpl(sigdata, blob); signature = get_string(pktin); success = ssh_key_verify(key, signature, ptrlen_from_strbuf(sigdata)); ssh_key_free(key); strbuf_free(sigdata); if (!success) goto failure; } else if (ptrlen_eq_string(s->method, "keyboard-interactive")) { int i, ok; unsigned n; s->this_method = AUTHMETHOD_KBDINT; if (!(s->methods & s->this_method)) goto failure; do { s->aki = auth_kbdint_prompts(s->authpolicy, s->username); if (!s->aki) goto failure; pktout = ssh_bpp_new_pktout( s->ppl.bpp, SSH2_MSG_USERAUTH_INFO_REQUEST); put_stringz(pktout, s->aki->title); put_stringz(pktout, s->aki->instruction); put_stringz(pktout, ""); /* language tag */ put_uint32(pktout, s->aki->nprompts); for (i = 0; i < s->aki->nprompts; i++) { put_stringz(pktout, s->aki->prompts[i].prompt); put_bool(pktout, s->aki->prompts[i].echo); } pq_push(s->ppl.out_pq, pktout); crMaybeWaitUntilV( (pktin = ssh2_userauth_server_pop(s)) != NULL); if (pktin->type != SSH2_MSG_USERAUTH_INFO_RESPONSE) { ssh_proto_error( s->ppl.ssh, "Received unexpected packet when " "expecting USERAUTH_INFO_RESPONSE, type %d (%s)", pktin->type, ssh2_pkt_type(s->ppl.bpp->pls->kctx, s->ppl.bpp->pls->actx, pktin->type)); return; } n = get_uint32(pktin); if (n != s->aki->nprompts) { ssh_proto_error( s->ppl.ssh, "Received %u keyboard-interactive " "responses after sending %u prompts", n, s->aki->nprompts); return; } { ptrlen *responses = snewn(s->aki->nprompts, ptrlen); for (i = 0; i < s->aki->nprompts; i++) responses[i] = get_string(pktin); ok = auth_kbdint_responses(s->authpolicy, responses); sfree(responses); } free_auth_kbdint(s->aki); s->aki = NULL; } while (ok == 0); if (ok <= 0) goto failure; } else { goto failure; } /* * If we get here, we've successfully completed this * authentication step. */ if (auth_successful(s->authpolicy, s->username, s->this_method)) { /* * ... and it was the last one, so we're completely done. */ pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_USERAUTH_SUCCESS); pq_push(s->ppl.out_pq, pktout); break; } else { /* * ... but another is required, so fall through to * generation of USERAUTH_FAILURE, having first refreshed * the bit mask of available methods. */ s->methods = auth_methods(s->authpolicy); } s->partial_success = true; failure: pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_USERAUTH_FAILURE); { strbuf *list = strbuf_new(); if (s->methods & AUTHMETHOD_NONE) add_to_commasep(list, "none"); if (s->methods & AUTHMETHOD_PASSWORD) add_to_commasep(list, "password"); if (s->methods & AUTHMETHOD_PUBLICKEY) add_to_commasep(list, "publickey"); if (s->methods & AUTHMETHOD_KBDINT) add_to_commasep(list, "keyboard-interactive"); put_stringsb(pktout, list); } put_bool(pktout, s->partial_success); pq_push(s->ppl.out_pq, pktout); } /* * Finally, hand over to our successor layer, and return * immediately without reaching the crFinishV: ssh_ppl_replace * will have freed us, so crFinishV's zeroing-out of crState would * be a use-after-free bug. */ { PacketProtocolLayer *successor = s->successor_layer; s->successor_layer = NULL; /* avoid freeing it ourself */ ssh_ppl_replace(&s->ppl, successor); return; /* we've just freed s, so avoid even touching s->crState */ } crFinishV; } putty-0.76/ssh2userauth.c0000644000175000017500000024106314072266312012371 00000000000000/* * Packet protocol layer for the client side of the SSH-2 userauth * protocol (RFC 4252). */ #include #include "putty.h" #include "ssh.h" #include "sshbpp.h" #include "sshppl.h" #include "sshcr.h" #ifndef NO_GSSAPI #include "sshgssc.h" #include "sshgss.h" #endif #define BANNER_LIMIT 131072 typedef struct agent_key { strbuf *blob, *comment; ptrlen algorithm; } agent_key; struct ssh2_userauth_state { int crState; PacketProtocolLayer *transport_layer, *successor_layer; Filename *keyfile; bool show_banner, tryagent, notrivialauth, change_username; char *hostname, *fullhostname; char *default_username; bool try_ki_auth, try_gssapi_auth, try_gssapi_kex_auth, gssapi_fwd; ptrlen session_id; 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; bool need_pw, can_pubkey, can_passwd, can_keyb_inter; int userpass_ret; bool tried_pubkey_config, done_agent; struct ssh_connection_shared_gss_state *shgss; #ifndef NO_GSSAPI bool can_gssapi; bool can_gssapi_keyex_auth; bool tried_gssapi; bool tried_gssapi_keyex_auth; time_t gss_cred_expiry; Ssh_gss_buf gss_buf; Ssh_gss_buf gss_rcvtok, gss_sndtok; Ssh_gss_stat gss_stat; #endif bool suppress_wait_for_response_packet; strbuf *last_methods_string; bool kbd_inter_refused; prompts_t *cur_prompt; uint32_t num_prompts; const char *username; char *locally_allocated_username; char *password; bool got_username; strbuf *publickey_blob; bool privatekey_available, privatekey_encrypted; char *publickey_algorithm; char *publickey_comment; void *agent_response_to_free; ptrlen agent_response; BinarySource asrc[1]; /* for reading SSH agent response */ size_t agent_keys_len; agent_key *agent_keys; size_t agent_key_index, agent_key_limit; ptrlen agent_keyalg; unsigned signflags; int len; PktOut *pktout; bool want_user_input; bool is_trivial_auth; agent_pending_query *auth_agent_query; bufchain banner; bufchain_sink banner_bs; StripCtrlChars *banner_scc; bool banner_scc_initialised; StripCtrlChars *ki_scc; bool ki_scc_initialised; bool ki_printed_header; PacketProtocolLayer ppl; }; static void ssh2_userauth_free(PacketProtocolLayer *); static void ssh2_userauth_process_queue(PacketProtocolLayer *); static bool ssh2_userauth_get_specials( PacketProtocolLayer *ppl, add_special_fn_t add_special, void *ctx); static void ssh2_userauth_special_cmd(PacketProtocolLayer *ppl, SessionSpecialCode code, int arg); static bool ssh2_userauth_want_user_input(PacketProtocolLayer *ppl); static void ssh2_userauth_got_user_input(PacketProtocolLayer *ppl); static void ssh2_userauth_reconfigure(PacketProtocolLayer *ppl, Conf *conf); static void ssh2_userauth_agent_query(struct ssh2_userauth_state *, strbuf *); static void ssh2_userauth_agent_callback(void *, void *, int); static void ssh2_userauth_add_sigblob( struct ssh2_userauth_state *s, PktOut *pkt, ptrlen pkblob, ptrlen sigblob); static void ssh2_userauth_add_session_id( struct ssh2_userauth_state *s, strbuf *sigdata); #ifndef NO_GSSAPI static PktOut *ssh2_userauth_gss_packet( struct ssh2_userauth_state *s, const char *authtype); #endif static void ssh2_userauth_antispoof_msg( struct ssh2_userauth_state *s, const char *msg); static const PacketProtocolLayerVtable ssh2_userauth_vtable = { .free = ssh2_userauth_free, .process_queue = ssh2_userauth_process_queue, .get_specials = ssh2_userauth_get_specials, .special_cmd = ssh2_userauth_special_cmd, .want_user_input = ssh2_userauth_want_user_input, .got_user_input = ssh2_userauth_got_user_input, .reconfigure = ssh2_userauth_reconfigure, .queued_data_size = ssh_ppl_default_queued_data_size, .name = "ssh-userauth", }; PacketProtocolLayer *ssh2_userauth_new( PacketProtocolLayer *successor_layer, const char *hostname, const char *fullhostname, Filename *keyfile, bool show_banner, bool tryagent, bool notrivialauth, const char *default_username, bool change_username, bool try_ki_auth, bool try_gssapi_auth, bool try_gssapi_kex_auth, bool gssapi_fwd, struct ssh_connection_shared_gss_state *shgss) { struct ssh2_userauth_state *s = snew(struct ssh2_userauth_state); memset(s, 0, sizeof(*s)); s->ppl.vt = &ssh2_userauth_vtable; s->successor_layer = successor_layer; s->hostname = dupstr(hostname); s->fullhostname = dupstr(fullhostname); s->keyfile = filename_copy(keyfile); s->show_banner = show_banner; s->tryagent = tryagent; s->notrivialauth = notrivialauth; s->default_username = dupstr(default_username); s->change_username = change_username; s->try_ki_auth = try_ki_auth; s->try_gssapi_auth = try_gssapi_auth; s->try_gssapi_kex_auth = try_gssapi_kex_auth; s->gssapi_fwd = gssapi_fwd; s->shgss = shgss; s->last_methods_string = strbuf_new(); s->is_trivial_auth = true; bufchain_init(&s->banner); bufchain_sink_init(&s->banner_bs, &s->banner); return &s->ppl; } void ssh2_userauth_set_transport_layer(PacketProtocolLayer *userauth, PacketProtocolLayer *transport) { struct ssh2_userauth_state *s = container_of(userauth, struct ssh2_userauth_state, ppl); s->transport_layer = transport; } static void ssh2_userauth_free(PacketProtocolLayer *ppl) { struct ssh2_userauth_state *s = container_of(ppl, struct ssh2_userauth_state, ppl); bufchain_clear(&s->banner); if (s->successor_layer) ssh_ppl_free(s->successor_layer); if (s->agent_keys) { for (size_t i = 0; i < s->agent_keys_len; i++) { strbuf_free(s->agent_keys[i].blob); strbuf_free(s->agent_keys[i].comment); } sfree(s->agent_keys); } sfree(s->agent_response_to_free); if (s->auth_agent_query) agent_cancel_query(s->auth_agent_query); filename_free(s->keyfile); sfree(s->default_username); sfree(s->locally_allocated_username); sfree(s->hostname); sfree(s->fullhostname); sfree(s->publickey_comment); sfree(s->publickey_algorithm); if (s->publickey_blob) strbuf_free(s->publickey_blob); strbuf_free(s->last_methods_string); if (s->banner_scc) stripctrl_free(s->banner_scc); if (s->ki_scc) stripctrl_free(s->ki_scc); sfree(s); } static void ssh2_userauth_filter_queue(struct ssh2_userauth_state *s) { PktIn *pktin; ptrlen string; while ((pktin = pq_peek(s->ppl.in_pq)) != NULL) { switch (pktin->type) { case SSH2_MSG_USERAUTH_BANNER: if (!s->show_banner) { pq_pop(s->ppl.in_pq); break; } string = get_string(pktin); if (string.len > BANNER_LIMIT - bufchain_size(&s->banner)) string.len = BANNER_LIMIT - bufchain_size(&s->banner); if (!s->banner_scc_initialised) { s->banner_scc = seat_stripctrl_new( s->ppl.seat, BinarySink_UPCAST(&s->banner_bs), SIC_BANNER); if (s->banner_scc) stripctrl_enable_line_limiting(s->banner_scc); s->banner_scc_initialised = true; } if (s->banner_scc) put_datapl(s->banner_scc, string); else put_datapl(&s->banner_bs, string); pq_pop(s->ppl.in_pq); break; default: return; } } } static PktIn *ssh2_userauth_pop(struct ssh2_userauth_state *s) { ssh2_userauth_filter_queue(s); return pq_pop(s->ppl.in_pq); } static void ssh2_userauth_process_queue(PacketProtocolLayer *ppl) { struct ssh2_userauth_state *s = container_of(ppl, struct ssh2_userauth_state, ppl); PktIn *pktin; ssh2_userauth_filter_queue(s); /* no matter why we were called */ crBegin(s->crState); #ifndef NO_GSSAPI s->tried_gssapi = false; s->tried_gssapi_keyex_auth = false; #endif /* * Misc one-time setup for authentication. */ s->publickey_blob = NULL; s->session_id = ssh2_transport_get_session_id(s->transport_layer); /* * Load the public half of any configured public key file for * later use. */ if (!filename_is_null(s->keyfile)) { int keytype; ppl_logevent("Reading key file \"%s\"", filename_to_str(s->keyfile)); keytype = key_type(s->keyfile); if (keytype == SSH_KEYTYPE_SSH2 || keytype == SSH_KEYTYPE_SSH2_PUBLIC_RFC4716 || keytype == SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH) { const char *error; s->publickey_blob = strbuf_new(); if (ppk_loadpub_f(s->keyfile, &s->publickey_algorithm, BinarySink_UPCAST(s->publickey_blob), &s->publickey_comment, &error)) { s->privatekey_available = (keytype == SSH_KEYTYPE_SSH2); if (!s->privatekey_available) ppl_logevent("Key file contains public key only"); s->privatekey_encrypted = ppk_encrypted_f(s->keyfile, NULL); } else { ppl_logevent("Unable to load key (%s)", error); ppl_printf("Unable to load key file \"%s\" (%s)\r\n", filename_to_str(s->keyfile), error); strbuf_free(s->publickey_blob); s->publickey_blob = NULL; } } else { ppl_logevent("Unable to use this key file (%s)", key_type_to_str(keytype)); ppl_printf("Unable to use key file \"%s\" (%s)\r\n", filename_to_str(s->keyfile), key_type_to_str(keytype)); s->publickey_blob = NULL; } } /* * Find out about any keys Pageant has (but if there's a public * key configured, filter out all others). */ if (s->tryagent && agent_exists()) { ppl_logevent("Pageant is running. Requesting keys."); /* Request the keys held by the agent. */ { strbuf *request = strbuf_new_for_agent_query(); put_byte(request, SSH2_AGENTC_REQUEST_IDENTITIES); ssh2_userauth_agent_query(s, request); strbuf_free(request); crWaitUntilV(!s->auth_agent_query); } BinarySource_BARE_INIT_PL(s->asrc, s->agent_response); get_uint32(s->asrc); /* skip length field */ if (get_byte(s->asrc) == SSH2_AGENT_IDENTITIES_ANSWER) { size_t nkeys = get_uint32(s->asrc); size_t origpos = s->asrc->pos; /* * Check that the agent response is well formed. */ for (size_t i = 0; i < nkeys; i++) { get_string(s->asrc); /* blob */ get_string(s->asrc); /* comment */ if (get_err(s->asrc)) { ppl_logevent("Pageant's response was truncated"); goto done_agent_query; } } /* * Copy the list of public-key blobs out of the Pageant * response. */ BinarySource_REWIND_TO(s->asrc, origpos); s->agent_keys_len = nkeys; s->agent_keys = snewn(s->agent_keys_len, agent_key); for (size_t i = 0; i < nkeys; i++) { s->agent_keys[i].blob = strbuf_new(); put_datapl(s->agent_keys[i].blob, get_string(s->asrc)); s->agent_keys[i].comment = strbuf_new(); put_datapl(s->agent_keys[i].comment, get_string(s->asrc)); /* Also, extract the algorithm string from the start * of the public-key blob. */ BinarySource src[1]; BinarySource_BARE_INIT_PL(src, ptrlen_from_strbuf( s->agent_keys[i].blob)); s->agent_keys[i].algorithm = get_string(src); } ppl_logevent("Pageant has %"SIZEu" SSH-2 keys", nkeys); if (s->publickey_blob) { /* * If we've been given a specific public key blob, * filter the list of keys to try from the agent down * to only that one, or none if it's not there. */ ptrlen our_blob = ptrlen_from_strbuf(s->publickey_blob); size_t i; for (i = 0; i < nkeys; i++) { if (ptrlen_eq_ptrlen(our_blob, ptrlen_from_strbuf( s->agent_keys[i].blob))) break; } if (i < nkeys) { ppl_logevent("Pageant key #%"SIZEu" matches " "configured key file", i); s->agent_key_index = i; s->agent_key_limit = i+1; } else { ppl_logevent("Configured key file not in Pageant"); s->agent_key_index = 0; s->agent_key_limit = 0; } } else { /* * Otherwise, try them all. */ s->agent_key_index = 0; s->agent_key_limit = nkeys; } } else { ppl_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 * types the wrong _password_, they can be sent back to the * beginning to try another username, if this is configured on. * (If they specify a username in the config, they are never * asked, even if they do give a wrong password.) * * I think this best serves the needs of * * - the people who have no configuration, no keys, and just * want to try repeated (username,password) pairs until they * type both correctly * * - people who have keys and configuration but occasionally * need to fall back to passwords * * - 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 (1) { /* * Get a username. */ if (s->got_username && !s->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 ((s->username = s->default_username) == NULL) { s->cur_prompt = new_prompts(); s->cur_prompt->to_server = true; s->cur_prompt->from_server = false; s->cur_prompt->name = dupstr("SSH login name"); add_prompt(s->cur_prompt, dupstr("login as: "), true); s->userpass_ret = seat_get_userpass_input( s->ppl.seat, s->cur_prompt, NULL); while (1) { while (s->userpass_ret < 0 && bufchain_size(s->ppl.user_input) > 0) s->userpass_ret = seat_get_userpass_input( s->ppl.seat, s->cur_prompt, s->ppl.user_input); if (s->userpass_ret >= 0) break; s->want_user_input = true; crReturnV; s->want_user_input = false; } if (!s->userpass_ret) { /* * seat_get_userpass_input() failed to get a username. * Terminate. */ free_prompts(s->cur_prompt); ssh_user_close(s->ppl.ssh, "No username provided"); return; } sfree(s->locally_allocated_username); /* for change_username */ s->username = s->locally_allocated_username = prompt_get_result(s->cur_prompt->prompts[0]); free_prompts(s->cur_prompt); } else { if (seat_verbose(s->ppl.seat) || seat_interactive(s->ppl.seat)) ppl_printf("Using username \"%s\".\r\n", s->username); } 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. */ s->ppl.bpp->pls->actx = SSH2_PKTCTX_NOAUTH; s->pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_USERAUTH_REQUEST); put_stringz(s->pktout, s->username); put_stringz(s->pktout, s->successor_layer->vt->name); put_stringz(s->pktout, "none"); /* method */ pq_push(s->ppl.out_pq, s->pktout); s->type = AUTH_TYPE_NONE; s->tried_pubkey_config = false; s->kbd_inter_refused = false; s->done_agent = false; while (1) { /* * Wait for the result of the last authentication request, * unless the request terminated for some reason on our * own side. */ if (s->suppress_wait_for_response_packet) { pktin = NULL; s->suppress_wait_for_response_packet = false; } else { crMaybeWaitUntilV((pktin = ssh2_userauth_pop(s)) != NULL); } /* * Now is a convenient point to spew any banner material * that we've accumulated. (This should ensure that when * we exit the auth loop, we haven't any left to deal * with.) * * Don't show the banner if we're operating in non-verbose * non-interactive mode. (It's probably a script, which * means nobody will read the banner _anyway_, and * moreover the printing of the banner will screw up * processing on the output of (say) plink.) * * The banner data has been sanitised already by this * point, but we still need to precede and follow it with * anti-spoofing header lines. */ if (bufchain_size(&s->banner) && (seat_verbose(s->ppl.seat) || seat_interactive(s->ppl.seat))) { if (s->banner_scc) { ssh2_userauth_antispoof_msg( s, "Pre-authentication banner message from server:"); seat_set_trust_status(s->ppl.seat, false); } bool mid_line = false; while (bufchain_size(&s->banner) > 0) { ptrlen data = bufchain_prefix(&s->banner); seat_stderr_pl(s->ppl.seat, data); mid_line = (((const char *)data.ptr)[data.len-1] != '\n'); bufchain_consume(&s->banner, data.len); } bufchain_clear(&s->banner); if (mid_line) seat_stderr_pl(s->ppl.seat, PTRLEN_LITERAL("\r\n")); if (s->banner_scc) { seat_set_trust_status(s->ppl.seat, true); ssh2_userauth_antispoof_msg( s, "End of banner message from server"); } } if (pktin && pktin->type == SSH2_MSG_USERAUTH_SUCCESS) { ppl_logevent("Access granted"); goto userauth_success; } if (pktin && pktin->type != SSH2_MSG_USERAUTH_FAILURE && s->type != AUTH_TYPE_GSSAPI) { ssh_proto_error(s->ppl.ssh, "Received unexpected packet " "in response to authentication request, " "type %d (%s)", pktin->type, ssh2_pkt_type(s->ppl.bpp->pls->kctx, s->ppl.bpp->pls->actx, pktin->type)); return; } /* * OK, we're now sitting on a USERAUTH_FAILURE message, so * we can look at the string in it and know what we can * helpfully try next. */ if (pktin && pktin->type == SSH2_MSG_USERAUTH_FAILURE) { ptrlen methods = get_string(pktin); bool partial_success = get_bool(pktin); if (!partial_success) { /* * We have received an unequivocal Access * Denied. This can translate to a variety of * messages, or no message at all. * * For forms of authentication which are attempted * implicitly, by which I mean without printing * anything in the window indicating that we're * trying them, we should never print 'Access * denied'. * * If we do print a message saying that we're * attempting some kind of authentication, it's OK * to print a followup message saying it failed - * but the message may sometimes be more specific * than simply 'Access denied'. * * Additionally, if we'd just tried password * authentication, we should break out of this * whole loop so as to go back to the username * prompt (iff we're configured to allow * username change attempts). */ if (s->type == AUTH_TYPE_NONE) { /* do nothing */ } else if (s->type == AUTH_TYPE_PUBLICKEY_OFFER_LOUD || s->type == AUTH_TYPE_PUBLICKEY_OFFER_QUIET) { if (s->type == AUTH_TYPE_PUBLICKEY_OFFER_LOUD) ppl_printf("Server refused our key\r\n"); ppl_logevent("Server refused our key"); } else if (s->type == AUTH_TYPE_PUBLICKEY) { /* This _shouldn't_ happen except by a * protocol bug causing client and server to * disagree on what is a correct signature. */ ppl_printf("Server refused public-key signature" " despite accepting key!\r\n"); ppl_logevent("Server refused public-key signature" " despite accepting key!"); } else if (s->type==AUTH_TYPE_KEYBOARD_INTERACTIVE_QUIET) { /* quiet, so no ppl_printf */ ppl_logevent("Server refused keyboard-interactive " "authentication"); } else if (s->type==AUTH_TYPE_GSSAPI) { /* always quiet, so no ppl_printf */ /* also, the code down in the GSSAPI block has * already logged this in the Event Log */ } else if (s->type == AUTH_TYPE_KEYBOARD_INTERACTIVE) { ppl_logevent("Keyboard-interactive authentication " "failed"); ppl_printf("Access denied\r\n"); } else { assert(s->type == AUTH_TYPE_PASSWORD); ppl_logevent("Password authentication failed"); ppl_printf("Access denied\r\n"); if (s->change_username) { /* XXX perhaps we should allow * keyboard-interactive to do this too? */ goto try_new_username; } } } else { ppl_printf("Further authentication required\r\n"); ppl_logevent("Further authentication required"); } /* * Save the methods string for use in error messages. */ strbuf_clear(s->last_methods_string); put_datapl(s->last_methods_string, methods); /* * Scan it for method identifiers we know about. */ bool srv_pubkey = false, srv_passwd = false; bool srv_keyb_inter = false; #ifndef NO_GSSAPI bool srv_gssapi = false, srv_gssapi_keyex_auth = false; #endif for (ptrlen method; get_commasep_word(&methods, &method) ;) { if (ptrlen_eq_string(method, "publickey")) srv_pubkey = true; else if (ptrlen_eq_string(method, "password")) srv_passwd = true; else if (ptrlen_eq_string(method, "keyboard-interactive")) srv_keyb_inter = true; #ifndef NO_GSSAPI else if (ptrlen_eq_string(method, "gssapi-with-mic")) srv_gssapi = true; else if (ptrlen_eq_string(method, "gssapi-keyex")) srv_gssapi_keyex_auth = true; #endif } /* * And combine those flags with our own configuration * and context to set the main can_foo variables. */ s->can_pubkey = srv_pubkey; s->can_passwd = srv_passwd; s->can_keyb_inter = s->try_ki_auth && srv_keyb_inter; #ifndef NO_GSSAPI s->can_gssapi = s->try_gssapi_auth && srv_gssapi && s->shgss->libs->nlibraries > 0; s->can_gssapi_keyex_auth = s->try_gssapi_kex_auth && srv_gssapi_keyex_auth && s->shgss->libs->nlibraries > 0 && s->shgss->ctx; #endif } s->ppl.bpp->pls->actx = SSH2_PKTCTX_NOAUTH; #ifndef NO_GSSAPI if (s->can_gssapi_keyex_auth && !s->tried_gssapi_keyex_auth) { /* gssapi-keyex authentication */ s->type = AUTH_TYPE_GSSAPI; s->tried_gssapi_keyex_auth = true; s->ppl.bpp->pls->actx = SSH2_PKTCTX_GSSAPI; if (s->shgss->lib->gsslogmsg) ppl_logevent("%s", s->shgss->lib->gsslogmsg); ppl_logevent("Trying gssapi-keyex..."); s->pktout = ssh2_userauth_gss_packet(s, "gssapi-keyex"); pq_push(s->ppl.out_pq, s->pktout); s->shgss->lib->release_cred(s->shgss->lib, &s->shgss->ctx); s->shgss->ctx = NULL; continue; } else #endif /* NO_GSSAPI */ if (s->can_pubkey && !s->done_agent && s->agent_key_index < s->agent_key_limit) { /* * Attempt public-key authentication using a key from Pageant. */ s->agent_keyalg = s->agent_keys[s->agent_key_index].algorithm; s->signflags = 0; if (ptrlen_eq_string(s->agent_keyalg, "ssh-rsa")) { /* Try to upgrade ssh-rsa to one of the rsa-sha2-* family, * if the server has announced support for them. */ if (s->ppl.bpp->ext_info_rsa_sha512_ok) { s->agent_keyalg = PTRLEN_LITERAL("rsa-sha2-512"); s->signflags = SSH_AGENT_RSA_SHA2_512; } else if (s->ppl.bpp->ext_info_rsa_sha256_ok) { s->agent_keyalg = PTRLEN_LITERAL("rsa-sha2-256"); s->signflags = SSH_AGENT_RSA_SHA2_256; } } s->ppl.bpp->pls->actx = SSH2_PKTCTX_PUBLICKEY; ppl_logevent("Trying Pageant key #%"SIZEu, s->agent_key_index); /* See if server will accept it */ s->pktout = ssh_bpp_new_pktout( s->ppl.bpp, SSH2_MSG_USERAUTH_REQUEST); put_stringz(s->pktout, s->username); put_stringz(s->pktout, s->successor_layer->vt->name); put_stringz(s->pktout, "publickey"); /* method */ put_bool(s->pktout, false); /* no signature included */ put_stringpl(s->pktout, s->agent_keyalg); put_stringpl(s->pktout, ptrlen_from_strbuf( s->agent_keys[s->agent_key_index].blob)); pq_push(s->ppl.out_pq, s->pktout); s->type = AUTH_TYPE_PUBLICKEY_OFFER_QUIET; crMaybeWaitUntilV((pktin = ssh2_userauth_pop(s)) != NULL); if (pktin->type != SSH2_MSG_USERAUTH_PK_OK) { /* Offer of key refused, presumably via * USERAUTH_FAILURE. Requeue for the next iteration. */ pq_push_front(s->ppl.in_pq, pktin); } else { strbuf *agentreq, *sigdata; ptrlen comment = ptrlen_from_strbuf( s->agent_keys[s->agent_key_index].comment); if (seat_verbose(s->ppl.seat)) ppl_printf("Authenticating with public key " "\"%.*s\" from agent\r\n", PTRLEN_PRINTF(comment)); /* * Server is willing to accept the key. * Construct a SIGN_REQUEST. */ s->pktout = ssh_bpp_new_pktout( s->ppl.bpp, SSH2_MSG_USERAUTH_REQUEST); put_stringz(s->pktout, s->username); put_stringz(s->pktout, s->successor_layer->vt->name); put_stringz(s->pktout, "publickey"); /* method */ put_bool(s->pktout, true); /* signature included */ put_stringpl(s->pktout, s->agent_keyalg); put_stringpl(s->pktout, ptrlen_from_strbuf( s->agent_keys[s->agent_key_index].blob)); /* Ask agent for signature. */ agentreq = strbuf_new_for_agent_query(); put_byte(agentreq, SSH2_AGENTC_SIGN_REQUEST); put_stringpl(agentreq, ptrlen_from_strbuf( s->agent_keys[s->agent_key_index].blob)); /* Now the data to be signed... */ sigdata = strbuf_new(); ssh2_userauth_add_session_id(s, sigdata); put_data(sigdata, s->pktout->data + 5, s->pktout->length - 5); put_stringsb(agentreq, sigdata); /* And finally the flags word. */ put_uint32(agentreq, s->signflags); ssh2_userauth_agent_query(s, agentreq); strbuf_free(agentreq); crWaitUntilV(!s->auth_agent_query); if (s->agent_response.ptr) { ptrlen sigblob; BinarySource src[1]; BinarySource_BARE_INIT(src, s->agent_response.ptr, s->agent_response.len); get_uint32(src); /* skip length field */ if (get_byte(src) == SSH2_AGENT_SIGN_RESPONSE && (sigblob = get_string(src), !get_err(src))) { ppl_logevent("Sending Pageant's response"); ssh2_userauth_add_sigblob( s, s->pktout, ptrlen_from_strbuf( s->agent_keys[s->agent_key_index].blob), sigblob); pq_push(s->ppl.out_pq, s->pktout); s->type = AUTH_TYPE_PUBLICKEY; s->is_trivial_auth = false; } else { ppl_logevent("Pageant refused signing request"); ppl_printf("Pageant failed to " "provide a signature\r\n"); s->suppress_wait_for_response_packet = true; ssh_free_pktout(s->pktout); } } else { ppl_logevent("Pageant failed to respond to " "signing request"); ppl_printf("Pageant failed to " "respond to signing request\r\n"); s->suppress_wait_for_response_packet = true; ssh_free_pktout(s->pktout); } } /* Do we have any keys left to try? */ if (++s->agent_key_index >= s->agent_key_limit) s->done_agent = true; } else if (s->can_pubkey && s->publickey_blob && s->privatekey_available && !s->tried_pubkey_config) { ssh2_userkey *key; /* not live over crReturn */ char *passphrase; /* not live over crReturn */ s->ppl.bpp->pls->actx = SSH2_PKTCTX_PUBLICKEY; s->tried_pubkey_config = true; /* * Try the public key supplied in the configuration. * * First, try to upgrade its algorithm. */ if (!strcmp(s->publickey_algorithm, "ssh-rsa")) { /* Try to upgrade ssh-rsa to one of the rsa-sha2-* family, * if the server has announced support for them. */ if (s->ppl.bpp->ext_info_rsa_sha512_ok) { sfree(s->publickey_algorithm); s->publickey_algorithm = dupstr("rsa-sha2-512"); s->signflags = SSH_AGENT_RSA_SHA2_512; } else if (s->ppl.bpp->ext_info_rsa_sha256_ok) { sfree(s->publickey_algorithm); s->publickey_algorithm = dupstr("rsa-sha2-256"); s->signflags = SSH_AGENT_RSA_SHA2_256; } } /* * Offer the public blob to see if the server is willing to * accept it. */ s->pktout = ssh_bpp_new_pktout( s->ppl.bpp, SSH2_MSG_USERAUTH_REQUEST); put_stringz(s->pktout, s->username); put_stringz(s->pktout, s->successor_layer->vt->name); put_stringz(s->pktout, "publickey"); /* method */ put_bool(s->pktout, false); /* no signature included */ put_stringz(s->pktout, s->publickey_algorithm); put_string(s->pktout, s->publickey_blob->s, s->publickey_blob->len); pq_push(s->ppl.out_pq, s->pktout); ppl_logevent("Offered public key"); crMaybeWaitUntilV((pktin = ssh2_userauth_pop(s)) != NULL); if (pktin->type != SSH2_MSG_USERAUTH_PK_OK) { /* Key refused. Give up. */ pq_push_front(s->ppl.in_pq, pktin); s->type = AUTH_TYPE_PUBLICKEY_OFFER_LOUD; continue; /* process this new message */ } ppl_logevent("Offer of public key accepted"); /* * Actually attempt a serious authentication using * the key. */ if (seat_verbose(s->ppl.seat)) ppl_printf("Authenticating with public key \"%s\"\r\n", s->publickey_comment); key = NULL; while (!key) { const char *error; /* not live over crReturn */ if (s->privatekey_encrypted) { /* * Get a passphrase from the user. */ s->cur_prompt = new_prompts(); s->cur_prompt->to_server = false; s->cur_prompt->from_server = false; s->cur_prompt->name = dupstr("SSH key passphrase"); add_prompt(s->cur_prompt, dupprintf("Passphrase for key \"%s\": ", s->publickey_comment), false); s->userpass_ret = seat_get_userpass_input( s->ppl.seat, s->cur_prompt, NULL); while (1) { while (s->userpass_ret < 0 && bufchain_size(s->ppl.user_input) > 0) s->userpass_ret = seat_get_userpass_input( s->ppl.seat, s->cur_prompt, s->ppl.user_input); if (s->userpass_ret >= 0) break; s->want_user_input = true; crReturnV; s->want_user_input = false; } if (!s->userpass_ret) { /* Failed to get a passphrase. Terminate. */ free_prompts(s->cur_prompt); ssh_bpp_queue_disconnect( s->ppl.bpp, "Unable to authenticate", SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER); ssh_user_close(s->ppl.ssh, "User aborted at " "passphrase prompt"); return; } passphrase = prompt_get_result(s->cur_prompt->prompts[0]); free_prompts(s->cur_prompt); } else { passphrase = NULL; /* no passphrase needed */ } /* * Try decrypting the key. */ key = ppk_load_f(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)) { ppl_printf("Wrong passphrase\r\n"); key = NULL; /* and loop again */ } else { ppl_printf("Unable to load private key (%s)\r\n", error); key = NULL; s->suppress_wait_for_response_packet = true; break; /* try something else */ } } else { /* FIXME: if we ever support variable signature * flags, this is somewhere they'll need to be * put */ char *invalid = ssh_key_invalid(key->key, 0); if (invalid) { ppl_printf("Cannot use this private key (%s)\r\n", invalid); ssh_key_free(key->key); sfree(key->comment); sfree(key); sfree(invalid); key = NULL; s->suppress_wait_for_response_packet = true; break; /* try something else */ } } } if (key) { strbuf *pkblob, *sigdata, *sigblob; /* * 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 = ssh_bpp_new_pktout( s->ppl.bpp, SSH2_MSG_USERAUTH_REQUEST); put_stringz(s->pktout, s->username); put_stringz(s->pktout, s->successor_layer->vt->name); put_stringz(s->pktout, "publickey"); /* method */ put_bool(s->pktout, true); /* signature follows */ put_stringz(s->pktout, s->publickey_algorithm); pkblob = strbuf_new(); ssh_key_public_blob(key->key, BinarySink_UPCAST(pkblob)); put_string(s->pktout, pkblob->s, pkblob->len); /* * The data to be signed is: * * string session-id * * followed by everything so far placed in the * outgoing packet. */ sigdata = strbuf_new(); ssh2_userauth_add_session_id(s, sigdata); put_data(sigdata, s->pktout->data + 5, s->pktout->length - 5); sigblob = strbuf_new(); ssh_key_sign(key->key, ptrlen_from_strbuf(sigdata), s->signflags, BinarySink_UPCAST(sigblob)); strbuf_free(sigdata); ssh2_userauth_add_sigblob( s, s->pktout, ptrlen_from_strbuf(pkblob), ptrlen_from_strbuf(sigblob)); strbuf_free(pkblob); strbuf_free(sigblob); pq_push(s->ppl.out_pq, s->pktout); ppl_logevent("Sent public key signature"); s->type = AUTH_TYPE_PUBLICKEY; ssh_key_free(key->key); sfree(key->comment); sfree(key); s->is_trivial_auth = false; } #ifndef NO_GSSAPI } else if (s->can_gssapi && !s->tried_gssapi) { /* gssapi-with-mic authentication */ ptrlen data; s->type = AUTH_TYPE_GSSAPI; s->tried_gssapi = true; s->ppl.bpp->pls->actx = SSH2_PKTCTX_GSSAPI; if (s->shgss->lib->gsslogmsg) ppl_logevent("%s", s->shgss->lib->gsslogmsg); /* Sending USERAUTH_REQUEST with "gssapi-with-mic" method */ ppl_logevent("Trying gssapi-with-mic..."); s->pktout = ssh_bpp_new_pktout( s->ppl.bpp, SSH2_MSG_USERAUTH_REQUEST); put_stringz(s->pktout, s->username); put_stringz(s->pktout, s->successor_layer->vt->name); put_stringz(s->pktout, "gssapi-with-mic"); ppl_logevent("Attempting GSSAPI authentication"); /* add mechanism info */ s->shgss->lib->indicate_mech(s->shgss->lib, &s->gss_buf); /* number of GSSAPI mechanisms */ put_uint32(s->pktout, 1); /* length of OID + 2 */ put_uint32(s->pktout, s->gss_buf.length + 2); put_byte(s->pktout, SSH2_GSS_OIDTYPE); /* length of OID */ put_byte(s->pktout, s->gss_buf.length); put_data(s->pktout, s->gss_buf.value, s->gss_buf.length); pq_push(s->ppl.out_pq, s->pktout); crMaybeWaitUntilV((pktin = ssh2_userauth_pop(s)) != NULL); if (pktin->type != SSH2_MSG_USERAUTH_GSSAPI_RESPONSE) { ppl_logevent("GSSAPI authentication request refused"); pq_push_front(s->ppl.in_pq, pktin); continue; } /* check returned packet ... */ data = get_string(pktin); s->gss_rcvtok.value = (char *)data.ptr; s->gss_rcvtok.length = data.len; if (s->gss_rcvtok.length != s->gss_buf.length + 2 || ((char *)s->gss_rcvtok.value)[0] != SSH2_GSS_OIDTYPE || ((char *)s->gss_rcvtok.value)[1] != s->gss_buf.length || memcmp((char *)s->gss_rcvtok.value + 2, s->gss_buf.value,s->gss_buf.length) ) { ppl_logevent("GSSAPI authentication - wrong response " "from server"); continue; } /* Import server name if not cached from KEX */ if (s->shgss->srv_name == GSS_C_NO_NAME) { s->gss_stat = s->shgss->lib->import_name( s->shgss->lib, s->fullhostname, &s->shgss->srv_name); if (s->gss_stat != SSH_GSS_OK) { if (s->gss_stat == SSH_GSS_BAD_HOST_NAME) ppl_logevent("GSSAPI import name failed -" " Bad service name"); else ppl_logevent("GSSAPI import name failed"); continue; } } /* Allocate our gss_ctx */ s->gss_stat = s->shgss->lib->acquire_cred( s->shgss->lib, &s->shgss->ctx, NULL); if (s->gss_stat != SSH_GSS_OK) { ppl_logevent("GSSAPI authentication failed to get " "credentials"); /* The failure was on our side, so the server * won't be sending a response packet indicating * failure. Avoid waiting for it next time round * the loop. */ s->suppress_wait_for_response_packet = true; continue; } /* initial tokens are empty */ SSH_GSS_CLEAR_BUF(&s->gss_rcvtok); SSH_GSS_CLEAR_BUF(&s->gss_sndtok); /* now enter the loop */ do { /* * When acquire_cred yields no useful expiration, go with * the service ticket expiration. */ s->gss_stat = s->shgss->lib->init_sec_context (s->shgss->lib, &s->shgss->ctx, s->shgss->srv_name, s->gssapi_fwd, &s->gss_rcvtok, &s->gss_sndtok, NULL, NULL); if (s->gss_stat!=SSH_GSS_S_COMPLETE && s->gss_stat!=SSH_GSS_S_CONTINUE_NEEDED) { ppl_logevent("GSSAPI authentication initialisation " "failed"); if (s->shgss->lib->display_status(s->shgss->lib, s->shgss->ctx, &s->gss_buf) == SSH_GSS_OK) { ppl_logevent("%s", (char *)s->gss_buf.value); sfree(s->gss_buf.value); } pq_push_front(s->ppl.in_pq, pktin); break; } ppl_logevent("GSSAPI authentication initialised"); /* * Client and server now exchange tokens until GSSAPI * no longer says CONTINUE_NEEDED */ if (s->gss_sndtok.length != 0) { s->is_trivial_auth = false; s->pktout = ssh_bpp_new_pktout( s->ppl.bpp, SSH2_MSG_USERAUTH_GSSAPI_TOKEN); put_string(s->pktout, s->gss_sndtok.value, s->gss_sndtok.length); pq_push(s->ppl.out_pq, s->pktout); s->shgss->lib->free_tok(s->shgss->lib, &s->gss_sndtok); } if (s->gss_stat == SSH_GSS_S_CONTINUE_NEEDED) { crMaybeWaitUntilV((pktin = ssh2_userauth_pop(s)) != NULL); if (pktin->type == SSH2_MSG_USERAUTH_GSSAPI_ERRTOK) { /* * Per RFC 4462 section 3.9, this packet * type MUST immediately precede an * ordinary USERAUTH_FAILURE. * * We currently don't know how to do * anything with the GSSAPI error token * contained in this packet, so we ignore * it and just wait for the following * FAILURE. */ crMaybeWaitUntilV( (pktin = ssh2_userauth_pop(s)) != NULL); if (pktin->type != SSH2_MSG_USERAUTH_FAILURE) { ssh_proto_error( s->ppl.ssh, "Received unexpected packet " "after SSH_MSG_USERAUTH_GSSAPI_ERRTOK " "(expected SSH_MSG_USERAUTH_FAILURE): " "type %d (%s)", pktin->type, ssh2_pkt_type(s->ppl.bpp->pls->kctx, s->ppl.bpp->pls->actx, pktin->type)); return; } } if (pktin->type == SSH2_MSG_USERAUTH_FAILURE) { ppl_logevent("GSSAPI authentication failed"); s->gss_stat = SSH_GSS_FAILURE; pq_push_front(s->ppl.in_pq, pktin); break; } else if (pktin->type != SSH2_MSG_USERAUTH_GSSAPI_TOKEN) { ppl_logevent("GSSAPI authentication -" " bad server response"); s->gss_stat = SSH_GSS_FAILURE; break; } data = get_string(pktin); s->gss_rcvtok.value = (char *)data.ptr; s->gss_rcvtok.length = data.len; } } while (s-> gss_stat == SSH_GSS_S_CONTINUE_NEEDED); if (s->gss_stat != SSH_GSS_OK) { s->shgss->lib->release_cred(s->shgss->lib, &s->shgss->ctx); continue; } ppl_logevent("GSSAPI authentication loop finished OK"); /* Now send the MIC */ s->pktout = ssh2_userauth_gss_packet(s, "gssapi-with-mic"); pq_push(s->ppl.out_pq, s->pktout); s->shgss->lib->release_cred(s->shgss->lib, &s->shgss->ctx); continue; #endif } else if (s->can_keyb_inter && !s->kbd_inter_refused) { /* * Keyboard-interactive authentication. */ s->type = AUTH_TYPE_KEYBOARD_INTERACTIVE; s->ppl.bpp->pls->actx = SSH2_PKTCTX_KBDINTER; s->pktout = ssh_bpp_new_pktout( s->ppl.bpp, SSH2_MSG_USERAUTH_REQUEST); put_stringz(s->pktout, s->username); put_stringz(s->pktout, s->successor_layer->vt->name); put_stringz(s->pktout, "keyboard-interactive"); /* method */ put_stringz(s->pktout, ""); /* lang */ put_stringz(s->pktout, ""); /* submethods */ pq_push(s->ppl.out_pq, s->pktout); ppl_logevent("Attempting keyboard-interactive authentication"); if (!s->ki_scc_initialised) { s->ki_scc = seat_stripctrl_new( s->ppl.seat, NULL, SIC_KI_PROMPTS); if (s->ki_scc) stripctrl_enable_line_limiting(s->ki_scc); s->ki_scc_initialised = true; } crMaybeWaitUntilV((pktin = ssh2_userauth_pop(s)) != NULL); if (pktin->type != SSH2_MSG_USERAUTH_INFO_REQUEST) { /* Server is not willing to do keyboard-interactive * at all (or, bizarrely but legally, accepts the * user without actually issuing any prompts). * Give up on it entirely. */ pq_push_front(s->ppl.in_pq, pktin); s->type = AUTH_TYPE_KEYBOARD_INTERACTIVE_QUIET; s->kbd_inter_refused = true; /* don't try it again */ continue; } s->ki_printed_header = false; /* * Loop while the server continues to send INFO_REQUESTs. */ while (pktin->type == SSH2_MSG_USERAUTH_INFO_REQUEST) { ptrlen name, inst; strbuf *sb; /* * We've got a fresh USERAUTH_INFO_REQUEST. * Get the preamble and start building a prompt. */ name = get_string(pktin); inst = get_string(pktin); get_string(pktin); /* skip language tag */ s->cur_prompt = new_prompts(); s->cur_prompt->to_server = true; s->cur_prompt->from_server = true; /* * Get any prompt(s) from the packet. */ s->num_prompts = get_uint32(pktin); for (uint32_t i = 0; i < s->num_prompts; i++) { s->is_trivial_auth = false; ptrlen prompt = get_string(pktin); bool echo = get_bool(pktin); if (get_err(pktin)) { ssh_proto_error( s->ppl.ssh, "Server sent truncated " "SSH_MSG_USERAUTH_INFO_REQUEST packet"); return; } sb = strbuf_new(); if (!prompt.len) { put_datapl(sb, PTRLEN_LITERAL( ": ")); } else if (s->ki_scc) { stripctrl_retarget( s->ki_scc, BinarySink_UPCAST(sb)); put_datapl(s->ki_scc, prompt); stripctrl_retarget(s->ki_scc, NULL); } else { put_datapl(sb, prompt); } add_prompt(s->cur_prompt, strbuf_to_str(sb), echo); } /* * Make the header strings. This includes the * 'name' (optional dialog-box title) and * 'instruction' from the server. * * First, display our disambiguating header line * if this is the first time round the loop - * _unless_ the server has sent a completely empty * k-i packet with no prompts _or_ text, which * apparently some do. In that situation there's * no need to alert the user that the following * text is server- supplied, because, well, _what_ * text? * * We also only do this if we got a stripctrl, * because if we didn't, that suggests this is all * being done via dialog boxes anyway. */ if (!s->ki_printed_header && s->ki_scc && (s->num_prompts || name.len || inst.len)) { ssh2_userauth_antispoof_msg( s, "Keyboard-interactive authentication " "prompts from server:"); s->ki_printed_header = true; seat_set_trust_status(s->ppl.seat, false); } sb = strbuf_new(); if (name.len) { if (s->ki_scc) { stripctrl_retarget(s->ki_scc, BinarySink_UPCAST(sb)); put_datapl(s->ki_scc, name); stripctrl_retarget(s->ki_scc, NULL); } else { put_datapl(sb, name); } s->cur_prompt->name_reqd = true; } else { put_datapl(sb, PTRLEN_LITERAL( "SSH server authentication")); s->cur_prompt->name_reqd = false; } s->cur_prompt->name = strbuf_to_str(sb); sb = strbuf_new(); if (inst.len) { if (s->ki_scc) { stripctrl_retarget(s->ki_scc, BinarySink_UPCAST(sb)); put_datapl(s->ki_scc, inst); stripctrl_retarget(s->ki_scc, NULL); } else { put_datapl(sb, inst); } s->cur_prompt->instr_reqd = true; } else { s->cur_prompt->instr_reqd = false; } if (sb->len) s->cur_prompt->instruction = strbuf_to_str(sb); else strbuf_free(sb); /* * Our prompts_t is fully constructed now. Get the * user's response(s). */ s->userpass_ret = seat_get_userpass_input( s->ppl.seat, s->cur_prompt, NULL); while (1) { while (s->userpass_ret < 0 && bufchain_size(s->ppl.user_input) > 0) s->userpass_ret = seat_get_userpass_input( s->ppl.seat, s->cur_prompt, s->ppl.user_input); if (s->userpass_ret >= 0) break; s->want_user_input = true; crReturnV; s->want_user_input = false; } if (!s->userpass_ret) { /* * Failed to get responses. Terminate. */ free_prompts(s->cur_prompt); ssh_bpp_queue_disconnect( s->ppl.bpp, "Unable to authenticate", SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER); ssh_user_close(s->ppl.ssh, "User aborted during " "keyboard-interactive authentication"); return; } /* * Send the response(s) to the server. */ s->pktout = ssh_bpp_new_pktout( s->ppl.bpp, SSH2_MSG_USERAUTH_INFO_RESPONSE); put_uint32(s->pktout, s->num_prompts); for (uint32_t i = 0; i < s->num_prompts; i++) { put_stringz(s->pktout, prompt_get_result_ref( s->cur_prompt->prompts[i])); } s->pktout->minlen = 256; pq_push(s->ppl.out_pq, s->pktout); /* * 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. */ free_prompts(s->cur_prompt); /* * Get the next packet in case it's another * INFO_REQUEST. */ crMaybeWaitUntilV((pktin = ssh2_userauth_pop(s)) != NULL); } /* * Print our trailer line, if we printed a header. */ if (s->ki_printed_header) { seat_set_trust_status(s->ppl.seat, true); ssh2_userauth_antispoof_msg( s, "End of keyboard-interactive prompts from server"); } /* * We should have SUCCESS or FAILURE now. */ pq_push_front(s->ppl.in_pq, pktin); } else if (s->can_passwd) { s->is_trivial_auth = false; /* * Plain old password authentication. */ bool changereq_first_time; /* not live over crReturn */ s->ppl.bpp->pls->actx = SSH2_PKTCTX_PASSWORD; s->cur_prompt = new_prompts(); s->cur_prompt->to_server = true; s->cur_prompt->from_server = false; s->cur_prompt->name = dupstr("SSH password"); add_prompt(s->cur_prompt, dupprintf("%s@%s's password: ", s->username, s->hostname), false); s->userpass_ret = seat_get_userpass_input( s->ppl.seat, s->cur_prompt, NULL); while (1) { while (s->userpass_ret < 0 && bufchain_size(s->ppl.user_input) > 0) s->userpass_ret = seat_get_userpass_input( s->ppl.seat, s->cur_prompt, s->ppl.user_input); if (s->userpass_ret >= 0) break; s->want_user_input = true; crReturnV; s->want_user_input = false; } if (!s->userpass_ret) { /* * Failed to get responses. Terminate. */ free_prompts(s->cur_prompt); ssh_bpp_queue_disconnect( s->ppl.bpp, "Unable to authenticate", SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER); ssh_user_close(s->ppl.ssh, "User aborted during password " "authentication"); return; } /* * Squirrel away the password. (We may need it later if * asked to change it.) */ s->password = prompt_get_result(s->cur_prompt->prompts[0]); free_prompts(s->cur_prompt); /* * Send the password packet. * * We pad out the password packet to 256 bytes to make * it harder for an attacker to find the length of the * 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 = ssh_bpp_new_pktout( s->ppl.bpp, SSH2_MSG_USERAUTH_REQUEST); put_stringz(s->pktout, s->username); put_stringz(s->pktout, s->successor_layer->vt->name); put_stringz(s->pktout, "password"); put_bool(s->pktout, false); put_stringz(s->pktout, s->password); s->pktout->minlen = 256; pq_push(s->ppl.out_pq, s->pktout); ppl_logevent("Sent password"); s->type = AUTH_TYPE_PASSWORD; /* * Wait for next packet, in case it's a password change * request. */ crMaybeWaitUntilV((pktin = ssh2_userauth_pop(s)) != NULL); changereq_first_time = true; while (pktin->type == SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ) { /* * We're being asked for a new password * (perhaps not for the first time). * Loop until the server accepts it. */ bool got_new = false; /* not live over crReturn */ ptrlen prompt; /* not live over crReturn */ { const char *msg; if (changereq_first_time) msg = "Server requested password change"; else msg = "Server rejected new password"; ppl_logevent("%s", msg); ppl_printf("%s\r\n", msg); } prompt = get_string(pktin); s->cur_prompt = new_prompts(); s->cur_prompt->to_server = true; s->cur_prompt->from_server = false; s->cur_prompt->name = dupstr("New SSH password"); s->cur_prompt->instruction = mkstr(prompt); s->cur_prompt->instr_reqd = true; /* * There's no explicit requirement in the protocol * for the "old" passwords in the original and * password-change messages to be the same, and * apparently some Cisco kit supports password change * by the user entering a blank password originally * and the real password subsequently, so, * 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) { s->userpass_ret = seat_get_userpass_input( s->ppl.seat, s->cur_prompt, NULL); while (1) { while (s->userpass_ret < 0 && bufchain_size(s->ppl.user_input) > 0) s->userpass_ret = seat_get_userpass_input( s->ppl.seat, s->cur_prompt, s->ppl.user_input); if (s->userpass_ret >= 0) break; s->want_user_input = true; crReturnV; s->want_user_input = false; } if (!s->userpass_ret) { /* * Failed to get responses. Terminate. */ /* burn the evidence */ free_prompts(s->cur_prompt); smemclr(s->password, strlen(s->password)); sfree(s->password); ssh_bpp_queue_disconnect( s->ppl.bpp, "Unable to authenticate", SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER); ssh_user_close(s->ppl.ssh, "User aborted during " "password changing"); return; } /* * 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->s[0]) { smemclr(s->password, strlen(s->password)); /* burn the evidence */ sfree(s->password); s->password = prompt_get_result( s->cur_prompt->prompts[0]); } /* * Check the two new passwords match. */ got_new = !strcmp( prompt_get_result_ref(s->cur_prompt->prompts[1]), prompt_get_result_ref(s->cur_prompt->prompts[2])); if (!got_new) /* They don't. Silly user. */ ppl_printf("Passwords do not match\r\n"); } /* * Send the new password (along with the old one). * (see above for padding rationale) */ s->pktout = ssh_bpp_new_pktout( s->ppl.bpp, SSH2_MSG_USERAUTH_REQUEST); put_stringz(s->pktout, s->username); put_stringz(s->pktout, s->successor_layer->vt->name); put_stringz(s->pktout, "password"); put_bool(s->pktout, true); put_stringz(s->pktout, s->password); put_stringz(s->pktout, prompt_get_result_ref( s->cur_prompt->prompts[1])); free_prompts(s->cur_prompt); s->pktout->minlen = 256; pq_push(s->ppl.out_pq, s->pktout); ppl_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.) */ crMaybeWaitUntilV((pktin = ssh2_userauth_pop(s)) != NULL); changereq_first_time = false; } /* * We need to reexamine the current pktin at the top * of the loop. Either: * - we weren't asked to change password at all, in * which case it's a SUCCESS or FAILURE with the * usual meaning * - we sent a new password, and the server was * either OK with it (SUCCESS or FAILURE w/partial * success) or unhappy with the _old_ password * (FAILURE w/o partial success) * In any of these cases, we go back to the top of * the loop and start again. */ pq_push_front(s->ppl.in_pq, pktin); /* * 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 { ssh_bpp_queue_disconnect( s->ppl.bpp, "No supported authentication methods available", SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE); ssh_sw_abort(s->ppl.ssh, "No supported authentication methods " "available (server sent: %s)", s->last_methods_string->s); return; } } try_new_username:; } userauth_success: if (s->notrivialauth && s->is_trivial_auth) { ssh_proto_error(s->ppl.ssh, "Authentication was trivial! " "Abandoning session as specified in configuration."); return; } /* * We've just received USERAUTH_SUCCESS, and we haven't sent * any packets since. Signal the transport layer to consider * doing an immediate rekey, if it has any reason to want to. */ ssh2_transport_notify_auth_done(s->transport_layer); /* * Finally, hand over to our successor layer, and return * immediately without reaching the crFinishV: ssh_ppl_replace * will have freed us, so crFinishV's zeroing-out of crState would * be a use-after-free bug. */ { PacketProtocolLayer *successor = s->successor_layer; s->successor_layer = NULL; /* avoid freeing it ourself */ ssh_ppl_replace(&s->ppl, successor); return; /* we've just freed s, so avoid even touching s->crState */ } crFinishV; } static void ssh2_userauth_add_session_id( struct ssh2_userauth_state *s, strbuf *sigdata) { if (s->ppl.remote_bugs & BUG_SSH2_PK_SESSIONID) { put_datapl(sigdata, s->session_id); } else { put_stringpl(sigdata, s->session_id); } } static void ssh2_userauth_agent_query( struct ssh2_userauth_state *s, strbuf *req) { void *response; int response_len; sfree(s->agent_response_to_free); s->agent_response_to_free = NULL; s->auth_agent_query = agent_query(req, &response, &response_len, ssh2_userauth_agent_callback, s); if (!s->auth_agent_query) ssh2_userauth_agent_callback(s, response, response_len); } static void ssh2_userauth_agent_callback(void *uav, void *reply, int replylen) { struct ssh2_userauth_state *s = (struct ssh2_userauth_state *)uav; s->auth_agent_query = NULL; s->agent_response_to_free = reply; s->agent_response = make_ptrlen(reply, replylen); queue_idempotent_callback(&s->ppl.ic_process_queue); } /* * Helper function to add an SSH-2 signature blob to a packet. Expects * to be shown the public key blob as well as the signature blob. * Normally just appends the sig blob unmodified as a string, except * that it optionally breaks it open and fiddle with it to work around * BUG_SSH2_RSA_PADDING. */ static void ssh2_userauth_add_sigblob( struct ssh2_userauth_state *s, PktOut *pkt, ptrlen pkblob, ptrlen sigblob) { BinarySource pk[1], sig[1]; BinarySource_BARE_INIT_PL(pk, pkblob); BinarySource_BARE_INIT_PL(sig, sigblob); /* 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 ((s->ppl.remote_bugs & BUG_SSH2_RSA_PADDING) && ptrlen_eq_string(get_string(pk), "ssh-rsa") && ptrlen_eq_string(get_string(sig), "ssh-rsa")) { ptrlen mod_mp, sig_mp; size_t sig_prefix_len; /* * Find the modulus and signature integers. */ get_string(pk); /* skip over exponent */ mod_mp = get_string(pk); /* remember modulus */ sig_prefix_len = sig->pos; sig_mp = get_string(sig); if (get_err(pk) || get_err(sig)) goto give_up; /* * Find the byte length of the modulus, not counting leading * zeroes. */ while (mod_mp.len > 0 && *(const char *)mod_mp.ptr == 0) { mod_mp.len--; mod_mp.ptr = (const char *)mod_mp.ptr + 1; } /* debug("modulus length is %d\n", len); */ /* debug("signature length is %d\n", siglen); */ if (mod_mp.len > sig_mp.len) { strbuf *substr = strbuf_new(); put_data(substr, sigblob.ptr, sig_prefix_len); put_uint32(substr, mod_mp.len); put_padding(substr, mod_mp.len - sig_mp.len, 0); put_datapl(substr, sig_mp); put_stringsb(pkt, substr); 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:; } put_stringpl(pkt, sigblob); } #ifndef NO_GSSAPI static PktOut *ssh2_userauth_gss_packet( struct ssh2_userauth_state *s, const char *authtype) { strbuf *sb; PktOut *p; Ssh_gss_buf buf; Ssh_gss_buf mic; /* * The mic is computed over the session id + intended * USERAUTH_REQUEST packet. */ sb = strbuf_new(); put_stringpl(sb, s->session_id); put_byte(sb, SSH2_MSG_USERAUTH_REQUEST); put_stringz(sb, s->username); put_stringz(sb, s->successor_layer->vt->name); put_stringz(sb, authtype); /* Compute the mic */ buf.value = sb->s; buf.length = sb->len; s->shgss->lib->get_mic(s->shgss->lib, s->shgss->ctx, &buf, &mic); strbuf_free(sb); /* Now we can build the real packet */ if (strcmp(authtype, "gssapi-with-mic") == 0) { p = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_USERAUTH_GSSAPI_MIC); } else { p = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_USERAUTH_REQUEST); put_stringz(p, s->username); put_stringz(p, s->successor_layer->vt->name); put_stringz(p, authtype); } put_string(p, mic.value, mic.length); return p; } #endif static bool ssh2_userauth_get_specials( PacketProtocolLayer *ppl, add_special_fn_t add_special, void *ctx) { /* No specials provided by this layer. */ return false; } static void ssh2_userauth_special_cmd(PacketProtocolLayer *ppl, SessionSpecialCode code, int arg) { /* No specials provided by this layer. */ } static bool ssh2_userauth_want_user_input(PacketProtocolLayer *ppl) { struct ssh2_userauth_state *s = container_of(ppl, struct ssh2_userauth_state, ppl); return s->want_user_input; } static void ssh2_userauth_got_user_input(PacketProtocolLayer *ppl) { struct ssh2_userauth_state *s = container_of(ppl, struct ssh2_userauth_state, ppl); if (s->want_user_input) queue_idempotent_callback(&s->ppl.ic_process_queue); } static void ssh2_userauth_reconfigure(PacketProtocolLayer *ppl, Conf *conf) { struct ssh2_userauth_state *s = container_of(ppl, struct ssh2_userauth_state, ppl); ssh_ppl_reconfigure(s->successor_layer, conf); } static void ssh2_userauth_antispoof_msg( struct ssh2_userauth_state *s, const char *msg) { strbuf *sb = strbuf_new(); if (seat_set_trust_status(s->ppl.seat, true)) { /* * If the seat can directly indicate that this message is * generated by the client, then we can just use the message * unmodified as an unspoofable header. */ put_datapl(sb, ptrlen_from_asciz(msg)); } else { /* * Otherwise, add enough padding around it that the server * wouldn't be able to mimic it within our line-length * constraint. */ strbuf_catf(sb, "-- %s ", msg); while (sb->len < 78) put_byte(sb, '-'); } put_datapl(sb, PTRLEN_LITERAL("\r\n")); seat_stderr_pl(s->ppl.seat, ptrlen_from_strbuf(sb)); strbuf_free(sb); } putty-0.76/sshaes.c0000644000175000017500000023326414072266312011223 00000000000000/* * sshaes.c - implementation of AES */ #include #include #include "ssh.h" #include "mpint_i.h" /* we reuse the BignumInt system */ /* * Start by deciding whether we can support hardware AES at all. */ #define HW_AES_NONE 0 #define HW_AES_NI 1 #define HW_AES_NEON 2 #ifdef _FORCE_AES_NI # define HW_AES HW_AES_NI #elif defined(__clang__) # if __has_attribute(target) && __has_include() && \ (defined(__x86_64__) || defined(__i386)) # define HW_AES HW_AES_NI # endif #elif defined(__GNUC__) # if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4)) && \ (defined(__x86_64__) || defined(__i386)) # define HW_AES HW_AES_NI # endif #elif defined (_MSC_VER) # if (defined(_M_X64) || defined(_M_IX86)) && _MSC_FULL_VER >= 150030729 # define HW_AES HW_AES_NI # endif #endif #ifdef _FORCE_AES_NEON # define HW_AES HW_AES_NEON #elif defined __BYTE_ORDER__ && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ /* Arm can potentially support both endiannesses, but this code * hasn't been tested on anything but little. If anyone wants to * run big-endian, they'll need to fix it first. */ #elif defined __ARM_FEATURE_CRYPTO /* If the Arm crypto extension is available already, we can * support NEON AES without having to enable anything by hand */ # define HW_AES HW_AES_NEON #elif defined(__clang__) # if __has_attribute(target) && __has_include() && \ (defined(__aarch64__)) /* clang can enable the crypto extension in AArch64 using * __attribute__((target)) */ # define HW_AES HW_AES_NEON # define USE_CLANG_ATTR_TARGET_AARCH64 # endif #elif defined _MSC_VER # if defined _M_ARM64 # define HW_AES HW_AES_NEON /* 64-bit Visual Studio uses the header in place * of the standard */ # define USE_ARM64_NEON_H # elif defined _M_ARM # define HW_AES HW_AES_NEON /* 32-bit Visual Studio uses the right header name, but requires * this #define to enable a set of intrinsic definitions that * do not omit one of the parameters for vaes[ed]q_u8 */ # define _ARM_USE_NEW_NEON_INTRINSICS # endif #endif #if defined _FORCE_SOFTWARE_AES || !defined HW_AES # undef HW_AES # define HW_AES HW_AES_NONE #endif #if HW_AES == HW_AES_NI #define HW_NAME_SUFFIX " (AES-NI accelerated)" #elif HW_AES == HW_AES_NEON #define HW_NAME_SUFFIX " (NEON accelerated)" #else #define HW_NAME_SUFFIX " (!NONEXISTENT ACCELERATED VERSION!)" #endif /* * Vtable collection for AES. For each SSH-level cipher id (i.e. * combination of key length and cipher mode), we provide three * vtables: one for the pure software implementation, one using * hardware acceleration (if available), and a top-level one which is * never actually instantiated, and only contains a new() method whose * job is to decide which of the other two to return an actual * instance of. */ static ssh_cipher *aes_select(const ssh_cipheralg *alg); static ssh_cipher *aes_sw_new(const ssh_cipheralg *alg); static void aes_sw_free(ssh_cipher *); static void aes_sw_setiv_cbc(ssh_cipher *, const void *iv); static void aes_sw_setiv_sdctr(ssh_cipher *, const void *iv); static void aes_sw_setkey(ssh_cipher *, const void *key); static ssh_cipher *aes_hw_new(const ssh_cipheralg *alg); static void aes_hw_free(ssh_cipher *); static void aes_hw_setiv_cbc(ssh_cipher *, const void *iv); static void aes_hw_setiv_sdctr(ssh_cipher *, const void *iv); static void aes_hw_setkey(ssh_cipher *, const void *key); struct aes_extra { const ssh_cipheralg *sw, *hw; }; #define VTABLES_INNER(cid, pid, bits, name, encsuffix, \ decsuffix, setivsuffix, flagsval) \ static void cid##_sw##encsuffix(ssh_cipher *, void *blk, int len); \ static void cid##_sw##decsuffix(ssh_cipher *, void *blk, int len); \ const ssh_cipheralg ssh_##cid##_sw = { \ .new = aes_sw_new, \ .free = aes_sw_free, \ .setiv = aes_sw_##setivsuffix, \ .setkey = aes_sw_setkey, \ .encrypt = cid##_sw##encsuffix, \ .decrypt = cid##_sw##decsuffix, \ .ssh2_id = pid, \ .blksize = 16, \ .real_keybits = bits, \ .padded_keybytes = bits/8, \ .flags = flagsval, \ .text_name = name " (unaccelerated)", \ }; \ \ static void cid##_hw##encsuffix(ssh_cipher *, void *blk, int len); \ static void cid##_hw##decsuffix(ssh_cipher *, void *blk, int len); \ const ssh_cipheralg ssh_##cid##_hw = { \ .new = aes_hw_new, \ .free = aes_hw_free, \ .setiv = aes_hw_##setivsuffix, \ .setkey = aes_hw_setkey, \ .encrypt = cid##_hw##encsuffix, \ .decrypt = cid##_hw##decsuffix, \ .ssh2_id = pid, \ .blksize = 16, \ .real_keybits = bits, \ .padded_keybytes = bits/8, \ .flags = flagsval, \ .text_name = name HW_NAME_SUFFIX, \ }; \ \ static const struct aes_extra extra_##cid = { \ &ssh_##cid##_sw, &ssh_##cid##_hw }; \ \ const ssh_cipheralg ssh_##cid = { \ .new = aes_select, \ .ssh2_id = pid, \ .blksize = 16, \ .real_keybits = bits, \ .padded_keybytes = bits/8, \ .flags = flagsval, \ .text_name = name " (dummy selector vtable)", \ .extra = &extra_##cid \ }; \ #define VTABLES(keylen) \ VTABLES_INNER(aes ## keylen ## _cbc, "aes" #keylen "-cbc", \ keylen, "AES-" #keylen " CBC", _encrypt, _decrypt, \ setiv_cbc, SSH_CIPHER_IS_CBC) \ VTABLES_INNER(aes ## keylen ## _sdctr, "aes" #keylen "-ctr", \ keylen, "AES-" #keylen " SDCTR",,, setiv_sdctr, 0) VTABLES(128) VTABLES(192) VTABLES(256) static const ssh_cipheralg ssh_rijndael_lysator = { /* Same as aes256_cbc, but with a different protocol ID */ .new = aes_select, .ssh2_id = "rijndael-cbc@lysator.liu.se", .blksize = 16, .real_keybits = 256, .padded_keybytes = 256/8, .flags = 0, .text_name = "AES-256 CBC (dummy selector vtable)", .extra = &extra_aes256_cbc, }; static const ssh_cipheralg *const aes_list[] = { &ssh_aes256_sdctr, &ssh_aes256_cbc, &ssh_rijndael_lysator, &ssh_aes192_sdctr, &ssh_aes192_cbc, &ssh_aes128_sdctr, &ssh_aes128_cbc, }; const ssh2_ciphers ssh2_aes = { lenof(aes_list), aes_list }; /* * The actual query function that asks if hardware acceleration is * available. */ static bool aes_hw_available(void); /* * The top-level selection function, caching the results of * aes_hw_available() so it only has to run once. */ static bool aes_hw_available_cached(void) { static bool initialised = false; static bool hw_available; if (!initialised) { hw_available = aes_hw_available(); initialised = true; } return hw_available; } static ssh_cipher *aes_select(const ssh_cipheralg *alg) { const struct aes_extra *extra = (const struct aes_extra *)alg->extra; const ssh_cipheralg *real_alg = aes_hw_available_cached() ? extra->hw : extra->sw; return ssh_cipher_new(real_alg); } /* ---------------------------------------------------------------------- * Definitions likely to be helpful to multiple implementations. */ #define REP2(x) x x #define REP4(x) REP2(REP2(x)) #define REP8(x) REP2(REP4(x)) #define REP9(x) REP8(x) x #define REP11(x) REP8(x) REP2(x) x #define REP13(x) REP8(x) REP4(x) x static const uint8_t key_setup_round_constants[] = { /* The first few powers of X in GF(2^8), used during key setup. * This can safely be a lookup table without side channel risks, * because key setup iterates through it once in a standard way * regardless of the key. */ 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, }; #define MAXROUNDKEYS 15 /* ---------------------------------------------------------------------- * Software implementation of AES. * * This implementation uses a bit-sliced representation. Instead of * the obvious approach of storing the cipher state so that each byte * (or field element, or entry in the cipher matrix) occupies 8 * contiguous bits in a machine integer somewhere, we organise the * cipher state as an array of 8 integers, in such a way that each * logical byte of the cipher state occupies one bit in each integer, * all at the same position. This allows us to do parallel logic on * all bytes of the state by doing bitwise operations between the 8 * integers; in particular, the S-box (SubBytes) lookup is done this * way, which takes about 110 operations - but for those 110 bitwise * ops you get 64 S-box lookups, not just one. */ #define SLICE_PARALLELISM (BIGNUM_INT_BYTES / 2) #ifdef BITSLICED_DEBUG /* Dump function that undoes the bitslicing transform, so you can see * the logical data represented by a set of slice words. */ static inline void dumpslices_uint16_t( const char *prefix, const uint16_t slices[8]) { printf("%-30s", prefix); for (unsigned byte = 0; byte < 16; byte++) { unsigned byteval = 0; for (unsigned bit = 0; bit < 8; bit++) byteval |= (1 & (slices[bit] >> byte)) << bit; printf("%02x", byteval); } printf("\n"); } static inline void dumpslices_BignumInt( const char *prefix, const BignumInt slices[8]) { printf("%-30s", prefix); for (unsigned iter = 0; iter < SLICE_PARALLELISM; iter++) { for (unsigned byte = 0; byte < 16; byte++) { unsigned byteval = 0; for (unsigned bit = 0; bit < 8; bit++) byteval |= (1 & (slices[bit] >> (iter*16+byte))) << bit; printf("%02x", byteval); } if (iter+1 < SLICE_PARALLELISM) printf(" "); } printf("\n"); } #else #define dumpslices_uintN_t(prefix, slices) ((void)0) #define dumpslices_BignumInt(prefix, slices) ((void)0) #endif /* ----- * Bit-slicing transformation: convert between an array of 16 uint8_t * and an array of 8 uint16_t, so as to interchange the bit index * within each element and the element index within the array. (That * is, bit j of input[i] == bit i of output[j]. */ #define SWAPWORDS(shift) do \ { \ uint64_t mask = ~(uint64_t)0 / ((1ULL << shift) + 1); \ uint64_t diff = ((i0 >> shift) ^ i1) & mask; \ i0 ^= diff << shift; \ i1 ^= diff; \ } while (0) #define SWAPINWORD(i, bigshift, smallshift) do \ { \ uint64_t mask = ~(uint64_t)0; \ mask /= ((1ULL << bigshift) + 1); \ mask /= ((1ULL << smallshift) + 1); \ mask <<= smallshift; \ unsigned shift = bigshift - smallshift; \ uint64_t diff = ((i >> shift) ^ i) & mask; \ i ^= diff ^ (diff << shift); \ } while (0) #define TO_BITSLICES(slices, bytes, uintN_t, assign_op, shift) do \ { \ uint64_t i0 = GET_64BIT_LSB_FIRST(bytes); \ uint64_t i1 = GET_64BIT_LSB_FIRST(bytes + 8); \ SWAPINWORD(i0, 8, 1); \ SWAPINWORD(i1, 8, 1); \ SWAPINWORD(i0, 16, 2); \ SWAPINWORD(i1, 16, 2); \ SWAPINWORD(i0, 32, 4); \ SWAPINWORD(i1, 32, 4); \ SWAPWORDS(8); \ slices[0] assign_op (uintN_t)((i0 >> 0) & 0xFFFF) << (shift); \ slices[2] assign_op (uintN_t)((i0 >> 16) & 0xFFFF) << (shift); \ slices[4] assign_op (uintN_t)((i0 >> 32) & 0xFFFF) << (shift); \ slices[6] assign_op (uintN_t)((i0 >> 48) & 0xFFFF) << (shift); \ slices[1] assign_op (uintN_t)((i1 >> 0) & 0xFFFF) << (shift); \ slices[3] assign_op (uintN_t)((i1 >> 16) & 0xFFFF) << (shift); \ slices[5] assign_op (uintN_t)((i1 >> 32) & 0xFFFF) << (shift); \ slices[7] assign_op (uintN_t)((i1 >> 48) & 0xFFFF) << (shift); \ } while (0) #define FROM_BITSLICES(bytes, slices, shift) do \ { \ uint64_t i1 = ((slices[7] >> (shift)) & 0xFFFF); \ i1 = (i1 << 16) | ((slices[5] >> (shift)) & 0xFFFF); \ i1 = (i1 << 16) | ((slices[3] >> (shift)) & 0xFFFF); \ i1 = (i1 << 16) | ((slices[1] >> (shift)) & 0xFFFF); \ uint64_t i0 = ((slices[6] >> (shift)) & 0xFFFF); \ i0 = (i0 << 16) | ((slices[4] >> (shift)) & 0xFFFF); \ i0 = (i0 << 16) | ((slices[2] >> (shift)) & 0xFFFF); \ i0 = (i0 << 16) | ((slices[0] >> (shift)) & 0xFFFF); \ SWAPWORDS(8); \ SWAPINWORD(i0, 32, 4); \ SWAPINWORD(i1, 32, 4); \ SWAPINWORD(i0, 16, 2); \ SWAPINWORD(i1, 16, 2); \ SWAPINWORD(i0, 8, 1); \ SWAPINWORD(i1, 8, 1); \ PUT_64BIT_LSB_FIRST(bytes, i0); \ PUT_64BIT_LSB_FIRST((bytes) + 8, i1); \ } while (0) /* ----- * Some macros that will be useful repeatedly. */ /* Iterate a unary transformation over all 8 slices. */ #define ITERATE(MACRO, output, input, uintN_t) do \ { \ MACRO(output[0], input[0], uintN_t); \ MACRO(output[1], input[1], uintN_t); \ MACRO(output[2], input[2], uintN_t); \ MACRO(output[3], input[3], uintN_t); \ MACRO(output[4], input[4], uintN_t); \ MACRO(output[5], input[5], uintN_t); \ MACRO(output[6], input[6], uintN_t); \ MACRO(output[7], input[7], uintN_t); \ } while (0) /* Simply add (i.e. XOR) two whole sets of slices together. */ #define BITSLICED_ADD(output, lhs, rhs) do \ { \ output[0] = lhs[0] ^ rhs[0]; \ output[1] = lhs[1] ^ rhs[1]; \ output[2] = lhs[2] ^ rhs[2]; \ output[3] = lhs[3] ^ rhs[3]; \ output[4] = lhs[4] ^ rhs[4]; \ output[5] = lhs[5] ^ rhs[5]; \ output[6] = lhs[6] ^ rhs[6]; \ output[7] = lhs[7] ^ rhs[7]; \ } while (0) /* ----- * The AES S-box, in pure bitwise logic so that it can be run in * parallel on whole words full of bit-sliced field elements. * * Source: 'A new combinational logic minimization technique with * applications to cryptology', https://eprint.iacr.org/2009/191 * * As a minor speed optimisation, I use a modified version of the * S-box which omits the additive constant 0x63, i.e. this S-box * consists of only the field inversion and linear map components. * Instead, the addition of the constant is deferred until after the * subsequent ShiftRows and MixColumns stages, so that it happens at * the same time as adding the next round key - and then we just make * it _part_ of the round key, so it doesn't cost any extra * instructions to add. * * (Obviously adding a constant to each byte commutes with ShiftRows, * which only permutes the bytes. It also commutes with MixColumns: * that's not quite so obvious, but since the effect of MixColumns is * to multiply a constant polynomial M into each column, it is obvious * that adding some polynomial K and then multiplying by M is * equivalent to multiplying by M and then adding the product KM. And * in fact, since the coefficients of M happen to sum to 1, it turns * out that KM = K, so we don't even have to change the constant when * we move it to the far side of MixColumns.) * * Of course, one knock-on effect of this is that the use of the S-box * *during* key setup has to be corrected by manually adding on the * constant afterwards! */ /* Initial linear transformation for the forward S-box, from Fig 2 of * the paper. */ #define SBOX_FORWARD_TOP_TRANSFORM(input, uintN_t) \ uintN_t y14 = input[4] ^ input[2]; \ uintN_t y13 = input[7] ^ input[1]; \ uintN_t y9 = input[7] ^ input[4]; \ uintN_t y8 = input[7] ^ input[2]; \ uintN_t t0 = input[6] ^ input[5]; \ uintN_t y1 = t0 ^ input[0]; \ uintN_t y4 = y1 ^ input[4]; \ uintN_t y12 = y13 ^ y14; \ uintN_t y2 = y1 ^ input[7]; \ uintN_t y5 = y1 ^ input[1]; \ uintN_t y3 = y5 ^ y8; \ uintN_t t1 = input[3] ^ y12; \ uintN_t y15 = t1 ^ input[2]; \ uintN_t y20 = t1 ^ input[6]; \ uintN_t y6 = y15 ^ input[0]; \ uintN_t y10 = y15 ^ t0; \ uintN_t y11 = y20 ^ y9; \ uintN_t y7 = input[0] ^ y11; \ uintN_t y17 = y10 ^ y11; \ uintN_t y19 = y10 ^ y8; \ uintN_t y16 = t0 ^ y11; \ uintN_t y21 = y13 ^ y16; \ uintN_t y18 = input[7] ^ y16; \ /* Make a copy of input[0] under a new name, because the core * will refer to it, and in the inverse version of the S-box * the corresponding value will be one of the calculated ones * and not in input[0] itself. */ \ uintN_t i0 = input[0]; \ /* end */ /* Core nonlinear component, from Fig 3 of the paper. */ #define SBOX_CORE(uintN_t) \ uintN_t t2 = y12 & y15; \ uintN_t t3 = y3 & y6; \ uintN_t t4 = t3 ^ t2; \ uintN_t t5 = y4 & i0; \ uintN_t t6 = t5 ^ t2; \ uintN_t t7 = y13 & y16; \ uintN_t t8 = y5 & y1; \ uintN_t t9 = t8 ^ t7; \ uintN_t t10 = y2 & y7; \ uintN_t t11 = t10 ^ t7; \ uintN_t t12 = y9 & y11; \ uintN_t t13 = y14 & y17; \ uintN_t t14 = t13 ^ t12; \ uintN_t t15 = y8 & y10; \ uintN_t t16 = t15 ^ t12; \ uintN_t t17 = t4 ^ t14; \ uintN_t t18 = t6 ^ t16; \ uintN_t t19 = t9 ^ t14; \ uintN_t t20 = t11 ^ t16; \ uintN_t t21 = t17 ^ y20; \ uintN_t t22 = t18 ^ y19; \ uintN_t t23 = t19 ^ y21; \ uintN_t t24 = t20 ^ y18; \ uintN_t t25 = t21 ^ t22; \ uintN_t t26 = t21 & t23; \ uintN_t t27 = t24 ^ t26; \ uintN_t t28 = t25 & t27; \ uintN_t t29 = t28 ^ t22; \ uintN_t t30 = t23 ^ t24; \ uintN_t t31 = t22 ^ t26; \ uintN_t t32 = t31 & t30; \ uintN_t t33 = t32 ^ t24; \ uintN_t t34 = t23 ^ t33; \ uintN_t t35 = t27 ^ t33; \ uintN_t t36 = t24 & t35; \ uintN_t t37 = t36 ^ t34; \ uintN_t t38 = t27 ^ t36; \ uintN_t t39 = t29 & t38; \ uintN_t t40 = t25 ^ t39; \ uintN_t t41 = t40 ^ t37; \ uintN_t t42 = t29 ^ t33; \ uintN_t t43 = t29 ^ t40; \ uintN_t t44 = t33 ^ t37; \ uintN_t t45 = t42 ^ t41; \ uintN_t z0 = t44 & y15; \ uintN_t z1 = t37 & y6; \ uintN_t z2 = t33 & i0; \ uintN_t z3 = t43 & y16; \ uintN_t z4 = t40 & y1; \ uintN_t z5 = t29 & y7; \ uintN_t z6 = t42 & y11; \ uintN_t z7 = t45 & y17; \ uintN_t z8 = t41 & y10; \ uintN_t z9 = t44 & y12; \ uintN_t z10 = t37 & y3; \ uintN_t z11 = t33 & y4; \ uintN_t z12 = t43 & y13; \ uintN_t z13 = t40 & y5; \ uintN_t z14 = t29 & y2; \ uintN_t z15 = t42 & y9; \ uintN_t z16 = t45 & y14; \ uintN_t z17 = t41 & y8; \ /* end */ /* Final linear transformation for the forward S-box, from Fig 4 of * the paper. */ #define SBOX_FORWARD_BOTTOM_TRANSFORM(output, uintN_t) \ uintN_t t46 = z15 ^ z16; \ uintN_t t47 = z10 ^ z11; \ uintN_t t48 = z5 ^ z13; \ uintN_t t49 = z9 ^ z10; \ uintN_t t50 = z2 ^ z12; \ uintN_t t51 = z2 ^ z5; \ uintN_t t52 = z7 ^ z8; \ uintN_t t53 = z0 ^ z3; \ uintN_t t54 = z6 ^ z7; \ uintN_t t55 = z16 ^ z17; \ uintN_t t56 = z12 ^ t48; \ uintN_t t57 = t50 ^ t53; \ uintN_t t58 = z4 ^ t46; \ uintN_t t59 = z3 ^ t54; \ uintN_t t60 = t46 ^ t57; \ uintN_t t61 = z14 ^ t57; \ uintN_t t62 = t52 ^ t58; \ uintN_t t63 = t49 ^ t58; \ uintN_t t64 = z4 ^ t59; \ uintN_t t65 = t61 ^ t62; \ uintN_t t66 = z1 ^ t63; \ output[7] = t59 ^ t63; \ output[1] = t56 ^ t62; \ output[0] = t48 ^ t60; \ uintN_t t67 = t64 ^ t65; \ output[4] = t53 ^ t66; \ output[3] = t51 ^ t66; \ output[2] = t47 ^ t65; \ output[6] = t64 ^ output[4]; \ output[5] = t55 ^ t67; \ /* end */ #define BITSLICED_SUBBYTES(output, input, uintN_t) do { \ SBOX_FORWARD_TOP_TRANSFORM(input, uintN_t); \ SBOX_CORE(uintN_t); \ SBOX_FORWARD_BOTTOM_TRANSFORM(output, uintN_t); \ } while (0) /* * Initial and final linear transformations for the backward S-box. I * generated these myself, by implementing the linear-transform * optimisation algorithm in the paper, and applying it to the * matrices calculated by _their_ top and bottom transformations, pre- * and post-multiplied as appropriate by the linear map in the inverse * S_box. */ #define SBOX_BACKWARD_TOP_TRANSFORM(input, uintN_t) \ uintN_t y5 = input[4] ^ input[6]; \ uintN_t y19 = input[3] ^ input[0]; \ uintN_t itmp8 = y5 ^ input[0]; \ uintN_t y4 = itmp8 ^ input[1]; \ uintN_t y9 = input[4] ^ input[3]; \ uintN_t y2 = y9 ^ y4; \ uintN_t itmp9 = y2 ^ input[7]; \ uintN_t y1 = y9 ^ input[0]; \ uintN_t y6 = y5 ^ input[7]; \ uintN_t y18 = y9 ^ input[5]; \ uintN_t y7 = y18 ^ y2; \ uintN_t y16 = y7 ^ y1; \ uintN_t y21 = y7 ^ input[1]; \ uintN_t y3 = input[4] ^ input[7]; \ uintN_t y13 = y16 ^ y21; \ uintN_t y8 = input[4] ^ y6; \ uintN_t y10 = y8 ^ y19; \ uintN_t y14 = y8 ^ y9; \ uintN_t y20 = itmp9 ^ input[2]; \ uintN_t y11 = y9 ^ y20; \ uintN_t i0 = y11 ^ y7; \ uintN_t y15 = i0 ^ y6; \ uintN_t y17 = y16 ^ y15; \ uintN_t y12 = itmp9 ^ input[3]; \ /* end */ #define SBOX_BACKWARD_BOTTOM_TRANSFORM(output, uintN_t) \ uintN_t otmp18 = z15 ^ z6; \ uintN_t otmp19 = z13 ^ otmp18; \ uintN_t otmp20 = z12 ^ otmp19; \ uintN_t otmp21 = z16 ^ otmp20; \ uintN_t otmp22 = z8 ^ otmp21; \ uintN_t otmp23 = z0 ^ otmp22; \ uintN_t otmp24 = otmp22 ^ z3; \ uintN_t otmp25 = otmp24 ^ z4; \ uintN_t otmp26 = otmp25 ^ z2; \ uintN_t otmp27 = z1 ^ otmp26; \ uintN_t otmp28 = z14 ^ otmp27; \ uintN_t otmp29 = otmp28 ^ z10; \ output[4] = z2 ^ otmp23; \ output[7] = z5 ^ otmp24; \ uintN_t otmp30 = z11 ^ otmp29; \ output[5] = z13 ^ otmp30; \ uintN_t otmp31 = otmp25 ^ z8; \ output[1] = z7 ^ otmp31; \ uintN_t otmp32 = z11 ^ z9; \ uintN_t otmp33 = z17 ^ otmp32; \ uintN_t otmp34 = otmp30 ^ otmp33; \ output[0] = z15 ^ otmp33; \ uintN_t otmp35 = z12 ^ otmp34; \ output[6] = otmp35 ^ z16; \ uintN_t otmp36 = z1 ^ otmp23; \ uintN_t otmp37 = z5 ^ otmp36; \ output[2] = z4 ^ otmp37; \ uintN_t otmp38 = z11 ^ output[1]; \ uintN_t otmp39 = z2 ^ otmp38; \ uintN_t otmp40 = z17 ^ otmp39; \ uintN_t otmp41 = z0 ^ otmp40; \ uintN_t otmp42 = z5 ^ otmp41; \ uintN_t otmp43 = otmp42 ^ z10; \ uintN_t otmp44 = otmp43 ^ z3; \ output[3] = otmp44 ^ z16; \ /* end */ #define BITSLICED_INVSUBBYTES(output, input, uintN_t) do { \ SBOX_BACKWARD_TOP_TRANSFORM(input, uintN_t); \ SBOX_CORE(uintN_t); \ SBOX_BACKWARD_BOTTOM_TRANSFORM(output, uintN_t); \ } while (0) /* ----- * The ShiftRows transformation. This operates independently on each * bit slice. */ #define SINGLE_BITSLICE_SHIFTROWS(output, input, uintN_t) do \ { \ uintN_t mask, mask2, mask3, diff, x = (input); \ /* Rotate rows 2 and 3 by 16 bits */ \ mask = 0x00CC * (((uintN_t)~(uintN_t)0) / 0xFFFF); \ diff = ((x >> 8) ^ x) & mask; \ x ^= diff ^ (diff << 8); \ /* Rotate rows 1 and 3 by 8 bits */ \ mask = 0x0AAA * (((uintN_t)~(uintN_t)0) / 0xFFFF); \ mask2 = 0xA000 * (((uintN_t)~(uintN_t)0) / 0xFFFF); \ mask3 = 0x5555 * (((uintN_t)~(uintN_t)0) / 0xFFFF); \ x = ((x >> 4) & mask) | ((x << 12) & mask2) | (x & mask3); \ /* Write output */ \ (output) = x; \ } while (0) #define SINGLE_BITSLICE_INVSHIFTROWS(output, input, uintN_t) do \ { \ uintN_t mask, mask2, mask3, diff, x = (input); \ /* Rotate rows 2 and 3 by 16 bits */ \ mask = 0x00CC * (((uintN_t)~(uintN_t)0) / 0xFFFF); \ diff = ((x >> 8) ^ x) & mask; \ x ^= diff ^ (diff << 8); \ /* Rotate rows 1 and 3 by 8 bits, the opposite way to ShiftRows */ \ mask = 0x000A * (((uintN_t)~(uintN_t)0) / 0xFFFF); \ mask2 = 0xAAA0 * (((uintN_t)~(uintN_t)0) / 0xFFFF); \ mask3 = 0x5555 * (((uintN_t)~(uintN_t)0) / 0xFFFF); \ x = ((x >> 12) & mask) | ((x << 4) & mask2) | (x & mask3); \ /* Write output */ \ (output) = x; \ } while (0) #define BITSLICED_SHIFTROWS(output, input, uintN_t) do \ { \ ITERATE(SINGLE_BITSLICE_SHIFTROWS, output, input, uintN_t); \ } while (0) #define BITSLICED_INVSHIFTROWS(output, input, uintN_t) do \ { \ ITERATE(SINGLE_BITSLICE_INVSHIFTROWS, output, input, uintN_t); \ } while (0) /* ----- * The MixColumns transformation. This has to operate on all eight bit * slices at once, and also passes data back and forth between the * bits in an adjacent group of 4 within each slice. * * Notation: let F = GF(2)[X]/ be the finite field * used in AES, and let R = F[Y]/ be the ring whose elements * represent the possible contents of a column of the matrix. I use X * and Y below in those senses, i.e. X is the value in F that * represents the byte 0x02, and Y is the value in R that cycles the * four bytes around by one if you multiply by it. */ /* Multiply every column by Y^3, i.e. cycle it round one place to the * right. Operates on one bit slice at a time; you have to wrap it in * ITERATE to affect all the data at once. */ #define BITSLICED_MUL_BY_Y3(output, input, uintN_t) do \ { \ uintN_t mask, mask2, x; \ mask = 0x8 * (((uintN_t)~(uintN_t)0) / 0xF); \ mask2 = 0x7 * (((uintN_t)~(uintN_t)0) / 0xF); \ x = input; \ output = ((x << 3) & mask) ^ ((x >> 1) & mask2); \ } while (0) /* Multiply every column by Y^2. */ #define BITSLICED_MUL_BY_Y2(output, input, uintN_t) do \ { \ uintN_t mask, mask2, x; \ mask = 0xC * (((uintN_t)~(uintN_t)0) / 0xF); \ mask2 = 0x3 * (((uintN_t)~(uintN_t)0) / 0xF); \ x = input; \ output = ((x << 2) & mask) ^ ((x >> 2) & mask2); \ } while (0) #define BITSLICED_MUL_BY_1_Y3(output, input, uintN_t) do \ { \ uintN_t tmp = input; \ BITSLICED_MUL_BY_Y3(tmp, input, uintN_t); \ output = input ^ tmp; \ } while (0) /* Multiply every column by 1+Y^2. */ #define BITSLICED_MUL_BY_1_Y2(output, input, uintN_t) do \ { \ uintN_t tmp = input; \ BITSLICED_MUL_BY_Y2(tmp, input, uintN_t); \ output = input ^ tmp; \ } while (0) /* Multiply every field element by X. This has to feed data between * slices, so it does the whole job in one go without needing ITERATE. */ #define BITSLICED_MUL_BY_X(output, input, uintN_t) do \ { \ uintN_t bit7 = input[7]; \ output[7] = input[6]; \ output[6] = input[5]; \ output[5] = input[4]; \ output[4] = input[3] ^ bit7; \ output[3] = input[2] ^ bit7; \ output[2] = input[1]; \ output[1] = input[0] ^ bit7; \ output[0] = bit7; \ } while (0) /* * The MixColumns constant is * M = X + Y + Y^2 + (X+1)Y^3 * which we construct by rearranging it into * M = 1 + (1+Y^3) [ X + (1+Y^2) ] */ #define BITSLICED_MIXCOLUMNS(output, input, uintN_t) do \ { \ uintN_t a[8], aX[8], b[8]; \ /* a = input * (1+Y^3) */ \ ITERATE(BITSLICED_MUL_BY_1_Y3, a, input, uintN_t); \ /* aX = a * X */ \ BITSLICED_MUL_BY_X(aX, a, uintN_t); \ /* b = a * (1+Y^2) = input * (1+Y+Y^2+Y^3) */ \ ITERATE(BITSLICED_MUL_BY_1_Y2, b, a, uintN_t); \ /* output = input + aX + b (reusing a as a temp */ \ BITSLICED_ADD(a, aX, b); \ BITSLICED_ADD(output, input, a); \ } while (0) /* * The InvMixColumns constant, written out longhand, is * I = (X^3+X^2+X) + (X^3+1)Y + (X^3+X^2+1)Y^2 + (X^3+X+1)Y^3 * We represent this as * I = (X^3+X^2+X+1)(Y^3+Y^2+Y+1) + 1 + X(Y+Y^2) + X^2(Y+Y^3) */ #define BITSLICED_INVMIXCOLUMNS(output, input, uintN_t) do \ { \ /* We need input * X^i for i=1,...,3 */ \ uintN_t X[8], X2[8], X3[8]; \ BITSLICED_MUL_BY_X(X, input, uintN_t); \ BITSLICED_MUL_BY_X(X2, X, uintN_t); \ BITSLICED_MUL_BY_X(X3, X2, uintN_t); \ /* Sum them all and multiply by 1+Y+Y^2+Y^3. */ \ uintN_t S[8]; \ BITSLICED_ADD(S, input, X); \ BITSLICED_ADD(S, S, X2); \ BITSLICED_ADD(S, S, X3); \ ITERATE(BITSLICED_MUL_BY_1_Y3, S, S, uintN_t); \ ITERATE(BITSLICED_MUL_BY_1_Y2, S, S, uintN_t); \ /* Compute the X(Y+Y^2) term. */ \ uintN_t A[8]; \ ITERATE(BITSLICED_MUL_BY_1_Y3, A, X, uintN_t); \ ITERATE(BITSLICED_MUL_BY_Y2, A, A, uintN_t); \ /* Compute the X^2(Y+Y^3) term. */ \ uintN_t B[8]; \ ITERATE(BITSLICED_MUL_BY_1_Y2, B, X2, uintN_t); \ ITERATE(BITSLICED_MUL_BY_Y3, B, B, uintN_t); \ /* And add all the pieces together. */ \ BITSLICED_ADD(S, S, input); \ BITSLICED_ADD(S, S, A); \ BITSLICED_ADD(output, S, B); \ } while (0) /* ----- * Put it all together into a cipher round. */ /* Dummy macro to get rid of the MixColumns in the final round. */ #define NO_MIXCOLUMNS(out, in, uintN_t) do {} while (0) #define ENCRYPT_ROUND_FN(suffix, uintN_t, mixcol_macro) \ static void aes_sliced_round_e_##suffix( \ uintN_t output[8], const uintN_t input[8], const uintN_t roundkey[8]) \ { \ BITSLICED_SUBBYTES(output, input, uintN_t); \ BITSLICED_SHIFTROWS(output, output, uintN_t); \ mixcol_macro(output, output, uintN_t); \ BITSLICED_ADD(output, output, roundkey); \ } ENCRYPT_ROUND_FN(serial, uint16_t, BITSLICED_MIXCOLUMNS) ENCRYPT_ROUND_FN(serial_last, uint16_t, NO_MIXCOLUMNS) ENCRYPT_ROUND_FN(parallel, BignumInt, BITSLICED_MIXCOLUMNS) ENCRYPT_ROUND_FN(parallel_last, BignumInt, NO_MIXCOLUMNS) #define DECRYPT_ROUND_FN(suffix, uintN_t, mixcol_macro) \ static void aes_sliced_round_d_##suffix( \ uintN_t output[8], const uintN_t input[8], const uintN_t roundkey[8]) \ { \ BITSLICED_ADD(output, input, roundkey); \ mixcol_macro(output, output, uintN_t); \ BITSLICED_INVSUBBYTES(output, output, uintN_t); \ BITSLICED_INVSHIFTROWS(output, output, uintN_t); \ } #if 0 /* no cipher mode we support requires serial decryption */ DECRYPT_ROUND_FN(serial, uint16_t, BITSLICED_INVMIXCOLUMNS) DECRYPT_ROUND_FN(serial_first, uint16_t, NO_MIXCOLUMNS) #endif DECRYPT_ROUND_FN(parallel, BignumInt, BITSLICED_INVMIXCOLUMNS) DECRYPT_ROUND_FN(parallel_first, BignumInt, NO_MIXCOLUMNS) /* ----- * Key setup function. */ typedef struct aes_sliced_key aes_sliced_key; struct aes_sliced_key { BignumInt roundkeys_parallel[MAXROUNDKEYS * 8]; uint16_t roundkeys_serial[MAXROUNDKEYS * 8]; unsigned rounds; }; static void aes_sliced_key_setup( aes_sliced_key *sk, const void *vkey, size_t keybits) { const unsigned char *key = (const unsigned char *)vkey; size_t key_words = keybits / 32; sk->rounds = key_words + 6; size_t sched_words = (sk->rounds + 1) * 4; unsigned rconpos = 0; uint16_t *outslices = sk->roundkeys_serial; unsigned outshift = 0; memset(sk->roundkeys_serial, 0, sizeof(sk->roundkeys_serial)); uint8_t inblk[16]; memset(inblk, 0, 16); uint16_t slices[8]; for (size_t i = 0; i < sched_words; i++) { /* * Prepare a word of round key in the low 4 bits of each * integer in slices[]. */ if (i < key_words) { memcpy(inblk, key + 4*i, 4); TO_BITSLICES(slices, inblk, uint16_t, =, 0); } else { unsigned wordindex, bitshift; uint16_t *prevslices; /* Fetch the (i-1)th key word */ wordindex = i-1; bitshift = 4 * (wordindex & 3); prevslices = sk->roundkeys_serial + 8 * (wordindex >> 2); for (size_t i = 0; i < 8; i++) slices[i] = prevslices[i] >> bitshift; /* Decide what we're doing in this expansion stage */ bool rotate_and_round_constant = (i % key_words == 0); bool sub = rotate_and_round_constant || (key_words == 8 && i % 8 == 4); if (rotate_and_round_constant) { for (size_t i = 0; i < 8; i++) slices[i] = ((slices[i] << 3) | (slices[i] >> 1)) & 0xF; } if (sub) { /* Apply the SubBytes transform to the key word. But * here we need to apply the _full_ SubBytes from the * spec, including the constant which our S-box leaves * out. */ BITSLICED_SUBBYTES(slices, slices, uint16_t); slices[0] ^= 0xFFFF; slices[1] ^= 0xFFFF; slices[5] ^= 0xFFFF; slices[6] ^= 0xFFFF; } if (rotate_and_round_constant) { assert(rconpos < lenof(key_setup_round_constants)); uint8_t rcon = key_setup_round_constants[rconpos++]; for (size_t i = 0; i < 8; i++) slices[i] ^= 1 & (rcon >> i); } /* Combine with the (i-Nk)th key word */ wordindex = i - key_words; bitshift = 4 * (wordindex & 3); prevslices = sk->roundkeys_serial + 8 * (wordindex >> 2); for (size_t i = 0; i < 8; i++) slices[i] ^= prevslices[i] >> bitshift; } /* * Now copy it into sk. */ for (unsigned b = 0; b < 8; b++) outslices[b] |= (slices[b] & 0xF) << outshift; outshift += 4; if (outshift == 16) { outshift = 0; outslices += 8; } } smemclr(inblk, sizeof(inblk)); smemclr(slices, sizeof(slices)); /* * Add the S-box constant to every round key after the first one, * compensating for it being left out in the main cipher. */ for (size_t i = 8; i < 8 * (sched_words/4); i += 8) { sk->roundkeys_serial[i+0] ^= 0xFFFF; sk->roundkeys_serial[i+1] ^= 0xFFFF; sk->roundkeys_serial[i+5] ^= 0xFFFF; sk->roundkeys_serial[i+6] ^= 0xFFFF; } /* * Replicate that set of round keys into larger integers for the * parallel versions of the cipher. */ for (size_t i = 0; i < 8 * (sched_words / 4); i++) { sk->roundkeys_parallel[i] = sk->roundkeys_serial[i] * ((BignumInt)~(BignumInt)0 / 0xFFFF); } } /* ----- * The full cipher primitive, including transforming the input and * output to/from bit-sliced form. */ #define ENCRYPT_FN(suffix, uintN_t, nblocks) \ static void aes_sliced_e_##suffix( \ uint8_t *output, const uint8_t *input, const aes_sliced_key *sk) \ { \ uintN_t state[8]; \ TO_BITSLICES(state, input, uintN_t, =, 0); \ for (unsigned i = 1; i < nblocks; i++) { \ input += 16; \ TO_BITSLICES(state, input, uintN_t, |=, i*16); \ } \ const uintN_t *keys = sk->roundkeys_##suffix; \ BITSLICED_ADD(state, state, keys); \ keys += 8; \ for (unsigned i = 0; i < sk->rounds-1; i++) { \ aes_sliced_round_e_##suffix(state, state, keys); \ keys += 8; \ } \ aes_sliced_round_e_##suffix##_last(state, state, keys); \ for (unsigned i = 0; i < nblocks; i++) { \ FROM_BITSLICES(output, state, i*16); \ output += 16; \ } \ } #define DECRYPT_FN(suffix, uintN_t, nblocks) \ static void aes_sliced_d_##suffix( \ uint8_t *output, const uint8_t *input, const aes_sliced_key *sk) \ { \ uintN_t state[8]; \ TO_BITSLICES(state, input, uintN_t, =, 0); \ for (unsigned i = 1; i < nblocks; i++) { \ input += 16; \ TO_BITSLICES(state, input, uintN_t, |=, i*16); \ } \ const uintN_t *keys = sk->roundkeys_##suffix + 8*sk->rounds; \ aes_sliced_round_d_##suffix##_first(state, state, keys); \ keys -= 8; \ for (unsigned i = 0; i < sk->rounds-1; i++) { \ aes_sliced_round_d_##suffix(state, state, keys); \ keys -= 8; \ } \ BITSLICED_ADD(state, state, keys); \ for (unsigned i = 0; i < nblocks; i++) { \ FROM_BITSLICES(output, state, i*16); \ output += 16; \ } \ } ENCRYPT_FN(serial, uint16_t, 1) #if 0 /* no cipher mode we support requires serial decryption */ DECRYPT_FN(serial, uint16_t, 1) #endif ENCRYPT_FN(parallel, BignumInt, SLICE_PARALLELISM) DECRYPT_FN(parallel, BignumInt, SLICE_PARALLELISM) /* ----- * The SSH interface and the cipher modes. */ #define SDCTR_WORDS (16 / BIGNUM_INT_BYTES) typedef struct aes_sw_context aes_sw_context; struct aes_sw_context { aes_sliced_key sk; union { struct { /* In CBC mode, the IV is just a copy of the last seen * cipher block. */ uint8_t prevblk[16]; } cbc; struct { /* In SDCTR mode, we keep the counter itself in a form * that's easy to increment. We also use the parallel * version of the core AES function, so we'll encrypt * multiple counter values in one go. That won't align * nicely with the sizes of data we're asked to encrypt, * so we must also store a cache of the last set of * keystream blocks we generated, and our current position * within that cache. */ BignumInt counter[SDCTR_WORDS]; uint8_t keystream[SLICE_PARALLELISM * 16]; uint8_t *keystream_pos; } sdctr; } iv; ssh_cipher ciph; }; static ssh_cipher *aes_sw_new(const ssh_cipheralg *alg) { aes_sw_context *ctx = snew(aes_sw_context); ctx->ciph.vt = alg; return &ctx->ciph; } static void aes_sw_free(ssh_cipher *ciph) { aes_sw_context *ctx = container_of(ciph, aes_sw_context, ciph); smemclr(ctx, sizeof(*ctx)); sfree(ctx); } static void aes_sw_setkey(ssh_cipher *ciph, const void *vkey) { aes_sw_context *ctx = container_of(ciph, aes_sw_context, ciph); aes_sliced_key_setup(&ctx->sk, vkey, ctx->ciph.vt->real_keybits); } static void aes_sw_setiv_cbc(ssh_cipher *ciph, const void *iv) { aes_sw_context *ctx = container_of(ciph, aes_sw_context, ciph); memcpy(ctx->iv.cbc.prevblk, iv, 16); } static void aes_sw_setiv_sdctr(ssh_cipher *ciph, const void *viv) { aes_sw_context *ctx = container_of(ciph, aes_sw_context, ciph); const uint8_t *iv = (const uint8_t *)viv; /* Import the initial counter value into the internal representation */ for (unsigned i = 0; i < SDCTR_WORDS; i++) ctx->iv.sdctr.counter[i] = GET_BIGNUMINT_MSB_FIRST( iv + 16 - BIGNUM_INT_BYTES - i*BIGNUM_INT_BYTES); /* Set keystream_pos to indicate that the keystream cache is * currently empty */ ctx->iv.sdctr.keystream_pos = ctx->iv.sdctr.keystream + sizeof(ctx->iv.sdctr.keystream); } typedef void (*aes_sw_fn)(uint32_t v[4], const uint32_t *keysched); static inline void memxor16(void *vout, const void *vlhs, const void *vrhs) { uint8_t *out = (uint8_t *)vout; const uint8_t *lhs = (const uint8_t *)vlhs, *rhs = (const uint8_t *)vrhs; uint64_t w; w = GET_64BIT_LSB_FIRST(lhs); w ^= GET_64BIT_LSB_FIRST(rhs); PUT_64BIT_LSB_FIRST(out, w); w = GET_64BIT_LSB_FIRST(lhs + 8); w ^= GET_64BIT_LSB_FIRST(rhs + 8); PUT_64BIT_LSB_FIRST(out + 8, w); } static inline void aes_cbc_sw_encrypt( ssh_cipher *ciph, void *vblk, int blklen) { aes_sw_context *ctx = container_of(ciph, aes_sw_context, ciph); /* * CBC encryption has to be done serially, because the input to * each run of the cipher includes the output from the previous * run. */ for (uint8_t *blk = (uint8_t *)vblk, *finish = blk + blklen; blk < finish; blk += 16) { /* * We use the IV array itself as the location for the * encryption, because there's no reason not to. */ /* XOR the new plaintext block into the previous cipher block */ memxor16(ctx->iv.cbc.prevblk, ctx->iv.cbc.prevblk, blk); /* Run the cipher over the result, which leaves it * conveniently already stored in ctx->iv */ aes_sliced_e_serial( ctx->iv.cbc.prevblk, ctx->iv.cbc.prevblk, &ctx->sk); /* Copy it to the output location */ memcpy(blk, ctx->iv.cbc.prevblk, 16); } } static inline void aes_cbc_sw_decrypt( ssh_cipher *ciph, void *vblk, int blklen) { aes_sw_context *ctx = container_of(ciph, aes_sw_context, ciph); uint8_t *blk = (uint8_t *)vblk; /* * CBC decryption can run in parallel, because all the * _ciphertext_ blocks are already available. */ size_t blocks_remaining = blklen / 16; uint8_t data[SLICE_PARALLELISM * 16]; /* Zeroing the data array is probably overcautious, but it avoids * technically undefined behaviour from leaving it uninitialised * if our very first iteration doesn't include enough cipher * blocks to populate it fully */ memset(data, 0, sizeof(data)); while (blocks_remaining > 0) { /* Number of blocks we'll handle in this iteration. If we're * dealing with fewer than the maximum, it doesn't matter - * it's harmless to run the full parallel cipher function * anyway. */ size_t blocks = (blocks_remaining < SLICE_PARALLELISM ? blocks_remaining : SLICE_PARALLELISM); /* Parallel-decrypt the input, in a separate array so we still * have the cipher stream available for XORing. */ memcpy(data, blk, 16 * blocks); aes_sliced_d_parallel(data, data, &ctx->sk); /* Write the output and update the IV */ for (size_t i = 0; i < blocks; i++) { uint8_t *decrypted = data + 16*i; uint8_t *output = blk + 16*i; memxor16(decrypted, decrypted, ctx->iv.cbc.prevblk); memcpy(ctx->iv.cbc.prevblk, output, 16); memcpy(output, decrypted, 16); } /* Advance the input pointer. */ blk += 16 * blocks; blocks_remaining -= blocks; } smemclr(data, sizeof(data)); } static inline void aes_sdctr_sw( ssh_cipher *ciph, void *vblk, int blklen) { aes_sw_context *ctx = container_of(ciph, aes_sw_context, ciph); /* * SDCTR encrypt/decrypt loops round one block at a time XORing * the keystream into the user's data, and periodically has to run * a parallel encryption operation to get more keystream. */ uint8_t *keystream_end = ctx->iv.sdctr.keystream + sizeof(ctx->iv.sdctr.keystream); for (uint8_t *blk = (uint8_t *)vblk, *finish = blk + blklen; blk < finish; blk += 16) { if (ctx->iv.sdctr.keystream_pos == keystream_end) { /* * Generate some keystream. */ for (uint8_t *block = ctx->iv.sdctr.keystream; block < keystream_end; block += 16) { /* Format the counter value into the buffer. */ for (unsigned i = 0; i < SDCTR_WORDS; i++) PUT_BIGNUMINT_MSB_FIRST( block + 16 - BIGNUM_INT_BYTES - i*BIGNUM_INT_BYTES, ctx->iv.sdctr.counter[i]); /* Increment the counter. */ BignumCarry carry = 1; for (unsigned i = 0; i < SDCTR_WORDS; i++) BignumADC(ctx->iv.sdctr.counter[i], carry, ctx->iv.sdctr.counter[i], 0, carry); } /* Encrypt all those counter blocks. */ aes_sliced_e_parallel(ctx->iv.sdctr.keystream, ctx->iv.sdctr.keystream, &ctx->sk); /* Reset keystream_pos to the start of the buffer. */ ctx->iv.sdctr.keystream_pos = ctx->iv.sdctr.keystream; } memxor16(blk, blk, ctx->iv.sdctr.keystream_pos); ctx->iv.sdctr.keystream_pos += 16; } } #define SW_ENC_DEC(len) \ static void aes##len##_cbc_sw_encrypt( \ ssh_cipher *ciph, void *vblk, int blklen) \ { aes_cbc_sw_encrypt(ciph, vblk, blklen); } \ static void aes##len##_cbc_sw_decrypt( \ ssh_cipher *ciph, void *vblk, int blklen) \ { aes_cbc_sw_decrypt(ciph, vblk, blklen); } \ static void aes##len##_sdctr_sw( \ ssh_cipher *ciph, void *vblk, int blklen) \ { aes_sdctr_sw(ciph, vblk, blklen); } SW_ENC_DEC(128) SW_ENC_DEC(192) SW_ENC_DEC(256) /* ---------------------------------------------------------------------- * Hardware-accelerated implementation of AES using x86 AES-NI. */ #if HW_AES == HW_AES_NI /* * Set target architecture for Clang and GCC */ #if !defined(__clang__) && defined(__GNUC__) # pragma GCC target("aes") # pragma GCC target("sse4.1") #endif #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) # define FUNC_ISA __attribute__ ((target("sse4.1,aes"))) #else # define FUNC_ISA #endif #include #include #if defined(__clang__) || defined(__GNUC__) #include #define GET_CPU_ID(out) __cpuid(1, (out)[0], (out)[1], (out)[2], (out)[3]) #else #define GET_CPU_ID(out) __cpuid(out, 1) #endif bool aes_hw_available(void) { /* * Determine if AES is available on this CPU, by checking that * both AES itself and SSE4.1 are supported. */ unsigned int CPUInfo[4]; GET_CPU_ID(CPUInfo); return (CPUInfo[2] & (1 << 25)) && (CPUInfo[2] & (1 << 19)); } /* * Core AES-NI encrypt/decrypt functions, one per length and direction. */ #define NI_CIPHER(len, dir, dirlong, repmacro) \ static FUNC_ISA inline __m128i aes_ni_##len##_##dir( \ __m128i v, const __m128i *keysched) \ { \ v = _mm_xor_si128(v, *keysched++); \ repmacro(v = _mm_aes##dirlong##_si128(v, *keysched++);); \ return _mm_aes##dirlong##last_si128(v, *keysched); \ } NI_CIPHER(128, e, enc, REP9) NI_CIPHER(128, d, dec, REP9) NI_CIPHER(192, e, enc, REP11) NI_CIPHER(192, d, dec, REP11) NI_CIPHER(256, e, enc, REP13) NI_CIPHER(256, d, dec, REP13) /* * The main key expansion. */ static FUNC_ISA void aes_ni_key_expand( const unsigned char *key, size_t key_words, __m128i *keysched_e, __m128i *keysched_d) { size_t rounds = key_words + 6; size_t sched_words = (rounds + 1) * 4; /* * Store the key schedule as 32-bit integers during expansion, so * that it's easy to refer back to individual previous words. We * collect them into the final __m128i form at the end. */ uint32_t sched[MAXROUNDKEYS * 4]; unsigned rconpos = 0; for (size_t i = 0; i < sched_words; i++) { if (i < key_words) { sched[i] = GET_32BIT_LSB_FIRST(key + 4 * i); } else { uint32_t temp = sched[i - 1]; bool rotate_and_round_constant = (i % key_words == 0); bool only_sub = (key_words == 8 && i % 8 == 4); if (rotate_and_round_constant) { __m128i v = _mm_setr_epi32(0,temp,0,0); v = _mm_aeskeygenassist_si128(v, 0); temp = _mm_extract_epi32(v, 1); assert(rconpos < lenof(key_setup_round_constants)); temp ^= key_setup_round_constants[rconpos++]; } else if (only_sub) { __m128i v = _mm_setr_epi32(0,temp,0,0); v = _mm_aeskeygenassist_si128(v, 0); temp = _mm_extract_epi32(v, 0); } sched[i] = sched[i - key_words] ^ temp; } } /* * Combine the key schedule words into __m128i vectors and store * them in the output context. */ for (size_t round = 0; round <= rounds; round++) keysched_e[round] = _mm_setr_epi32( sched[4*round ], sched[4*round+1], sched[4*round+2], sched[4*round+3]); smemclr(sched, sizeof(sched)); /* * Now prepare the modified keys for the inverse cipher. */ for (size_t eround = 0; eround <= rounds; eround++) { size_t dround = rounds - eround; __m128i rkey = keysched_e[eround]; if (eround && dround) /* neither first nor last */ rkey = _mm_aesimc_si128(rkey); keysched_d[dround] = rkey; } } /* * Auxiliary routine to increment the 128-bit counter used in SDCTR * mode. */ static FUNC_ISA inline __m128i aes_ni_sdctr_increment(__m128i v) { const __m128i ONE = _mm_setr_epi32(1,0,0,0); const __m128i ZERO = _mm_setzero_si128(); /* Increment the low-order 64 bits of v */ v = _mm_add_epi64(v, ONE); /* Check if they've become zero */ __m128i cmp = _mm_cmpeq_epi64(v, ZERO); /* If so, the low half of cmp is all 1s. Pack that into the high * half of addend with zero in the low half. */ __m128i addend = _mm_unpacklo_epi64(ZERO, cmp); /* And subtract that from v, which increments the high 64 bits iff * the low 64 wrapped round. */ v = _mm_sub_epi64(v, addend); return v; } /* * Auxiliary routine to reverse the byte order of a vector, so that * the SDCTR IV can be made big-endian for feeding to the cipher. */ static FUNC_ISA inline __m128i aes_ni_sdctr_reverse(__m128i v) { v = _mm_shuffle_epi8( v, _mm_setr_epi8(15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0)); return v; } /* * The SSH interface and the cipher modes. */ typedef struct aes_ni_context aes_ni_context; struct aes_ni_context { __m128i keysched_e[MAXROUNDKEYS], keysched_d[MAXROUNDKEYS], iv; void *pointer_to_free; ssh_cipher ciph; }; static ssh_cipher *aes_hw_new(const ssh_cipheralg *alg) { if (!aes_hw_available_cached()) return NULL; /* * The __m128i variables in the context structure need to be * 16-byte aligned, but not all malloc implementations that this * code has to work with will guarantee to return a 16-byte * aligned pointer. So we over-allocate, manually realign the * pointer ourselves, and store the original one inside the * context so we know how to free it later. */ void *allocation = smalloc(sizeof(aes_ni_context) + 15); uintptr_t alloc_address = (uintptr_t)allocation; uintptr_t aligned_address = (alloc_address + 15) & ~15; aes_ni_context *ctx = (aes_ni_context *)aligned_address; ctx->ciph.vt = alg; ctx->pointer_to_free = allocation; return &ctx->ciph; } static void aes_hw_free(ssh_cipher *ciph) { aes_ni_context *ctx = container_of(ciph, aes_ni_context, ciph); void *allocation = ctx->pointer_to_free; smemclr(ctx, sizeof(*ctx)); sfree(allocation); } static void aes_hw_setkey(ssh_cipher *ciph, const void *vkey) { aes_ni_context *ctx = container_of(ciph, aes_ni_context, ciph); const unsigned char *key = (const unsigned char *)vkey; aes_ni_key_expand(key, ctx->ciph.vt->real_keybits / 32, ctx->keysched_e, ctx->keysched_d); } static FUNC_ISA void aes_hw_setiv_cbc(ssh_cipher *ciph, const void *iv) { aes_ni_context *ctx = container_of(ciph, aes_ni_context, ciph); ctx->iv = _mm_loadu_si128(iv); } static FUNC_ISA void aes_hw_setiv_sdctr(ssh_cipher *ciph, const void *iv) { aes_ni_context *ctx = container_of(ciph, aes_ni_context, ciph); __m128i counter = _mm_loadu_si128(iv); ctx->iv = aes_ni_sdctr_reverse(counter); } typedef __m128i (*aes_ni_fn)(__m128i v, const __m128i *keysched); static FUNC_ISA inline void aes_cbc_ni_encrypt( ssh_cipher *ciph, void *vblk, int blklen, aes_ni_fn encrypt) { aes_ni_context *ctx = container_of(ciph, aes_ni_context, ciph); for (uint8_t *blk = (uint8_t *)vblk, *finish = blk + blklen; blk < finish; blk += 16) { __m128i plaintext = _mm_loadu_si128((const __m128i *)blk); __m128i cipher_input = _mm_xor_si128(plaintext, ctx->iv); __m128i ciphertext = encrypt(cipher_input, ctx->keysched_e); _mm_storeu_si128((__m128i *)blk, ciphertext); ctx->iv = ciphertext; } } static FUNC_ISA inline void aes_cbc_ni_decrypt( ssh_cipher *ciph, void *vblk, int blklen, aes_ni_fn decrypt) { aes_ni_context *ctx = container_of(ciph, aes_ni_context, ciph); for (uint8_t *blk = (uint8_t *)vblk, *finish = blk + blklen; blk < finish; blk += 16) { __m128i ciphertext = _mm_loadu_si128((const __m128i *)blk); __m128i decrypted = decrypt(ciphertext, ctx->keysched_d); __m128i plaintext = _mm_xor_si128(decrypted, ctx->iv); _mm_storeu_si128((__m128i *)blk, plaintext); ctx->iv = ciphertext; } } static FUNC_ISA inline void aes_sdctr_ni( ssh_cipher *ciph, void *vblk, int blklen, aes_ni_fn encrypt) { aes_ni_context *ctx = container_of(ciph, aes_ni_context, ciph); for (uint8_t *blk = (uint8_t *)vblk, *finish = blk + blklen; blk < finish; blk += 16) { __m128i counter = aes_ni_sdctr_reverse(ctx->iv); __m128i keystream = encrypt(counter, ctx->keysched_e); __m128i input = _mm_loadu_si128((const __m128i *)blk); __m128i output = _mm_xor_si128(input, keystream); _mm_storeu_si128((__m128i *)blk, output); ctx->iv = aes_ni_sdctr_increment(ctx->iv); } } #define NI_ENC_DEC(len) \ static FUNC_ISA void aes##len##_cbc_hw_encrypt( \ ssh_cipher *ciph, void *vblk, int blklen) \ { aes_cbc_ni_encrypt(ciph, vblk, blklen, aes_ni_##len##_e); } \ static FUNC_ISA void aes##len##_cbc_hw_decrypt( \ ssh_cipher *ciph, void *vblk, int blklen) \ { aes_cbc_ni_decrypt(ciph, vblk, blklen, aes_ni_##len##_d); } \ static FUNC_ISA void aes##len##_sdctr_hw( \ ssh_cipher *ciph, void *vblk, int blklen) \ { aes_sdctr_ni(ciph, vblk, blklen, aes_ni_##len##_e); } \ NI_ENC_DEC(128) NI_ENC_DEC(192) NI_ENC_DEC(256) /* ---------------------------------------------------------------------- * Hardware-accelerated implementation of AES using Arm NEON. */ #elif HW_AES == HW_AES_NEON /* * Manually set the target architecture, if we decided above that we * need to. */ #ifdef USE_CLANG_ATTR_TARGET_AARCH64 /* * A spot of cheating: redefine some ACLE feature macros before * including arm_neon.h. Otherwise we won't get the AES intrinsics * defined by that header, because it will be looking at the settings * for the whole translation unit rather than the ones we're going to * put on some particular functions using __attribute__((target)). */ #define __ARM_NEON 1 #define __ARM_FEATURE_CRYPTO 1 #define __ARM_FEATURE_AES 1 #define FUNC_ISA __attribute__ ((target("neon,crypto"))) #endif /* USE_CLANG_ATTR_TARGET_AARCH64 */ #ifndef FUNC_ISA #define FUNC_ISA #endif #ifdef USE_ARM64_NEON_H #include #else #include #endif static bool aes_hw_available(void) { /* * For Arm, we delegate to a per-platform AES detection function, * because it has to be implemented by asking the operating system * rather than directly querying the CPU. * * That's because Arm systems commonly have multiple cores that * are not all alike, so any method of querying whether NEON * crypto instructions work on the _current_ CPU - even one as * crude as just trying one and catching the SIGILL - wouldn't * give an answer that you could still rely on the first time the * OS migrated your process to another CPU. */ return platform_aes_hw_available(); } /* * Core NEON encrypt/decrypt functions, one per length and direction. */ #define NEON_CIPHER(len, repmacro) \ static FUNC_ISA inline uint8x16_t aes_neon_##len##_e( \ uint8x16_t v, const uint8x16_t *keysched) \ { \ repmacro(v = vaesmcq_u8(vaeseq_u8(v, *keysched++));); \ v = vaeseq_u8(v, *keysched++); \ return veorq_u8(v, *keysched); \ } \ static FUNC_ISA inline uint8x16_t aes_neon_##len##_d( \ uint8x16_t v, const uint8x16_t *keysched) \ { \ repmacro(v = vaesimcq_u8(vaesdq_u8(v, *keysched++));); \ v = vaesdq_u8(v, *keysched++); \ return veorq_u8(v, *keysched); \ } NEON_CIPHER(128, REP9) NEON_CIPHER(192, REP11) NEON_CIPHER(256, REP13) /* * The main key expansion. */ static FUNC_ISA void aes_neon_key_expand( const unsigned char *key, size_t key_words, uint8x16_t *keysched_e, uint8x16_t *keysched_d) { size_t rounds = key_words + 6; size_t sched_words = (rounds + 1) * 4; /* * Store the key schedule as 32-bit integers during expansion, so * that it's easy to refer back to individual previous words. We * collect them into the final uint8x16_t form at the end. */ uint32_t sched[MAXROUNDKEYS * 4]; unsigned rconpos = 0; for (size_t i = 0; i < sched_words; i++) { if (i < key_words) { sched[i] = GET_32BIT_LSB_FIRST(key + 4 * i); } else { uint32_t temp = sched[i - 1]; bool rotate_and_round_constant = (i % key_words == 0); bool sub = rotate_and_round_constant || (key_words == 8 && i % 8 == 4); if (rotate_and_round_constant) temp = (temp << 24) | (temp >> 8); if (sub) { uint32x4_t v32 = vdupq_n_u32(temp); uint8x16_t v8 = vreinterpretq_u8_u32(v32); v8 = vaeseq_u8(v8, vdupq_n_u8(0)); v32 = vreinterpretq_u32_u8(v8); temp = vget_lane_u32(vget_low_u32(v32), 0); } if (rotate_and_round_constant) { assert(rconpos < lenof(key_setup_round_constants)); temp ^= key_setup_round_constants[rconpos++]; } sched[i] = sched[i - key_words] ^ temp; } } /* * Combine the key schedule words into uint8x16_t vectors and * store them in the output context. */ for (size_t round = 0; round <= rounds; round++) keysched_e[round] = vreinterpretq_u8_u32(vld1q_u32(sched + 4*round)); smemclr(sched, sizeof(sched)); /* * Now prepare the modified keys for the inverse cipher. */ for (size_t eround = 0; eround <= rounds; eround++) { size_t dround = rounds - eround; uint8x16_t rkey = keysched_e[eround]; if (eround && dround) /* neither first nor last */ rkey = vaesimcq_u8(rkey); keysched_d[dround] = rkey; } } /* * Auxiliary routine to reverse the byte order of a vector, so that * the SDCTR IV can be made big-endian for feeding to the cipher. * * In fact we don't need to reverse the vector _all_ the way; we leave * the two lanes in MSW,LSW order, because that makes no difference to * the efficiency of the increment. That way we only have to reverse * bytes within each lane in this function. */ static FUNC_ISA inline uint8x16_t aes_neon_sdctr_reverse(uint8x16_t v) { return vrev64q_u8(v); } /* * Auxiliary routine to increment the 128-bit counter used in SDCTR * mode. There's no instruction to treat a 128-bit vector as a single * long integer, so instead we have to increment the bottom half * unconditionally, and the top half if the bottom half started off as * all 1s (in which case there was about to be a carry). */ static FUNC_ISA inline uint8x16_t aes_neon_sdctr_increment(uint8x16_t in) { #ifdef __aarch64__ /* There will be a carry if the low 64 bits are all 1s. */ uint64x1_t all1 = vcreate_u64(0xFFFFFFFFFFFFFFFF); uint64x1_t carry = vceq_u64(vget_high_u64(vreinterpretq_u64_u8(in)), all1); /* Make a word whose bottom half is unconditionally all 1s, and * the top half is 'carry', i.e. all 0s most of the time but all * 1s if we need to increment the top half. Then that word is what * we need to _subtract_ from the input counter. */ uint64x2_t subtrahend = vcombine_u64(carry, all1); #else /* AArch32 doesn't have comparisons that operate on a 64-bit lane, * so we start by comparing each 32-bit half of the low 64 bits * _separately_ to all-1s. */ uint32x2_t all1 = vdup_n_u32(0xFFFFFFFF); uint32x2_t carry = vceq_u32( vget_high_u32(vreinterpretq_u32_u8(in)), all1); /* Swap the 32-bit words of the compare output, and AND with the * unswapped version. Now carry is all 1s iff the bottom half of * the input counter was all 1s, and all 0s otherwise. */ carry = vand_u32(carry, vrev64_u32(carry)); /* Now make the vector to subtract in the same way as above. */ uint64x2_t subtrahend = vreinterpretq_u64_u32(vcombine_u32(carry, all1)); #endif return vreinterpretq_u8_u64( vsubq_u64(vreinterpretq_u64_u8(in), subtrahend)); } /* * The SSH interface and the cipher modes. */ typedef struct aes_neon_context aes_neon_context; struct aes_neon_context { uint8x16_t keysched_e[MAXROUNDKEYS], keysched_d[MAXROUNDKEYS], iv; ssh_cipher ciph; }; static ssh_cipher *aes_hw_new(const ssh_cipheralg *alg) { if (!aes_hw_available_cached()) return NULL; aes_neon_context *ctx = snew(aes_neon_context); ctx->ciph.vt = alg; return &ctx->ciph; } static void aes_hw_free(ssh_cipher *ciph) { aes_neon_context *ctx = container_of(ciph, aes_neon_context, ciph); smemclr(ctx, sizeof(*ctx)); sfree(ctx); } static void aes_hw_setkey(ssh_cipher *ciph, const void *vkey) { aes_neon_context *ctx = container_of(ciph, aes_neon_context, ciph); const unsigned char *key = (const unsigned char *)vkey; aes_neon_key_expand(key, ctx->ciph.vt->real_keybits / 32, ctx->keysched_e, ctx->keysched_d); } static FUNC_ISA void aes_hw_setiv_cbc(ssh_cipher *ciph, const void *iv) { aes_neon_context *ctx = container_of(ciph, aes_neon_context, ciph); ctx->iv = vld1q_u8(iv); } static FUNC_ISA void aes_hw_setiv_sdctr(ssh_cipher *ciph, const void *iv) { aes_neon_context *ctx = container_of(ciph, aes_neon_context, ciph); uint8x16_t counter = vld1q_u8(iv); ctx->iv = aes_neon_sdctr_reverse(counter); } typedef uint8x16_t (*aes_neon_fn)(uint8x16_t v, const uint8x16_t *keysched); static FUNC_ISA inline void aes_cbc_neon_encrypt( ssh_cipher *ciph, void *vblk, int blklen, aes_neon_fn encrypt) { aes_neon_context *ctx = container_of(ciph, aes_neon_context, ciph); for (uint8_t *blk = (uint8_t *)vblk, *finish = blk + blklen; blk < finish; blk += 16) { uint8x16_t plaintext = vld1q_u8(blk); uint8x16_t cipher_input = veorq_u8(plaintext, ctx->iv); uint8x16_t ciphertext = encrypt(cipher_input, ctx->keysched_e); vst1q_u8(blk, ciphertext); ctx->iv = ciphertext; } } static FUNC_ISA inline void aes_cbc_neon_decrypt( ssh_cipher *ciph, void *vblk, int blklen, aes_neon_fn decrypt) { aes_neon_context *ctx = container_of(ciph, aes_neon_context, ciph); for (uint8_t *blk = (uint8_t *)vblk, *finish = blk + blklen; blk < finish; blk += 16) { uint8x16_t ciphertext = vld1q_u8(blk); uint8x16_t decrypted = decrypt(ciphertext, ctx->keysched_d); uint8x16_t plaintext = veorq_u8(decrypted, ctx->iv); vst1q_u8(blk, plaintext); ctx->iv = ciphertext; } } static FUNC_ISA inline void aes_sdctr_neon( ssh_cipher *ciph, void *vblk, int blklen, aes_neon_fn encrypt) { aes_neon_context *ctx = container_of(ciph, aes_neon_context, ciph); for (uint8_t *blk = (uint8_t *)vblk, *finish = blk + blklen; blk < finish; blk += 16) { uint8x16_t counter = aes_neon_sdctr_reverse(ctx->iv); uint8x16_t keystream = encrypt(counter, ctx->keysched_e); uint8x16_t input = vld1q_u8(blk); uint8x16_t output = veorq_u8(input, keystream); vst1q_u8(blk, output); ctx->iv = aes_neon_sdctr_increment(ctx->iv); } } #define NEON_ENC_DEC(len) \ static FUNC_ISA void aes##len##_cbc_hw_encrypt( \ ssh_cipher *ciph, void *vblk, int blklen) \ { aes_cbc_neon_encrypt(ciph, vblk, blklen, aes_neon_##len##_e); } \ static FUNC_ISA void aes##len##_cbc_hw_decrypt( \ ssh_cipher *ciph, void *vblk, int blklen) \ { aes_cbc_neon_decrypt(ciph, vblk, blklen, aes_neon_##len##_d); } \ static FUNC_ISA void aes##len##_sdctr_hw( \ ssh_cipher *ciph, void *vblk, int blklen) \ { aes_sdctr_neon(ciph, vblk, blklen, aes_neon_##len##_e); } \ NEON_ENC_DEC(128) NEON_ENC_DEC(192) NEON_ENC_DEC(256) /* ---------------------------------------------------------------------- * Stub functions if we have no hardware-accelerated AES. In this * case, aes_hw_new returns NULL (though it should also never be * selected by aes_select, so the only thing that should even be * _able_ to call it is testcrypt). As a result, the remaining vtable * functions should never be called at all. */ #elif HW_AES == HW_AES_NONE bool aes_hw_available(void) { return false; } static ssh_cipher *aes_hw_new(const ssh_cipheralg *alg) { return NULL; } #define STUB_BODY { unreachable("Should never be called"); } static void aes_hw_free(ssh_cipher *ciph) STUB_BODY static void aes_hw_setkey(ssh_cipher *ciph, const void *key) STUB_BODY static void aes_hw_setiv_cbc(ssh_cipher *ciph, const void *iv) STUB_BODY static void aes_hw_setiv_sdctr(ssh_cipher *ciph, const void *iv) STUB_BODY #define STUB_ENC_DEC(len) \ static void aes##len##_cbc_hw_encrypt( \ ssh_cipher *ciph, void *vblk, int blklen) STUB_BODY \ static void aes##len##_cbc_hw_decrypt( \ ssh_cipher *ciph, void *vblk, int blklen) STUB_BODY \ static void aes##len##_sdctr_hw( \ ssh_cipher *ciph, void *vblk, int blklen) STUB_BODY STUB_ENC_DEC(128) STUB_ENC_DEC(192) STUB_ENC_DEC(256) #endif /* HW_AES */ putty-0.76/ssharcf.c0000644000175000017500000000676614072266312011373 00000000000000/* * Arcfour (RC4) implementation for PuTTY. * * Coded from Schneier. */ #include #include "ssh.h" typedef struct { unsigned char i, j, s[256]; ssh_cipher ciph; } ArcfourContext; static void arcfour_block(void *handle, void *vblk, int len) { unsigned char *blk = (unsigned char *)vblk; ArcfourContext *ctx = (ArcfourContext *)handle; unsigned k; unsigned char tmp, i, j, *s; s = ctx->s; i = ctx->i; j = ctx->j; for (k = 0; (int)k < len; k++) { i = (i + 1) & 0xff; j = (j + s[i]) & 0xff; tmp = s[i]; s[i] = s[j]; s[j] = tmp; blk[k] ^= s[(s[i]+s[j]) & 0xff]; } ctx->i = i; ctx->j = j; } static void arcfour_setkey(ArcfourContext *ctx, unsigned char const *key, unsigned keybytes) { unsigned char tmp, k[256], *s; unsigned i, j; s = ctx->s; assert(keybytes <= 256); ctx->i = ctx->j = 0; for (i = 0; i < 256; i++) { s[i] = i; k[i] = key[i % keybytes]; } j = 0; for (i = 0; i < 256; i++) { j = (j + s[i] + k[i]) & 0xff; tmp = s[i]; s[i] = s[j]; s[j] = tmp; } } /* -- Interface with PuTTY -- */ /* * We don't implement Arcfour in SSH-1 because it's utterly insecure in * several ways. See CERT Vulnerability Notes VU#25309, VU#665372, * and VU#565052. * * We don't implement the "arcfour" algorithm in SSH-2 because it doesn't * stir the cipher state before emitting keystream, and hence is likely * to leak data about the key. */ static ssh_cipher *arcfour_new(const ssh_cipheralg *alg) { ArcfourContext *ctx = snew(ArcfourContext); ctx->ciph.vt = alg; return &ctx->ciph; } static void arcfour_free(ssh_cipher *cipher) { ArcfourContext *ctx = container_of(cipher, ArcfourContext, ciph); smemclr(ctx, sizeof(*ctx)); sfree(ctx); } 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 arcfour_ssh2_setiv(ssh_cipher *cipher, const void *key) { /* As a pure stream cipher, Arcfour has no IV separate from the key */ } static void arcfour_ssh2_setkey(ssh_cipher *cipher, const void *key) { ArcfourContext *ctx = container_of(cipher, ArcfourContext, ciph); arcfour_setkey(ctx, key, ctx->ciph.vt->padded_keybytes); arcfour_stir(ctx); } static void arcfour_ssh2_block(ssh_cipher *cipher, void *blk, int len) { ArcfourContext *ctx = container_of(cipher, ArcfourContext, ciph); arcfour_block(ctx, blk, len); } const ssh_cipheralg ssh_arcfour128_ssh2 = { .new = arcfour_new, .free = arcfour_free, .setiv = arcfour_ssh2_setiv, .setkey = arcfour_ssh2_setkey, .encrypt = arcfour_ssh2_block, .decrypt = arcfour_ssh2_block, .ssh2_id = "arcfour128", .blksize = 1, .real_keybits = 128, .padded_keybytes = 16, .flags = 0, .text_name = "Arcfour-128", }; const ssh_cipheralg ssh_arcfour256_ssh2 = { .new = arcfour_new, .free = arcfour_free, .setiv = arcfour_ssh2_setiv, .setkey = arcfour_ssh2_setkey, .encrypt = arcfour_ssh2_block, .decrypt = arcfour_ssh2_block, .ssh2_id = "arcfour256", .blksize = 1, .real_keybits = 256, .padded_keybytes = 32, .flags = 0, .text_name = "Arcfour-256", }; static const ssh_cipheralg *const arcfour_list[] = { &ssh_arcfour256_ssh2, &ssh_arcfour128_ssh2, }; const ssh2_ciphers ssh2_arcfour = { lenof(arcfour_list), arcfour_list }; putty-0.76/sshargon2.c0000644000175000017500000005634214072266312011643 00000000000000/* * Implementation of the Argon2 password hash function. * * My sources for the algorithm description and test vectors (the latter in * test/cryptsuite.py) were the reference implementation on Github, and also * the Internet-Draft description: * * https://github.com/P-H-C/phc-winner-argon2 * https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-argon2-13 */ #include #include "putty.h" #include "ssh.h" #include "marshal.h" /* ---------------------------------------------------------------------- * Argon2 uses data marshalling rules similar to SSH but with 32-bit integers * stored little-endian. Start with some local BinarySink routines for storing * a uint32 and a string in that fashion. */ static void BinarySink_put_uint32_le(BinarySink *bs, unsigned long val) { unsigned char data[4]; PUT_32BIT_LSB_FIRST(data, val); bs->write(bs, data, sizeof(data)); } static void BinarySink_put_stringpl_le(BinarySink *bs, ptrlen pl) { /* Check that the string length fits in a uint32, without doing a * potentially implementation-defined shift of more than 31 bits */ assert((pl.len >> 31) < 2); BinarySink_put_uint32_le(bs, pl.len); bs->write(bs, pl.ptr, pl.len); } #define put_uint32_le(bs, val) \ BinarySink_put_uint32_le(BinarySink_UPCAST(bs), val) #define put_stringpl_le(bs, val) \ BinarySink_put_stringpl_le(BinarySink_UPCAST(bs), val) /* ---------------------------------------------------------------------- * Argon2 defines a hash-function family that's an extension of BLAKE2b to * generate longer output digests, by repeatedly outputting half of a BLAKE2 * hash output and then re-hashing the whole thing until there are 64 or fewer * bytes left to output. The spec calls this H' (a variant of the original * hash it calls H, which is the unmodified BLAKE2b). */ static ssh_hash *hprime_new(unsigned length) { ssh_hash *h = blake2b_new_general(length > 64 ? 64 : length); put_uint32_le(h, length); return h; } static void hprime_final(ssh_hash *h, unsigned length, void *vout) { uint8_t *out = (uint8_t *)vout; while (length > 64) { uint8_t hashbuf[64]; ssh_hash_final(h, hashbuf); memcpy(out, hashbuf, 32); out += 32; length -= 32; h = blake2b_new_general(length > 64 ? 64 : length); put_data(h, hashbuf, 64); smemclr(hashbuf, sizeof(hashbuf)); } ssh_hash_final(h, out); } /* Externally visible entry point for the long hash function. This is only * used by testcrypt, so it would be overkill to set it up like a proper * ssh_hash. */ strbuf *argon2_long_hash(unsigned length, ptrlen data) { ssh_hash *h = hprime_new(length); put_datapl(h, data); strbuf *out = strbuf_new(); hprime_final(h, length, strbuf_append(out, length)); return out; } /* ---------------------------------------------------------------------- * Argon2's own mixing function G, which operates on 1Kb blocks of data. * * The definition of G in the spec takes two 1Kb blocks as input and produces * a 1Kb output block. The first thing that happens to the input blocks is * that they get XORed together, and then only the XOR output is used, so you * could perfectly well regard G as a 1Kb->1Kb function. */ static inline uint64_t ror(uint64_t x, unsigned rotation) { unsigned lshift = 63 & -rotation, rshift = 63 & rotation; return (x << lshift) | (x >> rshift); } static inline uint64_t trunc32(uint64_t x) { return x & 0xFFFFFFFF; } /* Internal function similar to the BLAKE2b round, which mixes up four 64-bit * words */ static inline void GB(uint64_t *a, uint64_t *b, uint64_t *c, uint64_t *d) { *a += *b + 2 * trunc32(*a) * trunc32(*b); *d = ror(*d ^ *a, 32); *c += *d + 2 * trunc32(*c) * trunc32(*d); *b = ror(*b ^ *c, 24); *a += *b + 2 * trunc32(*a) * trunc32(*b); *d = ror(*d ^ *a, 16); *c += *d + 2 * trunc32(*c) * trunc32(*d); *b = ror(*b ^ *c, 63); } /* Higher-level internal function which mixes up sixteen 64-bit words. This is * applied to different subsets of the 128 words in a kilobyte block, and the * API here is designed to make it easy to apply in the circumstances the spec * requires. In every call, the sixteen words form eight pairs adjacent in * memory, whose addresses are in arithmetic progression. So the 16 input * words are in[0], in[1], in[instep], in[instep+1], ..., in[7*instep], * in[7*instep+1], and the 16 output words similarly. */ static inline void P(uint64_t *out, unsigned outstep, uint64_t *in, unsigned instep) { for (unsigned i = 0; i < 8; i++) { out[i*outstep] = in[i*instep]; out[i*outstep+1] = in[i*instep+1]; } GB(out+0*outstep+0, out+2*outstep+0, out+4*outstep+0, out+6*outstep+0); GB(out+0*outstep+1, out+2*outstep+1, out+4*outstep+1, out+6*outstep+1); GB(out+1*outstep+0, out+3*outstep+0, out+5*outstep+0, out+7*outstep+0); GB(out+1*outstep+1, out+3*outstep+1, out+5*outstep+1, out+7*outstep+1); GB(out+0*outstep+0, out+2*outstep+1, out+5*outstep+0, out+7*outstep+1); GB(out+0*outstep+1, out+3*outstep+0, out+5*outstep+1, out+6*outstep+0); GB(out+1*outstep+0, out+3*outstep+1, out+4*outstep+0, out+6*outstep+1); GB(out+1*outstep+1, out+2*outstep+0, out+4*outstep+1, out+7*outstep+0); } /* The full G function, taking input blocks X and Y. The result of G is most * often XORed into an existing output block, so this API is designed with * that in mind: the mixing function's output is always XORed into whatever * 1Kb of data is already at 'out'. */ static void G_xor(uint8_t *out, const uint8_t *X, const uint8_t *Y) { uint64_t R[128], Q[128], Z[128]; for (unsigned i = 0; i < 128; i++) R[i] = GET_64BIT_LSB_FIRST(X + 8*i) ^ GET_64BIT_LSB_FIRST(Y + 8*i); for (unsigned i = 0; i < 8; i++) P(Q+16*i, 2, R+16*i, 2); for (unsigned i = 0; i < 8; i++) P(Z+2*i, 16, Q+2*i, 16); for (unsigned i = 0; i < 128; i++) PUT_64BIT_LSB_FIRST(out + 8*i, GET_64BIT_LSB_FIRST(out + 8*i) ^ R[i] ^ Z[i]); smemclr(R, sizeof(R)); smemclr(Q, sizeof(Q)); smemclr(Z, sizeof(Z)); } /* ---------------------------------------------------------------------- * The main Argon2 function. */ static void argon2_internal(uint32_t p, uint32_t T, uint32_t m, uint32_t t, uint32_t y, ptrlen P, ptrlen S, ptrlen K, ptrlen X, uint8_t *out) { /* * Start by hashing all the input data together: the four string arguments * (password P, salt S, optional secret key K, optional associated data * X), plus all the parameters for the function's memory and time usage. * * The output of this hash is the sole input to the subsequent mixing * step: Argon2 does not preserve any more entropy from the inputs, it * just makes it extra painful to get the final answer. */ uint8_t h0[64]; { ssh_hash *h = blake2b_new_general(64); put_uint32_le(h, p); put_uint32_le(h, T); put_uint32_le(h, m); put_uint32_le(h, t); put_uint32_le(h, 0x13); /* hash function version number */ put_uint32_le(h, y); put_stringpl_le(h, P); put_stringpl_le(h, S); put_stringpl_le(h, K); put_stringpl_le(h, X); ssh_hash_final(h, h0); } struct blk { uint8_t data[1024]; }; /* * Array of 1Kb blocks. The total size is (approximately) m, the * caller-specified parameter for how much memory to use; the blocks are * regarded as a rectangular array of p rows ('lanes') by q columns, where * p is the 'parallelism' input parameter (the lanes can be processed * concurrently up to a point) and q is whatever makes the product pq come * to m. * * Additionally, each row is divided into four equal 'segments', which are * important to the way the algorithm decides which blocks to use as input * to each step of the function. * * The term 'slice' refers to a whole set of vertically aligned segments, * i.e. slice 0 is the whole left quarter of the array, and slice 3 the * whole right quarter. */ size_t SL = m / (4*p); /* segment length: # of 1Kb blocks in a segment */ size_t q = 4 * SL; /* width of the array: 4 segments times SL */ size_t mprime = q * p; /* total size of the array, approximately m */ /* Allocate the memory. */ struct blk *B = snewn(mprime, struct blk); memset(B, 0, mprime * sizeof(struct blk)); /* * Initial setup: fill the first two full columns of the array with data * expanded from the starting hash h0. Each block is the result of using * the long-output hash function H' to hash h0 itself plus the block's * coordinates in the array. */ for (size_t i = 0; i < p; i++) { ssh_hash *h = hprime_new(1024); put_data(h, h0, 64); put_uint32_le(h, 0); put_uint32_le(h, i); hprime_final(h, 1024, B[i].data); } for (size_t i = 0; i < p; i++) { ssh_hash *h = hprime_new(1024); put_data(h, h0, 64); put_uint32_le(h, 1); put_uint32_le(h, i); hprime_final(h, 1024, B[i+p].data); } /* * Declarations for the main loop. * * The basic structure of the main loop is going to involve processing the * array one whole slice (vertically divided quarter) at a time. Usually * we'll write a new value into every single block in the slice, except * that in the initial slice on the first pass, we've already written * values into the first two columns during the initial setup above. So * 'jstart' indicates the starting index in each segment we process; it * starts off as 2 so that we don't overwrite the inital setup, and then * after the first slice is done, we set it to 0, and it stays there. * * d_mode indicates whether we're being data-dependent (true) or * data-independent (false). In the hybrid Argon2id mode, we start off * independent, and then once we've mixed things up enough, switch over to * dependent mode to force long serial chains of computation. */ size_t jstart = 2; bool d_mode = (y == 0); struct blk out2i, tmp2i, in2i; /* Outermost loop: t whole passes from left to right over the array */ for (size_t pass = 0; pass < t; pass++) { /* Within that, we process the array in its four main slices */ for (unsigned slice = 0; slice < 4; slice++) { /* In Argon2id mode, if we're half way through the first pass, * this is the moment to switch d_mode from false to true */ if (pass == 0 && slice == 2 && y == 2) d_mode = true; /* Loop over every segment in the slice (i.e. every row). So i is * the y-coordinate of each block we process. */ for (size_t i = 0; i < p; i++) { /* And within that segment, process the blocks from left to * right, starting at 'jstart' (usually 0, but 2 in the first * slice). */ for (size_t jpre = jstart; jpre < SL; jpre++) { /* j is the x-coordinate of each block we process, made up * of the slice number and the index 'jpre' within the * segment. */ size_t j = slice * SL + jpre; /* jm1 is j-1 (mod q) */ uint32_t jm1 = (j == 0 ? q-1 : j-1); /* * Construct two 32-bit pseudorandom integers J1 and J2. * This is the part of the algorithm that varies between * the data-dependent and independent modes. */ uint32_t J1, J2; if (d_mode) { /* * Data-dependent: grab the first 64 bits of the block * to the left of this one. */ J1 = GET_32BIT_LSB_FIRST(B[i + p * jm1].data); J2 = GET_32BIT_LSB_FIRST(B[i + p * jm1].data + 4); } else { /* * Data-independent: generate pseudorandom data by * hashing a sequence of preimage blocks that include * all our input parameters, plus the coordinates of * this point in the algorithm (array position and * pass number) to make all the hash outputs distinct. * * The hash we use is G itself, applied twice. So we * generate 1Kb of data at a time, which is enough for * 128 (J1,J2) pairs. Hence we only need to do the * hashing if our index within the segment is a * multiple of 128, or if we're at the very start of * the algorithm (in which case we started at 2 rather * than 0). After that we can just keep picking data * out of our most recent hash output. */ if (jpre == jstart || jpre % 128 == 0) { /* * Hash preimage is mostly zeroes, with a * collection of assorted integer values we had * anyway. */ memset(in2i.data, 0, sizeof(in2i.data)); PUT_64BIT_LSB_FIRST(in2i.data + 0, pass); PUT_64BIT_LSB_FIRST(in2i.data + 8, i); PUT_64BIT_LSB_FIRST(in2i.data + 16, slice); PUT_64BIT_LSB_FIRST(in2i.data + 24, mprime); PUT_64BIT_LSB_FIRST(in2i.data + 32, t); PUT_64BIT_LSB_FIRST(in2i.data + 40, y); PUT_64BIT_LSB_FIRST(in2i.data + 48, jpre / 128 + 1); /* * Now apply G twice to generate the hash output * in out2i. */ memset(tmp2i.data, 0, sizeof(tmp2i.data)); G_xor(tmp2i.data, tmp2i.data, in2i.data); memset(out2i.data, 0, sizeof(out2i.data)); G_xor(out2i.data, out2i.data, tmp2i.data); } /* * Extract J1 and J2 from the most recent hash output * (whether we've just computed it or not). */ J1 = GET_32BIT_LSB_FIRST( out2i.data + 8 * (jpre % 128)); J2 = GET_32BIT_LSB_FIRST( out2i.data + 8 * (jpre % 128) + 4); } /* * Now convert J1 and J2 into the index of an existing * block of the array to use as input to this step. This * is fairly fiddly. * * The easy part: the y-coordinate of the input block is * obtained by reducing J2 mod p, except that at the very * start of the algorithm (processing the first slice on * the first pass) we simply use the same y-coordinate as * our output block. * * Note that it's safe to use the ordinary % operator * here, without any concern for timing side channels: in * data-independent mode J2 is not correlated to any * secrets, and in data-dependent mode we're going to be * giving away side-channel data _anyway_ when we use it * as an array index (and by assumption we don't care, * because it's already massively randomised from the real * inputs). */ uint32_t index_l = (pass == 0 && slice == 0) ? i : J2 % p; /* * The hard part: which block in this array row do we use? * * First, we decide what the possible candidates are. This * requires some case analysis, and depends on whether the * array row is the same one we're writing into or not. * * If it's not the same row: we can't use any block from * the current slice (because the segments within a slice * have to be processable in parallel, so in a concurrent * implementation those blocks are potentially in the * process of being overwritten by other threads). But the * other three slices are fair game, except that in the * first pass, slices to the right of us won't have had * any values written into them yet at all. * * If it is the same row, we _are_ allowed to use blocks * from the current slice, but only the ones before our * current position. * * In both cases, we also exclude the individual _column_ * just to the left of the current one. (The block * immediately to our left is going to be the _other_ * input to G, but the spec also says that we avoid that * column even in a different row.) * * All of this means that we end up choosing from a * cyclically contiguous interval of blocks within this * lane, but the start and end points require some thought * to get them right. */ /* Start position is the beginning of the _next_ slice * (containing data from the previous pass), unless we're * on pass 0, where the start position has to be 0. */ uint32_t Wstart = (pass == 0 ? 0 : (slice + 1) % 4 * SL); /* End position splits up by cases. */ uint32_t Wend; if (index_l == i) { /* Same lane as output: we can use anything up to (but * not including) the block immediately left of us. */ Wend = jm1; } else { /* Different lane from output: we can use anything up * to the previous slice boundary, or one less than * that if we're at the very left edge of our slice * right now. */ Wend = SL * slice; if (jpre == 0) Wend = (Wend + q-1) % q; } /* Total number of blocks available to choose from */ uint32_t Wsize = (Wend + q - Wstart) % q; /* Fiddly computation from the spec that chooses from the * available blocks, in a deliberately non-uniform * fashion, using J1 as pseudorandom input data. Output is * zz which is the index within our contiguous interval. */ uint32_t x = ((uint64_t)J1 * J1) >> 32; uint32_t y = ((uint64_t)Wsize * x) >> 32; uint32_t zz = Wsize - 1 - y; /* And index_z is the actual x coordinate of the block we * want. */ uint32_t index_z = (Wstart + zz) % q; /* Phew! Combine that block with the one immediately to * our left, and XOR over the top of whatever is already * in our current output block. */ G_xor(B[i + p * j].data, B[i + p * jm1].data, B[index_l + p * index_z].data); } } /* We've finished processing a slice. Reset jstart to 0. It will * onily _not_ have been 0 if this was pass 0 slice 0, in which * case it still had its initial value of 2 to avoid the starting * data. */ jstart = 0; } } /* * The main output is all done. Final output works by taking the XOR of * all the blocks in the rightmost column of the array, and then using * that as input to our long hash H'. The output of _that_ is what we * deliver to the caller. */ struct blk C = B[p * (q-1)]; for (size_t i = 1; i < p; i++) memxor(C.data, C.data, B[i + p * (q-1)].data, 1024); { ssh_hash *h = hprime_new(T); put_data(h, C.data, 1024); hprime_final(h, T, out); } /* * Clean up. */ smemclr(out2i.data, sizeof(out2i.data)); smemclr(tmp2i.data, sizeof(tmp2i.data)); smemclr(in2i.data, sizeof(in2i.data)); smemclr(C.data, sizeof(C.data)); smemclr(B, mprime * sizeof(struct blk)); sfree(B); } /* * Wrapper function that appends to a strbuf (which sshpubk.c will want). */ void argon2(Argon2Flavour flavour, uint32_t mem, uint32_t passes, uint32_t parallel, uint32_t taglen, ptrlen P, ptrlen S, ptrlen K, ptrlen X, strbuf *out) { argon2_internal(parallel, taglen, mem, passes, flavour, P, S, K, X, strbuf_append(out, taglen)); } /* * Wrapper function which dynamically chooses the number of passes to run in * order to hit an approximate total amount of CPU time. Writes the result * into 'passes'. */ void argon2_choose_passes( Argon2Flavour flavour, uint32_t mem, uint32_t milliseconds, uint32_t *passes, uint32_t parallel, uint32_t taglen, ptrlen P, ptrlen S, ptrlen K, ptrlen X, strbuf *out) { unsigned long desired_time = (TICKSPERSEC * milliseconds) / 1000; /* * We only need the time taken to be approximately right, so we * scale up the number of passes geometrically, which avoids * taking O(t^2) time to find a pass count taking time t. * * Using the Fibonacci numbers is slightly nicer than the obvious * approach of powers of 2, because it's still very easy to * compute, and grows less fast (powers of 1.6 instead of 2), so * you get just a touch more precision. */ uint32_t a = 1, b = 1; while (true) { unsigned long start_time = GETTICKCOUNT(); argon2(flavour, mem, b, parallel, taglen, P, S, K, X, out); unsigned long ticks = GETTICKCOUNT() - start_time; /* But just in case computers get _too_ fast, we have to cap * the growth before it gets past the uint32_t upper bound! So * if computing a+b would overflow, stop here. */ if (ticks >= desired_time || a > (uint32_t)~b) { *passes = b; return; } else { strbuf_clear(out); /* Next Fibonacci number: replace (a, b) with (b, a+b) */ b += a; a = b - a; } } } putty-0.76/sshauxcrypt.c0000644000175000017500000001107314072266312012322 00000000000000/* * sshauxcrypt.c: wrapper functions on crypto primitives for use in * other contexts than the main SSH packet protocol, such as * encrypting private key files and performing XDM-AUTHORIZATION-1. * * These all work through the standard cipher/hash/MAC APIs, so they * don't need to live in the same actual source files as the ciphers * they wrap, and I think it keeps things tidier to have them out of * the way here instead. */ #include "ssh.h" static ssh_cipher *aes256_pubkey_cipher(const void *key, const void *iv) { /* * PuTTY's own .PPK format for SSH-2 private key files is * encrypted with 256-bit AES in CBC mode. */ ssh_cipher *cipher = ssh_cipher_new(&ssh_aes256_cbc); ssh_cipher_setkey(cipher, key); ssh_cipher_setiv(cipher, iv); return cipher; } void aes256_encrypt_pubkey(const void *key, const void *iv, void *blk, int len) { ssh_cipher *c = aes256_pubkey_cipher(key, iv); ssh_cipher_encrypt(c, blk, len); ssh_cipher_free(c); } void aes256_decrypt_pubkey(const void *key, const void *iv, void *blk, int len) { ssh_cipher *c = aes256_pubkey_cipher(key, iv); ssh_cipher_decrypt(c, blk, len); ssh_cipher_free(c); } static ssh_cipher *des3_pubkey_cipher(const void *vkey) { /* * SSH-1 private key files are encrypted with triple-DES in SSH-1 * style (three separate CBC layers), but the same key is used for * the first and third layers. */ ssh_cipher *c = ssh_cipher_new(&ssh_3des_ssh1); uint8_t keys3[24], iv[8]; memcpy(keys3, vkey, 16); memcpy(keys3 + 16, vkey, 8); ssh_cipher_setkey(c, keys3); smemclr(keys3, sizeof(keys3)); memset(iv, 0, 8); ssh_cipher_setiv(c, iv); return c; } void des3_decrypt_pubkey(const void *vkey, void *vblk, int len) { ssh_cipher *c = des3_pubkey_cipher(vkey); ssh_cipher_decrypt(c, vblk, len); ssh_cipher_free(c); } void des3_encrypt_pubkey(const void *vkey, void *vblk, int len) { ssh_cipher *c = des3_pubkey_cipher(vkey); ssh_cipher_encrypt(c, vblk, len); ssh_cipher_free(c); } static ssh_cipher *des3_pubkey_ossh_cipher(const void *vkey, const void *viv) { /* * OpenSSH PEM private key files are encrypted with triple-DES in * SSH-2 style (one CBC layer), with three distinct keys, and an * IV also generated from the passphrase. */ ssh_cipher *c = ssh_cipher_new(&ssh_3des_ssh2); ssh_cipher_setkey(c, vkey); ssh_cipher_setiv(c, viv); return c; } void des3_decrypt_pubkey_ossh(const void *vkey, const void *viv, void *vblk, int len) { ssh_cipher *c = des3_pubkey_ossh_cipher(vkey, viv); ssh_cipher_decrypt(c, vblk, len); ssh_cipher_free(c); } void des3_encrypt_pubkey_ossh(const void *vkey, const void *viv, void *vblk, int len) { ssh_cipher *c = des3_pubkey_ossh_cipher(vkey, viv); ssh_cipher_encrypt(c, vblk, len); ssh_cipher_free(c); } static ssh_cipher *des_xdmauth_cipher(const void *vkeydata) { /* * XDM-AUTHORIZATION-1 uses single-DES, but packs the key into 7 * bytes, so here we have to repack it manually into the canonical * form where it occupies 8 bytes each with the low bit unused. */ const unsigned char *keydata = (const unsigned char *)vkeydata; unsigned char key[8]; int i, nbits, j; unsigned int bits; bits = 0; nbits = 0; j = 0; for (i = 0; i < 8; i++) { if (nbits < 7) { bits = (bits << 8) | keydata[j]; nbits += 8; j++; } key[i] = (bits >> (nbits - 7)) << 1; bits &= ~(0x7F << (nbits - 7)); nbits -= 7; } ssh_cipher *c = ssh_cipher_new(&ssh_des); ssh_cipher_setkey(c, key); smemclr(key, sizeof(key)); ssh_cipher_setiv(c, key); return c; } void des_encrypt_xdmauth(const void *keydata, void *blk, int len) { ssh_cipher *c = des_xdmauth_cipher(keydata); ssh_cipher_encrypt(c, blk, len); ssh_cipher_free(c); } void des_decrypt_xdmauth(const void *keydata, void *blk, int len) { ssh_cipher *c = des_xdmauth_cipher(keydata); ssh_cipher_decrypt(c, blk, len); ssh_cipher_free(c); } void hash_simple(const ssh_hashalg *alg, ptrlen data, void *output) { ssh_hash *hash = ssh_hash_new(alg); put_datapl(hash, data); ssh_hash_final(hash, output); } void mac_simple(const ssh2_macalg *alg, ptrlen key, ptrlen data, void *output) { ssh2_mac *mac = ssh2_mac_new(alg, NULL); ssh2_mac_setkey(mac, key); ssh2_mac_start(mac); put_datapl(mac, data); ssh2_mac_genresult(mac, output); ssh2_mac_free(mac); } putty-0.76/sshbcrypt.c0000644000175000017500000001006014072266312011741 00000000000000/* * 'bcrypt' password hash function, for PuTTY's import/export of * OpenSSH encrypted private key files. * * This is not really the same as the original bcrypt; OpenSSH has * modified it in various ways, and of course we have to do the same. */ #include #include #include "ssh.h" #include "sshblowf.h" BlowfishContext *bcrypt_setup(const unsigned char *key, int keybytes, const unsigned char *salt, int saltbytes) { int i; BlowfishContext *ctx; ctx = blowfish_make_context(); blowfish_initkey(ctx); blowfish_expandkey(ctx, key, keybytes, salt, saltbytes); /* Original bcrypt replaces this fixed loop count with the * variable cost. OpenSSH instead iterates the whole thing more * than once if it wants extra rounds. */ for (i = 0; i < 64; i++) { blowfish_expandkey(ctx, salt, saltbytes, NULL, 0); blowfish_expandkey(ctx, key, keybytes, NULL, 0); } return ctx; } void bcrypt_hash(const unsigned char *key, int keybytes, const unsigned char *salt, int saltbytes, unsigned char output[32]) { BlowfishContext *ctx; int i; ctx = bcrypt_setup(key, keybytes, salt, saltbytes); /* This was quite a nice starting string until it ran into * little-endian Blowfish :-/ */ memcpy(output, "cyxOmorhcitawolBhsiftawSanyDetim", 32); for (i = 0; i < 64; i++) { blowfish_lsb_encrypt_ecb(output, 32, ctx); } blowfish_free_context(ctx); } void bcrypt_genblock(int counter, const unsigned char hashed_passphrase[64], const unsigned char *salt, int saltbytes, unsigned char output[32]) { unsigned char hashed_salt[64]; /* Hash the input salt with the counter value optionally suffixed * to get our real 32-byte salt */ ssh_hash *h = ssh_hash_new(&ssh_sha512); put_data(h, salt, saltbytes); if (counter) put_uint32(h, counter); ssh_hash_final(h, hashed_salt); bcrypt_hash(hashed_passphrase, 64, hashed_salt, 64, output); smemclr(&hashed_salt, sizeof(hashed_salt)); } void openssh_bcrypt(const char *passphrase, const unsigned char *salt, int saltbytes, int rounds, unsigned char *out, int outbytes) { unsigned char hashed_passphrase[64]; unsigned char block[32], outblock[32]; const unsigned char *thissalt; int thissaltbytes; int modulus, residue, i, j, round; /* Hash the passphrase to get the bcrypt key material */ hash_simple(&ssh_sha512, ptrlen_from_asciz(passphrase), hashed_passphrase); /* We output key bytes in a scattered fashion to meld all output * key blocks into all parts of the output. To do this, we pick a * modulus, and we output the key bytes to indices of out[] in the * following order: first the indices that are multiples of the * modulus, then the ones congruent to 1 mod modulus, etc. Each of * those passes consumes exactly one block output from * bcrypt_genblock, so we must pick a modulus large enough that at * most 32 bytes are used in the pass. */ modulus = (outbytes + 31) / 32; for (residue = 0; residue < modulus; residue++) { /* Our output block of data is the XOR of all blocks generated * by bcrypt in the following loop */ memset(outblock, 0, sizeof(outblock)); thissalt = salt; thissaltbytes = saltbytes; for (round = 0; round < rounds; round++) { bcrypt_genblock(round == 0 ? residue+1 : 0, hashed_passphrase, thissalt, thissaltbytes, block); /* Each subsequent bcrypt call reuses the previous one's * output as its salt */ thissalt = block; thissaltbytes = 32; for (i = 0; i < 32; i++) outblock[i] ^= block[i]; } for (i = residue, j = 0; i < outbytes; i += modulus, j++) out[i] = outblock[j]; } smemclr(&hashed_passphrase, sizeof(hashed_passphrase)); } putty-0.76/sshblake2.c0000644000175000017500000001534014072266312011604 00000000000000/* * BLAKE2 (RFC 7693) implementation for PuTTY. * * The BLAKE2 hash family includes BLAKE2s, in which the hash state is * operated on as a collection of 32-bit integers, and BLAKE2b, based * on 64-bit integers. At present this code implements BLAKE2b only. */ #include #include "ssh.h" static inline uint64_t ror(uint64_t x, unsigned rotation) { unsigned lshift = 63 & -rotation, rshift = 63 & rotation; return (x << lshift) | (x >> rshift); } /* RFC 7963 section 2.1 */ enum { R1 = 32, R2 = 24, R3 = 16, R4 = 63 }; /* RFC 7693 section 2.6 */ static const uint64_t iv[] = { 0x6a09e667f3bcc908, /* floor(2^64 * frac(sqrt(2))) */ 0xbb67ae8584caa73b, /* floor(2^64 * frac(sqrt(3))) */ 0x3c6ef372fe94f82b, /* floor(2^64 * frac(sqrt(5))) */ 0xa54ff53a5f1d36f1, /* floor(2^64 * frac(sqrt(7))) */ 0x510e527fade682d1, /* floor(2^64 * frac(sqrt(11))) */ 0x9b05688c2b3e6c1f, /* floor(2^64 * frac(sqrt(13))) */ 0x1f83d9abfb41bd6b, /* floor(2^64 * frac(sqrt(17))) */ 0x5be0cd19137e2179, /* floor(2^64 * frac(sqrt(19))) */ }; /* RFC 7693 section 2.7 */ static const unsigned char sigma[][16] = { { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, {14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3}, {11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4}, { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8}, { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13}, { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9}, {12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11}, {13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10}, { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5}, {10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0}, /* This array recycles if you have more than 10 rounds. BLAKE2b * has 12, so we repeat the first two rows again. */ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, {14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3}, }; static inline void g_half(uint64_t v[16], unsigned a, unsigned b, unsigned c, unsigned d, uint64_t x, unsigned r1, unsigned r2) { v[a] += v[b] + x; v[d] ^= v[a]; v[d] = ror(v[d], r1); v[c] += v[d]; v[b] ^= v[c]; v[b] = ror(v[b], r2); } static inline void g(uint64_t v[16], unsigned a, unsigned b, unsigned c, unsigned d, uint64_t x, uint64_t y) { g_half(v, a, b, c, d, x, R1, R2); g_half(v, a, b, c, d, y, R3, R4); } static inline void f(uint64_t h[8], uint64_t m[16], uint64_t offset_hi, uint64_t offset_lo, unsigned final) { uint64_t v[16]; memcpy(v, h, 8 * sizeof(*v)); memcpy(v + 8, iv, 8 * sizeof(*v)); v[12] ^= offset_lo; v[13] ^= offset_hi; v[14] ^= -(uint64_t)final; for (unsigned round = 0; round < 12; round++) { const unsigned char *s = sigma[round]; g(v, 0, 4, 8, 12, m[s[ 0]], m[s[ 1]]); g(v, 1, 5, 9, 13, m[s[ 2]], m[s[ 3]]); g(v, 2, 6, 10, 14, m[s[ 4]], m[s[ 5]]); g(v, 3, 7, 11, 15, m[s[ 6]], m[s[ 7]]); g(v, 0, 5, 10, 15, m[s[ 8]], m[s[ 9]]); g(v, 1, 6, 11, 12, m[s[10]], m[s[11]]); g(v, 2, 7, 8, 13, m[s[12]], m[s[13]]); g(v, 3, 4, 9, 14, m[s[14]], m[s[15]]); } for (unsigned i = 0; i < 8; i++) h[i] ^= v[i] ^ v[i+8]; smemclr(v, sizeof(v)); } static inline void f_outer(uint64_t h[8], uint8_t blk[128], uint64_t offset_hi, uint64_t offset_lo, unsigned final) { uint64_t m[16]; for (unsigned i = 0; i < 16; i++) m[i] = GET_64BIT_LSB_FIRST(blk + 8*i); f(h, m, offset_hi, offset_lo, final); smemclr(m, sizeof(m)); } typedef struct blake2b { uint64_t h[8]; unsigned hashlen; uint8_t block[128]; size_t used; uint64_t lenhi, lenlo; BinarySink_IMPLEMENTATION; ssh_hash hash; } blake2b; static void blake2b_write(BinarySink *bs, const void *vp, size_t len); static ssh_hash *blake2b_new_inner(unsigned hashlen) { assert(hashlen <= ssh_blake2b.hlen); blake2b *s = snew(blake2b); s->hash.vt = &ssh_blake2b; s->hashlen = hashlen; BinarySink_INIT(s, blake2b_write); BinarySink_DELEGATE_INIT(&s->hash, s); return &s->hash; } static ssh_hash *blake2b_new(const ssh_hashalg *alg) { return blake2b_new_inner(alg->hlen); } ssh_hash *blake2b_new_general(unsigned hashlen) { ssh_hash *h = blake2b_new_inner(hashlen); ssh_hash_reset(h); return h; } static void blake2b_reset(ssh_hash *hash) { blake2b *s = container_of(hash, blake2b, hash); /* Initialise the hash to the standard IV */ memcpy(s->h, iv, sizeof(s->h)); /* XOR in the parameters: secret key length (here always 0) in * byte 1, and hash length in byte 0. */ s->h[0] ^= 0x01010000 ^ s->hashlen; s->used = 0; s->lenhi = s->lenlo = 0; } static void blake2b_copyfrom(ssh_hash *hcopy, ssh_hash *horig) { blake2b *copy = container_of(hcopy, blake2b, hash); blake2b *orig = container_of(horig, blake2b, hash); memcpy(copy, orig, sizeof(*copy)); BinarySink_COPIED(copy); BinarySink_DELEGATE_INIT(©->hash, copy); } static void blake2b_free(ssh_hash *hash) { blake2b *s = container_of(hash, blake2b, hash); smemclr(s, sizeof(*s)); sfree(s); } static void blake2b_write(BinarySink *bs, const void *vp, size_t len) { blake2b *s = BinarySink_DOWNCAST(bs, blake2b); const uint8_t *p = vp; while (len > 0) { if (s->used == sizeof(s->block)) { f_outer(s->h, s->block, s->lenhi, s->lenlo, 0); s->used = 0; } size_t chunk = sizeof(s->block) - s->used; if (chunk > len) chunk = len; memcpy(s->block + s->used, p, chunk); s->used += chunk; p += chunk; len -= chunk; s->lenlo += chunk; s->lenhi += (s->lenlo < chunk); } } static void blake2b_digest(ssh_hash *hash, uint8_t *digest) { blake2b *s = container_of(hash, blake2b, hash); memset(s->block + s->used, 0, sizeof(s->block) - s->used); f_outer(s->h, s->block, s->lenhi, s->lenlo, 1); uint8_t hash_pre[128]; for (unsigned i = 0; i < 8; i++) PUT_64BIT_LSB_FIRST(hash_pre + 8*i, s->h[i]); memcpy(digest, hash_pre, s->hashlen); smemclr(hash_pre, sizeof(hash_pre)); } const ssh_hashalg ssh_blake2b = { .new = blake2b_new, .reset = blake2b_reset, .copyfrom = blake2b_copyfrom, .digest = blake2b_digest, .free = blake2b_free, .hlen = 64, .blocklen = 128, HASHALG_NAMES_BARE("BLAKE2b-64"), }; putty-0.76/sshblowf.c0000644000175000017500000006410014072266312011553 00000000000000/* * Blowfish implementation for PuTTY. * * Coded from scratch from the algorithm description. */ #include #include #include "ssh.h" #include "sshblowf.h" struct BlowfishContext { uint32_t S0[256], S1[256], S2[256], S3[256], P[18]; uint32_t iv0, iv1; /* for CBC mode */ }; /* * The Blowfish init data: hex digits of the fractional part of pi. * (ie pi as a hex fraction is 3.243F6A8885A308D3...) * * If you have Simon Tatham's 'spigot' exact real calculator * available, or any other method of generating 8336 fractional hex * digits of pi on standard output, you can regenerate these tables * exactly as below using the following Perl script (adjusting the * first line or two if your pi-generator is not spigot). open my $spig, "spigot -n -B16 -d8336 pi |"; read $spig, $ignore, 2; # throw away the leading "3." for my $name ("parray", "sbox0".."sbox3") { print "static const uint32_t ${name}[] = {\n"; my $len = $name eq "parray" ? 18 : 256; for my $i (1..$len) { read $spig, $word, 8; printf "%s0x%s,", ($i%6==1 ? " " : " "), uc $word; print "\n" if ($i == $len || $i%6 == 0); } print "};\n\n"; } close $spig; */ static const uint32_t parray[] = { 0x243F6A88, 0x85A308D3, 0x13198A2E, 0x03707344, 0xA4093822, 0x299F31D0, 0x082EFA98, 0xEC4E6C89, 0x452821E6, 0x38D01377, 0xBE5466CF, 0x34E90C6C, 0xC0AC29B7, 0xC97C50DD, 0x3F84D5B5, 0xB5470917, 0x9216D5D9, 0x8979FB1B, }; static const uint32_t sbox0[] = { 0xD1310BA6, 0x98DFB5AC, 0x2FFD72DB, 0xD01ADFB7, 0xB8E1AFED, 0x6A267E96, 0xBA7C9045, 0xF12C7F99, 0x24A19947, 0xB3916CF7, 0x0801F2E2, 0x858EFC16, 0x636920D8, 0x71574E69, 0xA458FEA3, 0xF4933D7E, 0x0D95748F, 0x728EB658, 0x718BCD58, 0x82154AEE, 0x7B54A41D, 0xC25A59B5, 0x9C30D539, 0x2AF26013, 0xC5D1B023, 0x286085F0, 0xCA417918, 0xB8DB38EF, 0x8E79DCB0, 0x603A180E, 0x6C9E0E8B, 0xB01E8A3E, 0xD71577C1, 0xBD314B27, 0x78AF2FDA, 0x55605C60, 0xE65525F3, 0xAA55AB94, 0x57489862, 0x63E81440, 0x55CA396A, 0x2AAB10B6, 0xB4CC5C34, 0x1141E8CE, 0xA15486AF, 0x7C72E993, 0xB3EE1411, 0x636FBC2A, 0x2BA9C55D, 0x741831F6, 0xCE5C3E16, 0x9B87931E, 0xAFD6BA33, 0x6C24CF5C, 0x7A325381, 0x28958677, 0x3B8F4898, 0x6B4BB9AF, 0xC4BFE81B, 0x66282193, 0x61D809CC, 0xFB21A991, 0x487CAC60, 0x5DEC8032, 0xEF845D5D, 0xE98575B1, 0xDC262302, 0xEB651B88, 0x23893E81, 0xD396ACC5, 0x0F6D6FF3, 0x83F44239, 0x2E0B4482, 0xA4842004, 0x69C8F04A, 0x9E1F9B5E, 0x21C66842, 0xF6E96C9A, 0x670C9C61, 0xABD388F0, 0x6A51A0D2, 0xD8542F68, 0x960FA728, 0xAB5133A3, 0x6EEF0B6C, 0x137A3BE4, 0xBA3BF050, 0x7EFB2A98, 0xA1F1651D, 0x39AF0176, 0x66CA593E, 0x82430E88, 0x8CEE8619, 0x456F9FB4, 0x7D84A5C3, 0x3B8B5EBE, 0xE06F75D8, 0x85C12073, 0x401A449F, 0x56C16AA6, 0x4ED3AA62, 0x363F7706, 0x1BFEDF72, 0x429B023D, 0x37D0D724, 0xD00A1248, 0xDB0FEAD3, 0x49F1C09B, 0x075372C9, 0x80991B7B, 0x25D479D8, 0xF6E8DEF7, 0xE3FE501A, 0xB6794C3B, 0x976CE0BD, 0x04C006BA, 0xC1A94FB6, 0x409F60C4, 0x5E5C9EC2, 0x196A2463, 0x68FB6FAF, 0x3E6C53B5, 0x1339B2EB, 0x3B52EC6F, 0x6DFC511F, 0x9B30952C, 0xCC814544, 0xAF5EBD09, 0xBEE3D004, 0xDE334AFD, 0x660F2807, 0x192E4BB3, 0xC0CBA857, 0x45C8740F, 0xD20B5F39, 0xB9D3FBDB, 0x5579C0BD, 0x1A60320A, 0xD6A100C6, 0x402C7279, 0x679F25FE, 0xFB1FA3CC, 0x8EA5E9F8, 0xDB3222F8, 0x3C7516DF, 0xFD616B15, 0x2F501EC8, 0xAD0552AB, 0x323DB5FA, 0xFD238760, 0x53317B48, 0x3E00DF82, 0x9E5C57BB, 0xCA6F8CA0, 0x1A87562E, 0xDF1769DB, 0xD542A8F6, 0x287EFFC3, 0xAC6732C6, 0x8C4F5573, 0x695B27B0, 0xBBCA58C8, 0xE1FFA35D, 0xB8F011A0, 0x10FA3D98, 0xFD2183B8, 0x4AFCB56C, 0x2DD1D35B, 0x9A53E479, 0xB6F84565, 0xD28E49BC, 0x4BFB9790, 0xE1DDF2DA, 0xA4CB7E33, 0x62FB1341, 0xCEE4C6E8, 0xEF20CADA, 0x36774C01, 0xD07E9EFE, 0x2BF11FB4, 0x95DBDA4D, 0xAE909198, 0xEAAD8E71, 0x6B93D5A0, 0xD08ED1D0, 0xAFC725E0, 0x8E3C5B2F, 0x8E7594B7, 0x8FF6E2FB, 0xF2122B64, 0x8888B812, 0x900DF01C, 0x4FAD5EA0, 0x688FC31C, 0xD1CFF191, 0xB3A8C1AD, 0x2F2F2218, 0xBE0E1777, 0xEA752DFE, 0x8B021FA1, 0xE5A0CC0F, 0xB56F74E8, 0x18ACF3D6, 0xCE89E299, 0xB4A84FE0, 0xFD13E0B7, 0x7CC43B81, 0xD2ADA8D9, 0x165FA266, 0x80957705, 0x93CC7314, 0x211A1477, 0xE6AD2065, 0x77B5FA86, 0xC75442F5, 0xFB9D35CF, 0xEBCDAF0C, 0x7B3E89A0, 0xD6411BD3, 0xAE1E7E49, 0x00250E2D, 0x2071B35E, 0x226800BB, 0x57B8E0AF, 0x2464369B, 0xF009B91E, 0x5563911D, 0x59DFA6AA, 0x78C14389, 0xD95A537F, 0x207D5BA2, 0x02E5B9C5, 0x83260376, 0x6295CFA9, 0x11C81968, 0x4E734A41, 0xB3472DCA, 0x7B14A94A, 0x1B510052, 0x9A532915, 0xD60F573F, 0xBC9BC6E4, 0x2B60A476, 0x81E67400, 0x08BA6FB5, 0x571BE91F, 0xF296EC6B, 0x2A0DD915, 0xB6636521, 0xE7B9F9B6, 0xFF34052E, 0xC5855664, 0x53B02D5D, 0xA99F8FA1, 0x08BA4799, 0x6E85076A, }; static const uint32_t sbox1[] = { 0x4B7A70E9, 0xB5B32944, 0xDB75092E, 0xC4192623, 0xAD6EA6B0, 0x49A7DF7D, 0x9CEE60B8, 0x8FEDB266, 0xECAA8C71, 0x699A17FF, 0x5664526C, 0xC2B19EE1, 0x193602A5, 0x75094C29, 0xA0591340, 0xE4183A3E, 0x3F54989A, 0x5B429D65, 0x6B8FE4D6, 0x99F73FD6, 0xA1D29C07, 0xEFE830F5, 0x4D2D38E6, 0xF0255DC1, 0x4CDD2086, 0x8470EB26, 0x6382E9C6, 0x021ECC5E, 0x09686B3F, 0x3EBAEFC9, 0x3C971814, 0x6B6A70A1, 0x687F3584, 0x52A0E286, 0xB79C5305, 0xAA500737, 0x3E07841C, 0x7FDEAE5C, 0x8E7D44EC, 0x5716F2B8, 0xB03ADA37, 0xF0500C0D, 0xF01C1F04, 0x0200B3FF, 0xAE0CF51A, 0x3CB574B2, 0x25837A58, 0xDC0921BD, 0xD19113F9, 0x7CA92FF6, 0x94324773, 0x22F54701, 0x3AE5E581, 0x37C2DADC, 0xC8B57634, 0x9AF3DDA7, 0xA9446146, 0x0FD0030E, 0xECC8C73E, 0xA4751E41, 0xE238CD99, 0x3BEA0E2F, 0x3280BBA1, 0x183EB331, 0x4E548B38, 0x4F6DB908, 0x6F420D03, 0xF60A04BF, 0x2CB81290, 0x24977C79, 0x5679B072, 0xBCAF89AF, 0xDE9A771F, 0xD9930810, 0xB38BAE12, 0xDCCF3F2E, 0x5512721F, 0x2E6B7124, 0x501ADDE6, 0x9F84CD87, 0x7A584718, 0x7408DA17, 0xBC9F9ABC, 0xE94B7D8C, 0xEC7AEC3A, 0xDB851DFA, 0x63094366, 0xC464C3D2, 0xEF1C1847, 0x3215D908, 0xDD433B37, 0x24C2BA16, 0x12A14D43, 0x2A65C451, 0x50940002, 0x133AE4DD, 0x71DFF89E, 0x10314E55, 0x81AC77D6, 0x5F11199B, 0x043556F1, 0xD7A3C76B, 0x3C11183B, 0x5924A509, 0xF28FE6ED, 0x97F1FBFA, 0x9EBABF2C, 0x1E153C6E, 0x86E34570, 0xEAE96FB1, 0x860E5E0A, 0x5A3E2AB3, 0x771FE71C, 0x4E3D06FA, 0x2965DCB9, 0x99E71D0F, 0x803E89D6, 0x5266C825, 0x2E4CC978, 0x9C10B36A, 0xC6150EBA, 0x94E2EA78, 0xA5FC3C53, 0x1E0A2DF4, 0xF2F74EA7, 0x361D2B3D, 0x1939260F, 0x19C27960, 0x5223A708, 0xF71312B6, 0xEBADFE6E, 0xEAC31F66, 0xE3BC4595, 0xA67BC883, 0xB17F37D1, 0x018CFF28, 0xC332DDEF, 0xBE6C5AA5, 0x65582185, 0x68AB9802, 0xEECEA50F, 0xDB2F953B, 0x2AEF7DAD, 0x5B6E2F84, 0x1521B628, 0x29076170, 0xECDD4775, 0x619F1510, 0x13CCA830, 0xEB61BD96, 0x0334FE1E, 0xAA0363CF, 0xB5735C90, 0x4C70A239, 0xD59E9E0B, 0xCBAADE14, 0xEECC86BC, 0x60622CA7, 0x9CAB5CAB, 0xB2F3846E, 0x648B1EAF, 0x19BDF0CA, 0xA02369B9, 0x655ABB50, 0x40685A32, 0x3C2AB4B3, 0x319EE9D5, 0xC021B8F7, 0x9B540B19, 0x875FA099, 0x95F7997E, 0x623D7DA8, 0xF837889A, 0x97E32D77, 0x11ED935F, 0x16681281, 0x0E358829, 0xC7E61FD6, 0x96DEDFA1, 0x7858BA99, 0x57F584A5, 0x1B227263, 0x9B83C3FF, 0x1AC24696, 0xCDB30AEB, 0x532E3054, 0x8FD948E4, 0x6DBC3128, 0x58EBF2EF, 0x34C6FFEA, 0xFE28ED61, 0xEE7C3C73, 0x5D4A14D9, 0xE864B7E3, 0x42105D14, 0x203E13E0, 0x45EEE2B6, 0xA3AAABEA, 0xDB6C4F15, 0xFACB4FD0, 0xC742F442, 0xEF6ABBB5, 0x654F3B1D, 0x41CD2105, 0xD81E799E, 0x86854DC7, 0xE44B476A, 0x3D816250, 0xCF62A1F2, 0x5B8D2646, 0xFC8883A0, 0xC1C7B6A3, 0x7F1524C3, 0x69CB7492, 0x47848A0B, 0x5692B285, 0x095BBF00, 0xAD19489D, 0x1462B174, 0x23820E00, 0x58428D2A, 0x0C55F5EA, 0x1DADF43E, 0x233F7061, 0x3372F092, 0x8D937E41, 0xD65FECF1, 0x6C223BDB, 0x7CDE3759, 0xCBEE7460, 0x4085F2A7, 0xCE77326E, 0xA6078084, 0x19F8509E, 0xE8EFD855, 0x61D99735, 0xA969A7AA, 0xC50C06C2, 0x5A04ABFC, 0x800BCADC, 0x9E447A2E, 0xC3453484, 0xFDD56705, 0x0E1E9EC9, 0xDB73DBD3, 0x105588CD, 0x675FDA79, 0xE3674340, 0xC5C43465, 0x713E38D8, 0x3D28F89E, 0xF16DFF20, 0x153E21E7, 0x8FB03D4A, 0xE6E39F2B, 0xDB83ADF7, }; static const uint32_t sbox2[] = { 0xE93D5A68, 0x948140F7, 0xF64C261C, 0x94692934, 0x411520F7, 0x7602D4F7, 0xBCF46B2E, 0xD4A20068, 0xD4082471, 0x3320F46A, 0x43B7D4B7, 0x500061AF, 0x1E39F62E, 0x97244546, 0x14214F74, 0xBF8B8840, 0x4D95FC1D, 0x96B591AF, 0x70F4DDD3, 0x66A02F45, 0xBFBC09EC, 0x03BD9785, 0x7FAC6DD0, 0x31CB8504, 0x96EB27B3, 0x55FD3941, 0xDA2547E6, 0xABCA0A9A, 0x28507825, 0x530429F4, 0x0A2C86DA, 0xE9B66DFB, 0x68DC1462, 0xD7486900, 0x680EC0A4, 0x27A18DEE, 0x4F3FFEA2, 0xE887AD8C, 0xB58CE006, 0x7AF4D6B6, 0xAACE1E7C, 0xD3375FEC, 0xCE78A399, 0x406B2A42, 0x20FE9E35, 0xD9F385B9, 0xEE39D7AB, 0x3B124E8B, 0x1DC9FAF7, 0x4B6D1856, 0x26A36631, 0xEAE397B2, 0x3A6EFA74, 0xDD5B4332, 0x6841E7F7, 0xCA7820FB, 0xFB0AF54E, 0xD8FEB397, 0x454056AC, 0xBA489527, 0x55533A3A, 0x20838D87, 0xFE6BA9B7, 0xD096954B, 0x55A867BC, 0xA1159A58, 0xCCA92963, 0x99E1DB33, 0xA62A4A56, 0x3F3125F9, 0x5EF47E1C, 0x9029317C, 0xFDF8E802, 0x04272F70, 0x80BB155C, 0x05282CE3, 0x95C11548, 0xE4C66D22, 0x48C1133F, 0xC70F86DC, 0x07F9C9EE, 0x41041F0F, 0x404779A4, 0x5D886E17, 0x325F51EB, 0xD59BC0D1, 0xF2BCC18F, 0x41113564, 0x257B7834, 0x602A9C60, 0xDFF8E8A3, 0x1F636C1B, 0x0E12B4C2, 0x02E1329E, 0xAF664FD1, 0xCAD18115, 0x6B2395E0, 0x333E92E1, 0x3B240B62, 0xEEBEB922, 0x85B2A20E, 0xE6BA0D99, 0xDE720C8C, 0x2DA2F728, 0xD0127845, 0x95B794FD, 0x647D0862, 0xE7CCF5F0, 0x5449A36F, 0x877D48FA, 0xC39DFD27, 0xF33E8D1E, 0x0A476341, 0x992EFF74, 0x3A6F6EAB, 0xF4F8FD37, 0xA812DC60, 0xA1EBDDF8, 0x991BE14C, 0xDB6E6B0D, 0xC67B5510, 0x6D672C37, 0x2765D43B, 0xDCD0E804, 0xF1290DC7, 0xCC00FFA3, 0xB5390F92, 0x690FED0B, 0x667B9FFB, 0xCEDB7D9C, 0xA091CF0B, 0xD9155EA3, 0xBB132F88, 0x515BAD24, 0x7B9479BF, 0x763BD6EB, 0x37392EB3, 0xCC115979, 0x8026E297, 0xF42E312D, 0x6842ADA7, 0xC66A2B3B, 0x12754CCC, 0x782EF11C, 0x6A124237, 0xB79251E7, 0x06A1BBE6, 0x4BFB6350, 0x1A6B1018, 0x11CAEDFA, 0x3D25BDD8, 0xE2E1C3C9, 0x44421659, 0x0A121386, 0xD90CEC6E, 0xD5ABEA2A, 0x64AF674E, 0xDA86A85F, 0xBEBFE988, 0x64E4C3FE, 0x9DBC8057, 0xF0F7C086, 0x60787BF8, 0x6003604D, 0xD1FD8346, 0xF6381FB0, 0x7745AE04, 0xD736FCCC, 0x83426B33, 0xF01EAB71, 0xB0804187, 0x3C005E5F, 0x77A057BE, 0xBDE8AE24, 0x55464299, 0xBF582E61, 0x4E58F48F, 0xF2DDFDA2, 0xF474EF38, 0x8789BDC2, 0x5366F9C3, 0xC8B38E74, 0xB475F255, 0x46FCD9B9, 0x7AEB2661, 0x8B1DDF84, 0x846A0E79, 0x915F95E2, 0x466E598E, 0x20B45770, 0x8CD55591, 0xC902DE4C, 0xB90BACE1, 0xBB8205D0, 0x11A86248, 0x7574A99E, 0xB77F19B6, 0xE0A9DC09, 0x662D09A1, 0xC4324633, 0xE85A1F02, 0x09F0BE8C, 0x4A99A025, 0x1D6EFE10, 0x1AB93D1D, 0x0BA5A4DF, 0xA186F20F, 0x2868F169, 0xDCB7DA83, 0x573906FE, 0xA1E2CE9B, 0x4FCD7F52, 0x50115E01, 0xA70683FA, 0xA002B5C4, 0x0DE6D027, 0x9AF88C27, 0x773F8641, 0xC3604C06, 0x61A806B5, 0xF0177A28, 0xC0F586E0, 0x006058AA, 0x30DC7D62, 0x11E69ED7, 0x2338EA63, 0x53C2DD94, 0xC2C21634, 0xBBCBEE56, 0x90BCB6DE, 0xEBFC7DA1, 0xCE591D76, 0x6F05E409, 0x4B7C0188, 0x39720A3D, 0x7C927C24, 0x86E3725F, 0x724D9DB9, 0x1AC15BB4, 0xD39EB8FC, 0xED545578, 0x08FCA5B5, 0xD83D7CD3, 0x4DAD0FC4, 0x1E50EF5E, 0xB161E6F8, 0xA28514D9, 0x6C51133C, 0x6FD5C7E7, 0x56E14EC4, 0x362ABFCE, 0xDDC6C837, 0xD79A3234, 0x92638212, 0x670EFA8E, 0x406000E0, }; static const uint32_t sbox3[] = { 0x3A39CE37, 0xD3FAF5CF, 0xABC27737, 0x5AC52D1B, 0x5CB0679E, 0x4FA33742, 0xD3822740, 0x99BC9BBE, 0xD5118E9D, 0xBF0F7315, 0xD62D1C7E, 0xC700C47B, 0xB78C1B6B, 0x21A19045, 0xB26EB1BE, 0x6A366EB4, 0x5748AB2F, 0xBC946E79, 0xC6A376D2, 0x6549C2C8, 0x530FF8EE, 0x468DDE7D, 0xD5730A1D, 0x4CD04DC6, 0x2939BBDB, 0xA9BA4650, 0xAC9526E8, 0xBE5EE304, 0xA1FAD5F0, 0x6A2D519A, 0x63EF8CE2, 0x9A86EE22, 0xC089C2B8, 0x43242EF6, 0xA51E03AA, 0x9CF2D0A4, 0x83C061BA, 0x9BE96A4D, 0x8FE51550, 0xBA645BD6, 0x2826A2F9, 0xA73A3AE1, 0x4BA99586, 0xEF5562E9, 0xC72FEFD3, 0xF752F7DA, 0x3F046F69, 0x77FA0A59, 0x80E4A915, 0x87B08601, 0x9B09E6AD, 0x3B3EE593, 0xE990FD5A, 0x9E34D797, 0x2CF0B7D9, 0x022B8B51, 0x96D5AC3A, 0x017DA67D, 0xD1CF3ED6, 0x7C7D2D28, 0x1F9F25CF, 0xADF2B89B, 0x5AD6B472, 0x5A88F54C, 0xE029AC71, 0xE019A5E6, 0x47B0ACFD, 0xED93FA9B, 0xE8D3C48D, 0x283B57CC, 0xF8D56629, 0x79132E28, 0x785F0191, 0xED756055, 0xF7960E44, 0xE3D35E8C, 0x15056DD4, 0x88F46DBA, 0x03A16125, 0x0564F0BD, 0xC3EB9E15, 0x3C9057A2, 0x97271AEC, 0xA93A072A, 0x1B3F6D9B, 0x1E6321F5, 0xF59C66FB, 0x26DCF319, 0x7533D928, 0xB155FDF5, 0x03563482, 0x8ABA3CBB, 0x28517711, 0xC20AD9F8, 0xABCC5167, 0xCCAD925F, 0x4DE81751, 0x3830DC8E, 0x379D5862, 0x9320F991, 0xEA7A90C2, 0xFB3E7BCE, 0x5121CE64, 0x774FBE32, 0xA8B6E37E, 0xC3293D46, 0x48DE5369, 0x6413E680, 0xA2AE0810, 0xDD6DB224, 0x69852DFD, 0x09072166, 0xB39A460A, 0x6445C0DD, 0x586CDECF, 0x1C20C8AE, 0x5BBEF7DD, 0x1B588D40, 0xCCD2017F, 0x6BB4E3BB, 0xDDA26A7E, 0x3A59FF45, 0x3E350A44, 0xBCB4CDD5, 0x72EACEA8, 0xFA6484BB, 0x8D6612AE, 0xBF3C6F47, 0xD29BE463, 0x542F5D9E, 0xAEC2771B, 0xF64E6370, 0x740E0D8D, 0xE75B1357, 0xF8721671, 0xAF537D5D, 0x4040CB08, 0x4EB4E2CC, 0x34D2466A, 0x0115AF84, 0xE1B00428, 0x95983A1D, 0x06B89FB4, 0xCE6EA048, 0x6F3F3B82, 0x3520AB82, 0x011A1D4B, 0x277227F8, 0x611560B1, 0xE7933FDC, 0xBB3A792B, 0x344525BD, 0xA08839E1, 0x51CE794B, 0x2F32C9B7, 0xA01FBAC9, 0xE01CC87E, 0xBCC7D1F6, 0xCF0111C3, 0xA1E8AAC7, 0x1A908749, 0xD44FBD9A, 0xD0DADECB, 0xD50ADA38, 0x0339C32A, 0xC6913667, 0x8DF9317C, 0xE0B12B4F, 0xF79E59B7, 0x43F5BB3A, 0xF2D519FF, 0x27D9459C, 0xBF97222C, 0x15E6FC2A, 0x0F91FC71, 0x9B941525, 0xFAE59361, 0xCEB69CEB, 0xC2A86459, 0x12BAA8D1, 0xB6C1075E, 0xE3056A0C, 0x10D25065, 0xCB03A442, 0xE0EC6E0E, 0x1698DB3B, 0x4C98A0BE, 0x3278E964, 0x9F1F9532, 0xE0D392DF, 0xD3A0342B, 0x8971F21E, 0x1B0A7441, 0x4BA3348C, 0xC5BE7120, 0xC37632D8, 0xDF359F8D, 0x9B992F2E, 0xE60B6F47, 0x0FE3F11D, 0xE54CDA54, 0x1EDAD891, 0xCE6279CF, 0xCD3E7E6F, 0x1618B166, 0xFD2C1D05, 0x848FD2C5, 0xF6FB2299, 0xF523F357, 0xA6327623, 0x93A83531, 0x56CCCD02, 0xACF08162, 0x5A75EBB5, 0x6E163697, 0x88D273CC, 0xDE966292, 0x81B949D0, 0x4C50901B, 0x71C65614, 0xE6C6C7BD, 0x327A140A, 0x45E1D006, 0xC3F27B9A, 0xC9AA53FD, 0x62A80F00, 0xBB25BFE2, 0x35BDD2F6, 0x71126905, 0xB2040222, 0xB6CBCF7C, 0xCD769C2B, 0x53113EC0, 0x1640E3D3, 0x38ABBD60, 0x2547ADF0, 0xBA38209C, 0xF746CE76, 0x77AFA1C5, 0x20756060, 0x85CBFE4E, 0x8AE88DD8, 0x7AAAF9B0, 0x4CF9AA7E, 0x1948C25C, 0x02FB8A8C, 0x01C36AE4, 0xD6EBE1F9, 0x90D4F869, 0xA65CDEA0, 0x3F09252D, 0xC208E69F, 0xB74E6132, 0xCE77E25B, 0x578FDFE3, 0x3AC372E6, }; #define Fprime(a,b,c,d) ( ( (S0[a] + S1[b]) ^ S2[c] ) + S3[d] ) #define F(x) Fprime( ((x>>24)&0xFF), ((x>>16)&0xFF), ((x>>8)&0xFF), (x&0xFF) ) #define ROUND(n) ( xL ^= P[n], t = xL, xL = F(xL) ^ xR, xR = t ) static void blowfish_encrypt(uint32_t xL, uint32_t xR, uint32_t *output, BlowfishContext * ctx) { uint32_t *S0 = ctx->S0; uint32_t *S1 = ctx->S1; uint32_t *S2 = ctx->S2; uint32_t *S3 = ctx->S3; uint32_t *P = ctx->P; uint32_t t; ROUND(0); ROUND(1); ROUND(2); ROUND(3); ROUND(4); ROUND(5); ROUND(6); ROUND(7); ROUND(8); ROUND(9); ROUND(10); ROUND(11); ROUND(12); ROUND(13); ROUND(14); ROUND(15); xL ^= P[16]; xR ^= P[17]; output[0] = xR; output[1] = xL; } static void blowfish_decrypt(uint32_t xL, uint32_t xR, uint32_t *output, BlowfishContext * ctx) { uint32_t *S0 = ctx->S0; uint32_t *S1 = ctx->S1; uint32_t *S2 = ctx->S2; uint32_t *S3 = ctx->S3; uint32_t *P = ctx->P; uint32_t t; ROUND(17); ROUND(16); ROUND(15); ROUND(14); ROUND(13); ROUND(12); ROUND(11); ROUND(10); ROUND(9); ROUND(8); ROUND(7); ROUND(6); ROUND(5); ROUND(4); ROUND(3); ROUND(2); xL ^= P[1]; xR ^= P[0]; output[0] = xR; output[1] = xL; } static void blowfish_lsb_encrypt_cbc(unsigned char *blk, int len, BlowfishContext * ctx) { uint32_t xL, xR, out[2], iv0, iv1; assert((len & 7) == 0); iv0 = ctx->iv0; iv1 = ctx->iv1; while (len > 0) { xL = GET_32BIT_LSB_FIRST(blk); xR = GET_32BIT_LSB_FIRST(blk + 4); iv0 ^= xL; iv1 ^= xR; blowfish_encrypt(iv0, iv1, out, ctx); iv0 = out[0]; iv1 = out[1]; PUT_32BIT_LSB_FIRST(blk, iv0); PUT_32BIT_LSB_FIRST(blk + 4, iv1); blk += 8; len -= 8; } ctx->iv0 = iv0; ctx->iv1 = iv1; } void blowfish_lsb_encrypt_ecb(void *vblk, int len, BlowfishContext * ctx) { unsigned char *blk = (unsigned char *)vblk; uint32_t xL, xR, out[2]; assert((len & 7) == 0); while (len > 0) { xL = GET_32BIT_LSB_FIRST(blk); xR = GET_32BIT_LSB_FIRST(blk + 4); blowfish_encrypt(xL, xR, out, ctx); PUT_32BIT_LSB_FIRST(blk, out[0]); PUT_32BIT_LSB_FIRST(blk + 4, out[1]); blk += 8; len -= 8; } } static void blowfish_lsb_decrypt_cbc(unsigned char *blk, int len, BlowfishContext * ctx) { uint32_t xL, xR, out[2], iv0, iv1; assert((len & 7) == 0); iv0 = ctx->iv0; iv1 = ctx->iv1; while (len > 0) { xL = GET_32BIT_LSB_FIRST(blk); xR = GET_32BIT_LSB_FIRST(blk + 4); blowfish_decrypt(xL, xR, out, ctx); iv0 ^= out[0]; iv1 ^= out[1]; PUT_32BIT_LSB_FIRST(blk, iv0); PUT_32BIT_LSB_FIRST(blk + 4, iv1); iv0 = xL; iv1 = xR; blk += 8; len -= 8; } ctx->iv0 = iv0; ctx->iv1 = iv1; } static void blowfish_msb_encrypt_cbc(unsigned char *blk, int len, BlowfishContext * ctx) { uint32_t xL, xR, out[2], iv0, iv1; assert((len & 7) == 0); iv0 = ctx->iv0; iv1 = ctx->iv1; while (len > 0) { xL = GET_32BIT_MSB_FIRST(blk); xR = GET_32BIT_MSB_FIRST(blk + 4); iv0 ^= xL; iv1 ^= xR; blowfish_encrypt(iv0, iv1, out, ctx); iv0 = out[0]; iv1 = out[1]; PUT_32BIT_MSB_FIRST(blk, iv0); PUT_32BIT_MSB_FIRST(blk + 4, iv1); blk += 8; len -= 8; } ctx->iv0 = iv0; ctx->iv1 = iv1; } static void blowfish_msb_decrypt_cbc(unsigned char *blk, int len, BlowfishContext * ctx) { uint32_t xL, xR, out[2], iv0, iv1; assert((len & 7) == 0); iv0 = ctx->iv0; iv1 = ctx->iv1; while (len > 0) { xL = GET_32BIT_MSB_FIRST(blk); xR = GET_32BIT_MSB_FIRST(blk + 4); blowfish_decrypt(xL, xR, out, ctx); iv0 ^= out[0]; iv1 ^= out[1]; PUT_32BIT_MSB_FIRST(blk, iv0); PUT_32BIT_MSB_FIRST(blk + 4, iv1); iv0 = xL; iv1 = xR; blk += 8; len -= 8; } ctx->iv0 = iv0; ctx->iv1 = iv1; } static void blowfish_msb_sdctr(unsigned char *blk, int len, BlowfishContext * ctx) { uint32_t b[2], iv0, iv1, tmp; assert((len & 7) == 0); iv0 = ctx->iv0; iv1 = ctx->iv1; while (len > 0) { blowfish_encrypt(iv0, iv1, b, ctx); tmp = GET_32BIT_MSB_FIRST(blk); PUT_32BIT_MSB_FIRST(blk, tmp ^ b[0]); tmp = GET_32BIT_MSB_FIRST(blk + 4); PUT_32BIT_MSB_FIRST(blk + 4, tmp ^ b[1]); if ((iv1 = (iv1 + 1) & 0xffffffff) == 0) iv0 = (iv0 + 1) & 0xffffffff; blk += 8; len -= 8; } ctx->iv0 = iv0; ctx->iv1 = iv1; } void blowfish_initkey(BlowfishContext *ctx) { int i; for (i = 0; i < 18; i++) { ctx->P[i] = parray[i]; } for (i = 0; i < 256; i++) { ctx->S0[i] = sbox0[i]; ctx->S1[i] = sbox1[i]; ctx->S2[i] = sbox2[i]; ctx->S3[i] = sbox3[i]; } } void blowfish_expandkey(BlowfishContext * ctx, const void *vkey, short keybytes, const void *vsalt, short saltbytes) { const unsigned char *key = (const unsigned char *)vkey; const unsigned char *salt = (const unsigned char *)vsalt; uint32_t *S0 = ctx->S0; uint32_t *S1 = ctx->S1; uint32_t *S2 = ctx->S2; uint32_t *S3 = ctx->S3; uint32_t *P = ctx->P; uint32_t str[2]; int i, j; int saltpos; unsigned char dummysalt[1]; saltpos = 0; if (!salt) { saltbytes = 1; salt = dummysalt; dummysalt[0] = 0; } for (i = 0; i < 18; i++) { P[i] ^= ((uint32_t) (unsigned char) (key[(i * 4 + 0) % keybytes])) << 24; P[i] ^= ((uint32_t) (unsigned char) (key[(i * 4 + 1) % keybytes])) << 16; P[i] ^= ((uint32_t) (unsigned char) (key[(i * 4 + 2) % keybytes])) << 8; P[i] ^= ((uint32_t) (unsigned char) (key[(i * 4 + 3) % keybytes])); } str[0] = str[1] = 0; for (i = 0; i < 18; i += 2) { for (j = 0; j < 8; j++) str[j/4] ^= ((uint32_t)salt[saltpos++ % saltbytes]) << (24-8*(j%4)); blowfish_encrypt(str[0], str[1], str, ctx); P[i] = str[0]; P[i + 1] = str[1]; } for (i = 0; i < 256; i += 2) { for (j = 0; j < 8; j++) str[j/4] ^= ((uint32_t)salt[saltpos++ % saltbytes]) << (24-8*(j%4)); blowfish_encrypt(str[0], str[1], str, ctx); S0[i] = str[0]; S0[i + 1] = str[1]; } for (i = 0; i < 256; i += 2) { for (j = 0; j < 8; j++) str[j/4] ^= ((uint32_t)salt[saltpos++ % saltbytes]) << (24-8*(j%4)); blowfish_encrypt(str[0], str[1], str, ctx); S1[i] = str[0]; S1[i + 1] = str[1]; } for (i = 0; i < 256; i += 2) { for (j = 0; j < 8; j++) str[j/4] ^= ((uint32_t)salt[saltpos++ % saltbytes]) << (24-8*(j%4)); blowfish_encrypt(str[0], str[1], str, ctx); S2[i] = str[0]; S2[i + 1] = str[1]; } for (i = 0; i < 256; i += 2) { for (j = 0; j < 8; j++) str[j/4] ^= ((uint32_t)salt[saltpos++ % saltbytes]) << (24-8*(j%4)); blowfish_encrypt(str[0], str[1], str, ctx); S3[i] = str[0]; S3[i + 1] = str[1]; } } static void blowfish_setkey(BlowfishContext *ctx, const unsigned char *key, short keybytes) { blowfish_initkey(ctx); blowfish_expandkey(ctx, key, keybytes, NULL, 0); } /* -- Interface with PuTTY -- */ #define SSH1_SESSION_KEY_LENGTH 32 BlowfishContext *blowfish_make_context(void) { return snew(BlowfishContext); } void blowfish_free_context(BlowfishContext *ctx) { sfree(ctx); } static void blowfish_iv_be(BlowfishContext *ctx, const void *viv) { const unsigned char *iv = (const unsigned char *)viv; ctx->iv0 = GET_32BIT_MSB_FIRST(iv); ctx->iv1 = GET_32BIT_MSB_FIRST(iv + 4); } static void blowfish_iv_le(BlowfishContext *ctx, const void *viv) { const unsigned char *iv = (const unsigned char *)viv; ctx->iv0 = GET_32BIT_LSB_FIRST(iv); ctx->iv1 = GET_32BIT_LSB_FIRST(iv + 4); } struct blowfish_ctx { BlowfishContext context; ssh_cipher ciph; }; static ssh_cipher *blowfish_new(const ssh_cipheralg *alg) { struct blowfish_ctx *ctx = snew(struct blowfish_ctx); ctx->ciph.vt = alg; return &ctx->ciph; } static void blowfish_free(ssh_cipher *cipher) { struct blowfish_ctx *ctx = container_of(cipher, struct blowfish_ctx, ciph); smemclr(ctx, sizeof(*ctx)); sfree(ctx); } static void blowfish_ssh_setkey(ssh_cipher *cipher, const void *key) { struct blowfish_ctx *ctx = container_of(cipher, struct blowfish_ctx, ciph); blowfish_setkey(&ctx->context, key, ctx->ciph.vt->padded_keybytes); } static void blowfish_ssh1_setiv(ssh_cipher *cipher, const void *iv) { struct blowfish_ctx *ctx = container_of(cipher, struct blowfish_ctx, ciph); blowfish_iv_le(&ctx->context, iv); } static void blowfish_ssh2_setiv(ssh_cipher *cipher, const void *iv) { struct blowfish_ctx *ctx = container_of(cipher, struct blowfish_ctx, ciph); blowfish_iv_be(&ctx->context, iv); } static void blowfish_ssh1_encrypt_blk(ssh_cipher *cipher, void *blk, int len) { struct blowfish_ctx *ctx = container_of(cipher, struct blowfish_ctx, ciph); blowfish_lsb_encrypt_cbc(blk, len, &ctx->context); } static void blowfish_ssh1_decrypt_blk(ssh_cipher *cipher, void *blk, int len) { struct blowfish_ctx *ctx = container_of(cipher, struct blowfish_ctx, ciph); blowfish_lsb_decrypt_cbc(blk, len, &ctx->context); } static void blowfish_ssh2_encrypt_blk(ssh_cipher *cipher, void *blk, int len) { struct blowfish_ctx *ctx = container_of(cipher, struct blowfish_ctx, ciph); blowfish_msb_encrypt_cbc(blk, len, &ctx->context); } static void blowfish_ssh2_decrypt_blk(ssh_cipher *cipher, void *blk, int len) { struct blowfish_ctx *ctx = container_of(cipher, struct blowfish_ctx, ciph); blowfish_msb_decrypt_cbc(blk, len, &ctx->context); } static void blowfish_ssh2_sdctr(ssh_cipher *cipher, void *blk, int len) { struct blowfish_ctx *ctx = container_of(cipher, struct blowfish_ctx, ciph); blowfish_msb_sdctr(blk, len, &ctx->context); } const ssh_cipheralg ssh_blowfish_ssh1 = { .new = blowfish_new, .free = blowfish_free, .setiv = blowfish_ssh1_setiv, .setkey = blowfish_ssh_setkey, .encrypt = blowfish_ssh1_encrypt_blk, .decrypt = blowfish_ssh1_decrypt_blk, .blksize = 8, .real_keybits = 128, .padded_keybytes = SSH1_SESSION_KEY_LENGTH, .flags = SSH_CIPHER_IS_CBC, .text_name = "Blowfish-256 CBC", }; const ssh_cipheralg ssh_blowfish_ssh2 = { .new = blowfish_new, .free = blowfish_free, .setiv = blowfish_ssh2_setiv, .setkey = blowfish_ssh_setkey, .encrypt = blowfish_ssh2_encrypt_blk, .decrypt = blowfish_ssh2_decrypt_blk, .ssh2_id = "blowfish-cbc", .blksize = 8, .real_keybits = 128, .padded_keybytes = 16, .flags = SSH_CIPHER_IS_CBC, .text_name = "Blowfish-128 CBC", }; const ssh_cipheralg ssh_blowfish_ssh2_ctr = { .new = blowfish_new, .free = blowfish_free, .setiv = blowfish_ssh2_setiv, .setkey = blowfish_ssh_setkey, .encrypt = blowfish_ssh2_sdctr, .decrypt = blowfish_ssh2_sdctr, .ssh2_id = "blowfish-ctr", .blksize = 8, .real_keybits = 256, .padded_keybytes = 32, .flags = 0, .text_name = "Blowfish-256 SDCTR", }; static const ssh_cipheralg *const blowfish_list[] = { &ssh_blowfish_ssh2_ctr, &ssh_blowfish_ssh2 }; const ssh2_ciphers ssh2_blowfish = { lenof(blowfish_list), blowfish_list }; putty-0.76/sshblowf.h0000644000175000017500000000105014072266312011553 00000000000000/* * Header file shared between sshblowf.c and sshbcrypt.c. Exposes the * internal Blowfish routines needed by bcrypt. */ typedef struct BlowfishContext BlowfishContext; BlowfishContext *blowfish_make_context(void); void blowfish_free_context(BlowfishContext *ctx); void blowfish_initkey(BlowfishContext *ctx); void blowfish_expandkey(BlowfishContext *ctx, const void *key, short keybytes, const void *salt, short saltbytes); void blowfish_lsb_encrypt_ecb(void *blk, int len, BlowfishContext *ctx); putty-0.76/sshbpp.h0000644000175000017500000001573414072266312011241 00000000000000/* * Abstraction of the binary packet protocols used in SSH. */ #ifndef PUTTY_SSHBPP_H #define PUTTY_SSHBPP_H typedef struct BinaryPacketProtocolVtable BinaryPacketProtocolVtable; struct BinaryPacketProtocolVtable { void (*free)(BinaryPacketProtocol *); void (*handle_input)(BinaryPacketProtocol *); void (*handle_output)(BinaryPacketProtocol *); PktOut *(*new_pktout)(int type); void (*queue_disconnect)(BinaryPacketProtocol *, const char *msg, int category); uint32_t packet_size_limit; }; struct BinaryPacketProtocol { const struct BinaryPacketProtocolVtable *vt; bufchain *in_raw, *out_raw; bool input_eof; /* set this if in_raw will never be added to again */ PktInQueue in_pq; PktOutQueue out_pq; PacketLogSettings *pls; LogContext *logctx; Ssh *ssh; /* ic_in_raw is filled in by the BPP (probably by calling * ssh_bpp_common_setup). The BPP's owner triggers it when data is * added to in_raw, and also when the BPP is newly created. */ IdempotentCallback ic_in_raw; /* ic_out_pq is entirely internal to the BPP itself; it's used as * the callback on out_pq. */ IdempotentCallback ic_out_pq; /* Information that all packet layers sharing this BPP will * potentially be interested in. */ int remote_bugs; bool ext_info_rsa_sha256_ok, ext_info_rsa_sha512_ok; /* Set this if remote connection closure should not generate an * error message (either because it's not to be treated as an * error at all, or because some other error message has already * been emitted). */ bool expect_close; }; static inline void ssh_bpp_handle_input(BinaryPacketProtocol *bpp) { bpp->vt->handle_input(bpp); } static inline void ssh_bpp_handle_output(BinaryPacketProtocol *bpp) { bpp->vt->handle_output(bpp); } static inline PktOut *ssh_bpp_new_pktout(BinaryPacketProtocol *bpp, int type) { return bpp->vt->new_pktout(type); } static inline void ssh_bpp_queue_disconnect(BinaryPacketProtocol *bpp, const char *msg, int category) { bpp->vt->queue_disconnect(bpp, msg, category); } /* ssh_bpp_free is more than just a macro wrapper on the vtable; it * does centralised parts of the freeing too. */ void ssh_bpp_free(BinaryPacketProtocol *bpp); BinaryPacketProtocol *ssh1_bpp_new(LogContext *logctx); void ssh1_bpp_new_cipher(BinaryPacketProtocol *bpp, const ssh_cipheralg *cipher, const void *session_key); /* This is only called from outside the BPP in server mode; in client * mode the BPP detects compression start time automatically by * snooping message types */ void ssh1_bpp_start_compression(BinaryPacketProtocol *bpp); /* Helper routine which does common BPP initialisation, e.g. setting * up in_pq and out_pq, and initialising input_consumer. */ void ssh_bpp_common_setup(BinaryPacketProtocol *); /* Common helper functions between the SSH-2 full and bare BPPs */ void ssh2_bpp_queue_disconnect(BinaryPacketProtocol *bpp, const char *msg, int category); bool ssh2_bpp_check_unimplemented(BinaryPacketProtocol *bpp, PktIn *pktin); /* Convenience macro for BPPs to send formatted strings to the Event * Log. Assumes a function parameter called 'bpp' is in scope. */ #define bpp_logevent(...) ( \ logevent_and_free((bpp)->logctx, dupprintf(__VA_ARGS__))) /* * Structure that tracks how much data is sent and received, for * purposes of triggering an SSH-2 rekey when either one gets over a * configured limit. In each direction, the flag 'running' indicates * that we haven't hit the limit yet, and 'remaining' tracks how much * longer until we do. The function dts_consume() subtracts a given * amount from the counter in a particular direction, and sets * 'expired' if the limit has been hit. * * The limit is sticky: once 'running' has flipped to false, * 'remaining' is no longer decremented, so it shouldn't dangerously * wrap round. */ struct DataTransferStatsDirection { bool running, expired; unsigned long remaining; }; struct DataTransferStats { struct DataTransferStatsDirection in, out; }; static inline void dts_consume(struct DataTransferStatsDirection *s, unsigned long size_consumed) { if (s->running) { if (s->remaining <= size_consumed) { s->running = false; s->expired = true; } else { s->remaining -= size_consumed; } } } static inline void dts_reset(struct DataTransferStatsDirection *s, unsigned long starting_size) { s->expired = false; s->remaining = starting_size; /* * The semantics of setting CONF_ssh_rekey_data to zero are to * disable data-volume based rekeying completely. So if the * starting size is actually zero, we don't set 'running' to true * in the first place, which means we won't ever set the expired * flag. */ s->running = (starting_size != 0); } BinaryPacketProtocol *ssh2_bpp_new( LogContext *logctx, struct DataTransferStats *stats, bool is_server); void ssh2_bpp_new_outgoing_crypto( BinaryPacketProtocol *bpp, const ssh_cipheralg *cipher, const void *ckey, const void *iv, const ssh2_macalg *mac, bool etm_mode, const void *mac_key, const ssh_compression_alg *compression, bool delayed_compression); void ssh2_bpp_new_incoming_crypto( BinaryPacketProtocol *bpp, const ssh_cipheralg *cipher, const void *ckey, const void *iv, const ssh2_macalg *mac, bool etm_mode, const void *mac_key, const ssh_compression_alg *compression, bool delayed_compression); /* * A query method specific to the interface between ssh2transport and * ssh2bpp. If true, it indicates that we're potentially in the * race-condition-prone part of delayed compression setup and so * asynchronous outgoing transport-layer packets are currently not * being sent, which means in particular that it would be a bad idea * to start a rekey because then we'd stop responding to anything * _other_ than transport-layer packets and deadlock the protocol. */ bool ssh2_bpp_rekey_inadvisable(BinaryPacketProtocol *bpp); BinaryPacketProtocol *ssh2_bare_bpp_new(LogContext *logctx); /* * The initial code to handle the SSH version exchange is also * structured as an implementation of BinaryPacketProtocol, because * that makes it easy to switch from that to the next BPP once it * tells us which one we're using. */ struct ssh_version_receiver { void (*got_ssh_version)(struct ssh_version_receiver *rcv, int major_version); }; BinaryPacketProtocol *ssh_verstring_new( Conf *conf, LogContext *logctx, bool bare_connection_mode, const char *protoversion, struct ssh_version_receiver *rcv, bool server_mode, const char *impl_name); const char *ssh_verstring_get_remote(BinaryPacketProtocol *); const char *ssh_verstring_get_local(BinaryPacketProtocol *); int ssh_verstring_get_bugs(BinaryPacketProtocol *); #endif /* PUTTY_SSHBPP_H */ putty-0.76/sshccp.c0000644000175000017500000010216214072266312011210 00000000000000/* * ChaCha20-Poly1305 Implementation for SSH-2 * * Protocol spec: * http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.chacha20poly1305?rev=1.2&content-type=text/x-cvsweb-markup * * ChaCha20 spec: * http://cr.yp.to/chacha/chacha-20080128.pdf * * Salsa20 spec: * http://cr.yp.to/snuffle/spec.pdf * * Poly1305-AES spec: * http://cr.yp.to/mac/poly1305-20050329.pdf * * The nonce for the Poly1305 is the second part of the key output * from the first round of ChaCha20. This removes the AES requirement. * This is undocumented! * * This has an intricate link between the cipher and the MAC. The * keying of both is done in by the cipher and setting of the IV is * done by the MAC. One cannot operate without the other. The * configuration of the ssh_cipheralg structure ensures that the MAC is * set (and others ignored) if this cipher is chosen. * * This cipher also encrypts the length using a different * instantiation of the cipher using a different key and IV made from * the sequence number which is passed in addition when calling * encrypt/decrypt on it. */ #include "ssh.h" #include "mpint_i.h" #ifndef INLINE #define INLINE #endif /* ChaCha20 implementation, only supporting 256-bit keys */ /* State for each ChaCha20 instance */ struct chacha20 { /* Current context, usually with the count incremented * 0-3 are the static constant * 4-11 are the key * 12-13 are the counter * 14-15 are the IV */ uint32_t state[16]; /* The output of the state above ready to xor */ unsigned char current[64]; /* The index of the above currently used to allow a true streaming cipher */ int currentIndex; }; static INLINE void chacha20_round(struct chacha20 *ctx) { int i; uint32_t copy[16]; /* Take a copy */ memcpy(copy, ctx->state, sizeof(copy)); /* A circular rotation for a 32bit number */ #define rotl(x, shift) x = ((x << shift) | (x >> (32 - shift))) /* What to do for each quarter round operation */ #define qrop(a, b, c, d) \ copy[a] += copy[b]; \ copy[c] ^= copy[a]; \ rotl(copy[c], d) /* A quarter round */ #define quarter(a, b, c, d) \ qrop(a, b, d, 16); \ qrop(c, d, b, 12); \ qrop(a, b, d, 8); \ qrop(c, d, b, 7) /* Do 20 rounds, in pairs because every other is different */ for (i = 0; i < 20; i += 2) { /* A round */ quarter(0, 4, 8, 12); quarter(1, 5, 9, 13); quarter(2, 6, 10, 14); quarter(3, 7, 11, 15); /* Another slightly different round */ quarter(0, 5, 10, 15); quarter(1, 6, 11, 12); quarter(2, 7, 8, 13); quarter(3, 4, 9, 14); } /* Dump the macros, don't need them littering */ #undef rotl #undef qrop #undef quarter /* Add the initial state */ for (i = 0; i < 16; ++i) { copy[i] += ctx->state[i]; } /* Update the content of the xor buffer */ for (i = 0; i < 16; ++i) { ctx->current[i * 4 + 0] = copy[i] >> 0; ctx->current[i * 4 + 1] = copy[i] >> 8; ctx->current[i * 4 + 2] = copy[i] >> 16; ctx->current[i * 4 + 3] = copy[i] >> 24; } /* State full, reset pointer to beginning */ ctx->currentIndex = 0; smemclr(copy, sizeof(copy)); /* Increment round counter */ ++ctx->state[12]; /* Check for overflow, not done in one line so the 32 bits are chopped by the type */ if (!(uint32_t)(ctx->state[12])) { ++ctx->state[13]; } } /* Initialise context with 256bit key */ static void chacha20_key(struct chacha20 *ctx, const unsigned char *key) { static const char constant[16] = "expand 32-byte k"; /* Add the fixed string to the start of the state */ ctx->state[0] = GET_32BIT_LSB_FIRST(constant + 0); ctx->state[1] = GET_32BIT_LSB_FIRST(constant + 4); ctx->state[2] = GET_32BIT_LSB_FIRST(constant + 8); ctx->state[3] = GET_32BIT_LSB_FIRST(constant + 12); /* Add the key */ ctx->state[4] = GET_32BIT_LSB_FIRST(key + 0); ctx->state[5] = GET_32BIT_LSB_FIRST(key + 4); ctx->state[6] = GET_32BIT_LSB_FIRST(key + 8); ctx->state[7] = GET_32BIT_LSB_FIRST(key + 12); ctx->state[8] = GET_32BIT_LSB_FIRST(key + 16); ctx->state[9] = GET_32BIT_LSB_FIRST(key + 20); ctx->state[10] = GET_32BIT_LSB_FIRST(key + 24); ctx->state[11] = GET_32BIT_LSB_FIRST(key + 28); /* New key, dump context */ ctx->currentIndex = 64; } static void chacha20_iv(struct chacha20 *ctx, const unsigned char *iv) { ctx->state[12] = 0; ctx->state[13] = 0; ctx->state[14] = GET_32BIT_MSB_FIRST(iv); ctx->state[15] = GET_32BIT_MSB_FIRST(iv + 4); /* New IV, dump context */ ctx->currentIndex = 64; } static void chacha20_encrypt(struct chacha20 *ctx, unsigned char *blk, int len) { while (len) { /* If we don't have any state left, then cycle to the next */ if (ctx->currentIndex >= 64) { chacha20_round(ctx); } /* Do the xor while there's some state left and some plaintext left */ while (ctx->currentIndex < 64 && len) { *blk++ ^= ctx->current[ctx->currentIndex++]; --len; } } } /* Decrypt is encrypt... It's xor against a PRNG... */ static INLINE void chacha20_decrypt(struct chacha20 *ctx, unsigned char *blk, int len) { chacha20_encrypt(ctx, blk, len); } /* Poly1305 implementation (no AES, nonce is not encrypted) */ #define NWORDS ((130 + BIGNUM_INT_BITS-1) / BIGNUM_INT_BITS) typedef struct bigval { BignumInt w[NWORDS]; } bigval; static void bigval_clear(bigval *r) { int i; for (i = 0; i < NWORDS; i++) r->w[i] = 0; } static void bigval_import_le(bigval *r, const void *vdata, int len) { const unsigned char *data = (const unsigned char *)vdata; int i; bigval_clear(r); for (i = 0; i < len; i++) r->w[i / BIGNUM_INT_BYTES] |= (BignumInt)data[i] << (8 * (i % BIGNUM_INT_BYTES)); } static void bigval_export_le(const bigval *r, void *vdata, int len) { unsigned char *data = (unsigned char *)vdata; int i; for (i = 0; i < len; i++) data[i] = r->w[i / BIGNUM_INT_BYTES] >> (8 * (i % BIGNUM_INT_BYTES)); } /* * Core functions to do arithmetic mod p = 2^130-5. The whole * collection of these, up to and including the surrounding #if, are * generated automatically for various sizes of BignumInt by * contrib/make1305.py. */ #if BIGNUM_INT_BITS == 16 static void bigval_add(bigval *r, const bigval *a, const bigval *b) { BignumInt v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14; BignumInt v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26; BignumCarry carry; v0 = a->w[0]; v1 = a->w[1]; v2 = a->w[2]; v3 = a->w[3]; v4 = a->w[4]; v5 = a->w[5]; v6 = a->w[6]; v7 = a->w[7]; v8 = a->w[8]; v9 = b->w[0]; v10 = b->w[1]; v11 = b->w[2]; v12 = b->w[3]; v13 = b->w[4]; v14 = b->w[5]; v15 = b->w[6]; v16 = b->w[7]; v17 = b->w[8]; BignumADC(v18, carry, v0, v9, 0); BignumADC(v19, carry, v1, v10, carry); BignumADC(v20, carry, v2, v11, carry); BignumADC(v21, carry, v3, v12, carry); BignumADC(v22, carry, v4, v13, carry); BignumADC(v23, carry, v5, v14, carry); BignumADC(v24, carry, v6, v15, carry); BignumADC(v25, carry, v7, v16, carry); v26 = v8 + v17 + carry; r->w[0] = v18; r->w[1] = v19; r->w[2] = v20; r->w[3] = v21; r->w[4] = v22; r->w[5] = v23; r->w[6] = v24; r->w[7] = v25; r->w[8] = v26; } static void bigval_mul_mod_p(bigval *r, const bigval *a, const bigval *b) { BignumInt v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14; BignumInt v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27; BignumInt v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40; BignumInt v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53; BignumInt v54, v55, v56, v57, v58, v59, v60, v61, v62, v63, v64, v65, v66; BignumInt v67, v68, v69, v70, v71, v72, v73, v74, v75, v76, v77, v78, v79; BignumInt v80, v81, v82, v83, v84, v85, v86, v87, v88, v89, v90, v91, v92; BignumInt v93, v94, v95, v96, v97, v98, v99, v100, v101, v102, v103, v104; BignumInt v105, v106, v107, v108, v109, v110, v111, v112, v113, v114; BignumInt v115, v116, v117, v118, v119, v120, v121, v122, v123, v124; BignumInt v125, v126, v127, v128, v129, v130, v131, v132, v133, v134; BignumInt v135, v136, v137, v138, v139, v140, v141, v142, v143, v144; BignumInt v145, v146, v147, v148, v149, v150, v151, v152, v153, v154; BignumInt v155, v156, v157, v158, v159, v160, v161, v162, v163, v164; BignumInt v165, v166, v167, v168, v169, v170, v171, v172, v173, v174; BignumInt v175, v176, v177, v178, v180, v181, v182, v183, v184, v185; BignumInt v186, v187, v188, v189, v190, v191, v192, v193, v194, v195; BignumInt v196, v197, v198, v199, v200, v201, v202, v203, v204, v205; BignumInt v206, v207, v208, v210, v212, v213, v214, v215, v216, v217; BignumInt v218, v219, v220, v221, v222, v223, v224, v225, v226, v227; BignumInt v228, v229; BignumCarry carry; v0 = a->w[0]; v1 = a->w[1]; v2 = a->w[2]; v3 = a->w[3]; v4 = a->w[4]; v5 = a->w[5]; v6 = a->w[6]; v7 = a->w[7]; v8 = a->w[8]; v9 = b->w[0]; v10 = b->w[1]; v11 = b->w[2]; v12 = b->w[3]; v13 = b->w[4]; v14 = b->w[5]; v15 = b->w[6]; v16 = b->w[7]; v17 = b->w[8]; BignumMUL(v19, v18, v0, v9); BignumMULADD(v21, v20, v0, v10, v19); BignumMULADD(v23, v22, v0, v11, v21); BignumMULADD(v25, v24, v0, v12, v23); BignumMULADD(v27, v26, v0, v13, v25); BignumMULADD(v29, v28, v0, v14, v27); BignumMULADD(v31, v30, v0, v15, v29); BignumMULADD(v33, v32, v0, v16, v31); BignumMULADD(v35, v34, v0, v17, v33); BignumMULADD(v37, v36, v1, v9, v20); BignumMULADD2(v39, v38, v1, v10, v22, v37); BignumMULADD2(v41, v40, v1, v11, v24, v39); BignumMULADD2(v43, v42, v1, v12, v26, v41); BignumMULADD2(v45, v44, v1, v13, v28, v43); BignumMULADD2(v47, v46, v1, v14, v30, v45); BignumMULADD2(v49, v48, v1, v15, v32, v47); BignumMULADD2(v51, v50, v1, v16, v34, v49); BignumMULADD2(v53, v52, v1, v17, v35, v51); BignumMULADD(v55, v54, v2, v9, v38); BignumMULADD2(v57, v56, v2, v10, v40, v55); BignumMULADD2(v59, v58, v2, v11, v42, v57); BignumMULADD2(v61, v60, v2, v12, v44, v59); BignumMULADD2(v63, v62, v2, v13, v46, v61); BignumMULADD2(v65, v64, v2, v14, v48, v63); BignumMULADD2(v67, v66, v2, v15, v50, v65); BignumMULADD2(v69, v68, v2, v16, v52, v67); BignumMULADD2(v71, v70, v2, v17, v53, v69); BignumMULADD(v73, v72, v3, v9, v56); BignumMULADD2(v75, v74, v3, v10, v58, v73); BignumMULADD2(v77, v76, v3, v11, v60, v75); BignumMULADD2(v79, v78, v3, v12, v62, v77); BignumMULADD2(v81, v80, v3, v13, v64, v79); BignumMULADD2(v83, v82, v3, v14, v66, v81); BignumMULADD2(v85, v84, v3, v15, v68, v83); BignumMULADD2(v87, v86, v3, v16, v70, v85); BignumMULADD2(v89, v88, v3, v17, v71, v87); BignumMULADD(v91, v90, v4, v9, v74); BignumMULADD2(v93, v92, v4, v10, v76, v91); BignumMULADD2(v95, v94, v4, v11, v78, v93); BignumMULADD2(v97, v96, v4, v12, v80, v95); BignumMULADD2(v99, v98, v4, v13, v82, v97); BignumMULADD2(v101, v100, v4, v14, v84, v99); BignumMULADD2(v103, v102, v4, v15, v86, v101); BignumMULADD2(v105, v104, v4, v16, v88, v103); BignumMULADD2(v107, v106, v4, v17, v89, v105); BignumMULADD(v109, v108, v5, v9, v92); BignumMULADD2(v111, v110, v5, v10, v94, v109); BignumMULADD2(v113, v112, v5, v11, v96, v111); BignumMULADD2(v115, v114, v5, v12, v98, v113); BignumMULADD2(v117, v116, v5, v13, v100, v115); BignumMULADD2(v119, v118, v5, v14, v102, v117); BignumMULADD2(v121, v120, v5, v15, v104, v119); BignumMULADD2(v123, v122, v5, v16, v106, v121); BignumMULADD2(v125, v124, v5, v17, v107, v123); BignumMULADD(v127, v126, v6, v9, v110); BignumMULADD2(v129, v128, v6, v10, v112, v127); BignumMULADD2(v131, v130, v6, v11, v114, v129); BignumMULADD2(v133, v132, v6, v12, v116, v131); BignumMULADD2(v135, v134, v6, v13, v118, v133); BignumMULADD2(v137, v136, v6, v14, v120, v135); BignumMULADD2(v139, v138, v6, v15, v122, v137); BignumMULADD2(v141, v140, v6, v16, v124, v139); BignumMULADD2(v143, v142, v6, v17, v125, v141); BignumMULADD(v145, v144, v7, v9, v128); BignumMULADD2(v147, v146, v7, v10, v130, v145); BignumMULADD2(v149, v148, v7, v11, v132, v147); BignumMULADD2(v151, v150, v7, v12, v134, v149); BignumMULADD2(v153, v152, v7, v13, v136, v151); BignumMULADD2(v155, v154, v7, v14, v138, v153); BignumMULADD2(v157, v156, v7, v15, v140, v155); BignumMULADD2(v159, v158, v7, v16, v142, v157); BignumMULADD2(v161, v160, v7, v17, v143, v159); BignumMULADD(v163, v162, v8, v9, v146); BignumMULADD2(v165, v164, v8, v10, v148, v163); BignumMULADD2(v167, v166, v8, v11, v150, v165); BignumMULADD2(v169, v168, v8, v12, v152, v167); BignumMULADD2(v171, v170, v8, v13, v154, v169); BignumMULADD2(v173, v172, v8, v14, v156, v171); BignumMULADD2(v175, v174, v8, v15, v158, v173); BignumMULADD2(v177, v176, v8, v16, v160, v175); v178 = v8 * v17 + v161 + v177; v180 = (v162) & ((((BignumInt)1) << 2)-1); v181 = ((v162) >> 2) | ((v164) << 14); v182 = ((v164) >> 2) | ((v166) << 14); v183 = ((v166) >> 2) | ((v168) << 14); v184 = ((v168) >> 2) | ((v170) << 14); v185 = ((v170) >> 2) | ((v172) << 14); v186 = ((v172) >> 2) | ((v174) << 14); v187 = ((v174) >> 2) | ((v176) << 14); v188 = ((v176) >> 2) | ((v178) << 14); v189 = (v178) >> 2; v190 = (v189) & ((((BignumInt)1) << 2)-1); v191 = (v178) >> 4; BignumMUL(v193, v192, 5, v181); BignumMULADD(v195, v194, 5, v182, v193); BignumMULADD(v197, v196, 5, v183, v195); BignumMULADD(v199, v198, 5, v184, v197); BignumMULADD(v201, v200, 5, v185, v199); BignumMULADD(v203, v202, 5, v186, v201); BignumMULADD(v205, v204, 5, v187, v203); BignumMULADD(v207, v206, 5, v188, v205); v208 = 5 * v190 + v207; v210 = 25 * v191; BignumADC(v212, carry, v18, v192, 0); BignumADC(v213, carry, v36, v194, carry); BignumADC(v214, carry, v54, v196, carry); BignumADC(v215, carry, v72, v198, carry); BignumADC(v216, carry, v90, v200, carry); BignumADC(v217, carry, v108, v202, carry); BignumADC(v218, carry, v126, v204, carry); BignumADC(v219, carry, v144, v206, carry); v220 = v180 + v208 + carry; BignumADC(v221, carry, v212, v210, 0); BignumADC(v222, carry, v213, 0, carry); BignumADC(v223, carry, v214, 0, carry); BignumADC(v224, carry, v215, 0, carry); BignumADC(v225, carry, v216, 0, carry); BignumADC(v226, carry, v217, 0, carry); BignumADC(v227, carry, v218, 0, carry); BignumADC(v228, carry, v219, 0, carry); v229 = v220 + 0 + carry; r->w[0] = v221; r->w[1] = v222; r->w[2] = v223; r->w[3] = v224; r->w[4] = v225; r->w[5] = v226; r->w[6] = v227; r->w[7] = v228; r->w[8] = v229; } static void bigval_final_reduce(bigval *n) { BignumInt v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v13, v14, v15; BignumInt v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28; BignumInt v29, v30, v31, v32, v34, v35, v36, v37, v38, v39, v40, v41, v42; BignumInt v43; BignumCarry carry; v0 = n->w[0]; v1 = n->w[1]; v2 = n->w[2]; v3 = n->w[3]; v4 = n->w[4]; v5 = n->w[5]; v6 = n->w[6]; v7 = n->w[7]; v8 = n->w[8]; v9 = (v8) >> 2; v10 = (v8) & ((((BignumInt)1) << 2)-1); v11 = 5 * v9; BignumADC(v13, carry, v0, v11, 0); BignumADC(v14, carry, v1, 0, carry); BignumADC(v15, carry, v2, 0, carry); BignumADC(v16, carry, v3, 0, carry); BignumADC(v17, carry, v4, 0, carry); BignumADC(v18, carry, v5, 0, carry); BignumADC(v19, carry, v6, 0, carry); BignumADC(v20, carry, v7, 0, carry); v21 = v10 + 0 + carry; BignumADC(v22, carry, v13, 5, 0); (void)v22; BignumADC(v23, carry, v14, 0, carry); (void)v23; BignumADC(v24, carry, v15, 0, carry); (void)v24; BignumADC(v25, carry, v16, 0, carry); (void)v25; BignumADC(v26, carry, v17, 0, carry); (void)v26; BignumADC(v27, carry, v18, 0, carry); (void)v27; BignumADC(v28, carry, v19, 0, carry); (void)v28; BignumADC(v29, carry, v20, 0, carry); (void)v29; v30 = v21 + 0 + carry; v31 = (v30) >> 2; v32 = 5 * v31; BignumADC(v34, carry, v13, v32, 0); BignumADC(v35, carry, v14, 0, carry); BignumADC(v36, carry, v15, 0, carry); BignumADC(v37, carry, v16, 0, carry); BignumADC(v38, carry, v17, 0, carry); BignumADC(v39, carry, v18, 0, carry); BignumADC(v40, carry, v19, 0, carry); BignumADC(v41, carry, v20, 0, carry); v42 = v21 + 0 + carry; v43 = (v42) & ((((BignumInt)1) << 2)-1); n->w[0] = v34; n->w[1] = v35; n->w[2] = v36; n->w[3] = v37; n->w[4] = v38; n->w[5] = v39; n->w[6] = v40; n->w[7] = v41; n->w[8] = v43; } #elif BIGNUM_INT_BITS == 32 static void bigval_add(bigval *r, const bigval *a, const bigval *b) { BignumInt v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14; BignumCarry carry; v0 = a->w[0]; v1 = a->w[1]; v2 = a->w[2]; v3 = a->w[3]; v4 = a->w[4]; v5 = b->w[0]; v6 = b->w[1]; v7 = b->w[2]; v8 = b->w[3]; v9 = b->w[4]; BignumADC(v10, carry, v0, v5, 0); BignumADC(v11, carry, v1, v6, carry); BignumADC(v12, carry, v2, v7, carry); BignumADC(v13, carry, v3, v8, carry); v14 = v4 + v9 + carry; r->w[0] = v10; r->w[1] = v11; r->w[2] = v12; r->w[3] = v13; r->w[4] = v14; } static void bigval_mul_mod_p(bigval *r, const bigval *a, const bigval *b) { BignumInt v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14; BignumInt v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27; BignumInt v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40; BignumInt v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53; BignumInt v54, v55, v56, v57, v58, v60, v61, v62, v63, v64, v65, v66, v67; BignumInt v68, v69, v70, v71, v72, v73, v74, v75, v76, v78, v80, v81, v82; BignumInt v83, v84, v85, v86, v87, v88, v89; BignumCarry carry; v0 = a->w[0]; v1 = a->w[1]; v2 = a->w[2]; v3 = a->w[3]; v4 = a->w[4]; v5 = b->w[0]; v6 = b->w[1]; v7 = b->w[2]; v8 = b->w[3]; v9 = b->w[4]; BignumMUL(v11, v10, v0, v5); BignumMULADD(v13, v12, v0, v6, v11); BignumMULADD(v15, v14, v0, v7, v13); BignumMULADD(v17, v16, v0, v8, v15); BignumMULADD(v19, v18, v0, v9, v17); BignumMULADD(v21, v20, v1, v5, v12); BignumMULADD2(v23, v22, v1, v6, v14, v21); BignumMULADD2(v25, v24, v1, v7, v16, v23); BignumMULADD2(v27, v26, v1, v8, v18, v25); BignumMULADD2(v29, v28, v1, v9, v19, v27); BignumMULADD(v31, v30, v2, v5, v22); BignumMULADD2(v33, v32, v2, v6, v24, v31); BignumMULADD2(v35, v34, v2, v7, v26, v33); BignumMULADD2(v37, v36, v2, v8, v28, v35); BignumMULADD2(v39, v38, v2, v9, v29, v37); BignumMULADD(v41, v40, v3, v5, v32); BignumMULADD2(v43, v42, v3, v6, v34, v41); BignumMULADD2(v45, v44, v3, v7, v36, v43); BignumMULADD2(v47, v46, v3, v8, v38, v45); BignumMULADD2(v49, v48, v3, v9, v39, v47); BignumMULADD(v51, v50, v4, v5, v42); BignumMULADD2(v53, v52, v4, v6, v44, v51); BignumMULADD2(v55, v54, v4, v7, v46, v53); BignumMULADD2(v57, v56, v4, v8, v48, v55); v58 = v4 * v9 + v49 + v57; v60 = (v50) & ((((BignumInt)1) << 2)-1); v61 = ((v50) >> 2) | ((v52) << 30); v62 = ((v52) >> 2) | ((v54) << 30); v63 = ((v54) >> 2) | ((v56) << 30); v64 = ((v56) >> 2) | ((v58) << 30); v65 = (v58) >> 2; v66 = (v65) & ((((BignumInt)1) << 2)-1); v67 = (v58) >> 4; BignumMUL(v69, v68, 5, v61); BignumMULADD(v71, v70, 5, v62, v69); BignumMULADD(v73, v72, 5, v63, v71); BignumMULADD(v75, v74, 5, v64, v73); v76 = 5 * v66 + v75; v78 = 25 * v67; BignumADC(v80, carry, v10, v68, 0); BignumADC(v81, carry, v20, v70, carry); BignumADC(v82, carry, v30, v72, carry); BignumADC(v83, carry, v40, v74, carry); v84 = v60 + v76 + carry; BignumADC(v85, carry, v80, v78, 0); BignumADC(v86, carry, v81, 0, carry); BignumADC(v87, carry, v82, 0, carry); BignumADC(v88, carry, v83, 0, carry); v89 = v84 + 0 + carry; r->w[0] = v85; r->w[1] = v86; r->w[2] = v87; r->w[3] = v88; r->w[4] = v89; } static void bigval_final_reduce(bigval *n) { BignumInt v0, v1, v2, v3, v4, v5, v6, v7, v9, v10, v11, v12, v13, v14; BignumInt v15, v16, v17, v18, v19, v20, v22, v23, v24, v25, v26, v27; BignumCarry carry; v0 = n->w[0]; v1 = n->w[1]; v2 = n->w[2]; v3 = n->w[3]; v4 = n->w[4]; v5 = (v4) >> 2; v6 = (v4) & ((((BignumInt)1) << 2)-1); v7 = 5 * v5; BignumADC(v9, carry, v0, v7, 0); BignumADC(v10, carry, v1, 0, carry); BignumADC(v11, carry, v2, 0, carry); BignumADC(v12, carry, v3, 0, carry); v13 = v6 + 0 + carry; BignumADC(v14, carry, v9, 5, 0); (void)v14; BignumADC(v15, carry, v10, 0, carry); (void)v15; BignumADC(v16, carry, v11, 0, carry); (void)v16; BignumADC(v17, carry, v12, 0, carry); (void)v17; v18 = v13 + 0 + carry; v19 = (v18) >> 2; v20 = 5 * v19; BignumADC(v22, carry, v9, v20, 0); BignumADC(v23, carry, v10, 0, carry); BignumADC(v24, carry, v11, 0, carry); BignumADC(v25, carry, v12, 0, carry); v26 = v13 + 0 + carry; v27 = (v26) & ((((BignumInt)1) << 2)-1); n->w[0] = v22; n->w[1] = v23; n->w[2] = v24; n->w[3] = v25; n->w[4] = v27; } #elif BIGNUM_INT_BITS == 64 static void bigval_add(bigval *r, const bigval *a, const bigval *b) { BignumInt v0, v1, v2, v3, v4, v5, v6, v7, v8; BignumCarry carry; v0 = a->w[0]; v1 = a->w[1]; v2 = a->w[2]; v3 = b->w[0]; v4 = b->w[1]; v5 = b->w[2]; BignumADC(v6, carry, v0, v3, 0); BignumADC(v7, carry, v1, v4, carry); v8 = v2 + v5 + carry; r->w[0] = v6; r->w[1] = v7; r->w[2] = v8; } static void bigval_mul_mod_p(bigval *r, const bigval *a, const bigval *b) { BignumInt v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14; BignumInt v15, v16, v17, v18, v19, v20, v21, v22, v24, v25, v26, v27, v28; BignumInt v29, v30, v31, v32, v33, v34, v36, v38, v39, v40, v41, v42, v43; BignumCarry carry; v0 = a->w[0]; v1 = a->w[1]; v2 = a->w[2]; v3 = b->w[0]; v4 = b->w[1]; v5 = b->w[2]; BignumMUL(v7, v6, v0, v3); BignumMULADD(v9, v8, v0, v4, v7); BignumMULADD(v11, v10, v0, v5, v9); BignumMULADD(v13, v12, v1, v3, v8); BignumMULADD2(v15, v14, v1, v4, v10, v13); BignumMULADD2(v17, v16, v1, v5, v11, v15); BignumMULADD(v19, v18, v2, v3, v14); BignumMULADD2(v21, v20, v2, v4, v16, v19); v22 = v2 * v5 + v17 + v21; v24 = (v18) & ((((BignumInt)1) << 2)-1); v25 = ((v18) >> 2) | ((v20) << 62); v26 = ((v20) >> 2) | ((v22) << 62); v27 = (v22) >> 2; v28 = (v27) & ((((BignumInt)1) << 2)-1); v29 = (v22) >> 4; BignumMUL(v31, v30, 5, v25); BignumMULADD(v33, v32, 5, v26, v31); v34 = 5 * v28 + v33; v36 = 25 * v29; BignumADC(v38, carry, v6, v30, 0); BignumADC(v39, carry, v12, v32, carry); v40 = v24 + v34 + carry; BignumADC(v41, carry, v38, v36, 0); BignumADC(v42, carry, v39, 0, carry); v43 = v40 + 0 + carry; r->w[0] = v41; r->w[1] = v42; r->w[2] = v43; } static void bigval_final_reduce(bigval *n) { BignumInt v0, v1, v2, v3, v4, v5, v7, v8, v9, v10, v11, v12, v13, v14; BignumInt v16, v17, v18, v19; BignumCarry carry; v0 = n->w[0]; v1 = n->w[1]; v2 = n->w[2]; v3 = (v2) >> 2; v4 = (v2) & ((((BignumInt)1) << 2)-1); v5 = 5 * v3; BignumADC(v7, carry, v0, v5, 0); BignumADC(v8, carry, v1, 0, carry); v9 = v4 + 0 + carry; BignumADC(v10, carry, v7, 5, 0); (void)v10; BignumADC(v11, carry, v8, 0, carry); (void)v11; v12 = v9 + 0 + carry; v13 = (v12) >> 2; v14 = 5 * v13; BignumADC(v16, carry, v7, v14, 0); BignumADC(v17, carry, v8, 0, carry); v18 = v9 + 0 + carry; v19 = (v18) & ((((BignumInt)1) << 2)-1); n->w[0] = v16; n->w[1] = v17; n->w[2] = v19; } #else #error Add another bit count to contrib/make1305.py and rerun it #endif struct poly1305 { unsigned char nonce[16]; bigval r; bigval h; /* Buffer in case we get less that a multiple of 16 bytes */ unsigned char buffer[16]; int bufferIndex; }; static void poly1305_init(struct poly1305 *ctx) { memset(ctx->nonce, 0, 16); ctx->bufferIndex = 0; bigval_clear(&ctx->h); } static void poly1305_key(struct poly1305 *ctx, ptrlen key) { assert(key.len == 32); /* Takes a 256 bit key */ unsigned char key_copy[16]; memcpy(key_copy, key.ptr, 16); /* Key the MAC itself * bytes 4, 8, 12 and 16 are required to have their top four bits clear */ key_copy[3] &= 0x0f; key_copy[7] &= 0x0f; key_copy[11] &= 0x0f; key_copy[15] &= 0x0f; /* bytes 5, 9 and 13 are required to have their bottom two bits clear */ key_copy[4] &= 0xfc; key_copy[8] &= 0xfc; key_copy[12] &= 0xfc; bigval_import_le(&ctx->r, key_copy, 16); smemclr(key_copy, sizeof(key_copy)); /* Use second 128 bits as the nonce */ memcpy(ctx->nonce, (const char *)key.ptr + 16, 16); } /* Feed up to 16 bytes (should only be less for the last chunk) */ static void poly1305_feed_chunk(struct poly1305 *ctx, const unsigned char *chunk, int len) { bigval c; bigval_import_le(&c, chunk, len); c.w[len / BIGNUM_INT_BYTES] |= (BignumInt)1 << (8 * (len % BIGNUM_INT_BYTES)); bigval_add(&c, &c, &ctx->h); bigval_mul_mod_p(&ctx->h, &c, &ctx->r); } static void poly1305_feed(struct poly1305 *ctx, const unsigned char *buf, int len) { /* Check for stuff left in the buffer from last time */ if (ctx->bufferIndex) { /* Try to fill up to 16 */ while (ctx->bufferIndex < 16 && len) { ctx->buffer[ctx->bufferIndex++] = *buf++; --len; } if (ctx->bufferIndex == 16) { poly1305_feed_chunk(ctx, ctx->buffer, 16); ctx->bufferIndex = 0; } } /* Process 16 byte whole chunks */ while (len >= 16) { poly1305_feed_chunk(ctx, buf, 16); len -= 16; buf += 16; } /* Cache stuff that's left over */ if (len) { memcpy(ctx->buffer, buf, len); ctx->bufferIndex = len; } } /* Finalise and populate buffer with 16 byte with MAC */ static void poly1305_finalise(struct poly1305 *ctx, unsigned char *mac) { bigval tmp; if (ctx->bufferIndex) { poly1305_feed_chunk(ctx, ctx->buffer, ctx->bufferIndex); } bigval_import_le(&tmp, ctx->nonce, 16); bigval_final_reduce(&ctx->h); bigval_add(&tmp, &tmp, &ctx->h); bigval_export_le(&tmp, mac, 16); } /* SSH-2 wrapper */ struct ccp_context { struct chacha20 a_cipher; /* Used for length */ struct chacha20 b_cipher; /* Used for content */ /* Cache of the first 4 bytes because they are the sequence number */ /* Kept in 8 bytes with the top as zero to allow easy passing to setiv */ int mac_initialised; /* Where we have got to in filling mac_iv */ unsigned char mac_iv[8]; struct poly1305 mac; BinarySink_IMPLEMENTATION; ssh_cipher ciph; ssh2_mac mac_if; }; static ssh2_mac *poly_ssh2_new( const ssh2_macalg *alg, ssh_cipher *cipher) { struct ccp_context *ctx = container_of(cipher, struct ccp_context, ciph); ctx->mac_if.vt = alg; BinarySink_DELEGATE_INIT(&ctx->mac_if, ctx); return &ctx->mac_if; } static void poly_ssh2_free(ssh2_mac *mac) { /* Not allocated, just forwarded, no need to free */ } static void poly_setkey(ssh2_mac *mac, ptrlen key) { /* Uses the same context as ChaCha20, so ignore */ } static void poly_start(ssh2_mac *mac) { struct ccp_context *ctx = container_of(mac, struct ccp_context, mac_if); ctx->mac_initialised = 0; memset(ctx->mac_iv, 0, 8); poly1305_init(&ctx->mac); } static void poly_BinarySink_write(BinarySink *bs, const void *blkv, size_t len) { struct ccp_context *ctx = BinarySink_DOWNCAST(bs, struct ccp_context); const unsigned char *blk = (const unsigned char *)blkv; /* First 4 bytes are the IV */ while (ctx->mac_initialised < 4 && len) { ctx->mac_iv[7 - ctx->mac_initialised] = *blk++; ++ctx->mac_initialised; --len; } /* Initialise the IV if needed */ if (ctx->mac_initialised == 4) { chacha20_iv(&ctx->b_cipher, ctx->mac_iv); ++ctx->mac_initialised; /* Don't do it again */ /* Do first rotation */ chacha20_round(&ctx->b_cipher); /* Set the poly key */ poly1305_key(&ctx->mac, make_ptrlen(ctx->b_cipher.current, 32)); /* Set the first round as used */ ctx->b_cipher.currentIndex = 64; } /* Update the MAC with anything left */ if (len) { poly1305_feed(&ctx->mac, blk, len); } } static void poly_genresult(ssh2_mac *mac, unsigned char *blk) { struct ccp_context *ctx = container_of(mac, struct ccp_context, mac_if); poly1305_finalise(&ctx->mac, blk); } static const char *poly_text_name(ssh2_mac *mac) { return "Poly1305"; } const ssh2_macalg ssh2_poly1305 = { .new = poly_ssh2_new, .free = poly_ssh2_free, .setkey = poly_setkey, .start = poly_start, .genresult = poly_genresult, .text_name = poly_text_name, .name = "", .etm_name = "", /* Not selectable individually, just part of * ChaCha20-Poly1305 */ .len = 16, .keylen = 0, }; static ssh_cipher *ccp_new(const ssh_cipheralg *alg) { struct ccp_context *ctx = snew(struct ccp_context); BinarySink_INIT(ctx, poly_BinarySink_write); poly1305_init(&ctx->mac); ctx->ciph.vt = alg; return &ctx->ciph; } static void ccp_free(ssh_cipher *cipher) { struct ccp_context *ctx = container_of(cipher, struct ccp_context, ciph); smemclr(&ctx->a_cipher, sizeof(ctx->a_cipher)); smemclr(&ctx->b_cipher, sizeof(ctx->b_cipher)); smemclr(&ctx->mac, sizeof(ctx->mac)); sfree(ctx); } static void ccp_iv(ssh_cipher *cipher, const void *iv) { /* struct ccp_context *ctx = container_of(cipher, struct ccp_context, ciph); */ /* IV is set based on the sequence number */ } static void ccp_key(ssh_cipher *cipher, const void *vkey) { const unsigned char *key = (const unsigned char *)vkey; struct ccp_context *ctx = container_of(cipher, struct ccp_context, ciph); /* Initialise the a_cipher (for decrypting lengths) with the first 256 bits */ chacha20_key(&ctx->a_cipher, key + 32); /* Initialise the b_cipher (for content and MAC) with the second 256 bits */ chacha20_key(&ctx->b_cipher, key); } static void ccp_encrypt(ssh_cipher *cipher, void *blk, int len) { struct ccp_context *ctx = container_of(cipher, struct ccp_context, ciph); chacha20_encrypt(&ctx->b_cipher, blk, len); } static void ccp_decrypt(ssh_cipher *cipher, void *blk, int len) { struct ccp_context *ctx = container_of(cipher, struct ccp_context, ciph); chacha20_decrypt(&ctx->b_cipher, blk, len); } static void ccp_length_op(struct ccp_context *ctx, void *blk, int len, unsigned long seq) { unsigned char iv[8]; /* * According to RFC 4253 (section 6.4), the packet sequence number wraps * at 2^32, so its 32 high-order bits will always be zero. */ PUT_32BIT_LSB_FIRST(iv, 0); PUT_32BIT_LSB_FIRST(iv + 4, seq); chacha20_iv(&ctx->a_cipher, iv); chacha20_iv(&ctx->b_cipher, iv); /* Reset content block count to 1, as the first is the key for Poly1305 */ ++ctx->b_cipher.state[12]; smemclr(iv, sizeof(iv)); } static void ccp_encrypt_length(ssh_cipher *cipher, void *blk, int len, unsigned long seq) { struct ccp_context *ctx = container_of(cipher, struct ccp_context, ciph); ccp_length_op(ctx, blk, len, seq); chacha20_encrypt(&ctx->a_cipher, blk, len); } static void ccp_decrypt_length(ssh_cipher *cipher, void *blk, int len, unsigned long seq) { struct ccp_context *ctx = container_of(cipher, struct ccp_context, ciph); ccp_length_op(ctx, blk, len, seq); chacha20_decrypt(&ctx->a_cipher, blk, len); } const ssh_cipheralg ssh2_chacha20_poly1305 = { .new = ccp_new, .free = ccp_free, .setiv = ccp_iv, .setkey = ccp_key, .encrypt = ccp_encrypt, .decrypt = ccp_decrypt, .encrypt_length = ccp_encrypt_length, .decrypt_length = ccp_decrypt_length, .ssh2_id = "chacha20-poly1305@openssh.com", .blksize = 1, .real_keybits = 512, .padded_keybytes = 64, .flags = SSH_CIPHER_SEPARATE_LENGTH, .text_name = "ChaCha20", .required_mac = &ssh2_poly1305, }; static const ssh_cipheralg *const ccp_list[] = { &ssh2_chacha20_poly1305 }; const ssh2_ciphers ssh2_ccp = { lenof(ccp_list), ccp_list }; putty-0.76/sshchan.h0000644000175000017500000003407714072266312011372 00000000000000/* * Abstraction of the various ways to handle the local end of an SSH * connection-layer channel. */ #ifndef PUTTY_SSHCHAN_H #define PUTTY_SSHCHAN_H typedef struct ChannelVtable ChannelVtable; struct ChannelVtable { void (*free)(Channel *); /* Called for channel types that were created at the same time as * we sent an outgoing CHANNEL_OPEN, when the confirmation comes * back from the server indicating that the channel has been * opened, or the failure message indicating that it hasn't, * respectively. In the latter case, this must _not_ free the * Channel structure - the client will call the free method * separately. But it might do logging or other local cleanup. */ void (*open_confirmation)(Channel *); void (*open_failed)(Channel *, const char *error_text); size_t (*send)(Channel *, bool is_stderr, const void *buf, size_t len); void (*send_eof)(Channel *); void (*set_input_wanted)(Channel *, bool wanted); char *(*log_close_msg)(Channel *); bool (*want_close)(Channel *, bool sent_local_eof, bool rcvd_remote_eof); /* A method for every channel request we know of. All of these * return true for success or false for failure. */ bool (*rcvd_exit_status)(Channel *, int status); bool (*rcvd_exit_signal)( Channel *chan, ptrlen signame, bool core_dumped, ptrlen msg); bool (*rcvd_exit_signal_numeric)( Channel *chan, int signum, bool core_dumped, ptrlen msg); bool (*run_shell)(Channel *chan); bool (*run_command)(Channel *chan, ptrlen command); bool (*run_subsystem)(Channel *chan, ptrlen subsys); bool (*enable_x11_forwarding)( Channel *chan, bool oneshot, ptrlen authproto, ptrlen authdata, unsigned screen_number); bool (*enable_agent_forwarding)(Channel *chan); bool (*allocate_pty)( Channel *chan, ptrlen termtype, unsigned width, unsigned height, unsigned pixwidth, unsigned pixheight, struct ssh_ttymodes modes); bool (*set_env)(Channel *chan, ptrlen var, ptrlen value); bool (*send_break)(Channel *chan, unsigned length); bool (*send_signal)(Channel *chan, ptrlen signame); bool (*change_window_size)( Channel *chan, unsigned width, unsigned height, unsigned pixwidth, unsigned pixheight); /* A method for signalling success/failure responses to channel * requests initiated from the SshChannel vtable with want_reply * true. */ void (*request_response)(Channel *, bool success); }; struct Channel { const struct ChannelVtable *vt; unsigned initial_fixed_window_size; }; static inline void chan_free(Channel *ch) { ch->vt->free(ch); } static inline void chan_open_confirmation(Channel *ch) { ch->vt->open_confirmation(ch); } static inline void chan_open_failed(Channel *ch, const char *err) { ch->vt->open_failed(ch, err); } static inline size_t chan_send( Channel *ch, bool err, const void *buf, size_t len) { return ch->vt->send(ch, err, buf, len); } static inline void chan_send_eof(Channel *ch) { ch->vt->send_eof(ch); } static inline void chan_set_input_wanted(Channel *ch, bool wanted) { ch->vt->set_input_wanted(ch, wanted); } static inline char *chan_log_close_msg(Channel *ch) { return ch->vt->log_close_msg(ch); } static inline bool chan_want_close(Channel *ch, bool leof, bool reof) { return ch->vt->want_close(ch, leof, reof); } static inline bool chan_rcvd_exit_status(Channel *ch, int status) { return ch->vt->rcvd_exit_status(ch, status); } static inline bool chan_rcvd_exit_signal( Channel *ch, ptrlen sig, bool core, ptrlen msg) { return ch->vt->rcvd_exit_signal(ch, sig, core, msg); } static inline bool chan_rcvd_exit_signal_numeric( Channel *ch, int sig, bool core, ptrlen msg) { return ch->vt->rcvd_exit_signal_numeric(ch, sig, core, msg); } static inline bool chan_run_shell(Channel *ch) { return ch->vt->run_shell(ch); } static inline bool chan_run_command(Channel *ch, ptrlen cmd) { return ch->vt->run_command(ch, cmd); } static inline bool chan_run_subsystem(Channel *ch, ptrlen subsys) { return ch->vt->run_subsystem(ch, subsys); } static inline bool chan_enable_x11_forwarding( Channel *ch, bool oneshot, ptrlen ap, ptrlen ad, unsigned scr) { return ch->vt->enable_x11_forwarding(ch, oneshot, ap, ad, scr); } static inline bool chan_enable_agent_forwarding(Channel *ch) { return ch->vt->enable_agent_forwarding(ch); } static inline bool chan_allocate_pty( Channel *ch, ptrlen termtype, unsigned w, unsigned h, unsigned pw, unsigned ph, struct ssh_ttymodes modes) { return ch->vt->allocate_pty(ch, termtype, w, h, pw, ph, modes); } static inline bool chan_set_env(Channel *ch, ptrlen var, ptrlen value) { return ch->vt->set_env(ch, var, value); } static inline bool chan_send_break(Channel *ch, unsigned length) { return ch->vt->send_break(ch, length); } static inline bool chan_send_signal(Channel *ch, ptrlen signame) { return ch->vt->send_signal(ch, signame); } static inline bool chan_change_window_size( Channel *ch, unsigned w, unsigned h, unsigned pw, unsigned ph) { return ch->vt->change_window_size(ch, w, h, pw, ph); } static inline void chan_request_response(Channel *ch, bool success) { ch->vt->request_response(ch, success); } /* * Reusable methods you can put in vtables to give default handling of * some of those functions. */ /* open_confirmation / open_failed for any channel it doesn't apply to */ void chan_remotely_opened_confirmation(Channel *chan); void chan_remotely_opened_failure(Channel *chan, const char *errtext); /* want_close for any channel that wants the default behaviour of not * closing until both directions have had an EOF */ bool chan_default_want_close(Channel *, bool, bool); /* default implementations that refuse all the channel requests */ bool chan_no_exit_status(Channel *, int); bool chan_no_exit_signal(Channel *, ptrlen, bool, ptrlen); bool chan_no_exit_signal_numeric(Channel *, int, bool, ptrlen); bool chan_no_run_shell(Channel *chan); bool chan_no_run_command(Channel *chan, ptrlen command); bool chan_no_run_subsystem(Channel *chan, ptrlen subsys); bool chan_no_enable_x11_forwarding( Channel *chan, bool oneshot, ptrlen authproto, ptrlen authdata, unsigned screen_number); bool chan_no_enable_agent_forwarding(Channel *chan); bool chan_no_allocate_pty( Channel *chan, ptrlen termtype, unsigned width, unsigned height, unsigned pixwidth, unsigned pixheight, struct ssh_ttymodes modes); bool chan_no_set_env(Channel *chan, ptrlen var, ptrlen value); bool chan_no_send_break(Channel *chan, unsigned length); bool chan_no_send_signal(Channel *chan, ptrlen signame); bool chan_no_change_window_size( Channel *chan, unsigned width, unsigned height, unsigned pixwidth, unsigned pixheight); /* default implementation that never expects to receive a response */ void chan_no_request_response(Channel *, bool); /* * Constructor for a trivial do-nothing implementation of * ChannelVtable. Used for 'zombie' channels, i.e. channels whose * proper local source of data has been shut down or otherwise stopped * existing, but the SSH side is still there and needs some kind of a * Channel implementation to talk to. In particular, the want_close * method for this channel always returns 'yes, please close this * channel asap', regardless of whether local and/or remote EOF have * been sent - indeed, even if _neither_ has. */ Channel *zombiechan_new(void); /* ---------------------------------------------------------------------- * This structure is owned by an SSH connection layer, and identifies * the connection layer's end of the channel, for the Channel * implementation to talk back to. */ typedef struct SshChannelVtable SshChannelVtable; struct SshChannelVtable { size_t (*write)(SshChannel *c, bool is_stderr, const void *, size_t); void (*write_eof)(SshChannel *c); void (*initiate_close)(SshChannel *c, const char *err); void (*unthrottle)(SshChannel *c, size_t bufsize); Conf *(*get_conf)(SshChannel *c); void (*window_override_removed)(SshChannel *c); void (*x11_sharing_handover)(SshChannel *c, ssh_sharing_connstate *share_cs, share_channel *share_chan, const char *peer_addr, int peer_port, int endian, int protomajor, int protominor, const void *initial_data, int initial_len); /* * All the outgoing channel requests we support. Each one has a * want_reply flag, which will cause a callback to * chan_request_response when the result is available. * * The ones that return 'bool' use it to indicate that the SSH * protocol in use doesn't support this request at all. * * (It's also intentional that not all of them have a want_reply * flag: the ones that don't are because SSH-1 has no method for * signalling success or failure of that request, or because we * wouldn't do anything usefully different with the reply in any * case.) */ void (*send_exit_status)(SshChannel *c, int status); void (*send_exit_signal)( SshChannel *c, ptrlen signame, bool core_dumped, ptrlen msg); void (*send_exit_signal_numeric)( SshChannel *c, int signum, bool core_dumped, ptrlen msg); void (*request_x11_forwarding)( SshChannel *c, bool want_reply, const char *authproto, const char *authdata, int screen_number, bool oneshot); void (*request_agent_forwarding)( SshChannel *c, bool want_reply); void (*request_pty)( SshChannel *c, bool want_reply, Conf *conf, int w, int h); bool (*send_env_var)( SshChannel *c, bool want_reply, const char *var, const char *value); void (*start_shell)( SshChannel *c, bool want_reply); void (*start_command)( SshChannel *c, bool want_reply, const char *command); bool (*start_subsystem)( SshChannel *c, bool want_reply, const char *subsystem); bool (*send_serial_break)( SshChannel *c, bool want_reply, int length); /* length=0 for default */ bool (*send_signal)( SshChannel *c, bool want_reply, const char *signame); void (*send_terminal_size_change)( SshChannel *c, int w, int h); void (*hint_channel_is_simple)(SshChannel *c); }; struct SshChannel { const struct SshChannelVtable *vt; ConnectionLayer *cl; }; static inline size_t sshfwd_write_ext( SshChannel *c, bool is_stderr, const void *data, size_t len) { return c->vt->write(c, is_stderr, data, len); } static inline size_t sshfwd_write(SshChannel *c, const void *data, size_t len) { return sshfwd_write_ext(c, false, data, len); } static inline void sshfwd_write_eof(SshChannel *c) { c->vt->write_eof(c); } static inline void sshfwd_initiate_close(SshChannel *c, const char *err) { c->vt->initiate_close(c, err); } static inline void sshfwd_unthrottle(SshChannel *c, size_t bufsize) { c->vt->unthrottle(c, bufsize); } static inline Conf *sshfwd_get_conf(SshChannel *c) { return c->vt->get_conf(c); } static inline void sshfwd_window_override_removed(SshChannel *c) { c->vt->window_override_removed(c); } static inline void sshfwd_x11_sharing_handover( SshChannel *c, ssh_sharing_connstate *cs, share_channel *sch, const char *addr, int port, int endian, int maj, int min, const void *idata, int ilen) { c->vt->x11_sharing_handover(c, cs, sch, addr, port, endian, maj, min, idata, ilen); } static inline void sshfwd_send_exit_status(SshChannel *c, int status) { c->vt->send_exit_status(c, status); } static inline void sshfwd_send_exit_signal( SshChannel *c, ptrlen signame, bool core_dumped, ptrlen msg) { c->vt->send_exit_signal(c, signame, core_dumped, msg); } static inline void sshfwd_send_exit_signal_numeric( SshChannel *c, int signum, bool core_dumped, ptrlen msg) { c->vt->send_exit_signal_numeric(c, signum, core_dumped, msg); } static inline void sshfwd_request_x11_forwarding( SshChannel *c, bool want_reply, const char *proto, const char *data, int scr, bool once) { c->vt->request_x11_forwarding(c, want_reply, proto, data, scr, once); } static inline void sshfwd_request_agent_forwarding( SshChannel *c, bool want_reply) { c->vt->request_agent_forwarding(c, want_reply); } static inline void sshfwd_request_pty( SshChannel *c, bool want_reply, Conf *conf, int w, int h) { c->vt->request_pty(c, want_reply, conf, w, h); } static inline bool sshfwd_send_env_var( SshChannel *c, bool want_reply, const char *var, const char *value) { return c->vt->send_env_var(c, want_reply, var, value); } static inline void sshfwd_start_shell( SshChannel *c, bool want_reply) { c->vt->start_shell(c, want_reply); } static inline void sshfwd_start_command( SshChannel *c, bool want_reply, const char *command) { c->vt->start_command(c, want_reply, command); } static inline bool sshfwd_start_subsystem( SshChannel *c, bool want_reply, const char *subsystem) { return c->vt->start_subsystem(c, want_reply, subsystem); } static inline bool sshfwd_send_serial_break( SshChannel *c, bool want_reply, int length) { return c->vt->send_serial_break(c, want_reply, length); } static inline bool sshfwd_send_signal( SshChannel *c, bool want_reply, const char *signame) { return c->vt->send_signal(c, want_reply, signame); } static inline void sshfwd_send_terminal_size_change( SshChannel *c, int w, int h) { c->vt->send_terminal_size_change(c, w, h); } static inline void sshfwd_hint_channel_is_simple(SshChannel *c) { c->vt->hint_channel_is_simple(c); } /* ---------------------------------------------------------------------- * The 'main' or primary channel of the SSH connection is special, * because it's the one that's connected directly to parts of the * frontend such as the terminal and the specials menu. So it exposes * a richer API. */ mainchan *mainchan_new( PacketProtocolLayer *ppl, ConnectionLayer *cl, Conf *conf, int term_width, int term_height, bool is_simple, SshChannel **sc_out); void mainchan_get_specials( mainchan *mc, add_special_fn_t add_special, void *ctx); void mainchan_special_cmd(mainchan *mc, SessionSpecialCode code, int arg); void mainchan_terminal_size(mainchan *mc, int width, int height); #endif /* PUTTY_SSHCHAN_H */ putty-0.76/sshcommon.c0000644000175000017500000006731214072266312011742 00000000000000/* * Supporting routines used in common by all the various components of * the SSH system. */ #include #include #include "putty.h" #include "mpint.h" #include "ssh.h" #include "sshbpp.h" #include "sshppl.h" #include "sshchan.h" /* ---------------------------------------------------------------------- * Implementation of PacketQueue. */ static void pq_ensure_unlinked(PacketQueueNode *node) { if (node->on_free_queue) { node->next->prev = node->prev; node->prev->next = node->next; } else { assert(!node->next); assert(!node->prev); } } void pq_base_push(PacketQueueBase *pqb, PacketQueueNode *node) { pq_ensure_unlinked(node); node->next = &pqb->end; node->prev = pqb->end.prev; node->next->prev = node; node->prev->next = node; pqb->total_size += node->formal_size; if (pqb->ic) queue_idempotent_callback(pqb->ic); } void pq_base_push_front(PacketQueueBase *pqb, PacketQueueNode *node) { pq_ensure_unlinked(node); node->prev = &pqb->end; node->next = pqb->end.next; node->next->prev = node; node->prev->next = node; pqb->total_size += node->formal_size; if (pqb->ic) queue_idempotent_callback(pqb->ic); } static PacketQueueNode pktin_freeq_head = { &pktin_freeq_head, &pktin_freeq_head, true }; static void pktin_free_queue_callback(void *vctx) { while (pktin_freeq_head.next != &pktin_freeq_head) { PacketQueueNode *node = pktin_freeq_head.next; PktIn *pktin = container_of(node, PktIn, qnode); pktin_freeq_head.next = node->next; sfree(pktin); } pktin_freeq_head.prev = &pktin_freeq_head; } static IdempotentCallback ic_pktin_free = { pktin_free_queue_callback, NULL, false }; static inline void pq_unlink_common(PacketQueueBase *pqb, PacketQueueNode *node) { node->next->prev = node->prev; node->prev->next = node->next; /* Check total_size doesn't drift out of sync downwards, by * ensuring it doesn't underflow when we do this subtraction */ assert(pqb->total_size >= node->formal_size); pqb->total_size -= node->formal_size; /* Check total_size doesn't drift out of sync upwards, by checking * that it's returned to exactly zero whenever a queue is * emptied */ assert(pqb->end.next != &pqb->end || pqb->total_size == 0); } static PktIn *pq_in_after(PacketQueueBase *pqb, PacketQueueNode *prev, bool pop) { PacketQueueNode *node = prev->next; if (node == &pqb->end) return NULL; if (pop) { pq_unlink_common(pqb, node); node->prev = pktin_freeq_head.prev; node->next = &pktin_freeq_head; node->next->prev = node; node->prev->next = node; node->on_free_queue = true; queue_idempotent_callback(&ic_pktin_free); } return container_of(node, PktIn, qnode); } static PktOut *pq_out_after(PacketQueueBase *pqb, PacketQueueNode *prev, bool pop) { PacketQueueNode *node = prev->next; if (node == &pqb->end) return NULL; if (pop) { pq_unlink_common(pqb, node); node->prev = node->next = NULL; } return container_of(node, PktOut, qnode); } void pq_in_init(PktInQueue *pq) { pq->pqb.ic = NULL; pq->pqb.end.next = pq->pqb.end.prev = &pq->pqb.end; pq->after = pq_in_after; pq->pqb.total_size = 0; } void pq_out_init(PktOutQueue *pq) { pq->pqb.ic = NULL; pq->pqb.end.next = pq->pqb.end.prev = &pq->pqb.end; pq->after = pq_out_after; pq->pqb.total_size = 0; } void pq_in_clear(PktInQueue *pq) { PktIn *pkt; pq->pqb.ic = NULL; while ((pkt = pq_pop(pq)) != NULL) { /* No need to actually free these packets: pq_pop on a * PktInQueue will automatically move them to the free * queue. */ } } void pq_out_clear(PktOutQueue *pq) { PktOut *pkt; pq->pqb.ic = NULL; while ((pkt = pq_pop(pq)) != NULL) ssh_free_pktout(pkt); } /* * Concatenate the contents of the two queues q1 and q2, and leave the * result in qdest. qdest must be either empty, or one of the input * queues. */ void pq_base_concatenate(PacketQueueBase *qdest, PacketQueueBase *q1, PacketQueueBase *q2) { struct PacketQueueNode *head1, *tail1, *head2, *tail2; size_t total_size = q1->total_size + q2->total_size; /* * Extract the contents from both input queues, and empty them. */ head1 = (q1->end.next == &q1->end ? NULL : q1->end.next); tail1 = (q1->end.prev == &q1->end ? NULL : q1->end.prev); head2 = (q2->end.next == &q2->end ? NULL : q2->end.next); tail2 = (q2->end.prev == &q2->end ? NULL : q2->end.prev); q1->end.next = q1->end.prev = &q1->end; q2->end.next = q2->end.prev = &q2->end; q1->total_size = q2->total_size = 0; /* * Link the two lists together, handling the case where one or * both is empty. */ if (tail1) tail1->next = head2; else head1 = head2; if (head2) head2->prev = tail1; else tail2 = tail1; /* * Check the destination queue is currently empty. (If it was one * of the input queues, then it will be, because we emptied both * of those just a moment ago.) */ assert(qdest->end.next == &qdest->end); assert(qdest->end.prev == &qdest->end); /* * If our concatenated list has anything in it, then put it in * dest. */ if (!head1) { assert(!tail2); } else { assert(tail2); qdest->end.next = head1; qdest->end.prev = tail2; head1->prev = &qdest->end; tail2->next = &qdest->end; if (qdest->ic) queue_idempotent_callback(qdest->ic); } qdest->total_size = total_size; } /* ---------------------------------------------------------------------- * Low-level functions for the packet structures themselves. */ static void ssh_pkt_BinarySink_write(BinarySink *bs, const void *data, size_t len); PktOut *ssh_new_packet(void) { PktOut *pkt = snew(PktOut); BinarySink_INIT(pkt, ssh_pkt_BinarySink_write); pkt->data = NULL; pkt->length = 0; pkt->maxlen = 0; pkt->downstream_id = 0; pkt->additional_log_text = NULL; pkt->qnode.next = pkt->qnode.prev = NULL; pkt->qnode.on_free_queue = false; return pkt; } static void ssh_pkt_adddata(PktOut *pkt, const void *data, int len) { sgrowarrayn_nm(pkt->data, pkt->maxlen, pkt->length, len); memcpy(pkt->data + pkt->length, data, len); pkt->length += len; pkt->qnode.formal_size = pkt->length; } static void ssh_pkt_BinarySink_write(BinarySink *bs, const void *data, size_t len) { PktOut *pkt = BinarySink_DOWNCAST(bs, PktOut); ssh_pkt_adddata(pkt, data, len); } void ssh_free_pktout(PktOut *pkt) { sfree(pkt->data); sfree(pkt); } /* ---------------------------------------------------------------------- * Implement zombiechan_new() and its trivial vtable. */ static void zombiechan_free(Channel *chan); static size_t zombiechan_send( Channel *chan, bool is_stderr, const void *, size_t); static void zombiechan_set_input_wanted(Channel *chan, bool wanted); static void zombiechan_do_nothing(Channel *chan); static void zombiechan_open_failure(Channel *chan, const char *); static bool zombiechan_want_close(Channel *chan, bool sent_eof, bool rcvd_eof); static char *zombiechan_log_close_msg(Channel *chan) { return NULL; } static const ChannelVtable zombiechan_channelvt = { .free = zombiechan_free, .open_confirmation = zombiechan_do_nothing, .open_failed = zombiechan_open_failure, .send = zombiechan_send, .send_eof = zombiechan_do_nothing, .set_input_wanted = zombiechan_set_input_wanted, .log_close_msg = zombiechan_log_close_msg, .want_close = zombiechan_want_close, .rcvd_exit_status = chan_no_exit_status, .rcvd_exit_signal = chan_no_exit_signal, .rcvd_exit_signal_numeric = chan_no_exit_signal_numeric, .run_shell = chan_no_run_shell, .run_command = chan_no_run_command, .run_subsystem = chan_no_run_subsystem, .enable_x11_forwarding = chan_no_enable_x11_forwarding, .enable_agent_forwarding = chan_no_enable_agent_forwarding, .allocate_pty = chan_no_allocate_pty, .set_env = chan_no_set_env, .send_break = chan_no_send_break, .send_signal = chan_no_send_signal, .change_window_size = chan_no_change_window_size, .request_response = chan_no_request_response, }; Channel *zombiechan_new(void) { Channel *chan = snew(Channel); chan->vt = &zombiechan_channelvt; chan->initial_fixed_window_size = 0; return chan; } static void zombiechan_free(Channel *chan) { assert(chan->vt == &zombiechan_channelvt); sfree(chan); } static void zombiechan_do_nothing(Channel *chan) { assert(chan->vt == &zombiechan_channelvt); } static void zombiechan_open_failure(Channel *chan, const char *errtext) { assert(chan->vt == &zombiechan_channelvt); } static size_t zombiechan_send(Channel *chan, bool is_stderr, const void *data, size_t length) { assert(chan->vt == &zombiechan_channelvt); return 0; } static void zombiechan_set_input_wanted(Channel *chan, bool enable) { assert(chan->vt == &zombiechan_channelvt); } static bool zombiechan_want_close(Channel *chan, bool sent_eof, bool rcvd_eof) { return true; } /* ---------------------------------------------------------------------- * Common routines for handling SSH tty modes. */ static unsigned real_ttymode_opcode(unsigned our_opcode, int ssh_version) { switch (our_opcode) { case TTYMODE_ISPEED: return ssh_version == 1 ? TTYMODE_ISPEED_SSH1 : TTYMODE_ISPEED_SSH2; case TTYMODE_OSPEED: return ssh_version == 1 ? TTYMODE_OSPEED_SSH1 : TTYMODE_OSPEED_SSH2; default: return our_opcode; } } static unsigned our_ttymode_opcode(unsigned real_opcode, int ssh_version) { if (ssh_version == 1) { switch (real_opcode) { case TTYMODE_ISPEED_SSH1: return TTYMODE_ISPEED; case TTYMODE_OSPEED_SSH1: return TTYMODE_OSPEED; default: return real_opcode; } } else { switch (real_opcode) { case TTYMODE_ISPEED_SSH2: return TTYMODE_ISPEED; case TTYMODE_OSPEED_SSH2: return TTYMODE_OSPEED; default: return real_opcode; } } } struct ssh_ttymodes get_ttymodes_from_conf(Seat *seat, Conf *conf) { struct ssh_ttymodes modes; size_t i; static const struct mode_name_type { const char *mode; int opcode; enum { TYPE_CHAR, TYPE_BOOL } type; } modes_names_types[] = { #define TTYMODE_CHAR(name, val, index) { #name, val, TYPE_CHAR }, #define TTYMODE_FLAG(name, val, field, mask) { #name, val, TYPE_BOOL }, #include "sshttymodes.h" #undef TTYMODE_CHAR #undef TTYMODE_FLAG }; memset(&modes, 0, sizeof(modes)); for (i = 0; i < lenof(modes_names_types); i++) { const struct mode_name_type *mode = &modes_names_types[i]; const char *sval = conf_get_str_str(conf, CONF_ttymodes, mode->mode); char *to_free = NULL; if (!sval) sval = "N"; /* just in case */ /* * sval[0] can be * - 'V', indicating that an explicit value follows it; * - 'A', indicating that we should pass the value through from * the local environment via get_ttymode; or * - 'N', indicating that we should explicitly not send this * mode. */ if (sval[0] == 'A') { sval = to_free = seat_get_ttymode(seat, mode->mode); } else if (sval[0] == 'V') { sval++; /* skip the 'V' */ } else { /* else 'N', or something from the future we don't understand */ continue; } if (sval) { /* * Parse the string representation of the tty mode * into the integer value it will take on the wire. */ unsigned ival = 0; switch (mode->type) { case TYPE_CHAR: if (*sval) { char *next = NULL; /* We know ctrlparse won't write to the string, so * casting away const is ugly but allowable. */ ival = ctrlparse((char *)sval, &next); if (!next) ival = sval[0]; } else { ival = 255; /* special value meaning "don't set" */ } break; case TYPE_BOOL: if (stricmp(sval, "yes") == 0 || stricmp(sval, "on") == 0 || stricmp(sval, "true") == 0 || stricmp(sval, "+") == 0) ival = 1; /* true */ else if (stricmp(sval, "no") == 0 || stricmp(sval, "off") == 0 || stricmp(sval, "false") == 0 || stricmp(sval, "-") == 0) ival = 0; /* false */ else ival = (atoi(sval) != 0); break; default: unreachable("Bad mode->type"); } modes.have_mode[mode->opcode] = true; modes.mode_val[mode->opcode] = ival; } sfree(to_free); } { unsigned ospeed, ispeed; /* Unpick the terminal-speed config string. */ ospeed = ispeed = 38400; /* last-resort defaults */ sscanf(conf_get_str(conf, CONF_termspeed), "%u,%u", &ospeed, &ispeed); /* Currently we unconditionally set these */ modes.have_mode[TTYMODE_ISPEED] = true; modes.mode_val[TTYMODE_ISPEED] = ispeed; modes.have_mode[TTYMODE_OSPEED] = true; modes.mode_val[TTYMODE_OSPEED] = ospeed; } return modes; } struct ssh_ttymodes read_ttymodes_from_packet( BinarySource *bs, int ssh_version) { struct ssh_ttymodes modes; memset(&modes, 0, sizeof(modes)); while (1) { unsigned real_opcode, our_opcode; real_opcode = get_byte(bs); if (real_opcode == TTYMODE_END_OF_LIST) break; if (real_opcode >= 160) { /* * RFC 4254 (and the SSH 1.5 spec): "Opcodes 160 to 255 * are not yet defined, and cause parsing to stop (they * should only be used after any other data)." * * My interpretation of this is that if one of these * opcodes appears, it's not a parse _error_, but it is * something that we don't know how to parse even well * enough to step over it to find the next opcode, so we * stop parsing now and assume that the rest of the string * is composed entirely of things we don't understand and * (as usual for unsupported terminal modes) silently * ignore. */ return modes; } our_opcode = our_ttymode_opcode(real_opcode, ssh_version); assert(our_opcode < TTYMODE_LIMIT); modes.have_mode[our_opcode] = true; if (ssh_version == 1 && real_opcode >= 1 && real_opcode <= 127) modes.mode_val[our_opcode] = get_byte(bs); else modes.mode_val[our_opcode] = get_uint32(bs); } return modes; } void write_ttymodes_to_packet(BinarySink *bs, int ssh_version, struct ssh_ttymodes modes) { unsigned i; for (i = 0; i < TTYMODE_LIMIT; i++) { if (modes.have_mode[i]) { unsigned val = modes.mode_val[i]; unsigned opcode = real_ttymode_opcode(i, ssh_version); put_byte(bs, opcode); if (ssh_version == 1 && opcode >= 1 && opcode <= 127) put_byte(bs, val); else put_uint32(bs, val); } } put_byte(bs, TTYMODE_END_OF_LIST); } /* ---------------------------------------------------------------------- * Routine for allocating a new channel ID, given a means of finding * the index field in a given channel structure. */ unsigned alloc_channel_id_general(tree234 *channels, size_t localid_offset) { const unsigned CHANNEL_NUMBER_OFFSET = 256; search234_state ss; /* * First-fit allocation of channel numbers: we always pick the * lowest unused one. * * Every channel before that, and no channel after it, has an ID * exactly equal to its tree index plus CHANNEL_NUMBER_OFFSET. So * we can use the search234 system to identify the length of that * initial sequence, in a single log-time pass down the channels * tree. */ search234_start(&ss, channels); while (ss.element) { unsigned localid = *(unsigned *)((char *)ss.element + localid_offset); if (localid == ss.index + CHANNEL_NUMBER_OFFSET) search234_step(&ss, +1); else search234_step(&ss, -1); } /* * Now ss.index gives exactly the number of channels in that * initial sequence. So adding CHANNEL_NUMBER_OFFSET to it must * give precisely the lowest unused channel number. */ return ss.index + CHANNEL_NUMBER_OFFSET; } /* ---------------------------------------------------------------------- * Functions for handling the comma-separated strings used to store * lists of protocol identifiers in SSH-2. */ void add_to_commasep(strbuf *buf, const char *data) { if (buf->len > 0) put_byte(buf, ','); put_data(buf, data, strlen(data)); } bool get_commasep_word(ptrlen *list, ptrlen *word) { const char *comma; /* * Discard empty list elements, should there be any, because we * never want to return one as if it was a real string. (This * introduces a mild tolerance of badly formatted data in lists we * receive, but I think that's acceptable.) */ while (list->len > 0 && *(const char *)list->ptr == ',') { list->ptr = (const char *)list->ptr + 1; list->len--; } if (!list->len) return false; comma = memchr(list->ptr, ',', list->len); if (!comma) { *word = *list; list->len = 0; } else { size_t wordlen = comma - (const char *)list->ptr; word->ptr = list->ptr; word->len = wordlen; list->ptr = (const char *)list->ptr + wordlen + 1; list->len -= wordlen + 1; } return true; } /* ---------------------------------------------------------------------- * Functions for translating SSH packet type codes into their symbolic * string names. */ #define TRANSLATE_UNIVERSAL(y, name, value) \ if (type == value) return #name; #define TRANSLATE_KEX(y, name, value, ctx) \ if (type == value && pkt_kctx == ctx) return #name; #define TRANSLATE_AUTH(y, name, value, ctx) \ if (type == value && pkt_actx == ctx) return #name; const char *ssh1_pkt_type(int type) { SSH1_MESSAGE_TYPES(TRANSLATE_UNIVERSAL, y); return "unknown"; } const char *ssh2_pkt_type(Pkt_KCtx pkt_kctx, Pkt_ACtx pkt_actx, int type) { SSH2_MESSAGE_TYPES(TRANSLATE_UNIVERSAL, TRANSLATE_KEX, TRANSLATE_AUTH, y); return "unknown"; } #undef TRANSLATE_UNIVERSAL #undef TRANSLATE_KEX #undef TRANSLATE_AUTH /* ---------------------------------------------------------------------- * Common helper function for clients and implementations of * PacketProtocolLayer. */ void ssh_ppl_replace(PacketProtocolLayer *old, PacketProtocolLayer *new) { new->bpp = old->bpp; ssh_ppl_setup_queues(new, old->in_pq, old->out_pq); new->selfptr = old->selfptr; new->user_input = old->user_input; new->seat = old->seat; new->ssh = old->ssh; *new->selfptr = new; ssh_ppl_free(old); /* The new layer might need to be the first one that sends a * packet, so trigger a call to its main coroutine immediately. If * it doesn't need to go first, the worst that will do is return * straight away. */ queue_idempotent_callback(&new->ic_process_queue); } void ssh_ppl_free(PacketProtocolLayer *ppl) { delete_callbacks_for_context(ppl); ppl->vt->free(ppl); } static void ssh_ppl_ic_process_queue_callback(void *context) { PacketProtocolLayer *ppl = (PacketProtocolLayer *)context; ssh_ppl_process_queue(ppl); } void ssh_ppl_setup_queues(PacketProtocolLayer *ppl, PktInQueue *inq, PktOutQueue *outq) { ppl->in_pq = inq; ppl->out_pq = outq; ppl->in_pq->pqb.ic = &ppl->ic_process_queue; ppl->ic_process_queue.fn = ssh_ppl_ic_process_queue_callback; ppl->ic_process_queue.ctx = ppl; /* If there's already something on the input queue, it will want * handling immediately. */ if (pq_peek(ppl->in_pq)) queue_idempotent_callback(&ppl->ic_process_queue); } void ssh_ppl_user_output_string_and_free(PacketProtocolLayer *ppl, char *text) { /* Messages sent via this function are from the SSH layer, not * from the server-side process, so they always have the stderr * flag set. */ seat_stderr_pl(ppl->seat, ptrlen_from_asciz(text)); sfree(text); } size_t ssh_ppl_default_queued_data_size(PacketProtocolLayer *ppl) { return ppl->out_pq->pqb.total_size; } /* ---------------------------------------------------------------------- * Common helper functions for clients and implementations of * BinaryPacketProtocol. */ static void ssh_bpp_input_raw_data_callback(void *context) { BinaryPacketProtocol *bpp = (BinaryPacketProtocol *)context; Ssh *ssh = bpp->ssh; /* in case bpp is about to get freed */ ssh_bpp_handle_input(bpp); /* If we've now cleared enough backlog on the input connection, we * may need to unfreeze it. */ ssh_conn_processed_data(ssh); } static void ssh_bpp_output_packet_callback(void *context) { BinaryPacketProtocol *bpp = (BinaryPacketProtocol *)context; ssh_bpp_handle_output(bpp); } void ssh_bpp_common_setup(BinaryPacketProtocol *bpp) { pq_in_init(&bpp->in_pq); pq_out_init(&bpp->out_pq); bpp->input_eof = false; bpp->ic_in_raw.fn = ssh_bpp_input_raw_data_callback; bpp->ic_in_raw.ctx = bpp; bpp->ic_out_pq.fn = ssh_bpp_output_packet_callback; bpp->ic_out_pq.ctx = bpp; bpp->out_pq.pqb.ic = &bpp->ic_out_pq; } void ssh_bpp_free(BinaryPacketProtocol *bpp) { delete_callbacks_for_context(bpp); bpp->vt->free(bpp); } void ssh2_bpp_queue_disconnect(BinaryPacketProtocol *bpp, const char *msg, int category) { PktOut *pkt = ssh_bpp_new_pktout(bpp, SSH2_MSG_DISCONNECT); put_uint32(pkt, category); put_stringz(pkt, msg); put_stringz(pkt, "en"); /* language tag */ pq_push(&bpp->out_pq, pkt); } #define BITMAP_UNIVERSAL(y, name, value) \ | (value >= y && value < y+32 ? 1UL << (value-y) : 0) #define BITMAP_CONDITIONAL(y, name, value, ctx) \ BITMAP_UNIVERSAL(y, name, value) #define SSH2_BITMAP_WORD(y) \ (0 SSH2_MESSAGE_TYPES(BITMAP_UNIVERSAL, BITMAP_CONDITIONAL, \ BITMAP_CONDITIONAL, (32*y))) bool ssh2_bpp_check_unimplemented(BinaryPacketProtocol *bpp, PktIn *pktin) { static const unsigned valid_bitmap[] = { SSH2_BITMAP_WORD(0), SSH2_BITMAP_WORD(1), SSH2_BITMAP_WORD(2), SSH2_BITMAP_WORD(3), SSH2_BITMAP_WORD(4), SSH2_BITMAP_WORD(5), SSH2_BITMAP_WORD(6), SSH2_BITMAP_WORD(7), }; if (pktin->type < 0x100 && !((valid_bitmap[pktin->type >> 5] >> (pktin->type & 0x1F)) & 1)) { PktOut *pkt = ssh_bpp_new_pktout(bpp, SSH2_MSG_UNIMPLEMENTED); put_uint32(pkt, pktin->sequence); pq_push(&bpp->out_pq, pkt); return true; } return false; } #undef BITMAP_UNIVERSAL #undef BITMAP_CONDITIONAL #undef SSH1_BITMAP_WORD /* ---------------------------------------------------------------------- * Function to check a host key against any manually configured in Conf. */ int verify_ssh_manual_host_key(Conf *conf, char **fingerprints, ssh_key *key) { if (!conf_get_str_nthstrkey(conf, CONF_ssh_manual_hostkeys, 0)) return -1; /* no manual keys configured */ if (fingerprints) { for (size_t i = 0; i < SSH_N_FPTYPES; i++) { /* * Each fingerprint string we've been given will have * things like 'ssh-rsa 2048' at the front of it. Strip * those off and narrow down to just the hash at the end * of the string. */ const char *fingerprint = fingerprints[i]; if (!fingerprint) continue; const char *p = strrchr(fingerprint, ' '); fingerprint = p ? p+1 : fingerprint; if (conf_get_str_str_opt(conf, CONF_ssh_manual_hostkeys, fingerprint)) return 1; /* success */ } } if (key) { /* * Construct the base64-encoded public key blob and see if * that's listed. */ strbuf *binblob; char *base64blob; int atoms, i; binblob = strbuf_new(); ssh_key_public_blob(key, BinarySink_UPCAST(binblob)); atoms = (binblob->len + 2) / 3; base64blob = snewn(atoms * 4 + 1, char); for (i = 0; i < atoms; i++) base64_encode_atom(binblob->u + 3*i, binblob->len - 3*i, base64blob + 4*i); base64blob[atoms * 4] = '\0'; strbuf_free(binblob); if (conf_get_str_str_opt(conf, CONF_ssh_manual_hostkeys, base64blob)) { sfree(base64blob); return 1; /* success */ } sfree(base64blob); } return 0; } /* ---------------------------------------------------------------------- * Common functions shared between SSH-1 layers. */ bool ssh1_common_get_specials( PacketProtocolLayer *ppl, add_special_fn_t add_special, void *ctx) { /* * Don't bother offering IGNORE if we've decided the remote * won't cope with it, since we wouldn't bother sending it if * asked anyway. */ if (!(ppl->remote_bugs & BUG_CHOKES_ON_SSH1_IGNORE)) { add_special(ctx, "IGNORE message", SS_NOP, 0); return true; } return false; } bool ssh1_common_filter_queue(PacketProtocolLayer *ppl) { PktIn *pktin; ptrlen msg; while ((pktin = pq_peek(ppl->in_pq)) != NULL) { switch (pktin->type) { case SSH1_MSG_DISCONNECT: msg = get_string(pktin); ssh_remote_error(ppl->ssh, "Remote side sent disconnect message:\n\"%.*s\"", PTRLEN_PRINTF(msg)); /* don't try to pop the queue, because we've been freed! */ return true; /* indicate that we've been freed */ case SSH1_MSG_DEBUG: msg = get_string(pktin); ppl_logevent("Remote debug message: %.*s", PTRLEN_PRINTF(msg)); pq_pop(ppl->in_pq); break; case SSH1_MSG_IGNORE: /* Do nothing, because we're ignoring it! Duhh. */ pq_pop(ppl->in_pq); break; default: return false; } } return false; } void ssh1_compute_session_id( unsigned char *session_id, const unsigned char *cookie, RSAKey *hostkey, RSAKey *servkey) { ssh_hash *hash = ssh_hash_new(&ssh_md5); for (size_t i = (mp_get_nbits(hostkey->modulus) + 7) / 8; i-- ;) put_byte(hash, mp_get_byte(hostkey->modulus, i)); for (size_t i = (mp_get_nbits(servkey->modulus) + 7) / 8; i-- ;) put_byte(hash, mp_get_byte(servkey->modulus, i)); put_data(hash, cookie, 8); ssh_hash_final(hash, session_id); } putty-0.76/sshcr.h0000644000175000017500000000644314072266312011061 00000000000000/* * Coroutine mechanics used in PuTTY's SSH code. */ #ifndef PUTTY_SSHCR_H #define PUTTY_SSHCR_H /* * If these macros look impenetrable to you, you might find it helpful * to read * * https://www.chiark.greenend.org.uk/~sgtatham/coroutines.html * * which explains the theory behind these macros. * * In particular, if you are getting `case expression not constant' * errors when building with MS Visual Studio, this is because MS's * Edit and Continue debugging feature causes their compiler to * violate ANSI C. To disable Edit and Continue debugging: * * - 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) do { 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); } while (0) #define crFinishV } *crLine = 0; return; } while (0) #define crFinishFreed(z) } return (z); } while (0) #define crFinishFreedV } return; } while (0) #define crFinishFree(z) } sfree(s); return (z); } while (0) #define crFinishFreeV } sfree(s); return; } while (0) #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) /* * The crMaybeWaitUntil macros could have been more easily written in * terms of the simple crReturn above, by writing things like * * while (!condition) { crReturn(whatever); } * * (or do-while in the case of crWaitUntil). But it's better to do it * directly by writing _once_ to crLine before first testing the * condition, because this way it's robust against the condition check * potentially freeing the entire coroutine state structure as a side * effect (as long as it also evaluates false if it does that), * because we don't write into crLine between the condition evaluating * to false and the 'return' statement. */ #define crMaybeWaitUntil(c) \ do { \ *crLine =__LINE__; \ case __LINE__: if (!(c)) return 0; \ } while (0) #define crMaybeWaitUntilV(c) \ do { \ *crLine =__LINE__; \ case __LINE__: if (!(c)) return; \ } while (0) #define crWaitUntil(c) \ do { \ *crLine =__LINE__; return; \ case __LINE__: if (!(c)) return 0; \ } while (0) #define crWaitUntilV(c) \ do { \ *crLine =__LINE__; return; \ case __LINE__: if (!(c)) return; \ } while (0) #endif /* PUTTY_SSHCR_H */ putty-0.76/sshcrc.c0000644000175000017500000001017414072266312011213 00000000000000/* * CRC32 implementation, as used in SSH-1. * * This particular form of the CRC uses the polynomial * P(x) = x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x^1+1 * and represents polynomials in bit-reversed form, so that the x^0 * coefficient (constant term) appears in the bit with place value * 2^31, and the x^31 coefficient in the bit with place value 2^0. In * this representation, (x^32 mod P) = 0xEDB88320, so multiplying the * current state by x is done by shifting right by one bit, and XORing * that constant into the result if the bit shifted out was 1. * * There's a bewildering array of subtly different variants of CRC out * there, using different polynomials, both bit orders, and varying * the start and end conditions. There are catalogue websites such as * http://reveng.sourceforge.net/crc-catalogue/ , which generally seem * to have the convention of indexing CRCs by their 'check value', * defined as whatever you get if you hash the 9-byte test string * "123456789". * * The crc32_rfc1662() function below, which starts off the CRC state * at 0xFFFFFFFF and complements it after feeding all the data, gives * the check value 0xCBF43926, and matches the hash function that the * above catalogue refers to as "CRC-32/ISO-HDLC"; among other things, * it's also the "FCS-32" checksum described in RFC 1662 section C.3 * (hence the name I've given it here). * * The crc32_ssh1() function implements the variant form used by * SSH-1, which uses the same update function, but starts the state at * zero and doesn't complement it at the end of the computation. The * check value for that version is 0x2DFD2D88, which that CRC * catalogue doesn't list at all. */ #include #include #include "ssh.h" /* * Multiply a CRC value by x^4. This implementation strategy avoids * using a lookup table (which would be a side-channel hazard, since * SSH-1 applies this CRC to decrypted session data). * * The basic idea is that you'd like to "multiply" the shifted-out 4 * bits by the CRC polynomial value 0xEDB88320, or rather by that * value shifted right 3 bits (since you want the _last_ bit shifted * out, i.e. the one originally at the 2^3 position, to generate * 0xEDB88320 itself). But the scare-quoted "multiply" would have to * be a multiplication of polynomials over GF(2), which differs from * integer multiplication in that you don't have any carries. In other * words, you make a copy of one input shifted left by the index of * each set bit in the other, so that adding them all together would * give you the ordinary integer product, and then you XOR them * together instead. * * With a 4-bit multiplier, the two kinds of multiplication coincide * provided the multiplicand has no two set bits at positions * differing by less than 4, because then no two copies of the * multiplier can overlap to generate a carry. So I break up the * intended multiplicand K = 0xEDB88320 >> 3 into three sub-constants * a,b,c with that property, such that a^b^c = K. Then I can multiply * m by each of them separately, and XOR together the results. */ static inline uint32_t crc32_shift_4(uint32_t v) { const uint32_t a = 0x11111044, b = 0x08840020, c = 0x04220000; uint32_t m = v & 0xF; return (v >> 4) ^ (a*m) ^ (b*m) ^ (c*m); } /* * The 8-bit shift you need every time you absorb an input byte, * implemented simply by iterating the 4-bit shift twice. */ static inline uint32_t crc32_shift_8(uint32_t v) { return crc32_shift_4(crc32_shift_4(v)); } /* * Update an existing hash value with extra bytes of data. */ uint32_t crc32_update(uint32_t crc, ptrlen data) { const uint8_t *p = (const uint8_t *)data.ptr; for (size_t len = data.len; len-- > 0 ;) crc = crc32_shift_8(crc ^ *p++); return crc; } /* * The SSH-1 variant of CRC-32. */ uint32_t crc32_ssh1(ptrlen data) { return crc32_update(0, data); } /* * The official version of CRC-32. Nothing in PuTTY proper uses this, * but it's useful to expose it to testcrypt so that we can implement * standard test vectors. */ uint32_t crc32_rfc1662(ptrlen data) { return crc32_update(0xFFFFFFFF, data) ^ 0xFFFFFFFF; } putty-0.76/sshcrcda.c0000644000175000017500000001142014072266312011513 00000000000000/* $OpenBSD: deattack.c,v 1.14 2001/06/23 15:12:18 itojun Exp $ */ /* * Cryptographic attack detector for ssh - source code * * Copyright (c) 1998 CORE SDI S.A., Buenos Aires, Argentina. * * All rights reserved. Redistribution and use in source and binary * forms, with or without modification, are permitted provided that * this copyright notice is retained. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES ARE DISCLAIMED. IN NO EVENT SHALL CORE SDI S.A. BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY OR * CONSEQUENTIAL DAMAGES RESULTING FROM THE USE OR MISUSE OF THIS * SOFTWARE. * * Ariel Futoransky * * * Modified for use in PuTTY by Simon Tatham */ #include #include "misc.h" #include "ssh.h" /* SSH Constants */ #define SSH_MAXBLOCKS (32 * 1024) #define SSH_BLOCKSIZE (8) /* Hashing constants */ #define HASH_MINSIZE (8 * 1024) #define HASH_ENTRYSIZE (sizeof(uint16_t)) #define HASH_FACTOR(x) ((x)*3/2) #define HASH_UNUSEDCHAR (0xff) #define HASH_UNUSED (0xffff) #define HASH_IV (0xfffe) #define HASH_MINBLOCKS (7*SSH_BLOCKSIZE) /* Hash function (Input keys are cipher results) */ #define HASH(x) GET_32BIT_MSB_FIRST(x) #define CMP(a, b) (memcmp(a, b, SSH_BLOCKSIZE)) static const uint8_t ONE[4] = { 1, 0, 0, 0 }; static const uint8_t ZERO[4] = { 0, 0, 0, 0 }; struct crcda_ctx { uint16_t *h; uint32_t n; }; struct crcda_ctx *crcda_make_context(void) { struct crcda_ctx *ret = snew(struct crcda_ctx); ret->h = NULL; ret->n = HASH_MINSIZE / HASH_ENTRYSIZE; return ret; } void crcda_free_context(struct crcda_ctx *ctx) { if (ctx) { sfree(ctx->h); ctx->h = NULL; sfree(ctx); } } static void crc_update(uint32_t *a, const void *b) { *a = crc32_update(*a, make_ptrlen(b, 4)); } /* detect if a block is used in a particular pattern */ static bool check_crc(const uint8_t *S, const uint8_t *buf, uint32_t len, const uint8_t *IV) { uint32_t crc; const uint8_t *c; crc = 0; if (IV && !CMP(S, IV)) { crc_update(&crc, ONE); crc_update(&crc, ZERO); } for (c = buf; c < buf + len; c += SSH_BLOCKSIZE) { if (!CMP(S, c)) { crc_update(&crc, ONE); crc_update(&crc, ZERO); } else { crc_update(&crc, ZERO); crc_update(&crc, ZERO); } } return (crc == 0); } /* Detect a crc32 compensation attack on a packet */ bool detect_attack(struct crcda_ctx *ctx, const unsigned char *buf, uint32_t len, const unsigned char *IV) { register uint32_t i, j; uint32_t l; register const uint8_t *c; const uint8_t *d; assert(!(len > (SSH_MAXBLOCKS * SSH_BLOCKSIZE) || len % SSH_BLOCKSIZE != 0)); for (l = ctx->n; l < HASH_FACTOR(len / SSH_BLOCKSIZE); l = l << 2) ; if (ctx->h == NULL) { ctx->n = l; ctx->h = snewn(ctx->n, uint16_t); } else { if (l > ctx->n) { ctx->n = l; ctx->h = sresize(ctx->h, ctx->n, uint16_t); } } if (len <= HASH_MINBLOCKS) { for (c = buf; c < buf + len; c += SSH_BLOCKSIZE) { if (IV && (!CMP(c, IV))) { if ((check_crc(c, buf, len, IV))) return true; /* attack detected */ else break; } for (d = buf; d < c; d += SSH_BLOCKSIZE) { if (!CMP(c, d)) { if ((check_crc(c, buf, len, IV))) return true; /* attack detected */ else break; } } } return false; /* ok */ } memset(ctx->h, HASH_UNUSEDCHAR, ctx->n * HASH_ENTRYSIZE); if (IV) ctx->h[HASH(IV) & (ctx->n - 1)] = HASH_IV; for (c = buf, j = 0; c < (buf + len); c += SSH_BLOCKSIZE, j++) { for (i = HASH(c) & (ctx->n - 1); ctx->h[i] != HASH_UNUSED; i = (i + 1) & (ctx->n - 1)) { if (ctx->h[i] == HASH_IV) { assert(IV); /* or we wouldn't have stored HASH_IV above */ if (!CMP(c, IV)) { if (check_crc(c, buf, len, IV)) return true; /* attack detected */ else break; } } else if (!CMP(c, buf + ctx->h[i] * SSH_BLOCKSIZE)) { if (check_crc(c, buf, len, IV)) return true; /* attack detected */ else break; } } ctx->h[i] = j; } return false; /* ok */ } putty-0.76/sshdes.c0000644000175000017500000011221214072266312011213 00000000000000/* * sshdes.c: implementation of DES. */ /* * Background * ---------- * * The basic structure of DES is a Feistel network: the 64-bit cipher * block is divided into two 32-bit halves L and R, and in each round, * a mixing function is applied to one of them, the result is XORed * into the other, and then the halves are swapped so that the other * one will be the input to the mixing function next time. (This * structure guarantees reversibility no matter whether the mixing * function itself is bijective.) * * The mixing function for DES goes like this: * + Extract eight contiguous 6-bit strings from the 32-bit word. * They start at positions 4 bits apart, so each string overlaps * the next one by one bit. At least one has to wrap cyclically * round the end of the word. * + XOR each of those strings with 6 bits of data from the key * schedule (which consists of 8 x 6-bit strings per round). * + Use the resulting 6-bit numbers as the indices into eight * different lookup tables ('S-boxes'), each of which delivers a * 4-bit output. * + Concatenate those eight 4-bit values into a 32-bit word. * + Finally, apply a fixed permutation P to that word. * * DES adds one more wrinkle on top of this structure, which is to * conjugate it by a bitwise permutation of the cipher block. That is, * before starting the main cipher rounds, the input bits are permuted * according to a 64-bit permutation called IP, and after the rounds * are finished, the output bits are permuted back again by applying * the inverse of IP. * * This gives a lot of leeway to redefine the components of the cipher * without actually changing the input and output. You could permute * the bits in the output of any or all of the S-boxes, or reorder the * S-boxes among themselves, and adjust the following permutation P to * compensate. And you could adjust IP by post-composing a rotation of * each 32-bit half, and adjust the starting offsets of the 6-bit * S-box indices to compensate. * * test/desref.py demonstrates this by providing two equivalent forms * of the cipher, called DES and SGTDES, which give the same output. * DES is the form described in the original spec: if you make it * print diagnostic output during the cipher and check it against the * original, you should recognise the S-box outputs as matching the * ones you expect. But SGTDES, which I egotistically name after * myself, is much closer to the form implemented here: I've changed * the permutation P to suit my implementation strategy and * compensated by permuting the S-boxes, and also I've added a * rotation right by 1 bit to IP so that only one S-box index has to * wrap round the word and also so that the indices are nicely aligned * for the constant-time selection system I'm using. */ #include #include "ssh.h" #include "mpint_i.h" /* we reuse the BignumInt system */ /* If you compile with -DDES_DIAGNOSTICS, intermediate results will be * sent to debug() (so you also need to compile with -DDEBUG). * Otherwise this ifdef will condition away all the debug() calls. */ #ifndef DES_DIAGNOSTICS #undef debug #define debug(...) ((void)0) #endif /* * General utility functions. */ static inline uint32_t rol(uint32_t x, unsigned c) { return (x << (31 & c)) | (x >> (31 & -c)); } static inline uint32_t ror(uint32_t x, unsigned c) { return rol(x, -c); } /* * The hard part of doing DES in constant time is the S-box lookup. * * My strategy is to iterate over the whole lookup table! That's slow, * but I don't see any way to avoid _something_ along those lines: in * every round, every entry in every S-box is potentially needed, and * if you can't change your memory access pattern based on the input * data, it follows that you have to read a quantity of information * equal to the size of all the S-boxes. (Unless they were to turn out * to be significantly compressible, but I for one couldn't show them * to be.) * * In more detail, I construct a sort of counter-based 'selection * gadget', which is 15 bits wide and starts off with the top bit * zero, the next eight bits all 1, and the bottom six set to the * input S-box index: * * 011111111xxxxxx * * Now if you add 1 in the lowest bit position, then either it carries * into the top section (resetting it to 100000000), or it doesn't do * that yet. If you do that 64 times, then it will _guarantee_ to have * ticked over into 100000000. In between those increments, the eight * bits that started off as 11111111 will have stayed that way for * some number of iterations and then become 00000000, and exactly how * many iterations depends on the input index. * * The purpose of the 0 bit at the top is to absorb the carry when the * switch happens, which means you can pack more than one gadget into * the same machine word and have them all work in parallel without * each one intefering with the next. * * The next step is to use each of those 8-bit segments as a bit mask: * each one is ANDed with a lookup table entry, and all the results * are XORed together. So you end up with the bitwise XOR of some * initial segment of the table entries. And the stored S-box tables * are transformed in such a way that the real S-box values are given * not by the individual entries, but by the cumulative XORs * constructed in this way. * * A refinement is that I increment each gadget by 2 rather than 1 * each time, so I only iterate 32 times instead of 64. That's why * there are 8 selection bits instead of 4: each gadget selects enough * bits to reconstruct _two_ S-box entries, for a pair of indices * (2n,2n+1), and then finally I use the low bit of the index to do a * parallel selection between each of those pairs. * * The selection gadget is not quite 16 bits wide. So you can fit four * of them across a 64-bit word at 16-bit intervals, which is also * convenient because the place the S-box indices are coming from also * has pairs of them separated by 16-bit distances, so it's easy to * copy them into the gadgets in the first place. */ /* * The S-box data. Each pair of nonzero columns here describes one of * the S-boxes, corresponding to the SGTDES tables in test/desref.py, * under the following transformation. * * Take S-box #3 as an example. Its values in successive rows of this * table are eb,e8,54,3d, ... So the cumulative XORs of initial * sequences of those values are eb,(eb^e8),(eb^e8^54), ... which * comes to eb,03,57,... Of _those_ values, the top nibble (e,0,5,...) * gives the even-numbered entries in the S-box, in _reverse_ order * (because a lower input index selects the XOR of a longer * subsequence). The odd-numbered entries are given by XORing the two * digits together: (e^b),(0^3),(5^7),... = 5,3,2,... And indeed, if * you check SGTDES.sboxes[3] you find it ends ... 52 03 e5. */ #define SBOX_ITERATION(X) \ /* 66 22 44 00 77 33 55 11 */ \ X(0xf600970083008500, 0x0e00eb007b002e00) \ X(0xda00e4009000e000, 0xad00e800a700b400) \ X(0x1a009d003f003600, 0xf60054004300cd00) \ X(0xaf00c500e900a900, 0x63003d00f2005900) \ X(0xf300750079001400, 0x80005000a2008900) \ X(0xa100d400d6007b00, 0xd3009000d300e100) \ X(0x450087002600ac00, 0xae003c0031009c00) \ X(0xd000b100b6003600, 0x3e006f0092005900) \ X(0x4d008a0026001000, 0x89007a00b8004a00) \ X(0xca00f5003f00ac00, 0x6f00f0003c009400) \ X(0x92008d0090001000, 0x8c00c600ce004a00) \ X(0xe2005900e9006d00, 0x790078007800fa00) \ X(0x1300b10090008d00, 0xa300170027001800) \ X(0xc70058005f006a00, 0x9c00c100e0006300) \ X(0x9b002000f000f000, 0xf70057001600f900) \ X(0xeb00b0009000af00, 0xa9006300b0005800) \ X(0xa2001d00cf000000, 0x3800b00066000000) \ X(0xf100da007900d000, 0xbc00790094007900) \ X(0x570015001900ad00, 0x6f00ef005100cb00) \ X(0xc3006100e9006d00, 0xc000b700f800f200) \ X(0x1d005800b600d000, 0x67004d00cd002c00) \ X(0xf400b800d600e000, 0x5e00a900b000e700) \ X(0x5400d1003f009c00, 0xc90069002c005300) \ X(0xe200e50060005900, 0x6a00b800c500f200) \ X(0xdf0047007900d500, 0x7000ec004c00ea00) \ X(0x7100d10060009c00, 0x3f00b10095005e00) \ X(0x82008200f0002000, 0x87001d00cd008000) \ X(0xd0007000af00c000, 0xe200be006100f200) \ X(0x8000930060001000, 0x36006e0081001200) \ X(0x6500a300d600ac00, 0xcf003d007d00c000) \ X(0x9000700060009800, 0x62008100ad009200) \ X(0xe000e4003f00f400, 0x5a00ed009000f200) \ /* end of list */ /* * The S-box mapping function. Expects two 32-bit input words: si6420 * contains the table indices for S-boxes 0,2,4,6 with their low bits * starting at position 2 (for S-box 0) and going up in steps of 8. * si7531 has indices 1,3,5,7 in the same bit positions. */ static inline uint32_t des_S(uint32_t si6420, uint32_t si7531) { debug("sindices: %02x %02x %02x %02x %02x %02x %02x %02x\n", 0x3F & (si6420 >> 2), 0x3F & (si7531 >> 2), 0x3F & (si6420 >> 10), 0x3F & (si7531 >> 10), 0x3F & (si6420 >> 18), 0x3F & (si7531 >> 18), 0x3F & (si6420 >> 26), 0x3F & (si7531 >> 26)); #ifdef SIXTY_FOUR_BIT /* * On 64-bit machines, we store the table in exactly the form * shown above, and make two 64-bit words containing four * selection gadgets each. */ /* Set up the gadgets. The 'cNNNN' variables will be gradually * incremented, and the bits in positions FF00FF00FF00FF00 will * act as selectors for the words in the table. * * A side effect of moving the input indices further apart is that * they change order, because it's easier to keep a pair that were * originally 16 bits apart still 16 bits apart, which now makes * them adjacent instead of separated by one. So the fact that * si6420 turns into c6240 (with the 2,4 reversed) is not a typo! * This will all be undone when we rebuild the output word later. */ uint64_t c6240 = ((si6420 | ((uint64_t)si6420 << 24)) & 0x00FC00FC00FC00FC) | 0xFF00FF00FF00FF00; uint64_t c7351 = ((si7531 | ((uint64_t)si7531 << 24)) & 0x00FC00FC00FC00FC) | 0xFF00FF00FF00FF00; debug("S in: c6240=%016"PRIx64" c7351=%016"PRIx64"\n", c6240, c7351); /* Iterate over the table. The 'sNNNN' variables accumulate the * XOR of all the table entries not masked out. */ static const struct tbl { uint64_t t6240, t7351; } tbl[32] = { #define TABLE64(a, b) { a, b }, SBOX_ITERATION(TABLE64) #undef TABLE64 }; uint64_t s6240 = 0, s7351 = 0; for (const struct tbl *t = tbl, *limit = tbl + 32; t < limit; t++) { s6240 ^= c6240 & t->t6240; c6240 += 0x0008000800080008; s7351 ^= c7351 & t->t7351; c7351 += 0x0008000800080008; } debug("S out: s6240=%016"PRIx64" s7351=%016"PRIx64"\n", s6240, s7351); /* Final selection between each even/odd pair: mask off the low * bits of all the input indices (which haven't changed throughout * the iteration), and multiply by a bit mask that will turn each * set bit into a mask covering the upper nibble of the selected * pair. Then use those masks to control which set of lower * nibbles is XORed into the upper nibbles. */ s6240 ^= (s6240 << 4) & ((0xf000/0x004) * (c6240 & 0x0004000400040004)); s7351 ^= (s7351 << 4) & ((0xf000/0x004) * (c7351 & 0x0004000400040004)); /* Now the eight final S-box outputs are in the upper nibble of * each selection position. Mask away the rest of the clutter. */ s6240 &= 0xf000f000f000f000; s7351 &= 0xf000f000f000f000; debug("s0=%x s1=%x s2=%x s3=%x s4=%x s5=%x s6=%x s7=%x\n", (unsigned)(0xF & (s6240 >> 12)), (unsigned)(0xF & (s7351 >> 12)), (unsigned)(0xF & (s6240 >> 44)), (unsigned)(0xF & (s7351 >> 44)), (unsigned)(0xF & (s6240 >> 28)), (unsigned)(0xF & (s7351 >> 28)), (unsigned)(0xF & (s6240 >> 60)), (unsigned)(0xF & (s7351 >> 60))); /* Combine them all into a single 32-bit output word, which will * come out in the order 76543210. */ uint64_t combined = (s6240 >> 12) | (s7351 >> 8); return combined | (combined >> 24); #else /* SIXTY_FOUR_BIT */ /* * For 32-bit platforms, we do the same thing but in four 32-bit * words instead of two 64-bit ones, so the CPU doesn't have to * waste time propagating carries or shifted bits between the two * halves of a uint64 that weren't needed anyway. */ /* Set up the gadgets */ uint32_t c40 = ((si6420 ) & 0x00FC00FC) | 0xFF00FF00; uint32_t c62 = ((si6420 >> 8) & 0x00FC00FC) | 0xFF00FF00; uint32_t c51 = ((si7531 ) & 0x00FC00FC) | 0xFF00FF00; uint32_t c73 = ((si7531 >> 8) & 0x00FC00FC) | 0xFF00FF00; debug("S in: c40=%08"PRIx32" c62=%08"PRIx32 " c51=%08"PRIx32" c73=%08"PRIx32"\n", c40, c62, c51, c73); /* Iterate over the table */ static const struct tbl { uint32_t t40, t62, t51, t73; } tbl[32] = { #define TABLE32(a, b) { ((uint32_t)a), (a>>32), ((uint32_t)b), (b>>32) }, SBOX_ITERATION(TABLE32) #undef TABLE32 }; uint32_t s40 = 0, s62 = 0, s51 = 0, s73 = 0; for (const struct tbl *t = tbl, *limit = tbl + 32; t < limit; t++) { s40 ^= c40 & t->t40; c40 += 0x00080008; s62 ^= c62 & t->t62; c62 += 0x00080008; s51 ^= c51 & t->t51; c51 += 0x00080008; s73 ^= c73 & t->t73; c73 += 0x00080008; } debug("S out: s40=%08"PRIx32" s62=%08"PRIx32 " s51=%08"PRIx32" s73=%08"PRIx32"\n", s40, s62, s51, s73); /* Final selection within each pair */ s40 ^= (s40 << 4) & ((0xf000/0x004) * (c40 & 0x00040004)); s62 ^= (s62 << 4) & ((0xf000/0x004) * (c62 & 0x00040004)); s51 ^= (s51 << 4) & ((0xf000/0x004) * (c51 & 0x00040004)); s73 ^= (s73 << 4) & ((0xf000/0x004) * (c73 & 0x00040004)); /* Clean up the clutter */ s40 &= 0xf000f000; s62 &= 0xf000f000; s51 &= 0xf000f000; s73 &= 0xf000f000; debug("s0=%x s1=%x s2=%x s3=%x s4=%x s5=%x s6=%x s7=%x\n", (unsigned)(0xF & (s40 >> 12)), (unsigned)(0xF & (s51 >> 12)), (unsigned)(0xF & (s62 >> 12)), (unsigned)(0xF & (s73 >> 12)), (unsigned)(0xF & (s40 >> 28)), (unsigned)(0xF & (s51 >> 28)), (unsigned)(0xF & (s62 >> 28)), (unsigned)(0xF & (s73 >> 28))); /* Recombine and return */ return (s40 >> 12) | (s62 >> 4) | (s51 >> 8) | (s73); #endif /* SIXTY_FOUR_BIT */ } /* * Now for the permutation P. The basic strategy here is to use a * Benes network: in each stage, the bit at position i is allowed to * either stay where it is or swap with i ^ D, where D is a power of 2 * that varies with each phase. (So when D=1, pairs of the form * {2n,2n+1} can swap; when D=2, the pairs are {4n+j,4n+j+2} for * j={0,1}, and so on.) * * You can recursively construct a Benes network for an arbitrary * permutation, in which the values of D iterate across all the powers * of 2 less than the permutation size and then go back again. For * example, the typical presentation for 32 bits would have D iterate * over 16,8,4,2,1,2,4,8,16, and there's an easy algorithm that can * express any permutation in that form by deciding which pairs of * bits to swap in the outer pair of stages and then recursing to do * all the stages in between. * * Actually implementing the swaps is easy when they're all between * bits at the same separation: make the value x ^ (x >> D), mask out * just the bits in the low position of a pair that needs to swap, and * then use the resulting value y to make x ^ y ^ (y << D) which is * the swapped version. * * In this particular case, I processed the bit indices in the other * order (going 1,2,4,8,16,8,4,2,1), which makes no significant * difference to the construction algorithm (it's just a relabelling), * but it now means that the first two steps only permute entries * within the output of each S-box - and therefore we can leave them * completely out, in favour of just defining the S-boxes so that * those permutation steps are already applied. Furthermore, by * exhaustive search over the rest of the possible bit-orders for each * S-box, I was able to find a version of P which could be represented * in such a way that two further phases had all their control bits * zero and could be skipped. So the number of swap stages is reduced * to 5 from the 9 that might have been needed. */ static inline uint32_t des_benes_step(uint32_t v, unsigned D, uint32_t mask) { uint32_t diff = (v ^ (v >> D)) & mask; return v ^ diff ^ (diff << D); } static inline uint32_t des_P(uint32_t v_orig) { uint32_t v = v_orig; /* initial stages with distance 1,2 are part of the S-box data table */ v = des_benes_step(v, 4, 0x07030702); v = des_benes_step(v, 8, 0x004E009E); v = des_benes_step(v, 16, 0x0000D9D3); /* v = des_benes_step(v, 8, 0x00000000); no-op, so we can skip it */ v = des_benes_step(v, 4, 0x05040004); /* v = des_benes_step(v, 2, 0x00000000); no-op, so we can skip it */ v = des_benes_step(v, 1, 0x04045015); debug("P(%08"PRIx32") = %08"PRIx32"\n", v_orig, v); return v; } /* * Putting the S and P functions together, and adding in the round key * as well, gives us the full mixing function f. */ static inline uint32_t des_f(uint32_t R, uint32_t K7531, uint32_t K6420) { uint32_t s7531 = R ^ K7531, s6420 = rol(R, 4) ^ K6420; return des_P(des_S(s6420, s7531)); } /* * The key schedule, and the function to set it up. */ typedef struct des_keysched des_keysched; struct des_keysched { uint32_t k7531[16], k6420[16]; }; /* * Simplistic function to select an arbitrary sequence of bits from * one value and glue them together into another value. bitnums[] * gives the sequence of bit indices of the input, from the highest * output bit downwards. An index of -1 means that output bit is left * at zero. * * This function is only used during key setup, so it doesn't need to * be highly optimised. */ static inline uint64_t bitsel( uint64_t input, const int8_t *bitnums, size_t size) { uint64_t ret = 0; while (size-- > 0) { int bitpos = *bitnums++; ret <<= 1; if (bitpos >= 0) ret |= 1 & (input >> bitpos); } return ret; } static void des_key_setup(uint64_t key, des_keysched *sched) { static const int8_t PC1[] = { 7, 15, 23, 31, 39, 47, 55, 63, 6, 14, 22, 30, 38, 46, 54, 62, 5, 13, 21, 29, 37, 45, 53, 61, 4, 12, 20, 28, -1, -1, -1, -1, 1, 9, 17, 25, 33, 41, 49, 57, 2, 10, 18, 26, 34, 42, 50, 58, 3, 11, 19, 27, 35, 43, 51, 59, 36, 44, 52, 60, }; static const int8_t PC2_7531[] = { 46, 43, 49, 36, 59, 55, -1, -1, /* index into S-box 7 */ 37, 41, 48, 56, 34, 52, -1, -1, /* index into S-box 5 */ 15, 4, 25, 19, 9, 1, -1, -1, /* index into S-box 3 */ 12, 7, 17, 0, 22, 3, -1, -1, /* index into S-box 1 */ }; static const int8_t PC2_6420[] = { 57, 32, 45, 54, 39, 50, -1, -1, /* index into S-box 6 */ 44, 53, 33, 40, 47, 58, -1, -1, /* index into S-box 4 */ 26, 16, 5, 11, 23, 8, -1, -1, /* index into S-box 2 */ 10, 14, 6, 20, 27, 24, -1, -1, /* index into S-box 0 */ }; static const int leftshifts[] = {1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1}; /* Select 56 bits from the 64-bit input key integer (the low bit * of each input byte is unused), into a word consisting of two * 28-bit integers starting at bits 0 and 32. */ uint64_t CD = bitsel(key, PC1, lenof(PC1)); for (size_t i = 0; i < 16; i++) { /* Rotate each 28-bit half of CD left by 1 or 2 bits (varying * between rounds) */ CD <<= leftshifts[i]; CD = (CD & 0x0FFFFFFF0FFFFFFF) | ((CD & 0xF0000000F0000000) >> 28); /* Select key bits from the rotated word to use during the * actual cipher */ sched->k7531[i] = bitsel(CD, PC2_7531, lenof(PC2_7531)); sched->k6420[i] = bitsel(CD, PC2_6420, lenof(PC2_6420)); } } /* * Helper routines for dealing with 64-bit blocks in the form of an L * and R word. */ typedef struct LR LR; struct LR { uint32_t L, R; }; static inline LR des_load_lr(const void *vp) { const uint8_t *p = (const uint8_t *)vp; LR out; out.L = GET_32BIT_MSB_FIRST(p); out.R = GET_32BIT_MSB_FIRST(p+4); return out; } static inline void des_store_lr(void *vp, LR lr) { uint8_t *p = (uint8_t *)vp; PUT_32BIT_MSB_FIRST(p, lr.L); PUT_32BIT_MSB_FIRST(p+4, lr.R); } static inline LR des_xor_lr(LR a, LR b) { a.L ^= b.L; a.R ^= b.R; return a; } static inline LR des_swap_lr(LR in) { LR out; out.L = in.R; out.R = in.L; return out; } /* * The initial and final permutations of official DES are in a * restricted form, in which the 'before' and 'after' positions of a * given data bit are derived from each other by permuting the bits of * the _index_ and flipping some of them. This allows the permutation * to be performed effectively by a method that looks rather like * _half_ of a general Benes network, because the restricted form * means only half of it is actually needed. * * _Our_ initial and final permutations include a rotation by 1 bit, * but it's still easier to just suffix that to the standard IP/FP * than to regenerate everything using a more general method. * * Because we're permuting 64 bits in this case, between two 32-bit * words, there's a separate helper function for this code that * doesn't look quite like des_benes_step() above. */ static inline void des_bitswap_IP_FP(uint32_t *L, uint32_t *R, unsigned D, uint32_t mask) { uint32_t diff = mask & ((*R >> D) ^ *L); *R ^= diff << D; *L ^= diff; } static inline LR des_IP(LR lr) { des_bitswap_IP_FP(&lr.R, &lr.L, 4, 0x0F0F0F0F); des_bitswap_IP_FP(&lr.R, &lr.L, 16, 0x0000FFFF); des_bitswap_IP_FP(&lr.L, &lr.R, 2, 0x33333333); des_bitswap_IP_FP(&lr.L, &lr.R, 8, 0x00FF00FF); des_bitswap_IP_FP(&lr.R, &lr.L, 1, 0x55555555); lr.L = ror(lr.L, 1); lr.R = ror(lr.R, 1); return lr; } static inline LR des_FP(LR lr) { lr.L = rol(lr.L, 1); lr.R = rol(lr.R, 1); des_bitswap_IP_FP(&lr.R, &lr.L, 1, 0x55555555); des_bitswap_IP_FP(&lr.L, &lr.R, 8, 0x00FF00FF); des_bitswap_IP_FP(&lr.L, &lr.R, 2, 0x33333333); des_bitswap_IP_FP(&lr.R, &lr.L, 16, 0x0000FFFF); des_bitswap_IP_FP(&lr.R, &lr.L, 4, 0x0F0F0F0F); return lr; } /* * The main cipher functions, which are identical except that they use * the key schedule in opposite orders. * * We provide a version without the initial and final permutations, * for use in triple-DES mode (no sense undoing and redoing it in * between the phases). */ static inline LR des_round(LR in, const des_keysched *sched, size_t round) { LR out; out.L = in.R; out.R = in.L ^ des_f(in.R, sched->k7531[round], sched->k6420[round]); return out; } static inline LR des_inner_cipher(LR lr, const des_keysched *sched, size_t start, size_t step) { lr = des_round(lr, sched, start+0x0*step); lr = des_round(lr, sched, start+0x1*step); lr = des_round(lr, sched, start+0x2*step); lr = des_round(lr, sched, start+0x3*step); lr = des_round(lr, sched, start+0x4*step); lr = des_round(lr, sched, start+0x5*step); lr = des_round(lr, sched, start+0x6*step); lr = des_round(lr, sched, start+0x7*step); lr = des_round(lr, sched, start+0x8*step); lr = des_round(lr, sched, start+0x9*step); lr = des_round(lr, sched, start+0xa*step); lr = des_round(lr, sched, start+0xb*step); lr = des_round(lr, sched, start+0xc*step); lr = des_round(lr, sched, start+0xd*step); lr = des_round(lr, sched, start+0xe*step); lr = des_round(lr, sched, start+0xf*step); return des_swap_lr(lr); } static inline LR des_full_cipher(LR lr, const des_keysched *sched, size_t start, size_t step) { lr = des_IP(lr); lr = des_inner_cipher(lr, sched, start, step); lr = des_FP(lr); return lr; } /* * Parameter pairs for the start,step arguments to the cipher routines * above, causing them to use the same key schedule in opposite orders. */ #define ENCIPHER 0, 1 /* for encryption */ #define DECIPHER 15, -1 /* for decryption */ /* ---------------------------------------------------------------------- * Single-DES */ struct des_cbc_ctx { des_keysched sched; LR iv; ssh_cipher ciph; }; static ssh_cipher *des_cbc_new(const ssh_cipheralg *alg) { struct des_cbc_ctx *ctx = snew(struct des_cbc_ctx); ctx->ciph.vt = alg; return &ctx->ciph; } static void des_cbc_free(ssh_cipher *ciph) { struct des_cbc_ctx *ctx = container_of(ciph, struct des_cbc_ctx, ciph); smemclr(ctx, sizeof(*ctx)); sfree(ctx); } static void des_cbc_setkey(ssh_cipher *ciph, const void *vkey) { struct des_cbc_ctx *ctx = container_of(ciph, struct des_cbc_ctx, ciph); const uint8_t *key = (const uint8_t *)vkey; des_key_setup(GET_64BIT_MSB_FIRST(key), &ctx->sched); } static void des_cbc_setiv(ssh_cipher *ciph, const void *iv) { struct des_cbc_ctx *ctx = container_of(ciph, struct des_cbc_ctx, ciph); ctx->iv = des_load_lr(iv); } static void des_cbc_encrypt(ssh_cipher *ciph, void *vdata, int len) { struct des_cbc_ctx *ctx = container_of(ciph, struct des_cbc_ctx, ciph); uint8_t *data = (uint8_t *)vdata; for (; len > 0; len -= 8, data += 8) { LR plaintext = des_load_lr(data); LR cipher_in = des_xor_lr(plaintext, ctx->iv); LR ciphertext = des_full_cipher(cipher_in, &ctx->sched, ENCIPHER); des_store_lr(data, ciphertext); ctx->iv = ciphertext; } } static void des_cbc_decrypt(ssh_cipher *ciph, void *vdata, int len) { struct des_cbc_ctx *ctx = container_of(ciph, struct des_cbc_ctx, ciph); uint8_t *data = (uint8_t *)vdata; for (; len > 0; len -= 8, data += 8) { LR ciphertext = des_load_lr(data); LR cipher_out = des_full_cipher(ciphertext, &ctx->sched, DECIPHER); LR plaintext = des_xor_lr(cipher_out, ctx->iv); des_store_lr(data, plaintext); ctx->iv = ciphertext; } } const ssh_cipheralg ssh_des = { .new = des_cbc_new, .free = des_cbc_free, .setiv = des_cbc_setiv, .setkey = des_cbc_setkey, .encrypt = des_cbc_encrypt, .decrypt = des_cbc_decrypt, .ssh2_id = "des-cbc", .blksize = 8, .real_keybits = 56, .padded_keybytes = 8, .flags = SSH_CIPHER_IS_CBC, .text_name = "single-DES CBC", }; const ssh_cipheralg ssh_des_sshcom_ssh2 = { /* Same as ssh_des_cbc, but with a different SSH-2 ID */ .new = des_cbc_new, .free = des_cbc_free, .setiv = des_cbc_setiv, .setkey = des_cbc_setkey, .encrypt = des_cbc_encrypt, .decrypt = des_cbc_decrypt, .ssh2_id = "des-cbc@ssh.com", .blksize = 8, .real_keybits = 56, .padded_keybytes = 8, .flags = SSH_CIPHER_IS_CBC, .text_name = "single-DES CBC", }; static const ssh_cipheralg *const des_list[] = { &ssh_des, &ssh_des_sshcom_ssh2 }; const ssh2_ciphers ssh2_des = { lenof(des_list), des_list }; /* ---------------------------------------------------------------------- * Triple-DES CBC, SSH-2 style. The CBC mode treats the three * invocations of DES as a single unified cipher, and surrounds it * with just one layer of CBC, so only one IV is needed. */ struct des3_cbc1_ctx { des_keysched sched[3]; LR iv; ssh_cipher ciph; }; static ssh_cipher *des3_cbc1_new(const ssh_cipheralg *alg) { struct des3_cbc1_ctx *ctx = snew(struct des3_cbc1_ctx); ctx->ciph.vt = alg; return &ctx->ciph; } static void des3_cbc1_free(ssh_cipher *ciph) { struct des3_cbc1_ctx *ctx = container_of(ciph, struct des3_cbc1_ctx, ciph); smemclr(ctx, sizeof(*ctx)); sfree(ctx); } static void des3_cbc1_setkey(ssh_cipher *ciph, const void *vkey) { struct des3_cbc1_ctx *ctx = container_of(ciph, struct des3_cbc1_ctx, ciph); const uint8_t *key = (const uint8_t *)vkey; for (size_t i = 0; i < 3; i++) des_key_setup(GET_64BIT_MSB_FIRST(key + 8*i), &ctx->sched[i]); } static void des3_cbc1_setiv(ssh_cipher *ciph, const void *iv) { struct des3_cbc1_ctx *ctx = container_of(ciph, struct des3_cbc1_ctx, ciph); ctx->iv = des_load_lr(iv); } static void des3_cbc1_cbc_encrypt(ssh_cipher *ciph, void *vdata, int len) { struct des3_cbc1_ctx *ctx = container_of(ciph, struct des3_cbc1_ctx, ciph); uint8_t *data = (uint8_t *)vdata; for (; len > 0; len -= 8, data += 8) { LR plaintext = des_load_lr(data); LR cipher_in = des_xor_lr(plaintext, ctx->iv); /* Run three copies of the cipher, without undoing and redoing * IP/FP in between. */ LR lr = des_IP(cipher_in); lr = des_inner_cipher(lr, &ctx->sched[0], ENCIPHER); lr = des_inner_cipher(lr, &ctx->sched[1], DECIPHER); lr = des_inner_cipher(lr, &ctx->sched[2], ENCIPHER); LR ciphertext = des_FP(lr); des_store_lr(data, ciphertext); ctx->iv = ciphertext; } } static void des3_cbc1_cbc_decrypt(ssh_cipher *ciph, void *vdata, int len) { struct des3_cbc1_ctx *ctx = container_of(ciph, struct des3_cbc1_ctx, ciph); uint8_t *data = (uint8_t *)vdata; for (; len > 0; len -= 8, data += 8) { LR ciphertext = des_load_lr(data); /* Similarly to encryption, but with the order reversed. */ LR lr = des_IP(ciphertext); lr = des_inner_cipher(lr, &ctx->sched[2], DECIPHER); lr = des_inner_cipher(lr, &ctx->sched[1], ENCIPHER); lr = des_inner_cipher(lr, &ctx->sched[0], DECIPHER); LR cipher_out = des_FP(lr); LR plaintext = des_xor_lr(cipher_out, ctx->iv); des_store_lr(data, plaintext); ctx->iv = ciphertext; } } const ssh_cipheralg ssh_3des_ssh2 = { .new = des3_cbc1_new, .free = des3_cbc1_free, .setiv = des3_cbc1_setiv, .setkey = des3_cbc1_setkey, .encrypt = des3_cbc1_cbc_encrypt, .decrypt = des3_cbc1_cbc_decrypt, .ssh2_id = "3des-cbc", .blksize = 8, .real_keybits = 168, .padded_keybytes = 24, .flags = SSH_CIPHER_IS_CBC, .text_name = "triple-DES CBC", }; /* ---------------------------------------------------------------------- * Triple-DES in SDCTR mode. Again, the three DES instances are * treated as one big cipher, with a single counter encrypted through * all three. */ #define SDCTR_WORDS (8 / BIGNUM_INT_BYTES) struct des3_sdctr_ctx { des_keysched sched[3]; BignumInt counter[SDCTR_WORDS]; ssh_cipher ciph; }; static ssh_cipher *des3_sdctr_new(const ssh_cipheralg *alg) { struct des3_sdctr_ctx *ctx = snew(struct des3_sdctr_ctx); ctx->ciph.vt = alg; return &ctx->ciph; } static void des3_sdctr_free(ssh_cipher *ciph) { struct des3_sdctr_ctx *ctx = container_of( ciph, struct des3_sdctr_ctx, ciph); smemclr(ctx, sizeof(*ctx)); sfree(ctx); } static void des3_sdctr_setkey(ssh_cipher *ciph, const void *vkey) { struct des3_sdctr_ctx *ctx = container_of( ciph, struct des3_sdctr_ctx, ciph); const uint8_t *key = (const uint8_t *)vkey; for (size_t i = 0; i < 3; i++) des_key_setup(GET_64BIT_MSB_FIRST(key + 8*i), &ctx->sched[i]); } static void des3_sdctr_setiv(ssh_cipher *ciph, const void *viv) { struct des3_sdctr_ctx *ctx = container_of( ciph, struct des3_sdctr_ctx, ciph); const uint8_t *iv = (const uint8_t *)viv; /* Import the initial counter value into the internal representation */ for (unsigned i = 0; i < SDCTR_WORDS; i++) ctx->counter[i] = GET_BIGNUMINT_MSB_FIRST( iv + 8 - BIGNUM_INT_BYTES - i*BIGNUM_INT_BYTES); } static void des3_sdctr_encrypt_decrypt(ssh_cipher *ciph, void *vdata, int len) { struct des3_sdctr_ctx *ctx = container_of( ciph, struct des3_sdctr_ctx, ciph); uint8_t *data = (uint8_t *)vdata; uint8_t iv_buf[8]; for (; len > 0; len -= 8, data += 8) { /* Format the counter value into the buffer. */ for (unsigned i = 0; i < SDCTR_WORDS; i++) PUT_BIGNUMINT_MSB_FIRST( iv_buf + 8 - BIGNUM_INT_BYTES - i*BIGNUM_INT_BYTES, ctx->counter[i]); /* Increment the counter. */ BignumCarry carry = 1; for (unsigned i = 0; i < SDCTR_WORDS; i++) BignumADC(ctx->counter[i], carry, ctx->counter[i], 0, carry); /* Triple-encrypt the counter value from the IV. */ LR lr = des_IP(des_load_lr(iv_buf)); lr = des_inner_cipher(lr, &ctx->sched[0], ENCIPHER); lr = des_inner_cipher(lr, &ctx->sched[1], DECIPHER); lr = des_inner_cipher(lr, &ctx->sched[2], ENCIPHER); LR keystream = des_FP(lr); LR input = des_load_lr(data); LR output = des_xor_lr(input, keystream); des_store_lr(data, output); } smemclr(iv_buf, sizeof(iv_buf)); } const ssh_cipheralg ssh_3des_ssh2_ctr = { .new = des3_sdctr_new, .free = des3_sdctr_free, .setiv = des3_sdctr_setiv, .setkey = des3_sdctr_setkey, .encrypt = des3_sdctr_encrypt_decrypt, .decrypt = des3_sdctr_encrypt_decrypt, .ssh2_id = "3des-ctr", .blksize = 8, .real_keybits = 168, .padded_keybytes = 24, .flags = 0, .text_name = "triple-DES SDCTR", }; static const ssh_cipheralg *const des3_list[] = { &ssh_3des_ssh2_ctr, &ssh_3des_ssh2 }; const ssh2_ciphers ssh2_3des = { lenof(des3_list), des3_list }; /* ---------------------------------------------------------------------- * Triple-DES, SSH-1 style. SSH-1 replicated the whole CBC structure * three times, so there have to be three separate IVs, one in each * layer. */ struct des3_cbc3_ctx { des_keysched sched[3]; LR iv[3]; ssh_cipher ciph; }; static ssh_cipher *des3_cbc3_new(const ssh_cipheralg *alg) { struct des3_cbc3_ctx *ctx = snew(struct des3_cbc3_ctx); ctx->ciph.vt = alg; return &ctx->ciph; } static void des3_cbc3_free(ssh_cipher *ciph) { struct des3_cbc3_ctx *ctx = container_of(ciph, struct des3_cbc3_ctx, ciph); smemclr(ctx, sizeof(*ctx)); sfree(ctx); } static void des3_cbc3_setkey(ssh_cipher *ciph, const void *vkey) { struct des3_cbc3_ctx *ctx = container_of(ciph, struct des3_cbc3_ctx, ciph); const uint8_t *key = (const uint8_t *)vkey; for (size_t i = 0; i < 3; i++) des_key_setup(GET_64BIT_MSB_FIRST(key + 8*i), &ctx->sched[i]); } static void des3_cbc3_setiv(ssh_cipher *ciph, const void *viv) { struct des3_cbc3_ctx *ctx = container_of(ciph, struct des3_cbc3_ctx, ciph); /* * In principle, we ought to provide an interface for the user to * input 24 instead of 8 bytes of IV. But that would make this an * ugly exception to the otherwise universal rule that IV size = * cipher block size, and there's really no need to violate that * rule given that this is a historical one-off oddity and SSH-1 * always initialises all three IVs to zero anyway. So we fudge it * by just setting all the IVs to the same value. */ LR iv = des_load_lr(viv); /* But we store the IVs in permuted form, so that we can handle * all three CBC layers without having to do IP/FP in between. */ iv = des_IP(iv); for (size_t i = 0; i < 3; i++) ctx->iv[i] = iv; } static void des3_cbc3_cbc_encrypt(ssh_cipher *ciph, void *vdata, int len) { struct des3_cbc3_ctx *ctx = container_of(ciph, struct des3_cbc3_ctx, ciph); uint8_t *data = (uint8_t *)vdata; for (; len > 0; len -= 8, data += 8) { /* Load and IP the input. */ LR plaintext = des_IP(des_load_lr(data)); LR lr = plaintext; /* Do three passes of CBC, with the middle one inverted. */ lr = des_xor_lr(lr, ctx->iv[0]); lr = des_inner_cipher(lr, &ctx->sched[0], ENCIPHER); ctx->iv[0] = lr; LR ciphertext = lr; lr = des_inner_cipher(ciphertext, &ctx->sched[1], DECIPHER); lr = des_xor_lr(lr, ctx->iv[1]); ctx->iv[1] = ciphertext; lr = des_xor_lr(lr, ctx->iv[2]); lr = des_inner_cipher(lr, &ctx->sched[2], ENCIPHER); ctx->iv[2] = lr; des_store_lr(data, des_FP(lr)); } } static void des3_cbc3_cbc_decrypt(ssh_cipher *ciph, void *vdata, int len) { struct des3_cbc3_ctx *ctx = container_of(ciph, struct des3_cbc3_ctx, ciph); uint8_t *data = (uint8_t *)vdata; for (; len > 0; len -= 8, data += 8) { /* Load and IP the input */ LR lr = des_IP(des_load_lr(data)); LR ciphertext; /* Do three passes of CBC, with the middle one inverted. */ ciphertext = lr; lr = des_inner_cipher(ciphertext, &ctx->sched[2], DECIPHER); lr = des_xor_lr(lr, ctx->iv[2]); ctx->iv[2] = ciphertext; lr = des_xor_lr(lr, ctx->iv[1]); lr = des_inner_cipher(lr, &ctx->sched[1], ENCIPHER); ctx->iv[1] = lr; ciphertext = lr; lr = des_inner_cipher(ciphertext, &ctx->sched[0], DECIPHER); lr = des_xor_lr(lr, ctx->iv[0]); ctx->iv[0] = ciphertext; des_store_lr(data, des_FP(lr)); } } const ssh_cipheralg ssh_3des_ssh1 = { .new = des3_cbc3_new, .free = des3_cbc3_free, .setiv = des3_cbc3_setiv, .setkey = des3_cbc3_setkey, .encrypt = des3_cbc3_cbc_encrypt, .decrypt = des3_cbc3_cbc_decrypt, .blksize = 8, .real_keybits = 168, .padded_keybytes = 24, .flags = SSH_CIPHER_IS_CBC, .text_name = "triple-DES inner-CBC", }; putty-0.76/sshdh.c0000644000175000017500000001624314072266312011042 00000000000000/* * Diffie-Hellman implementation for PuTTY. */ #include #include "ssh.h" #include "misc.h" #include "mpint.h" struct dh_ctx { mp_int *x, *e, *p, *q, *g; }; struct dh_extra { bool gex; void (*construct)(dh_ctx *ctx); }; static void dh_group1_construct(dh_ctx *ctx) { ctx->p = MP_LITERAL(0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF); ctx->g = mp_from_integer(2); } static void dh_group14_construct(dh_ctx *ctx) { ctx->p = MP_LITERAL(0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF); ctx->g = mp_from_integer(2); } static const struct dh_extra extra_group1 = { false, dh_group1_construct, }; static const ssh_kex ssh_diffiehellman_group1_sha1 = { "diffie-hellman-group1-sha1", "group1", KEXTYPE_DH, &ssh_sha1, &extra_group1, }; static const ssh_kex *const group1_list[] = { &ssh_diffiehellman_group1_sha1 }; const ssh_kexes ssh_diffiehellman_group1 = { lenof(group1_list), group1_list }; static const struct dh_extra extra_group14 = { false, dh_group14_construct, }; static const ssh_kex ssh_diffiehellman_group14_sha256 = { "diffie-hellman-group14-sha256", "group14", KEXTYPE_DH, &ssh_sha256, &extra_group14, }; static const ssh_kex ssh_diffiehellman_group14_sha1 = { "diffie-hellman-group14-sha1", "group14", KEXTYPE_DH, &ssh_sha1, &extra_group14, }; static const ssh_kex *const group14_list[] = { &ssh_diffiehellman_group14_sha256, &ssh_diffiehellman_group14_sha1 }; const ssh_kexes ssh_diffiehellman_group14 = { lenof(group14_list), group14_list }; static const struct dh_extra extra_gex = { true }; static const ssh_kex ssh_diffiehellman_gex_sha256 = { "diffie-hellman-group-exchange-sha256", NULL, KEXTYPE_DH, &ssh_sha256, &extra_gex, }; static const ssh_kex ssh_diffiehellman_gex_sha1 = { "diffie-hellman-group-exchange-sha1", NULL, KEXTYPE_DH, &ssh_sha1, &extra_gex, }; static const ssh_kex *const gex_list[] = { &ssh_diffiehellman_gex_sha256, &ssh_diffiehellman_gex_sha1 }; const ssh_kexes ssh_diffiehellman_gex = { lenof(gex_list), gex_list }; /* * Suffix on GSSAPI SSH protocol identifiers that indicates Kerberos 5 * as the mechanism. * * This suffix is the base64-encoded MD5 hash of the byte sequence * 06 09 2A 86 48 86 F7 12 01 02 02, which in turn is the ASN.1 DER * encoding of the object ID 1.2.840.113554.1.2.2 which designates * Kerberos v5. * * (The same encoded OID, minus the two-byte DER header, is defined in * pgssapi.c as GSS_MECH_KRB5.) */ #define GSS_KRB5_OID_HASH "toWM5Slw5Ew8Mqkay+al2g==" static const ssh_kex ssh_gssk5_diffiehellman_gex_sha1 = { "gss-gex-sha1-" GSS_KRB5_OID_HASH, NULL, KEXTYPE_GSS, &ssh_sha1, &extra_gex, }; static const ssh_kex ssh_gssk5_diffiehellman_group14_sha1 = { "gss-group14-sha1-" GSS_KRB5_OID_HASH, "group14", KEXTYPE_GSS, &ssh_sha1, &extra_group14, }; static const ssh_kex ssh_gssk5_diffiehellman_group1_sha1 = { "gss-group1-sha1-" GSS_KRB5_OID_HASH, "group1", KEXTYPE_GSS, &ssh_sha1, &extra_group1, }; static const ssh_kex *const gssk5_sha1_kex_list[] = { &ssh_gssk5_diffiehellman_gex_sha1, &ssh_gssk5_diffiehellman_group14_sha1, &ssh_gssk5_diffiehellman_group1_sha1 }; const ssh_kexes ssh_gssk5_sha1_kex = { lenof(gssk5_sha1_kex_list), gssk5_sha1_kex_list }; /* * Common DH initialisation. */ static void dh_init(dh_ctx *ctx) { ctx->q = mp_rshift_fixed(ctx->p, 1); ctx->x = ctx->e = NULL; } bool dh_is_gex(const ssh_kex *kex) { const struct dh_extra *extra = (const struct dh_extra *)kex->extra; return extra->gex; } /* * Initialise DH for a standard group. */ dh_ctx *dh_setup_group(const ssh_kex *kex) { const struct dh_extra *extra = (const struct dh_extra *)kex->extra; assert(!extra->gex); dh_ctx *ctx = snew(dh_ctx); extra->construct(ctx); dh_init(ctx); return ctx; } /* * Initialise DH for a server-supplied group. */ dh_ctx *dh_setup_gex(mp_int *pval, mp_int *gval) { dh_ctx *ctx = snew(dh_ctx); ctx->p = mp_copy(pval); ctx->g = mp_copy(gval); dh_init(ctx); return ctx; } /* * Return size of DH modulus p. */ int dh_modulus_bit_size(const dh_ctx *ctx) { return mp_get_nbits(ctx->p); } /* * Clean up and free a context. */ void dh_cleanup(dh_ctx *ctx) { if (ctx->x) mp_free(ctx->x); if (ctx->e) mp_free(ctx->e); if (ctx->p) mp_free(ctx->p); if (ctx->g) mp_free(ctx->g); if (ctx->q) mp_free(ctx->q); sfree(ctx); } /* * DH stage 1: invent a number x between 1 and q, and compute e = * g^x mod p. Return e. * * If `nbits' is greater than zero, it is used as an upper limit * for the number of bits in x. This is safe provided that (a) you * use twice as many bits in x as the number of bits you expect to * use in your session key, and (b) the DH group is a safe prime * (which SSH demands that it must be). * * P. C. van Oorschot, M. J. Wiener * "On Diffie-Hellman Key Agreement with Short Exponents". * Advances in Cryptology: Proceedings of Eurocrypt '96 * Springer-Verlag, May 1996. */ mp_int *dh_create_e(dh_ctx *ctx, int nbits) { /* * Lower limit is just 2. */ mp_int *lo = mp_from_integer(2); /* * Upper limit. */ mp_int *hi = mp_copy(ctx->q); mp_sub_integer_into(hi, hi, 1); if (nbits) { mp_int *pow2 = mp_power_2(nbits+1); mp_min_into(pow2, pow2, hi); mp_free(hi); hi = pow2; } /* * Make a random number in that range. */ ctx->x = mp_random_in_range(lo, hi); mp_free(lo); mp_free(hi); /* * Now compute e = g^x mod p. */ ctx->e = mp_modpow(ctx->g, ctx->x, ctx->p); return ctx->e; } /* * DH stage 2-epsilon: given a number f, validate it to ensure it's in * range. (RFC 4253 section 8: "Values of 'e' or 'f' that are not in * the range [1, p-1] MUST NOT be sent or accepted by either side." * Also, we rule out 1 and p-1 too, since that's easy to do and since * they lead to obviously weak keys that even a passive eavesdropper * can figure out.) */ const char *dh_validate_f(dh_ctx *ctx, mp_int *f) { if (!mp_hs_integer(f, 2)) { return "f value received is too small"; } else { mp_int *pm1 = mp_copy(ctx->p); mp_sub_integer_into(pm1, pm1, 1); unsigned cmp = mp_cmp_hs(f, pm1); mp_free(pm1); if (cmp) return "f value received is too large"; } return NULL; } /* * DH stage 2: given a number f, compute K = f^x mod p. */ mp_int *dh_find_K(dh_ctx *ctx, mp_int *f) { return mp_modpow(f, ctx->x, ctx->p); } putty-0.76/sshdss.c0000644000175000017500000003357714072266312011251 00000000000000/* * Digital Signature Standard implementation for PuTTY. */ #include #include #include #include "ssh.h" #include "mpint.h" #include "misc.h" static void dss_freekey(ssh_key *key); /* forward reference */ static ssh_key *dss_new_pub(const ssh_keyalg *self, ptrlen data) { BinarySource src[1]; struct dss_key *dss; BinarySource_BARE_INIT_PL(src, data); if (!ptrlen_eq_string(get_string(src), "ssh-dss")) return NULL; dss = snew(struct dss_key); dss->sshk.vt = &ssh_dss; dss->p = get_mp_ssh2(src); dss->q = get_mp_ssh2(src); dss->g = get_mp_ssh2(src); dss->y = get_mp_ssh2(src); dss->x = NULL; if (get_err(src) || mp_eq_integer(dss->p, 0) || mp_eq_integer(dss->q, 0)) { /* Invalid key. */ dss_freekey(&dss->sshk); return NULL; } return &dss->sshk; } static void dss_freekey(ssh_key *key) { struct dss_key *dss = container_of(key, struct dss_key, sshk); if (dss->p) mp_free(dss->p); if (dss->q) mp_free(dss->q); if (dss->g) mp_free(dss->g); if (dss->y) mp_free(dss->y); if (dss->x) mp_free(dss->x); sfree(dss); } static void append_hex_to_strbuf(strbuf *sb, mp_int *x) { if (sb->len > 0) put_byte(sb, ','); put_data(sb, "0x", 2); char *hex = mp_get_hex(x); size_t hexlen = strlen(hex); put_data(sb, hex, hexlen); smemclr(hex, hexlen); sfree(hex); } static char *dss_cache_str(ssh_key *key) { struct dss_key *dss = container_of(key, struct dss_key, sshk); strbuf *sb = strbuf_new(); if (!dss->p) { strbuf_free(sb); return NULL; } append_hex_to_strbuf(sb, dss->p); append_hex_to_strbuf(sb, dss->q); append_hex_to_strbuf(sb, dss->g); append_hex_to_strbuf(sb, dss->y); return strbuf_to_str(sb); } static key_components *dss_components(ssh_key *key) { struct dss_key *dss = container_of(key, struct dss_key, sshk); key_components *kc = key_components_new(); key_components_add_text(kc, "key_type", "DSA"); assert(dss->p); key_components_add_mp(kc, "p", dss->p); key_components_add_mp(kc, "q", dss->q); key_components_add_mp(kc, "g", dss->g); key_components_add_mp(kc, "public_y", dss->y); if (dss->x) key_components_add_mp(kc, "private_x", dss->x); return kc; } static char *dss_invalid(ssh_key *key, unsigned flags) { /* No validity criterion will stop us from using a DSA key at all */ return NULL; } static bool dss_verify(ssh_key *key, ptrlen sig, ptrlen data) { struct dss_key *dss = container_of(key, struct dss_key, sshk); BinarySource src[1]; unsigned char hash[20]; bool toret; if (!dss->p) return false; BinarySource_BARE_INIT_PL(src, sig); /* * Commercial SSH (2.0.13) and OpenSSH disagree over the format * of a DSA signature. OpenSSH is in line with RFC 4253: * it uses a string "ssh-dss", followed by a 40-byte string * containing two 160-bit integers end-to-end. Commercial SSH * can't be bothered with the header bit, and considers a DSA * signature blob to be _just_ the 40-byte string containing * the two 160-bit integers. We tell them apart by measuring * the length: length 40 means the commercial-SSH bug, anything * else is assumed to be RFC-compliant. */ if (sig.len != 40) { /* bug not present; read admin fields */ ptrlen type = get_string(src); sig = get_string(src); if (get_err(src) || !ptrlen_eq_string(type, "ssh-dss") || sig.len != 40) return false; } /* Now we're sitting on a 40-byte string for sure. */ mp_int *r = mp_from_bytes_be(make_ptrlen(sig.ptr, 20)); mp_int *s = mp_from_bytes_be(make_ptrlen((const char *)sig.ptr + 20, 20)); if (!r || !s) { if (r) mp_free(r); if (s) mp_free(s); return false; } /* Basic sanity checks: 0 < r,s < q */ unsigned invalid = 0; invalid |= mp_eq_integer(r, 0); invalid |= mp_eq_integer(s, 0); invalid |= mp_cmp_hs(r, dss->q); invalid |= mp_cmp_hs(s, dss->q); if (invalid) { mp_free(r); mp_free(s); return false; } /* * Step 1. w <- s^-1 mod q. */ mp_int *w = mp_invert(s, dss->q); if (!w) { mp_free(r); mp_free(s); return false; } /* * Step 2. u1 <- SHA(message) * w mod q. */ hash_simple(&ssh_sha1, data, hash); mp_int *sha = mp_from_bytes_be(make_ptrlen(hash, 20)); mp_int *u1 = mp_modmul(sha, w, dss->q); /* * Step 3. u2 <- r * w mod q. */ mp_int *u2 = mp_modmul(r, w, dss->q); /* * Step 4. v <- (g^u1 * y^u2 mod p) mod q. */ mp_int *gu1p = mp_modpow(dss->g, u1, dss->p); mp_int *yu2p = mp_modpow(dss->y, u2, dss->p); mp_int *gu1yu2p = mp_modmul(gu1p, yu2p, dss->p); mp_int *v = mp_mod(gu1yu2p, dss->q); /* * Step 5. v should now be equal to r. */ toret = mp_cmp_eq(v, r); mp_free(w); mp_free(sha); mp_free(u1); mp_free(u2); mp_free(gu1p); mp_free(yu2p); mp_free(gu1yu2p); mp_free(v); mp_free(r); mp_free(s); return toret; } static void dss_public_blob(ssh_key *key, BinarySink *bs) { struct dss_key *dss = container_of(key, struct dss_key, sshk); put_stringz(bs, "ssh-dss"); put_mp_ssh2(bs, dss->p); put_mp_ssh2(bs, dss->q); put_mp_ssh2(bs, dss->g); put_mp_ssh2(bs, dss->y); } static void dss_private_blob(ssh_key *key, BinarySink *bs) { struct dss_key *dss = container_of(key, struct dss_key, sshk); put_mp_ssh2(bs, dss->x); } static ssh_key *dss_new_priv(const ssh_keyalg *self, ptrlen pub, ptrlen priv) { BinarySource src[1]; ssh_key *sshk; struct dss_key *dss; ptrlen hash; unsigned char digest[20]; mp_int *ytest; sshk = dss_new_pub(self, pub); if (!sshk) return NULL; dss = container_of(sshk, struct dss_key, sshk); BinarySource_BARE_INIT_PL(src, priv); dss->x = get_mp_ssh2(src); if (get_err(src)) { dss_freekey(&dss->sshk); return NULL; } /* * Check the obsolete hash in the old DSS key format. */ hash = get_string(src); if (hash.len == 20) { ssh_hash *h = ssh_hash_new(&ssh_sha1); put_mp_ssh2(h, dss->p); put_mp_ssh2(h, dss->q); put_mp_ssh2(h, dss->g); ssh_hash_final(h, digest); if (!smemeq(hash.ptr, digest, 20)) { dss_freekey(&dss->sshk); return NULL; } } /* * Now ensure g^x mod p really is y. */ ytest = mp_modpow(dss->g, dss->x, dss->p); if (!mp_cmp_eq(ytest, dss->y)) { mp_free(ytest); dss_freekey(&dss->sshk); return NULL; } mp_free(ytest); return &dss->sshk; } static ssh_key *dss_new_priv_openssh(const ssh_keyalg *self, BinarySource *src) { struct dss_key *dss; dss = snew(struct dss_key); dss->sshk.vt = &ssh_dss; dss->p = get_mp_ssh2(src); dss->q = get_mp_ssh2(src); dss->g = get_mp_ssh2(src); dss->y = get_mp_ssh2(src); dss->x = get_mp_ssh2(src); if (get_err(src) || mp_eq_integer(dss->q, 0) || mp_eq_integer(dss->p, 0)) { /* Invalid key. */ dss_freekey(&dss->sshk); return NULL; } return &dss->sshk; } static void dss_openssh_blob(ssh_key *key, BinarySink *bs) { struct dss_key *dss = container_of(key, struct dss_key, sshk); put_mp_ssh2(bs, dss->p); put_mp_ssh2(bs, dss->q); put_mp_ssh2(bs, dss->g); put_mp_ssh2(bs, dss->y); put_mp_ssh2(bs, dss->x); } static int dss_pubkey_bits(const ssh_keyalg *self, ptrlen pub) { ssh_key *sshk; struct dss_key *dss; int ret; sshk = dss_new_pub(self, pub); if (!sshk) return -1; dss = container_of(sshk, struct dss_key, sshk); ret = mp_get_nbits(dss->p); dss_freekey(&dss->sshk); return ret; } mp_int *dss_gen_k(const char *id_string, mp_int *modulus, mp_int *private_key, unsigned char *digest, int digest_len) { /* * The basic DSS signing algorithm is: * * - invent a random k between 1 and q-1 (exclusive). * - Compute r = (g^k mod p) mod q. * - Compute s = k^-1 * (hash + x*r) mod q. * * This has the dangerous properties that: * * - if an attacker in possession of the public key _and_ the * signature (for example, the host you just authenticated * to) can guess your k, he can reverse the computation of s * and work out x = r^-1 * (s*k - hash) mod q. That is, he * can deduce the private half of your key, and masquerade * as you for as long as the key is still valid. * * - since r is a function purely of k and the public key, if * the attacker only has a _range of possibilities_ for k * it's easy for him to work through them all and check each * one against r; he'll never be unsure of whether he's got * the right one. * * - if you ever sign two different hashes with the same k, it * will be immediately obvious because the two signatures * will have the same r, and moreover an attacker in * possession of both signatures (and the public key of * course) can compute k = (hash1-hash2) * (s1-s2)^-1 mod q, * and from there deduce x as before. * * - the Bleichenbacher attack on DSA makes use of methods of * generating k which are significantly non-uniformly * distributed; in particular, generating a 160-bit random * number and reducing it mod q is right out. * * For this reason we must be pretty careful about how we * generate our k. Since this code runs on Windows, with no * particularly good system entropy sources, we can't trust our * RNG itself to produce properly unpredictable data. Hence, we * use a totally different scheme instead. * * What we do is to take a SHA-512 (_big_) hash of the private * key x, and then feed this into another SHA-512 hash that * also includes the message hash being signed. That is: * * proto_k = SHA512 ( SHA512(x) || SHA160(message) ) * * This number is 512 bits long, so reducing it mod q won't be * noticeably non-uniform. So * * k = proto_k mod q * * This has the interesting property that it's _deterministic_: * signing the same hash twice with the same key yields the * same signature. * * Despite this determinism, it's still not predictable to an * attacker, because in order to repeat the SHA-512 * construction that created it, the attacker would have to * know the private key value x - and by assumption he doesn't, * because if he knew that he wouldn't be attacking k! * * (This trick doesn't, _per se_, protect against reuse of k. * Reuse of k is left to chance; all it does is prevent * _excessively high_ chances of reuse of k due to entropy * problems.) * * Thanks to Colin Plumb for the general idea of using x to * ensure k is hard to guess, and to the Cambridge University * Computer Security Group for helping to argue out all the * fine details. */ ssh_hash *h; unsigned char digest512[64]; /* * Hash some identifying text plus x. */ h = ssh_hash_new(&ssh_sha512); put_asciz(h, id_string); put_mp_ssh2(h, private_key); ssh_hash_digest(h, digest512); /* * Now hash that digest plus the message hash. */ ssh_hash_reset(h); put_data(h, digest512, sizeof(digest512)); put_data(h, digest, digest_len); ssh_hash_final(h, digest512); /* * Now convert the result into a bignum, and coerce it to the * range [2,q), which we do by reducing it mod q-2 and adding 2. */ mp_int *modminus2 = mp_copy(modulus); mp_sub_integer_into(modminus2, modminus2, 2); mp_int *proto_k = mp_from_bytes_be(make_ptrlen(digest512, 64)); mp_int *k = mp_mod(proto_k, modminus2); mp_free(proto_k); mp_free(modminus2); mp_add_integer_into(k, k, 2); smemclr(digest512, sizeof(digest512)); return k; } static void dss_sign(ssh_key *key, ptrlen data, unsigned flags, BinarySink *bs) { struct dss_key *dss = container_of(key, struct dss_key, sshk); unsigned char digest[20]; int i; hash_simple(&ssh_sha1, data, digest); mp_int *k = dss_gen_k("DSA deterministic k generator", dss->q, dss->x, digest, sizeof(digest)); mp_int *kinv = mp_invert(k, dss->q); /* k^-1 mod q */ /* * Now we have k, so just go ahead and compute the signature. */ mp_int *gkp = mp_modpow(dss->g, k, dss->p); /* g^k mod p */ mp_int *r = mp_mod(gkp, dss->q); /* r = (g^k mod p) mod q */ mp_free(gkp); mp_int *hash = mp_from_bytes_be(make_ptrlen(digest, 20)); mp_int *xr = mp_mul(dss->x, r); mp_int *hxr = mp_add(xr, hash); /* hash + x*r */ mp_int *s = mp_modmul(kinv, hxr, dss->q); /* s = k^-1 * (hash+x*r) mod q */ mp_free(hxr); mp_free(xr); mp_free(kinv); mp_free(k); mp_free(hash); put_stringz(bs, "ssh-dss"); put_uint32(bs, 40); for (i = 0; i < 20; i++) put_byte(bs, mp_get_byte(r, 19 - i)); for (i = 0; i < 20; i++) put_byte(bs, mp_get_byte(s, 19 - i)); mp_free(r); mp_free(s); } const ssh_keyalg ssh_dss = { .new_pub = dss_new_pub, .new_priv = dss_new_priv, .new_priv_openssh = dss_new_priv_openssh, .freekey = dss_freekey, .invalid = dss_invalid, .sign = dss_sign, .verify = dss_verify, .public_blob = dss_public_blob, .private_blob = dss_private_blob, .openssh_blob = dss_openssh_blob, .cache_str = dss_cache_str, .components = dss_components, .pubkey_bits = dss_pubkey_bits, .ssh_id = "ssh-dss", .cache_id = "dss", }; putty-0.76/sshdssg.c0000644000175000017500000000625414072266312011410 00000000000000/* * DSS key generation. */ #include "misc.h" #include "ssh.h" #include "sshkeygen.h" #include "mpint.h" int dsa_generate(struct dss_key *key, int bits, PrimeGenerationContext *pgc, ProgressReceiver *prog) { /* * Progress-reporting setup. * * DSA generation involves three potentially long jobs: inventing * the small prime q, the large prime p, and finding an order-q * element of the multiplicative group of p. * * The latter is done by finding an element whose order is * _divisible_ by q and raising it to the power of (p-1)/q. Every * element whose order is not divisible by q is a qth power of q * distinct elements whose order _is_ divisible by q, so the * probability of not finding a suitable element on the first try * is in the region of 1/q, i.e. at most 2^-159. * * (So the probability of success will end up indistinguishable * from 1 in IEEE standard floating point! But what can you do.) */ ProgressPhase phase_q = primegen_add_progress_phase(pgc, prog, 160); ProgressPhase phase_p = primegen_add_progress_phase(pgc, prog, bits); double g_failure_probability = 1.0 / (double)(1ULL << 53) / (double)(1ULL << 53) / (double)(1ULL << 53); ProgressPhase phase_g = progress_add_probabilistic( prog, estimate_modexp_cost(bits), 1.0 - g_failure_probability); progress_ready(prog); PrimeCandidateSource *pcs; /* * Generate q: a prime of length 160. */ progress_start_phase(prog, phase_q); pcs = pcs_new(160); mp_int *q = primegen_generate(pgc, pcs, prog); progress_report_phase_complete(prog); /* * Now generate p: a prime of length `bits', such that p-1 is * divisible by q. */ progress_start_phase(prog, phase_p); pcs = pcs_new(bits); pcs_require_residue_1_mod_prime(pcs, q); mp_int *p = primegen_generate(pgc, pcs, prog); progress_report_phase_complete(prog); /* * 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. */ progress_start_phase(prog, phase_g); mp_int *power = mp_div(p, q); /* this is floor(p/q) == (p-1)/q */ mp_int *h = mp_from_integer(2); mp_int *g; while (1) { progress_report_attempt(prog); g = mp_modpow(h, power, p); if (mp_hs_integer(g, 2)) break; /* got one */ mp_free(g); mp_add_integer_into(h, h, 1); } mp_free(h); mp_free(power); progress_report_phase_complete(prog); /* * Now we're nearly done. All we need now is our private key x, * which should be a number between 1 and q-1 exclusive, and * our public key y = g^x mod p. */ mp_int *two = mp_from_integer(2); mp_int *qm1 = mp_copy(q); mp_sub_integer_into(qm1, qm1, 1); mp_int *x = mp_random_in_range(two, qm1); mp_free(two); mp_free(qm1); key->sshk.vt = &ssh_dss; key->p = p; key->q = q; key->g = g; key->x = x; key->y = mp_modpow(key->g, key->x, key->p); return 1; } putty-0.76/sshecc.c0000644000175000017500000015075114072266312011204 00000000000000/* * Elliptic-curve crypto module for PuTTY * Implements the three required curves, no optional curves * * NOTE: Only curves on prime field are handled by the maths functions * in Weierstrass form using Jacobian co-ordinates. * * Montgomery form curves are supported for DH. (Curve25519) * * Edwards form curves are supported for DSA. (Ed25519, Ed448) */ /* * References: * * Elliptic curves in SSH are specified in RFC 5656: * http://tools.ietf.org/html/rfc5656 * * That specification delegates details of public key formatting and a * lot of underlying mechanism to SEC 1: * http://www.secg.org/sec1-v2.pdf * * Montgomery maths from: * Handbook of elliptic and hyperelliptic curve cryptography, Chapter 13 * http://cs.ucsb.edu/~koc/ccs130h/2013/EllipticHyperelliptic-CohenFrey.pdf * * Curve25519 spec from libssh (with reference to other things in the * libssh code): * https://git.libssh.org/users/aris/libssh.git/tree/doc/curve25519-sha256@libssh.org.txt * * Edwards DSA: * http://ed25519.cr.yp.to/ed25519-20110926.pdf */ #include #include #include "ssh.h" #include "mpint.h" #include "ecc.h" /* ---------------------------------------------------------------------- * Elliptic curve definitions */ static void initialise_common( struct ec_curve *curve, EllipticCurveType type, mp_int *p, unsigned extrabits) { curve->type = type; curve->p = mp_copy(p); curve->fieldBits = mp_get_nbits(p); curve->fieldBytes = (curve->fieldBits + extrabits + 7) / 8; } static void initialise_wcurve( struct ec_curve *curve, mp_int *p, mp_int *a, mp_int *b, mp_int *nonsquare, mp_int *G_x, mp_int *G_y, mp_int *G_order) { initialise_common(curve, EC_WEIERSTRASS, p, 0); curve->w.wc = ecc_weierstrass_curve(p, a, b, nonsquare); curve->w.G = ecc_weierstrass_point_new(curve->w.wc, G_x, G_y); curve->w.G_order = mp_copy(G_order); } static void initialise_mcurve( struct ec_curve *curve, mp_int *p, mp_int *a, mp_int *b, mp_int *G_x, unsigned log2_cofactor) { initialise_common(curve, EC_MONTGOMERY, p, 0); curve->m.mc = ecc_montgomery_curve(p, a, b); curve->m.log2_cofactor = log2_cofactor; curve->m.G = ecc_montgomery_point_new(curve->m.mc, G_x); } static void initialise_ecurve( struct ec_curve *curve, mp_int *p, mp_int *d, mp_int *a, mp_int *nonsquare, mp_int *G_x, mp_int *G_y, mp_int *G_order, unsigned log2_cofactor) { /* Ensure curve->fieldBytes is long enough to store an extra bit * for a compressed point */ initialise_common(curve, EC_EDWARDS, p, 1); curve->e.ec = ecc_edwards_curve(p, d, a, nonsquare); curve->e.log2_cofactor = log2_cofactor; curve->e.G = ecc_edwards_point_new(curve->e.ec, G_x, G_y); curve->e.G_order = mp_copy(G_order); } static struct ec_curve *ec_p256(void) { static struct ec_curve curve = { 0 }; static bool initialised = false; if (!initialised) { mp_int *p = MP_LITERAL(0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff); mp_int *a = MP_LITERAL(0xffffffff00000001000000000000000000000000fffffffffffffffffffffffc); mp_int *b = MP_LITERAL(0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b); mp_int *G_x = MP_LITERAL(0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296); mp_int *G_y = MP_LITERAL(0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5); mp_int *G_order = MP_LITERAL(0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551); mp_int *nonsquare_mod_p = mp_from_integer(3); initialise_wcurve(&curve, p, a, b, nonsquare_mod_p, G_x, G_y, G_order); mp_free(p); mp_free(a); mp_free(b); mp_free(G_x); mp_free(G_y); mp_free(G_order); mp_free(nonsquare_mod_p); curve.textname = curve.name = "nistp256"; /* Now initialised, no need to do it again */ initialised = true; } return &curve; } static struct ec_curve *ec_p384(void) { static struct ec_curve curve = { 0 }; static bool initialised = false; if (!initialised) { mp_int *p = MP_LITERAL(0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000ffffffff); mp_int *a = MP_LITERAL(0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000fffffffc); mp_int *b = MP_LITERAL(0xb3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef); mp_int *G_x = MP_LITERAL(0xaa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7); mp_int *G_y = MP_LITERAL(0x3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f); mp_int *G_order = MP_LITERAL(0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52973); mp_int *nonsquare_mod_p = mp_from_integer(19); initialise_wcurve(&curve, p, a, b, nonsquare_mod_p, G_x, G_y, G_order); mp_free(p); mp_free(a); mp_free(b); mp_free(G_x); mp_free(G_y); mp_free(G_order); mp_free(nonsquare_mod_p); curve.textname = curve.name = "nistp384"; /* Now initialised, no need to do it again */ initialised = true; } return &curve; } static struct ec_curve *ec_p521(void) { static struct ec_curve curve = { 0 }; static bool initialised = false; if (!initialised) { mp_int *p = MP_LITERAL(0x01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff); mp_int *a = MP_LITERAL(0x01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc); mp_int *b = MP_LITERAL(0x0051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00); mp_int *G_x = MP_LITERAL(0x00c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66); mp_int *G_y = MP_LITERAL(0x011839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650); mp_int *G_order = MP_LITERAL(0x01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386409); mp_int *nonsquare_mod_p = mp_from_integer(3); initialise_wcurve(&curve, p, a, b, nonsquare_mod_p, G_x, G_y, G_order); mp_free(p); mp_free(a); mp_free(b); mp_free(G_x); mp_free(G_y); mp_free(G_order); mp_free(nonsquare_mod_p); curve.textname = curve.name = "nistp521"; /* Now initialised, no need to do it again */ initialised = true; } return &curve; } static struct ec_curve *ec_curve25519(void) { static struct ec_curve curve = { 0 }; static bool initialised = false; if (!initialised) { mp_int *p = MP_LITERAL(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed); mp_int *a = MP_LITERAL(0x0000000000000000000000000000000000000000000000000000000000076d06); mp_int *b = MP_LITERAL(0x0000000000000000000000000000000000000000000000000000000000000001); mp_int *G_x = MP_LITERAL(0x0000000000000000000000000000000000000000000000000000000000000009); initialise_mcurve(&curve, p, a, b, G_x, 3); mp_free(p); mp_free(a); mp_free(b); mp_free(G_x); /* This curve doesn't need a name, because it's never used in * any format that embeds the curve name */ curve.name = NULL; curve.textname = "Curve25519"; /* Now initialised, no need to do it again */ initialised = true; } return &curve; } static struct ec_curve *ec_curve448(void) { static struct ec_curve curve = { 0 }; static bool initialised = false; if (!initialised) { mp_int *p = MP_LITERAL(0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffff); mp_int *a = MP_LITERAL(0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000262a6); mp_int *b = MP_LITERAL(0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001); mp_int *G_x = MP_LITERAL(0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005); initialise_mcurve(&curve, p, a, b, G_x, 2); mp_free(p); mp_free(a); mp_free(b); mp_free(G_x); /* This curve doesn't need a name, because it's never used in * any format that embeds the curve name */ curve.name = NULL; curve.textname = "Curve448"; /* Now initialised, no need to do it again */ initialised = true; } return &curve; } static struct ec_curve *ec_ed25519(void) { static struct ec_curve curve = { 0 }; static bool initialised = false; if (!initialised) { mp_int *p = MP_LITERAL(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed); mp_int *d = MP_LITERAL(0x52036cee2b6ffe738cc740797779e89800700a4d4141d8ab75eb4dca135978a3); mp_int *a = MP_LITERAL(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec); /* == p-1 */ mp_int *G_x = MP_LITERAL(0x216936d3cd6e53fec0a4e231fdd6dc5c692cc7609525a7b2c9562d608f25d51a); mp_int *G_y = MP_LITERAL(0x6666666666666666666666666666666666666666666666666666666666666658); mp_int *G_order = MP_LITERAL(0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed); mp_int *nonsquare_mod_p = mp_from_integer(2); initialise_ecurve(&curve, p, d, a, nonsquare_mod_p, G_x, G_y, G_order, 3); mp_free(p); mp_free(d); mp_free(a); mp_free(G_x); mp_free(G_y); mp_free(G_order); mp_free(nonsquare_mod_p); /* This curve doesn't need a name, because it's never used in * any format that embeds the curve name */ curve.name = NULL; curve.textname = "Ed25519"; /* Now initialised, no need to do it again */ initialised = true; } return &curve; } static struct ec_curve *ec_ed448(void) { static struct ec_curve curve = { 0 }; static bool initialised = false; if (!initialised) { mp_int *p = MP_LITERAL(0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffff); mp_int *d = MP_LITERAL(0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffff6756); /* = p - 39081 */ mp_int *a = MP_LITERAL(0x1); mp_int *G_x = MP_LITERAL(0x4f1970c66bed0ded221d15a622bf36da9e146570470f1767ea6de324a3d3a46412ae1af72ab66511433b80e18b00938e2626a82bc70cc05e); mp_int *G_y = MP_LITERAL(0x693f46716eb6bc248876203756c9c7624bea73736ca3984087789c1e05a0c2d73ad3ff1ce67c39c4fdbd132c4ed7c8ad9808795bf230fa14); mp_int *G_order = MP_LITERAL(0x3fffffffffffffffffffffffffffffffffffffffffffffffffffffff7cca23e9c44edb49aed63690216cc2728dc58f552378c292ab5844f3); mp_int *nonsquare_mod_p = mp_from_integer(7); initialise_ecurve(&curve, p, d, a, nonsquare_mod_p, G_x, G_y, G_order, 2); mp_free(p); mp_free(d); mp_free(a); mp_free(G_x); mp_free(G_y); mp_free(G_order); mp_free(nonsquare_mod_p); /* This curve doesn't need a name, because it's never used in * any format that embeds the curve name */ curve.name = NULL; curve.textname = "Ed448"; /* Now initialised, no need to do it again */ initialised = true; } return &curve; } /* ---------------------------------------------------------------------- * Public point from private */ struct ecsign_extra { struct ec_curve *(*curve)(void); const ssh_hashalg *hash; /* These fields are used by the OpenSSH PEM format importer/exporter */ const unsigned char *oid; int oidlen; /* Some EdDSA instances prefix a string to all hash preimages, to * disambiguate which signature variant they're being used with */ ptrlen hash_prefix; }; WeierstrassPoint *ecdsa_public(mp_int *private_key, const ssh_keyalg *alg) { const struct ecsign_extra *extra = (const struct ecsign_extra *)alg->extra; struct ec_curve *curve = extra->curve(); assert(curve->type == EC_WEIERSTRASS); mp_int *priv_reduced = mp_mod(private_key, curve->p); WeierstrassPoint *toret = ecc_weierstrass_multiply( curve->w.G, priv_reduced); mp_free(priv_reduced); return toret; } static mp_int *eddsa_exponent_from_hash( ptrlen hash, const struct ec_curve *curve) { /* * Make an integer out of the hash data, little-endian. */ assert(hash.len >= curve->fieldBytes); mp_int *e = mp_from_bytes_le(make_ptrlen(hash.ptr, curve->fieldBytes)); /* * Set the highest bit that fits in the modulus, and clear any * above that. */ mp_set_bit(e, curve->fieldBits - 1, 1); mp_reduce_mod_2to(e, curve->fieldBits); /* * Clear a curve-specific number of low bits. */ for (unsigned bit = 0; bit < curve->e.log2_cofactor; bit++) mp_set_bit(e, bit, 0); return e; } EdwardsPoint *eddsa_public(mp_int *private_key, const ssh_keyalg *alg) { const struct ecsign_extra *extra = (const struct ecsign_extra *)alg->extra; struct ec_curve *curve = extra->curve(); assert(curve->type == EC_EDWARDS); ssh_hash *h = ssh_hash_new(extra->hash); for (size_t i = 0; i < curve->fieldBytes; ++i) put_byte(h, mp_get_byte(private_key, i)); unsigned char hash[MAX_HASH_LEN]; ssh_hash_final(h, hash); mp_int *exponent = eddsa_exponent_from_hash( make_ptrlen(hash, extra->hash->hlen), curve); EdwardsPoint *toret = ecc_edwards_multiply(curve->e.G, exponent); mp_free(exponent); return toret; } /* ---------------------------------------------------------------------- * Marshalling and unmarshalling functions */ static mp_int *BinarySource_get_mp_le(BinarySource *src) { return mp_from_bytes_le(get_string(src)); } #define get_mp_le(src) BinarySource_get_mp_le(BinarySource_UPCAST(src)) static void BinarySink_put_mp_le_fixedlen(BinarySink *bs, mp_int *x, size_t bytes) { put_uint32(bs, bytes); for (size_t i = 0; i < bytes; ++i) put_byte(bs, mp_get_byte(x, i)); } #define put_mp_le_fixedlen(bs, x, bytes) \ BinarySink_put_mp_le_fixedlen(BinarySink_UPCAST(bs), x, bytes) static WeierstrassPoint *ecdsa_decode( ptrlen encoded, const struct ec_curve *curve) { assert(curve->type == EC_WEIERSTRASS); BinarySource src[1]; BinarySource_BARE_INIT_PL(src, encoded); unsigned char format_type = get_byte(src); WeierstrassPoint *P; size_t len = get_avail(src); mp_int *x; mp_int *y; switch (format_type) { case 0: /* The identity. */ P = ecc_weierstrass_point_new_identity(curve->w.wc); break; case 2: case 3: /* A compressed point, in which the x-coordinate is stored in * full, and y is deduced from that and a single bit * indicating its parity (stored in the format type byte). */ x = mp_from_bytes_be(get_data(src, len)); P = ecc_weierstrass_point_new_from_x(curve->w.wc, x, format_type & 1); mp_free(x); if (!P) /* this can fail if the input is invalid */ return NULL; break; case 4: /* An uncompressed point: the x,y coordinates are stored in * full. We expect the rest of the string to have even length, * and be divided half and half between the two values. */ if (len % 2 != 0) return NULL; len /= 2; x = mp_from_bytes_be(get_data(src, len)); y = mp_from_bytes_be(get_data(src, len)); P = ecc_weierstrass_point_new(curve->w.wc, x, y); mp_free(x); mp_free(y); break; default: /* An unrecognised type byte. */ return NULL; } /* Verify the point is on the curve */ if (!ecc_weierstrass_point_valid(P)) { ecc_weierstrass_point_free(P); return NULL; } return P; } static WeierstrassPoint *BinarySource_get_wpoint( BinarySource *src, const struct ec_curve *curve) { ptrlen str = get_string(src); if (get_err(src)) return NULL; return ecdsa_decode(str, curve); } #define get_wpoint(src, curve) \ BinarySource_get_wpoint(BinarySource_UPCAST(src), curve) static void BinarySink_put_wpoint( BinarySink *bs, WeierstrassPoint *point, const struct ec_curve *curve, bool bare) { strbuf *sb; BinarySink *bs_inner; if (!bare) { /* * Encapsulate the raw data inside an outermost string layer. */ sb = strbuf_new(); bs_inner = BinarySink_UPCAST(sb); } else { /* * Just write the data directly to the output. */ bs_inner = bs; } if (ecc_weierstrass_is_identity(point)) { put_byte(bs_inner, 0); } else { mp_int *x, *y; ecc_weierstrass_get_affine(point, &x, &y); /* * For ECDSA, we only ever output uncompressed points. */ put_byte(bs_inner, 0x04); for (size_t i = curve->fieldBytes; i--;) put_byte(bs_inner, mp_get_byte(x, i)); for (size_t i = curve->fieldBytes; i--;) put_byte(bs_inner, mp_get_byte(y, i)); mp_free(x); mp_free(y); } if (!bare) put_stringsb(bs, sb); } #define put_wpoint(bs, point, curve, bare) \ BinarySink_put_wpoint(BinarySink_UPCAST(bs), point, curve, bare) static EdwardsPoint *eddsa_decode(ptrlen encoded, const struct ec_curve *curve) { assert(curve->type == EC_EDWARDS); mp_int *y = mp_from_bytes_le(encoded); /* The topmost bit of the encoding isn't part of y, so it stores * the bottom bit of x. Extract it, and zero that bit in y. */ unsigned desired_x_parity = mp_get_bit(y, curve->fieldBytes * 8 - 1); mp_set_bit(y, curve->fieldBytes * 8 - 1, 0); /* What's left should now be within the range of the curve's modulus */ if (mp_cmp_hs(y, curve->p)) { mp_free(y); return NULL; } EdwardsPoint *P = ecc_edwards_point_new_from_y( curve->e.ec, y, desired_x_parity); mp_free(y); /* A point constructed in this way will always satisfy the curve * equation, unless ecc.c wasn't able to construct one at all, in * which case P is now NULL. Either way, return it. */ return P; } static EdwardsPoint *BinarySource_get_epoint( BinarySource *src, const struct ec_curve *curve) { ptrlen str = get_string(src); if (get_err(src)) return NULL; return eddsa_decode(str, curve); } #define get_epoint(src, curve) \ BinarySource_get_epoint(BinarySource_UPCAST(src), curve) static void BinarySink_put_epoint( BinarySink *bs, EdwardsPoint *point, const struct ec_curve *curve, bool bare) { mp_int *x, *y; ecc_edwards_get_affine(point, &x, &y); assert(curve->fieldBytes >= 2); /* * EdDSA requires point compression. We store a single integer, * with bytes in little-endian order, which mostly contains y but * in which the topmost bit is the low bit of x. */ if (!bare) put_uint32(bs, curve->fieldBytes); /* string length field */ for (size_t i = 0; i < curve->fieldBytes - 1; i++) put_byte(bs, mp_get_byte(y, i)); put_byte(bs, (mp_get_byte(y, curve->fieldBytes - 1) & 0x7F) | (mp_get_bit(x, 0) << 7)); mp_free(x); mp_free(y); } #define put_epoint(bs, point, curve, bare) \ BinarySink_put_epoint(BinarySink_UPCAST(bs), point, curve, bare) /* ---------------------------------------------------------------------- * Exposed ECDSA interface */ static void ecdsa_freekey(ssh_key *key) { struct ecdsa_key *ek = container_of(key, struct ecdsa_key, sshk); if (ek->publicKey) ecc_weierstrass_point_free(ek->publicKey); if (ek->privateKey) mp_free(ek->privateKey); sfree(ek); } static void eddsa_freekey(ssh_key *key) { struct eddsa_key *ek = container_of(key, struct eddsa_key, sshk); if (ek->publicKey) ecc_edwards_point_free(ek->publicKey); if (ek->privateKey) mp_free(ek->privateKey); sfree(ek); } static char *ec_signkey_invalid(ssh_key *key, unsigned flags) { /* All validity criteria for both ECDSA and EdDSA were checked * when we loaded the key in the first place */ return NULL; } static ssh_key *ecdsa_new_pub(const ssh_keyalg *alg, ptrlen data) { const struct ecsign_extra *extra = (const struct ecsign_extra *)alg->extra; struct ec_curve *curve = extra->curve(); assert(curve->type == EC_WEIERSTRASS); BinarySource src[1]; BinarySource_BARE_INIT_PL(src, data); get_string(src); /* Curve name is duplicated for Weierstrass form */ if (!ptrlen_eq_string(get_string(src), curve->name)) return NULL; struct ecdsa_key *ek = snew(struct ecdsa_key); ek->sshk.vt = alg; ek->curve = curve; ek->privateKey = NULL; ek->publicKey = get_wpoint(src, curve); if (!ek->publicKey) { ecdsa_freekey(&ek->sshk); return NULL; } return &ek->sshk; } static ssh_key *eddsa_new_pub(const ssh_keyalg *alg, ptrlen data) { const struct ecsign_extra *extra = (const struct ecsign_extra *)alg->extra; struct ec_curve *curve = extra->curve(); assert(curve->type == EC_EDWARDS); BinarySource src[1]; BinarySource_BARE_INIT_PL(src, data); get_string(src); struct eddsa_key *ek = snew(struct eddsa_key); ek->sshk.vt = alg; ek->curve = curve; ek->privateKey = NULL; ek->publicKey = get_epoint(src, curve); if (!ek->publicKey) { eddsa_freekey(&ek->sshk); return NULL; } return &ek->sshk; } static char *ecc_cache_str_shared( const char *curve_name, mp_int *x, mp_int *y) { strbuf *sb = strbuf_new(); if (curve_name) strbuf_catf(sb, "%s,", curve_name); char *hx = mp_get_hex(x); char *hy = mp_get_hex(y); strbuf_catf(sb, "0x%s,0x%s", hx, hy); sfree(hx); sfree(hy); return strbuf_to_str(sb); } static char *ecdsa_cache_str(ssh_key *key) { struct ecdsa_key *ek = container_of(key, struct ecdsa_key, sshk); mp_int *x, *y; ecc_weierstrass_get_affine(ek->publicKey, &x, &y); char *toret = ecc_cache_str_shared(ek->curve->name, x, y); mp_free(x); mp_free(y); return toret; } static key_components *ecdsa_components(ssh_key *key) { struct ecdsa_key *ek = container_of(key, struct ecdsa_key, sshk); key_components *kc = key_components_new(); key_components_add_text(kc, "key_type", "ECDSA"); key_components_add_text(kc, "curve_name", ek->curve->textname); mp_int *x, *y; ecc_weierstrass_get_affine(ek->publicKey, &x, &y); key_components_add_mp(kc, "public_affine_x", x); key_components_add_mp(kc, "public_affine_y", y); mp_free(x); mp_free(y); if (ek->privateKey) key_components_add_mp(kc, "private_exponent", ek->privateKey); return kc; } static char *eddsa_cache_str(ssh_key *key) { struct eddsa_key *ek = container_of(key, struct eddsa_key, sshk); mp_int *x, *y; ecc_edwards_get_affine(ek->publicKey, &x, &y); char *toret = ecc_cache_str_shared(ek->curve->name, x, y); mp_free(x); mp_free(y); return toret; } static key_components *eddsa_components(ssh_key *key) { struct eddsa_key *ek = container_of(key, struct eddsa_key, sshk); key_components *kc = key_components_new(); key_components_add_text(kc, "key_type", "EdDSA"); key_components_add_text(kc, "curve_name", ek->curve->textname); mp_int *x, *y; ecc_edwards_get_affine(ek->publicKey, &x, &y); key_components_add_mp(kc, "public_affine_x", x); key_components_add_mp(kc, "public_affine_y", y); mp_free(x); mp_free(y); if (ek->privateKey) key_components_add_mp(kc, "private_exponent", ek->privateKey); return kc; } static void ecdsa_public_blob(ssh_key *key, BinarySink *bs) { struct ecdsa_key *ek = container_of(key, struct ecdsa_key, sshk); put_stringz(bs, ek->sshk.vt->ssh_id); put_stringz(bs, ek->curve->name); put_wpoint(bs, ek->publicKey, ek->curve, false); } static void eddsa_public_blob(ssh_key *key, BinarySink *bs) { struct eddsa_key *ek = container_of(key, struct eddsa_key, sshk); put_stringz(bs, ek->sshk.vt->ssh_id); put_epoint(bs, ek->publicKey, ek->curve, false); } static void ecdsa_private_blob(ssh_key *key, BinarySink *bs) { struct ecdsa_key *ek = container_of(key, struct ecdsa_key, sshk); /* ECDSA uses ordinary SSH-2 mpint format to store the private key */ assert(ek->privateKey); put_mp_ssh2(bs, ek->privateKey); } static void eddsa_private_blob(ssh_key *key, BinarySink *bs) { struct eddsa_key *ek = container_of(key, struct eddsa_key, sshk); /* EdDSA stores the private key integer little-endian and unsigned */ assert(ek->privateKey); put_mp_le_fixedlen(bs, ek->privateKey, ek->curve->fieldBytes); } static ssh_key *ecdsa_new_priv(const ssh_keyalg *alg, ptrlen pub, ptrlen priv) { ssh_key *sshk = ecdsa_new_pub(alg, pub); if (!sshk) return NULL; struct ecdsa_key *ek = container_of(sshk, struct ecdsa_key, sshk); BinarySource src[1]; BinarySource_BARE_INIT_PL(src, priv); ek->privateKey = get_mp_ssh2(src); return &ek->sshk; } static ssh_key *eddsa_new_priv(const ssh_keyalg *alg, ptrlen pub, ptrlen priv) { ssh_key *sshk = eddsa_new_pub(alg, pub); if (!sshk) return NULL; struct eddsa_key *ek = container_of(sshk, struct eddsa_key, sshk); BinarySource src[1]; BinarySource_BARE_INIT_PL(src, priv); ek->privateKey = get_mp_le(src); return &ek->sshk; } static ssh_key *eddsa_new_priv_openssh( const ssh_keyalg *alg, BinarySource *src) { const struct ecsign_extra *extra = (const struct ecsign_extra *)alg->extra; struct ec_curve *curve = extra->curve(); assert(curve->type == EC_EDWARDS); ptrlen pubkey_pl = get_string(src); ptrlen privkey_extended_pl = get_string(src); if (get_err(src) || pubkey_pl.len != curve->fieldBytes) return NULL; /* * The OpenSSH format for ed25519 private keys also for some * reason encodes an extra copy of the public key in the second * half of the secret-key string. Check that that's present and * correct as well, otherwise the key we think we've imported * won't behave identically to the way OpenSSH would have treated * it. * * We assume that Ed448 will work the same way, as and when * OpenSSH implements it, which at the time of writing this they * had not. */ BinarySource subsrc[1]; BinarySource_BARE_INIT_PL(subsrc, privkey_extended_pl); ptrlen privkey_pl = get_data(subsrc, curve->fieldBytes); ptrlen pubkey_copy_pl = get_data(subsrc, curve->fieldBytes); if (get_err(subsrc) || get_avail(subsrc)) return NULL; if (!ptrlen_eq_ptrlen(pubkey_pl, pubkey_copy_pl)) return NULL; struct eddsa_key *ek = snew(struct eddsa_key); ek->sshk.vt = alg; ek->curve = curve; ek->privateKey = NULL; ek->publicKey = eddsa_decode(pubkey_pl, curve); if (!ek->publicKey) { eddsa_freekey(&ek->sshk); return NULL; } ek->privateKey = mp_from_bytes_le(privkey_pl); return &ek->sshk; } static void eddsa_openssh_blob(ssh_key *key, BinarySink *bs) { struct eddsa_key *ek = container_of(key, struct eddsa_key, sshk); assert(ek->curve->type == EC_EDWARDS); /* Encode the public and private points as strings */ strbuf *pub_sb = strbuf_new(); put_epoint(pub_sb, ek->publicKey, ek->curve, false); ptrlen pub = make_ptrlen(pub_sb->s + 4, pub_sb->len - 4); strbuf *priv_sb = strbuf_new_nm(); put_mp_le_fixedlen(priv_sb, ek->privateKey, ek->curve->fieldBytes); ptrlen priv = make_ptrlen(priv_sb->s + 4, priv_sb->len - 4); put_stringpl(bs, pub); /* Encode the private key as the concatenation of the * little-endian key integer and the public key again */ put_uint32(bs, priv.len + pub.len); put_datapl(bs, priv); put_datapl(bs, pub); strbuf_free(pub_sb); strbuf_free(priv_sb); } static ssh_key *ecdsa_new_priv_openssh( const ssh_keyalg *alg, BinarySource *src) { const struct ecsign_extra *extra = (const struct ecsign_extra *)alg->extra; struct ec_curve *curve = extra->curve(); assert(curve->type == EC_WEIERSTRASS); get_string(src); struct ecdsa_key *ek = snew(struct ecdsa_key); ek->sshk.vt = alg; ek->curve = curve; ek->privateKey = NULL; ek->publicKey = get_wpoint(src, curve); if (!ek->publicKey) { ecdsa_freekey(&ek->sshk); return NULL; } ek->privateKey = get_mp_ssh2(src); return &ek->sshk; } static void ecdsa_openssh_blob(ssh_key *key, BinarySink *bs) { struct ecdsa_key *ek = container_of(key, struct ecdsa_key, sshk); put_stringz(bs, ek->curve->name); put_wpoint(bs, ek->publicKey, ek->curve, false); put_mp_ssh2(bs, ek->privateKey); } static int ec_shared_pubkey_bits(const ssh_keyalg *alg, ptrlen blob) { const struct ecsign_extra *extra = (const struct ecsign_extra *)alg->extra; struct ec_curve *curve = extra->curve(); return curve->fieldBits; } static mp_int *ecdsa_signing_exponent_from_data( const struct ec_curve *curve, const struct ecsign_extra *extra, ptrlen data) { /* Hash the data being signed. */ unsigned char hash[MAX_HASH_LEN]; ssh_hash *h = ssh_hash_new(extra->hash); put_datapl(h, data); ssh_hash_final(h, hash); /* * Take the leftmost b bits of the hash of the signed data (where * b is the number of bits in order(G)), interpreted big-endian. */ mp_int *z = mp_from_bytes_be(make_ptrlen(hash, extra->hash->hlen)); size_t zbits = mp_get_nbits(z); size_t nbits = mp_get_nbits(curve->w.G_order); size_t shift = zbits - nbits; /* Bound the shift count below at 0, using bit twiddling to avoid * a conditional branch */ shift &= ~-(shift >> (CHAR_BIT * sizeof(size_t) - 1)); mp_int *toret = mp_rshift_safe(z, shift); mp_free(z); return toret; } static bool ecdsa_verify(ssh_key *key, ptrlen sig, ptrlen data) { struct ecdsa_key *ek = container_of(key, struct ecdsa_key, sshk); const struct ecsign_extra *extra = (const struct ecsign_extra *)ek->sshk.vt->extra; BinarySource src[1]; BinarySource_BARE_INIT_PL(src, sig); /* Check the signature starts with the algorithm name */ if (!ptrlen_eq_string(get_string(src), ek->sshk.vt->ssh_id)) return false; /* Everything else is nested inside a sub-string. Descend into that. */ ptrlen sigstr = get_string(src); if (get_err(src)) return false; BinarySource_BARE_INIT_PL(src, sigstr); /* Extract the signature integers r,s */ mp_int *r = get_mp_ssh2(src); mp_int *s = get_mp_ssh2(src); if (get_err(src)) { mp_free(r); mp_free(s); return false; } /* Basic sanity checks: 0 < r,s < order(G) */ unsigned invalid = 0; invalid |= mp_eq_integer(r, 0); invalid |= mp_eq_integer(s, 0); invalid |= mp_cmp_hs(r, ek->curve->w.G_order); invalid |= mp_cmp_hs(s, ek->curve->w.G_order); /* Get the hash of the signed data, converted to an integer */ mp_int *z = ecdsa_signing_exponent_from_data(ek->curve, extra, data); /* Verify the signature integers against the hash */ mp_int *w = mp_invert(s, ek->curve->w.G_order); mp_int *u1 = mp_modmul(z, w, ek->curve->w.G_order); mp_free(z); mp_int *u2 = mp_modmul(r, w, ek->curve->w.G_order); mp_free(w); WeierstrassPoint *u1G = ecc_weierstrass_multiply(ek->curve->w.G, u1); mp_free(u1); WeierstrassPoint *u2P = ecc_weierstrass_multiply(ek->publicKey, u2); mp_free(u2); WeierstrassPoint *sum = ecc_weierstrass_add_general(u1G, u2P); ecc_weierstrass_point_free(u1G); ecc_weierstrass_point_free(u2P); mp_int *x; ecc_weierstrass_get_affine(sum, &x, NULL); ecc_weierstrass_point_free(sum); mp_divmod_into(x, ek->curve->w.G_order, NULL, x); invalid |= (1 ^ mp_cmp_eq(r, x)); mp_free(x); mp_free(r); mp_free(s); return !invalid; } static mp_int *eddsa_signing_exponent_from_data( struct eddsa_key *ek, const struct ecsign_extra *extra, ptrlen r_encoded, ptrlen data) { /* Hash (r || public key || message) */ unsigned char hash[MAX_HASH_LEN]; ssh_hash *h = ssh_hash_new(extra->hash); put_datapl(h, extra->hash_prefix); put_datapl(h, r_encoded); put_epoint(h, ek->publicKey, ek->curve, true); /* omit string header */ put_datapl(h, data); ssh_hash_final(h, hash); /* Convert to an integer */ mp_int *toret = mp_from_bytes_le(make_ptrlen(hash, extra->hash->hlen)); smemclr(hash, extra->hash->hlen); return toret; } static bool eddsa_verify(ssh_key *key, ptrlen sig, ptrlen data) { struct eddsa_key *ek = container_of(key, struct eddsa_key, sshk); const struct ecsign_extra *extra = (const struct ecsign_extra *)ek->sshk.vt->extra; BinarySource src[1]; BinarySource_BARE_INIT_PL(src, sig); /* Check the signature starts with the algorithm name */ if (!ptrlen_eq_string(get_string(src), ek->sshk.vt->ssh_id)) return false; /* Now expect a single string which is the concatenation of an * encoded curve point r and an integer s. */ ptrlen sigstr = get_string(src); if (get_err(src)) return false; BinarySource_BARE_INIT_PL(src, sigstr); ptrlen rstr = get_data(src, ek->curve->fieldBytes); ptrlen sstr = get_data(src, ek->curve->fieldBytes); if (get_err(src) || get_avail(src)) return false; EdwardsPoint *r = eddsa_decode(rstr, ek->curve); if (!r) return false; mp_int *s = mp_from_bytes_le(sstr); mp_int *H = eddsa_signing_exponent_from_data(ek, extra, rstr, data); /* Verify that s*G == r + H*publicKey */ EdwardsPoint *lhs = ecc_edwards_multiply(ek->curve->e.G, s); mp_free(s); EdwardsPoint *hpk = ecc_edwards_multiply(ek->publicKey, H); mp_free(H); EdwardsPoint *rhs = ecc_edwards_add(r, hpk); ecc_edwards_point_free(hpk); unsigned valid = ecc_edwards_eq(lhs, rhs); ecc_edwards_point_free(lhs); ecc_edwards_point_free(rhs); ecc_edwards_point_free(r); return valid; } static void ecdsa_sign(ssh_key *key, ptrlen data, unsigned flags, BinarySink *bs) { struct ecdsa_key *ek = container_of(key, struct ecdsa_key, sshk); const struct ecsign_extra *extra = (const struct ecsign_extra *)ek->sshk.vt->extra; assert(ek->privateKey); mp_int *z = ecdsa_signing_exponent_from_data(ek->curve, extra, data); /* Generate k between 1 and curve->n, using the same deterministic * k generation system we use for conventional DSA. */ mp_int *k; { unsigned char digest[20]; hash_simple(&ssh_sha1, data, digest); k = dss_gen_k( "ECDSA deterministic k generator", ek->curve->w.G_order, ek->privateKey, digest, sizeof(digest)); } WeierstrassPoint *kG = ecc_weierstrass_multiply(ek->curve->w.G, k); mp_int *x; ecc_weierstrass_get_affine(kG, &x, NULL); ecc_weierstrass_point_free(kG); /* r = kG.x mod order(G) */ mp_int *r = mp_mod(x, ek->curve->w.G_order); mp_free(x); /* s = (z + r * priv)/k mod n */ mp_int *rPriv = mp_modmul(r, ek->privateKey, ek->curve->w.G_order); mp_int *numerator = mp_modadd(z, rPriv, ek->curve->w.G_order); mp_free(z); mp_free(rPriv); mp_int *kInv = mp_invert(k, ek->curve->w.G_order); mp_free(k); mp_int *s = mp_modmul(numerator, kInv, ek->curve->w.G_order); mp_free(numerator); mp_free(kInv); /* Format the output */ put_stringz(bs, ek->sshk.vt->ssh_id); strbuf *substr = strbuf_new(); put_mp_ssh2(substr, r); put_mp_ssh2(substr, s); put_stringsb(bs, substr); mp_free(r); mp_free(s); } static void eddsa_sign(ssh_key *key, ptrlen data, unsigned flags, BinarySink *bs) { struct eddsa_key *ek = container_of(key, struct eddsa_key, sshk); const struct ecsign_extra *extra = (const struct ecsign_extra *)ek->sshk.vt->extra; assert(ek->privateKey); /* * EdDSA prescribes a specific method of generating the random * nonce integer for the signature. (A verifier can't tell * whether you followed that method, but it's important to * follow it anyway, because test vectors will want a specific * signature for a given message, and because this preserves * determinism of signatures even if the same signature were * made twice by different software.) */ /* * First, we hash the private key integer (bare, little-endian) * into a hash generating 2*fieldBytes of output. */ unsigned char hash[MAX_HASH_LEN]; ssh_hash *h = ssh_hash_new(extra->hash); for (size_t i = 0; i < ek->curve->fieldBytes; ++i) put_byte(h, mp_get_byte(ek->privateKey, i)); ssh_hash_final(h, hash); /* * The first half of the output hash is converted into an * integer a, by the standard EdDSA transformation. */ mp_int *a = eddsa_exponent_from_hash( make_ptrlen(hash, ek->curve->fieldBytes), ek->curve); /* * The second half of the hash of the private key is hashed again * with the message to be signed, and used as an exponent to * generate the signature point r. */ h = ssh_hash_new(extra->hash); put_datapl(h, extra->hash_prefix); put_data(h, hash + ek->curve->fieldBytes, extra->hash->hlen - ek->curve->fieldBytes); put_datapl(h, data); ssh_hash_final(h, hash); mp_int *log_r_unreduced = mp_from_bytes_le( make_ptrlen(hash, extra->hash->hlen)); mp_int *log_r = mp_mod(log_r_unreduced, ek->curve->e.G_order); mp_free(log_r_unreduced); EdwardsPoint *r = ecc_edwards_multiply(ek->curve->e.G, log_r); /* * Encode r now, because we'll need its encoding for the next * hashing step as well as to write into the actual signature. */ strbuf *r_enc = strbuf_new(); put_epoint(r_enc, r, ek->curve, true); /* omit string header */ ecc_edwards_point_free(r); /* * Compute the hash of (r || public key || message) just as * eddsa_verify does. */ mp_int *H = eddsa_signing_exponent_from_data( ek, extra, ptrlen_from_strbuf(r_enc), data); /* And then s = (log(r) + H*a) mod order(G). */ mp_int *Ha = mp_modmul(H, a, ek->curve->e.G_order); mp_int *s = mp_modadd(log_r, Ha, ek->curve->e.G_order); mp_free(H); mp_free(a); mp_free(Ha); mp_free(log_r); /* Format the output */ put_stringz(bs, ek->sshk.vt->ssh_id); put_uint32(bs, r_enc->len + ek->curve->fieldBytes); put_data(bs, r_enc->u, r_enc->len); strbuf_free(r_enc); for (size_t i = 0; i < ek->curve->fieldBytes; ++i) put_byte(bs, mp_get_byte(s, i)); mp_free(s); } static const struct ecsign_extra sign_extra_ed25519 = { ec_ed25519, &ssh_sha512, NULL, 0, PTRLEN_DECL_LITERAL(""), }; const ssh_keyalg ssh_ecdsa_ed25519 = { .new_pub = eddsa_new_pub, .new_priv = eddsa_new_priv, .new_priv_openssh = eddsa_new_priv_openssh, .freekey = eddsa_freekey, .invalid = ec_signkey_invalid, .sign = eddsa_sign, .verify = eddsa_verify, .public_blob = eddsa_public_blob, .private_blob = eddsa_private_blob, .openssh_blob = eddsa_openssh_blob, .cache_str = eddsa_cache_str, .components = eddsa_components, .pubkey_bits = ec_shared_pubkey_bits, .ssh_id = "ssh-ed25519", .cache_id = "ssh-ed25519", .extra = &sign_extra_ed25519, }; static const struct ecsign_extra sign_extra_ed448 = { ec_ed448, &ssh_shake256_114bytes, NULL, 0, PTRLEN_DECL_LITERAL("SigEd448\0\0"), }; const ssh_keyalg ssh_ecdsa_ed448 = { .new_pub = eddsa_new_pub, .new_priv = eddsa_new_priv, .new_priv_openssh = eddsa_new_priv_openssh, .freekey = eddsa_freekey, .invalid = ec_signkey_invalid, .sign = eddsa_sign, .verify = eddsa_verify, .public_blob = eddsa_public_blob, .private_blob = eddsa_private_blob, .openssh_blob = eddsa_openssh_blob, .cache_str = eddsa_cache_str, .components = eddsa_components, .pubkey_bits = ec_shared_pubkey_bits, .ssh_id = "ssh-ed448", .cache_id = "ssh-ed448", .extra = &sign_extra_ed448, }; /* OID: 1.2.840.10045.3.1.7 (ansiX9p256r1) */ static const unsigned char nistp256_oid[] = { 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07 }; static const struct ecsign_extra sign_extra_nistp256 = { ec_p256, &ssh_sha256, nistp256_oid, lenof(nistp256_oid), }; const ssh_keyalg ssh_ecdsa_nistp256 = { .new_pub = ecdsa_new_pub, .new_priv = ecdsa_new_priv, .new_priv_openssh = ecdsa_new_priv_openssh, .freekey = ecdsa_freekey, .invalid = ec_signkey_invalid, .sign = ecdsa_sign, .verify = ecdsa_verify, .public_blob = ecdsa_public_blob, .private_blob = ecdsa_private_blob, .openssh_blob = ecdsa_openssh_blob, .cache_str = ecdsa_cache_str, .components = ecdsa_components, .pubkey_bits = ec_shared_pubkey_bits, .ssh_id = "ecdsa-sha2-nistp256", .cache_id = "ecdsa-sha2-nistp256", .extra = &sign_extra_nistp256, }; /* OID: 1.3.132.0.34 (secp384r1) */ static const unsigned char nistp384_oid[] = { 0x2b, 0x81, 0x04, 0x00, 0x22 }; static const struct ecsign_extra sign_extra_nistp384 = { ec_p384, &ssh_sha384, nistp384_oid, lenof(nistp384_oid), }; const ssh_keyalg ssh_ecdsa_nistp384 = { .new_pub = ecdsa_new_pub, .new_priv = ecdsa_new_priv, .new_priv_openssh = ecdsa_new_priv_openssh, .freekey = ecdsa_freekey, .invalid = ec_signkey_invalid, .sign = ecdsa_sign, .verify = ecdsa_verify, .public_blob = ecdsa_public_blob, .private_blob = ecdsa_private_blob, .openssh_blob = ecdsa_openssh_blob, .cache_str = ecdsa_cache_str, .components = ecdsa_components, .pubkey_bits = ec_shared_pubkey_bits, .ssh_id = "ecdsa-sha2-nistp384", .cache_id = "ecdsa-sha2-nistp384", .extra = &sign_extra_nistp384, }; /* OID: 1.3.132.0.35 (secp521r1) */ static const unsigned char nistp521_oid[] = { 0x2b, 0x81, 0x04, 0x00, 0x23 }; static const struct ecsign_extra sign_extra_nistp521 = { ec_p521, &ssh_sha512, nistp521_oid, lenof(nistp521_oid), }; const ssh_keyalg ssh_ecdsa_nistp521 = { .new_pub = ecdsa_new_pub, .new_priv = ecdsa_new_priv, .new_priv_openssh = ecdsa_new_priv_openssh, .freekey = ecdsa_freekey, .invalid = ec_signkey_invalid, .sign = ecdsa_sign, .verify = ecdsa_verify, .public_blob = ecdsa_public_blob, .private_blob = ecdsa_private_blob, .openssh_blob = ecdsa_openssh_blob, .cache_str = ecdsa_cache_str, .components = ecdsa_components, .pubkey_bits = ec_shared_pubkey_bits, .ssh_id = "ecdsa-sha2-nistp521", .cache_id = "ecdsa-sha2-nistp521", .extra = &sign_extra_nistp521, }; /* ---------------------------------------------------------------------- * Exposed ECDH interface */ struct eckex_extra { struct ec_curve *(*curve)(void); void (*setup)(ecdh_key *dh); void (*cleanup)(ecdh_key *dh); void (*getpublic)(ecdh_key *dh, BinarySink *bs); mp_int *(*getkey)(ecdh_key *dh, ptrlen remoteKey); }; struct ecdh_key { const struct eckex_extra *extra; const struct ec_curve *curve; mp_int *private; union { WeierstrassPoint *w_public; MontgomeryPoint *m_public; }; }; const char *ssh_ecdhkex_curve_textname(const ssh_kex *kex) { const struct eckex_extra *extra = (const struct eckex_extra *)kex->extra; struct ec_curve *curve = extra->curve(); return curve->textname; } static void ssh_ecdhkex_w_setup(ecdh_key *dh) { mp_int *one = mp_from_integer(1); dh->private = mp_random_in_range(one, dh->curve->w.G_order); mp_free(one); dh->w_public = ecc_weierstrass_multiply(dh->curve->w.G, dh->private); } static void ssh_ecdhkex_m_setup(ecdh_key *dh) { strbuf *bytes = strbuf_new_nm(); random_read(strbuf_append(bytes, dh->curve->fieldBytes), dh->curve->fieldBytes); dh->private = mp_from_bytes_le(ptrlen_from_strbuf(bytes)); /* Ensure the private key has the highest valid bit set, and no * bits _above_ the highest valid one */ mp_reduce_mod_2to(dh->private, dh->curve->fieldBits); mp_set_bit(dh->private, dh->curve->fieldBits - 1, 1); /* Clear a curve-specific number of low bits */ for (unsigned bit = 0; bit < dh->curve->m.log2_cofactor; bit++) mp_set_bit(dh->private, bit, 0); strbuf_free(bytes); dh->m_public = ecc_montgomery_multiply(dh->curve->m.G, dh->private); } ecdh_key *ssh_ecdhkex_newkey(const ssh_kex *kex) { const struct eckex_extra *extra = (const struct eckex_extra *)kex->extra; const struct ec_curve *curve = extra->curve(); ecdh_key *dh = snew(ecdh_key); dh->extra = extra; dh->curve = curve; dh->extra->setup(dh); return dh; } static void ssh_ecdhkex_w_getpublic(ecdh_key *dh, BinarySink *bs) { put_wpoint(bs, dh->w_public, dh->curve, true); } static void ssh_ecdhkex_m_getpublic(ecdh_key *dh, BinarySink *bs) { mp_int *x; ecc_montgomery_get_affine(dh->m_public, &x); for (size_t i = 0; i < dh->curve->fieldBytes; ++i) put_byte(bs, mp_get_byte(x, i)); mp_free(x); } void ssh_ecdhkex_getpublic(ecdh_key *dh, BinarySink *bs) { dh->extra->getpublic(dh, bs); } static mp_int *ssh_ecdhkex_w_getkey(ecdh_key *dh, ptrlen remoteKey) { WeierstrassPoint *remote_p = ecdsa_decode(remoteKey, dh->curve); if (!remote_p) return NULL; if (ecc_weierstrass_is_identity(remote_p)) { /* Not a sensible Diffie-Hellman input value */ ecc_weierstrass_point_free(remote_p); return NULL; } WeierstrassPoint *p = ecc_weierstrass_multiply(remote_p, dh->private); mp_int *x; ecc_weierstrass_get_affine(p, &x, NULL); ecc_weierstrass_point_free(remote_p); ecc_weierstrass_point_free(p); return x; } static mp_int *ssh_ecdhkex_m_getkey(ecdh_key *dh, ptrlen remoteKey) { mp_int *remote_x = mp_from_bytes_le(remoteKey); /* Per RFC 7748 section 5, discard any set bits of the other * side's public value beyond the minimum number of bits required * to represent all valid values. However, an overlarge value that * still fits into the remaining number of bits is accepted, and * will be reduced mod p. */ mp_reduce_mod_2to(remote_x, dh->curve->fieldBits); MontgomeryPoint *remote_p = ecc_montgomery_point_new( dh->curve->m.mc, remote_x); mp_free(remote_x); MontgomeryPoint *p = ecc_montgomery_multiply(remote_p, dh->private); if (ecc_montgomery_is_identity(p)) { ecc_montgomery_point_free(remote_p); ecc_montgomery_point_free(p); return NULL; } mp_int *x; ecc_montgomery_get_affine(p, &x); ecc_montgomery_point_free(remote_p); ecc_montgomery_point_free(p); /* * Endianness-swap. The Curve25519 algorithm definition assumes * you were doing your computation in arrays of 32 little-endian * bytes, and now specifies that you take your final one of those * and convert it into a bignum in _network_ byte order, i.e. * big-endian. * * In particular, the spec says, you convert the _whole_ 32 bytes * into a bignum. That is, on the rare occasions that x has come * out with the most significant 8 bits zero, we have to imagine * that being represented by a 32-byte string with the last byte * being zero, so that has to be converted into an SSH-2 bignum * with the _low_ byte zero, i.e. a multiple of 256. */ strbuf *sb = strbuf_new(); for (size_t i = 0; i < dh->curve->fieldBytes; ++i) put_byte(sb, mp_get_byte(x, i)); mp_free(x); x = mp_from_bytes_be(ptrlen_from_strbuf(sb)); strbuf_free(sb); return x; } mp_int *ssh_ecdhkex_getkey(ecdh_key *dh, ptrlen remoteKey) { return dh->extra->getkey(dh, remoteKey); } static void ssh_ecdhkex_w_cleanup(ecdh_key *dh) { ecc_weierstrass_point_free(dh->w_public); } static void ssh_ecdhkex_m_cleanup(ecdh_key *dh) { ecc_montgomery_point_free(dh->m_public); } void ssh_ecdhkex_freekey(ecdh_key *dh) { mp_free(dh->private); dh->extra->cleanup(dh); sfree(dh); } static const struct eckex_extra kex_extra_curve25519 = { ec_curve25519, ssh_ecdhkex_m_setup, ssh_ecdhkex_m_cleanup, ssh_ecdhkex_m_getpublic, ssh_ecdhkex_m_getkey, }; const ssh_kex ssh_ec_kex_curve25519 = { "curve25519-sha256", NULL, KEXTYPE_ECDH, &ssh_sha256, &kex_extra_curve25519, }; /* Pre-RFC alias */ const ssh_kex ssh_ec_kex_curve25519_libssh = { "curve25519-sha256@libssh.org", NULL, KEXTYPE_ECDH, &ssh_sha256, &kex_extra_curve25519, }; static const struct eckex_extra kex_extra_curve448 = { ec_curve448, ssh_ecdhkex_m_setup, ssh_ecdhkex_m_cleanup, ssh_ecdhkex_m_getpublic, ssh_ecdhkex_m_getkey, }; const ssh_kex ssh_ec_kex_curve448 = { "curve448-sha512", NULL, KEXTYPE_ECDH, &ssh_sha512, &kex_extra_curve448, }; static const struct eckex_extra kex_extra_nistp256 = { ec_p256, ssh_ecdhkex_w_setup, ssh_ecdhkex_w_cleanup, ssh_ecdhkex_w_getpublic, ssh_ecdhkex_w_getkey, }; const ssh_kex ssh_ec_kex_nistp256 = { "ecdh-sha2-nistp256", NULL, KEXTYPE_ECDH, &ssh_sha256, &kex_extra_nistp256, }; static const struct eckex_extra kex_extra_nistp384 = { ec_p384, ssh_ecdhkex_w_setup, ssh_ecdhkex_w_cleanup, ssh_ecdhkex_w_getpublic, ssh_ecdhkex_w_getkey, }; const ssh_kex ssh_ec_kex_nistp384 = { "ecdh-sha2-nistp384", NULL, KEXTYPE_ECDH, &ssh_sha384, &kex_extra_nistp384, }; static const struct eckex_extra kex_extra_nistp521 = { ec_p521, ssh_ecdhkex_w_setup, ssh_ecdhkex_w_cleanup, ssh_ecdhkex_w_getpublic, ssh_ecdhkex_w_getkey, }; const ssh_kex ssh_ec_kex_nistp521 = { "ecdh-sha2-nistp521", NULL, KEXTYPE_ECDH, &ssh_sha512, &kex_extra_nistp521, }; static const ssh_kex *const ec_kex_list[] = { &ssh_ec_kex_curve448, &ssh_ec_kex_curve25519, &ssh_ec_kex_curve25519_libssh, &ssh_ec_kex_nistp256, &ssh_ec_kex_nistp384, &ssh_ec_kex_nistp521, }; const ssh_kexes ssh_ecdh_kex = { lenof(ec_kex_list), ec_kex_list }; /* ---------------------------------------------------------------------- * Helper functions for finding key algorithms and returning auxiliary * data. */ const ssh_keyalg *ec_alg_by_oid(int len, const void *oid, const struct ec_curve **curve) { static const ssh_keyalg *algs_with_oid[] = { &ssh_ecdsa_nistp256, &ssh_ecdsa_nistp384, &ssh_ecdsa_nistp521, }; int i; for (i = 0; i < lenof(algs_with_oid); i++) { const ssh_keyalg *alg = algs_with_oid[i]; const struct ecsign_extra *extra = (const struct ecsign_extra *)alg->extra; if (len == extra->oidlen && !memcmp(oid, extra->oid, len)) { *curve = extra->curve(); return alg; } } return NULL; } const unsigned char *ec_alg_oid(const ssh_keyalg *alg, int *oidlen) { const struct ecsign_extra *extra = (const struct ecsign_extra *)alg->extra; *oidlen = extra->oidlen; return extra->oid; } const int ec_nist_curve_lengths[] = { 256, 384, 521 }; const int n_ec_nist_curve_lengths = lenof(ec_nist_curve_lengths); const int ec_ed_curve_lengths[] = { 255, 448 }; const int n_ec_ed_curve_lengths = lenof(ec_ed_curve_lengths); bool ec_nist_alg_and_curve_by_bits( int bits, const struct ec_curve **curve, const ssh_keyalg **alg) { switch (bits) { case 256: *alg = &ssh_ecdsa_nistp256; break; case 384: *alg = &ssh_ecdsa_nistp384; break; case 521: *alg = &ssh_ecdsa_nistp521; break; default: return false; } *curve = ((struct ecsign_extra *)(*alg)->extra)->curve(); return true; } bool ec_ed_alg_and_curve_by_bits( int bits, const struct ec_curve **curve, const ssh_keyalg **alg) { switch (bits) { case 255: case 256: *alg = &ssh_ecdsa_ed25519; break; case 448: *alg = &ssh_ecdsa_ed448; break; default: return false; } *curve = ((struct ecsign_extra *)(*alg)->extra)->curve(); return true; } putty-0.76/sshecdsag.c0000644000175000017500000000162414072266312011672 00000000000000/* * EC key generation. */ #include "ssh.h" #include "sshkeygen.h" #include "mpint.h" int ecdsa_generate(struct ecdsa_key *ek, int bits) { if (!ec_nist_alg_and_curve_by_bits(bits, &ek->curve, &ek->sshk.vt)) return 0; mp_int *one = mp_from_integer(1); ek->privateKey = mp_random_in_range(one, ek->curve->w.G_order); mp_free(one); ek->publicKey = ecdsa_public(ek->privateKey, ek->sshk.vt); return 1; } int eddsa_generate(struct eddsa_key *ek, int bits) { if (!ec_ed_alg_and_curve_by_bits(bits, &ek->curve, &ek->sshk.vt)) return 0; /* EdDSA secret keys are just 32 bytes of hash preimage; the * 64-byte SHA-512 hash of that key will be used when signing, * but the form of the key stored on disk is the preimage * only. */ ek->privateKey = mp_random_bits(bits); ek->publicKey = eddsa_public(ek->privateKey, ek->sshk.vt); return 1; } putty-0.76/sshgss.h0000644000175000017500000001570114072266312011246 00000000000000#ifndef PUTTY_SSHGSS_H #define PUTTY_SSHGSS_H #include "putty.h" #include "pgssapi.h" #ifndef NO_GSSAPI #define SSH2_GSS_OIDTYPE 0x06 typedef void *Ssh_gss_ctx; typedef enum Ssh_gss_stat { SSH_GSS_OK = 0, SSH_GSS_S_CONTINUE_NEEDED, SSH_GSS_NO_MEM, SSH_GSS_BAD_HOST_NAME, SSH_GSS_BAD_MIC, SSH_GSS_NO_CREDS, SSH_GSS_FAILURE } Ssh_gss_stat; #define SSH_GSS_S_COMPLETE SSH_GSS_OK #define SSH_GSS_CLEAR_BUF(buf) do { \ (*buf).length = 0; \ (*buf).value = NULL; \ } while (0) typedef gss_buffer_desc Ssh_gss_buf; typedef gss_name_t Ssh_gss_name; #define GSS_NO_EXPIRATION ((time_t)-1) #define GSS_DEF_REKEY_MINS 2 /* Default minutes between GSS cache checks */ /* Functions, provided by either wingss.c or sshgssc.c */ struct ssh_gss_library; /* * Prepare a collection of GSSAPI libraries for use in a single SSH * connection. Returns a structure containing a list of libraries, * with their ids (see struct ssh_gss_library below) filled in so * that the client can go through them in the SSH user's preferred * order. * * Must always return non-NULL. (Even if no libraries are available, * it must return an empty structure.) * * 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, Ssh_gss_buf *buf); /* * Converts a name such as a hostname into a GSSAPI internal form, * which is placed in "out". The result should be freed by * ssh_gss_release_name(). */ typedef Ssh_gss_stat (*t_ssh_gss_import_name)(struct ssh_gss_library *lib, char *in, Ssh_gss_name *out); /* * Frees the contents of an Ssh_gss_name structure filled in by * ssh_gss_import_name(). */ typedef Ssh_gss_stat (*t_ssh_gss_release_name)(struct ssh_gss_library *lib, Ssh_gss_name *name); /* * The main GSSAPI security context setup function. The "out" * parameter will need to be freed by ssh_gss_free_tok. */ typedef Ssh_gss_stat (*t_ssh_gss_init_sec_context) (struct ssh_gss_library *lib, Ssh_gss_ctx *ctx, Ssh_gss_name name, int delegate, Ssh_gss_buf *in, Ssh_gss_buf *out, time_t *expiry, unsigned long *lifetime); /* * Frees the contents of an Ssh_gss_buf filled in by * ssh_gss_init_sec_context(). Do not accidentally call this on * something filled in by ssh_gss_get_mic() (which requires a * different free function) or something filled in by any other * way. */ typedef Ssh_gss_stat (*t_ssh_gss_free_tok)(struct ssh_gss_library *lib, Ssh_gss_buf *); /* * Acquires the credentials to perform authentication in the first * place. Needs to be freed by ssh_gss_release_cred(). */ typedef Ssh_gss_stat (*t_ssh_gss_acquire_cred)(struct ssh_gss_library *lib, Ssh_gss_ctx *, time_t *expiry); /* * Frees the contents of an Ssh_gss_ctx filled in by * ssh_gss_acquire_cred(). */ typedef Ssh_gss_stat (*t_ssh_gss_release_cred)(struct ssh_gss_library *lib, Ssh_gss_ctx *); /* * Gets a MIC for some input data. "out" needs to be freed by * ssh_gss_free_mic(). */ typedef Ssh_gss_stat (*t_ssh_gss_get_mic)(struct ssh_gss_library *lib, Ssh_gss_ctx ctx, Ssh_gss_buf *in, Ssh_gss_buf *out); /* * Validates an input MIC for some input data. */ typedef Ssh_gss_stat (*t_ssh_gss_verify_mic)(struct ssh_gss_library *lib, Ssh_gss_ctx ctx, Ssh_gss_buf *in_data, Ssh_gss_buf *in_mic); /* * Frees the contents of an Ssh_gss_buf filled in by * ssh_gss_get_mic(). Do not accidentally call this on something * filled in by ssh_gss_init_sec_context() (which requires a * different free function) or something filled in by any other * way. */ typedef Ssh_gss_stat (*t_ssh_gss_free_mic)(struct ssh_gss_library *lib, Ssh_gss_buf *); /* * Return an error message after authentication failed. The * message string is returned in "buf", with buf->len giving the * number of characters of printable message text and buf->data * containing one more character which is a trailing NUL. * buf->data should be manually freed by the caller. */ typedef Ssh_gss_stat (*t_ssh_gss_display_status)(struct ssh_gss_library *lib, Ssh_gss_ctx, Ssh_gss_buf *buf); struct ssh_gss_library { /* * Identifying number in the enumeration used by the * configuration code to specify a preference order. */ int id; /* * Filled in at initialisation time, if there's anything * interesting to say about how GSSAPI was initialised (e.g. * which of a number of alternative libraries was used). */ const char *gsslogmsg; /* * Function pointers implementing the SSH wrapper layer on top * of GSSAPI. (Defined in sshgssc, typically, though Windows * provides an alternative layer to sit on top of the annoyingly * different SSPI.) */ t_ssh_gss_indicate_mech indicate_mech; t_ssh_gss_import_name import_name; t_ssh_gss_release_name release_name; t_ssh_gss_init_sec_context init_sec_context; t_ssh_gss_free_tok free_tok; t_ssh_gss_acquire_cred acquire_cred; t_ssh_gss_release_cred release_cred; t_ssh_gss_get_mic get_mic; t_ssh_gss_verify_mic verify_mic; t_ssh_gss_free_mic free_mic; t_ssh_gss_display_status display_status; /* * Additional data for the wrapper layers. */ union { struct gssapi_functions gssapi; /* * The SSPI wrappers don't need to store their Windows API * function pointers in this structure, because there can't * be more than one set of them available. */ } u; /* * Wrapper layers will often also need to store a library handle * of some sort for cleanup time. */ void *handle; }; /* * State that has to be shared between all GSSAPI-using parts of the * same SSH connection, in particular between GSS key exchange and the * subsequent trivial userauth method that reuses its output. */ struct ssh_connection_shared_gss_state { struct ssh_gss_liblist *libs; struct ssh_gss_library *lib; Ssh_gss_name srv_name; Ssh_gss_ctx ctx; }; #endif /* NO_GSSAPI */ #endif /*PUTTY_SSHGSS_H*/ putty-0.76/sshgssc.c0000644000175000017500000002364414072266312011411 00000000000000#include "putty.h" #include #include #include "sshgssc.h" #include "misc.h" #ifndef NO_GSSAPI static Ssh_gss_stat ssh_gssapi_indicate_mech(struct ssh_gss_library *lib, Ssh_gss_buf *mech) { /* Copy constant into mech */ mech->length = GSS_MECH_KRB5->length; mech->value = GSS_MECH_KRB5->elements; return SSH_GSS_OK; } static Ssh_gss_stat ssh_gssapi_import_name(struct ssh_gss_library *lib, char *host, Ssh_gss_name *srv_name) { struct gssapi_functions *gss = &lib->u.gssapi; OM_uint32 min_stat,maj_stat; gss_buffer_desc host_buf; char *pStr; pStr = dupcat("host@", host); host_buf.value = pStr; host_buf.length = strlen(pStr); maj_stat = gss->import_name(&min_stat, &host_buf, GSS_C_NT_HOSTBASED_SERVICE, srv_name); /* Release buffer */ sfree(pStr); if (maj_stat == GSS_S_COMPLETE) return SSH_GSS_OK; return SSH_GSS_FAILURE; } static Ssh_gss_stat ssh_gssapi_acquire_cred(struct ssh_gss_library *lib, Ssh_gss_ctx *ctx, time_t *expiry) { struct gssapi_functions *gss = &lib->u.gssapi; gss_OID_set_desc k5only = { 1, GSS_MECH_KRB5 }; gss_cred_id_t cred; OM_uint32 dummy; OM_uint32 time_rec; gssapi_ssh_gss_ctx *gssctx = snew(gssapi_ssh_gss_ctx); gssctx->ctx = GSS_C_NO_CONTEXT; gssctx->expiry = 0; gssctx->maj_stat = gss->acquire_cred(&gssctx->min_stat, GSS_C_NO_NAME, GSS_C_INDEFINITE, &k5only, GSS_C_INITIATE, &cred, (gss_OID_set *)0, &time_rec); if (gssctx->maj_stat != GSS_S_COMPLETE) { sfree(gssctx); return SSH_GSS_FAILURE; } /* * When the credential lifetime is not yet available due to deferred * processing, gss_acquire_cred should return a 0 lifetime which is * distinct from GSS_C_INDEFINITE which signals a crential that never * expires. However, not all implementations get this right, and with * Kerberos, initiator credentials always expire at some point. So when * lifetime is 0 or GSS_C_INDEFINITE we call gss_inquire_cred_by_mech() to * complete deferred processing. */ if (time_rec == GSS_C_INDEFINITE || time_rec == 0) { gssctx->maj_stat = gss->inquire_cred_by_mech(&gssctx->min_stat, cred, (gss_OID) GSS_MECH_KRB5, GSS_C_NO_NAME, &time_rec, NULL, NULL); } (void) gss->release_cred(&dummy, &cred); if (gssctx->maj_stat != GSS_S_COMPLETE) { sfree(gssctx); return SSH_GSS_FAILURE; } if (time_rec != GSS_C_INDEFINITE) gssctx->expiry = time(NULL) + time_rec; else gssctx->expiry = GSS_NO_EXPIRATION; if (expiry) { *expiry = gssctx->expiry; } *ctx = (Ssh_gss_ctx) gssctx; return SSH_GSS_OK; } static Ssh_gss_stat ssh_gssapi_init_sec_context(struct ssh_gss_library *lib, Ssh_gss_ctx *ctx, Ssh_gss_name srv_name, int to_deleg, Ssh_gss_buf *recv_tok, Ssh_gss_buf *send_tok, time_t *expiry, unsigned long *lifetime) { struct gssapi_functions *gss = &lib->u.gssapi; gssapi_ssh_gss_ctx *gssctx = (gssapi_ssh_gss_ctx*) *ctx; OM_uint32 ret_flags; OM_uint32 lifetime_rec; if (to_deleg) to_deleg = GSS_C_DELEG_FLAG; gssctx->maj_stat = gss->init_sec_context(&gssctx->min_stat, GSS_C_NO_CREDENTIAL, &gssctx->ctx, srv_name, (gss_OID) GSS_MECH_KRB5, GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG | to_deleg, 0, GSS_C_NO_CHANNEL_BINDINGS, recv_tok, NULL, /* ignore mech type */ send_tok, &ret_flags, &lifetime_rec); if (lifetime) { if (lifetime_rec == GSS_C_INDEFINITE) *lifetime = ULONG_MAX; else *lifetime = lifetime_rec; } if (expiry) { if (lifetime_rec == GSS_C_INDEFINITE) *expiry = GSS_NO_EXPIRATION; else *expiry = time(NULL) + lifetime_rec; } if (gssctx->maj_stat == GSS_S_COMPLETE) return SSH_GSS_S_COMPLETE; if (gssctx->maj_stat == GSS_S_CONTINUE_NEEDED) return SSH_GSS_S_CONTINUE_NEEDED; return SSH_GSS_FAILURE; } static Ssh_gss_stat ssh_gssapi_display_status(struct ssh_gss_library *lib, Ssh_gss_ctx ctx, Ssh_gss_buf *buf) { struct gssapi_functions *gss = &lib->u.gssapi; gssapi_ssh_gss_ctx *gssctx = (gssapi_ssh_gss_ctx *) ctx; OM_uint32 lmin,lmax; OM_uint32 ccc; gss_buffer_desc msg_maj=GSS_C_EMPTY_BUFFER; gss_buffer_desc msg_min=GSS_C_EMPTY_BUFFER; /* Return empty buffer in case of failure */ SSH_GSS_CLEAR_BUF(buf); /* get first mesg from GSS */ ccc=0; lmax=gss->display_status(&lmin,gssctx->maj_stat,GSS_C_GSS_CODE,(gss_OID) GSS_MECH_KRB5,&ccc,&msg_maj); if (lmax != GSS_S_COMPLETE) return SSH_GSS_FAILURE; /* get first mesg from Kerberos */ ccc=0; lmax=gss->display_status(&lmin,gssctx->min_stat,GSS_C_MECH_CODE,(gss_OID) GSS_MECH_KRB5,&ccc,&msg_min); if (lmax != GSS_S_COMPLETE) { gss->release_buffer(&lmin, &msg_maj); return SSH_GSS_FAILURE; } /* copy data into buffer */ buf->length = msg_maj.length + msg_min.length + 1; buf->value = snewn(buf->length + 1, char); /* copy mem */ memcpy((char *)buf->value, msg_maj.value, msg_maj.length); ((char *)buf->value)[msg_maj.length] = ' '; memcpy((char *)buf->value + msg_maj.length + 1, msg_min.value, msg_min.length); ((char *)buf->value)[buf->length] = 0; /* free mem & exit */ gss->release_buffer(&lmin, &msg_maj); gss->release_buffer(&lmin, &msg_min); return SSH_GSS_OK; } static Ssh_gss_stat ssh_gssapi_free_tok(struct ssh_gss_library *lib, Ssh_gss_buf *send_tok) { struct gssapi_functions *gss = &lib->u.gssapi; OM_uint32 min_stat,maj_stat; maj_stat = gss->release_buffer(&min_stat, send_tok); if (maj_stat == GSS_S_COMPLETE) return SSH_GSS_OK; return SSH_GSS_FAILURE; } static Ssh_gss_stat ssh_gssapi_release_cred(struct ssh_gss_library *lib, Ssh_gss_ctx *ctx) { struct gssapi_functions *gss = &lib->u.gssapi; gssapi_ssh_gss_ctx *gssctx = (gssapi_ssh_gss_ctx *) *ctx; OM_uint32 min_stat; OM_uint32 maj_stat=GSS_S_COMPLETE; if (gssctx == NULL) return SSH_GSS_FAILURE; if (gssctx->ctx != GSS_C_NO_CONTEXT) maj_stat = gss->delete_sec_context(&min_stat,&gssctx->ctx,GSS_C_NO_BUFFER); sfree(gssctx); *ctx = NULL; if (maj_stat == GSS_S_COMPLETE) return SSH_GSS_OK; return SSH_GSS_FAILURE; } static Ssh_gss_stat ssh_gssapi_release_name(struct ssh_gss_library *lib, Ssh_gss_name *srv_name) { struct gssapi_functions *gss = &lib->u.gssapi; OM_uint32 min_stat,maj_stat; maj_stat = gss->release_name(&min_stat, srv_name); if (maj_stat == GSS_S_COMPLETE) return SSH_GSS_OK; return SSH_GSS_FAILURE; } static Ssh_gss_stat ssh_gssapi_get_mic(struct ssh_gss_library *lib, Ssh_gss_ctx ctx, Ssh_gss_buf *buf, Ssh_gss_buf *hash) { struct gssapi_functions *gss = &lib->u.gssapi; gssapi_ssh_gss_ctx *gssctx = (gssapi_ssh_gss_ctx *) ctx; if (gssctx == NULL) return SSH_GSS_FAILURE; return gss->get_mic(&(gssctx->min_stat), gssctx->ctx, 0, buf, hash); } static Ssh_gss_stat ssh_gssapi_verify_mic(struct ssh_gss_library *lib, Ssh_gss_ctx ctx, Ssh_gss_buf *buf, Ssh_gss_buf *hash) { struct gssapi_functions *gss = &lib->u.gssapi; gssapi_ssh_gss_ctx *gssctx = (gssapi_ssh_gss_ctx *) ctx; if (gssctx == NULL) return SSH_GSS_FAILURE; return gss->verify_mic(&(gssctx->min_stat), gssctx->ctx, buf, hash, NULL); } static Ssh_gss_stat ssh_gssapi_free_mic(struct ssh_gss_library *lib, Ssh_gss_buf *hash) { /* On Unix this is the same freeing process as ssh_gssapi_free_tok. */ return ssh_gssapi_free_tok(lib, hash); } void ssh_gssapi_bind_fns(struct ssh_gss_library *lib) { lib->indicate_mech = ssh_gssapi_indicate_mech; lib->import_name = ssh_gssapi_import_name; lib->release_name = ssh_gssapi_release_name; lib->init_sec_context = ssh_gssapi_init_sec_context; lib->free_tok = ssh_gssapi_free_tok; lib->acquire_cred = ssh_gssapi_acquire_cred; lib->release_cred = ssh_gssapi_release_cred; lib->get_mic = ssh_gssapi_get_mic; lib->verify_mic = ssh_gssapi_verify_mic; lib->free_mic = ssh_gssapi_free_mic; lib->display_status = ssh_gssapi_display_status; } #else /* Dummy function so this source file defines something if NO_GSSAPI is defined. */ int ssh_gssapi_init(void) { return 0; } #endif putty-0.76/sshgssc.h0000644000175000017500000000064014072266312011405 00000000000000#ifndef PUTTY_SSHGSSC_H #define PUTTY_SSHGSSC_H #include "putty.h" #ifndef NO_GSSAPI #include "pgssapi.h" #include "sshgss.h" typedef struct gssapi_ssh_gss_ctx { OM_uint32 maj_stat; OM_uint32 min_stat; gss_ctx_id_t ctx; time_t expiry; } gssapi_ssh_gss_ctx; void ssh_gssapi_bind_fns(struct ssh_gss_library *lib); #else int ssh_gssapi_init(void); #endif /*NO_GSSAPI*/ #endif /*PUTTY_SSHGSSC_H*/ putty-0.76/sshhmac.c0000644000175000017500000001626014072266312011356 00000000000000/* * Implementation of HMAC (RFC 2104) for PuTTY, in a general form that * can wrap any underlying hash function. */ #include "ssh.h" struct hmac { const ssh_hashalg *hashalg; ssh_hash *h_outer, *h_inner, *h_live; uint8_t *digest; strbuf *text_name; ssh2_mac mac; }; struct hmac_extra { const ssh_hashalg *hashalg_base; const char *suffix, *annotation; }; static ssh2_mac *hmac_new(const ssh2_macalg *alg, ssh_cipher *cipher) { struct hmac *ctx = snew(struct hmac); const struct hmac_extra *extra = (const struct hmac_extra *)alg->extra; ctx->h_outer = ssh_hash_new(extra->hashalg_base); /* In case that hashalg was a selector vtable, we'll now switch to * using whatever real one it selected, for all future purposes. */ ctx->hashalg = ssh_hash_alg(ctx->h_outer); ctx->h_inner = ssh_hash_new(ctx->hashalg); ctx->h_live = ssh_hash_new(ctx->hashalg); /* * HMAC is not well defined as a wrapper on an absolutely general * hash function; it expects that the function it's wrapping will * consume data in fixed-size blocks, and it's partially defined * in terms of that block size. So we insist that the hash we're * given must have defined a meaningful block size. */ assert(ctx->hashalg->blocklen); ctx->digest = snewn(ctx->hashalg->hlen, uint8_t); ctx->text_name = strbuf_new(); strbuf_catf(ctx->text_name, "HMAC-%s%s", ctx->hashalg->text_basename, extra->suffix); if (extra->annotation || ctx->hashalg->annotation) { strbuf_catf(ctx->text_name, " ("); const char *sep = ""; if (extra->annotation) { strbuf_catf(ctx->text_name, "%s%s", sep, extra->annotation); sep = ", "; } if (ctx->hashalg->annotation) { strbuf_catf(ctx->text_name, "%s%s", sep, ctx->hashalg->annotation); sep = ", "; } strbuf_catf(ctx->text_name, ")"); } ctx->mac.vt = alg; BinarySink_DELEGATE_INIT(&ctx->mac, ctx->h_live); return &ctx->mac; } static void hmac_free(ssh2_mac *mac) { struct hmac *ctx = container_of(mac, struct hmac, mac); ssh_hash_free(ctx->h_outer); ssh_hash_free(ctx->h_inner); ssh_hash_free(ctx->h_live); smemclr(ctx->digest, ctx->hashalg->hlen); sfree(ctx->digest); strbuf_free(ctx->text_name); smemclr(ctx, sizeof(*ctx)); sfree(ctx); } #define PAD_OUTER 0x5C #define PAD_INNER 0x36 static void hmac_key(ssh2_mac *mac, ptrlen key) { struct hmac *ctx = container_of(mac, struct hmac, mac); const uint8_t *kp; size_t klen; strbuf *sb = NULL; if (key.len > ctx->hashalg->blocklen) { /* * RFC 2104 section 2: if the key exceeds the block length of * the underlying hash, then we start by hashing the key, and * use that hash as the 'true' key for the HMAC construction. */ sb = strbuf_new_nm(); strbuf_append(sb, ctx->hashalg->hlen); hash_simple(ctx->hashalg, key, sb->u); kp = sb->u; klen = sb->len; } else { /* * A short enough key is used as is. */ kp = (const uint8_t *)key.ptr; klen = key.len; } ssh_hash_reset(ctx->h_outer); for (size_t i = 0; i < klen; i++) put_byte(ctx->h_outer, PAD_OUTER ^ kp[i]); for (size_t i = klen; i < ctx->hashalg->blocklen; i++) put_byte(ctx->h_outer, PAD_OUTER); ssh_hash_reset(ctx->h_inner); for (size_t i = 0; i < klen; i++) put_byte(ctx->h_inner, PAD_INNER ^ kp[i]); for (size_t i = klen; i < ctx->hashalg->blocklen; i++) put_byte(ctx->h_inner, PAD_INNER); if (sb) strbuf_free(sb); } static void hmac_start(ssh2_mac *mac) { struct hmac *ctx = container_of(mac, struct hmac, mac); ssh_hash_copyfrom(ctx->h_live, ctx->h_inner); } static void hmac_genresult(ssh2_mac *mac, unsigned char *output) { struct hmac *ctx = container_of(mac, struct hmac, mac); ssh_hash *htmp; /* Leave h_live and h_outer in place, so that the SSH-2 BPP can * continue regenerating test results from different-length * prefixes of the packet */ ssh_hash_digest_nondestructive(ctx->h_live, ctx->digest); htmp = ssh_hash_copy(ctx->h_outer); put_data(htmp, ctx->digest, ctx->hashalg->hlen); ssh_hash_final(htmp, ctx->digest); /* * Some instances of HMAC truncate the output hash, so instead of * writing it directly to 'output' we wrote it to our own * full-length buffer, and now we copy the required amount. */ memcpy(output, ctx->digest, mac->vt->len); smemclr(ctx->digest, ctx->hashalg->hlen); } static const char *hmac_text_name(ssh2_mac *mac) { struct hmac *ctx = container_of(mac, struct hmac, mac); return ctx->text_name->s; } static const struct hmac_extra ssh_hmac_sha256_extra = { &ssh_sha256, "" }; const ssh2_macalg ssh_hmac_sha256 = { .new = hmac_new, .free = hmac_free, .setkey = hmac_key, .start = hmac_start, .genresult = hmac_genresult, .text_name = hmac_text_name, .name = "hmac-sha2-256", .etm_name = "hmac-sha2-256-etm@openssh.com", .len = 32, .keylen = 32, .extra = &ssh_hmac_sha256_extra, }; static const struct hmac_extra ssh_hmac_md5_extra = { &ssh_md5, "" }; const ssh2_macalg ssh_hmac_md5 = { .new = hmac_new, .free = hmac_free, .setkey = hmac_key, .start = hmac_start, .genresult = hmac_genresult, .text_name = hmac_text_name, .name = "hmac-md5", .etm_name = "hmac-md5-etm@openssh.com", .len = 16, .keylen = 16, .extra = &ssh_hmac_md5_extra, }; static const struct hmac_extra ssh_hmac_sha1_extra = { &ssh_sha1, "" }; const ssh2_macalg ssh_hmac_sha1 = { .new = hmac_new, .free = hmac_free, .setkey = hmac_key, .start = hmac_start, .genresult = hmac_genresult, .text_name = hmac_text_name, .name = "hmac-sha1", .etm_name = "hmac-sha1-etm@openssh.com", .len = 20, .keylen = 20, .extra = &ssh_hmac_sha1_extra, }; static const struct hmac_extra ssh_hmac_sha1_96_extra = { &ssh_sha1, "-96" }; const ssh2_macalg ssh_hmac_sha1_96 = { .new = hmac_new, .free = hmac_free, .setkey = hmac_key, .start = hmac_start, .genresult = hmac_genresult, .text_name = hmac_text_name, .name = "hmac-sha1-96", .etm_name = "hmac-sha1-96-etm@openssh.com", .len = 12, .keylen = 20, .extra = &ssh_hmac_sha1_96_extra, }; static const struct hmac_extra ssh_hmac_sha1_buggy_extra = { &ssh_sha1, "", "bug-compatible" }; const ssh2_macalg ssh_hmac_sha1_buggy = { .new = hmac_new, .free = hmac_free, .setkey = hmac_key, .start = hmac_start, .genresult = hmac_genresult, .text_name = hmac_text_name, .name = "hmac-sha1", .len = 20, .keylen = 16, .extra = &ssh_hmac_sha1_buggy_extra, }; static const struct hmac_extra ssh_hmac_sha1_96_buggy_extra = { &ssh_sha1, "-96", "bug-compatible" }; const ssh2_macalg ssh_hmac_sha1_96_buggy = { .new = hmac_new, .free = hmac_free, .setkey = hmac_key, .start = hmac_start, .genresult = hmac_genresult, .text_name = hmac_text_name, .name = "hmac-sha1-96", .len = 12, .keylen = 16, .extra = &ssh_hmac_sha1_96_buggy_extra, }; putty-0.76/sshkeygen.h0000644000175000017500000003024114072266312011730 00000000000000/* * sshkeygen.h: routines used internally to key generation. */ /* ---------------------------------------------------------------------- * A table of all the primes that fit in a 16-bit integer. Call * init_primes_array to make sure it's been initialised. */ #define NSMALLPRIMES 6542 /* number of primes < 65536 */ extern const unsigned short *const smallprimes; void init_smallprimes(void); /* ---------------------------------------------------------------------- * A system for making up random candidate integers during prime * generation. This unconditionally ensures that the numbers have the * right number of bits and are not divisible by any prime in the * smallprimes[] array above. It can also impose further constraints, * as documented below. */ typedef struct PrimeCandidateSource PrimeCandidateSource; /* * pcs_new: you say how many bits you want the prime to have (with the * usual semantics that an n-bit number is in the range [2^{n-1},2^n)) * and also optionally specify what you want its topmost 'nfirst' bits * to be. * * (The 'first' system is used for RSA keys, where you need to arrange * that the product of your two primes is in a more tightly * constrained range than the factor of 4 you'd get by just generating * two (n/2)-bit primes and multiplying them.) */ PrimeCandidateSource *pcs_new(unsigned bits); PrimeCandidateSource *pcs_new_with_firstbits(unsigned bits, unsigned first, unsigned nfirst); /* Insist that generated numbers must be congruent to 'res' mod 'mod' */ void pcs_require_residue(PrimeCandidateSource *s, mp_int *mod, mp_int *res); /* Convenience wrapper for the common case where res = 1 */ void pcs_require_residue_1(PrimeCandidateSource *s, mp_int *mod); /* Same as pcs_require_residue_1, but also records that the modulus is * known to be prime */ void pcs_require_residue_1_mod_prime(PrimeCandidateSource *s, mp_int *mod); /* Insist that generated numbers must _not_ be congruent to 'res' mod * 'mod'. This is used to avoid being 1 mod the RSA public exponent, * which is small, so it only needs ordinary integer parameters. */ void pcs_avoid_residue_small(PrimeCandidateSource *s, unsigned mod, unsigned res); /* Exclude any prime that has no chance of being a Sophie Germain prime. */ void pcs_try_sophie_germain(PrimeCandidateSource *s); /* Mark a PrimeCandidateSource as one-shot, so that the prime generation * function will return NULL if an attempt fails, rather than looping. */ void pcs_set_oneshot(PrimeCandidateSource *s); /* Prepare a PrimeCandidateSource to actually generate numbers. This * function does last-minute computation that has to be delayed until * all constraints have been input. */ void pcs_ready(PrimeCandidateSource *s); /* Actually generate a candidate integer. You must free the result, of * course. */ mp_int *pcs_generate(PrimeCandidateSource *s); /* Free a PrimeCandidateSource. */ void pcs_free(PrimeCandidateSource *s); /* Return some internal fields of the PCS. Used by testcrypt for * unit-testing this system. */ void pcs_inspect(PrimeCandidateSource *pcs, mp_int **limit_out, mp_int **factor_out, mp_int **addend_out); /* Query functions for primegen to use */ unsigned pcs_get_bits(PrimeCandidateSource *pcs); unsigned pcs_get_bits_remaining(PrimeCandidateSource *pcs); mp_int *pcs_get_upper_bound(PrimeCandidateSource *pcs); mp_int **pcs_get_known_prime_factors(PrimeCandidateSource *pcs, size_t *nout); /* ---------------------------------------------------------------------- * A system for doing Miller-Rabin probabilistic primality tests. * These benefit from having set up some context beforehand, if you're * going to do more than one of them on the same candidate prime, so * we declare an object type here to store that context. */ typedef struct MillerRabin MillerRabin; /* Make and free a Miller-Rabin context. */ MillerRabin *miller_rabin_new(mp_int *p); void miller_rabin_free(MillerRabin *mr); /* Perform a single Miller-Rabin test, using a random witness value. */ bool miller_rabin_test_random(MillerRabin *mr); /* Suggest how many tests are needed to make it sufficiently unlikely * that a composite number will pass them all */ unsigned miller_rabin_checks_needed(unsigned bits); /* An extension to the M-R test, which iterates until it either finds * a witness value that is potentially a primitive root, or one * that proves the number to be composite. */ mp_int *miller_rabin_find_potential_primitive_root(MillerRabin *mr); /* ---------------------------------------------------------------------- * A system for proving numbers to be prime, using the Pocklington * test, which requires knowing a partial factorisation of p-1 * (specifically, factors whose product is at least cbrt(p)) and a * primitive root. * * The API consists of instantiating a 'Pockle' object, which * internally stores a list of numbers you've already convinced it is * prime, and can accept further primes if you give a satisfactory * certificate of their primality based on primes it already knows * about. */ typedef struct Pockle Pockle; /* In real use, you only really need to know whether the Pockle * successfully accepted your prime. But for testcrypt, it's useful to * expose many different failure modes so we can try to provoke them * all in unit tests and check they're working. */ #define POCKLE_STATUSES(X) \ X(POCKLE_OK) \ X(POCKLE_SMALL_PRIME_NOT_SMALL) \ X(POCKLE_SMALL_PRIME_NOT_PRIME) \ X(POCKLE_PRIME_SMALLER_THAN_2) \ X(POCKLE_FACTOR_NOT_KNOWN_PRIME) \ X(POCKLE_FACTOR_NOT_A_FACTOR) \ X(POCKLE_PRODUCT_OF_FACTORS_TOO_SMALL) \ X(POCKLE_FERMAT_TEST_FAILED) \ X(POCKLE_DISCRIMINANT_IS_SQUARE) \ X(POCKLE_WITNESS_POWER_IS_1) \ X(POCKLE_WITNESS_POWER_NOT_COPRIME) \ /* end of list */ #define DEFINE_ENUM(id) id, typedef enum PockleStatus { POCKLE_STATUSES(DEFINE_ENUM) } PockleStatus; #undef DEFINE_ENUM /* Make a new empty Pockle, containing no primes. */ Pockle *pockle_new(void); /* Insert a prime below 2^32 into the Pockle. No evidence is required: * Pockle will check it itself. */ PockleStatus pockle_add_small_prime(Pockle *pockle, mp_int *p); /* Insert a general prime into the Pockle. You must provide a list of * prime factors of p-1, whose product exceeds the cube root of p, and * also a primitive root mod p. */ PockleStatus pockle_add_prime(Pockle *pockle, mp_int *p, mp_int **factors, size_t nfactors, mp_int *primitive_root); /* If you call pockle_mark, and later pass the returned value to * pockle_release, it will free all the primes that were added to the * Pockle between those two calls. Useful in recursive algorithms, to * stop the Pockle growing unboundedly if the recursion keeps having * to backtrack. */ size_t pockle_mark(Pockle *pockle); void pockle_release(Pockle *pockle, size_t mark); /* Free a Pockle. */ void pockle_free(Pockle *pockle); /* Generate a certificate of primality for a prime already known to * the Pockle, in a format acceptable to Math::Prime::Util. */ strbuf *pockle_mpu(Pockle *pockle, mp_int *p); /* ---------------------------------------------------------------------- * Callback API that allows key generation to report progress to its * caller. */ typedef struct ProgressReceiverVtable ProgressReceiverVtable; typedef struct ProgressReceiver ProgressReceiver; typedef union ProgressPhase ProgressPhase; union ProgressPhase { int n; void *p; }; struct ProgressReceiver { const ProgressReceiverVtable *vt; }; struct ProgressReceiverVtable { ProgressPhase (*add_linear)(ProgressReceiver *prog, double overall_cost); ProgressPhase (*add_probabilistic)(ProgressReceiver *prog, double cost_per_attempt, double attempt_probability); void (*ready)(ProgressReceiver *prog); void (*start_phase)(ProgressReceiver *prog, ProgressPhase phase); void (*report)(ProgressReceiver *prog, double progress); void (*report_attempt)(ProgressReceiver *prog); void (*report_phase_complete)(ProgressReceiver *prog); }; static inline ProgressPhase progress_add_linear(ProgressReceiver *prog, double c) { return prog->vt->add_linear(prog, c); } static inline ProgressPhase progress_add_probabilistic(ProgressReceiver *prog, double c, double p) { return prog->vt->add_probabilistic(prog, c, p); } static inline void progress_ready(ProgressReceiver *prog) { prog->vt->ready(prog); } static inline void progress_start_phase( ProgressReceiver *prog, ProgressPhase phase) { prog->vt->start_phase(prog, phase); } static inline void progress_report(ProgressReceiver *prog, double progress) { prog->vt->report(prog, progress); } static inline void progress_report_attempt(ProgressReceiver *prog) { prog->vt->report_attempt(prog); } static inline void progress_report_phase_complete(ProgressReceiver *prog) { prog->vt->report_phase_complete(prog); } ProgressPhase null_progress_add_linear( ProgressReceiver *prog, double c); ProgressPhase null_progress_add_probabilistic( ProgressReceiver *prog, double c, double p); void null_progress_ready(ProgressReceiver *prog); void null_progress_start_phase(ProgressReceiver *prog, ProgressPhase phase); void null_progress_report(ProgressReceiver *prog, double progress); void null_progress_report_attempt(ProgressReceiver *prog); void null_progress_report_phase_complete(ProgressReceiver *prog); extern const ProgressReceiverVtable null_progress_vt; /* A helper function for dreaming up progress cost estimates. */ double estimate_modexp_cost(unsigned bits); /* ---------------------------------------------------------------------- * The top-level API for generating primes. */ typedef struct PrimeGenerationPolicy PrimeGenerationPolicy; typedef struct PrimeGenerationContext PrimeGenerationContext; struct PrimeGenerationContext { const PrimeGenerationPolicy *vt; }; struct PrimeGenerationPolicy { ProgressPhase (*add_progress_phase)(const PrimeGenerationPolicy *policy, ProgressReceiver *prog, unsigned bits); PrimeGenerationContext *(*new_context)( const PrimeGenerationPolicy *policy); void (*free_context)(PrimeGenerationContext *ctx); mp_int *(*generate)( PrimeGenerationContext *ctx, PrimeCandidateSource *pcs, ProgressReceiver *prog); strbuf *(*mpu_certificate)(PrimeGenerationContext *ctx, mp_int *p); const void *extra; /* additional data a particular impl might need */ }; static inline ProgressPhase primegen_add_progress_phase( PrimeGenerationContext *ctx, ProgressReceiver *prog, unsigned bits) { return ctx->vt->add_progress_phase(ctx->vt, prog, bits); } static inline PrimeGenerationContext *primegen_new_context( const PrimeGenerationPolicy *policy) { return policy->new_context(policy); } static inline void primegen_free_context(PrimeGenerationContext *ctx) { ctx->vt->free_context(ctx); } static inline mp_int *primegen_generate( PrimeGenerationContext *ctx, PrimeCandidateSource *pcs, ProgressReceiver *prog) { return ctx->vt->generate(ctx, pcs, prog); } static inline strbuf *primegen_mpu_certificate( PrimeGenerationContext *ctx, mp_int *p) { return ctx->vt->mpu_certificate(ctx, p); } extern const PrimeGenerationPolicy primegen_probabilistic; extern const PrimeGenerationPolicy primegen_provable_fast; extern const PrimeGenerationPolicy primegen_provable_maurer_simple; extern const PrimeGenerationPolicy primegen_provable_maurer_complex; /* ---------------------------------------------------------------------- * The overall top-level API for generating entire key pairs. */ int rsa_generate(RSAKey *key, int bits, bool strong, PrimeGenerationContext *pgc, ProgressReceiver *prog); int dsa_generate(struct dss_key *key, int bits, PrimeGenerationContext *pgc, ProgressReceiver *prog); int ecdsa_generate(struct ecdsa_key *key, int bits); int eddsa_generate(struct eddsa_key *key, int bits); putty-0.76/sshmac.c0000644000175000017500000000207414072266312011204 00000000000000/* * Centralised parts of the SSH-2 MAC API, which don't need to vary * with the MAC implementation. */ #include #include "ssh.h" bool ssh2_mac_verresult(ssh2_mac *mac, const void *candidate) { unsigned char correct[64]; /* at least as big as all known MACs */ bool toret; assert(mac->vt->len <= sizeof(correct)); ssh2_mac_genresult(mac, correct); toret = smemeq(correct, candidate, mac->vt->len); smemclr(correct, sizeof(correct)); return toret; } static void ssh2_mac_prepare(ssh2_mac *mac, const void *blk, int len, unsigned long seq) { ssh2_mac_start(mac); put_uint32(mac, seq); put_data(mac, blk, len); } void ssh2_mac_generate(ssh2_mac *mac, void *blk, int len, unsigned long seq) { ssh2_mac_prepare(mac, blk, len, seq); ssh2_mac_genresult(mac, (unsigned char *)blk + len); } bool ssh2_mac_verify( ssh2_mac *mac, const void *blk, int len, unsigned long seq) { ssh2_mac_prepare(mac, blk, len, seq); return ssh2_mac_verresult(mac, (const unsigned char *)blk + len); } putty-0.76/sshmd5.c0000644000175000017500000001612714072266312011135 00000000000000/* * MD5 implementation for PuTTY. Written directly from the spec by * Simon Tatham. */ #include #include "ssh.h" static const uint32_t md5_initial_state[] = { 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, }; static const struct md5_round_constant { uint32_t addition, rotation, msg_index; } md5_round_constants[] = { { 0xd76aa478, 7, 0 }, { 0xe8c7b756, 12, 1 }, { 0x242070db, 17, 2 }, { 0xc1bdceee, 22, 3 }, { 0xf57c0faf, 7, 4 }, { 0x4787c62a, 12, 5 }, { 0xa8304613, 17, 6 }, { 0xfd469501, 22, 7 }, { 0x698098d8, 7, 8 }, { 0x8b44f7af, 12, 9 }, { 0xffff5bb1, 17, 10 }, { 0x895cd7be, 22, 11 }, { 0x6b901122, 7, 12 }, { 0xfd987193, 12, 13 }, { 0xa679438e, 17, 14 }, { 0x49b40821, 22, 15 }, { 0xf61e2562, 5, 1 }, { 0xc040b340, 9, 6 }, { 0x265e5a51, 14, 11 }, { 0xe9b6c7aa, 20, 0 }, { 0xd62f105d, 5, 5 }, { 0x02441453, 9, 10 }, { 0xd8a1e681, 14, 15 }, { 0xe7d3fbc8, 20, 4 }, { 0x21e1cde6, 5, 9 }, { 0xc33707d6, 9, 14 }, { 0xf4d50d87, 14, 3 }, { 0x455a14ed, 20, 8 }, { 0xa9e3e905, 5, 13 }, { 0xfcefa3f8, 9, 2 }, { 0x676f02d9, 14, 7 }, { 0x8d2a4c8a, 20, 12 }, { 0xfffa3942, 4, 5 }, { 0x8771f681, 11, 8 }, { 0x6d9d6122, 16, 11 }, { 0xfde5380c, 23, 14 }, { 0xa4beea44, 4, 1 }, { 0x4bdecfa9, 11, 4 }, { 0xf6bb4b60, 16, 7 }, { 0xbebfbc70, 23, 10 }, { 0x289b7ec6, 4, 13 }, { 0xeaa127fa, 11, 0 }, { 0xd4ef3085, 16, 3 }, { 0x04881d05, 23, 6 }, { 0xd9d4d039, 4, 9 }, { 0xe6db99e5, 11, 12 }, { 0x1fa27cf8, 16, 15 }, { 0xc4ac5665, 23, 2 }, { 0xf4292244, 6, 0 }, { 0x432aff97, 10, 7 }, { 0xab9423a7, 15, 14 }, { 0xfc93a039, 21, 5 }, { 0x655b59c3, 6, 12 }, { 0x8f0ccc92, 10, 3 }, { 0xffeff47d, 15, 10 }, { 0x85845dd1, 21, 1 }, { 0x6fa87e4f, 6, 8 }, { 0xfe2ce6e0, 10, 15 }, { 0xa3014314, 15, 6 }, { 0x4e0811a1, 21, 13 }, { 0xf7537e82, 6, 4 }, { 0xbd3af235, 10, 11 }, { 0x2ad7d2bb, 15, 2 }, { 0xeb86d391, 21, 9 }, }; typedef struct md5_block md5_block; struct md5_block { uint8_t block[64]; size_t used; uint64_t len; }; static inline void md5_block_setup(md5_block *blk) { blk->used = 0; blk->len = 0; } static inline bool md5_block_write( md5_block *blk, const void **vdata, size_t *len) { size_t blkleft = sizeof(blk->block) - blk->used; size_t chunk = *len < blkleft ? *len : blkleft; const uint8_t *p = *vdata; memcpy(blk->block + blk->used, p, chunk); *vdata = p + chunk; *len -= chunk; blk->used += chunk; blk->len += chunk; if (blk->used == sizeof(blk->block)) { blk->used = 0; return true; } return false; } static inline void md5_block_pad(md5_block *blk, BinarySink *bs) { uint64_t final_len = blk->len << 3; size_t pad = 63 & (55 - blk->used); put_byte(bs, 0x80); put_padding(bs, pad, 0); unsigned char buf[8]; PUT_64BIT_LSB_FIRST(buf, final_len); put_data(bs, buf, 8); smemclr(buf, 8); assert(blk->used == 0 && "Should have exactly hit a block boundary"); } static inline uint32_t rol(uint32_t x, unsigned y) { return (x << (31 & y)) | (x >> (31 & -y)); } static inline uint32_t Ch(uint32_t ctrl, uint32_t if1, uint32_t if0) { return if0 ^ (ctrl & (if1 ^ if0)); } /* Parameter functions for the four MD5 round types */ static inline uint32_t F(uint32_t x, uint32_t y, uint32_t z) { return Ch(x, y, z); } static inline uint32_t G(uint32_t x, uint32_t y, uint32_t z) { return Ch(z, x, y); } static inline uint32_t H(uint32_t x, uint32_t y, uint32_t z) { return x ^ y ^ z; } static inline uint32_t I(uint32_t x, uint32_t y, uint32_t z) { return y ^ (x | ~z); } static inline void md5_round( unsigned round_index, const uint32_t *message, uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d, uint32_t (*f)(uint32_t, uint32_t, uint32_t)) { struct md5_round_constant rc = md5_round_constants[round_index]; *a = *b + rol(*a + f(*b, *c, *d) + message[rc.msg_index] + rc.addition, rc.rotation); } static void md5_do_block(uint32_t *core, const uint8_t *block) { uint32_t message_words[16]; for (size_t i = 0; i < 16; i++) message_words[i] = GET_32BIT_LSB_FIRST(block + 4*i); uint32_t a = core[0], b = core[1], c = core[2], d = core[3]; size_t t = 0; for (size_t u = 0; u < 4; u++) { md5_round(t++, message_words, &a, &b, &c, &d, F); md5_round(t++, message_words, &d, &a, &b, &c, F); md5_round(t++, message_words, &c, &d, &a, &b, F); md5_round(t++, message_words, &b, &c, &d, &a, F); } for (size_t u = 0; u < 4; u++) { md5_round(t++, message_words, &a, &b, &c, &d, G); md5_round(t++, message_words, &d, &a, &b, &c, G); md5_round(t++, message_words, &c, &d, &a, &b, G); md5_round(t++, message_words, &b, &c, &d, &a, G); } for (size_t u = 0; u < 4; u++) { md5_round(t++, message_words, &a, &b, &c, &d, H); md5_round(t++, message_words, &d, &a, &b, &c, H); md5_round(t++, message_words, &c, &d, &a, &b, H); md5_round(t++, message_words, &b, &c, &d, &a, H); } for (size_t u = 0; u < 4; u++) { md5_round(t++, message_words, &a, &b, &c, &d, I); md5_round(t++, message_words, &d, &a, &b, &c, I); md5_round(t++, message_words, &c, &d, &a, &b, I); md5_round(t++, message_words, &b, &c, &d, &a, I); } core[0] += a; core[1] += b; core[2] += c; core[3] += d; smemclr(message_words, sizeof(message_words)); } typedef struct md5 { uint32_t core[4]; md5_block blk; BinarySink_IMPLEMENTATION; ssh_hash hash; } md5; static void md5_write(BinarySink *bs, const void *vp, size_t len); static ssh_hash *md5_new(const ssh_hashalg *alg) { md5 *s = snew(md5); s->hash.vt = alg; BinarySink_INIT(s, md5_write); BinarySink_DELEGATE_INIT(&s->hash, s); return &s->hash; } static void md5_reset(ssh_hash *hash) { md5 *s = container_of(hash, md5, hash); memcpy(s->core, md5_initial_state, sizeof(s->core)); md5_block_setup(&s->blk); } static void md5_copyfrom(ssh_hash *hcopy, ssh_hash *horig) { md5 *copy = container_of(hcopy, md5, hash); md5 *orig = container_of(horig, md5, hash); memcpy(copy, orig, sizeof(*copy)); BinarySink_COPIED(copy); BinarySink_DELEGATE_INIT(©->hash, copy); } static void md5_free(ssh_hash *hash) { md5 *s = container_of(hash, md5, hash); smemclr(s, sizeof(*s)); sfree(s); } static void md5_write(BinarySink *bs, const void *vp, size_t len) { md5 *s = BinarySink_DOWNCAST(bs, md5); while (len > 0) if (md5_block_write(&s->blk, &vp, &len)) md5_do_block(s->core, s->blk.block); } static void md5_digest(ssh_hash *hash, uint8_t *digest) { md5 *s = container_of(hash, md5, hash); md5_block_pad(&s->blk, BinarySink_UPCAST(s)); for (size_t i = 0; i < 4; i++) PUT_32BIT_LSB_FIRST(digest + 4*i, s->core[i]); } const ssh_hashalg ssh_md5 = { .new = md5_new, .reset = md5_reset, .copyfrom = md5_copyfrom, .digest = md5_digest, .free = md5_free, .hlen = 16, .blocklen = 64, HASHALG_NAMES_BARE("MD5"), }; putty-0.76/sshnogss.c0000644000175000017500000000056214072266312011575 00000000000000#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; } void ssh_gss_cleanup(struct ssh_gss_liblist *list) { sfree(list); } #endif /* NO_GSSAPI */ putty-0.76/sshppl.h0000644000175000017500000001673114072266312011251 00000000000000/* * Abstraction of the various layers of SSH packet-level protocol, * general enough to take in all three of the main SSH-2 layers and * both of the SSH-1 phases. */ #ifndef PUTTY_SSHPPL_H #define PUTTY_SSHPPL_H typedef void (*packet_handler_fn_t)(PacketProtocolLayer *ppl, PktIn *pktin); typedef struct PacketProtocolLayerVtable PacketProtocolLayerVtable; struct PacketProtocolLayerVtable { void (*free)(PacketProtocolLayer *); void (*process_queue)(PacketProtocolLayer *ppl); bool (*get_specials)( PacketProtocolLayer *ppl, add_special_fn_t add_special, void *ctx); void (*special_cmd)( PacketProtocolLayer *ppl, SessionSpecialCode code, int arg); bool (*want_user_input)(PacketProtocolLayer *ppl); void (*got_user_input)(PacketProtocolLayer *ppl); void (*reconfigure)(PacketProtocolLayer *ppl, Conf *conf); size_t (*queued_data_size)(PacketProtocolLayer *ppl); /* Protocol-level name of this layer. */ const char *name; }; struct PacketProtocolLayer { const struct PacketProtocolLayerVtable *vt; /* Link to the underlying SSH BPP. */ BinaryPacketProtocol *bpp; /* Queue from which the layer receives its input packets, and one * to put its output packets on. */ PktInQueue *in_pq; PktOutQueue *out_pq; /* Idempotent callback that in_pq will be linked to, causing a * call to the process_queue method. in_pq points to this, so it * will be automatically triggered by pushing things on the * layer's input queue, but it can also be triggered on purpose. */ IdempotentCallback ic_process_queue; /* Owner's pointer to this layer. Permits a layer to unilaterally * abdicate in favour of a replacement, by overwriting this * pointer and then freeing itself. */ PacketProtocolLayer **selfptr; /* Bufchain of keyboard input from the user, for login prompts and * similar. */ bufchain *user_input; /* Logging and error-reporting facilities. */ LogContext *logctx; Seat *seat; /* for dialog boxes, session output etc */ Ssh *ssh; /* for session termination + assorted connection-layer ops */ /* Known bugs in the remote implementation. */ unsigned remote_bugs; }; static inline void ssh_ppl_process_queue(PacketProtocolLayer *ppl) { ppl->vt->process_queue(ppl); } static inline bool ssh_ppl_get_specials( PacketProtocolLayer *ppl, add_special_fn_t add_special, void *ctx) { return ppl->vt->get_specials(ppl, add_special, ctx); } static inline void ssh_ppl_special_cmd( PacketProtocolLayer *ppl, SessionSpecialCode code, int arg) { ppl->vt->special_cmd(ppl, code, arg); } static inline bool ssh_ppl_want_user_input(PacketProtocolLayer *ppl) { return ppl->vt->want_user_input(ppl); } static inline void ssh_ppl_got_user_input(PacketProtocolLayer *ppl) { ppl->vt->got_user_input(ppl); } static inline void ssh_ppl_reconfigure(PacketProtocolLayer *ppl, Conf *conf) { ppl->vt->reconfigure(ppl, conf); } static inline size_t ssh_ppl_queued_data_size(PacketProtocolLayer *ppl) { return ppl->vt->queued_data_size(ppl); } /* ssh_ppl_free is more than just a macro wrapper on the vtable; it * does centralised parts of the freeing too. */ void ssh_ppl_free(PacketProtocolLayer *ppl); /* Helper routine to point a PPL at its input and output queues. Also * sets up the IdempotentCallback on the input queue to trigger a call * to process_queue whenever packets are added to it. */ void ssh_ppl_setup_queues(PacketProtocolLayer *ppl, PktInQueue *inq, PktOutQueue *outq); /* Routine a PPL can call to abdicate in favour of a replacement, by * overwriting ppl->selfptr. Has the side effect of freeing 'old', so * if 'old' actually called this (which is likely) then it should * avoid dereferencing itself on return from this function! */ void ssh_ppl_replace(PacketProtocolLayer *old, PacketProtocolLayer *new); /* Default implementation of queued_data_size, which just adds up the * sizes of all the packets in pq_out. A layer can override this if it * has other things to take into account as well. */ size_t ssh_ppl_default_queued_data_size(PacketProtocolLayer *ppl); PacketProtocolLayer *ssh1_login_new( Conf *conf, const char *host, int port, PacketProtocolLayer *successor_layer); PacketProtocolLayer *ssh1_connection_new( Ssh *ssh, Conf *conf, ConnectionLayer **cl_out); struct DataTransferStats; struct ssh_connection_shared_gss_state; PacketProtocolLayer *ssh2_transport_new( Conf *conf, const char *host, int port, const char *fullhostname, const char *client_greeting, const char *server_greeting, struct ssh_connection_shared_gss_state *shgss, struct DataTransferStats *stats, PacketProtocolLayer *higher_layer, const SshServerConfig *ssc); PacketProtocolLayer *ssh2_userauth_new( PacketProtocolLayer *successor_layer, const char *hostname, const char *fullhostname, Filename *keyfile, bool show_banner, bool tryagent, bool notrivialauth, const char *default_username, bool change_username, bool try_ki_auth, bool try_gssapi_auth, bool try_gssapi_kex_auth, bool gssapi_fwd, struct ssh_connection_shared_gss_state *shgss); PacketProtocolLayer *ssh2_connection_new( Ssh *ssh, ssh_sharing_state *connshare, bool is_simple, Conf *conf, const char *peer_verstring, ConnectionLayer **cl_out); /* Can't put this in the userauth constructor without having a * dependency loop at setup time (transport and userauth can't _both_ * be constructed second and given a pointer to the other). */ void ssh2_userauth_set_transport_layer(PacketProtocolLayer *userauth, PacketProtocolLayer *transport); /* Convenience macro for protocol layers to send formatted strings to * the Event Log. Assumes a function parameter called 'ppl' is in * scope. */ #define ppl_logevent(...) ( \ logevent_and_free((ppl)->logctx, dupprintf(__VA_ARGS__))) /* Convenience macro for protocol layers to send formatted strings to * the terminal. Also expects 'ppl' to be in scope. */ #define ppl_printf(...) \ ssh_ppl_user_output_string_and_free(ppl, dupprintf(__VA_ARGS__)) void ssh_ppl_user_output_string_and_free(PacketProtocolLayer *ppl, char *text); /* Methods for userauth to communicate back to the transport layer */ ptrlen ssh2_transport_get_session_id(PacketProtocolLayer *ssh2_transport_ptr); void ssh2_transport_notify_auth_done(PacketProtocolLayer *ssh2_transport_ptr); /* Shared method between ssh2 layers (defined in ssh2transport.c) to * handle the common packets between login and connection: DISCONNECT, * DEBUG and IGNORE. Those messages are handled by the ssh2transport * layer if we have one, but in bare ssh2-connection mode they have to * be handled by ssh2connection. */ bool ssh2_common_filter_queue(PacketProtocolLayer *ppl); /* Methods for ssh1login to pass protocol flags to ssh1connection */ void ssh1_connection_set_protoflags( PacketProtocolLayer *ppl, int local, int remote); /* Shared get_specials method between the two ssh1 layers */ bool ssh1_common_get_specials(PacketProtocolLayer *, add_special_fn_t, void *); /* Other shared functions between ssh1 layers */ bool ssh1_common_filter_queue(PacketProtocolLayer *ppl); void ssh1_compute_session_id( unsigned char *session_id, const unsigned char *cookie, RSAKey *hostkey, RSAKey *servkey); /* Method used by the SSH server */ void ssh2_transport_provide_hostkeys(PacketProtocolLayer *ssh2_transport_ptr, ssh_key *const *hostkeys, int nhostkeys); #endif /* PUTTY_SSHPPL_H */ putty-0.76/sshprime.c0000644000175000017500000006575514072266312011577 00000000000000/* * Prime generation. */ #include #include #include "ssh.h" #include "mpint.h" #include "mpunsafe.h" #include "sshkeygen.h" /* ---------------------------------------------------------------------- * Standard probabilistic prime-generation algorithm: * * - get a number from our PrimeCandidateSource which will at least * avoid being divisible by any prime under 2^16 * * - perform the Miller-Rabin primality test enough times to * ensure the probability of it being composite is 2^-80 or * less * * - go back to square one if any M-R test fails. */ static PrimeGenerationContext *probprime_new_context( const PrimeGenerationPolicy *policy) { PrimeGenerationContext *ctx = snew(PrimeGenerationContext); ctx->vt = policy; return ctx; } static void probprime_free_context(PrimeGenerationContext *ctx) { sfree(ctx); } static ProgressPhase probprime_add_progress_phase( const PrimeGenerationPolicy *policy, ProgressReceiver *prog, unsigned bits) { /* * The density of primes near x is 1/(log x). When x is about 2^b, * that's 1/(b log 2). * * But we're only doing the expensive part of the process (the M-R * checks) for a number that passes the initial winnowing test of * having no factor less than 2^16 (at least, unless the prime is * so small that PrimeCandidateSource gives up on that winnowing). * The density of _those_ numbers is about 1/19.76. So the odds of * hitting a prime per expensive attempt are boosted by a factor * of 19.76. */ const double log_2 = 0.693147180559945309417232121458; double winnow_factor = (bits < 32 ? 1.0 : 19.76); double prob = winnow_factor / (bits * log_2); /* * Estimate the cost of prime generation as the cost of the M-R * modexps. */ double cost = (miller_rabin_checks_needed(bits) * estimate_modexp_cost(bits)); return progress_add_probabilistic(prog, cost, prob); } static mp_int *probprime_generate( PrimeGenerationContext *ctx, PrimeCandidateSource *pcs, ProgressReceiver *prog) { pcs_ready(pcs); while (true) { progress_report_attempt(prog); mp_int *p = pcs_generate(pcs); if (!p) { pcs_free(pcs); return NULL; } MillerRabin *mr = miller_rabin_new(p); bool known_bad = false; unsigned nchecks = miller_rabin_checks_needed(mp_get_nbits(p)); for (unsigned check = 0; check < nchecks; check++) { if (!miller_rabin_test_random(mr)) { known_bad = true; break; } } miller_rabin_free(mr); if (!known_bad) { /* * We have a prime! */ pcs_free(pcs); return p; } mp_free(p); } } static strbuf *null_mpu_certificate(PrimeGenerationContext *ctx, mp_int *p) { return NULL; } const PrimeGenerationPolicy primegen_probabilistic = { probprime_add_progress_phase, probprime_new_context, probprime_free_context, probprime_generate, null_mpu_certificate, }; /* ---------------------------------------------------------------------- * Alternative provable-prime algorithm, based on the following paper: * * [MAURER] Maurer, U.M. Fast generation of prime numbers and secure * public-key cryptographic parameters. J. Cryptology 8, 123–155 * (1995). https://doi.org/10.1007/BF00202269 */ typedef enum SubprimePolicy { SPP_FAST, SPP_MAURER_SIMPLE, SPP_MAURER_COMPLEX, } SubprimePolicy; typedef struct ProvablePrimePolicyExtra { SubprimePolicy spp; } ProvablePrimePolicyExtra; typedef struct ProvablePrimeContext ProvablePrimeContext; struct ProvablePrimeContext { Pockle *pockle; PrimeGenerationContext pgc; const ProvablePrimePolicyExtra *extra; }; static PrimeGenerationContext *provableprime_new_context( const PrimeGenerationPolicy *policy) { ProvablePrimeContext *ppc = snew(ProvablePrimeContext); ppc->pgc.vt = policy; ppc->pockle = pockle_new(); ppc->extra = policy->extra; return &ppc->pgc; } static void provableprime_free_context(PrimeGenerationContext *ctx) { ProvablePrimeContext *ppc = container_of(ctx, ProvablePrimeContext, pgc); pockle_free(ppc->pockle); sfree(ppc); } static ProgressPhase provableprime_add_progress_phase( const PrimeGenerationPolicy *policy, ProgressReceiver *prog, unsigned bits) { /* * Estimating the cost of making a _provable_ prime is difficult * because of all the recursions to smaller sizes. * * Once you have enough factors of p-1 to certify primality of p, * the remaining work in provable prime generation is not very * different from probabilistic: you generate a random candidate, * test its primality probabilistically, and use the witness value * generated as a byproduct of that test for the full Pocklington * verification. The expensive part, as usual, is made of modpows. * * The Pocklington test needs at least two modpows (one for the * Fermat check, and one per known factor of p-1). * * The prior M-R step needs an unknown number, because we iterate * until we find a value whose order is divisible by the largest * power of 2 that divides p-1, say 2^j. That excludes half the * possible witness values (specifically, the quadratic residues), * so we expect to need on average two M-R operations to find one. * But that's only if the number _is_ prime - as usual, it's also * possible that we hit a non-prime and have to try again. * * So, if we were only estimating the cost of that final step, it * would look a lot like the probabilistic version: we'd have to * estimate the expected total number of modexps by knowing * something about the density of primes among our candidate * integers, and then multiply that by estimate_modexp_cost(bits). * But the problem is that we also have to _find_ a smaller prime, * so we have to recurse. * * In the MAURER_SIMPLE version of the algorithm, you recurse to * any one of a range of possible smaller sizes i, each with * probability proportional to 1/i. So your expected time to * generate an n-bit prime is given by a horrible recurrence of * the form E_n = S_n + (sum E_i/i) / (sum 1/i), in which S_n is * the expected cost of the final step once you have your smaller * primes, and both sums are over ceil(n/2) <= i <= n-20. * * At this point I ran out of effort to actually do the maths * rigorously, so instead I did the empirical experiment of * generating that sequence in Python and plotting it on a graph. * My Python code is here, in case I need it again: from math import log alpha = log(3)/log(2) + 1 # exponent for modexp using Karatsuba mult E = [1] * 16 # assume generating tiny primes is trivial for n in range(len(E), 4096): # Expected time for sub-generations, as a weighted mean of prior # values of the same sequence. lo = (n+1)//2 hi = n-20 if lo <= hi: subrange = range(lo, hi+1) num = sum(E[i]/i for i in subrange) den = sum(1/i for i in subrange) else: num, den = 0, 1 # Constant term (cost of final step). # Similar to probprime_add_progress_phase. winnow_factor = 1 if n < 32 else 19.76 prob = winnow_factor / (n * log(2)) cost = 4 * n**alpha / prob E.append(cost + num / den) for i, p in enumerate(E): try: print(log(i), log(p)) except ValueError: continue * The output loop prints the logs of both i and E_i, so that when * I plot the resulting data file in gnuplot I get a log-log * diagram. That showed me some early noise and then a very * straight-looking line; feeding the straight part of the graph * to linear-regression analysis reported that it fits the line * * log E_n = -1.7901825337965498 + 3.6199197179662517 * log(n) * => E_n = 0.16692969657466802 * n^3.6199197179662517 * * So my somewhat empirical estimate is that Maurer prime * generation costs about 0.167 * bits^3.62, in the same arbitrary * time units used by estimate_modexp_cost. */ return progress_add_linear(prog, 0.167 * pow(bits, 3.62)); } static mp_int *primegen_small(Pockle *pockle, PrimeCandidateSource *pcs) { assert(pcs_get_bits(pcs) <= 32); pcs_ready(pcs); while (true) { mp_int *p = pcs_generate(pcs); if (!p) { pcs_free(pcs); return NULL; } if (pockle_add_small_prime(pockle, p) == POCKLE_OK) { pcs_free(pcs); return p; } mp_free(p); } } #ifdef DEBUG_PRIMEGEN static void timestamp(FILE *fp) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); fprintf(fp, "%lu.%09lu: ", (unsigned long)ts.tv_sec, (unsigned long)ts.tv_nsec); } static PRINTF_LIKE(1, 2) void debug_f(const char *fmt, ...) { va_list ap; va_start(ap, fmt); timestamp(stderr); vfprintf(stderr, fmt, ap); fputc('\n', stderr); va_end(ap); } static void debug_f_mp(const char *fmt, mp_int *x, ...) { va_list ap; va_start(ap, x); timestamp(stderr); vfprintf(stderr, fmt, ap); mp_dump(stderr, "", x, "\n"); va_end(ap); } #else #define debug_f(...) ((void)0) #define debug_f_mp(...) ((void)0) #endif static double uniform_random_double(void) { unsigned char randbuf[8]; random_read(randbuf, 8); return GET_64BIT_MSB_FIRST(randbuf) * 0x1.0p-64; } static mp_int *mp_ceil_div(mp_int *n, mp_int *d) { mp_int *nplus = mp_add(n, d); mp_sub_integer_into(nplus, nplus, 1); mp_int *toret = mp_div(nplus, d); mp_free(nplus); return toret; } static mp_int *provableprime_generate_inner( ProvablePrimeContext *ppc, PrimeCandidateSource *pcs, ProgressReceiver *prog, double progress_origin, double progress_scale) { unsigned bits = pcs_get_bits(pcs); assert(bits > 1); if (bits <= 32) { debug_f("ppgi(%u) -> small", bits); return primegen_small(ppc->pockle, pcs); } unsigned min_bits_needed, max_bits_needed; { /* * Find the product of all the prime factors we already know * about. */ mp_int *size_got = mp_from_integer(1); size_t nfactors; mp_int **factors = pcs_get_known_prime_factors(pcs, &nfactors); for (size_t i = 0; i < nfactors; i++) { mp_int *to_free = size_got; size_got = mp_unsafe_shrink(mp_mul(size_got, factors[i])); mp_free(to_free); } /* * Find the largest cofactor we might be able to use, and the * smallest one we can get away with. */ mp_int *upperbound = pcs_get_upper_bound(pcs); mp_int *size_needed = mp_nthroot(upperbound, 3, NULL); debug_f_mp("upperbound = ", upperbound); { mp_int *to_free = upperbound; upperbound = mp_unsafe_shrink(mp_div(upperbound, size_got)); mp_free(to_free); } debug_f_mp("size_needed = ", size_needed); { mp_int *to_free = size_needed; size_needed = mp_unsafe_shrink(mp_ceil_div(size_needed, size_got)); mp_free(to_free); } max_bits_needed = pcs_get_bits_remaining(pcs); /* * We need a prime that is greater than or equal to * 'size_needed' in order for the product of all our known * factors of p-1 to exceed the cube root of the largest value * p might take. * * Since pcs_new wants a size specified in bits, we must count * the bits in size_needed and then add 1. Otherwise we might * get a value with the same bit count as size_needed but * slightly smaller than it. * * An exception is if size_needed = 1. In that case the * product of existing known factors is _already_ enough, so * we don't need to generate an extra factor at all. */ if (mp_hs_integer(size_needed, 2)) { min_bits_needed = mp_get_nbits(size_needed) + 1; } else { min_bits_needed = 0; } mp_free(upperbound); mp_free(size_needed); mp_free(size_got); } double progress = 0.0; if (min_bits_needed) { debug_f("ppgi(%u) recursing, need [%u,%u] more bits", bits, min_bits_needed, max_bits_needed); unsigned *sizes = NULL; size_t nsizes = 0, sizesize = 0; unsigned real_min = max_bits_needed / 2; unsigned real_max = (max_bits_needed >= 20 ? max_bits_needed - 20 : 0); if (real_min < min_bits_needed) real_min = min_bits_needed; if (real_max < real_min) real_max = real_min; debug_f("ppgi(%u) revised bits interval = [%u,%u]", bits, real_min, real_max); switch (ppc->extra->spp) { case SPP_FAST: /* * Always pick the smallest subsidiary prime we can get * away with: just over n/3 bits. * * This is not a good mode for cryptographic prime * generation, because it skews the distribution of primes * greatly, and worse, it skews them in a direction that * heads away from the properties crypto algorithms tend * to like. * * (For both discrete-log systems and RSA, people have * tended to recommend in the past that p-1 should have a * _large_ factor if possible. There's some disagreement * on which algorithms this is really necessary for, but * certainly I've never seen anyone recommend arranging a * _small_ factor on purpose.) * * I originally implemented this mode because it was * convenient for debugging - it wastes as little time as * possible on finding a sub-prime and lets you get to the * interesting part! And I leave it in the code because it * might still be useful for _something_. Because it's * cryptographically questionable, it's not selectable in * the UI of either version of PuTTYgen proper; but it can * be accessed through testcrypt, and if for some reason a * definite prime is needed for non-crypto purposes, it * may still be the fastest way to put your hands on one. */ debug_f("ppgi(%u) fast mode, just ask for %u bits", bits, min_bits_needed); sgrowarray(sizes, sizesize, nsizes); sizes[nsizes++] = min_bits_needed; break; case SPP_MAURER_SIMPLE: { /* * Select the size of the subsidiary prime at random from * sqrt(outputprime) up to outputprime/2^20, in such a way * that the probability distribution matches that of the * largest prime factor of a random n-bit number. * * Per [MAURER] section 3.4, the cumulative distribution * function of this relative size is 1+log2(x), for x in * [1/2,1]. You can generate a value from the distribution * given by a cdf by applying the inverse cdf to a uniform * value in [0,1]. Simplifying that in this case, what we * have to do is raise 2 to the power of a random real * number between -1 and 0. (And that gives you the number * of _bits_ in the sub-prime, as a factor of the desired * output number of bits.) * * We also require that the subsidiary prime q is at least * 20 bits smaller than the output one, to give us a * fighting chance of there being _any_ prime we can find * such that q | p-1. * * (But these rules have to be applied in an order that * still leaves us _some_ interval of possible sizes we * can pick!) */ maurer_simple: debug_f("ppgi(%u) Maurer simple mode", bits); unsigned sub_bits; do { double uniform = uniform_random_double(); sub_bits = real_max * pow(2.0, uniform - 1) + 0.5; debug_f(" ... %.6f -> %u?", uniform, sub_bits); } while (!(real_min <= sub_bits && sub_bits <= real_max)); debug_f("ppgi(%u) asking for %u bits", bits, sub_bits); sgrowarray(sizes, sizesize, nsizes); sizes[nsizes++] = sub_bits; break; } case SPP_MAURER_COMPLEX: { /* * In this mode, we may generate multiple factors of p-1 * which between them add up to at least n/2 bits, in such * a way that those are guaranteed to be the largest * factors of p-1 and that they have the same probability * distribution as the largest k factors would have in a * random integer. The idea is that this more elaborate * procedure gets as close as possible to the same * probability distribution you'd get by selecting a * completely random prime (if you feasibly could). * * Algorithm from Appendix 1 of [MAURER]: we generate * random real numbers that sum to at most 1, by choosing * each one uniformly from the range [0, 1 - sum of all * the previous ones]. We maintain them in a list in * decreasing order, and we stop as soon as we find an * initial subsequence of the list s_1,...,s_r such that * s_1 + ... + s_{r-1} + 2 s_r > 1. In particular, this * guarantees that the sum of that initial subsequence is * at least 1/2, so we end up with enough factors to * satisfy Pocklington. */ if (max_bits_needed / 2 + 1 > real_max) { /* Early exit path in the case where this algorithm * can't possibly generate a value in the range we * need. In that situation, fall back to Maurer * simple. */ debug_f("ppgi(%u) skipping GenerateSizeList, " "real_max too small", bits); goto maurer_simple; /* sorry! */ } double *s = NULL; size_t ns, ssize = 0; while (true) { debug_f("ppgi(%u) starting GenerateSizeList", bits); ns = 0; double range = 1.0; while (true) { /* Generate the next number */ double u = uniform_random_double() * range; range -= u; debug_f(" u_%"SIZEu" = %g", ns, u); /* Insert it in the list */ sgrowarray(s, ssize, ns); size_t i; for (i = ns; i > 0 && s[i-1] < u; i--) s[i] = s[i-1]; s[i] = u; ns++; debug_f(" inserting as s[%"SIZEu"]", i); /* Look for a suitable initial subsequence */ double sum = 0; for (i = 0; i < ns; i++) { sum += s[i]; if (sum + s[i] > 1.0) { debug_f(" s[0..%"SIZEu"] works!", i); /* Truncate the sequence here, and stop * generating random real numbers. */ ns = i+1; goto got_list; } } } got_list:; /* * Now translate those real numbers into actual bit * counts, and do a last-minute check to make sure * their product is going to be in range. * * We have to check both the min and max sizes of the * total. A b-bit number is in [2^{b-1},2^b). So the * product of numbers of sizes b_1,...,b_k is at least * 2^{\sum (b_i-1)}, and less than 2^{\sum b_i}. */ nsizes = 0; unsigned min_total = 0, max_total = 0; for (size_t i = 0; i < ns; i++) { /* These sizes are measured in actual entropy, so * add 1 bit each time to account for the * zero-information leading 1 */ unsigned this_size = max_bits_needed * s[i] + 1; debug_f(" bits[%"SIZEu"] = %u", i, this_size); sgrowarray(sizes, sizesize, nsizes); sizes[nsizes++] = this_size; min_total += this_size - 1; max_total += this_size; } debug_f(" total bits = [%u,%u)", min_total, max_total); if (min_total < real_min || max_total > real_max+1) { debug_f(" total out of range, try again"); } else { debug_f(" success! %"SIZEu" sub-primes totalling [%u,%u) " "bits", nsizes, min_total, max_total); break; } } smemclr(s, ssize * sizeof(*s)); sfree(s); break; } default: unreachable("bad subprime policy"); } for (size_t i = 0; i < nsizes; i++) { unsigned sub_bits = sizes[i]; double progress_in_this_prime = (double)sub_bits / bits; mp_int *q = provableprime_generate_inner( ppc, pcs_new(sub_bits), prog, progress_origin + progress_scale * progress, progress_scale * progress_in_this_prime); progress += progress_in_this_prime; assert(q); debug_f_mp("ppgi(%u) got factor ", q, bits); pcs_require_residue_1_mod_prime(pcs, q); mp_free(q); } smemclr(sizes, sizesize * sizeof(*sizes)); sfree(sizes); } else { debug_f("ppgi(%u) no need to recurse", bits); } debug_f("ppgi(%u) ready, %u bits remaining", bits, pcs_get_bits_remaining(pcs)); pcs_ready(pcs); while (true) { mp_int *p = pcs_generate(pcs); if (!p) { pcs_free(pcs); return NULL; } debug_f_mp("provable_step p=", p); MillerRabin *mr = miller_rabin_new(p); debug_f("provable_step mr setup done"); mp_int *witness = miller_rabin_find_potential_primitive_root(mr); miller_rabin_free(mr); if (!witness) { debug_f("provable_step mr failed"); mp_free(p); continue; } size_t nfactors; mp_int **factors = pcs_get_known_prime_factors(pcs, &nfactors); PockleStatus st = pockle_add_prime( ppc->pockle, p, factors, nfactors, witness); if (st != POCKLE_OK) { debug_f("provable_step proof failed %d", (int)st); /* * Check by assertion that the error status is not one of * the ones we ought to have ruled out already by * construction. If there's a bug in this code that means * we can _never_ pass this test (e.g. picking products of * factors that never quite reach cbrt(n)), we'd rather * fail an assertion than loop forever. */ assert(st == POCKLE_DISCRIMINANT_IS_SQUARE || st == POCKLE_WITNESS_POWER_IS_1 || st == POCKLE_WITNESS_POWER_NOT_COPRIME); mp_free(p); if (witness) mp_free(witness); continue; } mp_free(witness); pcs_free(pcs); debug_f_mp("ppgi(%u) done, got ", p, bits); progress_report(prog, progress_origin + progress_scale); return p; } } static mp_int *provableprime_generate( PrimeGenerationContext *ctx, PrimeCandidateSource *pcs, ProgressReceiver *prog) { ProvablePrimeContext *ppc = container_of(ctx, ProvablePrimeContext, pgc); mp_int *p = provableprime_generate_inner(ppc, pcs, prog, 0.0, 1.0); return p; } static inline strbuf *provableprime_mpu_certificate( PrimeGenerationContext *ctx, mp_int *p) { ProvablePrimeContext *ppc = container_of(ctx, ProvablePrimeContext, pgc); return pockle_mpu(ppc->pockle, p); } #define DECLARE_POLICY(name, policy) \ static const struct ProvablePrimePolicyExtra \ pppextra_##name = {policy}; \ const PrimeGenerationPolicy name = { \ provableprime_add_progress_phase, \ provableprime_new_context, \ provableprime_free_context, \ provableprime_generate, \ provableprime_mpu_certificate, \ &pppextra_##name, \ } DECLARE_POLICY(primegen_provable_fast, SPP_FAST); DECLARE_POLICY(primegen_provable_maurer_simple, SPP_MAURER_SIMPLE); DECLARE_POLICY(primegen_provable_maurer_complex, SPP_MAURER_COMPLEX); /* ---------------------------------------------------------------------- * Reusable null implementation of the progress-reporting API. */ static inline ProgressPhase null_progress_add(void) { ProgressPhase ph = { .n = 0 }; return ph; } ProgressPhase null_progress_add_linear( ProgressReceiver *prog, double c) { return null_progress_add(); } ProgressPhase null_progress_add_probabilistic( ProgressReceiver *prog, double c, double p) { return null_progress_add(); } void null_progress_ready(ProgressReceiver *prog) {} void null_progress_start_phase(ProgressReceiver *prog, ProgressPhase phase) {} void null_progress_report(ProgressReceiver *prog, double progress) {} void null_progress_report_attempt(ProgressReceiver *prog) {} void null_progress_report_phase_complete(ProgressReceiver *prog) {} const ProgressReceiverVtable null_progress_vt = { .add_linear = null_progress_add_linear, .add_probabilistic = null_progress_add_probabilistic, .ready = null_progress_ready, .start_phase = null_progress_start_phase, .report = null_progress_report, .report_attempt = null_progress_report_attempt, .report_phase_complete = null_progress_report_phase_complete, }; /* ---------------------------------------------------------------------- * Helper function for progress estimation. */ double estimate_modexp_cost(unsigned bits) { /* * A modexp of n bits goes roughly like O(n^2.58), on the grounds * that our modmul is O(n^1.58) (Karatsuba) and you need O(n) of * them in a modexp. */ return pow(bits, 2.58); } putty-0.76/sshprng.c0000644000175000017500000002146514072266312011417 00000000000000/* * sshprng.c: PuTTY's cryptographic pseudorandom number generator. * * This module just defines the PRNG object type and its methods. The * usual global instance of it is managed by sshrand.c. */ #include "putty.h" #include "ssh.h" #include "mpint_i.h" #ifdef PRNG_DIAGNOSTICS #define prngdebug debug #else #define prngdebug(...) ((void)0) #endif /* * This random number generator is based on the 'Fortuna' design by * Niels Ferguson and Bruce Schneier. The biggest difference is that I * use SHA-256 in place of a block cipher: the generator side of the * system works by computing HASH(key || counter) instead of * ENCRYPT(counter, key). * * Rationale: the Fortuna description itself suggests that using * SHA-256 would be nice but people wouldn't accept it because it's * too slow - but PuTTY isn't a heavy enough user of random numbers to * make that a serious worry. In fact even with SHA-256 this generator * is faster than the one we previously used. Also the Fortuna * description worries about periodic rekeying to avoid the barely * detectable pattern of never repeating a cipher block - but with * SHA-256, even that shouldn't be a worry, because the output * 'blocks' are twice the size, and also SHA-256 has no guarantee of * bijectivity, so it surely _could_ be possible to generate the same * block from two counter values. Thirdly, Fortuna has to have a hash * function anyway, for reseeding and entropy collection, so reusing * the same one means it only depends on one underlying primitive and * can be easily reinstantiated with a larger hash function if you * decide you'd like to do that on a particular occasion. */ #define NCOLLECTORS 32 #define RESEED_DATA_SIZE 64 typedef struct prng_impl prng_impl; struct prng_impl { prng Prng; const ssh_hashalg *hashalg; /* * Generation side: * * 'generator' is a hash object with the current key preloaded * into it. The counter-mode generation is achieved by copying * that hash object, appending the counter value to the copy, and * calling ssh_hash_final. */ ssh_hash *generator; BignumInt counter[128 / BIGNUM_INT_BITS]; /* * When re-seeding the generator, you call prng_seed_begin(), * which sets up a hash object in 'keymaker'. You write your new * seed data into it (which you can do by calling put_data on the * PRNG object itself) and then call prng_seed_finish(), which * finalises this hash and uses the output to set up the new * generator. * * The keymaker hash preimage includes the previous key, so if you * just want to change keys for the sake of not keeping the same * one for too long, you don't have to put any extra seed data in * at all. */ ssh_hash *keymaker; /* * Collection side: * * There are NCOLLECTORS hash objects collecting entropy. Each * separately numbered entropy source puts its output into those * hash objects in the order 0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,..., * that is to say, each entropy source has a separate counter * which is incremented every time that source generates an event, * and the event data is added to the collector corresponding to * the index of the lowest set bit in the current counter value. * * Whenever collector #0 has at least RESEED_DATA_SIZE bytes (and * it's not at least 100ms since the last reseed), the PRNG is * reseeded, with seed data on reseed #n taken from the first j * collectors, where j is one more than the number of factors of 2 * in n. That is, collector #0 is used in every reseed; #1 in * every other one, #2 in every fourth, etc. * * 'until_reseed' counts the amount of data that still needs to be * added to collector #0 before a reseed will be triggered. */ uint32_t source_counters[NOISE_MAX_SOURCES]; ssh_hash *collectors[NCOLLECTORS]; size_t until_reseed; uint32_t reseeds; uint64_t last_reseed_time; }; static void prng_seed_BinarySink_write( BinarySink *bs, const void *data, size_t len); prng *prng_new(const ssh_hashalg *hashalg) { prng_impl *pi = snew(prng_impl); memset(pi, 0, sizeof(prng_impl)); pi->hashalg = hashalg; pi->keymaker = NULL; pi->generator = NULL; memset(pi->counter, 0, sizeof(pi->counter)); for (size_t i = 0; i < NCOLLECTORS; i++) pi->collectors[i] = ssh_hash_new(pi->hashalg); pi->until_reseed = 0; BinarySink_INIT(&pi->Prng, prng_seed_BinarySink_write); pi->Prng.savesize = pi->hashalg->hlen * 4; return &pi->Prng; } void prng_free(prng *pr) { prng_impl *pi = container_of(pr, prng_impl, Prng); smemclr(pi->counter, sizeof(pi->counter)); for (size_t i = 0; i < NCOLLECTORS; i++) ssh_hash_free(pi->collectors[i]); if (pi->generator) ssh_hash_free(pi->generator); if (pi->keymaker) ssh_hash_free(pi->keymaker); smemclr(pi, sizeof(*pi)); sfree(pi); } void prng_seed_begin(prng *pr) { prng_impl *pi = container_of(pr, prng_impl, Prng); assert(!pi->keymaker); prngdebug("prng: reseed begin\n"); /* * Make a hash instance that will generate the key for the new one. */ if (pi->generator) { pi->keymaker = pi->generator; pi->generator = NULL; } else { pi->keymaker = ssh_hash_new(pi->hashalg); } put_byte(pi->keymaker, 'R'); } static void prng_seed_BinarySink_write( BinarySink *bs, const void *data, size_t len) { prng *pr = BinarySink_DOWNCAST(bs, prng); prng_impl *pi = container_of(pr, prng_impl, Prng); assert(pi->keymaker); prngdebug("prng: got %"SIZEu" bytes of seed\n", len); put_data(pi->keymaker, data, len); } void prng_seed_finish(prng *pr) { prng_impl *pi = container_of(pr, prng_impl, Prng); unsigned char buf[MAX_HASH_LEN]; assert(pi->keymaker); prngdebug("prng: reseed finish\n"); /* * Actually generate the key. */ ssh_hash_final(pi->keymaker, buf); pi->keymaker = NULL; /* * Load that key into a fresh hash instance, which will become the * new generator. */ assert(!pi->generator); pi->generator = ssh_hash_new(pi->hashalg); put_data(pi->generator, buf, pi->hashalg->hlen); pi->until_reseed = RESEED_DATA_SIZE; pi->last_reseed_time = prng_reseed_time_ms(); smemclr(buf, sizeof(buf)); } static inline void prng_generate(prng_impl *pi, void *outbuf) { ssh_hash *h = ssh_hash_copy(pi->generator); prngdebug("prng_generate\n"); put_byte(h, 'G'); for (unsigned i = 0; i < 128; i += 8) put_byte(h, pi->counter[i/BIGNUM_INT_BITS] >> (i%BIGNUM_INT_BITS)); BignumCarry c = 1; for (unsigned i = 0; i < lenof(pi->counter); i++) BignumADC(pi->counter[i], c, pi->counter[i], 0, c); ssh_hash_final(h, outbuf); } void prng_read(prng *pr, void *vout, size_t size) { prng_impl *pi = container_of(pr, prng_impl, Prng); unsigned char buf[MAX_HASH_LEN]; assert(!pi->keymaker); prngdebug("prng_read %"SIZEu"\n", size); uint8_t *out = (uint8_t *)vout; while (size > 0) { prng_generate(pi, buf); size_t to_use = size > pi->hashalg->hlen ? pi->hashalg->hlen : size; memcpy(out, buf, to_use); out += to_use; size -= to_use; } smemclr(buf, sizeof(buf)); prng_seed_begin(&pi->Prng); prng_seed_finish(&pi->Prng); } void prng_add_entropy(prng *pr, unsigned source_id, ptrlen data) { prng_impl *pi = container_of(pr, prng_impl, Prng); assert(source_id < NOISE_MAX_SOURCES); uint32_t counter = ++pi->source_counters[source_id]; size_t index = 0; while (index+1 < NCOLLECTORS && !(counter & 1)) { counter >>= 1; index++; } prngdebug("prng_add_entropy source=%u size=%"SIZEu" -> collector %zi\n", source_id, data.len, index); put_datapl(pi->collectors[index], data); if (index == 0) pi->until_reseed = (pi->until_reseed < data.len ? 0 : pi->until_reseed - data.len); if (pi->until_reseed == 0 && prng_reseed_time_ms() - pi->last_reseed_time >= 100) { prng_seed_begin(&pi->Prng); unsigned char buf[MAX_HASH_LEN]; uint32_t reseed_index = ++pi->reseeds; prngdebug("prng entropy reseed #%"PRIu32"\n", reseed_index); for (size_t i = 0; i < NCOLLECTORS; i++) { prngdebug("emptying collector %"SIZEu"\n", i); ssh_hash_digest(pi->collectors[i], buf); put_data(&pi->Prng, buf, pi->hashalg->hlen); ssh_hash_reset(pi->collectors[i]); if (reseed_index & 1) break; reseed_index >>= 1; } smemclr(buf, sizeof(buf)); prng_seed_finish(&pi->Prng); } } size_t prng_seed_bits(prng *pr) { prng_impl *pi = container_of(pr, prng_impl, Prng); return pi->hashalg->hlen * 8; } putty-0.76/sshpubk.c0000644000175000017500000016172414072266312011415 00000000000000/* * Generic SSH public-key handling operations. In particular, * reading of SSH public-key files, and also the generic `sign' * operation for SSH-2 (which checks the type of the key and * dispatches to the appropriate key-type specific function). */ #include #include #include #include #include #include #include "putty.h" #include "mpint.h" #include "ssh.h" #include "misc.h" /* * Fairly arbitrary size limit on any public or private key blob. * Chosen to match AGENT_MAX_MSGLEN, on the basis that any key too * large to transfer over the ssh-agent protocol is probably too large * to be useful in general. * * MAX_KEY_BLOB_LINES is the corresponding limit on the Public-Lines * or Private-Lines header field in a key file. */ #define MAX_KEY_BLOB_SIZE 262144 #define MAX_KEY_BLOB_LINES (MAX_KEY_BLOB_SIZE / 48) /* * Corresponding limit on the size of a key _file_ itself, based on * base64-encoding the key blob and then adding a few Kb for * surrounding metadata. */ #define MAX_KEY_FILE_SIZE (MAX_KEY_BLOB_SIZE * 4 / 3 + 4096) static const ptrlen rsa1_signature = PTRLEN_DECL_LITERAL("SSH PRIVATE KEY FILE FORMAT 1.1\n\0"); #define BASE64_TOINT(x) ( (x)-'A'<26 ? (x)-'A'+0 :\ (x)-'a'<26 ? (x)-'a'+26 :\ (x)-'0'<10 ? (x)-'0'+52 :\ (x)=='+' ? 62 : \ (x)=='/' ? 63 : 0 ) LoadedFile *lf_new(size_t max_size) { LoadedFile *lf = snew_plus(LoadedFile, max_size); lf->data = snew_plus_get_aux(lf); lf->len = 0; lf->max_size = max_size; return lf; } void lf_free(LoadedFile *lf) { smemclr(lf->data, lf->max_size); smemclr(lf, sizeof(LoadedFile)); sfree(lf); } LoadFileStatus lf_load_fp(LoadedFile *lf, FILE *fp) { lf->len = 0; while (lf->len < lf->max_size) { size_t retd = fread(lf->data + lf->len, 1, lf->max_size - lf->len, fp); if (ferror(fp)) return LF_ERROR; if (retd == 0) break; lf->len += retd; } LoadFileStatus status = LF_OK; if (lf->len == lf->max_size) { /* The file might be too long to fit in our fixed-size * structure. Try reading one more byte, to check. */ if (fgetc(fp) != EOF) status = LF_TOO_BIG; } BinarySource_INIT(lf, lf->data, lf->len); return status; } LoadFileStatus lf_load(LoadedFile *lf, const Filename *filename) { FILE *fp = f_open(filename, "rb", false); if (!fp) return LF_ERROR; LoadFileStatus status = lf_load_fp(lf, fp); fclose(fp); return status; } static inline bool lf_load_keyfile_helper(LoadFileStatus status, const char **errptr) { const char *error; switch (status) { case LF_OK: return true; case LF_TOO_BIG: error = "file is too large to be a key file"; break; case LF_ERROR: error = strerror(errno); break; default: unreachable("bad status value in lf_load_keyfile_helper"); } if (errptr) *errptr = error; return false; } LoadedFile *lf_load_keyfile(const Filename *filename, const char **errptr) { LoadedFile *lf = lf_new(MAX_KEY_FILE_SIZE); if (!lf_load_keyfile_helper(lf_load(lf, filename), errptr)) { lf_free(lf); return NULL; } return lf; } LoadedFile *lf_load_keyfile_fp(FILE *fp, const char **errptr) { LoadedFile *lf = lf_new(MAX_KEY_FILE_SIZE); if (!lf_load_keyfile_helper(lf_load_fp(lf, fp), errptr)) { lf_free(lf); return NULL; } return lf; } static bool expect_signature(BinarySource *src, ptrlen realsig) { ptrlen thissig = get_data(src, realsig.len); return !get_err(src) && ptrlen_eq_ptrlen(realsig, thissig); } static int rsa1_load_s_internal(BinarySource *src, RSAKey *key, bool pub_only, char **commentptr, const char *passphrase, const char **error) { strbuf *buf = NULL; int ciphertype; int ret = 0; ptrlen comment; *error = "not an SSH-1 RSA file"; if (!expect_signature(src, rsa1_signature)) goto end; *error = "file format error"; /* One byte giving encryption type, and one reserved uint32. */ ciphertype = get_byte(src); if (ciphertype != 0 && ciphertype != SSH1_CIPHER_3DES) goto end; if (get_uint32(src) != 0) goto end; /* reserved field nonzero, panic! */ /* Now the serious stuff. An ordinary SSH-1 public key. */ get_rsa_ssh1_pub(src, key, RSA_SSH1_MODULUS_FIRST); /* Next, the comment field. */ comment = get_string(src); if (commentptr) *commentptr = mkstr(comment); if (key) key->comment = mkstr(comment); if (pub_only) { ret = 1; goto end; } if (!key) { ret = ciphertype != 0; *error = NULL; goto end; } /* * Decrypt remainder of buffer. */ if (ciphertype) { size_t enclen = get_avail(src); if (enclen & 7) goto end; buf = strbuf_new_nm(); put_datapl(buf, get_data(src, enclen)); unsigned char keybuf[16]; hash_simple(&ssh_md5, ptrlen_from_asciz(passphrase), keybuf); des3_decrypt_pubkey(keybuf, buf->u, enclen); smemclr(keybuf, sizeof(keybuf)); /* burn the evidence */ BinarySource_BARE_INIT_PL(src, ptrlen_from_strbuf(buf)); } /* * We are now in the secret part of the key. The first four * bytes should be of the form a, b, a, b. */ { int b0a = get_byte(src); int b1a = get_byte(src); int b0b = get_byte(src); int b1b = get_byte(src); if (b0a != b0b || b1a != b1b) { *error = "wrong passphrase"; ret = -1; goto end; } } /* * After that, we have one further bignum which is our * decryption exponent, and then the three auxiliary values * (iqmp, q, p). */ get_rsa_ssh1_priv(src, key); key->iqmp = get_mp_ssh1(src); key->q = get_mp_ssh1(src); key->p = get_mp_ssh1(src); if (!rsa_verify(key)) { *error = "rsa_verify failed"; freersakey(key); ret = 0; } else { *error = NULL; ret = 1; } end: if (buf) strbuf_free(buf); return ret; } int rsa1_load_s(BinarySource *src, RSAKey *key, const char *passphrase, const char **errstr) { return rsa1_load_s_internal(src, key, false, NULL, passphrase, errstr); } int rsa1_load_f(const Filename *filename, RSAKey *key, const char *passphrase, const char **errstr) { LoadedFile *lf = lf_load_keyfile(filename, errstr); if (!lf) return false; int toret = rsa1_load_s(BinarySource_UPCAST(lf), key, passphrase, errstr); lf_free(lf); return toret; } /* * See whether an RSA key is encrypted. Return its comment field as * well. */ bool rsa1_encrypted_s(BinarySource *src, char **comment) { const char *dummy; return rsa1_load_s_internal(src, NULL, false, comment, NULL, &dummy) == 1; } bool rsa1_encrypted_f(const Filename *filename, char **comment) { LoadedFile *lf = lf_load_keyfile(filename, NULL); if (!lf) return false; /* couldn't even open the file */ bool toret = rsa1_encrypted_s(BinarySource_UPCAST(lf), comment); lf_free(lf); return toret; } /* * Read the public part of an SSH-1 RSA key from a file (public or * private), and generate its public blob in exponent-first order. */ int rsa1_loadpub_s(BinarySource *src, BinarySink *bs, char **commentptr, const char **errorstr) { RSAKey key; int ret; const char *error = NULL; /* Default return if we fail. */ ret = 0; bool is_privkey_file = expect_signature(src, rsa1_signature); BinarySource_REWIND(src); if (is_privkey_file) { /* * Load just the public half from an SSH-1 private key file. */ memset(&key, 0, sizeof(key)); if (rsa1_load_s_internal(src, &key, true, commentptr, NULL, &error)) { rsa_ssh1_public_blob(bs, &key, RSA_SSH1_EXPONENT_FIRST); freersakey(&key); ret = 1; } } else { /* * Try interpreting the file as an SSH-1 public key. */ char *line, *p, *bitsp, *expp, *modp, *commentp; line = mkstr(get_chomped_line(src)); p = line; bitsp = p; p += strspn(p, "0123456789"); if (*p != ' ') goto not_public_either; *p++ = '\0'; expp = p; p += strspn(p, "0123456789"); if (*p != ' ') goto not_public_either; *p++ = '\0'; modp = p; p += strspn(p, "0123456789"); if (*p) { if (*p != ' ') goto not_public_either; *p++ = '\0'; commentp = p; } else { commentp = NULL; } memset(&key, 0, sizeof(key)); key.exponent = mp_from_decimal(expp); key.modulus = mp_from_decimal(modp); if (atoi(bitsp) != mp_get_nbits(key.modulus)) { mp_free(key.exponent); mp_free(key.modulus); sfree(line); error = "key bit count does not match in SSH-1 public key file"; goto end; } if (commentptr) *commentptr = commentp ? dupstr(commentp) : NULL; rsa_ssh1_public_blob(bs, &key, RSA_SSH1_EXPONENT_FIRST); freersakey(&key); sfree(line); return 1; not_public_either: sfree(line); error = "not an SSH-1 RSA file"; } end: if ((ret != 1) && errorstr) *errorstr = error; return ret; } int rsa1_loadpub_f(const Filename *filename, BinarySink *bs, char **commentptr, const char **errorstr) { LoadedFile *lf = lf_load_keyfile(filename, errorstr); if (!lf) return 0; int toret = rsa1_loadpub_s(BinarySource_UPCAST(lf), bs, commentptr, errorstr); lf_free(lf); return toret; } strbuf *rsa1_save_sb(RSAKey *key, const char *passphrase) { strbuf *buf = strbuf_new_nm(); int estart; /* * The public part of the key. */ put_datapl(buf, rsa1_signature); put_byte(buf, passphrase ? SSH1_CIPHER_3DES : 0); /* encryption type */ put_uint32(buf, 0); /* reserved */ rsa_ssh1_public_blob(BinarySink_UPCAST(buf), key, RSA_SSH1_MODULUS_FIRST); put_stringz(buf, NULLTOEMPTY(key->comment)); /* * The encrypted portion starts here. */ estart = buf->len; /* * Two bytes, then the same two bytes repeated. */ { uint8_t bytes[2]; random_read(bytes, 2); put_data(buf, bytes, 2); put_data(buf, bytes, 2); } /* * Four more bignums: the decryption exponent, then iqmp, then * q, then p. */ put_mp_ssh1(buf, key->private_exponent); put_mp_ssh1(buf, key->iqmp); put_mp_ssh1(buf, key->q); put_mp_ssh1(buf, key->p); /* * Now write zeros until the encrypted portion is a multiple of * 8 bytes. */ put_padding(buf, (estart - buf->len) & 7, 0); /* * Now encrypt the encrypted portion. */ if (passphrase) { unsigned char keybuf[16]; hash_simple(&ssh_md5, ptrlen_from_asciz(passphrase), keybuf); des3_encrypt_pubkey(keybuf, buf->u + estart, buf->len - estart); smemclr(keybuf, sizeof(keybuf)); /* burn the evidence */ } return buf; } /* * Save an RSA key file. Return true on success. */ bool rsa1_save_f(const Filename *filename, RSAKey *key, const char *passphrase) { FILE *fp = f_open(filename, "wb", true); if (!fp) return false; strbuf *buf = rsa1_save_sb(key, passphrase); bool toret = fwrite(buf->s, 1, buf->len, fp) == buf->len; if (fclose(fp)) toret = false; strbuf_free(buf); return toret; } /* ---------------------------------------------------------------------- * SSH-2 private key load/store functions. * * PuTTY's own file format for SSH-2 keys is given in doc/ppk.but, aka * the "PPK file format" appendix in the PuTTY manual. */ static bool read_header(BinarySource *src, char *header) { int len = 39; int c; while (1) { c = get_byte(src); if (c == '\n' || c == '\r' || get_err(src)) return false; /* failure */ if (c == ':') { c = get_byte(src); if (c != ' ') return false; *header = '\0'; return true; /* success! */ } if (len == 0) return false; /* failure */ *header++ = c; len--; } return false; /* failure */ } static char *read_body(BinarySource *src) { strbuf *buf = strbuf_new_nm(); while (1) { int c = get_byte(src); if (c == '\r' || c == '\n' || get_err(src)) { if (!get_err(src)) { c = get_byte(src); if (c != '\r' && c != '\n' && !get_err(src)) src->pos--; } return strbuf_to_str(buf); } put_byte(buf, c); } } static bool read_blob(BinarySource *src, int nlines, BinarySink *bs) { unsigned char *blob; char *line; int linelen; int i, j, k; /* We expect at most 64 base64 characters, ie 48 real bytes, per line. */ assert(nlines < MAX_KEY_BLOB_LINES); blob = snewn(48 * nlines, unsigned char); for (i = 0; i < nlines; i++) { line = read_body(src); if (!line) { sfree(blob); return false; } linelen = strlen(line); if (linelen % 4 != 0 || linelen > 64) { sfree(blob); sfree(line); return false; } for (j = 0; j < linelen; j += 4) { unsigned char decoded[3]; k = base64_decode_atom(line + j, decoded); if (!k) { sfree(line); sfree(blob); return false; } put_data(bs, decoded, k); } sfree(line); } sfree(blob); return true; } /* * Magic error return value for when the passphrase is wrong. */ ssh2_userkey ssh2_wrong_passphrase = { NULL, NULL }; const ssh_keyalg *const all_keyalgs[] = { &ssh_rsa, &ssh_rsa_sha256, &ssh_rsa_sha512, &ssh_dss, &ssh_ecdsa_nistp256, &ssh_ecdsa_nistp384, &ssh_ecdsa_nistp521, &ssh_ecdsa_ed25519, &ssh_ecdsa_ed448, }; const size_t n_keyalgs = lenof(all_keyalgs); const ssh_keyalg *find_pubkey_alg_len(ptrlen name) { for (size_t i = 0; i < n_keyalgs; i++) if (ptrlen_eq_string(name, all_keyalgs[i]->ssh_id)) return all_keyalgs[i]; return NULL; } const ssh_keyalg *find_pubkey_alg(const char *name) { return find_pubkey_alg_len(ptrlen_from_asciz(name)); } struct ppk_cipher { const char *name; size_t blocklen, keylen, ivlen; }; static const struct ppk_cipher ppk_cipher_none = { "none", 1, 0, 0 }; static const struct ppk_cipher ppk_cipher_aes256_cbc = { "aes256-cbc", 16, 32, 16 }; static void ssh2_ppk_derive_keys( unsigned fmt_version, const struct ppk_cipher *ciphertype, ptrlen passphrase, strbuf *storage, ptrlen *cipherkey, ptrlen *cipheriv, ptrlen *mackey, ptrlen passphrase_salt, ppk_save_parameters *params) { size_t mac_keylen; switch (fmt_version) { case 3: { if (ciphertype->keylen == 0) { mac_keylen = 0; break; } ptrlen empty = PTRLEN_LITERAL(""); mac_keylen = 32; uint32_t taglen = ciphertype->keylen + ciphertype->ivlen + mac_keylen; if (params->argon2_passes_auto) { uint32_t passes; argon2_choose_passes( params->argon2_flavour, params->argon2_mem, params->argon2_milliseconds, &passes, params->argon2_parallelism, taglen, passphrase, passphrase_salt, empty, empty, storage); params->argon2_passes_auto = false; params->argon2_passes = passes; } else { argon2(params->argon2_flavour, params->argon2_mem, params->argon2_passes, params->argon2_parallelism, taglen, passphrase, passphrase_salt, empty, empty, storage); } break; } case 2: case 1: { /* Counter-mode iteration to generate cipher key data. */ for (unsigned ctr = 0; ctr * 20 < ciphertype->keylen; ctr++) { ssh_hash *h = ssh_hash_new(&ssh_sha1); put_uint32(h, ctr); put_datapl(h, passphrase); ssh_hash_final(h, strbuf_append(storage, 20)); } strbuf_shrink_to(storage, ciphertype->keylen); /* In this version of the format, the CBC IV was always all 0. */ put_padding(storage, ciphertype->ivlen, 0); /* Completely separate hash for the MAC key. */ ssh_hash *h = ssh_hash_new(&ssh_sha1); mac_keylen = ssh_hash_alg(h)->hlen; put_datapl(h, PTRLEN_LITERAL("putty-private-key-file-mac-key")); put_datapl(h, passphrase); ssh_hash_final(h, strbuf_append(storage, mac_keylen)); break; } default: unreachable("bad format version in ssh2_ppk_derive_keys"); } BinarySource src[1]; BinarySource_BARE_INIT_PL(src, ptrlen_from_strbuf(storage)); *cipherkey = get_data(src, ciphertype->keylen); *cipheriv = get_data(src, ciphertype->ivlen); *mackey = get_data(src, mac_keylen); } static int userkey_parse_line_counter(const char *text) { char *endptr; unsigned long ul = strtoul(text, &endptr, 10); if (*text && !*endptr && ul < MAX_KEY_BLOB_LINES) return ul; else return -1; } static bool str_to_uint32_t(const char *s, uint32_t *out) { char *endptr; unsigned long converted = strtoul(s, &endptr, 10); if (*s && !*endptr && converted <= ~(uint32_t)0) { *out = converted; return true; } else { return false; } } ssh2_userkey *ppk_load_s(BinarySource *src, const char *passphrase, const char **errorstr) { char header[40], *b, *encryption, *comment, *mac; const ssh_keyalg *alg; ssh2_userkey *ret; strbuf *public_blob, *private_blob, *cipher_mac_keys_blob; strbuf *passphrase_salt = strbuf_new(); ptrlen cipherkey, cipheriv, mackey; const struct ppk_cipher *ciphertype; int i; bool is_mac; unsigned fmt_version; const char *error = NULL; ppk_save_parameters params; ret = NULL; /* return NULL for most errors */ encryption = comment = mac = NULL; public_blob = private_blob = cipher_mac_keys_blob = NULL; /* Read the first header line which contains the key type. */ if (!read_header(src, header)) { error = "no header line found in key file"; goto error; } if (0 == strcmp(header, "PuTTY-User-Key-File-3")) { fmt_version = 3; } else if (0 == strcmp(header, "PuTTY-User-Key-File-2")) { fmt_version = 2; } else if (0 == strcmp(header, "PuTTY-User-Key-File-1")) { /* this is an old key file; warn and then continue */ old_keyfile_warning(); fmt_version = 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(src)) == NULL) goto error; /* Select key algorithm structure. */ alg = find_pubkey_alg(b); if (!alg) { sfree(b); goto error; } sfree(b); /* Read the Encryption header line. */ if (!read_header(src, header) || 0 != strcmp(header, "Encryption")) goto error; if ((encryption = read_body(src)) == NULL) goto error; if (!strcmp(encryption, "aes256-cbc")) { ciphertype = &ppk_cipher_aes256_cbc; } else if (!strcmp(encryption, "none")) { ciphertype = &ppk_cipher_none; } else { goto error; } /* Read the Comment header line. */ if (!read_header(src, header) || 0 != strcmp(header, "Comment")) goto error; if ((comment = read_body(src)) == NULL) goto error; memset(¶ms, 0, sizeof(params)); /* in particular, sets * passes_auto=false */ /* Read the Public-Lines header line and the public blob. */ if (!read_header(src, header) || 0 != strcmp(header, "Public-Lines")) goto error; if ((b = read_body(src)) == NULL) goto error; i = userkey_parse_line_counter(b); sfree(b); if (i < 0) goto error; public_blob = strbuf_new(); if (!read_blob(src, i, BinarySink_UPCAST(public_blob))) goto error; if (fmt_version >= 3 && ciphertype->keylen != 0) { /* Read Argon2 key derivation parameters. */ if (!read_header(src, header) || 0 != strcmp(header, "Key-Derivation")) goto error; if ((b = read_body(src)) == NULL) goto error; if (!strcmp(b, "Argon2d")) { params.argon2_flavour = Argon2d; } else if (!strcmp(b, "Argon2i")) { params.argon2_flavour = Argon2i; } else if (!strcmp(b, "Argon2id")) { params.argon2_flavour = Argon2id; } else { sfree(b); goto error; } sfree(b); if (!read_header(src, header) || 0 != strcmp(header, "Argon2-Memory")) goto error; if ((b = read_body(src)) == NULL) goto error; if (!str_to_uint32_t(b, ¶ms.argon2_mem)) { sfree(b); goto error; } sfree(b); if (!read_header(src, header) || 0 != strcmp(header, "Argon2-Passes")) goto error; if ((b = read_body(src)) == NULL) goto error; if (!str_to_uint32_t(b, ¶ms.argon2_passes)) { sfree(b); goto error; } sfree(b); if (!read_header(src, header) || 0 != strcmp(header, "Argon2-Parallelism")) goto error; if ((b = read_body(src)) == NULL) goto error; if (!str_to_uint32_t(b, ¶ms.argon2_parallelism)) { sfree(b); goto error; } sfree(b); if (!read_header(src, header) || 0 != strcmp(header, "Argon2-Salt")) goto error; if ((b = read_body(src)) == NULL) goto error; for (size_t i = 0; b[i]; i += 2) { if (isxdigit((unsigned char)b[i]) && b[i+1] && isxdigit((unsigned char)b[i+1])) { char s[3]; s[0] = b[i]; s[1] = b[i+1]; s[2] = '\0'; put_byte(passphrase_salt, strtoul(s, NULL, 16)); } else { sfree(b); goto error; } } sfree(b); } /* Read the Private-Lines header line and the Private blob. */ if (!read_header(src, header) || 0 != strcmp(header, "Private-Lines")) goto error; if ((b = read_body(src)) == NULL) goto error; i = userkey_parse_line_counter(b); sfree(b); if (i < 0) goto error; private_blob = strbuf_new_nm(); if (!read_blob(src, i, BinarySink_UPCAST(private_blob))) goto error; /* Read the Private-MAC or Private-Hash header line. */ if (!read_header(src, header)) goto error; if (0 == strcmp(header, "Private-MAC")) { if ((mac = read_body(src)) == NULL) goto error; is_mac = true; } else if (0 == strcmp(header, "Private-Hash") && fmt_version == 1) { if ((mac = read_body(src)) == NULL) goto error; is_mac = false; } else goto error; cipher_mac_keys_blob = strbuf_new(); ssh2_ppk_derive_keys(fmt_version, ciphertype, ptrlen_from_asciz(passphrase ? passphrase : ""), cipher_mac_keys_blob, &cipherkey, &cipheriv, &mackey, ptrlen_from_strbuf(passphrase_salt), ¶ms); /* * Decrypt the private blob. */ if (private_blob->len % ciphertype->blocklen) goto error; if (ciphertype == &ppk_cipher_aes256_cbc) { aes256_decrypt_pubkey(cipherkey.ptr, cipheriv.ptr, private_blob->u, private_blob->len); } /* * Verify the MAC. */ { unsigned char binary[32]; char realmac[sizeof(binary) * 2 + 1]; strbuf *macdata; bool free_macdata; const ssh2_macalg *mac_alg = fmt_version <= 2 ? &ssh_hmac_sha1 : &ssh_hmac_sha256; if (fmt_version == 1) { /* MAC (or hash) only covers the private blob. */ macdata = private_blob; free_macdata = false; } else { macdata = strbuf_new_nm(); put_stringz(macdata, alg->ssh_id); put_stringz(macdata, encryption); put_stringz(macdata, comment); put_string(macdata, public_blob->s, public_blob->len); put_string(macdata, private_blob->s, private_blob->len); free_macdata = true; } if (is_mac) { ssh2_mac *mac; mac = ssh2_mac_new(mac_alg, NULL); ssh2_mac_setkey(mac, mackey); ssh2_mac_start(mac); put_data(mac, macdata->s, macdata->len); ssh2_mac_genresult(mac, binary); ssh2_mac_free(mac); } else { hash_simple(&ssh_sha1, ptrlen_from_strbuf(macdata), binary); } if (free_macdata) strbuf_free(macdata); for (i = 0; i < mac_alg->len; i++) sprintf(realmac + 2 * i, "%02x", binary[i]); if (strcmp(mac, realmac)) { /* An incorrect MAC is an unconditional Error if the key is * unencrypted. Otherwise, it means Wrong Passphrase. */ if (ciphertype->keylen != 0) { error = "wrong passphrase"; ret = SSH2_WRONG_PASSPHRASE; } else { error = "MAC failed"; ret = NULL; } goto error; } } /* * Create and return the key. */ ret = snew(ssh2_userkey); ret->comment = comment; comment = NULL; ret->key = ssh_key_new_priv( alg, ptrlen_from_strbuf(public_blob), ptrlen_from_strbuf(private_blob)); if (!ret->key) { sfree(ret); ret = NULL; error = "createkey failed"; goto error; } error = NULL; /* * Error processing. */ error: if (comment) sfree(comment); if (encryption) sfree(encryption); if (mac) sfree(mac); if (public_blob) strbuf_free(public_blob); if (private_blob) strbuf_free(private_blob); if (cipher_mac_keys_blob) strbuf_free(cipher_mac_keys_blob); strbuf_free(passphrase_salt); if (errorstr) *errorstr = error; return ret; } ssh2_userkey *ppk_load_f(const Filename *filename, const char *passphrase, const char **errorstr) { LoadedFile *lf = lf_load_keyfile(filename, errorstr); ssh2_userkey *toret; if (lf) { toret = ppk_load_s(BinarySource_UPCAST(lf), passphrase, errorstr); lf_free(lf); } else { toret = NULL; *errorstr = "can't open file"; } return toret; } static bool rfc4716_loadpub(BinarySource *src, char **algorithm, BinarySink *bs, char **commentptr, const char **errorstr) { const char *error; char *line, *colon, *value; char *comment = NULL; strbuf *pubblob = NULL; char base64in[4]; unsigned char base64out[3]; int base64bytes; int alglen; line = mkstr(get_chomped_line(src)); if (!line || 0 != strcmp(line, "---- BEGIN SSH2 PUBLIC KEY ----")) { error = "invalid begin line in SSH-2 public key file"; goto error; } sfree(line); line = NULL; while (1) { line = mkstr(get_chomped_line(src)); if (!line) { error = "truncated SSH-2 public key file"; goto error; } colon = strstr(line, ": "); if (!colon) break; *colon = '\0'; value = colon + 2; if (!strcmp(line, "Comment")) { char *p, *q; /* Remove containing double quotes, if present */ p = value; if (*p == '"' && p[strlen(p)-1] == '"') { p[strlen(p)-1] = '\0'; p++; } /* Remove \-escaping, not in RFC4716 but seen in the wild * in practice. */ for (q = line; *p; p++) { if (*p == '\\' && p[1]) p++; *q++ = *p; } *q = '\0'; sfree(comment); /* *just* in case of multiple Comment headers */ comment = dupstr(line); } else if (!strcmp(line, "Subject") || !strncmp(line, "x-", 2)) { /* Headers we recognise and ignore. Do nothing. */ } else { error = "unrecognised header in SSH-2 public key file"; goto error; } sfree(line); line = NULL; } /* * Now line contains the initial line of base64 data. Loop round * while it still does contain base64. */ pubblob = strbuf_new(); base64bytes = 0; while (line && line[0] != '-') { char *p; for (p = line; *p; p++) { base64in[base64bytes++] = *p; if (base64bytes == 4) { int n = base64_decode_atom(base64in, base64out); put_data(pubblob, base64out, n); base64bytes = 0; } } sfree(line); line = NULL; line = mkstr(get_chomped_line(src)); } /* * Finally, check the END line makes sense. */ if (!line || 0 != strcmp(line, "---- END SSH2 PUBLIC KEY ----")) { error = "invalid end line in SSH-2 public key file"; goto error; } sfree(line); line = NULL; /* * OK, we now have a public blob and optionally a comment. We must * return the key algorithm string too, so look for that at the * start of the public blob. */ if (pubblob->len < 4) { error = "not enough data in SSH-2 public key file"; goto error; } alglen = toint(GET_32BIT_MSB_FIRST(pubblob->u)); if (alglen < 0 || alglen > pubblob->len-4) { error = "invalid algorithm prefix in SSH-2 public key file"; goto error; } if (algorithm) *algorithm = dupprintf("%.*s", alglen, pubblob->s+4); if (commentptr) *commentptr = comment; else sfree(comment); put_datapl(bs, ptrlen_from_strbuf(pubblob)); strbuf_free(pubblob); return true; error: sfree(line); sfree(comment); if (pubblob) strbuf_free(pubblob); if (errorstr) *errorstr = error; return false; } static bool openssh_loadpub(BinarySource *src, char **algorithm, BinarySink *bs, char **commentptr, const char **errorstr) { const char *error; char *line, *base64; char *comment = NULL; unsigned char *pubblob = NULL; int pubbloblen, pubblobsize; int alglen; line = mkstr(get_chomped_line(src)); base64 = strchr(line, ' '); if (!base64) { error = "no key blob in OpenSSH public key file"; goto error; } *base64++ = '\0'; comment = strchr(base64, ' '); if (comment) { *comment++ = '\0'; comment = dupstr(comment); } pubblobsize = strlen(base64) / 4 * 3; pubblob = snewn(pubblobsize, unsigned char); pubbloblen = 0; while (!memchr(base64, '\0', 4)) { assert(pubbloblen + 3 <= pubblobsize); pubbloblen += base64_decode_atom(base64, pubblob + pubbloblen); base64 += 4; } if (*base64) { error = "invalid length for base64 data in OpenSSH public key file"; goto error; } /* * Sanity check: the first word on the line should be the key * algorithm, and should match the encoded string at the start of * the public blob. */ alglen = strlen(line); if (pubbloblen < alglen + 4 || GET_32BIT_MSB_FIRST(pubblob) != alglen || 0 != memcmp(pubblob + 4, line, alglen)) { error = "key algorithms do not match in OpenSSH public key file"; goto error; } /* * Done. */ if (algorithm) *algorithm = dupstr(line); if (commentptr) *commentptr = comment; else sfree(comment); sfree(line); put_data(bs, pubblob, pubbloblen); sfree(pubblob); return true; error: sfree(line); sfree(comment); sfree(pubblob); if (errorstr) *errorstr = error; return false; } bool ppk_loadpub_s(BinarySource *src, char **algorithm, BinarySink *bs, char **commentptr, const char **errorstr) { char header[40], *b; const ssh_keyalg *alg; int type, i; const char *error = NULL; char *comment = NULL; /* Initially, check if this is a public-only key file. Sometimes * we'll be asked to read a public blob from one of those. */ type = key_type_s(src); if (type == SSH_KEYTYPE_SSH2_PUBLIC_RFC4716) { bool ret = rfc4716_loadpub(src, algorithm, bs, commentptr, errorstr); return ret; } else if (type == SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH) { bool ret = openssh_loadpub(src, algorithm, bs, commentptr, errorstr); return ret; } else if (type != SSH_KEYTYPE_SSH2) { error = "not a PuTTY SSH-2 private key"; goto error; } /* Read the first header line which contains the key type. */ if (!read_header(src, header) || (0 != strcmp(header, "PuTTY-User-Key-File-3") && 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(src)) == NULL) goto error; /* Select key algorithm structure. */ alg = find_pubkey_alg(b); sfree(b); if (!alg) { goto error; } /* Read the Encryption header line. */ if (!read_header(src, header) || 0 != strcmp(header, "Encryption")) goto error; if ((b = read_body(src)) == NULL) goto error; sfree(b); /* we don't care */ /* Read the Comment header line. */ if (!read_header(src, header) || 0 != strcmp(header, "Comment")) goto error; if ((comment = read_body(src)) == NULL) goto error; if (commentptr) *commentptr = comment; else sfree(comment); /* Read the Public-Lines header line and the public blob. */ if (!read_header(src, header) || 0 != strcmp(header, "Public-Lines")) goto error; if ((b = read_body(src)) == NULL) goto error; i = userkey_parse_line_counter(b); sfree(b); if (i < 0) goto error; if (!read_blob(src, i, bs)) goto error; if (algorithm) *algorithm = dupstr(alg->ssh_id); return true; /* * Error processing. */ error: if (errorstr) *errorstr = error; if (comment && commentptr) { sfree(comment); *commentptr = NULL; } return false; } bool ppk_loadpub_f(const Filename *filename, char **algorithm, BinarySink *bs, char **commentptr, const char **errorstr) { LoadedFile *lf = lf_load_keyfile(filename, errorstr); if (!lf) return false; bool toret = ppk_loadpub_s(BinarySource_UPCAST(lf), algorithm, bs, commentptr, errorstr); lf_free(lf); return toret; } bool ppk_encrypted_s(BinarySource *src, char **commentptr) { char header[40], *b, *comment; bool ret; if (commentptr) *commentptr = NULL; if (!read_header(src, header) || (0 != strcmp(header, "PuTTY-User-Key-File-3") && 0 != strcmp(header, "PuTTY-User-Key-File-2") && 0 != strcmp(header, "PuTTY-User-Key-File-1"))) { return false; } if ((b = read_body(src)) == NULL) { return false; } sfree(b); /* we don't care about key type here */ /* Read the Encryption header line. */ if (!read_header(src, header) || 0 != strcmp(header, "Encryption")) { return false; } if ((b = read_body(src)) == NULL) { return false; } /* Read the Comment header line. */ if (!read_header(src, header) || 0 != strcmp(header, "Comment")) { sfree(b); return true; } if ((comment = read_body(src)) == NULL) { sfree(b); return true; } if (commentptr) *commentptr = comment; else sfree(comment); if (!strcmp(b, "aes256-cbc")) ret = true; else ret = false; sfree(b); return ret; } bool ppk_encrypted_f(const Filename *filename, char **commentptr) { LoadedFile *lf = lf_load_keyfile(filename, NULL); if (!lf) { if (commentptr) *commentptr = NULL; return false; } bool toret = ppk_encrypted_s(BinarySource_UPCAST(lf), commentptr); lf_free(lf); return toret; } int base64_lines(int datalen) { /* When encoding, we use 64 chars/line, which equals 48 real chars. */ return (datalen + 47) / 48; } static void base64_encode_s(BinarySink *bs, const unsigned char *data, int datalen, int cpl) { int linelen = 0; char out[4]; int n, i; while (datalen > 0) { n = (datalen < 3 ? datalen : 3); base64_encode_atom(data, n, out); data += n; datalen -= n; for (i = 0; i < 4; i++) { if (linelen >= cpl) { linelen = 0; put_byte(bs, '\n'); } put_byte(bs, out[i]); linelen++; } } put_byte(bs, '\n'); } void base64_encode(FILE *fp, const unsigned char *data, int datalen, int cpl) { stdio_sink ss; stdio_sink_init(&ss, fp); base64_encode_s(BinarySink_UPCAST(&ss), data, datalen, cpl); } const ppk_save_parameters ppk_save_default_parameters = { .fmt_version = 3, /* * The Argon2 spec recommends the hybrid variant Argon2id, where * you don't have a good reason to go with the pure Argon2d or * Argon2i. */ .argon2_flavour = Argon2id, /* * Memory requirement for hashing a password: I don't want to set * this to some truly huge thing like a gigabyte, because for all * I know people might perfectly reasonably be running PuTTY on * machines that don't _have_ a gigabyte spare to hash a private * key passphrase in the legitimate use cases. * * I've picked 8 MB as an amount of memory that isn't unreasonable * to expect a desktop client machine to have, but is also large * compared to the memory requirements of the PPK v2 password hash * (which was plain SHA-1), so it still imposes a limit on * parallel attacks on someone's key file. */ .argon2_mem = 8192, /* require 8 Mb memory */ /* * Automatically scale the number of Argon2 passes so that the * overall time taken is about 1/10 second. (Again, I could crank * this up to a larger time and _most_ people might be OK with it, * but for the moment, I'm trying to err on the side of not * stopping anyone from using the tools at all.) */ .argon2_passes_auto = true, .argon2_milliseconds = 100, /* * PuTTY's own Argon2 implementation is single-threaded. So we * might as well set parallelism to 1, which requires that * attackers' implementations must also be effectively * single-threaded, and they don't get any benefit from using * multiple cores on the same hash attempt. (Of course they can * still use multiple cores for _separate_ hash attempts, but at * least they don't get a speed advantage over us in computing * even one hash.) */ .argon2_parallelism = 1, }; strbuf *ppk_save_sb(ssh2_userkey *key, const char *passphrase, const ppk_save_parameters *params_orig) { strbuf *pub_blob, *priv_blob, *cipher_mac_keys_blob; unsigned char *priv_blob_encrypted; int priv_encrypted_len; int cipherblk; int i; const char *cipherstr; ptrlen cipherkey, cipheriv, mackey; const struct ppk_cipher *ciphertype; unsigned char priv_mac[32]; /* * Fetch the key component blobs. */ pub_blob = strbuf_new(); ssh_key_public_blob(key->key, BinarySink_UPCAST(pub_blob)); priv_blob = strbuf_new_nm(); ssh_key_private_blob(key->key, BinarySink_UPCAST(priv_blob)); /* * Determine encryption details, and encrypt the private blob. */ if (passphrase) { cipherstr = "aes256-cbc"; cipherblk = 16; ciphertype = &ppk_cipher_aes256_cbc; } else { cipherstr = "none"; cipherblk = 1; ciphertype = &ppk_cipher_none; } priv_encrypted_len = priv_blob->len + cipherblk - 1; priv_encrypted_len -= priv_encrypted_len % cipherblk; priv_blob_encrypted = snewn(priv_encrypted_len, unsigned char); memset(priv_blob_encrypted, 0, priv_encrypted_len); memcpy(priv_blob_encrypted, priv_blob->u, priv_blob->len); /* Create padding based on the SHA hash of the unpadded blob. This prevents * too easy a known-plaintext attack on the last block. */ hash_simple(&ssh_sha1, ptrlen_from_strbuf(priv_blob), priv_mac); assert(priv_encrypted_len - priv_blob->len < 20); memcpy(priv_blob_encrypted + priv_blob->len, priv_mac, priv_encrypted_len - priv_blob->len); /* Copy the save parameters, so that when derive_keys chooses the * number of Argon2 passes, it can write the result back to our * copy for us to retrieve. */ ppk_save_parameters params = *params_orig; strbuf *passphrase_salt = strbuf_new(); if (params.fmt_version == 3) { /* Invent a salt for the password hash. */ if (params.salt) put_data(passphrase_salt, params.salt, params.saltlen); else random_read(strbuf_append(passphrase_salt, 16), 16); } cipher_mac_keys_blob = strbuf_new(); ssh2_ppk_derive_keys(params.fmt_version, ciphertype, ptrlen_from_asciz(passphrase ? passphrase : ""), cipher_mac_keys_blob, &cipherkey, &cipheriv, &mackey, ptrlen_from_strbuf(passphrase_salt), ¶ms); const ssh2_macalg *macalg = (params.fmt_version == 2 ? &ssh_hmac_sha1 : &ssh_hmac_sha256); /* Now create the MAC. */ { strbuf *macdata; macdata = strbuf_new_nm(); put_stringz(macdata, ssh_key_ssh_id(key->key)); put_stringz(macdata, cipherstr); put_stringz(macdata, key->comment); put_string(macdata, pub_blob->s, pub_blob->len); put_string(macdata, priv_blob_encrypted, priv_encrypted_len); mac_simple(macalg, mackey, ptrlen_from_strbuf(macdata), priv_mac); strbuf_free(macdata); } if (passphrase) { assert(cipherkey.len == 32); aes256_encrypt_pubkey(cipherkey.ptr, cipheriv.ptr, priv_blob_encrypted, priv_encrypted_len); } strbuf *out = strbuf_new_nm(); strbuf_catf(out, "PuTTY-User-Key-File-%u: %s\n", params.fmt_version, ssh_key_ssh_id(key->key)); strbuf_catf(out, "Encryption: %s\n", cipherstr); strbuf_catf(out, "Comment: %s\n", key->comment); strbuf_catf(out, "Public-Lines: %d\n", base64_lines(pub_blob->len)); base64_encode_s(BinarySink_UPCAST(out), pub_blob->u, pub_blob->len, 64); if (params.fmt_version == 3 && ciphertype->keylen != 0) { strbuf_catf(out, "Key-Derivation: %s\n", params.argon2_flavour == Argon2d ? "Argon2d" : params.argon2_flavour == Argon2i ? "Argon2i" : "Argon2id"); strbuf_catf(out, "Argon2-Memory: %"PRIu32"\n", params.argon2_mem); assert(!params.argon2_passes_auto); strbuf_catf(out, "Argon2-Passes: %"PRIu32"\n", params.argon2_passes); strbuf_catf(out, "Argon2-Parallelism: %"PRIu32"\n", params.argon2_parallelism); strbuf_catf(out, "Argon2-Salt: "); for (size_t i = 0; i < passphrase_salt->len; i++) strbuf_catf(out, "%02x", passphrase_salt->u[i]); strbuf_catf(out, "\n"); } strbuf_catf(out, "Private-Lines: %d\n", base64_lines(priv_encrypted_len)); base64_encode_s(BinarySink_UPCAST(out), priv_blob_encrypted, priv_encrypted_len, 64); strbuf_catf(out, "Private-MAC: "); for (i = 0; i < macalg->len; i++) strbuf_catf(out, "%02x", priv_mac[i]); strbuf_catf(out, "\n"); strbuf_free(cipher_mac_keys_blob); strbuf_free(passphrase_salt); strbuf_free(pub_blob); strbuf_free(priv_blob); smemclr(priv_blob_encrypted, priv_encrypted_len); sfree(priv_blob_encrypted); return out; } bool ppk_save_f(const Filename *filename, ssh2_userkey *key, const char *passphrase, const ppk_save_parameters *params) { FILE *fp = f_open(filename, "wb", true); if (!fp) return false; strbuf *buf = ppk_save_sb(key, passphrase, params); bool toret = fwrite(buf->s, 1, buf->len, fp) == buf->len; if (fclose(fp)) toret = false; strbuf_free(buf); return toret; } /* ---------------------------------------------------------------------- * Output public keys. */ char *ssh1_pubkey_str(RSAKey *key) { char *buffer; char *dec1, *dec2; dec1 = mp_get_decimal(key->exponent); dec2 = mp_get_decimal(key->modulus); buffer = dupprintf("%"SIZEu" %s %s%s%s", mp_get_nbits(key->modulus), dec1, dec2, key->comment ? " " : "", key->comment ? key->comment : ""); sfree(dec1); sfree(dec2); return buffer; } void ssh1_write_pubkey(FILE *fp, RSAKey *key) { char *buffer = ssh1_pubkey_str(key); fprintf(fp, "%s\n", buffer); sfree(buffer); } static char *ssh2_pubkey_openssh_str_internal(const char *comment, const void *v_pub_blob, int pub_len) { const unsigned char *ssh2blob = (const unsigned char *)v_pub_blob; ptrlen alg; char *buffer, *p; int i; { BinarySource src[1]; BinarySource_BARE_INIT(src, ssh2blob, pub_len); alg = get_string(src); if (get_err(src)) { const char *replacement_str = "INVALID-ALGORITHM"; alg.ptr = replacement_str; alg.len = strlen(replacement_str); } } buffer = snewn(alg.len + 4 * ((pub_len+2) / 3) + (comment ? strlen(comment) : 0) + 3, char); p = buffer + sprintf(buffer, "%.*s ", PTRLEN_PRINTF(alg)); i = 0; while (i < pub_len) { int n = (pub_len - i < 3 ? pub_len - i : 3); base64_encode_atom(ssh2blob + i, n, p); i += n; p += 4; } if (comment) { *p++ = ' '; strcpy(p, comment); } else *p++ = '\0'; return buffer; } char *ssh2_pubkey_openssh_str(ssh2_userkey *key) { strbuf *blob; char *ret; blob = strbuf_new(); ssh_key_public_blob(key->key, BinarySink_UPCAST(blob)); ret = ssh2_pubkey_openssh_str_internal( key->comment, blob->s, blob->len); strbuf_free(blob); return ret; } void ssh2_write_pubkey(FILE *fp, const char *comment, const void *v_pub_blob, int pub_len, int keytype) { unsigned char *pub_blob = (unsigned char *)v_pub_blob; if (keytype == SSH_KEYTYPE_SSH2_PUBLIC_RFC4716) { const char *p; int i, column; fprintf(fp, "---- BEGIN SSH2 PUBLIC KEY ----\n"); if (comment) { fprintf(fp, "Comment: \""); for (p = comment; *p; p++) { if (*p == '\\' || *p == '\"') fputc('\\', fp); fputc(*p, fp); } fprintf(fp, "\"\n"); } i = 0; column = 0; while (i < pub_len) { char buf[5]; int n = (pub_len - i < 3 ? pub_len - i : 3); base64_encode_atom(pub_blob + i, n, buf); i += n; buf[4] = '\0'; fputs(buf, fp); if (++column >= 16) { fputc('\n', fp); column = 0; } } if (column > 0) fputc('\n', fp); fprintf(fp, "---- END SSH2 PUBLIC KEY ----\n"); } else if (keytype == SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH) { char *buffer = ssh2_pubkey_openssh_str_internal(comment, v_pub_blob, pub_len); fprintf(fp, "%s\n", buffer); sfree(buffer); } else { unreachable("Bad key type in ssh2_write_pubkey"); } } /* ---------------------------------------------------------------------- * Utility functions to compute SSH-2 fingerprints in a uniform way. */ static void ssh2_fingerprint_blob_md5(ptrlen blob, strbuf *sb) { unsigned char digest[16]; hash_simple(&ssh_md5, blob, digest); for (unsigned i = 0; i < 16; i++) strbuf_catf(sb, "%02x%s", digest[i], i==15 ? "" : ":"); } static void ssh2_fingerprint_blob_sha256(ptrlen blob, strbuf *sb) { unsigned char digest[32]; hash_simple(&ssh_sha256, blob, digest); put_datapl(sb, PTRLEN_LITERAL("SHA256:")); for (unsigned i = 0; i < 32; i += 3) { char buf[5]; unsigned len = 32-i; if (len > 3) len = 3; base64_encode_atom(digest + i, len, buf); put_data(sb, buf, 4); } strbuf_chomp(sb, '='); } char *ssh2_fingerprint_blob(ptrlen blob, FingerprintType fptype) { strbuf *sb = strbuf_new(); /* * Identify the key algorithm, if possible. * * If we can't do that, then we have a seriously confused key * blob, in which case we return only the hash. */ BinarySource src[1]; BinarySource_BARE_INIT_PL(src, blob); ptrlen algname = get_string(src); if (!get_err(src)) { const ssh_keyalg *alg = find_pubkey_alg_len(algname); if (alg) { int bits = ssh_key_public_bits(alg, blob); strbuf_catf(sb, "%.*s %d ", PTRLEN_PRINTF(algname), bits); } else { strbuf_catf(sb, "%.*s ", PTRLEN_PRINTF(algname)); } } switch (fptype) { case SSH_FPTYPE_MD5: ssh2_fingerprint_blob_md5(blob, sb); break; case SSH_FPTYPE_SHA256: ssh2_fingerprint_blob_sha256(blob, sb); break; } return strbuf_to_str(sb); } char **ssh2_all_fingerprints_for_blob(ptrlen blob) { char **fps = snewn(SSH_N_FPTYPES, char *); for (unsigned i = 0; i < SSH_N_FPTYPES; i++) fps[i] = ssh2_fingerprint_blob(blob, i); return fps; } char *ssh2_fingerprint(ssh_key *data, FingerprintType fptype) { strbuf *blob = strbuf_new(); ssh_key_public_blob(data, BinarySink_UPCAST(blob)); char *ret = ssh2_fingerprint_blob(ptrlen_from_strbuf(blob), fptype); strbuf_free(blob); return ret; } char **ssh2_all_fingerprints(ssh_key *data) { strbuf *blob = strbuf_new(); ssh_key_public_blob(data, BinarySink_UPCAST(blob)); char **ret = ssh2_all_fingerprints_for_blob(ptrlen_from_strbuf(blob)); strbuf_free(blob); return ret; } void ssh2_free_all_fingerprints(char **fps) { for (unsigned i = 0; i < SSH_N_FPTYPES; i++) sfree(fps[i]); sfree(fps); } /* ---------------------------------------------------------------------- * Determine the type of a private key file. */ static int key_type_s_internal(BinarySource *src) { static const ptrlen public_std_sig = PTRLEN_DECL_LITERAL("---- BEGIN SSH2 PUBLIC KEY"); static const ptrlen putty2_sig = PTRLEN_DECL_LITERAL("PuTTY-User-Key-File-"); static const ptrlen sshcom_sig = PTRLEN_DECL_LITERAL("---- BEGIN SSH2 ENCRYPTED PRIVAT"); static const ptrlen openssh_new_sig = PTRLEN_DECL_LITERAL("-----BEGIN OPENSSH PRIVATE KEY"); static const ptrlen openssh_sig = PTRLEN_DECL_LITERAL("-----BEGIN "); if (BinarySource_REWIND(src), expect_signature(src, rsa1_signature)) return SSH_KEYTYPE_SSH1; if (BinarySource_REWIND(src), expect_signature(src, public_std_sig)) return SSH_KEYTYPE_SSH2_PUBLIC_RFC4716; if (BinarySource_REWIND(src), expect_signature(src, putty2_sig)) return SSH_KEYTYPE_SSH2; if (BinarySource_REWIND(src), expect_signature(src, openssh_new_sig)) return SSH_KEYTYPE_OPENSSH_NEW; if (BinarySource_REWIND(src), expect_signature(src, openssh_sig)) return SSH_KEYTYPE_OPENSSH_PEM; if (BinarySource_REWIND(src), expect_signature(src, sshcom_sig)) return SSH_KEYTYPE_SSHCOM; BinarySource_REWIND(src); if (get_chars(src, "0123456789").len > 0 && get_chars(src, " ").len == 1 && get_chars(src, "0123456789").len > 0 && get_chars(src, " ").len == 1 && get_chars(src, "0123456789").len > 0 && get_nonchars(src, " \n").len == 0) return SSH_KEYTYPE_SSH1_PUBLIC; BinarySource_REWIND(src); if (find_pubkey_alg_len(get_nonchars(src, " \n")) > 0 && get_chars(src, " ").len == 1 && get_chars(src, "0123456789ABCDEFGHIJKLMNOPQRSTUV" "WXYZabcdefghijklmnopqrstuvwxyz+/=").len > 0 && get_nonchars(src, " \n").len == 0) return SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH; return SSH_KEYTYPE_UNKNOWN; /* unrecognised or EOF */ } int key_type_s(BinarySource *src) { int toret = key_type_s_internal(src); BinarySource_REWIND(src); return toret; } int key_type(const Filename *filename) { LoadedFile *lf = lf_new(1024); if (lf_load(lf, filename) == LF_ERROR) { lf_free(lf); return SSH_KEYTYPE_UNOPENABLE; } int toret = key_type_s(BinarySource_UPCAST(lf)); lf_free(lf); return toret; } /* * Convert the type word to a string, for `wrong type' error * messages. */ const char *key_type_to_str(int type) { switch (type) { case SSH_KEYTYPE_UNOPENABLE: return "unable to open file"; case SSH_KEYTYPE_UNKNOWN: return "not a recognised key file format"; case SSH_KEYTYPE_SSH1_PUBLIC: return "SSH-1 public key"; case SSH_KEYTYPE_SSH2_PUBLIC_RFC4716: return "SSH-2 public key (RFC 4716 format)"; case SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH: return "SSH-2 public key (OpenSSH format)"; case SSH_KEYTYPE_SSH1: return "SSH-1 private key"; case SSH_KEYTYPE_SSH2: return "PuTTY SSH-2 private key"; case SSH_KEYTYPE_OPENSSH_PEM: return "OpenSSH SSH-2 private key (old PEM format)"; case SSH_KEYTYPE_OPENSSH_NEW: return "OpenSSH SSH-2 private key (new format)"; case SSH_KEYTYPE_SSHCOM: return "ssh.com SSH-2 private key"; /* * This function is called with a key type derived from * looking at an actual key file, so the output-only type * OPENSSH_AUTO should never get here, and is much an INTERNAL * ERROR as a code we don't even understand. */ case SSH_KEYTYPE_OPENSSH_AUTO: unreachable("OPENSSH_AUTO should never reach key_type_to_str"); default: unreachable("bad key type in key_type_to_str"); } } key_components *key_components_new(void) { key_components *kc = snew(key_components); kc->ncomponents = 0; kc->componentsize = 0; kc->components = NULL; return kc; } void key_components_add_text(key_components *kc, const char *name, const char *value) { sgrowarray(kc->components, kc->componentsize, kc->ncomponents); size_t n = kc->ncomponents++; kc->components[n].name = dupstr(name); kc->components[n].is_mp_int = false; kc->components[n].text = dupstr(value); } void key_components_add_mp(key_components *kc, const char *name, mp_int *value) { sgrowarray(kc->components, kc->componentsize, kc->ncomponents); size_t n = kc->ncomponents++; kc->components[n].name = dupstr(name); kc->components[n].is_mp_int = true; kc->components[n].mp = mp_copy(value); } void key_components_free(key_components *kc) { for (size_t i = 0; i < kc->ncomponents; i++) { sfree(kc->components[i].name); if (kc->components[i].is_mp_int) { mp_free(kc->components[i].mp); } else { smemclr(kc->components[i].text, strlen(kc->components[i].text)); sfree(kc->components[i].text); } } sfree(kc->components); sfree(kc); } putty-0.76/sshrand.c0000644000175000017500000000667614072266312011404 00000000000000/* * sshrand.c: manage the global live PRNG instance. */ #include "putty.h" #include "ssh.h" #include "storage.h" #include /* Collect environmental noise every 5 minutes */ #define NOISE_REGULAR_INTERVAL (5*60*TICKSPERSEC) int random_active = 0; #ifdef FUZZING /* * Special dummy version of the RNG for use when fuzzing. */ void random_add_noise(NoiseSourceId source, const void *noise, int length) { } void random_ref(void) { } void random_setup_custom(const ssh_hashalg *hash) { } void random_unref(void) { } void random_read(void *out, size_t size) { memset(out, 0x45, size); /* Chosen by eight fair coin tosses */ } void random_get_savedata(void **data, int *len) { } #else /* !FUZZING */ /* Dummy structure for the sake of having something to expire_timer_context */ static struct random_timer_context { int dummy; } random_timer_ctx; static prng *global_prng; static unsigned long next_noise_collection; void random_add_noise(NoiseSourceId source, const void *noise, int length) { if (!random_active) return; prng_add_entropy(global_prng, source, make_ptrlen(noise, length)); } 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, &random_timer_ctx); } } static void random_seed_callback(void *noise, int length) { put_data(global_prng, noise, length); } static void random_create(const ssh_hashalg *hashalg) { assert(!global_prng); global_prng = prng_new(hashalg); prng_seed_begin(global_prng); noise_get_heavy(random_seed_callback); prng_seed_finish(global_prng); next_noise_collection = schedule_timer(NOISE_REGULAR_INTERVAL, random_timer, &random_timer_ctx); /* noise_get_heavy probably read our random seed file. * Therefore (in fact, even if it didn't), we should write a * fresh one, in case another instance of ourself starts up * before we finish, and also in case an attacker gets hold of * the seed data we used. */ random_save_seed(); } void random_save_seed(void) { int len; void *data; if (random_active) { random_get_savedata(&data, &len); write_random_seed(data, len); sfree(data); } } void random_ref(void) { if (!random_active++) random_create(&ssh_sha256); } void random_setup_custom(const ssh_hashalg *hash) { random_active++; random_create(hash); } void random_reseed(ptrlen seed) { prng_seed_begin(global_prng); put_datapl(global_prng, seed); prng_seed_finish(global_prng); } void random_clear(void) { if (global_prng) { random_save_seed(); expire_timer_context(&random_timer_ctx); prng_free(global_prng); global_prng = NULL; random_active = 0; } } void random_unref(void) { assert(random_active > 0); if (--random_active == 0) random_clear(); } void random_read(void *buf, size_t size) { assert(random_active > 0); prng_read(global_prng, buf, size); } void random_get_savedata(void **data, int *len) { void *buf = snewn(global_prng->savesize, char); random_read(buf, global_prng->savesize); *len = global_prng->savesize; *data = buf; } size_t random_seed_bits(void) { assert(random_active > 0); return prng_seed_bits(global_prng); } #endif /* FUZZING */ putty-0.76/sshrsa.c0000644000175000017500000007556214072266312011245 00000000000000/* * RSA implementation for PuTTY. */ #include #include #include #include #include "ssh.h" #include "mpint.h" #include "misc.h" void BinarySource_get_rsa_ssh1_pub( BinarySource *src, RSAKey *rsa, RsaSsh1Order order) { unsigned bits; mp_int *e, *m; bits = get_uint32(src); if (order == RSA_SSH1_EXPONENT_FIRST) { e = get_mp_ssh1(src); m = get_mp_ssh1(src); } else { m = get_mp_ssh1(src); e = get_mp_ssh1(src); } if (rsa) { rsa->bits = bits; rsa->exponent = e; rsa->modulus = m; rsa->bytes = (mp_get_nbits(m) + 7) / 8; } else { mp_free(e); mp_free(m); } } void BinarySource_get_rsa_ssh1_priv( BinarySource *src, RSAKey *rsa) { rsa->private_exponent = get_mp_ssh1(src); } key_components *rsa_components(RSAKey *rsa) { key_components *kc = key_components_new(); key_components_add_text(kc, "key_type", "RSA"); key_components_add_mp(kc, "public_modulus", rsa->modulus); key_components_add_mp(kc, "public_exponent", rsa->exponent); if (rsa->private_exponent) { key_components_add_mp(kc, "private_exponent", rsa->private_exponent); key_components_add_mp(kc, "private_p", rsa->p); key_components_add_mp(kc, "private_q", rsa->q); key_components_add_mp(kc, "private_inverse_q_mod_p", rsa->iqmp); } return kc; } RSAKey *BinarySource_get_rsa_ssh1_priv_agent(BinarySource *src) { RSAKey *rsa = snew(RSAKey); memset(rsa, 0, sizeof(RSAKey)); get_rsa_ssh1_pub(src, rsa, RSA_SSH1_MODULUS_FIRST); get_rsa_ssh1_priv(src, rsa); /* SSH-1 names p and q the other way round, i.e. we have the * inverse of p mod q and not of q mod p. We swap the names, * because our internal RSA wants iqmp. */ rsa->iqmp = get_mp_ssh1(src); rsa->q = get_mp_ssh1(src); rsa->p = get_mp_ssh1(src); return rsa; } bool rsa_ssh1_encrypt(unsigned char *data, int length, RSAKey *key) { mp_int *b1, *b2; int i; unsigned char *p; if (key->bytes < length + 4) return false; /* RSA key too short! */ memmove(data + key->bytes - length, data, length); data[0] = 0; data[1] = 2; size_t npad = key->bytes - length - 3; /* * Generate a sequence of nonzero padding bytes. We do this in a * reasonably uniform way and without having to loop round * retrying the random number generation, by first generating an * integer in [0,2^n) for an appropriately large n; then we * repeatedly multiply by 255 to give an integer in [0,255*2^n), * extract the top 8 bits to give an integer in [0,255), and mask * those bits off before multiplying up again for the next digit. * This gives us a sequence of numbers in [0,255), and of course * adding 1 to each of them gives numbers in [1,256) as we wanted. * * (You could imagine this being a sort of fixed-point operation: * given a uniformly random binary _fraction_, multiplying it by k * and subtracting off the integer part will yield you a sequence * of integers each in [0,k). I'm just doing that scaled up by a * power of 2 to avoid the fractions.) */ size_t random_bits = (npad + 16) * 8; mp_int *randval = mp_new(random_bits + 8); mp_int *tmp = mp_random_bits(random_bits); mp_copy_into(randval, tmp); mp_free(tmp); for (i = 2; i < key->bytes - length - 1; i++) { mp_mul_integer_into(randval, randval, 255); uint8_t byte = mp_get_byte(randval, random_bits / 8); assert(byte != 255); data[i] = byte + 1; mp_reduce_mod_2to(randval, random_bits); } mp_free(randval); data[key->bytes - length - 1] = 0; b1 = mp_from_bytes_be(make_ptrlen(data, key->bytes)); b2 = mp_modpow(b1, key->exponent, key->modulus); p = data; for (i = key->bytes; i--;) { *p++ = mp_get_byte(b2, i); } mp_free(b1); mp_free(b2); return true; } /* * 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. */ static mp_int *crt_modpow(mp_int *base, mp_int *exp, mp_int *mod, mp_int *p, mp_int *q, mp_int *iqmp) { mp_int *pm1, *qm1, *pexp, *qexp, *presult, *qresult; mp_int *diff, *multiplier, *ret0, *ret; /* * Reduce the exponent mod phi(p) and phi(q), to save time when * exponentiating mod p and mod q respectively. Of course, since p * and q are prime, phi(p) == p-1 and similarly for q. */ pm1 = mp_copy(p); mp_sub_integer_into(pm1, pm1, 1); qm1 = mp_copy(q); mp_sub_integer_into(qm1, qm1, 1); pexp = mp_mod(exp, pm1); qexp = mp_mod(exp, qm1); /* * Do the two modpows. */ mp_int *base_mod_p = mp_mod(base, p); presult = mp_modpow(base_mod_p, pexp, p); mp_free(base_mod_p); mp_int *base_mod_q = mp_mod(base, q); qresult = mp_modpow(base_mod_q, qexp, q); mp_free(base_mod_q); /* * Recombine the results. We want a value which is congruent to * qresult mod q, and to presult mod p. * * We know that iqmp * q is congruent to 1 * mod p (by definition * of iqmp) and to 0 mod q (obviously). So we start with qresult * (which is congruent to qresult mod both primes), and add on * (presult-qresult) * (iqmp * q) which adjusts it to be congruent * to presult mod p without affecting its value mod q. * * (If presult-qresult < 0, we add p to it to keep it positive.) */ unsigned presult_too_small = mp_cmp_hs(qresult, presult); mp_cond_add_into(presult, presult, p, presult_too_small); diff = mp_sub(presult, qresult); multiplier = mp_mul(iqmp, q); ret0 = mp_mul(multiplier, diff); mp_add_into(ret0, ret0, qresult); /* * Finally, reduce the result mod n. */ ret = mp_mod(ret0, mod); /* * Free all the intermediate results before returning. */ mp_free(pm1); mp_free(qm1); mp_free(pexp); mp_free(qexp); mp_free(presult); mp_free(qresult); mp_free(diff); mp_free(multiplier); mp_free(ret0); return ret; } /* * Wrapper on crt_modpow that looks up all the right values from an * RSAKey. */ static mp_int *rsa_privkey_op(mp_int *input, RSAKey *key) { return crt_modpow(input, key->private_exponent, key->modulus, key->p, key->q, key->iqmp); } mp_int *rsa_ssh1_decrypt(mp_int *input, RSAKey *key) { return rsa_privkey_op(input, key); } bool rsa_ssh1_decrypt_pkcs1(mp_int *input, RSAKey *key, strbuf *outbuf) { strbuf *data = strbuf_new_nm(); bool success = false; BinarySource src[1]; { mp_int *b = rsa_ssh1_decrypt(input, key); for (size_t i = (mp_get_nbits(key->modulus) + 7) / 8; i-- > 0 ;) { put_byte(data, mp_get_byte(b, i)); } mp_free(b); } BinarySource_BARE_INIT(src, data->u, data->len); /* Check PKCS#1 formatting prefix */ if (get_byte(src) != 0) goto out; if (get_byte(src) != 2) goto out; while (1) { unsigned char byte = get_byte(src); if (get_err(src)) goto out; if (byte == 0) break; } /* Everything else is the payload */ success = true; put_data(outbuf, get_ptr(src), get_avail(src)); out: strbuf_free(data); return success; } static void append_hex_to_strbuf(strbuf *sb, mp_int *x) { if (sb->len > 0) put_byte(sb, ','); put_data(sb, "0x", 2); char *hex = mp_get_hex(x); size_t hexlen = strlen(hex); put_data(sb, hex, hexlen); smemclr(hex, hexlen); sfree(hex); } char *rsastr_fmt(RSAKey *key) { strbuf *sb = strbuf_new(); append_hex_to_strbuf(sb, key->exponent); append_hex_to_strbuf(sb, key->modulus); return strbuf_to_str(sb); } /* * Generate a fingerprint string for the key. Compatible with the * OpenSSH fingerprint code. */ char *rsa_ssh1_fingerprint(RSAKey *key) { unsigned char digest[16]; strbuf *out; int i; /* * The hash preimage for SSH-1 key fingerprinting consists of the * modulus and exponent _without_ any preceding length field - * just the minimum number of bytes to represent each integer, * stored big-endian, concatenated with no marker at the division * between them. */ ssh_hash *hash = ssh_hash_new(&ssh_md5); for (size_t i = (mp_get_nbits(key->modulus) + 7) / 8; i-- > 0 ;) put_byte(hash, mp_get_byte(key->modulus, i)); for (size_t i = (mp_get_nbits(key->exponent) + 7) / 8; i-- > 0 ;) put_byte(hash, mp_get_byte(key->exponent, i)); ssh_hash_final(hash, digest); out = strbuf_new(); strbuf_catf(out, "%"SIZEu" ", mp_get_nbits(key->modulus)); for (i = 0; i < 16; i++) strbuf_catf(out, "%s%02x", i ? ":" : "", digest[i]); if (key->comment) strbuf_catf(out, " %s", key->comment); return strbuf_to_str(out); } /* * Wrap the output of rsa_ssh1_fingerprint up into the same kind of * structure that comes from ssh2_all_fingerprints. */ char **rsa_ssh1_fake_all_fingerprints(RSAKey *key) { char **ret = snewn(SSH_N_FPTYPES, char *); for (unsigned i = 0; i < SSH_N_FPTYPES; i++) ret[i] = NULL; ret[SSH_FPTYPE_MD5] = rsa_ssh1_fingerprint(key); return ret; } /* * Verify that the public data in an RSA key matches the private * data. We also check the private data itself: we ensure that p > * q and that iqmp really is the inverse of q mod p. */ bool rsa_verify(RSAKey *key) { mp_int *n, *ed, *pm1, *qm1; unsigned ok = 1; /* Preliminary checks: p,q can't be 0 or 1. (Of course no other * very small value is any good either, but these are the values * we _must_ check for to avoid assertion failures further down * this function.) */ if (!(mp_hs_integer(key->p, 2) & mp_hs_integer(key->q, 2))) return false; /* n must equal pq. */ n = mp_mul(key->p, key->q); ok &= mp_cmp_eq(n, key->modulus); mp_free(n); /* e * d must be congruent to 1, modulo (p-1) and modulo (q-1). */ pm1 = mp_copy(key->p); mp_sub_integer_into(pm1, pm1, 1); ed = mp_modmul(key->exponent, key->private_exponent, pm1); mp_free(pm1); ok &= mp_eq_integer(ed, 1); mp_free(ed); qm1 = mp_copy(key->q); mp_sub_integer_into(qm1, qm1, 1); ed = mp_modmul(key->exponent, key->private_exponent, qm1); mp_free(qm1); ok &= mp_eq_integer(ed, 1); mp_free(ed); /* * 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. */ mp_int *p_new = mp_max(key->p, key->q); mp_int *q_new = mp_min(key->p, key->q); mp_free(key->p); mp_free(key->q); mp_free(key->iqmp); key->p = p_new; key->q = q_new; key->iqmp = mp_invert(key->q, key->p); return ok; } void rsa_ssh1_public_blob(BinarySink *bs, RSAKey *key, RsaSsh1Order order) { put_uint32(bs, mp_get_nbits(key->modulus)); if (order == RSA_SSH1_EXPONENT_FIRST) { put_mp_ssh1(bs, key->exponent); put_mp_ssh1(bs, key->modulus); } else { put_mp_ssh1(bs, key->modulus); put_mp_ssh1(bs, key->exponent); } } void rsa_ssh1_private_blob_agent(BinarySink *bs, RSAKey *key) { rsa_ssh1_public_blob(bs, key, RSA_SSH1_MODULUS_FIRST); put_mp_ssh1(bs, key->private_exponent); put_mp_ssh1(bs, key->iqmp); put_mp_ssh1(bs, key->q); put_mp_ssh1(bs, key->p); } /* Given an SSH-1 public key blob, determine its length. */ int rsa_ssh1_public_blob_len(ptrlen data) { BinarySource src[1]; BinarySource_BARE_INIT_PL(src, data); /* Expect a length word, then exponent and modulus. (It doesn't * even matter which order.) */ get_uint32(src); mp_free(get_mp_ssh1(src)); mp_free(get_mp_ssh1(src)); if (get_err(src)) return -1; /* Return the number of bytes consumed. */ return src->pos; } void freersapriv(RSAKey *key) { if (key->private_exponent) { mp_free(key->private_exponent); key->private_exponent = NULL; } if (key->p) { mp_free(key->p); key->p = NULL; } if (key->q) { mp_free(key->q); key->q = NULL; } if (key->iqmp) { mp_free(key->iqmp); key->iqmp = NULL; } } void freersakey(RSAKey *key) { freersapriv(key); if (key->modulus) { mp_free(key->modulus); key->modulus = NULL; } if (key->exponent) { mp_free(key->exponent); key->exponent = NULL; } if (key->comment) { sfree(key->comment); key->comment = NULL; } } /* ---------------------------------------------------------------------- * Implementation of the ssh-rsa signing key type family. */ struct ssh2_rsa_extra { unsigned signflags; }; static void rsa2_freekey(ssh_key *key); /* forward reference */ static ssh_key *rsa2_new_pub(const ssh_keyalg *self, ptrlen data) { BinarySource src[1]; RSAKey *rsa; BinarySource_BARE_INIT_PL(src, data); if (!ptrlen_eq_string(get_string(src), "ssh-rsa")) return NULL; rsa = snew(RSAKey); rsa->sshk.vt = self; rsa->exponent = get_mp_ssh2(src); rsa->modulus = get_mp_ssh2(src); rsa->private_exponent = NULL; rsa->p = rsa->q = rsa->iqmp = NULL; rsa->comment = NULL; if (get_err(src)) { rsa2_freekey(&rsa->sshk); return NULL; } return &rsa->sshk; } static void rsa2_freekey(ssh_key *key) { RSAKey *rsa = container_of(key, RSAKey, sshk); freersakey(rsa); sfree(rsa); } static char *rsa2_cache_str(ssh_key *key) { RSAKey *rsa = container_of(key, RSAKey, sshk); return rsastr_fmt(rsa); } static key_components *rsa2_components(ssh_key *key) { RSAKey *rsa = container_of(key, RSAKey, sshk); return rsa_components(rsa); } static void rsa2_public_blob(ssh_key *key, BinarySink *bs) { RSAKey *rsa = container_of(key, RSAKey, sshk); put_stringz(bs, "ssh-rsa"); put_mp_ssh2(bs, rsa->exponent); put_mp_ssh2(bs, rsa->modulus); } static void rsa2_private_blob(ssh_key *key, BinarySink *bs) { RSAKey *rsa = container_of(key, RSAKey, sshk); put_mp_ssh2(bs, rsa->private_exponent); put_mp_ssh2(bs, rsa->p); put_mp_ssh2(bs, rsa->q); put_mp_ssh2(bs, rsa->iqmp); } static ssh_key *rsa2_new_priv(const ssh_keyalg *self, ptrlen pub, ptrlen priv) { BinarySource src[1]; ssh_key *sshk; RSAKey *rsa; sshk = rsa2_new_pub(self, pub); if (!sshk) return NULL; rsa = container_of(sshk, RSAKey, sshk); BinarySource_BARE_INIT_PL(src, priv); rsa->private_exponent = get_mp_ssh2(src); rsa->p = get_mp_ssh2(src); rsa->q = get_mp_ssh2(src); rsa->iqmp = get_mp_ssh2(src); if (get_err(src) || !rsa_verify(rsa)) { rsa2_freekey(&rsa->sshk); return NULL; } return &rsa->sshk; } static ssh_key *rsa2_new_priv_openssh(const ssh_keyalg *self, BinarySource *src) { RSAKey *rsa; rsa = snew(RSAKey); rsa->sshk.vt = &ssh_rsa; rsa->comment = NULL; rsa->modulus = get_mp_ssh2(src); rsa->exponent = get_mp_ssh2(src); rsa->private_exponent = get_mp_ssh2(src); rsa->iqmp = get_mp_ssh2(src); rsa->p = get_mp_ssh2(src); rsa->q = get_mp_ssh2(src); if (get_err(src) || !rsa_verify(rsa)) { rsa2_freekey(&rsa->sshk); return NULL; } return &rsa->sshk; } static void rsa2_openssh_blob(ssh_key *key, BinarySink *bs) { RSAKey *rsa = container_of(key, RSAKey, sshk); put_mp_ssh2(bs, rsa->modulus); put_mp_ssh2(bs, rsa->exponent); put_mp_ssh2(bs, rsa->private_exponent); put_mp_ssh2(bs, rsa->iqmp); put_mp_ssh2(bs, rsa->p); put_mp_ssh2(bs, rsa->q); } static int rsa2_pubkey_bits(const ssh_keyalg *self, ptrlen pub) { ssh_key *sshk; RSAKey *rsa; int ret; sshk = rsa2_new_pub(self, pub); if (!sshk) return -1; rsa = container_of(sshk, RSAKey, sshk); ret = mp_get_nbits(rsa->modulus); rsa2_freekey(&rsa->sshk); return ret; } static inline const ssh_hashalg *rsa2_hash_alg_for_flags( unsigned flags, const char **protocol_id_out) { const ssh_hashalg *halg; const char *protocol_id; if (flags & SSH_AGENT_RSA_SHA2_256) { halg = &ssh_sha256; protocol_id = "rsa-sha2-256"; } else if (flags & SSH_AGENT_RSA_SHA2_512) { halg = &ssh_sha512; protocol_id = "rsa-sha2-512"; } else { halg = &ssh_sha1; protocol_id = "ssh-rsa"; } if (protocol_id_out) *protocol_id_out = protocol_id; return halg; } static inline ptrlen rsa_pkcs1_prefix_for_hash(const ssh_hashalg *halg) { if (halg == &ssh_sha1) { /* * This is the magic ASN.1/DER prefix that goes in the decoded * signature, between the string of FFs and the actual SHA-1 * hash value. The meaning of it is: * * 00 -- this marks the end of the FFs; not part of the ASN.1 * bit itself * * 30 21 -- a constructed SEQUENCE of length 0x21 * 30 09 -- a constructed sub-SEQUENCE of length 9 * 06 05 -- an object identifier, length 5 * 2B 0E 03 02 1A -- object id { 1 3 14 3 2 26 } * (the 1,3 comes from 0x2B = 43 = 40*1+3) * 05 00 -- NULL * 04 14 -- a primitive OCTET STRING of length 0x14 * [0x14 bytes of hash data follows] * * The object id in the middle there is listed as `id-sha1' in * ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-1/pkcs-1v2-1d2.asn * (the ASN module for PKCS #1) and its expanded form is as * follows: * * id-sha1 OBJECT IDENTIFIER ::= { * iso(1) identified-organization(3) oiw(14) secsig(3) * algorithms(2) 26 } */ static const unsigned char sha1_asn1_prefix[] = { 0x00, 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A, 0x05, 0x00, 0x04, 0x14, }; return PTRLEN_FROM_CONST_BYTES(sha1_asn1_prefix); } if (halg == &ssh_sha256) { /* * A similar piece of ASN.1 used for signatures using SHA-256, * in the same format but differing only in various length * fields and OID. */ static const unsigned char sha256_asn1_prefix[] = { 0x00, 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20, }; return PTRLEN_FROM_CONST_BYTES(sha256_asn1_prefix); } if (halg == &ssh_sha512) { /* * And one more for SHA-512. */ static const unsigned char sha512_asn1_prefix[] = { 0x00, 0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40, }; return PTRLEN_FROM_CONST_BYTES(sha512_asn1_prefix); } unreachable("bad hash algorithm for RSA PKCS#1"); } static inline size_t rsa_pkcs1_length_of_fixed_parts(const ssh_hashalg *halg) { ptrlen asn1_prefix = rsa_pkcs1_prefix_for_hash(halg); return halg->hlen + asn1_prefix.len + 2; } static unsigned char *rsa_pkcs1_signature_string( size_t nbytes, const ssh_hashalg *halg, ptrlen data) { size_t fixed_parts = rsa_pkcs1_length_of_fixed_parts(halg); assert(nbytes >= fixed_parts); size_t padding = nbytes - fixed_parts; ptrlen asn1_prefix = rsa_pkcs1_prefix_for_hash(halg); unsigned char *bytes = snewn(nbytes, unsigned char); bytes[0] = 0; bytes[1] = 1; memset(bytes + 2, 0xFF, padding); memcpy(bytes + 2 + padding, asn1_prefix.ptr, asn1_prefix.len); ssh_hash *h = ssh_hash_new(halg); put_datapl(h, data); ssh_hash_final(h, bytes + 2 + padding + asn1_prefix.len); return bytes; } static bool rsa2_verify(ssh_key *key, ptrlen sig, ptrlen data) { RSAKey *rsa = container_of(key, RSAKey, sshk); BinarySource src[1]; ptrlen type, in_pl; mp_int *in, *out; const struct ssh2_rsa_extra *extra = (const struct ssh2_rsa_extra *)key->vt->extra; const ssh_hashalg *halg = rsa2_hash_alg_for_flags(extra->signflags, NULL); /* Start by making sure the key is even long enough to encode a * signature. If not, everything fails to verify. */ size_t nbytes = (mp_get_nbits(rsa->modulus) + 7) / 8; if (nbytes < rsa_pkcs1_length_of_fixed_parts(halg)) return false; BinarySource_BARE_INIT_PL(src, sig); type = get_string(src); /* * RFC 4253 section 6.6: the signature integer in an ssh-rsa * signature is 'without lengths or padding'. That is, we _don't_ * expect the usual leading zero byte if the topmost bit of the * first byte is set. (However, because of the possibility of * BUG_SSH2_RSA_PADDING at the other end, we tolerate it if it's * there.) So we can't use get_mp_ssh2, which enforces that * leading-byte scheme; instead we use get_string and * mp_from_bytes_be, which will tolerate anything. */ in_pl = get_string(src); if (get_err(src) || !ptrlen_eq_string(type, key->vt->ssh_id)) return false; in = mp_from_bytes_be(in_pl); out = mp_modpow(in, rsa->exponent, rsa->modulus); mp_free(in); unsigned diff = 0; unsigned char *bytes = rsa_pkcs1_signature_string(nbytes, halg, data); for (size_t i = 0; i < nbytes; i++) diff |= bytes[nbytes-1 - i] ^ mp_get_byte(out, i); smemclr(bytes, nbytes); sfree(bytes); mp_free(out); return diff == 0; } static void rsa2_sign(ssh_key *key, ptrlen data, unsigned flags, BinarySink *bs) { RSAKey *rsa = container_of(key, RSAKey, sshk); unsigned char *bytes; size_t nbytes; mp_int *in, *out; const ssh_hashalg *halg; const char *sign_alg_name; const struct ssh2_rsa_extra *extra = (const struct ssh2_rsa_extra *)key->vt->extra; flags |= extra->signflags; halg = rsa2_hash_alg_for_flags(flags, &sign_alg_name); nbytes = (mp_get_nbits(rsa->modulus) + 7) / 8; bytes = rsa_pkcs1_signature_string(nbytes, halg, data); in = mp_from_bytes_be(make_ptrlen(bytes, nbytes)); smemclr(bytes, nbytes); sfree(bytes); out = rsa_privkey_op(in, rsa); mp_free(in); put_stringz(bs, sign_alg_name); nbytes = (mp_get_nbits(out) + 7) / 8; put_uint32(bs, nbytes); for (size_t i = 0; i < nbytes; i++) put_byte(bs, mp_get_byte(out, nbytes - 1 - i)); mp_free(out); } static char *rsa2_invalid(ssh_key *key, unsigned flags) { RSAKey *rsa = container_of(key, RSAKey, sshk); size_t bits = mp_get_nbits(rsa->modulus), nbytes = (bits + 7) / 8; const char *sign_alg_name; const ssh_hashalg *halg = rsa2_hash_alg_for_flags(flags, &sign_alg_name); if (nbytes < rsa_pkcs1_length_of_fixed_parts(halg)) { return dupprintf( "%"SIZEu"-bit RSA key is too short to generate %s signatures", bits, sign_alg_name); } return NULL; } static const struct ssh2_rsa_extra rsa_extra = { 0 }, rsa_sha256_extra = { SSH_AGENT_RSA_SHA2_256 }, rsa_sha512_extra = { SSH_AGENT_RSA_SHA2_512 }; #define COMMON_KEYALG_FIELDS \ .new_pub = rsa2_new_pub, \ .new_priv = rsa2_new_priv, \ .new_priv_openssh = rsa2_new_priv_openssh, \ .freekey = rsa2_freekey, \ .invalid = rsa2_invalid, \ .sign = rsa2_sign, \ .verify = rsa2_verify, \ .public_blob = rsa2_public_blob, \ .private_blob = rsa2_private_blob, \ .openssh_blob = rsa2_openssh_blob, \ .cache_str = rsa2_cache_str, \ .components = rsa2_components, \ .pubkey_bits = rsa2_pubkey_bits, \ .cache_id = "rsa2" const ssh_keyalg ssh_rsa = { COMMON_KEYALG_FIELDS, .ssh_id = "ssh-rsa", .supported_flags = SSH_AGENT_RSA_SHA2_256 | SSH_AGENT_RSA_SHA2_512, .extra = &rsa_extra, }; const ssh_keyalg ssh_rsa_sha256 = { COMMON_KEYALG_FIELDS, .ssh_id = "rsa-sha2-256", .supported_flags = 0, .extra = &rsa_sha256_extra, }; const ssh_keyalg ssh_rsa_sha512 = { COMMON_KEYALG_FIELDS, .ssh_id = "rsa-sha2-512", .supported_flags = 0, .extra = &rsa_sha512_extra, }; RSAKey *ssh_rsakex_newkey(ptrlen data) { ssh_key *sshk = rsa2_new_pub(&ssh_rsa, data); if (!sshk) return NULL; return container_of(sshk, RSAKey, sshk); } void ssh_rsakex_freekey(RSAKey *key) { rsa2_freekey(&key->sshk); } int ssh_rsakex_klen(RSAKey *rsa) { return mp_get_nbits(rsa->modulus); } static void oaep_mask(const ssh_hashalg *h, void *seed, int seedlen, void *vdata, int datalen) { unsigned char *data = (unsigned char *)vdata; unsigned count = 0; ssh_hash *s = ssh_hash_new(h); while (datalen > 0) { int i, max = (datalen > h->hlen ? h->hlen : datalen); unsigned char hash[MAX_HASH_LEN]; ssh_hash_reset(s); assert(h->hlen <= MAX_HASH_LEN); put_data(s, seed, seedlen); put_uint32(s, count); ssh_hash_digest(s, hash); count++; for (i = 0; i < max; i++) data[i] ^= hash[i]; data += max; datalen -= max; } ssh_hash_free(s); } strbuf *ssh_rsakex_encrypt(RSAKey *rsa, const ssh_hashalg *h, ptrlen in) { mp_int *b1, *b2; int k, i; char *p; const int HLEN = h->hlen; /* * Here we encrypt using RSAES-OAEP. Essentially this means: * * - we have a SHA-based `mask generation function' which * creates a pseudo-random stream of mask data * deterministically from an input chunk of data. * * - we have a random chunk of data called a seed. * * - we use the seed to generate a mask which we XOR with our * plaintext. * * - then we use _the masked plaintext_ to generate a mask * which we XOR with the seed. * * - then we concatenate the masked seed and the masked * plaintext, and RSA-encrypt that lot. * * The result is that the data input to the encryption function * is random-looking and (hopefully) contains no exploitable * structure such as PKCS1-v1_5 does. * * For a precise specification, see RFC 3447, section 7.1.1. * Some of the variable names below are derived from that, so * it'd probably help to read it anyway. */ /* k denotes the length in octets of the RSA modulus. */ k = (7 + mp_get_nbits(rsa->modulus)) / 8; /* The length of the input data must be at most k - 2hLen - 2. */ assert(in.len > 0 && in.len <= k - 2*HLEN - 2); /* The length of the output data wants to be precisely k. */ strbuf *toret = strbuf_new_nm(); int outlen = k; unsigned char *out = strbuf_append(toret, outlen); /* * Now perform EME-OAEP encoding. First set up all the unmasked * output data. */ /* Leading byte zero. */ out[0] = 0; /* At position 1, the seed: HLEN bytes of random data. */ random_read(out + 1, HLEN); /* At position 1+HLEN, the data block DB, consisting of: */ /* The hash of the label (we only support an empty label here) */ hash_simple(h, PTRLEN_LITERAL(""), out + HLEN + 1); /* A bunch of zero octets */ memset(out + 2*HLEN + 1, 0, outlen - (2*HLEN + 1)); /* A single 1 octet, followed by the input message data. */ out[outlen - in.len - 1] = 1; memcpy(out + outlen - in.len, in.ptr, in.len); /* * Now use the seed data to mask the block DB. */ oaep_mask(h, out+1, HLEN, out+HLEN+1, outlen-HLEN-1); /* * And now use the masked DB to mask the seed itself. */ oaep_mask(h, out+HLEN+1, outlen-HLEN-1, out+1, HLEN); /* * Now `out' contains precisely the data we want to * RSA-encrypt. */ b1 = mp_from_bytes_be(make_ptrlen(out, outlen)); b2 = mp_modpow(b1, rsa->exponent, rsa->modulus); p = (char *)out; for (i = outlen; i--;) { *p++ = mp_get_byte(b2, i); } mp_free(b1); mp_free(b2); /* * And we're done. */ return toret; } mp_int *ssh_rsakex_decrypt( RSAKey *rsa, const ssh_hashalg *h, ptrlen ciphertext) { mp_int *b1, *b2; int outlen, i; unsigned char *out; unsigned char labelhash[64]; BinarySource src[1]; const int HLEN = h->hlen; /* * Decryption side of the RSA key exchange operation. */ /* The length of the encrypted data should be exactly the length * in octets of the RSA modulus.. */ outlen = (7 + mp_get_nbits(rsa->modulus)) / 8; if (ciphertext.len != outlen) return NULL; /* Do the RSA decryption, and extract the result into a byte array. */ b1 = mp_from_bytes_be(ciphertext); b2 = rsa_privkey_op(b1, rsa); out = snewn(outlen, unsigned char); for (i = 0; i < outlen; i++) out[i] = mp_get_byte(b2, outlen-1-i); mp_free(b1); mp_free(b2); /* Do the OAEP masking operations, in the reverse order from encryption */ oaep_mask(h, out+HLEN+1, outlen-HLEN-1, out+1, HLEN); oaep_mask(h, out+1, HLEN, out+HLEN+1, outlen-HLEN-1); /* Check the leading byte is zero. */ if (out[0] != 0) { sfree(out); return NULL; } /* Check the label hash at position 1+HLEN */ assert(HLEN <= lenof(labelhash)); hash_simple(h, PTRLEN_LITERAL(""), labelhash); if (memcmp(out + HLEN + 1, labelhash, HLEN)) { sfree(out); return NULL; } /* Expect zero bytes followed by a 1 byte */ for (i = 1 + 2 * HLEN; i < outlen; i++) { if (out[i] == 1) { i++; /* skip over the 1 byte */ break; } else if (out[i] != 0) { sfree(out); return NULL; } } /* And what's left is the input message data, which should be * encoded as an ordinary SSH-2 mpint. */ BinarySource_BARE_INIT(src, out + i, outlen - i); b1 = get_mp_ssh2(src); sfree(out); if (get_err(src) || get_avail(src) != 0) { mp_free(b1); return NULL; } /* Success! */ return b1; } static const struct ssh_rsa_kex_extra ssh_rsa_kex_extra_sha1 = { 1024 }; static const struct ssh_rsa_kex_extra ssh_rsa_kex_extra_sha256 = { 2048 }; static const ssh_kex ssh_rsa_kex_sha1 = { "rsa1024-sha1", NULL, KEXTYPE_RSA, &ssh_sha1, &ssh_rsa_kex_extra_sha1, }; static const ssh_kex ssh_rsa_kex_sha256 = { "rsa2048-sha256", NULL, KEXTYPE_RSA, &ssh_sha256, &ssh_rsa_kex_extra_sha256, }; static const ssh_kex *const rsa_kex_list[] = { &ssh_rsa_kex_sha256, &ssh_rsa_kex_sha1 }; const ssh_kexes ssh_rsa_kex = { lenof(rsa_kex_list), rsa_kex_list }; putty-0.76/sshrsag.c0000644000175000017500000002173014072266312011400 00000000000000/* * RSA key generation. */ #include #include "ssh.h" #include "sshkeygen.h" #include "mpint.h" #define RSA_EXPONENT 65537 #define NFIRSTBITS 13 static void invent_firstbits(unsigned *one, unsigned *two, unsigned min_separation); typedef struct RSAPrimeDetails RSAPrimeDetails; struct RSAPrimeDetails { bool strong; int bits, bitsm1m1, bitsm1, bitsp1; unsigned firstbits; ProgressPhase phase_main, phase_m1m1, phase_m1, phase_p1; }; #define STRONG_MARGIN (20 + NFIRSTBITS) static RSAPrimeDetails setup_rsa_prime( int bits, bool strong, PrimeGenerationContext *pgc, ProgressReceiver *prog) { RSAPrimeDetails pd; pd.bits = bits; if (strong) { pd.bitsm1 = (bits - STRONG_MARGIN) / 2; pd.bitsp1 = (bits - STRONG_MARGIN) - pd.bitsm1; pd.bitsm1m1 = (pd.bitsm1 - STRONG_MARGIN) / 2; if (pd.bitsm1m1 < STRONG_MARGIN) { /* Absurdly small prime, but we should at least not crash. */ strong = false; } } pd.strong = strong; if (pd.strong) { pd.phase_m1m1 = primegen_add_progress_phase(pgc, prog, pd.bitsm1m1); pd.phase_m1 = primegen_add_progress_phase(pgc, prog, pd.bitsm1); pd.phase_p1 = primegen_add_progress_phase(pgc, prog, pd.bitsp1); } pd.phase_main = primegen_add_progress_phase(pgc, prog, pd.bits); return pd; } static mp_int *generate_rsa_prime( RSAPrimeDetails pd, PrimeGenerationContext *pgc, ProgressReceiver *prog) { mp_int *m1m1 = NULL, *m1 = NULL, *p1 = NULL, *p = NULL; PrimeCandidateSource *pcs; if (pd.strong) { progress_start_phase(prog, pd.phase_m1m1); pcs = pcs_new_with_firstbits(pd.bitsm1m1, pd.firstbits, NFIRSTBITS); m1m1 = primegen_generate(pgc, pcs, prog); progress_report_phase_complete(prog); progress_start_phase(prog, pd.phase_m1); pcs = pcs_new_with_firstbits(pd.bitsm1, pd.firstbits, NFIRSTBITS); pcs_require_residue_1_mod_prime(pcs, m1m1); m1 = primegen_generate(pgc, pcs, prog); progress_report_phase_complete(prog); progress_start_phase(prog, pd.phase_p1); pcs = pcs_new_with_firstbits(pd.bitsp1, pd.firstbits, NFIRSTBITS); p1 = primegen_generate(pgc, pcs, prog); progress_report_phase_complete(prog); } progress_start_phase(prog, pd.phase_main); pcs = pcs_new_with_firstbits(pd.bits, pd.firstbits, NFIRSTBITS); pcs_avoid_residue_small(pcs, RSA_EXPONENT, 1); if (pd.strong) { pcs_require_residue_1_mod_prime(pcs, m1); mp_int *p1_minus_1 = mp_copy(p1); mp_sub_integer_into(p1_minus_1, p1, 1); pcs_require_residue(pcs, p1, p1_minus_1); mp_free(p1_minus_1); } p = primegen_generate(pgc, pcs, prog); progress_report_phase_complete(prog); if (m1m1) mp_free(m1m1); if (m1) mp_free(m1); if (p1) mp_free(p1); return p; } int rsa_generate(RSAKey *key, int bits, bool strong, PrimeGenerationContext *pgc, ProgressReceiver *prog) { key->sshk.vt = &ssh_rsa; /* * We don't generate e; we just use a standard one always. */ mp_int *exponent = mp_from_integer(RSA_EXPONENT); /* * 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.) * * We give a min_separation of 2 to invent_firstbits(), ensuring * that the two primes won't be very close to each other. (The * chance of them being _dangerously_ close is negligible - even * more so than an attacker guessing a whole 256-bit session key - * but it doesn't cost much to make sure.) */ int qbits = bits / 2; int pbits = bits - qbits; assert(pbits >= qbits); RSAPrimeDetails pd = setup_rsa_prime(pbits, strong, pgc, prog); RSAPrimeDetails qd = setup_rsa_prime(qbits, strong, pgc, prog); progress_ready(prog); invent_firstbits(&pd.firstbits, &qd.firstbits, 2); mp_int *p = generate_rsa_prime(pd, pgc, prog); mp_int *q = generate_rsa_prime(qd, pgc, prog); /* * Ensure p > q, by swapping them if not. * * We only need to do this if the two primes were generated with * the same number of bits (i.e. if the requested key size is * even) - otherwise it's already guaranteed! */ if (pbits == qbits) { mp_cond_swap(p, q, mp_cmp_hs(q, p)); } else { assert(mp_cmp_hs(p, q)); } /* * Now we have p, q and e. All we need to do now is work out * the other helpful quantities: n=pq, d=e^-1 mod (p-1)(q-1), * and (q^-1 mod p). */ mp_int *modulus = mp_mul(p, q); mp_int *pm1 = mp_copy(p); mp_sub_integer_into(pm1, pm1, 1); mp_int *qm1 = mp_copy(q); mp_sub_integer_into(qm1, qm1, 1); mp_int *phi_n = mp_mul(pm1, qm1); mp_free(pm1); mp_free(qm1); mp_int *private_exponent = mp_invert(exponent, phi_n); mp_free(phi_n); mp_int *iqmp = mp_invert(q, p); /* * Populate the returned structure. */ key->modulus = modulus; key->exponent = exponent; key->private_exponent = private_exponent; key->p = p; key->q = q; key->iqmp = iqmp; key->bits = mp_get_nbits(modulus); key->bytes = (key->bits + 7) / 8; return 1; } /* * Invent a pair of values suitable for use as the 'firstbits' values * for the two RSA primes, such that their product is at least 2, and * such that their difference is also at least min_separation. * * This is used for generating RSA 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!' */ static inline unsigned firstbits_b_min( unsigned a, unsigned lo, unsigned hi, unsigned min_separation) { /* To get a large enough product, b must be at least this much */ unsigned b_min = (2*lo*lo + a - 1) / a; /* Now enforce a hi) b_min = hi; return b_min; } static void invent_firstbits(unsigned *one, unsigned *two, unsigned min_separation) { /* * We'll pick 12 initial bits (number selected at random) for each * prime, not counting the leading 1. So we want to return two * values in the range [2^12,2^13) whose product is at least 2^25. * * Strategy: count up all the viable pairs, then select a random * number in that range and use it to pick a pair. * * To keep things simple, we'll ensure a < b, and randomly swap * them at the end. */ const unsigned lo = 1<<12, hi = 1<<13, minproduct = 2*lo*lo; unsigned a, b; /* * Count up the number of prefixes of b that would be valid for * each prefix of a. */ mp_int *total = mp_new(32); for (a = lo; a < hi; a++) { unsigned b_min = firstbits_b_min(a, lo, hi, min_separation); mp_add_integer_into(total, total, hi - b_min); } /* * Make up a random number in the range [0,2*total). */ mp_int *mlo = mp_from_integer(0), *mhi = mp_new(32); mp_lshift_fixed_into(mhi, total, 1); mp_int *randval = mp_random_in_range(mlo, mhi); mp_free(mlo); mp_free(mhi); /* * Use the low bit of randval as our swap indicator, leaving the * rest of it in the range [0,total). */ unsigned swap = mp_get_bit(randval, 0); mp_rshift_fixed_into(randval, randval, 1); /* * Now do the same counting loop again to make the actual choice. */ a = b = 0; for (unsigned a_candidate = lo; a_candidate < hi; a_candidate++) { unsigned b_min = firstbits_b_min(a_candidate, lo, hi, min_separation); unsigned limit = hi - b_min; unsigned b_candidate = b_min + mp_get_integer(randval); unsigned use_it = 1 ^ mp_hs_integer(randval, limit); a ^= (a ^ a_candidate) & -use_it; b ^= (b ^ b_candidate) & -use_it; mp_sub_integer_into(randval, randval, limit); } mp_free(randval); mp_free(total); /* * Check everything came out right. */ assert(lo <= a); assert(a < hi); assert(lo <= b); assert(b < hi); assert(a * b >= minproduct); assert(b >= a + min_separation); /* * Last-minute optional swap of a and b. */ unsigned diff = (a ^ b) & (-swap); a ^= diff; b ^= diff; *one = a; *two = b; } putty-0.76/sshserver.c0000644000175000017500000004455414072266312011763 00000000000000/* * Top-level code for SSH server implementation. */ #include #include #include "putty.h" #include "ssh.h" #include "sshbpp.h" #include "sshppl.h" #include "sshchan.h" #include "sshserver.h" #ifndef NO_GSSAPI #include "sshgssc.h" #include "sshgss.h" #endif struct Ssh { int dummy; }; typedef struct server server; struct server { bufchain in_raw, out_raw; IdempotentCallback ic_out_raw; bool pending_close; bufchain dummy_user_input; /* we never put anything on this */ PacketLogSettings pls; LogContext *logctx; struct DataTransferStats stats; int remote_bugs; Socket *socket; Plug plug; int conn_throttle_count; bool frozen; Conf *conf; const SshServerConfig *ssc; ssh_key *const *hostkeys; int nhostkeys; RSAKey *hostkey1; AuthPolicy *authpolicy; LogPolicy *logpolicy; const SftpServerVtable *sftpserver_vt; agentfwd *stunt_agentfwd; Seat seat; Ssh ssh; struct ssh_version_receiver version_receiver; BinaryPacketProtocol *bpp; PacketProtocolLayer *base_layer; ConnectionLayer *cl; #ifndef NO_GSSAPI struct ssh_connection_shared_gss_state gss_state; #endif }; static void ssh_server_free_callback(void *vsrv); static void server_got_ssh_version(struct ssh_version_receiver *rcv, int major_version); static void server_connect_bpp(server *srv); static void server_bpp_output_raw_data_callback(void *vctx); void share_activate(ssh_sharing_state *sharestate, const char *server_verstring) {} void ssh_connshare_provide_connlayer(ssh_sharing_state *sharestate, ConnectionLayer *cl) {} int share_ndownstreams(ssh_sharing_state *sharestate) { return 0; } void share_got_pkt_from_server(ssh_sharing_connstate *cs, int type, const void *vpkt, int pktlen) {} void share_setup_x11_channel(ssh_sharing_connstate *cs, share_channel *chan, 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) {} Channel *agentf_new(SshChannel *c) { return NULL; } bool agent_exists(void) { return false; } void ssh_got_exitcode(Ssh *ssh, int exitcode) {} void ssh_check_frozen(Ssh *ssh) {} mainchan *mainchan_new( PacketProtocolLayer *ppl, ConnectionLayer *cl, Conf *conf, int term_width, int term_height, bool is_simple, SshChannel **sc_out) { return NULL; } void mainchan_get_specials( mainchan *mc, add_special_fn_t add_special, void *ctx) {} void mainchan_special_cmd(mainchan *mc, SessionSpecialCode code, int arg) {} void mainchan_terminal_size(mainchan *mc, int width, int height) {} /* Seat functions to ensure we don't get choosy about crypto - as the * server, it's not up to us to give user warnings */ static int server_confirm_weak_crypto_primitive( Seat *seat, const char *algtype, const char *algname, void (*callback)(void *ctx, int result), void *ctx) { return 1; } static int server_confirm_weak_cached_hostkey( Seat *seat, const char *algname, const char *betteralgs, void (*callback)(void *ctx, int result), void *ctx) { return 1; } static const SeatVtable server_seat_vt = { .output = nullseat_output, .eof = nullseat_eof, .get_userpass_input = nullseat_get_userpass_input, .notify_remote_exit = nullseat_notify_remote_exit, .connection_fatal = nullseat_connection_fatal, .update_specials_menu = nullseat_update_specials_menu, .get_ttymode = nullseat_get_ttymode, .set_busy_status = nullseat_set_busy_status, .verify_ssh_host_key = nullseat_verify_ssh_host_key, .confirm_weak_crypto_primitive = server_confirm_weak_crypto_primitive, .confirm_weak_cached_hostkey = server_confirm_weak_cached_hostkey, .is_utf8 = nullseat_is_never_utf8, .echoedit_update = nullseat_echoedit_update, .get_x_display = nullseat_get_x_display, .get_windowid = nullseat_get_windowid, .get_window_pixel_size = nullseat_get_window_pixel_size, .stripctrl_new = nullseat_stripctrl_new, .set_trust_status = nullseat_set_trust_status, .verbose = nullseat_verbose_no, .interactive = nullseat_interactive_no, .get_cursor_position = nullseat_get_cursor_position, }; static void server_socket_log(Plug *plug, PlugLogType type, SockAddr *addr, int port, const char *error_msg, int error_code) { /* server *srv = container_of(plug, server, plug); */ /* FIXME */ } static void server_closing(Plug *plug, const char *error_msg, int error_code, bool calling_back) { server *srv = container_of(plug, server, plug); if (error_msg) { ssh_remote_error(&srv->ssh, "%s", error_msg); } else if (srv->bpp) { srv->bpp->input_eof = true; queue_idempotent_callback(&srv->bpp->ic_in_raw); } } static void server_receive( Plug *plug, int urgent, const char *data, size_t len) { server *srv = container_of(plug, server, plug); /* Log raw data, if we're in that mode. */ if (srv->logctx) log_packet(srv->logctx, PKT_INCOMING, -1, NULL, data, len, 0, NULL, NULL, 0, NULL); bufchain_add(&srv->in_raw, data, len); if (!srv->frozen && srv->bpp) queue_idempotent_callback(&srv->bpp->ic_in_raw); } static void server_sent(Plug *plug, size_t bufsize) { #ifdef FIXME server *srv = container_of(plug, server, plug); /* * If the send backlog on the SSH socket itself clears, we should * unthrottle the whole world if it was throttled. Also trigger an * extra call to the consumer of the BPP's output, to try to send * some more data off its bufchain. */ if (bufsize < SSH_MAX_BACKLOG) { srv_throttle_all(srv, 0, bufsize); queue_idempotent_callback(&srv->ic_out_raw); } #endif } LogContext *ssh_get_logctx(Ssh *ssh) { server *srv = container_of(ssh, server, ssh); return srv->logctx; } void ssh_throttle_conn(Ssh *ssh, int adjust) { server *srv = container_of(ssh, server, ssh); int old_count = srv->conn_throttle_count; bool frozen; srv->conn_throttle_count += adjust; assert(srv->conn_throttle_count >= 0); if (srv->conn_throttle_count && !old_count) { frozen = true; } else if (!srv->conn_throttle_count && old_count) { frozen = false; } else { return; /* don't change current frozen state */ } srv->frozen = frozen; if (srv->socket) { sk_set_frozen(srv->socket, frozen); /* * Now process any SSH connection data that was stashed in our * queue while we were frozen. */ queue_idempotent_callback(&srv->bpp->ic_in_raw); } } void ssh_conn_processed_data(Ssh *ssh) { /* FIXME: we could add the same check_frozen_state system as we * have in ssh.c, but because that was originally added to work * around a peculiarity of the GUI event loop, I haven't yet. */ } Conf *make_ssh_server_conf(void) { Conf *conf = conf_new(); load_open_settings(NULL, conf); /* In Uppity, we support even the legacy des-cbc cipher by * default, so that it will be available if the user forces it by * overriding the KEXINIT strings. If the user wants it _not_ * supported, of course, they can override KEXINIT in the other * direction. */ conf_set_bool(conf, CONF_ssh2_des_cbc, true); return conf; } static const PlugVtable ssh_server_plugvt = { .log = server_socket_log, .closing = server_closing, .receive = server_receive, .sent = server_sent, }; Plug *ssh_server_plug( Conf *conf, const SshServerConfig *ssc, ssh_key *const *hostkeys, int nhostkeys, RSAKey *hostkey1, AuthPolicy *authpolicy, LogPolicy *logpolicy, const SftpServerVtable *sftpserver_vt) { server *srv = snew(server); memset(srv, 0, sizeof(server)); srv->plug.vt = &ssh_server_plugvt; srv->conf = conf_copy(conf); srv->ssc = ssc; srv->logctx = log_init(logpolicy, conf); conf_set_bool(srv->conf, CONF_ssh_no_shell, true); srv->nhostkeys = nhostkeys; srv->hostkeys = hostkeys; srv->hostkey1 = hostkey1; srv->authpolicy = authpolicy; srv->logpolicy = logpolicy; srv->sftpserver_vt = sftpserver_vt; srv->seat.vt = &server_seat_vt; bufchain_init(&srv->in_raw); bufchain_init(&srv->out_raw); bufchain_init(&srv->dummy_user_input); #ifndef NO_GSSAPI /* FIXME: replace with sensible */ srv->gss_state.libs = snew(struct ssh_gss_liblist); srv->gss_state.libs->nlibraries = 0; #endif return &srv->plug; } void ssh_server_start(Plug *plug, Socket *socket) { server *srv = container_of(plug, server, plug); const char *our_protoversion; if (srv->ssc->bare_connection) { our_protoversion = "2.0"; /* SSH-2 only */ } else if (srv->hostkey1 && srv->nhostkeys) { our_protoversion = "1.99"; /* offer both SSH-1 and SSH-2 */ } else if (srv->hostkey1) { our_protoversion = "1.5"; /* SSH-1 only */ } else { assert(srv->nhostkeys); our_protoversion = "2.0"; /* SSH-2 only */ } srv->socket = socket; srv->ic_out_raw.fn = server_bpp_output_raw_data_callback; srv->ic_out_raw.ctx = srv; srv->version_receiver.got_ssh_version = server_got_ssh_version; srv->bpp = ssh_verstring_new( srv->conf, srv->logctx, srv->ssc->bare_connection, our_protoversion, &srv->version_receiver, true, srv->ssc->application_name); server_connect_bpp(srv); queue_idempotent_callback(&srv->bpp->ic_in_raw); } static void ssh_server_free_callback(void *vsrv) { server *srv = (server *)vsrv; logeventf(srv->logctx, "freeing server instance"); bufchain_clear(&srv->in_raw); bufchain_clear(&srv->out_raw); bufchain_clear(&srv->dummy_user_input); if (srv->socket) sk_close(srv->socket); if (srv->stunt_agentfwd) agentfwd_free(srv->stunt_agentfwd); if (srv->base_layer) ssh_ppl_free(srv->base_layer); if (srv->bpp) ssh_bpp_free(srv->bpp); delete_callbacks_for_context(srv); conf_free(srv->conf); log_free(srv->logctx); #ifndef NO_GSSAPI sfree(srv->gss_state.libs); /* FIXME: replace with sensible */ #endif LogPolicy *lp = srv->logpolicy; sfree(srv); server_instance_terminated(lp); } static void server_connect_bpp(server *srv) { srv->bpp->ssh = &srv->ssh; srv->bpp->in_raw = &srv->in_raw; srv->bpp->out_raw = &srv->out_raw; bufchain_set_callback(srv->bpp->out_raw, &srv->ic_out_raw); srv->bpp->pls = &srv->pls; srv->bpp->logctx = srv->logctx; srv->bpp->remote_bugs = srv->remote_bugs; /* Servers don't really have a notion of 'unexpected' connection * closure. The client is free to close if it likes. */ srv->bpp->expect_close = true; } static void server_connect_ppl(server *srv, PacketProtocolLayer *ppl) { ppl->bpp = srv->bpp; ppl->user_input = &srv->dummy_user_input; ppl->logctx = srv->logctx; ppl->ssh = &srv->ssh; ppl->seat = &srv->seat; ppl->remote_bugs = srv->remote_bugs; } static void server_bpp_output_raw_data_callback(void *vctx) { server *srv = (server *)vctx; if (!srv->socket) return; while (bufchain_size(&srv->out_raw) > 0) { size_t backlog; ptrlen data = bufchain_prefix(&srv->out_raw); if (srv->logctx) log_packet(srv->logctx, PKT_OUTGOING, -1, NULL, data.ptr, data.len, 0, NULL, NULL, 0, NULL); backlog = sk_write(srv->socket, data.ptr, data.len); bufchain_consume(&srv->out_raw, data.len); if (backlog > SSH_MAX_BACKLOG) { #ifdef FIXME ssh_throttle_all(ssh, 1, backlog); #endif return; } } if (srv->pending_close) { sk_close(srv->socket); srv->socket = NULL; queue_toplevel_callback(ssh_server_free_callback, srv); } } static void server_shutdown_internal(server *srv) { /* * We only need to free the base PPL, which will free the others * (if any) transitively. */ if (srv->base_layer) { ssh_ppl_free(srv->base_layer); srv->base_layer = NULL; } srv->cl = NULL; } static void server_initiate_connection_close(server *srv) { /* Wind up everything above the BPP. */ server_shutdown_internal(srv); /* Force any remaining queued SSH packets through the BPP, and * schedule closing the network socket after they go out. */ ssh_bpp_handle_output(srv->bpp); srv->pending_close = true; queue_idempotent_callback(&srv->ic_out_raw); /* Now we expect the other end to close the connection too in * response, so arrange that we'll receive notification of that * via ssh_remote_eof. */ srv->bpp->expect_close = true; } #define GET_FORMATTED_MSG(fmt) \ char *msg; \ va_list ap; \ va_start(ap, fmt); \ msg = dupvprintf(fmt, ap); \ va_end(ap); #define LOG_FORMATTED_MSG(logctx, fmt) do \ { \ va_list ap; \ va_start(ap, fmt); \ logeventvf(logctx, fmt, ap); \ va_end(ap); \ } while (0) void ssh_remote_error(Ssh *ssh, const char *fmt, ...) { server *srv = container_of(ssh, server, ssh); LOG_FORMATTED_MSG(srv->logctx, fmt); queue_toplevel_callback(ssh_server_free_callback, srv); } void ssh_remote_eof(Ssh *ssh, const char *fmt, ...) { server *srv = container_of(ssh, server, ssh); LOG_FORMATTED_MSG(srv->logctx, fmt); queue_toplevel_callback(ssh_server_free_callback, srv); } void ssh_proto_error(Ssh *ssh, const char *fmt, ...) { server *srv = container_of(ssh, server, ssh); if (srv->base_layer) { GET_FORMATTED_MSG(fmt); ssh_bpp_queue_disconnect(srv->bpp, msg, SSH2_DISCONNECT_PROTOCOL_ERROR); server_initiate_connection_close(srv); logeventf(srv->logctx, "Protocol error: %s", msg); sfree(msg); } } void ssh_sw_abort(Ssh *ssh, const char *fmt, ...) { server *srv = container_of(ssh, server, ssh); LOG_FORMATTED_MSG(srv->logctx, fmt); queue_toplevel_callback(ssh_server_free_callback, srv); } void ssh_user_close(Ssh *ssh, const char *fmt, ...) { server *srv = container_of(ssh, server, ssh); LOG_FORMATTED_MSG(srv->logctx, fmt); queue_toplevel_callback(ssh_server_free_callback, srv); } static void server_got_ssh_version(struct ssh_version_receiver *rcv, int major_version) { server *srv = container_of(rcv, server, version_receiver); BinaryPacketProtocol *old_bpp; PacketProtocolLayer *connection_layer; old_bpp = srv->bpp; srv->remote_bugs = ssh_verstring_get_bugs(old_bpp); if (srv->ssc->bare_connection) { srv->bpp = ssh2_bare_bpp_new(srv->logctx); server_connect_bpp(srv); connection_layer = ssh2_connection_new( &srv->ssh, NULL, false, srv->conf, ssh_verstring_get_local(old_bpp), &srv->cl); ssh2connection_server_configure(connection_layer, srv->sftpserver_vt, srv->ssc); server_connect_ppl(srv, connection_layer); srv->base_layer = connection_layer; } else if (major_version == 2) { PacketProtocolLayer *userauth_layer, *transport_child_layer; srv->bpp = ssh2_bpp_new(srv->logctx, &srv->stats, true); server_connect_bpp(srv); connection_layer = ssh2_connection_new( &srv->ssh, NULL, false, srv->conf, ssh_verstring_get_local(old_bpp), &srv->cl); ssh2connection_server_configure(connection_layer, srv->sftpserver_vt, srv->ssc); server_connect_ppl(srv, connection_layer); if (conf_get_bool(srv->conf, CONF_ssh_no_userauth)) { userauth_layer = NULL; transport_child_layer = connection_layer; } else { userauth_layer = ssh2_userauth_server_new( connection_layer, srv->authpolicy, srv->ssc); server_connect_ppl(srv, userauth_layer); transport_child_layer = userauth_layer; } srv->base_layer = ssh2_transport_new( srv->conf, NULL, 0, NULL, ssh_verstring_get_remote(old_bpp), ssh_verstring_get_local(old_bpp), #ifndef NO_GSSAPI &srv->gss_state, #else NULL, #endif &srv->stats, transport_child_layer, srv->ssc); ssh2_transport_provide_hostkeys( srv->base_layer, srv->hostkeys, srv->nhostkeys); if (userauth_layer) ssh2_userauth_server_set_transport_layer( userauth_layer, srv->base_layer); server_connect_ppl(srv, srv->base_layer); } else { srv->bpp = ssh1_bpp_new(srv->logctx); server_connect_bpp(srv); connection_layer = ssh1_connection_new(&srv->ssh, srv->conf, &srv->cl); ssh1connection_server_configure(connection_layer, srv->ssc); server_connect_ppl(srv, connection_layer); srv->base_layer = ssh1_login_server_new( connection_layer, srv->hostkey1, srv->authpolicy, srv->ssc); server_connect_ppl(srv, srv->base_layer); } /* Connect the base layer - whichever it is - to the BPP, and set * up its selfptr. */ srv->base_layer->selfptr = &srv->base_layer; ssh_ppl_setup_queues(srv->base_layer, &srv->bpp->in_pq, &srv->bpp->out_pq); #ifdef FIXME // we probably will want one of these, in the end srv->pinger = pinger_new(srv->conf, &srv->backend); #endif if (srv->ssc->stunt_open_unconditional_agent_socket) { char *socketname; srv->stunt_agentfwd = agentfwd_new(srv->cl, &socketname); if (srv->stunt_agentfwd) { logeventf(srv->logctx, "opened unconditional agent socket at %s\n", socketname); sfree(socketname); } } queue_idempotent_callback(&srv->bpp->ic_in_raw); ssh_ppl_process_queue(srv->base_layer); ssh_bpp_free(old_bpp); } putty-0.76/sshserver.h0000644000175000017500000001314214072266312011755 00000000000000typedef struct AuthPolicy AuthPolicy; struct SshServerConfig { const char *application_name; const char *session_starting_dir; RSAKey *rsa_kex_key; /* * In all of these ptrlens, setting the 'ptr' member to NULL means * that we're not overriding the default configuration. */ ptrlen banner; /* default here is 'no banner' */ ptrlen kex_override[NKEXLIST]; bool exit_signal_numeric; /* mimic an old server bug */ unsigned long ssh1_cipher_mask; bool ssh1_allow_compression; bool bare_connection; bool stunt_pretend_to_accept_any_pubkey; bool stunt_open_unconditional_agent_socket; }; Plug *ssh_server_plug( Conf *conf, const SshServerConfig *ssc, ssh_key *const *hostkeys, int nhostkeys, RSAKey *hostkey1, AuthPolicy *authpolicy, LogPolicy *logpolicy, const SftpServerVtable *sftpserver_vt); void ssh_server_start(Plug *plug, Socket *socket); void server_instance_terminated(LogPolicy *logpolicy); void platform_logevent(const char *msg); #define AUTHMETHODS(X) \ X(NONE) \ X(PASSWORD) \ X(PUBLICKEY) \ X(KBDINT) \ X(TIS) \ X(CRYPTOCARD) \ /* end of list */ #define AUTHMETHOD_BIT_INDEX(name) AUTHMETHOD_BIT_INDEX_##name, enum { AUTHMETHODS(AUTHMETHOD_BIT_INDEX) AUTHMETHOD_BIT_INDEX_dummy }; #define AUTHMETHOD_BIT_VALUE(name) \ AUTHMETHOD_##name = 1 << AUTHMETHOD_BIT_INDEX_##name, enum { AUTHMETHODS(AUTHMETHOD_BIT_VALUE) AUTHMETHOD_BIT_VALUE_dummy }; typedef struct AuthKbdInt AuthKbdInt; typedef struct AuthKbdIntPrompt AuthKbdIntPrompt; struct AuthKbdInt { char *title, *instruction; /* both need freeing */ int nprompts; AuthKbdIntPrompt *prompts; /* the array itself needs freeing */ }; struct AuthKbdIntPrompt { char *prompt; /* needs freeing */ bool echo; }; unsigned auth_methods(AuthPolicy *); bool auth_none(AuthPolicy *, ptrlen username); int auth_password(AuthPolicy *, ptrlen username, ptrlen password, ptrlen *opt_new_password); /* auth_password returns 1 for 'accepted', 0 for 'rejected', and 2 for * 'ok but now you need to change your password' */ bool auth_publickey(AuthPolicy *, ptrlen username, ptrlen public_blob); /* auth_publickey_ssh1 must return the whole public key given the modulus, * because the SSH-1 client never transmits the exponent over the wire. * The key remains owned by the AuthPolicy. */ AuthKbdInt *auth_kbdint_prompts(AuthPolicy *, ptrlen username); /* auth_kbdint_prompts returns NULL to trigger auth failure */ int auth_kbdint_responses(AuthPolicy *, const ptrlen *responses); /* auth_kbdint_responses returns >0 for success, <0 for failure, and 0 * to indicate that we haven't decided yet and further prompts are * coming */ /* The very similar SSH-1 TIS and CryptoCard methods are combined into * a single API for AuthPolicy, which takes a method argument */ char *auth_ssh1int_challenge(AuthPolicy *, unsigned method, ptrlen username); bool auth_ssh1int_response(AuthPolicy *, ptrlen response); RSAKey *auth_publickey_ssh1( AuthPolicy *ap, ptrlen username, mp_int *rsa_modulus); /* auth_successful returns false if further authentication is needed */ bool auth_successful(AuthPolicy *, ptrlen username, unsigned method); PacketProtocolLayer *ssh2_userauth_server_new( PacketProtocolLayer *successor_layer, AuthPolicy *authpolicy, const SshServerConfig *ssc); void ssh2_userauth_server_set_transport_layer( PacketProtocolLayer *userauth, PacketProtocolLayer *transport); void ssh2connection_server_configure( PacketProtocolLayer *ppl, const SftpServerVtable *sftpserver_vt, const SshServerConfig *ssc); void ssh1connection_server_configure( PacketProtocolLayer *ppl, const SshServerConfig *ssc); PacketProtocolLayer *ssh1_login_server_new( PacketProtocolLayer *successor_layer, RSAKey *hostkey, AuthPolicy *authpolicy, const SshServerConfig *ssc); Channel *sesschan_new(SshChannel *c, LogContext *logctx, const SftpServerVtable *sftpserver_vt, const SshServerConfig *ssc); Backend *pty_backend_create( Seat *seat, LogContext *logctx, Conf *conf, char **argv, const char *cmd, struct ssh_ttymodes ttymodes, bool pipes_instead_of_pty, const char *dir, const char *const *env_vars_to_unset); int pty_backend_exit_signum(Backend *be); ptrlen pty_backend_exit_signame(Backend *be, char **aux_msg); /* * Establish a listening X server. Return value is the _number_ of * Sockets that it established pointing at the given Plug. (0 * indicates complete failure.) The socket pointers themselves are * written into sockets[], up to a possible total of MAX_X11_SOCKETS. * * The supplied Conf has necessary environment variables written into * it. (And is also used to open the port listeners, though that * shouldn't affect anything.) */ #define MAX_X11_SOCKETS 2 int platform_make_x11_server(Plug *plug, const char *progname, int mindisp, const char *screen_number_suffix, ptrlen authproto, ptrlen authdata, Socket **sockets, Conf *conf); Conf *make_ssh_server_conf(void); /* Provided by Unix front end programs to uxsftpserver.c */ void make_unix_sftp_filehandle_key(void *data, size_t size); typedef struct agentfwd agentfwd; agentfwd *agentfwd_new(ConnectionLayer *cl, char **socketname_out); void agentfwd_free(agentfwd *agent); putty-0.76/sshsh256.c0000644000175000017500000007153514072266312011323 00000000000000/* * SHA-256 algorithm as described at * * http://csrc.nist.gov/cryptval/shs.html */ #include "ssh.h" #include /* * Start by deciding whether we can support hardware SHA at all. */ #define HW_SHA256_NONE 0 #define HW_SHA256_NI 1 #define HW_SHA256_NEON 2 #ifdef _FORCE_SHA_NI # define HW_SHA256 HW_SHA256_NI #elif defined(__clang__) # if __has_attribute(target) && __has_include() && \ (defined(__x86_64__) || defined(__i386)) # define HW_SHA256 HW_SHA256_NI # endif #elif defined(__GNUC__) # if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9)) && \ (defined(__x86_64__) || defined(__i386)) # define HW_SHA256 HW_SHA256_NI # endif #elif defined (_MSC_VER) # if (defined(_M_X64) || defined(_M_IX86)) && _MSC_FULL_VER >= 150030729 # define HW_SHA256 HW_SHA256_NI # endif #endif #ifdef _FORCE_SHA_NEON # define HW_SHA256 HW_SHA256_NEON #elif defined __BYTE_ORDER__ && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ /* Arm can potentially support both endiannesses, but this code * hasn't been tested on anything but little. If anyone wants to * run big-endian, they'll need to fix it first. */ #elif defined __ARM_FEATURE_CRYPTO /* If the Arm crypto extension is available already, we can * support NEON SHA without having to enable anything by hand */ # define HW_SHA256 HW_SHA256_NEON #elif defined(__clang__) # if __has_attribute(target) && __has_include() && \ (defined(__aarch64__)) /* clang can enable the crypto extension in AArch64 using * __attribute__((target)) */ # define HW_SHA256 HW_SHA256_NEON # define USE_CLANG_ATTR_TARGET_AARCH64 # endif #elif defined _MSC_VER /* Visual Studio supports the crypto extension when targeting * AArch64, but as of VS2017, the AArch32 header doesn't quite * manage it (declaring the shae/shad intrinsics without a round * key operand). */ # if defined _M_ARM64 # define HW_SHA256 HW_SHA256_NEON # if defined _M_ARM64 # define USE_ARM64_NEON_H /* unusual header name in this case */ # endif # endif #endif #if defined _FORCE_SOFTWARE_SHA || !defined HW_SHA256 # undef HW_SHA256 # define HW_SHA256 HW_SHA256_NONE #endif /* * The actual query function that asks if hardware acceleration is * available. */ static bool sha256_hw_available(void); /* * The top-level selection function, caching the results of * sha256_hw_available() so it only has to run once. */ static bool sha256_hw_available_cached(void) { static bool initialised = false; static bool hw_available; if (!initialised) { hw_available = sha256_hw_available(); initialised = true; } return hw_available; } static ssh_hash *sha256_select(const ssh_hashalg *alg) { const ssh_hashalg *real_alg = sha256_hw_available_cached() ? &ssh_sha256_hw : &ssh_sha256_sw; return ssh_hash_new(real_alg); } const ssh_hashalg ssh_sha256 = { .new = sha256_select, .hlen = 32, .blocklen = 64, HASHALG_NAMES_ANNOTATED("SHA-256", "dummy selector vtable"), }; /* ---------------------------------------------------------------------- * Definitions likely to be helpful to multiple implementations. */ static const uint32_t sha256_initial_state[] = { 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19, }; static const uint32_t sha256_round_constants[] = { 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, }; #define SHA256_ROUNDS 64 typedef struct sha256_block sha256_block; struct sha256_block { uint8_t block[64]; size_t used; uint64_t len; }; static inline void sha256_block_setup(sha256_block *blk) { blk->used = 0; blk->len = 0; } static inline bool sha256_block_write( sha256_block *blk, const void **vdata, size_t *len) { size_t blkleft = sizeof(blk->block) - blk->used; size_t chunk = *len < blkleft ? *len : blkleft; const uint8_t *p = *vdata; memcpy(blk->block + blk->used, p, chunk); *vdata = p + chunk; *len -= chunk; blk->used += chunk; blk->len += chunk; if (blk->used == sizeof(blk->block)) { blk->used = 0; return true; } return false; } static inline void sha256_block_pad(sha256_block *blk, BinarySink *bs) { uint64_t final_len = blk->len << 3; size_t pad = 1 + (63 & (55 - blk->used)); put_byte(bs, 0x80); for (size_t i = 1; i < pad; i++) put_byte(bs, 0); put_uint64(bs, final_len); assert(blk->used == 0 && "Should have exactly hit a block boundary"); } /* ---------------------------------------------------------------------- * Software implementation of SHA-256. */ static inline uint32_t ror(uint32_t x, unsigned y) { return (x << (31 & -y)) | (x >> (31 & y)); } static inline uint32_t Ch(uint32_t ctrl, uint32_t if1, uint32_t if0) { return if0 ^ (ctrl & (if1 ^ if0)); } static inline uint32_t Maj(uint32_t x, uint32_t y, uint32_t z) { return (x & y) | (z & (x | y)); } static inline uint32_t Sigma_0(uint32_t x) { return ror(x,2) ^ ror(x,13) ^ ror(x,22); } static inline uint32_t Sigma_1(uint32_t x) { return ror(x,6) ^ ror(x,11) ^ ror(x,25); } static inline uint32_t sigma_0(uint32_t x) { return ror(x,7) ^ ror(x,18) ^ (x >> 3); } static inline uint32_t sigma_1(uint32_t x) { return ror(x,17) ^ ror(x,19) ^ (x >> 10); } static inline void sha256_sw_round( unsigned round_index, const uint32_t *schedule, uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d, uint32_t *e, uint32_t *f, uint32_t *g, uint32_t *h) { uint32_t t1 = *h + Sigma_1(*e) + Ch(*e,*f,*g) + sha256_round_constants[round_index] + schedule[round_index]; uint32_t t2 = Sigma_0(*a) + Maj(*a,*b,*c); *d += t1; *h = t1 + t2; } static void sha256_sw_block(uint32_t *core, const uint8_t *block) { uint32_t w[SHA256_ROUNDS]; uint32_t a,b,c,d,e,f,g,h; for (size_t t = 0; t < 16; t++) w[t] = GET_32BIT_MSB_FIRST(block + 4*t); for (size_t t = 16; t < SHA256_ROUNDS; t++) w[t] = sigma_1(w[t-2]) + w[t-7] + sigma_0(w[t-15]) + w[t-16]; a = core[0]; b = core[1]; c = core[2]; d = core[3]; e = core[4]; f = core[5]; g = core[6]; h = core[7]; for (size_t t = 0; t < SHA256_ROUNDS; t += 8) { sha256_sw_round(t+0, w, &a,&b,&c,&d,&e,&f,&g,&h); sha256_sw_round(t+1, w, &h,&a,&b,&c,&d,&e,&f,&g); sha256_sw_round(t+2, w, &g,&h,&a,&b,&c,&d,&e,&f); sha256_sw_round(t+3, w, &f,&g,&h,&a,&b,&c,&d,&e); sha256_sw_round(t+4, w, &e,&f,&g,&h,&a,&b,&c,&d); sha256_sw_round(t+5, w, &d,&e,&f,&g,&h,&a,&b,&c); sha256_sw_round(t+6, w, &c,&d,&e,&f,&g,&h,&a,&b); sha256_sw_round(t+7, w, &b,&c,&d,&e,&f,&g,&h,&a); } core[0] += a; core[1] += b; core[2] += c; core[3] += d; core[4] += e; core[5] += f; core[6] += g; core[7] += h; smemclr(w, sizeof(w)); } typedef struct sha256_sw { uint32_t core[8]; sha256_block blk; BinarySink_IMPLEMENTATION; ssh_hash hash; } sha256_sw; static void sha256_sw_write(BinarySink *bs, const void *vp, size_t len); static ssh_hash *sha256_sw_new(const ssh_hashalg *alg) { sha256_sw *s = snew(sha256_sw); s->hash.vt = alg; BinarySink_INIT(s, sha256_sw_write); BinarySink_DELEGATE_INIT(&s->hash, s); return &s->hash; } static void sha256_sw_reset(ssh_hash *hash) { sha256_sw *s = container_of(hash, sha256_sw, hash); memcpy(s->core, sha256_initial_state, sizeof(s->core)); sha256_block_setup(&s->blk); } static void sha256_sw_copyfrom(ssh_hash *hcopy, ssh_hash *horig) { sha256_sw *copy = container_of(hcopy, sha256_sw, hash); sha256_sw *orig = container_of(horig, sha256_sw, hash); memcpy(copy, orig, sizeof(*copy)); BinarySink_COPIED(copy); BinarySink_DELEGATE_INIT(©->hash, copy); } static void sha256_sw_free(ssh_hash *hash) { sha256_sw *s = container_of(hash, sha256_sw, hash); smemclr(s, sizeof(*s)); sfree(s); } static void sha256_sw_write(BinarySink *bs, const void *vp, size_t len) { sha256_sw *s = BinarySink_DOWNCAST(bs, sha256_sw); while (len > 0) if (sha256_block_write(&s->blk, &vp, &len)) sha256_sw_block(s->core, s->blk.block); } static void sha256_sw_digest(ssh_hash *hash, uint8_t *digest) { sha256_sw *s = container_of(hash, sha256_sw, hash); sha256_block_pad(&s->blk, BinarySink_UPCAST(s)); for (size_t i = 0; i < 8; i++) PUT_32BIT_MSB_FIRST(digest + 4*i, s->core[i]); } const ssh_hashalg ssh_sha256_sw = { .new = sha256_sw_new, .reset = sha256_sw_reset, .copyfrom = sha256_sw_copyfrom, .digest = sha256_sw_digest, .free = sha256_sw_free, .hlen = 32, .blocklen = 64, HASHALG_NAMES_ANNOTATED("SHA-256", "unaccelerated"), }; /* ---------------------------------------------------------------------- * Hardware-accelerated implementation of SHA-256 using x86 SHA-NI. */ #if HW_SHA256 == HW_SHA256_NI /* * Set target architecture for Clang and GCC */ #if defined(__clang__) || defined(__GNUC__) # define FUNC_ISA __attribute__ ((target("sse4.1,sha"))) #if !defined(__clang__) # pragma GCC target("sha") # pragma GCC target("sse4.1") #endif #else # define FUNC_ISA #endif #include #include #include #if defined(__clang__) || defined(__GNUC__) #include #endif #if defined(__clang__) || defined(__GNUC__) #include #define GET_CPU_ID_0(out) \ __cpuid(0, (out)[0], (out)[1], (out)[2], (out)[3]) #define GET_CPU_ID_7(out) \ __cpuid_count(7, 0, (out)[0], (out)[1], (out)[2], (out)[3]) #else #define GET_CPU_ID_0(out) __cpuid(out, 0) #define GET_CPU_ID_7(out) __cpuidex(out, 7, 0) #endif static bool sha256_hw_available(void) { unsigned int CPUInfo[4]; GET_CPU_ID_0(CPUInfo); if (CPUInfo[0] < 7) return false; GET_CPU_ID_7(CPUInfo); return CPUInfo[1] & (1 << 29); /* Check SHA */ } /* SHA256 implementation using new instructions The code is based on Jeffrey Walton's SHA256 implementation: https://github.com/noloader/SHA-Intrinsics */ FUNC_ISA static inline void sha256_ni_block(__m128i *core, const uint8_t *p) { __m128i STATE0, STATE1; __m128i MSG, TMP; __m128i MSG0, MSG1, MSG2, MSG3; const __m128i *block = (const __m128i *)p; const __m128i MASK = _mm_set_epi64x( 0x0c0d0e0f08090a0bULL, 0x0405060700010203ULL); /* Load initial values */ STATE0 = core[0]; STATE1 = core[1]; /* Rounds 0-3 */ MSG = _mm_loadu_si128(block); MSG0 = _mm_shuffle_epi8(MSG, MASK); MSG = _mm_add_epi32(MSG0, _mm_set_epi64x( 0xE9B5DBA5B5C0FBCFULL, 0x71374491428A2F98ULL)); STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); MSG = _mm_shuffle_epi32(MSG, 0x0E); STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG); /* Rounds 4-7 */ MSG1 = _mm_loadu_si128(block + 1); MSG1 = _mm_shuffle_epi8(MSG1, MASK); MSG = _mm_add_epi32(MSG1, _mm_set_epi64x( 0xAB1C5ED5923F82A4ULL, 0x59F111F13956C25BULL)); STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); MSG = _mm_shuffle_epi32(MSG, 0x0E); STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG); MSG0 = _mm_sha256msg1_epu32(MSG0, MSG1); /* Rounds 8-11 */ MSG2 = _mm_loadu_si128(block + 2); MSG2 = _mm_shuffle_epi8(MSG2, MASK); MSG = _mm_add_epi32(MSG2, _mm_set_epi64x( 0x550C7DC3243185BEULL, 0x12835B01D807AA98ULL)); STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); MSG = _mm_shuffle_epi32(MSG, 0x0E); STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG); MSG1 = _mm_sha256msg1_epu32(MSG1, MSG2); /* Rounds 12-15 */ MSG3 = _mm_loadu_si128(block + 3); MSG3 = _mm_shuffle_epi8(MSG3, MASK); MSG = _mm_add_epi32(MSG3, _mm_set_epi64x( 0xC19BF1749BDC06A7ULL, 0x80DEB1FE72BE5D74ULL)); STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); TMP = _mm_alignr_epi8(MSG3, MSG2, 4); MSG0 = _mm_add_epi32(MSG0, TMP); MSG0 = _mm_sha256msg2_epu32(MSG0, MSG3); MSG = _mm_shuffle_epi32(MSG, 0x0E); STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG); MSG2 = _mm_sha256msg1_epu32(MSG2, MSG3); /* Rounds 16-19 */ MSG = _mm_add_epi32(MSG0, _mm_set_epi64x( 0x240CA1CC0FC19DC6ULL, 0xEFBE4786E49B69C1ULL)); STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); TMP = _mm_alignr_epi8(MSG0, MSG3, 4); MSG1 = _mm_add_epi32(MSG1, TMP); MSG1 = _mm_sha256msg2_epu32(MSG1, MSG0); MSG = _mm_shuffle_epi32(MSG, 0x0E); STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG); MSG3 = _mm_sha256msg1_epu32(MSG3, MSG0); /* Rounds 20-23 */ MSG = _mm_add_epi32(MSG1, _mm_set_epi64x( 0x76F988DA5CB0A9DCULL, 0x4A7484AA2DE92C6FULL)); STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); TMP = _mm_alignr_epi8(MSG1, MSG0, 4); MSG2 = _mm_add_epi32(MSG2, TMP); MSG2 = _mm_sha256msg2_epu32(MSG2, MSG1); MSG = _mm_shuffle_epi32(MSG, 0x0E); STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG); MSG0 = _mm_sha256msg1_epu32(MSG0, MSG1); /* Rounds 24-27 */ MSG = _mm_add_epi32(MSG2, _mm_set_epi64x( 0xBF597FC7B00327C8ULL, 0xA831C66D983E5152ULL)); STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); TMP = _mm_alignr_epi8(MSG2, MSG1, 4); MSG3 = _mm_add_epi32(MSG3, TMP); MSG3 = _mm_sha256msg2_epu32(MSG3, MSG2); MSG = _mm_shuffle_epi32(MSG, 0x0E); STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG); MSG1 = _mm_sha256msg1_epu32(MSG1, MSG2); /* Rounds 28-31 */ MSG = _mm_add_epi32(MSG3, _mm_set_epi64x( 0x1429296706CA6351ULL, 0xD5A79147C6E00BF3ULL)); STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); TMP = _mm_alignr_epi8(MSG3, MSG2, 4); MSG0 = _mm_add_epi32(MSG0, TMP); MSG0 = _mm_sha256msg2_epu32(MSG0, MSG3); MSG = _mm_shuffle_epi32(MSG, 0x0E); STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG); MSG2 = _mm_sha256msg1_epu32(MSG2, MSG3); /* Rounds 32-35 */ MSG = _mm_add_epi32(MSG0, _mm_set_epi64x( 0x53380D134D2C6DFCULL, 0x2E1B213827B70A85ULL)); STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); TMP = _mm_alignr_epi8(MSG0, MSG3, 4); MSG1 = _mm_add_epi32(MSG1, TMP); MSG1 = _mm_sha256msg2_epu32(MSG1, MSG0); MSG = _mm_shuffle_epi32(MSG, 0x0E); STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG); MSG3 = _mm_sha256msg1_epu32(MSG3, MSG0); /* Rounds 36-39 */ MSG = _mm_add_epi32(MSG1, _mm_set_epi64x( 0x92722C8581C2C92EULL, 0x766A0ABB650A7354ULL)); STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); TMP = _mm_alignr_epi8(MSG1, MSG0, 4); MSG2 = _mm_add_epi32(MSG2, TMP); MSG2 = _mm_sha256msg2_epu32(MSG2, MSG1); MSG = _mm_shuffle_epi32(MSG, 0x0E); STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG); MSG0 = _mm_sha256msg1_epu32(MSG0, MSG1); /* Rounds 40-43 */ MSG = _mm_add_epi32(MSG2, _mm_set_epi64x( 0xC76C51A3C24B8B70ULL, 0xA81A664BA2BFE8A1ULL)); STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); TMP = _mm_alignr_epi8(MSG2, MSG1, 4); MSG3 = _mm_add_epi32(MSG3, TMP); MSG3 = _mm_sha256msg2_epu32(MSG3, MSG2); MSG = _mm_shuffle_epi32(MSG, 0x0E); STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG); MSG1 = _mm_sha256msg1_epu32(MSG1, MSG2); /* Rounds 44-47 */ MSG = _mm_add_epi32(MSG3, _mm_set_epi64x( 0x106AA070F40E3585ULL, 0xD6990624D192E819ULL)); STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); TMP = _mm_alignr_epi8(MSG3, MSG2, 4); MSG0 = _mm_add_epi32(MSG0, TMP); MSG0 = _mm_sha256msg2_epu32(MSG0, MSG3); MSG = _mm_shuffle_epi32(MSG, 0x0E); STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG); MSG2 = _mm_sha256msg1_epu32(MSG2, MSG3); /* Rounds 48-51 */ MSG = _mm_add_epi32(MSG0, _mm_set_epi64x( 0x34B0BCB52748774CULL, 0x1E376C0819A4C116ULL)); STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); TMP = _mm_alignr_epi8(MSG0, MSG3, 4); MSG1 = _mm_add_epi32(MSG1, TMP); MSG1 = _mm_sha256msg2_epu32(MSG1, MSG0); MSG = _mm_shuffle_epi32(MSG, 0x0E); STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG); MSG3 = _mm_sha256msg1_epu32(MSG3, MSG0); /* Rounds 52-55 */ MSG = _mm_add_epi32(MSG1, _mm_set_epi64x( 0x682E6FF35B9CCA4FULL, 0x4ED8AA4A391C0CB3ULL)); STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); TMP = _mm_alignr_epi8(MSG1, MSG0, 4); MSG2 = _mm_add_epi32(MSG2, TMP); MSG2 = _mm_sha256msg2_epu32(MSG2, MSG1); MSG = _mm_shuffle_epi32(MSG, 0x0E); STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG); /* Rounds 56-59 */ MSG = _mm_add_epi32(MSG2, _mm_set_epi64x( 0x8CC7020884C87814ULL, 0x78A5636F748F82EEULL)); STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); TMP = _mm_alignr_epi8(MSG2, MSG1, 4); MSG3 = _mm_add_epi32(MSG3, TMP); MSG3 = _mm_sha256msg2_epu32(MSG3, MSG2); MSG = _mm_shuffle_epi32(MSG, 0x0E); STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG); /* Rounds 60-63 */ MSG = _mm_add_epi32(MSG3, _mm_set_epi64x( 0xC67178F2BEF9A3F7ULL, 0xA4506CEB90BEFFFAULL)); STATE1 = _mm_sha256rnds2_epu32(STATE1, STATE0, MSG); MSG = _mm_shuffle_epi32(MSG, 0x0E); STATE0 = _mm_sha256rnds2_epu32(STATE0, STATE1, MSG); /* Combine state */ core[0] = _mm_add_epi32(STATE0, core[0]); core[1] = _mm_add_epi32(STATE1, core[1]); } typedef struct sha256_ni { /* * These two vectors store the 8 words of the SHA-256 state, but * not in the same order they appear in the spec: the first word * holds A,B,E,F and the second word C,D,G,H. */ __m128i core[2]; sha256_block blk; void *pointer_to_free; BinarySink_IMPLEMENTATION; ssh_hash hash; } sha256_ni; static void sha256_ni_write(BinarySink *bs, const void *vp, size_t len); static sha256_ni *sha256_ni_alloc(void) { /* * The __m128i variables in the context structure need to be * 16-byte aligned, but not all malloc implementations that this * code has to work with will guarantee to return a 16-byte * aligned pointer. So we over-allocate, manually realign the * pointer ourselves, and store the original one inside the * context so we know how to free it later. */ void *allocation = smalloc(sizeof(sha256_ni) + 15); uintptr_t alloc_address = (uintptr_t)allocation; uintptr_t aligned_address = (alloc_address + 15) & ~15; sha256_ni *s = (sha256_ni *)aligned_address; s->pointer_to_free = allocation; return s; } static ssh_hash *sha256_ni_new(const ssh_hashalg *alg) { if (!sha256_hw_available_cached()) return NULL; sha256_ni *s = sha256_ni_alloc(); s->hash.vt = alg; BinarySink_INIT(s, sha256_ni_write); BinarySink_DELEGATE_INIT(&s->hash, s); return &s->hash; } FUNC_ISA static void sha256_ni_reset(ssh_hash *hash) { sha256_ni *s = container_of(hash, sha256_ni, hash); /* Initialise the core vectors in their storage order */ s->core[0] = _mm_set_epi64x( 0x6a09e667bb67ae85ULL, 0x510e527f9b05688cULL); s->core[1] = _mm_set_epi64x( 0x3c6ef372a54ff53aULL, 0x1f83d9ab5be0cd19ULL); sha256_block_setup(&s->blk); } static void sha256_ni_copyfrom(ssh_hash *hcopy, ssh_hash *horig) { sha256_ni *copy = container_of(hcopy, sha256_ni, hash); sha256_ni *orig = container_of(horig, sha256_ni, hash); void *ptf_save = copy->pointer_to_free; *copy = *orig; /* structure copy */ copy->pointer_to_free = ptf_save; BinarySink_COPIED(copy); BinarySink_DELEGATE_INIT(©->hash, copy); } static void sha256_ni_free(ssh_hash *hash) { sha256_ni *s = container_of(hash, sha256_ni, hash); void *ptf = s->pointer_to_free; smemclr(s, sizeof(*s)); sfree(ptf); } static void sha256_ni_write(BinarySink *bs, const void *vp, size_t len) { sha256_ni *s = BinarySink_DOWNCAST(bs, sha256_ni); while (len > 0) if (sha256_block_write(&s->blk, &vp, &len)) sha256_ni_block(s->core, s->blk.block); } FUNC_ISA static void sha256_ni_digest(ssh_hash *hash, uint8_t *digest) { sha256_ni *s = container_of(hash, sha256_ni, hash); sha256_block_pad(&s->blk, BinarySink_UPCAST(s)); /* Rearrange the words into the output order */ __m128i feba = _mm_shuffle_epi32(s->core[0], 0x1B); __m128i dchg = _mm_shuffle_epi32(s->core[1], 0xB1); __m128i dcba = _mm_blend_epi16(feba, dchg, 0xF0); __m128i hgfe = _mm_alignr_epi8(dchg, feba, 8); /* Byte-swap them into the output endianness */ const __m128i mask = _mm_setr_epi8(3,2,1,0,7,6,5,4,11,10,9,8,15,14,13,12); dcba = _mm_shuffle_epi8(dcba, mask); hgfe = _mm_shuffle_epi8(hgfe, mask); /* And store them */ __m128i *output = (__m128i *)digest; _mm_storeu_si128(output, dcba); _mm_storeu_si128(output+1, hgfe); } const ssh_hashalg ssh_sha256_hw = { .new = sha256_ni_new, .reset = sha256_ni_reset, .copyfrom = sha256_ni_copyfrom, .digest = sha256_ni_digest, .free = sha256_ni_free, .hlen = 32, .blocklen = 64, HASHALG_NAMES_ANNOTATED("SHA-256", "SHA-NI accelerated"), }; /* ---------------------------------------------------------------------- * Hardware-accelerated implementation of SHA-256 using Arm NEON. */ #elif HW_SHA256 == HW_SHA256_NEON /* * Manually set the target architecture, if we decided above that we * need to. */ #ifdef USE_CLANG_ATTR_TARGET_AARCH64 /* * A spot of cheating: redefine some ACLE feature macros before * including arm_neon.h. Otherwise we won't get the SHA intrinsics * defined by that header, because it will be looking at the settings * for the whole translation unit rather than the ones we're going to * put on some particular functions using __attribute__((target)). */ #define __ARM_NEON 1 #define __ARM_FEATURE_CRYPTO 1 #define __ARM_FEATURE_SHA2 1 #define FUNC_ISA __attribute__ ((target("neon,crypto"))) #endif /* USE_CLANG_ATTR_TARGET_AARCH64 */ #ifndef FUNC_ISA #define FUNC_ISA #endif #ifdef USE_ARM64_NEON_H #include #else #include #endif static bool sha256_hw_available(void) { /* * For Arm, we delegate to a per-platform detection function (see * explanation in sshaes.c). */ return platform_sha256_hw_available(); } typedef struct sha256_neon_core sha256_neon_core; struct sha256_neon_core { uint32x4_t abcd, efgh; }; FUNC_ISA static inline uint32x4_t sha256_neon_load_input(const uint8_t *p) { return vreinterpretq_u32_u8(vrev32q_u8(vld1q_u8(p))); } FUNC_ISA static inline uint32x4_t sha256_neon_schedule_update( uint32x4_t m4, uint32x4_t m3, uint32x4_t m2, uint32x4_t m1) { return vsha256su1q_u32(vsha256su0q_u32(m4, m3), m2, m1); } FUNC_ISA static inline sha256_neon_core sha256_neon_round4( sha256_neon_core old, uint32x4_t sched, unsigned round) { sha256_neon_core new; uint32x4_t round_input = vaddq_u32( sched, vld1q_u32(sha256_round_constants + round)); new.abcd = vsha256hq_u32 (old.abcd, old.efgh, round_input); new.efgh = vsha256h2q_u32(old.efgh, old.abcd, round_input); return new; } FUNC_ISA static inline void sha256_neon_block(sha256_neon_core *core, const uint8_t *p) { uint32x4_t s0, s1, s2, s3; sha256_neon_core cr = *core; s0 = sha256_neon_load_input(p); cr = sha256_neon_round4(cr, s0, 0); s1 = sha256_neon_load_input(p+16); cr = sha256_neon_round4(cr, s1, 4); s2 = sha256_neon_load_input(p+32); cr = sha256_neon_round4(cr, s2, 8); s3 = sha256_neon_load_input(p+48); cr = sha256_neon_round4(cr, s3, 12); s0 = sha256_neon_schedule_update(s0, s1, s2, s3); cr = sha256_neon_round4(cr, s0, 16); s1 = sha256_neon_schedule_update(s1, s2, s3, s0); cr = sha256_neon_round4(cr, s1, 20); s2 = sha256_neon_schedule_update(s2, s3, s0, s1); cr = sha256_neon_round4(cr, s2, 24); s3 = sha256_neon_schedule_update(s3, s0, s1, s2); cr = sha256_neon_round4(cr, s3, 28); s0 = sha256_neon_schedule_update(s0, s1, s2, s3); cr = sha256_neon_round4(cr, s0, 32); s1 = sha256_neon_schedule_update(s1, s2, s3, s0); cr = sha256_neon_round4(cr, s1, 36); s2 = sha256_neon_schedule_update(s2, s3, s0, s1); cr = sha256_neon_round4(cr, s2, 40); s3 = sha256_neon_schedule_update(s3, s0, s1, s2); cr = sha256_neon_round4(cr, s3, 44); s0 = sha256_neon_schedule_update(s0, s1, s2, s3); cr = sha256_neon_round4(cr, s0, 48); s1 = sha256_neon_schedule_update(s1, s2, s3, s0); cr = sha256_neon_round4(cr, s1, 52); s2 = sha256_neon_schedule_update(s2, s3, s0, s1); cr = sha256_neon_round4(cr, s2, 56); s3 = sha256_neon_schedule_update(s3, s0, s1, s2); cr = sha256_neon_round4(cr, s3, 60); core->abcd = vaddq_u32(core->abcd, cr.abcd); core->efgh = vaddq_u32(core->efgh, cr.efgh); } typedef struct sha256_neon { sha256_neon_core core; sha256_block blk; BinarySink_IMPLEMENTATION; ssh_hash hash; } sha256_neon; static void sha256_neon_write(BinarySink *bs, const void *vp, size_t len); static ssh_hash *sha256_neon_new(const ssh_hashalg *alg) { if (!sha256_hw_available_cached()) return NULL; sha256_neon *s = snew(sha256_neon); s->hash.vt = alg; BinarySink_INIT(s, sha256_neon_write); BinarySink_DELEGATE_INIT(&s->hash, s); return &s->hash; } static void sha256_neon_reset(ssh_hash *hash) { sha256_neon *s = container_of(hash, sha256_neon, hash); s->core.abcd = vld1q_u32(sha256_initial_state); s->core.efgh = vld1q_u32(sha256_initial_state + 4); sha256_block_setup(&s->blk); } static void sha256_neon_copyfrom(ssh_hash *hcopy, ssh_hash *horig) { sha256_neon *copy = container_of(hcopy, sha256_neon, hash); sha256_neon *orig = container_of(horig, sha256_neon, hash); *copy = *orig; /* structure copy */ BinarySink_COPIED(copy); BinarySink_DELEGATE_INIT(©->hash, copy); } static void sha256_neon_free(ssh_hash *hash) { sha256_neon *s = container_of(hash, sha256_neon, hash); smemclr(s, sizeof(*s)); sfree(s); } static void sha256_neon_write(BinarySink *bs, const void *vp, size_t len) { sha256_neon *s = BinarySink_DOWNCAST(bs, sha256_neon); while (len > 0) if (sha256_block_write(&s->blk, &vp, &len)) sha256_neon_block(&s->core, s->blk.block); } static void sha256_neon_digest(ssh_hash *hash, uint8_t *digest) { sha256_neon *s = container_of(hash, sha256_neon, hash); sha256_block_pad(&s->blk, BinarySink_UPCAST(s)); vst1q_u8(digest, vrev32q_u8(vreinterpretq_u8_u32(s->core.abcd))); vst1q_u8(digest + 16, vrev32q_u8(vreinterpretq_u8_u32(s->core.efgh))); } const ssh_hashalg ssh_sha256_hw = { .new = sha256_neon_new, .reset = sha256_neon_reset, .copyfrom = sha256_neon_copyfrom, .digest = sha256_neon_digest, .free = sha256_neon_free, .hlen = 32, .blocklen = 64, HASHALG_NAMES_ANNOTATED("SHA-256", "NEON accelerated"), }; /* ---------------------------------------------------------------------- * Stub functions if we have no hardware-accelerated SHA-256. In this * case, sha256_hw_new returns NULL (though it should also never be * selected by sha256_select, so the only thing that should even be * _able_ to call it is testcrypt). As a result, the remaining vtable * functions should never be called at all. */ #elif HW_SHA256 == HW_SHA256_NONE static bool sha256_hw_available(void) { return false; } static ssh_hash *sha256_stub_new(const ssh_hashalg *alg) { return NULL; } #define STUB_BODY { unreachable("Should never be called"); } static void sha256_stub_reset(ssh_hash *hash) STUB_BODY static void sha256_stub_copyfrom(ssh_hash *hash, ssh_hash *orig) STUB_BODY static void sha256_stub_free(ssh_hash *hash) STUB_BODY static void sha256_stub_digest(ssh_hash *hash, uint8_t *digest) STUB_BODY const ssh_hashalg ssh_sha256_hw = { .new = sha256_stub_new, .reset = sha256_stub_reset, .copyfrom = sha256_stub_copyfrom, .digest = sha256_stub_digest, .free = sha256_stub_free, .hlen = 32, .blocklen = 64, HASHALG_NAMES_ANNOTATED("SHA-256", "!NONEXISTENT ACCELERATED VERSION!"), }; #endif /* HW_SHA256 */ putty-0.76/sshsh512.c0000644000175000017500000006576114072266312011322 00000000000000/* * SHA-512 algorithm as described at * * http://csrc.nist.gov/cryptval/shs.html * * Modifications made for SHA-384 also */ #include #include "ssh.h" /* * Start by deciding whether we can support hardware SHA at all. */ #define HW_SHA512_NONE 0 #define HW_SHA512_NEON 1 #ifdef _FORCE_SHA512_NEON # define HW_SHA512 HW_SHA512_NEON #elif defined __BYTE_ORDER__ && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ /* Arm can potentially support both endiannesses, but this code * hasn't been tested on anything but little. If anyone wants to * run big-endian, they'll need to fix it first. */ #elif defined __ARM_FEATURE_SHA512 /* If the Arm SHA-512 extension is available already, we can * support NEON SHA without having to enable anything by hand */ # define HW_SHA512 HW_SHA512_NEON #elif defined(__clang__) # if __has_attribute(target) && __has_include() && \ (defined(__aarch64__)) /* clang can enable the crypto extension in AArch64 using * __attribute__((target)) */ # define HW_SHA512 HW_SHA512_NEON # define USE_CLANG_ATTR_TARGET_AARCH64 # endif #endif #if defined _FORCE_SOFTWARE_SHA || !defined HW_SHA512 # undef HW_SHA512 # define HW_SHA512 HW_SHA512_NONE #endif /* * The actual query function that asks if hardware acceleration is * available. */ static bool sha512_hw_available(void); /* * The top-level selection function, caching the results of * sha512_hw_available() so it only has to run once. */ static bool sha512_hw_available_cached(void) { static bool initialised = false; static bool hw_available; if (!initialised) { hw_available = sha512_hw_available(); initialised = true; } return hw_available; } struct sha512_select_options { const ssh_hashalg *hw, *sw; }; static ssh_hash *sha512_select(const ssh_hashalg *alg) { const struct sha512_select_options *options = (const struct sha512_select_options *)alg->extra; const ssh_hashalg *real_alg = sha512_hw_available_cached() ? options->hw : options->sw; return ssh_hash_new(real_alg); } const struct sha512_select_options ssh_sha512_select_options = { &ssh_sha512_hw, &ssh_sha512_sw, }; const struct sha512_select_options ssh_sha384_select_options = { &ssh_sha384_hw, &ssh_sha384_sw, }; const ssh_hashalg ssh_sha512 = { .new = sha512_select, .hlen = 64, .blocklen = 128, HASHALG_NAMES_ANNOTATED("SHA-512", "dummy selector vtable"), .extra = &ssh_sha512_select_options, }; const ssh_hashalg ssh_sha384 = { .new = sha512_select, .hlen = 48, .blocklen = 128, HASHALG_NAMES_ANNOTATED("SHA-384", "dummy selector vtable"), .extra = &ssh_sha384_select_options, }; /* ---------------------------------------------------------------------- * Definitions likely to be helpful to multiple implementations. */ static const uint64_t sha512_initial_state[] = { 0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL, 0x3c6ef372fe94f82bULL, 0xa54ff53a5f1d36f1ULL, 0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL, 0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL, }; static const uint64_t sha384_initial_state[] = { 0xcbbb9d5dc1059ed8ULL, 0x629a292a367cd507ULL, 0x9159015a3070dd17ULL, 0x152fecd8f70e5939ULL, 0x67332667ffc00b31ULL, 0x8eb44a8768581511ULL, 0xdb0c2e0d64f98fa7ULL, 0x47b5481dbefa4fa4ULL, }; static const uint64_t sha512_round_constants[] = { 0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL, 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL, 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, 0xd807aa98a3030242ULL, 0x12835b0145706fbeULL, 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL, 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, 0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL, 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, 0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL, 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL, 0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, 0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL, 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, 0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL, 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL, 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, 0x81c2c92e47edaee6ULL, 0x92722c851482353bULL, 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, 0xd192e819d6ef5218ULL, 0xd69906245565a910ULL, 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL, 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, 0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL, 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL, 0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL, 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL, 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, 0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL, 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL, 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, 0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL, 0x113f9804bef90daeULL, 0x1b710b35131c471bULL, 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, 0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL, 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL, }; #define SHA512_ROUNDS 80 typedef struct sha512_block sha512_block; struct sha512_block { uint8_t block[128]; size_t used; uint64_t lenhi, lenlo; }; static inline void sha512_block_setup(sha512_block *blk) { blk->used = 0; blk->lenhi = blk->lenlo = 0; } static inline bool sha512_block_write( sha512_block *blk, const void **vdata, size_t *len) { size_t blkleft = sizeof(blk->block) - blk->used; size_t chunk = *len < blkleft ? *len : blkleft; const uint8_t *p = *vdata; memcpy(blk->block + blk->used, p, chunk); *vdata = p + chunk; *len -= chunk; blk->used += chunk; size_t chunkbits = chunk << 3; blk->lenlo += chunkbits; blk->lenhi += (blk->lenlo < chunkbits); if (blk->used == sizeof(blk->block)) { blk->used = 0; return true; } return false; } static inline void sha512_block_pad(sha512_block *blk, BinarySink *bs) { uint64_t final_lenhi = blk->lenhi; uint64_t final_lenlo = blk->lenlo; size_t pad = 127 & (111 - blk->used); put_byte(bs, 0x80); put_padding(bs, pad, 0); put_uint64(bs, final_lenhi); put_uint64(bs, final_lenlo); assert(blk->used == 0 && "Should have exactly hit a block boundary"); } /* ---------------------------------------------------------------------- * Software implementation of SHA-512. */ static inline uint64_t ror(uint64_t x, unsigned y) { return (x << (63 & -y)) | (x >> (63 & y)); } static inline uint64_t Ch(uint64_t ctrl, uint64_t if1, uint64_t if0) { return if0 ^ (ctrl & (if1 ^ if0)); } static inline uint64_t Maj(uint64_t x, uint64_t y, uint64_t z) { return (x & y) | (z & (x | y)); } static inline uint64_t Sigma_0(uint64_t x) { return ror(x,28) ^ ror(x,34) ^ ror(x,39); } static inline uint64_t Sigma_1(uint64_t x) { return ror(x,14) ^ ror(x,18) ^ ror(x,41); } static inline uint64_t sigma_0(uint64_t x) { return ror(x,1) ^ ror(x,8) ^ (x >> 7); } static inline uint64_t sigma_1(uint64_t x) { return ror(x,19) ^ ror(x,61) ^ (x >> 6); } static inline void sha512_sw_round( unsigned round_index, const uint64_t *schedule, uint64_t *a, uint64_t *b, uint64_t *c, uint64_t *d, uint64_t *e, uint64_t *f, uint64_t *g, uint64_t *h) { uint64_t t1 = *h + Sigma_1(*e) + Ch(*e,*f,*g) + sha512_round_constants[round_index] + schedule[round_index]; uint64_t t2 = Sigma_0(*a) + Maj(*a,*b,*c); *d += t1; *h = t1 + t2; } static void sha512_sw_block(uint64_t *core, const uint8_t *block) { uint64_t w[SHA512_ROUNDS]; uint64_t a,b,c,d,e,f,g,h; int t; for (t = 0; t < 16; t++) w[t] = GET_64BIT_MSB_FIRST(block + 8*t); for (t = 16; t < SHA512_ROUNDS; t++) w[t] = w[t-16] + w[t-7] + sigma_0(w[t-15]) + sigma_1(w[t-2]); a = core[0]; b = core[1]; c = core[2]; d = core[3]; e = core[4]; f = core[5]; g = core[6]; h = core[7]; for (t = 0; t < SHA512_ROUNDS; t+=8) { sha512_sw_round(t+0, w, &a,&b,&c,&d,&e,&f,&g,&h); sha512_sw_round(t+1, w, &h,&a,&b,&c,&d,&e,&f,&g); sha512_sw_round(t+2, w, &g,&h,&a,&b,&c,&d,&e,&f); sha512_sw_round(t+3, w, &f,&g,&h,&a,&b,&c,&d,&e); sha512_sw_round(t+4, w, &e,&f,&g,&h,&a,&b,&c,&d); sha512_sw_round(t+5, w, &d,&e,&f,&g,&h,&a,&b,&c); sha512_sw_round(t+6, w, &c,&d,&e,&f,&g,&h,&a,&b); sha512_sw_round(t+7, w, &b,&c,&d,&e,&f,&g,&h,&a); } core[0] += a; core[1] += b; core[2] += c; core[3] += d; core[4] += e; core[5] += f; core[6] += g; core[7] += h; smemclr(w, sizeof(w)); } typedef struct sha512_sw { uint64_t core[8]; sha512_block blk; BinarySink_IMPLEMENTATION; ssh_hash hash; } sha512_sw; static void sha512_sw_write(BinarySink *bs, const void *vp, size_t len); static ssh_hash *sha512_sw_new(const ssh_hashalg *alg) { sha512_sw *s = snew(sha512_sw); s->hash.vt = alg; BinarySink_INIT(s, sha512_sw_write); BinarySink_DELEGATE_INIT(&s->hash, s); return &s->hash; } static void sha512_sw_reset(ssh_hash *hash) { sha512_sw *s = container_of(hash, sha512_sw, hash); /* The 'extra' field in the ssh_hashalg indicates which * initialisation vector we're using */ memcpy(s->core, hash->vt->extra, sizeof(s->core)); sha512_block_setup(&s->blk); } static void sha512_sw_copyfrom(ssh_hash *hcopy, ssh_hash *horig) { sha512_sw *copy = container_of(hcopy, sha512_sw, hash); sha512_sw *orig = container_of(horig, sha512_sw, hash); memcpy(copy, orig, sizeof(*copy)); BinarySink_COPIED(copy); BinarySink_DELEGATE_INIT(©->hash, copy); } static void sha512_sw_free(ssh_hash *hash) { sha512_sw *s = container_of(hash, sha512_sw, hash); smemclr(s, sizeof(*s)); sfree(s); } static void sha512_sw_write(BinarySink *bs, const void *vp, size_t len) { sha512_sw *s = BinarySink_DOWNCAST(bs, sha512_sw); while (len > 0) if (sha512_block_write(&s->blk, &vp, &len)) sha512_sw_block(s->core, s->blk.block); } static void sha512_sw_digest(ssh_hash *hash, uint8_t *digest) { sha512_sw *s = container_of(hash, sha512_sw, hash); sha512_block_pad(&s->blk, BinarySink_UPCAST(s)); for (size_t i = 0; i < hash->vt->hlen / 8; i++) PUT_64BIT_MSB_FIRST(digest + 8*i, s->core[i]); } const ssh_hashalg ssh_sha512_sw = { .new = sha512_sw_new, .reset = sha512_sw_reset, .copyfrom = sha512_sw_copyfrom, .digest = sha512_sw_digest, .free = sha512_sw_free, .hlen = 64, .blocklen = 128, HASHALG_NAMES_ANNOTATED("SHA-512", "unaccelerated"), .extra = sha512_initial_state, }; const ssh_hashalg ssh_sha384_sw = { .new = sha512_sw_new, .reset = sha512_sw_reset, .copyfrom = sha512_sw_copyfrom, .digest = sha512_sw_digest, .free = sha512_sw_free, .hlen = 48, .blocklen = 128, HASHALG_NAMES_ANNOTATED("SHA-384", "unaccelerated"), .extra = sha384_initial_state, }; /* ---------------------------------------------------------------------- * Hardware-accelerated implementation of SHA-512 using Arm NEON. */ #if HW_SHA512 == HW_SHA512_NEON /* * Manually set the target architecture, if we decided above that we * need to. */ #ifdef USE_CLANG_ATTR_TARGET_AARCH64 /* * A spot of cheating: redefine some ACLE feature macros before * including arm_neon.h. Otherwise we won't get the SHA intrinsics * defined by that header, because it will be looking at the settings * for the whole translation unit rather than the ones we're going to * put on some particular functions using __attribute__((target)). */ #define __ARM_NEON 1 #define __ARM_FEATURE_CRYPTO 1 #define FUNC_ISA __attribute__ ((target("neon,sha3"))) #endif /* USE_CLANG_ATTR_TARGET_AARCH64 */ #ifndef FUNC_ISA #define FUNC_ISA #endif #ifdef USE_ARM64_NEON_H #include #else #include #endif static bool sha512_hw_available(void) { /* * For Arm, we delegate to a per-platform detection function (see * explanation in sshaes.c). */ return platform_sha512_hw_available(); } #if defined __clang__ /* * As of 2020-12-24, I've found that clang doesn't provide the SHA-512 * NEON intrinsics. So I define my own set using inline assembler, and * use #define to effectively rename them over the top of the standard * names. * * The aim of that #define technique is that it should avoid a build * failure if these intrinsics _are_ defined in . * Obviously it would be better in that situation to switch back to * using the real intrinsics, but until I see a version of clang that * supports them, I won't know what version number to test in the * ifdef. */ static inline FUNC_ISA uint64x2_t vsha512su0q_u64_asm(uint64x2_t x, uint64x2_t y) { __asm__("sha512su0 %0.2D,%1.2D" : "+w" (x) : "w" (y)); return x; } static inline FUNC_ISA uint64x2_t vsha512su1q_u64_asm(uint64x2_t x, uint64x2_t y, uint64x2_t z) { __asm__("sha512su1 %0.2D,%1.2D,%2.2D" : "+w" (x) : "w" (y), "w" (z)); return x; } static inline FUNC_ISA uint64x2_t vsha512hq_u64_asm(uint64x2_t x, uint64x2_t y, uint64x2_t z) { __asm__("sha512h %0,%1,%2.2D" : "+w" (x) : "w" (y), "w" (z)); return x; } static inline FUNC_ISA uint64x2_t vsha512h2q_u64_asm(uint64x2_t x, uint64x2_t y, uint64x2_t z) { __asm__("sha512h2 %0,%1,%2.2D" : "+w" (x) : "w" (y), "w" (z)); return x; } #undef vsha512su0q_u64 #define vsha512su0q_u64 vsha512su0q_u64_asm #undef vsha512su1q_u64 #define vsha512su1q_u64 vsha512su1q_u64_asm #undef vsha512hq_u64 #define vsha512hq_u64 vsha512hq_u64_asm #undef vsha512h2q_u64 #define vsha512h2q_u64 vsha512h2q_u64_asm #endif /* defined __clang__ */ typedef struct sha512_neon_core sha512_neon_core; struct sha512_neon_core { uint64x2_t ab, cd, ef, gh; }; FUNC_ISA static inline uint64x2_t sha512_neon_load_input(const uint8_t *p) { return vreinterpretq_u64_u8(vrev64q_u8(vld1q_u8(p))); } FUNC_ISA static inline uint64x2_t sha512_neon_schedule_update( uint64x2_t m8, uint64x2_t m7, uint64x2_t m4, uint64x2_t m3, uint64x2_t m1) { /* * vsha512su0q_u64() takes words from a long way back in the * schedule and performs the sigma_0 half of the computation of * the next two 64-bit message-schedule words. * * vsha512su1q_u64() combines the result of that with the sigma_1 * steps, to output the finished version of those two words. The * total amount of input data it requires fits nicely into three * 128-bit vector registers, but one of those registers is * misaligned compared to the 128-bit chunks that the message * schedule is stored in. So we use vextq_u64 to make one of its * input words out of the second half of m4 and the first half of * m3. */ return vsha512su1q_u64(vsha512su0q_u64(m8, m7), m1, vextq_u64(m4, m3, 1)); } FUNC_ISA static inline void sha512_neon_round2( unsigned round_index, uint64x2_t schedule_words, uint64x2_t *ab, uint64x2_t *cd, uint64x2_t *ef, uint64x2_t *gh) { /* * vsha512hq_u64 performs the Sigma_1 and Ch half of the * computation of two rounds of SHA-512 (including feeding back * one of the outputs from the first of those half-rounds into the * second one). * * vsha512h2q_u64 combines the result of that with the Sigma_0 and * Maj steps, and outputs one 128-bit vector that replaces the gh * piece of the input hash state, and a second that updates cd by * addition. * * Similarly to vsha512su1q_u64 above, some of the input registers * expected by these instructions are misaligned by 64 bits * relative to the chunks we've divided the hash state into, so we * have to start by making 'de' and 'fg' words out of our input * cd,ef,gh, using vextq_u64. * * Also, one of the inputs to vsha512hq_u64 is expected to contain * the results of summing gh + two round constants + two words of * message schedule, but the two words of the message schedule * have to be the opposite way round in the vector register from * the way that vsha512su1q_u64 output them. Hence, there's * another vextq_u64 in here that swaps the two halves of the * initial_sum vector register. * * (This also means that I don't have to prepare a specially * reordered version of the sha512_round_constants[] array: as * long as I'm unavoidably doing a swap at run time _anyway_, I * can load from the normally ordered version of that array, and * just take care to fold in that data _before_ the swap rather * than after.) */ /* Load two round constants, with the first one in the low half */ uint64x2_t round_constants = vld1q_u64( sha512_round_constants + round_index); /* Add schedule words to round constants */ uint64x2_t initial_sum = vaddq_u64(schedule_words, round_constants); /* Swap that sum around so the word used in the first of the two * rounds is in the _high_ half of the vector, matching where h * lives in the gh vector */ uint64x2_t swapped_initial_sum = vextq_u64(initial_sum, initial_sum, 1); /* Add gh to that, now that they're matching ways round */ uint64x2_t sum = vaddq_u64(swapped_initial_sum, *gh); /* Make the misaligned de and fg words */ uint64x2_t de = vextq_u64(*cd, *ef, 1); uint64x2_t fg = vextq_u64(*ef, *gh, 1); /* Now we're ready to put all the pieces together. The output from * vsha512h2q_u64 can be used directly as the new gh, and the * output from vsha512hq_u64 is simultaneously the intermediate * value passed to h2 and the thing you have to add on to cd. */ uint64x2_t intermed = vsha512hq_u64(sum, fg, de); *gh = vsha512h2q_u64(intermed, *cd, *ab); *cd = vaddq_u64(*cd, intermed); } FUNC_ISA static inline void sha512_neon_block(sha512_neon_core *core, const uint8_t *p) { uint64x2_t s0, s1, s2, s3, s4, s5, s6, s7; uint64x2_t ab = core->ab, cd = core->cd, ef = core->ef, gh = core->gh; s0 = sha512_neon_load_input(p + 16*0); sha512_neon_round2(0, s0, &ab, &cd, &ef, &gh); s1 = sha512_neon_load_input(p + 16*1); sha512_neon_round2(2, s1, &gh, &ab, &cd, &ef); s2 = sha512_neon_load_input(p + 16*2); sha512_neon_round2(4, s2, &ef, &gh, &ab, &cd); s3 = sha512_neon_load_input(p + 16*3); sha512_neon_round2(6, s3, &cd, &ef, &gh, &ab); s4 = sha512_neon_load_input(p + 16*4); sha512_neon_round2(8, s4, &ab, &cd, &ef, &gh); s5 = sha512_neon_load_input(p + 16*5); sha512_neon_round2(10, s5, &gh, &ab, &cd, &ef); s6 = sha512_neon_load_input(p + 16*6); sha512_neon_round2(12, s6, &ef, &gh, &ab, &cd); s7 = sha512_neon_load_input(p + 16*7); sha512_neon_round2(14, s7, &cd, &ef, &gh, &ab); s0 = sha512_neon_schedule_update(s0, s1, s4, s5, s7); sha512_neon_round2(16, s0, &ab, &cd, &ef, &gh); s1 = sha512_neon_schedule_update(s1, s2, s5, s6, s0); sha512_neon_round2(18, s1, &gh, &ab, &cd, &ef); s2 = sha512_neon_schedule_update(s2, s3, s6, s7, s1); sha512_neon_round2(20, s2, &ef, &gh, &ab, &cd); s3 = sha512_neon_schedule_update(s3, s4, s7, s0, s2); sha512_neon_round2(22, s3, &cd, &ef, &gh, &ab); s4 = sha512_neon_schedule_update(s4, s5, s0, s1, s3); sha512_neon_round2(24, s4, &ab, &cd, &ef, &gh); s5 = sha512_neon_schedule_update(s5, s6, s1, s2, s4); sha512_neon_round2(26, s5, &gh, &ab, &cd, &ef); s6 = sha512_neon_schedule_update(s6, s7, s2, s3, s5); sha512_neon_round2(28, s6, &ef, &gh, &ab, &cd); s7 = sha512_neon_schedule_update(s7, s0, s3, s4, s6); sha512_neon_round2(30, s7, &cd, &ef, &gh, &ab); s0 = sha512_neon_schedule_update(s0, s1, s4, s5, s7); sha512_neon_round2(32, s0, &ab, &cd, &ef, &gh); s1 = sha512_neon_schedule_update(s1, s2, s5, s6, s0); sha512_neon_round2(34, s1, &gh, &ab, &cd, &ef); s2 = sha512_neon_schedule_update(s2, s3, s6, s7, s1); sha512_neon_round2(36, s2, &ef, &gh, &ab, &cd); s3 = sha512_neon_schedule_update(s3, s4, s7, s0, s2); sha512_neon_round2(38, s3, &cd, &ef, &gh, &ab); s4 = sha512_neon_schedule_update(s4, s5, s0, s1, s3); sha512_neon_round2(40, s4, &ab, &cd, &ef, &gh); s5 = sha512_neon_schedule_update(s5, s6, s1, s2, s4); sha512_neon_round2(42, s5, &gh, &ab, &cd, &ef); s6 = sha512_neon_schedule_update(s6, s7, s2, s3, s5); sha512_neon_round2(44, s6, &ef, &gh, &ab, &cd); s7 = sha512_neon_schedule_update(s7, s0, s3, s4, s6); sha512_neon_round2(46, s7, &cd, &ef, &gh, &ab); s0 = sha512_neon_schedule_update(s0, s1, s4, s5, s7); sha512_neon_round2(48, s0, &ab, &cd, &ef, &gh); s1 = sha512_neon_schedule_update(s1, s2, s5, s6, s0); sha512_neon_round2(50, s1, &gh, &ab, &cd, &ef); s2 = sha512_neon_schedule_update(s2, s3, s6, s7, s1); sha512_neon_round2(52, s2, &ef, &gh, &ab, &cd); s3 = sha512_neon_schedule_update(s3, s4, s7, s0, s2); sha512_neon_round2(54, s3, &cd, &ef, &gh, &ab); s4 = sha512_neon_schedule_update(s4, s5, s0, s1, s3); sha512_neon_round2(56, s4, &ab, &cd, &ef, &gh); s5 = sha512_neon_schedule_update(s5, s6, s1, s2, s4); sha512_neon_round2(58, s5, &gh, &ab, &cd, &ef); s6 = sha512_neon_schedule_update(s6, s7, s2, s3, s5); sha512_neon_round2(60, s6, &ef, &gh, &ab, &cd); s7 = sha512_neon_schedule_update(s7, s0, s3, s4, s6); sha512_neon_round2(62, s7, &cd, &ef, &gh, &ab); s0 = sha512_neon_schedule_update(s0, s1, s4, s5, s7); sha512_neon_round2(64, s0, &ab, &cd, &ef, &gh); s1 = sha512_neon_schedule_update(s1, s2, s5, s6, s0); sha512_neon_round2(66, s1, &gh, &ab, &cd, &ef); s2 = sha512_neon_schedule_update(s2, s3, s6, s7, s1); sha512_neon_round2(68, s2, &ef, &gh, &ab, &cd); s3 = sha512_neon_schedule_update(s3, s4, s7, s0, s2); sha512_neon_round2(70, s3, &cd, &ef, &gh, &ab); s4 = sha512_neon_schedule_update(s4, s5, s0, s1, s3); sha512_neon_round2(72, s4, &ab, &cd, &ef, &gh); s5 = sha512_neon_schedule_update(s5, s6, s1, s2, s4); sha512_neon_round2(74, s5, &gh, &ab, &cd, &ef); s6 = sha512_neon_schedule_update(s6, s7, s2, s3, s5); sha512_neon_round2(76, s6, &ef, &gh, &ab, &cd); s7 = sha512_neon_schedule_update(s7, s0, s3, s4, s6); sha512_neon_round2(78, s7, &cd, &ef, &gh, &ab); core->ab = vaddq_u64(core->ab, ab); core->cd = vaddq_u64(core->cd, cd); core->ef = vaddq_u64(core->ef, ef); core->gh = vaddq_u64(core->gh, gh); } typedef struct sha512_neon { sha512_neon_core core; sha512_block blk; BinarySink_IMPLEMENTATION; ssh_hash hash; } sha512_neon; static void sha512_neon_write(BinarySink *bs, const void *vp, size_t len); static ssh_hash *sha512_neon_new(const ssh_hashalg *alg) { if (!sha512_hw_available_cached()) return NULL; sha512_neon *s = snew(sha512_neon); s->hash.vt = alg; BinarySink_INIT(s, sha512_neon_write); BinarySink_DELEGATE_INIT(&s->hash, s); return &s->hash; } static void sha512_neon_reset(ssh_hash *hash) { sha512_neon *s = container_of(hash, sha512_neon, hash); const uint64_t *iv = (const uint64_t *)hash->vt->extra; s->core.ab = vld1q_u64(iv); s->core.cd = vld1q_u64(iv+2); s->core.ef = vld1q_u64(iv+4); s->core.gh = vld1q_u64(iv+6); sha512_block_setup(&s->blk); } static void sha512_neon_copyfrom(ssh_hash *hcopy, ssh_hash *horig) { sha512_neon *copy = container_of(hcopy, sha512_neon, hash); sha512_neon *orig = container_of(horig, sha512_neon, hash); *copy = *orig; /* structure copy */ BinarySink_COPIED(copy); BinarySink_DELEGATE_INIT(©->hash, copy); } static void sha512_neon_free(ssh_hash *hash) { sha512_neon *s = container_of(hash, sha512_neon, hash); smemclr(s, sizeof(*s)); sfree(s); } static void sha512_neon_write(BinarySink *bs, const void *vp, size_t len) { sha512_neon *s = BinarySink_DOWNCAST(bs, sha512_neon); while (len > 0) if (sha512_block_write(&s->blk, &vp, &len)) sha512_neon_block(&s->core, s->blk.block); } static void sha512_neon_digest(ssh_hash *hash, uint8_t *digest) { sha512_neon *s = container_of(hash, sha512_neon, hash); sha512_block_pad(&s->blk, BinarySink_UPCAST(s)); vst1q_u8(digest, vrev64q_u8(vreinterpretq_u8_u64(s->core.ab))); vst1q_u8(digest+16, vrev64q_u8(vreinterpretq_u8_u64(s->core.cd))); vst1q_u8(digest+32, vrev64q_u8(vreinterpretq_u8_u64(s->core.ef))); vst1q_u8(digest+48, vrev64q_u8(vreinterpretq_u8_u64(s->core.gh))); } static void sha384_neon_digest(ssh_hash *hash, uint8_t *digest) { sha512_neon *s = container_of(hash, sha512_neon, hash); sha512_block_pad(&s->blk, BinarySink_UPCAST(s)); vst1q_u8(digest, vrev64q_u8(vreinterpretq_u8_u64(s->core.ab))); vst1q_u8(digest+16, vrev64q_u8(vreinterpretq_u8_u64(s->core.cd))); vst1q_u8(digest+32, vrev64q_u8(vreinterpretq_u8_u64(s->core.ef))); } const ssh_hashalg ssh_sha512_hw = { .new = sha512_neon_new, .reset = sha512_neon_reset, .copyfrom = sha512_neon_copyfrom, .digest = sha512_neon_digest, .free = sha512_neon_free, .hlen = 64, .blocklen = 128, HASHALG_NAMES_ANNOTATED("SHA-512", "NEON accelerated"), .extra = sha512_initial_state, }; const ssh_hashalg ssh_sha384_hw = { .new = sha512_neon_new, .reset = sha512_neon_reset, .copyfrom = sha512_neon_copyfrom, .digest = sha384_neon_digest, .free = sha512_neon_free, .hlen = 48, .blocklen = 128, HASHALG_NAMES_ANNOTATED("SHA-384", "NEON accelerated"), .extra = sha384_initial_state, }; /* ---------------------------------------------------------------------- * Stub functions if we have no hardware-accelerated SHA-512. In this * case, sha512_hw_new returns NULL (though it should also never be * selected by sha512_select, so the only thing that should even be * _able_ to call it is testcrypt). As a result, the remaining vtable * functions should never be called at all. */ #elif HW_SHA512 == HW_SHA512_NONE static bool sha512_hw_available(void) { return false; } static ssh_hash *sha512_stub_new(const ssh_hashalg *alg) { return NULL; } #define STUB_BODY { unreachable("Should never be called"); } static void sha512_stub_reset(ssh_hash *hash) STUB_BODY static void sha512_stub_copyfrom(ssh_hash *hash, ssh_hash *orig) STUB_BODY static void sha512_stub_free(ssh_hash *hash) STUB_BODY static void sha512_stub_digest(ssh_hash *hash, uint8_t *digest) STUB_BODY const ssh_hashalg ssh_sha512_hw = { .new = sha512_stub_new, .reset = sha512_stub_reset, .copyfrom = sha512_stub_copyfrom, .digest = sha512_stub_digest, .free = sha512_stub_free, .hlen = 64, .blocklen = 128, HASHALG_NAMES_ANNOTATED("SHA-512", "!NONEXISTENT ACCELERATED VERSION!"), }; const ssh_hashalg ssh_sha384_hw = { .new = sha512_stub_new, .reset = sha512_stub_reset, .copyfrom = sha512_stub_copyfrom, .digest = sha512_stub_digest, .free = sha512_stub_free, .hlen = 48, .blocklen = 128, HASHALG_NAMES_ANNOTATED("SHA-384", "!NONEXISTENT ACCELERATED VERSION!"), }; #endif /* HW_SHA512 */ putty-0.76/sshsha.c0000644000175000017500000006661414072266312011231 00000000000000/* * SHA-1 algorithm as described at * * http://csrc.nist.gov/cryptval/shs.html */ #include "ssh.h" #include /* * Start by deciding whether we can support hardware SHA at all. */ #define HW_SHA1_NONE 0 #define HW_SHA1_NI 1 #define HW_SHA1_NEON 2 #ifdef _FORCE_SHA_NI # define HW_SHA1 HW_SHA1_NI #elif defined(__clang__) # if __has_attribute(target) && __has_include() && \ (defined(__x86_64__) || defined(__i386)) # define HW_SHA1 HW_SHA1_NI # endif #elif defined(__GNUC__) # if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9)) && \ (defined(__x86_64__) || defined(__i386)) # define HW_SHA1 HW_SHA1_NI # endif #elif defined (_MSC_VER) # if (defined(_M_X64) || defined(_M_IX86)) && _MSC_FULL_VER >= 150030729 # define HW_SHA1 HW_SHA1_NI # endif #endif #ifdef _FORCE_SHA_NEON # define HW_SHA1 HW_SHA1_NEON #elif defined __BYTE_ORDER__ && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ /* Arm can potentially support both endiannesses, but this code * hasn't been tested on anything but little. If anyone wants to * run big-endian, they'll need to fix it first. */ #elif defined __ARM_FEATURE_CRYPTO /* If the Arm crypto extension is available already, we can * support NEON SHA without having to enable anything by hand */ # define HW_SHA1 HW_SHA1_NEON #elif defined(__clang__) # if __has_attribute(target) && __has_include() && \ (defined(__aarch64__)) /* clang can enable the crypto extension in AArch64 using * __attribute__((target)) */ # define HW_SHA1 HW_SHA1_NEON # define USE_CLANG_ATTR_TARGET_AARCH64 # endif #elif defined _MSC_VER /* Visual Studio supports the crypto extension when targeting * AArch64, but as of VS2017, the AArch32 header doesn't quite * manage it (declaring the shae/shad intrinsics without a round * key operand). */ # if defined _M_ARM64 # define HW_SHA1 HW_SHA1_NEON # if defined _M_ARM64 # define USE_ARM64_NEON_H /* unusual header name in this case */ # endif # endif #endif #if defined _FORCE_SOFTWARE_SHA || !defined HW_SHA1 # undef HW_SHA1 # define HW_SHA1 HW_SHA1_NONE #endif /* * The actual query function that asks if hardware acceleration is * available. */ static bool sha1_hw_available(void); /* * The top-level selection function, caching the results of * sha1_hw_available() so it only has to run once. */ static bool sha1_hw_available_cached(void) { static bool initialised = false; static bool hw_available; if (!initialised) { hw_available = sha1_hw_available(); initialised = true; } return hw_available; } static ssh_hash *sha1_select(const ssh_hashalg *alg) { const ssh_hashalg *real_alg = sha1_hw_available_cached() ? &ssh_sha1_hw : &ssh_sha1_sw; return ssh_hash_new(real_alg); } const ssh_hashalg ssh_sha1 = { .new = sha1_select, .hlen = 20, .blocklen = 64, HASHALG_NAMES_ANNOTATED("SHA-1", "dummy selector vtable"), }; /* ---------------------------------------------------------------------- * Definitions likely to be helpful to multiple implementations. */ static const uint32_t sha1_initial_state[] = { 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0, }; #define SHA1_ROUNDS_PER_STAGE 20 #define SHA1_STAGE0_CONSTANT 0x5a827999 #define SHA1_STAGE1_CONSTANT 0x6ed9eba1 #define SHA1_STAGE2_CONSTANT 0x8f1bbcdc #define SHA1_STAGE3_CONSTANT 0xca62c1d6 #define SHA1_ROUNDS (4 * SHA1_ROUNDS_PER_STAGE) typedef struct sha1_block sha1_block; struct sha1_block { uint8_t block[64]; size_t used; uint64_t len; }; static inline void sha1_block_setup(sha1_block *blk) { blk->used = 0; blk->len = 0; } static inline bool sha1_block_write( sha1_block *blk, const void **vdata, size_t *len) { size_t blkleft = sizeof(blk->block) - blk->used; size_t chunk = *len < blkleft ? *len : blkleft; const uint8_t *p = *vdata; memcpy(blk->block + blk->used, p, chunk); *vdata = p + chunk; *len -= chunk; blk->used += chunk; blk->len += chunk; if (blk->used == sizeof(blk->block)) { blk->used = 0; return true; } return false; } static inline void sha1_block_pad(sha1_block *blk, BinarySink *bs) { uint64_t final_len = blk->len << 3; size_t pad = 1 + (63 & (55 - blk->used)); put_byte(bs, 0x80); for (size_t i = 1; i < pad; i++) put_byte(bs, 0); put_uint64(bs, final_len); assert(blk->used == 0 && "Should have exactly hit a block boundary"); } /* ---------------------------------------------------------------------- * Software implementation of SHA-1. */ static inline uint32_t rol(uint32_t x, unsigned y) { return (x << (31 & y)) | (x >> (31 & -y)); } static inline uint32_t Ch(uint32_t ctrl, uint32_t if1, uint32_t if0) { return if0 ^ (ctrl & (if1 ^ if0)); } static inline uint32_t Maj(uint32_t x, uint32_t y, uint32_t z) { return (x & y) | (z & (x | y)); } static inline uint32_t Par(uint32_t x, uint32_t y, uint32_t z) { return (x ^ y ^ z); } static inline void sha1_sw_round( unsigned round_index, const uint32_t *schedule, uint32_t *a, uint32_t *b, uint32_t *c, uint32_t *d, uint32_t *e, uint32_t f, uint32_t constant) { *e = rol(*a, 5) + f + *e + schedule[round_index] + constant; *b = rol(*b, 30); } static void sha1_sw_block(uint32_t *core, const uint8_t *block) { uint32_t w[SHA1_ROUNDS]; uint32_t a,b,c,d,e; for (size_t t = 0; t < 16; t++) w[t] = GET_32BIT_MSB_FIRST(block + 4*t); for (size_t t = 16; t < SHA1_ROUNDS; t++) w[t] = rol(w[t - 3] ^ w[t - 8] ^ w[t - 14] ^ w[t - 16], 1); a = core[0]; b = core[1]; c = core[2]; d = core[3]; e = core[4]; size_t t = 0; for (size_t u = 0; u < SHA1_ROUNDS_PER_STAGE/5; u++) { sha1_sw_round(t++,w, &a,&b,&c,&d,&e, Ch(b,c,d), SHA1_STAGE0_CONSTANT); sha1_sw_round(t++,w, &e,&a,&b,&c,&d, Ch(a,b,c), SHA1_STAGE0_CONSTANT); sha1_sw_round(t++,w, &d,&e,&a,&b,&c, Ch(e,a,b), SHA1_STAGE0_CONSTANT); sha1_sw_round(t++,w, &c,&d,&e,&a,&b, Ch(d,e,a), SHA1_STAGE0_CONSTANT); sha1_sw_round(t++,w, &b,&c,&d,&e,&a, Ch(c,d,e), SHA1_STAGE0_CONSTANT); } for (size_t u = 0; u < SHA1_ROUNDS_PER_STAGE/5; u++) { sha1_sw_round(t++,w, &a,&b,&c,&d,&e, Par(b,c,d), SHA1_STAGE1_CONSTANT); sha1_sw_round(t++,w, &e,&a,&b,&c,&d, Par(a,b,c), SHA1_STAGE1_CONSTANT); sha1_sw_round(t++,w, &d,&e,&a,&b,&c, Par(e,a,b), SHA1_STAGE1_CONSTANT); sha1_sw_round(t++,w, &c,&d,&e,&a,&b, Par(d,e,a), SHA1_STAGE1_CONSTANT); sha1_sw_round(t++,w, &b,&c,&d,&e,&a, Par(c,d,e), SHA1_STAGE1_CONSTANT); } for (size_t u = 0; u < SHA1_ROUNDS_PER_STAGE/5; u++) { sha1_sw_round(t++,w, &a,&b,&c,&d,&e, Maj(b,c,d), SHA1_STAGE2_CONSTANT); sha1_sw_round(t++,w, &e,&a,&b,&c,&d, Maj(a,b,c), SHA1_STAGE2_CONSTANT); sha1_sw_round(t++,w, &d,&e,&a,&b,&c, Maj(e,a,b), SHA1_STAGE2_CONSTANT); sha1_sw_round(t++,w, &c,&d,&e,&a,&b, Maj(d,e,a), SHA1_STAGE2_CONSTANT); sha1_sw_round(t++,w, &b,&c,&d,&e,&a, Maj(c,d,e), SHA1_STAGE2_CONSTANT); } for (size_t u = 0; u < SHA1_ROUNDS_PER_STAGE/5; u++) { sha1_sw_round(t++,w, &a,&b,&c,&d,&e, Par(b,c,d), SHA1_STAGE3_CONSTANT); sha1_sw_round(t++,w, &e,&a,&b,&c,&d, Par(a,b,c), SHA1_STAGE3_CONSTANT); sha1_sw_round(t++,w, &d,&e,&a,&b,&c, Par(e,a,b), SHA1_STAGE3_CONSTANT); sha1_sw_round(t++,w, &c,&d,&e,&a,&b, Par(d,e,a), SHA1_STAGE3_CONSTANT); sha1_sw_round(t++,w, &b,&c,&d,&e,&a, Par(c,d,e), SHA1_STAGE3_CONSTANT); } core[0] += a; core[1] += b; core[2] += c; core[3] += d; core[4] += e; smemclr(w, sizeof(w)); } typedef struct sha1_sw { uint32_t core[5]; sha1_block blk; BinarySink_IMPLEMENTATION; ssh_hash hash; } sha1_sw; static void sha1_sw_write(BinarySink *bs, const void *vp, size_t len); static ssh_hash *sha1_sw_new(const ssh_hashalg *alg) { sha1_sw *s = snew(sha1_sw); s->hash.vt = alg; BinarySink_INIT(s, sha1_sw_write); BinarySink_DELEGATE_INIT(&s->hash, s); return &s->hash; } static void sha1_sw_reset(ssh_hash *hash) { sha1_sw *s = container_of(hash, sha1_sw, hash); memcpy(s->core, sha1_initial_state, sizeof(s->core)); sha1_block_setup(&s->blk); } static void sha1_sw_copyfrom(ssh_hash *hcopy, ssh_hash *horig) { sha1_sw *copy = container_of(hcopy, sha1_sw, hash); sha1_sw *orig = container_of(horig, sha1_sw, hash); memcpy(copy, orig, sizeof(*copy)); BinarySink_COPIED(copy); BinarySink_DELEGATE_INIT(©->hash, copy); } static void sha1_sw_free(ssh_hash *hash) { sha1_sw *s = container_of(hash, sha1_sw, hash); smemclr(s, sizeof(*s)); sfree(s); } static void sha1_sw_write(BinarySink *bs, const void *vp, size_t len) { sha1_sw *s = BinarySink_DOWNCAST(bs, sha1_sw); while (len > 0) if (sha1_block_write(&s->blk, &vp, &len)) sha1_sw_block(s->core, s->blk.block); } static void sha1_sw_digest(ssh_hash *hash, uint8_t *digest) { sha1_sw *s = container_of(hash, sha1_sw, hash); sha1_block_pad(&s->blk, BinarySink_UPCAST(s)); for (size_t i = 0; i < 5; i++) PUT_32BIT_MSB_FIRST(digest + 4*i, s->core[i]); } const ssh_hashalg ssh_sha1_sw = { .new = sha1_sw_new, .reset = sha1_sw_reset, .copyfrom = sha1_sw_copyfrom, .digest = sha1_sw_digest, .free = sha1_sw_free, .hlen = 20, .blocklen = 64, HASHALG_NAMES_ANNOTATED("SHA-1", "unaccelerated"), }; /* ---------------------------------------------------------------------- * Hardware-accelerated implementation of SHA-1 using x86 SHA-NI. */ #if HW_SHA1 == HW_SHA1_NI /* * Set target architecture for Clang and GCC */ #if defined(__clang__) || defined(__GNUC__) # define FUNC_ISA __attribute__ ((target("sse4.1,sha"))) #if !defined(__clang__) # pragma GCC target("sha") # pragma GCC target("sse4.1") #endif #else # define FUNC_ISA #endif #include #include #include #if defined(__clang__) || defined(__GNUC__) #include #endif #if defined(__clang__) || defined(__GNUC__) #include #define GET_CPU_ID_0(out) \ __cpuid(0, (out)[0], (out)[1], (out)[2], (out)[3]) #define GET_CPU_ID_7(out) \ __cpuid_count(7, 0, (out)[0], (out)[1], (out)[2], (out)[3]) #else #define GET_CPU_ID_0(out) __cpuid(out, 0) #define GET_CPU_ID_7(out) __cpuidex(out, 7, 0) #endif static bool sha1_hw_available(void) { unsigned int CPUInfo[4]; GET_CPU_ID_0(CPUInfo); if (CPUInfo[0] < 7) return false; GET_CPU_ID_7(CPUInfo); return CPUInfo[1] & (1 << 29); /* Check SHA */ } /* SHA1 implementation using new instructions The code is based on Jeffrey Walton's SHA1 implementation: https://github.com/noloader/SHA-Intrinsics */ FUNC_ISA static inline void sha1_ni_block(__m128i *core, const uint8_t *p) { __m128i ABCD, E0, E1, MSG0, MSG1, MSG2, MSG3; const __m128i MASK = _mm_set_epi64x( 0x0001020304050607ULL, 0x08090a0b0c0d0e0fULL); const __m128i *block = (const __m128i *)p; /* Load initial values */ ABCD = core[0]; E0 = core[1]; /* Rounds 0-3 */ MSG0 = _mm_loadu_si128(block); MSG0 = _mm_shuffle_epi8(MSG0, MASK); E0 = _mm_add_epi32(E0, MSG0); E1 = ABCD; ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 0); /* Rounds 4-7 */ MSG1 = _mm_loadu_si128(block + 1); MSG1 = _mm_shuffle_epi8(MSG1, MASK); E1 = _mm_sha1nexte_epu32(E1, MSG1); E0 = ABCD; ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 0); MSG0 = _mm_sha1msg1_epu32(MSG0, MSG1); /* Rounds 8-11 */ MSG2 = _mm_loadu_si128(block + 2); MSG2 = _mm_shuffle_epi8(MSG2, MASK); E0 = _mm_sha1nexte_epu32(E0, MSG2); E1 = ABCD; ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 0); MSG1 = _mm_sha1msg1_epu32(MSG1, MSG2); MSG0 = _mm_xor_si128(MSG0, MSG2); /* Rounds 12-15 */ MSG3 = _mm_loadu_si128(block + 3); MSG3 = _mm_shuffle_epi8(MSG3, MASK); E1 = _mm_sha1nexte_epu32(E1, MSG3); E0 = ABCD; MSG0 = _mm_sha1msg2_epu32(MSG0, MSG3); ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 0); MSG2 = _mm_sha1msg1_epu32(MSG2, MSG3); MSG1 = _mm_xor_si128(MSG1, MSG3); /* Rounds 16-19 */ E0 = _mm_sha1nexte_epu32(E0, MSG0); E1 = ABCD; MSG1 = _mm_sha1msg2_epu32(MSG1, MSG0); ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 0); MSG3 = _mm_sha1msg1_epu32(MSG3, MSG0); MSG2 = _mm_xor_si128(MSG2, MSG0); /* Rounds 20-23 */ E1 = _mm_sha1nexte_epu32(E1, MSG1); E0 = ABCD; MSG2 = _mm_sha1msg2_epu32(MSG2, MSG1); ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 1); MSG0 = _mm_sha1msg1_epu32(MSG0, MSG1); MSG3 = _mm_xor_si128(MSG3, MSG1); /* Rounds 24-27 */ E0 = _mm_sha1nexte_epu32(E0, MSG2); E1 = ABCD; MSG3 = _mm_sha1msg2_epu32(MSG3, MSG2); ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 1); MSG1 = _mm_sha1msg1_epu32(MSG1, MSG2); MSG0 = _mm_xor_si128(MSG0, MSG2); /* Rounds 28-31 */ E1 = _mm_sha1nexte_epu32(E1, MSG3); E0 = ABCD; MSG0 = _mm_sha1msg2_epu32(MSG0, MSG3); ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 1); MSG2 = _mm_sha1msg1_epu32(MSG2, MSG3); MSG1 = _mm_xor_si128(MSG1, MSG3); /* Rounds 32-35 */ E0 = _mm_sha1nexte_epu32(E0, MSG0); E1 = ABCD; MSG1 = _mm_sha1msg2_epu32(MSG1, MSG0); ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 1); MSG3 = _mm_sha1msg1_epu32(MSG3, MSG0); MSG2 = _mm_xor_si128(MSG2, MSG0); /* Rounds 36-39 */ E1 = _mm_sha1nexte_epu32(E1, MSG1); E0 = ABCD; MSG2 = _mm_sha1msg2_epu32(MSG2, MSG1); ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 1); MSG0 = _mm_sha1msg1_epu32(MSG0, MSG1); MSG3 = _mm_xor_si128(MSG3, MSG1); /* Rounds 40-43 */ E0 = _mm_sha1nexte_epu32(E0, MSG2); E1 = ABCD; MSG3 = _mm_sha1msg2_epu32(MSG3, MSG2); ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 2); MSG1 = _mm_sha1msg1_epu32(MSG1, MSG2); MSG0 = _mm_xor_si128(MSG0, MSG2); /* Rounds 44-47 */ E1 = _mm_sha1nexte_epu32(E1, MSG3); E0 = ABCD; MSG0 = _mm_sha1msg2_epu32(MSG0, MSG3); ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 2); MSG2 = _mm_sha1msg1_epu32(MSG2, MSG3); MSG1 = _mm_xor_si128(MSG1, MSG3); /* Rounds 48-51 */ E0 = _mm_sha1nexte_epu32(E0, MSG0); E1 = ABCD; MSG1 = _mm_sha1msg2_epu32(MSG1, MSG0); ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 2); MSG3 = _mm_sha1msg1_epu32(MSG3, MSG0); MSG2 = _mm_xor_si128(MSG2, MSG0); /* Rounds 52-55 */ E1 = _mm_sha1nexte_epu32(E1, MSG1); E0 = ABCD; MSG2 = _mm_sha1msg2_epu32(MSG2, MSG1); ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 2); MSG0 = _mm_sha1msg1_epu32(MSG0, MSG1); MSG3 = _mm_xor_si128(MSG3, MSG1); /* Rounds 56-59 */ E0 = _mm_sha1nexte_epu32(E0, MSG2); E1 = ABCD; MSG3 = _mm_sha1msg2_epu32(MSG3, MSG2); ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 2); MSG1 = _mm_sha1msg1_epu32(MSG1, MSG2); MSG0 = _mm_xor_si128(MSG0, MSG2); /* Rounds 60-63 */ E1 = _mm_sha1nexte_epu32(E1, MSG3); E0 = ABCD; MSG0 = _mm_sha1msg2_epu32(MSG0, MSG3); ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 3); MSG2 = _mm_sha1msg1_epu32(MSG2, MSG3); MSG1 = _mm_xor_si128(MSG1, MSG3); /* Rounds 64-67 */ E0 = _mm_sha1nexte_epu32(E0, MSG0); E1 = ABCD; MSG1 = _mm_sha1msg2_epu32(MSG1, MSG0); ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 3); MSG3 = _mm_sha1msg1_epu32(MSG3, MSG0); MSG2 = _mm_xor_si128(MSG2, MSG0); /* Rounds 68-71 */ E1 = _mm_sha1nexte_epu32(E1, MSG1); E0 = ABCD; MSG2 = _mm_sha1msg2_epu32(MSG2, MSG1); ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 3); MSG3 = _mm_xor_si128(MSG3, MSG1); /* Rounds 72-75 */ E0 = _mm_sha1nexte_epu32(E0, MSG2); E1 = ABCD; MSG3 = _mm_sha1msg2_epu32(MSG3, MSG2); ABCD = _mm_sha1rnds4_epu32(ABCD, E0, 3); /* Rounds 76-79 */ E1 = _mm_sha1nexte_epu32(E1, MSG3); E0 = ABCD; ABCD = _mm_sha1rnds4_epu32(ABCD, E1, 3); /* Combine state */ core[0] = _mm_add_epi32(ABCD, core[0]); core[1] = _mm_sha1nexte_epu32(E0, core[1]); } typedef struct sha1_ni { /* * core[0] stores the first four words of the SHA-1 state. core[1] * stores just the fifth word, in the vector lane at the highest * address. */ __m128i core[2]; sha1_block blk; void *pointer_to_free; BinarySink_IMPLEMENTATION; ssh_hash hash; } sha1_ni; static void sha1_ni_write(BinarySink *bs, const void *vp, size_t len); static sha1_ni *sha1_ni_alloc(void) { /* * The __m128i variables in the context structure need to be * 16-byte aligned, but not all malloc implementations that this * code has to work with will guarantee to return a 16-byte * aligned pointer. So we over-allocate, manually realign the * pointer ourselves, and store the original one inside the * context so we know how to free it later. */ void *allocation = smalloc(sizeof(sha1_ni) + 15); uintptr_t alloc_address = (uintptr_t)allocation; uintptr_t aligned_address = (alloc_address + 15) & ~15; sha1_ni *s = (sha1_ni *)aligned_address; s->pointer_to_free = allocation; return s; } static ssh_hash *sha1_ni_new(const ssh_hashalg *alg) { if (!sha1_hw_available_cached()) return NULL; sha1_ni *s = sha1_ni_alloc(); s->hash.vt = alg; BinarySink_INIT(s, sha1_ni_write); BinarySink_DELEGATE_INIT(&s->hash, s); return &s->hash; } FUNC_ISA static void sha1_ni_reset(ssh_hash *hash) { sha1_ni *s = container_of(hash, sha1_ni, hash); /* Initialise the core vectors in their storage order */ s->core[0] = _mm_set_epi64x( 0x67452301efcdab89ULL, 0x98badcfe10325476ULL); s->core[1] = _mm_set_epi32(0xc3d2e1f0, 0, 0, 0); sha1_block_setup(&s->blk); } static void sha1_ni_copyfrom(ssh_hash *hcopy, ssh_hash *horig) { sha1_ni *copy = container_of(hcopy, sha1_ni, hash); sha1_ni *orig = container_of(horig, sha1_ni, hash); void *ptf_save = copy->pointer_to_free; *copy = *orig; /* structure copy */ copy->pointer_to_free = ptf_save; BinarySink_COPIED(copy); BinarySink_DELEGATE_INIT(©->hash, copy); } static void sha1_ni_free(ssh_hash *hash) { sha1_ni *s = container_of(hash, sha1_ni, hash); void *ptf = s->pointer_to_free; smemclr(s, sizeof(*s)); sfree(ptf); } static void sha1_ni_write(BinarySink *bs, const void *vp, size_t len) { sha1_ni *s = BinarySink_DOWNCAST(bs, sha1_ni); while (len > 0) if (sha1_block_write(&s->blk, &vp, &len)) sha1_ni_block(s->core, s->blk.block); } FUNC_ISA static void sha1_ni_digest(ssh_hash *hash, uint8_t *digest) { sha1_ni *s = container_of(hash, sha1_ni, hash); sha1_block_pad(&s->blk, BinarySink_UPCAST(s)); /* Rearrange the first vector into its output order */ __m128i abcd = _mm_shuffle_epi32(s->core[0], 0x1B); /* Byte-swap it into the output endianness */ const __m128i mask = _mm_setr_epi8(3,2,1,0,7,6,5,4,11,10,9,8,15,14,13,12); abcd = _mm_shuffle_epi8(abcd, mask); /* And store it */ _mm_storeu_si128((__m128i *)digest, abcd); /* Finally, store the leftover word */ uint32_t e = _mm_extract_epi32(s->core[1], 3); PUT_32BIT_MSB_FIRST(digest + 16, e); } const ssh_hashalg ssh_sha1_hw = { .new = sha1_ni_new, .reset = sha1_ni_reset, .copyfrom = sha1_ni_copyfrom, .digest = sha1_ni_digest, .free = sha1_ni_free, .hlen = 20, .blocklen = 64, HASHALG_NAMES_ANNOTATED("SHA-1", "SHA-NI accelerated"), }; /* ---------------------------------------------------------------------- * Hardware-accelerated implementation of SHA-1 using Arm NEON. */ #elif HW_SHA1 == HW_SHA1_NEON /* * Manually set the target architecture, if we decided above that we * need to. */ #ifdef USE_CLANG_ATTR_TARGET_AARCH64 /* * A spot of cheating: redefine some ACLE feature macros before * including arm_neon.h. Otherwise we won't get the SHA intrinsics * defined by that header, because it will be looking at the settings * for the whole translation unit rather than the ones we're going to * put on some particular functions using __attribute__((target)). */ #define __ARM_NEON 1 #define __ARM_FEATURE_CRYPTO 1 #define __ARM_FEATURE_SHA2 1 #define FUNC_ISA __attribute__ ((target("neon,crypto"))) #endif /* USE_CLANG_ATTR_TARGET_AARCH64 */ #ifndef FUNC_ISA #define FUNC_ISA #endif #ifdef USE_ARM64_NEON_H #include #else #include #endif static bool sha1_hw_available(void) { /* * For Arm, we delegate to a per-platform detection function (see * explanation in sshaes.c). */ return platform_sha1_hw_available(); } typedef struct sha1_neon_core sha1_neon_core; struct sha1_neon_core { uint32x4_t abcd; uint32_t e; }; FUNC_ISA static inline uint32x4_t sha1_neon_load_input(const uint8_t *p) { return vreinterpretq_u32_u8(vrev32q_u8(vld1q_u8(p))); } FUNC_ISA static inline uint32x4_t sha1_neon_schedule_update( uint32x4_t m4, uint32x4_t m3, uint32x4_t m2, uint32x4_t m1) { return vsha1su1q_u32(vsha1su0q_u32(m4, m3, m2), m1); } /* * SHA-1 has three different kinds of round, differing in whether they * use the Ch, Maj or Par functions defined above. Each one uses a * separate NEON instruction, so we define three inline functions for * the different round types using this macro. * * The two batches of Par-type rounds also use a different constant, * but that's passed in as an operand, so we don't need a fourth * inline function just for that. */ #define SHA1_NEON_ROUND_FN(type) \ FUNC_ISA static inline sha1_neon_core sha1_neon_round4_##type( \ sha1_neon_core old, uint32x4_t sched, uint32x4_t constant) \ { \ sha1_neon_core new; \ uint32x4_t round_input = vaddq_u32(sched, constant); \ new.abcd = vsha1##type##q_u32(old.abcd, old.e, round_input); \ new.e = vsha1h_u32(vget_lane_u32(vget_low_u32(old.abcd), 0)); \ return new; \ } SHA1_NEON_ROUND_FN(c) SHA1_NEON_ROUND_FN(p) SHA1_NEON_ROUND_FN(m) FUNC_ISA static inline void sha1_neon_block(sha1_neon_core *core, const uint8_t *p) { uint32x4_t constant, s0, s1, s2, s3; sha1_neon_core cr = *core; constant = vdupq_n_u32(SHA1_STAGE0_CONSTANT); s0 = sha1_neon_load_input(p); cr = sha1_neon_round4_c(cr, s0, constant); s1 = sha1_neon_load_input(p + 16); cr = sha1_neon_round4_c(cr, s1, constant); s2 = sha1_neon_load_input(p + 32); cr = sha1_neon_round4_c(cr, s2, constant); s3 = sha1_neon_load_input(p + 48); cr = sha1_neon_round4_c(cr, s3, constant); s0 = sha1_neon_schedule_update(s0, s1, s2, s3); cr = sha1_neon_round4_c(cr, s0, constant); constant = vdupq_n_u32(SHA1_STAGE1_CONSTANT); s1 = sha1_neon_schedule_update(s1, s2, s3, s0); cr = sha1_neon_round4_p(cr, s1, constant); s2 = sha1_neon_schedule_update(s2, s3, s0, s1); cr = sha1_neon_round4_p(cr, s2, constant); s3 = sha1_neon_schedule_update(s3, s0, s1, s2); cr = sha1_neon_round4_p(cr, s3, constant); s0 = sha1_neon_schedule_update(s0, s1, s2, s3); cr = sha1_neon_round4_p(cr, s0, constant); s1 = sha1_neon_schedule_update(s1, s2, s3, s0); cr = sha1_neon_round4_p(cr, s1, constant); constant = vdupq_n_u32(SHA1_STAGE2_CONSTANT); s2 = sha1_neon_schedule_update(s2, s3, s0, s1); cr = sha1_neon_round4_m(cr, s2, constant); s3 = sha1_neon_schedule_update(s3, s0, s1, s2); cr = sha1_neon_round4_m(cr, s3, constant); s0 = sha1_neon_schedule_update(s0, s1, s2, s3); cr = sha1_neon_round4_m(cr, s0, constant); s1 = sha1_neon_schedule_update(s1, s2, s3, s0); cr = sha1_neon_round4_m(cr, s1, constant); s2 = sha1_neon_schedule_update(s2, s3, s0, s1); cr = sha1_neon_round4_m(cr, s2, constant); constant = vdupq_n_u32(SHA1_STAGE3_CONSTANT); s3 = sha1_neon_schedule_update(s3, s0, s1, s2); cr = sha1_neon_round4_p(cr, s3, constant); s0 = sha1_neon_schedule_update(s0, s1, s2, s3); cr = sha1_neon_round4_p(cr, s0, constant); s1 = sha1_neon_schedule_update(s1, s2, s3, s0); cr = sha1_neon_round4_p(cr, s1, constant); s2 = sha1_neon_schedule_update(s2, s3, s0, s1); cr = sha1_neon_round4_p(cr, s2, constant); s3 = sha1_neon_schedule_update(s3, s0, s1, s2); cr = sha1_neon_round4_p(cr, s3, constant); core->abcd = vaddq_u32(core->abcd, cr.abcd); core->e += cr.e; } typedef struct sha1_neon { sha1_neon_core core; sha1_block blk; BinarySink_IMPLEMENTATION; ssh_hash hash; } sha1_neon; static void sha1_neon_write(BinarySink *bs, const void *vp, size_t len); static ssh_hash *sha1_neon_new(const ssh_hashalg *alg) { if (!sha1_hw_available_cached()) return NULL; sha1_neon *s = snew(sha1_neon); s->hash.vt = alg; BinarySink_INIT(s, sha1_neon_write); BinarySink_DELEGATE_INIT(&s->hash, s); return &s->hash; } static void sha1_neon_reset(ssh_hash *hash) { sha1_neon *s = container_of(hash, sha1_neon, hash); s->core.abcd = vld1q_u32(sha1_initial_state); s->core.e = sha1_initial_state[4]; sha1_block_setup(&s->blk); } static void sha1_neon_copyfrom(ssh_hash *hcopy, ssh_hash *horig) { sha1_neon *copy = container_of(hcopy, sha1_neon, hash); sha1_neon *orig = container_of(horig, sha1_neon, hash); *copy = *orig; /* structure copy */ BinarySink_COPIED(copy); BinarySink_DELEGATE_INIT(©->hash, copy); } static void sha1_neon_free(ssh_hash *hash) { sha1_neon *s = container_of(hash, sha1_neon, hash); smemclr(s, sizeof(*s)); sfree(s); } static void sha1_neon_write(BinarySink *bs, const void *vp, size_t len) { sha1_neon *s = BinarySink_DOWNCAST(bs, sha1_neon); while (len > 0) if (sha1_block_write(&s->blk, &vp, &len)) sha1_neon_block(&s->core, s->blk.block); } static void sha1_neon_digest(ssh_hash *hash, uint8_t *digest) { sha1_neon *s = container_of(hash, sha1_neon, hash); sha1_block_pad(&s->blk, BinarySink_UPCAST(s)); vst1q_u8(digest, vrev32q_u8(vreinterpretq_u8_u32(s->core.abcd))); PUT_32BIT_MSB_FIRST(digest + 16, s->core.e); } const ssh_hashalg ssh_sha1_hw = { .new = sha1_neon_new, .reset = sha1_neon_reset, .copyfrom = sha1_neon_copyfrom, .digest = sha1_neon_digest, .free = sha1_neon_free, .hlen = 20, .blocklen = 64, HASHALG_NAMES_ANNOTATED("SHA-1", "NEON accelerated"), }; /* ---------------------------------------------------------------------- * Stub functions if we have no hardware-accelerated SHA-1. In this * case, sha1_hw_new returns NULL (though it should also never be * selected by sha1_select, so the only thing that should even be * _able_ to call it is testcrypt). As a result, the remaining vtable * functions should never be called at all. */ #elif HW_SHA1 == HW_SHA1_NONE static bool sha1_hw_available(void) { return false; } static ssh_hash *sha1_stub_new(const ssh_hashalg *alg) { return NULL; } #define STUB_BODY { unreachable("Should never be called"); } static void sha1_stub_reset(ssh_hash *hash) STUB_BODY static void sha1_stub_copyfrom(ssh_hash *hash, ssh_hash *orig) STUB_BODY static void sha1_stub_free(ssh_hash *hash) STUB_BODY static void sha1_stub_digest(ssh_hash *hash, uint8_t *digest) STUB_BODY const ssh_hashalg ssh_sha1_hw = { .new = sha1_stub_new, .reset = sha1_stub_reset, .copyfrom = sha1_stub_copyfrom, .digest = sha1_stub_digest, .free = sha1_stub_free, .hlen = 20, .blocklen = 64, HASHALG_NAMES_ANNOTATED("SHA-1", "!NONEXISTENT ACCELERATED VERSION!"), }; #endif /* HW_SHA1 */ putty-0.76/sshsha3.c0000644000175000017500000002271314072266312011304 00000000000000/* * SHA-3, as defined in FIPS PUB 202. */ #include #include #include "ssh.h" static inline uint64_t rol(uint64_t x, unsigned shift) { unsigned L = (+shift) & 63; unsigned R = (-shift) & 63; return (x << L) | (x >> R); } /* * General Keccak is defined such that its state is a 5x5 array of * words which can be any power-of-2 size from 1 up to 64. SHA-3 fixes * on 64, and so do we. * * The number of rounds is defined as 12 + 2k if the word size is 2^k. * Here we have 64-bit words only, so k=6, so 24 rounds always. */ typedef uint64_t keccak_core_state[5][5]; #define NROUNDS 24 /* would differ for other word sizes */ static const uint64_t round_constants[NROUNDS]; static const unsigned rotation_counts[5][5]; /* * Core Keccak transform: just squodge the state around internally, * without adding or extracting any data from it. */ static void keccak_transform(keccak_core_state A) { union { uint64_t C[5]; uint64_t B[5][5]; } u; for (unsigned round = 0; round < NROUNDS; round++) { /* theta step */ for (unsigned x = 0; x < 5; x++) u.C[x] = A[x][0] ^ A[x][1] ^ A[x][2] ^ A[x][3] ^ A[x][4]; for (unsigned x = 0; x < 5; x++) { uint64_t D = rol(u.C[(x+1) % 5], 1) ^ u.C[(x+4) % 5]; for (unsigned y = 0; y < 5; y++) A[x][y] ^= D; } /* rho and pi steps */ for (unsigned x = 0; x < 5; x++) for (unsigned y = 0; y < 5; y++) u.B[y][(2*x+3*y) % 5] = rol(A[x][y], rotation_counts[x][y]); /* chi step */ for (unsigned x = 0; x < 5; x++) for (unsigned y = 0; y < 5; y++) A[x][y] = u.B[x][y] ^ (u.B[(x+2)%5][y] & ~u.B[(x+1)%5][y]); /* iota step */ A[0][0] ^= round_constants[round]; } smemclr(&u, sizeof(u)); } typedef struct { keccak_core_state A; unsigned char bytes[25*8]; unsigned char first_pad_byte; size_t bytes_got, bytes_wanted, hash_bytes; } keccak_state; /* * Keccak accumulation function: given a piece of message, add it to * the hash. */ static void keccak_accumulate(keccak_state *s, const void *vdata, size_t len) { const unsigned char *data = (const unsigned char *)vdata; while (len >= s->bytes_wanted - s->bytes_got) { size_t b = s->bytes_wanted - s->bytes_got; memcpy(s->bytes + s->bytes_got, data, b); len -= b; data += b; size_t n = 0; for (unsigned y = 0; y < 5; y++) { for (unsigned x = 0; x < 5; x++) { if (n >= s->bytes_wanted) break; s->A[x][y] ^= GET_64BIT_LSB_FIRST(s->bytes + n); n += 8; } } keccak_transform(s->A); s->bytes_got = 0; } memcpy(s->bytes + s->bytes_got, data, len); s->bytes_got += len; } /* * Keccak output function. */ static void keccak_output(keccak_state *s, void *voutput) { unsigned char *output = (unsigned char *)voutput; /* * Add message padding. */ { unsigned char padding[25*8]; size_t len = s->bytes_wanted - s->bytes_got; if (len == 0) len = s->bytes_wanted; memset(padding, 0, len); padding[0] |= s->first_pad_byte; padding[len-1] |= 0x80; keccak_accumulate(s, padding, len); } size_t n = 0; for (unsigned y = 0; y < 5; y++) { for (unsigned x = 0; x < 5; x++) { size_t to_copy = s->hash_bytes - n; if (to_copy == 0) break; if (to_copy > 8) to_copy = 8; unsigned char outbytes[8]; PUT_64BIT_LSB_FIRST(outbytes, s->A[x][y]); memcpy(output + n, outbytes, to_copy); n += to_copy; } } } static void keccak_init(keccak_state *s, unsigned hashbits, unsigned ratebits, unsigned char first_pad_byte) { int x, y; assert(hashbits % 8 == 0); assert(ratebits % 8 == 0); s->hash_bytes = hashbits / 8; s->bytes_wanted = (25 * 64 - ratebits) / 8; s->bytes_got = 0; s->first_pad_byte = first_pad_byte; assert(s->bytes_wanted % 8 == 0); for (y = 0; y < 5; y++) for (x = 0; x < 5; x++) s->A[x][y] = 0; } static void keccak_sha3_init(keccak_state *s, int hashbits) { keccak_init(s, hashbits, hashbits * 2, 0x06); } static void keccak_shake_init(keccak_state *s, int parambits, int hashbits) { keccak_init(s, hashbits, parambits * 2, 0x1f); } /* * Keccak round constants, generated via the LFSR specified in the * Keccak reference by the following piece of Python: import textwrap from functools import reduce rbytes = [1] while len(rbytes) < 7*24: k = rbytes[-1] * 2 rbytes.append(k ^ (0x171 * (k >> 8))) rbits = [byte & 1 for byte in rbytes] rwords = [sum(rbits[i+j] << ((1 << j) - 1) for j in range(7)) for i in range(0, len(rbits), 7)] print(textwrap.indent("\n".join(textwrap.wrap(", ".join( map("0x{:016x}".format, rwords)))), " "*4)) */ static const uint64_t round_constants[24] = { 0x0000000000000001, 0x0000000000008082, 0x800000000000808a, 0x8000000080008000, 0x000000000000808b, 0x0000000080000001, 0x8000000080008081, 0x8000000000008009, 0x000000000000008a, 0x0000000000000088, 0x0000000080008009, 0x000000008000000a, 0x000000008000808b, 0x800000000000008b, 0x8000000000008089, 0x8000000000008003, 0x8000000000008002, 0x8000000000000080, 0x000000000000800a, 0x800000008000000a, 0x8000000080008081, 0x8000000000008080, 0x0000000080000001, 0x8000000080008008 }; /* * Keccak per-element rotation counts, generated from the matrix * formula in the Keccak reference by the following piece of Python: coords = [1, 0] while len(coords) < 26: coords.append((2*coords[-2] + 3*coords[-1]) % 5) matrix = { (coords[i], coords[i+1]) : i for i in range(24) } matrix[0,0] = -1 f = lambda t: (t+1) * (t+2) // 2 % 64 for y in range(5): print(" {{{}}},".format(", ".join("{:2d}".format(f(matrix[y,x])) for x in range(5)))) */ static const unsigned rotation_counts[5][5] = { { 0, 36, 3, 41, 18}, { 1, 44, 10, 45, 2}, {62, 6, 43, 15, 61}, {28, 55, 25, 21, 56}, {27, 20, 39, 8, 14}, }; /* * The PuTTY ssh_hashalg abstraction. */ struct keccak_hash { keccak_state state; ssh_hash hash; BinarySink_IMPLEMENTATION; }; static void keccak_BinarySink_write(BinarySink *bs, const void *p, size_t len) { struct keccak_hash *kh = BinarySink_DOWNCAST(bs, struct keccak_hash); keccak_accumulate(&kh->state, p, len); } static ssh_hash *keccak_new(const ssh_hashalg *alg) { struct keccak_hash *kh = snew(struct keccak_hash); kh->hash.vt = alg; BinarySink_INIT(kh, keccak_BinarySink_write); BinarySink_DELEGATE_INIT(&kh->hash, kh); return ssh_hash_reset(&kh->hash); } static void keccak_free(ssh_hash *hash) { struct keccak_hash *kh = container_of(hash, struct keccak_hash, hash); smemclr(kh, sizeof(*kh)); sfree(kh); } static void keccak_copyfrom(ssh_hash *hnew, ssh_hash *hold) { struct keccak_hash *khold = container_of(hold, struct keccak_hash, hash); struct keccak_hash *khnew = container_of(hnew, struct keccak_hash, hash); khnew->state = khold->state; } static void keccak_digest(ssh_hash *hash, unsigned char *output) { struct keccak_hash *kh = container_of(hash, struct keccak_hash, hash); keccak_output(&kh->state, output); } static void sha3_reset(ssh_hash *hash) { struct keccak_hash *kh = container_of(hash, struct keccak_hash, hash); keccak_sha3_init(&kh->state, hash->vt->hlen * 8); } #define DEFINE_SHA3(bits) \ const ssh_hashalg ssh_sha3_##bits = { \ .new = keccak_new, \ .reset = sha3_reset, \ .copyfrom = keccak_copyfrom, \ .digest = keccak_digest, \ .free = keccak_free, \ .hlen = bits/8, \ .blocklen = 200 - 2*(bits/8), \ HASHALG_NAMES_BARE("SHA3-" #bits), \ } DEFINE_SHA3(224); DEFINE_SHA3(256); DEFINE_SHA3(384); DEFINE_SHA3(512); static void shake256_reset(ssh_hash *hash) { struct keccak_hash *kh = container_of(hash, struct keccak_hash, hash); keccak_shake_init(&kh->state, 256, hash->vt->hlen * 8); } /* * There is some confusion over the output length parameter for the * SHAKE functions. By my reading, FIPS PUB 202 defines SHAKE256(M,d) * to generate d _bits_ of output. But RFC 8032 (defining Ed448) talks * about "SHAKE256(x,114)" in a context where it definitely means * generating 114 _bytes_ of output. * * Our internal ID therefore suffixes the output length with "bytes", * to be clear which we're talking about */ #define DEFINE_SHAKE(param, hashbytes) \ const ssh_hashalg ssh_shake##param##_##hashbytes##bytes = { \ .new = keccak_new, \ .reset = shake##param##_reset, \ .copyfrom = keccak_copyfrom, \ .digest = keccak_digest, \ .free = keccak_free, \ .hlen = hashbytes, \ .blocklen = 0, \ HASHALG_NAMES_BARE("SHAKE" #param), \ } DEFINE_SHAKE(256, 114); putty-0.76/sshshare.c0000644000175000017500000023065414072266312011555 00000000000000/* * 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 * * -- [comments] \r\n * * The only difference is that in real SSH-2, 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 #include #include #include #include #include "putty.h" #include "tree234.h" #include "ssh.h" #include "sshcr.h" struct ssh_sharing_state { 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 */ ConnectionLayer *cl; /* instance of the ssh connection layer */ char *server_verstring; /* server version string after "SSH-" */ Plug plug; }; struct share_globreq; struct ssh_sharing_connstate { 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 */ bool sent_verstring, got_verstring; int curr_packetlen; unsigned char recvbuf[0x4010]; size_t 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; Plug plug; }; 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; bool x11_one_shot; }; struct share_forwarding { char *host; int port; bool active; /* has the server sent REQUEST_SUCCESS? */ struct ssh_rportfwd *rpf; }; 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. */ bool 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; bool 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); } if (cs->sock) sk_close(cs->sock); sfree(cs); } void sharestate_free(ssh_sharing_state *sharestate) { 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); if (sharestate->listensock) { sk_close(sharestate->listensock); sharestate->listensock = NULL; } 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 = false; 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_remove_sharing_x11_display(cs->parent->cl, 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 PRINTF_LIKE(2, 3) void log_downstream(struct ssh_sharing_connstate *cs, const char *logfmt, ...) { va_list ap; char *buf; va_start(ap, logfmt); buf = dupvprintf(logfmt, ap); va_end(ap); logeventf(cs->parent->cl->logctx, "Connection sharing downstream #%u: %s", cs->id, buf); sfree(buf); } static PRINTF_LIKE(2, 3) void log_general(struct ssh_sharing_state *sharestate, const char *logfmt, ...) { va_list ap; char *buf; va_start(ap, logfmt); buf = dupvprintf(logfmt, ap); va_end(ap); logeventf(sharestate->cl->logctx, "Connection sharing: %s", buf); sfree(buf); } static void send_packet_to_downstream(struct ssh_sharing_connstate *cs, int type, const void *pkt, int pktlen, struct share_channel *chan) { strbuf *packet; 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. */ BinarySource src[1]; unsigned channel; ptrlen data; BinarySource_BARE_INIT(src, pkt, pktlen); channel = get_uint32(src); data = get_string(src); do { int this_len = (data.len > chan->downstream_maxpkt ? chan->downstream_maxpkt : data.len); packet = strbuf_new_nm(); put_uint32(packet, 0); /* placeholder for length field */ put_byte(packet, type); put_uint32(packet, channel); put_uint32(packet, this_len); put_data(packet, data.ptr, this_len); data.ptr = (const char *)data.ptr + this_len; data.len -= this_len; PUT_32BIT_MSB_FIRST(packet->s, packet->len-4); sk_write(cs->sock, packet->s, packet->len); strbuf_free(packet); } while (data.len > 0); } else { /* * Just do the obvious thing. */ packet = strbuf_new_nm(); put_uint32(packet, 0); /* placeholder for length field */ put_byte(packet, type); put_data(packet, pkt, pktlen); PUT_32BIT_MSB_FIRST(packet->s, packet->len-4); sk_write(cs->sock, packet->s, packet->len); strbuf_free(packet); } } 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"; strbuf *packet; packet = strbuf_new(); put_uint32(packet, hc->server_id); put_uint32(packet, SSH2_OPEN_CONNECT_FAILED); put_stringz(packet, reason); put_stringz(packet, lang); ssh_send_packet_from_downstream( cs->parent->cl, cs->id, SSH2_MSG_CHANNEL_OPEN_FAILURE, packet->s, packet->len, "cleanup after downstream went away"); strbuf_free(packet); 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++) { strbuf *packet; if (chan->state != SENT_CLOSE && chan->state != UNACKNOWLEDGED) { packet = strbuf_new(); put_uint32(packet, chan->server_id); ssh_send_packet_from_downstream( cs->parent->cl, cs->id, SSH2_MSG_CHANNEL_CLOSE, packet->s, packet->len, "cleanup after downstream went away"); strbuf_free(packet); 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->cl, 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) { strbuf *packet = strbuf_new(); put_stringz(packet, "cancel-tcpip-forward"); put_bool(packet, false); /* !want_reply */ put_stringz(packet, fwd->host); put_uint32(packet, fwd->port); ssh_send_packet_from_downstream( cs->parent->cl, cs->id, SSH2_MSG_GLOBAL_REQUEST, packet->s, packet->len, "cleanup after downstream went away"); strbuf_free(packet); ssh_rportfwd_remove(cs->parent->cl, fwd->rpf); 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) { struct ssh_sharing_state *sharestate = cs->parent; /* * Now we're _really_ done, so we can get rid of cs completely. */ del234(sharestate->connections, cs); log_downstream(cs, "disconnected"); share_connstate_free(cs); /* * And if this was the last downstream, notify the connection * layer, because it might now be time to wind up the whole * SSH connection. */ if (count234(sharestate->connections) == 0 && sharestate->cl) ssh_sharing_no_more_downstreams(sharestate->cl); } } 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) { strbuf *packet = strbuf_new(); put_uint32(packet, SSH2_DISCONNECT_PROTOCOL_ERROR); put_stringz(packet, message); put_stringz(packet, "en"); /* language */ send_packet_to_downstream(cs, SSH2_MSG_DISCONNECT, packet->s, packet->len, NULL); strbuf_free(packet); share_begin_cleanup(cs); } static void share_closing(Plug *plug, const char *error_msg, int error_code, bool calling_back) { struct ssh_sharing_connstate *cs = container_of( plug, struct ssh_sharing_connstate, plug); if (error_msg) { #ifdef BROKEN_PIPE_ERROR_CODE /* * Most of the time, we log what went wrong when a downstream * disappears with a socket error. One exception, though, is * receiving EPIPE when we haven't received a protocol version * string from the downstream, because that can happen as a result * of plink -shareexists (opening the connection and instantly * closing it again without bothering to read our version string). * So that one case is not treated as a log-worthy error. */ if (error_code == BROKEN_PIPE_ERROR_CODE && !cs->got_verstring) /* do nothing */; else #endif log_downstream(cs, "Socket error: %s", error_msg); } share_begin_cleanup(cs); } /* * Append a message to the end of an xchannel's queue. */ static void share_xchannel_add_message( struct share_xchannel *xc, int type, const void *data, int len) { struct share_xchannel_message *msg; /* * Allocate the 'struct share_xchannel_message' and the actual * data in one unit. */ msg = snew_plus(struct share_xchannel_message, len); msg->data = snew_plus_get_aux(msg); msg->datalen = len; msg->type = type; memcpy(msg->data, data, len); /* * Queue it in the xchannel. */ if (xc->msgtail) xc->msgtail->next = msg; else xc->msghead = msg; msg->next = NULL; xc->msgtail = 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). */ bool 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. */ BinarySource src[1]; BinarySource_BARE_INIT(src, msg->data, msg->datalen); get_uint32(src); /* skip channel id */ get_string(src); /* skip request type */ if (get_bool(src)) { strbuf *packet = strbuf_new(); put_uint32(packet, xc->server_id); ssh_send_packet_from_downstream (cs->parent->cl, cs->id, SSH2_MSG_CHANNEL_FAILURE, packet->s, packet->len, "downstream refused X channel open"); strbuf_free(packet); } } 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->cl, 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) { strbuf *packet; /* * 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_MSB_FIRST(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. */ packet = strbuf_new(); put_uint32(packet, xc->server_id); put_uint32(packet, downstream_window - xc->window); ssh_send_packet_from_downstream( cs->parent->cl, cs->id, SSH2_MSG_CHANNEL_WINDOW_ADJUST, packet->s, packet->len, "window adjustment after downstream accepted X channel"); strbuf_free(packet); } 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. */ strbuf *packet = strbuf_new(); put_uint32(packet, xc->server_id); ssh_send_packet_from_downstream( cs->parent->cl, cs->id, SSH2_MSG_CHANNEL_CLOSE, packet->s, packet->len, "downstream refused X channel open"); strbuf_free(packet); /* * 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(ssh_sharing_connstate *cs, share_channel *chan, 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 share_xchannel *xc; void *greeting; int greeting_len; strbuf *packet; /* * 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); packet = strbuf_new_nm(); put_uint32(packet, 0); /* leave the channel id field unfilled - we * don't know the downstream id yet */ put_uint32(packet, greeting_len + initial_len); put_data(packet, greeting, greeting_len); put_data(packet, initial_data, initial_len); sfree(greeting); share_xchannel_add_message(xc, SSH2_MSG_CHANNEL_DATA, packet->s, packet->len); strbuf_free(packet); xc->window = client_adjusted_window + greeting_len; /* * Send on a CHANNEL_OPEN to downstream. */ packet = strbuf_new(); put_stringz(packet, "x11"); put_uint32(packet, server_id); put_uint32(packet, server_currwin); put_uint32(packet, server_maxpkt); put_stringz(packet, peer_addr); put_uint32(packet, peer_port); send_packet_to_downstream(cs, SSH2_MSG_CHANNEL_OPEN, packet->s, packet->len, NULL); strbuf_free(packet); /* * If this was a once-only X forwarding, clean it up now. */ if (chan->x11_one_shot) { ssh_remove_sharing_x11_display(cs->parent->cl, 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 = false; } } void share_got_pkt_from_server(ssh_sharing_connstate *cs, int type, const void *vpkt, int pktlen) { const unsigned char *pkt = (const unsigned char *)vpkt; struct share_globreq *globreq; size_t id_pos; unsigned upstream_id, server_id; struct share_channel *chan; struct share_xchannel *xc; BinarySource src[1]; BinarySource_BARE_INIT(src, pkt, pktlen); switch (type) { case SSH2_MSG_REQUEST_SUCCESS: case SSH2_MSG_REQUEST_FAILURE: globreq = cs->globreq_head; assert(globreq); /* should match the queue in ssh.c */ 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: get_string(src); server_id = get_uint32(src); assert(!get_err(src)); 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. */ id_pos = src->pos; upstream_id = get_uint32(src); if ((chan = share_find_channel_by_upstream(cs, upstream_id)) != NULL) { /* * The normal case: this id refers to an open channel. */ unsigned char *rewritten = snewn(pktlen, unsigned char); memcpy(rewritten, pkt, pktlen); PUT_32BIT_MSB_FIRST(rewritten + id_pos, chan->downstream_id); send_packet_to_downstream(cs, type, rewritten, pktlen, chan); sfree(rewritten); /* * 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_MSB_FIRST(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->cl, 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->cl, 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. */ share_xchannel_add_message(xc, type, 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: unreachable("This packet type should never have come from ssh.c"); } } static void share_got_pkt_from_downstream(struct ssh_sharing_connstate *cs, int type, unsigned char *pkt, int pktlen) { ptrlen request_name; struct share_forwarding *fwd; size_t id_pos; unsigned maxpkt; unsigned old_id, new_id, server_id; struct share_globreq *globreq; struct share_channel *chan; struct share_halfchannel *hc; struct share_xchannel *xc; strbuf *packet; char *err = NULL; BinarySource src[1]; size_t wantreplypos; bool orig_wantreply; BinarySource_BARE_INIT(src, pkt, pktlen); 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 = get_string(src); wantreplypos = src->pos; orig_wantreply = get_bool(src); if (ptrlen_eq_string(request_name, "tcpip-forward")) { ptrlen hostpl; char *host; int port; struct ssh_rportfwd *rpf; /* * Pick the packet apart to find the want_reply field and * the host/port we're going to ask to listen on. */ hostpl = get_string(src); port = toint(get_uint32(src)); if (get_err(src)) { err = dupprintf("Truncated GLOBAL_REQUEST packet"); goto confused; } host = mkstr(hostpl); /* * 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. */ rpf = ssh_rportfwd_alloc( cs->parent->cl, host, port, NULL, 0, 0, NULL, NULL, cs); if (!rpf) { 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. */ pkt[wantreplypos] = 1; ssh_send_packet_from_downstream (cs->parent->cl, cs->id, type, pkt, pktlen, orig_wantreply ? NULL : "upstream added want_reply flag"); fwd = share_add_forwarding(cs, host, port); ssh_sharing_queue_global_request(cs->parent->cl, 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; fwd->rpf = rpf; } } sfree(host); } else if (ptrlen_eq_string(request_name, "cancel-tcpip-forward")) { ptrlen hostpl; char *host; int port; struct share_forwarding *fwd; /* * Pick the packet apart to find the want_reply field and * the host/port we're going to ask to listen on. */ hostpl = get_string(src); port = toint(get_uint32(src)); if (get_err(src)) { err = dupprintf("Truncated GLOBAL_REQUEST packet"); goto confused; } host = mkstr(hostpl); /* * 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 { /* * Tell ssh.c to stop sending us channel-opens for * this forwarding. */ ssh_rportfwd_remove(cs->parent->cl, fwd->rpf); /* * 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. */ pkt[wantreplypos] = 1; ssh_send_packet_from_downstream (cs->parent->cl, cs->id, type, pkt, pktlen, orig_wantreply ? NULL : "upstream added want_reply flag"); ssh_sharing_queue_global_request(cs->parent->cl, cs); /* * And queue a globreq so that when the reply comes * back we know to cancel it. */ 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_CANCEL_TCPIP_FORWARD; } sfree(host); } else { /* * Request we don't understand. Manufacture a failure * message if an answer was required. */ if (orig_wantreply) 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 */ get_string(src); id_pos = src->pos; old_id = get_uint32(src); new_id = ssh_alloc_sharing_channel(cs->parent->cl, cs); get_uint32(src); /* skip initial window size */ maxpkt = get_uint32(src); if (get_err(src)) { err = dupprintf("Truncated CHANNEL_OPEN packet"); goto confused; } share_add_channel(cs, old_id, new_id, 0, UNACKNOWLEDGED, maxpkt); PUT_32BIT_MSB_FIRST(pkt + id_pos, new_id); ssh_send_packet_from_downstream(cs->parent->cl, 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; } server_id = get_uint32(src); id_pos = src->pos; old_id = get_uint32(src); get_uint32(src); /* skip initial window size */ maxpkt = get_uint32(src); if (get_err(src)) { err = dupprintf("Truncated CHANNEL_OPEN_CONFIRMATION packet"); goto confused; } /* 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->cl, 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_MSB_FIRST(pkt + id_pos, new_id); chan = share_add_channel(cs, old_id, new_id, server_id, OPEN, maxpkt); if (hc) { ssh_send_packet_from_downstream(cs->parent->cl, cs->id, type, pkt, pktlen, NULL); share_remove_halfchannel(cs, hc); } else if (xc) { unsigned downstream_window = GET_32BIT_MSB_FIRST(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: server_id = get_uint32(src); if (get_err(src)) { err = dupprintf("Truncated CHANNEL_OPEN_FAILURE packet"); goto confused; } /* 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->cl, 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: server_id = get_uint32(src); if (type == SSH2_MSG_CHANNEL_REQUEST) { request_name = get_string(src); /* * 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 (ptrlen_eq_string(request_name, "auth-agent-req@openssh.com") && !ssh_agent_forwarding_permitted(cs->parent->cl)) { chan = share_find_channel_by_server(cs, server_id); if (chan) { packet = strbuf_new(); put_uint32(packet, chan->downstream_id); send_packet_to_downstream( cs, SSH2_MSG_CHANNEL_FAILURE, packet->s, packet->len, NULL); strbuf_free(packet); } 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 (ptrlen_eq_string(request_name, "x11-req")) { bool want_reply, single_connection; int screen; ptrlen auth_data; int auth_proto; 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. */ want_reply = get_bool(src); single_connection = get_bool(src); auth_proto = x11_identify_auth_proto(get_string(src)); auth_data = get_string(src); screen = toint(get_uint32(src)); if (get_err(src)) { err = dupprintf("Truncated CHANNEL_REQUEST(\"x11-req\")" " packet"); goto confused; } if (auth_proto < 0) { /* Reject due to not understanding downstream's * requested authorisation method. */ packet = strbuf_new(); put_uint32(packet, chan->downstream_id); send_packet_to_downstream( cs, SSH2_MSG_CHANNEL_FAILURE, packet->s, packet->len, NULL); strbuf_free(packet); break; } chan->x11_auth_proto = auth_proto; chan->x11_auth_data = x11_dehexify(auth_data, &chan->x11_auth_datalen); chan->x11_auth_upstream = ssh_add_sharing_x11_display(cs->parent->cl, 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. */ packet = strbuf_new_nm(); put_uint32(packet, server_id); put_stringz(packet, "x11-req"); put_bool(packet, want_reply); put_bool(packet, single_connection); put_stringz(packet, chan->x11_auth_upstream->protoname); put_stringz(packet, chan->x11_auth_upstream->datastring); put_uint32(packet, screen); ssh_send_packet_from_downstream( cs->parent->cl, cs->id, SSH2_MSG_CHANNEL_REQUEST, packet->s, packet->len, NULL); strbuf_free(packet); break; } } ssh_send_packet_from_downstream(cs->parent->cl, cs->id, type, pkt, pktlen, NULL); if (type == SSH2_MSG_CHANNEL_CLOSE && pktlen >= 4) { chan = share_find_channel_by_server(cs, server_id); if (chan) { if (chan->state == RCVD_CLOSE) { ssh_delete_sharing_channel(cs->parent->cl, 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; } } /* * An extra coroutine macro, specific to this code which is consuming * 'const char *data'. */ #define crGetChar(c) do \ { \ while (len == 0) { \ *crLine =__LINE__; return; case __LINE__:; \ } \ len--; \ (c) = (unsigned char)*data++; \ } while (0) static void share_receive(Plug *plug, int urgent, const char *data, size_t len) { ssh_sharing_connstate *cs = container_of( plug, 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 */ ptrlen verstring = make_ptrlen(cs->recvbuf, cs->recvlen); log_downstream(cs, "Downstream version string: %.*s", PTRLEN_PRINTF(verstring)); cs->got_verstring = true; /* * 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_MSB_FIRST(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:; crFinishV; } static void share_sent(Plug *plug, size_t bufsize) { /* ssh_sharing_connstate *cs = container_of( plug, 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 void share_listen_closing(Plug *plug, const char *error_msg, int error_code, bool calling_back) { ssh_sharing_state *sharestate = container_of(plug, ssh_sharing_state, plug); if (error_msg) log_general(sharestate, "listening socket: %s", error_msg); sk_close(sharestate->listensock); sharestate->listensock = NULL; } static void share_send_verstring(ssh_sharing_connstate *cs) { char *fullstring = dupcat("SSHCONNECTION@putty.projects.tartarus.org-2.0-", cs->parent->server_verstring, "\015\012"); sk_write(cs->sock, fullstring, strlen(fullstring)); sfree(fullstring); cs->sent_verstring = true; } int share_ndownstreams(ssh_sharing_state *sharestate) { return count234(sharestate->connections); } void share_activate(ssh_sharing_state *sharestate, 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_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 const PlugVtable ssh_sharing_conn_plugvt = { .closing = share_closing, .receive = share_receive, .sent = share_sent, }; static int share_listen_accepting(Plug *plug, accept_fn_t constructor, accept_ctx_t ctx) { struct ssh_sharing_state *sharestate = container_of( plug, struct ssh_sharing_state, plug); struct ssh_sharing_connstate *cs; const char *err; SocketPeerInfo *peerinfo; /* * A new downstream has connected to us. */ cs = snew(struct ssh_sharing_connstate); cs->plug.vt = &ssh_sharing_conn_plugvt; 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, &cs->plug); if ((err = sk_socket_error(cs->sock)) != NULL) { sfree(cs); return err != NULL; } sk_set_frozen(cs->sock, false); 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; peerinfo = sk_peer_info(cs->sock); log_downstream(cs, "connected%s%s", (peerinfo && peerinfo->log_text ? " from " : ""), (peerinfo && peerinfo->log_text ? peerinfo->log_text : "")); sk_free_peer_info(peerinfo); return 0; } /* * 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. * * The per-platform code will quite likely hash or obfuscate this name * in turn, for privacy from other users; failing that, it might * transform it to avoid dangerous filename characters and so on. But * that doesn't matter to us: for us, the point is that two session * configurations which return the same string from this function will * be treated as potentially shareable with each other. */ char *ssh_share_sockname(const char *host, int port, Conf *conf) { char *username = NULL; char *sockname; /* Include the username we're logging in as in the hash, unless * we're using a protocol for which it's completely irrelevant. */ if (conf_get_int(conf, CONF_protocol) != PROT_SSHCONN) 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); return sockname; } bool ssh_share_test_for_upstream(const char *host, int port, Conf *conf) { char *sockname, *logtext, *ds_err, *us_err; int result; Socket *sock; sockname = ssh_share_sockname(host, port, conf); sock = NULL; logtext = ds_err = us_err = NULL; result = platform_ssh_share(sockname, conf, nullplug, (Plug *)NULL, &sock, &logtext, &ds_err, &us_err, false, true); sfree(logtext); sfree(ds_err); sfree(us_err); sfree(sockname); if (result == SHARE_NONE) { assert(sock == NULL); return false; } else { assert(result == SHARE_DOWNSTREAM); sk_close(sock); return true; } } static const PlugVtable ssh_sharing_listen_plugvt = { .closing = share_listen_closing, .accepting = share_listen_accepting, }; void ssh_connshare_provide_connlayer(ssh_sharing_state *sharestate, ConnectionLayer *cl) { sharestate->cl = cl; } /* * 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, LogContext *logctx, Plug *sshplug, ssh_sharing_state **state) { int result; bool can_upstream, can_downstream; char *logtext, *ds_err, *us_err; char *sockname; Socket *sock, *toret = NULL; struct ssh_sharing_state *sharestate; if (!conf_get_bool(conf, CONF_ssh_connection_sharing)) return NULL; /* do not share anything */ can_upstream = share_can_be_upstream && conf_get_bool(conf, CONF_ssh_connection_sharing_upstream); can_downstream = share_can_be_downstream && conf_get_bool(conf, CONF_ssh_connection_sharing_downstream); if (!can_upstream && !can_downstream) return NULL; sockname = ssh_share_sockname(host, port, conf); /* * Create a data structure for the listening plug if we turn out * to be an upstream. */ sharestate = snew(struct ssh_sharing_state); sharestate->plug.vt = &ssh_sharing_listen_plugvt; sharestate->listensock = NULL; sharestate->cl = 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, sshplug, &sharestate->plug, &sock, &logtext, &ds_err, &us_err, can_upstream, can_downstream); 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. */ if (logtext) { /* For this result, if 'logtext' is not NULL then it is an * error message indicating a reason why connection sharing * couldn't be set up _at all_ */ logeventf(logctx, "Could not set up connection sharing: %s", logtext); } else { /* Failing that, ds_err and us_err indicate why we * couldn't be a downstream and an upstream respectively */ if (ds_err) logeventf(logctx, "Could not set up connection sharing" " as downstream: %s", ds_err); if (us_err) logeventf(logctx, "Could not set up connection sharing" " as upstream: %s", us_err); } assert(sock == NULL); *state = NULL; sfree(sharestate); sfree(sockname); break; 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. */ /* 'logtext' is a local endpoint address */ logeventf(logctx, "Using existing shared connection at %s", logtext); *state = NULL; sfree(sharestate); sfree(sockname); toret = sock; break; 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. */ /* 'logtext' is a local endpoint address */ logeventf(logctx, "Sharing this connection at %s", logtext); *state = sharestate; sharestate->listensock = sock; sharestate->connections = newtree234(share_connstate_cmp); sharestate->server_verstring = NULL; sharestate->sockname = sockname; sharestate->nextid = 1; break; } sfree(logtext); sfree(ds_err); sfree(us_err); return toret; } putty-0.76/sshsignals.h0000644000175000017500000000304214072266312012105 00000000000000/* * List of signal names known to SSH, indicating whether PuTTY's UI * for special session commands likes to put them in the main specials * menu or in a submenu (and if the former, what title they have). * * This is a separate header file rather than my usual style of a * parametric list macro, because in this case I need to be able to * #ifdef out each mode in case it's not defined on a particular * target system. * * If you want only the locally defined signals, #define * SIGNALS_LOCAL_ONLY before including this header. */ #if !defined SIGNALS_LOCAL_ONLY || defined SIGINT SIGNAL_MAIN(INT, "Interrupt") #endif #if !defined SIGNALS_LOCAL_ONLY || defined SIGTERM SIGNAL_MAIN(TERM, "Terminate") #endif #if !defined SIGNALS_LOCAL_ONLY || defined SIGKILL SIGNAL_MAIN(KILL, "Kill") #endif #if !defined SIGNALS_LOCAL_ONLY || defined SIGQUIT SIGNAL_MAIN(QUIT, "Quit") #endif #if !defined SIGNALS_LOCAL_ONLY || defined SIGHUP SIGNAL_MAIN(HUP, "Hangup") #endif #if !defined SIGNALS_LOCAL_ONLY || defined SIGABRT SIGNAL_SUB(ABRT) #endif #if !defined SIGNALS_LOCAL_ONLY || defined SIGALRM SIGNAL_SUB(ALRM) #endif #if !defined SIGNALS_LOCAL_ONLY || defined SIGFPE SIGNAL_SUB(FPE) #endif #if !defined SIGNALS_LOCAL_ONLY || defined SIGILL SIGNAL_SUB(ILL) #endif #if !defined SIGNALS_LOCAL_ONLY || defined SIGPIPE SIGNAL_SUB(PIPE) #endif #if !defined SIGNALS_LOCAL_ONLY || defined SIGSEGV SIGNAL_SUB(SEGV) #endif #if !defined SIGNALS_LOCAL_ONLY || defined SIGUSR1 SIGNAL_SUB(USR1) #endif #if !defined SIGNALS_LOCAL_ONLY || defined SIGUSR2 SIGNAL_SUB(USR2) #endif putty-0.76/sshttymodes.h0000644000175000017500000001276414072266312012330 00000000000000/* * List of SSH terminal modes, indicating whether SSH types them as * char or boolean, and if they're boolean, which POSIX flags field of * a termios structure they appear in, and what bit mask removes them * (e.g. CS7 and CS8 aren't single bits). * * Sources: RFC 4254, SSH-1 RFC-1.2.31, POSIX 2017, and the Linux * termios manpage for flags not specified by POSIX. * * This is a separate header file rather than my usual style of a * parametric list macro, because in this case I need to be able to * #ifdef out each mode in case it's not defined on a particular * target system. * * If you want only the locally defined modes, #define * TTYMODES_LOCAL_ONLY before including this header. */ #if !defined TTYMODES_LOCAL_ONLY || defined VINTR TTYMODE_CHAR(INTR, 1, VINTR) #endif #if !defined TTYMODES_LOCAL_ONLY || defined VQUIT TTYMODE_CHAR(QUIT, 2, VQUIT) #endif #if !defined TTYMODES_LOCAL_ONLY || defined VERASE TTYMODE_CHAR(ERASE, 3, VERASE) #endif #if !defined TTYMODES_LOCAL_ONLY || defined VKILL TTYMODE_CHAR(KILL, 4, VKILL) #endif #if !defined TTYMODES_LOCAL_ONLY || defined VEOF TTYMODE_CHAR(EOF, 5, VEOF) #endif #if !defined TTYMODES_LOCAL_ONLY || defined VEOL TTYMODE_CHAR(EOL, 6, VEOL) #endif #if !defined TTYMODES_LOCAL_ONLY || defined VEOL2 TTYMODE_CHAR(EOL2, 7, VEOL2) #endif #if !defined TTYMODES_LOCAL_ONLY || defined VSTART TTYMODE_CHAR(START, 8, VSTART) #endif #if !defined TTYMODES_LOCAL_ONLY || defined VSTOP TTYMODE_CHAR(STOP, 9, VSTOP) #endif #if !defined TTYMODES_LOCAL_ONLY || defined VSUSP TTYMODE_CHAR(SUSP, 10, VSUSP) #endif #if !defined TTYMODES_LOCAL_ONLY || defined VDSUSP TTYMODE_CHAR(DSUSP, 11, VDSUSP) #endif #if !defined TTYMODES_LOCAL_ONLY || defined VREPRINT TTYMODE_CHAR(REPRINT, 12, VREPRINT) #endif #if !defined TTYMODES_LOCAL_ONLY || defined VWERASE TTYMODE_CHAR(WERASE, 13, VWERASE) #endif #if !defined TTYMODES_LOCAL_ONLY || defined VLNEXT TTYMODE_CHAR(LNEXT, 14, VLNEXT) #endif #if !defined TTYMODES_LOCAL_ONLY || defined VFLUSH TTYMODE_CHAR(FLUSH, 15, VFLUSH) #endif #if !defined TTYMODES_LOCAL_ONLY || defined VSWTCH TTYMODE_CHAR(SWTCH, 16, VSWTCH) #endif #if !defined TTYMODES_LOCAL_ONLY || defined VSTATUS TTYMODE_CHAR(STATUS, 17, VSTATUS) #endif #if !defined TTYMODES_LOCAL_ONLY || defined VDISCARD TTYMODE_CHAR(DISCARD, 18, VDISCARD) #endif #if !defined TTYMODES_LOCAL_ONLY || defined IGNPAR TTYMODE_FLAG(IGNPAR, 30, i, IGNPAR) #endif #if !defined TTYMODES_LOCAL_ONLY || defined PARMRK TTYMODE_FLAG(PARMRK, 31, i, PARMRK) #endif #if !defined TTYMODES_LOCAL_ONLY || defined INPCK TTYMODE_FLAG(INPCK, 32, i, INPCK) #endif #if !defined TTYMODES_LOCAL_ONLY || defined ISTRIP TTYMODE_FLAG(ISTRIP, 33, i, ISTRIP) #endif #if !defined TTYMODES_LOCAL_ONLY || defined INLCR TTYMODE_FLAG(INLCR, 34, i, INLCR) #endif #if !defined TTYMODES_LOCAL_ONLY || defined IGNCR TTYMODE_FLAG(IGNCR, 35, i, IGNCR) #endif #if !defined TTYMODES_LOCAL_ONLY || defined ICRNL TTYMODE_FLAG(ICRNL, 36, i, ICRNL) #endif #if !defined TTYMODES_LOCAL_ONLY || defined IUCLC TTYMODE_FLAG(IUCLC, 37, i, IUCLC) #endif #if !defined TTYMODES_LOCAL_ONLY || defined IXON TTYMODE_FLAG(IXON, 38, i, IXON) #endif #if !defined TTYMODES_LOCAL_ONLY || defined IXANY TTYMODE_FLAG(IXANY, 39, i, IXANY) #endif #if !defined TTYMODES_LOCAL_ONLY || defined IXOFF TTYMODE_FLAG(IXOFF, 40, i, IXOFF) #endif #if !defined TTYMODES_LOCAL_ONLY || defined IMAXBEL TTYMODE_FLAG(IMAXBEL, 41, i, IMAXBEL) #endif #if !defined TTYMODES_LOCAL_ONLY || defined IUTF8 TTYMODE_FLAG(IUTF8, 42, i, IUTF8) #endif #if !defined TTYMODES_LOCAL_ONLY || defined ISIG TTYMODE_FLAG(ISIG, 50, l, ISIG) #endif #if !defined TTYMODES_LOCAL_ONLY || defined ICANON TTYMODE_FLAG(ICANON, 51, l, ICANON) #endif #if !defined TTYMODES_LOCAL_ONLY || defined XCASE TTYMODE_FLAG(XCASE, 52, l, XCASE) #endif #if !defined TTYMODES_LOCAL_ONLY || defined ECHO TTYMODE_FLAG(ECHO, 53, l, ECHO) #endif #if !defined TTYMODES_LOCAL_ONLY || defined ECHOE TTYMODE_FLAG(ECHOE, 54, l, ECHOE) #endif #if !defined TTYMODES_LOCAL_ONLY || defined ECHOK TTYMODE_FLAG(ECHOK, 55, l, ECHOK) #endif #if !defined TTYMODES_LOCAL_ONLY || defined ECHONL TTYMODE_FLAG(ECHONL, 56, l, ECHONL) #endif #if !defined TTYMODES_LOCAL_ONLY || defined NOFLSH TTYMODE_FLAG(NOFLSH, 57, l, NOFLSH) #endif #if !defined TTYMODES_LOCAL_ONLY || defined TOSTOP TTYMODE_FLAG(TOSTOP, 58, l, TOSTOP) #endif #if !defined TTYMODES_LOCAL_ONLY || defined IEXTEN TTYMODE_FLAG(IEXTEN, 59, l, IEXTEN) #endif #if !defined TTYMODES_LOCAL_ONLY || defined ECHOCTL TTYMODE_FLAG(ECHOCTL, 60, l, ECHOCTL) #endif #if !defined TTYMODES_LOCAL_ONLY || defined ECHOKE TTYMODE_FLAG(ECHOKE, 61, l, ECHOKE) #endif #if !defined TTYMODES_LOCAL_ONLY || defined PENDIN TTYMODE_FLAG(PENDIN, 62, l, PENDIN) #endif #if !defined TTYMODES_LOCAL_ONLY || defined OPOST TTYMODE_FLAG(OPOST, 70, o, OPOST) #endif #if !defined TTYMODES_LOCAL_ONLY || defined OLCUC TTYMODE_FLAG(OLCUC, 71, o, OLCUC) #endif #if !defined TTYMODES_LOCAL_ONLY || defined ONLCR TTYMODE_FLAG(ONLCR, 72, o, ONLCR) #endif #if !defined TTYMODES_LOCAL_ONLY || defined OCRNL TTYMODE_FLAG(OCRNL, 73, o, OCRNL) #endif #if !defined TTYMODES_LOCAL_ONLY || defined ONOCR TTYMODE_FLAG(ONOCR, 74, o, ONOCR) #endif #if !defined TTYMODES_LOCAL_ONLY || defined ONLRET TTYMODE_FLAG(ONLRET, 75, o, ONLRET) #endif #if !defined TTYMODES_LOCAL_ONLY || defined CS7 TTYMODE_FLAG(CS7, 90, c, CSIZE) #endif #if !defined TTYMODES_LOCAL_ONLY || defined CS8 TTYMODE_FLAG(CS8, 91, c, CSIZE) #endif #if !defined TTYMODES_LOCAL_ONLY || defined PARENB TTYMODE_FLAG(PARENB, 92, c, PARENB) #endif #if !defined TTYMODES_LOCAL_ONLY || defined PARODD TTYMODE_FLAG(PARODD, 93, c, PARODD) #endif putty-0.76/sshutils.c0000644000175000017500000000523414072266312011605 00000000000000/* * Supporting routines used in common by all the various components of * the SSH system. */ #include #include #include "putty.h" #include "ssh.h" #include "sshchan.h" /* ---------------------------------------------------------------------- * Centralised standard methods for other channel implementations to * borrow. */ void chan_remotely_opened_confirmation(Channel *chan) { unreachable("this channel type should never receive OPEN_CONFIRMATION"); } void chan_remotely_opened_failure(Channel *chan, const char *errtext) { unreachable("this channel type should never receive OPEN_FAILURE"); } bool chan_default_want_close( Channel *chan, bool sent_local_eof, bool rcvd_remote_eof) { /* * Default close policy: we start initiating the CHANNEL_CLOSE * procedure as soon as both sides of the channel have seen EOF. */ return sent_local_eof && rcvd_remote_eof; } bool chan_no_exit_status(Channel *chan, int status) { return false; } bool chan_no_exit_signal( Channel *chan, ptrlen signame, bool core_dumped, ptrlen msg) { return false; } bool chan_no_exit_signal_numeric( Channel *chan, int signum, bool core_dumped, ptrlen msg) { return false; } bool chan_no_run_shell(Channel *chan) { return false; } bool chan_no_run_command(Channel *chan, ptrlen command) { return false; } bool chan_no_run_subsystem(Channel *chan, ptrlen subsys) { return false; } bool chan_no_enable_x11_forwarding( Channel *chan, bool oneshot, ptrlen authproto, ptrlen authdata, unsigned screen_number) { return false; } bool chan_no_enable_agent_forwarding(Channel *chan) { return false; } bool chan_no_allocate_pty( Channel *chan, ptrlen termtype, unsigned width, unsigned height, unsigned pixwidth, unsigned pixheight, struct ssh_ttymodes modes) { return false; } bool chan_no_set_env(Channel *chan, ptrlen var, ptrlen value) { return false; } bool chan_no_send_break(Channel *chan, unsigned length) { return false; } bool chan_no_send_signal(Channel *chan, ptrlen signame) { return false; } bool chan_no_change_window_size( Channel *chan, unsigned width, unsigned height, unsigned pixwidth, unsigned pixheight) { return false; } void chan_no_request_response(Channel *chan, bool success) { unreachable("this channel type should never send a want-reply request"); } /* ---------------------------------------------------------------------- * Other miscellaneous utility functions. */ void free_rportfwd(struct ssh_rportfwd *rpf) { if (rpf) { sfree(rpf->log_description); sfree(rpf->shost); sfree(rpf->dhost); sfree(rpf); } } putty-0.76/sshverstring.c0000644000175000017500000005162114072266312012471 00000000000000/* * Code to handle the initial SSH version string exchange. */ #include #include #include #include "putty.h" #include "ssh.h" #include "sshbpp.h" #include "sshcr.h" #define PREFIX_MAXLEN 64 struct ssh_verstring_state { int crState; Conf *conf; ptrlen prefix_wanted; char *our_protoversion; struct ssh_version_receiver *receiver; bool send_early; bool found_prefix; int major_protoversion; int remote_bugs; char prefix[PREFIX_MAXLEN]; char *impl_name; strbuf *vstring; char *protoversion; const char *softwareversion; char *our_vstring; int i; BinaryPacketProtocol bpp; }; static void ssh_verstring_free(BinaryPacketProtocol *bpp); static void ssh_verstring_handle_input(BinaryPacketProtocol *bpp); static void ssh_verstring_handle_output(BinaryPacketProtocol *bpp); static PktOut *ssh_verstring_new_pktout(int type); static void ssh_verstring_queue_disconnect(BinaryPacketProtocol *bpp, const char *msg, int category); static const BinaryPacketProtocolVtable ssh_verstring_vtable = { .free = ssh_verstring_free, .handle_input = ssh_verstring_handle_input, .handle_output = ssh_verstring_handle_output, .new_pktout = ssh_verstring_new_pktout, .queue_disconnect = ssh_verstring_queue_disconnect, .packet_size_limit = 0xFFFFFFFF, /* no special limit for this bpp */ }; static void ssh_detect_bugs(struct ssh_verstring_state *s); static bool ssh_version_includes_v1(const char *ver); static bool ssh_version_includes_v2(const char *ver); BinaryPacketProtocol *ssh_verstring_new( Conf *conf, LogContext *logctx, bool bare_connection_mode, const char *protoversion, struct ssh_version_receiver *rcv, bool server_mode, const char *impl_name) { struct ssh_verstring_state *s = snew(struct ssh_verstring_state); memset(s, 0, sizeof(struct ssh_verstring_state)); if (!bare_connection_mode) { s->prefix_wanted = PTRLEN_LITERAL("SSH-"); } else { /* * Ordinary SSH begins with the banner "SSH-x.y-...". Here, * we're going to be speaking just the ssh-connection * subprotocol, extracted and given a trivial binary packet * protocol, so we need a new banner. * * The new banner is like the ordinary SSH banner, but * replaces the prefix '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. */ s->prefix_wanted = PTRLEN_LITERAL( "SSHCONNECTION@putty.projects.tartarus.org-"); } assert(s->prefix_wanted.len <= PREFIX_MAXLEN); s->conf = conf_copy(conf); s->bpp.logctx = logctx; s->our_protoversion = dupstr(protoversion); s->receiver = rcv; s->impl_name = dupstr(impl_name); s->vstring = strbuf_new(); /* * We send our version string early if we can. But if it includes * SSH-1, we can't, because we have to take the other end into * account too (see below). * * In server mode, we do send early. */ s->send_early = server_mode || !ssh_version_includes_v1(protoversion); s->bpp.vt = &ssh_verstring_vtable; ssh_bpp_common_setup(&s->bpp); return &s->bpp; } void ssh_verstring_free(BinaryPacketProtocol *bpp) { struct ssh_verstring_state *s = container_of(bpp, struct ssh_verstring_state, bpp); conf_free(s->conf); sfree(s->impl_name); strbuf_free(s->vstring); sfree(s->protoversion); sfree(s->our_vstring); sfree(s->our_protoversion); sfree(s); } static int ssh_versioncmp(const char *a, const char *b) { char *ae, *be; unsigned long av, bv; av = strtoul(a, &ae, 10); bv = strtoul(b, &be, 10); if (av != bv) return (av < bv ? -1 : +1); if (*ae == '.') ae++; if (*be == '.') be++; av = strtoul(ae, &ae, 10); bv = strtoul(be, &be, 10); if (av != bv) return (av < bv ? -1 : +1); return 0; } static bool ssh_version_includes_v1(const char *ver) { return ssh_versioncmp(ver, "2.0") < 0; } static bool ssh_version_includes_v2(const char *ver) { return ssh_versioncmp(ver, "1.99") >= 0; } static void ssh_verstring_send(struct ssh_verstring_state *s) { BinaryPacketProtocol *bpp = &s->bpp; /* for bpp_logevent */ char *p; int sv_pos; /* * Construct our outgoing version string. */ s->our_vstring = dupprintf( "%.*s%s-%s%s", (int)s->prefix_wanted.len, (const char *)s->prefix_wanted.ptr, s->our_protoversion, s->impl_name, sshver); sv_pos = s->prefix_wanted.len + strlen(s->our_protoversion) + 1; /* Convert minus signs and spaces in the software version string * into underscores. */ for (p = s->our_vstring + sv_pos; *p; p++) { if (*p == '-' || *p == ' ') *p = '_'; } #ifdef FUZZING /* * Replace the first character of the string with an "I" if we're * compiling this code for fuzzing - i.e. the protocol prefix * becomes "ISH-" instead of "SSH-". * * This is irrelevant to any real client software (the only thing * reading the output of PuTTY built for fuzzing is the fuzzer, * which can adapt to whatever it sees anyway). But it's a safety * precaution making it difficult to accidentally run such a * version of PuTTY (which would be hugely insecure) against a * live peer implementation. * * (So the replacement prefix "ISH" notionally stands for * 'Insecure Shell', of course.) */ s->our_vstring[0] = 'I'; #endif /* * Now send that version string, plus trailing \r\n or just \n * (the latter in SSH-1 mode). */ bufchain_add(s->bpp.out_raw, s->our_vstring, strlen(s->our_vstring)); if (ssh_version_includes_v2(s->our_protoversion)) bufchain_add(s->bpp.out_raw, "\015", 1); bufchain_add(s->bpp.out_raw, "\012", 1); bpp_logevent("We claim version: %s", s->our_vstring); } #define BPP_WAITFOR(minlen) do \ { \ bool success; \ crMaybeWaitUntilV( \ (success = (bufchain_size(s->bpp.in_raw) >= (minlen))) || \ s->bpp.input_eof); \ if (!success) \ goto eof; \ } while (0) void ssh_verstring_handle_input(BinaryPacketProtocol *bpp) { struct ssh_verstring_state *s = container_of(bpp, struct ssh_verstring_state, bpp); crBegin(s->crState); /* * If we're sending our version string up front before seeing the * other side's, then do it now. */ if (s->send_early) ssh_verstring_send(s); /* * Search for a line beginning with the protocol name prefix in * the input. */ s->i = 0; while (1) { /* * Every time round this loop, we're at the start of a new * line, so look for the prefix. */ BPP_WAITFOR(s->prefix_wanted.len); bufchain_fetch(s->bpp.in_raw, s->prefix, s->prefix_wanted.len); if (!memcmp(s->prefix, s->prefix_wanted.ptr, s->prefix_wanted.len)) { bufchain_consume(s->bpp.in_raw, s->prefix_wanted.len); ssh_check_frozen(s->bpp.ssh); break; } /* * If we didn't find it, consume data until we see a newline. */ while (1) { ptrlen data; char *nl; /* Wait to receive at least 1 byte, but then consume more * than that if it's there. */ BPP_WAITFOR(1); data = bufchain_prefix(s->bpp.in_raw); if ((nl = memchr(data.ptr, '\012', data.len)) != NULL) { bufchain_consume(s->bpp.in_raw, nl - (char *)data.ptr + 1); ssh_check_frozen(s->bpp.ssh); break; } else { bufchain_consume(s->bpp.in_raw, data.len); ssh_check_frozen(s->bpp.ssh); } } } s->found_prefix = true; /* * Copy the greeting line so far into vstring. */ put_data(s->vstring, s->prefix_wanted.ptr, s->prefix_wanted.len); /* * Now read the rest of the greeting line. */ s->i = 0; do { ptrlen data; char *nl; BPP_WAITFOR(1); data = bufchain_prefix(s->bpp.in_raw); if ((nl = memchr(data.ptr, '\012', data.len)) != NULL) { data.len = nl - (char *)data.ptr + 1; } put_datapl(s->vstring, data); bufchain_consume(s->bpp.in_raw, data.len); ssh_check_frozen(s->bpp.ssh); } while (s->vstring->s[s->vstring->len-1] != '\012'); /* * Trim \r and \n from the version string, and replace them with * a NUL terminator. */ while (s->vstring->len > 0 && (s->vstring->s[s->vstring->len-1] == '\r' || s->vstring->s[s->vstring->len-1] == '\n')) strbuf_shrink_by(s->vstring, 1); bpp_logevent("Remote version: %s", s->vstring->s); /* * Pick out the protocol version and software version. The former * goes in a separately allocated string, so that s->vstring * remains intact for later use in key exchange; the latter is the * tail of s->vstring, so it doesn't need to be allocated. */ { const char *pv_start = s->vstring->s + s->prefix_wanted.len; int pv_len = strcspn(pv_start, "-"); s->protoversion = dupprintf("%.*s", pv_len, pv_start); s->softwareversion = pv_start + pv_len; if (*s->softwareversion) { assert(*s->softwareversion == '-'); s->softwareversion++; } } ssh_detect_bugs(s); /* * Figure out what actual SSH protocol version we're speaking. */ if (ssh_version_includes_v2(s->our_protoversion) && ssh_version_includes_v2(s->protoversion)) { /* * We're doing SSH-2. */ s->major_protoversion = 2; } else if (ssh_version_includes_v1(s->our_protoversion) && ssh_version_includes_v1(s->protoversion)) { /* * We're doing SSH-1. */ s->major_protoversion = 1; /* * There are multiple minor versions of SSH-1, and the * protocol does not specify that the minimum of client * and server versions is used. So we must adjust our * outgoing protocol version to be no higher than that of * the other side. */ if (!s->send_early && ssh_versioncmp(s->our_protoversion, s->protoversion) > 0) { sfree(s->our_protoversion); s->our_protoversion = dupstr(s->protoversion); } } else { /* * Unable to agree on a major protocol version at all. */ if (!ssh_version_includes_v2(s->our_protoversion)) { ssh_sw_abort(s->bpp.ssh, "SSH protocol version 1 required by our " "configuration but not provided by remote"); } else { ssh_sw_abort(s->bpp.ssh, "SSH protocol version 2 required by our " "configuration but remote only provides " "(old, insecure) SSH-1"); } crStopV; } bpp_logevent("Using SSH protocol version %d", s->major_protoversion); if (!s->send_early) { /* * If we didn't send our version string early, construct and * send it now, because now we know what it is. */ ssh_verstring_send(s); } /* * And we're done. Notify our receiver that we now know our * protocol version. This will cause it to disconnect us from the * input stream and ultimately free us, because our job is now * done. */ s->receiver->got_ssh_version(s->receiver, s->major_protoversion); return; eof: ssh_remote_error(s->bpp.ssh, "Remote side unexpectedly closed network connection"); return; /* avoid touching s now it's been freed */ crFinishV; } static PktOut *ssh_verstring_new_pktout(int type) { unreachable("Should never try to send packets during SSH version " "string exchange"); } static void ssh_verstring_handle_output(BinaryPacketProtocol *bpp) { if (pq_peek(&bpp->out_pq)) { unreachable("Should never try to send packets during SSH version " "string exchange"); } } /* * Examine the remote side's version string, and compare it against a * list of known buggy implementations. */ static void ssh_detect_bugs(struct ssh_verstring_state *s) { BinaryPacketProtocol *bpp = &s->bpp; /* for bpp_logevent */ const char *imp = s->softwareversion; s->remote_bugs = 0; /* * 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(s->conf, CONF_sshbug_ignore1) == FORCE_ON || (conf_get_int(s->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. */ s->remote_bugs |= BUG_CHOKES_ON_SSH1_IGNORE; bpp_logevent("We believe remote version has SSH-1 ignore bug"); } if (conf_get_int(s->conf, CONF_sshbug_plainpw1) == FORCE_ON || (conf_get_int(s->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. */ s->remote_bugs |= BUG_NEEDS_SSH1_PLAIN_PASSWORD; bpp_logevent("We believe remote version needs a " "plain SSH-1 password"); } if (conf_get_int(s->conf, CONF_sshbug_rsa1) == FORCE_ON || (conf_get_int(s->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. */ s->remote_bugs |= BUG_CHOKES_ON_RSA; bpp_logevent("We believe remote version can't handle SSH-1 " "RSA authentication"); } if (conf_get_int(s->conf, CONF_sshbug_hmac2) == FORCE_ON || (conf_get_int(s->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. */ s->remote_bugs |= BUG_SSH2_HMAC; bpp_logevent("We believe remote version has SSH-2 HMAC bug"); } if (conf_get_int(s->conf, CONF_sshbug_derivekey2) == FORCE_ON || (conf_get_int(s->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). */ s->remote_bugs |= BUG_SSH2_DERIVEKEY; bpp_logevent("We believe remote version has SSH-2 " "key-derivation bug"); } if (conf_get_int(s->conf, CONF_sshbug_rsapad2) == FORCE_ON || (conf_get_int(s->conf, CONF_sshbug_rsapad2) == AUTO && (wc_match("OpenSSH_2.[5-9]*", imp) || wc_match("OpenSSH_3.[0-2]*", imp) || wc_match("mod_sftp/0.[0-8]*", imp) || wc_match("mod_sftp/0.9.[0-8]", imp)))) { /* * These versions have the SSH-2 RSA padding bug. */ s->remote_bugs |= BUG_SSH2_RSA_PADDING; bpp_logevent("We believe remote version has SSH-2 RSA padding bug"); } if (conf_get_int(s->conf, CONF_sshbug_pksessid2) == FORCE_ON || (conf_get_int(s->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. */ s->remote_bugs |= BUG_SSH2_PK_SESSIONID; bpp_logevent("We believe remote version has SSH-2 " "public-key-session-ID bug"); } if (conf_get_int(s->conf, CONF_sshbug_rekey2) == FORCE_ON || (conf_get_int(s->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. */ s->remote_bugs |= BUG_SSH2_REKEY; bpp_logevent("We believe remote version has SSH-2 rekey bug"); } if (conf_get_int(s->conf, CONF_sshbug_maxpkt2) == FORCE_ON || (conf_get_int(s->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. */ s->remote_bugs |= BUG_SSH2_MAXPKT; bpp_logevent("We believe remote version ignores SSH-2 " "maximum packet size"); } if (conf_get_int(s->conf, CONF_sshbug_ignore2) == FORCE_ON) { /* * Servers that don't support SSH2_MSG_IGNORE. Currently, * none detected automatically. */ s->remote_bugs |= BUG_CHOKES_ON_SSH2_IGNORE; bpp_logevent("We believe remote version has SSH-2 ignore bug"); } if (conf_get_int(s->conf, CONF_sshbug_oldgex2) == FORCE_ON || (conf_get_int(s->conf, CONF_sshbug_oldgex2) == AUTO && (wc_match("OpenSSH_2.[235]*", imp)))) { /* * These versions only support the original (pre-RFC4419) * SSH-2 GEX request, and disconnect with a protocol error if * we use the newer version. */ s->remote_bugs |= BUG_SSH2_OLDGEX; bpp_logevent("We believe remote version has outdated SSH-2 GEX"); } if (conf_get_int(s->conf, CONF_sshbug_winadj) == FORCE_ON) { /* * Servers that don't support our winadj request for one * reason or another. Currently, none detected automatically. */ s->remote_bugs |= BUG_CHOKES_ON_WINADJ; bpp_logevent("We believe remote version has winadj bug"); } if (conf_get_int(s->conf, CONF_sshbug_chanreq) == FORCE_ON || (conf_get_int(s->conf, CONF_sshbug_chanreq) == AUTO && (wc_match("OpenSSH_[2-5].*", imp) || wc_match("OpenSSH_6.[0-6]*", imp) || wc_match("dropbear_0.[2-4][0-9]*", imp) || wc_match("dropbear_0.5[01]*", imp)))) { /* * These versions have the SSH-2 channel request bug. * OpenSSH 6.7 and above do not: * https://bugzilla.mindrot.org/show_bug.cgi?id=1818 * dropbear_0.52 and above do not: * https://secure.ucc.asn.au/hg/dropbear/rev/cd02449b709c */ s->remote_bugs |= BUG_SENDS_LATE_REQUEST_REPLY; bpp_logevent("We believe remote version has SSH-2 " "channel request bug"); } } const char *ssh_verstring_get_remote(BinaryPacketProtocol *bpp) { struct ssh_verstring_state *s = container_of(bpp, struct ssh_verstring_state, bpp); return s->vstring->s; } const char *ssh_verstring_get_local(BinaryPacketProtocol *bpp) { struct ssh_verstring_state *s = container_of(bpp, struct ssh_verstring_state, bpp); return s->our_vstring; } int ssh_verstring_get_bugs(BinaryPacketProtocol *bpp) { struct ssh_verstring_state *s = container_of(bpp, struct ssh_verstring_state, bpp); return s->remote_bugs; } static void ssh_verstring_queue_disconnect(BinaryPacketProtocol *bpp, const char *msg, int category) { /* No way to send disconnect messages at this stage of the protocol! */ } putty-0.76/sshzlib.c0000644000175000017500000012154414072266312011410 00000000000000/* * Zlib (RFC1950 / RFC1951) compression for PuTTY. * * There will no doubt be criticism of my decision to reimplement * Zlib compression from scratch instead of using the existing zlib * code. People will cry `reinventing the wheel'; they'll claim * that the `fundamental basis of OSS' is code reuse; they'll want * to see a really good reason for me having chosen not to use the * existing code. * * Well, here are my reasons. Firstly, I don't want to link the * whole of zlib into the PuTTY binary; PuTTY is justifiably proud * of its small size and I think zlib contains a lot of unnecessary * baggage for the kind of compression that SSH requires. * * Secondly, I also don't like the alternative of using zlib.dll. * Another thing PuTTY is justifiably proud of is its ease of * installation, and the last thing I want to do is to start * mandating DLLs. Not only that, but there are two _kinds_ of * zlib.dll kicking around, one with C calling conventions on the * exported functions and another with WINAPI conventions, and * there would be a significant danger of getting the wrong one. * * Thirdly, there seems to be a difference of opinion on the IETF * secsh mailing list about the correct way to round off a * compressed packet and start the next. In particular, there's * some talk of switching to a mechanism zlib isn't currently * capable of supporting (see below for an explanation). Given that * sort of uncertainty, I thought it might be better to have code * that will support even the zlib-incompatible worst case. * * Fourthly, it's a _second implementation_. Second implementations * are fundamentally a Good Thing in standardisation efforts. The * 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 #include #include #include "defs.h" #include "ssh.h" /* ---------------------------------------------------------------------- * Basic LZ77 code. This bit is designed modularly, so it could be * ripped out and used in a different LZ77 compressor. Go to it, * and good luck :-) */ struct LZ77InternalContext; struct LZ77Context { struct LZ77InternalContext *ictx; void *userdata; void (*literal) (struct LZ77Context * ctx, unsigned char c); void (*match) (struct LZ77Context * ctx, int distance, int len); }; /* * Initialise the private fields of an LZ77Context. It's up to the * user to initialise the public fields. */ static int lz77_init(struct LZ77Context *ctx); /* * Supply data to be compressed. Will update the private fields of * the LZ77Context, and will call literal() and match() to output. * If `compress' is false, it will never emit a match, but will * instead call literal() for everything. */ static void lz77_compress(struct LZ77Context *ctx, const unsigned char *data, int len); /* * Modifiable parameters. */ #define WINSIZE 32768 /* window size. Must be power of 2! */ #define HASHMAX 2039 /* one more than max hash value */ #define MAXMATCH 32 /* how many matches we track */ #define HASHCHARS 3 /* how many chars make a hash */ /* * This compressor takes a less slapdash approach than the * gzip/zlib one. Rather than allowing our hash chains to fall into * disuse near the far end, we keep them doubly linked so we can * _find_ the far end, and then every time we add a new byte to the * window (thus rolling round by one and removing the previous * byte), we can carefully remove the hash chain entry. */ #define INVALID -1 /* invalid hash _and_ invalid offset */ struct WindowEntry { short next, prev; /* array indices within the window */ short hashval; }; struct HashEntry { short first; /* window index of first in chain */ }; struct Match { int distance, len; }; struct LZ77InternalContext { struct WindowEntry win[WINSIZE]; unsigned char data[WINSIZE]; int winpos; struct HashEntry hashtab[HASHMAX]; unsigned char pending[HASHCHARS]; int npending; }; static int lz77_hash(const unsigned char *data) { return (257 * data[0] + 263 * data[1] + 269 * data[2]) % HASHMAX; } static int lz77_init(struct LZ77Context *ctx) { struct LZ77InternalContext *st; int i; st = snew(struct LZ77InternalContext); if (!st) return 0; ctx->ictx = st; for (i = 0; i < WINSIZE; i++) st->win[i].next = st->win[i].prev = st->win[i].hashval = INVALID; for (i = 0; i < HASHMAX; i++) st->hashtab[i].first = INVALID; st->winpos = 0; st->npending = 0; return 1; } static void lz77_advance(struct LZ77InternalContext *st, unsigned char c, int hash) { int off; /* * Remove the hash entry at winpos from the tail of its chain, * or empty the chain if it's the only thing on the chain. */ if (st->win[st->winpos].prev != INVALID) { st->win[st->win[st->winpos].prev].next = INVALID; } else if (st->win[st->winpos].hashval != INVALID) { st->hashtab[st->win[st->winpos].hashval].first = INVALID; } /* * Create a new entry at winpos and add it to the head of its * hash chain. */ st->win[st->winpos].hashval = hash; st->win[st->winpos].prev = INVALID; off = st->win[st->winpos].next = st->hashtab[hash].first; st->hashtab[hash].first = st->winpos; if (off != INVALID) st->win[off].prev = st->winpos; st->data[st->winpos] = c; /* * Advance the window pointer. */ st->winpos = (st->winpos + 1) & (WINSIZE - 1); } #define CHARAT(k) ( (k)<0 ? st->data[(st->winpos+k)&(WINSIZE-1)] : data[k] ) static void lz77_compress(struct LZ77Context *ctx, const unsigned char *data, int len) { struct LZ77InternalContext *st = ctx->ictx; int i, distance, off, nmatch, matchlen, advance; struct Match defermatch, matches[MAXMATCH]; int deferchr; assert(st->npending <= HASHCHARS); /* * Add any pending characters from last time to the window. (We * might not be able to.) * * This leaves st->pending empty in the usual case (when len >= * HASHCHARS); otherwise it leaves st->pending empty enough that * adding all the remaining 'len' characters will not push it past * HASHCHARS in size. */ for (i = 0; i < st->npending; i++) { unsigned char foo[HASHCHARS]; int j; if (len + st->npending - i < HASHCHARS) { /* Update the pending array. */ for (j = i; j < st->npending; j++) st->pending[j - i] = st->pending[j]; break; } for (j = 0; j < HASHCHARS; j++) foo[j] = (i + j < st->npending ? st->pending[i + j] : data[i + j - st->npending]); lz77_advance(st, foo[0], lz77_hash(foo)); } st->npending -= i; defermatch.distance = 0; /* appease compiler */ defermatch.len = 0; deferchr = '\0'; while (len > 0) { if (len >= HASHCHARS) { /* * Hash the next few characters. */ int hash = lz77_hash(data); /* * Look the hash up in the corresponding hash chain and see * what we can find. */ nmatch = 0; for (off = st->hashtab[hash].first; off != INVALID; off = st->win[off].next) { /* distance = 1 if off == st->winpos-1 */ /* distance = WINSIZE if off == st->winpos */ distance = WINSIZE - (off + WINSIZE - st->winpos) % WINSIZE; for (i = 0; i < HASHCHARS; i++) if (CHARAT(i) != CHARAT(i - distance)) break; if (i == HASHCHARS) { matches[nmatch].distance = distance; matches[nmatch].len = 3; if (++nmatch >= MAXMATCH) break; } } } else { nmatch = 0; } if (nmatch > 0) { /* * We've now filled up matches[] with nmatch potential * matches. Follow them down to find the longest. (We * assume here that it's always worth favouring a * longer match over a shorter one.) */ matchlen = HASHCHARS; while (matchlen < len) { int j; for (i = j = 0; i < nmatch; i++) { if (CHARAT(matchlen) == CHARAT(matchlen - matches[i].distance)) { matches[j++] = matches[i]; } } if (j == 0) break; matchlen++; nmatch = j; } /* * We've now got all the longest matches. We favour the * shorter distances, which means we go with matches[0]. * So see if we want to defer it or throw it away. */ matches[0].len = matchlen; if (defermatch.len > 0) { if (matches[0].len > defermatch.len + 1) { /* We have a better match. Emit the deferred char, * and defer this match. */ ctx->literal(ctx, (unsigned char) deferchr); defermatch = matches[0]; deferchr = data[0]; advance = 1; } else { /* We don't have a better match. Do the deferred one. */ ctx->match(ctx, defermatch.distance, defermatch.len); advance = defermatch.len - 1; defermatch.len = 0; } } else { /* There was no deferred match. Defer this one. */ defermatch = matches[0]; deferchr = data[0]; advance = 1; } } else { /* * We found no matches. Emit the deferred match, if * any; otherwise emit a literal. */ if (defermatch.len > 0) { ctx->match(ctx, defermatch.distance, defermatch.len); advance = defermatch.len - 1; defermatch.len = 0; } else { ctx->literal(ctx, data[0]); advance = 1; } } /* * Now advance the position by `advance' characters, * keeping the window and hash chains consistent. */ while (advance > 0) { if (len >= HASHCHARS) { lz77_advance(st, *data, lz77_hash(data)); } else { assert(st->npending < HASHCHARS); st->pending[st->npending++] = *data; } data++; len--; advance--; } } } /* ---------------------------------------------------------------------- * Zlib compression. We always use the static Huffman tree option. * Mostly this is because it's hard to scan a block in advance to * work out better trees; dynamic trees are great when you're * compressing a large file under no significant time constraint, * but when you're compressing little bits in real time, things get * hairier. * * I suppose it's possible that I could compute Huffman trees based * on the frequencies in the _previous_ block, as a sort of * heuristic, but I'm not confident that the gain would balance out * having to transmit the trees. */ struct Outbuf { strbuf *outbuf; unsigned long outbits; int noutbits; bool firstblock; }; static void outbits(struct Outbuf *out, unsigned long bits, int nbits) { assert(out->noutbits + nbits <= 32); out->outbits |= bits << out->noutbits; out->noutbits += nbits; while (out->noutbits >= 8) { put_byte(out->outbuf, out->outbits & 0xFF); out->outbits >>= 8; out->noutbits -= 8; } } static const unsigned char mirrorbytes[256] = { 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff, }; typedef struct { short code, extrabits; int min, max; } coderecord; static const coderecord lencodes[] = { {257, 0, 3, 3}, {258, 0, 4, 4}, {259, 0, 5, 5}, {260, 0, 6, 6}, {261, 0, 7, 7}, {262, 0, 8, 8}, {263, 0, 9, 9}, {264, 0, 10, 10}, {265, 1, 11, 12}, {266, 1, 13, 14}, {267, 1, 15, 16}, {268, 1, 17, 18}, {269, 2, 19, 22}, {270, 2, 23, 26}, {271, 2, 27, 30}, {272, 2, 31, 34}, {273, 3, 35, 42}, {274, 3, 43, 50}, {275, 3, 51, 58}, {276, 3, 59, 66}, {277, 4, 67, 82}, {278, 4, 83, 98}, {279, 4, 99, 114}, {280, 4, 115, 130}, {281, 5, 131, 162}, {282, 5, 163, 194}, {283, 5, 195, 226}, {284, 5, 227, 257}, {285, 0, 258, 258}, }; static const coderecord distcodes[] = { {0, 0, 1, 1}, {1, 0, 2, 2}, {2, 0, 3, 3}, {3, 0, 4, 4}, {4, 1, 5, 6}, {5, 1, 7, 8}, {6, 2, 9, 12}, {7, 2, 13, 16}, {8, 3, 17, 24}, {9, 3, 25, 32}, {10, 4, 33, 48}, {11, 4, 49, 64}, {12, 5, 65, 96}, {13, 5, 97, 128}, {14, 6, 129, 192}, {15, 6, 193, 256}, {16, 7, 257, 384}, {17, 7, 385, 512}, {18, 8, 513, 768}, {19, 8, 769, 1024}, {20, 9, 1025, 1536}, {21, 9, 1537, 2048}, {22, 10, 2049, 3072}, {23, 10, 3073, 4096}, {24, 11, 4097, 6144}, {25, 11, 6145, 8192}, {26, 12, 8193, 12288}, {27, 12, 12289, 16384}, {28, 13, 16385, 24576}, {29, 13, 24577, 32768}, }; static void zlib_literal(struct LZ77Context *ectx, unsigned char c) { struct Outbuf *out = (struct Outbuf *) ectx->userdata; if (c <= 143) { /* 0 through 143 are 8 bits long starting at 00110000. */ outbits(out, mirrorbytes[0x30 + c], 8); } else { /* 144 through 255 are 9 bits long starting at 110010000. */ outbits(out, 1 + 2 * mirrorbytes[0x90 - 144 + c], 9); } } static void zlib_match(struct LZ77Context *ectx, int distance, int len) { const coderecord *d, *l; int i, j, k; struct Outbuf *out = (struct Outbuf *) ectx->userdata; while (len > 0) { int thislen; /* * We can transmit matches of lengths 3 through 258 * inclusive. So if len exceeds 258, we must transmit in * several steps, with 258 or less in each step. * * Specifically: if len >= 261, we can transmit 258 and be * sure of having at least 3 left for the next step. And if * len <= 258, we can just transmit len. But if len == 259 * or 260, we must transmit len-3. */ thislen = (len > 260 ? 258 : len <= 258 ? len : len - 3); len -= thislen; /* * Binary-search to find which length code we're * transmitting. */ i = -1; j = lenof(lencodes); while (1) { assert(j - i >= 2); k = (j + i) / 2; if (thislen < lencodes[k].min) j = k; else if (thislen > lencodes[k].max) i = k; else { l = &lencodes[k]; break; /* found it! */ } } /* * Transmit the length code. 256-279 are seven bits * starting at 0000000; 280-287 are eight bits starting at * 11000000. */ if (l->code <= 279) { outbits(out, mirrorbytes[(l->code - 256) * 2], 7); } else { outbits(out, mirrorbytes[0xc0 - 280 + l->code], 8); } /* * Transmit the extra bits. */ if (l->extrabits) outbits(out, thislen - l->min, l->extrabits); /* * Binary-search to find which distance code we're * transmitting. */ i = -1; j = lenof(distcodes); while (1) { assert(j - i >= 2); k = (j + i) / 2; if (distance < distcodes[k].min) j = k; else if (distance > distcodes[k].max) i = k; else { d = &distcodes[k]; break; /* found it! */ } } /* * Transmit the distance code. Five bits starting at 00000. */ outbits(out, mirrorbytes[d->code * 8], 5); /* * Transmit the extra bits. */ if (d->extrabits) outbits(out, distance - d->min, d->extrabits); } } struct ssh_zlib_compressor { struct LZ77Context ectx; ssh_compressor sc; }; ssh_compressor *zlib_compress_init(void) { struct Outbuf *out; struct ssh_zlib_compressor *comp = snew(struct ssh_zlib_compressor); lz77_init(&comp->ectx); comp->sc.vt = &ssh_zlib; comp->ectx.literal = zlib_literal; comp->ectx.match = zlib_match; out = snew(struct Outbuf); out->outbuf = NULL; out->outbits = out->noutbits = 0; out->firstblock = true; comp->ectx.userdata = out; return &comp->sc; } void zlib_compress_cleanup(ssh_compressor *sc) { struct ssh_zlib_compressor *comp = container_of(sc, struct ssh_zlib_compressor, sc); struct Outbuf *out = (struct Outbuf *)comp->ectx.userdata; if (out->outbuf) strbuf_free(out->outbuf); sfree(out); sfree(comp->ectx.ictx); sfree(comp); } void zlib_compress_block(ssh_compressor *sc, const unsigned char *block, int len, unsigned char **outblock, int *outlen, int minlen) { struct ssh_zlib_compressor *comp = container_of(sc, struct ssh_zlib_compressor, sc); struct Outbuf *out = (struct Outbuf *) comp->ectx.userdata; bool in_block; assert(!out->outbuf); out->outbuf = strbuf_new_nm(); /* * If this is the first block, output the Zlib (RFC1950) header * bytes 78 9C. (Deflate compression, 32K window size, default * algorithm.) */ if (out->firstblock) { outbits(out, 0x9C78, 16); out->firstblock = false; in_block = false; } else in_block = true; if (!in_block) { /* * Start a Deflate (RFC1951) fixed-trees block. We * transmit a zero bit (BFINAL=0), followed by a zero * bit and a one bit (BTYPE=01). Of course these are in * the wrong order (01 0). */ outbits(out, 2, 3); } /* * Do the compression. */ lz77_compress(&comp->ectx, block, len); /* * End the block (by transmitting code 256, which is * 0000000 in fixed-tree mode), and transmit some empty * blocks to ensure we have emitted the byte containing the * last piece of genuine data. There are three ways we can * do this: * * - Minimal flush. Output end-of-block and then open a * new static block. This takes 9 bits, which is * guaranteed to flush out the last genuine code in the * closed block; but allegedly zlib can't handle it. * * - Zlib partial flush. Output EOB, open and close an * empty static block, and _then_ open the new block. * This is the best zlib can handle. * * - Zlib sync flush. Output EOB, then an empty * _uncompressed_ block (000, then sync to byte * boundary, then send bytes 00 00 FF FF). Then open the * new block. * * For the moment, we will use Zlib partial flush. */ outbits(out, 0, 7); /* close block */ outbits(out, 2, 3 + 7); /* empty static block */ outbits(out, 2, 3); /* open new block */ /* * If we've been asked to pad out the compressed data until it's * at least a given length, do so by emitting further empty static * blocks. */ while (out->outbuf->len < minlen) { outbits(out, 0, 7); /* close block */ outbits(out, 2, 3); /* open new static block */ } *outlen = out->outbuf->len; *outblock = (unsigned char *)strbuf_to_str(out->outbuf); out->outbuf = NULL; } /* ---------------------------------------------------------------------- * Zlib decompression. Of course, even though our compressor always * uses static trees, our _decompressor_ has to be capable of * handling dynamic trees if it sees them. */ /* * The way we work the Huffman decode is to have a table lookup on * the first N bits of the input stream (in the order they arrive, * of course, i.e. the first bit of the Huffman code is in bit 0). * Each table entry lists the number of bits to consume, plus * either an output code or a pointer to a secondary table. */ struct zlib_table; struct zlib_tableentry; struct zlib_tableentry { unsigned char nbits; short code; struct zlib_table *nexttable; }; struct zlib_table { int mask; /* mask applied to input bit stream */ struct zlib_tableentry *table; }; #define MAXCODELEN 16 #define MAXSYMS 288 /* * Build a single-level decode table for elements * [minlength,maxlength) of the provided code/length tables, and * recurse to build subtables. */ static struct zlib_table *zlib_mkonetab(int *codes, unsigned char *lengths, int nsyms, int pfx, int pfxbits, int bits) { struct zlib_table *tab = snew(struct zlib_table); int pfxmask = (1 << pfxbits) - 1; int nbits, i, j, code; tab->table = snewn((size_t)1 << bits, struct zlib_tableentry); tab->mask = (1 << bits) - 1; for (code = 0; code <= tab->mask; code++) { tab->table[code].code = -1; tab->table[code].nbits = 0; tab->table[code].nexttable = NULL; } for (i = 0; i < nsyms; i++) { if (lengths[i] <= pfxbits || (codes[i] & pfxmask) != pfx) continue; code = (codes[i] >> pfxbits) & tab->mask; for (j = code; j <= tab->mask; j += 1 << (lengths[i] - pfxbits)) { tab->table[j].code = i; nbits = lengths[i] - pfxbits; if (tab->table[j].nbits < nbits) tab->table[j].nbits = nbits; } } for (code = 0; code <= tab->mask; code++) { if (tab->table[code].nbits <= bits) continue; /* Generate a subtable. */ tab->table[code].code = -1; nbits = tab->table[code].nbits - bits; if (nbits > 7) nbits = 7; tab->table[code].nbits = bits; tab->table[code].nexttable = zlib_mkonetab(codes, lengths, nsyms, pfx | (code << pfxbits), pfxbits + bits, nbits); } return tab; } /* * Build a decode table, given a set of Huffman tree lengths. */ static struct zlib_table *zlib_mktable(unsigned char *lengths, int nlengths) { int count[MAXCODELEN], startcode[MAXCODELEN], codes[MAXSYMS]; int code, maxlen; int i, j; /* Count the codes of each length. */ maxlen = 0; for (i = 1; i < MAXCODELEN; i++) count[i] = 0; for (i = 0; i < nlengths; i++) { count[lengths[i]]++; if (maxlen < lengths[i]) maxlen = lengths[i]; } /* Determine the starting code for each length block. */ code = 0; for (i = 1; i < MAXCODELEN; i++) { startcode[i] = code; code += count[i]; code <<= 1; } /* Determine the code for each symbol. Mirrored, of course. */ for (i = 0; i < nlengths; i++) { code = startcode[lengths[i]]++; codes[i] = 0; for (j = 0; j < lengths[i]; j++) { codes[i] = (codes[i] << 1) | (code & 1); code >>= 1; } } /* * Now we have the complete list of Huffman codes. Build a * table. */ return zlib_mkonetab(codes, lengths, nlengths, 0, 0, maxlen < 9 ? maxlen : 9); } static int zlib_freetable(struct zlib_table **ztab) { struct zlib_table *tab; int code; if (ztab == NULL) return -1; if (*ztab == NULL) return 0; tab = *ztab; for (code = 0; code <= tab->mask; code++) if (tab->table[code].nexttable != NULL) zlib_freetable(&tab->table[code].nexttable); sfree(tab->table); tab->table = NULL; sfree(tab); *ztab = NULL; return (0); } struct zlib_decompress_ctx { struct zlib_table *staticlentable, *staticdisttable; struct zlib_table *currlentable, *currdisttable, *lenlentable; enum { START, OUTSIDEBLK, TREES_HDR, TREES_LENLEN, TREES_LEN, TREES_LENREP, INBLK, GOTLENSYM, GOTLEN, GOTDISTSYM, UNCOMP_LEN, UNCOMP_NLEN, UNCOMP_DATA } state; int sym, hlit, hdist, hclen, lenptr, lenextrabits, lenaddon, len, lenrep; int uncomplen; unsigned char lenlen[19]; /* * Array that accumulates the code lengths sent in the header of a * dynamic-Huffman-tree block. * * There are 286 actual symbols in the literal/length alphabet * (256 literals plus 20 length categories), and 30 symbols in the * distance alphabet. However, the block header transmits the * number of code lengths for the former alphabet as a 5-bit value * HLIT to be added to 257, and the latter as a 5-bit value HDIST * to be added to 1. This means that the number of _code lengths_ * can go as high as 288 for the symbol alphabet and 32 for the * distance alphabet - each of those values being 2 more than the * maximum number of actual symbols. * * It's tempting to rule that sending out-of-range HLIT or HDIST * is therefore just illegal, and to fault it when we initially * receive that header. But instead I've chosen to permit the * Huffman-code definition to include code length entries for * those unused symbols; if a header of that form is transmitted, * then the effect will be that in the main body of the block, * some bit sequence(s) will generate an illegal symbol number, * and _that_ will be faulted as a decoding error. * * Rationale: this can already happen! The standard Huffman code * used in a _static_ block for the literal/length alphabet is * defined in such a way that it includes codes for symbols 287 * and 288, which are then never actually sent in the body of the * block. And I think that if the standard static tree definition * is willing to include Huffman codes that don't correspond to a * symbol, then it's an excessive restriction on dynamic tables * not to permit them to do the same. In particular, it would be * strange for a dynamic block not to be able to exactly mimic * either or both of the Huffman codes used by a static block for * the corresponding alphabet. * * So we place no constraint on HLIT or HDIST during code * construction, and we make this array large enough to include * the maximum number of code lengths that can possibly arise as a * result. It's only trying to _use_ the junk Huffman codes after * table construction is completed that will provoke a decode * error. */ unsigned char lengths[288 + 32]; unsigned long bits; int nbits; unsigned char window[WINSIZE]; int winpos; strbuf *outblk; ssh_decompressor dc; }; ssh_decompressor *zlib_decompress_init(void) { struct zlib_decompress_ctx *dctx = snew(struct zlib_decompress_ctx); unsigned char lengths[288]; memset(lengths, 8, 144); memset(lengths + 144, 9, 256 - 144); memset(lengths + 256, 7, 280 - 256); memset(lengths + 280, 8, 288 - 280); dctx->staticlentable = zlib_mktable(lengths, 288); memset(lengths, 5, 32); dctx->staticdisttable = zlib_mktable(lengths, 32); dctx->state = START; /* even before header */ dctx->currlentable = dctx->currdisttable = dctx->lenlentable = NULL; dctx->bits = 0; dctx->nbits = 0; dctx->winpos = 0; dctx->outblk = NULL; dctx->dc.vt = &ssh_zlib; return &dctx->dc; } void zlib_decompress_cleanup(ssh_decompressor *dc) { struct zlib_decompress_ctx *dctx = container_of(dc, struct zlib_decompress_ctx, dc); if (dctx->currlentable && dctx->currlentable != dctx->staticlentable) zlib_freetable(&dctx->currlentable); if (dctx->currdisttable && dctx->currdisttable != dctx->staticdisttable) zlib_freetable(&dctx->currdisttable); if (dctx->lenlentable) zlib_freetable(&dctx->lenlentable); zlib_freetable(&dctx->staticlentable); zlib_freetable(&dctx->staticdisttable); if (dctx->outblk) strbuf_free(dctx->outblk); sfree(dctx); } static int zlib_huflookup(unsigned long *bitsp, int *nbitsp, struct zlib_table *tab) { unsigned long bits = *bitsp; int nbits = *nbitsp; while (1) { struct zlib_tableentry *ent; ent = &tab->table[bits & tab->mask]; if (ent->nbits > nbits) return -1; /* not enough data */ bits >>= ent->nbits; nbits -= ent->nbits; if (ent->code == -1) tab = ent->nexttable; else { *bitsp = bits; *nbitsp = nbits; return ent->code; } if (!tab) { /* * There was a missing entry in the table, presumably * due to an invalid Huffman table description, and the * subsequent data has attempted to use the missing * entry. Return a decoding failure. */ return -2; } } } static void zlib_emit_char(struct zlib_decompress_ctx *dctx, int c) { dctx->window[dctx->winpos] = c; dctx->winpos = (dctx->winpos + 1) & (WINSIZE - 1); put_byte(dctx->outblk, c); } #define EATBITS(n) ( dctx->nbits -= (n), dctx->bits >>= (n) ) bool zlib_decompress_block(ssh_decompressor *dc, const unsigned char *block, int len, unsigned char **outblock, int *outlen) { struct zlib_decompress_ctx *dctx = container_of(dc, struct zlib_decompress_ctx, dc); const coderecord *rec; int code, blktype, rep, dist, nlen, header; static const unsigned char lenlenmap[] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; assert(!dctx->outblk); dctx->outblk = strbuf_new_nm(); while (len > 0 || dctx->nbits > 0) { while (dctx->nbits < 24 && len > 0) { dctx->bits |= (*block++) << dctx->nbits; dctx->nbits += 8; len--; } switch (dctx->state) { case START: /* Expect 16-bit zlib header. */ if (dctx->nbits < 16) goto finished; /* done all we can */ /* * The header is stored as a big-endian 16-bit integer, * in contrast to the general little-endian policy in * the rest of the format :-( */ header = (((dctx->bits & 0xFF00) >> 8) | ((dctx->bits & 0x00FF) << 8)); EATBITS(16); /* * Check the header: * * - bits 8-11 should be 1000 (Deflate/RFC1951) * - bits 12-15 should be at most 0111 (window size) * - bit 5 should be zero (no dictionary present) * - we don't care about bits 6-7 (compression rate) * - bits 0-4 should be set up to make the whole thing * a multiple of 31 (checksum). */ if ((header & 0x0F00) != 0x0800 || (header & 0xF000) > 0x7000 || (header & 0x0020) != 0x0000 || (header % 31) != 0) goto decode_error; dctx->state = OUTSIDEBLK; break; case OUTSIDEBLK: /* Expect 3-bit block header. */ if (dctx->nbits < 3) goto finished; /* done all we can */ EATBITS(1); blktype = dctx->bits & 3; EATBITS(2); if (blktype == 0) { int to_eat = dctx->nbits & 7; dctx->state = UNCOMP_LEN; EATBITS(to_eat); /* align to byte boundary */ } else if (blktype == 1) { dctx->currlentable = dctx->staticlentable; dctx->currdisttable = dctx->staticdisttable; dctx->state = INBLK; } else if (blktype == 2) { dctx->state = TREES_HDR; } break; case TREES_HDR: /* * Dynamic block header. Five bits of HLIT, five of * HDIST, four of HCLEN. */ if (dctx->nbits < 5 + 5 + 4) goto finished; /* done all we can */ dctx->hlit = 257 + (dctx->bits & 31); EATBITS(5); dctx->hdist = 1 + (dctx->bits & 31); EATBITS(5); dctx->hclen = 4 + (dctx->bits & 15); EATBITS(4); dctx->lenptr = 0; dctx->state = TREES_LENLEN; memset(dctx->lenlen, 0, sizeof(dctx->lenlen)); break; case TREES_LENLEN: if (dctx->nbits < 3) goto finished; while (dctx->lenptr < dctx->hclen && dctx->nbits >= 3) { dctx->lenlen[lenlenmap[dctx->lenptr++]] = (unsigned char) (dctx->bits & 7); EATBITS(3); } if (dctx->lenptr == dctx->hclen) { dctx->lenlentable = zlib_mktable(dctx->lenlen, 19); dctx->state = TREES_LEN; dctx->lenptr = 0; } break; case TREES_LEN: if (dctx->lenptr >= dctx->hlit + dctx->hdist) { dctx->currlentable = zlib_mktable(dctx->lengths, dctx->hlit); dctx->currdisttable = zlib_mktable(dctx->lengths + dctx->hlit, dctx->hdist); zlib_freetable(&dctx->lenlentable); dctx->lenlentable = NULL; dctx->state = INBLK; break; } code = zlib_huflookup(&dctx->bits, &dctx->nbits, dctx->lenlentable); if (code == -1) goto finished; if (code == -2) goto decode_error; if (code < 16) dctx->lengths[dctx->lenptr++] = code; else { dctx->lenextrabits = (code == 16 ? 2 : code == 17 ? 3 : 7); dctx->lenaddon = (code == 18 ? 11 : 3); dctx->lenrep = (code == 16 && dctx->lenptr > 0 ? dctx->lengths[dctx->lenptr - 1] : 0); dctx->state = TREES_LENREP; } break; case TREES_LENREP: if (dctx->nbits < dctx->lenextrabits) goto finished; rep = dctx->lenaddon + (dctx->bits & ((1 << dctx->lenextrabits) - 1)); EATBITS(dctx->lenextrabits); while (rep > 0 && dctx->lenptr < dctx->hlit + dctx->hdist) { dctx->lengths[dctx->lenptr] = dctx->lenrep; dctx->lenptr++; rep--; } dctx->state = TREES_LEN; break; case INBLK: code = zlib_huflookup(&dctx->bits, &dctx->nbits, dctx->currlentable); if (code == -1) goto finished; if (code == -2) goto decode_error; if (code < 256) zlib_emit_char(dctx, code); else if (code == 256) { dctx->state = OUTSIDEBLK; if (dctx->currlentable != dctx->staticlentable) { zlib_freetable(&dctx->currlentable); dctx->currlentable = NULL; } if (dctx->currdisttable != dctx->staticdisttable) { zlib_freetable(&dctx->currdisttable); dctx->currdisttable = NULL; } } else if (code < 286) { dctx->state = GOTLENSYM; dctx->sym = code; } else { /* literal/length symbols 286 and 287 are invalid */ goto decode_error; } break; case GOTLENSYM: rec = &lencodes[dctx->sym - 257]; if (dctx->nbits < rec->extrabits) goto finished; dctx->len = rec->min + (dctx->bits & ((1 << rec->extrabits) - 1)); EATBITS(rec->extrabits); dctx->state = GOTLEN; break; case GOTLEN: code = zlib_huflookup(&dctx->bits, &dctx->nbits, dctx->currdisttable); if (code == -1) goto finished; if (code == -2) goto decode_error; if (code >= 30) /* dist symbols 30 and 31 are invalid */ goto decode_error; dctx->state = GOTDISTSYM; dctx->sym = code; break; case GOTDISTSYM: rec = &distcodes[dctx->sym]; if (dctx->nbits < rec->extrabits) goto finished; dist = rec->min + (dctx->bits & ((1 << rec->extrabits) - 1)); EATBITS(rec->extrabits); dctx->state = INBLK; while (dctx->len--) zlib_emit_char(dctx, dctx->window[(dctx->winpos - dist) & (WINSIZE - 1)]); break; case UNCOMP_LEN: /* * Uncompressed block. We expect to see a 16-bit LEN. */ if (dctx->nbits < 16) goto finished; dctx->uncomplen = dctx->bits & 0xFFFF; EATBITS(16); dctx->state = UNCOMP_NLEN; break; case UNCOMP_NLEN: /* * Uncompressed block. We expect to see a 16-bit NLEN, * which should be the one's complement of the previous * LEN. */ if (dctx->nbits < 16) goto finished; nlen = dctx->bits & 0xFFFF; EATBITS(16); if (dctx->uncomplen != (nlen ^ 0xFFFF)) goto decode_error; if (dctx->uncomplen == 0) dctx->state = OUTSIDEBLK; /* block is empty */ else dctx->state = UNCOMP_DATA; break; case UNCOMP_DATA: if (dctx->nbits < 8) goto finished; zlib_emit_char(dctx, dctx->bits & 0xFF); EATBITS(8); if (--dctx->uncomplen == 0) dctx->state = OUTSIDEBLK; /* end of uncompressed block */ break; } } finished: *outlen = dctx->outblk->len; *outblock = (unsigned char *)strbuf_to_str(dctx->outblk); dctx->outblk = NULL; return true; decode_error: *outblock = NULL; *outlen = 0; return false; } const ssh_compression_alg ssh_zlib = { .name = "zlib", .delayed_name = "zlib@openssh.com", /* delayed version */ .compress_new = zlib_compress_init, .compress_free = zlib_compress_cleanup, .compress = zlib_compress_block, .decompress_new = zlib_decompress_init, .decompress_free = zlib_decompress_cleanup, .decompress = zlib_decompress_block, .text_name = "zlib (RFC1950)", }; putty-0.76/storage.h0000644000175000017500000001020014072266312011365 00000000000000/* * 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. */ settings_w *open_settings_w(const char *sessionname, char **errmsg); void write_setting_s(settings_w *handle, const char *key, const char *value); void write_setting_i(settings_w *handle, const char *key, int value); void write_setting_filename(settings_w *handle, const char *key, Filename *value); void write_setting_fontspec(settings_w *handle, const char *key, FontSpec *font); void close_settings_w(settings_w *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. */ settings_r *open_settings_r(const char *sessionname); char *read_setting_s(settings_r *handle, const char *key); int read_setting_i(settings_r *handle, const char *key, int defvalue); Filename *read_setting_filename(settings_r *handle, const char *key); FontSpec *read_setting_fontspec(settings_r *handle, const char *key); void close_settings_r(settings_r *handle); /* * Delete a whole saved session. */ void del_settings(const char *sessionname); /* * Enumerate all saved sessions. */ settings_e *enum_settings_start(void); bool enum_settings_next(settings_e *handle, strbuf *out); void enum_settings_finish(settings_e *handle); /* ---------------------------------------------------------------------- * Functions to access PuTTY's host key database. */ /* * See if a host key matches the database entry. Return values can * be 0 (entry matches database), 1 (entry is absent in database), * or 2 (entry exists in database and is different). */ int verify_host_key(const char *hostname, int port, const char *keytype, const char *key); /* * Write a host key into the database, overwriting any previous * entry that might have been there. */ void store_host_key(const char *hostname, int port, const char *keytype, const char *key); /* ---------------------------------------------------------------------- * Functions to access PuTTY's random number seed file. */ typedef void (*noise_consumer_t) (void *data, int len); /* * Read PuTTY's random seed file and pass its contents to a noise * consumer function. */ void read_random_seed(noise_consumer_t consumer); /* * Write PuTTY's random seed file from a given chunk of noise. */ void write_random_seed(void *data, int len); /* ---------------------------------------------------------------------- * Cleanup function: remove all of PuTTY's persistent state. */ void cleanup_all(void); #endif putty-0.76/stripctrl.c0000644000175000017500000003466014072266312011762 00000000000000/* * stripctrl.c: a facility for stripping control characters out of a * data stream (defined as any multibyte character in the system * locale which is neither printable nor \n), using the standard C * library multibyte character facilities. */ #include #include #include #include #include #include "putty.h" #include "terminal.h" #include "misc.h" #include "marshal.h" #define SCC_BUFSIZE 64 #define LINE_LIMIT 77 typedef struct StripCtrlCharsImpl StripCtrlCharsImpl; struct StripCtrlCharsImpl { mbstate_t mbs_in, mbs_out; bool permit_cr; wchar_t substitution; char buf[SCC_BUFSIZE]; size_t buflen; Terminal *term; bool last_term_utf; struct term_utf8_decode utf8; unsigned long (*translate)(Terminal *, term_utf8_decode *, unsigned char); bool line_limit; bool line_start; size_t line_chars_remaining; BinarySink *bs_out; StripCtrlChars public; }; static void stripctrl_locale_BinarySink_write( BinarySink *bs, const void *vp, size_t len); static void stripctrl_term_BinarySink_write( BinarySink *bs, const void *vp, size_t len); static StripCtrlCharsImpl *stripctrl_new_common( BinarySink *bs_out, bool permit_cr, wchar_t substitution) { StripCtrlCharsImpl *scc = snew(StripCtrlCharsImpl); memset(scc, 0, sizeof(StripCtrlCharsImpl)); /* zeroes mbstates */ scc->bs_out = bs_out; scc->permit_cr = permit_cr; scc->substitution = substitution; return scc; } StripCtrlChars *stripctrl_new( BinarySink *bs_out, bool permit_cr, wchar_t substitution) { StripCtrlCharsImpl *scc = stripctrl_new_common( bs_out, permit_cr, substitution); BinarySink_INIT(&scc->public, stripctrl_locale_BinarySink_write); return &scc->public; } StripCtrlChars *stripctrl_new_term_fn( BinarySink *bs_out, bool permit_cr, wchar_t substitution, Terminal *term, unsigned long (*translate)( Terminal *, term_utf8_decode *, unsigned char)) { StripCtrlCharsImpl *scc = stripctrl_new_common( bs_out, permit_cr, substitution); scc->term = term; scc->translate = translate; BinarySink_INIT(&scc->public, stripctrl_term_BinarySink_write); return &scc->public; } void stripctrl_retarget(StripCtrlChars *sccpub, BinarySink *new_bs_out) { StripCtrlCharsImpl *scc = container_of(sccpub, StripCtrlCharsImpl, public); scc->bs_out = new_bs_out; stripctrl_reset(sccpub); } void stripctrl_reset(StripCtrlChars *sccpub) { StripCtrlCharsImpl *scc = container_of(sccpub, StripCtrlCharsImpl, public); /* * Clear all the fields that might have been in the middle of a * multibyte character or non-default shift state, so that we can * start converting a fresh piece of data to send to a channel * that hasn't seen the previous output. */ memset(&scc->utf8, 0, sizeof(scc->utf8)); memset(&scc->mbs_in, 0, sizeof(scc->mbs_in)); memset(&scc->mbs_out, 0, sizeof(scc->mbs_out)); /* * Also, reset the line-limiting system to its starting state. */ scc->line_start = true; } void stripctrl_free(StripCtrlChars *sccpub) { StripCtrlCharsImpl *scc = container_of(sccpub, StripCtrlCharsImpl, public); smemclr(scc, sizeof(StripCtrlCharsImpl)); sfree(scc); } void stripctrl_enable_line_limiting(StripCtrlChars *sccpub) { StripCtrlCharsImpl *scc = container_of(sccpub, StripCtrlCharsImpl, public); scc->line_limit = true; scc->line_start = true; } static inline bool stripctrl_ctrlchar_ok(StripCtrlCharsImpl *scc, wchar_t wc) { return wc == L'\n' || (wc == L'\r' && scc->permit_cr); } static inline void stripctrl_check_line_limit( StripCtrlCharsImpl *scc, wchar_t wc, size_t width) { if (!scc->line_limit) return; /* nothing to do */ if (scc->line_start) { put_datapl(scc->bs_out, PTRLEN_LITERAL("| ")); scc->line_start = false; scc->line_chars_remaining = LINE_LIMIT; } if (wc == '\n') { scc->line_start = true; return; } if (scc->line_chars_remaining < width) { put_datapl(scc->bs_out, PTRLEN_LITERAL("\r\n> ")); scc->line_chars_remaining = LINE_LIMIT; } assert(width <= scc->line_chars_remaining); scc->line_chars_remaining -= width; } static inline void stripctrl_locale_put_wc(StripCtrlCharsImpl *scc, wchar_t wc) { int width = mk_wcwidth(wc); if ((iswprint(wc) && width >= 0) || stripctrl_ctrlchar_ok(scc, wc)) { /* Printable character, or one we're going to let through anyway. */ if (width < 0) width = 0; /* sanitise for stripctrl_check_line_limit */ } else if (scc->substitution) { wc = scc->substitution; width = mk_wcwidth(wc); assert(width >= 0); } else { /* No defined substitution, so don't write any output wchar_t. */ return; } stripctrl_check_line_limit(scc, wc, width); char outbuf[MB_LEN_MAX]; size_t produced = wcrtomb(outbuf, wc, &scc->mbs_out); if (produced > 0) put_data(scc->bs_out, outbuf, produced); } static inline void stripctrl_term_put_wc( StripCtrlCharsImpl *scc, unsigned long wc) { ptrlen prefix = PTRLEN_LITERAL(""); int width = term_char_width(scc->term, wc); if (!(wc & ~0x9F) || width < 0) { /* This is something the terminal interprets as a control * character. */ if (!stripctrl_ctrlchar_ok(scc, wc)) { if (!scc->substitution) { return; } else { wc = scc->substitution; width = term_char_width(scc->term, wc); assert(width >= 0); } } else { if (width < 0) width = 0; /* sanitise for stripctrl_check_line_limit */ } if (wc == '\012') { /* Precede \n with \r, because our terminal will not * generally be in the ONLCR mode where it assumes that * internally, and any \r on input has been stripped * out. */ prefix = PTRLEN_LITERAL("\r"); } } stripctrl_check_line_limit(scc, wc, width); if (prefix.len) put_datapl(scc->bs_out, prefix); char outbuf[6]; size_t produced; /* * The Terminal implementation encodes 7-bit ASCII characters in * UTF-8 mode, and all printing characters in non-UTF-8 (i.e. * single-byte character set) mode, as values in the surrogate * range (a conveniently unused piece of space in this context) * whose low byte is the original 1-byte representation of the * character. */ if ((wc - 0xD800) < (0xE000 - 0xD800)) wc &= 0xFF; if (in_utf(scc->term)) { produced = encode_utf8(outbuf, wc); } else { outbuf[0] = wc; produced = 1; } if (produced > 0) put_data(scc->bs_out, outbuf, produced); } static inline size_t stripctrl_locale_try_consume( StripCtrlCharsImpl *scc, const char *p, size_t len) { wchar_t wc; mbstate_t mbs_orig = scc->mbs_in; size_t consumed = mbrtowc(&wc, p, len, &scc->mbs_in); if (consumed == (size_t)-2) { /* * The buffer is too short to see the end of the multibyte * character that it appears to be starting with. We return 0 * for 'no data consumed', restore the conversion state from * before consuming the partial character, and our caller will * come back when it has more data available. */ scc->mbs_in = mbs_orig; return 0; } if (consumed == (size_t)-1) { /* * The buffer contains an illegal multibyte sequence. There's * no really good way to recover from this, so we'll just * reset our input state, consume a single byte without * emitting anything, and hope we can resynchronise to * _something_ sooner or later. */ memset(&scc->mbs_in, 0, sizeof(scc->mbs_in)); return 1; } if (consumed == 0) { /* * A zero wide character is encoded by the data, but mbrtowc * hasn't told us how many input bytes it takes. There isn't * really anything good we can do here, so we just advance by * one byte in the hope that that was the NUL. * * (If it wasn't - that is, if we're in a multibyte encoding * in which the terminator of a normal C string is encoded in * some way other than a single zero byte - then probably lots * of other things will have gone wrong before we get here!) */ stripctrl_locale_put_wc(scc, L'\0'); return 1; } /* * Otherwise, this is the easy case: consumed > 0, and we've eaten * a valid multibyte character. */ stripctrl_locale_put_wc(scc, wc); return consumed; } static void stripctrl_locale_BinarySink_write( BinarySink *bs, const void *vp, size_t len) { StripCtrlChars *sccpub = BinarySink_DOWNCAST(bs, StripCtrlChars); StripCtrlCharsImpl *scc = container_of(sccpub, StripCtrlCharsImpl, public); const char *p = (const char *)vp; const char *previous_locale = setlocale(LC_CTYPE, NULL); setlocale(LC_CTYPE, ""); /* * Deal with any partial multibyte character buffered from last * time. */ while (scc->buflen > 0) { size_t to_copy = SCC_BUFSIZE - scc->buflen; if (to_copy > len) to_copy = len; memcpy(scc->buf + scc->buflen, p, to_copy); size_t consumed = stripctrl_locale_try_consume( scc, scc->buf, scc->buflen + to_copy); if (consumed >= scc->buflen) { /* * We've consumed a multibyte character that includes all * the data buffered from last time. So we can clear our * buffer and move on to processing the main input string * in situ, having first discarded whatever initial * segment of it completed our previous character. */ size_t consumed_from_main_string = consumed - scc->buflen; assert(consumed_from_main_string <= len); p += consumed_from_main_string; len -= consumed_from_main_string; scc->buflen = 0; break; } if (consumed == 0) { /* * If we didn't manage to consume anything, i.e. the whole * buffer contains an incomplete sequence, it had better * be because our entire input string _this_ time plus * whatever leftover data we had from _last_ time still * comes to less than SCC_BUFSIZE. In other words, we've * already copied all the new data on to the end of our * buffer, and it still hasn't helped. So increment buflen * to reflect the new data, and return. */ assert(to_copy == len); scc->buflen += to_copy; goto out; } /* * Otherwise, we've somehow consumed _less_ data than we had * buffered, and yet we weren't able to consume that data in * the last call to this function. That sounds impossible, but * I can think of one situation in which it could happen: if * we had an incomplete MB sequence last time, and now more * data has arrived, it turns out to be an _illegal_ one, so * we consume one byte in the hope of resynchronising. * * Anyway, in this case we move the buffer up and go back * round this initial loop. */ scc->buflen -= consumed; memmove(scc->buf, scc->buf + consumed, scc->buflen); } /* * Now charge along the main string. */ while (len > 0) { size_t consumed = stripctrl_locale_try_consume(scc, p, len); if (consumed == 0) break; assert(consumed <= len); p += consumed; len -= consumed; } /* * Any data remaining should be copied into our buffer, to keep * for next time. */ assert(len <= SCC_BUFSIZE); memcpy(scc->buf, p, len); scc->buflen = len; out: setlocale(LC_CTYPE, previous_locale); } static void stripctrl_term_BinarySink_write( BinarySink *bs, const void *vp, size_t len) { StripCtrlChars *sccpub = BinarySink_DOWNCAST(bs, StripCtrlChars); StripCtrlCharsImpl *scc = container_of(sccpub, StripCtrlCharsImpl, public); bool utf = in_utf(scc->term); if (utf != scc->last_term_utf) { scc->last_term_utf = utf; scc->utf8.state = 0; } for (const unsigned char *p = (const unsigned char *)vp; len > 0; len--, p++) { unsigned long t = scc->translate(scc->term, &scc->utf8, *p); if (t == UCSTRUNCATED) { stripctrl_term_put_wc(scc, 0xFFFD); /* go round again */ t = scc->translate(scc->term, &scc->utf8, *p); } if (t == UCSINCOMPLETE) continue; if (t == UCSINVALID) t = 0xFFFD; stripctrl_term_put_wc(scc, t); } } char *stripctrl_string_ptrlen(StripCtrlChars *sccpub, ptrlen str) { strbuf *out = strbuf_new(); stripctrl_retarget(sccpub, BinarySink_UPCAST(out)); put_datapl(sccpub, str); stripctrl_retarget(sccpub, NULL); return strbuf_to_str(out); } #ifdef STRIPCTRL_TEST /* gcc -std=c99 -DSTRIPCTRL_TEST -o scctest stripctrl.c marshal.c utils.c memory.c wcwidth.c -I . -I unix -I charset */ void out_of_memory(void) { fprintf(stderr, "out of memory\n"); abort(); } void stripctrl_write(BinarySink *bs, const void *vdata, size_t len) { const uint8_t *p = vdata; printf("["); for (size_t i = 0; i < len; i++) printf("%*s%02x", i?1:0, "", (unsigned)p[i]); printf("]"); } void stripctrl_test(StripCtrlChars *scc, ptrlen pl) { stripctrl_write(NULL, pl.ptr, pl.len); printf(" -> "); put_datapl(scc, pl); printf("\n"); } int main(void) { struct foo { BinarySink_IMPLEMENTATION; } foo; BinarySink_INIT(&foo, stripctrl_write); StripCtrlChars *scc = stripctrl_new(BinarySink_UPCAST(&foo), false, '?'); stripctrl_test(scc, PTRLEN_LITERAL("a\033[1mb")); stripctrl_test(scc, PTRLEN_LITERAL("a\xC2\x9B[1mb")); stripctrl_test(scc, PTRLEN_LITERAL("a\xC2\xC2[1mb")); stripctrl_test(scc, PTRLEN_LITERAL("\xC3")); stripctrl_test(scc, PTRLEN_LITERAL("\xA9")); stripctrl_test(scc, PTRLEN_LITERAL("\xE2\x80\x8F")); stripctrl_test(scc, PTRLEN_LITERAL("a\0b")); stripctrl_free(scc); return 0; } #endif /* STRIPCTRL_TEST */ putty-0.76/supdup.c0000644000175000017500000007025714072266312011256 00000000000000/* * Supdup backend */ #include #include #include #include "putty.h" /* * TTYOPT FUNCTION BITS (36-bit bitmasks) */ #define TOALT 0200000000000LL // Characters 0175 and 0176 are converted to altmode (0033) on input #define TOCLC 0100000000000LL // (user option bit) Convert lower-case input to upper-case #define TOERS 0040000000000LL // Selective erase is supported #define TOMVB 0010000000000LL // Backspacing is supported #define TOSAI 0004000000000LL // Stanford/ITS extended ASCII graphics character set is supported #define TOSA1 0002000000000LL // (user option bit) Characters 0001-0037 displayed using Stanford/ITS chars #define TOOVR 0001000000000LL // Overprinting is supported #define TOMVU 0000400000000LL // Moving cursor upwards is supported #define TOMOR 0000200000000LL // (user option bit) System should provide **MORE** processing #define TOROL 0000100000000LL // (user option bit) Terminal should scroll instead of wrapping #define TOLWR 0000020000000LL // Lowercase characters are supported #define TOFCI 0000010000000LL // Terminal can generate CONTROL and META characters #define TOLID 0000002000000LL // Line insert/delete operations supported #define TOCID 0000001000000LL // Character insert/delete operations supported #define TPCBS 0000000000040LL // Terminal is using the "intelligent terminal protocol" (must be on) #define TPORS 0000000000010LL // Server should process output resets // Initialization words (36-bit constants) #define WORDS 0777773000000 // Negative number of config words to send (6) in high 18 bits #define TCTYP 0000000000007 // Defines the terminal type (MUST be 7) #define TTYROL 0000000000001 // Scroll amount for terminal (1 line at a time) // %TD opcodes // #define TDMOV 0200 // Cursor positioning #define TDMV1 0201 // Internal cursor positioning #define TDEOF 0202 // Erase to end of screen #define TDEOL 0203 // Erase to end of line #define TDDLF 0204 // Clear the character the cursor is on #define TDCRL 0207 // Carriage return #define TDNOP 0210 // No-op; should be ignored. #define TDBS 0211 // Backspace (not in official SUPDUP spec) #define TDLF 0212 // Linefeed (not in official SUPDUP spec) #define TDCR 0213 // Carriage Return (ditto) #define TDORS 0214 // Output reset #define TDQOT 0215 // Quotes the following character #define TDFS 0216 // Non-destructive forward space #define TDMV0 0217 // General cursor positioning code #define TDCLR 0220 // Erase the screen, home cursor #define TDBEL 0221 // Generate an audio tone, bell, whatever #define TDILP 0223 // Insert blank lines at the cursor #define TDDLP 0224 // Delete lines at the cursor #define TDICP 0225 // Insert blanks at cursor #define TDDCP 0226 // Delete characters at cursor #define TDBOW 0227 // Display black chars on white screen #define TDRST 0230 // Reset %TDBOW /* Maximum number of octets following a %TD code. */ #define TD_ARGS_MAX 4 typedef struct supdup_tag Supdup; struct supdup_tag { Socket *s; bool closed_on_socket_error; Seat *seat; LogContext *logctx; int term_width, term_height; long long ttyopt; long tcmxv; long tcmxh; bool sent_location; Conf *conf; int bufsize; enum { CONNECTING, // waiting for %TDNOP from server after sending connection params CONNECTED // %TDNOP received, connected. } state; enum { TD_TOPLEVEL, TD_ARGS, TD_ARGSDONE } tdstate; int td_code; int td_argcount; char td_args[TD_ARGS_MAX]; int td_argindex; void (*print) (strbuf *outbuf, int c); Pinger *pinger; Plug plug; Backend backend; }; #define SUPDUP_MAX_BACKLOG 4096 static void c_write(Supdup *supdup, unsigned char *buf, int len) { size_t backlog = seat_stdout(supdup->seat, buf, len); sk_set_frozen(supdup->s, backlog > SUPDUP_MAX_BACKLOG); } static void supdup_send_location(Supdup *supdup) { char locHeader[] = { 0300, 0302 }; char* locString = conf_get_str(supdup->conf, CONF_supdup_location); sk_write(supdup->s, locHeader, sizeof(locHeader)); sk_write(supdup->s, locString, strlen(locString) + 1); // include NULL terminator } static void print_ascii(strbuf *outbuf, int c) { /* In ASCII mode, ignore control characters. The server shouldn't send them. */ if (c >= 040 && c < 0177) put_byte (outbuf, c); } static void print_its(strbuf *outbuf, int c) { /* The ITS character set is documented in RFC 734. */ static const char *map[] = { "\xc2\xb7", "\342\206\223", "\316\261", "\316\262", "\342\210\247", "\302\254", "\316\265", "\317\200", "\316\273", "\xce\xb3", "\xce\xb4", "\xe2\x86\x91", "\xc2\xb1", "\xe2\x8a\x95", "\342\210\236", "\342\210\202", "\342\212\202", "\342\212\203", "\342\210\251", "\342\210\252", "\342\210\200", "\342\210\203", "\xe2\x8a\x97", "\342\206\224", "\xe2\x86\x90", "\342\206\222", "\xe2\x89\xa0", "\xe2\x97\x8a", "\342\211\244", "\342\211\245", "\342\211\241", "\342\210\250", " ", "!", "\"", "#", "$", "%", "&", "'", "(", ")", "*", "+", ",", "-", ".", "/", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ":", ";", "<", "=", ">", "?", "@", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "[", "\\", "]", "^", "_", "`", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "{", "|", "}", "~", "\xe2\x88\xab" }; put_data (outbuf, map[c], strlen(map[c])); } static void print_waits(strbuf *outbuf, int c) { /* The WAITS character set used at the Stanford AI Lab is documented here: https://www.saildart.org/allow/sail-charset-utf8.html */ static const char *map[] = { "", "\342\206\223", "\316\261", "\316\262", "\342\210\247", "\302\254", "\316\265", "\317\200", "\316\273", "", "", "", "", "", "\342\210\236", "\342\210\202", "\342\212\202", "\342\212\203", "\342\210\251", "\342\210\252", "\342\210\200", "\342\210\203", "\xe2\x8a\x97", "\342\206\224", "_", "\342\206\222", "~", "\xe2\x89\xa0", "\342\211\244", "\342\211\245", "\342\211\241", "\342\210\250", " ", "!", "\"", "#", "$", "%", "&", "'", "(", ")", "*", "+", ",", "-", ".", "/", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ":", ";", "<", "=", ">", "?", "@", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "[", "\\", "]", "\xe2\x86\x91", "\xe2\x86\x90", "`", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "{", "|", "\xe2\x97\x8a", "}", "" }; put_data (outbuf, map[c], strlen(map[c])); } static void do_toplevel(Supdup *supdup, strbuf *outbuf, int c) { // Toplevel: Waiting for a %TD code or a printable character if (c >= 0200) { // Handle SUPDUP %TD codes (codes greater than or equal to 200) supdup->td_argindex = 0; supdup->td_code = c; switch (c) { case TDMOV: // %TD codes using 4 arguments supdup->td_argcount = 4; supdup->tdstate = TD_ARGS; break; case TDMV0: case TDMV1: // %TD codes using 2 arguments supdup->td_argcount = 2; supdup->tdstate = TD_ARGS; break; case TDQOT: case TDILP: case TDDLP: case TDICP: case TDDCP: // %TD codes using 1 argument supdup->td_argcount = 1; supdup->tdstate = TD_ARGS; break; case TDEOF: case TDEOL: case TDDLF: case TDCRL: case TDNOP: case TDORS: case TDFS: case TDCLR: case TDBEL: case TDBOW: case TDRST: case TDBS: case TDCR: case TDLF: // %TD codes using 0 arguments supdup->td_argcount = 0; supdup->tdstate = TD_ARGSDONE; break; default: // Unhandled, ignore break; } } else { supdup->print(outbuf, c); } } static void do_args(Supdup *supdup, strbuf *outbuf, int c) { // Collect up args for %TD code if (supdup->td_argindex < TD_ARGS_MAX) { supdup->td_args[supdup->td_argindex] = c; supdup->td_argindex++; if (supdup->td_argcount == supdup->td_argindex) { // No more args, %TD code is ready to go. supdup->tdstate = TD_ARGSDONE; } } else { // Should never hit this state, if we do we will just // return to TOPLEVEL. supdup->tdstate = TD_TOPLEVEL; } } static void do_argsdone(Supdup *supdup, strbuf *outbuf, int c) { char buf[4]; int x, y; // Arguments for %TD code have been collected; dispatch based // on the %TD code we're handling. switch (supdup->td_code) { case TDMOV: /* General cursor position code. Followed by four bytes; the first two are the "old" vertical and horizontal positions and may be ignored. The next two are the new vertical and horizontal positions. The cursor should be moved to this position. */ // We only care about the new position. strbuf_catf(outbuf, "\033[%d;%dH", supdup->td_args[2]+1, supdup->td_args[3]+1); break; case TDMV0: case TDMV1: /* General cursor position code. Followed by two bytes; the new vertical and horizontal positions. */ strbuf_catf(outbuf, "\033[%d;%dH", supdup->td_args[0]+1, supdup->td_args[1]+1); break; case TDEOF: /* Erase to end of screen. This is an optional function since many terminals do not support this. If the terminal does not support this function, it should be treated the same as %TDEOL. %TDEOF does an erase to end of line, then erases all lines lower on the screen than the cursor. The cursor does not move. */ strbuf_catf(outbuf, "\033[J"); break; case TDEOL: /* Erase to end of line. This erases the character position the cursor is at and all positions to the right on the same line. The cursor does not move. */ strbuf_catf(outbuf, "\033[K"); break; case TDDLF: /* Clear the character position the cursor is on. The cursor does not move. */ strbuf_catf(outbuf, "\033[X"); break; case TDCRL: /* If the cursor is not on the bottom line of the screen, move cursor to the beginning of the next line and clear that line. If the cursor is at the bottom line, scroll up. */ strbuf_catf(outbuf, "\015\012"); break; case TDNOP: /* No-op; should be ignored. */ break; case TDORS: /* Output reset. This code serves as a data mark for aborting output much as IAC DM does in the ordinary TELNET protocol. */ outbuf->len = 0; if (!seat_get_cursor_position(supdup->seat, &x, &y)) x = y = 0; buf[0] = 034; buf[1] = 020; buf[2] = y; buf[3] = x; sk_write(supdup->s, buf, 4); break; case TDQOT: /* Quotes the following character. This is used when sending 8-bit codes which are not %TD codes, for instance when loading programs into an intelligent terminal. The following character should be passed through intact to the terminal. */ put_byte(outbuf, supdup->td_args[0]); break; case TDFS: /* Non-destructive forward space. The cursor moves right one position; this code will not be sent at the end of a line. */ strbuf_catf(outbuf, "\033[C"); break; case TDCLR: /* Erase the screen. Home the cursor to the top left hand corner of the screen. */ strbuf_catf(outbuf, "\033[2J\033[H"); break; case TDBEL: /* Generate an audio tone, bell, whatever. */ strbuf_catf(outbuf, "\007"); break; case TDILP: /* Insert blank lines at the cursor; followed by a byte containing a count of the number of blank lines to insert. The cursor is unmoved. The line the cursor is on and all lines below it move down; lines moved off the bottom of the screen are lost. */ strbuf_catf(outbuf, "\033[%dL", supdup->td_args[0]); break; case TDDLP: /* Delete lines at the cursor; followed by a count. The cursor is unmoved. The first line deleted is the one the cursor is on. Lines below those deleted move up. Newly- created lines at the bottom of the screen are blank. */ strbuf_catf(outbuf, "\033[%dM", supdup->td_args[0]); break; case TDICP: /* Insert blank character positions at the cursor; followed by a count. The cursor is unmoved. The character the cursor is on and all characters to the right on the current line move to the right; characters moved off the end of the line are lost. */ strbuf_catf(outbuf, "\033[%d@", supdup->td_args[0]); break; case TDDCP: /* Delete characters at the cursor; followed by a count. The cursor is unmoved. The first character deleted is the one the cursor is on. Newly-created characters at the end of the line are blank. */ strbuf_catf(outbuf, "\033[%dP", supdup->td_args[0]); break; case TDBOW: case TDRST: /* Display black characters on white screen. HIGHLY OPTIONAL. */ // Since this is HIGHLY OPTIONAL, I'm not going // to implement it yet. break; /* * Non-standard (whatever "standard" means here) SUPDUP * commands. These are used (at the very least) by * Genera's SUPDUP implementation. Cannot find any * official documentation, behavior is based on UNIX * SUPDUP implementation from MIT. */ case TDBS: /* * Backspace -- move cursor back one character (does not * appear to wrap...) */ put_byte(outbuf, '\010'); break; case TDLF: /* * Linefeed -- move cursor down one line (again, no wrapping) */ put_byte(outbuf, '\012'); break; case TDCR: /* * Carriage return -- move cursor to start of current line. */ put_byte(outbuf, '\015'); break; } // Return to top level to pick up the next %TD code or // printable character. supdup->tdstate = TD_TOPLEVEL; } static void term_out_supdup(Supdup *supdup, strbuf *outbuf, int c) { if (supdup->tdstate == TD_TOPLEVEL) { do_toplevel (supdup, outbuf, c); } else if (supdup->tdstate == TD_ARGS) { do_args (supdup, outbuf, c); } // If all arguments for a %TD code are ready, we will execute the code now. if (supdup->tdstate == TD_ARGSDONE) { do_argsdone (supdup, outbuf, c); } } static void do_supdup_read(Supdup *supdup, const char *buf, size_t len) { strbuf *outbuf = strbuf_new(); while (len--) { int c = (unsigned char)*buf++; switch (supdup->state) { case CONNECTING: // "Following the transmission of the terminal options by // the user, the server should respond with an ASCII // greeting message, terminated with a %TDNOP code..." if (TDNOP == c) { // Greeting done, switch to the CONNECTED state. supdup->state = CONNECTED; supdup->tdstate = TD_TOPLEVEL; } else { // Forward the greeting message (which is straight // ASCII, no controls) on so it gets displayed TODO: // filter out only printable chars? put_byte(outbuf, c); } break; case CONNECTED: // "All transmissions from the server after the %TDNOP // [see above] are either printing characters or virtual // terminal display codes." Forward these on to the // frontend which will decide what to do with them. term_out_supdup(supdup, outbuf, c); /* * Hack to make Symbolics Genera SUPDUP happy: Wait until * after we're connected (finished the initial handshake * and have gotten additional data) before sending the * location string. For some reason doing so earlier * causes the Symbolics SUPDUP to end up in an odd state. */ if (!supdup->sent_location) { supdup_send_location(supdup); supdup->sent_location = true; } break; } if (outbuf->len >= 4096) { c_write(supdup, outbuf->u, outbuf->len); outbuf->len = 0; } } if (outbuf->len) c_write(supdup, outbuf->u, outbuf->len); strbuf_free(outbuf); } static void supdup_log(Plug *plug, PlugLogType type, SockAddr *addr, int port, const char *error_msg, int error_code) { Supdup *supdup = container_of(plug, Supdup, plug); backend_socket_log(supdup->seat, supdup->logctx, type, addr, port, error_msg, error_code, supdup->conf, supdup->state != CONNECTING); } static void supdup_closing(Plug *plug, const char *error_msg, int error_code, bool calling_back) { Supdup *supdup = container_of(plug, Supdup, 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 (supdup->s) { sk_close(supdup->s); supdup->s = NULL; if (error_msg) supdup->closed_on_socket_error = true; seat_notify_remote_exit(supdup->seat); } if (error_msg) { logevent(supdup->logctx, error_msg); seat_connection_fatal(supdup->seat, "%s", error_msg); } /* Otherwise, the remote side closed the connection normally. */ } static void supdup_receive(Plug *plug, int urgent, const char *data, size_t len) { Supdup *supdup = container_of(plug, Supdup, plug); do_supdup_read(supdup, data, len); } static void supdup_sent(Plug *plug, size_t bufsize) { Supdup *supdup = container_of(plug, Supdup, plug); supdup->bufsize = bufsize; } static void supdup_send_36bits(Supdup *supdup, unsigned long long thirtysix) { // // From RFC734: // "Each word is sent through the 8-bit connection as six // 6-bit bytes, most-significant first." // // Split the 36-bit word into 6 6-bit "bytes", packed into // 8-bit bytes and send, most-significant byte first. // for (int i = 5; i >= 0; i--) { char sixBits = (thirtysix >> (i * 6)) & 077; sk_write(supdup->s, &sixBits, 1); } } static void supdup_send_config(Supdup *supdup) { supdup_send_36bits(supdup, WORDS); // negative length supdup_send_36bits(supdup, TCTYP); // terminal type supdup_send_36bits(supdup, supdup->ttyopt); // options supdup_send_36bits(supdup, supdup->tcmxv); // height supdup_send_36bits(supdup, supdup->tcmxh); // width supdup_send_36bits(supdup, TTYROL); // scroll amount } /* * Called to set up the Supdup 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 char *supdup_init(const BackendVtable *x, Seat *seat, Backend **backend_handle, LogContext *logctx, Conf *conf, const char *host, int port, char **realhost, bool nodelay, bool keepalive) { static const PlugVtable fn_table = { .log = supdup_log, .closing = supdup_closing, .receive = supdup_receive, .sent = supdup_sent, }; SockAddr *addr; const char *err; Supdup *supdup; char *loghost; int addressfamily; const char *utf8 = "\033%G"; supdup = snew(struct supdup_tag); supdup->plug.vt = &fn_table; supdup->backend.vt = &supdup_backend; supdup->logctx = logctx; supdup->conf = conf_copy(conf); supdup->s = NULL; supdup->closed_on_socket_error = false; supdup->seat = seat; supdup->term_width = conf_get_int(supdup->conf, CONF_width); supdup->term_height = conf_get_int(supdup->conf, CONF_height); supdup->pinger = NULL; supdup->sent_location = false; *backend_handle = &supdup->backend; switch (conf_get_int(supdup->conf, CONF_supdup_ascii_set)) { case SUPDUP_CHARSET_ASCII: supdup->print = print_ascii; break; case SUPDUP_CHARSET_ITS: supdup->print = print_its; break; case SUPDUP_CHARSET_WAITS: supdup->print = print_waits; break; } /* * Try to find host. */ { char *buf; addressfamily = conf_get_int(supdup->conf, CONF_addressfamily); buf = dupprintf("Looking up host \"%s\"%s", host, (addressfamily == ADDRTYPE_IPV4 ? " (IPv4)" : (addressfamily == ADDRTYPE_IPV6 ? " (IPv6)" : ""))); logevent(supdup->logctx, buf); sfree(buf); } addr = name_lookup(host, port, realhost, supdup->conf, addressfamily, NULL, ""); if ((err = sk_addr_error(addr)) != NULL) { sk_addr_free(addr); return dupstr(err); } if (port < 0) port = 0137; /* default supdup port */ /* * Open socket. */ supdup->s = new_connection(addr, *realhost, port, false, true, nodelay, keepalive, &supdup->plug, supdup->conf); if ((err = sk_socket_error(supdup->s)) != NULL) return dupstr(err); supdup->pinger = pinger_new(supdup->conf, &supdup->backend); /* * We can send special commands from the start. */ seat_update_specials_menu(supdup->seat); /* * loghost overrides realhost, if specified. */ loghost = conf_get_str(supdup->conf, CONF_loghost); if (*loghost) { char *colon; sfree(*realhost); *realhost = dupstr(loghost); colon = host_strrchr(*realhost, ':'); if (colon) *colon++ = '\0'; } /* * Set up TTYOPTS based on config */ int ascii_set = conf_get_int(supdup->conf, CONF_supdup_ascii_set); int more_processing = conf_get_bool(supdup->conf, CONF_supdup_more); int scrolling = conf_get_bool(supdup->conf, CONF_supdup_scroll); supdup->ttyopt = TOERS | TOMVB | (ascii_set == SUPDUP_CHARSET_ASCII ? 0 : TOSAI | TOSA1) | TOMVU | TOLWR | TOLID | TOCID | TPCBS | (scrolling ? TOROL : 0) | (more_processing ? TOMOR : 0) | TPORS; supdup->tcmxh = supdup->term_width - 1; // -1 "..one column is used to indicate line continuation." supdup->tcmxv = supdup->term_height; /* * Send our configuration words to the server */ supdup_send_config(supdup); /* * We next expect a connection message followed by %TDNOP from the server */ supdup->state = CONNECTING; seat_set_trust_status(supdup->seat, false); /* Make sure the terminal is in UTF-8 mode. */ c_write(supdup, (unsigned char *)utf8, strlen(utf8)); return NULL; } static void supdup_free(Backend *be) { Supdup *supdup = container_of(be, Supdup, backend); if (supdup->s) sk_close(supdup->s); if (supdup->pinger) pinger_free(supdup->pinger); conf_free(supdup->conf); sfree(supdup); } /* * Reconfigure the Supdup backend. */ static void supdup_reconfig(Backend *be, Conf *conf) { /* Nothing to do; SUPDUP cannot be reconfigured while running. */ } /* * Called to send data down the Supdup connection. */ static size_t supdup_send(Backend *be, const char *buf, size_t len) { Supdup *supdup = container_of(be, Supdup, backend); char c; int i; if (supdup->s == NULL) return 0; for (i = 0; i < len; i++) { if (buf[i] == 034) supdup->bufsize = sk_write(supdup->s, "\034\034", 2); else { c = buf[i] & 0177; supdup->bufsize = sk_write(supdup->s, &c, 1); } } return supdup->bufsize; } /* * Called to query the current socket sendability status. */ static size_t supdup_sendbuffer(Backend *be) { Supdup *supdup = container_of(be, Supdup, backend); return supdup->bufsize; } /* * Called to set the size of the window from Supdup's POV. */ static void supdup_size(Backend *be, int width, int height) { Supdup *supdup = container_of(be, Supdup, backend); supdup->term_width = width; supdup->term_height = height; // // SUPDUP does not support resizing the terminal after connection // establishment. // } /* * Send Telnet special codes. */ static void supdup_special(Backend *be, SessionSpecialCode code, int arg) { } static const SessionSpecial *supdup_get_specials(Backend *be) { return NULL; } static bool supdup_connected(Backend *be) { Supdup *supdup = container_of(be, Supdup, backend); return supdup->s != NULL; } static bool supdup_sendok(Backend *be) { return 1; } static void supdup_unthrottle(Backend *be, size_t backlog) { Supdup *supdup = container_of(be, Supdup, backend); sk_set_frozen(supdup->s, backlog > SUPDUP_MAX_BACKLOG); } static bool supdup_ldisc(Backend *be, int option) { /* No support for echoing or local editing. */ return false; } static void supdup_provide_ldisc(Backend *be, Ldisc *ldisc) { } static int supdup_exitcode(Backend *be) { Supdup *supdup = container_of(be, Supdup, backend); if (supdup->s != NULL) return -1; /* still connected */ else if (supdup->closed_on_socket_error) return INT_MAX; /* a socket error counts as an unclean exit */ else /* Supdup doesn't transmit exit codes back to the client */ return 0; } /* * cfg_info for Dupdup does nothing at all. */ static int supdup_cfg_info(Backend *be) { return 0; } const BackendVtable supdup_backend = { .init = supdup_init, .free = supdup_free, .reconfig = supdup_reconfig, .send = supdup_send, .sendbuffer = supdup_sendbuffer, .size = supdup_size, .special = supdup_special, .get_specials = supdup_get_specials, .connected = supdup_connected, .exitcode = supdup_exitcode, .sendok = supdup_sendok, .ldisc_option_state = supdup_ldisc, .provide_ldisc = supdup_provide_ldisc, .unthrottle = supdup_unthrottle, .cfg_info = supdup_cfg_info, .id = "supdup", .displayname = "SUPDUP", .protocol = PROT_SUPDUP, .default_port = 0137, .flags = BACKEND_RESIZE_FORBIDDEN | BACKEND_NEEDS_TERMINAL, }; putty-0.76/telnet.c0000644000175000017500000010523114072266312011220 00000000000000/* * Telnet backend. */ #include #include #include #include "putty.h" #define IAC 255 /* interpret as command: */ #define DONT 254 /* you are not to use option */ #define DO 253 /* please, you use option */ #define WONT 252 /* I won't use option */ #define WILL 251 /* I will use option */ #define SB 250 /* interpret as subnegotiation */ #define SE 240 /* end sub negotiation */ #define GA 249 /* you may reverse the line */ #define EL 248 /* erase the current line */ #define EC 247 /* erase the current character */ #define AYT 246 /* are you there */ #define AO 245 /* abort output--but let prog finish */ #define IP 244 /* interrupt process--permanently */ #define BREAK 243 /* break */ #define DM 242 /* data mark--for connect. cleaning */ #define NOP 241 /* nop */ #define EOR 239 /* end of record (transparent mode) */ #define ABORT 238 /* Abort process */ #define SUSP 237 /* Suspend process */ #define xEOF 236 /* End of file: EOF is already used... */ #define TELOPTS(X) \ X(BINARY, 0) /* 8-bit data path */ \ X(ECHO, 1) /* echo */ \ X(RCP, 2) /* prepare to reconnect */ \ X(SGA, 3) /* suppress go ahead */ \ X(NAMS, 4) /* approximate message size */ \ X(STATUS, 5) /* give status */ \ X(TM, 6) /* timing mark */ \ X(RCTE, 7) /* remote controlled transmission and echo */ \ X(NAOL, 8) /* negotiate about output line width */ \ X(NAOP, 9) /* negotiate about output page size */ \ X(NAOCRD, 10) /* negotiate about CR disposition */ \ X(NAOHTS, 11) /* negotiate about horizontal tabstops */ \ X(NAOHTD, 12) /* negotiate about horizontal tab disposition */ \ X(NAOFFD, 13) /* negotiate about formfeed disposition */ \ X(NAOVTS, 14) /* negotiate about vertical tab stops */ \ X(NAOVTD, 15) /* negotiate about vertical tab disposition */ \ X(NAOLFD, 16) /* negotiate about output LF disposition */ \ X(XASCII, 17) /* extended ascic character set */ \ X(LOGOUT, 18) /* force logout */ \ X(BM, 19) /* byte macro */ \ X(DET, 20) /* data entry terminal */ \ X(SUPDUP, 21) /* supdup protocol */ \ X(SUPDUPOUTPUT, 22) /* supdup output */ \ X(SNDLOC, 23) /* send location */ \ X(TTYPE, 24) /* terminal type */ \ X(EOR, 25) /* end or record */ \ X(TUID, 26) /* TACACS user identification */ \ X(OUTMRK, 27) /* output marking */ \ X(TTYLOC, 28) /* terminal location number */ \ X(3270REGIME, 29) /* 3270 regime */ \ X(X3PAD, 30) /* X.3 PAD */ \ X(NAWS, 31) /* window size */ \ X(TSPEED, 32) /* terminal speed */ \ X(LFLOW, 33) /* remote flow control */ \ X(LINEMODE, 34) /* Linemode option */ \ X(XDISPLOC, 35) /* X Display Location */ \ X(OLD_ENVIRON, 36) /* Old - Environment variables */ \ X(AUTHENTICATION, 37) /* Authenticate */ \ X(ENCRYPT, 38) /* Encryption option */ \ X(NEW_ENVIRON, 39) /* New - Environment variables */ \ X(TN3270E, 40) /* TN3270 enhancements */ \ X(XAUTH, 41) \ X(CHARSET, 42) /* Character set */ \ X(RSP, 43) /* Remote serial port */ \ X(COM_PORT_OPTION, 44) /* Com port control */ \ X(SLE, 45) /* Suppress local echo */ \ X(STARTTLS, 46) /* Start TLS */ \ X(KERMIT, 47) /* Automatic Kermit file transfer */ \ X(SEND_URL, 48) \ X(FORWARD_X, 49) \ X(PRAGMA_LOGON, 138) \ X(SSPI_LOGON, 139) \ X(PRAGMA_HEARTBEAT, 140) \ X(EXOPL, 255) /* extended-options-list */ #define telnet_enum(x,y) TELOPT_##x = y, enum { TELOPTS(telnet_enum) dummy=0 }; #undef telnet_enum #define TELQUAL_IS 0 /* option is... */ #define TELQUAL_SEND 1 /* send option */ #define TELQUAL_INFO 2 /* ENVIRON: informational version of IS */ #define BSD_VAR 1 #define BSD_VALUE 0 #define RFC_VAR 0 #define RFC_VALUE 1 #define CR 13 #define LF 10 #define NUL 0 #define iswritable(x) \ ( (x) != IAC && \ (telnet->opt_states[o_we_bin.index] == ACTIVE || (x) != CR)) static const char *telopt(int opt) { #define telnet_str(x,y) case TELOPT_##x: return #x; switch (opt) { TELOPTS(telnet_str) default: return ""; } #undef telnet_str } struct Opt { int send; /* what we initially send */ int nsend; /* -ve send if requested to stop it */ int ack, nak; /* +ve and -ve acknowledgements */ int option; /* the option code */ int index; /* index into telnet->opt_states[] */ enum { REQUESTED, ACTIVE, INACTIVE, REALLY_INACTIVE } initial_state; }; enum { OPTINDEX_NAWS, OPTINDEX_TSPEED, OPTINDEX_TTYPE, OPTINDEX_OENV, OPTINDEX_NENV, OPTINDEX_ECHO, OPTINDEX_WE_SGA, OPTINDEX_THEY_SGA, OPTINDEX_WE_BIN, OPTINDEX_THEY_BIN, NUM_OPTS }; static const struct Opt o_naws = { WILL, WONT, DO, DONT, TELOPT_NAWS, OPTINDEX_NAWS, REQUESTED }; static const struct Opt o_tspeed = { WILL, WONT, DO, DONT, TELOPT_TSPEED, OPTINDEX_TSPEED, REQUESTED }; static const struct Opt o_ttype = { WILL, WONT, DO, DONT, TELOPT_TTYPE, OPTINDEX_TTYPE, REQUESTED }; static const struct Opt o_oenv = { WILL, WONT, DO, DONT, TELOPT_OLD_ENVIRON, OPTINDEX_OENV, INACTIVE }; static const struct Opt o_nenv = { WILL, WONT, DO, DONT, TELOPT_NEW_ENVIRON, OPTINDEX_NENV, REQUESTED }; static const struct Opt o_echo = { DO, DONT, WILL, WONT, TELOPT_ECHO, OPTINDEX_ECHO, REQUESTED }; static const struct Opt o_we_sga = { WILL, WONT, DO, DONT, TELOPT_SGA, OPTINDEX_WE_SGA, REQUESTED }; static const struct Opt o_they_sga = { DO, DONT, WILL, WONT, TELOPT_SGA, OPTINDEX_THEY_SGA, REQUESTED }; static const struct Opt o_we_bin = { WILL, WONT, DO, DONT, TELOPT_BINARY, OPTINDEX_WE_BIN, INACTIVE }; static const struct Opt o_they_bin = { DO, DONT, WILL, WONT, TELOPT_BINARY, OPTINDEX_THEY_BIN, INACTIVE }; static const struct Opt *const opts[] = { &o_naws, &o_tspeed, &o_ttype, &o_oenv, &o_nenv, &o_echo, &o_we_sga, &o_they_sga, &o_we_bin, &o_they_bin, NULL }; typedef struct Telnet Telnet; struct Telnet { Socket *s; bool closed_on_socket_error; Seat *seat; LogContext *logctx; Ldisc *ldisc; int term_width, term_height; int opt_states[NUM_OPTS]; bool echoing, editing; bool activated; size_t bufsize; bool in_synch; int sb_opt; strbuf *sb_buf; bool session_started; enum { TOP_LEVEL, SEENIAC, SEENWILL, SEENWONT, SEENDO, SEENDONT, SEENSB, SUBNEGOT, SUBNEG_IAC, SEENCR } state; Conf *conf; Pinger *pinger; Plug plug; Backend backend; }; #define TELNET_MAX_BACKLOG 4096 #define SB_DELTA 1024 static void c_write(Telnet *telnet, const void *buf, size_t len) { size_t backlog = seat_stdout(telnet->seat, buf, len); sk_set_frozen(telnet->s, backlog > TELNET_MAX_BACKLOG); } static void log_option(Telnet *telnet, const char *sender, int cmd, int option) { /* * The strange-looking "" below is there to avoid a * trigraph - a double question mark followed by > maps to a * closing brace character! */ logeventf(telnet->logctx, "%s:\t%s %s", sender, (cmd == WILL ? "WILL" : cmd == WONT ? "WONT" : cmd == DO ? "DO" : cmd == DONT ? "DONT" : ""), telopt(option)); } static void send_opt(Telnet *telnet, int cmd, int option) { unsigned char b[3]; b[0] = IAC; b[1] = cmd; b[2] = option; telnet->bufsize = sk_write(telnet->s, b, 3); log_option(telnet, "client", cmd, option); } static void deactivate_option(Telnet *telnet, const struct Opt *o) { if (telnet->opt_states[o->index] == REQUESTED || telnet->opt_states[o->index] == ACTIVE) send_opt(telnet, o->nsend, o->option); telnet->opt_states[o->index] = REALLY_INACTIVE; } /* * Generate side effects of enabling or disabling an option. */ static void option_side_effects( Telnet *telnet, const struct Opt *o, bool enabled) { if (o->option == TELOPT_ECHO && o->send == DO) telnet->echoing = !enabled; else if (o->option == TELOPT_SGA && o->send == DO) telnet->editing = !enabled; if (telnet->ldisc) /* cause ldisc to notice the change */ ldisc_echoedit_update(telnet->ldisc); /* Ensure we get the minimum options */ if (!telnet->activated) { if (telnet->opt_states[o_echo.index] == INACTIVE) { telnet->opt_states[o_echo.index] = REQUESTED; send_opt(telnet, o_echo.send, o_echo.option); } if (telnet->opt_states[o_we_sga.index] == INACTIVE) { telnet->opt_states[o_we_sga.index] = REQUESTED; send_opt(telnet, o_we_sga.send, o_we_sga.option); } if (telnet->opt_states[o_they_sga.index] == INACTIVE) { telnet->opt_states[o_they_sga.index] = REQUESTED; send_opt(telnet, o_they_sga.send, o_they_sga.option); } telnet->activated = true; } } static void activate_option(Telnet *telnet, const struct Opt *o) { if (o->send == WILL && o->option == TELOPT_NAWS) backend_size(&telnet->backend, telnet->term_width, telnet->term_height); if (o->send == WILL && (o->option == TELOPT_NEW_ENVIRON || o->option == TELOPT_OLD_ENVIRON)) { /* * We may only have one kind of ENVIRON going at a time. * This is a hack, but who cares. */ deactivate_option(telnet, o->option == TELOPT_NEW_ENVIRON ? &o_oenv : &o_nenv); } option_side_effects(telnet, o, true); } static void refused_option(Telnet *telnet, const struct Opt *o) { if (o->send == WILL && o->option == TELOPT_NEW_ENVIRON && telnet->opt_states[o_oenv.index] == INACTIVE) { send_opt(telnet, WILL, TELOPT_OLD_ENVIRON); telnet->opt_states[o_oenv.index] = REQUESTED; } option_side_effects(telnet, o, false); } static void proc_rec_opt(Telnet *telnet, int cmd, int option) { const struct Opt *const *o; log_option(telnet, "server", cmd, option); for (o = opts; *o; o++) { if ((*o)->option == option && (*o)->ack == cmd) { switch (telnet->opt_states[(*o)->index]) { case REQUESTED: telnet->opt_states[(*o)->index] = ACTIVE; activate_option(telnet, *o); break; case ACTIVE: break; case INACTIVE: telnet->opt_states[(*o)->index] = ACTIVE; send_opt(telnet, (*o)->send, option); activate_option(telnet, *o); break; case REALLY_INACTIVE: send_opt(telnet, (*o)->nsend, option); break; } return; } else if ((*o)->option == option && (*o)->nak == cmd) { switch (telnet->opt_states[(*o)->index]) { case REQUESTED: telnet->opt_states[(*o)->index] = INACTIVE; refused_option(telnet, *o); break; case ACTIVE: telnet->opt_states[(*o)->index] = INACTIVE; send_opt(telnet, (*o)->nsend, option); option_side_effects(telnet, *o, false); break; case INACTIVE: case REALLY_INACTIVE: break; } return; } } /* * If we reach here, the option was one we weren't prepared to * cope with. If the request was positive (WILL or DO), we send * a negative ack to indicate refusal. If the request was * negative (WONT / DONT), we must do nothing. */ 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_buf->len == 1 && telnet->sb_buf->u[0] == TELQUAL_SEND) { 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, b, n + 2); logevent(telnet->logctx, "server:\tSB TSPEED SEND"); logeventf(telnet->logctx, "client:\tSB TSPEED IS %s", termspeed); sfree(b); } else logevent(telnet->logctx, "server:\tSB TSPEED "); break; case TELOPT_TTYPE: if (telnet->sb_buf->len == 1 && telnet->sb_buf->u[0] == TELQUAL_SEND) { 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, b, n + 6); b[n + 4] = 0; logevent(telnet->logctx, "server:\tSB TTYPE SEND"); logeventf(telnet->logctx, "client:\tSB TTYPE IS %s", b + 4); sfree(b); } else logevent(telnet->logctx, "server:\tSB TTYPE \r\n"); break; case TELOPT_OLD_ENVIRON: case TELOPT_NEW_ENVIRON: p = telnet->sb_buf->u; q = p + telnet->sb_buf->len; if (p < q && *p == TELQUAL_SEND) { p++; logeventf(telnet->logctx, "server:\tSB %s SEND", telopt(telnet->sb_opt)); if (telnet->sb_opt == TELOPT_OLD_ENVIRON) { if (conf_get_bool(telnet->conf, CONF_rfc_environ)) { value = RFC_VALUE; var = RFC_VAR; } else { value = BSD_VALUE; var = BSD_VAR; } /* * Try to guess the sense of VAR and VALUE. */ while (p < q) { if (*p == RFC_VAR) { value = RFC_VALUE; var = RFC_VAR; } else if (*p == BSD_VAR) { value = BSD_VALUE; var = BSD_VAR; } p++; } } else { /* * 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, b, n); if (n == 6) { logeventf(telnet->logctx, "client:\tSB %s IS ", telopt(telnet->sb_opt)); } else { logeventf(telnet->logctx, "client:\tSB %s IS:", telopt(telnet->sb_opt)); 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)) { logeventf(telnet->logctx, "\t%s=%s", ekey, eval); } if (user) logeventf(telnet->logctx, "\tUSER=%s", user); } sfree(b); sfree(user); } break; } } static void do_telnet_read(Telnet *telnet, const char *buf, size_t len) { strbuf *outbuf = strbuf_new_nm(); while (len--) { int c = (unsigned char) *buf++; switch (telnet->state) { case TOP_LEVEL: case SEENCR: if (c == NUL && telnet->state == SEENCR) telnet->state = TOP_LEVEL; else if (c == IAC) telnet->state = SEENIAC; else { if (!telnet->in_synch) put_byte(outbuf, c); #if 1 /* I can't get the F***ing winsock to insert the urgent IAC * into the right position! Even with SO_OOBINLINE it gives * it to recv too soon. And of course the DM byte (that * arrives in the same packet!) appears several K later!! * * Oh well, we do get the DM in the right place so I'll * just stop hiding on the next 0xf2 and hope for the best. */ else if (c == DM) telnet->in_synch = false; #endif if (c == CR && telnet->opt_states[o_they_bin.index] != ACTIVE) telnet->state = SEENCR; else telnet->state = TOP_LEVEL; } break; case SEENIAC: if (c == DO) telnet->state = SEENDO; else if (c == DONT) telnet->state = SEENDONT; else if (c == WILL) telnet->state = SEENWILL; else if (c == WONT) telnet->state = SEENWONT; else if (c == SB) telnet->state = SEENSB; else if (c == DM) { telnet->in_synch = false; telnet->state = TOP_LEVEL; } else { /* ignore everything else; print it if it's IAC */ if (c == IAC) { put_byte(outbuf, c); } telnet->state = TOP_LEVEL; } break; case SEENWILL: proc_rec_opt(telnet, WILL, c); telnet->state = TOP_LEVEL; break; case SEENWONT: proc_rec_opt(telnet, WONT, c); telnet->state = TOP_LEVEL; break; case SEENDO: proc_rec_opt(telnet, DO, c); telnet->state = TOP_LEVEL; break; case SEENDONT: proc_rec_opt(telnet, DONT, c); telnet->state = TOP_LEVEL; break; case SEENSB: telnet->sb_opt = c; strbuf_clear(telnet->sb_buf); telnet->state = SUBNEGOT; break; case SUBNEGOT: if (c == IAC) telnet->state = SUBNEG_IAC; else { subneg_addchar: put_byte(telnet->sb_buf, c); telnet->state = SUBNEGOT; /* in case we came here by goto */ } break; case SUBNEG_IAC: if (c != SE) goto subneg_addchar; /* yes, it's a hack, I know, but... */ else { process_subneg(telnet); telnet->state = TOP_LEVEL; } break; } if (outbuf->len >= 4096) { c_write(telnet, outbuf->u, outbuf->len); strbuf_clear(outbuf); } } if (outbuf->len) c_write(telnet, outbuf->u, outbuf->len); strbuf_free(outbuf); } static void telnet_log(Plug *plug, PlugLogType type, SockAddr *addr, int port, const char *error_msg, int error_code) { Telnet *telnet = container_of(plug, Telnet, plug); backend_socket_log(telnet->seat, telnet->logctx, type, addr, port, error_msg, error_code, telnet->conf, telnet->session_started); } static void telnet_closing(Plug *plug, const char *error_msg, int error_code, bool calling_back) { Telnet *telnet = container_of(plug, 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; seat_notify_remote_exit(telnet->seat); } if (error_msg) { logevent(telnet->logctx, error_msg); seat_connection_fatal(telnet->seat, "%s", error_msg); } /* Otherwise, the remote side closed the connection normally. */ } static void telnet_receive( Plug *plug, int urgent, const char *data, size_t len) { Telnet *telnet = container_of(plug, Telnet, plug); if (urgent) telnet->in_synch = true; telnet->session_started = true; do_telnet_read(telnet, data, len); } static void telnet_sent(Plug *plug, size_t bufsize) { Telnet *telnet = container_of(plug, Telnet, plug); telnet->bufsize = bufsize; } static const PlugVtable Telnet_plugvt = { .log = telnet_log, .closing = telnet_closing, .receive = telnet_receive, .sent = telnet_sent, }; /* * Called to set up the Telnet 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 char *telnet_init(const BackendVtable *vt, Seat *seat, Backend **backend_handle, LogContext *logctx, Conf *conf, const char *host, int port, char **realhost, bool nodelay, bool keepalive) { SockAddr *addr; const char *err; Telnet *telnet; char *loghost; int addressfamily; /* No local authentication phase in this protocol */ seat_set_trust_status(seat, false); telnet = snew(Telnet); telnet->plug.vt = &Telnet_plugvt; telnet->backend.vt = vt; 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 = strbuf_new(); telnet->seat = seat; telnet->logctx = logctx; 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; telnet->session_started = true; *backend_handle = &telnet->backend; /* * Try to find host. */ addressfamily = conf_get_int(telnet->conf, CONF_addressfamily); addr = name_lookup(host, port, realhost, telnet->conf, addressfamily, telnet->logctx, "Telnet connection"); if ((err = sk_addr_error(addr)) != NULL) { sk_addr_free(addr); return dupstr(err); } if (port < 0) port = 23; /* default telnet port */ /* * Open socket. */ telnet->s = new_connection(addr, *realhost, port, false, true, nodelay, keepalive, &telnet->plug, telnet->conf); if ((err = sk_socket_error(telnet->s)) != NULL) return dupstr(err); telnet->pinger = pinger_new(telnet->conf, &telnet->backend); /* * Initialise option states. */ if (conf_get_bool(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; for (o = opts; *o; o++) { telnet->opt_states[(*o)->index] = (*o)->initial_state; if (telnet->opt_states[(*o)->index] == REQUESTED) send_opt(telnet, (*o)->send, (*o)->option); } telnet->activated = true; } /* * Set up SYNCH state. */ telnet->in_synch = false; /* * We can send special commands from the start. */ seat_update_specials_menu(telnet->seat); /* * loghost overrides realhost, if specified. */ loghost = conf_get_str(telnet->conf, CONF_loghost); if (*loghost) { char *colon; sfree(*realhost); *realhost = dupstr(loghost); colon = host_strrchr(*realhost, ':'); if (colon) *colon++ = '\0'; } return NULL; } static void telnet_free(Backend *be) { Telnet *telnet = container_of(be, Telnet, backend); strbuf_free(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(Backend *be, Conf *conf) { Telnet *telnet = container_of(be, Telnet, backend); 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 size_t telnet_send(Backend *be, const char *buf, size_t len) { Telnet *telnet = container_of(be, Telnet, backend); unsigned char *p, *end; static const unsigned char iac[2] = { IAC, IAC }; static const unsigned char cr[2] = { CR, NUL }; #if 0 static const unsigned char nl[2] = { CR, LF }; #endif if (telnet->s == NULL) return 0; p = (unsigned char *)buf; end = (unsigned char *)(buf + len); while (p < end) { unsigned char *q = p; while (p < end && iswritable(*p)) p++; telnet->bufsize = sk_write(telnet->s, q, p - q); while (p < end && !iswritable(*p)) { telnet->bufsize = sk_write(telnet->s, *p == IAC ? iac : cr, 2); p++; } } return telnet->bufsize; } /* * Called to query the current socket sendability status. */ static size_t telnet_sendbuffer(Backend *be) { Telnet *telnet = container_of(be, Telnet, backend); return telnet->bufsize; } /* * Called to set the size of the window from Telnet's POV. */ static void telnet_size(Backend *be, int width, int height) { Telnet *telnet = container_of(be, Telnet, backend); unsigned char b[24]; int n; telnet->term_width = width; telnet->term_height = height; if (telnet->s == NULL || telnet->opt_states[o_naws.index] != ACTIVE) return; n = 0; b[n++] = IAC; b[n++] = SB; b[n++] = TELOPT_NAWS; b[n++] = telnet->term_width >> 8; if (b[n-1] == IAC) b[n++] = IAC; /* duplicate any IAC byte occurs */ b[n++] = telnet->term_width & 0xFF; if (b[n-1] == IAC) b[n++] = IAC; /* duplicate any IAC byte occurs */ b[n++] = telnet->term_height >> 8; if (b[n-1] == IAC) b[n++] = IAC; /* duplicate any IAC byte occurs */ b[n++] = telnet->term_height & 0xFF; if (b[n-1] == IAC) b[n++] = IAC; /* duplicate any IAC byte occurs */ b[n++] = IAC; b[n++] = SE; telnet->bufsize = sk_write(telnet->s, b, n); logeventf(telnet->logctx, "client:\tSB NAWS %d,%d", telnet->term_width, telnet->term_height); } /* * Send Telnet special codes. */ static void telnet_special(Backend *be, SessionSpecialCode code, int arg) { Telnet *telnet = container_of(be, Telnet, backend); unsigned char b[2]; if (telnet->s == NULL) return; b[0] = IAC; switch (code) { case SS_AYT: b[1] = AYT; telnet->bufsize = sk_write(telnet->s, b, 2); break; case SS_BRK: b[1] = BREAK; telnet->bufsize = sk_write(telnet->s, b, 2); break; case SS_EC: b[1] = EC; telnet->bufsize = sk_write(telnet->s, b, 2); break; case SS_EL: b[1] = EL; telnet->bufsize = sk_write(telnet->s, b, 2); break; case SS_GA: b[1] = GA; telnet->bufsize = sk_write(telnet->s, b, 2); break; case SS_NOP: b[1] = NOP; telnet->bufsize = sk_write(telnet->s, b, 2); break; case SS_ABORT: b[1] = ABORT; telnet->bufsize = sk_write(telnet->s, b, 2); break; case SS_AO: b[1] = AO; telnet->bufsize = sk_write(telnet->s, b, 2); break; case SS_IP: b[1] = IP; telnet->bufsize = sk_write(telnet->s, b, 2); break; case SS_SUSP: b[1] = SUSP; telnet->bufsize = sk_write(telnet->s, b, 2); break; case SS_EOR: b[1] = EOR; telnet->bufsize = sk_write(telnet->s, b, 2); break; case SS_EOF: b[1] = xEOF; telnet->bufsize = sk_write(telnet->s, b, 2); break; case SS_EOL: /* In BINARY mode, CR-LF becomes just CR - * and without the NUL suffix too. */ if (telnet->opt_states[o_we_bin.index] == ACTIVE) telnet->bufsize = sk_write(telnet->s, "\r", 1); else telnet->bufsize = sk_write(telnet->s, "\r\n", 2); break; case SS_SYNCH: b[1] = DM; telnet->bufsize = sk_write(telnet->s, b, 1); telnet->bufsize = sk_write_oob(telnet->s, b + 1, 1); break; case SS_PING: if (telnet->opt_states[o_they_sga.index] == ACTIVE) { b[1] = NOP; telnet->bufsize = sk_write(telnet->s, b, 2); } break; default: break; /* never heard of it */ } } static const SessionSpecial *telnet_get_specials(Backend *be) { static const SessionSpecial specials[] = { {"Are You There", SS_AYT}, {"Break", SS_BRK}, {"Synch", SS_SYNCH}, {"Erase Character", SS_EC}, {"Erase Line", SS_EL}, {"Go Ahead", SS_GA}, {"No Operation", SS_NOP}, {NULL, SS_SEP}, {"Abort Process", SS_ABORT}, {"Abort Output", SS_AO}, {"Interrupt Process", SS_IP}, {"Suspend Process", SS_SUSP}, {NULL, SS_SEP}, {"End Of Record", SS_EOR}, {"End Of File", SS_EOF}, {NULL, SS_EXITMENU} }; return specials; } static bool telnet_connected(Backend *be) { Telnet *telnet = container_of(be, Telnet, backend); return telnet->s != NULL; } static bool telnet_sendok(Backend *be) { /* Telnet *telnet = container_of(be, Telnet, backend); */ return true; } static void telnet_unthrottle(Backend *be, size_t backlog) { Telnet *telnet = container_of(be, Telnet, backend); sk_set_frozen(telnet->s, backlog > TELNET_MAX_BACKLOG); } static bool telnet_ldisc(Backend *be, int option) { Telnet *telnet = container_of(be, Telnet, backend); if (option == LD_ECHO) return telnet->echoing; if (option == LD_EDIT) return telnet->editing; return false; } static void telnet_provide_ldisc(Backend *be, Ldisc *ldisc) { Telnet *telnet = container_of(be, Telnet, backend); telnet->ldisc = ldisc; } static int telnet_exitcode(Backend *be) { Telnet *telnet = container_of(be, Telnet, backend); 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. */ static int telnet_cfg_info(Backend *be) { return 0; } const BackendVtable telnet_backend = { .init = telnet_init, .free = telnet_free, .reconfig = telnet_reconfig, .send = telnet_send, .sendbuffer = telnet_sendbuffer, .size = telnet_size, .special = telnet_special, .get_specials = telnet_get_specials, .connected = telnet_connected, .exitcode = telnet_exitcode, .sendok = telnet_sendok, .ldisc_option_state = telnet_ldisc, .provide_ldisc = telnet_provide_ldisc, .unthrottle = telnet_unthrottle, .cfg_info = telnet_cfg_info, .id = "telnet", .displayname = "Telnet", .protocol = PROT_TELNET, .default_port = 23, }; putty-0.76/terminal.c0000644000175000017500000104441514072266312011547 00000000000000/* * Terminal emulator. */ #include #include #include #include #include #include #include #include "putty.h" #include "terminal.h" #define VT52_PLUS #define CL_ANSIMIN 0x0001 /* Codes in all ANSI like terminals. */ #define CL_VT100 0x0002 /* VT100 */ #define CL_VT100AVO 0x0004 /* VT100 +AVO; 132x24 (not 132x14) & attrs */ #define CL_VT102 0x0008 /* VT102 */ #define CL_VT220 0x0010 /* VT220 */ #define CL_VT320 0x0020 /* VT320 */ #define CL_VT420 0x0040 /* VT420 */ #define CL_VT510 0x0080 /* VT510, NB VT510 includes ANSI */ #define CL_VT340TEXT 0x0100 /* VT340 extensions that appear in the VT420 */ #define CL_SCOANSI 0x1000 /* SCOANSI not in ANSIMIN. */ #define CL_ANSI 0x2000 /* ANSI ECMA-48 not in the VT100..VT420 */ #define CL_OTHER 0x4000 /* Others, Xterm, linux, putty, dunno, etc */ #define TM_VT100 (CL_ANSIMIN|CL_VT100) #define TM_VT100AVO (TM_VT100|CL_VT100AVO) #define TM_VT102 (TM_VT100AVO|CL_VT102) #define TM_VT220 (TM_VT102|CL_VT220) #define TM_VTXXX (TM_VT220|CL_VT340TEXT|CL_VT510|CL_VT420|CL_VT320) #define TM_SCOANSI (CL_ANSIMIN|CL_SCOANSI) #define TM_PUTTY (0xFFFF) #define UPDATE_DELAY ((TICKSPERSEC+49)/50)/* ticks to defer window update */ #define TBLINK_DELAY ((TICKSPERSEC*9+19)/20)/* ticks between text blinks*/ #define CBLINK_DELAY (CURSORBLINK) /* ticks between cursor blinks */ #define VBELL_DELAY (VBELL_TIMEOUT) /* visual bell timeout in ticks */ #define compatibility(x) \ if ( ((CL_##x)&term->compatibility_level) == 0 ) { \ term->termstate=TOPLEVEL; \ break; \ } #define compatibility2(x,y) \ if ( ((CL_##x|CL_##y)&term->compatibility_level) == 0 ) { \ term->termstate=TOPLEVEL; \ break; \ } #define has_compat(x) ( ((CL_##x)&term->compatibility_level) != 0 ) static const char *const EMPTY_WINDOW_TITLE = ""; static const char sco2ansicolour[] = { 0, 4, 2, 6, 1, 5, 3, 7 }; #define sel_nl_sz (sizeof(sel_nl)/sizeof(wchar_t)) static const wchar_t sel_nl[] = SEL_NL; /* * Fetch the character at a particular position in a line array, * for purposes of `wordtype'. The reason this isn't just a simple * array reference is that if the character we find is UCSWIDE, * then we must look one space further to the left. */ #define UCSGET(a, x) \ ( (x)>0 && (a)[(x)].chr == UCSWIDE ? (a)[(x)-1].chr : (a)[(x)].chr ) /* * Detect the various aliases of U+0020 SPACE. */ #define IS_SPACE_CHR(chr) \ ((chr) == 0x20 || (DIRECT_CHAR(chr) && ((chr) & 0xFF) == 0x20)) /* * Spot magic CSETs. */ #define CSET_OF(chr) (DIRECT_CHAR(chr)||DIRECT_FONT(chr) ? (chr)&CSET_MASK : 0) /* * Internal prototypes. */ static void resizeline(Terminal *, termline *, int); static termline *lineptr(Terminal *, int, int, int); static void unlineptr(termline *); static void check_line_size(Terminal *, termline *); static void do_paint(Terminal *); static void erase_lots(Terminal *, bool, bool, bool); static int find_last_nonempty_line(Terminal *, tree234 *); static void swap_screen(Terminal *, int, bool, bool); static void update_sbar(Terminal *); static void deselect(Terminal *); static void term_print_finish(Terminal *); static void scroll(Terminal *, int, int, int, bool); static void parse_optionalrgb(optionalrgb *out, unsigned *values); static void term_added_data(Terminal *term); static void term_update_raw_mouse_mode(Terminal *term); static termline *newtermline(Terminal *term, int cols, bool bce) { termline *line; int j; line = snew(termline); line->chars = snewn(cols, termchar); for (j = 0; j < cols; j++) line->chars[j] = (bce ? term->erase_char : term->basic_erase_char); line->cols = line->size = cols; line->lattr = LATTR_NORM; line->trusted = false; line->temporary = false; line->cc_free = 0; return line; } static void freetermline(termline *line) { if (line) { sfree(line->chars); sfree(line); } } static void unlineptr(termline *line) { if (line->temporary) freetermline(line); } const int colour_indices_conf_to_oscp[CONF_NCOLOURS] = { #define COLOUR_ENTRY(id,name) OSCP_COLOUR_##id, CONF_COLOUR_LIST(COLOUR_ENTRY) #undef COLOUR_ENTRY }; const int colour_indices_conf_to_osc4[CONF_NCOLOURS] = { #define COLOUR_ENTRY(id,name) OSC4_COLOUR_##id, CONF_COLOUR_LIST(COLOUR_ENTRY) #undef COLOUR_ENTRY }; const int colour_indices_oscp_to_osc4[OSCP_NCOLOURS] = { #define COLOUR_ENTRY(id) OSC4_COLOUR_##id, OSCP_COLOUR_LIST(COLOUR_ENTRY) #undef COLOUR_ENTRY }; #ifdef TERM_CC_DIAGS /* * Diagnostic function: verify that a termline has a correct * combining character structure. * * This is a performance-intensive check, so it's no longer enabled * by default. */ static void cc_check(termline *line) { unsigned char *flags; int i, j; assert(line->size >= line->cols); flags = snewn(line->size, unsigned char); for (i = 0; i < line->size; i++) flags[i] = (i < line->cols); for (i = 0; i < line->cols; i++) { j = i; while (line->chars[j].cc_next) { j += line->chars[j].cc_next; assert(j >= line->cols && j < line->size); assert(!flags[j]); flags[j] = true; } } j = line->cc_free; if (j) { while (1) { assert(j >= line->cols && j < line->size); assert(!flags[j]); flags[j] = true; if (line->chars[j].cc_next) j += line->chars[j].cc_next; else break; } } j = 0; for (i = 0; i < line->size; i++) j += (flags[i] != 0); assert(j == line->size); sfree(flags); } #endif static void clear_cc(termline *line, int col); /* * Add a combining character to a character cell. */ static void add_cc(termline *line, int col, unsigned long chr) { int newcc; assert(col >= 0 && col < line->cols); /* * Don't add combining characters at all to U+FFFD REPLACEMENT * CHARACTER. (Partly it's a slightly incoherent idea in the first * place; mostly, U+FFFD is what we generate if a cell already has * too many ccs, in which case we want it to be a fixed point when * further ccs are added.) */ if (line->chars[col].chr == 0xFFFD) return; /* * Walk the cc list of the cell in question to find its current * end point. */ size_t ncc = 0; int origcol = col; while (line->chars[col].cc_next) { col += line->chars[col].cc_next; if (++ncc >= CC_LIMIT) { /* * There are already too many combining characters in this * character cell. Change strategy: throw out the entire * chain and replace the main character with U+FFFD. * * (Rationale: extrapolating from UTR #36 section 3.6.2 * suggests the principle that it's better to substitute * U+FFFD than to _ignore_ input completely. Also, if the * user copies and pastes an overcombined character cell, * this way it will clearly indicate that we haven't * reproduced the writer's original intentions, instead of * looking as if it was the _writer's_ fault that the 33rd * cc is missing.) * * Per the code above, this will also prevent any further * ccs from being added to this cell. */ clear_cc(line, origcol); line->chars[origcol].chr = 0xFFFD; return; } } /* * Extend the cols array if the free list is empty. */ if (!line->cc_free) { int n = line->size; size_t tmpsize = line->size; sgrowarray(line->chars, tmpsize, tmpsize); assert(tmpsize <= INT_MAX); line->size = tmpsize; line->cc_free = n; while (n < line->size) { if (n+1 < line->size) line->chars[n].cc_next = 1; else line->chars[n].cc_next = 0; n++; } } /* * `col' now points at the last cc currently in this cell; so * we simply add another one. */ newcc = line->cc_free; if (line->chars[newcc].cc_next) line->cc_free = newcc + line->chars[newcc].cc_next; else line->cc_free = 0; line->chars[newcc].cc_next = 0; line->chars[newcc].chr = chr; line->chars[col].cc_next = newcc - col; #ifdef TERM_CC_DIAGS cc_check(line); #endif } /* * Clear the combining character list in a character cell. */ static void clear_cc(termline *line, int col) { int oldfree, origcol = col; assert(col >= 0 && col < line->cols); if (!line->chars[col].cc_next) return; /* nothing needs doing */ oldfree = line->cc_free; line->cc_free = col + line->chars[col].cc_next; while (line->chars[col].cc_next) col += line->chars[col].cc_next; if (oldfree) line->chars[col].cc_next = oldfree - col; else line->chars[col].cc_next = 0; line->chars[origcol].cc_next = 0; #ifdef TERM_CC_DIAGS cc_check(line); #endif } /* * Compare two character cells for equality. Special case required * in do_paint() where we override what we expect the chr and attr * fields to be. */ static bool termchars_equal_override(termchar *a, termchar *b, unsigned long bchr, unsigned long battr) { /* FULL-TERMCHAR */ if (!truecolour_equal(a->truecolour, b->truecolour)) return false; if (a->chr != bchr) return false; if ((a->attr &~ DATTR_MASK) != (battr &~ DATTR_MASK)) return false; while (a->cc_next || b->cc_next) { if (!a->cc_next || !b->cc_next) return false; /* one cc-list ends, other does not */ a += a->cc_next; b += b->cc_next; if (a->chr != b->chr) return false; } return true; } static bool termchars_equal(termchar *a, termchar *b) { return termchars_equal_override(a, b, b->chr, b->attr); } /* * Copy a character cell. (Requires a pointer to the destination * termline, so as to access its free list.) */ static void copy_termchar(termline *destline, int x, termchar *src) { clear_cc(destline, x); destline->chars[x] = *src; /* copy everything except cc-list */ destline->chars[x].cc_next = 0; /* and make sure this is zero */ while (src->cc_next) { src += src->cc_next; add_cc(destline, x, src->chr); } #ifdef TERM_CC_DIAGS cc_check(destline); #endif } /* * Move a character cell within its termline. */ static void move_termchar(termline *line, termchar *dest, termchar *src) { /* First clear the cc list from the original char, just in case. */ clear_cc(line, dest - line->chars); /* Move the character cell and adjust its cc_next. */ *dest = *src; /* copy everything except cc-list */ if (src->cc_next) dest->cc_next = src->cc_next - (dest-src); /* Ensure the original cell doesn't have a cc list. */ src->cc_next = 0; #ifdef TERM_CC_DIAGS cc_check(line); #endif } /* * Compress and decompress a termline into an RLE-based format for * storing in scrollback. (Since scrollback almost never needs to * be modified and exists in huge quantities, this is a sensible * tradeoff, particularly since it allows us to continue adding * features to the main termchar structure without proportionally * bloating the terminal emulator's memory footprint unless those * features are in constant use.) */ static void makerle(strbuf *b, termline *ldata, void (*makeliteral)(strbuf *b, termchar *c, unsigned long *state)) { int hdrpos, hdrsize, n, prevlen, prevpos, thislen, thispos; bool prev2; termchar *c = ldata->chars; unsigned long state = 0, oldstate; n = ldata->cols; hdrpos = b->len; hdrsize = 0; put_byte(b, 0); prevlen = prevpos = 0; prev2 = false; while (n-- > 0) { thispos = b->len; makeliteral(b, c++, &state); thislen = b->len - thispos; if (thislen == prevlen && !memcmp(b->u + prevpos, b->u + thispos, thislen)) { /* * This literal precisely matches the previous one. * Turn it into a run if it's worthwhile. * * With one-byte literals, it costs us two bytes to * encode a run, plus another byte to write the header * to resume normal output; so a three-element run is * neutral, and anything beyond that is unconditionally * worthwhile. With two-byte literals or more, even a * 2-run is a win. */ if (thislen > 1 || prev2) { int runpos, runlen; /* * It's worth encoding a run. Start at prevpos, * unless hdrsize==0 in which case we can back up * another one and start by overwriting hdrpos. */ hdrsize--; /* remove the literal at prevpos */ if (prev2) { assert(hdrsize > 0); hdrsize--; prevpos -= prevlen;/* and possibly another one */ } if (hdrsize == 0) { assert(prevpos == hdrpos + 1); runpos = hdrpos; strbuf_shrink_to(b, prevpos+prevlen); } else { memmove(b->u + prevpos+1, b->u + prevpos, prevlen); runpos = prevpos; strbuf_shrink_to(b, prevpos+prevlen+1); /* * Terminate the previous run of ordinary * literals. */ assert(hdrsize >= 1 && hdrsize <= 128); b->u[hdrpos] = hdrsize - 1; } runlen = prev2 ? 3 : 2; while (n > 0 && runlen < 129) { int tmppos, tmplen; tmppos = b->len; oldstate = state; makeliteral(b, c, &state); tmplen = b->len - tmppos; bool match = tmplen == thislen && !memcmp(b->u + runpos+1, b->u + tmppos, tmplen); strbuf_shrink_to(b, tmppos); if (!match) { state = oldstate; break; /* run over */ } n--, c++, runlen++; } assert(runlen >= 2 && runlen <= 129); b->u[runpos] = runlen + 0x80 - 2; hdrpos = b->len; hdrsize = 0; put_byte(b, 0); /* And ensure this run doesn't interfere with the next. */ prevlen = prevpos = 0; prev2 = false; continue; } else { /* * Just flag that the previous two literals were * identical, in case we find a third identical one * we want to turn into a run. */ prev2 = true; prevlen = thislen; prevpos = thispos; } } else { prev2 = false; prevlen = thislen; prevpos = thispos; } /* * This character isn't (yet) part of a run. Add it to * hdrsize. */ hdrsize++; if (hdrsize == 128) { b->u[hdrpos] = hdrsize - 1; hdrpos = b->len; hdrsize = 0; put_byte(b, 0); prevlen = prevpos = 0; prev2 = false; } } /* * Clean up. */ if (hdrsize > 0) { assert(hdrsize <= 128); b->u[hdrpos] = hdrsize - 1; } else { strbuf_shrink_to(b, hdrpos); } } static void makeliteral_chr(strbuf *b, termchar *c, unsigned long *state) { /* * My encoding for characters is UTF-8-like, in that it stores * 7-bit ASCII in one byte and uses high-bit-set bytes as * introducers to indicate a longer sequence. However, it's * unlike UTF-8 in that it doesn't need to be able to * resynchronise, and therefore I don't want to waste two bits * per byte on having recognisable continuation characters. * Also I don't want to rule out the possibility that I may one * day use values 0x80000000-0xFFFFFFFF for interesting * purposes, so unlike UTF-8 I need a full 32-bit range. * Accordingly, here is my encoding: * * 00000000-0000007F: 0xxxxxxx (but see below) * 00000080-00003FFF: 10xxxxxx xxxxxxxx * 00004000-001FFFFF: 110xxxxx xxxxxxxx xxxxxxxx * 00200000-0FFFFFFF: 1110xxxx xxxxxxxx xxxxxxxx xxxxxxxx * 10000000-FFFFFFFF: 11110ZZZ xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx * * (`Z' is like `x' but is always going to be zero since the * values I'm encoding don't go above 2^32. In principle the * five-byte form of the encoding could extend to 2^35, and * there could be six-, seven-, eight- and nine-byte forms as * well to allow up to 64-bit values to be encoded. But that's * completely unnecessary for these purposes!) * * The encoding as written above would be very simple, except * that 7-bit ASCII can occur in several different ways in the * terminal data; sometimes it crops up in the D800 page * (CSET_ASCII) but at other times it's in the 0000 page (real * Unicode). Therefore, this encoding is actually _stateful_: * the one-byte encoding of 00-7F actually indicates `reuse the * upper three bytes of the last character', and to encode an * absolute value of 00-7F you need to use the two-byte form * instead. */ if ((c->chr & ~0x7F) == *state) { put_byte(b, (unsigned char)(c->chr & 0x7F)); } else if (c->chr < 0x4000) { put_byte(b, (unsigned char)(((c->chr >> 8) & 0x3F) | 0x80)); put_byte(b, (unsigned char)(c->chr & 0xFF)); } else if (c->chr < 0x200000) { put_byte(b, (unsigned char)(((c->chr >> 16) & 0x1F) | 0xC0)); put_uint16(b, c->chr & 0xFFFF); } else if (c->chr < 0x10000000) { put_byte(b, (unsigned char)(((c->chr >> 24) & 0x0F) | 0xE0)); put_byte(b, (unsigned char)((c->chr >> 16) & 0xFF)); put_uint16(b, c->chr & 0xFFFF); } else { put_byte(b, 0xF0); put_uint32(b, c->chr); } *state = c->chr & ~0xFF; } static void makeliteral_attr(strbuf *b, termchar *c, unsigned long *state) { /* * My encoding for attributes is 16-bit-granular and assumes * that the top bit of the word is never required. I either * store a two-byte value with the top bit clear (indicating * just that value), or a four-byte value with the top bit set * (indicating the same value with its top bit clear). * * However, first I permute the bits of the attribute value, so * that the eight bits of colour (four in each of fg and bg) * which are never non-zero unless xterm 256-colour mode is in * use are placed higher up the word than everything else. This * ensures that attribute values remain 16-bit _unless_ the * user uses extended colour. */ unsigned attr, colourbits; attr = c->attr; assert(ATTR_BGSHIFT > ATTR_FGSHIFT); colourbits = (attr >> (ATTR_BGSHIFT + 4)) & 0xF; colourbits <<= 4; colourbits |= (attr >> (ATTR_FGSHIFT + 4)) & 0xF; attr = (((attr >> (ATTR_BGSHIFT + 8)) << (ATTR_BGSHIFT + 4)) | (attr & ((1 << (ATTR_BGSHIFT + 4))-1))); attr = (((attr >> (ATTR_FGSHIFT + 8)) << (ATTR_FGSHIFT + 4)) | (attr & ((1 << (ATTR_FGSHIFT + 4))-1))); attr |= (colourbits << (32-9)); if (attr < 0x8000) { put_byte(b, (unsigned char)((attr >> 8) & 0xFF)); put_byte(b, (unsigned char)(attr & 0xFF)); } else { put_byte(b, (unsigned char)(((attr >> 24) & 0x7F) | 0x80)); put_byte(b, (unsigned char)((attr >> 16) & 0xFF)); put_byte(b, (unsigned char)((attr >> 8) & 0xFF)); put_byte(b, (unsigned char)(attr & 0xFF)); } } static void makeliteral_truecolour(strbuf *b, termchar *c, unsigned long *state) { /* * Put the used parts of the colour info into the buffer. */ put_byte(b, ((c->truecolour.fg.enabled ? 1 : 0) | (c->truecolour.bg.enabled ? 2 : 0))); if (c->truecolour.fg.enabled) { put_byte(b, c->truecolour.fg.r); put_byte(b, c->truecolour.fg.g); put_byte(b, c->truecolour.fg.b); } if (c->truecolour.bg.enabled) { put_byte(b, c->truecolour.bg.r); put_byte(b, c->truecolour.bg.g); put_byte(b, c->truecolour.bg.b); } } static void makeliteral_cc(strbuf *b, termchar *c, unsigned long *state) { /* * For combining characters, I just encode a bunch of ordinary * chars using makeliteral_chr, and terminate with a \0 * character (which I know won't come up as a combining char * itself). * * I don't use the stateful encoding in makeliteral_chr. */ unsigned long zstate; termchar z; while (c->cc_next) { c += c->cc_next; assert(c->chr != 0); zstate = 0; makeliteral_chr(b, c, &zstate); } z.chr = 0; zstate = 0; makeliteral_chr(b, &z, &zstate); } typedef struct compressed_scrollback_line { size_t len; } compressed_scrollback_line; static termline *decompressline(compressed_scrollback_line *line); static compressed_scrollback_line *compressline(termline *ldata) { strbuf *b = strbuf_new(); /* Leave space for the header structure */ strbuf_append(b, sizeof(compressed_scrollback_line)); /* * First, store the column count, 7 bits at a time, least * significant `digit' first, with the high bit set on all but * the last. */ { int n = ldata->cols; while (n >= 128) { put_byte(b, (unsigned char)((n & 0x7F) | 0x80)); n >>= 7; } put_byte(b, (unsigned char)(n)); } /* * Next store the lattrs; same principle. We add one extra bit to * this to indicate the trust state of the line. */ { int n = ldata->lattr | (ldata->trusted ? 0x10000 : 0); while (n >= 128) { put_byte(b, (unsigned char)((n & 0x7F) | 0x80)); n >>= 7; } put_byte(b, (unsigned char)(n)); } /* * Now we store a sequence of separate run-length encoded * fragments, each containing exactly as many symbols as there * are columns in the ldata. * * All of these have a common basic format: * * - a byte 00-7F indicates that X+1 literals follow it * - a byte 80-FF indicates that a single literal follows it * and expects to be repeated (X-0x80)+2 times. * * The format of the `literals' varies between the fragments. */ makerle(b, ldata, makeliteral_chr); makerle(b, ldata, makeliteral_attr); makerle(b, ldata, makeliteral_truecolour); makerle(b, ldata, makeliteral_cc); size_t linelen = b->len - sizeof(compressed_scrollback_line); compressed_scrollback_line *line = (compressed_scrollback_line *)strbuf_to_str(b); line->len = linelen; /* * Diagnostics: ensure that the compressed data really does * decompress to the right thing. * * This is a bit performance-heavy for production code. */ #ifdef TERM_CC_DIAGS #ifndef CHECK_SB_COMPRESSION { termline *dcl; int i; #ifdef DIAGNOSTIC_SB_COMPRESSION for (i = 0; i < b->len; i++) { printf(" %02x ", b->data[i]); } printf("\n"); #endif dcl = decompressline(line); assert(ldata->cols == dcl->cols); assert(ldata->lattr == dcl->lattr); for (i = 0; i < ldata->cols; i++) assert(termchars_equal(&ldata->chars[i], &dcl->chars[i])); #ifdef DIAGNOSTIC_SB_COMPRESSION printf("%d cols (%d bytes) -> %d bytes (factor of %g)\n", ldata->cols, 4 * ldata->cols, dused, (double)dused / (4 * ldata->cols)); #endif freetermline(dcl); } #endif #endif /* TERM_CC_DIAGS */ return line; } static void readrle(BinarySource *bs, termline *ldata, void (*readliteral)(BinarySource *bs, termchar *c, termline *ldata, unsigned long *state)) { int n = 0; unsigned long state = 0; while (n < ldata->cols) { int hdr = get_byte(bs); if (hdr >= 0x80) { /* A run. */ size_t pos = bs->pos, count = hdr + 2 - 0x80; while (count--) { assert(n < ldata->cols); bs->pos = pos; readliteral(bs, ldata->chars + n, ldata, &state); n++; } } else { /* Just a sequence of consecutive literals. */ int count = hdr + 1; while (count--) { assert(n < ldata->cols); readliteral(bs, ldata->chars + n, ldata, &state); n++; } } } assert(n == ldata->cols); } static void readliteral_chr(BinarySource *bs, termchar *c, termline *ldata, unsigned long *state) { int byte; /* * 00000000-0000007F: 0xxxxxxx * 00000080-00003FFF: 10xxxxxx xxxxxxxx * 00004000-001FFFFF: 110xxxxx xxxxxxxx xxxxxxxx * 00200000-0FFFFFFF: 1110xxxx xxxxxxxx xxxxxxxx xxxxxxxx * 10000000-FFFFFFFF: 11110ZZZ xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx */ byte = get_byte(bs); if (byte < 0x80) { c->chr = byte | *state; } else if (byte < 0xC0) { c->chr = (byte &~ 0xC0) << 8; c->chr |= get_byte(bs); } else if (byte < 0xE0) { c->chr = (byte &~ 0xE0) << 16; c->chr |= get_uint16(bs); } else if (byte < 0xF0) { c->chr = (byte &~ 0xF0) << 24; c->chr |= get_byte(bs) << 16; c->chr |= get_uint16(bs); } else { assert(byte == 0xF0); c->chr = get_uint32(bs); } *state = c->chr & ~0xFF; } static void readliteral_attr(BinarySource *bs, termchar *c, termline *ldata, unsigned long *state) { unsigned val, attr, colourbits; val = get_uint16(bs); if (val >= 0x8000) { val &= ~0x8000; val <<= 16; val |= get_uint16(bs); } colourbits = (val >> (32-9)) & 0xFF; attr = (val & ((1<<(32-9))-1)); attr = (((attr >> (ATTR_FGSHIFT + 4)) << (ATTR_FGSHIFT + 8)) | (attr & ((1 << (ATTR_FGSHIFT + 4))-1))); attr = (((attr >> (ATTR_BGSHIFT + 4)) << (ATTR_BGSHIFT + 8)) | (attr & ((1 << (ATTR_BGSHIFT + 4))-1))); attr |= (colourbits >> 4) << (ATTR_BGSHIFT + 4); attr |= (colourbits & 0xF) << (ATTR_FGSHIFT + 4); c->attr = attr; } static void readliteral_truecolour( BinarySource *bs, termchar *c, termline *ldata, unsigned long *state) { int flags = get_byte(bs); if (flags & 1) { c->truecolour.fg.enabled = true; c->truecolour.fg.r = get_byte(bs); c->truecolour.fg.g = get_byte(bs); c->truecolour.fg.b = get_byte(bs); } else { c->truecolour.fg = optionalrgb_none; } if (flags & 2) { c->truecolour.bg.enabled = true; c->truecolour.bg.r = get_byte(bs); c->truecolour.bg.g = get_byte(bs); c->truecolour.bg.b = get_byte(bs); } else { c->truecolour.bg = optionalrgb_none; } } static void readliteral_cc(BinarySource *bs, termchar *c, termline *ldata, unsigned long *state) { termchar n; unsigned long zstate; int x = c - ldata->chars; c->cc_next = 0; while (1) { zstate = 0; readliteral_chr(bs, &n, ldata, &zstate); if (!n.chr) break; add_cc(ldata, x, n.chr); } } static termline *decompressline(compressed_scrollback_line *line) { int ncols, byte, shift; BinarySource bs[1]; termline *ldata; BinarySource_BARE_INIT(bs, line+1, line->len); /* * First read in the column count. */ ncols = shift = 0; do { byte = get_byte(bs); ncols |= (byte & 0x7F) << shift; shift += 7; } while (byte & 0x80); /* * Now create the output termline. */ ldata = snew(termline); ldata->chars = snewn(ncols, termchar); ldata->cols = ldata->size = ncols; ldata->temporary = true; ldata->cc_free = 0; /* * We must set all the cc pointers in ldata->chars to 0 right * now, so that cc diagnostics that verify the integrity of the * whole line will make sense while we're in the middle of * building it up. */ { int i; for (i = 0; i < ldata->cols; i++) ldata->chars[i].cc_next = 0; } /* * Now read in the lattr. */ int lattr = shift = 0; do { byte = get_byte(bs); lattr |= (byte & 0x7F) << shift; shift += 7; } while (byte & 0x80); ldata->lattr = lattr & 0xFFFF; ldata->trusted = (lattr & 0x10000) != 0; /* * Now we read in each of the RLE streams in turn. */ readrle(bs, ldata, readliteral_chr); readrle(bs, ldata, readliteral_attr); readrle(bs, ldata, readliteral_truecolour); readrle(bs, ldata, readliteral_cc); /* And we always expect that we ended up exactly at the end of the * compressed data. */ assert(!get_err(bs)); assert(get_avail(bs) == 0); return ldata; } /* * Resize a line to make it `cols' columns wide. */ static void resizeline(Terminal *term, termline *line, int cols) { int i, oldcols; if (line->cols != cols) { oldcols = line->cols; /* * This line is the wrong length, which probably means it * hasn't been accessed since a resize. Resize it now. * * First, go through all the characters that will be thrown * out in the resize (if we're shrinking the line) and * return their cc lists to the cc free list. */ for (i = cols; i < oldcols; i++) clear_cc(line, i); /* * If we're shrinking the line, we now bodily move the * entire cc section from where it started to where it now * needs to be. (We have to do this before the resize, so * that the data we're copying is still there. However, if * we're expanding, we have to wait until _after_ the * resize so that the space we're copying into is there.) */ if (cols < oldcols) memmove(line->chars + cols, line->chars + oldcols, (line->size - line->cols) * TSIZE); /* * Now do the actual resize, leaving the _same_ amount of * cc space as there was to begin with. */ line->size += cols - oldcols; line->chars = sresize(line->chars, line->size, TTYPE); line->cols = cols; /* * If we're expanding the line, _now_ we move the cc * section. */ if (cols > oldcols) memmove(line->chars + cols, line->chars + oldcols, (line->size - line->cols) * TSIZE); /* * Go through what's left of the original line, and adjust * the first cc_next pointer in each list. (All the * subsequent ones are still valid because they are * relative offsets within the cc block.) Also do the same * to the head of the cc_free list. */ for (i = 0; i < oldcols && i < cols; i++) if (line->chars[i].cc_next) line->chars[i].cc_next += cols - oldcols; if (line->cc_free) line->cc_free += cols - oldcols; /* * And finally fill in the new space with erase chars. (We * don't have to worry about cc lists here, because we * _know_ the erase char doesn't have one.) */ for (i = oldcols; i < cols; i++) line->chars[i] = term->basic_erase_char; #ifdef TERM_CC_DIAGS cc_check(line); #endif } } /* * 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; } static void null_line_error(Terminal *term, int y, int lineno, tree234 *whichtree, int treeindex, const char *varname) { modalfatalbox("%s==NULL in terminal.c\n" "lineno=%d y=%d w=%d h=%d\n" "count(scrollback=%p)=%d\n" "count(screen=%p)=%d\n" "count(alt=%p)=%d alt_sblines=%d\n" "whichtree=%p treeindex=%d\n" "commitid=%s\n\n" "Please contact " "and pass on the above information.", varname, lineno, y, term->cols, term->rows, term->scrollback, count234(term->scrollback), term->screen, count234(term->screen), term->alt_screen, count234(term->alt_screen), term->alt_sblines, whichtree, treeindex, commitid); } /* * Retrieve a line of the screen or of the scrollback, according to * whether the y coordinate is non-negative or negative * (respectively). */ static termline *lineptr(Terminal *term, int y, int lineno, int screen) { termline *line; tree234 *whichtree; int treeindex; if (y >= 0) { 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 { whichtree = term->alt_screen; treeindex = y + term->alt_sblines; /* treeindex = y + count234(term->alt_screen); */ } } if (whichtree == term->scrollback) { compressed_scrollback_line *cline = index234(whichtree, treeindex); if (!cline) null_line_error(term, y, lineno, whichtree, treeindex, "cline"); line = decompressline(cline); } else { line = index234(whichtree, treeindex); } /* We assume that we don't screw up and retrieve something out of range. */ if (line == NULL) null_line_error(term, y, lineno, whichtree, treeindex, "line"); assert(line != NULL); /* * Here we resize lines to _at least_ the right length, but we * don't truncate them. Truncation is done as a side effect of * modifying the line. * * The point of this policy is to try to arrange that resizing the * terminal window repeatedly - e.g. successive steps in an X11 * opaque window-resize drag, or resizing as a side effect of * retiling by tiling WMs such as xmonad - does not throw away * data gratuitously. Specifically, we want a sequence of resize * operations with no terminal output between them to have the * same effect as a single resize to the ultimate terminal size, * and also (for the case in which xmonad narrows a window that's * scrolling things) we want scrolling up new text at the bottom * of a narrowed window to avoid truncating lines further up when * the window is re-widened. */ if (term->cols > line->cols) resizeline(term, line, term->cols); return line; } #define lineptr(x) (lineptr)(term,x,__LINE__,0) #define scrlineptr(x) (lineptr)(term,x,__LINE__,1) /* * Coerce a termline to the terminal's current width. Unlike the * optional resize in lineptr() above, this is potentially destructive * of text, since it can shrink as well as grow the line. * * We call this whenever a termline is actually going to be modified. * Helpfully, putting a single call to this function in check_boundary * deals with _nearly_ all such cases, leaving only a few things like * bulk erase and ESC#8 to handle separately. */ static void check_line_size(Terminal *term, termline *line) { if (term->cols != line->cols) /* trivial optimisation */ resizeline(term, line, term->cols); } static void term_schedule_tblink(Terminal *term); static void term_schedule_cblink(Terminal *term); static void term_update_callback(void *ctx); static void term_timer(void *ctx, unsigned long now) { Terminal *term = (Terminal *)ctx; if (term->tblink_pending && now == term->next_tblink) { term->tblinker = !term->tblinker; term->tblink_pending = false; term_schedule_tblink(term); term->window_update_pending = true; } if (term->cblink_pending && now == term->next_cblink) { term->cblinker = !term->cblinker; term->cblink_pending = false; term_schedule_cblink(term); term->window_update_pending = true; } if (term->in_vbell && now == term->vbell_end) { term->in_vbell = false; term->window_update_pending = true; } if (term->window_update_cooldown && now == term->window_update_cooldown_end) { term->window_update_cooldown = false; } if (term->window_update_pending) term_update_callback(term); } static void term_update_callback(void *ctx) { Terminal *term = (Terminal *)ctx; if (!term->window_update_pending) return; if (!term->window_update_cooldown) { term_update(term); term->window_update_cooldown = true; term->window_update_cooldown_end = schedule_timer( UPDATE_DELAY, term_timer, term); } } static void term_schedule_update(Terminal *term) { if (!term->window_update_pending) { term->window_update_pending = true; queue_toplevel_callback(term_update_callback, term); } } /* * Call this whenever the terminal window state changes, to queue * an update. */ static void seen_disp_event(Terminal *term) { term->seen_disp_event = true; /* for scrollback-reset-on-activity */ term_schedule_update(term); } /* * Call when the terminal's blinking-text settings change, or when * a text blink has just occurred. */ static void term_schedule_tblink(Terminal *term) { if (term->blink_is_real) { if (!term->tblink_pending) term->next_tblink = schedule_timer(TBLINK_DELAY, term_timer, term); term->tblink_pending = true; } else { term->tblinker = true; /* reset when not in use */ term->tblink_pending = false; } } /* * 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 = true; /* reset when not in use */ term->cblink_pending = false; } } /* * Call to reset cursor blinking on new output. */ static void term_reset_cblink(Terminal *term) { seen_disp_event(term); term->cblinker = true; term->cblink_pending = false; term_schedule_cblink(term); } /* * Call to begin a visual bell. */ static void term_schedule_vbell(Terminal *term, bool already_started, long startpoint) { long ticks_already_gone; if (already_started) ticks_already_gone = GETTICKCOUNT() - startpoint; else ticks_already_gone = 0; if (ticks_already_gone < VBELL_DELAY) { term->in_vbell = true; term->vbell_end = schedule_timer(VBELL_DELAY - ticks_already_gone, term_timer, term); } else { term->in_vbell = false; } } /* * Set up power-on settings for the terminal. * If 'clear' is false, don't actually clear the primary screen, and * position the cursor below the last non-blank line (scrolling if * necessary). */ static void power_on(Terminal *term, bool clear) { term->alt_x = term->alt_y = 0; term->savecurs.x = term->savecurs.y = 0; term->alt_savecurs.x = term->alt_savecurs.y = 0; term->alt_t = term->marg_t = 0; if (term->rows != -1) term->alt_b = term->marg_b = term->rows - 1; 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_bool(term->conf, CONF_dec_om); term->alt_ins = false; term->insert = false; term->alt_wnext = false; term->wrapnext = false; term->save_wnext = false; term->alt_save_wnext = false; term->alt_wrap = term->wrap = conf_get_bool(term->conf, CONF_wrap_mode); term->alt_cset = term->cset = term->save_cset = term->alt_save_cset = 0; term->alt_utf = false; term->utf = false; term->save_utf = false; term->alt_save_utf = false; term->utf8.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 = false; term->in_vbell = false; term->cursor_on = true; term->big_cursor = false; term->default_attr = term->save_attr = term->alt_save_attr = term->curr_attr = ATTR_DEFAULT; term->curr_truecolour.fg = term->curr_truecolour.bg = optionalrgb_none; term->save_truecolour = term->alt_save_truecolour = term->curr_truecolour; term->app_cursor_keys = conf_get_bool(term->conf, CONF_app_cursor); term->app_keypad_keys = conf_get_bool(term->conf, CONF_app_keypad); term->use_bce = conf_get_bool(term->conf, CONF_bce); term->blink_is_real = conf_get_bool(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 = false; term->urxvt_extended_mouse = false; win_set_raw_mouse_mode(term->win, false); term->win_pointer_shape_pending = true; term->win_pointer_shape_raw = false; term->bracketed_paste = false; term->srm_echo = 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); term->curs.y = find_last_nonempty_line(term, term->screen) + 1; if (term->curs.y == term->rows) { term->curs.y--; scroll(term, 0, term->rows - 1, 1, true); } } else { term->curs.y = 0; } term->curs.x = 0; term_schedule_tblink(term); term_schedule_cblink(term); term_schedule_update(term); } /* * Force a screen update. */ void term_update(Terminal *term) { term->window_update_pending = false; if (term->win_move_pending) { win_move(term->win, term->win_move_pending_x, term->win_move_pending_y); term->win_move_pending = false; } if (term->win_resize_pending) { win_request_resize(term->win, term->win_resize_pending_w, term->win_resize_pending_h); term->win_resize_pending = false; } if (term->win_zorder_pending) { win_set_zorder(term->win, term->win_zorder_top); term->win_zorder_pending = false; } if (term->win_minimise_pending) { win_set_minimised(term->win, term->win_minimise_enable); term->win_minimise_pending = false; } if (term->win_maximise_pending) { win_set_maximised(term->win, term->win_maximise_enable); term->win_maximise_pending = false; } if (term->win_title_pending) { win_set_title(term->win, term->window_title); term->win_title_pending = false; } if (term->win_icon_title_pending) { win_set_icon_title(term->win, term->icon_title); term->win_icon_title_pending = false; } if (term->win_pointer_shape_pending) { win_set_raw_mouse_mode_pointer(term->win, term->win_pointer_shape_raw); term->win_pointer_shape_pending = false; } if (term->win_refresh_pending) { win_refresh(term->win); term->win_refresh_pending = false; } if (term->win_palette_pending) { unsigned start = term->win_palette_pending_min; unsigned ncolours = term->win_palette_pending_limit - start; win_palette_set(term->win, start, ncolours, term->palette + start); term->win_palette_pending = false; } if (win_setup_draw_ctx(term->win)) { bool need_sbar_update = term->seen_disp_event || term->win_scrollbar_update_pending; term->win_scrollbar_update_pending = false; if (term->seen_disp_event && term->scroll_on_disp) { term->disptop = 0; /* return to main screen */ term->seen_disp_event = false; need_sbar_update = true; } if (need_sbar_update) update_sbar(term); do_paint(term); win_set_cursor_pos( term->win, term->curs.x, term->curs.y - term->disptop); win_free_draw_ctx(term->win); } } /* * Called from front end when a keypress occurs, to trigger * anything magical that needs to happen in that situation. */ void term_seen_key_event(Terminal *term) { /* * On any keypress, clear the bell overload mechanism * completely, on the grounds that large numbers of * beeps coming from deliberate key action are likely * to be intended (e.g. beeps from filename completion * blocking repeatedly). */ term->beep_overloaded = false; while (term->beephead) { struct beeptime *tmp = term->beephead; term->beephead = tmp->next; sfree(tmp); } 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. */ void term_pwron(Terminal *term, bool clear) { power_on(term, clear); if (term->ldisc) /* cause ldisc to notice changes */ ldisc_echoedit_update(term->ldisc); term->disptop = 0; deselect(term); term_update(term); } 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)); term->erase_char.truecolour.bg = term->curr_truecolour.bg; } } /* * 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_bool(term->conf, CONF_ansi_colour); term->no_arabicshaping = conf_get_bool(term->conf, CONF_no_arabicshaping); term->beep = conf_get_int(term->conf, CONF_beep); term->bellovl = conf_get_bool(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->no_bidi = conf_get_bool(term->conf, CONF_no_bidi); term->bksp_is_delete = conf_get_bool(term->conf, CONF_bksp_is_delete); term->blink_cur = conf_get_bool(term->conf, CONF_blink_cur); term->blinktext = conf_get_bool(term->conf, CONF_blinktext); term->cjk_ambig_wide = conf_get_bool(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_bool(term->conf, CONF_crhaslf); term->erase_to_scrollback = conf_get_bool(term->conf, CONF_erase_to_scrollback); term->funky_type = conf_get_int(term->conf, CONF_funky_type); term->lfhascr = conf_get_bool(term->conf, CONF_lfhascr); term->logflush = conf_get_bool(term->conf, CONF_logflush); term->logtype = conf_get_int(term->conf, CONF_logtype); term->mouse_override = conf_get_bool(term->conf, CONF_mouse_override); term->nethack_keypad = conf_get_bool(term->conf, CONF_nethack_keypad); term->no_alt_screen = conf_get_bool(term->conf, CONF_no_alt_screen); term->no_applic_c = conf_get_bool(term->conf, CONF_no_applic_c); term->no_applic_k = conf_get_bool(term->conf, CONF_no_applic_k); term->no_dbackspace = conf_get_bool(term->conf, CONF_no_dbackspace); term->no_mouse_rep = conf_get_bool(term->conf, CONF_no_mouse_rep); term->no_remote_charset = conf_get_bool(term->conf, CONF_no_remote_charset); term->no_remote_resize = conf_get_bool(term->conf, CONF_no_remote_resize); term->no_remote_wintitle = conf_get_bool(term->conf, CONF_no_remote_wintitle); term->no_remote_clearscroll = conf_get_bool(term->conf, CONF_no_remote_clearscroll); term->rawcnp = conf_get_bool(term->conf, CONF_rawcnp); term->utf8linedraw = conf_get_bool(term->conf, CONF_utf8linedraw); term->rect_select = conf_get_bool(term->conf, CONF_rect_select); term->remote_qtitle_action = conf_get_int(term->conf, CONF_remote_qtitle_action); term->rxvt_homeend = conf_get_bool(term->conf, CONF_rxvt_homeend); term->scroll_on_disp = conf_get_bool(term->conf, CONF_scroll_on_disp); term->scroll_on_key = conf_get_bool(term->conf, CONF_scroll_on_key); term->xterm_mouse_forbidden = conf_get_bool(term->conf, CONF_no_mouse_rep); term->xterm_256_colour = conf_get_bool(term->conf, CONF_xterm_256_colour); term->true_colour = conf_get_bool(term->conf, CONF_true_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++; } } } } void term_pre_reconfig(Terminal *term, Conf *conf) { /* * 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, term->window_title); } /* * 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. */ bool reset_wrap, reset_decom, reset_bce, reset_tblink, reset_charclass; bool palette_changed = false; int i; reset_wrap = (conf_get_bool(term->conf, CONF_wrap_mode) != conf_get_bool(conf, CONF_wrap_mode)); reset_decom = (conf_get_bool(term->conf, CONF_dec_om) != conf_get_bool(conf, CONF_dec_om)); reset_bce = (conf_get_bool(term->conf, CONF_bce) != conf_get_bool(conf, CONF_bce)); reset_tblink = (conf_get_bool(term->conf, CONF_blinktext) != conf_get_bool(conf, CONF_blinktext)); reset_charclass = false; 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 = true; /* * If the bidi or shaping settings have changed, flush the bidi * cache completely. */ if (conf_get_bool(term->conf, CONF_no_arabicshaping) != conf_get_bool(conf, CONF_no_arabicshaping) || conf_get_bool(term->conf, CONF_no_bidi) != conf_get_bool(conf, CONF_no_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; } } { const char *old_title = conf_get_str(term->conf, CONF_wintitle); const char *new_title = conf_get_str(conf, CONF_wintitle); if (strcmp(old_title, new_title)) { sfree(term->window_title); term->window_title = dupstr(new_title); term->win_title_pending = true; term_schedule_update(term); } } /* * Just setting 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, so that * they can be updated immediately if they haven't been overridden * by some escape sequence. */ { int i, j; for (i = 0; i < CONF_NCOLOURS; i++) { for (j = 0; j < 3; j++) if (conf_get_int_int(term->conf, CONF_colours, i*3+j) != conf_get_int_int(conf, CONF_colours, i*3+j)) break; if (j < 3) { /* Actually enacting the change has to be deferred * until the new conf is installed. */ palette_changed = true; break; } } } conf_free(term->conf); term->conf = conf_copy(conf); if (reset_wrap) term->alt_wrap = term->wrap = conf_get_bool(term->conf, CONF_wrap_mode); if (reset_decom) term->alt_om = term->dec_om = conf_get_bool(term->conf, CONF_dec_om); if (reset_bce) { term->use_bce = conf_get_bool(term->conf, CONF_bce); set_erase_char(term); } if (reset_tblink) { term->blink_is_real = conf_get_bool(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_bool(term->conf, CONF_no_alt_screen)) swap_screen(term, 0, false, false); if (conf_get_bool(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 = false; } if (!conf_get_str(term->conf, CONF_printer)) { term_print_finish(term); } if (palette_changed) term_notify_palette_changed(term); term_schedule_tblink(term); term_schedule_cblink(term); term_copy_stuff_from_conf(term); term_update_raw_mouse_mode(term); } /* * Clear the scrollback. */ void term_clrsb(Terminal *term) { unsigned char *line; int i; /* * Scroll forward to the current screen, if we were back in the * scrollback somewhere until now. */ term->disptop = 0; /* * Clear the actual scrollback. */ while ((line = delpos234(term->scrollback, 0)) != NULL) { sfree(line); /* this is compressed data, not a termline */ } /* * When clearing the scrollback, we also truncate any termlines on * the current screen which have remembered data from a previous * larger window size. Rationale: clearing the scrollback is * sometimes done to protect privacy, so the user intention is * specifically that we should not retain evidence of what * previously happened in the terminal, and that ought to include * evidence to the right as well as evidence above. */ for (i = 0; i < term->rows; i++) check_line_size(term, scrlineptr(i)); /* * That operation has invalidated the selection, if it overlapped * the scrollback at all. */ if (term->selstate != NO_SELECTION && term->selstart.y < 0) deselect(term); /* * There are now no lines of real scrollback which can be pulled * back into the screen by a resize, and no lines of the alternate * screen which should be displayed as if part of the scrollback. */ term->tempsblines = 0; term->alt_sblines = 0; /* * The scrollbar will need updating to reflect the new state of * the world. */ term->win_scrollbar_update_pending = true; term_schedule_update(term); } const optionalrgb optionalrgb_none = {0, 0, 0, 0}; void term_setup_window_titles(Terminal *term, const char *title_hostname) { const char *conf_title = conf_get_str(term->conf, CONF_wintitle); sfree(term->window_title); sfree(term->icon_title); if (*conf_title) { term->window_title = dupstr(conf_title); term->icon_title = dupstr(conf_title); } else { if (title_hostname && *title_hostname) term->window_title = dupcat(title_hostname, " - ", appname); else term->window_title = dupstr(appname); term->icon_title = dupstr(term->window_title); } term->win_title_pending = true; term->win_icon_title_pending = true; } static void palette_rebuild(Terminal *term) { unsigned min_changed = OSC4_NCOLOURS, max_changed = 0; if (term->win_palette_pending) { /* Possibly extend existing range. */ min_changed = term->win_palette_pending_min; max_changed = term->win_palette_pending_limit - 1; } else { /* Start with empty range. */ min_changed = OSC4_NCOLOURS; max_changed = 0; } for (unsigned i = 0; i < OSC4_NCOLOURS; i++) { rgb new_value; bool found = false; for (unsigned j = lenof(term->subpalettes); j-- > 0 ;) { if (term->subpalettes[j].present[i]) { new_value = term->subpalettes[j].values[i]; found = true; break; } } assert(found); /* we expect SUBPAL_CONF to always be set */ if (new_value.r != term->palette[i].r || new_value.g != term->palette[i].g || new_value.b != term->palette[i].b) { term->palette[i] = new_value; if (min_changed > i) min_changed = i; if (max_changed < i) max_changed = i; } } if (min_changed <= max_changed) { /* * At least one colour changed (or we had an update scheduled * already). Schedule a redraw event to pass the result back * to the TermWin. This also requires invalidating the rest * of the window, because usually all the text will need * redrawing in the new colours. * (If there was an update pending and this palette rebuild * didn't actually change anything, we'll harmlessly reinforce * the existing update request.) */ term->win_palette_pending = true; term->win_palette_pending_min = min_changed; term->win_palette_pending_limit = max_changed + 1; term_invalidate(term); term_schedule_update(term); } } /* * Rebuild the palette from configuration and platform colours. * If 'keep_overrides' set, any escape-sequence-specified overrides will * remain in place. */ static void palette_reset(Terminal *term, bool keep_overrides) { for (unsigned i = 0; i < OSC4_NCOLOURS; i++) term->subpalettes[SUBPAL_CONF].present[i] = true; /* * Copy all the palette information out of the Conf. */ for (unsigned i = 0; i < CONF_NCOLOURS; i++) { rgb *col = &term->subpalettes[SUBPAL_CONF].values[ colour_indices_conf_to_osc4[i]]; col->r = conf_get_int_int(term->conf, CONF_colours, i*3+0); col->g = conf_get_int_int(term->conf, CONF_colours, i*3+1); col->b = conf_get_int_int(term->conf, CONF_colours, i*3+2); } /* * Directly invent the rest of the xterm-256 colours. */ for (unsigned i = 0; i < 216; i++) { rgb *col = &term->subpalettes[SUBPAL_CONF].values[i + 16]; int r = i / 36, g = (i / 6) % 6, b = i % 6; col->r = r ? r * 40 + 55 : 0; col->g = g ? g * 40 + 55 : 0; col->b = b ? b * 40 + 55 : 0; } for (unsigned i = 0; i < 24; i++) { rgb *col = &term->subpalettes[SUBPAL_CONF].values[i + 232]; int shade = i * 10 + 8; col->r = col->g = col->b = shade; } /* * Re-fetch any OS-local overrides. */ for (unsigned i = 0; i < OSC4_NCOLOURS; i++) term->subpalettes[SUBPAL_PLATFORM].present[i] = false; win_palette_get_overrides(term->win, term); if (!keep_overrides) { /* * Get rid of all escape-sequence configuration. */ for (unsigned i = 0; i < OSC4_NCOLOURS; i++) term->subpalettes[SUBPAL_SESSION].present[i] = false; } /* * Rebuild the composite palette. */ palette_rebuild(term); } void term_palette_override(Terminal *term, unsigned osc4_index, rgb rgb) { /* * We never expect to be called except as re-entry from our own * call to win_palette_get_overrides above, so we need not mess * about calling palette_rebuild. */ term->subpalettes[SUBPAL_PLATFORM].present[osc4_index] = true; term->subpalettes[SUBPAL_PLATFORM].values[osc4_index] = rgb; } /* * Initialise the terminal. */ Terminal *term_init(Conf *myconf, struct unicode_data *ucsdata, TermWin *win) { Terminal *term; /* * Allocate a new Terminal structure and initialise the fields * that need it. */ term = snew(Terminal); term->win = win; 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 = 0; term->reset_132 = false; term->cblinker = false; term->tblinker = false; term->has_focus = true; 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; term->tabs = NULL; deselect(term); term->rows = term->cols = -1; power_on(term, true); term->beephead = term->beeptail = NULL; term->nbeeps = 0; term->lastbeep = false; term->beep_overloaded = false; term->attr_mask = 0xffffffff; term->backend = NULL; term->in_term_out = false; term->ltemp = NULL; term->ltemp_size = 0; term->wcFrom = NULL; term->wcTo = NULL; term->wcFromTo_size = 0; term->window_update_pending = false; term->window_update_cooldown = false; term->bidi_cache_size = 0; term->pre_bidi_cache = term->post_bidi_cache = NULL; /* FULL-TERMCHAR */ term->basic_erase_char.chr = CSET_ASCII | ' '; term->basic_erase_char.attr = ATTR_DEFAULT; term->basic_erase_char.cc_next = 0; term->basic_erase_char.truecolour.fg = optionalrgb_none; term->basic_erase_char.truecolour.bg = optionalrgb_none; term->erase_char = term->basic_erase_char; term->last_selected_text = NULL; term->last_selected_attr = NULL; term->last_selected_tc = NULL; term->last_selected_len = 0; /* TermWin implementations will typically extend these with * clipboard ids they know about */ term->mouse_select_clipboards[0] = CLIP_LOCAL; term->n_mouse_select_clipboards = 1; term->mouse_paste_clipboard = CLIP_NULL; term->last_graphic_char = 0; term->trusted = true; term->bracketed_paste_active = false; term->window_title = dupstr(""); term->icon_title = dupstr(""); term->minimised = false; term->winpos_x = term->winpos_y = 0; term->winpixsize_x = term->winpixsize_y = 0; term->win_move_pending = false; term->win_resize_pending = false; term->win_zorder_pending = false; term->win_minimise_pending = false; term->win_maximise_pending = false; term->win_title_pending = false; term->win_icon_title_pending = false; term->win_pointer_shape_pending = false; term->win_refresh_pending = false; term->win_scrollbar_update_pending = false; term->win_palette_pending = false; palette_reset(term, false); return term; } void term_free(Terminal *term) { termline *line; struct beeptime *beep; int i; while ((line = delpos234(term->scrollback, 0)) != NULL) sfree(line); /* compressed data, not a termline */ freetree234(term->scrollback); while ((line = delpos234(term->screen, 0)) != NULL) freetermline(line); freetree234(term->screen); while ((line = delpos234(term->alt_screen, 0)) != NULL) freetermline(line); freetree234(term->alt_screen); if (term->disptext) { for (i = 0; i < term->rows; i++) freetermline(term->disptext[i]); } sfree(term->disptext); while (term->beephead) { beep = term->beephead; term->beephead = beep->next; sfree(beep); } bufchain_clear(&term->inbuf); if(term->print_job) printer_finish_job(term->print_job); bufchain_clear(&term->printer_buf); sfree(term->paste_buffer); sfree(term->ltemp); sfree(term->wcFrom); sfree(term->wcTo); sfree(term->answerback); 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); delete_callbacks_for_context(term); conf_free(term->conf); sfree(term->window_title); sfree(term->icon_title); sfree(term); } void term_set_trust_status(Terminal *term, bool trusted) { term->trusted = trusted; } void term_get_cursor_position(Terminal *term, int *x, int *y) { *x = term->curs.x; *y = term->curs.y; } /* * Set up the terminal for a given size. */ void term_size(Terminal *term, int newrows, int newcols, int newsavelines) { tree234 *newalt; termline **newdisp, *line; int i, j, oldrows = term->rows; int sblen; int save_alt_which = term->alt_which; if (newrows == term->rows && newcols == term->cols && newsavelines == term->savelines) return; /* nothing to do */ /* Behave sensibly if we're given zero (or negative) rows/cols */ if (newrows < 1) newrows = 1; if (newcols < 1) newcols = 1; deselect(term); swap_screen(term, 0, false, false); term->alt_t = term->marg_t = 0; term->alt_b = term->marg_b = newrows - 1; if (term->rows == -1) { term->scrollback = newtree234(NULL); term->screen = newtree234(NULL); term->tempsblines = 0; term->rows = 0; } /* * Resize the screen and scrollback. We only need to shift * lines around within our data structures, because lineptr() * will take care of resizing each individual line if * necessary. So: * * - If the new screen is longer, we shunt lines in from temporary * scrollback if possible, otherwise we add new blank lines at * the bottom. * * - If the new screen is shorter, we remove any blank lines at * the bottom if possible, otherwise shunt lines above the cursor * to scrollback if possible, otherwise delete lines below the * cursor. * * - Then, if the new scrollback length is less than the * amount of scrollback we actually have, we must throw some * away. */ sblen = count234(term->scrollback); /* Do this loop to expand the screen if newrows > rows */ assert(term->rows == count234(term->screen)); while (term->rows < newrows) { if (term->tempsblines > 0) { compressed_scrollback_line *cline; /* Insert a line from the scrollback at the top of the screen. */ assert(sblen >= term->tempsblines); cline = delpos234(term->scrollback, --sblen); line = decompressline(cline); sfree(cline); line->temporary = false; /* reconstituted line is now real */ term->tempsblines -= 1; addpos234(term->screen, line, 0); term->curs.y += 1; term->savecurs.y += 1; term->alt_y += 1; term->alt_savecurs.y += 1; } else { /* Add a new blank line at the bottom of the screen. */ line = newtermline(term, newcols, false); addpos234(term->screen, line, count234(term->screen)); } 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); freetermline(line); } else { /* push top row to scrollback */ line = delpos234(term->screen, 0); addpos234(term->scrollback, compressline(line), sblen++); freetermline(line); term->tempsblines += 1; term->curs.y -= 1; term->savecurs.y -= 1; term->alt_y -= 1; term->alt_savecurs.y -= 1; } term->rows -= 1; } assert(term->rows == newrows); assert(count234(term->screen) == newrows); /* Delete any excess lines from the scrollback. */ while (sblen > newsavelines) { line = delpos234(term->scrollback, 0); sfree(line); sblen--; } if (sblen < term->tempsblines) term->tempsblines = sblen; assert(count234(term->scrollback) <= newsavelines); assert(count234(term->scrollback) >= term->tempsblines); term->disptop = 0; /* Make a new displayed text buffer. */ newdisp = snewn(newrows, termline *); for (i = 0; i < newrows; i++) { newdisp[i] = newtermline(term, newcols, false); for (j = 0; j < newcols; j++) newdisp[i]->chars[j].attr = ATTR_INVALID; } if (term->disptext) { for (i = 0; i < oldrows; i++) freetermline(term->disptext[i]); } sfree(term->disptext); term->disptext = newdisp; term->dispcursx = term->dispcursy = -1; /* Make a new alternate screen. */ newalt = newtree234(NULL); for (i = 0; i < newrows; i++) { line = newtermline(term, newcols, true); addpos234(newalt, line, i); } if (term->alt_screen) { while (NULL != (line = delpos234(term->alt_screen, 0))) freetermline(line); freetree234(term->alt_screen); } term->alt_screen = newalt; term->alt_sblines = 0; term->tabs = sresize(term->tabs, newcols, unsigned char); { int i; for (i = (term->cols > 0 ? term->cols : 0); i < newcols; i++) term->tabs[i] = (i % 8 == 0 ? true : false); } /* Check that the cursor positions are still valid. */ if (term->savecurs.y < 0) term->savecurs.y = 0; if (term->savecurs.y >= newrows) term->savecurs.y = newrows - 1; if (term->savecurs.x >= newcols) term->savecurs.x = newcols - 1; if (term->alt_savecurs.y < 0) term->alt_savecurs.y = 0; if (term->alt_savecurs.y >= newrows) term->alt_savecurs.y = newrows - 1; if (term->alt_savecurs.x >= newcols) term->alt_savecurs.x = newcols - 1; if (term->curs.y < 0) term->curs.y = 0; if (term->curs.y >= newrows) term->curs.y = newrows - 1; if (term->curs.x >= newcols) term->curs.x = newcols - 1; if (term->alt_y < 0) term->alt_y = 0; if (term->alt_y >= newrows) term->alt_y = newrows - 1; if (term->alt_x >= newcols) term->alt_x = newcols - 1; term->alt_x = term->alt_y = 0; term->wrapnext = false; term->alt_wnext = false; term->rows = newrows; term->cols = newcols; term->savelines = newsavelines; swap_screen(term, save_alt_which, false, false); term->win_scrollbar_update_pending = true; term_schedule_update(term); if (term->backend) backend_size(term->backend, term->cols, term->rows); } /* * Hand a backend to the terminal, so it can be notified of resizes. */ void term_provide_backend(Terminal *term, Backend *backend) { term->backend = backend; if (term->backend && term->cols > 0 && term->rows > 0) backend_size(term->backend, term->cols, term->rows); } /* Find the bottom line on the screen that has any content. * If only the top line has content, returns 0. * If no lines have content, return -1. */ static int find_last_nonempty_line(Terminal * term, tree234 * screen) { int i; for (i = count234(screen) - 1; i >= 0; i--) { termline *line = index234(screen, i); int j; for (j = 0; j < line->cols; j++) if (!termchars_equal(&line->chars[j], &term->erase_char)) break; if (j != line->cols) break; } return i; } /* * Swap screens. If `reset' is true and we have been asked to * switch to the alternate screen, we must bring most of its * configuration from the main screen and erase the contents of the * alternate screen completely. (This is even true if we're already * on it! Blame xterm.) */ static void swap_screen(Terminal *term, int which, bool reset, bool keep_cur_pos) { int t; bool bt; pos tp; truecolour ttc; tree234 *ttr; if (!which) reset = false; /* do no weird resetting if which==0 */ if (which != term->alt_which) { if (term->erase_to_scrollback && term->alt_screen && term->alt_which && term->disptop < 0) { /* * We're swapping away from the alternate screen, so some * lines are about to vanish from the virtual scrollback. * Adjust disptop by that much, so that (if we're not * resetting the scrollback anyway on a display event) the * current scroll position still ends up pointing at the * same text. */ term->disptop += term->alt_sblines; if (term->disptop > 0) term->disptop = 0; } term->alt_which = which; ttr = term->alt_screen; term->alt_screen = term->screen; term->screen = ttr; term->alt_sblines = ( term->alt_screen ? find_last_nonempty_line(term, term->alt_screen) + 1 : 0); t = term->curs.x; if (!reset && !keep_cur_pos) term->curs.x = term->alt_x; term->alt_x = t; t = term->curs.y; if (!reset && !keep_cur_pos) term->curs.y = term->alt_y; term->alt_y = t; t = term->marg_t; if (!reset) term->marg_t = term->alt_t; term->alt_t = t; t = term->marg_b; if (!reset) term->marg_b = term->alt_b; term->alt_b = t; bt = term->dec_om; if (!reset) term->dec_om = term->alt_om; term->alt_om = bt; bt = term->wrap; if (!reset) term->wrap = term->alt_wrap; term->alt_wrap = bt; bt = term->wrapnext; if (!reset) term->wrapnext = term->alt_wnext; term->alt_wnext = bt; bt = term->insert; if (!reset) term->insert = term->alt_ins; term->alt_ins = bt; t = term->cset; if (!reset) term->cset = term->alt_cset; term->alt_cset = t; bt = term->utf; if (!reset) term->utf = term->alt_utf; term->alt_utf = bt; t = term->sco_acs; if (!reset) term->sco_acs = term->alt_sco_acs; term->alt_sco_acs = t; tp = term->savecurs; if (!reset) term->savecurs = term->alt_savecurs; term->alt_savecurs = tp; t = term->save_cset; if (!reset) term->save_cset = term->alt_save_cset; term->alt_save_cset = t; t = term->save_csattr; if (!reset) term->save_csattr = term->alt_save_csattr; term->alt_save_csattr = t; t = term->save_attr; if (!reset) term->save_attr = term->alt_save_attr; term->alt_save_attr = t; ttc = term->save_truecolour; if (!reset) term->save_truecolour = term->alt_save_truecolour; term->alt_save_truecolour = ttc; bt = term->save_utf; if (!reset) term->save_utf = term->alt_save_utf; term->alt_save_utf = bt; bt = term->save_wnext; if (!reset) term->save_wnext = term->alt_save_wnext; term->alt_save_wnext = bt; t = term->save_sco_acs; if (!reset) term->save_sco_acs = term->alt_save_sco_acs; term->alt_save_sco_acs = t; if (term->erase_to_scrollback && term->alt_screen && term->alt_which && term->disptop < 0) { /* * Inverse of the adjustment at the top of this function. * This time, we're swapping _to_ the alternate screen, so * some lines are about to _appear_ in the virtual * scrollback, and we adjust disptop in the other * direction. * * Both these adjustments depend on the value stored in * term->alt_sblines while the alt screen is selected, * which is why we had to do one _before_ switching away * from it and the other _after_ switching to it. */ term->disptop -= term->alt_sblines; int limit = -sblines(term); if (term->disptop < limit) term->disptop = limit; } } if (reset && term->screen) { /* * Yes, this _is_ supposed to honour background-colour-erase. */ erase_lots(term, false, true, true); } } /* * Update the scroll bar. */ static void update_sbar(Terminal *term) { int nscroll = sblines(term); win_set_scrollbar(term->win, nscroll + term->rows, nscroll + term->disptop, term->rows); } /* * Check whether the region bounded by the two pointers intersects * the scroll region, and de-select the on-screen selection if so. */ static void check_selection(Terminal *term, pos from, pos to) { if (poslt(from, term->selend) && poslt(term->selstart, to)) deselect(term); } static void clear_line(Terminal *term, termline *line) { resizeline(term, line, term->cols); for (int i = 0; i < term->cols; i++) copy_termchar(line, i, &term->erase_char); line->lattr = LATTR_NORM; } static void check_trust_status(Terminal *term, termline *line) { if (line->trusted != term->trusted) { /* * If we're displaying trusted output on a previously * untrusted line, or vice versa, we need to switch the * 'trusted' attribute on this terminal line, and also clear * all its previous contents. */ clear_line(term, line); line->trusted = term->trusted; } } /* * 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, bool sb) { termline *line; int seltop, scrollwinsize; if (topline != 0 || term->alt_which != 0) sb = false; 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); clear_line(term, line); addpos234(term->screen, line, topline); if (term->selstart.y >= topline && term->selstart.y <= botline) { term->selstart.y++; if (term->selstart.y > botline) { term->selstart.y = botline + 1; term->selstart.x = 0; } } 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); /* * We must add this line to the scrollback. We'll * remove a line from the top of the scrollback if * the scrollback is full. */ if (sblen == term->savelines) { unsigned char *cline; sblen--; cline = delpos234(term->scrollback, 0); sfree(cline); } else term->tempsblines += 1; addpos234(term->scrollback, compressline(line), sblen); /* now `line' itself can be reused as the bottom line */ /* * If the user is currently looking at part of the * scrollback, and they haven't enabled any options * that are going to reset the scrollback as a * result of this movement, then the chances are * they'd like to keep looking at the same line. So * we move their viewpoint at the same rate as the * scroll, at least until their viewpoint hits the * top end of the scrollback buffer, at which point * we don't have the choice any more. * * Thanks to Jan Holmen Holsten for the idea and * initial implementation. */ if (term->disptop > -term->savelines && term->disptop < 0) term->disptop--; } resizeline(term, line, term->cols); clear_line(term, line); check_trust_status(term, line); addpos234(term->screen, line, botline); /* * If the selection endpoints move into the scrollback, * we keep them moving until they hit the top. However, * of course, if the line _hasn't_ moved into the * scrollback then we don't do this, and cut them off * at the top of the scroll region. * * This applies to selstart and selend (for an existing * selection), and also selanchor (for one being * selected as we speak). */ seltop = sb ? -term->savelines : topline; if (term->selstate != NO_SELECTION) { if (term->selstart.y >= seltop && term->selstart.y <= botline) { term->selstart.y--; if (term->selstart.y < seltop) { term->selstart.y = seltop; term->selstart.x = 0; } } if (term->selend.y >= seltop && term->selend.y <= botline) { term->selend.y--; if (term->selend.y < seltop) { term->selend.y = seltop; term->selend.x = 0; } } if (term->selanchor.y >= seltop && term->selanchor.y <= botline) { term->selanchor.y--; if (term->selanchor.y < seltop) { term->selanchor.y = seltop; term->selanchor.x = 0; } } } } } } /* * Move the cursor to a given position, clipping at boundaries. We * may or may not want to clip at the scroll margin: marg_clip is 0 * not to, 1 to disallow _passing_ the margins, and 2 to disallow * even _being_ outside the margins. */ static void move(Terminal *term, int x, int y, int marg_clip) { if (x < 0) x = 0; if (x >= term->cols) x = term->cols - 1; if (marg_clip) { if ((term->curs.y >= term->marg_t || marg_clip == 2) && y < term->marg_t) y = term->marg_t; if ((term->curs.y <= term->marg_b || marg_clip == 2) && y > term->marg_b) y = term->marg_b; } if (y < 0) y = 0; if (y >= term->rows) y = term->rows - 1; term->curs.x = x; term->curs.y = y; term->wrapnext = false; } /* * Save or restore the cursor and SGR mode. */ static void save_cursor(Terminal *term, bool save) { if (save) { term->savecurs = term->curs; term->save_attr = term->curr_attr; term->save_truecolour = term->curr_truecolour; term->save_cset = term->cset; term->save_utf = term->utf; term->save_wnext = term->wrapnext; term->save_csattr = term->cset_attr[term->cset]; term->save_sco_acs = term->sco_acs; } else { term->curs = term->savecurs; /* Make sure the window hasn't shrunk since the save */ if (term->curs.x >= term->cols) term->curs.x = term->cols - 1; if (term->curs.y >= term->rows) term->curs.y = term->rows - 1; term->curr_attr = term->save_attr; term->curr_truecolour = term->save_truecolour; term->cset = term->save_cset; term->utf = term->save_utf; term->wrapnext = term->save_wnext; /* * wrapnext might reset to False if the x position is no * longer at the rightmost edge. */ if (term->wrapnext && term->curs.x < term->cols-1) term->wrapnext = false; term->cset_attr[term->cset] = term->save_csattr; term->sco_acs = term->save_sco_acs; set_erase_char(term); } } /* * This function is called before doing _anything_ which affects * only part of a line of text. It is used to mark the boundary * between two character positions, and it indicates that some sort * of effect is going to happen on only one side of that boundary. * * The effect of this function is to check whether a CJK * double-width character is straddling the boundary, and to remove * it and replace it with two spaces if so. (Of course, one or * other of those spaces is then likely to be replaced with * something else again, as a result of whatever happens next.) * * Also, if the boundary is at the right-hand _edge_ of the screen, * it implies something deliberate is being done to the rightmost * column position; hence we must clear LATTR_WRAPPED2. * * The input to the function is the coordinates of the _second_ * character of the pair. */ static void check_boundary(Terminal *term, int x, int y) { termline *ldata; /* Validate input coordinates, just in case. */ if (x <= 0 || x > term->cols) return; ldata = scrlineptr(y); check_trust_status(term, ldata); check_line_size(term, ldata); if (x == term->cols) { ldata->lattr &= ~LATTR_WRAPPED2; } else { if (ldata->chars[x].chr == UCSWIDE) { clear_cc(ldata, x-1); clear_cc(ldata, x); ldata->chars[x-1].chr = ' ' | CSET_ASCII; ldata->chars[x] = ldata->chars[x-1]; } } } /* * Erase a large portion of the screen: the whole screen, or the * whole line, or parts thereof. */ static void erase_lots(Terminal *term, bool line_only, bool from_begin, bool to_end) { pos start, end; bool erase_lattr; bool erasing_lines_from_top = false; if (line_only) { start.y = term->curs.y; start.x = 0; end.y = term->curs.y + 1; end.x = 0; erase_lattr = false; } else { start.y = 0; start.x = 0; end.y = term->rows; end.x = 0; erase_lattr = true; } /* This is the endpoint of the clearing operation that is not * either the start or end of the line / screen. */ pos boundary = term->curs; if (!from_begin) { /* * If we're erasing from the current char to the end of * line/screen, then we take account of wrapnext, so as to * maintain the invariant that writing a printing character * followed by ESC[K should not overwrite the character you * _just wrote_. That is, when wrapnext says the cursor is * 'logically' at the very rightmost edge of the screen * instead of just before the last printing char, ESC[K should * do nothing at all, and ESC[J should clear the next line but * leave this one unchanged. * * This adjusted position will also be the position we use for * check_boundary (i.e. the thing we ensure isn't in the * middle of a double-width printing char). */ if (term->wrapnext) incpos(boundary); start = boundary; } if (!to_end) { /* * If we're erasing from the start of (at least) the line _to_ * the current position, then that is taken to mean 'inclusive * of the cell under the cursor', which means we don't * consider wrapnext at all: whether it's set or not, we still * clear the cell under the cursor. * * Again, that incremented boundary position is where we * should be careful of a straddling wide character. */ incpos(boundary); end = boundary; } if (!from_begin || !to_end) check_boundary(term, boundary.x, boundary.y); check_selection(term, start, end); /* Clear screen also forces a full window redraw, just in case. */ if (start.y == 0 && start.x == 0 && end.y == term->rows) 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 = true; 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; } if (scrolllines > 0) scroll(term, 0, scrolllines - 1, scrolllines, true); } else { termline *ldata = scrlineptr(start.y); check_trust_status(term, ldata); while (poslt(start, end)) { check_line_size(term, ldata); if (start.x == term->cols) { if (!erase_lattr) ldata->lattr &= ~(LATTR_WRAPPED | LATTR_WRAPPED2); else ldata->lattr = LATTR_NORM; } else { copy_termchar(ldata, start.x, &term->erase_char); } if (incpos(start) && start.y < term->rows) { ldata = scrlineptr(start.y); check_trust_status(term, ldata); } } } /* After an erase of lines from the top of the screen, we shouldn't * bring the lines back again if the terminal enlarges (since the user or * application has explicitly thrown them away). */ if (erasing_lines_from_top && !(term->alt_which)) term->tempsblines = 0; } /* * Insert or delete characters within the current line. n is +ve if * insertion is desired, and -ve for deletion. */ static void insch(Terminal *term, int n) { int dir = (n < 0 ? -1 : +1); int m, j; pos eol; termline *ldata; n = (n < 0 ? -n : n); if (n > term->cols - term->curs.x) n = term->cols - term->curs.x; m = term->cols - term->curs.x - n; /* * We must de-highlight the selection if it overlaps any part of * the region affected by this operation, i.e. the region from the * current cursor position to end-of-line, _unless_ the entirety * of the selection is going to be moved to the left or right by * this operation but otherwise unchanged, in which case we can * simply move the highlight with the text. */ eol.y = term->curs.y; eol.x = term->cols; if (poslt(term->curs, term->selend) && poslt(term->selstart, eol)) { pos okstart = term->curs; pos okend = eol; if (dir > 0) { /* Insertion: n characters at EOL will be splatted. */ okend.x -= n; } else { /* Deletion: n characters at cursor position will be splatted. */ okstart.x += n; } if (posle(okstart, term->selstart) && posle(term->selend, okend)) { /* Selection is contained entirely in the interval * [okstart,okend), so we need only adjust the selection * bounds. */ term->selstart.x += dir * n; term->selend.x += dir * n; assert(term->selstart.x >= term->curs.x); assert(term->selstart.x < term->cols); assert(term->selend.x > term->curs.x); assert(term->selend.x <= term->cols); } else { /* Selection is not wholly contained in that interval, so * we must unhighlight it. */ deselect(term); } } check_boundary(term, term->curs.x, term->curs.y); if (dir < 0) check_boundary(term, term->curs.x + n, term->curs.y); ldata = scrlineptr(term->curs.y); check_trust_status(term, ldata); if (dir < 0) { for (j = 0; j < m; j++) move_termchar(ldata, ldata->chars + term->curs.x + j, ldata->chars + term->curs.x + j + n); while (n--) copy_termchar(ldata, term->curs.x + m++, &term->erase_char); } else { for (j = m; j-- ;) move_termchar(ldata, ldata->chars + term->curs.x + j + n, ldata->chars + term->curs.x + j); while (n--) copy_termchar(ldata, term->curs.x + n, &term->erase_char); } } static void term_update_raw_mouse_mode(Terminal *term) { bool want_raw = (term->xterm_mouse != 0 && !term->xterm_mouse_forbidden); win_set_raw_mouse_mode(term->win, want_raw); term->win_pointer_shape_pending = true; term->win_pointer_shape_raw = want_raw; term_schedule_update(term); } /* * Toggle terminal mode `mode' to state `state'. (`query' indicates * whether the mode is a DEC private one or a normal one.) */ static void toggle_mode(Terminal *term, int mode, int query, bool state) { if (query == 1) { switch (mode) { case 1: /* DECCKM: application cursor keys */ term->app_cursor_keys = state; 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) { term->win_resize_pending = true; term->win_resize_pending_w = state ? 132 : 80; term->win_resize_pending_h = term->rows; term_schedule_update(term); } 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; case 5: /* DECSCNM: reverse video */ /* * Toggle reverse video. If we receive an OFF within the * visual bell timeout period after an ON, we trigger an * effective visual bell, so that ESC[?5hESC[?5l will * always be an actually _visible_ visual bell. */ if (term->rvideo && !state) { /* This is an OFF, so set up a vbell */ term_schedule_vbell(term, true, term->rvbell_startpoint); } else if (!term->rvideo && state) { /* This is an ON, so we notice the time and save it. */ term->rvbell_startpoint = GETTICKCOUNT(); } term->rvideo = state; seen_disp_event(term); break; case 6: /* DECOM: DEC origin mode */ term->dec_om = state; break; case 7: /* DECAWM: auto wrap */ term->wrap = state; break; case 8: /* DECARM: auto key repeat */ term->repeat_off = !state; break; case 25: /* DECTCEM: enable/disable cursor */ 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); if (term->scroll_on_disp) term->disptop = 0; break; case 1000: /* xterm mouse 1 (normal) */ term->xterm_mouse = state ? 1 : 0; term_update_raw_mouse_mode(term); break; case 1002: /* xterm mouse 2 (inc. button drags) */ term->xterm_mouse = state ? 2 : 0; term_update_raw_mouse_mode(term); break; case 1006: /* xterm extended mouse */ term->xterm_extended_mouse = state; break; case 1015: /* urxvt extended mouse */ term->urxvt_extended_mouse = state; break; case 1047: /* alternate screen */ compatibility(OTHER); deselect(term); swap_screen(term, term->no_alt_screen ? 0 : state, true, true); if (term->scroll_on_disp) 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); if (term->scroll_on_disp) term->disptop = 0; break; case 2004: /* xterm bracketed paste */ term->bracketed_paste = state ? true : false; break; } } else if (query == 0) { switch (mode) { case 4: /* IRM: set insert mode */ compatibility(VT102); term->insert = state; break; case 12: /* SRM: set echo mode */ term->srm_echo = !state; break; case 20: /* LNM: Return sends ... */ term->cr_lf_return = state; break; case 34: /* WYULCURM: Make cursor BIG */ compatibility2(OTHER, VT220); term->big_cursor = !state; } } } /* * Process an OSC sequence: set window title or icon name. */ static void do_osc(Terminal *term) { if (term->osc_w) { while (term->osc_strlen--) 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) { sfree(term->icon_title); term->icon_title = dupstr(term->osc_string); term->win_icon_title_pending = true; term_schedule_update(term); } if (term->esc_args[0] == 1) break; /* fall through: parameter 0 means set both */ case 2: case 21: if (!term->no_remote_wintitle) { sfree(term->window_title); term->window_title = dupstr(term->osc_string); term->win_title_pending = true; term_schedule_update(term); } break; case 4: if (term->ldisc && !strcmp(term->osc_string, "?")) { unsigned index = term->esc_args[1]; if (index < OSC4_NCOLOURS) { rgb colour = term->palette[index]; char *reply_buf = dupprintf( "\033]4;%u;rgb:%04x/%04x/%04x\007", index, (unsigned)colour.r * 0x0101, (unsigned)colour.g * 0x0101, (unsigned)colour.b * 0x0101); ldisc_send(term->ldisc, reply_buf, strlen(reply_buf), false); sfree(reply_buf); } } 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) { size_t size; while ((size = bufchain_size(&term->printer_buf)) > 5) { ptrlen data = bufchain_prefix(&term->printer_buf); if (data.len > size-5) data.len = size-5; printer_job_data(term->print_job, data.ptr, data.len); bufchain_consume(&term->printer_buf, data.len); } } static void term_print_finish(Terminal *term) { size_t size; char c; if (!term->printing && !term->only_printing) return; /* we need do nothing */ term_print_flush(term); while ((size = bufchain_size(&term->printer_buf)) > 0) { ptrlen data = bufchain_prefix(&term->printer_buf); c = *(char *)data.ptr; if (c == '\033' || c == '\233') { bufchain_consume(&term->printer_buf, size); break; } else { printer_job_data(term->print_job, &c, 1); bufchain_consume(&term->printer_buf, 1); } } printer_finish_job(term->print_job); term->print_job = NULL; term->printing = term->only_printing = false; } static void term_display_graphic_char(Terminal *term, unsigned long c) { termline *cline = scrlineptr(term->curs.y); int width = 0; if (DIRECT_CHAR(c)) width = 1; if (!width) width = term_char_width(term, 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++; term->curs.x = 0; term->wrapnext = false; cline = scrlineptr(term->curs.y); } if (term->insert && width > 0) insch(term, width); if (term->selstate != NO_SELECTION) { pos cursplus = term->curs; incpos(cursplus); check_selection(term, term->curs, cursplus); } if (((c & CSET_MASK) == CSET_ASCII || (c & CSET_MASK) == 0) && term->logctx) logtraffic(term->logctx, (unsigned char) c, LGTYP_ASCII); check_trust_status(term, cline); int linecols = term->cols; if (cline->trusted) linecols -= TRUST_SIGIL_WIDTH; /* * Preliminary check: if the terminal is only one character cell * wide, then we cannot display any double-width character at all. * Substitute single-width REPLACEMENT CHARACTER instead. */ if (width == 2 && linecols < 2) { width = 1; c = 0xFFFD; } switch (width) { case 2: /* * If we're about to display a double-width character starting * in the rightmost column, then we do something special * instead. We must print a space in the last column of the * screen, then wrap; and we also set LATTR_WRAPPED2 which * instructs subsequent cut-and-pasting not only to splice * this line to the one after it, but to ignore the space in * the last character position as well. (Because what was * actually output to the terminal was presumably just a * sequence of CJK characters, and we don't want a space to be * pasted in the middle of those just because they had the * misfortune to start in the wrong parity column. xterm * concurs.) */ check_boundary(term, term->curs.x, term->curs.y); check_boundary(term, term->curs.x+2, term->curs.y); if (term->curs.x >= linecols-1) { copy_termchar(cline, term->curs.x, &term->erase_char); cline->lattr |= LATTR_WRAPPED | LATTR_WRAPPED2; 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++; term->curs.x = 0; cline = scrlineptr(term->curs.y); /* Now we must check_boundary again, of course. */ check_boundary(term, term->curs.x, term->curs.y); check_boundary(term, term->curs.x+2, term->curs.y); } /* FULL-TERMCHAR */ clear_cc(cline, term->curs.x); cline->chars[term->curs.x].chr = c; cline->chars[term->curs.x].attr = term->curr_attr; cline->chars[term->curs.x].truecolour = term->curr_truecolour; term->curs.x++; /* FULL-TERMCHAR */ clear_cc(cline, term->curs.x); cline->chars[term->curs.x].chr = UCSWIDE; cline->chars[term->curs.x].attr = term->curr_attr; cline->chars[term->curs.x].truecolour = term->curr_truecolour; break; case 1: check_boundary(term, term->curs.x, term->curs.y); check_boundary(term, term->curs.x+1, term->curs.y); /* FULL-TERMCHAR */ clear_cc(cline, term->curs.x); cline->chars[term->curs.x].chr = c; cline->chars[term->curs.x].attr = term->curr_attr; cline->chars[term->curs.x].truecolour = term->curr_truecolour; break; case 0: if (term->curs.x > 0) { int x = term->curs.x - 1; /* If we're in wrapnext state, the character to combine * with is _here_, not to our left. */ if (term->wrapnext) x++; /* * If the previous character is UCSWIDE, back up another * one. */ if (cline->chars[x].chr == UCSWIDE) { assert(x > 0); x--; } add_cc(cline, x, c); seen_disp_event(term); } return; default: return; } term->curs.x++; if (term->curs.x >= linecols) { term->curs.x = linecols - 1; term->wrapnext = true; if (term->wrap && term->vt52_mode) { 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++; term->curs.x = 0; term->wrapnext = false; } } seen_disp_event(term); } static strbuf *term_input_data_from_unicode( Terminal *term, const wchar_t *widebuf, int len) { strbuf *buf = strbuf_new(); if (in_utf(term)) { /* * Translate input wide characters into UTF-8 to go in the * terminal's input data queue. */ for (int 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 = '.'; } } char utf8_chr[6]; put_data(buf, utf8_chr, encode_utf8(utf8_chr, ch)); } } else { /* * Call to the character-set subsystem to translate into * whatever charset the terminal is currently configured in. * * Since the terminal doesn't currently support any multibyte * character set other than UTF-8, we can assume here that * there will be at most one output byte per input wchar_t. * (But also we must allow space for the trailing NUL that * wc_to_mb will write.) */ char *bufptr = strbuf_append(buf, len + 1); int rv; rv = wc_to_mb(term->ucsdata->line_codepage, 0, widebuf, len, bufptr, len + 1, NULL, term->ucsdata); strbuf_shrink_to(buf, rv < 0 ? 0 : rv); } return buf; } static strbuf *term_input_data_from_charset( Terminal *term, int codepage, const char *str, int len) { strbuf *buf; if (codepage < 0) { buf = strbuf_new(); put_data(buf, str, len); } else { int widesize = len * 2; /* allow for UTF-16 surrogates */ wchar_t *widebuf = snewn(widesize, wchar_t); int widelen = mb_to_wc(codepage, 0, str, len, widebuf, widesize); buf = term_input_data_from_unicode(term, widebuf, widelen); sfree(widebuf); } return buf; } static inline void term_bracketed_paste_start(Terminal *term) { ptrlen seq = PTRLEN_LITERAL("\033[200~"); if (term->ldisc) ldisc_send(term->ldisc, seq.ptr, seq.len, false); term->bracketed_paste_active = true; } static inline void term_bracketed_paste_stop(Terminal *term) { if (!term->bracketed_paste_active) return; ptrlen seq = PTRLEN_LITERAL("\033[201~"); if (term->ldisc) ldisc_send(term->ldisc, seq.ptr, seq.len, false); term->bracketed_paste_active = false; } static inline void term_keyinput_internal( Terminal *term, const void *buf, int len, bool interactive) { if (term->srm_echo) { /* * Implement the terminal-level local echo behaviour that * ECMA-48 specifies when terminal mode 12 is configured off * (ESC[12l). In this mode, data input to the terminal via the * keyboard is also added to the output buffer. But this * doesn't apply to escape sequences generated as session * input _within_ the terminal, e.g. in response to terminal * query sequences, or the bracketing sequences of bracketed * paste mode. Those will be sent directly via * ldisc_send(term->ldisc, ...) and won't go through this * function. */ /* Mimic the special case of negative length in ldisc_send */ int true_len = len >= 0 ? len : strlen(buf); bufchain_add(&term->inbuf, buf, true_len); term_added_data(term); } if (interactive) term_bracketed_paste_stop(term); if (term->ldisc) ldisc_send(term->ldisc, buf, len, interactive); term_seen_key_event(term); } unsigned long term_translate( Terminal *term, struct term_utf8_decode *utf8, unsigned char c) { if (in_utf(term)) { switch (utf8->state) { case 0: if (c < 0x80) { /* UTF-8 must be stateless so we ignore iso2022. */ if (term->ucsdata->unitab_ctrl[c] != 0xFF) { return term->ucsdata->unitab_ctrl[c]; } else if ((term->utf8linedraw) && (term->cset_attr[term->cset] == CSET_LINEDRW)) { /* Linedraw characters are explicitly enabled */ return c | CSET_LINEDRW; } else { return c | CSET_ASCII; } } else if ((c & 0xe0) == 0xc0) { utf8->size = utf8->state = 1; utf8->chr = (c & 0x1f); } else if ((c & 0xf0) == 0xe0) { utf8->size = utf8->state = 2; utf8->chr = (c & 0x0f); } else if ((c & 0xf8) == 0xf0) { utf8->size = utf8->state = 3; utf8->chr = (c & 0x07); } else if ((c & 0xfc) == 0xf8) { utf8->size = utf8->state = 4; utf8->chr = (c & 0x03); } else if ((c & 0xfe) == 0xfc) { utf8->size = utf8->state = 5; utf8->chr = (c & 0x01); } else { return UCSINVALID; } return UCSINCOMPLETE; case 1: case 2: case 3: case 4: case 5: if ((c & 0xC0) != 0x80) { utf8->state = 0; return UCSTRUNCATED; /* caller will then give us the * same byte again */ } utf8->chr = (utf8->chr << 6) | (c & 0x3f); if (--utf8->state) return UCSINCOMPLETE; unsigned long t = utf8->chr; /* Is somebody trying to be evil! */ if (t < 0x80 || (t < 0x800 && utf8->size >= 2) || (t < 0x10000 && utf8->size >= 3) || (t < 0x200000 && utf8->size >= 4) || (t < 0x4000000 && utf8->size >= 5)) return UCSINVALID; /* Unicode line separator and paragraph separator are CR-LF */ if (t == 0x2028 || t == 0x2029) return 0x85; /* High controls are probably a Baaad idea too. */ if (t < 0xA0) return 0xFFFD; /* The UTF-16 surrogates are not nice either. */ /* The standard give the option of decoding these: * I don't want to! */ if (t >= 0xD800 && t < 0xE000) return UCSINVALID; /* ISO 10646 characters now limited to UTF-16 range. */ if (t > 0x10FFFF) return UCSINVALID; /* This is currently a TagPhobic application.. */ if (t >= 0xE0000 && t <= 0xE007F) return UCSINCOMPLETE; /* U+FEFF is best seen as a null. */ if (t == 0xFEFF) return UCSINCOMPLETE; /* But U+FFFE is an error. */ if (t == 0xFFFE || t == 0xFFFF) return UCSINVALID; return t; } } else if (term->sco_acs && (c!='\033' && c!='\012' && c!='\015' && c!='\b')) { /* Are we in the nasty ACS mode? Note: no sco in utf mode. */ if (term->sco_acs == 2) c |= 0x80; return c | CSET_SCOACS; } else { switch (term->cset_attr[term->cset]) { /* * Linedraw characters are different from 'ESC ( B' * only for a small range. For ones outside that * range, make sure we use the same font as well as * the same encoding. */ case CSET_LINEDRW: if (term->ucsdata->unitab_ctrl[c] != 0xFF) return term->ucsdata->unitab_ctrl[c]; else return c | CSET_LINEDRW; break; case CSET_GBCHR: /* If UK-ASCII, make the '#' a LineDraw Pound */ if (c == '#') return '}' | CSET_LINEDRW; /* fall through */ case CSET_ASCII: if (term->ucsdata->unitab_ctrl[c] != 0xFF) return term->ucsdata->unitab_ctrl[c]; else return c | CSET_ASCII; break; case CSET_SCOACS: if (c >= ' ') return c | CSET_SCOACS; break; } } return c; } /* * Remove everything currently in `inbuf' and stick it up on the * in-memory display. There's a big state machine in here to * process escape sequences... */ static void term_out(Terminal *term) { unsigned long c; int unget; unsigned char localbuf[256], *chars; size_t nchars = 0; unget = -1; chars = NULL; /* placate compiler warnings */ while (nchars > 0 || unget != -1 || bufchain_size(&term->inbuf) > 0) { if (unget == -1) { if (nchars == 0) { ptrlen data = bufchain_prefix(&term->inbuf); if (data.len > sizeof(localbuf)) data.len = sizeof(localbuf); memcpy(localbuf, data.ptr, data.len); bufchain_consume(&term->inbuf, data.len); nchars = data.len; chars = localbuf; assert(chars != NULL); assert(nchars > 0); } 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 * be able to display 8-bit characters, but I'll let that go 'cause * of i18n. */ /* * If we're printing, add the character to the printer * buffer. */ if (term->printing) { bufchain_add(&term->printer_buf, &c, 1); /* * If we're in print-only mode, we use a much simpler * state machine designed only to recognise the ESC[4i * termination sequence. */ if (term->only_printing) { if (c == '\033') term->print_state = 1; else if (c == (unsigned char)'\233') term->print_state = 2; else if (c == '[' && term->print_state == 1) term->print_state = 2; else if (c == '4' && term->print_state == 2) term->print_state = 3; else if (c == 'i' && term->print_state == 3) term->print_state = 4; else term->print_state = 0; if (term->print_state == 4) { term_print_finish(term); } continue; } } /* Do character-set translation. */ if (term->termstate == TOPLEVEL) { unsigned long t = term_translate(term, &term->utf8, c); switch (t) { case UCSINCOMPLETE: continue; /* didn't complete a multibyte char */ case UCSTRUNCATED: unget = c; /* fall through */ case UCSINVALID: c = UCSERR; break; default: c = t; break; } } /* * How about C1 controls? * Explicitly ignore SCI (0x9a), which we don't translate to DECID. */ if ((c & -32) == 0x80 && term->termstate < DO_CTRLS && !term->vt52_mode && has_compat(VT220)) { if (c == 0x9a) c = 0; else { term->termstate = SEEN_ESC; term->esc_query = 0; c = '@' + (c & 0x1F); } } /* 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) { strbuf *buf = term_input_data_from_charset( term, DEFAULT_CODEPAGE, term->answerback, term->answerbacklen); ldisc_send(term->ldisc, buf->s, buf->len, false); strbuf_free(buf); } break; case '\007': { /* BEL: Bell */ struct beeptime *newbeep; unsigned long ticks; ticks = GETTICKCOUNT(); if (!term->beep_overloaded) { newbeep = snew(struct beeptime); newbeep->ticks = ticks; newbeep->next = NULL; if (!term->beephead) term->beephead = newbeep; else term->beeptail->next = newbeep; term->beeptail = newbeep; term->nbeeps++; } /* * 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) { win_bell(term->win, term->beep); if (term->beep == BELL_VISUAL) { term_schedule_vbell(term, false, 0); } } seen_disp_event(term); break; } case '\b': /* BS: Back space */ if (term->curs.x == 0 && (term->curs.y == 0 || !term->wrap)) /* do nothing */ ; else if (term->curs.x == 0 && term->curs.y > 0) term->curs.x = term->cols - 1, term->curs.y--; else if (term->wrapnext) term->wrapnext = false; else term->curs.x--; seen_disp_event(term); break; case '\016': /* LS1: Locking-shift one */ compatibility(VT100); term->cset = 1; break; case '\017': /* LS0: Locking-shift zero */ compatibility(VT100); term->cset = 0; break; case '\033': /* ESC: Escape */ if (term->vt52_mode) term->termstate = VT52_ESC; else { compatibility(ANSIMIN); term->termstate = SEEN_ESC; term->esc_query = 0; } 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); if (term->scroll_on_disp) 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); do { term->curs.x++; } while (term->curs.x < term->cols - 1 && !term->tabs[term->curs.x]); if ((ldata->lattr & LATTR_MODE) != LATTR_NORM) { if (term->curs.x >= term->cols / 2) term->curs.x = term->cols / 2 - 1; } else { if (term->curs.x >= term->cols) term->curs.x = term->cols - 1; } check_selection(term, old_curs, term->curs); seen_disp_event(term); break; } } } else switch (term->termstate) { case TOPLEVEL: /* Only graphic characters get this far; * ctrls are stripped above */ term_display_graphic_char(term, c); term->last_graphic_char = c; break; case OSC_MAYBE_ST: /* * This state is virtually identical to SEEN_ESC, with the * exception that we have an OSC sequence in the pipeline, * and _if_ we see a backslash, we process it. */ if (c == '\\') { do_osc(term); term->termstate = TOPLEVEL; break; } /* else fall through */ case SEEN_ESC: if (c >= ' ' && c <= '/') { if (term->esc_query) term->esc_query = -1; else term->esc_query = c; break; } term->termstate = TOPLEVEL; switch (ANSI(c, term->esc_query)) { case '[': /* enter CSI mode */ term->termstate = SEEN_CSI; term->esc_nargs = 1; term->esc_args[0] = ARG_DEFAULT; term->esc_query = 0; break; case ']': /* OSC: xterm escape sequences */ /* Compatibility is nasty here, xterm, linux, decterm yuk! */ compatibility(OTHER); term->termstate = SEEN_OSC; term->esc_args[0] = 0; term->esc_nargs = 1; break; case '7': /* DECSC: save cursor */ compatibility(VT100); save_cursor(term, true); break; case '8': /* DECRC: restore cursor */ compatibility(VT100); save_cursor(term, false); seen_disp_event(term); break; case '=': /* DECKPAM: Keypad application mode */ compatibility(VT100); term->app_keypad_keys = true; break; case '>': /* DECKPNM: Keypad numeric mode */ compatibility(VT100); term->app_keypad_keys = false; break; case 'D': /* IND: exactly equivalent to LF */ compatibility(VT100); 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++; term->wrapnext = false; seen_disp_event(term); break; case 'E': /* NEL: exactly equivalent to CR-LF */ compatibility(VT100); term->curs.x = 0; 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++; term->wrapnext = false; seen_disp_event(term); break; case 'M': /* RI: reverse index - backwards LF */ compatibility(VT100); if (term->curs.y == term->marg_t) scroll(term, term->marg_t, term->marg_b, -1, true); else if (term->curs.y > 0) term->curs.y--; term->wrapnext = false; seen_disp_event(term); break; case 'Z': /* DECID: terminal type query */ compatibility(VT100); if (term->ldisc) ldisc_send(term->ldisc, term->id_string, strlen(term->id_string), false); break; case 'c': /* RIS: restore power-on settings */ compatibility(VT100); power_on(term, true); if (term->ldisc) /* cause ldisc to notice changes */ ldisc_echoedit_update(term->ldisc); if (term->reset_132) { if (!term->no_remote_resize) { term->win_resize_pending = true; term->win_resize_pending_w = 80; term->win_resize_pending_h = term->rows; term_schedule_update(term); } term->reset_132 = false; } if (term->scroll_on_disp) term->disptop = 0; seen_disp_event(term); break; case 'H': /* HTS: set a tab */ compatibility(VT100); term->tabs[term->curs.x] = true; break; case ANSI('8', '#'): { /* DECALN: fills screen with Es :-) */ compatibility(VT100); termline *ldata; int i, j; pos scrtop, scrbot; for (i = 0; i < term->rows; i++) { ldata = scrlineptr(i); check_line_size(term, ldata); for (j = 0; j < term->cols; j++) { copy_termchar(ldata, j, &term->basic_erase_char); ldata->chars[j].chr = 'E'; } ldata->lattr = LATTR_NORM; } if (term->scroll_on_disp) term->disptop = 0; seen_disp_event(term); scrtop.x = scrtop.y = 0; scrbot.x = 0; scrbot.y = term->rows; check_selection(term, scrtop, scrbot); break; } case ANSI('3', '#'): case ANSI('4', '#'): case ANSI('5', '#'): case ANSI('6', '#'): { compatibility(VT100); int nlattr; termline *ldata; switch (ANSI(c, term->esc_query)) { case ANSI('3', '#'): /* DECDHL: 2*height, top */ nlattr = LATTR_TOP; break; case ANSI('4', '#'): /* DECDHL: 2*height, bottom */ nlattr = LATTR_BOT; break; case ANSI('5', '#'): /* DECSWL: normal */ nlattr = LATTR_NORM; break; default: /* case ANSI('6', '#'): DECDWL: 2*width */ nlattr = LATTR_WIDE; break; } ldata = scrlineptr(term->curs.y); check_line_size(term, ldata); check_trust_status(term, ldata); ldata->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 = true; break; case ANSI('@', '%'): compatibility(OTHER); if (!term->no_remote_charset) term->utf = false; break; } break; case SEEN_CSI: term->termstate = TOPLEVEL; /* default */ if (isdigit(c)) { if (term->esc_nargs <= ARGS_MAX) { if (term->esc_args[term->esc_nargs - 1] == ARG_DEFAULT) term->esc_args[term->esc_nargs - 1] = 0; if (term->esc_args[term->esc_nargs - 1] <= UINT_MAX / 10 && term->esc_args[term->esc_nargs - 1] * 10 <= UINT_MAX - c - '0') term->esc_args[term->esc_nargs - 1] = 10 * term->esc_args[term->esc_nargs - 1] + c - '0'; else term->esc_args[term->esc_nargs - 1] = UINT_MAX; } term->termstate = SEEN_CSI; } else if (c == ';') { if (term->esc_nargs < ARGS_MAX) term->esc_args[term->esc_nargs++] = ARG_DEFAULT; term->termstate = SEEN_CSI; } else if (c < '@') { if (term->esc_query) term->esc_query = -1; else if (c == '?') term->esc_query = 1; else term->esc_query = c; term->termstate = SEEN_CSI; } else #define CLAMP(arg, lim) ((arg) = ((arg) > (lim)) ? (lim) : (arg)) switch (ANSI(c, term->esc_query)) { case 'A': /* CUU: move up N lines */ CLAMP(term->esc_args[0], term->rows); move(term, term->curs.x, term->curs.y - def(term->esc_args[0], 1), 1); seen_disp_event(term); break; case 'e': /* VPR: move down N lines */ compatibility(ANSI); /* FALLTHROUGH */ case 'B': /* CUD: Cursor down */ CLAMP(term->esc_args[0], term->rows); move(term, term->curs.x, term->curs.y + def(term->esc_args[0], 1), 1); seen_disp_event(term); break; case 'b': /* REP: repeat previous grap */ CLAMP(term->esc_args[0], term->rows * term->cols); if (term->last_graphic_char) { unsigned i; for (i = 0; i < term->esc_args[0]; i++) term_display_graphic_char( term, term->last_graphic_char); } break; case ANSI('c', '>'): /* DA: report xterm version */ compatibility(OTHER); /* this reports xterm version 136 so that VIM can use the drag messages from the mouse reporting */ if (term->ldisc) ldisc_send(term->ldisc, "\033[>0;136;0c", 11, false); break; case 'a': /* HPR: move right N cols */ compatibility(ANSI); /* FALLTHROUGH */ case 'C': /* CUF: Cursor right */ CLAMP(term->esc_args[0], term->cols); move(term, term->curs.x + def(term->esc_args[0], 1), term->curs.y, 1); seen_disp_event(term); break; case 'D': /* CUB: move left N cols */ CLAMP(term->esc_args[0], term->cols); move(term, term->curs.x - def(term->esc_args[0], 1), term->curs.y, 1); seen_disp_event(term); break; case 'E': /* CNL: move down N lines and CR */ compatibility(ANSI); CLAMP(term->esc_args[0], term->rows); move(term, 0, term->curs.y + def(term->esc_args[0], 1), 1); seen_disp_event(term); break; case 'F': /* CPL: move up N lines and CR */ compatibility(ANSI); CLAMP(term->esc_args[0], term->rows); move(term, 0, term->curs.y - def(term->esc_args[0], 1), 1); seen_disp_event(term); break; case 'G': /* CHA */ case '`': /* HPA: set horizontal posn */ compatibility(ANSI); CLAMP(term->esc_args[0], term->cols); move(term, def(term->esc_args[0], 1) - 1, term->curs.y, 0); seen_disp_event(term); break; case 'd': /* VPA: set vertical posn */ compatibility(ANSI); CLAMP(term->esc_args[0], term->rows); move(term, term->curs.x, ((term->dec_om ? term->marg_t : 0) + def(term->esc_args[0], 1) - 1), (term->dec_om ? 2 : 0)); seen_disp_event(term); break; case 'H': /* CUP */ case 'f': /* HVP: set horz and vert posns at once */ if (term->esc_nargs < 2) term->esc_args[1] = ARG_DEFAULT; CLAMP(term->esc_args[0], term->rows); CLAMP(term->esc_args[1], term->cols); move(term, def(term->esc_args[1], 1) - 1, ((term->dec_om ? term->marg_t : 0) + def(term->esc_args[0], 1) - 1), (term->dec_om ? 2 : 0)); seen_disp_event(term); break; case 'J': { /* ED: erase screen or parts of it */ unsigned int i = def(term->esc_args[0], 0); if (i == 3) { /* Erase Saved Lines (xterm) * This follows Thomas Dickey's xterm. */ if (!term->no_remote_clearscroll) term_clrsb(term); } else { i++; if (i > 3) i = 0; erase_lots(term, false, !!(i & 2), !!(i & 1)); } if (term->scroll_on_disp) term->disptop = 0; seen_disp_event(term); break; } case 'K': { /* EL: erase line or parts of it */ unsigned int i = def(term->esc_args[0], 0) + 1; if (i > 3) i = 0; erase_lots(term, true, !!(i & 2), !!(i & 1)); seen_disp_event(term); break; } case 'L': /* IL: insert lines */ compatibility(VT102); CLAMP(term->esc_args[0], term->rows); if (term->curs.y <= term->marg_b) scroll(term, term->curs.y, term->marg_b, -def(term->esc_args[0], 1), false); seen_disp_event(term); break; case 'M': /* DL: delete lines */ compatibility(VT102); CLAMP(term->esc_args[0], term->rows); if (term->curs.y <= term->marg_b) scroll(term, term->curs.y, term->marg_b, def(term->esc_args[0], 1), true); seen_disp_event(term); break; case '@': /* ICH: insert chars */ /* XXX VTTEST says this is vt220, vt510 manual says vt102 */ compatibility(VT102); CLAMP(term->esc_args[0], term->cols); insch(term, def(term->esc_args[0], 1)); seen_disp_event(term); break; case 'P': /* DCH: delete chars */ compatibility(VT102); CLAMP(term->esc_args[0], term->cols); insch(term, -def(term->esc_args[0], 1)); seen_disp_event(term); break; case 'c': /* DA: terminal type query */ compatibility(VT100); /* This is the response for a VT102 */ if (term->ldisc) ldisc_send(term->ldisc, term->id_string, strlen(term->id_string), false); break; case 'n': /* DSR: cursor position query */ if (term->ldisc) { if (term->esc_args[0] == 6) { char buf[32]; sprintf(buf, "\033[%d;%dR", term->curs.y + 1, term->curs.x + 1); ldisc_send(term->ldisc, buf, strlen(buf), false); } else if (term->esc_args[0] == 5) { ldisc_send(term->ldisc, "\033[0n", 4, false); } } break; case 'h': /* SM: toggle modes to high */ case ANSI_QUE('h'): compatibility(VT100); for (int i = 0; i < term->esc_nargs; i++) toggle_mode(term, term->esc_args[i], 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 */ case ANSI_QUE('l'): compatibility(VT100); for (int i = 0; i < term->esc_nargs; i++) toggle_mode(term, term->esc_args[i], term->esc_query, false); break; case 'g': /* TBC: clear tabs */ compatibility(VT100); if (term->esc_nargs == 1) { if (term->esc_args[0] == 0) { term->tabs[term->curs.x] = false; } else if (term->esc_args[0] == 3) { int i; for (i = 0; i < term->cols; i++) term->tabs[i] = false; } } break; case 'r': /* DECSTBM: set scroll margins */ compatibility(VT100); if (term->esc_nargs <= 2) { int top, bot; CLAMP(term->esc_args[0], term->rows); CLAMP(term->esc_args[1], term->rows); top = def(term->esc_args[0], 1) - 1; bot = (term->esc_nargs <= 1 || term->esc_args[1] == 0 ? term->rows : def(term->esc_args[1], term->rows)) - 1; if (bot >= term->rows) bot = term->rows - 1; /* VTTEST Bug 9 - if region is less than 2 lines * don't change region. */ if (bot - top > 0) { term->marg_t = top; term->marg_b = bot; term->curs.x = 0; /* * I used to think the cursor should be * placed at the top of the newly marginned * area. Apparently not: VMS TPU falls over * if so. * * Well actually it should for * Origin mode - RDB */ term->curs.y = (term->dec_om ? term->marg_t : 0); seen_disp_event(term); } } break; case 'm': /* SGR: set graphics rendition */ /* * A VT100 without the AVO only had one * attribute, either underline or reverse * video depending on the cursor type, this * was selected by CSI 7m. * * case 2: * This is sometimes DIM, eg on the GIGI and * Linux * case 8: * This is sometimes INVIS various ANSI. * case 21: * This like 22 disables BOLD, DIM and INVIS * * The ANSI colours appear on any terminal * that has colour (obviously) but the * interaction between sgr0 and the colours * varies but is usually related to the * background colour erase item. The * interaction between colour attributes and * the mono ones is also very implementation * dependent. * * The 39 and 49 attributes are likely to be * unimplemented. */ for (int i = 0; i < term->esc_nargs; i++) switch (def(term->esc_args[i], 0)) { case 0: /* restore defaults */ term->curr_attr = term->default_attr; term->curr_truecolour = term->basic_erase_char.truecolour; break; case 1: /* enable bold */ compatibility(VT100AVO); term->curr_attr |= ATTR_BOLD; break; case 2: /* enable dim */ compatibility(OTHER); term->curr_attr |= ATTR_DIM; break; case 21: /* (enable double underline) */ compatibility(OTHER); case 4: /* enable underline */ compatibility(VT100AVO); term->curr_attr |= ATTR_UNDER; break; case 5: /* enable blink */ compatibility(VT100AVO); term->curr_attr |= ATTR_BLINK; break; case 6: /* SCO light bkgrd */ compatibility(SCOANSI); term->blink_is_real = false; term->curr_attr |= ATTR_BLINK; term_schedule_tblink(term); break; case 7: /* enable reverse video */ term->curr_attr |= ATTR_REVERSE; break; case 9: /* enable strikethrough */ term->curr_attr |= ATTR_STRIKE; 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 and dim */ compatibility2(OTHER, VT220); term->curr_attr &= ~(ATTR_BOLD | ATTR_DIM); break; case 24: /* disable underline */ compatibility2(OTHER, VT220); term->curr_attr &= ~ATTR_UNDER; break; case 25: /* disable blink */ compatibility2(OTHER, VT220); term->curr_attr &= ~ATTR_BLINK; break; case 27: /* disable reverse video */ compatibility2(OTHER, VT220); term->curr_attr &= ~ATTR_REVERSE; break; case 29: /* disable strikethrough */ term->curr_attr &= ~ATTR_STRIKE; break; case 30: case 31: case 32: case 33: case 34: case 35: case 36: case 37: /* foreground */ term->curr_truecolour.fg.enabled = false; term->curr_attr &= ~ATTR_FGMASK; term->curr_attr |= (term->esc_args[i] - 30)<curr_truecolour.fg.enabled = false; term->curr_attr &= ~ATTR_FGMASK; term->curr_attr |= ((term->esc_args[i] - 90 + 8) << ATTR_FGSHIFT); break; case 39: /* default-foreground */ term->curr_truecolour.fg.enabled = false; term->curr_attr &= ~ATTR_FGMASK; term->curr_attr |= ATTR_DEFFG; break; case 40: case 41: case 42: case 43: case 44: case 45: case 46: case 47: /* background */ term->curr_truecolour.bg.enabled = false; term->curr_attr &= ~ATTR_BGMASK; term->curr_attr |= (term->esc_args[i] - 40)<curr_truecolour.bg.enabled = false; term->curr_attr &= ~ATTR_BGMASK; term->curr_attr |= ((term->esc_args[i] - 100 + 8) << ATTR_BGSHIFT); break; case 49: /* default-background */ term->curr_truecolour.bg.enabled = false; term->curr_attr &= ~ATTR_BGMASK; term->curr_attr |= ATTR_DEFBG; break; /* * 256-colour and true-colour * sequences. A 256-colour * foreground is selected by a * sequence of 3 arguments in the * form 38;5;n, where n is in the * range 0-255. A true-colour RGB * triple is selected by 5 args of * the form 38;2;r;g;b. Replacing * the initial 38 with 48 in both * cases selects the same colour * as the background. */ case 38: if (i+2 < term->esc_nargs && term->esc_args[i+1] == 5) { term->curr_attr &= ~ATTR_FGMASK; term->curr_attr |= ((term->esc_args[i+2] & 0xFF) << ATTR_FGSHIFT); term->curr_truecolour.fg = optionalrgb_none; i += 2; } if (i + 4 < term->esc_nargs && term->esc_args[i + 1] == 2) { parse_optionalrgb( &term->curr_truecolour.fg, term->esc_args + (i+2)); i += 4; } break; case 48: if (i+2 < term->esc_nargs && term->esc_args[i+1] == 5) { term->curr_attr &= ~ATTR_BGMASK; term->curr_attr |= ((term->esc_args[i+2] & 0xFF) << ATTR_BGSHIFT); term->curr_truecolour.bg = optionalrgb_none; i += 2; } if (i + 4 < term->esc_nargs && term->esc_args[i+1] == 2) { parse_optionalrgb( &term->curr_truecolour.bg, term->esc_args + (i+2)); i += 4; } break; } set_erase_char(term); break; case 's': /* save cursor */ save_cursor(term, true); break; case 'u': /* restore cursor */ save_cursor(term, false); seen_disp_event(term); break; case 't': /* DECSLPP: set page size - ie window height */ /* * VT340/VT420 sequence DECSLPP, DEC only allows values * 24/25/36/48/72/144 other emulators (eg dtterm) use * 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) { term->win_resize_pending = true; term->win_resize_pending_w = term->cols; term->win_resize_pending_h = def(term->esc_args[0], 24); term_schedule_update(term); } 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 len; char buf[80]; const char *p; case 1: term->win_minimise_pending = true; term->win_minimise_enable = false; term_schedule_update(term); break; case 2: term->win_minimise_pending = true; term->win_minimise_enable = true; term_schedule_update(term); break; case 3: if (term->esc_nargs >= 3) { if (!term->no_remote_resize) { term->win_move_pending = true; term->win_move_pending_x = def(term->esc_args[1], 0); term->win_move_pending_y = def(term->esc_args[2], 0); term_schedule_update(term); } } break; case 4: /* We should resize the window to a given * size in pixels here, but currently our * resizing code isn't healthy enough to * manage it. */ break; case 5: /* move to top */ term->win_zorder_pending = true; term->win_zorder_top = true; term_schedule_update(term); break; case 6: /* move to bottom */ term->win_zorder_pending = true; term->win_zorder_top = false; term_schedule_update(term); break; case 7: term->win_refresh_pending = true; term_schedule_update(term); break; case 8: if (term->esc_nargs >= 3 && !term->no_remote_resize) { term->win_resize_pending = true; term->win_resize_pending_w = def(term->esc_args[2], term->conf_width); term->win_resize_pending_h = def(term->esc_args[1], term->conf_height); term_schedule_update(term); } break; case 9: if (term->esc_nargs >= 2) { term->win_maximise_pending = true; term->win_maximise_enable = term->esc_args[1]; term_schedule_update(term); } break; case 11: if (term->ldisc) ldisc_send(term->ldisc, term->minimised ? "\033[2t" : "\033[1t", 4, false); break; case 13: if (term->ldisc) { len = sprintf(buf, "\033[3;%u;%ut", term->winpos_x, term->winpos_y); ldisc_send(term->ldisc, buf, len, false); } break; case 14: if (term->ldisc) { len = sprintf(buf, "\033[4;%u;%ut", term->winpixsize_y, term->winpixsize_x); ldisc_send(term->ldisc, buf, len, false); } break; case 18: if (term->ldisc) { len = sprintf(buf, "\033[8;%d;%dt", term->rows, term->cols); ldisc_send(term->ldisc, buf, len, false); } break; case 19: /* * Hmmm. Strictly speaking we * should return `the size of the * screen in characters', but * that's not easy: (a) window * furniture being what it is it's * hard to compute, and (b) in * resize-font mode maximising the * window wouldn't change the * number of characters. *shrug*. I * think we'll ignore it for the * 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 = term->icon_title; else p = EMPTY_WINDOW_TITLE; len = strlen(p); ldisc_send(term->ldisc, "\033]L", 3, false); ldisc_send(term->ldisc, p, len, false); ldisc_send(term->ldisc, "\033\\", 2, false); } break; case 21: if (term->ldisc && term->remote_qtitle_action != TITLE_NONE) { if(term->remote_qtitle_action == TITLE_REAL) p = term->window_title; else p = EMPTY_WINDOW_TITLE; len = strlen(p); ldisc_send(term->ldisc, "\033]l", 3, false); ldisc_send(term->ldisc, p, len, false); ldisc_send(term->ldisc, "\033\\", 2, false); } break; } } break; case 'S': /* SU: Scroll up */ CLAMP(term->esc_args[0], term->rows); compatibility(SCOANSI); scroll(term, term->marg_t, term->marg_b, def(term->esc_args[0], 1), true); term->wrapnext = false; seen_disp_event(term); break; case 'T': /* SD: Scroll down */ CLAMP(term->esc_args[0], term->rows); compatibility(SCOANSI); scroll(term, term->marg_t, term->marg_b, -def(term->esc_args[0], 1), true); term->wrapnext = false; seen_disp_event(term); break; case ANSI('|', '*'): /* DECSNLS */ /* * 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) { term->win_resize_pending = true; term->win_resize_pending_w = term->cols; term->win_resize_pending_h = def(term->esc_args[0], term->conf_height); term_schedule_update(term); } 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) { term->win_resize_pending = true; term->win_resize_pending_w = def(term->esc_args[0], term->conf_width); term->win_resize_pending_h = term->rows; term_schedule_update(term); } 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); CLAMP(term->esc_args[0], term->cols); int n = def(term->esc_args[0], 1); pos cursplus; int p = term->curs.x; termline *cline = scrlineptr(term->curs.y); check_trust_status(term, cline); if (n > term->cols - term->curs.x) n = term->cols - term->curs.x; cursplus = term->curs; cursplus.x += n; check_boundary(term, term->curs.x, term->curs.y); check_boundary(term, term->curs.x+n, term->curs.y); check_selection(term, term->curs, cursplus); while (n--) copy_termchar(cline, p++, &term->erase_char); seen_disp_event(term); break; } case 'x': /* DECREQTPARM: report terminal characteristics */ compatibility(VT100); if (term->ldisc) { char buf[32]; int i = def(term->esc_args[0], 0); if (i == 0 || i == 1) { strcpy(buf, "\033[2;1;1;112;112;1;0x"); buf[2] += i; ldisc_send(term->ldisc, buf, 20, false); } } break; case 'Z': { /* CBT */ compatibility(OTHER); CLAMP(term->esc_args[0], term->cols); int i = def(term->esc_args[0], 1); pos old_curs = term->curs; for(;i>0 && term->curs.x>0; i--) { do { term->curs.x--; } while (term->curs.x >0 && !term->tabs[term->curs.x]); } check_selection(term, old_curs, term->curs); break; } case ANSI('c', '='): /* Hide or Show Cursor */ compatibility(SCOANSI); switch(term->esc_args[0]) { case 0: /* hide cursor */ term->cursor_on = false; break; case 1: /* restore cursor */ term->big_cursor = false; term->cursor_on = true; break; case 2: /* block cursor */ term->big_cursor = true; term->cursor_on = true; break; } break; case ANSI('C', '='): /* * set cursor start on scanline esc_args[0] and * end on scanline esc_args[1].If you set * the bottom scan line to a value less than * the top scan line, the cursor will disappear. */ compatibility(SCOANSI); if (term->esc_nargs >= 2) { if (term->esc_args[0] > term->esc_args[1]) term->cursor_on = false; else term->cursor_on = true; } break; case ANSI('D', '='): compatibility(SCOANSI); term->blink_is_real = false; term_schedule_tblink(term); if (term->esc_args[0]>=1) term->curr_attr |= ATTR_BLINK; else term->curr_attr &= ~ATTR_BLINK; break; case ANSI('E', '='): compatibility(SCOANSI); term->blink_is_real = (term->esc_args[0] >= 1); term_schedule_tblink(term); break; case ANSI('F', '='): /* set normal foreground */ compatibility(SCOANSI); if (term->esc_args[0] < 16) { long colour = (sco2ansicolour[term->esc_args[0] & 0x7] | (term->esc_args[0] & 0x8)) << ATTR_FGSHIFT; term->curr_attr &= ~ATTR_FGMASK; term->curr_attr |= colour; term->curr_truecolour.fg = optionalrgb_none; term->default_attr &= ~ATTR_FGMASK; term->default_attr |= colour; set_erase_char(term); } break; case ANSI('G', '='): /* set normal background */ compatibility(SCOANSI); if (term->esc_args[0] < 16) { long colour = (sco2ansicolour[term->esc_args[0] & 0x7] | (term->esc_args[0] & 0x8)) << ATTR_BGSHIFT; term->curr_attr &= ~ATTR_BGMASK; term->curr_attr |= colour; term->curr_truecolour.bg = optionalrgb_none; term->default_attr &= ~ATTR_BGMASK; term->default_attr |= colour; set_erase_char(term); } break; case ANSI('L', '='): compatibility(SCOANSI); term->use_bce = (term->esc_args[0] <= 0); set_erase_char(term); break; case ANSI('p', '"'): /* DECSCL: set compat level */ /* * Allow the host to make this emulator a * 'perfect' VT102. This first appeared in * the VT220, but we do need to get back to * PuTTY mode so I won't check it. * * The arg in 40..42,50 are a PuTTY extension. * The 2nd arg, 8bit vs 7bit is not checked. * * Setting VT102 mode should also change * the Fkeys to generate PF* codes as a * real VT102 has no Fkeys. The VT220 does * this, F11..F13 become ESC,BS,LF other * Fkeys send nothing. * * Note ESC c will NOT change this! */ switch (term->esc_args[0]) { case 61: term->compatibility_level &= ~TM_VTXXX; term->compatibility_level |= TM_VT102; break; case 62: term->compatibility_level &= ~TM_VTXXX; term->compatibility_level |= TM_VT220; break; default: if (term->esc_args[0] > 60 && term->esc_args[0] < 70) term->compatibility_level |= TM_VTXXX; break; case 40: term->compatibility_level &= TM_VTXXX; break; case 41: term->compatibility_level = TM_PUTTY; break; case 42: term->compatibility_level = TM_SCOANSI; break; case ARG_DEFAULT: term->compatibility_level = TM_PUTTY; break; case 50: break; } /* Change the response to CSI c */ if (term->esc_args[0] == 50) { int i; char lbuf[64]; strcpy(term->id_string, "\033[?"); for (i = 1; i < term->esc_nargs; i++) { if (i != 1) strcat(term->id_string, ";"); sprintf(lbuf, "%u", term->esc_args[i]); strcat(term->id_string, lbuf); } 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) { term->win_resize_pending = true; term->win_resize_pending_w = term->reset_132 ? 132 : 80; term->win_resize_pending_h = 24; term_schedule_update(term); } } #endif break; } break; case SEEN_OSC: term->osc_w = false; switch (c) { case 'P': /* Linux palette sequence */ term->termstate = SEEN_OSC_P; term->osc_strlen = 0; break; case 'R': /* Linux palette reset */ palette_reset(term, false); term_invalidate(term); term->termstate = TOPLEVEL; break; case 'W': /* word-set */ term->termstate = SEEN_OSC_W; term->osc_w = true; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (term->esc_args[term->esc_nargs-1] <= UINT_MAX / 10 && term->esc_args[term->esc_nargs-1] * 10 <= UINT_MAX - c - '0') term->esc_args[term->esc_nargs-1] = 10 * term->esc_args[term->esc_nargs-1] + c - '0'; else term->esc_args[term->esc_nargs-1] = UINT_MAX; break; default: /* * _Most_ other characters here terminate the * immediate parsing of the OSC sequence and go * into OSC_STRING state, but we deal with a * couple of exceptions first. */ if (c == 'L' && term->esc_args[0] == 2) { /* * Grotty hack to support xterm and DECterm title * sequences concurrently. */ term->esc_args[0] = 1; } else if (c == ';' && term->esc_nargs == 1 && term->esc_args[0] == 4) { /* * xterm's OSC 4 sequence to query the current * RGB value of a colour takes a second * numeric argument which is easiest to parse * using the existing system rather than in * do_osc. */ term->esc_args[term->esc_nargs++] = 0; } else { term->termstate = OSC_STRING; term->osc_strlen = 0; } } break; case OSC_STRING: /* * This OSC stuff is EVIL. It takes just one character to get into * sysline mode and it's not initially obvious how to get out. * So I've added CR and LF as string aborts. * This shouldn't effect compatibility as I believe embedded * control characters are supposed to be interpreted (maybe?) * and they don't display anything useful anyway. * * -- RDB */ if (c == '\012' || c == '\015') { term->termstate = TOPLEVEL; } else if (c == 0234 || c == '\007') { /* * These characters terminate the string; ST and BEL * terminate the sequence and trigger instant * processing of it, whereas ESC goes back to SEEN_ESC * mode unless it is followed by \, in which case it is * synonymous with ST in the first place. */ do_osc(term); term->termstate = TOPLEVEL; } else if (c == '\033') term->termstate = OSC_MAYBE_ST; else if (term->osc_strlen < OSC_STR_MAX) term->osc_string[term->osc_strlen++] = (char)c; break; case SEEN_OSC_P: { int max = (term->osc_strlen == 0 ? 21 : 15); int val; if ((int)c >= '0' && (int)c <= '9') val = c - '0'; else if ((int)c >= 'A' && (int)c <= 'A' + max - 10) val = c - 'A' + 10; else if ((int)c >= 'a' && (int)c <= 'a' + max - 10) val = c - 'a' + 10; else { term->termstate = TOPLEVEL; break; } term->osc_string[term->osc_strlen++] = val; if (term->osc_strlen >= 7) { unsigned oscp_index = term->osc_string[0]; assert(oscp_index < OSCP_NCOLOURS); unsigned osc4_index = colour_indices_oscp_to_osc4[oscp_index]; rgb *value = &term->subpalettes[SUBPAL_SESSION].values[ osc4_index]; value->r = term->osc_string[1] * 16 + term->osc_string[2]; value->g = term->osc_string[3] * 16 + term->osc_string[4]; value->b = term->osc_string[5] * 16 + term->osc_string[6]; term->subpalettes[SUBPAL_SESSION].present[ osc4_index] = true; palette_rebuild(term); term->termstate = TOPLEVEL; } break; } case SEEN_OSC_W: switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (term->esc_args[0] <= UINT_MAX / 10 && term->esc_args[0] * 10 <= UINT_MAX - c - '0') term->esc_args[0] = 10 * term->esc_args[0] + c - '0'; else term->esc_args[0] = UINT_MAX; break; default: term->termstate = OSC_STRING; term->osc_strlen = 0; } break; case VT52_ESC: term->termstate = TOPLEVEL; seen_disp_event(term); switch (c) { case 'A': move(term, term->curs.x, term->curs.y - 1, 1); break; case 'B': move(term, term->curs.x, term->curs.y + 1, 1); break; case 'C': move(term, term->curs.x + 1, term->curs.y, 1); break; case 'D': move(term, term->curs.x - 1, term->curs.y, 1); break; /* * From the VT100 Manual * NOTE: The special graphics characters in the VT100 * are different from those in the VT52 * * From VT102 manual: * 137 _ Blank - Same * 140 ` Reserved - Humm. * 141 a Solid rectangle - Similar * 142 b 1/ - Top half of fraction for the * 143 c 3/ - subscript numbers below. * 144 d 5/ * 145 e 7/ * 146 f Degrees - Same * 147 g Plus or minus - Same * 150 h Right arrow * 151 i Ellipsis (dots) * 152 j Divide by * 153 k Down arrow * 154 l Bar at scan 0 * 155 m Bar at scan 1 * 156 n Bar at scan 2 * 157 o Bar at scan 3 - Similar * 160 p Bar at scan 4 - Similar * 161 q Bar at scan 5 - Similar * 162 r Bar at scan 6 - Same * 163 s Bar at scan 7 - Similar * 164 t Subscript 0 * 165 u Subscript 1 * 166 v Subscript 2 * 167 w Subscript 3 * 170 x Subscript 4 * 171 y Subscript 5 * 172 z Subscript 6 * 173 { Subscript 7 * 174 | Subscript 8 * 175 } Subscript 9 * 176 ~ Paragraph * */ case 'F': term->cset_attr[term->cset = 0] = CSET_LINEDRW; break; case 'G': term->cset_attr[term->cset = 0] = CSET_ASCII; break; case 'H': move(term, 0, 0, 0); break; case 'I': if (term->curs.y == 0) scroll(term, 0, term->rows - 1, -1, true); else if (term->curs.y > 0) term->curs.y--; term->wrapnext = false; break; case 'J': erase_lots(term, false, false, true); if (term->scroll_on_disp) term->disptop = 0; break; case 'K': erase_lots(term, true, false, true); break; #if 0 case 'V': /* XXX Print cursor line */ break; case 'W': /* XXX Start controller mode */ break; case 'X': /* XXX Stop controller mode */ break; #endif case 'Y': term->termstate = VT52_Y1; break; case 'Z': if (term->ldisc) ldisc_send(term->ldisc, "\033/Z", 3, false); break; case '=': term->app_keypad_keys = true; break; case '>': term->app_keypad_keys = false; 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 '_': /* XXX Exit auto print mode */ break; case ']': /* XXX Print screen */ break; #endif #ifdef VT52_PLUS case 'E': /* compatibility(ATARI) */ move(term, 0, 0, 0); erase_lots(term, false, false, true); if (term->scroll_on_disp) term->disptop = 0; break; case 'L': /* compatibility(ATARI) */ if (term->curs.y <= term->marg_b) scroll(term, term->curs.y, term->marg_b, -1, false); break; case 'M': /* compatibility(ATARI) */ if (term->curs.y <= term->marg_b) scroll(term, term->curs.y, term->marg_b, 1, true); break; case 'b': /* compatibility(ATARI) */ term->termstate = VT52_FG; break; case 'c': /* compatibility(ATARI) */ term->termstate = VT52_BG; break; case 'd': /* compatibility(ATARI) */ erase_lots(term, false, true, false); if (term->scroll_on_disp) term->disptop = 0; break; case 'e': /* compatibility(ATARI) */ term->cursor_on = true; break; case 'f': /* compatibility(ATARI) */ term->cursor_on = false; break; /* case 'j': Save cursor position - broken on ST */ /* case 'k': Restore cursor position */ case 'l': /* compatibility(ATARI) */ erase_lots(term, true, true, true); term->curs.x = 0; term->wrapnext = false; break; case 'o': /* compatibility(ATARI) */ erase_lots(term, true, true, false); break; case 'p': /* compatibility(ATARI) */ term->curr_attr |= ATTR_REVERSE; break; case 'q': /* compatibility(ATARI) */ term->curr_attr &= ~ATTR_REVERSE; break; case 'v': /* wrap Autowrap on - Wyse style */ /* compatibility(ATARI) */ term->wrap = true; break; case 'w': /* Autowrap off */ /* compatibility(ATARI) */ term->wrap = false; break; case 'R': /* compatibility(OTHER) */ term->vt52_bold = false; term->curr_attr = ATTR_DEFAULT; term->curr_truecolour.fg = optionalrgb_none; term->curr_truecolour.bg = optionalrgb_none; set_erase_char(term); break; case 'S': /* compatibility(VI50) */ term->curr_attr |= ATTR_UNDER; break; case 'W': /* compatibility(VI50) */ term->curr_attr &= ~ATTR_UNDER; break; case 'U': /* compatibility(VI50) */ term->vt52_bold = true; term->curr_attr |= ATTR_BOLD; break; case 'T': /* compatibility(VI50) */ term->vt52_bold = false; term->curr_attr &= ~ATTR_BOLD; break; #endif } break; case VT52_Y1: term->termstate = VT52_Y2; move(term, term->curs.x, c - ' ', 0); break; case VT52_Y2: term->termstate = TOPLEVEL; move(term, c - ' ', term->curs.y, 0); break; #ifdef VT52_PLUS case VT52_FG: term->termstate = TOPLEVEL; term->curr_attr &= ~ATTR_FGMASK; term->curr_attr &= ~ATTR_BOLD; term->curr_attr |= (c & 0xF) << ATTR_FGSHIFT; set_erase_char(term); break; case VT52_BG: term->termstate = TOPLEVEL; term->curr_attr &= ~ATTR_BGMASK; term->curr_attr &= ~ATTR_BLINK; term->curr_attr |= (c & 0xF) << ATTR_BGSHIFT; set_erase_char(term); break; #endif default: break; /* placate gcc warning about enum use */ } if (term->selstate != NO_SELECTION) { pos cursplus = term->curs; incpos(cursplus); check_selection(term, term->curs, cursplus); } } term_print_flush(term); if (term->logflush && term->logctx) logflush(term->logctx); } /* * Small subroutine to parse three consecutive escape-sequence * arguments representing a true-colour RGB triple into an * optionalrgb. */ static void parse_optionalrgb(optionalrgb *out, unsigned *values) { out->enabled = true; out->r = values[0] < 256 ? values[0] : 0; out->g = values[1] < 256 ? values[1] : 0; out->b = values[2] < 256 ? values[2] : 0; } /* * 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. */ static bool term_bidi_cache_hit(Terminal *term, int line, termchar *lbefore, int width, bool trusted) { int i; if (!term->pre_bidi_cache) return false; /* cache doesn't even exist yet! */ if (line >= term->bidi_cache_size) return false; /* cache doesn't have this many lines */ if (!term->pre_bidi_cache[line].chars) return false; /* cache doesn't contain _this_ line */ if (term->pre_bidi_cache[line].width != width) return false; /* line is wrong width */ if (term->pre_bidi_cache[line].trusted != trusted) return false; /* line has wrong trust state */ for (i = 0; i < width; i++) if (!termchars_equal(term->pre_bidi_cache[line].chars+i, lbefore+i)) return false; /* line doesn't match cache */ return true; /* it didn't match. */ } static void term_bidi_cache_store(Terminal *term, int line, termchar *lbefore, termchar *lafter, bidi_char *wcTo, int width, int size, bool trusted) { size_t i, j; if (!term->pre_bidi_cache || term->bidi_cache_size <= line) { j = term->bidi_cache_size; sgrowarray(term->pre_bidi_cache, term->bidi_cache_size, line); term->post_bidi_cache = sresize(term->post_bidi_cache, term->bidi_cache_size, struct bidi_cache_entry); while (j < term->bidi_cache_size) { term->pre_bidi_cache[j].chars = term->post_bidi_cache[j].chars = NULL; term->pre_bidi_cache[j].width = term->post_bidi_cache[j].width = -1; term->pre_bidi_cache[j].trusted = false; term->post_bidi_cache[j].trusted = false; term->pre_bidi_cache[j].forward = term->post_bidi_cache[j].forward = NULL; term->pre_bidi_cache[j].backward = term->post_bidi_cache[j].backward = NULL; j++; } } sfree(term->pre_bidi_cache[line].chars); sfree(term->post_bidi_cache[line].chars); sfree(term->post_bidi_cache[line].forward); sfree(term->post_bidi_cache[line].backward); term->pre_bidi_cache[line].width = width; term->pre_bidi_cache[line].trusted = trusted; term->pre_bidi_cache[line].chars = snewn(size, termchar); term->post_bidi_cache[line].width = width; term->post_bidi_cache[line].trusted = trusted; term->post_bidi_cache[line].chars = snewn(size, termchar); term->post_bidi_cache[line].forward = snewn(width, int); term->post_bidi_cache[line].backward = snewn(width, int); memcpy(term->pre_bidi_cache[line].chars, lbefore, size * TSIZE); memcpy(term->post_bidi_cache[line].chars, lafter, size * TSIZE); memset(term->post_bidi_cache[line].forward, 0, width * sizeof(int)); memset(term->post_bidi_cache[line].backward, 0, width * sizeof(int)); for (i = j = 0; j < width; j += wcTo[i].nchars, i++) { int p = wcTo[i].index; if (p != BIDI_CHAR_INDEX_NONE) { assert(0 <= p && p < width); for (int x = 0; x < wcTo[i].nchars; x++) { term->post_bidi_cache[line].backward[j+x] = p+x; term->post_bidi_cache[line].forward[p+x] = j+x; } } } } /* * Prepare the bidi information for a screen line. Returns the * transformed list of termchars, or NULL if no transformation at * all took place (because bidi is disabled). If return was * non-NULL, auxiliary information such as the forward and reverse * mappings of permutation position are available in * term->post_bidi_cache[scr_y].*. */ static termchar *term_bidi_line(Terminal *term, struct termline *ldata, int scr_y) { termchar *lchars; int it; /* Do Arabic shaping and bidi. */ if (!term->no_bidi || !term->no_arabicshaping || (ldata->trusted && term->cols > TRUST_SIGIL_WIDTH)) { if (!term_bidi_cache_hit(term, scr_y, ldata->chars, term->cols, ldata->trusted)) { 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; itcols ; 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; term->wcFrom[it].nchars = 1; } if (ldata->trusted && term->cols > TRUST_SIGIL_WIDTH) { memmove( term->wcFrom + TRUST_SIGIL_WIDTH, term->wcFrom, (term->cols - TRUST_SIGIL_WIDTH) * sizeof(*term->wcFrom)); for (it = 0; it < TRUST_SIGIL_WIDTH; it++) { term->wcFrom[it].origwc = term->wcFrom[it].wc = (it == 0 ? TRUST_SIGIL_CHAR : it == 1 ? UCSWIDE : ' '); term->wcFrom[it].index = BIDI_CHAR_INDEX_NONE; term->wcFrom[it].nchars = 1; } } int nbc = 0; for (it = 0; it < term->cols; it++) { term->wcFrom[nbc] = term->wcFrom[it]; if (it+1 < term->cols && term->wcFrom[it+1].wc == UCSWIDE) { term->wcFrom[nbc].nchars++; it++; } nbc++; } if(!term->no_bidi) do_bidi(term->wcFrom, nbc); if(!term->no_arabicshaping) { do_shape(term->wcFrom, term->wcTo, nbc); } else { /* If we're not calling do_shape, we must copy the * data into wcTo anyway, unchanged */ memcpy(term->wcTo, term->wcFrom, nbc * sizeof(*term->wcTo)); } if (term->ltemp_size < ldata->size) { term->ltemp_size = ldata->size; term->ltemp = sresize(term->ltemp, term->ltemp_size, termchar); } memcpy(term->ltemp, ldata->chars, ldata->size * TSIZE); int opos = 0; for (it=0; itwcTo[it].index; for (int j = 0; j < term->wcTo[it].nchars; j++) { if (ipos != BIDI_CHAR_INDEX_NONE) { term->ltemp[opos] = ldata->chars[ipos]; if (term->ltemp[opos].cc_next) term->ltemp[opos].cc_next -= opos - ipos; if (j > 0) term->ltemp[opos].chr = UCSWIDE; else if (term->wcTo[it].origwc != term->wcTo[it].wc) term->ltemp[opos].chr = term->wcTo[it].wc; } else { term->ltemp[opos] = term->basic_erase_char; term->ltemp[opos].chr = j > 0 ? UCSWIDE : term->wcTo[it].origwc; } opos++; } } assert(opos == term->cols); term_bidi_cache_store(term, scr_y, ldata->chars, term->ltemp, term->wcTo, term->cols, ldata->size, ldata->trusted); lchars = term->ltemp; } else { lchars = term->post_bidi_cache[scr_y].chars; } } else { lchars = NULL; } return lchars; } static void do_paint_draw(Terminal *term, termline *ldata, int x, int y, wchar_t *ch, int ccount, unsigned long attr, truecolour tc) { if (ch[0] == TRUST_SIGIL_CHAR) { assert(ldata->trusted); assert(ccount == 1); assert(attr & ATTR_WIDE); wchar_t tch[2]; tch[0] = tch[1] = L' '; win_draw_text(term->win, x, y, tch, 2, term->basic_erase_char.attr, ldata->lattr, term->basic_erase_char.truecolour); win_draw_trust_sigil(term->win, x, y); } else { win_draw_text(term->win, x, y, ch, ccount, attr, ldata->lattr, tc); if (attr & (TATTR_ACTCURS | TATTR_PASCURS)) win_draw_cursor(term->win, x, y, ch, ccount, attr, ldata->lattr, tc); } } /* * Given a context, update the window. */ static void do_paint(Terminal *term) { int i, j, our_curs_y, our_curs_x; int rv, cursor; pos scrpos; wchar_t *ch; size_t chlen; termchar *newline; chlen = 1024; ch = snewn(chlen, wchar_t); 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; } else cursor = 0; our_curs_y = term->curs.y - term->disptop; { /* * Adjust the cursor position: * - for bidi * - in the case where it's resting on the right-hand half * of a CJK wide character. xterm's behaviour here, * which seems adequate to me, is to display the cursor * covering the _whole_ character, exactly as if it were * one space to the left. */ termline *ldata = lineptr(term->curs.y); termchar *lchars; our_curs_x = term->curs.x; if ( (lchars = term_bidi_line(term, ldata, our_curs_y)) != NULL) { our_curs_x = term->post_bidi_cache[our_curs_y].forward[our_curs_x]; } else lchars = ldata->chars; if (our_curs_x > 0 && lchars[our_curs_x].chr == UCSWIDE) our_curs_x--; unlineptr(ldata); } /* * If the cursor is not where it was last time we painted, and * its previous position is visible on screen, invalidate its * previous position. */ if (term->dispcursy >= 0 && (term->curstype != cursor || term->dispcursy != our_curs_y || term->dispcursx != our_curs_x)) { termchar *dispcurs = term->disptext[term->dispcursy]->chars + term->dispcursx; if (term->dispcursx > 0 && dispcurs->chr == UCSWIDE) dispcurs[-1].attr |= ATTR_INVALID; if (term->dispcursx < term->cols-1 && dispcurs[1].chr == UCSWIDE) dispcurs[1].attr |= ATTR_INVALID; dispcurs->attr |= ATTR_INVALID; term->curstype = 0; } term->dispcursx = term->dispcursy = -1; /* The normal screen data */ for (i = 0; i < term->rows; i++) { termline *ldata; termchar *lchars; bool dirty_line, dirty_run, selected; unsigned long attr = 0, cset = 0; int start = 0; int ccount = 0; bool last_run_dirty = false; int laststart; bool dirtyrect; int *backward; truecolour tc; scrpos.y = i + term->disptop; ldata = lineptr(scrpos.y); /* Do Arabic shaping and bidi. */ lchars = term_bidi_line(term, ldata, i); if (lchars) { backward = term->post_bidi_cache[i].backward; } else { lchars = ldata->chars; backward = NULL; } /* * First loop: work along the line deciding what we want * each character cell to look like. */ for (j = 0; j < term->cols; j++) { 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; } if (term->true_colour) { tc = d->truecolour; } else { tc.fg = tc.bg = optionalrgb_none; } switch (tchar & CSET_MASK) { case CSET_ASCII: tchar = term->ucsdata->unitab_line[tchar & 0xFF]; break; case CSET_LINEDRW: tchar = term->ucsdata->unitab_xterm[tchar & 0xFF]; break; case CSET_SCOACS: tchar = term->ucsdata->unitab_scoacs[tchar&0xFF]; break; } if (j < term->cols-1 && d[1].chr == UCSWIDE) tattr |= ATTR_WIDE; /* Video reversing things */ if (term->selstate == DRAGGING || term->selstate == SELECTED) { if (term->seltype == LEXICOGRAPHIC) selected = (posle(term->selstart, scrpos) && poslt(scrpos, term->selend)); else selected = (posPle(term->selstart, scrpos) && posPle_left(scrpos, term->selend)); } else selected = false; tattr = (tattr ^ rv ^ (selected ? ATTR_REVERSE : 0)); /* 'Real' blinking ? */ if (term->blink_is_real && (tattr & ATTR_BLINK)) { if (term->has_focus && term->tblinker) { tchar = term->ucsdata->unitab_line[(unsigned char)' ']; } tattr &= ~ATTR_BLINK; } /* * Check the font we'll _probably_ be using to see if * the character is wide when we don't want it to be. */ if (tchar != term->disptext[i]->chars[j].chr || tattr != (term->disptext[i]->chars[j].attr &~ (ATTR_NARROW | DATTR_MASK))) { if ((tattr & ATTR_WIDE) == 0 && win_char_width(term->win, tchar) == 2) tattr |= ATTR_NARROW; } else if (term->disptext[i]->chars[j].attr & ATTR_NARROW) tattr |= ATTR_NARROW; if (i == our_curs_y && j == our_curs_x) { tattr |= cursor; term->curstype = cursor; term->dispcursx = j; term->dispcursy = i; } /* FULL-TERMCHAR */ newline[j].attr = tattr; newline[j].chr = tchar; newline[j].truecolour = tc; /* Combining characters are still read from lchars */ newline[j].cc_next = 0; } /* * Now loop over the line again, noting where things have * changed. * * During this loop, we keep track of where we last saw * DATTR_STARTRUN. Any mismatch automatically invalidates * _all_ of the containing run that was last printed: that * is, any rectangle that was drawn in one go in the * previous update should be either left completely alone * or overwritten in its entirety. This, along with the * expectation that front ends clip all text runs to their * bounding rectangle, should solve any possible problems * with fonts that overflow their character cells. */ laststart = 0; dirtyrect = false; for (j = 0; j < term->cols; j++) { if (term->disptext[i]->chars[j].attr & DATTR_STARTRUN) { laststart = j; dirtyrect = false; } if (term->disptext[i]->chars[j].chr != newline[j].chr || (term->disptext[i]->chars[j].attr &~ DATTR_MASK) != newline[j].attr) { int k; if (!dirtyrect) { for (k = laststart; k < j; k++) term->disptext[i]->chars[k].attr |= ATTR_INVALID; dirtyrect = true; } } if (dirtyrect) term->disptext[i]->chars[j].attr |= ATTR_INVALID; } /* * Finally, loop once more and actually do the drawing. */ dirty_run = dirty_line = (ldata->lattr != term->disptext[i]->lattr); term->disptext[i]->lattr = ldata->lattr; tc = term->erase_char.truecolour; for (j = 0; j < term->cols; j++) { unsigned long tattr, tchar; bool break_run, do_copy; termchar *d = lchars + j; tattr = newline[j].attr; 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; if (!truecolour_equal(newline[j].truecolour, tc)) break_run = true; #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; /* * Break on both sides of any combined-character cell. */ if (d->cc_next != 0 || (j > 0 && d[-1].cc_next != 0)) break_run = true; /* * Break on both sides of a trust sigil. */ if (d->chr == TRUST_SIGIL_CHAR || (j >= 2 && d[-1].chr == UCSWIDE && d[-2].chr == TRUST_SIGIL_CHAR)) break_run = true; if (!term->ucsdata->dbcs_screenfont && !dirty_line) { if (term->disptext[i]->chars[j].chr == tchar && (term->disptext[i]->chars[j].attr &~ DATTR_MASK) == tattr) break_run = true; else if (!dirty_run && ccount == 1) break_run = true; } if (break_run) { if ((dirty_run || last_run_dirty) && ccount > 0) do_paint_draw(term, ldata, start, i, ch, ccount, attr, tc); start = j; ccount = 0; attr = tattr; tc = newline[j].truecolour; cset = CSET_OF(tchar); if (term->ucsdata->dbcs_screenfont) last_run_dirty = dirty_run; dirty_run = dirty_line; } do_copy = false; if (!termchars_equal_override(&term->disptext[i]->chars[j], d, tchar, tattr)) { do_copy = true; dirty_run = true; } sgrowarrayn(ch, chlen, ccount, 2); #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; dd += dd->cc_next; schar = dd->chr; switch (schar & CSET_MASK) { case CSET_ASCII: schar = term->ucsdata->unitab_line[schar & 0xFF]; break; case CSET_LINEDRW: schar = term->ucsdata->unitab_xterm[schar & 0xFF]; break; case CSET_SCOACS: schar = term->ucsdata->unitab_scoacs[schar&0xFF]; break; } sgrowarrayn(ch, chlen, ccount, 2); #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) { copy_termchar(term->disptext[i], j, d); term->disptext[i]->chars[j].chr = tchar; term->disptext[i]->chars[j].attr = tattr; term->disptext[i]->chars[j].truecolour = tc; if (start == j) term->disptext[i]->chars[j].attr |= DATTR_STARTRUN; } /* If it's a wide char step along to the next one. */ if (tattr & ATTR_WIDE) { if (++j < term->cols) { d++; /* * By construction above, the cursor should not * be on the right-hand half of this character. * Ever. */ assert(!(i == our_curs_y && j == our_curs_x)); if (!termchars_equal(&term->disptext[i]->chars[j], d)) dirty_run = true; copy_termchar(term->disptext[i], j, d); } } } if (dirty_run && ccount > 0) do_paint_draw(term, ldata, start, i, ch, ccount, attr, tc); unlineptr(ldata); } sfree(newline); sfree(ch); } /* * Invalidate the whole screen so it will be repainted in full. */ void term_invalidate(Terminal *term) { int i, j; for (i = 0; i < term->rows; i++) for (j = 0; j < term->cols; j++) term->disptext[i]->chars[j].attr |= ATTR_INVALID; term_schedule_update(term); } /* * Paint the window in response to a WM_PAINT message. */ void term_paint(Terminal *term, int left, int top, int right, int bottom, bool immediately) { int i, j; if (left < 0) left = 0; if (top < 0) top = 0; if (right >= term->cols) right = term->cols-1; if (bottom >= term->rows) bottom = term->rows-1; for (i = top; i <= bottom && i < term->rows; i++) { if ((term->disptext[i]->lattr & LATTR_MODE) == LATTR_NORM) for (j = left; j <= right && j < term->cols; j++) term->disptext[i]->chars[j].attr |= ATTR_INVALID; else for (j = left / 2; j <= right / 2 + 1 && j < term->cols; j++) term->disptext[i]->chars[j].attr |= ATTR_INVALID; } if (immediately) { do_paint(term); } else { term_schedule_update(term); } } /* * Attempt to scroll the scrollback. The second parameter gives the * position we want to scroll to; the first is +1 to denote that * this position is relative to the beginning of the scrollback, -1 * to denote it is relative to the end, and 0 to denote that it is * relative to the current position. */ void term_scroll(Terminal *term, int rel, int where) { int sbtop = -sblines(term); term->disptop = (rel < 0 ? 0 : rel > 0 ? sbtop : term->disptop) + where; if (term->disptop < sbtop) term->disptop = sbtop; if (term->disptop > 0) term->disptop = 0; term->win_scrollbar_update_pending = true; term_schedule_update(term); } /* * Scroll the scrollback to centre it on the beginning or end of the * current selection, if any. */ void term_scroll_to_selection(Terminal *term, int which_end) { pos target; int y; int sbtop = -sblines(term); if (term->selstate != SELECTED) return; if (which_end) target = term->selend; else target = term->selstart; y = target.y - term->rows/2; if (y < sbtop) y = sbtop; else if (y > 0) y = 0; term_scroll(term, -1, y); } /* * Helper routine for clipme(): growing buffer. */ typedef struct { size_t bufsize; /* amount of allocated space in textbuf/attrbuf */ size_t bufpos; /* amount of actual data */ wchar_t *textbuf; /* buffer for copied text */ wchar_t *textptr; /* = textbuf + bufpos (current insertion point) */ int *attrbuf; /* buffer for copied attributes */ int *attrptr; /* = attrbuf + bufpos */ truecolour *tcbuf; /* buffer for copied colours */ truecolour *tcptr; /* = tcbuf + bufpos */ } clip_workbuf; static void clip_addchar(clip_workbuf *b, wchar_t chr, int attr, truecolour tc) { if (b->bufpos >= b->bufsize) { sgrowarray(b->textbuf, b->bufsize, b->bufpos); b->textptr = b->textbuf + b->bufpos; b->attrbuf = sresize(b->attrbuf, b->bufsize, int); b->attrptr = b->attrbuf + b->bufpos; b->tcbuf = sresize(b->tcbuf, b->bufsize, truecolour); b->tcptr = b->tcbuf + b->bufpos; } *b->textptr++ = chr; *b->attrptr++ = attr; *b->tcptr++ = tc; b->bufpos++; } static void clipme(Terminal *term, pos top, pos bottom, bool rect, bool desel, const int *clipboards, int n_clipboards) { clip_workbuf buf; int old_top_x; int attr; truecolour tc; buf.bufsize = 5120; buf.bufpos = 0; buf.textptr = buf.textbuf = snewn(buf.bufsize, wchar_t); buf.attrptr = buf.attrbuf = snewn(buf.bufsize, int); buf.tcptr = buf.tcbuf = snewn(buf.bufsize, truecolour); old_top_x = top.x; /* needed for rect==1 */ while (poslt(top, bottom)) { bool nl = false; termline *ldata = lineptr(top.y); pos nlpos; /* * nlpos will point at the maximum position on this line we * should copy up to. So we start it at the end of the * line... */ nlpos.y = top.y; nlpos.x = term->cols; /* * ... move it backwards if there's unused space at the end * of the line (and also set `nl' if this is the case, * because in normal selection mode this means we need a * newline at the end)... */ if (!(ldata->lattr & LATTR_WRAPPED)) { while (nlpos.x && IS_SPACE_CHR(ldata->chars[nlpos.x - 1].chr) && !ldata->chars[nlpos.x - 1].cc_next && poslt(top, nlpos)) decpos(nlpos); if (poslt(nlpos, bottom)) nl = true; } else { if (ldata->trusted) { /* A wrapped line with a trust sigil on it terminates * a few characters earlier. */ nlpos.x = (nlpos.x < TRUST_SIGIL_WIDTH ? 0 : nlpos.x - TRUST_SIGIL_WIDTH); } if (ldata->lattr & LATTR_WRAPPED2) { /* Ignore the last char on the line in a WRAPPED2 line. */ decpos(nlpos); } } /* * ... and then clip it to the terminal x coordinate if * we're doing rectangular selection. (In this case we * still did the above, so that copying e.g. the right-hand * column from a table doesn't fill with spaces on the * right.) */ if (rect) { if (nlpos.x > bottom.x) nlpos.x = bottom.x; nl = (top.y < bottom.y); } while (poslt(top, bottom) && poslt(top, nlpos)) { #if 0 char cbuf[16], *p; sprintf(cbuf, "", (ldata[top.x] & 0xFFFF)); #else wchar_t cbuf[16], *p; int c; int x = top.x; if (ldata->chars[x].chr == UCSWIDE) { top.x++; continue; } while (1) { int uc = ldata->chars[x].chr; attr = ldata->chars[x].attr; tc = ldata->chars[x].truecolour; 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; } c = (uc & ~CSET_MASK); #ifdef PLATFORM_IS_UTF16 if (uc > 0x10000 && uc < 0x110000) { cbuf[0] = 0xD800 | ((uc - 0x10000) >> 10); cbuf[1] = 0xDC00 | ((uc - 0x10000) & 0x3FF); cbuf[2] = 0; } else #endif { cbuf[0] = uc; cbuf[1] = 0; } if (DIRECT_FONT(uc)) { if (c >= ' ' && c != 0x7F) { char buf[4]; WCHAR wbuf[4]; int rv; if (is_dbcs_leadbyte(term->ucsdata->font_codepage, (BYTE) c)) { buf[0] = c; buf[1] = (char) (0xFF & ldata->chars[top.x + 1].chr); rv = mb_to_wc(term->ucsdata->font_codepage, 0, buf, 2, wbuf, 4); top.x++; } else { buf[0] = c; rv = mb_to_wc(term->ucsdata->font_codepage, 0, buf, 1, wbuf, 4); } if (rv > 0) { memcpy(cbuf, wbuf, rv * sizeof(wchar_t)); cbuf[rv] = 0; } } } #endif for (p = cbuf; *p; p++) clip_addchar(&buf, *p, attr, tc); if (ldata->chars[x].cc_next) x += ldata->chars[x].cc_next; else break; } top.x++; } if (nl) { int i; for (i = 0; i < sel_nl_sz; i++) clip_addchar(&buf, sel_nl[i], 0, term->basic_erase_char.truecolour); } top.y++; top.x = rect ? old_top_x : 0; unlineptr(ldata); } #if SELECTION_NUL_TERMINATED clip_addchar(&buf, 0, 0, term->basic_erase_char.truecolour); #endif /* Finally, transfer all that to the clipboard(s). */ { int i; bool clip_local = false; for (i = 0; i < n_clipboards; i++) { if (clipboards[i] == CLIP_LOCAL) { clip_local = true; } else if (clipboards[i] != CLIP_NULL) { win_clip_write( term->win, clipboards[i], buf.textbuf, buf.attrbuf, buf.tcbuf, buf.bufpos, desel); } } if (clip_local) { sfree(term->last_selected_text); sfree(term->last_selected_attr); sfree(term->last_selected_tc); term->last_selected_text = buf.textbuf; term->last_selected_attr = buf.attrbuf; term->last_selected_tc = buf.tcbuf; term->last_selected_len = buf.bufpos; } else { sfree(buf.textbuf); sfree(buf.attrbuf); sfree(buf.tcbuf); } } } void term_copyall(Terminal *term, const int *clipboards, int n_clipboards) { pos top; pos bottom; tree234 *screen = term->screen; top.y = -sblines(term); top.x = 0; bottom.y = find_last_nonempty_line(term, screen); bottom.x = term->cols; clipme(term, top, bottom, false, true, clipboards, n_clipboards); } static void paste_from_clip_local(void *vterm) { Terminal *term = (Terminal *)vterm; term_do_paste(term, term->last_selected_text, term->last_selected_len); } void term_request_copy(Terminal *term, const int *clipboards, int n_clipboards) { int i; for (i = 0; i < n_clipboards; i++) { assert(clipboards[i] != CLIP_LOCAL); if (clipboards[i] != CLIP_NULL) { win_clip_write(term->win, clipboards[i], term->last_selected_text, term->last_selected_attr, term->last_selected_tc, term->last_selected_len, false); } } } void term_request_paste(Terminal *term, int clipboard) { switch (clipboard) { case CLIP_NULL: /* Do nothing: CLIP_NULL never has data in it. */ break; case CLIP_LOCAL: queue_toplevel_callback(paste_from_clip_local, term); break; default: win_clip_request_paste(term->win, clipboard); break; } } /* * The wordness array is mainly for deciding the disposition of the * US-ASCII characters. */ static int wordtype(Terminal *term, int uc) { struct ucsword { int start, end, ctype; }; static const struct ucsword ucs_words[] = { { 128, 160, 0}, { 161, 191, 1}, { 215, 215, 1}, { 247, 247, 1}, { 0x037e, 0x037e, 1}, /* Greek question mark */ { 0x0387, 0x0387, 1}, /* Greek ano teleia */ { 0x055a, 0x055f, 1}, /* Armenian punctuation */ { 0x0589, 0x0589, 1}, /* Armenian full stop */ { 0x0700, 0x070d, 1}, /* Syriac punctuation */ { 0x104a, 0x104f, 1}, /* Myanmar punctuation */ { 0x10fb, 0x10fb, 1}, /* Georgian punctuation */ { 0x1361, 0x1368, 1}, /* Ethiopic punctuation */ { 0x166d, 0x166e, 1}, /* Canadian Syl. punctuation */ { 0x17d4, 0x17dc, 1}, /* Khmer punctuation */ { 0x1800, 0x180a, 1}, /* Mongolian punctuation */ { 0x2000, 0x200a, 0}, /* Various spaces */ { 0x2070, 0x207f, 2}, /* superscript */ { 0x2080, 0x208f, 2}, /* subscript */ { 0x200b, 0x27ff, 1}, /* punctuation and symbols */ { 0x3000, 0x3000, 0}, /* ideographic space */ { 0x3001, 0x3020, 1}, /* ideographic punctuation */ { 0x303f, 0x309f, 3}, /* Hiragana */ { 0x30a0, 0x30ff, 3}, /* Katakana */ { 0x3300, 0x9fff, 3}, /* CJK Ideographs */ { 0xac00, 0xd7a3, 3}, /* Hangul Syllables */ { 0xf900, 0xfaff, 3}, /* CJK Ideographs */ { 0xfe30, 0xfe6b, 1}, /* punctuation forms */ { 0xff00, 0xff0f, 1}, /* half/fullwidth ASCII */ { 0xff1a, 0xff20, 1}, /* half/fullwidth ASCII */ { 0xff3b, 0xff40, 1}, /* half/fullwidth ASCII */ { 0xff5b, 0xff64, 1}, /* half/fullwidth ASCII */ { 0xfff0, 0xffff, 0}, /* half/fullwidth ASCII */ { 0, 0, 0} }; const struct ucsword *wptr; switch (uc & CSET_MASK) { case CSET_LINEDRW: 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; } /* For DBCS fonts I can't do anything useful. Even this will sometimes * fail as there's such a thing as a double width space. :-( */ if (term->ucsdata->dbcs_screenfont && term->ucsdata->font_codepage == term->ucsdata->line_codepage) return (uc != ' '); if (uc < 0x80) return term->wordness[uc]; for (wptr = ucs_words; wptr->start; wptr++) { if (uc >= wptr->start && uc <= wptr->end) return wptr->ctype; } return 2; } static int line_cols(Terminal *term, termline *ldata) { int cols = term->cols; if (ldata->trusted) { cols -= TRUST_SIGIL_WIDTH; } if (ldata->lattr & LATTR_WRAPPED2) cols--; if (cols < 0) cols = 0; return cols; } /* * Spread the selection outwards according to the selection mode. */ static pos sel_spread_half(Terminal *term, pos p, int dir) { termline *ldata; short wvalue; int topy = -sblines(term); ldata = lineptr(p.y); switch (term->selmode) { case SM_CHAR: /* * In this mode, every character is a separate unit, except * for runs of spaces at the end of a non-wrapping line. */ if (!(ldata->lattr & LATTR_WRAPPED)) { termchar *q = ldata->chars + line_cols(term, ldata); while (q > ldata->chars && IS_SPACE_CHR(q[-1].chr) && !q[-1].cc_next) q--; if (q == ldata->chars + term->cols) q--; if (p.x >= q - ldata->chars) p.x = (dir == -1 ? q - ldata->chars : term->cols - 1); } break; case SM_WORD: /* * In this mode, the units are maximal runs of characters * whose `wordness' has the same value. */ wvalue = wordtype(term, UCSGET(ldata->chars, p.x)); if (dir == +1) { while (1) { int maxcols = line_cols(term, ldata); 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); ldata = ldata2; } else { unlineptr(ldata2); break; } } else break; } } } else { while (1) { if (p.x > 0) { if (wordtype(term, UCSGET(ldata->chars, p.x-1)) == wvalue) p.x--; else break; } else { termline *ldata2; int maxcols; if (p.y <= topy) break; ldata2 = lineptr(p.y-1); maxcols = line_cols(term, ldata2); if (ldata2->lattr & LATTR_WRAPPED) { if (wordtype(term, UCSGET(ldata2->chars, maxcols-1)) == wvalue) { p.x = maxcols-1; p.y--; unlineptr(ldata); ldata = ldata2; } else { unlineptr(ldata2); break; } } else break; } } } break; case SM_LINE: /* * In this mode, every line is a unit. */ p.x = (dir == -1 ? 0 : term->cols - 1); break; } unlineptr(ldata); return p; } static void sel_spread(Terminal *term) { 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) { strbuf *buf = term_input_data_from_unicode( term, term->paste_buffer + term->paste_pos, n); term_keyinput_internal(term, buf->s, buf->len, false); strbuf_free(buf); } term->paste_pos += n; if (term->paste_pos < term->paste_len) { queue_toplevel_callback(term_paste_callback, term); return; } } term_bracketed_paste_stop(term); sfree(term->paste_buffer); term->paste_buffer = NULL; term->paste_len = 0; } /* * Specialist string compare function. Returns true if the buffer of * alen wide characters starting at a has as a prefix the buffer of * blen characters starting at b. */ static bool wstartswith(const wchar_t *a, size_t alen, const wchar_t *b, size_t blen) { return alen >= blen && !wcsncmp(a, b, blen); } void term_do_paste(Terminal *term, const wchar_t *data, int len) { const wchar_t *p; bool paste_controls = conf_get_bool(term->conf, CONF_paste_controls); /* * Pasting data into the terminal counts as a keyboard event (for * purposes of the 'Reset scrollback on keypress' config option), * unless the paste is zero-length. */ if (len == 0) return; term_seen_key_event(term); 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) term_bracketed_paste_start(term); p = data; while (p < data + len) { wchar_t wc = *p++; if (wc == sel_nl[0] && wstartswith(p-1, data+len-(p-1), sel_nl, sel_nl_sz)) { /* * This is the (platform-dependent) sequence that the host * OS uses to represent newlines in clipboard data. * Normalise it to a press of CR. */ p += sel_nl_sz - 1; wc = '\015'; } if ((wc & ~(wint_t)0x9F) == 0) { /* * This is a control code, either in the range 0x00-0x1F * or 0x80-0x9F. We reject all of these in pastecontrols * mode, except for a small set of permitted ones. */ if (!paste_controls) { /* In line with xterm 292, accepted control chars are: * CR, LF, tab, backspace. (And DEL, i.e. 0x7F, but * that's permitted by virtue of not matching the bit * mask that got us into this if statement, so we * don't have to permit it here. */ static const unsigned mask = (1<<13) | (1<<10) | (1<<9) | (1<<8); if (wc > 15 || !((mask >> wc) & 1)) continue; } if (wc == '\033' && term->bracketed_paste && wstartswith(p-1, data+len-(p-1), L"\033[201~", 6)) { /* * Also, in bracketed-paste mode, reject the ESC * character that begins the end-of-paste sequence. */ continue; } } term->paste_buffer[term->paste_len++] = wc; } /* Assume a small paste will be OK in one go. */ if (term->paste_len < 256) { if (term->ldisc) { strbuf *buf = term_input_data_from_unicode( term, term->paste_buffer, term->paste_len); term_keyinput_internal(term, buf->s, buf->len, false); strbuf_free(buf); } if (term->paste_buffer) sfree(term->paste_buffer); term_bracketed_paste_stop(term); term->paste_buffer = NULL; term->paste_pos = term->paste_len = 0; } 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, bool shift, bool ctrl, bool alt) { pos selpoint; termline *ldata; bool 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); } if (y >= term->rows) { y = term->rows - 1; if (a == MA_DRAG && !raw_mouse) term_scroll(term, 0, +1); } if (x < 0) { if (y > 0 && !raw_mouse && term->seltype != RECTANGULAR) { /* * When we're using the mouse for normal raster-based * selection, dragging off the left edge of a terminal row * is treated the same as the right-hand end of the * previous row, in that it's considered to identify a * point _before_ the first character on row y. * * But if the mouse action is going to be used for * anything else - rectangular selection, or xterm mouse * tracking - then we disable this special treatment. */ x = term->cols - 1; y--; } else x = 0; } if (x >= term->cols) x = term->cols - 1; selpoint.y = y + term->disptop; ldata = lineptr(selpoint.y); if ((ldata->lattr & LATTR_MODE) != LATTR_NORM) x /= 2; /* * Transform x through the bidi algorithm to find the _logical_ * click point from the physical one. */ if (term_bidi_line(term, ldata, y) != NULL) { x = term->post_bidi_cache[y].backward[x]; } selpoint.x = x; unlineptr(ldata); /* * If we're in the middle of a selection operation, we ignore raw * mouse mode until it's done (we must have been not in raw mouse * mode when it started). * 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; bool wheel; char abuf[32]; int len = 0; if (term->ldisc) { switch (braw) { case MBT_LEFT: encstate = 0x00; /* left button down */ wheel = false; break; case MBT_MIDDLE: encstate = 0x01; wheel = false; break; case MBT_RIGHT: encstate = 0x02; wheel = false; break; case MBT_WHEEL_UP: encstate = 0x40; wheel = true; break; case MBT_WHEEL_DOWN: encstate = 0x41; wheel = true; break; default: return; } if (wheel) { /* For mouse wheel buttons, we only ever expect to see * MA_CLICK actions, and we don't try to keep track of * the buttons being 'pressed' (since without matching * click/release pairs that's pointless). */ if (a != MA_CLICK) return; } else 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: return; } 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); } if (len > 0) ldisc_send(term->ldisc, abuf, len, false); } 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; } if (bcooked == MBT_SELECT && a == MA_CLICK) { deselect(term); term->selstate = ABOUT_TO; term->seltype = default_seltype; term->selanchor = selpoint; term->selmode = SM_CHAR; } else if (bcooked == MBT_SELECT && (a == MA_2CLK || a == MA_3CLK)) { deselect(term); term->selmode = (a == MA_2CLK ? SM_WORD : SM_LINE); term->selstate = DRAGGING; term->selstart = term->selanchor = selpoint; term->selend = term->selstart; incpos(term->selend); sel_spread(term); } else if ((bcooked == MBT_SELECT && a == MA_DRAG) || (bcooked == MBT_EXTEND && a != MA_RELEASE)) { if (a == MA_DRAG && (term->selstate == NO_SELECTION || term->selstate == SELECTED)) { /* * This can happen if a front end has passed us a MA_DRAG * without a prior MA_CLICK. OS X GTK does so, for * example, if the initial button press was eaten by the * WM when it activated the window in the first place. The * nicest thing to do in this situation is to ignore * further drags, and wait for the user to click in the * window again properly if they want to select. */ return; } if (term->selstate == ABOUT_TO && poseq(term->selanchor, selpoint)) return; if (bcooked == MBT_EXTEND && a != MA_DRAG && term->selstate == SELECTED) { if (term->seltype == LEXICOGRAPHIC) { /* * For normal selection, we extend by moving * whichever end of the current selection is closer * to the mouse. */ if (posdiff(selpoint, term->selstart) < posdiff(term->selend, term->selstart) / 2) { term->selanchor = term->selend; decpos(term->selanchor); } else { term->selanchor = term->selstart; } } else { /* * For rectangular selection, we have a choice of * _four_ places to put selanchor and selpoint: the * four corners of the selection. */ if (2*selpoint.x < term->selstart.x + term->selend.x) term->selanchor.x = term->selend.x-1; else term->selanchor.x = term->selstart.x; if (2*selpoint.y < term->selstart.y + term->selend.y) term->selanchor.y = term->selend.y; else term->selanchor.y = term->selstart.y; } term->selstate = DRAGGING; } if (term->selstate != ABOUT_TO && term->selstate != DRAGGING) term->selanchor = selpoint; term->selstate = DRAGGING; if (term->seltype == LEXICOGRAPHIC) { /* * For normal selection, we set (selstart,selend) to * (selpoint,selanchor) in some order. */ if (poslt(selpoint, term->selanchor)) { term->selstart = selpoint; term->selend = term->selanchor; incpos(term->selend); } else { term->selstart = term->selanchor; term->selend = selpoint; incpos(term->selend); } } else { /* * For rectangular selection, we may need to * interchange x and y coordinates (if the user has * dragged in the -x and +y directions, or vice versa). */ term->selstart.x = min(term->selanchor.x, selpoint.x); term->selend.x = 1+max(term->selanchor.x, selpoint.x); term->selstart.y = min(term->selanchor.y, selpoint.y); term->selend.y = max(term->selanchor.y, selpoint.y); } sel_spread(term); } else if ((bcooked == MBT_SELECT || bcooked == MBT_EXTEND) && a == MA_RELEASE) { if (term->selstate == DRAGGING) { /* * We've completed a selection. We now transfer the * data to the clipboard. */ clipme(term, term->selstart, term->selend, (term->seltype == RECTANGULAR), false, term->mouse_select_clipboards, term->n_mouse_select_clipboards); term->selstate = SELECTED; } else term->selstate = NO_SELECTION; } else if (bcooked == MBT_PASTE && (a == MA_CLICK #if MULTICLICK_ONLY_EVENT || a == MA_2CLK || a == MA_3CLK #endif )) { term_request_paste(term, term->mouse_paste_clipboard); } /* * 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_schedule_update(term); } int format_arrow_key(char *buf, Terminal *term, int xkey, bool ctrl) { char *p = buf; if (term->vt52_mode) p += sprintf(p, "\x1B%c", xkey); else { bool 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 * and the behaviour is back to PuTTY's original: app * cursor and app keypad are independently switchable * modes. If anyone complains about _this_ I'll have to * put in a configurable option. */ if (!term->app_keypad_keys) app_flg = 0; #endif /* Useful mapping of Ctrl-arrows */ if (ctrl) app_flg = !app_flg; if (app_flg) p += sprintf(p, "\x1BO%c", xkey); else p += sprintf(p, "\x1B[%c", xkey); } return p - buf; } int format_function_key(char *buf, Terminal *term, int key_number, bool shift, bool ctrl) { char *p = buf; static const int key_number_to_tilde_code[] = { -1, /* no such key as F0 */ 11, 12, 13, 14, 15, /*gap*/ 17, 18, 19, 20, 21, /*gap*/ 23, 24, 25, 26, /*gap*/ 28, 29, /*gap*/ 31, 32, 33, 34, }; assert(key_number > 0); assert(key_number < lenof(key_number_to_tilde_code)); int index = (shift && key_number <= 10) ? key_number + 10 : key_number; int code = key_number_to_tilde_code[index]; if (term->funky_type == FUNKY_SCO) { /* SCO function keys */ static const char sco_codes[] = "MNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@[\\]^_`{"; index = (key_number >= 1 && key_number <= 12) ? key_number - 1 : 0; if (shift) index += 12; if (ctrl) index += 24; p += sprintf(p, "\x1B[%c", sco_codes[index]); } else if ((term->vt52_mode || term->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(p, "\x1B%c", code + 'P' - 11 - offt); else p += sprintf(p, "\x1BO%c", code + 'P' - 11 - offt); } else if (term->funky_type == FUNKY_LINUX && code >= 11 && code <= 15) { p += sprintf(p, "\x1B[[%c", code + 'A' - 11); } else if (term->funky_type == FUNKY_XTERM && code >= 11 && code <= 14) { if (term->vt52_mode) p += sprintf(p, "\x1B%c", code + 'P' - 11); else p += sprintf(p, "\x1BO%c", code + 'P' - 11); } else { p += sprintf(p, "\x1B[%d~", code); } return p - buf; } int format_small_keypad_key(char *buf, Terminal *term, SmallKeypadKey key) { char *p = buf; int code; switch (key) { case SKK_HOME: code = 1; break; case SKK_INSERT: code = 2; break; case SKK_DELETE: code = 3; break; case SKK_END: code = 4; break; case SKK_PGUP: code = 5; break; case SKK_PGDN: code = 6; break; default: unreachable("bad small keypad key enum value"); } /* Reorder edit keys to physical order */ if (term->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(p, "\x1B%c", " HLMEIG"[code]); } else if (term->funky_type == FUNKY_SCO) { static const char codes[] = "HL.FIG"; if (code == 3) { *p++ = '\x7F'; } else { p += sprintf(p, "\x1B[%c", codes[code-1]); } } else if ((code == 1 || code == 4) && term->rxvt_homeend) { p += sprintf(p, code == 1 ? "\x1B[H" : "\x1BOw"); } else { p += sprintf(p, "\x1B[%d~", code); } return p - buf; } int format_numeric_keypad_key(char *buf, Terminal *term, char key, bool shift, bool ctrl) { char *p = buf; bool app_keypad = (term->app_keypad_keys && !term->no_applic_k); if (term->nethack_keypad && (key >= '1' && key <= '9')) { static const char nh_base[] = "bjnh.lyku"; char c = nh_base[key - '1']; if (ctrl && c != '.') c &= 0x1F; else if (shift && c != '.') c += 'A'-'a'; *p++ = c; } else { int xkey = 0; if (term->funky_type == FUNKY_VT400 || (term->funky_type <= FUNKY_LINUX && app_keypad)) { switch (key) { case 'G': xkey = 'P'; break; case '/': xkey = 'Q'; break; case '*': xkey = 'R'; break; case '-': xkey = 'S'; break; } } if (app_keypad) { switch (key) { case '0': xkey = 'p'; break; case '1': xkey = 'q'; break; case '2': xkey = 'r'; break; case '3': xkey = 's'; break; case '4': xkey = 't'; break; case '5': xkey = 'u'; break; case '6': xkey = 'v'; break; case '7': xkey = 'w'; break; case '8': xkey = 'x'; break; case '9': xkey = 'y'; break; case '.': xkey = 'n'; break; case '\r': xkey = 'M'; break; case '+': /* * 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... */ if (term->funky_type == FUNKY_XTERM) xkey = shift ? 'l' : 'k'; else xkey = shift ? 'm' : 'l'; break; case '/': if (term->funky_type == FUNKY_XTERM) xkey = 'o'; break; case '*': if (term->funky_type == FUNKY_XTERM) xkey = 'j'; break; case '-': if (term->funky_type == FUNKY_XTERM) xkey = 'm'; break; } } if (xkey) { if (term->vt52_mode) { if (xkey >= 'P' && xkey <= 'S') p += sprintf(p, "\x1B%c", xkey); else p += sprintf(p, "\x1B?%c", xkey); } else p += sprintf(p, "\x1BO%c", xkey); } } return p - buf; } void term_keyinputw(Terminal *term, const wchar_t *widebuf, int len) { strbuf *buf = term_input_data_from_unicode(term, widebuf, len); if (buf->len) term_keyinput_internal(term, buf->s, buf->len, true); strbuf_free(buf); } void term_keyinput(Terminal *term, int codepage, const void *str, int len) { if (codepage < 0 || codepage == term->ucsdata->line_codepage) { /* * This text needs no translation, either because it's already * in the right character set, or because we got the special * codepage value -1 from our caller which means 'this data * should be charset-agnostic, just send it raw' (for really * simple things like control characters). */ term_keyinput_internal(term, str, len, true); } else { strbuf *buf = term_input_data_from_charset(term, codepage, str, len); if (buf->len) term_keyinput_internal(term, buf->s, buf->len, true); strbuf_free(buf); } } void term_nopaste(Terminal *term) { if (term->paste_len == 0) return; sfree(term->paste_buffer); term_bracketed_paste_stop(term); 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_lost_clipboard_ownership(Terminal *term, int clipboard) { if (!(term->n_mouse_select_clipboards > 1 && clipboard == term->mouse_select_clipboards[1])) return; 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); } static void term_added_data(Terminal *term) { if (!term->in_term_out) { term->in_term_out = true; term_reset_cblink(term); /* * During drag-selects, we do not process terminal input, * because the user will want the screen to hold still to * be selected. */ if (term->selstate != DRAGGING) term_out(term); term->in_term_out = false; } } size_t term_data(Terminal *term, bool is_stderr, const void *data, size_t len) { bufchain_add(&term->inbuf, data, len); term_added_data(term); /* * term_out() always completely empties inbuf. Therefore, * there's no reason at all to return anything other than zero * from this function, because there _can't_ be a question of * the remote side needing to wait until term_out() has cleared * a backlog. * * This is a slightly suboptimal way to deal with SSH-2 - in * principle, the window mechanism would allow us to continue * to accept data on forwarded ports and X connections even * while the terminal processing was going slowly - but we * can't do the 100% right thing without moving the terminal * processing into a separate thread, and that might hurt * portability. So we manage stdout buffering the old SSH-1 way: * if the terminal processing goes slowly, the whole SSH * connection stops accepting data until it's ready. * * In practice, I can't imagine this causing serious trouble. */ return 0; } void term_provide_logctx(Terminal *term, LogContext *logctx) { term->logctx = logctx; } void term_set_focus(Terminal *term, bool has_focus) { term->has_focus = has_focus; term_schedule_cblink(term); } /* * Provide "auto" settings for remote tty modes, suitable for an * application with a terminal window. */ char *term_get_ttymode(Terminal *term, const char *mode) { const char *val = NULL; if (strcmp(mode, "ERASE") == 0) { val = term->bksp_is_delete ? "^?" : "^H"; } else if (strcmp(mode, "IUTF8") == 0) { val = (term->ucsdata->line_codepage == CP_UTF8) ? "yes" : "no"; } /* 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; bool done_prompt; /* printed out prompt yet? */ }; /* Tiny wrapper to make it easier to write lots of little strings */ static inline void term_write(Terminal *term, ptrlen data) { term_data(term, false, data.ptr, data.len); } /* * Process some terminal data in the course of username/password * input. */ int term_get_userpass_input(Terminal *term, prompts_t *p, bufchain *input) { struct term_userpass_state *s = (struct term_userpass_state *)p->data; if (!s) { /* * First call. Set some stuff up. */ p->data = s = snew(struct term_userpass_state); s->curr_prompt = 0; s->done_prompt = false; /* We only print the `name' caption if we have to... */ if (p->name_reqd && p->name) { ptrlen plname = ptrlen_from_asciz(p->name); term_write(term, plname); if (!ptrlen_endswith(plname, PTRLEN_LITERAL("\n"), NULL)) term_write(term, PTRLEN_LITERAL("\r\n")); } /* ...but we always print any `instruction'. */ if (p->instruction) { ptrlen plinst = ptrlen_from_asciz(p->instruction); term_write(term, plinst); if (!ptrlen_endswith(plinst, PTRLEN_LITERAL("\n"), NULL)) term_write(term, PTRLEN_LITERAL("\r\n")); } /* * 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]; bool finished_prompt = false; if (!s->done_prompt) { term_write(term, ptrlen_from_asciz(pr->prompt)); s->done_prompt = true; } /* Breaking out here ensures that the prompt is printed even * if we're now waiting for user data. */ if (!input || !bufchain_size(input)) break; /* FIXME: should we be using local-line-editing code instead? */ while (!finished_prompt && bufchain_size(input) > 0) { char c; bufchain_fetch_consume(input, &c, 1); switch (c) { case 10: case 13: term_write(term, PTRLEN_LITERAL("\r\n")); /* go to next prompt, if any */ s->curr_prompt++; s->done_prompt = false; finished_prompt = true; /* break out */ break; case 8: case 127: if (pr->result->len > 0) { if (pr->echo) term_write(term, PTRLEN_LITERAL("\b \b")); strbuf_shrink_by(pr->result, 1); } break; case 21: case 27: while (pr->result->len > 0) { if (pr->echo) term_write(term, PTRLEN_LITERAL("\b \b")); strbuf_shrink_by(pr->result, 1); } break; case 3: case 4: /* Immediate abort. */ term_write(term, PTRLEN_LITERAL("\r\n")); sfree(s); p->data = NULL; 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)) { put_byte(pr->result, c); if (pr->echo) term_write(term, make_ptrlen(&c, 1)); } break; } } } if (s->curr_prompt < p->n_prompts) { return -1; /* more data required */ } else { sfree(s); p->data = NULL; return +1; /* all done */ } } void term_notify_minimised(Terminal *term, bool minimised) { term->minimised = minimised; } void term_notify_palette_changed(Terminal *term) { palette_reset(term, true); } void term_notify_window_pos(Terminal *term, int x, int y) { term->winpos_x = x; term->winpos_y = y; } void term_notify_window_size_pixels(Terminal *term, int x, int y) { term->winpixsize_x = x; term->winpixsize_y = y; } putty-0.76/terminal.h0000644000175000017500000004057114072266312011552 00000000000000/* * Internals of the Terminal structure, for those other modules * which need to look inside it. It would be nice if this could be * folded back into terminal.c in future, with an abstraction layer * to handle everything that other modules need to know about it; * but for the moment, this will do. */ #ifndef PUTTY_TERMINAL_H #define PUTTY_TERMINAL_H #include "tree234.h" struct beeptime { struct beeptime *next; unsigned long ticks; }; #define TRUST_SIGIL_WIDTH 3 #define TRUST_SIGIL_CHAR 0xDFFE typedef struct { int y, x; } pos; typedef struct termchar termchar; typedef struct termline termline; struct termchar { /* * Any code in terminal.c which definitely needs to be changed * when extra fields are added here is labelled with a comment * saying FULL-TERMCHAR. */ unsigned long chr; unsigned long attr; truecolour truecolour; /* * The cc_next field is used to link multiple termchars * together into a list, so as to fit more than one character * into a character cell (Unicode combining characters). * * cc_next is a relative offset into the current array of * termchars. I.e. to advance to the next character in a list, * one does `tc += tc->next'. * * Zero means end of list. */ int cc_next; }; struct termline { unsigned short lattr; int cols; /* number of real columns on the line */ int size; /* number of allocated termchars * (cc-lists may make this > cols) */ bool temporary; /* true if decompressed from scrollback */ int cc_free; /* offset to first cc in free list */ struct termchar *chars; bool trusted; }; struct bidi_cache_entry { int width; bool trusted; struct termchar *chars; int *forward, *backward; /* the permutations of line positions */ }; struct term_utf8_decode { int state; /* Is there a pending UTF-8 character */ int chr; /* and what is it so far? */ int size; /* The size of the UTF character. */ }; struct terminal_tag { int compatibility_level; tree234 *scrollback; /* lines scrolled off top of screen */ tree234 *screen; /* lines on primary screen */ tree234 *alt_screen; /* lines on alternate screen */ int disptop; /* distance scrolled back (0 or -ve) */ int tempsblines; /* number of lines of .scrollback that can be retrieved onto the terminal ("temporary scrollback") */ termline **disptext; /* buffer of text on real screen */ int dispcursx, dispcursy; /* location of cursor on real screen */ int curstype; /* type of cursor on real screen */ #define VBELL_TIMEOUT (TICKSPERSEC/10) /* visual bell lasts 1/10 sec */ struct beeptime *beephead, *beeptail; int nbeeps; bool beep_overloaded; long lastbeep; #define TTYPE termchar #define TSIZE (sizeof(TTYPE)) int default_attr, curr_attr, save_attr; truecolour curr_truecolour, save_truecolour; termchar basic_erase_char, erase_char; bufchain inbuf; /* terminal input buffer */ pos curs; /* cursor */ pos savecurs; /* saved cursor position */ int marg_t, marg_b; /* scroll margins */ bool dec_om; /* DEC origin mode flag */ bool wrap, wrapnext; /* wrap flags */ bool insert; /* insert-mode flag */ int cset; /* 0 or 1: which char set */ int save_cset, save_csattr; /* saved with cursor position */ bool save_utf, save_wnext; /* saved with cursor position */ bool rvideo; /* global reverse video flag */ unsigned long rvbell_startpoint; /* for ESC[?5hESC[?5l vbell */ bool cursor_on; /* cursor enabled flag */ bool reset_132; /* Flag ESC c resets to 80 cols */ bool use_bce; /* Use Background coloured erase */ bool cblinker; /* When blinking is the cursor on ? */ bool tblinker; /* When the blinking text is on */ bool blink_is_real; /* Actually blink blinking text */ int sco_acs, save_sco_acs; /* CSI 10,11,12m -> OEM charset */ bool vt52_bold; /* Force bold on non-bold colours */ bool utf; /* Are we in toggleable UTF-8 mode? */ term_utf8_decode utf8; /* If so, here's our decoding state */ bool printing, only_printing; /* Are we doing ANSI printing? */ int print_state; /* state of print-end-sequence scan */ bufchain printer_buf; /* buffered data for printer */ printer_job *print_job; /* ESC 7 saved state for the alternate screen */ pos alt_savecurs; int alt_save_attr; truecolour alt_save_truecolour; int alt_save_cset, alt_save_csattr; bool alt_save_utf; bool alt_save_wnext; int alt_save_sco_acs; int rows, cols, savelines; bool has_focus; bool in_vbell; long vbell_end; bool app_cursor_keys, app_keypad_keys, vt52_mode; bool repeat_off, srm_echo, cr_lf_return; bool seen_disp_event; bool big_cursor; bool xterm_mouse_forbidden; int xterm_mouse; /* send mouse messages to host */ bool xterm_extended_mouse; bool urxvt_extended_mouse; int mouse_is_down; /* used while tracking mouse buttons */ bool bracketed_paste, bracketed_paste_active; int cset_attr[2]; /* * Saved settings on the alternate screen. */ int alt_x, alt_y; bool alt_wnext, alt_ins; bool alt_om, alt_wrap; int alt_cset, alt_sco_acs; bool alt_utf; int alt_t, alt_b; int alt_which; int alt_sblines; /* # of lines on alternate screen that should be used for scrollback. */ #define ARGS_MAX 32 /* max # of esc sequence arguments */ #define ARG_DEFAULT 0 /* if an arg isn't specified */ #define def(a,d) ( (a) == ARG_DEFAULT ? (d) : (a) ) unsigned esc_args[ARGS_MAX]; int esc_nargs; int esc_query; #define ANSI(x,y) ((x)+((y)*256)) #define ANSI_QUE(x) ANSI(x,1) #define OSC_STR_MAX 2048 int osc_strlen; char osc_string[OSC_STR_MAX + 1]; bool osc_w; char id_string[1024]; unsigned char *tabs; enum { TOPLEVEL, SEEN_ESC, SEEN_CSI, SEEN_OSC, SEEN_OSC_W, DO_CTRLS, SEEN_OSC_P, OSC_STRING, OSC_MAYBE_ST, VT52_ESC, VT52_Y1, VT52_Y2, VT52_FG, VT52_BG } termstate; enum { NO_SELECTION, ABOUT_TO, DRAGGING, SELECTED } selstate; enum { LEXICOGRAPHIC, RECTANGULAR } seltype; enum { SM_CHAR, SM_WORD, SM_LINE } selmode; pos selstart, selend, selanchor; short wordness[256]; /* Mask of attributes to pay attention to when painting. */ int attr_mask; wchar_t *paste_buffer; int paste_len, paste_pos; Backend *backend; Ldisc *ldisc; TermWin *win; LogContext *logctx; struct unicode_data *ucsdata; unsigned long last_graphic_char; /* * 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; /* * GUI implementations of seat_output call 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 * seat_output is called recursively like this, it will simply add * data to the end of the buffer term_out is in the process of * working through. */ bool in_term_out; /* * We don't permit window updates too close together, to avoid CPU * churn pointlessly redrawing the window faster than the user can * read. So after an update, we set window_update_cooldown = true * and schedule a timer to reset it to false. In between those * times, window updates are not performed, and instead we set * window_update_pending = true, which will remind us to perform * the deferred redraw when the cooldown period ends and * window_update_cooldown is reset to false. */ bool window_update_pending, window_update_cooldown; long window_update_cooldown_end; /* * Track pending blinks and tblinks. */ bool tblink_pending, cblink_pending; long next_tblink, next_cblink; /* * These are buffers used by the bidi and Arabic shaping code. */ termchar *ltemp; int ltemp_size; bidi_char *wcFrom, *wcTo; int wcFromTo_size; struct bidi_cache_entry *pre_bidi_cache, *post_bidi_cache; size_t bidi_cache_size; /* * Current trust state, used to annotate every line of the * terminal that a graphic character is output to. */ bool trusted; /* * 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. */ bool ansi_colour; char *answerback; int answerbacklen; bool no_arabicshaping; int beep; bool bellovl; int bellovl_n; int bellovl_s; int bellovl_t; bool no_bidi; bool bksp_is_delete; bool blink_cur; bool blinktext; bool cjk_ambig_wide; int conf_height; int conf_width; bool crhaslf; bool erase_to_scrollback; int funky_type; bool lfhascr; bool logflush; int logtype; bool mouse_override; bool nethack_keypad; bool no_alt_screen; bool no_applic_c; bool no_applic_k; bool no_dbackspace; bool no_mouse_rep; bool no_remote_charset; bool no_remote_resize; bool no_remote_wintitle; bool no_remote_clearscroll; bool rawcnp; bool utf8linedraw; bool rect_select; int remote_qtitle_action; bool rxvt_homeend; bool scroll_on_disp; bool scroll_on_key; bool xterm_256_colour; bool true_colour; wchar_t *last_selected_text; int *last_selected_attr; truecolour *last_selected_tc; size_t last_selected_len; int mouse_select_clipboards[N_CLIPBOARDS]; int n_mouse_select_clipboards; int mouse_paste_clipboard; char *window_title, *icon_title; bool minimised; /* Multi-layered colour palette. The colours from Conf (plus the * default xterm-256 ones that don't have Conf ids at all) have * lowest priority, followed by platform overrides if any, * followed by escape-sequence overrides during the session. */ struct term_subpalette { rgb values[OSC4_NCOLOURS]; bool present[OSC4_NCOLOURS]; } subpalettes[3]; #define SUBPAL_CONF 0 #define SUBPAL_PLATFORM 1 #define SUBPAL_SESSION 2 /* The composite palette that we make out of the above */ rgb palette[OSC4_NCOLOURS]; unsigned winpos_x, winpos_y, winpixsize_x, winpixsize_y; /* * Assorted 'pending' flags for ancillary window changes performed * in term_update. Generally, to trigger one of these operations, * you set the pending flag and/or the parameters here, then call * term_schedule_update. */ bool win_move_pending; int win_move_pending_x, win_move_pending_y; bool win_resize_pending; int win_resize_pending_w, win_resize_pending_h; bool win_zorder_pending; bool win_zorder_top; bool win_minimise_pending; bool win_minimise_enable; bool win_maximise_pending; bool win_maximise_enable; bool win_title_pending, win_icon_title_pending; bool win_pointer_shape_pending; bool win_pointer_shape_raw; bool win_refresh_pending; bool win_scrollbar_update_pending; bool win_palette_pending; unsigned win_palette_pending_min, win_palette_pending_limit; }; static inline bool in_utf(Terminal *term) { return term->utf || term->ucsdata->line_codepage == CP_UTF8; } unsigned long term_translate( Terminal *term, term_utf8_decode *utf8, unsigned char c); static inline int term_char_width(Terminal *term, unsigned int c) { return term->cjk_ambig_wide ? mk_wcwidth_cjk(c) : mk_wcwidth(c); } /* * UCSINCOMPLETE is returned from term_translate if it's successfully * absorbed a byte but not emitted a complete character yet. * UCSTRUNCATED indicates a truncated multibyte sequence (so the * caller emits an error character and then calls term_translate again * with the same input byte). UCSINVALID indicates some other invalid * multibyte sequence, such as an overlong synonym, or a standalone * continuation byte, or a completely illegal thing like 0xFE. These * values are not stored in the terminal data structures at all. */ #define UCSINCOMPLETE 0x8000003FU /* '?' */ #define UCSTRUNCATED 0x80000021U /* '!' */ #define UCSINVALID 0x8000002AU /* '*' */ /* * Maximum number of combining characters we're willing to store in a * character cell. Our linked-list data representation permits an * unlimited number of these in principle, but if we allowed that in * practice then it would be an easy DoS to just squirt a squillion * identical combining characters to someone's terminal and cause * their PuTTY or pterm to consume lots of memory and CPU pointlessly. * * The precise figure of 32 is more or less arbitrary, but one point * supporting it is UAX #15's comment that 30 combining characters is * "significantly beyond what is required for any linguistic or * technical usage". */ #define CC_LIMIT 32 /* ---------------------------------------------------------------------- * Helper functions for dealing with the small 'pos' structure. */ static inline bool poslt(pos p1, pos p2) { if (p1.y != p2.y) return p1.y < p2.y; return p1.x < p2.x; } static inline bool posle(pos p1, pos p2) { if (p1.y != p2.y) return p1.y < p2.y; return p1.x <= p2.x; } static inline bool poseq(pos p1, pos p2) { return p1.y == p2.y && p1.x == p2.x; } static inline int posdiff_fn(pos p1, pos p2, int cols) { return (p1.y - p2.y) * (cols+1) + (p1.x - p2.x); } /* Convenience wrapper on posdiff_fn which uses the 'Terminal *term' * that more or less every function in terminal.c will have in scope. * For safety's sake I include a TYPECHECK that ensures it really is a * structure pointer of the right type. */ #define GET_TERM_COLS TYPECHECK(term == (Terminal *)0, term->cols) #define posdiff(p1,p2) posdiff_fn(p1, p2, GET_TERM_COLS) /* Product-order comparisons for rectangular block selection. */ static inline bool posPle(pos p1, pos p2) { return p1.y <= p2.y && p1.x <= p2.x; } static inline bool posPle_left(pos p1, pos p2) { /* * This function is used for checking whether a given character * cell of the terminal ought to be highlighted as part of the * selection, by comparing with term->selend. term->selend stores * the location one space to the right of the last highlighted * character. So we want to highlight the characters that are * less-or-equal (in the product order) to the character just left * of p2. * * (Setting up term->selend that way was the easiest way to get * rectangular selection working at all, in a code base that had * done lexicographic selection the way I happened to have done * it.) */ return p1.y <= p2.y && p1.x < p2.x; } static inline bool incpos_fn(pos *p, int cols) { if (p->x == cols) { p->x = 0; p->y++; return true; } p->x++; return false; } static inline bool decpos_fn(pos *p, int cols) { if (p->x == 0) { p->x = cols; p->y--; return true; } p->x--; return false; } /* Convenience wrappers on incpos and decpos which use term->cols * (similarly to posdiff above), and also (for mild convenience and * mostly historical inertia) let you leave off the & at every call * site. */ #define incpos(p) incpos_fn(&(p), GET_TERM_COLS) #define decpos(p) decpos_fn(&(p), GET_TERM_COLS) #endif putty-0.76/testback.c0000644000175000017500000001313314072266313011525 00000000000000/* * 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 * 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: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ /* PuTTY test backends */ #include #include #include "putty.h" static char *null_init(const BackendVtable *, Seat *, Backend **, LogContext *, Conf *, const char *, int, char **, bool, bool); static char *loop_init(const BackendVtable *, Seat *, Backend **, LogContext *, Conf *, const char *, int, char **, bool, bool); static void null_free(Backend *); static void loop_free(Backend *); static void null_reconfig(Backend *, Conf *); static size_t null_send(Backend *, const char *, size_t); static size_t loop_send(Backend *, const char *, size_t); static size_t null_sendbuffer(Backend *); static void null_size(Backend *, int, int); static void null_special(Backend *, SessionSpecialCode, int); static const SessionSpecial *null_get_specials(Backend *); static int null_connected(Backend *); static int null_exitcode(Backend *); static int null_sendok(Backend *); static int null_ldisc(Backend *, int); static void null_provide_ldisc(Backend *, Ldisc *); static void null_unthrottle(Backend *, size_t); static int null_cfg_info(Backend *); const BackendVtable null_backend = { .init = null_init, .free = null_free, .reconfig = null_reconfig, .send = null_send, .sendbuffer = null_sendbuffer, .size = null_size, .special = null_special, .get_specials = null_get_specials, .connected = null_connected, .exitcode = null_exitcode, .sendok = null_sendok, .ldisc_option_state = null_ldisc, .provide_ldisc = null_provide_ldisc, .unthrottle = null_unthrottle, .cfg_info = null_cfg_info, .id = "null", .displayname = "null", .protocol = -1, .default_port = 0, }; const BackendVtable loop_backend = { .init = loop_init, .free = loop_free, .reconfig = null_reconfig, .send = loop_send, .sendbuffer = null_sendbuffer, .size = null_size, .special = null_special, .get_specials = null_get_specials, .connected = null_connected, .exitcode = null_exitcode, .sendok = null_sendok, .ldisc_option_state = null_ldisc, .provide_ldisc = null_provide_ldisc, .unthrottle = null_unthrottle, .cfg_info = null_cfg_info, .id = "loop", .displayname = "loop", .protocol = -1, .default_port = 0, }; struct loop_state { Seat *seat; Backend backend; }; static char *null_init(const BackendVtable *vt, Seat *seat, Backend **backend_handle, LogContext *logctx, Conf *conf, const char *host, int port, char **realhost, bool nodelay, bool keepalive) { /* No local authentication phase in this protocol */ seat_set_trust_status(seat, false); *backend_handle = NULL; return NULL; } static char *loop_init(const BackendVtable *vt, Seat *seat, Backend **backend_handle, LogContext *logctx, Conf *conf, const char *host, int port, char **realhost, bool nodelay, bool keepalive) { struct loop_state *st = snew(struct loop_state); /* No local authentication phase in this protocol */ seat_set_trust_status(seat, false); st->seat = seat; *backend_handle = &st->backend; return NULL; } static void null_free(Backend *be) { } static void loop_free(Backend *be) { struct loop_state *st = container_of(be, struct loop_state, backend); sfree(st); } static void null_reconfig(Backend *be, Conf *conf) { } static size_t null_send(Backend *be, const char *buf, size_t len) { return 0; } static size_t loop_send(Backend *be, const char *buf, size_t len) { struct loop_state *st = container_of(be, struct loop_state, backend); return seat_output(st->seat, 0, buf, len); } static size_t null_sendbuffer(Backend *be) { return 0; } static void null_size(Backend *be, int width, int height) { } static void null_special(Backend *be, SessionSpecialCode code, int arg) { } static const SessionSpecial *null_get_specials (Backend *be) { return NULL; } static int null_connected(Backend *be) { return 0; } static int null_exitcode(Backend *be) { return 0; } static int null_sendok(Backend *be) { return 1; } static void null_unthrottle(Backend *be, size_t backlog) { } static int null_ldisc(Backend *be, int option) { return 0; } static void null_provide_ldisc (Backend *be, Ldisc *ldisc) { } static int null_cfg_info(Backend *be) { return 0; } /* * Emacs magic: * Local Variables: * c-file-style: "simon" * End: */ putty-0.76/testcrypt.c0000644000175000017500000014322214072266313011771 00000000000000/* * testcrypt: a standalone test program that provides direct access to * PuTTY's cryptography and mp_int code. */ /* * This program speaks a line-oriented protocol on standard input and * standard output. It's a half-duplex protocol: it expects to read * one line of command, and then produce a fixed amount of output * (namely a line containing a decimal integer, followed by that many * lines each containing one return value). * * The protocol is human-readable enough to make it debuggable, but * verbose enough that you probably wouldn't want to speak it by hand * at any great length. The Python program test/testcrypt.py wraps it * to give a more useful user-facing API, by invoking this binary as a * subprocess. * * (I decided that was a better idea than making this program an * actual Python module, partly because you can rewrap the same binary * in another scripting language if you prefer, but mostly because * it's easy to attach a debugger to testcrypt or to run it under * sanitisers or valgrind or what have you.) */ #include #include #include #include #include #include "defs.h" #include "ssh.h" #include "sshkeygen.h" #include "misc.h" #include "mpint.h" #include "ecc.h" static NORETURN PRINTF_LIKE(1, 2) void fatal_error(const char *p, ...) { va_list ap; fprintf(stderr, "testcrypt: "); va_start(ap, p); vfprintf(stderr, p, ap); va_end(ap); fputc('\n', stderr); exit(1); } void out_of_memory(void) { fatal_error("out of memory"); } /* For platforms where getticks is defined within this code base */ unsigned long (getticks)(void) { unreachable("this is a stub needed to link, and should never be called"); } FILE *f_open(const struct Filename *fn, char const *mode, bool private) { unreachable("f_open should never be called by this test program"); } static bool old_keyfile_warning_given; void old_keyfile_warning(void) { old_keyfile_warning_given = true; } static bufchain random_data_queue; static prng *test_prng; void random_read(void *buf, size_t size) { if (test_prng) { prng_read(test_prng, buf, size); } else { if (!bufchain_try_fetch_consume(&random_data_queue, buf, size)) fatal_error("No random data in queue"); } } uint64_t prng_reseed_time_ms(void) { static uint64_t previous_time = 0; return previous_time += 200; } #define VALUE_TYPES(X) \ X(string, strbuf *, strbuf_free(v)) \ X(mpint, mp_int *, mp_free(v)) \ X(modsqrt, ModsqrtContext *, modsqrt_free(v)) \ X(monty, MontyContext *, monty_free(v)) \ X(wcurve, WeierstrassCurve *, ecc_weierstrass_curve_free(v)) \ X(wpoint, WeierstrassPoint *, ecc_weierstrass_point_free(v)) \ X(mcurve, MontgomeryCurve *, ecc_montgomery_curve_free(v)) \ X(mpoint, MontgomeryPoint *, ecc_montgomery_point_free(v)) \ X(ecurve, EdwardsCurve *, ecc_edwards_curve_free(v)) \ X(epoint, EdwardsPoint *, ecc_edwards_point_free(v)) \ X(hash, ssh_hash *, ssh_hash_free(v)) \ X(key, ssh_key *, ssh_key_free(v)) \ X(cipher, ssh_cipher *, ssh_cipher_free(v)) \ X(mac, ssh2_mac *, ssh2_mac_free(v)) \ X(dh, dh_ctx *, dh_cleanup(v)) \ X(ecdh, ecdh_key *, ssh_ecdhkex_freekey(v)) \ X(rsakex, RSAKey *, ssh_rsakex_freekey(v)) \ X(rsa, RSAKey *, rsa_free(v)) \ X(prng, prng *, prng_free(v)) \ X(keycomponents, key_components *, key_components_free(v)) \ X(pcs, PrimeCandidateSource *, pcs_free(v)) \ X(pgc, PrimeGenerationContext *, primegen_free_context(v)) \ X(pockle, Pockle *, pockle_free(v)) \ /* end of list */ typedef struct Value Value; enum ValueType { #define VALTYPE_ENUM(n,t,f) VT_##n, VALUE_TYPES(VALTYPE_ENUM) #undef VALTYPE_ENUM }; typedef enum ValueType ValueType; static const char *const type_names[] = { #define VALTYPE_NAME(n,t,f) #n, VALUE_TYPES(VALTYPE_NAME) #undef VALTYPE_NAME }; #define VALTYPE_TYPEDEF(n,t,f) \ typedef t TD_val_##n; \ typedef t *TD_out_val_##n; VALUE_TYPES(VALTYPE_TYPEDEF) #undef VALTYPE_TYPEDEF struct Value { /* * Protocol identifier assigned to this value when it was created. * Lives in the same malloced block as this Value object itself. */ ptrlen id; /* * Type of the value. */ ValueType type; /* * Union of all the things it could hold. */ union { #define VALTYPE_UNION(n,t,f) t vu_##n; VALUE_TYPES(VALTYPE_UNION) #undef VALTYPE_UNION char *bare_string; }; }; static int valuecmp(void *av, void *bv) { Value *a = (Value *)av, *b = (Value *)bv; return ptrlen_strcmp(a->id, b->id); } static int valuefind(void *av, void *bv) { ptrlen *a = (ptrlen *)av; Value *b = (Value *)bv; return ptrlen_strcmp(*a, b->id); } static tree234 *values; static Value *value_new(ValueType vt) { static uint64_t next_index = 0; char *name = dupprintf("%s%"PRIu64, type_names[vt], next_index++); size_t namelen = strlen(name); Value *val = snew_plus(Value, namelen+1); memcpy(snew_plus_get_aux(val), name, namelen+1); val->id.ptr = snew_plus_get_aux(val); val->id.len = namelen; val->type = vt; Value *added = add234(values, val); assert(added == val); sfree(name); return val; } #define VALTYPE_RETURNFN(n,t,f) \ void return_val_##n(strbuf *out, t v) { \ Value *val = value_new(VT_##n); \ val->vu_##n = v; \ put_datapl(out, val->id); \ put_byte(out, '\n'); \ } VALUE_TYPES(VALTYPE_RETURNFN) #undef VALTYPE_RETURNFN static ptrlen get_word(BinarySource *in) { ptrlen toret; toret.ptr = get_ptr(in); toret.len = 0; while (get_avail(in) && get_byte(in) != ' ') toret.len++; return toret; } static const ssh_hashalg *get_hashalg(BinarySource *in) { static const struct { const char *key; const ssh_hashalg *value; } algs[] = { {"md5", &ssh_md5}, {"sha1", &ssh_sha1}, {"sha1_sw", &ssh_sha1_sw}, {"sha1_hw", &ssh_sha1_hw}, {"sha256", &ssh_sha256}, {"sha256_sw", &ssh_sha256_sw}, {"sha256_hw", &ssh_sha256_hw}, {"sha384", &ssh_sha384}, {"sha384_sw", &ssh_sha384_sw}, {"sha384_hw", &ssh_sha384_hw}, {"sha512", &ssh_sha512}, {"sha512_sw", &ssh_sha512_sw}, {"sha512_hw", &ssh_sha512_hw}, {"sha3_224", &ssh_sha3_224}, {"sha3_256", &ssh_sha3_256}, {"sha3_384", &ssh_sha3_384}, {"sha3_512", &ssh_sha3_512}, {"shake256_114bytes", &ssh_shake256_114bytes}, {"blake2b", &ssh_blake2b}, }; ptrlen name = get_word(in); for (size_t i = 0; i < lenof(algs); i++) if (ptrlen_eq_string(name, algs[i].key)) return algs[i].value; fatal_error("hashalg '%.*s': not found", PTRLEN_PRINTF(name)); } static const ssh2_macalg *get_macalg(BinarySource *in) { static const struct { const char *key; const ssh2_macalg *value; } algs[] = { {"hmac_md5", &ssh_hmac_md5}, {"hmac_sha1", &ssh_hmac_sha1}, {"hmac_sha1_buggy", &ssh_hmac_sha1_buggy}, {"hmac_sha1_96", &ssh_hmac_sha1_96}, {"hmac_sha1_96_buggy", &ssh_hmac_sha1_96_buggy}, {"hmac_sha256", &ssh_hmac_sha256}, {"poly1305", &ssh2_poly1305}, }; ptrlen name = get_word(in); for (size_t i = 0; i < lenof(algs); i++) if (ptrlen_eq_string(name, algs[i].key)) return algs[i].value; fatal_error("macalg '%.*s': not found", PTRLEN_PRINTF(name)); } static const ssh_keyalg *get_keyalg(BinarySource *in) { static const struct { const char *key; const ssh_keyalg *value; } algs[] = { {"dsa", &ssh_dss}, {"rsa", &ssh_rsa}, {"ed25519", &ssh_ecdsa_ed25519}, {"ed448", &ssh_ecdsa_ed448}, {"p256", &ssh_ecdsa_nistp256}, {"p384", &ssh_ecdsa_nistp384}, {"p521", &ssh_ecdsa_nistp521}, }; ptrlen name = get_word(in); for (size_t i = 0; i < lenof(algs); i++) if (ptrlen_eq_string(name, algs[i].key)) return algs[i].value; fatal_error("keyalg '%.*s': not found", PTRLEN_PRINTF(name)); } static const ssh_cipheralg *get_cipheralg(BinarySource *in) { static const struct { const char *key; const ssh_cipheralg *value; } algs[] = { {"3des_ctr", &ssh_3des_ssh2_ctr}, {"3des_ssh2", &ssh_3des_ssh2}, {"3des_ssh1", &ssh_3des_ssh1}, {"des_cbc", &ssh_des}, {"aes256_ctr", &ssh_aes256_sdctr}, {"aes256_ctr_hw", &ssh_aes256_sdctr_hw}, {"aes256_ctr_sw", &ssh_aes256_sdctr_sw}, {"aes256_cbc", &ssh_aes256_cbc}, {"aes256_cbc_hw", &ssh_aes256_cbc_hw}, {"aes256_cbc_sw", &ssh_aes256_cbc_sw}, {"aes192_ctr", &ssh_aes192_sdctr}, {"aes192_ctr_hw", &ssh_aes192_sdctr_hw}, {"aes192_ctr_sw", &ssh_aes192_sdctr_sw}, {"aes192_cbc", &ssh_aes192_cbc}, {"aes192_cbc_hw", &ssh_aes192_cbc_hw}, {"aes192_cbc_sw", &ssh_aes192_cbc_sw}, {"aes128_ctr", &ssh_aes128_sdctr}, {"aes128_ctr_hw", &ssh_aes128_sdctr_hw}, {"aes128_ctr_sw", &ssh_aes128_sdctr_sw}, {"aes128_cbc", &ssh_aes128_cbc}, {"aes128_cbc_hw", &ssh_aes128_cbc_hw}, {"aes128_cbc_sw", &ssh_aes128_cbc_sw}, {"blowfish_ctr", &ssh_blowfish_ssh2_ctr}, {"blowfish_ssh2", &ssh_blowfish_ssh2}, {"blowfish_ssh1", &ssh_blowfish_ssh1}, {"arcfour256", &ssh_arcfour256_ssh2}, {"arcfour128", &ssh_arcfour128_ssh2}, {"chacha20_poly1305", &ssh2_chacha20_poly1305}, }; ptrlen name = get_word(in); for (size_t i = 0; i < lenof(algs); i++) if (ptrlen_eq_string(name, algs[i].key)) return algs[i].value; fatal_error("cipheralg '%.*s': not found", PTRLEN_PRINTF(name)); } static const ssh_kex *get_dh_group(BinarySource *in) { static const struct { const char *key; const ssh_kexes *value; } algs[] = { {"group1", &ssh_diffiehellman_group1}, {"group14", &ssh_diffiehellman_group14}, }; ptrlen name = get_word(in); for (size_t i = 0; i < lenof(algs); i++) if (ptrlen_eq_string(name, algs[i].key)) return algs[i].value->list[0]; fatal_error("dh_group '%.*s': not found", PTRLEN_PRINTF(name)); } static const ssh_kex *get_ecdh_alg(BinarySource *in) { static const struct { const char *key; const ssh_kex *value; } algs[] = { {"curve25519", &ssh_ec_kex_curve25519}, {"curve448", &ssh_ec_kex_curve448}, {"nistp256", &ssh_ec_kex_nistp256}, {"nistp384", &ssh_ec_kex_nistp384}, {"nistp521", &ssh_ec_kex_nistp521}, }; ptrlen name = get_word(in); for (size_t i = 0; i < lenof(algs); i++) if (ptrlen_eq_string(name, algs[i].key)) return algs[i].value; fatal_error("ecdh_alg '%.*s': not found", PTRLEN_PRINTF(name)); } static RsaSsh1Order get_rsaorder(BinarySource *in) { static const struct { const char *key; RsaSsh1Order value; } orders[] = { {"exponent_first", RSA_SSH1_EXPONENT_FIRST}, {"modulus_first", RSA_SSH1_MODULUS_FIRST}, }; ptrlen name = get_word(in); for (size_t i = 0; i < lenof(orders); i++) if (ptrlen_eq_string(name, orders[i].key)) return orders[i].value; fatal_error("rsaorder '%.*s': not found", PTRLEN_PRINTF(name)); } static const PrimeGenerationPolicy *get_primegenpolicy(BinarySource *in) { static const struct { const char *key; const PrimeGenerationPolicy *value; } algs[] = { {"probabilistic", &primegen_probabilistic}, {"provable_fast", &primegen_provable_fast}, {"provable_maurer_simple", &primegen_provable_maurer_simple}, {"provable_maurer_complex", &primegen_provable_maurer_complex}, }; ptrlen name = get_word(in); for (size_t i = 0; i < lenof(algs); i++) if (ptrlen_eq_string(name, algs[i].key)) return algs[i].value; fatal_error("primegenpolicy '%.*s': not found", PTRLEN_PRINTF(name)); } static Argon2Flavour get_argon2flavour(BinarySource *in) { static const struct { const char *key; Argon2Flavour value; } algs[] = { {"d", Argon2d}, {"i", Argon2i}, {"id", Argon2id}, /* I expect to forget which spelling I chose, so let's support many */ {"argon2d", Argon2d}, {"argon2i", Argon2i}, {"argon2id", Argon2id}, {"Argon2d", Argon2d}, {"Argon2i", Argon2i}, {"Argon2id", Argon2id}, }; ptrlen name = get_word(in); for (size_t i = 0; i < lenof(algs); i++) if (ptrlen_eq_string(name, algs[i].key)) return algs[i].value; fatal_error("Argon2 flavour '%.*s': not found", PTRLEN_PRINTF(name)); } static FingerprintType get_fptype(BinarySource *in) { static const struct { const char *key; FingerprintType value; } ids[] = { {"md5", SSH_FPTYPE_MD5}, {"sha256", SSH_FPTYPE_SHA256}, }; ptrlen name = get_word(in); for (size_t i = 0; i < lenof(ids); i++) if (ptrlen_eq_string(name, ids[i].key)) return ids[i].value; fatal_error("fingerprint type '%.*s': not found", PTRLEN_PRINTF(name)); } static uintmax_t get_uint(BinarySource *in) { ptrlen word = get_word(in); char *string = mkstr(word); uintmax_t toret = strtoumax(string, NULL, 0); sfree(string); return toret; } static bool get_boolean(BinarySource *in) { return ptrlen_eq_string(get_word(in), "true"); } static Value *lookup_value(ptrlen word) { Value *val = find234(values, &word, valuefind); if (!val) fatal_error("id '%.*s': not found", PTRLEN_PRINTF(word)); return val; } static Value *get_value(BinarySource *in) { return lookup_value(get_word(in)); } typedef void (*finaliser_fn_t)(strbuf *out, void *ctx); struct finaliser { finaliser_fn_t fn; void *ctx; }; static struct finaliser *finalisers; static size_t nfinalisers, finalisersize; static void add_finaliser(finaliser_fn_t fn, void *ctx) { sgrowarray(finalisers, finalisersize, nfinalisers); finalisers[nfinalisers].fn = fn; finalisers[nfinalisers].ctx = ctx; nfinalisers++; } static void run_finalisers(strbuf *out) { for (size_t i = 0; i < nfinalisers; i++) finalisers[i].fn(out, finalisers[i].ctx); nfinalisers = 0; } static void finaliser_return_value(strbuf *out, void *ctx) { Value *val = (Value *)ctx; put_datapl(out, val->id); put_byte(out, '\n'); } static void finaliser_sfree(strbuf *out, void *ctx) { sfree(ctx); } #define VALTYPE_GETFN(n,t,f) \ static Value *unwrap_value_##n(Value *val) { \ ValueType expected = VT_##n; \ if (expected != val->type) \ fatal_error("id '%.*s': expected %s, got %s", \ PTRLEN_PRINTF(val->id), \ type_names[expected], type_names[val->type]); \ return val; \ } \ static Value *get_value_##n(BinarySource *in) { \ return unwrap_value_##n(get_value(in)); \ } \ static t get_val_##n(BinarySource *in) { \ return get_value_##n(in)->vu_##n; \ } VALUE_TYPES(VALTYPE_GETFN) #undef VALTYPE_GETFN static ptrlen get_val_string_ptrlen(BinarySource *in) { return ptrlen_from_strbuf(get_val_string(in)); } static char *get_val_string_asciz(BinarySource *in) { return get_val_string(in)->s; } static strbuf *get_opt_val_string(BinarySource *in); static char *get_opt_val_string_asciz(BinarySource *in) { strbuf *sb = get_opt_val_string(in); return sb ? sb->s : NULL; } static mp_int **get_out_val_mpint(BinarySource *in) { Value *val = value_new(VT_mpint); add_finaliser(finaliser_return_value, val); return &val->vu_mpint; } struct mpint_list { size_t n; mp_int **integers; }; static struct mpint_list get_mpint_list(BinarySource *in) { size_t n = get_uint(in); struct mpint_list mpl; mpl.n = n; mpl.integers = snewn(n, mp_int *); for (size_t i = 0; i < n; i++) mpl.integers[i] = get_val_mpint(in); add_finaliser(finaliser_sfree, mpl.integers); return mpl; } static void finaliser_return_uint(strbuf *out, void *ctx) { unsigned *uval = (unsigned *)ctx; strbuf_catf(out, "%u\n", *uval); sfree(uval); } static unsigned *get_out_uint(BinarySource *in) { unsigned *uval = snew(unsigned); add_finaliser(finaliser_return_uint, uval); return uval; } static BinarySink *get_out_val_string_binarysink(BinarySource *in) { Value *val = value_new(VT_string); val->vu_string = strbuf_new(); add_finaliser(finaliser_return_value, val); return BinarySink_UPCAST(val->vu_string); } static void return_val_string_asciz_const(strbuf *out, const char *s); static void return_val_string_asciz(strbuf *out, char *s); static void finaliser_return_opt_string_asciz(strbuf *out, void *ctx) { char **valp = (char **)ctx; char *val = *valp; sfree(valp); if (!val) strbuf_catf(out, "NULL\n"); else return_val_string_asciz(out, val); } static char **get_out_opt_val_string_asciz(BinarySource *in) { char **valp = snew(char *); *valp = NULL; add_finaliser(finaliser_return_opt_string_asciz, valp); return valp; } static void finaliser_return_opt_string_asciz_const(strbuf *out, void *ctx) { const char **valp = (const char **)ctx; const char *val = *valp; sfree(valp); if (!val) strbuf_catf(out, "NULL\n"); else return_val_string_asciz_const(out, val); } static const char **get_out_opt_val_string_asciz_const(BinarySource *in) { const char **valp = snew(const char *); *valp = NULL; add_finaliser(finaliser_return_opt_string_asciz_const, valp); return valp; } static BinarySource *get_val_string_binarysource(BinarySource *in) { strbuf *sb = get_val_string(in); BinarySource *src = snew(BinarySource); BinarySource_BARE_INIT(src, sb->u, sb->len); add_finaliser(finaliser_sfree, src); return src; } #define GET_CONSUMED_FN(type) \ typedef TD_val_##type TD_consumed_val_##type; \ static TD_val_##type get_consumed_val_##type(BinarySource *in) \ { \ Value *val = get_value_##type(in); \ TD_val_##type toret = val->vu_##type; \ del234(values, val); \ sfree(val); \ return toret; \ } GET_CONSUMED_FN(hash) GET_CONSUMED_FN(pcs) static void return_int(strbuf *out, intmax_t u) { strbuf_catf(out, "%"PRIdMAX"\n", u); } static void return_uint(strbuf *out, uintmax_t u) { strbuf_catf(out, "0x%"PRIXMAX"\n", u); } static void return_boolean(strbuf *out, bool b) { strbuf_catf(out, "%s\n", b ? "true" : "false"); } static void return_pocklestatus(strbuf *out, PockleStatus status) { switch (status) { default: strbuf_catf(out, "POCKLE_BAD_STATUS_VALUE\n"); break; #define STATUS_CASE(id) \ case id: \ strbuf_catf(out, "%s\n", #id); \ break; POCKLE_STATUSES(STATUS_CASE); #undef STATUS_CASE } } static void return_val_string_asciz_const(strbuf *out, const char *s) { strbuf *sb = strbuf_new(); put_data(sb, s, strlen(s)); return_val_string(out, sb); } static void return_val_string_asciz(strbuf *out, char *s) { return_val_string_asciz_const(out, s); sfree(s); } #define NULLABLE_RETURN_WRAPPER(type_name, c_type) \ static void return_opt_##type_name(strbuf *out, c_type ptr) \ { \ if (!ptr) \ strbuf_catf(out, "NULL\n"); \ else \ return_##type_name(out, ptr); \ } NULLABLE_RETURN_WRAPPER(val_string, strbuf *) NULLABLE_RETURN_WRAPPER(val_string_asciz, char *) NULLABLE_RETURN_WRAPPER(val_string_asciz_const, const char *) NULLABLE_RETURN_WRAPPER(val_cipher, ssh_cipher *) NULLABLE_RETURN_WRAPPER(val_hash, ssh_hash *) NULLABLE_RETURN_WRAPPER(val_key, ssh_key *) NULLABLE_RETURN_WRAPPER(val_mpint, mp_int *) static void handle_hello(BinarySource *in, strbuf *out) { strbuf_catf(out, "hello, world\n"); } static void rsa_free(RSAKey *rsa) { freersakey(rsa); sfree(rsa); } static void free_value(Value *val) { switch (val->type) { #define VALTYPE_FREE(n,t,f) case VT_##n: { t v = val->vu_##n; (f); break; } VALUE_TYPES(VALTYPE_FREE) #undef VALTYPE_FREE } sfree(val); } static void handle_free(BinarySource *in, strbuf *out) { Value *val = get_value(in); del234(values, val); free_value(val); } static void handle_newstring(BinarySource *in, strbuf *out) { strbuf *sb = strbuf_new(); while (get_avail(in)) { char c = get_byte(in); if (c == '%') { char hex[3]; hex[0] = get_byte(in); if (hex[0] != '%') { hex[1] = get_byte(in); hex[2] = '\0'; c = strtoul(hex, NULL, 16); } } put_byte(sb, c); } return_val_string(out, sb); } static void handle_getstring(BinarySource *in, strbuf *out) { strbuf *sb = get_val_string(in); for (size_t i = 0; i < sb->len; i++) { char c = sb->s[i]; if (c > ' ' && c < 0x7F && c != '%') { put_byte(out, c); } else { strbuf_catf(out, "%%%02X", 0xFFU & (unsigned)c); } } put_byte(out, '\n'); } static void handle_mp_literal(BinarySource *in, strbuf *out) { ptrlen pl = get_word(in); char *str = mkstr(pl); mp_int *mp = mp__from_string_literal(str); sfree(str); return_val_mpint(out, mp); } static void handle_mp_dump(BinarySource *in, strbuf *out) { mp_int *mp = get_val_mpint(in); for (size_t i = mp_max_bytes(mp); i-- > 0 ;) strbuf_catf(out, "%02X", mp_get_byte(mp, i)); put_byte(out, '\n'); } static void random_queue(ptrlen pl) { bufchain_add(&random_data_queue, pl.ptr, pl.len); } static size_t random_queue_len(void) { return bufchain_size(&random_data_queue); } static void random_clear(void) { if (test_prng) { prng_free(test_prng); test_prng = NULL; } bufchain_clear(&random_data_queue); } static void random_make_prng(const ssh_hashalg *hashalg, ptrlen seed) { random_clear(); test_prng = prng_new(hashalg); prng_seed_begin(test_prng); put_datapl(test_prng, seed); prng_seed_finish(test_prng); } mp_int *monty_identity_wrapper(MontyContext *mc) { return mp_copy(monty_identity(mc)); } #define monty_identity monty_identity_wrapper mp_int *monty_modulus_wrapper(MontyContext *mc) { return mp_copy(monty_modulus(mc)); } #define monty_modulus monty_modulus_wrapper strbuf *ssh_hash_digest_wrapper(ssh_hash *h) { strbuf *sb = strbuf_new(); void *p = strbuf_append(sb, ssh_hash_alg(h)->hlen); ssh_hash_digest(h, p); return sb; } #undef ssh_hash_digest #define ssh_hash_digest ssh_hash_digest_wrapper strbuf *ssh_hash_final_wrapper(ssh_hash *h) { strbuf *sb = strbuf_new(); void *p = strbuf_append(sb, ssh_hash_alg(h)->hlen); ssh_hash_final(h, p); return sb; } #undef ssh_hash_final #define ssh_hash_final ssh_hash_final_wrapper void ssh_cipher_setiv_wrapper(ssh_cipher *c, ptrlen key) { if (key.len != ssh_cipher_alg(c)->blksize) fatal_error("ssh_cipher_setiv: needs exactly %d bytes", ssh_cipher_alg(c)->blksize); ssh_cipher_setiv(c, key.ptr); } #undef ssh_cipher_setiv #define ssh_cipher_setiv ssh_cipher_setiv_wrapper void ssh_cipher_setkey_wrapper(ssh_cipher *c, ptrlen key) { if (key.len != ssh_cipher_alg(c)->padded_keybytes) fatal_error("ssh_cipher_setkey: needs exactly %d bytes", ssh_cipher_alg(c)->padded_keybytes); ssh_cipher_setkey(c, key.ptr); } #undef ssh_cipher_setkey #define ssh_cipher_setkey ssh_cipher_setkey_wrapper strbuf *ssh_cipher_encrypt_wrapper(ssh_cipher *c, ptrlen input) { if (input.len % ssh_cipher_alg(c)->blksize) fatal_error("ssh_cipher_encrypt: needs a multiple of %d bytes", ssh_cipher_alg(c)->blksize); strbuf *sb = strbuf_new(); put_datapl(sb, input); ssh_cipher_encrypt(c, sb->u, sb->len); return sb; } #undef ssh_cipher_encrypt #define ssh_cipher_encrypt ssh_cipher_encrypt_wrapper strbuf *ssh_cipher_decrypt_wrapper(ssh_cipher *c, ptrlen input) { if (input.len % ssh_cipher_alg(c)->blksize) fatal_error("ssh_cipher_decrypt: needs a multiple of %d bytes", ssh_cipher_alg(c)->blksize); strbuf *sb = strbuf_new(); put_datapl(sb, input); ssh_cipher_decrypt(c, sb->u, sb->len); return sb; } #undef ssh_cipher_decrypt #define ssh_cipher_decrypt ssh_cipher_decrypt_wrapper strbuf *ssh_cipher_encrypt_length_wrapper(ssh_cipher *c, ptrlen input, unsigned long seq) { if (input.len != 4) fatal_error("ssh_cipher_encrypt_length: needs exactly 4 bytes"); strbuf *sb = strbuf_new(); put_datapl(sb, input); ssh_cipher_encrypt_length(c, sb->u, sb->len, seq); return sb; } #undef ssh_cipher_encrypt_length #define ssh_cipher_encrypt_length ssh_cipher_encrypt_length_wrapper strbuf *ssh_cipher_decrypt_length_wrapper(ssh_cipher *c, ptrlen input, unsigned long seq) { if (input.len % ssh_cipher_alg(c)->blksize) fatal_error("ssh_cipher_decrypt_length: needs exactly 4 bytes"); strbuf *sb = strbuf_new(); put_datapl(sb, input); ssh_cipher_decrypt_length(c, sb->u, sb->len, seq); return sb; } #undef ssh_cipher_decrypt_length #define ssh_cipher_decrypt_length ssh_cipher_decrypt_length_wrapper strbuf *ssh2_mac_genresult_wrapper(ssh2_mac *m) { strbuf *sb = strbuf_new(); void *u = strbuf_append(sb, ssh2_mac_alg(m)->len); ssh2_mac_genresult(m, u); return sb; } #undef ssh2_mac_genresult #define ssh2_mac_genresult ssh2_mac_genresult_wrapper bool dh_validate_f_wrapper(dh_ctx *dh, mp_int *f) { return dh_validate_f(dh, f) == NULL; } #define dh_validate_f dh_validate_f_wrapper void ssh_hash_update(ssh_hash *h, ptrlen pl) { put_datapl(h, pl); } void ssh2_mac_update(ssh2_mac *m, ptrlen pl) { put_datapl(m, pl); } static RSAKey *rsa_new(void) { RSAKey *rsa = snew(RSAKey); memset(rsa, 0, sizeof(RSAKey)); return rsa; } strbuf *rsa_ssh1_encrypt_wrapper(ptrlen input, RSAKey *key) { /* Fold the boolean return value in C into the string return value * for this purpose, by returning NULL on failure */ strbuf *sb = strbuf_new(); put_datapl(sb, input); put_padding(sb, key->bytes - input.len, 0); if (!rsa_ssh1_encrypt(sb->u, input.len, key)) { strbuf_free(sb); return NULL; } return sb; } #define rsa_ssh1_encrypt rsa_ssh1_encrypt_wrapper strbuf *rsa_ssh1_decrypt_pkcs1_wrapper(mp_int *input, RSAKey *key) { /* Again, return "" on failure */ strbuf *sb = strbuf_new(); if (!rsa_ssh1_decrypt_pkcs1(input, key, sb)) strbuf_clear(sb); return sb; } #define rsa_ssh1_decrypt_pkcs1 rsa_ssh1_decrypt_pkcs1_wrapper strbuf *des_encrypt_xdmauth_wrapper(ptrlen key, ptrlen data) { if (key.len != 7) fatal_error("des_encrypt_xdmauth: key must be 7 bytes long"); if (data.len % 8 != 0) fatal_error("des_encrypt_xdmauth: data must be a multiple of 8 bytes"); strbuf *sb = strbuf_new(); put_datapl(sb, data); des_encrypt_xdmauth(key.ptr, sb->u, sb->len); return sb; } #define des_encrypt_xdmauth des_encrypt_xdmauth_wrapper strbuf *des_decrypt_xdmauth_wrapper(ptrlen key, ptrlen data) { if (key.len != 7) fatal_error("des_decrypt_xdmauth: key must be 7 bytes long"); if (data.len % 8 != 0) fatal_error("des_decrypt_xdmauth: data must be a multiple of 8 bytes"); strbuf *sb = strbuf_new(); put_datapl(sb, data); des_decrypt_xdmauth(key.ptr, sb->u, sb->len); return sb; } #define des_decrypt_xdmauth des_decrypt_xdmauth_wrapper strbuf *des3_encrypt_pubkey_wrapper(ptrlen key, ptrlen data) { if (key.len != 16) fatal_error("des3_encrypt_pubkey: key must be 16 bytes long"); if (data.len % 8 != 0) fatal_error("des3_encrypt_pubkey: data must be a multiple of 8 bytes"); strbuf *sb = strbuf_new(); put_datapl(sb, data); des3_encrypt_pubkey(key.ptr, sb->u, sb->len); return sb; } #define des3_encrypt_pubkey des3_encrypt_pubkey_wrapper strbuf *des3_decrypt_pubkey_wrapper(ptrlen key, ptrlen data) { if (key.len != 16) fatal_error("des3_decrypt_pubkey: key must be 16 bytes long"); if (data.len % 8 != 0) fatal_error("des3_decrypt_pubkey: data must be a multiple of 8 bytes"); strbuf *sb = strbuf_new(); put_datapl(sb, data); des3_decrypt_pubkey(key.ptr, sb->u, sb->len); return sb; } #define des3_decrypt_pubkey des3_decrypt_pubkey_wrapper strbuf *des3_encrypt_pubkey_ossh_wrapper(ptrlen key, ptrlen iv, ptrlen data) { if (key.len != 24) fatal_error("des3_encrypt_pubkey_ossh: key must be 24 bytes long"); if (iv.len != 8) fatal_error("des3_encrypt_pubkey_ossh: iv must be 8 bytes long"); if (data.len % 8 != 0) fatal_error("des3_encrypt_pubkey_ossh: data must be a multiple of 8 bytes"); strbuf *sb = strbuf_new(); put_datapl(sb, data); des3_encrypt_pubkey_ossh(key.ptr, iv.ptr, sb->u, sb->len); return sb; } #define des3_encrypt_pubkey_ossh des3_encrypt_pubkey_ossh_wrapper strbuf *des3_decrypt_pubkey_ossh_wrapper(ptrlen key, ptrlen iv, ptrlen data) { if (key.len != 24) fatal_error("des3_decrypt_pubkey_ossh: key must be 24 bytes long"); if (iv.len != 8) fatal_error("des3_encrypt_pubkey_ossh: iv must be 8 bytes long"); if (data.len % 8 != 0) fatal_error("des3_decrypt_pubkey_ossh: data must be a multiple of 8 bytes"); strbuf *sb = strbuf_new(); put_datapl(sb, data); des3_decrypt_pubkey_ossh(key.ptr, iv.ptr, sb->u, sb->len); return sb; } #define des3_decrypt_pubkey_ossh des3_decrypt_pubkey_ossh_wrapper strbuf *aes256_encrypt_pubkey_wrapper(ptrlen key, ptrlen iv, ptrlen data) { if (key.len != 32) fatal_error("aes256_encrypt_pubkey: key must be 32 bytes long"); if (iv.len != 16) fatal_error("aes256_encrypt_pubkey: iv must be 16 bytes long"); if (data.len % 16 != 0) fatal_error("aes256_encrypt_pubkey: data must be a multiple of 16 bytes"); strbuf *sb = strbuf_new(); put_datapl(sb, data); aes256_encrypt_pubkey(key.ptr, iv.ptr, sb->u, sb->len); return sb; } #define aes256_encrypt_pubkey aes256_encrypt_pubkey_wrapper strbuf *aes256_decrypt_pubkey_wrapper(ptrlen key, ptrlen iv, ptrlen data) { if (key.len != 32) fatal_error("aes256_decrypt_pubkey: key must be 32 bytes long"); if (iv.len != 16) fatal_error("aes256_encrypt_pubkey: iv must be 16 bytes long"); if (data.len % 16 != 0) fatal_error("aes256_decrypt_pubkey: data must be a multiple of 16 bytes"); strbuf *sb = strbuf_new(); put_datapl(sb, data); aes256_decrypt_pubkey(key.ptr, iv.ptr, sb->u, sb->len); return sb; } #define aes256_decrypt_pubkey aes256_decrypt_pubkey_wrapper strbuf *prng_read_wrapper(prng *pr, size_t size) { strbuf *sb = strbuf_new(); prng_read(pr, strbuf_append(sb, size), size); return sb; } #define prng_read prng_read_wrapper void prng_seed_update(prng *pr, ptrlen data) { put_datapl(pr, data); } bool crcda_detect(ptrlen packet, ptrlen iv) { if (iv.len != 0 && iv.len != 8) fatal_error("crcda_detect: iv must be empty or 8 bytes long"); if (packet.len % 8 != 0) fatal_error("crcda_detect: packet must be a multiple of 8 bytes"); struct crcda_ctx *ctx = crcda_make_context(); bool toret = detect_attack(ctx, packet.ptr, packet.len, iv.len ? iv.ptr : NULL); crcda_free_context(ctx); return toret; } ssh_key *ppk_load_s_wrapper(BinarySource *src, char **comment, const char *passphrase, const char **errorstr) { ssh2_userkey *uk = ppk_load_s(src, passphrase, errorstr); if (uk == SSH2_WRONG_PASSPHRASE) { /* Fudge this special return value */ *errorstr = "SSH2_WRONG_PASSPHRASE"; return NULL; } if (uk == NULL) return NULL; ssh_key *toret = uk->key; *comment = uk->comment; sfree(uk); return toret; } #define ppk_load_s ppk_load_s_wrapper int rsa1_load_s_wrapper(BinarySource *src, RSAKey *rsa, char **comment, const char *passphrase, const char **errorstr) { int toret = rsa1_load_s(src, rsa, passphrase, errorstr); *comment = rsa->comment; rsa->comment = NULL; return toret; } #define rsa1_load_s rsa1_load_s_wrapper strbuf *ppk_save_sb_wrapper( ssh_key *key, const char *comment, const char *passphrase, unsigned fmt_version, Argon2Flavour flavour, uint32_t mem, uint32_t passes, uint32_t parallel) { /* * For repeatable testing purposes, we never want a timing-dependent * choice of password hashing parameters, so this is easy. */ ppk_save_parameters save_params; memset(&save_params, 0, sizeof(save_params)); save_params.fmt_version = fmt_version; save_params.argon2_flavour = flavour; save_params.argon2_mem = mem; save_params.argon2_passes_auto = false; save_params.argon2_passes = passes; save_params.argon2_parallelism = parallel; ssh2_userkey uk; uk.key = key; uk.comment = dupstr(comment); strbuf *toret = ppk_save_sb(&uk, passphrase, &save_params); sfree(uk.comment); return toret; } #define ppk_save_sb ppk_save_sb_wrapper strbuf *rsa1_save_sb_wrapper(RSAKey *key, const char *comment, const char *passphrase) { key->comment = dupstr(comment); strbuf *toret = rsa1_save_sb(key, passphrase); sfree(key->comment); key->comment = NULL; return toret; } #define rsa1_save_sb rsa1_save_sb_wrapper #define return_void(out, expression) (expression) static ProgressReceiver null_progress = { .vt = &null_progress_vt }; mp_int *primegen_generate_wrapper( PrimeGenerationContext *ctx, PrimeCandidateSource *pcs) { return primegen_generate(ctx, pcs, &null_progress); } #define primegen_generate primegen_generate_wrapper RSAKey *rsa1_generate(int bits, bool strong, PrimeGenerationContext *pgc) { RSAKey *rsakey = snew(RSAKey); rsa_generate(rsakey, bits, strong, pgc, &null_progress); rsakey->comment = NULL; return rsakey; } ssh_key *rsa_generate_wrapper(int bits, bool strong, PrimeGenerationContext *pgc) { return &rsa1_generate(bits, strong, pgc)->sshk; } #define rsa_generate rsa_generate_wrapper ssh_key *dsa_generate_wrapper(int bits, PrimeGenerationContext *pgc) { struct dss_key *dsskey = snew(struct dss_key); dsa_generate(dsskey, bits, pgc, &null_progress); return &dsskey->sshk; } #define dsa_generate dsa_generate_wrapper ssh_key *ecdsa_generate_wrapper(int bits) { struct ecdsa_key *ek = snew(struct ecdsa_key); if (!ecdsa_generate(ek, bits)) { sfree(ek); return NULL; } return &ek->sshk; } #define ecdsa_generate ecdsa_generate_wrapper ssh_key *eddsa_generate_wrapper(int bits) { struct eddsa_key *ek = snew(struct eddsa_key); if (!eddsa_generate(ek, bits)) { sfree(ek); return NULL; } return &ek->sshk; } #define eddsa_generate eddsa_generate_wrapper size_t key_components_count(key_components *kc) { return kc->ncomponents; } const char *key_components_nth_name(key_components *kc, size_t n) { return (n >= kc->ncomponents ? NULL : kc->components[n].name); } const char *key_components_nth_str(key_components *kc, size_t n) { return (n >= kc->ncomponents ? NULL : kc->components[n].is_mp_int ? NULL : kc->components[n].text); } mp_int *key_components_nth_mp(key_components *kc, size_t n) { return (n >= kc->ncomponents ? NULL : !kc->components[n].is_mp_int ? NULL : mp_copy(kc->components[n].mp)); } PockleStatus pockle_add_prime_wrapper(Pockle *pockle, mp_int *p, struct mpint_list mpl, mp_int *witness) { return pockle_add_prime(pockle, p, mpl.integers, mpl.n, witness); } #define pockle_add_prime pockle_add_prime_wrapper strbuf *argon2_wrapper(Argon2Flavour flavour, uint32_t mem, uint32_t passes, uint32_t parallel, uint32_t taglen, ptrlen P, ptrlen S, ptrlen K, ptrlen X) { strbuf *out = strbuf_new(); argon2(flavour, mem, passes, parallel, taglen, P, S, K, X, out); return out; } #define argon2 argon2_wrapper #define OPTIONAL_PTR_FUNC(type) \ typedef TD_val_##type TD_opt_val_##type; \ static TD_opt_val_##type get_opt_val_##type(BinarySource *in) { \ ptrlen word = get_word(in); \ if (ptrlen_eq_string(word, "NULL")) \ return NULL; \ return unwrap_value_##type(lookup_value(word))->vu_##type; \ } OPTIONAL_PTR_FUNC(cipher) OPTIONAL_PTR_FUNC(mpint) OPTIONAL_PTR_FUNC(string) typedef uintmax_t TD_uint; typedef bool TD_boolean; typedef ptrlen TD_val_string_ptrlen; typedef char *TD_val_string_asciz; typedef BinarySource *TD_val_string_binarysource; typedef unsigned *TD_out_uint; typedef BinarySink *TD_out_val_string_binarysink; typedef const char *TD_opt_val_string_asciz; typedef char **TD_out_val_string_asciz; typedef char **TD_out_opt_val_string_asciz; typedef const char **TD_out_opt_val_string_asciz_const; typedef ssh_hash *TD_consumed_val_hash; typedef const ssh_hashalg *TD_hashalg; typedef const ssh2_macalg *TD_macalg; typedef const ssh_keyalg *TD_keyalg; typedef const ssh_cipheralg *TD_cipheralg; typedef const ssh_kex *TD_dh_group; typedef const ssh_kex *TD_ecdh_alg; typedef RsaSsh1Order TD_rsaorder; typedef key_components *TD_keycomponents; typedef const PrimeGenerationPolicy *TD_primegenpolicy; typedef struct mpint_list TD_mpint_list; typedef PockleStatus TD_pocklestatus; typedef Argon2Flavour TD_argon2flavour; typedef FingerprintType TD_fptype; #define FUNC0(rettype, function) \ static void handle_##function(BinarySource *in, strbuf *out) { \ return_##rettype(out, function()); \ } #define FUNC1(rettype, function, type1) \ static void handle_##function(BinarySource *in, strbuf *out) { \ TD_##type1 arg1 = get_##type1(in); \ return_##rettype(out, function(arg1)); \ } #define FUNC2(rettype, function, type1, type2) \ static void handle_##function(BinarySource *in, strbuf *out) { \ TD_##type1 arg1 = get_##type1(in); \ TD_##type2 arg2 = get_##type2(in); \ return_##rettype(out, function(arg1, arg2)); \ } #define FUNC3(rettype, function, type1, type2, type3) \ static void handle_##function(BinarySource *in, strbuf *out) { \ TD_##type1 arg1 = get_##type1(in); \ TD_##type2 arg2 = get_##type2(in); \ TD_##type3 arg3 = get_##type3(in); \ return_##rettype(out, function(arg1, arg2, arg3)); \ } #define FUNC4(rettype, function, type1, type2, type3, type4) \ static void handle_##function(BinarySource *in, strbuf *out) { \ TD_##type1 arg1 = get_##type1(in); \ TD_##type2 arg2 = get_##type2(in); \ TD_##type3 arg3 = get_##type3(in); \ TD_##type4 arg4 = get_##type4(in); \ return_##rettype(out, function(arg1, arg2, arg3, arg4)); \ } #define FUNC5(rettype, function, type1, type2, type3, type4, type5) \ static void handle_##function(BinarySource *in, strbuf *out) { \ TD_##type1 arg1 = get_##type1(in); \ TD_##type2 arg2 = get_##type2(in); \ TD_##type3 arg3 = get_##type3(in); \ TD_##type4 arg4 = get_##type4(in); \ TD_##type5 arg5 = get_##type5(in); \ return_##rettype(out, function(arg1, arg2, arg3, arg4, arg5)); \ } #define FUNC6(rettype, function, type1, type2, type3, type4, type5, \ type6) \ static void handle_##function(BinarySource *in, strbuf *out) { \ TD_##type1 arg1 = get_##type1(in); \ TD_##type2 arg2 = get_##type2(in); \ TD_##type3 arg3 = get_##type3(in); \ TD_##type4 arg4 = get_##type4(in); \ TD_##type5 arg5 = get_##type5(in); \ TD_##type6 arg6 = get_##type6(in); \ return_##rettype(out, function(arg1, arg2, arg3, arg4, arg5, \ arg6)); \ } #define FUNC7(rettype, function, type1, type2, type3, type4, type5, \ type6, type7) \ static void handle_##function(BinarySource *in, strbuf *out) { \ TD_##type1 arg1 = get_##type1(in); \ TD_##type2 arg2 = get_##type2(in); \ TD_##type3 arg3 = get_##type3(in); \ TD_##type4 arg4 = get_##type4(in); \ TD_##type5 arg5 = get_##type5(in); \ TD_##type6 arg6 = get_##type6(in); \ TD_##type7 arg7 = get_##type7(in); \ return_##rettype(out, function(arg1, arg2, arg3, arg4, arg5, \ arg6, arg7)); \ } #define FUNC8(rettype, function, type1, type2, type3, type4, type5, \ type6, type7, type8) \ static void handle_##function(BinarySource *in, strbuf *out) { \ TD_##type1 arg1 = get_##type1(in); \ TD_##type2 arg2 = get_##type2(in); \ TD_##type3 arg3 = get_##type3(in); \ TD_##type4 arg4 = get_##type4(in); \ TD_##type5 arg5 = get_##type5(in); \ TD_##type6 arg6 = get_##type6(in); \ TD_##type7 arg7 = get_##type7(in); \ TD_##type8 arg8 = get_##type8(in); \ return_##rettype(out, function(arg1, arg2, arg3, arg4, arg5, \ arg6, arg7, arg8)); \ } #define FUNC9(rettype, function, type1, type2, type3, type4, type5, \ type6, type7, type8, type9) \ static void handle_##function(BinarySource *in, strbuf *out) { \ TD_##type1 arg1 = get_##type1(in); \ TD_##type2 arg2 = get_##type2(in); \ TD_##type3 arg3 = get_##type3(in); \ TD_##type4 arg4 = get_##type4(in); \ TD_##type5 arg5 = get_##type5(in); \ TD_##type6 arg6 = get_##type6(in); \ TD_##type7 arg7 = get_##type7(in); \ TD_##type8 arg8 = get_##type8(in); \ TD_##type9 arg9 = get_##type9(in); \ return_##rettype(out, function(arg1, arg2, arg3, arg4, arg5, \ arg6, arg7, arg8, arg9)); \ } #include "testcrypt.h" #undef FUNC9 #undef FUNC8 #undef FUNC7 #undef FUNC6 #undef FUNC5 #undef FUNC4 #undef FUNC3 #undef FUNC2 #undef FUNC1 #undef FUNC0 static void process_line(BinarySource *in, strbuf *out) { ptrlen id = get_word(in); #define DISPATCH_INTERNAL(cmdname, handler) do { \ if (ptrlen_eq_string(id, cmdname)) { \ handler(in, out); \ return; \ } \ } while (0) #define DISPATCH_COMMAND(cmd) DISPATCH_INTERNAL(#cmd, handle_##cmd) DISPATCH_COMMAND(hello); DISPATCH_COMMAND(free); DISPATCH_COMMAND(newstring); DISPATCH_COMMAND(getstring); DISPATCH_COMMAND(mp_literal); DISPATCH_COMMAND(mp_dump); #undef DISPATCH_COMMAND #define FUNC0(ret,fn) DISPATCH_INTERNAL(#fn,handle_##fn); #define FUNC1(ret,fn,x) DISPATCH_INTERNAL(#fn,handle_##fn); #define FUNC2(ret,fn,x,y) DISPATCH_INTERNAL(#fn,handle_##fn); #define FUNC3(ret,fn,x,y,z) DISPATCH_INTERNAL(#fn,handle_##fn); #define FUNC4(ret,fn,x,y,z,v) DISPATCH_INTERNAL(#fn,handle_##fn); #define FUNC5(ret,fn,x,y,z,v,w) DISPATCH_INTERNAL(#fn,handle_##fn); #define FUNC6(ret,fn,x,y,z,v,w,u) DISPATCH_INTERNAL(#fn,handle_##fn); #define FUNC7(ret,fn,x,y,z,v,w,u,t) DISPATCH_INTERNAL(#fn,handle_##fn); #define FUNC8(ret,fn,x,y,z,v,w,u,t,s) DISPATCH_INTERNAL(#fn,handle_##fn); #define FUNC9(ret,fn,x,y,z,v,w,u,t,s,r) DISPATCH_INTERNAL(#fn,handle_##fn); #include "testcrypt.h" #undef FUNC9 #undef FUNC8 #undef FUNC7 #undef FUNC6 #undef FUNC5 #undef FUNC4 #undef FUNC3 #undef FUNC2 #undef FUNC1 #undef FUNC0 #undef DISPATCH_INTERNAL fatal_error("command '%.*s': unrecognised", PTRLEN_PRINTF(id)); } static void free_all_values(void) { for (Value *val; (val = delpos234(values, 0)) != NULL ;) free_value(val); freetree234(values); } void dputs(const char *buf) { fputs(buf, stderr); } int main(int argc, char **argv) { const char *infile = NULL, *outfile = NULL; bool doing_opts = true; while (--argc > 0) { char *p = *++argv; if (p[0] == '-' && doing_opts) { if (!strcmp(p, "-o")) { if (--argc <= 0) { fprintf(stderr, "'-o' expects a filename\n"); return 1; } outfile = *++argv; } else if (!strcmp(p, "--")) { doing_opts = false; } else if (!strcmp(p, "--help")) { printf("usage: testcrypt [INFILE] [-o OUTFILE]\n"); printf(" also: testcrypt --help display this text\n"); return 0; } else { fprintf(stderr, "unknown command line option '%s'\n", p); return 1; } } else if (!infile) { infile = p; } else { fprintf(stderr, "can only handle one input file name\n"); return 1; } } FILE *infp = stdin; if (infile) { infp = fopen(infile, "r"); if (!infp) { fprintf(stderr, "%s: open: %s\n", infile, strerror(errno)); return 1; } } FILE *outfp = stdout; if (outfile) { outfp = fopen(outfile, "w"); if (!outfp) { fprintf(stderr, "%s: open: %s\n", outfile, strerror(errno)); return 1; } } values = newtree234(valuecmp); atexit(free_all_values); for (char *line; (line = chomp(fgetline(infp))) != NULL ;) { BinarySource src[1]; BinarySource_BARE_INIT(src, line, strlen(line)); strbuf *sb = strbuf_new(); process_line(src, sb); run_finalisers(sb); size_t lines = 0; for (size_t i = 0; i < sb->len; i++) if (sb->s[i] == '\n') lines++; fprintf(outfp, "%"SIZEu"\n%s", lines, sb->s); fflush(outfp); strbuf_free(sb); sfree(line); } if (infp != stdin) fclose(infp); if (outfp != stdin) fclose(outfp); return 0; } putty-0.76/testcrypt.h0000644000175000017500000003756114072266313012006 00000000000000/* * mpint.h functions. */ FUNC1(val_mpint, mp_new, uint) FUNC1(void, mp_clear, val_mpint) FUNC1(val_mpint, mp_from_bytes_le, val_string_ptrlen) FUNC1(val_mpint, mp_from_bytes_be, val_string_ptrlen) FUNC1(val_mpint, mp_from_integer, uint) FUNC1(val_mpint, mp_from_decimal_pl, val_string_ptrlen) FUNC1(val_mpint, mp_from_decimal, val_string_asciz) FUNC1(val_mpint, mp_from_hex_pl, val_string_ptrlen) FUNC1(val_mpint, mp_from_hex, val_string_asciz) FUNC1(val_mpint, mp_copy, val_mpint) FUNC1(val_mpint, mp_power_2, uint) FUNC2(uint, mp_get_byte, val_mpint, uint) FUNC2(uint, mp_get_bit, val_mpint, uint) FUNC3(void, mp_set_bit, val_mpint, uint, uint) FUNC1(uint, mp_max_bytes, val_mpint) FUNC1(uint, mp_max_bits, val_mpint) FUNC1(uint, mp_get_nbits, val_mpint) FUNC1(val_string_asciz, mp_get_decimal, val_mpint) FUNC1(val_string_asciz, mp_get_hex, val_mpint) FUNC1(val_string_asciz, mp_get_hex_uppercase, val_mpint) FUNC2(uint, mp_cmp_hs, val_mpint, val_mpint) FUNC2(uint, mp_cmp_eq, val_mpint, val_mpint) FUNC2(uint, mp_hs_integer, val_mpint, uint) FUNC2(uint, mp_eq_integer, val_mpint, uint) FUNC3(void, mp_min_into, val_mpint, val_mpint, val_mpint) FUNC3(void, mp_max_into, val_mpint, val_mpint, val_mpint) FUNC2(val_mpint, mp_min, val_mpint, val_mpint) FUNC2(val_mpint, mp_max, val_mpint, val_mpint) FUNC2(void, mp_copy_into, val_mpint, val_mpint) FUNC4(void, mp_select_into, val_mpint, val_mpint, val_mpint, uint) FUNC3(void, mp_add_into, val_mpint, val_mpint, val_mpint) FUNC3(void, mp_sub_into, val_mpint, val_mpint, val_mpint) FUNC3(void, mp_mul_into, val_mpint, val_mpint, val_mpint) FUNC2(val_mpint, mp_add, val_mpint, val_mpint) FUNC2(val_mpint, mp_sub, val_mpint, val_mpint) FUNC2(val_mpint, mp_mul, val_mpint, val_mpint) FUNC3(void, mp_and_into, val_mpint, val_mpint, val_mpint) FUNC3(void, mp_or_into, val_mpint, val_mpint, val_mpint) FUNC3(void, mp_xor_into, val_mpint, val_mpint, val_mpint) FUNC3(void, mp_bic_into, val_mpint, val_mpint, val_mpint) FUNC2(void, mp_copy_integer_into, val_mpint, uint) FUNC3(void, mp_add_integer_into, val_mpint, val_mpint, uint) FUNC3(void, mp_sub_integer_into, val_mpint, val_mpint, uint) FUNC3(void, mp_mul_integer_into, val_mpint, val_mpint, uint) FUNC4(void, mp_cond_add_into, val_mpint, val_mpint, val_mpint, uint) FUNC4(void, mp_cond_sub_into, val_mpint, val_mpint, val_mpint, uint) FUNC3(void, mp_cond_swap, val_mpint, val_mpint, uint) FUNC2(void, mp_cond_clear, val_mpint, uint) FUNC4(void, mp_divmod_into, val_mpint, val_mpint, opt_val_mpint, opt_val_mpint) FUNC2(val_mpint, mp_div, val_mpint, val_mpint) FUNC2(val_mpint, mp_mod, val_mpint, val_mpint) FUNC3(val_mpint, mp_nthroot, val_mpint, uint, opt_val_mpint) FUNC2(void, mp_reduce_mod_2to, val_mpint, uint) FUNC2(val_mpint, mp_invert_mod_2to, val_mpint, uint) FUNC2(val_mpint, mp_invert, val_mpint, val_mpint) FUNC5(void, mp_gcd_into, val_mpint, val_mpint, opt_val_mpint, opt_val_mpint, opt_val_mpint) FUNC2(val_mpint, mp_gcd, val_mpint, val_mpint) FUNC2(uint, mp_coprime, val_mpint, val_mpint) FUNC2(val_modsqrt, modsqrt_new, val_mpint, val_mpint) /* The modsqrt functions' 'success' pointer becomes a second return value */ FUNC3(val_mpint, mp_modsqrt, val_modsqrt, val_mpint, out_uint) FUNC1(val_monty, monty_new, val_mpint) FUNC1(val_mpint, monty_modulus, val_monty) FUNC1(val_mpint, monty_identity, val_monty) FUNC3(void, monty_import_into, val_monty, val_mpint, val_mpint) FUNC2(val_mpint, monty_import, val_monty, val_mpint) FUNC3(void, monty_export_into, val_monty, val_mpint, val_mpint) FUNC2(val_mpint, monty_export, val_monty, val_mpint) FUNC4(void, monty_mul_into, val_monty, val_mpint, val_mpint, val_mpint) FUNC3(val_mpint, monty_add, val_monty, val_mpint, val_mpint) FUNC3(val_mpint, monty_sub, val_monty, val_mpint, val_mpint) FUNC3(val_mpint, monty_mul, val_monty, val_mpint, val_mpint) FUNC3(val_mpint, monty_pow, val_monty, val_mpint, val_mpint) FUNC2(val_mpint, monty_invert, val_monty, val_mpint) FUNC3(val_mpint, monty_modsqrt, val_modsqrt, val_mpint, out_uint) FUNC3(val_mpint, mp_modpow, val_mpint, val_mpint, val_mpint) FUNC3(val_mpint, mp_modmul, val_mpint, val_mpint, val_mpint) FUNC3(val_mpint, mp_modadd, val_mpint, val_mpint, val_mpint) FUNC3(val_mpint, mp_modsub, val_mpint, val_mpint, val_mpint) FUNC3(void, mp_lshift_safe_into, val_mpint, val_mpint, uint) FUNC3(void, mp_rshift_safe_into, val_mpint, val_mpint, uint) FUNC2(val_mpint, mp_rshift_safe, val_mpint, uint) FUNC3(void, mp_lshift_fixed_into, val_mpint, val_mpint, uint) FUNC3(void, mp_rshift_fixed_into, val_mpint, val_mpint, uint) FUNC2(val_mpint, mp_rshift_fixed, val_mpint, uint) FUNC1(val_mpint, mp_random_bits, uint) FUNC2(val_mpint, mp_random_in_range, val_mpint, val_mpint) /* * ecc.h functions. */ FUNC4(val_wcurve, ecc_weierstrass_curve, val_mpint, val_mpint, val_mpint, opt_val_mpint) FUNC1(val_wpoint, ecc_weierstrass_point_new_identity, val_wcurve) FUNC3(val_wpoint, ecc_weierstrass_point_new, val_wcurve, val_mpint, val_mpint) FUNC3(val_wpoint, ecc_weierstrass_point_new_from_x, val_wcurve, val_mpint, uint) FUNC1(val_wpoint, ecc_weierstrass_point_copy, val_wpoint) FUNC1(uint, ecc_weierstrass_point_valid, val_wpoint) FUNC2(val_wpoint, ecc_weierstrass_add_general, val_wpoint, val_wpoint) FUNC2(val_wpoint, ecc_weierstrass_add, val_wpoint, val_wpoint) FUNC1(val_wpoint, ecc_weierstrass_double, val_wpoint) FUNC2(val_wpoint, ecc_weierstrass_multiply, val_wpoint, val_mpint) FUNC1(uint, ecc_weierstrass_is_identity, val_wpoint) /* The output pointers in get_affine all become extra output values */ FUNC3(void, ecc_weierstrass_get_affine, val_wpoint, out_val_mpint, out_val_mpint) FUNC3(val_mcurve, ecc_montgomery_curve, val_mpint, val_mpint, val_mpint) FUNC2(val_mpoint, ecc_montgomery_point_new, val_mcurve, val_mpint) FUNC1(val_mpoint, ecc_montgomery_point_copy, val_mpoint) FUNC3(val_mpoint, ecc_montgomery_diff_add, val_mpoint, val_mpoint, val_mpoint) FUNC1(val_mpoint, ecc_montgomery_double, val_mpoint) FUNC2(val_mpoint, ecc_montgomery_multiply, val_mpoint, val_mpint) FUNC2(void, ecc_montgomery_get_affine, val_mpoint, out_val_mpint) FUNC1(boolean, ecc_montgomery_is_identity, val_mpoint) FUNC4(val_ecurve, ecc_edwards_curve, val_mpint, val_mpint, val_mpint, opt_val_mpint) FUNC3(val_epoint, ecc_edwards_point_new, val_ecurve, val_mpint, val_mpint) FUNC3(val_epoint, ecc_edwards_point_new_from_y, val_ecurve, val_mpint, uint) FUNC1(val_epoint, ecc_edwards_point_copy, val_epoint) FUNC2(val_epoint, ecc_edwards_add, val_epoint, val_epoint) FUNC2(val_epoint, ecc_edwards_multiply, val_epoint, val_mpint) FUNC2(uint, ecc_edwards_eq, val_epoint, val_epoint) FUNC3(void, ecc_edwards_get_affine, val_epoint, out_val_mpint, out_val_mpint) /* * The ssh_hash abstraction. Note the 'consumed', indicating that * ssh_hash_final puts its input ssh_hash beyond use. * * ssh_hash_update is an invention of testcrypt, handled in the real C * API by the hash object also functioning as a BinarySink. */ FUNC1(opt_val_hash, ssh_hash_new, hashalg) FUNC1(void, ssh_hash_reset, val_hash) FUNC1(val_hash, ssh_hash_copy, val_hash) FUNC1(val_string, ssh_hash_digest, val_hash) FUNC1(val_string, ssh_hash_final, consumed_val_hash) FUNC2(void, ssh_hash_update, val_hash, val_string_ptrlen) FUNC1(opt_val_hash, blake2b_new_general, uint) /* * The ssh2_mac abstraction. Note the optional ssh_cipher parameter * to ssh2_mac_new. Also, again, I've invented an ssh2_mac_update so * you can put data into the MAC. */ FUNC2(val_mac, ssh2_mac_new, macalg, opt_val_cipher) FUNC2(void, ssh2_mac_setkey, val_mac, val_string_ptrlen) FUNC1(void, ssh2_mac_start, val_mac) FUNC2(void, ssh2_mac_update, val_mac, val_string_ptrlen) FUNC1(val_string, ssh2_mac_genresult, val_mac) FUNC1(val_string_asciz_const, ssh2_mac_text_name, val_mac) /* * The ssh_key abstraction. All the uses of BinarySink and * BinarySource in parameters are replaced with ordinary strings for * the testing API: new_priv_openssh just takes a string input, and * all the functions that output key and signature blobs do it by * returning a string. */ FUNC2(val_key, ssh_key_new_pub, keyalg, val_string_ptrlen) FUNC3(opt_val_key, ssh_key_new_priv, keyalg, val_string_ptrlen, val_string_ptrlen) FUNC2(opt_val_key, ssh_key_new_priv_openssh, keyalg, val_string_binarysource) FUNC2(opt_val_string_asciz, ssh_key_invalid, val_key, uint) FUNC4(void, ssh_key_sign, val_key, val_string_ptrlen, uint, out_val_string_binarysink) FUNC3(boolean, ssh_key_verify, val_key, val_string_ptrlen, val_string_ptrlen) FUNC2(void, ssh_key_public_blob, val_key, out_val_string_binarysink) FUNC2(void, ssh_key_private_blob, val_key, out_val_string_binarysink) FUNC2(void, ssh_key_openssh_blob, val_key, out_val_string_binarysink) FUNC1(val_string_asciz, ssh_key_cache_str, val_key) FUNC1(val_keycomponents, ssh_key_components, val_key) FUNC2(uint, ssh_key_public_bits, keyalg, val_string_ptrlen) /* * Accessors to retrieve the innards of a 'key_components'. */ FUNC1(uint, key_components_count, val_keycomponents) FUNC2(opt_val_string_asciz_const, key_components_nth_name, val_keycomponents, uint) FUNC2(opt_val_string_asciz_const, key_components_nth_str, val_keycomponents, uint) FUNC2(opt_val_mpint, key_components_nth_mp, val_keycomponents, uint) /* * The ssh_cipher abstraction. The in-place encrypt and decrypt * functions are wrapped to replace them with versions that take one * string and return a separate string. */ FUNC1(opt_val_cipher, ssh_cipher_new, cipheralg) FUNC2(void, ssh_cipher_setiv, val_cipher, val_string_ptrlen) FUNC2(void, ssh_cipher_setkey, val_cipher, val_string_ptrlen) FUNC2(val_string, ssh_cipher_encrypt, val_cipher, val_string_ptrlen) FUNC2(val_string, ssh_cipher_decrypt, val_cipher, val_string_ptrlen) FUNC3(val_string, ssh_cipher_encrypt_length, val_cipher, val_string_ptrlen, uint) FUNC3(val_string, ssh_cipher_decrypt_length, val_cipher, val_string_ptrlen, uint) /* * Integer Diffie-Hellman. */ FUNC1(val_dh, dh_setup_group, dh_group) FUNC2(val_dh, dh_setup_gex, val_mpint, val_mpint) FUNC1(uint, dh_modulus_bit_size, val_dh) FUNC2(val_mpint, dh_create_e, val_dh, uint) FUNC2(boolean, dh_validate_f, val_dh, val_mpint) FUNC2(val_mpint, dh_find_K, val_dh, val_mpint) /* * Elliptic-curve Diffie-Hellman. */ FUNC1(val_ecdh, ssh_ecdhkex_newkey, ecdh_alg) FUNC2(void, ssh_ecdhkex_getpublic, val_ecdh, out_val_string_binarysink) FUNC2(opt_val_mpint, ssh_ecdhkex_getkey, val_ecdh, val_string_ptrlen) /* * RSA key exchange, and also the BinarySource get function * get_ssh1_rsa_priv_agent, which is a convenient way to make an * RSAKey for RSA kex testing purposes. */ FUNC1(val_rsakex, ssh_rsakex_newkey, val_string_ptrlen) FUNC1(uint, ssh_rsakex_klen, val_rsakex) FUNC3(val_string, ssh_rsakex_encrypt, val_rsakex, hashalg, val_string_ptrlen) FUNC3(opt_val_mpint, ssh_rsakex_decrypt, val_rsakex, hashalg, val_string_ptrlen) FUNC1(val_rsakex, get_rsa_ssh1_priv_agent, val_string_binarysource) /* * Bare RSA keys as used in SSH-1. The construction API functions * write into an existing RSAKey object, so I've invented an 'rsa_new' * function to make one in the first place. */ FUNC0(val_rsa, rsa_new) FUNC3(void, get_rsa_ssh1_pub, val_string_binarysource, val_rsa, rsaorder) FUNC2(void, get_rsa_ssh1_priv, val_string_binarysource, val_rsa) FUNC2(opt_val_string, rsa_ssh1_encrypt, val_string_ptrlen, val_rsa) FUNC2(val_mpint, rsa_ssh1_decrypt, val_mpint, val_rsa) FUNC2(val_string, rsa_ssh1_decrypt_pkcs1, val_mpint, val_rsa) FUNC1(val_string_asciz, rsastr_fmt, val_rsa) FUNC1(val_string_asciz, rsa_ssh1_fingerprint, val_rsa) FUNC3(void, rsa_ssh1_public_blob, out_val_string_binarysink, val_rsa, rsaorder) FUNC1(int, rsa_ssh1_public_blob_len, val_string_ptrlen) FUNC2(void, rsa_ssh1_private_blob_agent, out_val_string_binarysink, val_rsa) /* * The PRNG type. Similarly to hashes and MACs, I've invented an extra * function prng_seed_update for putting seed data into the PRNG's * exposed BinarySink. */ FUNC1(val_prng, prng_new, hashalg) FUNC1(void, prng_seed_begin, val_prng) FUNC2(void, prng_seed_update, val_prng, val_string_ptrlen) FUNC1(void, prng_seed_finish, val_prng) FUNC2(val_string, prng_read, val_prng, uint) FUNC3(void, prng_add_entropy, val_prng, uint, val_string_ptrlen) /* * Key load/save functions, or rather, the BinarySource / strbuf API * that sits just inside the file I/O versions. */ FUNC2(boolean, ppk_encrypted_s, val_string_binarysource, out_opt_val_string_asciz) FUNC2(boolean, rsa1_encrypted_s, val_string_binarysource, out_opt_val_string_asciz) FUNC5(boolean, ppk_loadpub_s, val_string_binarysource, out_opt_val_string_asciz, out_val_string_binarysink, out_opt_val_string_asciz, out_opt_val_string_asciz_const) FUNC4(int, rsa1_loadpub_s, val_string_binarysource, out_val_string_binarysink, out_opt_val_string_asciz, out_opt_val_string_asciz_const) FUNC4(opt_val_key, ppk_load_s, val_string_binarysource, out_opt_val_string_asciz, opt_val_string_asciz, out_opt_val_string_asciz_const) FUNC5(int, rsa1_load_s, val_string_binarysource, val_rsa, out_opt_val_string_asciz, opt_val_string_asciz, out_opt_val_string_asciz_const) FUNC8(val_string, ppk_save_sb, val_key, opt_val_string_asciz, opt_val_string_asciz, uint, argon2flavour, uint, uint, uint) FUNC3(val_string, rsa1_save_sb, val_rsa, opt_val_string_asciz, opt_val_string_asciz) FUNC2(val_string_asciz, ssh2_fingerprint_blob, val_string_ptrlen, fptype) /* * Password hashing. */ FUNC9(val_string, argon2, argon2flavour, uint, uint, uint, uint, val_string_ptrlen, val_string_ptrlen, val_string_ptrlen, val_string_ptrlen) FUNC2(val_string, argon2_long_hash, uint, val_string_ptrlen) /* * Key generation functions. */ FUNC3(val_key, rsa_generate, uint, boolean, val_pgc) FUNC2(val_key, dsa_generate, uint, val_pgc) FUNC1(opt_val_key, ecdsa_generate, uint) FUNC1(opt_val_key, eddsa_generate, uint) FUNC3(val_rsa, rsa1_generate, uint, boolean, val_pgc) FUNC1(val_pgc, primegen_new_context, primegenpolicy) FUNC2(opt_val_mpint, primegen_generate, val_pgc, consumed_val_pcs) FUNC2(val_string, primegen_mpu_certificate, val_pgc, val_mpint) FUNC1(val_pcs, pcs_new, uint) FUNC3(val_pcs, pcs_new_with_firstbits, uint, uint, uint) FUNC3(void, pcs_require_residue, val_pcs, val_mpint, val_mpint) FUNC2(void, pcs_require_residue_1, val_pcs, val_mpint) FUNC2(void, pcs_require_residue_1_mod_prime, val_pcs, val_mpint) FUNC3(void, pcs_avoid_residue_small, val_pcs, uint, uint) FUNC1(void, pcs_try_sophie_germain, val_pcs) FUNC1(void, pcs_set_oneshot, val_pcs) FUNC1(void, pcs_ready, val_pcs) FUNC4(void, pcs_inspect, val_pcs, out_val_mpint, out_val_mpint, out_val_mpint) FUNC1(val_mpint, pcs_generate, val_pcs) FUNC0(val_pockle, pockle_new) FUNC1(uint, pockle_mark, val_pockle) FUNC2(void, pockle_release, val_pockle, uint) FUNC2(pocklestatus, pockle_add_small_prime, val_pockle, val_mpint) FUNC4(pocklestatus, pockle_add_prime, val_pockle, val_mpint, mpint_list, val_mpint) FUNC2(val_string, pockle_mpu, val_pockle, val_mpint) /* * Miscellaneous. */ FUNC2(val_wpoint, ecdsa_public, val_mpint, keyalg) FUNC2(val_epoint, eddsa_public, val_mpint, keyalg) FUNC2(val_string, des_encrypt_xdmauth, val_string_ptrlen, val_string_ptrlen) FUNC2(val_string, des_decrypt_xdmauth, val_string_ptrlen, val_string_ptrlen) FUNC2(val_string, des3_encrypt_pubkey, val_string_ptrlen, val_string_ptrlen) FUNC2(val_string, des3_decrypt_pubkey, val_string_ptrlen, val_string_ptrlen) FUNC3(val_string, des3_encrypt_pubkey_ossh, val_string_ptrlen, val_string_ptrlen, val_string_ptrlen) FUNC3(val_string, des3_decrypt_pubkey_ossh, val_string_ptrlen, val_string_ptrlen, val_string_ptrlen) FUNC3(val_string, aes256_encrypt_pubkey, val_string_ptrlen, val_string_ptrlen, val_string_ptrlen) FUNC3(val_string, aes256_decrypt_pubkey, val_string_ptrlen, val_string_ptrlen, val_string_ptrlen) FUNC1(uint, crc32_rfc1662, val_string_ptrlen) FUNC1(uint, crc32_ssh1, val_string_ptrlen) FUNC2(uint, crc32_update, uint, val_string_ptrlen) FUNC2(boolean, crcda_detect, val_string_ptrlen, val_string_ptrlen) /* * These functions aren't part of PuTTY's own API, but are additions * by testcrypt itself for administrative purposes. */ FUNC1(void, random_queue, val_string_ptrlen) FUNC0(uint, random_queue_len) FUNC2(void, random_make_prng, hashalg, val_string_ptrlen) FUNC0(void, random_clear) putty-0.76/testsc.c0000644000175000017500000013767214072266313011251 00000000000000/* * testsc: run PuTTY's crypto primitives under instrumentation that * checks for cache and timing side channels. * * The idea is: cryptographic code should avoid leaking secret data * through timing information, or through traces of its activity left * in the caches. * * (This property is sometimes called 'constant-time', although really * that's a misnomer. It would be impossible to avoid the execution * time varying for any number of reasons outside the code's control, * such as the prior contents of caches and branch predictors, * temperature-based CPU throttling, system load, etc. And in any case * you don't _need_ the execution time to be literally constant: you * just need it to be independent of your secrets. It can vary as much * as it likes based on anything else.) * * To avoid this, you need to ensure that various aspects of the * code's behaviour do not depend on the secret data. The control * flow, for a start - no conditional branches based on secrets - and * also the memory access pattern (no using secret data as an index * into a lookup table). A couple of other kinds of CPU instruction * also can't be trusted to run in constant time: we check for * register-controlled shifts and hardware divisions. (But, again, * it's perfectly fine to _use_ those instructions in the course of * crypto code. You just can't use a secret as any time-affecting * operand.) * * This test program works by running the same crypto primitive * multiple times, with different secret input data. The relevant * details of each run is logged to a file via the DynamoRIO-based * instrumentation system living in the subdirectory test/sclog. Then * we check over all the files and ensure they're identical. * * This program itself (testsc) is built by the ordinary PuTTY * makefiles. But run by itself, it will do nothing useful: it needs * to be run under DynamoRIO, with the sclog instrumentation library. * * Here's an example of how I built it: * * Download the DynamoRIO source. I did this by cloning * https://github.com/DynamoRIO/dynamorio.git, and at the time of * writing this, 259c182a75ce80112bcad329c97ada8d56ba854d was the head * commit. * * In the DynamoRIO checkout: * * mkdir build * cd build * cmake -G Ninja .. * ninja * * Now set the shell variable DRBUILD to be the location of the build * directory you did that in. (Or not, if you prefer, but the example * build commands below will assume that that's where the DynamoRIO * libraries, headers and runtime can be found.) * * Then, in test/sclog: * * cmake -G Ninja -DCMAKE_PREFIX_PATH=$DRBUILD/cmake . * ninja * * Finally, to run the actual test, set SCTMP to some temp directory * you don't mind filling with large temp files (several GB at a * time), and in the main PuTTY source directory (assuming that's * where testsc has been built): * * $DRBUILD/bin64/drrun -c test/sclog/libsclog.so -- ./testsc -O $SCTMP */ #include #include #include #include #include #include "defs.h" #include "putty.h" #include "ssh.h" #include "misc.h" #include "mpint.h" #include "ecc.h" static NORETURN PRINTF_LIKE(1, 2) void fatal_error(const char *p, ...) { va_list ap; fprintf(stderr, "testsc: "); va_start(ap, p); vfprintf(stderr, p, ap); va_end(ap); fputc('\n', stderr); exit(1); } void out_of_memory(void) { fatal_error("out of memory"); } FILE *f_open(const Filename *filename, char const *mode, bool is_private) { unreachable("this is a stub needed to link, and should never be called"); } void old_keyfile_warning(void) { unreachable("this is a stub needed to link, and should never be called"); } /* For platforms where getticks is defined within this code base */ unsigned long (getticks)(void) { unreachable("this is a stub needed to link, and should never be called"); } /* * A simple deterministic PRNG, without any of the Fortuna * complexities, for generating test inputs in a way that's repeatable * between runs of the program, even if only a subset of test cases is * run. */ static uint64_t random_counter = 0; static const char *random_seedstr = NULL; static uint8_t random_buf[MAX_HASH_LEN]; static size_t random_buf_limit = 0; static ssh_hash *random_hash; static void random_seed(const char *seedstr) { random_seedstr = seedstr; random_counter = 0; random_buf_limit = 0; } void random_read(void *vbuf, size_t size) { assert(random_seedstr); uint8_t *buf = (uint8_t *)vbuf; while (size-- > 0) { if (random_buf_limit == 0) { ssh_hash_reset(random_hash); put_asciz(random_hash, random_seedstr); put_uint64(random_hash, random_counter); random_counter++; random_buf_limit = ssh_hash_alg(random_hash)->hlen; ssh_hash_digest(random_hash, random_buf); } *buf++ = random_buf[random_buf_limit--]; } } /* * Macro that defines a function, and also a volatile function pointer * pointing to it. Callers indirect through the function pointer * instead of directly calling the function, to ensure that the * compiler doesn't try to get clever by eliminating the call * completely, or inlining it. * * This is used to mark functions that DynamoRIO will look for to * intercept, and also to inhibit inlining and unrolling where they'd * cause a failure of experimental control in the main test. */ #define VOLATILE_WRAPPED_DEFN(qualifier, rettype, fn, params) \ qualifier rettype fn##_real params; \ qualifier rettype (*volatile fn) params = fn##_real; \ qualifier rettype fn##_real params VOLATILE_WRAPPED_DEFN(, void, log_to_file, (const char *filename)) { /* * This function is intercepted by the DynamoRIO side of the * mechanism. We use it to send instructions to the DR wrapper, * namely, 'please start logging to this file' or 'please stop * logging' (if filename == NULL). But we don't have to actually * do anything in _this_ program - all the functionality is in the * DR wrapper. */ } static const char *outdir = NULL; char *log_filename(const char *basename, size_t index) { return dupprintf("%s/%s.%04"SIZEu, outdir, basename, index); } static char *last_filename; static const char *test_basename; static size_t test_index = 0; void log_start(void) { last_filename = log_filename(test_basename, test_index++); log_to_file(last_filename); } void log_end(void) { log_to_file(NULL); sfree(last_filename); } static bool test_skipped = false; VOLATILE_WRAPPED_DEFN(, intptr_t, dry_run, (void)) { /* * This is another function intercepted by DynamoRIO. In this * case, DR overrides this function to return 0 rather than 1, so * we can use it as a check for whether we're running under * instrumentation, or whether this is just a dry run which goes * through the motions but doesn't expect to find any log files * created. */ return 1; } static void mp_random_bits_into(mp_int *r, size_t bits) { mp_int *x = mp_random_bits(bits); mp_copy_into(r, x); mp_free(x); } static void mp_random_fill(mp_int *r) { mp_random_bits_into(r, mp_max_bits(r)); } VOLATILE_WRAPPED_DEFN(static, size_t, looplimit, (size_t x)) { /* * looplimit() is the identity function on size_t, but the * compiler isn't allowed to rely on it being that. I use it to * make loops in the test functions look less attractive to * compilers' unrolling heuristics. */ return x; } /* Ciphers that we expect to pass this test. Blowfish and Arcfour are * intentionally omitted, because we already know they don't. */ #define CIPHERS(X, Y) \ X(Y, ssh_3des_ssh1) \ X(Y, ssh_3des_ssh2_ctr) \ X(Y, ssh_3des_ssh2) \ X(Y, ssh_des) \ X(Y, ssh_des_sshcom_ssh2) \ X(Y, ssh_aes256_sdctr) \ X(Y, ssh_aes256_sdctr_hw) \ X(Y, ssh_aes256_sdctr_sw) \ X(Y, ssh_aes256_cbc) \ X(Y, ssh_aes256_cbc_hw) \ X(Y, ssh_aes256_cbc_sw) \ X(Y, ssh_aes192_sdctr) \ X(Y, ssh_aes192_sdctr_hw) \ X(Y, ssh_aes192_sdctr_sw) \ X(Y, ssh_aes192_cbc) \ X(Y, ssh_aes192_cbc_hw) \ X(Y, ssh_aes192_cbc_sw) \ X(Y, ssh_aes128_sdctr) \ X(Y, ssh_aes128_sdctr_hw) \ X(Y, ssh_aes128_sdctr_sw) \ X(Y, ssh_aes128_cbc) \ X(Y, ssh_aes128_cbc_hw) \ X(Y, ssh_aes128_cbc_sw) \ X(Y, ssh2_chacha20_poly1305) \ /* end of list */ #define CIPHER_TESTLIST(X, name) X(cipher_ ## name) #define MACS(X, Y) \ X(Y, ssh_hmac_md5) \ X(Y, ssh_hmac_sha1) \ X(Y, ssh_hmac_sha1_buggy) \ X(Y, ssh_hmac_sha1_96) \ X(Y, ssh_hmac_sha1_96_buggy) \ X(Y, ssh_hmac_sha256) \ /* end of list */ #define MAC_TESTLIST(X, name) X(mac_ ## name) #define HASHES(X, Y) \ X(Y, ssh_md5) \ X(Y, ssh_sha1) \ X(Y, ssh_sha1_hw) \ X(Y, ssh_sha1_sw) \ X(Y, ssh_sha256) \ X(Y, ssh_sha256_hw) \ X(Y, ssh_sha256_sw) \ X(Y, ssh_sha384) \ X(Y, ssh_sha512) \ X(Y, ssh_sha3_224) \ X(Y, ssh_sha3_256) \ X(Y, ssh_sha3_384) \ X(Y, ssh_sha3_512) \ X(Y, ssh_shake256_114bytes) \ X(Y, ssh_blake2b) \ /* end of list */ #define HASH_TESTLIST(X, name) X(hash_ ## name) #define TESTLIST(X) \ X(mp_get_nbits) \ X(mp_from_decimal) \ X(mp_from_hex) \ X(mp_get_decimal) \ X(mp_get_hex) \ X(mp_cmp_hs) \ X(mp_cmp_eq) \ X(mp_min) \ X(mp_max) \ X(mp_select_into) \ X(mp_cond_swap) \ X(mp_cond_clear) \ X(mp_add) \ X(mp_sub) \ X(mp_mul) \ X(mp_rshift_safe) \ X(mp_divmod) \ X(mp_nthroot) \ X(mp_modadd) \ X(mp_modsub) \ X(mp_modmul) \ X(mp_modpow) \ X(mp_invert_mod_2to) \ X(mp_invert) \ X(mp_modsqrt) \ X(ecc_weierstrass_add) \ X(ecc_weierstrass_double) \ X(ecc_weierstrass_add_general) \ X(ecc_weierstrass_multiply) \ X(ecc_weierstrass_is_identity) \ X(ecc_weierstrass_get_affine) \ X(ecc_weierstrass_decompress) \ X(ecc_montgomery_diff_add) \ X(ecc_montgomery_double) \ X(ecc_montgomery_multiply) \ X(ecc_montgomery_get_affine) \ X(ecc_edwards_add) \ X(ecc_edwards_multiply) \ X(ecc_edwards_eq) \ X(ecc_edwards_get_affine) \ X(ecc_edwards_decompress) \ CIPHERS(CIPHER_TESTLIST, X) \ MACS(MAC_TESTLIST, X) \ HASHES(HASH_TESTLIST, X) \ X(argon2) \ /* end of list */ static void test_mp_get_nbits(void) { mp_int *z = mp_new(512); static const size_t bitposns[] = { 0, 1, 5, 16, 23, 32, 67, 123, 234, 511 }; mp_int *prev = mp_from_integer(0); for (size_t i = 0; i < looplimit(lenof(bitposns)); i++) { mp_int *x = mp_power_2(bitposns[i]); mp_add_into(z, x, prev); mp_free(prev); prev = x; log_start(); mp_get_nbits(z); log_end(); } mp_free(prev); mp_free(z); } static void test_mp_from_decimal(void) { char dec[64]; static const size_t starts[] = { 0, 1, 5, 16, 23, 32, 63, 64 }; for (size_t i = 0; i < looplimit(lenof(starts)); i++) { memset(dec, '0', lenof(dec)); for (size_t j = starts[i]; j < lenof(dec); j++) { uint8_t r[4]; random_read(r, 4); dec[j] = '0' + GET_32BIT_MSB_FIRST(r) % 10; } log_start(); mp_int *x = mp_from_decimal_pl(make_ptrlen(dec, lenof(dec))); log_end(); mp_free(x); } } static void test_mp_from_hex(void) { char hex[64]; static const size_t starts[] = { 0, 1, 5, 16, 23, 32, 63, 64 }; static const char digits[] = "0123456789abcdefABCDEF"; for (size_t i = 0; i < looplimit(lenof(starts)); i++) { memset(hex, '0', lenof(hex)); for (size_t j = starts[i]; j < lenof(hex); j++) { uint8_t r[4]; random_read(r, 4); hex[j] = digits[GET_32BIT_MSB_FIRST(r) % lenof(digits)]; } log_start(); mp_int *x = mp_from_hex_pl(make_ptrlen(hex, lenof(hex))); log_end(); mp_free(x); } } static void test_mp_string_format(char *(*mp_format)(mp_int *x)) { mp_int *z = mp_new(512); static const size_t bitposns[] = { 0, 1, 5, 16, 23, 32, 67, 123, 234, 511 }; for (size_t i = 0; i < looplimit(lenof(bitposns)); i++) { mp_random_bits_into(z, bitposns[i]); log_start(); char *formatted = mp_format(z); log_end(); sfree(formatted); } mp_free(z); } static void test_mp_get_decimal(void) { test_mp_string_format(mp_get_decimal); } static void test_mp_get_hex(void) { test_mp_string_format(mp_get_hex); } static void test_mp_cmp(unsigned (*mp_cmp)(mp_int *a, mp_int *b)) { mp_int *a = mp_new(512), *b = mp_new(512); static const size_t bitposns[] = { 0, 1, 5, 16, 23, 32, 67, 123, 234, 511 }; for (size_t i = 0; i < looplimit(lenof(bitposns)); i++) { mp_random_fill(b); mp_int *x = mp_random_bits(bitposns[i]); mp_xor_into(a, b, x); mp_free(x); log_start(); mp_cmp(a, b); log_end(); } mp_free(a); mp_free(b); } static void test_mp_cmp_hs(void) { test_mp_cmp(mp_cmp_hs); } static void test_mp_cmp_eq(void) { test_mp_cmp(mp_cmp_eq); } static void test_mp_minmax( void (*mp_minmax_into)(mp_int *r, mp_int *x, mp_int *y)) { mp_int *a = mp_new(256), *b = mp_new(256); for (size_t i = 0; i < looplimit(10); i++) { uint8_t lens[2]; random_read(lens, 2); mp_int *x = mp_random_bits(lens[0]); mp_copy_into(a, x); mp_free(x); mp_int *y = mp_random_bits(lens[1]); mp_copy_into(a, y); mp_free(y); log_start(); mp_minmax_into(a, a, b); log_end(); } mp_free(a); mp_free(b); } static void test_mp_max(void) { test_mp_minmax(mp_max_into); } static void test_mp_min(void) { test_mp_minmax(mp_min_into); } static void test_mp_select_into(void) { mp_int *a = mp_random_bits(256); mp_int *b = mp_random_bits(512); mp_int *r = mp_new(384); for (size_t i = 0; i < looplimit(16); i++) { log_start(); mp_select_into(r, a, b, i & 1); log_end(); } mp_free(a); mp_free(b); mp_free(r); } static void test_mp_cond_swap(void) { mp_int *a = mp_random_bits(512); mp_int *b = mp_random_bits(512); for (size_t i = 0; i < looplimit(16); i++) { log_start(); mp_cond_swap(a, b, i & 1); log_end(); } mp_free(a); mp_free(b); } static void test_mp_cond_clear(void) { mp_int *a = mp_random_bits(512); mp_int *x = mp_copy(a); for (size_t i = 0; i < looplimit(16); i++) { mp_copy_into(x, a); log_start(); mp_cond_clear(a, i & 1); log_end(); } mp_free(a); mp_free(x); } static void test_mp_arithmetic(mp_int *(*mp_arith)(mp_int *x, mp_int *y)) { mp_int *a = mp_new(256), *b = mp_new(512); for (size_t i = 0; i < looplimit(16); i++) { mp_random_fill(a); mp_random_fill(b); log_start(); mp_int *r = mp_arith(a, b); log_end(); mp_free(r); } mp_free(a); mp_free(b); } static void test_mp_add(void) { test_mp_arithmetic(mp_add); } static void test_mp_sub(void) { test_mp_arithmetic(mp_sub); } static void test_mp_mul(void) { test_mp_arithmetic(mp_mul); } static void test_mp_invert(void) { test_mp_arithmetic(mp_invert); } static void test_mp_rshift_safe(void) { mp_int *x = mp_random_bits(256); for (size_t i = 0; i < looplimit(mp_max_bits(x)+1); i++) { log_start(); mp_int *r = mp_rshift_safe(x, i); log_end(); mp_free(r); } mp_free(x); } static void test_mp_divmod(void) { mp_int *n = mp_new(256), *d = mp_new(256); mp_int *q = mp_new(256), *r = mp_new(256); for (size_t i = 0; i < looplimit(32); i++) { uint8_t sizes[2]; random_read(sizes, 2); mp_random_bits_into(n, sizes[0]); mp_random_bits_into(d, sizes[1]); log_start(); mp_divmod_into(n, d, q, r); log_end(); } mp_free(n); mp_free(d); mp_free(q); mp_free(r); } static void test_mp_nthroot(void) { mp_int *x = mp_new(256), *remainder = mp_new(256); for (size_t i = 0; i < looplimit(32); i++) { uint8_t sizes[1]; random_read(sizes, 1); mp_random_bits_into(x, sizes[0]); log_start(); mp_free(mp_nthroot(x, 3, remainder)); log_end(); } mp_free(x); mp_free(remainder); } static void test_mp_modarith( mp_int *(*mp_modarith)(mp_int *x, mp_int *y, mp_int *modulus)) { mp_int *base = mp_new(256); mp_int *exponent = mp_new(256); mp_int *modulus = mp_new(256); for (size_t i = 0; i < looplimit(8); i++) { mp_random_fill(base); mp_random_fill(exponent); mp_random_fill(modulus); mp_set_bit(modulus, 0, 1); /* we only support odd moduli */ log_start(); mp_int *out = mp_modarith(base, exponent, modulus); log_end(); mp_free(out); } mp_free(base); mp_free(exponent); mp_free(modulus); } static void test_mp_modadd(void) { test_mp_modarith(mp_modadd); } static void test_mp_modsub(void) { test_mp_modarith(mp_modsub); } static void test_mp_modmul(void) { test_mp_modarith(mp_modmul); } static void test_mp_modpow(void) { test_mp_modarith(mp_modpow); } static void test_mp_invert_mod_2to(void) { mp_int *x = mp_new(512); for (size_t i = 0; i < looplimit(32); i++) { mp_random_fill(x); mp_set_bit(x, 0, 1); /* input should be odd */ log_start(); mp_int *out = mp_invert_mod_2to(x, 511); log_end(); mp_free(out); } mp_free(x); } static void test_mp_modsqrt(void) { /* The prime isn't secret in this function (and in any case * finding a non-square on the fly would be prohibitively * annoying), so I hardcode a fixed one, selected to have a lot of * factors of two in p-1 so as to exercise lots of choices in the * algorithm. */ mp_int *p = MP_LITERAL(0xb56a517b206a88c73cfa9ec6f704c7030d18212cace82401); mp_int *nonsquare = MP_LITERAL(0x5); size_t bits = mp_max_bits(p); ModsqrtContext *sc = modsqrt_new(p, nonsquare); mp_free(p); mp_free(nonsquare); mp_int *x = mp_new(bits); unsigned success; /* Do one initial call to cause the lazily initialised sub-context * to be set up. This will take a while, but it can't be helped. */ mp_int *unwanted = mp_modsqrt(sc, x, &success); mp_free(unwanted); for (size_t i = 0; i < looplimit(8); i++) { mp_random_bits_into(x, bits - 1); log_start(); mp_int *out = mp_modsqrt(sc, x, &success); log_end(); mp_free(out); } mp_free(x); modsqrt_free(sc); } static WeierstrassCurve *wcurve(void) { mp_int *p = MP_LITERAL(0xc19337603dc856acf31e01375a696fdf5451); mp_int *a = MP_LITERAL(0x864946f50eecca4cde7abad4865e34be8f67); mp_int *b = MP_LITERAL(0x6a5bf56db3a03ba91cfbf3241916c90feeca); mp_int *nonsquare = mp_from_integer(3); WeierstrassCurve *wc = ecc_weierstrass_curve(p, a, b, nonsquare); mp_free(p); mp_free(a); mp_free(b); mp_free(nonsquare); return wc; } static WeierstrassPoint *wpoint(WeierstrassCurve *wc, size_t index) { mp_int *x = NULL, *y = NULL; WeierstrassPoint *wp; switch (index) { case 0: break; case 1: x = MP_LITERAL(0x12345); y = MP_LITERAL(0x3c2c799a365b53d003ef37dab65860bf80ae); break; case 2: x = MP_LITERAL(0x4e1c77e3c00f7c3b15869e6a4e5f86b3ee53); y = MP_LITERAL(0x5bde01693130591400b5c9d257d8325a44a5); break; case 3: x = MP_LITERAL(0xb5f0e722b2f0f7e729f55ba9f15511e3b399); y = MP_LITERAL(0x033d636b855c931cfe679f0b18db164a0d64); break; case 4: x = MP_LITERAL(0xb5f0e722b2f0f7e729f55ba9f15511e3b399); y = MP_LITERAL(0xbe55d3f4b86bc38ff4b6622c418e599546ed); break; default: unreachable("only 5 example Weierstrass points defined"); } if (x && y) { wp = ecc_weierstrass_point_new(wc, x, y); } else { wp = ecc_weierstrass_point_new_identity(wc); } if (x) mp_free(x); if (y) mp_free(y); return wp; } static void test_ecc_weierstrass_add(void) { WeierstrassCurve *wc = wcurve(); WeierstrassPoint *a = ecc_weierstrass_point_new_identity(wc); WeierstrassPoint *b = ecc_weierstrass_point_new_identity(wc); for (size_t i = 0; i < looplimit(5); i++) { for (size_t j = 0; j < looplimit(5); j++) { if (i == 0 || j == 0 || i == j || (i==3 && j==4) || (i==4 && j==3)) continue; /* difficult cases */ WeierstrassPoint *A = wpoint(wc, i), *B = wpoint(wc, j); ecc_weierstrass_point_copy_into(a, A); ecc_weierstrass_point_copy_into(b, B); ecc_weierstrass_point_free(A); ecc_weierstrass_point_free(B); log_start(); WeierstrassPoint *r = ecc_weierstrass_add(a, b); log_end(); ecc_weierstrass_point_free(r); } } ecc_weierstrass_point_free(a); ecc_weierstrass_point_free(b); ecc_weierstrass_curve_free(wc); } static void test_ecc_weierstrass_double(void) { WeierstrassCurve *wc = wcurve(); WeierstrassPoint *a = ecc_weierstrass_point_new_identity(wc); for (size_t i = 0; i < looplimit(5); i++) { WeierstrassPoint *A = wpoint(wc, i); ecc_weierstrass_point_copy_into(a, A); ecc_weierstrass_point_free(A); log_start(); WeierstrassPoint *r = ecc_weierstrass_double(a); log_end(); ecc_weierstrass_point_free(r); } ecc_weierstrass_point_free(a); ecc_weierstrass_curve_free(wc); } static void test_ecc_weierstrass_add_general(void) { WeierstrassCurve *wc = wcurve(); WeierstrassPoint *a = ecc_weierstrass_point_new_identity(wc); WeierstrassPoint *b = ecc_weierstrass_point_new_identity(wc); for (size_t i = 0; i < looplimit(5); i++) { for (size_t j = 0; j < looplimit(5); j++) { WeierstrassPoint *A = wpoint(wc, i), *B = wpoint(wc, j); ecc_weierstrass_point_copy_into(a, A); ecc_weierstrass_point_copy_into(b, B); ecc_weierstrass_point_free(A); ecc_weierstrass_point_free(B); log_start(); WeierstrassPoint *r = ecc_weierstrass_add_general(a, b); log_end(); ecc_weierstrass_point_free(r); } } ecc_weierstrass_point_free(a); ecc_weierstrass_point_free(b); ecc_weierstrass_curve_free(wc); } static void test_ecc_weierstrass_multiply(void) { WeierstrassCurve *wc = wcurve(); WeierstrassPoint *a = ecc_weierstrass_point_new_identity(wc); mp_int *exponent = mp_new(56); for (size_t i = 1; i < looplimit(5); i++) { WeierstrassPoint *A = wpoint(wc, i); ecc_weierstrass_point_copy_into(a, A); ecc_weierstrass_point_free(A); mp_random_fill(exponent); log_start(); WeierstrassPoint *r = ecc_weierstrass_multiply(a, exponent); log_end(); ecc_weierstrass_point_free(r); } ecc_weierstrass_point_free(a); ecc_weierstrass_curve_free(wc); mp_free(exponent); } static void test_ecc_weierstrass_is_identity(void) { WeierstrassCurve *wc = wcurve(); WeierstrassPoint *a = ecc_weierstrass_point_new_identity(wc); for (size_t i = 1; i < looplimit(5); i++) { WeierstrassPoint *A = wpoint(wc, i); ecc_weierstrass_point_copy_into(a, A); ecc_weierstrass_point_free(A); log_start(); ecc_weierstrass_is_identity(a); log_end(); } ecc_weierstrass_point_free(a); ecc_weierstrass_curve_free(wc); } static void test_ecc_weierstrass_get_affine(void) { WeierstrassCurve *wc = wcurve(); WeierstrassPoint *r = ecc_weierstrass_point_new_identity(wc); for (size_t i = 0; i < looplimit(4); i++) { WeierstrassPoint *A = wpoint(wc, i), *B = wpoint(wc, i+1); WeierstrassPoint *R = ecc_weierstrass_add_general(A, B); ecc_weierstrass_point_copy_into(r, R); ecc_weierstrass_point_free(A); ecc_weierstrass_point_free(B); ecc_weierstrass_point_free(R); log_start(); mp_int *x, *y; ecc_weierstrass_get_affine(r, &x, &y); log_end(); mp_free(x); mp_free(y); } ecc_weierstrass_point_free(r); ecc_weierstrass_curve_free(wc); } static void test_ecc_weierstrass_decompress(void) { WeierstrassCurve *wc = wcurve(); /* As in the mp_modsqrt test, prime the lazy initialisation of the * ModsqrtContext */ mp_int *x = mp_new(144); WeierstrassPoint *a = ecc_weierstrass_point_new_from_x(wc, x, 0); if (a) /* don't care whether this one succeeded */ ecc_weierstrass_point_free(a); for (size_t p = 0; p < looplimit(2); p++) { for (size_t i = 1; i < looplimit(5); i++) { WeierstrassPoint *A = wpoint(wc, i); mp_int *X; ecc_weierstrass_get_affine(A, &X, NULL); mp_copy_into(x, X); mp_free(X); ecc_weierstrass_point_free(A); log_start(); WeierstrassPoint *a = ecc_weierstrass_point_new_from_x(wc, x, p); log_end(); ecc_weierstrass_point_free(a); } } mp_free(x); ecc_weierstrass_curve_free(wc); } static MontgomeryCurve *mcurve(void) { mp_int *p = MP_LITERAL(0xde978eb1db35236a5792e9f0c04d86000659); mp_int *a = MP_LITERAL(0x799b62a612b1b30e1c23cea6d67b2e33c51a); mp_int *b = MP_LITERAL(0x944bf9042b56821a8c9e0b49b636c2502b2b); MontgomeryCurve *mc = ecc_montgomery_curve(p, a, b); mp_free(p); mp_free(a); mp_free(b); return mc; } static MontgomeryPoint *mpoint(MontgomeryCurve *wc, size_t index) { mp_int *x = NULL; MontgomeryPoint *mp; switch (index) { case 0: x = MP_LITERAL(31415); break; case 1: x = MP_LITERAL(0x4d352c654c06eecfe19104118857b38398e8); break; case 2: x = MP_LITERAL(0x03fca2a73983bc3434caae3134599cd69cce); break; case 3: x = MP_LITERAL(0xa0fd735ce9b3406498b5f035ee655bda4e15); break; case 4: x = MP_LITERAL(0x7c7f46a00cc286dbe47db39b6d8f5efd920e); break; case 5: x = MP_LITERAL(0x07a6dc30d3b320448e6f8999be417e6b7c6b); break; case 6: x = MP_LITERAL(0x7832da5fc16dfbd358170b2b96896cd3cd06); break; default: unreachable("only 7 example Weierstrass points defined"); } mp = ecc_montgomery_point_new(wc, x); mp_free(x); return mp; } static void test_ecc_montgomery_diff_add(void) { MontgomeryCurve *wc = mcurve(); MontgomeryPoint *a = NULL, *b = NULL, *c = NULL; for (size_t i = 0; i < looplimit(5); i++) { MontgomeryPoint *A = mpoint(wc, i); MontgomeryPoint *B = mpoint(wc, i); MontgomeryPoint *C = mpoint(wc, i); if (!a) { a = A; b = B; c = C; } else { ecc_montgomery_point_copy_into(a, A); ecc_montgomery_point_copy_into(b, B); ecc_montgomery_point_copy_into(c, C); ecc_montgomery_point_free(A); ecc_montgomery_point_free(B); ecc_montgomery_point_free(C); } log_start(); MontgomeryPoint *r = ecc_montgomery_diff_add(b, c, a); log_end(); ecc_montgomery_point_free(r); } ecc_montgomery_point_free(a); ecc_montgomery_point_free(b); ecc_montgomery_point_free(c); ecc_montgomery_curve_free(wc); } static void test_ecc_montgomery_double(void) { MontgomeryCurve *wc = mcurve(); MontgomeryPoint *a = NULL; for (size_t i = 0; i < looplimit(7); i++) { MontgomeryPoint *A = mpoint(wc, i); if (!a) { a = A; } else { ecc_montgomery_point_copy_into(a, A); ecc_montgomery_point_free(A); } log_start(); MontgomeryPoint *r = ecc_montgomery_double(a); log_end(); ecc_montgomery_point_free(r); } ecc_montgomery_point_free(a); ecc_montgomery_curve_free(wc); } static void test_ecc_montgomery_multiply(void) { MontgomeryCurve *wc = mcurve(); MontgomeryPoint *a = NULL; mp_int *exponent = mp_new(56); for (size_t i = 0; i < looplimit(7); i++) { MontgomeryPoint *A = mpoint(wc, i); if (!a) { a = A; } else { ecc_montgomery_point_copy_into(a, A); ecc_montgomery_point_free(A); } mp_random_fill(exponent); log_start(); MontgomeryPoint *r = ecc_montgomery_multiply(a, exponent); log_end(); ecc_montgomery_point_free(r); } ecc_montgomery_point_free(a); ecc_montgomery_curve_free(wc); mp_free(exponent); } static void test_ecc_montgomery_get_affine(void) { MontgomeryCurve *wc = mcurve(); MontgomeryPoint *r = NULL; for (size_t i = 0; i < looplimit(5); i++) { MontgomeryPoint *A = mpoint(wc, i); MontgomeryPoint *B = mpoint(wc, i); MontgomeryPoint *C = mpoint(wc, i); MontgomeryPoint *R = ecc_montgomery_diff_add(B, C, A); ecc_montgomery_point_free(A); ecc_montgomery_point_free(B); ecc_montgomery_point_free(C); if (!r) { r = R; } else { ecc_montgomery_point_copy_into(r, R); ecc_montgomery_point_free(R); } log_start(); mp_int *x; ecc_montgomery_get_affine(r, &x); log_end(); mp_free(x); } ecc_montgomery_point_free(r); ecc_montgomery_curve_free(wc); } static EdwardsCurve *ecurve(void) { mp_int *p = MP_LITERAL(0xfce2dac1704095de0b5c48876c45063cd475); mp_int *d = MP_LITERAL(0xbd4f77401c3b14ae1742a7d1d367adac8f3e); mp_int *a = MP_LITERAL(0x51d0845da3fa871aaac4341adea53b861919); mp_int *nonsquare = mp_from_integer(2); EdwardsCurve *ec = ecc_edwards_curve(p, d, a, nonsquare); mp_free(p); mp_free(d); mp_free(a); mp_free(nonsquare); return ec; } static EdwardsPoint *epoint(EdwardsCurve *wc, size_t index) { mp_int *x, *y; EdwardsPoint *ep; switch (index) { case 0: x = MP_LITERAL(0x0); y = MP_LITERAL(0x1); break; case 1: x = MP_LITERAL(0x3d8aef0294a67c1c7e8e185d987716250d7c); y = MP_LITERAL(0x27184); break; case 2: x = MP_LITERAL(0xf44ed5b8a6debfd3ab24b7874cd2589fd672); y = MP_LITERAL(0xd635d8d15d367881c8a3af472c8fe487bf40); break; case 3: x = MP_LITERAL(0xde114ecc8b944684415ef81126a07269cd30); y = MP_LITERAL(0xbe0fd45ff67ebba047ed0ec5a85d22e688a1); break; case 4: x = MP_LITERAL(0x76bd2f90898d271b492c9c20dd7bbfe39fe5); y = MP_LITERAL(0xbf1c82698b4a5a12c1057631c1ebdc216ae2); break; default: unreachable("only 5 example Edwards points defined"); } ep = ecc_edwards_point_new(wc, x, y); mp_free(x); mp_free(y); return ep; } static void test_ecc_edwards_add(void) { EdwardsCurve *ec = ecurve(); EdwardsPoint *a = NULL, *b = NULL; for (size_t i = 0; i < looplimit(5); i++) { for (size_t j = 0; j < looplimit(5); j++) { EdwardsPoint *A = epoint(ec, i), *B = epoint(ec, j); if (!a) { a = A; b = B; } else { ecc_edwards_point_copy_into(a, A); ecc_edwards_point_copy_into(b, B); ecc_edwards_point_free(A); ecc_edwards_point_free(B); } log_start(); EdwardsPoint *r = ecc_edwards_add(a, b); log_end(); ecc_edwards_point_free(r); } } ecc_edwards_point_free(a); ecc_edwards_point_free(b); ecc_edwards_curve_free(ec); } static void test_ecc_edwards_multiply(void) { EdwardsCurve *ec = ecurve(); EdwardsPoint *a = NULL; mp_int *exponent = mp_new(56); for (size_t i = 1; i < looplimit(5); i++) { EdwardsPoint *A = epoint(ec, i); if (!a) { a = A; } else { ecc_edwards_point_copy_into(a, A); ecc_edwards_point_free(A); } mp_random_fill(exponent); log_start(); EdwardsPoint *r = ecc_edwards_multiply(a, exponent); log_end(); ecc_edwards_point_free(r); } ecc_edwards_point_free(a); ecc_edwards_curve_free(ec); mp_free(exponent); } static void test_ecc_edwards_eq(void) { EdwardsCurve *ec = ecurve(); EdwardsPoint *a = NULL, *b = NULL; for (size_t i = 0; i < looplimit(5); i++) { for (size_t j = 0; j < looplimit(5); j++) { EdwardsPoint *A = epoint(ec, i), *B = epoint(ec, j); if (!a) { a = A; b = B; } else { ecc_edwards_point_copy_into(a, A); ecc_edwards_point_copy_into(b, B); ecc_edwards_point_free(A); ecc_edwards_point_free(B); } log_start(); ecc_edwards_eq(a, b); log_end(); } } ecc_edwards_point_free(a); ecc_edwards_point_free(b); ecc_edwards_curve_free(ec); } static void test_ecc_edwards_get_affine(void) { EdwardsCurve *ec = ecurve(); EdwardsPoint *r = NULL; for (size_t i = 0; i < looplimit(4); i++) { EdwardsPoint *A = epoint(ec, i), *B = epoint(ec, i+1); EdwardsPoint *R = ecc_edwards_add(A, B); ecc_edwards_point_free(A); ecc_edwards_point_free(B); if (!r) { r = R; } else { ecc_edwards_point_copy_into(r, R); ecc_edwards_point_free(R); } log_start(); mp_int *x, *y; ecc_edwards_get_affine(r, &x, &y); log_end(); mp_free(x); mp_free(y); } ecc_edwards_point_free(r); ecc_edwards_curve_free(ec); } static void test_ecc_edwards_decompress(void) { EdwardsCurve *ec = ecurve(); /* As in the mp_modsqrt test, prime the lazy initialisation of the * ModsqrtContext */ mp_int *y = mp_new(144); EdwardsPoint *a = ecc_edwards_point_new_from_y(ec, y, 0); if (a) /* don't care whether this one succeeded */ ecc_edwards_point_free(a); for (size_t p = 0; p < looplimit(2); p++) { for (size_t i = 0; i < looplimit(5); i++) { EdwardsPoint *A = epoint(ec, i); mp_int *Y; ecc_edwards_get_affine(A, NULL, &Y); mp_copy_into(y, Y); mp_free(Y); ecc_edwards_point_free(A); log_start(); EdwardsPoint *a = ecc_edwards_point_new_from_y(ec, y, p); log_end(); ecc_edwards_point_free(a); } } mp_free(y); ecc_edwards_curve_free(ec); } static void test_cipher(const ssh_cipheralg *calg) { ssh_cipher *c = ssh_cipher_new(calg); if (!c) { test_skipped = true; return; } const ssh2_macalg *malg = calg->required_mac; ssh2_mac *m = NULL; if (malg) { m = ssh2_mac_new(malg, c); if (!m) { ssh_cipher_free(c); test_skipped = true; return; } } uint8_t *ckey = snewn(calg->padded_keybytes, uint8_t); uint8_t *civ = snewn(calg->blksize, uint8_t); uint8_t *mkey = malg ? snewn(malg->keylen, uint8_t) : NULL; size_t datalen = calg->blksize * 8; size_t maclen = malg ? malg->len : 0; uint8_t *data = snewn(datalen + maclen, uint8_t); size_t lenlen = 4; uint8_t *lendata = snewn(lenlen, uint8_t); for (size_t i = 0; i < looplimit(16); i++) { random_read(ckey, calg->padded_keybytes); if (malg) random_read(mkey, malg->keylen); random_read(data, datalen); random_read(lendata, lenlen); if (i == 0) { /* Ensure one of our test IVs will cause SDCTR wraparound */ memset(civ, 0xFF, calg->blksize); } else { random_read(civ, calg->blksize); } uint8_t seqbuf[4]; random_read(seqbuf, 4); uint32_t seq = GET_32BIT_MSB_FIRST(seqbuf); log_start(); ssh_cipher_setkey(c, ckey); ssh_cipher_setiv(c, civ); if (m) ssh2_mac_setkey(m, make_ptrlen(mkey, malg->keylen)); if (calg->flags & SSH_CIPHER_SEPARATE_LENGTH) ssh_cipher_encrypt_length(c, data, datalen, seq); ssh_cipher_encrypt(c, data, datalen); if (m) { ssh2_mac_generate(m, data, datalen, seq); ssh2_mac_verify(m, data, datalen, seq); } if (calg->flags & SSH_CIPHER_SEPARATE_LENGTH) ssh_cipher_decrypt_length(c, data, datalen, seq); ssh_cipher_decrypt(c, data, datalen); log_end(); } sfree(ckey); sfree(civ); sfree(mkey); sfree(data); sfree(lendata); if (m) ssh2_mac_free(m); ssh_cipher_free(c); } #define CIPHER_TESTFN(Y_unused, cipher) \ static void test_cipher_##cipher(void) { test_cipher(&cipher); } CIPHERS(CIPHER_TESTFN, Y_unused) static void test_mac(const ssh2_macalg *malg) { ssh2_mac *m = ssh2_mac_new(malg, NULL); if (!m) { test_skipped = true; return; } uint8_t *mkey = snewn(malg->keylen, uint8_t); size_t datalen = 256; size_t maclen = malg->len; uint8_t *data = snewn(datalen + maclen, uint8_t); for (size_t i = 0; i < looplimit(16); i++) { random_read(mkey, malg->keylen); random_read(data, datalen); uint8_t seqbuf[4]; random_read(seqbuf, 4); uint32_t seq = GET_32BIT_MSB_FIRST(seqbuf); log_start(); ssh2_mac_setkey(m, make_ptrlen(mkey, malg->keylen)); ssh2_mac_generate(m, data, datalen, seq); ssh2_mac_verify(m, data, datalen, seq); log_end(); } sfree(mkey); sfree(data); ssh2_mac_free(m); } #define MAC_TESTFN(Y_unused, mac) \ static void test_mac_##mac(void) { test_mac(&mac); } MACS(MAC_TESTFN, Y_unused) static void test_hash(const ssh_hashalg *halg) { ssh_hash *h = ssh_hash_new(halg); if (!h) { test_skipped = true; return; } size_t datalen = 256; uint8_t *data = snewn(datalen, uint8_t); uint8_t *hash = snewn(halg->hlen, uint8_t); for (size_t i = 0; i < looplimit(16); i++) { random_read(data, datalen); log_start(); put_data(h, data, datalen); ssh_hash_final(h, hash); log_end(); h = ssh_hash_new(halg); } sfree(data); sfree(hash); ssh_hash_free(h); } #define HASH_TESTFN(Y_unused, hash) \ static void test_hash_##hash(void) { test_hash(&hash); } HASHES(HASH_TESTFN, Y_unused) struct test { const char *testname; void (*testfn)(void); }; static void test_argon2(void) { /* * We can only expect the Argon2i variant to pass this stringent * test for no data-dependency, because the other two variants of * Argon2 have _deliberate_ data-dependency. */ size_t inlen = 48+16+24+8; uint8_t *indata = snewn(inlen, uint8_t); ptrlen password = make_ptrlen(indata, 48); ptrlen salt = make_ptrlen(indata+48, 16); ptrlen secret = make_ptrlen(indata+48+16, 24); ptrlen assoc = make_ptrlen(indata+48+16+24, 8); strbuf *outdata = strbuf_new(); strbuf_append(outdata, 256); for (size_t i = 0; i < looplimit(16); i++) { strbuf_clear(outdata); random_read(indata, inlen); log_start(); argon2(Argon2i, 32, 2, 2, 144, password, salt, secret, assoc, outdata); log_end(); } sfree(indata); strbuf_free(outdata); } static const struct test tests[] = { #define STRUCT_TEST(X) { #X, test_##X }, TESTLIST(STRUCT_TEST) #undef STRUCT_TEST }; void dputs(const char *buf) { fputs(buf, stderr); } int main(int argc, char **argv) { bool doing_opts = true; const char *pname = argv[0]; uint8_t tests_to_run[lenof(tests)]; bool keep_outfiles = false; bool test_names_given = false; memset(tests_to_run, 1, sizeof(tests_to_run)); random_hash = ssh_hash_new(&ssh_sha256); while (--argc > 0) { char *p = *++argv; if (p[0] == '-' && doing_opts) { if (!strcmp(p, "-O")) { if (--argc <= 0) { fprintf(stderr, "'-O' expects a directory name\n"); return 1; } outdir = *++argv; } else if (!strcmp(p, "-k") || !strcmp(p, "--keep")) { keep_outfiles = true; } else if (!strcmp(p, "--")) { doing_opts = false; } else if (!strcmp(p, "--help")) { printf(" usage: drrun -c test/sclog/libsclog.so -- " "%s -O \n", pname); printf("options: -O " "put log files in the specified directory\n"); printf(" -k, --keep " "do not delete log files for tests that passed\n"); printf(" also: --help " "display this text\n"); return 0; } else { fprintf(stderr, "unknown command line option '%s'\n", p); return 1; } } else { if (!test_names_given) { test_names_given = true; memset(tests_to_run, 0, sizeof(tests_to_run)); } bool found_one = false; for (size_t i = 0; i < lenof(tests); i++) { if (wc_match(p, tests[i].testname)) { tests_to_run[i] = 1; found_one = true; } } if (!found_one) { fprintf(stderr, "no test name matched '%s'\n", p); return 1; } } } bool is_dry_run = dry_run(); if (is_dry_run) { printf("Dry run (DynamoRIO instrumentation not detected)\n"); } else { /* Print the address of main() in this run. The idea is that * if this image is compiled to be position-independent, then * PC values in the logs won't match the ones you get if you * disassemble the binary, so it'll be harder to match up the * log messages to the code. But if you know the address of a * fixed (and not inlined) function in both worlds, you can * find out the offset between them. */ printf("Live run, main = %p\n", (void *)main); if (!outdir) { fprintf(stderr, "expected -O option\n"); return 1; } printf("Will write log files to %s\n", outdir); } size_t nrun = 0, npass = 0; for (size_t i = 0; i < lenof(tests); i++) { bool keep_these_outfiles = true; if (!tests_to_run[i]) continue; const struct test *test = &tests[i]; printf("Running test %s ... ", test->testname); fflush(stdout); test_skipped = false; random_seed(test->testname); test_basename = test->testname; test_index = 0; test->testfn(); if (test_skipped) { /* Used for e.g. tests of hardware-accelerated crypto when * the hardware acceleration isn't available */ printf("skipped\n"); continue; } nrun++; if (is_dry_run) { printf("dry run done\n"); continue; /* test files won't exist anyway */ } if (test_index < 2) { printf("FAIL: test did not generate multiple output files\n"); goto test_done; } char *firstfile = log_filename(test_basename, 0); FILE *firstfp = fopen(firstfile, "rb"); if (!firstfp) { printf("ERR: %s: open: %s\n", firstfile, strerror(errno)); goto test_done; } for (size_t i = 1; i < test_index; i++) { char *nextfile = log_filename(test_basename, i); FILE *nextfp = fopen(nextfile, "rb"); if (!nextfp) { printf("ERR: %s: open: %s\n", nextfile, strerror(errno)); goto test_done; } rewind(firstfp); char buf1[4096], bufn[4096]; bool compare_ok = false; while (true) { size_t r1 = fread(buf1, 1, sizeof(buf1), firstfp); size_t rn = fread(bufn, 1, sizeof(bufn), nextfp); if (r1 != rn) { printf("FAIL: %s %s: different lengths\n", firstfile, nextfile); break; } if (r1 == 0) { if (feof(firstfp) && feof(nextfp)) { compare_ok = true; } else { printf("FAIL: %s %s: error at end of file\n", firstfile, nextfile); } break; } if (memcmp(buf1, bufn, r1) != 0) { printf("FAIL: %s %s: different content\n", firstfile, nextfile); break; } } fclose(nextfp); sfree(nextfile); if (!compare_ok) { goto test_done; } } fclose(firstfp); sfree(firstfile); printf("pass\n"); npass++; keep_these_outfiles = keep_outfiles; test_done: if (!keep_these_outfiles) { for (size_t i = 0; i < test_index; i++) { char *file = log_filename(test_basename, i); remove(file); sfree(file); } } } ssh_hash_free(random_hash); if (npass == nrun) { printf("All tests passed\n"); return 0; } else { printf("%"SIZEu" tests failed\n", nrun - npass); return 1; } } putty-0.76/testzlib.c0000644000175000017500000000565314072266313011575 00000000000000/* * Main program to compile sshzlib.c into a zlib decoding tool. * * This is potentially a handy tool in its own right for picking apart * Zip files or PDFs or PNGs, because it accepts the bare Deflate * format and the zlib wrapper format, unlike 'zcat' which accepts * only the gzip wrapper format. * * It's also useful as a means for a fuzzer to get reasonably direct * access to PuTTY's zlib decompressor. */ #include #include #include #include #include "defs.h" #include "ssh.h" void out_of_memory(void) { fprintf(stderr, "Out of memory!\n"); exit(1); } void dputs(const char *buf) { fputs(buf, stderr); } int main(int argc, char **argv) { unsigned char buf[16], *outbuf; int ret, outlen; ssh_decompressor *handle; int noheader = false, opts = true; char *filename = NULL; FILE *fp; while (--argc) { char *p = *++argv; if (p[0] == '-' && opts) { if (!strcmp(p, "-d")) { noheader = true; } else if (!strcmp(p, "--")) { opts = false; /* next thing is filename */ } else if (!strcmp(p, "--help")) { printf("usage: testzlib decode zlib (RFC1950) data" " from standard input\n"); printf(" testzlib -d decode Deflate (RFC1951) data" " from standard input\n"); printf(" testzlib --help display this text\n"); return 0; } else { fprintf(stderr, "unknown command line option '%s'\n", p); return 1; } } else if (!filename) { filename = p; } else { fprintf(stderr, "can only handle one filename\n"); return 1; } } handle = ssh_decompressor_new(&ssh_zlib); if (noheader) { /* * Provide missing zlib header if -d was specified. */ static const unsigned char ersatz_zlib_header[] = { 0x78, 0x9C }; ssh_decompressor_decompress( handle, ersatz_zlib_header, sizeof(ersatz_zlib_header), &outbuf, &outlen); assert(outlen == 0); } if (filename) fp = fopen(filename, "rb"); else fp = stdin; if (!fp) { assert(filename); fprintf(stderr, "unable to open '%s'\n", filename); return 1; } while (1) { ret = fread(buf, 1, sizeof(buf), fp); if (ret <= 0) break; ssh_decompressor_decompress(handle, buf, ret, &outbuf, &outlen); if (outbuf) { if (outlen) fwrite(outbuf, 1, outlen, stdout); sfree(outbuf); } else { fprintf(stderr, "decoding error\n"); fclose(fp); return 1; } } ssh_decompressor_free(handle); if (filename) fclose(fp); return 0; } putty-0.76/time.c0000644000175000017500000000055614072266313010670 00000000000000/* * Portable implementation of ltime() for any ISO-C platform where * time_t behaves. (In practice, we've found that platforms such as * Windows and Mac have needed their own specialised implementations.) */ #include #include struct tm ltime(void) { time_t t; time(&t); assert (t != ((time_t)-1)); return *localtime(&t); } putty-0.76/timing.c0000644000175000017500000001371714072266313011224 00000000000000/* * 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 #include #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 if (a->ctx < b->ctx) return -1; else if (a->ctx > b->ctx) return +1; /* * Failing _that_, the two entries genuinely are equal, and we * never have a need to store them separately in the tree. */ return 0; } static int compare_timer_contexts(void *av, void *bv) { char *a = (char *)av; char *b = (char *)bv; if (a < b) return -1; else if (a > b) return +1; return 0; } static void init_timers(void) { 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 */ } first = (struct timer *)index234(timers, 0); if (first == t) { /* * This timer is the very first on the list, so we must * notify the front end. */ timer_change_notify(first->now); } return when; } unsigned long timing_last_clock(void) { /* * Return the last value we stored in 'now'. In particular, * calling this just after schedule_timer returns the value of * 'now' that was used to decide when the timer you just set would * go off. */ return now; } /* * Call to run any timers whose time has reached the present. * Returns the time (in ticks) expected until the next timer after * that triggers. */ bool 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); } else { /* * This is the first still-active timer that is in the * future. Return how long it has yet to go. */ *next = first->now; return true; } } } /* * Call to expire all timers associated with a given context. */ void expire_timer_context(void *ctx) { init_timers(); /* * We don't bother to check the return value; if the context * already wasn't in the tree (presumably because no timers * ever actually got scheduled for it) then that's fine and we * simply don't need to do anything. */ del234(timer_contexts, ctx); } putty-0.76/tree234.c0000644000175000017500000015035714072266313011127 00000000000000/* * tree234.c: reasonably generic counted 2-3-4 tree routines. * * This file is copyright 1999-2001 Simon Tatham. * * 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: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL SIMON TATHAM BE LIABLE FOR * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include #include #include #include "defs.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; cmpfn234 cmp; }; struct node234_Tag { node234 *parent; node234 *kids[4]; int counts[4]; void *elems[3]; }; /* * Create a 2-3-4 tree. */ tree234 *newtree234(cmpfn234 cmp) { tree234 *ret = snew(tree234); LOG(("created tree %p\n", ret)); ret->root = NULL; ret->cmp = cmp; return ret; } /* * Free a 2-3-4 tree (not including freeing the elements). */ static void freenode234(node234 * n) { if (!n) return; freenode234(n->kids[0]); freenode234(n->kids[1]); freenode234(n->kids[2]); freenode234(n->kids[3]); sfree(n); } void freetree234(tree234 * t) { freenode234(t->root); sfree(t); } /* * Internal function to count a node. */ static int countnode234(node234 * n) { int count = 0; int i; if (!n) return 0; for (i = 0; i < 4; i++) count += n->counts[i]; for (i = 0; i < 3; i++) if (n->elems[i]) count++; return count; } /* * Internal function to return the number of elements in a node. */ static int elements234(node234 *n) { int i; for (i = 0; i < 3; i++) if (!n->elems[i]) break; return i; } /* * Count the elements in a tree. */ int count234(tree234 * t) { if (t->root) return countnode234(t->root); else return 0; } /* * Add an element e to a 2-3-4 tree t. Returns e on success, or if * an existing element compares equal, returns that. */ static void *add234_internal(tree234 * t, void *e, int index) { node234 *n, **np, *left, *right; void *orig_e = e; int c, lcount, rcount; LOG(("adding node %p to tree %p\n", e, t)); if (t->root == NULL) { t->root = snew(node234); t->root->elems[1] = t->root->elems[2] = NULL; t->root->kids[0] = t->root->kids[1] = NULL; t->root->kids[2] = t->root->kids[3] = NULL; t->root->counts[0] = t->root->counts[1] = 0; t->root->counts[2] = t->root->counts[3] = 0; t->root->parent = NULL; t->root->elems[0] = e; LOG((" created root %p\n", t->root)); return orig_e; } n = NULL; /* placate gcc; will always be set below since t->root != NULL */ np = &t->root; while (*np) { int childnum; n = *np; LOG((" node %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])); if (index >= 0) { if (!n->kids[0]) { /* * Leaf node. We want to insert at kid position * equal to the index: * * 0 A 1 B 2 C 3 */ childnum = index; } else { /* * Internal node. We always descend through it (add * always starts at the bottom, never in the * middle). */ do { /* this is a do ... while (0) to allow `break' */ if (index <= n->counts[0]) { childnum = 0; break; } index -= n->counts[0] + 1; if (index <= n->counts[1]) { childnum = 1; break; } index -= n->counts[1] + 1; if (index <= n->counts[2]) { childnum = 2; break; } index -= n->counts[2] + 1; if (index <= n->counts[3]) { childnum = 3; break; } return NULL; /* error: index out of range */ } while (0); } } else { if ((c = t->cmp(e, n->elems[0])) < 0) childnum = 0; else if (c == 0) return n->elems[0]; /* already exists */ else if (n->elems[1] == NULL || (c = t->cmp(e, n->elems[1])) < 0) childnum = 1; else if (c == 0) return n->elems[1]; /* already exists */ else if (n->elems[2] == NULL || (c = t->cmp(e, n->elems[2])) < 0) childnum = 2; else if (c == 0) return n->elems[2]; /* already exists */ else childnum = 3; } np = &n->kids[childnum]; LOG((" moving to child %d (%p)\n", childnum, *np)); } /* * We need to insert the new element in n at position np. */ left = NULL; lcount = 0; right = NULL; rcount = 0; while (n) { 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]; n->counts[2] = n->counts[1]; n->elems[1] = n->elems[0]; n->kids[1] = right; n->counts[1] = rcount; n->elems[0] = e; n->kids[0] = left; n->counts[0] = lcount; } else { /* np == &n->kids[1] */ LOG((" inserting on right of 2-node\n")); n->kids[2] = right; n->counts[2] = rcount; n->elems[1] = e; n->kids[1] = left; n->counts[1] = lcount; } if (n->kids[0]) n->kids[0]->parent = n; if (n->kids[1]) n->kids[1]->parent = n; if (n->kids[2]) n->kids[2]->parent = n; LOG((" done\n")); break; } else if (n->elems[2] == NULL) { /* * Insert in a 3-node; simple. */ if (np == &n->kids[0]) { LOG((" inserting on left of 3-node\n")); n->kids[3] = n->kids[2]; n->counts[3] = n->counts[2]; n->elems[2] = n->elems[1]; n->kids[2] = n->kids[1]; n->counts[2] = n->counts[1]; n->elems[1] = n->elems[0]; n->kids[1] = right; n->counts[1] = rcount; n->elems[0] = e; n->kids[0] = left; n->counts[0] = lcount; } else if (np == &n->kids[1]) { LOG((" inserting in middle of 3-node\n")); n->kids[3] = n->kids[2]; n->counts[3] = n->counts[2]; n->elems[2] = n->elems[1]; n->kids[2] = right; n->counts[2] = rcount; n->elems[1] = e; n->kids[1] = left; n->counts[1] = lcount; } else { /* np == &n->kids[2] */ LOG((" inserting on right of 3-node\n")); n->kids[3] = right; n->counts[3] = rcount; n->elems[2] = e; n->kids[2] = left; n->counts[2] = lcount; } if (n->kids[0]) n->kids[0]->parent = n; if (n->kids[1]) n->kids[1]->parent = n; if (n->kids[2]) n->kids[2]->parent = n; if (n->kids[3]) n->kids[3]->parent = n; LOG((" done\n")); break; } else { node234 *m = snew(node234); m->parent = n->parent; LOG((" splitting a 4-node; created new node %p\n", m)); /* * Insert in a 4-node; split into a 2-node and a * 3-node, and move focus up a level. * * I don't think it matters which way round we put the * 2 and the 3. For simplicity, we'll put the 3 first * always. */ if (np == &n->kids[0]) { m->kids[0] = left; m->counts[0] = lcount; m->elems[0] = e; m->kids[1] = right; m->counts[1] = rcount; m->elems[1] = n->elems[0]; m->kids[2] = n->kids[1]; m->counts[2] = n->counts[1]; e = n->elems[1]; n->kids[0] = n->kids[2]; n->counts[0] = n->counts[2]; n->elems[0] = n->elems[2]; n->kids[1] = n->kids[3]; n->counts[1] = n->counts[3]; } else if (np == &n->kids[1]) { m->kids[0] = n->kids[0]; m->counts[0] = n->counts[0]; m->elems[0] = n->elems[0]; m->kids[1] = left; m->counts[1] = lcount; m->elems[1] = e; m->kids[2] = right; m->counts[2] = rcount; e = n->elems[1]; n->kids[0] = n->kids[2]; n->counts[0] = n->counts[2]; n->elems[0] = n->elems[2]; n->kids[1] = n->kids[3]; n->counts[1] = n->counts[3]; } else if (np == &n->kids[2]) { m->kids[0] = n->kids[0]; m->counts[0] = n->counts[0]; m->elems[0] = n->elems[0]; m->kids[1] = n->kids[1]; m->counts[1] = n->counts[1]; m->elems[1] = n->elems[1]; m->kids[2] = left; m->counts[2] = lcount; /* e = e; */ n->kids[0] = right; n->counts[0] = rcount; n->elems[0] = n->elems[2]; n->kids[1] = n->kids[3]; n->counts[1] = n->counts[3]; } else { /* np == &n->kids[3] */ m->kids[0] = n->kids[0]; m->counts[0] = n->counts[0]; m->elems[0] = n->elems[0]; m->kids[1] = n->kids[1]; m->counts[1] = n->counts[1]; m->elems[1] = n->elems[1]; m->kids[2] = n->kids[2]; m->counts[2] = n->counts[2]; n->kids[0] = left; n->counts[0] = lcount; n->elems[0] = e; n->kids[1] = right; n->counts[1] = rcount; e = n->elems[2]; } m->kids[3] = n->kids[3] = n->kids[2] = NULL; m->counts[3] = n->counts[3] = n->counts[2] = 0; m->elems[2] = n->elems[2] = n->elems[1] = NULL; if (m->kids[0]) m->kids[0]->parent = m; if (m->kids[1]) m->kids[1]->parent = m; if (m->kids[2]) m->kids[2]->parent = m; if (n->kids[0]) n->kids[0]->parent = n; if (n->kids[1]) n->kids[1]->parent = n; LOG((" left (%p): %p/%d [%p] %p/%d [%p] %p/%d\n", m, m->kids[0], m->counts[0], m->elems[0], m->kids[1], m->counts[1], m->elems[1], m->kids[2], m->counts[2])); LOG((" right (%p): %p/%d [%p] %p/%d\n", n, n->kids[0], n->counts[0], n->elems[0], n->kids[1], n->counts[1])); left = m; lcount = countnode234(left); right = n; rcount = countnode234(right); } if (n->parent) np = (n->parent->kids[0] == n ? &n->parent->kids[0] : n->parent->kids[1] == n ? &n->parent->kids[1] : n->parent->kids[2] == n ? &n->parent->kids[2] : &n->parent->kids[3]); n = n->parent; } /* * If we've come out of here by `break', n will still be * non-NULL and all we need to do is go back up the tree * updating counts. If we've come here because n is NULL, we * need to create a new root for the tree because the old one * has just split into two. */ if (n) { while (n->parent) { int count = countnode234(n); int childnum; childnum = (n->parent->kids[0] == n ? 0 : n->parent->kids[1] == n ? 1 : n->parent->kids[2] == n ? 2 : 3); n->parent->counts[childnum] = count; n = n->parent; } } else { LOG((" root is overloaded, split into two\n")); t->root = snew(node234); t->root->kids[0] = left; t->root->counts[0] = lcount; t->root->elems[0] = e; t->root->kids[1] = right; t->root->counts[1] = rcount; t->root->elems[1] = NULL; t->root->kids[2] = NULL; t->root->counts[2] = 0; t->root->elems[2] = NULL; t->root->kids[3] = NULL; t->root->counts[3] = 0; t->root->parent = NULL; if (t->root->kids[0]) t->root->kids[0]->parent = t->root; if (t->root->kids[1]) t->root->kids[1]->parent = t->root; LOG((" new root is %p/%d [%p] %p/%d\n", t->root->kids[0], t->root->counts[0], t->root->elems[0], t->root->kids[1], t->root->counts[1])); } return orig_e; } void *add234(tree234 * t, void *e) { if (!t->cmp) /* tree is unsorted */ return NULL; return add234_internal(t, e, -1); } void *addpos234(tree234 * t, void *e, int index) { if (index < 0 || /* index out of range */ t->cmp) /* tree is sorted */ return NULL; /* return failure */ return add234_internal(t, e, index); /* this checks the upper bound */ } /* * Look up the element at a given numeric index in a 2-3-4 tree. * Returns NULL if the index is out of range. */ void *index234(tree234 * t, int index) { node234 *n; if (!t->root) return NULL; /* tree is empty */ if (index < 0 || index >= countnode234(t->root)) return NULL; /* out of range */ n = t->root; while (n) { if (index < n->counts[0]) n = n->kids[0]; else if (index -= n->counts[0] + 1, index < 0) return n->elems[0]; else if (index < n->counts[1]) n = n->kids[1]; else if (index -= n->counts[1] + 1, index < 0) return n->elems[1]; else if (index < n->counts[2]) n = n->kids[2]; else if (index -= n->counts[2] + 1, index < 0) return n->elems[2]; else n = n->kids[3]; } /* We shouldn't ever get here. I wonder how we did. */ return NULL; } /* * Find an element e in a sorted 2-3-4 tree t. Returns NULL if not * found. e is always passed as the first argument to cmp, so cmp * can be an asymmetric function if desired. cmp can also be passed * as NULL, in which case the compare function from the tree proper * will be used. */ void *findrelpos234(tree234 * t, void *e, cmpfn234 cmp, int relation, int *index) { search234_state ss; int reldir = (relation == REL234_LT || relation == REL234_LE ? -1 : relation == REL234_GT || relation == REL234_GE ? +1 : 0); bool equal_permitted = (relation != REL234_LT && relation != REL234_GT); void *toret; /* Only LT / GT relations are permitted with a null query element. */ assert(!(equal_permitted && !e)); if (cmp == NULL) cmp = t->cmp; search234_start(&ss, t); while (ss.element) { int cmpret; if (e) { cmpret = cmp(e, ss.element); } else { cmpret = -reldir; /* invent a fixed compare result */ } if (cmpret == 0) { /* * We've found an element that compares exactly equal to * the query element. */ if (equal_permitted) { /* If our search relation permits equality, we've * finished already. */ if (index) *index = ss.index; return ss.element; } else { /* Otherwise, pretend this element was slightly too * big/small, according to the direction of search. */ cmpret = reldir; } } search234_step(&ss, cmpret); } /* * No element compares equal to the one we were after, but * ss.index indicates the index that element would have if it were * inserted. * * So if our search relation is EQ, we must simply return failure. */ if (relation == REL234_EQ) return NULL; /* * Otherwise, we must do an index lookup for the previous index * (if we're going left - LE or LT) or this index (if we're going * right - GE or GT). */ if (relation == REL234_LT || relation == REL234_LE) { ss.index--; } /* * We know the index of the element we want; just call index234 * to do the rest. This will return NULL if the index is out of * bounds, which is exactly what we want. */ toret = index234(t, ss.index); if (toret && index) *index = ss.index; return toret; } void *find234(tree234 * t, void *e, cmpfn234 cmp) { return findrelpos234(t, e, cmp, REL234_EQ, NULL); } void *findrel234(tree234 * t, void *e, cmpfn234 cmp, int relation) { return findrelpos234(t, e, cmp, relation, NULL); } void *findpos234(tree234 * t, void *e, cmpfn234 cmp, int *index) { return findrelpos234(t, e, cmp, REL234_EQ, index); } void search234_start(search234_state *state, tree234 *t) { state->_node = t->root; state->_base = 0; /* index of first element in this node's subtree */ state->_last = -1; /* indicate that this node is not previously visted */ search234_step(state, 0); } void search234_step(search234_state *state, int direction) { node234 *node = state->_node; int i; if (!node) { state->element = NULL; state->index = 0; return; } if (state->_last != -1) { /* * We're already pointing at some element of a node, so we * should restrict to the elements left or right of it, * depending on the requested search direction. */ assert(direction); assert(node); if (direction > 0) state->_lo = state->_last + 1; else state->_hi = state->_last - 1; if (state->_lo > state->_hi) { /* * We've run out of elements in this node, i.e. we've * narrowed to nothing but a child pointer. Descend to * that child, and update _base to the leftmost index of * its subtree. */ for (i = 0; i < state->_lo; i++) state->_base += 1 + node->counts[i]; state->_node = node = node->kids[state->_lo]; state->_last = -1; } } if (state->_last == -1) { /* * We've just entered a new node - either because of the above * code, or because we were called from search234_start - and * anything in that node is a viable answer. */ state->_lo = 0; state->_hi = node ? elements234(node)-1 : 0; } /* * Now we've got something we can return. */ if (!node) { state->element = NULL; state->index = state->_base; } else { state->_last = (state->_lo + state->_hi) / 2; state->element = node->elems[state->_last]; state->index = state->_base + state->_last; for (i = 0; i <= state->_last; i++) state->index += node->counts[i]; } } /* * Delete an element e in a 2-3-4 tree. Does not free the element, * merely removes all links to it from the tree nodes. */ static void *delpos234_internal(tree234 * t, int index) { node234 *n; void *retval; int ei = -1; retval = 0; n = t->root; LOG(("deleting item %d from tree %p\n", index, t)); while (1) { while (n) { int ki; node234 *sub; LOG( (" node %p: %p/%d [%p] %p/%d [%p] %p/%d [%p] %p/%d index=%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], index)); if (index < n->counts[0]) { ki = 0; } else if (index -= n->counts[0] + 1, index < 0) { ei = 0; break; } else if (index < n->counts[1]) { ki = 1; } else if (index -= n->counts[1] + 1, index < 0) { ei = 1; break; } else if (index < n->counts[2]) { ki = 2; } else if (index -= n->counts[2] + 1, index < 0) { ei = 2; break; } else { ki = 3; } /* * Recurse down to subtree ki. If it has only one element, * we have to do some transformation to start with. */ LOG((" moving to subtree %d\n", ki)); sub = n->kids[ki]; if (!sub->elems[1]) { LOG((" subtree has only one element!\n")); if (ki > 0 && n->kids[ki - 1]->elems[1]) { /* * Case 3a, left-handed variant. Child ki has * only one element, but child ki-1 has two or * more. So we need to move a subtree from ki-1 * to ki. * * . C . . B . * / \ -> / \ * [more] a A b B c d D e [more] a A b c C d D e */ node234 *sib = n->kids[ki - 1]; int lastelem = (sib->elems[2] ? 2 : sib->elems[1] ? 1 : 0); sub->kids[2] = sub->kids[1]; sub->counts[2] = sub->counts[1]; sub->elems[1] = sub->elems[0]; sub->kids[1] = sub->kids[0]; sub->counts[1] = sub->counts[0]; sub->elems[0] = n->elems[ki - 1]; sub->kids[0] = sib->kids[lastelem + 1]; sub->counts[0] = sib->counts[lastelem + 1]; if (sub->kids[0]) sub->kids[0]->parent = sub; n->elems[ki - 1] = sib->elems[lastelem]; sib->kids[lastelem + 1] = NULL; sib->counts[lastelem + 1] = 0; sib->elems[lastelem] = NULL; n->counts[ki] = countnode234(sub); LOG((" case 3a left\n")); LOG( (" index and left subtree count before adjustment: %d, %d\n", index, n->counts[ki - 1])); index += n->counts[ki - 1]; n->counts[ki - 1] = countnode234(sib); index -= n->counts[ki - 1]; LOG( (" index and left subtree count after adjustment: %d, %d\n", index, n->counts[ki - 1])); } else if (ki < 3 && n->kids[ki + 1] && n->kids[ki + 1]->elems[1]) { /* * Case 3a, right-handed variant. ki has only * one element but ki+1 has two or more. Move a * subtree from ki+1 to ki. * * . B . . C . * / \ -> / \ * a A b c C d D e [more] a A b B c d D e [more] */ node234 *sib = n->kids[ki + 1]; int j; sub->elems[1] = n->elems[ki]; sub->kids[2] = sib->kids[0]; sub->counts[2] = sib->counts[0]; if (sub->kids[2]) sub->kids[2]->parent = sub; n->elems[ki] = sib->elems[0]; sib->kids[0] = sib->kids[1]; sib->counts[0] = sib->counts[1]; for (j = 0; j < 2 && sib->elems[j + 1]; j++) { sib->kids[j + 1] = sib->kids[j + 2]; sib->counts[j + 1] = sib->counts[j + 2]; sib->elems[j] = sib->elems[j + 1]; } sib->kids[j + 1] = NULL; sib->counts[j + 1] = 0; sib->elems[j] = NULL; n->counts[ki] = countnode234(sub); n->counts[ki + 1] = countnode234(sib); LOG((" case 3a right\n")); } else { /* * Case 3b. ki has only one element, and has no * neighbour with more than one. So pick a * neighbour and merge it with ki, taking an * element down from n to go in the middle. * * . B . . * / \ -> | * a A b c C d a A b B c C d * * (Since at all points we have avoided * descending to a node with only one element, * we can be sure that n is not reduced to * nothingness by this move, _unless_ it was * the very first node, ie the root of the * tree. In that case we remove the now-empty * root and replace it with its single large * child as shown.) */ node234 *sib; int j; if (ki > 0) { ki--; index += n->counts[ki] + 1; } sib = n->kids[ki]; sub = n->kids[ki + 1]; sub->kids[3] = sub->kids[1]; sub->counts[3] = sub->counts[1]; sub->elems[2] = sub->elems[0]; sub->kids[2] = sub->kids[0]; sub->counts[2] = sub->counts[0]; sub->elems[1] = n->elems[ki]; sub->kids[1] = sib->kids[1]; sub->counts[1] = sib->counts[1]; if (sub->kids[1]) sub->kids[1]->parent = sub; sub->elems[0] = sib->elems[0]; sub->kids[0] = sib->kids[0]; sub->counts[0] = sib->counts[0]; if (sub->kids[0]) sub->kids[0]->parent = sub; n->counts[ki + 1] = countnode234(sub); sfree(sib); /* * That's built the big node in sub. Now we * need to remove the reference to sib in n. */ for (j = ki; j < 3 && n->kids[j + 1]; j++) { n->kids[j] = n->kids[j + 1]; n->counts[j] = n->counts[j + 1]; n->elems[j] = j < 2 ? n->elems[j + 1] : NULL; } n->kids[j] = NULL; n->counts[j] = 0; if (j < 3) n->elems[j] = NULL; LOG((" case 3b ki=%d\n", ki)); if (!n->elems[0]) { /* * The root is empty and needs to be * removed. */ LOG((" shifting root!\n")); t->root = sub; sub->parent = NULL; sfree(n); } } } n = sub; } if (!retval) retval = n->elems[ei]; if (ei == -1) return NULL; /* although this shouldn't happen */ /* * Treat special case: this is the one remaining item in * the tree. n is the tree root (no parent), has one * element (no elems[1]), and has no kids (no kids[0]). */ if (!n->parent && !n->elems[1] && !n->kids[0]) { LOG((" removed last element in tree\n")); sfree(n); t->root = NULL; return retval; } /* * Now we have the element we want, as n->elems[ei], and we * have also arranged for that element not to be the only * one in its node. So... */ if (!n->kids[0] && n->elems[1]) { /* * Case 1. n is a leaf node with more than one element, * so it's _really easy_. Just delete the thing and * we're done. */ int i; LOG((" case 1\n")); for (i = ei; i < 2 && n->elems[i + 1]; i++) n->elems[i] = n->elems[i + 1]; n->elems[i] = NULL; /* * Having done that to the leaf node, we now go back up * the tree fixing the counts. */ while (n->parent) { int childnum; childnum = (n->parent->kids[0] == n ? 0 : n->parent->kids[1] == n ? 1 : n->parent->kids[2] == n ? 2 : 3); n->parent->counts[childnum]--; n = n->parent; } return retval; /* finished! */ } else if (n->kids[ei]->elems[1]) { /* * Case 2a. n is an internal node, and the root of the * subtree to the left of e has more than one element. * So find the predecessor p to e (ie the largest node * in that subtree), place it where e currently is, and * then start the deletion process over again on the * subtree with p as target. */ node234 *m = n->kids[ei]; void *target; LOG((" case 2a\n")); while (m->kids[0]) { m = (m->kids[3] ? m->kids[3] : m->kids[2] ? m->kids[2] : m->kids[1] ? m->kids[1] : m->kids[0]); } target = (m->elems[2] ? m->elems[2] : m->elems[1] ? m->elems[1] : m->elems[0]); n->elems[ei] = target; index = n->counts[ei] - 1; n = n->kids[ei]; } else if (n->kids[ei + 1]->elems[1]) { /* * Case 2b, symmetric to 2a but s/left/right/ and * s/predecessor/successor/. (And s/largest/smallest/). */ node234 *m = n->kids[ei + 1]; void *target; LOG((" case 2b\n")); while (m->kids[0]) { m = m->kids[0]; } target = m->elems[0]; n->elems[ei] = target; n = n->kids[ei + 1]; index = 0; } else { /* * Case 2c. n is an internal node, and the subtrees to * the left and right of e both have only one element. * So combine the two subnodes into a single big node * with their own elements on the left and right and e * in the middle, then restart the deletion process on * that subtree, with e still as target. */ node234 *a = n->kids[ei], *b = n->kids[ei + 1]; int j; LOG((" case 2c\n")); a->elems[1] = n->elems[ei]; a->kids[2] = b->kids[0]; a->counts[2] = b->counts[0]; if (a->kids[2]) a->kids[2]->parent = a; a->elems[2] = b->elems[0]; a->kids[3] = b->kids[1]; a->counts[3] = b->counts[1]; if (a->kids[3]) a->kids[3]->parent = a; sfree(b); n->counts[ei] = countnode234(a); /* * That's built the big node in a, and destroyed b. Now * remove the reference to b (and e) in n. */ for (j = ei; j < 2 && n->elems[j + 1]; j++) { n->elems[j] = n->elems[j + 1]; n->kids[j + 1] = n->kids[j + 2]; n->counts[j + 1] = n->counts[j + 2]; } n->elems[j] = NULL; n->kids[j + 1] = NULL; n->counts[j + 1] = 0; /* * It's possible, in this case, that we've just removed * the only element in the root of the tree. If so, * shift the root. */ if (n->elems[0] == NULL) { LOG((" shifting root!\n")); t->root = a; a->parent = NULL; sfree(n); } /* * Now go round the deletion process again, with n * pointing at the new big node and e still the same. */ n = a; index = a->counts[0] + a->counts[1] + 1; } } } void *delpos234(tree234 * t, int index) { if (index < 0 || index >= countnode234(t->root)) return NULL; return delpos234_internal(t, index); } void *del234(tree234 * t, void *e) { int index; if (!findrelpos234(t, e, NULL, REL234_EQ, &index)) return NULL; /* it wasn't in there anyway */ return delpos234_internal(t, index); /* it's there; delete it. */ } #ifdef TEST /* * Test code for the 2-3-4 tree. This code maintains an alternative * representation of the data in the tree, in an array (using the * obvious and slow insert and delete functions). After each tree * operation, the verify() function is called, which ensures all * the tree properties are preserved: * - node->child->parent always equals node * - tree->root->parent always equals NULL * - number of kids == 0 or number of elements + 1; * - tree has the same depth everywhere * - every node has at least one element * - subtree element counts are accurate * - any NULL kid pointer is accompanied by a zero count * - in a sorted tree: ordering property between elements of a * node and elements of its children is preserved * and also ensures the list represented by the tree is the same * list it should be. (This last check also doubly verifies the * ordering properties, because the `same list it should be' is by * definition correctly ordered. It also ensures all nodes are * distinct, because the enum functions would get caught in a loop * if not.) */ #include #include int n_errors = 0; /* * Error reporting function. */ PRINTF_LIKE(1, 2) void error(char *fmt, ...) { va_list ap; printf("ERROR: "); va_start(ap, fmt); vfprintf(stdout, fmt, ap); va_end(ap); printf("\n"); n_errors++; } /* The array representation of the data. */ void **array; int arraylen, arraysize; cmpfn234 cmp; /* The tree representation of the same data. */ tree234 *tree; typedef struct { int treedepth; int elemcount; } chkctx; int chknode(chkctx * ctx, int level, node234 * node, void *lowbound, void *highbound) { int nkids, nelems; int i; int count; /* Count the non-NULL kids. */ for (nkids = 0; nkids < 4 && node->kids[nkids]; nkids++); /* Ensure no kids beyond the first NULL are non-NULL. */ for (i = nkids; i < 4; i++) if (node->kids[i]) { error("node %p: nkids=%d but kids[%d] non-NULL", node, nkids, i); } else if (node->counts[i]) { error("node %p: kids[%d] NULL but count[%d]=%d nonzero", node, i, i, node->counts[i]); } /* Count the non-NULL elements. */ for (nelems = 0; nelems < 3 && node->elems[nelems]; nelems++); /* Ensure no elements beyond the first NULL are non-NULL. */ for (i = nelems; i < 3; i++) if (node->elems[i]) { error("node %p: nelems=%d but elems[%d] non-NULL", node, nelems, i); } if (nkids == 0) { /* * If nkids==0, this is a leaf node; verify that the tree * depth is the same everywhere. */ if (ctx->treedepth < 0) ctx->treedepth = level; /* we didn't know the depth yet */ else if (ctx->treedepth != level) error("node %p: leaf at depth %d, previously seen depth %d", node, level, ctx->treedepth); } else { /* * If nkids != 0, then it should be nelems+1, unless nelems * is 0 in which case nkids should also be 0 (and so we * shouldn't be in this condition at all). */ int shouldkids = (nelems ? nelems + 1 : 0); if (nkids != shouldkids) { error("node %p: %d elems should mean %d kids but has %d", node, nelems, shouldkids, nkids); } } /* * nelems should be at least 1. */ if (nelems == 0) { error("node %p: no elems", node, nkids); } /* * Add nelems to the running element count of the whole tree. */ ctx->elemcount += nelems; /* * Check ordering property: all elements should be strictly > * lowbound, strictly < highbound, and strictly < each other in * sequence. (lowbound and highbound are NULL at edges of tree * - both NULL at root node - and NULL is considered to be < * everything and > everything. IYSWIM.) */ if (cmp) { for (i = -1; i < nelems; i++) { void *lower = (i == -1 ? lowbound : node->elems[i]); void *higher = (i + 1 == nelems ? highbound : node->elems[i + 1]); if (lower && higher && cmp(lower, higher) >= 0) { error("node %p: kid comparison [%d=%s,%d=%s] failed", node, i, lower, i + 1, higher); } } } /* * Check parent pointers: all non-NULL kids should have a * parent pointer coming back to this node. */ for (i = 0; i < nkids; i++) if (node->kids[i]->parent != node) { error("node %p kid %d: parent ptr is %p not %p", node, i, node->kids[i]->parent, node); } /* * Now (finally!) recurse into subtrees. */ count = nelems; for (i = 0; i < nkids; i++) { void *lower = (i == 0 ? lowbound : node->elems[i - 1]); void *higher = (i >= nelems ? highbound : node->elems[i]); int subcount = chknode(ctx, level + 1, node->kids[i], lower, higher); if (node->counts[i] != subcount) { error("node %p kid %d: count says %d, subtree really has %d", node, i, node->counts[i], subcount); } count += subcount; } return count; } void verify(void) { chkctx ctx[1]; int i; void *p; ctx->treedepth = -1; /* depth unknown yet */ ctx->elemcount = 0; /* no elements seen yet */ /* * Verify validity of tree properties. */ if (tree->root) { if (tree->root->parent != NULL) error("root->parent is %p should be null", tree->root->parent); chknode(&ctx, 0, tree->root, NULL, NULL); } printf("tree depth: %d\n", ctx->treedepth); /* * Enumerate the tree and ensure it matches up to the array. */ for (i = 0; NULL != (p = index234(tree, i)); i++) { if (i >= arraylen) error("tree contains more than %d elements", arraylen); if (array[i] != p) error("enum at position %d: array says %s, tree says %s", i, array[i], p); } if (ctx->elemcount != i) { error("tree really contains %d elements, enum gave %d", ctx->elemcount, i); } if (i < arraylen) { error("enum gave only %d elements, array has %d", i, arraylen); } i = count234(tree); if (ctx->elemcount != i) { error("tree really contains %d elements, count234 gave %d", ctx->elemcount, i); } } void internal_addtest(void *elem, int index, void *realret) { int i, j; void *retval; if (arraysize < arraylen + 1) { arraysize = arraylen + 1 + 256; array = sresize(array, arraysize, void *); } i = index; /* now i points to the first element >= elem */ retval = elem; /* expect elem returned (success) */ for (j = arraylen; j > i; j--) array[j] = array[j - 1]; array[i] = elem; /* add elem to array */ arraylen++; if (realret != retval) { error("add: retval was %p expected %p", realret, retval); } verify(); } void addtest(void *elem) { int i; void *realret; realret = add234(tree, elem); i = 0; while (i < arraylen && cmp(elem, array[i]) > 0) i++; if (i < arraylen && !cmp(elem, array[i])) { void *retval = array[i]; /* expect that returned not elem */ if (realret != retval) { error("add: retval was %p expected %p", realret, retval); } } else internal_addtest(elem, i, realret); } void addpostest(void *elem, int i) { void *realret; realret = addpos234(tree, elem, i); internal_addtest(elem, i, realret); } void delpostest(int i) { int index = i; void *elem = array[i], *ret; /* i points to the right element */ while (i < arraylen - 1) { array[i] = array[i + 1]; i++; } arraylen--; /* delete elem from array */ if (tree->cmp) ret = del234(tree, elem); else ret = delpos234(tree, index); if (ret != elem) { error("del returned %p, expected %p", ret, elem); } verify(); } void deltest(void *elem) { int i; i = 0; while (i < arraylen && cmp(elem, array[i]) > 0) i++; if (i >= arraylen || cmp(elem, array[i]) != 0) return; /* don't do it! */ delpostest(i); } /* A sample data set and test utility. Designed for pseudo-randomness, * and yet repeatability. */ /* * This random number generator uses the `portable implementation' * given in ANSI C99 draft N869. It assumes `unsigned' is 32 bits; * change it if not. */ int randomnumber(unsigned *seed) { *seed *= 1103515245; *seed += 12345; return ((*seed) / 65536) % 32768; } int mycmp(void *av, void *bv) { char const *a = (char const *) av; char const *b = (char const *) bv; return strcmp(a, b); } #define lenof(x) ( sizeof((x)) / sizeof(*(x)) ) char *strings[] = { "a", "ab", "absque", "coram", "de", "palam", "clam", "cum", "ex", "e", "sine", "tenus", "pro", "prae", "banana", "carrot", "cabbage", "broccoli", "onion", "zebra", "penguin", "blancmange", "pangolin", "whale", "hedgehog", "giraffe", "peanut", "bungee", "foo", "bar", "baz", "quux", "murfl", "spoo", "breen", "flarn", "octothorpe", "snail", "tiger", "elephant", "octopus", "warthog", "armadillo", "aardvark", "wyvern", "dragon", "elf", "dwarf", "orc", "goblin", "pixie", "basilisk", "warg", "ape", "lizard", "newt", "shopkeeper", "wand", "ring", "amulet" }; #define NSTR lenof(strings) int findtest(void) { const static int rels[] = { REL234_EQ, REL234_GE, REL234_LE, REL234_LT, REL234_GT }; const static char *const relnames[] = { "EQ", "GE", "LE", "LT", "GT" }; int i, j, rel, index; char *p, *ret, *realret, *realret2; int lo, hi, mid, c; for (i = 0; i < NSTR; i++) { p = strings[i]; for (j = 0; j < sizeof(rels) / sizeof(*rels); j++) { rel = rels[j]; lo = 0; hi = arraylen - 1; while (lo <= hi) { mid = (lo + hi) / 2; c = strcmp(p, array[mid]); if (c < 0) hi = mid - 1; else if (c > 0) lo = mid + 1; else break; } if (c == 0) { if (rel == REL234_LT) ret = (mid > 0 ? array[--mid] : NULL); else if (rel == REL234_GT) ret = (mid < arraylen - 1 ? array[++mid] : NULL); else ret = array[mid]; } else { assert(lo == hi + 1); if (rel == REL234_LT || rel == REL234_LE) { mid = hi; ret = (hi >= 0 ? array[hi] : NULL); } else if (rel == REL234_GT || rel == REL234_GE) { mid = lo; ret = (lo < arraylen ? array[lo] : NULL); } else ret = NULL; } realret = findrelpos234(tree, p, NULL, rel, &index); if (realret != ret) { error("find(\"%s\",%s) gave %s should be %s", p, relnames[j], realret, ret); } if (realret && index != mid) { error("find(\"%s\",%s) gave %d should be %d", p, relnames[j], index, mid); } if (realret && rel == REL234_EQ) { realret2 = index234(tree, index); if (realret2 != realret) { error("find(\"%s\",%s) gave %s(%d) but %d -> %s", p, relnames[j], realret, index, index, realret2); } } #if 0 printf("find(\"%s\",%s) gave %s(%d)\n", p, relnames[j], realret, index); #endif } } realret = findrelpos234(tree, NULL, NULL, REL234_GT, &index); if (arraylen && (realret != array[0] || index != 0)) { error("find(NULL,GT) gave %s(%d) should be %s(0)", realret, index, array[0]); } else if (!arraylen && (realret != NULL)) { error("find(NULL,GT) gave %s(%d) should be NULL", realret, index); } realret = findrelpos234(tree, NULL, NULL, REL234_LT, &index); if (arraylen && (realret != array[arraylen - 1] || index != arraylen - 1)) { error("find(NULL,LT) gave %s(%d) should be %s(0)", realret, index, array[arraylen - 1]); } else if (!arraylen && (realret != NULL)) { error("find(NULL,LT) gave %s(%d) should be NULL", realret, index); } } void searchtest_recurse(search234_state ss, int lo, int hi, char **expected, char *directionbuf, char *directionptr) { *directionptr = '\0'; if (!ss.element) { if (lo != hi) { error("search234(%s) gave NULL for non-empty interval [%d,%d)", directionbuf, lo, hi); } else if (ss.index != lo) { error("search234(%s) gave index %d should be %d", directionbuf, ss.index, lo); } else { printf("%*ssearch234(%s) gave NULL,%d\n", (int)(directionptr-directionbuf) * 2, "", directionbuf, ss.index); } } else if (lo == hi) { error("search234(%s) gave %s for empty interval [%d,%d)", directionbuf, (char *)ss.element, lo, hi); } else if (ss.element != expected[ss.index]) { error("search234(%s) gave element %s should be %s", directionbuf, (char *)ss.element, expected[ss.index]); } else if (ss.index < lo || ss.index >= hi) { error("search234(%s) gave index %d should be in [%d,%d)", directionbuf, ss.index, lo, hi); return; } else { search234_state next; printf("%*ssearch234(%s) gave %s,%d\n", (int)(directionptr-directionbuf) * 2, "", directionbuf, (char *)ss.element, ss.index); next = ss; search234_step(&next, -1); *directionptr = '-'; searchtest_recurse(next, lo, ss.index, expected, directionbuf, directionptr+1); next = ss; search234_step(&next, +1); *directionptr = '+'; searchtest_recurse(next, ss.index+1, hi, expected, directionbuf, directionptr+1); } } void searchtest(void) { char *expected[NSTR], *p; char directionbuf[NSTR * 10]; int n; search234_state ss; printf("beginning searchtest:"); for (n = 0; (p = index234(tree, n)) != NULL; n++) { expected[n] = p; printf(" %d=%s", n, p); } printf(" count=%d\n", n); search234_start(&ss, tree); searchtest_recurse(ss, 0, n, expected, directionbuf, directionbuf); } int main(void) { int in[NSTR]; int i, j, k; unsigned seed = 0; for (i = 0; i < NSTR; i++) in[i] = 0; array = NULL; arraylen = arraysize = 0; tree = newtree234(mycmp); cmp = mycmp; verify(); searchtest(); for (i = 0; i < 10000; i++) { j = randomnumber(&seed); j %= NSTR; printf("trial: %d\n", i); if (in[j]) { printf("deleting %s (%d)\n", strings[j], j); deltest(strings[j]); in[j] = 0; } else { printf("adding %s (%d)\n", strings[j], j); addtest(strings[j]); in[j] = 1; } findtest(); searchtest(); } while (arraylen > 0) { j = randomnumber(&seed); j %= arraylen; deltest(array[j]); } freetree234(tree); /* * Now try an unsorted tree. We don't really need to test * delpos234 because we know del234 is based on it, so it's * already been tested in the above sorted-tree code; but for * completeness we'll use it to tear down our unsorted tree * once we've built it. */ tree = newtree234(NULL); cmp = NULL; verify(); for (i = 0; i < 1000; i++) { printf("trial: %d\n", i); j = randomnumber(&seed); j %= NSTR; k = randomnumber(&seed); k %= count234(tree) + 1; 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); } printf("%d errors found\n", n_errors); return (n_errors != 0); } #endif putty-0.76/tree234.h0000644000175000017500000001605714072266313011132 00000000000000/* * tree234.h: header defining functions in tree234.c. * * This file is copyright 1999-2001 Simon Tatham. * * 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: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL SIMON TATHAM BE LIABLE FOR * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef TREE234_H #define TREE234_H /* * This typedef is opaque outside tree234.c itself. */ typedef struct tree234_Tag tree234; typedef int (*cmpfn234) (void *, void *); /* * Create a 2-3-4 tree. If `cmp' is NULL, the tree is unsorted, and * lookups by key will fail: you can only look things up by numeric * index, and you have to use addpos234() and delpos234(). */ tree234 *newtree234(cmpfn234 cmp); /* * Free a 2-3-4 tree (not including freeing the elements). */ void freetree234(tree234 * t); /* * Add an element e to a sorted 2-3-4 tree t. Returns e on success, * or if an existing element compares equal, returns that. */ void *add234(tree234 * t, void *e); /* * Add an element e to an unsorted 2-3-4 tree t. Returns e on * success, NULL on failure. (Failure should only occur if the * index is out of range or the tree is sorted.) * * Index range can be from 0 to the tree's current element count, * inclusive. */ void *addpos234(tree234 * t, void *e, int index); /* * Look up the element at a given numeric index in a 2-3-4 tree. * Returns NULL if the index is out of range. * * One obvious use for this function is in iterating over the whole * of a tree (sorted or unsorted): * * for (i = 0; (p = index234(tree, i)) != NULL; i++) consume(p); * * or * * int maxcount = count234(tree); * for (i = 0; i < maxcount; i++) { * p = index234(tree, i); * assert(p != NULL); * consume(p); * } */ void *index234(tree234 * t, int index); /* * Find an element e in a sorted 2-3-4 tree t. Returns NULL if not * found. e is always passed as the first argument to cmp, so cmp * can be an asymmetric function if desired. cmp can also be passed * as NULL, in which case the compare function from the tree proper * will be used. * * Three of these functions are special cases of findrelpos234. The * non-`pos' variants lack the `index' parameter: if the parameter * is present and non-NULL, it must point to an integer variable * which will be filled with the numeric index of the returned * element. * * The non-`rel' variants lack the `relation' parameter. This * parameter allows you to specify what relation the element you * provide has to the element you're looking for. This parameter * can be: * * REL234_EQ - find only an element that compares equal to e * REL234_LT - find the greatest element that compares < e * REL234_LE - find the greatest element that compares <= e * REL234_GT - find the smallest element that compares > e * REL234_GE - find the smallest element that compares >= e * * Non-`rel' variants assume REL234_EQ. * * If `rel' is REL234_GT or REL234_LT, the `e' parameter may be * NULL. In this case, REL234_GT will return the smallest element * in the tree, and REL234_LT will return the greatest. This gives * an alternative means of iterating over a sorted tree, instead of * using index234: * * // to loop forwards * for (p = NULL; (p = findrel234(tree, p, NULL, REL234_GT)) != NULL ;) * consume(p); * * // to loop backwards * for (p = NULL; (p = findrel234(tree, p, NULL, REL234_LT)) != NULL ;) * consume(p); */ enum { REL234_EQ, REL234_LT, REL234_LE, REL234_GT, REL234_GE }; void *find234(tree234 * t, void *e, cmpfn234 cmp); void *findrel234(tree234 * t, void *e, cmpfn234 cmp, int relation); void *findpos234(tree234 * t, void *e, cmpfn234 cmp, int *index); void *findrelpos234(tree234 * t, void *e, cmpfn234 cmp, int relation, int *index); /* * A more general search type still. Use search234_start() to * initialise one of these state structures; it will fill in * state->element with an element of the tree, and state->index with * the index of that element. If you don't like that element, call * search234_step, with direction == -1 if you want an element earlier * in the tree, or +1 if you want a later one. * * If either function returns state->element == NULL, then you've * narrowed the search to a point between two adjacent elements, so * there are no further elements left to return consistent with the * constraints you've imposed. In this case, state->index tells you * how many elements come before the point you narrowed down to. After * this, you mustn't call search234_step again (unless the state * structure is first reinitialised). * * The use of this search system is that you get both the candidate * element _and_ its index at every stage, so you can use both of them * to make your decision. Also, you can remember element pointers from * earlier in the search. * * The fields beginning with underscores are private to the * implementation, and only exposed so that clients can know how much * space to allocate for the structure as a whole. Don't modify them. * (Except that it's safe to copy the whole structure.) */ typedef struct search234_state { void *element; int index; int _lo, _hi, _last, _base; void *_node; } search234_state; void search234_start(search234_state *state, tree234 *t); void search234_step(search234_state *state, int direction); /* * Delete an element e in a 2-3-4 tree. Does not free the element, * merely removes all links to it from the tree nodes. * * delpos234 deletes the element at a particular tree index: it * works on both sorted and unsorted trees. * * del234 deletes the element passed to it, so it only works on * sorted trees. (It's equivalent to using findpos234 to determine * the index of an element, and then passing that index to * delpos234.) * * Both functions return a pointer to the element they delete, for * the user to free or pass on elsewhere or whatever. If the index * is out of range (delpos234) or the element is already not in the * tree (del234) then they return NULL. */ void *del234(tree234 * t, void *e); void *delpos234(tree234 * t, int index); /* * Return the total element count of a tree234. */ int count234(tree234 * t); #endif /* TREE234_H */ putty-0.76/utils.c0000644000175000017500000007547614072266314011110 00000000000000/* * Platform-independent utility routines used throughout this code base. * * This file is linked into stand-alone test utilities which only want * to include the things they really need, so functions in here should * avoid depending on any functions outside it. Utility routines that * are more tightly integrated into the main code should live in * misc.c. */ #include #include #include #include #include #include #include "defs.h" #include "misc.h" #include "ssh.h" /* * Parse a string block size specification. This is approximately a * subset of the block size specs supported by GNU fileutils: * "nk" = n kilobytes * "nM" = n megabytes * "nG" = n gigabytes * All numbers are decimal, and suffixes refer to powers of two. * Case-insensitive. */ unsigned long parse_blocksize(const char *bs) { char *suf; unsigned long r = strtoul(bs, &suf, 10); if (*suf != '\0') { while (*suf && isspace((unsigned char)*suf)) suf++; switch (*suf) { case 'k': case 'K': r *= 1024ul; break; case 'm': case 'M': r *= 1024ul * 1024ul; break; case 'g': case 'G': r *= 1024ul * 1024ul * 1024ul; break; case '\0': default: break; } } return r; } /* * Parse a ^C style character specification. * Returns NULL in `next' if we didn't recognise it as a control character, * in which case `c' should be ignored. * The precise current parsing is an oddity inherited from the terminal * answerback-string parsing code. All sequences start with ^; all except * ^<123> are two characters. The ones that are worth keeping are probably: * ^? 127 * ^@A-Z[\]^_ 0-31 * a-z 1-26 * specified by number (decimal, 0octal, 0xHEX) * ~ ^ escape */ char ctrlparse(char *s, char **next) { char c = 0; if (*s != '^') { *next = NULL; } else { s++; if (*s == '\0') { *next = NULL; } else if (*s == '<') { s++; c = (char)strtol(s, next, 0); if ((*next == s) || (**next != '>')) { c = 0; *next = NULL; } else (*next)++; } else if (*s >= 'a' && *s <= 'z') { c = (*s - ('a' - 1)); *next = s+1; } else if ((*s >= '@' && *s <= '_') || *s == '?' || (*s & 0x80)) { c = ('@' ^ *s); *next = s+1; } else if (*s == '~') { c = '^'; *next = s+1; } } return c; } /* * Find a character in a string, unless it's a colon contained within * square brackets. Used for untangling strings of the form * 'host:port', where host can be an IPv6 literal. * * We provide several variants of this function, with semantics like * various standard string.h functions. */ static const char *host_strchr_internal(const char *s, const char *set, bool first) { int brackets = 0; const char *ret = NULL; while (1) { if (!*s) return ret; if (*s == '[') brackets++; else if (*s == ']' && brackets > 0) brackets--; else if (brackets && *s == ':') /* never match */ ; else if (strchr(set, *s)) { ret = s; if (first) return ret; } s++; } } size_t host_strcspn(const char *s, const char *set) { const char *answer = host_strchr_internal(s, set, true); if (answer) return answer - s; else return strlen(s); } char *host_strchr(const char *s, int c) { char set[2]; set[0] = c; set[1] = '\0'; return (char *) host_strchr_internal(s, set, true); } char *host_strrchr(const char *s, int c) { char set[2]; set[0] = c; set[1] = '\0'; return (char *) host_strchr_internal(s, set, false); } #ifdef TEST_HOST_STRFOO int main(void) { int passes = 0, fails = 0; #define TEST1(func, string, arg2, suffix, result) do \ { \ const char *str = string; \ unsigned ret = func(string, arg2) suffix; \ if (ret == result) { \ passes++; \ } else { \ printf("fail: %s(%s,%s)%s = %u, expected %u\n", \ #func, #string, #arg2, #suffix, ret, \ (unsigned)result); \ fails++; \ } \ } while (0) TEST1(host_strchr, "[1:2:3]:4:5", ':', -str, 7); TEST1(host_strrchr, "[1:2:3]:4:5", ':', -str, 9); TEST1(host_strcspn, "[1:2:3]:4:5", "/:",, 7); TEST1(host_strchr, "[1:2:3]", ':', == NULL, 1); TEST1(host_strrchr, "[1:2:3]", ':', == NULL, 1); TEST1(host_strcspn, "[1:2:3]", "/:",, 7); TEST1(host_strcspn, "[1:2/3]", "/:",, 4); TEST1(host_strcspn, "[1:2:3]/", "/:",, 7); printf("passed %d failed %d total %d\n", passes, fails, passes+fails); return fails != 0 ? 1 : 0; } /* Stubs to stop the rest of this module causing compile failures. */ static NORETURN void fatal_error(const char *p, ...) { va_list ap; fprintf(stderr, "host_string_test: "); va_start(ap, p); vfprintf(stderr, p, ap); va_end(ap); fputc('\n', stderr); exit(1); } void out_of_memory(void) { fatal_error("out of memory"); } #endif /* TEST_HOST_STRFOO */ /* * Trim square brackets off the outside of an IPv6 address literal. * Leave all other strings unchanged. Returns a fresh dynamically * allocated string. */ char *host_strduptrim(const char *s) { if (s[0] == '[') { const char *p = s+1; int colons = 0; while (*p && *p != ']') { if (isxdigit((unsigned char)*p)) /* OK */; else if (*p == ':') colons++; else break; p++; } if (*p == '%') { /* * This delimiter character introduces an RFC 4007 scope * id suffix (e.g. suffixing the address literal with * %eth1 or %2 or some such). There's no syntax * specification for the scope id, so just accept anything * except the closing ]. */ p += strcspn(p, "]"); } if (*p == ']' && !p[1] && colons > 1) { /* * This looks like an IPv6 address literal (hex digits and * at least two colons, plus optional scope id, contained * in square brackets). Trim off the brackets. */ return dupprintf("%.*s", (int)(p - (s+1)), s+1); } } /* * Any other shape of string is simply duplicated. */ return dupstr(s); } /* ---------------------------------------------------------------------- * String handling routines. */ char *dupstr(const char *s) { char *p = NULL; if (s) { int len = strlen(s); p = snewn(len + 1, char); strcpy(p, s); } return p; } /* Allocate the concatenation of N strings. Terminate arg list with NULL. */ char *dupcat_fn(const char *s1, ...) { int len; char *p, *q, *sn; va_list ap; len = strlen(s1); va_start(ap, s1); while (1) { sn = va_arg(ap, char *); if (!sn) break; len += strlen(sn); } va_end(ap); p = snewn(len + 1, char); strcpy(p, s1); q = p + strlen(p); va_start(ap, s1); while (1) { sn = va_arg(ap, char *); if (!sn) break; 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 string_length_for_printf(size_t s) { /* Truncate absurdly long strings (should one show up) to fit * within a positive 'int', which is what the "%.*s" format will * expect. */ if (s > INT_MAX) return INT_MAX; return s; } /* Work around lack of va_copy in old MSC */ #if defined _MSC_VER && !defined va_copy #define va_copy(a, b) TYPECHECK( \ (va_list *)0 == &(a) && (va_list *)0 == &(b), \ memcpy(&a, &b, sizeof(va_list))) #endif /* Also lack of vsnprintf before VS2015 */ #if defined _WINDOWS && \ !defined __MINGW32__ && \ !defined __WINE__ && \ _MSC_VER < 1900 #define vsnprintf _vsnprintf #endif /* * 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 * to be the local name for it on Windows. Other platforms may * lack it completely, in which case it'll be time to rewrite * this function in a totally different way. * * The only `properly' portable solution I can think of is to * implement my own format string scanner, which figures out an * upper bound for the length of each formatting directive, * allocates the buffer as it goes along, and calls sprintf() to * actually process each directive. If I ever need to actually do * this, some caveats: * * - It's very hard to find a reliable upper bound for * floating-point values. %f, in particular, when supplied with * a number near to the upper or lower limit of representable * numbers, could easily take several hundred characters. It's * probably feasible to predict this statically using the * constants in , or even to predict it dynamically by * looking at the exponent of the specific float provided, but * it won't be fun. * * - Don't forget to _check_, after calling sprintf, that it's * used at most the amount of space we had available. * * - Fault any formatting directive we don't fully understand. The * aim here is to _guarantee_ that we never overflow the buffer, * because this is a security-critical function. If we see a * directive we don't know about, we should panic and die rather * than run any risk. */ static char *dupvprintf_inner(char *buf, size_t oldlen, size_t *sizeptr, const char *fmt, va_list ap) { size_t size = *sizeptr; sgrowarrayn_nm(buf, size, oldlen, 512); while (1) { va_list aq; va_copy(aq, ap); int len = vsnprintf(buf + oldlen, size - oldlen, fmt, aq); va_end(aq); if (len >= 0 && len < size) { /* This is the C99-specified criterion for snprintf to have * been completely successful. */ *sizeptr = size; return buf; } else if (len > 0) { /* This is the C99 error condition: the returned length is * the required buffer size not counting the NUL. */ sgrowarrayn_nm(buf, size, oldlen + 1, len); } else { /* This is the pre-C99 glibc error condition: <0 means the * buffer wasn't big enough, so we enlarge it a bit and hope. */ sgrowarray_nm(buf, size, size); } } } char *dupvprintf(const char *fmt, va_list ap) { size_t size = 0; return dupvprintf_inner(NULL, 0, &size, fmt, ap); } char *dupprintf(const char *fmt, ...) { char *ret; va_list ap; va_start(ap, fmt); ret = dupvprintf(fmt, ap); va_end(ap); return ret; } struct strbuf_impl { size_t size; struct strbuf visible; bool nm; /* true if we insist on non-moving buffer resizes */ }; #define STRBUF_SET_UPTR(buf) \ ((buf)->visible.u = (unsigned char *)(buf)->visible.s) #define STRBUF_SET_PTR(buf, ptr) \ ((buf)->visible.s = (ptr), STRBUF_SET_UPTR(buf)) void *strbuf_append(strbuf *buf_o, size_t len) { struct strbuf_impl *buf = container_of(buf_o, struct strbuf_impl, visible); char *toret; sgrowarray_general( buf->visible.s, buf->size, buf->visible.len + 1, len, buf->nm); STRBUF_SET_UPTR(buf); toret = buf->visible.s + buf->visible.len; buf->visible.len += len; buf->visible.s[buf->visible.len] = '\0'; return toret; } void strbuf_shrink_to(strbuf *buf, size_t new_len) { assert(new_len <= buf->len); buf->len = new_len; buf->s[buf->len] = '\0'; } void strbuf_shrink_by(strbuf *buf, size_t amount_to_remove) { assert(amount_to_remove <= buf->len); buf->len -= amount_to_remove; buf->s[buf->len] = '\0'; } bool strbuf_chomp(strbuf *buf, char char_to_remove) { if (buf->len > 0 && buf->s[buf->len-1] == char_to_remove) { strbuf_shrink_by(buf, 1); return true; } return false; } static void strbuf_BinarySink_write( BinarySink *bs, const void *data, size_t len) { strbuf *buf_o = BinarySink_DOWNCAST(bs, strbuf); memcpy(strbuf_append(buf_o, len), data, len); } static strbuf *strbuf_new_general(bool nm) { struct strbuf_impl *buf = snew(struct strbuf_impl); BinarySink_INIT(&buf->visible, strbuf_BinarySink_write); buf->visible.len = 0; buf->size = 512; buf->nm = nm; STRBUF_SET_PTR(buf, snewn(buf->size, char)); *buf->visible.s = '\0'; return &buf->visible; } strbuf *strbuf_new(void) { return strbuf_new_general(false); } strbuf *strbuf_new_nm(void) { return strbuf_new_general(true); } void strbuf_free(strbuf *buf_o) { struct strbuf_impl *buf = container_of(buf_o, struct strbuf_impl, visible); if (buf->visible.s) { smemclr(buf->visible.s, buf->size); sfree(buf->visible.s); } sfree(buf); } char *strbuf_to_str(strbuf *buf_o) { struct strbuf_impl *buf = container_of(buf_o, struct strbuf_impl, visible); char *ret = buf->visible.s; sfree(buf); return ret; } void strbuf_catfv(strbuf *buf_o, const char *fmt, va_list ap) { struct strbuf_impl *buf = container_of(buf_o, struct strbuf_impl, visible); STRBUF_SET_PTR(buf, dupvprintf_inner(buf->visible.s, buf->visible.len, &buf->size, fmt, ap)); buf->visible.len += strlen(buf->visible.s + buf->visible.len); } void strbuf_catf(strbuf *buf_o, const char *fmt, ...) { va_list ap; va_start(ap, fmt); strbuf_catfv(buf_o, fmt, ap); va_end(ap); } strbuf *strbuf_new_for_agent_query(void) { strbuf *buf = strbuf_new(); strbuf_append(buf, 4); return buf; } void strbuf_finalise_agent_query(strbuf *buf_o) { struct strbuf_impl *buf = container_of(buf_o, struct strbuf_impl, visible); assert(buf->visible.len >= 5); PUT_32BIT_MSB_FIRST(buf->visible.u, buf->visible.len - 4); } /* * Read an entire line of text from a file. Return a buffer * malloced to be as big as necessary (caller must free). */ char *fgetline(FILE *fp) { char *ret = snewn(512, char); size_t size = 512, len = 0; while (fgets(ret + len, size - len, fp)) { len += strlen(ret + len); if (len > 0 && ret[len-1] == '\n') break; /* got a newline, we're done */ sgrowarrayn_nm(ret, size, len, 512); } if (len == 0) { /* first fgets returned NULL */ sfree(ret); return NULL; } ret[len] = '\0'; return ret; } /* * Read an entire file into a BinarySink. */ bool read_file_into(BinarySink *bs, FILE *fp) { char buf[4096]; while (1) { size_t retd = fread(buf, 1, sizeof(buf), fp); if (retd == 0) return !ferror(fp); put_data(bs, buf, retd); } } /* * Perl-style 'chomp', for a line we just read with fgetline. Unlike * Perl chomp, however, we're deliberately forgiving of strange * line-ending conventions. Also we forgive NULL on input, so you can * just write 'line = chomp(fgetline(fp));' and not bother checking * for NULL until afterwards. */ char *chomp(char *str) { if (str) { int len = strlen(str); while (len > 0 && (str[len-1] == '\r' || str[len-1] == '\n')) len--; str[len] = '\0'; } return str; } /* ---------------------------------------------------------------------- * Core base64 encoding and decoding routines. */ void base64_encode_atom(const unsigned char *data, int n, char *out) { static const char base64_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; unsigned word; word = data[0] << 16; if (n > 1) word |= data[1] << 8; if (n > 2) word |= data[2]; out[0] = base64_chars[(word >> 18) & 0x3F]; out[1] = base64_chars[(word >> 12) & 0x3F]; if (n > 1) out[2] = base64_chars[(word >> 6) & 0x3F]; else out[2] = '='; if (n > 2) out[3] = base64_chars[word & 0x3F]; else out[3] = '='; } int base64_decode_atom(const char *atom, unsigned char *out) { int vals[4]; int i, v, len; unsigned word; char c; for (i = 0; i < 4; i++) { c = atom[i]; if (c >= 'A' && c <= 'Z') v = c - 'A'; else if (c >= 'a' && c <= 'z') v = c - 'a' + 26; else if (c >= '0' && c <= '9') v = c - '0' + 52; else if (c == '+') v = 62; else if (c == '/') v = 63; else if (c == '=') v = -1; else return 0; /* invalid atom */ vals[i] = v; } if (vals[0] == -1 || vals[1] == -1) return 0; if (vals[2] == -1 && vals[3] != -1) return 0; if (vals[3] != -1) len = 3; else if (vals[2] != -1) len = 2; else len = 1; word = ((vals[0] << 18) | (vals[1] << 12) | ((vals[2] & 0x3F) << 6) | (vals[3] & 0x3F)); out[0] = (word >> 16) & 0xFF; if (len > 1) out[1] = (word >> 8) & 0xFF; if (len > 2) out[2] = word & 0xFF; return len; } /* ---------------------------------------------------------------------- * Generic routines to deal with send buffers: a linked list of * smallish blocks, with the operations * * - add an arbitrary amount of data to the end of the list * - remove the first N bytes from the list * - 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; }; static void uninitialised_queue_idempotent_callback(IdempotentCallback *ic) { unreachable("bufchain callback used while uninitialised"); } void bufchain_init(bufchain *ch) { ch->head = ch->tail = NULL; ch->buffersize = 0; ch->ic = NULL; ch->queue_idempotent_callback = uninitialised_queue_idempotent_callback; } void bufchain_clear(bufchain *ch) { struct bufchain_granule *b; while (ch->head) { b = ch->head; ch->head = ch->head->next; smemclr(b, sizeof(*b)); sfree(b); } ch->tail = NULL; ch->buffersize = 0; } size_t bufchain_size(bufchain *ch) { return ch->buffersize; } void bufchain_set_callback_inner( bufchain *ch, IdempotentCallback *ic, void (*queue_idempotent_callback)(IdempotentCallback *ic)) { ch->queue_idempotent_callback = queue_idempotent_callback; ch->ic = ic; } void bufchain_add(bufchain *ch, const void *data, size_t len) { 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) { size_t 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) { size_t 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; } } if (ch->ic) ch->queue_idempotent_callback(ch->ic); } void bufchain_consume(bufchain *ch, size_t 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; smemclr(tmp, sizeof(*tmp)); sfree(tmp); } else ch->head->bufpos += remlen; ch->buffersize -= remlen; len -= remlen; } } ptrlen bufchain_prefix(bufchain *ch) { return make_ptrlen(ch->head->bufpos, ch->head->bufend - ch->head->bufpos); } void bufchain_fetch(bufchain *ch, void *data, size_t 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; } } void bufchain_fetch_consume(bufchain *ch, void *data, size_t len) { bufchain_fetch(ch, data, len); bufchain_consume(ch, len); } bool bufchain_try_fetch_consume(bufchain *ch, void *data, size_t len) { if (ch->buffersize >= len) { bufchain_fetch_consume(ch, data, len); return true; } else { return false; } } size_t bufchain_fetch_consume_up_to(bufchain *ch, void *data, size_t len) { if (len > ch->buffersize) len = ch->buffersize; if (len) bufchain_fetch_consume(ch, data, len); return len; } /* ---------------------------------------------------------------------- * Debugging routines. */ #ifdef DEBUG extern void dputs(const char *); /* defined in per-platform *misc.c */ void debug_printf(const char *fmt, ...) { char *buf; va_list ap; va_start(ap, fmt); buf = dupvprintf(fmt, ap); dputs(buf); sfree(buf); va_end(ap); } void debug_memdump(const void *buf, int len, bool L) { int i; const unsigned char *p = buf; char foo[17]; if (L) { int delta; debug_printf("\t%d (0x%x) bytes:\n", len, len); delta = 15 & (uintptr_t)p; p -= delta; len += delta; } for (; 0 < len; p += 16, len -= 16) { dputs(" "); if (L) debug_printf("%p: ", p); strcpy(foo, "................"); /* sixteen dots */ for (i = 0; i < 16 && i < len; ++i) { if (&p[i] < (unsigned char *) buf) { dputs(" "); /* 3 spaces */ foo[i] = ' '; } else { debug_printf("%c%2.2x", &p[i] != (unsigned char *) buf && i % 4 ? '.' : ' ', p[i] ); if (p[i] >= ' ' && p[i] <= '~') foo[i] = (char) p[i]; } } foo[i] = '\0'; debug_printf("%*s%s\n", (16 - i) * 3 + 2, "", foo); } } #endif /* def DEBUG */ #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 bool smemeq(const void *av, const void *bv, size_t len) { const unsigned char *a = (const unsigned char *)av; const unsigned char *b = (const unsigned char *)bv; unsigned val = 0; while (len-- > 0) { val |= *a++ ^ *b++; } /* Now val is 0 iff we want to return 1, and in the range * 0x01..0xFF iff we want to return 0. So subtracting from 0x100 * will clear bit 8 iff we want to return 0, and leave it set iff * we want to return 1, so then we can just shift down. */ return (0x100 - val) >> 8; } int nullstrcmp(const char *a, const char *b) { if (a == NULL && b == NULL) return 0; if (a == NULL) return -1; if (b == NULL) return +1; return strcmp(a, b); } bool ptrlen_eq_string(ptrlen pl, const char *str) { size_t len = strlen(str); return (pl.len == len && !memcmp(pl.ptr, str, len)); } bool ptrlen_eq_ptrlen(ptrlen pl1, ptrlen pl2) { return (pl1.len == pl2.len && !memcmp(pl1.ptr, pl2.ptr, pl1.len)); } int ptrlen_strcmp(ptrlen pl1, ptrlen pl2) { size_t minlen = pl1.len < pl2.len ? pl1.len : pl2.len; if (minlen) { /* tolerate plX.ptr==NULL as long as plX.len==0 */ int cmp = memcmp(pl1.ptr, pl2.ptr, minlen); if (cmp) return cmp; } return pl1.len < pl2.len ? -1 : pl1.len > pl2.len ? +1 : 0; } bool ptrlen_startswith(ptrlen whole, ptrlen prefix, ptrlen *tail) { if (whole.len >= prefix.len && !memcmp(whole.ptr, prefix.ptr, prefix.len)) { if (tail) { tail->ptr = (const char *)whole.ptr + prefix.len; tail->len = whole.len - prefix.len; } return true; } return false; } bool ptrlen_endswith(ptrlen whole, ptrlen suffix, ptrlen *tail) { if (whole.len >= suffix.len && !memcmp((char *)whole.ptr + (whole.len - suffix.len), suffix.ptr, suffix.len)) { if (tail) { tail->ptr = whole.ptr; tail->len = whole.len - suffix.len; } return true; } return false; } ptrlen ptrlen_get_word(ptrlen *input, const char *separators) { const char *p = input->ptr, *end = p + input->len; ptrlen toret; while (p < end && strchr(separators, *p)) p++; toret.ptr = p; while (p < end && !strchr(separators, *p)) p++; toret.len = p - (const char *)toret.ptr; size_t to_consume = p - (const char *)input->ptr; assert(to_consume <= input->len); input->ptr = (const char *)input->ptr + to_consume; input->len -= to_consume; return toret; } char *mkstr(ptrlen pl) { char *p = snewn(pl.len + 1, char); memcpy(p, pl.ptr, pl.len); p[pl.len] = '\0'; return p; } bool strstartswith(const char *s, const char *t) { return !strncmp(s, t, strlen(t)); } bool strendswith(const char *s, const char *t) { size_t slen = strlen(s), tlen = strlen(t); return slen >= tlen && !strcmp(s + (slen - tlen), t); } size_t encode_utf8(void *output, unsigned long ch) { unsigned char *start = (unsigned char *)output, *p = start; if (ch < 0x80) { *p++ = ch; } else if (ch < 0x800) { *p++ = 0xC0 | (ch >> 6); *p++ = 0x80 | (ch & 0x3F); } else if (ch < 0x10000) { *p++ = 0xE0 | (ch >> 12); *p++ = 0x80 | ((ch >> 6) & 0x3F); *p++ = 0x80 | (ch & 0x3F); } else { *p++ = 0xF0 | (ch >> 18); *p++ = 0x80 | ((ch >> 12) & 0x3F); *p++ = 0x80 | ((ch >> 6) & 0x3F); *p++ = 0x80 | (ch & 0x3F); } return p - start; } void write_c_string_literal(FILE *fp, ptrlen str) { for (const char *p = str.ptr; p < (const char *)str.ptr + str.len; p++) { char c = *p; if (c == '\n') fputs("\\n", fp); else if (c == '\r') fputs("\\r", fp); else if (c == '\t') fputs("\\t", fp); else if (c == '\b') fputs("\\b", fp); else if (c == '\\') fputs("\\\\", fp); else if (c == '"') fputs("\\\"", fp); else if (c >= 32 && c <= 126) fputc(c, fp); else fprintf(fp, "\\%03o", (unsigned char)c); } } void memxor(uint8_t *out, const uint8_t *in1, const uint8_t *in2, size_t size) { switch (size & 15) { case 0: while (size >= 16) { size -= 16; *out++ = *in1++ ^ *in2++; case 15: *out++ = *in1++ ^ *in2++; case 14: *out++ = *in1++ ^ *in2++; case 13: *out++ = *in1++ ^ *in2++; case 12: *out++ = *in1++ ^ *in2++; case 11: *out++ = *in1++ ^ *in2++; case 10: *out++ = *in1++ ^ *in2++; case 9: *out++ = *in1++ ^ *in2++; case 8: *out++ = *in1++ ^ *in2++; case 7: *out++ = *in1++ ^ *in2++; case 6: *out++ = *in1++ ^ *in2++; case 5: *out++ = *in1++ ^ *in2++; case 4: *out++ = *in1++ ^ *in2++; case 3: *out++ = *in1++ ^ *in2++; case 2: *out++ = *in1++ ^ *in2++; case 1: *out++ = *in1++ ^ *in2++; } } } FingerprintType ssh2_pick_fingerprint( char **fingerprints, FingerprintType preferred_type) { /* * Keys are either SSH-2, in which case we have all fingerprint * types, or SSH-1, in which case we have only MD5. So we return * the default type if we can, or MD5 if that's all we have; no * need for a fully general preference-list system. */ FingerprintType fptype = fingerprints[preferred_type] ? preferred_type : SSH_FPTYPE_MD5; assert(fingerprints[fptype]); return fptype; } FingerprintType ssh2_pick_default_fingerprint(char **fingerprints) { return ssh2_pick_fingerprint(fingerprints, SSH_FPTYPE_DEFAULT); } putty-0.76/version.c0000644000175000017500000000115014072266314011407 00000000000000/* * PuTTY version numbering */ /* * The difficult part of deciding what goes in these version strings * is done in Buildscr, and then written into version.h. All we have * to do here is to drop it into variables of the right names. */ #include "putty.h" #include "ssh.h" #ifdef SOURCE_COMMIT #include "empty.h" #endif #include "version.h" const char ver[] = TEXTVER; const char sshver[] = SSHVER; const char commitid[] = SOURCE_COMMIT; /* * SSH local version string MUST be under 40 characters. Here's a * compile time assertion to verify this. */ enum { vorpal_sword = 1 / (sizeof(sshver) <= 40) }; putty-0.76/version.h0000644000175000017500000000033614072266315011422 00000000000000/* Generated by automated build script */ #define RELEASE 0.76 #define TEXTVER "Release 0.76" #define SSHVER "-Release-0.76" #define BINARY_VERSION 0,76,0,0 #define SOURCE_COMMIT "1fd7baa7344bb38d62a024e5dba3a720c67d05cf" putty-0.76/wcwidth.c0000644000175000017500000004164414072266314011407 00000000000000/* * This is an implementation of wcwidth() and wcswidth() (defined in * IEEE Std 1002.1-2001) for Unicode. * * http://www.opengroup.org/onlinepubs/007904975/functions/wcwidth.html * http://www.opengroup.org/onlinepubs/007904975/functions/wcswidth.html * * In fixed-width output devices, Latin characters all occupy a single * "cell" position of equal width, whereas ideographic CJK characters * occupy two such cells. Interoperability between terminal-line * applications and (teletype-style) character terminals using the * UTF-8 encoding requires agreement on which character should advance * the cursor by how many cell positions. No established formal * standards exist at present on which Unicode character shall occupy * how many cell positions on character terminals. These routines are * a first attempt of defining such behavior based on simple rules * applied to data provided by the Unicode Consortium. * * For some graphical characters, the Unicode standard explicitly * defines a character-cell width via the definition of the East Asian * FullWidth (F), Wide (W), Half-width (H), and Narrow (Na) classes. * In all these cases, there is no ambiguity about which width a * terminal shall use. For characters in the East Asian Ambiguous (A) * class, the width choice depends purely on a preference of backward * compatibility with either historic CJK or Western practice. * Choosing single-width for these characters is easy to justify as * the appropriate long-term solution, as the CJK practice of * displaying these characters as double-width comes from historic * implementation simplicity (8-bit encoded characters were displayed * single-width and 16-bit ones double-width, even for Greek, * Cyrillic, etc.) and not any typographic considerations. * * Much less clear is the choice of width for the Not East Asian * (Neutral) class. Existing practice does not dictate a width for any * of these characters. It would nevertheless make sense * typographically to allocate two character cells to characters such * as for instance EM SPACE or VOLUME INTEGRAL, which cannot be * represented adequately with a single-width glyph. The following * routines at present merely assign a single-cell width to all * neutral characters, in the interest of simplicity. This is not * entirely satisfactory and should be reconsidered before * establishing a formal standard in this area. At the moment, the * decision which Not East Asian (Neutral) characters should be * represented by double-width glyphs cannot yet be answered by * applying a simple rule from the Unicode database content. Setting * 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 #include "putty.h" /* for prototypes */ struct interval { unsigned int first; unsigned int last; }; /* auxiliary function for binary search in interval table */ static bool 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 false; while (max >= min) { mid = (min + max) / 2; if (ucs > table[mid].last) min = mid + 1; else if (ucs < table[mid].first) max = mid - 1; else return true; } return false; } /* The following two functions define the column width of an ISO 10646 * character as follows: * * - The null character (U+0000) has a column width of 0. * * - Other C0/C1 control characters and DEL will lead to a return * value of -1. * * - Non-spacing and enclosing combining characters (general * category code Mn or Me in the Unicode database) have a * column width of 0. * * - SOFT HYPHEN (U+00AD) has a column width of 1. * * - Other format characters (general category code Cf in the Unicode * database) and ZERO WIDTH SPACE (U+200B) have a column width of 0. * * - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF) * have a column width of 0. * * - Spacing characters in the East Asian Wide (W) or East Asian * Full-width (F) category as defined in Unicode Technical * Report #11 have a column width of 2. * * - All remaining characters (including all printable * 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 } }; /* A sorted list of intervals of double-width characters generated by: * https://raw.githubusercontent.com/GNOME/glib/37d4c2941bd0326b8b6e6bb22c81bd424fcc040b/glib/gen-unicode-tables.pl * from the Unicode 9.0.0 data files available at: * https://www.unicode.org/Public/13.0.0/ucd/ */ static const struct interval wide[] = { {0x1100, 0x115F}, {0x231A, 0x231B}, {0x2329, 0x232A}, {0x23E9, 0x23EC}, {0x23F0, 0x23F0}, {0x23F3, 0x23F3}, {0x25FD, 0x25FE}, {0x2614, 0x2615}, {0x2648, 0x2653}, {0x267F, 0x267F}, {0x2693, 0x2693}, {0x26A1, 0x26A1}, {0x26AA, 0x26AB}, {0x26BD, 0x26BE}, {0x26C4, 0x26C5}, {0x26CE, 0x26CE}, {0x26D4, 0x26D4}, {0x26EA, 0x26EA}, {0x26F2, 0x26F3}, {0x26F5, 0x26F5}, {0x26FA, 0x26FA}, {0x26FD, 0x26FD}, {0x2705, 0x2705}, {0x270A, 0x270B}, {0x2728, 0x2728}, {0x274C, 0x274C}, {0x274E, 0x274E}, {0x2753, 0x2755}, {0x2757, 0x2757}, {0x2795, 0x2797}, {0x27B0, 0x27B0}, {0x27BF, 0x27BF}, {0x2B1B, 0x2B1C}, {0x2B50, 0x2B50}, {0x2B55, 0x2B55}, {0x2E80, 0x2E99}, {0x2E9B, 0x2EF3}, {0x2F00, 0x2FD5}, {0x2FF0, 0x2FFB}, {0x3000, 0x303E}, {0x3041, 0x3096}, {0x3099, 0x30FF}, {0x3105, 0x312F}, {0x3131, 0x318E}, {0x3190, 0x31E3}, {0x31F0, 0x321E}, {0x3220, 0x3247}, {0x3250, 0x4DBF}, {0x4E00, 0xA48C}, {0xA490, 0xA4C6}, {0xA960, 0xA97C}, {0xAC00, 0xD7A3}, {0xF900, 0xFAFF}, {0xFE10, 0xFE19}, {0xFE30, 0xFE52}, {0xFE54, 0xFE66}, {0xFE68, 0xFE6B}, {0xFF01, 0xFF60}, {0xFFE0, 0xFFE6}, {0x16FE0, 0x16FE4}, {0x16FF0, 0x16FF1}, {0x17000, 0x187F7}, {0x18800, 0x18CD5}, {0x18D00, 0x18D08}, {0x1B000, 0x1B11E}, {0x1B150, 0x1B152}, {0x1B164, 0x1B167}, {0x1B170, 0x1B2FB}, {0x1F004, 0x1F004}, {0x1F0CF, 0x1F0CF}, {0x1F18E, 0x1F18E}, {0x1F191, 0x1F19A}, {0x1F200, 0x1F202}, {0x1F210, 0x1F23B}, {0x1F240, 0x1F248}, {0x1F250, 0x1F251}, {0x1F260, 0x1F265}, {0x1F300, 0x1F320}, {0x1F32D, 0x1F335}, {0x1F337, 0x1F37C}, {0x1F37E, 0x1F393}, {0x1F3A0, 0x1F3CA}, {0x1F3CF, 0x1F3D3}, {0x1F3E0, 0x1F3F0}, {0x1F3F4, 0x1F3F4}, {0x1F3F8, 0x1F43E}, {0x1F440, 0x1F440}, {0x1F442, 0x1F4FC}, {0x1F4FF, 0x1F53D}, {0x1F54B, 0x1F54E}, {0x1F550, 0x1F567}, {0x1F57A, 0x1F57A}, {0x1F595, 0x1F596}, {0x1F5A4, 0x1F5A4}, {0x1F5FB, 0x1F64F}, {0x1F680, 0x1F6C5}, {0x1F6CC, 0x1F6CC}, {0x1F6D0, 0x1F6D2}, {0x1F6D5, 0x1F6D7}, {0x1F6EB, 0x1F6EC}, {0x1F6F4, 0x1F6FC}, {0x1F7E0, 0x1F7EB}, {0x1F90C, 0x1F93A}, {0x1F93C, 0x1F945}, {0x1F947, 0x1F978}, {0x1F97A, 0x1F9CB}, {0x1F9CD, 0x1F9FF}, {0x1FA70, 0x1FA74}, {0x1FA78, 0x1FA7A}, {0x1FA80, 0x1FA86}, {0x1FA90, 0x1FAA8}, {0x1FAB0, 0x1FAB6}, {0x1FAC0, 0x1FAC2}, {0x1FAD0, 0x1FAD6}, {0x20000, 0x2FFFD}, {0x30000, 0x3FFFD}, }; /* test for 8-bit control characters */ if (ucs == 0) return 0; if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0)) return -1; /* binary search in table of non-spacing characters */ if (bisearch(ucs, combining, sizeof(combining) / sizeof(struct interval) - 1)) return 0; /* if we arrive here, ucs is not a combining or C0/C1 control character */ /* binary search in table of double-width characters */ if (bisearch(ucs, wide, sizeof(wide) / sizeof(struct interval) - 1)) return 2; /* normal width character */ return 1; } 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) { /* A sorted list of intervals of ambiguous width characters generated by: * https://raw.githubusercontent.com/GNOME/glib/37d4c2941bd0326b8b6e6bb22c81bd424fcc040b/glib/gen-unicode-tables.pl * from the Unicode 9.0.0 data files available at: * http://www.unicode.org/Public/9.0.0/ucd/ */ static const struct interval ambiguous[] = { {0x00A1, 0x00A1}, {0x00A4, 0x00A4}, {0x00A7, 0x00A8}, {0x00AA, 0x00AA}, {0x00AD, 0x00AE}, {0x00B0, 0x00B4}, {0x00B6, 0x00BA}, {0x00BC, 0x00BF}, {0x00C6, 0x00C6}, {0x00D0, 0x00D0}, {0x00D7, 0x00D8}, {0x00DE, 0x00E1}, {0x00E6, 0x00E6}, {0x00E8, 0x00EA}, {0x00EC, 0x00ED}, {0x00F0, 0x00F0}, {0x00F2, 0x00F3}, {0x00F7, 0x00FA}, {0x00FC, 0x00FC}, {0x00FE, 0x00FE}, {0x0101, 0x0101}, {0x0111, 0x0111}, {0x0113, 0x0113}, {0x011B, 0x011B}, {0x0126, 0x0127}, {0x012B, 0x012B}, {0x0131, 0x0133}, {0x0138, 0x0138}, {0x013F, 0x0142}, {0x0144, 0x0144}, {0x0148, 0x014B}, {0x014D, 0x014D}, {0x0152, 0x0153}, {0x0166, 0x0167}, {0x016B, 0x016B}, {0x01CE, 0x01CE}, {0x01D0, 0x01D0}, {0x01D2, 0x01D2}, {0x01D4, 0x01D4}, {0x01D6, 0x01D6}, {0x01D8, 0x01D8}, {0x01DA, 0x01DA}, {0x01DC, 0x01DC}, {0x0251, 0x0251}, {0x0261, 0x0261}, {0x02C4, 0x02C4}, {0x02C7, 0x02C7}, {0x02C9, 0x02CB}, {0x02CD, 0x02CD}, {0x02D0, 0x02D0}, {0x02D8, 0x02DB}, {0x02DD, 0x02DD}, {0x02DF, 0x02DF}, {0x0300, 0x036F}, {0x0391, 0x03A1}, {0x03A3, 0x03A9}, {0x03B1, 0x03C1}, {0x03C3, 0x03C9}, {0x0401, 0x0401}, {0x0410, 0x044F}, {0x0451, 0x0451}, {0x2010, 0x2010}, {0x2013, 0x2016}, {0x2018, 0x2019}, {0x201C, 0x201D}, {0x2020, 0x2022}, {0x2024, 0x2027}, {0x2030, 0x2030}, {0x2032, 0x2033}, {0x2035, 0x2035}, {0x203B, 0x203B}, {0x203E, 0x203E}, {0x2074, 0x2074}, {0x207F, 0x207F}, {0x2081, 0x2084}, {0x20AC, 0x20AC}, {0x2103, 0x2103}, {0x2105, 0x2105}, {0x2109, 0x2109}, {0x2113, 0x2113}, {0x2116, 0x2116}, {0x2121, 0x2122}, {0x2126, 0x2126}, {0x212B, 0x212B}, {0x2153, 0x2154}, {0x215B, 0x215E}, {0x2160, 0x216B}, {0x2170, 0x2179}, {0x2189, 0x2189}, {0x2190, 0x2199}, {0x21B8, 0x21B9}, {0x21D2, 0x21D2}, {0x21D4, 0x21D4}, {0x21E7, 0x21E7}, {0x2200, 0x2200}, {0x2202, 0x2203}, {0x2207, 0x2208}, {0x220B, 0x220B}, {0x220F, 0x220F}, {0x2211, 0x2211}, {0x2215, 0x2215}, {0x221A, 0x221A}, {0x221D, 0x2220}, {0x2223, 0x2223}, {0x2225, 0x2225}, {0x2227, 0x222C}, {0x222E, 0x222E}, {0x2234, 0x2237}, {0x223C, 0x223D}, {0x2248, 0x2248}, {0x224C, 0x224C}, {0x2252, 0x2252}, {0x2260, 0x2261}, {0x2264, 0x2267}, {0x226A, 0x226B}, {0x226E, 0x226F}, {0x2282, 0x2283}, {0x2286, 0x2287}, {0x2295, 0x2295}, {0x2299, 0x2299}, {0x22A5, 0x22A5}, {0x22BF, 0x22BF}, {0x2312, 0x2312}, {0x2460, 0x24E9}, {0x24EB, 0x254B}, {0x2550, 0x2573}, {0x2580, 0x258F}, {0x2592, 0x2595}, {0x25A0, 0x25A1}, {0x25A3, 0x25A9}, {0x25B2, 0x25B3}, {0x25B6, 0x25B7}, {0x25BC, 0x25BD}, {0x25C0, 0x25C1}, {0x25C6, 0x25C8}, {0x25CB, 0x25CB}, {0x25CE, 0x25D1}, {0x25E2, 0x25E5}, {0x25EF, 0x25EF}, {0x2605, 0x2606}, {0x2609, 0x2609}, {0x260E, 0x260F}, {0x261C, 0x261C}, {0x261E, 0x261E}, {0x2640, 0x2640}, {0x2642, 0x2642}, {0x2660, 0x2661}, {0x2663, 0x2665}, {0x2667, 0x266A}, {0x266C, 0x266D}, {0x266F, 0x266F}, {0x269E, 0x269F}, {0x26BF, 0x26BF}, {0x26C6, 0x26CD}, {0x26CF, 0x26D3}, {0x26D5, 0x26E1}, {0x26E3, 0x26E3}, {0x26E8, 0x26E9}, {0x26EB, 0x26F1}, {0x26F4, 0x26F4}, {0x26F6, 0x26F9}, {0x26FB, 0x26FC}, {0x26FE, 0x26FF}, {0x273D, 0x273D}, {0x2776, 0x277F}, {0x2B56, 0x2B59}, {0x3248, 0x324F}, {0xE000, 0xF8FF}, {0xFE00, 0xFE0F}, {0xFFFD, 0xFFFD}, {0x1F100, 0x1F10A}, {0x1F110, 0x1F12D}, {0x1F130, 0x1F169}, {0x1F170, 0x1F18D}, {0x1F18F, 0x1F190}, {0x1F19B, 0x1F1AC}, {0xE0100, 0xE01EF}, {0xF0000, 0xFFFFD}, {0x100000, 0x10FFFD}, }; /* binary search in table of non-spacing characters */ if (bisearch(ucs, ambiguous, 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; } putty-0.76/wildcard.c0000644000175000017500000003772714072266314011536 00000000000000/* * Wildcard matching engine for use with SFTP-based file transfer * programs (PSFTP, new-look PSCP): since SFTP has no notion of * getting the remote side to do globbing (and rightly so) we have * to do it locally, by retrieving all the filenames in a directory * and checking each against the wildcard pattern. */ #include #include #include #include "putty.h" /* * Definition of wildcard syntax: * * - * matches any sequence of characters, including zero. * - ? matches exactly one character which can be anything. * - [abc] matches exactly one character which is a, b or c. * - [a-f] matches anything from a through f. * - [^a-f] matches anything _except_ a through f. * - [-_] matches - or _; [^-_] matches anything else. (The - is * non-special if it occurs immediately after the opening * bracket or ^.) * - [a^] matches an a or a ^. (The ^ is non-special if it does * _not_ occur immediately after the opening bracket.) * - \*, \?, \[, \], \\ match the single characters *, ?, [, ], \. * - All other characters are non-special and match themselves. */ /* * Some notes on differences from POSIX globs (IEEE Std 1003.1, 2003 ed.): * - backslashes act as escapes even within [] bracket expressions * - does not support [!...] for non-matching list (POSIX are weird); * NB POSIX allows [^...] as well via "A bracket expression starting * with an unquoted circumflex character produces unspecified * results". If we wanted to allow [!...] we might want to define * [^!] as having its literal meaning (match '^' or '!'). * - none of the scary [[:class:]] stuff, etc */ /* * The wildcard matching technique we use is very simple and * potentially O(N^2) in running time, but I don't anticipate it * being that bad in reality (particularly since N will be the size * of a filename, which isn't all that much). Perhaps one day, once * PuTTY has grown a regexp matcher for some other reason, I might * come back and reimplement wildcards by translating them into * regexps or directly into NFAs; but for the moment, in the * absence of any other need for the NFA->DFA translation engine, * anything more than the simplest possible wildcard matcher is * vast code-size overkill. * * Essentially, these wildcards are much simpler than regexps in * that they consist of a sequence of rigid fragments (? and [...] * can never match more or less than one character) separated by * asterisks. It is therefore extremely simple to look at a rigid * fragment and determine whether or not it begins at a particular * point in the test string; so we can search along the string * until we find each fragment, then search for the next. As long * as we find each fragment in the _first_ place it occurs, there * will never be a danger of having to backpedal and try to find it * again somewhere else. */ enum { WC_TRAILINGBACKSLASH = 1, WC_UNCLOSEDCLASS, WC_INVALIDRANGE }; /* * Error reporting is done by returning various negative values * from the wildcard routines. Passing any such value to wc_error * will give a human-readable message. */ const char *wc_error(int value) { value = abs(value); switch (value) { case WC_TRAILINGBACKSLASH: return "'\' occurred at end of string (expected another character)"; case WC_UNCLOSEDCLASS: return "expected ']' to close character class"; case WC_INVALIDRANGE: return "character range was not terminated (']' just after '-')"; } return "INTERNAL ERROR: unrecognised wildcard error number"; } /* * This is the routine that tests a target string to see if an * initial substring of it matches a fragment. If successful, it * returns 1, and advances both `fragment' and `target' past the * fragment and matching substring respectively. If unsuccessful it * returns zero. If the wildcard fragment suffers a syntax error, * it returns <0 and the precise value indexes into wc_error. */ static int wc_match_fragment(const char **fragment, const char **target, const char *target_end) { const char *f, *t; f = *fragment; t = *target; /* * The fragment terminates at either the end of the string, or * the first (unescaped) *. */ while (*f && *f != '*' && t < target_end) { /* * Extract one character from t, and one character's worth * of pattern from f, and step along both. Return 0 if they * fail to match. */ if (*f == '\\') { /* * Backslash, which means f[1] is to be treated as a * literal character no matter what it is. It may not * be the end of the string. */ if (!f[1]) return -WC_TRAILINGBACKSLASH; /* error */ if (f[1] != *t) return 0; /* failed to match */ f += 2; } else if (*f == '?') { /* * Question mark matches anything. */ f++; } else if (*f == '[') { bool invert = false; bool matched = false; /* * Open bracket introduces a character class. */ f++; if (*f == '^') { invert = true; f++; } while (*f != ']') { if (*f == '\\') f++; /* backslashes still work */ if (!*f) return -WC_UNCLOSEDCLASS; /* error again */ if (f[1] == '-') { int lower, upper, ourchr; lower = (unsigned char) *f++; f++; /* eat the minus */ if (*f == ']') return -WC_INVALIDRANGE; /* different error! */ if (*f == '\\') f++; /* backslashes _still_ work */ if (!*f) return -WC_UNCLOSEDCLASS; /* error again */ upper = (unsigned char) *f++; ourchr = (unsigned char) *t; if (lower > upper) { int t = lower; lower = upper; upper = t; } if (ourchr >= lower && ourchr <= upper) matched = true; } else { matched |= (*t == *f++); } } if (invert == matched) return 0; /* failed to match character class */ f++; /* eat the ] */ } else { /* * Non-special character matches itself. */ if (*f != *t) return 0; f++; } /* * Now we've done that, increment t past the character we * matched. */ t++; } if (!*f || *f == '*') { /* * We have reached the end of f without finding a mismatch; * so we're done. Update the caller pointers and return 1. */ *fragment = f; *target = t; return 1; } /* * Otherwise, we must have reached the end of t before we * reached the end of f; so we've failed. Return 0. */ return 0; } /* * This is the real wildcard matching routine. It returns 1 for a * successful match, 0 for an unsuccessful match, and <0 for a * syntax error in the wildcard. */ static int wc_match_inner( const char *wildcard, const char *target, size_t target_len) { const char *target_end = target + target_len; int ret; /* * Every time we see a '*' _followed_ by a fragment, we just * search along the string for a location at which the fragment * matches. The only special case is when we see a fragment * right at the start, in which case we just call the matching * routine once and give up if it fails. */ if (*wildcard != '*') { ret = wc_match_fragment(&wildcard, &target, target_end); if (ret <= 0) return ret; /* pass back failure or error alike */ } while (*wildcard) { assert(*wildcard == '*'); while (*wildcard == '*') wildcard++; /* * It's possible we've just hit the end of the wildcard * after seeing a *, in which case there's no need to * bother searching any more because we've won. */ if (!*wildcard) return 1; /* * Now `wildcard' points at the next fragment. So we * attempt to match it against `target', and if that fails * we increment `target' and try again, and so on. When we * find we're about to try matching against the empty * string, we give up and return 0. */ ret = 0; while (*target) { const char *save_w = wildcard, *save_t = target; ret = wc_match_fragment(&wildcard, &target, target_end); if (ret < 0) return ret; /* syntax error */ if (ret > 0 && !*wildcard && target != target_end) { /* * Final special case - literally. * * This situation arises when we are matching a * _terminal_ fragment of the wildcard (that is, * there is nothing after it, e.g. "*a"), and it * has matched _too early_. For example, matching * "*a" against "parka" will match the "a" fragment * against the _first_ a, and then (if it weren't * for this special case) matching would fail * because we're at the end of the wildcard but not * at the end of the target string. * * In this case what we must do is measure the * length of the fragment in the target (which is * why we saved `target'), jump straight to that * distance from the end of the string using * strlen, and match the same fragment again there * (which is why we saved `wildcard'). Then we * return whatever that operation returns. */ target = target_end - (target - save_t); wildcard = save_w; return wc_match_fragment(&wildcard, &target, target_end); } if (ret > 0) break; target++; } if (ret > 0) continue; return 0; } /* * If we reach here, it must be because we successfully matched * a fragment and then found ourselves right at the end of the * wildcard. Hence, we return 1 if and only if we are also * right at the end of the target. */ return target == target_end; } int wc_match(const char *wildcard, const char *target) { return wc_match_inner(wildcard, target, strlen(target)); } int wc_match_pl(const char *wildcard, ptrlen target) { return wc_match_inner(wildcard, target.ptr, target.len); } /* * Another utility routine that translates a non-wildcard string * into its raw equivalent by removing any escaping backslashes. * Expects a target string buffer of anything up to the length of * the original wildcard. You can also pass NULL as the output * buffer if you're only interested in the return value. * * Returns true on success, or false if a wildcard character was * encountered. In the latter case the output string MAY not be * zero-terminated and you should not use it for anything! */ bool wc_unescape(char *output, const char *wildcard) { while (*wildcard) { if (*wildcard == '\\') { wildcard++; /* We are lenient about trailing backslashes in non-wildcards. */ if (*wildcard) { if (output) *output++ = *wildcard; wildcard++; } } else if (*wildcard == '*' || *wildcard == '?' || *wildcard == '[' || *wildcard == ']') { return false; /* it's a wildcard! */ } else { if (output) *output++ = *wildcard; wildcard++; } } if (output) *output = '\0'; return true; /* it's clean */ } #ifdef TESTMODE struct test { const char *wildcard; const char *target; int expected_result; }; const struct test fragment_tests[] = { /* * We exhaustively unit-test the fragment matching routine * itself, which should save us the need to test all its * intricacies during the full wildcard tests. */ {"abc", "abc", 1}, {"abc", "abd", 0}, {"abc", "abcd", 1}, {"abcd", "abc", 0}, {"ab[cd]", "abc", 1}, {"ab[cd]", "abd", 1}, {"ab[cd]", "abe", 0}, {"ab[^cd]", "abc", 0}, {"ab[^cd]", "abd", 0}, {"ab[^cd]", "abe", 1}, {"ab\\", "abc", -WC_TRAILINGBACKSLASH}, {"ab\\*", "ab*", 1}, {"ab\\?", "ab*", 0}, {"ab?", "abc", 1}, {"ab?", "ab", 0}, {"ab[", "abc", -WC_UNCLOSEDCLASS}, {"ab[c-", "abb", -WC_UNCLOSEDCLASS}, {"ab[c-]", "abb", -WC_INVALIDRANGE}, {"ab[c-e]", "abb", 0}, {"ab[c-e]", "abc", 1}, {"ab[c-e]", "abd", 1}, {"ab[c-e]", "abe", 1}, {"ab[c-e]", "abf", 0}, {"ab[e-c]", "abb", 0}, {"ab[e-c]", "abc", 1}, {"ab[e-c]", "abd", 1}, {"ab[e-c]", "abe", 1}, {"ab[e-c]", "abf", 0}, {"ab[^c-e]", "abb", 1}, {"ab[^c-e]", "abc", 0}, {"ab[^c-e]", "abd", 0}, {"ab[^c-e]", "abe", 0}, {"ab[^c-e]", "abf", 1}, {"ab[^e-c]", "abb", 1}, {"ab[^e-c]", "abc", 0}, {"ab[^e-c]", "abd", 0}, {"ab[^e-c]", "abe", 0}, {"ab[^e-c]", "abf", 1}, {"ab[a^]", "aba", 1}, {"ab[a^]", "ab^", 1}, {"ab[a^]", "abb", 0}, {"ab[^a^]", "aba", 0}, {"ab[^a^]", "ab^", 0}, {"ab[^a^]", "abb", 1}, {"ab[-c]", "ab-", 1}, {"ab[-c]", "abc", 1}, {"ab[-c]", "abd", 0}, {"ab[^-c]", "ab-", 0}, {"ab[^-c]", "abc", 0}, {"ab[^-c]", "abd", 1}, {"ab[\\[-\\]]", "abZ", 0}, {"ab[\\[-\\]]", "ab[", 1}, {"ab[\\[-\\]]", "ab\\", 1}, {"ab[\\[-\\]]", "ab]", 1}, {"ab[\\[-\\]]", "ab^", 0}, {"ab[^\\[-\\]]", "abZ", 1}, {"ab[^\\[-\\]]", "ab[", 0}, {"ab[^\\[-\\]]", "ab\\", 0}, {"ab[^\\[-\\]]", "ab]", 0}, {"ab[^\\[-\\]]", "ab^", 1}, {"ab[a-fA-F]", "aba", 1}, {"ab[a-fA-F]", "abF", 1}, {"ab[a-fA-F]", "abZ", 0}, }; const struct test full_tests[] = { {"a", "argh", 0}, {"a", "ba", 0}, {"a", "a", 1}, {"a*", "aardvark", 1}, {"a*", "badger", 0}, {"*a", "park", 0}, {"*a", "pArka", 1}, {"*a", "parka", 1}, {"*a*", "park", 1}, {"*a*", "perk", 0}, {"?b*r?", "abracadabra", 1}, {"?b*r?", "abracadabr", 0}, {"?b*r?", "abracadabzr", 0}, }; int main(void) { int i; int fails, passes; fails = passes = 0; for (i = 0; i < sizeof(fragment_tests)/sizeof(*fragment_tests); i++) { const char *f, *t; int eret, aret; f = fragment_tests[i].wildcard; t = fragment_tests[i].target; eret = fragment_tests[i].expected_result; aret = wc_match_fragment(&f, &t, t + strlen(t)); if (aret != eret) { printf("failed test: /%s/ against /%s/ returned %d not %d\n", fragment_tests[i].wildcard, fragment_tests[i].target, aret, eret); fails++; } else passes++; } for (i = 0; i < sizeof(full_tests)/sizeof(*full_tests); i++) { const char *f, *t; int eret, aret; f = full_tests[i].wildcard; t = full_tests[i].target; eret = full_tests[i].expected_result; aret = wc_match(f, t); if (aret != eret) { printf("failed test: /%s/ against /%s/ returned %d not %d\n", full_tests[i].wildcard, full_tests[i].target, aret, eret); fails++; } else passes++; } printf("passed %d, failed %d\n", passes, fails); return 0; } #endif putty-0.76/x11fwd.c0000644000175000017500000011627014072266315011047 00000000000000/* * Platform-independent bits of X11 forwarding. */ #include #include #include #include #include "putty.h" #include "ssh.h" #include "sshchan.h" #include "tree234.h" static inline uint16_t GET_16BIT_X11(char endian, const void *p) { return endian == 'B' ? GET_16BIT_MSB_FIRST(p) : GET_16BIT_LSB_FIRST(p); } static inline void PUT_16BIT_X11(char endian, void *p, uint16_t value) { if (endian == 'B') PUT_16BIT_MSB_FIRST(p, value); else PUT_16BIT_LSB_FIRST(p, value); } const char *const x11_authnames[] = { "", "MIT-MAGIC-COOKIE-1", "XDM-AUTHORIZATION-1" }; struct XDMSeen { unsigned int time; unsigned char clientid[6]; }; typedef struct X11Connection { 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; bool verified; bool input_wanted; bool no_data_sent_to_x_client; char *peer_addr; int peer_port; SshChannel *c; /* channel structure held by SSH backend */ Socket *s; Plug plug; Channel chan; } X11Connection; static int xdmseen_cmp(void *a, void *b) { struct XDMSeen *sa = a, *sb = b; return sa->time > sb->time ? 1 : sa->time < sb->time ? -1 : memcmp(sa->clientid, sb->clientid, sizeof(sa->clientid)); } 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) { random_read(auth->data, auth->datalen); 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) { random_read(auth->data, 15); auth->data[15] = auth->data[8]; auth->data[8] = 0; 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 = NULL; 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(const char *display, Conf *conf, char **error_msg) { struct X11Display *disp = snew(struct X11Display); char *localcopy; *error_msg = NULL; if (!display || !*display) { localcopy = platform_get_x_display(); if (!localcopy || !*localcopy) { sfree(localcopy); localcopy = dupstr(":0"); /* plausible default for any platform */ } } else localcopy = dupstr(display); /* * Parse the display name. * * We expect this to have one of the following forms: * * - the standard X format which looks like * [ [ protocol '/' ] host ] ':' displaynumber [ '.' screennumber ] * (X11 also permits a double colon to indicate DECnet, but * that's not our problem, thankfully!) * * - only seen in the wild on MacOS (so far): a pathname to a * Unix-domain socket, which will typically and confusingly * end in ":0", and which I'm currently distinguishing from * the standard scheme by noting that it starts with '/'. */ if (localcopy[0] == '/') { disp->unixsocketpath = localcopy; disp->unixdomain = true; disp->hostname = NULL; disp->displaynum = -1; disp->screennum = 0; disp->addr = NULL; } else { char *colon, *dot, *slash; char *protocol, *hostname; colon = host_strrchr(localcopy, ':'); if (!colon) { *error_msg = dupprintf("display name '%s' has no ':number'" " suffix", localcopy); sfree(disp); sfree(localcopy); return NULL; } *colon++ = '\0'; dot = strchr(colon, '.'); if (dot) *dot++ = '\0'; disp->displaynum = atoi(colon); if (dot) disp->screennum = atoi(dot); else disp->screennum = 0; protocol = NULL; hostname = localcopy; if (colon > localcopy) { slash = strchr(localcopy, '/'); if (slash) { *slash++ = '\0'; protocol = localcopy; hostname = slash; } } disp->hostname = *hostname ? dupstr(hostname) : NULL; if (protocol) disp->unixdomain = (!strcmp(protocol, "local") || !strcmp(protocol, "unix")); else if (!*hostname || !strcmp(hostname, "unix")) disp->unixdomain = platform_uses_x11_unix_by_default; else disp->unixdomain = false; if (!disp->hostname && !disp->unixdomain) disp->hostname = dupstr("localhost"); disp->unixsocketpath = NULL; disp->addr = NULL; sfree(localcopy); } /* * 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, NULL, NULL); if ((err = sk_addr_error(disp->addr)) != NULL) { *error_msg = dupprintf("unable to resolve host name '%s' in " "display name", disp->hostname); sk_addr_free(disp->addr); sfree(disp->hostname); sfree(disp->unixsocketpath); sfree(disp); return NULL; } } /* * Try upgrading an IP-style localhost display to a Unix-socket * display (as the standard X connection libraries do). */ if (!disp->unixdomain && sk_address_is_local(disp->addr)) { SockAddr *ux = platform_get_x11_unix_address(NULL, disp->displaynum); const char *err = sk_addr_error(ux); if (!err) { /* Create trial connection to see if there is a useful Unix-domain * socket */ Socket *s = sk_new(sk_addr_dup(ux), 0, false, false, false, false, nullplug); err = sk_socket_error(s); sk_close(s); } if (err) { sk_addr_free(ux); } else { sk_addr_free(disp->addr); disp->unixdomain = true; disp->addr = ux; /* Fill in the rest in a moment */ } } if (disp->unixdomain) { if (!disp->addr) disp->addr = platform_get_x11_unix_address(disp->unixsocketpath, disp->displaynum); 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 const 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 (((unsigned long)t - (unsigned long)tim + XDM_MAXSKEW) > 2*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; } ptrlen BinarySource_get_string_xauth(BinarySource *src) { size_t len = get_uint16(src); return get_data(src, len); } #define get_string_xauth(src) \ BinarySource_get_string_xauth(BinarySource_UPCAST(src)) void BinarySink_put_stringpl_xauth(BinarySink *bs, ptrlen pl) { assert((pl.len >> 16) == 0); put_uint16(bs, pl.len); put_datapl(bs, pl); } #define put_stringpl_xauth(bs, ptrlen) \ BinarySink_put_stringpl_xauth(BinarySink_UPCAST(bs),ptrlen) void x11_get_auth_from_authfile(struct X11Display *disp, const char *authfilename) { FILE *authfp; char *buf; int size; BinarySource src[1]; int family, protocol; ptrlen addr, protoname, data; char *displaynum_string; int displaynum; bool ideal_match = false; char *ourhostname; /* A maximally sized (wildly implausible) .Xauthority record * consists of a 16-bit integer to start with, then four strings, * each of which has a 16-bit length field followed by that many * bytes of data (i.e. up to 0xFFFF bytes). */ const size_t MAX_RECORD_SIZE = 2 + 4 * (2+0xFFFF); /* We'll want a buffer of twice that size (see below). */ const size_t BUF_SIZE = 2 * MAX_RECORD_SIZE; /* * 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 * server). * * This is apparently a fudge to get round the meaninglessness of * "localhost" in a shared-home-directory context -- xauth entries * for Unix-domain sockets already disambiguate this by storing * the *local* hostname in the conveniently-blank hostname field, * but IP "localhost" records couldn't do this. So, typically, an * IP "localhost" entry in the auth database isn't present and if * it were it would be ignored. * * However, we don't entirely trust that (say) Windows X servers * won't rely on a straight "localhost" entry, bad idea though * that is; so if we can't find a Unix-domain-socket entry we'll * fall back to an IP-based entry if we can find one. */ bool localhost = !disp->unixdomain && sk_address_is_local(disp->addr); authfp = fopen(authfilename, "rb"); if (!authfp) return; ourhostname = get_hostname(); /* * Allocate enough space to hold two maximally sized records, so * that a full record can start anywhere in the first half. That * way we avoid the accidentally-quadratic algorithm that would * arise if we moved everything to the front of the buffer after * consuming each record; instead, we only move everything to the * front after our current position gets past the half-way mark. * Before then, there's no need to move anyway; so this guarantees * linear time, in that every byte written into this buffer moves * at most once (because every move is from the second half of the * buffer to the first half). */ buf = snewn(BUF_SIZE, char); size = fread(buf, 1, BUF_SIZE, authfp); BinarySource_BARE_INIT(src, buf, size); while (!ideal_match) { bool match = false; if (src->pos >= MAX_RECORD_SIZE) { size -= src->pos; memcpy(buf, buf + src->pos, size); size += fread(buf + size, 1, BUF_SIZE - size, authfp); BinarySource_BARE_INIT(src, buf, size); } family = get_uint16(src); addr = get_string_xauth(src); displaynum_string = mkstr(get_string_xauth(src)); displaynum = displaynum_string[0] ? atoi(displaynum_string) : -1; sfree(displaynum_string); protoname = get_string_xauth(src); data = get_string_xauth(src); if (get_err(src)) break; /* * Now we have a full X authority record in memory. See * whether it matches the display we're trying to * authenticate to. * * The details we've just read should be interpreted as * follows: * * - 'family' is the network address family used to * connect to the display. 0 means IPv4; 6 means IPv6; * 256 means Unix-domain sockets. * * - 'addr' is the network address itself. For IPv4 and * IPv6, this is a string of binary data of the * appropriate length (respectively 4 and 16 bytes) * representing the address in big-endian format, e.g. * 7F 00 00 01 means IPv4 localhost. For Unix-domain * sockets, this is the host name of the machine on * which the Unix-domain display resides (so that an * .Xauthority file on a shared file system can contain * authority entries for Unix-domain displays on * several machines without them clashing). * * - 'displaynum' is the display number. An empty display * number is a wildcard for any display number. * * - 'protoname' is the authorisation protocol, encoded as * its canonical string name (i.e. "MIT-MAGIC-COOKIE-1", * "XDM-AUTHORIZATION-1" or something we don't recognise). * * - 'data' is the actual authorisation data, stored in * binary form. */ if (disp->displaynum < 0 || (displaynum >= 0 && disp->displaynum != displaynum)) continue; /* not the one */ for (protocol = 1; protocol < lenof(x11_authnames); protocol++) if (ptrlen_eq_string(protoname, x11_authnames[protocol])) break; if (protocol == lenof(x11_authnames)) continue; /* don't recognise this protocol, look for another */ switch (family) { case 0: /* IPv4 */ if (!disp->unixdomain && sk_addrtype(disp->addr) == ADDRTYPE_IPV4) { char buf[4]; sk_addrcopy(disp->addr, buf); if (addr.len == 4 && !memcmp(addr.ptr, buf, 4)) { match = true; /* If this is a "localhost" entry, note it down * but carry on looking for a Unix-domain entry. */ ideal_match = !localhost; } } break; case 6: /* IPv6 */ if (!disp->unixdomain && sk_addrtype(disp->addr) == ADDRTYPE_IPV6) { char buf[16]; sk_addrcopy(disp->addr, buf); if (addr.len == 16 && !memcmp(addr.ptr, buf, 16)) { match = true; ideal_match = !localhost; } } break; case 256: /* Unix-domain / localhost */ if ((disp->unixdomain || localhost) && ourhostname && ptrlen_eq_string(addr, ourhostname)) { /* A matching Unix-domain socket is always the best * match. */ match = true; ideal_match = true; } break; } if (match) { /* Current best guess -- may be overridden if !ideal_match */ disp->localauthproto = protocol; sfree(disp->localauthdata); /* free previous guess, if any */ disp->localauthdata = snewn(data.len, unsigned char); memcpy(disp->localauthdata, data.ptr, data.len); disp->localauthdatalen = data.len; } } fclose(authfp); smemclr(buf, 2 * MAX_RECORD_SIZE); sfree(buf); sfree(ourhostname); } void x11_format_auth_for_authfile( BinarySink *bs, SockAddr *addr, int display_no, ptrlen authproto, ptrlen authdata) { if (sk_address_is_special_local(addr)) { char *ourhostname = get_hostname(); put_uint16(bs, 256); /* indicates Unix-domain socket */ put_stringpl_xauth(bs, ptrlen_from_asciz(ourhostname)); sfree(ourhostname); } else if (sk_addrtype(addr) == ADDRTYPE_IPV4) { char ipv4buf[4]; sk_addrcopy(addr, ipv4buf); put_uint16(bs, 0); /* indicates IPv4 */ put_stringpl_xauth(bs, make_ptrlen(ipv4buf, 4)); } else if (sk_addrtype(addr) == ADDRTYPE_IPV6) { char ipv6buf[16]; sk_addrcopy(addr, ipv6buf); put_uint16(bs, 6); /* indicates IPv6 */ put_stringpl_xauth(bs, make_ptrlen(ipv6buf, 16)); } else { unreachable("Bad address type in x11_format_auth_for_authfile"); } { char *numberbuf = dupprintf("%d", display_no); put_stringpl_xauth(bs, ptrlen_from_asciz(numberbuf)); sfree(numberbuf); } put_stringpl_xauth(bs, authproto); put_stringpl_xauth(bs, authdata); } static void x11_log(Plug *p, PlugLogType 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 void x11_closing(Plug *plug, const char *error_msg, int error_code, bool calling_back) { struct X11Connection *xconn = container_of( plug, 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_initiate_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); } } static void x11_receive(Plug *plug, int urgent, const char *data, size_t len) { struct X11Connection *xconn = container_of( plug, struct X11Connection, plug); xconn->no_data_sent_to_x_client = false; sshfwd_write(xconn->c, data, len); } static void x11_sent(Plug *plug, size_t bufsize) { struct X11Connection *xconn = container_of( plug, 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. */ int x11_get_screen_number(char *display) { int n; n = host_strcspn(display, ":"); if (!display[n]) return 0; n = strcspn(display, "."); if (!display[n]) return 0; return atoi(display + n + 1); } static const PlugVtable X11Connection_plugvt = { .log = x11_log, .closing = x11_closing, .receive = x11_receive, .sent = x11_sent, }; static void x11_chan_free(Channel *chan); static size_t x11_send( Channel *chan, bool is_stderr, const void *vdata, size_t len); static void x11_send_eof(Channel *chan); static void x11_set_input_wanted(Channel *chan, bool wanted); static char *x11_log_close_msg(Channel *chan); static const ChannelVtable X11Connection_channelvt = { .free = x11_chan_free, .open_confirmation = chan_remotely_opened_confirmation, .open_failed = chan_remotely_opened_failure, .send = x11_send, .send_eof = x11_send_eof, .set_input_wanted = x11_set_input_wanted, .log_close_msg = x11_log_close_msg, .want_close = chan_default_want_close, .rcvd_exit_status = chan_no_exit_status, .rcvd_exit_signal = chan_no_exit_signal, .rcvd_exit_signal_numeric = chan_no_exit_signal_numeric, .run_shell = chan_no_run_shell, .run_command = chan_no_run_command, .run_subsystem = chan_no_run_subsystem, .enable_x11_forwarding = chan_no_enable_x11_forwarding, .enable_agent_forwarding = chan_no_enable_agent_forwarding, .allocate_pty = chan_no_allocate_pty, .set_env = chan_no_set_env, .send_break = chan_no_send_break, .send_signal = chan_no_send_signal, .change_window_size = chan_no_change_window_size, .request_response = chan_no_request_response, }; /* * Called to set up the X11Connection structure, though this does not * yet connect to an actual server. */ Channel *x11_new_channel(tree234 *authtree, SshChannel *c, const char *peeraddr, int peerport, bool connection_sharing_possible) { struct X11Connection *xconn; /* * Open socket. */ xconn = snew(struct X11Connection); xconn->plug.vt = &X11Connection_plugvt; xconn->chan.vt = &X11Connection_channelvt; xconn->chan.initial_fixed_window_size = (connection_sharing_possible ? 128 : 0); xconn->auth_protocol = NULL; xconn->authtree = authtree; xconn->verified = false; xconn->data_read = 0; xconn->input_wanted = true; 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->chan; } static void x11_chan_free(Channel *chan) { assert(chan->vt == &X11Connection_channelvt); X11Connection *xconn = container_of(chan, X11Connection, chan); 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); } static void x11_set_input_wanted(Channel *chan, bool wanted) { assert(chan->vt == &X11Connection_channelvt); X11Connection *xconn = container_of(chan, X11Connection, chan); xconn->input_wanted = wanted; if (xconn->s) sk_set_frozen(xconn->s, !xconn->input_wanted); } 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_X11(xconn->firstpkt[0], reply + 6, msgsize >> 2);/* data len */ memset(reply + 8, 0, msgsize); memcpy(reply + 8, full_message, msglen); sshfwd_write(xconn->c, reply, 8 + msgsize); sshfwd_write_eof(xconn->c); xconn->no_data_sent_to_x_client = false; sfree(reply); sfree(full_message); } static bool 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. */ static size_t x11_send( Channel *chan, bool is_stderr, const void *vdata, size_t len) { assert(chan->vt == &X11Connection_channelvt); X11Connection *xconn = container_of(chan, X11Connection, chan); const char *data = (const char *)vdata; /* * 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) { char endian = xconn->firstpkt[0]; xconn->auth_plen = GET_16BIT_X11(endian, xconn->firstpkt + 6); xconn->auth_dlen = GET_16BIT_X11(endian, 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; char endian = xconn->firstpkt[0]; protomajor = GET_16BIT_X11(endian, xconn->firstpkt + 2); protominor = GET_16BIT_X11(endian, 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. (This will * have the side effect of freeing xconn.) */ 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. */ xconn->chan.initial_fixed_window_size = 0; sshfwd_window_override_removed(xconn->c); xconn->disp = auth_matched->disp; xconn->s = new_connection(sk_addr_dup(xconn->disp->addr), xconn->disp->realhost, xconn->disp->port, false, true, false, false, &xconn->plug, 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. */ socketdatalen = 0; /* placate compiler warning */ 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 = true; } /* * After initialisation, just copy data simply. */ return sk_write(xconn->s, data, len); } static void x11_send_eof(Channel *chan) { assert(chan->vt == &X11Connection_channelvt); X11Connection *xconn = container_of(chan, X11Connection, chan); 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); } } static char *x11_log_close_msg(Channel *chan) { return dupstr("Forwarded X11 connection terminated"); } /* * 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(ptrlen protoname) { int protocol; for (protocol = 1; protocol < lenof(x11_authnames); protocol++) if (ptrlen_eq_string(protoname, x11_authnames[protocol])) return protocol; return -1; } void *x11_dehexify(ptrlen hexpl, int *outlen) { int len, i; unsigned char *ret; len = hexpl.len / 2; ret = snewn(len, unsigned char); for (i = 0; i < len; i++) { char bytestr[3]; unsigned val = 0; bytestr[0] = ((const char *)hexpl.ptr)[2*i]; bytestr[1] = ((const char *)hexpl.ptr)[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((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_X11(endian, greeting+2, protomajor); PUT_16BIT_X11(endian, greeting+4, protominor); PUT_16BIT_X11(endian, greeting+6, authnamelen); PUT_16BIT_X11(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; } putty-0.76/licence.h0000644000175000017500000000343514072266315011342 00000000000000/* * licence.h - macro definitions for the PuTTY licence. * * Generated by licence.pl from LICENCE. * You should edit those files rather than editing this one. */ #define LICENCE_TEXT(parsep) \ "PuTTY is copyright 1997-2021 Simon Tatham." \ parsep \ "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, Christopher Staite, Lorenz Diener, Christian Brabandt, Jeff Smith, Pavel Kryukov, Maxim Kuznetsov, Svyatoslav Kuzmich, Nico Williams, Viktor Dukhovni, Josh Dersch, Lars Brinkhoff, and CORE SDI S.A." \ parsep \ "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:" \ parsep \ "The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software." \ parsep \ "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." #define SHORT_COPYRIGHT_DETAILS "1997-2021 Simon Tatham" putty-0.76/empty.h0000644000175000017500000000011414072266315011065 00000000000000/* Empty file touched by automake makefile to force rebuild of version.o */ putty-0.76/Makefile.am0000644000175000017500000005527114072266315011630 00000000000000# Makefile.am for putty under Unix with Autoconf/Automake. # # This file was created by `mkfiles.pl' from the `Recipe' file. # DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead. AUTOMAKE_OPTIONS = subdir-objects allsources = agentf.c aqsync.c be_all_s.c be_misc.c be_none.c be_nos_s.c \ be_ssh.c callback.c cgtest.c charset/charset.h \ charset/enum.c charset/fromucs.c charset/internal.h \ charset/localenc.c charset/macenc.c charset/mimeenc.c \ charset/sbcs.c charset/sbcsdat.c charset/slookup.c \ charset/toucs.c charset/utf8.c charset/xenc.c clicons.c \ cmdgen.c cmdline.c conf.c config.c console.c console.h \ cproxy.c defs.h dialog.c dialog.h ecc.c ecc.h errsock.c \ fuzzterm.c import.c ldisc.c ldisc.h licence.h logging.c \ mainchan.c marshal.c marshal.h memory.c millerrabin.c \ minibidi.c misc.c misc.h miscucs.c mpint.c mpint.h mpint_i.h \ mpunsafe.c mpunsafe.h network.h nocmdline.c nocproxy.c \ nogss.c norand.c noshare.c noterm.c notiming.c nullplug.c \ pageant.c pageant.h pgssapi.c pgssapi.h pinger.c pockle.c \ portfwd.c primecandidate.c proxy.c proxy.h pscp.c psftp.c \ psftp.h psftpcommon.c psocks.c psocks.h putty.h puttymem.h \ puttyps.h raw.c rlogin.c scpserver.c sesschan.c sessprep.c \ settings.c sftp.c sftp.h sftpcommon.c sftpserver.c \ smallprimes.c ssh.c ssh.h ssh1bpp.c ssh1censor.c \ ssh1connection-client.c ssh1connection-server.c \ ssh1connection.c ssh1connection.h ssh1login-server.c \ ssh1login.c ssh2bpp-bare.c ssh2bpp.c ssh2censor.c \ ssh2connection-client.c ssh2connection-server.c \ ssh2connection.c ssh2connection.h ssh2kex-client.c \ ssh2kex-server.c ssh2transhk.c ssh2transport.c \ ssh2transport.h ssh2userauth-server.c ssh2userauth.c \ sshaes.c ssharcf.c sshargon2.c sshauxcrypt.c sshbcrypt.c \ sshblake2.c sshblowf.c sshblowf.h sshbpp.h sshccp.c \ sshchan.h sshcommon.c sshcr.h sshcrc.c sshcrcda.c sshdes.c \ sshdh.c sshdss.c sshdssg.c sshecc.c sshecdsag.c sshgss.h \ sshgssc.c sshgssc.h sshhmac.c sshkeygen.h sshmac.c sshmd5.c \ sshppl.h sshprime.c sshprng.c sshpubk.c sshrand.c sshrsa.c \ sshrsag.c sshserver.c sshserver.h sshsh256.c sshsh512.c \ sshsha.c sshsha3.c sshshare.c sshsignals.h sshttymodes.h \ sshutils.c sshverstring.c sshzlib.c storage.h stripctrl.c \ supdup.c telnet.c terminal.c terminal.h testcrypt.c \ testcrypt.h testsc.c testzlib.c time.c timing.c tree234.c \ tree234.h unix/gtkapp.c unix/gtkask.c unix/gtkcfg.c \ unix/gtkcols.c unix/gtkcols.h unix/gtkcomm.c \ unix/gtkcompat.h unix/gtkdlg.c unix/gtkfont.c unix/gtkfont.h \ unix/gtkmain.c unix/gtkmisc.c unix/gtkmisc.h unix/gtkwin.c \ unix/osxlaunch.c unix/procnet.c unix/unix.h unix/ux_x11.c \ unix/uxagentc.c unix/uxagentsock.c unix/uxcfg.c \ unix/uxcliloop.c unix/uxcons.c unix/uxfdsock.c unix/uxgen.c \ unix/uxgss.c unix/uxmisc.c unix/uxnet.c unix/uxnogtk.c \ unix/uxnoise.c unix/uxpeer.c unix/uxpgnt.c unix/uxplink.c \ unix/uxpoll.c unix/uxprint.c unix/uxproxy.c unix/uxpsusan.c \ unix/uxpterm.c unix/uxpty.c unix/uxputty.c unix/uxsel.c \ unix/uxser.c unix/uxserver.c unix/uxsftp.c \ unix/uxsftpserver.c unix/uxshare.c unix/uxsignal.c \ unix/uxsocks.c unix/uxstore.c unix/uxucs.c unix/uxutils.c \ unix/uxutils.h unix/x11misc.c unix/x11misc.h unix/xkeysym.c \ unix/xpmptcfg.c unix/xpmpterm.c unix/xpmpucfg.c \ unix/xpmputty.c utils.c version.c version.h wcwidth.c \ wildcard.c windows/pageant-rc.h windows/pageant.rc \ windows/plink.rc windows/pscp.rc windows/psftp.rc \ windows/putty.rc windows/puttygen-rc.h windows/puttygen.rc \ windows/puttytel.rc windows/rcstuff.h windows/sizetip.c \ windows/version.rc2 windows/win_res.h windows/win_res.rc2 \ windows/wincapi.c windows/wincapi.h windows/wincfg.c \ windows/wincliloop.c windows/wincons.c windows/winctrls.c \ windows/windefs.c windows/windlg.c windows/window.c \ windows/wingss.c windows/winhandl.c windows/winhelp.c \ windows/winhelp.h windows/winhelp.rc2 windows/winhsock.c \ windows/winjump.c windows/winmisc.c windows/winmiscs.c \ windows/winnet.c windows/winnohlp.c windows/winnoise.c \ windows/winnojmp.c windows/winnpc.c windows/winnps.c \ windows/winpgen.c windows/winpgnt.c windows/winpgntc.c \ windows/winplink.c windows/winprint.c windows/winproxy.c \ windows/winseat.h windows/winsecur.c windows/winsecur.h \ windows/winselcli.c windows/winselgui.c windows/winser.c \ windows/winsftp.c windows/winshare.c windows/winsocks.c \ windows/winstore.c windows/winstuff.h windows/wintime.c \ windows/winucs.c windows/winutils.c windows/winx11.c \ x11fwd.c if HAVE_GTK bin_PROGRAMS = plink pscp psftp psusan puttygen pageant pterm putty puttytel else bin_PROGRAMS = plink pscp psftp psusan puttygen endif if HAVE_GTK noinst_PROGRAMS = cgtest fuzzterm osxlaunch psocks testcrypt testsc testzlib \ uppity ptermapp puttyapp else noinst_PROGRAMS = cgtest fuzzterm osxlaunch psocks testcrypt testsc testzlib \ uppity endif AM_CPPFLAGS = -I$(srcdir)/./ -I$(srcdir)/charset/ -I$(srcdir)/windows/ \ -I$(srcdir)/unix/ if HAVE_GTK AM_CFLAGS = $(GTK_CFLAGS) $(COMPAT) $(XFLAGS) $(WARNINGOPTS) else AM_CFLAGS = $(COMPAT) $(XFLAGS) $(WARNINGOPTS) endif libversion_a_SOURCES = version.c libversion_a_CFLAGS = $(COMPAT) $(XFLAGS) $(WARNINGOPTS) noinst_LIBRARIES = libversion.a cgtest_SOURCES = cgtest.c conf.c console.c ecc.c import.c marshal.c memory.c \ millerrabin.c misc.c mpint.c mpunsafe.c notiming.c pockle.c \ primecandidate.c smallprimes.c sshaes.c sshargon2.c \ sshauxcrypt.c sshbcrypt.c sshblake2.c sshblowf.c sshdes.c \ sshdss.c sshdssg.c sshecc.c sshecdsag.c sshhmac.c sshmd5.c \ sshprime.c sshprng.c sshpubk.c sshrand.c sshrsa.c sshrsag.c \ sshsh256.c sshsh512.c sshsha.c sshsha3.c stripctrl.c time.c \ tree234.c unix/uxcons.c unix/uxgen.c unix/uxmisc.c \ unix/uxnogtk.c unix/uxnoise.c unix/uxpoll.c unix/uxstore.c \ unix/uxutils.c utils.c wcwidth.c cgtest_LDADD = libversion.a fuzzterm_SOURCES = be_none.c callback.c charset/fromucs.c charset/localenc.c \ charset/macenc.c charset/mimeenc.c charset/sbcs.c \ charset/sbcsdat.c charset/slookup.c charset/toucs.c \ charset/utf8.c charset/xenc.c conf.c config.c dialog.c \ fuzzterm.c logging.c marshal.c memory.c minibidi.c misc.c \ miscucs.c settings.c stripctrl.c terminal.c time.c timing.c \ tree234.c unix/uxcfg.c unix/uxmisc.c unix/uxnogtk.c \ unix/uxprint.c unix/uxstore.c unix/uxucs.c utils.c wcwidth.c fuzzterm_LDADD = libversion.a osxlaunch_SOURCES = unix/osxlaunch.c if HAVE_GTK pageant_SOURCES = aqsync.c be_misc.c be_none.c callback.c conf.c console.c \ ecc.c errsock.c logging.c marshal.c memory.c misc.c mpint.c \ nocproxy.c nogss.c nullplug.c pageant.c proxy.c settings.c \ sshaes.c sshargon2.c sshauxcrypt.c sshblake2.c sshdes.c \ sshdss.c sshecc.c sshhmac.c sshmd5.c sshprng.c sshpubk.c \ sshrsa.c sshsh256.c sshsh512.c sshsha.c sshsha3.c \ stripctrl.c time.c timing.c tree234.c unix/gtkask.c \ unix/gtkmisc.c unix/ux_x11.c unix/uxagentc.c \ unix/uxagentsock.c unix/uxcliloop.c unix/uxcons.c \ unix/uxfdsock.c unix/uxmisc.c unix/uxnet.c unix/uxnoise.c \ unix/uxpeer.c unix/uxpgnt.c unix/uxpoll.c unix/uxproxy.c \ unix/uxsel.c unix/uxsignal.c unix/uxstore.c unix/uxutils.c \ utils.c wcwidth.c x11fwd.c pageant_LDADD = libversion.a $(GTK_LIBS) endif plink_SOURCES = agentf.c aqsync.c be_all_s.c be_misc.c callback.c clicons.c \ cmdline.c conf.c console.c cproxy.c ecc.c errsock.c ldisc.c \ logging.c mainchan.c marshal.c memory.c misc.c mpint.c \ noterm.c nullplug.c pgssapi.c pinger.c portfwd.c proxy.c \ raw.c rlogin.c sessprep.c settings.c ssh.c ssh1bpp.c \ ssh1censor.c ssh1connection-client.c ssh1connection.c \ ssh1login.c ssh2bpp-bare.c ssh2bpp.c ssh2censor.c \ ssh2connection-client.c ssh2connection.c ssh2kex-client.c \ ssh2transhk.c ssh2transport.c ssh2userauth.c sshaes.c \ ssharcf.c sshargon2.c sshauxcrypt.c sshblake2.c sshblowf.c \ sshccp.c sshcommon.c sshcrc.c sshcrcda.c sshdes.c sshdh.c \ sshdss.c sshecc.c sshgssc.c sshhmac.c sshmac.c sshmd5.c \ sshprng.c sshpubk.c sshrand.c sshrsa.c sshsh256.c sshsh512.c \ sshsha.c sshsha3.c sshshare.c sshutils.c sshverstring.c \ sshzlib.c stripctrl.c supdup.c telnet.c time.c timing.c \ tree234.c unix/ux_x11.c unix/uxagentc.c unix/uxcliloop.c \ unix/uxcons.c unix/uxfdsock.c unix/uxgss.c unix/uxmisc.c \ unix/uxnet.c unix/uxnogtk.c unix/uxnoise.c unix/uxpeer.c \ unix/uxplink.c unix/uxpoll.c unix/uxproxy.c unix/uxsel.c \ unix/uxser.c unix/uxshare.c unix/uxsignal.c unix/uxstore.c \ unix/uxutils.c utils.c wcwidth.c wildcard.c x11fwd.c plink_LDADD = libversion.a pscp_SOURCES = agentf.c aqsync.c be_misc.c be_ssh.c callback.c clicons.c \ cmdline.c conf.c console.c cproxy.c ecc.c errsock.c \ logging.c mainchan.c marshal.c memory.c misc.c mpint.c \ nullplug.c pgssapi.c pinger.c portfwd.c proxy.c pscp.c \ psftpcommon.c settings.c sftp.c sftpcommon.c ssh.c ssh1bpp.c \ ssh1censor.c ssh1connection-client.c ssh1connection.c \ ssh1login.c ssh2bpp-bare.c ssh2bpp.c ssh2censor.c \ ssh2connection-client.c ssh2connection.c ssh2kex-client.c \ ssh2transhk.c ssh2transport.c ssh2userauth.c sshaes.c \ ssharcf.c sshargon2.c sshauxcrypt.c sshblake2.c sshblowf.c \ sshccp.c sshcommon.c sshcrc.c sshcrcda.c sshdes.c sshdh.c \ sshdss.c sshecc.c sshgssc.c sshhmac.c sshmac.c sshmd5.c \ sshprng.c sshpubk.c sshrand.c sshrsa.c sshsh256.c sshsh512.c \ sshsha.c sshsha3.c sshshare.c sshutils.c sshverstring.c \ sshzlib.c stripctrl.c time.c timing.c tree234.c \ unix/uxagentc.c unix/uxcliloop.c unix/uxcons.c \ unix/uxfdsock.c unix/uxgss.c unix/uxmisc.c unix/uxnet.c \ unix/uxnogtk.c unix/uxnoise.c unix/uxpeer.c unix/uxpoll.c \ unix/uxproxy.c unix/uxsel.c unix/uxsftp.c unix/uxshare.c \ unix/uxstore.c unix/uxutils.c utils.c wcwidth.c wildcard.c \ x11fwd.c pscp_LDADD = libversion.a psftp_SOURCES = agentf.c aqsync.c be_misc.c be_ssh.c callback.c clicons.c \ cmdline.c conf.c console.c cproxy.c ecc.c errsock.c \ logging.c mainchan.c marshal.c memory.c misc.c mpint.c \ nullplug.c pgssapi.c pinger.c portfwd.c proxy.c psftp.c \ psftpcommon.c settings.c sftp.c sftpcommon.c ssh.c ssh1bpp.c \ ssh1censor.c ssh1connection-client.c ssh1connection.c \ ssh1login.c ssh2bpp-bare.c ssh2bpp.c ssh2censor.c \ ssh2connection-client.c ssh2connection.c ssh2kex-client.c \ ssh2transhk.c ssh2transport.c ssh2userauth.c sshaes.c \ ssharcf.c sshargon2.c sshauxcrypt.c sshblake2.c sshblowf.c \ sshccp.c sshcommon.c sshcrc.c sshcrcda.c sshdes.c sshdh.c \ sshdss.c sshecc.c sshgssc.c sshhmac.c sshmac.c sshmd5.c \ sshprng.c sshpubk.c sshrand.c sshrsa.c sshsh256.c sshsh512.c \ sshsha.c sshsha3.c sshshare.c sshutils.c sshverstring.c \ sshzlib.c stripctrl.c time.c timing.c tree234.c \ unix/uxagentc.c unix/uxcliloop.c unix/uxcons.c \ unix/uxfdsock.c unix/uxgss.c unix/uxmisc.c unix/uxnet.c \ unix/uxnogtk.c unix/uxnoise.c unix/uxpeer.c unix/uxpoll.c \ unix/uxproxy.c unix/uxsel.c unix/uxsftp.c unix/uxshare.c \ unix/uxstore.c unix/uxutils.c utils.c wcwidth.c wildcard.c \ x11fwd.c psftp_LDADD = libversion.a psocks_SOURCES = be_misc.c callback.c conf.c console.c errsock.c logging.c \ marshal.c memory.c misc.c nocproxy.c norand.c portfwd.c \ proxy.c psocks.c sshutils.c stripctrl.c time.c timing.c \ tree234.c unix/uxcliloop.c unix/uxcons.c unix/uxfdsock.c \ unix/uxmisc.c unix/uxnet.c unix/uxnogtk.c unix/uxpeer.c \ unix/uxpoll.c unix/uxproxy.c unix/uxsel.c unix/uxsignal.c \ unix/uxsocks.c utils.c wcwidth.c psocks_LDADD = libversion.a psusan_SOURCES = be_misc.c be_none.c callback.c conf.c cproxy.c ecc.c \ errsock.c logging.c marshal.c memory.c millerrabin.c misc.c \ mpint.c mpunsafe.c nogss.c nullplug.c pgssapi.c pockle.c \ portfwd.c primecandidate.c proxy.c scpserver.c sesschan.c \ settings.c sftpcommon.c sftpserver.c smallprimes.c ssh1bpp.c \ ssh1censor.c ssh1connection-server.c ssh1connection.c \ ssh1login-server.c ssh2bpp-bare.c ssh2bpp.c ssh2censor.c \ ssh2connection-server.c ssh2connection.c ssh2kex-server.c \ ssh2transhk.c ssh2transport.c ssh2userauth-server.c sshaes.c \ ssharcf.c sshargon2.c sshauxcrypt.c sshblake2.c sshblowf.c \ sshccp.c sshcommon.c sshcrc.c sshcrcda.c sshdes.c sshdh.c \ sshdss.c sshecc.c sshgssc.c sshhmac.c sshmac.c sshmd5.c \ sshprime.c sshprng.c sshpubk.c sshrand.c sshrsa.c sshrsag.c \ sshserver.c sshsh256.c sshsh512.c sshsha.c sshsha3.c \ sshutils.c sshverstring.c sshzlib.c stripctrl.c time.c \ timing.c tree234.c unix/procnet.c unix/ux_x11.c \ unix/uxagentsock.c unix/uxcliloop.c unix/uxfdsock.c \ unix/uxmisc.c unix/uxnet.c unix/uxnogtk.c unix/uxnoise.c \ unix/uxpeer.c unix/uxpoll.c unix/uxproxy.c unix/uxpsusan.c \ unix/uxpty.c unix/uxsel.c unix/uxsftpserver.c \ unix/uxsignal.c unix/uxstore.c unix/uxutils.c utils.c \ wcwidth.c wildcard.c x11fwd.c psusan_LDADD = libversion.a if HAVE_GTK pterm_SOURCES = be_none.c callback.c charset/fromucs.c charset/localenc.c \ charset/macenc.c charset/mimeenc.c charset/sbcs.c \ charset/sbcsdat.c charset/slookup.c charset/toucs.c \ charset/utf8.c charset/xenc.c cmdline.c conf.c config.c \ dialog.c ldisc.c logging.c marshal.c memory.c minibidi.c \ misc.c miscucs.c nocproxy.c nogss.c sessprep.c settings.c \ stripctrl.c terminal.c time.c timing.c tree234.c \ unix/gtkcfg.c unix/gtkcols.c unix/gtkcomm.c unix/gtkdlg.c \ unix/gtkfont.c unix/gtkmain.c unix/gtkmisc.c unix/gtkwin.c \ unix/uxcfg.c unix/uxmisc.c unix/uxprint.c unix/uxpterm.c \ unix/uxpty.c unix/uxsel.c unix/uxsignal.c unix/uxstore.c \ unix/uxucs.c unix/x11misc.c unix/xkeysym.c unix/xpmptcfg.c \ unix/xpmpterm.c utils.c wcwidth.c pterm_LDADD = libversion.a $(GTK_LIBS) endif if HAVE_GTK ptermapp_SOURCES = be_none.c callback.c charset/fromucs.c charset/localenc.c \ charset/macenc.c charset/mimeenc.c charset/sbcs.c \ charset/sbcsdat.c charset/slookup.c charset/toucs.c \ charset/utf8.c charset/xenc.c conf.c config.c dialog.c \ ldisc.c logging.c marshal.c memory.c minibidi.c misc.c \ miscucs.c nocmdline.c nocproxy.c nogss.c sessprep.c \ settings.c stripctrl.c terminal.c time.c timing.c tree234.c \ unix/gtkapp.c unix/gtkcfg.c unix/gtkcols.c unix/gtkcomm.c \ unix/gtkdlg.c unix/gtkfont.c unix/gtkmisc.c unix/gtkwin.c \ unix/uxcfg.c unix/uxmisc.c unix/uxprint.c unix/uxpterm.c \ unix/uxpty.c unix/uxsel.c unix/uxsignal.c unix/uxstore.c \ unix/uxucs.c unix/x11misc.c unix/xkeysym.c unix/xpmptcfg.c \ unix/xpmpterm.c utils.c wcwidth.c ptermapp_LDADD = libversion.a $(GTK_LIBS) endif if HAVE_GTK putty_SOURCES = agentf.c aqsync.c be_all_s.c be_misc.c callback.c \ charset/fromucs.c charset/localenc.c charset/macenc.c \ charset/mimeenc.c charset/sbcs.c charset/sbcsdat.c \ charset/slookup.c charset/toucs.c charset/utf8.c \ charset/xenc.c cmdline.c conf.c config.c cproxy.c dialog.c \ ecc.c errsock.c ldisc.c logging.c mainchan.c marshal.c \ memory.c minibidi.c misc.c miscucs.c mpint.c nullplug.c \ pgssapi.c pinger.c portfwd.c proxy.c raw.c rlogin.c \ sessprep.c settings.c ssh.c ssh1bpp.c ssh1censor.c \ ssh1connection-client.c ssh1connection.c ssh1login.c \ ssh2bpp-bare.c ssh2bpp.c ssh2censor.c \ ssh2connection-client.c ssh2connection.c ssh2kex-client.c \ ssh2transhk.c ssh2transport.c ssh2userauth.c sshaes.c \ ssharcf.c sshargon2.c sshauxcrypt.c sshblake2.c sshblowf.c \ sshccp.c sshcommon.c sshcrc.c sshcrcda.c sshdes.c sshdh.c \ sshdss.c sshecc.c sshgssc.c sshhmac.c sshmac.c sshmd5.c \ sshprng.c sshpubk.c sshrand.c sshrsa.c sshsh256.c sshsh512.c \ sshsha.c sshsha3.c sshshare.c sshutils.c sshverstring.c \ sshzlib.c stripctrl.c supdup.c telnet.c terminal.c time.c \ timing.c tree234.c unix/gtkcfg.c unix/gtkcols.c \ unix/gtkcomm.c unix/gtkdlg.c unix/gtkfont.c unix/gtkmain.c \ unix/gtkmisc.c unix/gtkwin.c unix/ux_x11.c unix/uxagentc.c \ unix/uxcfg.c unix/uxfdsock.c unix/uxgss.c unix/uxmisc.c \ unix/uxnet.c unix/uxnoise.c unix/uxpeer.c unix/uxpoll.c \ unix/uxprint.c unix/uxproxy.c unix/uxputty.c unix/uxsel.c \ unix/uxser.c unix/uxshare.c unix/uxsignal.c unix/uxstore.c \ unix/uxucs.c unix/uxutils.c unix/x11misc.c unix/xkeysym.c \ unix/xpmpucfg.c unix/xpmputty.c utils.c wcwidth.c wildcard.c \ x11fwd.c putty_LDADD = libversion.a $(GTK_LIBS) endif if HAVE_GTK puttyapp_SOURCES = agentf.c aqsync.c be_all_s.c be_misc.c callback.c \ charset/fromucs.c charset/localenc.c charset/macenc.c \ charset/mimeenc.c charset/sbcs.c charset/sbcsdat.c \ charset/slookup.c charset/toucs.c charset/utf8.c \ charset/xenc.c conf.c config.c cproxy.c dialog.c ecc.c \ errsock.c ldisc.c logging.c mainchan.c marshal.c memory.c \ minibidi.c misc.c miscucs.c mpint.c nocmdline.c nullplug.c \ pgssapi.c pinger.c portfwd.c proxy.c raw.c rlogin.c \ sessprep.c settings.c ssh.c ssh1bpp.c ssh1censor.c \ ssh1connection-client.c ssh1connection.c ssh1login.c \ ssh2bpp-bare.c ssh2bpp.c ssh2censor.c \ ssh2connection-client.c ssh2connection.c ssh2kex-client.c \ ssh2transhk.c ssh2transport.c ssh2userauth.c sshaes.c \ ssharcf.c sshargon2.c sshauxcrypt.c sshblake2.c sshblowf.c \ sshccp.c sshcommon.c sshcrc.c sshcrcda.c sshdes.c sshdh.c \ sshdss.c sshecc.c sshgssc.c sshhmac.c sshmac.c sshmd5.c \ sshprng.c sshpubk.c sshrand.c sshrsa.c sshsh256.c sshsh512.c \ sshsha.c sshsha3.c sshshare.c sshutils.c sshverstring.c \ sshzlib.c stripctrl.c supdup.c telnet.c terminal.c time.c \ timing.c tree234.c unix/gtkapp.c unix/gtkcfg.c \ unix/gtkcols.c unix/gtkcomm.c unix/gtkdlg.c unix/gtkfont.c \ unix/gtkmisc.c unix/gtkwin.c unix/ux_x11.c unix/uxagentc.c \ unix/uxcfg.c unix/uxfdsock.c unix/uxgss.c unix/uxmisc.c \ unix/uxnet.c unix/uxnoise.c unix/uxpeer.c unix/uxpoll.c \ unix/uxprint.c unix/uxproxy.c unix/uxputty.c unix/uxsel.c \ unix/uxser.c unix/uxshare.c unix/uxsignal.c unix/uxstore.c \ unix/uxucs.c unix/uxutils.c unix/x11misc.c unix/xkeysym.c \ unix/xpmpucfg.c unix/xpmputty.c utils.c wcwidth.c wildcard.c \ x11fwd.c puttyapp_LDADD = libversion.a $(GTK_LIBS) endif puttygen_SOURCES = cmdgen.c conf.c console.c ecc.c import.c marshal.c \ memory.c millerrabin.c misc.c mpint.c mpunsafe.c notiming.c \ pockle.c primecandidate.c smallprimes.c sshaes.c sshargon2.c \ sshauxcrypt.c sshbcrypt.c sshblake2.c sshblowf.c sshdes.c \ sshdss.c sshdssg.c sshecc.c sshecdsag.c sshhmac.c sshmd5.c \ sshprime.c sshprng.c sshpubk.c sshrand.c sshrsa.c sshrsag.c \ sshsh256.c sshsh512.c sshsha.c sshsha3.c stripctrl.c time.c \ tree234.c unix/uxcons.c unix/uxgen.c unix/uxmisc.c \ unix/uxnogtk.c unix/uxnoise.c unix/uxpoll.c unix/uxstore.c \ unix/uxutils.c utils.c wcwidth.c puttygen_LDADD = libversion.a if HAVE_GTK puttytel_SOURCES = be_misc.c be_nos_s.c callback.c charset/fromucs.c \ charset/localenc.c charset/macenc.c charset/mimeenc.c \ charset/sbcs.c charset/sbcsdat.c charset/slookup.c \ charset/toucs.c charset/utf8.c charset/xenc.c cmdline.c \ conf.c config.c dialog.c errsock.c ldisc.c logging.c \ marshal.c memory.c minibidi.c misc.c miscucs.c nocproxy.c \ nogss.c norand.c pinger.c proxy.c raw.c rlogin.c sessprep.c \ settings.c stripctrl.c supdup.c telnet.c terminal.c time.c \ timing.c tree234.c unix/gtkcfg.c unix/gtkcols.c \ unix/gtkcomm.c unix/gtkdlg.c unix/gtkfont.c unix/gtkmain.c \ unix/gtkmisc.c unix/gtkwin.c unix/uxcfg.c unix/uxfdsock.c \ unix/uxmisc.c unix/uxnet.c unix/uxpeer.c unix/uxpoll.c \ unix/uxprint.c unix/uxproxy.c unix/uxputty.c unix/uxsel.c \ unix/uxser.c unix/uxsignal.c unix/uxstore.c unix/uxucs.c \ unix/uxutils.c unix/x11misc.c unix/xkeysym.c unix/xpmpucfg.c \ unix/xpmputty.c utils.c wcwidth.c puttytel_LDADD = libversion.a $(GTK_LIBS) endif testcrypt_SOURCES = ecc.c marshal.c memory.c millerrabin.c mpint.c \ mpunsafe.c pockle.c primecandidate.c smallprimes.c sshaes.c \ ssharcf.c sshargon2.c sshauxcrypt.c sshblake2.c sshblowf.c \ sshccp.c sshcrc.c sshcrcda.c sshdes.c sshdh.c sshdss.c \ sshdssg.c sshecc.c sshecdsag.c sshhmac.c sshmd5.c sshprime.c \ sshprng.c sshpubk.c sshrsa.c sshrsag.c sshsh256.c sshsh512.c \ sshsha.c sshsha3.c testcrypt.c tree234.c unix/uxutils.c \ utils.c testsc_SOURCES = ecc.c marshal.c memory.c mpint.c sshaes.c ssharcf.c \ sshargon2.c sshauxcrypt.c sshblake2.c sshblowf.c sshccp.c \ sshcrc.c sshcrcda.c sshdes.c sshdh.c sshdss.c sshecc.c \ sshhmac.c sshmac.c sshmd5.c sshpubk.c sshrsa.c sshsh256.c \ sshsh512.c sshsha.c sshsha3.c testsc.c tree234.c \ unix/uxutils.c utils.c wildcard.c testzlib_SOURCES = marshal.c memory.c sshzlib.c testzlib.c utils.c uppity_SOURCES = be_misc.c be_none.c callback.c conf.c cproxy.c ecc.c \ errsock.c logging.c marshal.c memory.c millerrabin.c misc.c \ mpint.c mpunsafe.c nullplug.c pgssapi.c pockle.c portfwd.c \ primecandidate.c proxy.c scpserver.c sesschan.c settings.c \ sftpcommon.c sftpserver.c smallprimes.c ssh1bpp.c \ ssh1censor.c ssh1connection-server.c ssh1connection.c \ ssh1login-server.c ssh2bpp-bare.c ssh2bpp.c ssh2censor.c \ ssh2connection-server.c ssh2connection.c ssh2kex-server.c \ ssh2transhk.c ssh2transport.c ssh2userauth-server.c sshaes.c \ ssharcf.c sshargon2.c sshauxcrypt.c sshblake2.c sshblowf.c \ sshccp.c sshcommon.c sshcrc.c sshcrcda.c sshdes.c sshdh.c \ sshdss.c sshecc.c sshgssc.c sshhmac.c sshmac.c sshmd5.c \ sshprime.c sshprng.c sshpubk.c sshrand.c sshrsa.c sshrsag.c \ sshserver.c sshsh256.c sshsh512.c sshsha.c sshsha3.c \ sshutils.c sshverstring.c sshzlib.c stripctrl.c time.c \ timing.c tree234.c unix/procnet.c unix/ux_x11.c \ unix/uxagentsock.c unix/uxcliloop.c unix/uxfdsock.c \ unix/uxgss.c unix/uxmisc.c unix/uxnet.c unix/uxnogtk.c \ unix/uxnoise.c unix/uxpeer.c unix/uxpoll.c unix/uxproxy.c \ unix/uxpty.c unix/uxsel.c unix/uxserver.c \ unix/uxsftpserver.c unix/uxsignal.c unix/uxstore.c \ unix/uxutils.c utils.c wcwidth.c wildcard.c x11fwd.c uppity_LDADD = libversion.a if AUTO_GIT_COMMIT BUILT_SOURCES = empty.h CLEANFILES = empty.h libversion_a_CFLAGS += -DSOURCE_COMMIT=\"`git --git-dir=$(srcdir)/.git rev-parse HEAD 2>/dev/null`\" empty.h: $(allsources) echo '/* Empty file touched by automake makefile to force rebuild of version.o */' >$@ endif # Run the cryptsuite tests as part of 'make check'. Override # PUTTY_TESTCRYPT so that cryptsuite will take the testcrypt binary # from the build directory instead of the source directory, in case # this is an out-of-tree build. check-local: testcrypt PUTTY_TESTCRYPT=./testcrypt $(srcdir)/test/cryptsuite.py if HAVE_GTK man1_MANS = doc/plink.1 doc/pscp.1 doc/psftp.1 doc/puttygen.1 doc/psusan.1 \ doc/pageant.1 doc/pterm.1 doc/putty.1 doc/puttytel.1 else man1_MANS = doc/plink.1 doc/pscp.1 doc/psftp.1 doc/puttygen.1 doc/psusan.1 endif if HAVE_SETID_CMD install-exec-local: @SETID_CMD@ $(bindir)/pterm chmod @SETID_MODE@ $(bindir)/pterm endif if HAVE_QUARTZ noinst_SCRIPTS = unix/PuTTY.app unix/Pterm.app unix/PuTTY.app: unix/putty.bundle puttyapp osxlaunch rm -rf $@ && PUTTY_GTK_PREFIX_FROM_MAKEFILE=$$(pkg-config --variable=prefix gtk+-3.0) gtk-mac-bundler $< unix/Pterm.app: unix/pterm.bundle ptermapp osxlaunch rm -rf $@ && PUTTY_GTK_PREFIX_FROM_MAKEFILE=$$(pkg-config --variable=prefix gtk+-3.0) gtk-mac-bundler $< endif putty-0.76/charset/0000755000175000017500000000000014072266316011274 500000000000000putty-0.76/charset/README0000644000175000017500000000135714072266307012102 00000000000000This subdirectory contains a general character-set conversion library, used in the Unix port of PuTTY, and available for use in other ports if it should happen to be useful. This is a variant of a library that's currently used in some other programs such as Timber and Halibut. At some future date, we would like to merge the two libraries, so that all programs use the same libcharset. It is therefore a _strong_ design goal that this library should remain perfectly general, and not tied to particulars of PuTTY. It must not reference any code outside its own subdirectory; it should not have PuTTY-specific helper routines added to it unless they can be documented in a general manner which might make them useful in other circumstances as well. putty-0.76/charset/charset.h0000644000175000017500000001067614072266307013030 00000000000000/* * charset.h - header file for general character set conversion * routines. */ #ifndef charset_charset_h #define charset_charset_h #include /* * Enumeration that lists all the multibyte or single-byte * character sets known to this library. */ typedef enum { CS_NONE, /* used for reporting errors, etc */ CS_ISO8859_1, CS_ISO8859_1_X11, /* X font encoding with VT100 glyphs */ CS_ISO8859_2, CS_ISO8859_3, CS_ISO8859_4, CS_ISO8859_5, CS_ISO8859_6, CS_ISO8859_7, CS_ISO8859_8, CS_ISO8859_9, CS_ISO8859_10, 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, CS_CP1256, CS_CP1257, CS_CP1258, CS_KOI8_R, CS_KOI8_U, CS_MAC_ROMAN, CS_MAC_TURKISH, CS_MAC_CROATIAN, CS_MAC_ICELAND, CS_MAC_ROMANIAN, CS_MAC_GREEK, CS_MAC_CYRILLIC, CS_MAC_THAI, CS_MAC_CENTEURO, CS_MAC_SYMBOL, CS_MAC_DINGBATS, CS_MAC_ROMAN_OLD, CS_MAC_CROATIAN_OLD, CS_MAC_ICELAND_OLD, CS_MAC_ROMANIAN_OLD, CS_MAC_GREEK_OLD, CS_MAC_CYRILLIC_OLD, CS_MAC_UKRAINE, CS_MAC_VT100, CS_MAC_VT100_OLD, CS_VISCII, CS_HP_ROMAN8, CS_DEC_MCS, CS_UTF8 } charset_t; typedef struct { unsigned long s0; } charset_state; /* * Routine to convert a MB/SB character set to Unicode. * * This routine accepts some number of bytes, updates a state * variable, and outputs some number of Unicode characters. There * are no guarantees. You can't even guarantee that at most one * Unicode character will be output per byte you feed in; for * example, suppose you're reading UTF-8, you've seen E1 80, and * then you suddenly see FE. Now you need to output _two_ error * characters - one for the incomplete sequence E1 80, and one for * the completely invalid UTF-8 byte FE. * * Returns the number of wide characters output; will never output * more than the size of the buffer (as specified on input). * Advances the `input' pointer and decrements `inlen', to indicate * how far along the input string it got. * * 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 * a state variable, and outputs some number of bytes. * * Returns the number of bytes characters output; will never output * more than the size of the buffer (as specified on input), and * will never output a partial MB character. Advances the `input' * pointer and decrements `inlen', to indicate how far along the * input string it got. * * 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); int charset_from_xenc(const char *name); /* * Convert MIME encoding names to and from our charset identifiers. */ const char *charset_to_mimeenc(int charset); int charset_from_mimeenc(const char *name); /* * Convert our own encoding names to and from our charset * identifiers. */ const char *charset_to_localenc(int charset); int charset_from_localenc(const char *name); int charset_localenc_nth(int n); /* * Convert Mac OS script/region/font to our charset identifiers. */ int charset_from_macenc(int script, int region, int sysvers, const char *fontname); #endif /* charset_charset_h */ putty-0.76/charset/enum.c0000644000175000017500000000125314072266307012325 00000000000000/* * enum.c - enumerate all charsets defined by the library. * * This file maintains a list of every other source file which * contains ENUM_CHARSET definitions. It #includes each one with * ENUM_CHARSETS defined, which causes those source files to do * nothing at all except call the ENUM_CHARSET macro on each * charset they define. * * This file in turn is included from various other places, with * the ENUM_CHARSET macro defined to various different things. This * allows us to have multiple implementations of the master charset * lookup table (a static one and a dynamic one). */ #define ENUM_CHARSETS #include "sbcsdat.c" #include "utf8.c" #undef ENUM_CHARSETS putty-0.76/charset/fromucs.c0000644000175000017500000000440514072266307013041 00000000000000/* * fromucs.c - convert Unicode to other character sets. */ #include "charset.h" #include "internal.h" struct charset_emit_param { char *output; int outlen; const char *errstr; int errlen; int stopped; }; static void charset_emit(void *ctx, long int output) { struct charset_emit_param *param = (struct charset_emit_param *)ctx; char outval; char const *p; int outlen; if (output == ERROR) { p = param->errstr; outlen = param->errlen; } else { outval = output; p = &outval; outlen = 1; } if (param->outlen >= outlen) { while (outlen > 0) { *param->output++ = *p++; param->outlen--; 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; param.output = output; param.outlen = outlen; param.stopped = 0; /* * charset_emit will expect a valid errstr. */ if (!errstr) { /* *shrug* this is good enough, and consistent across all SBCS... */ param.errstr = "."; param.errlen = 1; } param.errstr = errstr; param.errlen = errlen; if (!state) { localstate.s0 = 0; } else { localstate = *state; /* structure copy */ } state = &localstate; while (*inlen > 0) { int lenbefore = param.output - output; spec->write(spec, **input, &localstate, charset_emit, ¶m); if (param.stopped) { /* * The emit function has _tried_ to output some * characters, but ran up against the end of the * buffer. Leave immediately, and return what happened * _before_ attempting to process this character. */ return lenbefore; } if (state) *state = localstate; /* structure copy */ (*input)++; (*inlen)--; } return param.output - output; } putty-0.76/charset/internal.h0000644000175000017500000000620314072266307013202 00000000000000/* * internal.h - internal header stuff for the charset library. */ #ifndef charset_internal_h #define charset_internal_h /* This invariably comes in handy */ #define lenof(x) ( sizeof((x)) / sizeof(*(x)) ) /* This is an invalid Unicode value used to indicate an error. */ #define ERROR 0xFFFFL /* Unicode value representing error */ typedef struct charset_spec charset_spec; typedef struct sbcs_data sbcs_data; struct charset_spec { int charset; /* numeric identifier */ /* * A function to read the character set and output Unicode * characters. The `emit' function expects to get Unicode chars * passed to it; it should be sent ERROR for any encoding error * on the input. */ void (*read)(charset_spec const *charset, long int input_chr, charset_state *state, void (*emit)(void *ctx, long int output), void *emitctx); /* * A function to read Unicode characters and output in this * character set. The `emit' function expects to get byte * values passed to it; it should be sent ERROR for any * non-representable characters on the input. */ void (*write)(charset_spec const *charset, long int input_chr, charset_state *state, void (*emit)(void *ctx, long int output), void *emitctx); void const *data; }; /* * This is the format of `data' used by the SBCS read and write * functions; so it's the format used in all SBCS definitions. */ struct sbcs_data { /* * This is a simple mapping table converting each SBCS position * to a Unicode code point. Some positions may contain ERROR, * indicating that that byte value is not defined in the SBCS * in question and its occurrence in input is an error. */ unsigned long sbcs2ucs[256]; /* * This lookup table is used to convert Unicode back to the * SBCS. It consists of the valid byte values in the SBCS, * sorted in order of their Unicode translation. So given a * Unicode value U, you can do a binary search on this table * using the above table as a lookup: when testing the Xth * position in this table, you branch according to whether * sbcs2ucs[ucs2sbcs[X]] is less than, greater than, or equal * to U. * * Note that since there may be fewer than 256 valid byte * values in a particular SBCS, we must supply the length of * this table as well as the contents. */ unsigned char ucs2sbcs[256]; int nvalid; }; /* * Prototypes for internal library functions. */ charset_spec const *charset_find_spec(int charset); void read_sbcs(charset_spec const *charset, long int input_chr, charset_state *state, void (*emit)(void *ctx, long int output), void *emitctx); void write_sbcs(charset_spec const *charset, long int input_chr, charset_state *state, void (*emit)(void *ctx, long int output), void *emitctx); /* * Placate compiler warning about unused parameters, of which we * expect to have some in this library. */ #define UNUSEDARG(x) ( (x) = (x) ) #endif /* charset_internal_h */ putty-0.76/charset/localenc.c0000644000175000017500000000771314072266307013150 00000000000000/* * local.c - translate our internal character set codes to and from * our own set of plausibly legible character-set names. Also * provides a canonical name for each encoding (useful for software * announcing what character set it will be using), and a set of * enumeration functions which return a list of supported * encodings one by one. * * charset_from_localenc will attempt all other text translations * as well as this table, to maximise the number of different ways * you can select a supported charset. */ #include #include "charset.h" #include "internal.h" static const struct { const char *name; int charset; int return_in_enum; /* enumeration misses some charsets */ } localencs[] = { { "", 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 }, { "CP1256", CS_CP1256, 1 }, { "CP1257", CS_CP1257, 1 }, { "CP1258", CS_CP1258, 1 }, { "KOI8-R", CS_KOI8_R, 1 }, { "KOI8-U", CS_KOI8_U, 1 }, { "Mac Roman", CS_MAC_ROMAN, 1 }, { "Mac Turkish", CS_MAC_TURKISH, 1 }, { "Mac Croatian", CS_MAC_CROATIAN, 1 }, { "Mac Iceland", CS_MAC_ICELAND, 1 }, { "Mac Romanian", CS_MAC_ROMANIAN, 1 }, { "Mac Greek", CS_MAC_GREEK, 1 }, { "Mac Cyrillic", CS_MAC_CYRILLIC, 1 }, { "Mac Thai", CS_MAC_THAI, 1 }, { "Mac Centeuro", CS_MAC_CENTEURO, 1 }, { "Mac Symbol", CS_MAC_SYMBOL, 1 }, { "Mac Dingbats", CS_MAC_DINGBATS, 1 }, { "Mac Roman (old)", CS_MAC_ROMAN_OLD, 0 }, { "Mac Croatian (old)", CS_MAC_CROATIAN_OLD, 0 }, { "Mac Iceland (old)", CS_MAC_ICELAND_OLD, 0 }, { "Mac Romanian (old)", CS_MAC_ROMANIAN_OLD, 0 }, { "Mac Greek (old)", CS_MAC_GREEK_OLD, 0 }, { "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++) if (charset == localencs[i].charset) return localencs[i].name; return NULL; /* not found */ } int charset_from_localenc(const char *name) { int i; if ( (i = charset_from_mimeenc(name)) != CS_NONE) return i; if ( (i = charset_from_xenc(name)) != CS_NONE) return i; for (i = 0; i < (int)lenof(localencs); i++) { const char *p, *q; p = name; q = localencs[i].name; while (*p || *q) { if (tolower((unsigned char)*p) != tolower((unsigned char)*q)) break; p++; q++; } if (!*p && !*q) return localencs[i].charset; } return CS_NONE; /* not found */ } int charset_localenc_nth(int n) { int i; for (i = 0; i < (int)lenof(localencs); i++) if (localencs[i].return_in_enum && !n--) return localencs[i].charset; return CS_NONE; /* end of list */ } putty-0.76/charset/macenc.c0000644000175000017500000001651214072266307012613 00000000000000/* * Copyright (c) 2003 Ben Harris * All rights reserved. * * 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: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ /* * macenc.c -- Convert a Mac OS script/region/font combination to our * internal charset code. */ #include #include "charset.h" #include "internal.h" /* * These are defined by Mac OS's , but we'd like to be * independent of that. */ #define smRoman 0 #define smJapanese 1 #define smTradChinese 2 #define smKorean 3 #define smArabic 4 #define smHebrew 5 #define smCyrillic 7 #define smDevenagari 9 #define smGurmukhi 10 #define smGujurati 11 #define smThai 21 #define smSimpChinese 25 #define smTibetan 26 #define smEthiopic 28 #define smCentralEuroRoman 29 #define verGreece 20 #define verIceland 21 #define verTurkey 24 #define verYugoCroatian 25 #define verRomania 39 #define verFaroeIsl 47 #define verIran 48 #define verRussia 49 #define verSlovenian 66 #define verCroatia 68 #define verBulgaria 72 #define verScottishGaelic 75 #define verManxGaelic 76 #define verBreton 77 #define verNunavut 78 #define verWelsh 79 #define verIrishGaelicScript 81 static const struct { int script; int region; int sysvermin; char const *fontname; int charset; } macencs[] = { { smRoman, -1, 0x850, "VT100", CS_MAC_VT100 }, { smRoman, -1, 0, "VT100", CS_MAC_VT100_OLD }, /* * From here on, this table is largely derived from * , * with _OLD version added based on the comments in individual * mapping files. */ { smRoman, -1, 0, "Symbol", CS_MAC_SYMBOL }, { smRoman, -1, 0, "Zapf Dingbats", CS_MAC_DINGBATS }, { smRoman, verTurkey, 0, NULL, CS_MAC_TURKISH }, { smRoman, verYugoCroatian, 0x850, NULL, CS_MAC_CROATIAN }, { smRoman, verYugoCroatian, 0, NULL, CS_MAC_CROATIAN_OLD }, { smRoman, verSlovenian, 0x850, NULL, CS_MAC_CROATIAN }, { smRoman, verSlovenian, 0, NULL, CS_MAC_CROATIAN_OLD }, { smRoman, verCroatia, 0x850, NULL, CS_MAC_CROATIAN }, { smRoman, verCroatia, 0, NULL, CS_MAC_CROATIAN_OLD }, { smRoman, verIceland, 0x850, NULL, CS_MAC_ICELAND }, { smRoman, verIceland, 0, NULL, CS_MAC_ICELAND_OLD }, { smRoman, verFaroeIsl, 0x850, NULL, CS_MAC_ICELAND }, { smRoman, verFaroeIsl, 0, NULL, CS_MAC_ICELAND_OLD }, { smRoman, verRomania, 0x850, NULL, CS_MAC_ROMANIAN }, { smRoman, verRomania, 0, NULL, CS_MAC_ROMANIAN_OLD }, #if 0 /* No mapping table on ftp.unicode.org */ { smRoman, verIreland, 0x850, NULL, CS_MAC_CELTIC }, { smRoman, verIreland, 0, NULL, CS_MAC_CELTIC_OLD }, { smRoman, verScottishGaelic, 0x850, NULL, CS_MAC_CELTIC }, { smRoman, verScottishGaelic, 0, NULL, CS_MAC_CELTIC_OLD }, { smRoman, verManxGaelic, 0x850, NULL, CS_MAC_CELTIC }, { smRoman, verManxGaelic, 0, NULL, CS_MAC_CELTIC_OLD }, { smRoman, verBreton, 0x850, NULL, CS_MAC_CELTIC }, { smRoman, verBreton, 0, NULL, CS_MAC_CELTIC_OLD }, { smRoman, verWelsh, 0x850, NULL, CS_MAC_CELTIC }, { smRoman, verWelsh, 0, NULL, CS_MAC_CELTIC_OLD }, { smRoman, verIrishGaelicScript, 0x850, NULL, CS_MAC_GAELIC }, { smRoman, verIrishGaelicScript, 0, NULL, CS_MAC_GAELIC_OLD }, #endif { smRoman, verGreece, 0x922, NULL, CS_MAC_GREEK }, { smRoman, verGreece, 0, NULL, CS_MAC_GREEK_OLD }, { smRoman, -1, 0x850, NULL, CS_MAC_ROMAN }, { smRoman, -1, 0, NULL, CS_MAC_ROMAN_OLD }, #if 0 /* Multi-byte encodings, not yet supported */ { smJapanese, -1, 0, NULL, CS_MAC_JAPANESE }, { smTradChinese, -1, 0, NULL, CS_MAC_CHINTRAD }, { smKorean, -1, 0, NULL, CS_MAC_KOREAN }, #endif #if 0 /* Bidirectional encodings, not yet supported */ { smArabic, verIran, 0, NULL, CS_MAC_FARSI }, { smArabic, -1, 0, NULL, CS_MAC_ARABIC }, { smHebrew, -1, 0, NULL, CS_MAC_HEBREW }, #endif { smCyrillic, -1, 0x900, NULL, CS_MAC_CYRILLIC }, { smCyrillic, verRussia, 0, NULL, CS_MAC_CYRILLIC_OLD }, { smCyrillic, verBulgaria, 0, NULL, CS_MAC_CYRILLIC_OLD }, { smCyrillic, -1, 0, NULL, CS_MAC_UKRAINE }, #if 0 /* Complex Indic scripts, not yet supported */ { smDevanagari, -1, 0, NULL, CS_MAC_DEVENAGA }, { smGurmukhi, -1, 0, NULL, CS_MAC_GURMUKHI }, { smGujurati, -1, 0, NULL, CS_MAC_GUJURATI }, #endif { smThai, -1, 0, NULL, CS_MAC_THAI }, #if 0 /* Multi-byte encoding, not yet supported */ { smSimpChinese, -1, 0, NULL, CS_MAC_CHINSIMP }, #endif #if 0 /* No mapping table on ftp.unicode.org */ { smTibetan, -1, 0, NULL, CS_MAC_TIBETAN }, { smEthiopic, -1, 0, NULL, CS_MAC_ETHIOPIC }, { smEthiopic, verNanavut, 0, NULL, CS_MAC_INUIT }, #endif { smCentralEuroRoman, -1, 0, NULL, CS_MAC_CENTEURO }, }; int charset_from_macenc(int script, int region, int sysvers, char const *fontname) { int i; for (i = 0; i < (int)lenof(macencs); i++) if ((macencs[i].script == script) && (macencs[i].region < 0 || macencs[i].region == region) && (macencs[i].sysvermin <= sysvers) && (macencs[i].fontname == NULL || (fontname != NULL && strcmp(macencs[i].fontname, fontname) == 0))) return macencs[i].charset; return CS_NONE; } putty-0.76/charset/mimeenc.c0000644000175000017500000001331314072266307012776 00000000000000/* * mimeenc.c - translate our internal character set codes to and * from MIME standard character-set names. * */ #include #include "charset.h" #include "internal.h" static const struct { const char *name; int charset; } mimeencs[] = { /* * These names are taken from * * http://www.iana.org/assignments/character-sets * * Where multiple encoding names map to the same encoding id * (such as the variety of aliases for ISO-8859-1), the first * is considered canonical and will be returned when * translating the id to a string. */ { "ISO-8859-1", CS_ISO8859_1 }, { "iso-ir-100", CS_ISO8859_1 }, { "ISO_8859-1", CS_ISO8859_1 }, { "ISO_8859-1:1987", CS_ISO8859_1 }, { "latin1", CS_ISO8859_1 }, { "l1", CS_ISO8859_1 }, { "IBM819", CS_ISO8859_1 }, { "CP819", CS_ISO8859_1 }, { "csISOLatin1", CS_ISO8859_1 }, { "ISO-8859-2", CS_ISO8859_2 }, { "ISO_8859-2:1987", CS_ISO8859_2 }, { "iso-ir-101", CS_ISO8859_2 }, { "ISO_8859-2", CS_ISO8859_2 }, { "latin2", CS_ISO8859_2 }, { "l2", CS_ISO8859_2 }, { "csISOLatin2", CS_ISO8859_2 }, { "ISO-8859-3", CS_ISO8859_3 }, { "ISO_8859-3:1988", CS_ISO8859_3 }, { "iso-ir-109", CS_ISO8859_3 }, { "ISO_8859-3", CS_ISO8859_3 }, { "latin3", CS_ISO8859_3 }, { "l3", CS_ISO8859_3 }, { "csISOLatin3", CS_ISO8859_3 }, { "ISO-8859-4", CS_ISO8859_4 }, { "ISO_8859-4:1988", CS_ISO8859_4 }, { "iso-ir-110", CS_ISO8859_4 }, { "ISO_8859-4", CS_ISO8859_4 }, { "latin4", CS_ISO8859_4 }, { "l4", CS_ISO8859_4 }, { "csISOLatin4", CS_ISO8859_4 }, { "ISO-8859-5", CS_ISO8859_5 }, { "ISO_8859-5:1988", CS_ISO8859_5 }, { "iso-ir-144", CS_ISO8859_5 }, { "ISO_8859-5", CS_ISO8859_5 }, { "cyrillic", CS_ISO8859_5 }, { "csISOLatinCyrillic", CS_ISO8859_5 }, { "ISO-8859-6", CS_ISO8859_6 }, { "ISO_8859-6:1987", CS_ISO8859_6 }, { "iso-ir-127", CS_ISO8859_6 }, { "ISO_8859-6", CS_ISO8859_6 }, { "ECMA-114", CS_ISO8859_6 }, { "ASMO-708", CS_ISO8859_6 }, { "arabic", CS_ISO8859_6 }, { "csISOLatinArabic", CS_ISO8859_6 }, { "ISO-8859-7", CS_ISO8859_7 }, { "ISO_8859-7:1987", CS_ISO8859_7 }, { "iso-ir-126", CS_ISO8859_7 }, { "ISO_8859-7", CS_ISO8859_7 }, { "ELOT_928", CS_ISO8859_7 }, { "ECMA-118", CS_ISO8859_7 }, { "greek", CS_ISO8859_7 }, { "greek8", CS_ISO8859_7 }, { "csISOLatinGreek", CS_ISO8859_7 }, { "ISO-8859-8", CS_ISO8859_8 }, { "ISO_8859-8:1988", CS_ISO8859_8 }, { "iso-ir-138", CS_ISO8859_8 }, { "ISO_8859-8", CS_ISO8859_8 }, { "hebrew", CS_ISO8859_8 }, { "csISOLatinHebrew", CS_ISO8859_8 }, { "ISO-8859-9", CS_ISO8859_9 }, { "ISO_8859-9:1989", CS_ISO8859_9 }, { "iso-ir-148", CS_ISO8859_9 }, { "ISO_8859-9", CS_ISO8859_9 }, { "latin5", CS_ISO8859_9 }, { "l5", CS_ISO8859_9 }, { "csISOLatin5", CS_ISO8859_9 }, { "ISO-8859-10", CS_ISO8859_10 }, { "iso-ir-157", CS_ISO8859_10 }, { "l6", CS_ISO8859_10 }, { "ISO_8859-10:1992", CS_ISO8859_10 }, { "csISOLatin6", CS_ISO8859_10 }, { "latin6", CS_ISO8859_10 }, { "ISO-8859-13", CS_ISO8859_13 }, { "ISO-8859-14", CS_ISO8859_14 }, { "iso-ir-199", CS_ISO8859_14 }, { "ISO_8859-14:1998", CS_ISO8859_14 }, { "ISO_8859-14", CS_ISO8859_14 }, { "latin8", CS_ISO8859_14 }, { "iso-celtic", CS_ISO8859_14 }, { "l8", CS_ISO8859_14 }, { "ISO-8859-15", CS_ISO8859_15 }, { "ISO_8859-15", CS_ISO8859_15 }, { "Latin-9", CS_ISO8859_15 }, { "ISO-8859-16", CS_ISO8859_16 }, { "iso-ir-226", CS_ISO8859_16 }, { "ISO_8859-16", CS_ISO8859_16 }, { "ISO_8859-16:2001", CS_ISO8859_16 }, { "latin10", CS_ISO8859_16 }, { "l10", CS_ISO8859_16 }, { "IBM437", CS_CP437 }, { "cp437", CS_CP437 }, { "437", CS_CP437 }, { "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 }, { "windows-1251", CS_CP1251 }, { "windows-1252", CS_CP1252 }, { "windows-1253", CS_CP1253 }, { "windows-1254", CS_CP1254 }, { "windows-1255", CS_CP1255 }, { "windows-1256", CS_CP1256 }, { "windows-1257", CS_CP1257 }, { "windows-1258", CS_CP1258 }, { "KOI8-R", CS_KOI8_R }, { "csKOI8R", CS_KOI8_R }, { "KOI8-U", CS_KOI8_U }, { "macintosh", CS_MAC_ROMAN_OLD }, { "mac", CS_MAC_ROMAN_OLD }, { "csMacintosh", CS_MAC_ROMAN_OLD }, { "VISCII", CS_VISCII }, { "csVISCII", CS_VISCII }, { "hp-roman8", CS_HP_ROMAN8 }, { "roman8", CS_HP_ROMAN8 }, { "r8", CS_HP_ROMAN8 }, { "csHPRoman8", CS_HP_ROMAN8 }, { "DEC-MCS", CS_DEC_MCS }, { "dec", CS_DEC_MCS }, { "csDECMCS", CS_DEC_MCS }, { "UTF-8", CS_UTF8 }, }; const char *charset_to_mimeenc(int charset) { int i; for (i = 0; i < (int)lenof(mimeencs); i++) if (charset == mimeencs[i].charset) return mimeencs[i].name; return NULL; /* not found */ } int charset_from_mimeenc(const char *name) { int i; for (i = 0; i < (int)lenof(mimeencs); i++) { const char *p, *q; p = name; q = mimeencs[i].name; while (*p || *q) { if (tolower((unsigned char)*p) != tolower((unsigned char)*q)) break; p++; q++; } if (!*p && !*q) return mimeencs[i].charset; } return CS_NONE; /* not found */ } putty-0.76/charset/sbcs.c0000644000175000017500000000242614072266307012316 00000000000000/* * sbcs.c - routines to handle single-byte character sets. */ #include "charset.h" #include "internal.h" /* * The charset_spec for any single-byte character set should * provide read_sbcs() as its read function, and its `data' field * should be a wchar_t string constant containing the 256 entries * of the translation table. */ void read_sbcs(charset_spec const *charset, long int input_chr, charset_state *state, void (*emit)(void *ctx, long int output), void *emitctx) { const struct sbcs_data *sd = charset->data; UNUSEDARG(state); emit(emitctx, sd->sbcs2ucs[input_chr]); } void write_sbcs(charset_spec const *charset, long int input_chr, charset_state *state, void (*emit)(void *ctx, long int output), void *emitctx) { const struct sbcs_data *sd = charset->data; int i, j, k, c; UNUSEDARG(state); /* * Binary-search in the ucs2sbcs table. */ i = -1; j = sd->nvalid; while (i+1 < j) { k = (i+j)/2; c = sd->ucs2sbcs[k]; if (input_chr < sd->sbcs2ucs[c]) j = k; else if (input_chr > sd->sbcs2ucs[c]) i = k; else { emit(emitctx, c); return; } } emit(emitctx, ERROR); } putty-0.76/charset/sbcs.dat0000644000175000017500000022627114072266307012652 00000000000000 Data file defining single-byte character sets. All lines which begin with whitespace are considered comments. To generate an SBCS table from a unicode.org mapping table: gensbcs() { wget -q -O - "$1" | tr '\r' '\n' | \ perl -ne '/^(0x.*)\s+(0x.*)\s+/ and $a[hex $1]=sprintf "%04x", hex $2;' \ -e 'BEGIN{for($i=0;$i<256;$i++){$a[$i]="XXXX";' \ -e ' if ($i < 32 or $i == 127) {$a[$i]=sprintf "%04x", $i}}}' \ -e 'END{for($i=0;$i<256;$i++){printf"%s%s",$a[$i],$i%16==15?"\n":" "}}' } (A couple of noteworthy ickinesses here. For a start, any undefined characters in the control-code regions (00-1F and 7F) are assumed to be the Unicode code point corresponding to their index, since the Mac Roman mapping table declines to define them but realistically you don't want to be messing with that sort of thing. Secondly, the Mac mapping tables are shipped with Mac line endings, so note the `tr' to turn them into something legible to Perl...) Here are the ISO-8859-x tables, generated by this piece of Bourne shell: for i in 1 2 3 4 5 6 7 8 9 10 11 13 14 15 16; do echo charset CS_ISO8859_$i gensbcs http://www.unicode.org/Public/MAPPINGS/ISO8859/8859-$i.TXT echo done charset CS_ISO8859_1 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 0080 0081 0082 0083 0084 0085 0086 0087 0088 0089 008a 008b 008c 008d 008e 008f 0090 0091 0092 0093 0094 0095 0096 0097 0098 0099 009a 009b 009c 009d 009e 009f 00a0 00a1 00a2 00a3 00a4 00a5 00a6 00a7 00a8 00a9 00aa 00ab 00ac 00ad 00ae 00af 00b0 00b1 00b2 00b3 00b4 00b5 00b6 00b7 00b8 00b9 00ba 00bb 00bc 00bd 00be 00bf 00c0 00c1 00c2 00c3 00c4 00c5 00c6 00c7 00c8 00c9 00ca 00cb 00cc 00cd 00ce 00cf 00d0 00d1 00d2 00d3 00d4 00d5 00d6 00d7 00d8 00d9 00da 00db 00dc 00dd 00de 00df 00e0 00e1 00e2 00e3 00e4 00e5 00e6 00e7 00e8 00e9 00ea 00eb 00ec 00ed 00ee 00ef 00f0 00f1 00f2 00f3 00f4 00f5 00f6 00f7 00f8 00f9 00fa 00fb 00fc 00fd 00fe 00ff charset CS_ISO8859_2 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 0080 0081 0082 0083 0084 0085 0086 0087 0088 0089 008a 008b 008c 008d 008e 008f 0090 0091 0092 0093 0094 0095 0096 0097 0098 0099 009a 009b 009c 009d 009e 009f 00a0 0104 02d8 0141 00a4 013d 015a 00a7 00a8 0160 015e 0164 0179 00ad 017d 017b 00b0 0105 02db 0142 00b4 013e 015b 02c7 00b8 0161 015f 0165 017a 02dd 017e 017c 0154 00c1 00c2 0102 00c4 0139 0106 00c7 010c 00c9 0118 00cb 011a 00cd 00ce 010e 0110 0143 0147 00d3 00d4 0150 00d6 00d7 0158 016e 00da 0170 00dc 00dd 0162 00df 0155 00e1 00e2 0103 00e4 013a 0107 00e7 010d 00e9 0119 00eb 011b 00ed 00ee 010f 0111 0144 0148 00f3 00f4 0151 00f6 00f7 0159 016f 00fa 0171 00fc 00fd 0163 02d9 charset CS_ISO8859_3 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 0080 0081 0082 0083 0084 0085 0086 0087 0088 0089 008a 008b 008c 008d 008e 008f 0090 0091 0092 0093 0094 0095 0096 0097 0098 0099 009a 009b 009c 009d 009e 009f 00a0 0126 02d8 00a3 00a4 XXXX 0124 00a7 00a8 0130 015e 011e 0134 00ad XXXX 017b 00b0 0127 00b2 00b3 00b4 00b5 0125 00b7 00b8 0131 015f 011f 0135 00bd XXXX 017c 00c0 00c1 00c2 XXXX 00c4 010a 0108 00c7 00c8 00c9 00ca 00cb 00cc 00cd 00ce 00cf XXXX 00d1 00d2 00d3 00d4 0120 00d6 00d7 011c 00d9 00da 00db 00dc 016c 015c 00df 00e0 00e1 00e2 XXXX 00e4 010b 0109 00e7 00e8 00e9 00ea 00eb 00ec 00ed 00ee 00ef XXXX 00f1 00f2 00f3 00f4 0121 00f6 00f7 011d 00f9 00fa 00fb 00fc 016d 015d 02d9 charset CS_ISO8859_4 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 0080 0081 0082 0083 0084 0085 0086 0087 0088 0089 008a 008b 008c 008d 008e 008f 0090 0091 0092 0093 0094 0095 0096 0097 0098 0099 009a 009b 009c 009d 009e 009f 00a0 0104 0138 0156 00a4 0128 013b 00a7 00a8 0160 0112 0122 0166 00ad 017d 00af 00b0 0105 02db 0157 00b4 0129 013c 02c7 00b8 0161 0113 0123 0167 014a 017e 014b 0100 00c1 00c2 00c3 00c4 00c5 00c6 012e 010c 00c9 0118 00cb 0116 00cd 00ce 012a 0110 0145 014c 0136 00d4 00d5 00d6 00d7 00d8 0172 00da 00db 00dc 0168 016a 00df 0101 00e1 00e2 00e3 00e4 00e5 00e6 012f 010d 00e9 0119 00eb 0117 00ed 00ee 012b 0111 0146 014d 0137 00f4 00f5 00f6 00f7 00f8 0173 00fa 00fb 00fc 0169 016b 02d9 charset CS_ISO8859_5 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 0080 0081 0082 0083 0084 0085 0086 0087 0088 0089 008a 008b 008c 008d 008e 008f 0090 0091 0092 0093 0094 0095 0096 0097 0098 0099 009a 009b 009c 009d 009e 009f 00a0 0401 0402 0403 0404 0405 0406 0407 0408 0409 040a 040b 040c 00ad 040e 040f 0410 0411 0412 0413 0414 0415 0416 0417 0418 0419 041a 041b 041c 041d 041e 041f 0420 0421 0422 0423 0424 0425 0426 0427 0428 0429 042a 042b 042c 042d 042e 042f 0430 0431 0432 0433 0434 0435 0436 0437 0438 0439 043a 043b 043c 043d 043e 043f 0440 0441 0442 0443 0444 0445 0446 0447 0448 0449 044a 044b 044c 044d 044e 044f 2116 0451 0452 0453 0454 0455 0456 0457 0458 0459 045a 045b 045c 00a7 045e 045f charset CS_ISO8859_6 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 0080 0081 0082 0083 0084 0085 0086 0087 0088 0089 008a 008b 008c 008d 008e 008f 0090 0091 0092 0093 0094 0095 0096 0097 0098 0099 009a 009b 009c 009d 009e 009f 00a0 XXXX XXXX XXXX 00a4 XXXX XXXX XXXX XXXX XXXX XXXX XXXX 060c 00ad XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX 061b XXXX XXXX XXXX 061f XXXX 0621 0622 0623 0624 0625 0626 0627 0628 0629 062a 062b 062c 062d 062e 062f 0630 0631 0632 0633 0634 0635 0636 0637 0638 0639 063a XXXX XXXX XXXX XXXX XXXX 0640 0641 0642 0643 0644 0645 0646 0647 0648 0649 064a 064b 064c 064d 064e 064f 0650 0651 0652 XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX charset CS_ISO8859_7 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 0080 0081 0082 0083 0084 0085 0086 0087 0088 0089 008a 008b 008c 008d 008e 008f 0090 0091 0092 0093 0094 0095 0096 0097 0098 0099 009a 009b 009c 009d 009e 009f 00a0 2018 2019 00a3 XXXX XXXX 00a6 00a7 00a8 00a9 XXXX 00ab 00ac 00ad XXXX 2015 00b0 00b1 00b2 00b3 0384 0385 0386 00b7 0388 0389 038a 00bb 038c 00bd 038e 038f 0390 0391 0392 0393 0394 0395 0396 0397 0398 0399 039a 039b 039c 039d 039e 039f 03a0 03a1 XXXX 03a3 03a4 03a5 03a6 03a7 03a8 03a9 03aa 03ab 03ac 03ad 03ae 03af 03b0 03b1 03b2 03b3 03b4 03b5 03b6 03b7 03b8 03b9 03ba 03bb 03bc 03bd 03be 03bf 03c0 03c1 03c2 03c3 03c4 03c5 03c6 03c7 03c8 03c9 03ca 03cb 03cc 03cd 03ce XXXX charset CS_ISO8859_8 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 0080 0081 0082 0083 0084 0085 0086 0087 0088 0089 008a 008b 008c 008d 008e 008f 0090 0091 0092 0093 0094 0095 0096 0097 0098 0099 009a 009b 009c 009d 009e 009f 00a0 XXXX 00a2 00a3 00a4 00a5 00a6 00a7 00a8 00a9 00d7 00ab 00ac 00ad 00ae 00af 00b0 00b1 00b2 00b3 00b4 00b5 00b6 00b7 00b8 00b9 00f7 00bb 00bc 00bd 00be XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX 2017 05d0 05d1 05d2 05d3 05d4 05d5 05d6 05d7 05d8 05d9 05da 05db 05dc 05dd 05de 05df 05e0 05e1 05e2 05e3 05e4 05e5 05e6 05e7 05e8 05e9 05ea XXXX XXXX 200e 200f XXXX charset CS_ISO8859_9 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 0080 0081 0082 0083 0084 0085 0086 0087 0088 0089 008a 008b 008c 008d 008e 008f 0090 0091 0092 0093 0094 0095 0096 0097 0098 0099 009a 009b 009c 009d 009e 009f 00a0 00a1 00a2 00a3 00a4 00a5 00a6 00a7 00a8 00a9 00aa 00ab 00ac 00ad 00ae 00af 00b0 00b1 00b2 00b3 00b4 00b5 00b6 00b7 00b8 00b9 00ba 00bb 00bc 00bd 00be 00bf 00c0 00c1 00c2 00c3 00c4 00c5 00c6 00c7 00c8 00c9 00ca 00cb 00cc 00cd 00ce 00cf 011e 00d1 00d2 00d3 00d4 00d5 00d6 00d7 00d8 00d9 00da 00db 00dc 0130 015e 00df 00e0 00e1 00e2 00e3 00e4 00e5 00e6 00e7 00e8 00e9 00ea 00eb 00ec 00ed 00ee 00ef 011f 00f1 00f2 00f3 00f4 00f5 00f6 00f7 00f8 00f9 00fa 00fb 00fc 0131 015f 00ff charset CS_ISO8859_10 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 0080 0081 0082 0083 0084 0085 0086 0087 0088 0089 008a 008b 008c 008d 008e 008f 0090 0091 0092 0093 0094 0095 0096 0097 0098 0099 009a 009b 009c 009d 009e 009f 00a0 0104 0112 0122 012a 0128 0136 00a7 013b 0110 0160 0166 017d 00ad 016a 014a 00b0 0105 0113 0123 012b 0129 0137 00b7 013c 0111 0161 0167 017e 2015 016b 014b 0100 00c1 00c2 00c3 00c4 00c5 00c6 012e 010c 00c9 0118 00cb 0116 00cd 00ce 00cf 00d0 0145 014c 00d3 00d4 00d5 00d6 0168 00d8 0172 00da 00db 00dc 00dd 00de 00df 0101 00e1 00e2 00e3 00e4 00e5 00e6 012f 010d 00e9 0119 00eb 0117 00ed 00ee 00ef 00f0 0146 014d 00f3 00f4 00f5 00f6 0169 00f8 0173 00fa 00fb 00fc 00fd 00fe 0138 charset CS_ISO8859_11 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 0080 0081 0082 0083 0084 0085 0086 0087 0088 0089 008a 008b 008c 008d 008e 008f 0090 0091 0092 0093 0094 0095 0096 0097 0098 0099 009a 009b 009c 009d 009e 009f 00a0 0e01 0e02 0e03 0e04 0e05 0e06 0e07 0e08 0e09 0e0a 0e0b 0e0c 0e0d 0e0e 0e0f 0e10 0e11 0e12 0e13 0e14 0e15 0e16 0e17 0e18 0e19 0e1a 0e1b 0e1c 0e1d 0e1e 0e1f 0e20 0e21 0e22 0e23 0e24 0e25 0e26 0e27 0e28 0e29 0e2a 0e2b 0e2c 0e2d 0e2e 0e2f 0e30 0e31 0e32 0e33 0e34 0e35 0e36 0e37 0e38 0e39 0e3a XXXX XXXX XXXX XXXX 0e3f 0e40 0e41 0e42 0e43 0e44 0e45 0e46 0e47 0e48 0e49 0e4a 0e4b 0e4c 0e4d 0e4e 0e4f 0e50 0e51 0e52 0e53 0e54 0e55 0e56 0e57 0e58 0e59 0e5a 0e5b XXXX XXXX XXXX XXXX charset CS_ISO8859_13 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 0080 0081 0082 0083 0084 0085 0086 0087 0088 0089 008a 008b 008c 008d 008e 008f 0090 0091 0092 0093 0094 0095 0096 0097 0098 0099 009a 009b 009c 009d 009e 009f 00a0 201d 00a2 00a3 00a4 201e 00a6 00a7 00d8 00a9 0156 00ab 00ac 00ad 00ae 00c6 00b0 00b1 00b2 00b3 201c 00b5 00b6 00b7 00f8 00b9 0157 00bb 00bc 00bd 00be 00e6 0104 012e 0100 0106 00c4 00c5 0118 0112 010c 00c9 0179 0116 0122 0136 012a 013b 0160 0143 0145 00d3 014c 00d5 00d6 00d7 0172 0141 015a 016a 00dc 017b 017d 00df 0105 012f 0101 0107 00e4 00e5 0119 0113 010d 00e9 017a 0117 0123 0137 012b 013c 0161 0144 0146 00f3 014d 00f5 00f6 00f7 0173 0142 015b 016b 00fc 017c 017e 2019 charset CS_ISO8859_14 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 0080 0081 0082 0083 0084 0085 0086 0087 0088 0089 008a 008b 008c 008d 008e 008f 0090 0091 0092 0093 0094 0095 0096 0097 0098 0099 009a 009b 009c 009d 009e 009f 00a0 1e02 1e03 00a3 010a 010b 1e0a 00a7 1e80 00a9 1e82 1e0b 1ef2 00ad 00ae 0178 1e1e 1e1f 0120 0121 1e40 1e41 00b6 1e56 1e81 1e57 1e83 1e60 1ef3 1e84 1e85 1e61 00c0 00c1 00c2 00c3 00c4 00c5 00c6 00c7 00c8 00c9 00ca 00cb 00cc 00cd 00ce 00cf 0174 00d1 00d2 00d3 00d4 00d5 00d6 1e6a 00d8 00d9 00da 00db 00dc 00dd 0176 00df 00e0 00e1 00e2 00e3 00e4 00e5 00e6 00e7 00e8 00e9 00ea 00eb 00ec 00ed 00ee 00ef 0175 00f1 00f2 00f3 00f4 00f5 00f6 1e6b 00f8 00f9 00fa 00fb 00fc 00fd 0177 00ff charset CS_ISO8859_15 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 0080 0081 0082 0083 0084 0085 0086 0087 0088 0089 008a 008b 008c 008d 008e 008f 0090 0091 0092 0093 0094 0095 0096 0097 0098 0099 009a 009b 009c 009d 009e 009f 00a0 00a1 00a2 00a3 20ac 00a5 0160 00a7 0161 00a9 00aa 00ab 00ac 00ad 00ae 00af 00b0 00b1 00b2 00b3 017d 00b5 00b6 00b7 017e 00b9 00ba 00bb 0152 0153 0178 00bf 00c0 00c1 00c2 00c3 00c4 00c5 00c6 00c7 00c8 00c9 00ca 00cb 00cc 00cd 00ce 00cf 00d0 00d1 00d2 00d3 00d4 00d5 00d6 00d7 00d8 00d9 00da 00db 00dc 00dd 00de 00df 00e0 00e1 00e2 00e3 00e4 00e5 00e6 00e7 00e8 00e9 00ea 00eb 00ec 00ed 00ee 00ef 00f0 00f1 00f2 00f3 00f4 00f5 00f6 00f7 00f8 00f9 00fa 00fb 00fc 00fd 00fe 00ff charset CS_ISO8859_16 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 0080 0081 0082 0083 0084 0085 0086 0087 0088 0089 008a 008b 008c 008d 008e 008f 0090 0091 0092 0093 0094 0095 0096 0097 0098 0099 009a 009b 009c 009d 009e 009f 00a0 0104 0105 0141 20ac 201e 0160 00a7 0161 00a9 0218 00ab 0179 00ad 017a 017b 00b0 00b1 010c 0142 017d 201d 00b6 00b7 017e 010d 0219 00bb 0152 0153 0178 017c 00c0 00c1 00c2 0102 00c4 0106 00c6 00c7 00c8 00c9 00ca 00cb 00cc 00cd 00ce 00cf 0110 0143 00d2 00d3 00d4 0150 00d6 015a 0170 00d9 00da 00db 00dc 0118 021a 00df 00e0 00e1 00e2 0103 00e4 0107 00e6 00e7 00e8 00e9 00ea 00eb 00ec 00ed 00ee 00ef 0111 0144 00f2 00f3 00f4 0151 00f6 015b 0171 00f9 00fa 00fb 00fc 0119 021b 00ff Some X fonts are encoded in a variant form of ISO8859-1: everything above 0x20 (space) is as normal, but the first 32 characters contain the VT100 line drawing glyphs as they would appear from positions 0x5F to 0x7E inclusive. Here is the modified ISO8859-1 code table. Since this table contains a few duplicated positions, we use the `sortpriority' hint to indicate that things in the main part of the code table (0x20-0xFF) should be generated preferentially when converting _from_ Unicode. Hence, U+00b0 (for example) will yield 0xb0 rather than 0x07. charset CS_ISO8859_1_X11 sortpriority 00-1F -1 0020 2666 2592 2409 240c 240d 240a 00b0 00b1 2424 240b 2518 2510 250c 2514 253c 23ba 23bb 2500 23bc 23bd 251c 2524 2534 252c 2502 2264 2265 03c0 2260 00a3 00b7 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 0080 0081 0082 0083 0084 0085 0086 0087 0088 0089 008a 008b 008c 008d 008e 008f 0090 0091 0092 0093 0094 0095 0096 0097 0098 0099 009a 009b 009c 009d 009e 009f 00a0 00a1 00a2 00a3 00a4 00a5 00a6 00a7 00a8 00a9 00aa 00ab 00ac 00ad 00ae 00af 00b0 00b1 00b2 00b3 00b4 00b5 00b6 00b7 00b8 00b9 00ba 00bb 00bc 00bd 00be 00bf 00c0 00c1 00c2 00c3 00c4 00c5 00c6 00c7 00c8 00c9 00ca 00cb 00cc 00cd 00ce 00cf 00d0 00d1 00d2 00d3 00d4 00d5 00d6 00d7 00d8 00d9 00da 00db 00dc 00dd 00de 00df 00e0 00e1 00e2 00e3 00e4 00e5 00e6 00e7 00e8 00e9 00ea 00eb 00ec 00ed 00ee 00ef 00f0 00f1 00f2 00f3 00f4 00f5 00f6 00f7 00f8 00f9 00fa 00fb 00fc 00fd 00fe 00ff Here are some PC (old DOS) code pages, generated by this piece of Bourne shell: for i in 437 850 866; do echo charset CS_CP$i gensbcs http://www.unicode.org/Public/MAPPINGS/VENDORS/MICSFT/PC/CP$i.TXT echo done charset CS_CP437 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 00e0 00e5 00e7 00ea 00eb 00e8 00ef 00ee 00ec 00c4 00c5 00c9 00e6 00c6 00f4 00f6 00f2 00fb 00f9 00ff 00d6 00dc 00a2 00a3 00a5 20a7 0192 00e1 00ed 00f3 00fa 00f1 00d1 00aa 00ba 00bf 2310 00ac 00bd 00bc 00a1 00ab 00bb 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 03b1 00df 0393 03c0 03a3 03c3 00b5 03c4 03a6 0398 03a9 03b4 221e 03c6 03b5 2229 2261 00b1 2265 2264 2320 2321 00f7 2248 00b0 2219 00b7 221a 207f 00b2 25a0 00a0 charset CS_CP850 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 00e0 00e5 00e7 00ea 00eb 00e8 00ef 00ee 00ec 00c4 00c5 00c9 00e6 00c6 00f4 00f6 00f2 00fb 00f9 00ff 00d6 00dc 00f8 00a3 00d8 00d7 0192 00e1 00ed 00f3 00fa 00f1 00d1 00aa 00ba 00bf 00ae 00ac 00bd 00bc 00a1 00ab 00bb 2591 2592 2593 2502 2524 00c1 00c2 00c0 00a9 2563 2551 2557 255d 00a2 00a5 2510 2514 2534 252c 251c 2500 253c 00e3 00c3 255a 2554 2569 2566 2560 2550 256c 00a4 00f0 00d0 00ca 00cb 00c8 0131 00cd 00ce 00cf 2518 250c 2588 2584 00a6 00cc 2580 00d3 00df 00d4 00d2 00f5 00d5 00b5 00fe 00de 00da 00db 00d9 00fd 00dd 00af 00b4 00ad 00b1 2017 00be 00b6 00a7 00f7 00b8 00b0 00a8 00b7 00b9 00b3 00b2 25a0 00a0 charset CS_CP866 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 0410 0411 0412 0413 0414 0415 0416 0417 0418 0419 041a 041b 041c 041d 041e 041f 0420 0421 0422 0423 0424 0425 0426 0427 0428 0429 042a 042b 042c 042d 042e 042f 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 done charset CS_CP1250 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 20ac XXXX 201a XXXX 201e 2026 2020 2021 XXXX 2030 0160 2039 015a 0164 017d 0179 XXXX 2018 2019 201c 201d 2022 2013 2014 XXXX 2122 0161 203a 015b 0165 017e 017a 00a0 02c7 02d8 0141 00a4 0104 00a6 00a7 00a8 00a9 015e 00ab 00ac 00ad 00ae 017b 00b0 00b1 02db 0142 00b4 00b5 00b6 00b7 00b8 0105 015f 00bb 013d 02dd 013e 017c 0154 00c1 00c2 0102 00c4 0139 0106 00c7 010c 00c9 0118 00cb 011a 00cd 00ce 010e 0110 0143 0147 00d3 00d4 0150 00d6 00d7 0158 016e 00da 0170 00dc 00dd 0162 00df 0155 00e1 00e2 0103 00e4 013a 0107 00e7 010d 00e9 0119 00eb 011b 00ed 00ee 010f 0111 0144 0148 00f3 00f4 0151 00f6 00f7 0159 016f 00fa 0171 00fc 00fd 0163 02d9 charset CS_CP1251 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 0402 0403 201a 0453 201e 2026 2020 2021 20ac 2030 0409 2039 040a 040c 040b 040f 0452 2018 2019 201c 201d 2022 2013 2014 XXXX 2122 0459 203a 045a 045c 045b 045f 00a0 040e 045e 0408 00a4 0490 00a6 00a7 0401 00a9 0404 00ab 00ac 00ad 00ae 0407 00b0 00b1 0406 0456 0491 00b5 00b6 00b7 0451 2116 0454 00bb 0458 0405 0455 0457 0410 0411 0412 0413 0414 0415 0416 0417 0418 0419 041a 041b 041c 041d 041e 041f 0420 0421 0422 0423 0424 0425 0426 0427 0428 0429 042a 042b 042c 042d 042e 042f 0430 0431 0432 0433 0434 0435 0436 0437 0438 0439 043a 043b 043c 043d 043e 043f 0440 0441 0442 0443 0444 0445 0446 0447 0448 0449 044a 044b 044c 044d 044e 044f charset CS_CP1252 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 20ac XXXX 201a 0192 201e 2026 2020 2021 02c6 2030 0160 2039 0152 XXXX 017d XXXX XXXX 2018 2019 201c 201d 2022 2013 2014 02dc 2122 0161 203a 0153 XXXX 017e 0178 00a0 00a1 00a2 00a3 00a4 00a5 00a6 00a7 00a8 00a9 00aa 00ab 00ac 00ad 00ae 00af 00b0 00b1 00b2 00b3 00b4 00b5 00b6 00b7 00b8 00b9 00ba 00bb 00bc 00bd 00be 00bf 00c0 00c1 00c2 00c3 00c4 00c5 00c6 00c7 00c8 00c9 00ca 00cb 00cc 00cd 00ce 00cf 00d0 00d1 00d2 00d3 00d4 00d5 00d6 00d7 00d8 00d9 00da 00db 00dc 00dd 00de 00df 00e0 00e1 00e2 00e3 00e4 00e5 00e6 00e7 00e8 00e9 00ea 00eb 00ec 00ed 00ee 00ef 00f0 00f1 00f2 00f3 00f4 00f5 00f6 00f7 00f8 00f9 00fa 00fb 00fc 00fd 00fe 00ff charset CS_CP1253 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 20ac XXXX 201a 0192 201e 2026 2020 2021 XXXX 2030 XXXX 2039 XXXX XXXX XXXX XXXX XXXX 2018 2019 201c 201d 2022 2013 2014 XXXX 2122 XXXX 203a XXXX XXXX XXXX XXXX 00a0 0385 0386 00a3 00a4 00a5 00a6 00a7 00a8 00a9 XXXX 00ab 00ac 00ad 00ae 2015 00b0 00b1 00b2 00b3 0384 00b5 00b6 00b7 0388 0389 038a 00bb 038c 00bd 038e 038f 0390 0391 0392 0393 0394 0395 0396 0397 0398 0399 039a 039b 039c 039d 039e 039f 03a0 03a1 XXXX 03a3 03a4 03a5 03a6 03a7 03a8 03a9 03aa 03ab 03ac 03ad 03ae 03af 03b0 03b1 03b2 03b3 03b4 03b5 03b6 03b7 03b8 03b9 03ba 03bb 03bc 03bd 03be 03bf 03c0 03c1 03c2 03c3 03c4 03c5 03c6 03c7 03c8 03c9 03ca 03cb 03cc 03cd 03ce XXXX charset CS_CP1254 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 20ac XXXX 201a 0192 201e 2026 2020 2021 02c6 2030 0160 2039 0152 XXXX XXXX XXXX XXXX 2018 2019 201c 201d 2022 2013 2014 02dc 2122 0161 203a 0153 XXXX XXXX 0178 00a0 00a1 00a2 00a3 00a4 00a5 00a6 00a7 00a8 00a9 00aa 00ab 00ac 00ad 00ae 00af 00b0 00b1 00b2 00b3 00b4 00b5 00b6 00b7 00b8 00b9 00ba 00bb 00bc 00bd 00be 00bf 00c0 00c1 00c2 00c3 00c4 00c5 00c6 00c7 00c8 00c9 00ca 00cb 00cc 00cd 00ce 00cf 011e 00d1 00d2 00d3 00d4 00d5 00d6 00d7 00d8 00d9 00da 00db 00dc 0130 015e 00df 00e0 00e1 00e2 00e3 00e4 00e5 00e6 00e7 00e8 00e9 00ea 00eb 00ec 00ed 00ee 00ef 011f 00f1 00f2 00f3 00f4 00f5 00f6 00f7 00f8 00f9 00fa 00fb 00fc 0131 015f 00ff charset CS_CP1255 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 20ac XXXX 201a 0192 201e 2026 2020 2021 02c6 2030 XXXX 2039 XXXX XXXX XXXX XXXX XXXX 2018 2019 201c 201d 2022 2013 2014 02dc 2122 XXXX 203a XXXX XXXX XXXX XXXX 00a0 00a1 00a2 00a3 20aa 00a5 00a6 00a7 00a8 00a9 00d7 00ab 00ac 00ad 00ae 00af 00b0 00b1 00b2 00b3 00b4 00b5 00b6 00b7 00b8 00b9 00f7 00bb 00bc 00bd 00be 00bf 05b0 05b1 05b2 05b3 05b4 05b5 05b6 05b7 05b8 05b9 XXXX 05bb 05bc 05bd 05be 05bf 05c0 05c1 05c2 05c3 05f0 05f1 05f2 05f3 05f4 XXXX XXXX XXXX XXXX XXXX XXXX XXXX 05d0 05d1 05d2 05d3 05d4 05d5 05d6 05d7 05d8 05d9 05da 05db 05dc 05dd 05de 05df 05e0 05e1 05e2 05e3 05e4 05e5 05e6 05e7 05e8 05e9 05ea XXXX XXXX 200e 200f XXXX charset CS_CP1256 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 20ac 067e 201a 0192 201e 2026 2020 2021 02c6 2030 0679 2039 0152 0686 0698 0688 06af 2018 2019 201c 201d 2022 2013 2014 06a9 2122 0691 203a 0153 200c 200d 06ba 00a0 060c 00a2 00a3 00a4 00a5 00a6 00a7 00a8 00a9 06be 00ab 00ac 00ad 00ae 00af 00b0 00b1 00b2 00b3 00b4 00b5 00b6 00b7 00b8 00b9 061b 00bb 00bc 00bd 00be 061f 06c1 0621 0622 0623 0624 0625 0626 0627 0628 0629 062a 062b 062c 062d 062e 062f 0630 0631 0632 0633 0634 0635 0636 00d7 0637 0638 0639 063a 0640 0641 0642 0643 00e0 0644 00e2 0645 0646 0647 0648 00e7 00e8 00e9 00ea 00eb 0649 064a 00ee 00ef 064b 064c 064d 064e 00f4 064f 0650 00f7 0651 00f9 0652 00fb 00fc 200e 200f 06d2 charset CS_CP1257 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 20ac XXXX 201a XXXX 201e 2026 2020 2021 XXXX 2030 XXXX 2039 XXXX 00a8 02c7 00b8 XXXX 2018 2019 201c 201d 2022 2013 2014 XXXX 2122 XXXX 203a XXXX 00af 02db XXXX 00a0 XXXX 00a2 00a3 00a4 XXXX 00a6 00a7 00d8 00a9 0156 00ab 00ac 00ad 00ae 00c6 00b0 00b1 00b2 00b3 00b4 00b5 00b6 00b7 00f8 00b9 0157 00bb 00bc 00bd 00be 00e6 0104 012e 0100 0106 00c4 00c5 0118 0112 010c 00c9 0179 0116 0122 0136 012a 013b 0160 0143 0145 00d3 014c 00d5 00d6 00d7 0172 0141 015a 016a 00dc 017b 017d 00df 0105 012f 0101 0107 00e4 00e5 0119 0113 010d 00e9 017a 0117 0123 0137 012b 013c 0161 0144 0146 00f3 014d 00f5 00f6 00f7 0173 0142 015b 016b 00fc 017c 017e 02d9 charset CS_CP1258 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 20ac XXXX 201a 0192 201e 2026 2020 2021 02c6 2030 XXXX 2039 0152 XXXX XXXX XXXX XXXX 2018 2019 201c 201d 2022 2013 2014 02dc 2122 XXXX 203a 0153 XXXX XXXX 0178 00a0 00a1 00a2 00a3 00a4 00a5 00a6 00a7 00a8 00a9 00aa 00ab 00ac 00ad 00ae 00af 00b0 00b1 00b2 00b3 00b4 00b5 00b6 00b7 00b8 00b9 00ba 00bb 00bc 00bd 00be 00bf 00c0 00c1 00c2 0102 00c4 00c5 00c6 00c7 00c8 00c9 00ca 00cb 0300 00cd 00ce 00cf 0110 00d1 0309 00d3 00d4 01a0 00d6 00d7 00d8 00d9 00da 00db 00dc 01af 0303 00df 00e0 00e1 00e2 0103 00e4 00e5 00e6 00e7 00e8 00e9 00ea 00eb 0301 00ed 00ee 00ef 0111 00f1 0323 00f3 00f4 01a1 00f6 00f7 00f8 00f9 00fa 00fb 00fc 01b0 20ab 00ff KOI8-R, generated by this code: { echo charset CS_KOI8_R; gensbcs http://www.unicode.org/Public/MAPPINGS/VENDORS/MISC/KOI8-R.TXT; } charset CS_KOI8_R 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 2500 2502 250c 2510 2514 2518 251c 2524 252c 2534 253c 2580 2584 2588 258c 2590 2591 2592 2593 2320 25a0 2219 221a 2248 2264 2265 00a0 2321 00b0 00b2 00b7 00f7 2550 2551 2552 0451 2553 2554 2555 2556 2557 2558 2559 255a 255b 255c 255d 255e 255f 2560 2561 0401 2562 2563 2564 2565 2566 2567 2568 2569 256a 256b 256c 00a9 044e 0430 0431 0446 0434 0435 0444 0433 0445 0438 0439 043a 043b 043c 043d 043e 043f 044f 0440 0441 0442 0443 0436 0432 044c 044b 0437 0448 044d 0449 0447 044a 042e 0410 0411 0426 0414 0415 0424 0413 0425 0418 0419 041a 041b 041c 041d 041e 041f 042f 0420 0421 0422 0423 0416 0412 042c 042b 0417 0428 042d 0429 0427 042a KOI8-U: I can't find an easily machine-processable mapping table for this one, so I've created it by hand-editing the KOI8-R mapping table in accordance with the list of differences specified in RFC2319. Note that RFC2319 has an apparent error: position B4 is listed as U+0404 in the main character set list, but as U+0403 in Appendix A (differences from KOI8-R). Both agree that it should be CYRILLIC CAPITAL LETTER UKRAINIAN IE, however, and the Unicode character database says that therefore U+0404 is the correct value. charset CS_KOI8_U 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 2500 2502 250c 2510 2514 2518 251c 2524 252c 2534 253c 2580 2584 2588 258c 2590 2591 2592 2593 2320 25a0 2219 221a 2248 2264 2265 00a0 2321 00b0 00b2 00b7 00f7 2550 2551 2552 0451 0454 2554 0456 0457 2557 2558 2559 255a 255b 0491 255d 255e 255f 2560 2561 0401 0404 2563 0406 0407 2566 2567 2568 2569 256a 0490 256c 00a9 044e 0430 0431 0446 0434 0435 0444 0433 0445 0438 0439 043a 043b 043c 043d 043e 043f 044f 0440 0441 0442 0443 0436 0432 044c 044b 0437 0448 044d 0449 0447 044a 042e 0410 0411 0426 0414 0415 0424 0413 0425 0418 0419 041a 041b 041c 041d 041e 041f 042f 0420 0421 0422 0423 0416 0412 042c 042b 0417 0428 042d 0429 0427 042a Various Mac character sets, generated by: for i in ROMAN TURKISH CROATIAN ICELAND ROMANIAN GREEK CYRILLIC THAI \ CENTEURO SYMBOL DINGBATS; do echo charset CS_MAC_$i gensbcs http://www.unicode.org/Public/MAPPINGS/VENDORS/APPLE/$i.TXT | \ sed s/f8a0/XXXX/ echo done The code point F8FF at position F0 in Mac OS Roman an interesting one. In Unicode, it's the last of the Private Use section. The mapping table states that it should be an Apple logo. I suppose we should just leave it as it is; there's bound to be some software out there that understands U+F8FF to be an Apple logo! Code point F8A0 at position F5 in Mac OS Turkish is actually just an undefined character, so we make it properly undefined. Many of the positions 80-9F in Mac OS Thai are for presentation forms of other characters. When converting from Unicode, we use `sortpriority' to avoid them. Positions E2-E4 in Mac OS Symbol are for sans-serif variants of other characters. Similarly, we avoid them. charset CS_MAC_ROMAN 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 00c4 00c5 00c7 00c9 00d1 00d6 00dc 00e1 00e0 00e2 00e4 00e3 00e5 00e7 00e9 00e8 00ea 00eb 00ed 00ec 00ee 00ef 00f1 00f3 00f2 00f4 00f6 00f5 00fa 00f9 00fb 00fc 2020 00b0 00a2 00a3 00a7 2022 00b6 00df 00ae 00a9 2122 00b4 00a8 2260 00c6 00d8 221e 00b1 2264 2265 00a5 00b5 2202 2211 220f 03c0 222b 00aa 00ba 03a9 00e6 00f8 00bf 00a1 00ac 221a 0192 2248 2206 00ab 00bb 2026 00a0 00c0 00c3 00d5 0152 0153 2013 2014 201c 201d 2018 2019 00f7 25ca 00ff 0178 2044 20ac 2039 203a fb01 fb02 2021 00b7 201a 201e 2030 00c2 00ca 00c1 00cb 00c8 00cd 00ce 00cf 00cc 00d3 00d4 f8ff 00d2 00da 00db 00d9 0131 02c6 02dc 00af 02d8 02d9 02da 00b8 02dd 02db 02c7 charset CS_MAC_TURKISH 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 00c4 00c5 00c7 00c9 00d1 00d6 00dc 00e1 00e0 00e2 00e4 00e3 00e5 00e7 00e9 00e8 00ea 00eb 00ed 00ec 00ee 00ef 00f1 00f3 00f2 00f4 00f6 00f5 00fa 00f9 00fb 00fc 2020 00b0 00a2 00a3 00a7 2022 00b6 00df 00ae 00a9 2122 00b4 00a8 2260 00c6 00d8 221e 00b1 2264 2265 00a5 00b5 2202 2211 220f 03c0 222b 00aa 00ba 03a9 00e6 00f8 00bf 00a1 00ac 221a 0192 2248 2206 00ab 00bb 2026 00a0 00c0 00c3 00d5 0152 0153 2013 2014 201c 201d 2018 2019 00f7 25ca 00ff 0178 011e 011f 0130 0131 015e 015f 2021 00b7 201a 201e 2030 00c2 00ca 00c1 00cb 00c8 00cd 00ce 00cf 00cc 00d3 00d4 f8ff 00d2 00da 00db 00d9 XXXX 02c6 02dc 00af 02d8 02d9 02da 00b8 02dd 02db 02c7 charset CS_MAC_CROATIAN 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 00c4 00c5 00c7 00c9 00d1 00d6 00dc 00e1 00e0 00e2 00e4 00e3 00e5 00e7 00e9 00e8 00ea 00eb 00ed 00ec 00ee 00ef 00f1 00f3 00f2 00f4 00f6 00f5 00fa 00f9 00fb 00fc 2020 00b0 00a2 00a3 00a7 2022 00b6 00df 00ae 0160 2122 00b4 00a8 2260 017d 00d8 221e 00b1 2264 2265 2206 00b5 2202 2211 220f 0161 222b 00aa 00ba 03a9 017e 00f8 00bf 00a1 00ac 221a 0192 2248 0106 00ab 010c 2026 00a0 00c0 00c3 00d5 0152 0153 0110 2014 201c 201d 2018 2019 00f7 25ca f8ff 00a9 2044 20ac 2039 203a 00c6 00bb 2013 00b7 201a 201e 2030 00c2 0107 00c1 010d 00c8 00cd 00ce 00cf 00cc 00d3 00d4 0111 00d2 00da 00db 00d9 0131 02c6 02dc 00af 03c0 00cb 02da 00b8 00ca 00e6 02c7 charset CS_MAC_ICELAND 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 00c4 00c5 00c7 00c9 00d1 00d6 00dc 00e1 00e0 00e2 00e4 00e3 00e5 00e7 00e9 00e8 00ea 00eb 00ed 00ec 00ee 00ef 00f1 00f3 00f2 00f4 00f6 00f5 00fa 00f9 00fb 00fc 00dd 00b0 00a2 00a3 00a7 2022 00b6 00df 00ae 00a9 2122 00b4 00a8 2260 00c6 00d8 221e 00b1 2264 2265 00a5 00b5 2202 2211 220f 03c0 222b 00aa 00ba 03a9 00e6 00f8 00bf 00a1 00ac 221a 0192 2248 2206 00ab 00bb 2026 00a0 00c0 00c3 00d5 0152 0153 2013 2014 201c 201d 2018 2019 00f7 25ca 00ff 0178 2044 20ac 00d0 00f0 00de 00fe 00fd 00b7 201a 201e 2030 00c2 00ca 00c1 00cb 00c8 00cd 00ce 00cf 00cc 00d3 00d4 f8ff 00d2 00da 00db 00d9 0131 02c6 02dc 00af 02d8 02d9 02da 00b8 02dd 02db 02c7 charset CS_MAC_ROMANIAN 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 00c4 00c5 00c7 00c9 00d1 00d6 00dc 00e1 00e0 00e2 00e4 00e3 00e5 00e7 00e9 00e8 00ea 00eb 00ed 00ec 00ee 00ef 00f1 00f3 00f2 00f4 00f6 00f5 00fa 00f9 00fb 00fc 2020 00b0 00a2 00a3 00a7 2022 00b6 00df 00ae 00a9 2122 00b4 00a8 2260 0102 0218 221e 00b1 2264 2265 00a5 00b5 2202 2211 220f 03c0 222b 00aa 00ba 03a9 0103 0219 00bf 00a1 00ac 221a 0192 2248 2206 00ab 00bb 2026 00a0 00c0 00c3 00d5 0152 0153 2013 2014 201c 201d 2018 2019 00f7 25ca 00ff 0178 2044 20ac 2039 203a 021a 021b 2021 00b7 201a 201e 2030 00c2 00ca 00c1 00cb 00c8 00cd 00ce 00cf 00cc 00d3 00d4 f8ff 00d2 00da 00db 00d9 0131 02c6 02dc 00af 02d8 02d9 02da 00b8 02dd 02db 02c7 charset CS_MAC_GREEK 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 00c4 00b9 00b2 00c9 00b3 00d6 00dc 0385 00e0 00e2 00e4 0384 00a8 00e7 00e9 00e8 00ea 00eb 00a3 2122 00ee 00ef 2022 00bd 2030 00f4 00f6 00a6 20ac 00f9 00fb 00fc 2020 0393 0394 0398 039b 039e 03a0 00df 00ae 00a9 03a3 03aa 00a7 2260 00b0 00b7 0391 00b1 2264 2265 00a5 0392 0395 0396 0397 0399 039a 039c 03a6 03ab 03a8 03a9 03ac 039d 00ac 039f 03a1 2248 03a4 00ab 00bb 2026 00a0 03a5 03a7 0386 0388 0153 2013 2015 201c 201d 2018 2019 00f7 0389 038a 038c 038e 03ad 03ae 03af 03cc 038f 03cd 03b1 03b2 03c8 03b4 03b5 03c6 03b3 03b7 03b9 03be 03ba 03bb 03bc 03bd 03bf 03c0 03ce 03c1 03c3 03c4 03b8 03c9 03c2 03c7 03c5 03b6 03ca 03cb 0390 03b0 00ad charset CS_MAC_CYRILLIC 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 0410 0411 0412 0413 0414 0415 0416 0417 0418 0419 041a 041b 041c 041d 041e 041f 0420 0421 0422 0423 0424 0425 0426 0427 0428 0429 042a 042b 042c 042d 042e 042f 2020 00b0 0490 00a3 00a7 2022 00b6 0406 00ae 00a9 2122 0402 0452 2260 0403 0453 221e 00b1 2264 2265 0456 00b5 0491 0408 0404 0454 0407 0457 0409 0459 040a 045a 0458 0405 00ac 221a 0192 2248 2206 00ab 00bb 2026 00a0 040b 045b 040c 045c 0455 2013 2014 201c 201d 2018 2019 00f7 201e 040e 045e 040f 045f 2116 0401 0451 044f 0430 0431 0432 0433 0434 0435 0436 0437 0438 0439 043a 043b 043c 043d 043e 043f 0440 0441 0442 0443 0444 0445 0446 0447 0448 0449 044a 044b 044c 044d 044e 20ac charset CS_MAC_THAI sortpriority 83-8C -1 sortpriority 8F-8F -1 sortpriority 92-9C -1 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 00ab 00bb 2026 0e48 0e49 0e4a 0e4b 0e4c 0e48 0e49 0e4a 0e4b 0e4c 201c 201d 0e4d XXXX 2022 0e31 0e47 0e34 0e35 0e36 0e37 0e48 0e49 0e4a 0e4b 0e4c 2018 2019 XXXX 00a0 0e01 0e02 0e03 0e04 0e05 0e06 0e07 0e08 0e09 0e0a 0e0b 0e0c 0e0d 0e0e 0e0f 0e10 0e11 0e12 0e13 0e14 0e15 0e16 0e17 0e18 0e19 0e1a 0e1b 0e1c 0e1d 0e1e 0e1f 0e20 0e21 0e22 0e23 0e24 0e25 0e26 0e27 0e28 0e29 0e2a 0e2b 0e2c 0e2d 0e2e 0e2f 0e30 0e31 0e32 0e33 0e34 0e35 0e36 0e37 0e38 0e39 0e3a 2060 200b 2013 2014 0e3f 0e40 0e41 0e42 0e43 0e44 0e45 0e46 0e47 0e48 0e49 0e4a 0e4b 0e4c 0e4d 2122 0e4f 0e50 0e51 0e52 0e53 0e54 0e55 0e56 0e57 0e58 0e59 00ae 00a9 XXXX XXXX XXXX XXXX charset CS_MAC_CENTEURO 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 00c4 0100 0101 00c9 0104 00d6 00dc 00e1 0105 010c 00e4 010d 0106 0107 00e9 0179 017a 010e 00ed 010f 0112 0113 0116 00f3 0117 00f4 00f6 00f5 00fa 011a 011b 00fc 2020 00b0 0118 00a3 00a7 2022 00b6 00df 00ae 00a9 2122 0119 00a8 2260 0123 012e 012f 012a 2264 2265 012b 0136 2202 2211 0142 013b 013c 013d 013e 0139 013a 0145 0146 0143 00ac 221a 0144 0147 2206 00ab 00bb 2026 00a0 0148 0150 00d5 0151 014c 2013 2014 201c 201d 2018 2019 00f7 25ca 014d 0154 0155 0158 2039 203a 0159 0156 0157 0160 201a 201e 0161 015a 015b 00c1 0164 0165 00cd 017d 017e 016a 00d3 00d4 016b 016e 00da 016f 0170 0171 0172 0173 00dd 00fd 0137 017b 0141 017c 0122 02c7 charset CS_MAC_SYMBOL sortpriority E2-E4 -1 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 2200 0023 2203 0025 0026 220d 0028 0029 2217 002b 002c 2212 002e 002f 0030 0031 0032 0033 0034 0035 0036 0037 0038 0039 003a 003b 003c 003d 003e 003f 2245 0391 0392 03a7 0394 0395 03a6 0393 0397 0399 03d1 039a 039b 039c 039d 039f 03a0 0398 03a1 03a3 03a4 03a5 03c2 03a9 039e 03a8 0396 005b 2234 005d 22a5 005f f8e5 03b1 03b2 03c7 03b4 03b5 03c6 03b3 03b7 03b9 03d5 03ba 03bb 03bc 03bd 03bf 03c0 03b8 03c1 03c3 03c4 03c5 03d6 03c9 03be 03c8 03b6 007b 007c 007d 223c 007f XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX 20ac 03d2 2032 2264 2044 221e 0192 2663 2666 2665 2660 2194 2190 2191 2192 2193 00b0 00b1 2033 2265 00d7 221d 2202 2022 00f7 2260 2261 2248 2026 f8e6 23af 21b5 2135 2111 211c 2118 2297 2295 2205 2229 222a 2283 2287 2284 2282 2286 2208 2209 2220 2207 00ae 00a9 2122 220f 221a 22c5 00ac 2227 2228 21d4 21d0 21d1 21d2 21d3 22c4 3008 00ae 00a9 2122 2211 239b 239c 239d 23a1 23a2 23a3 23a7 23a8 23a9 23aa f8ff 3009 222b 2320 23ae 2321 239e 239f 23a0 23a4 23a5 23a6 23ab 23ac 23ad XXXX charset CS_MAC_DINGBATS 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 2701 2702 2703 2704 260e 2706 2707 2708 2709 261b 261e 270c 270d 270e 270f 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 271a 271b 271c 271d 271e 271f 2720 2721 2722 2723 2724 2725 2726 2727 2605 2729 272a 272b 272c 272d 272e 272f 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 273a 273b 273c 273d 273e 273f 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 274a 274b 25cf 274d 25a0 274f 2750 2751 2752 25b2 25bc 25c6 2756 25d7 2758 2759 275a 275b 275c 275d 275e 007f 2768 2769 276a 276b 276c 276d 276e 276f 2770 2771 2772 2773 2774 2775 XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX 2761 2762 2763 2764 2765 2766 2767 2663 2666 2665 2660 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2776 2777 2778 2779 277a 277b 277c 277d 277e 277f 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 278a 278b 278c 278d 278e 278f 2790 2791 2792 2793 2794 2192 2194 2195 2798 2799 279a 279b 279c 279d 279e 279f 27a0 27a1 27a2 27a3 27a4 27a5 27a6 27a7 27a8 27a9 27aa 27ab 27ac 27ad 27ae 27af XXXX 27b1 27b2 27b3 27b4 27b5 27b6 27b7 27b8 27b9 27ba 27bb 27bc 27bd 27be XXXX Various Mac character sets have older (usually pre-Euro) variants which are documented in the comments in their mapping tables. I've manually applied these changes below. Mac OS Roman variants before Mac OS 8.5 (CURRENCY SIGN rather than EURO SIGN): charset CS_MAC_ROMAN_OLD 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 00c4 00c5 00c7 00c9 00d1 00d6 00dc 00e1 00e0 00e2 00e4 00e3 00e5 00e7 00e9 00e8 00ea 00eb 00ed 00ec 00ee 00ef 00f1 00f3 00f2 00f4 00f6 00f5 00fa 00f9 00fb 00fc 2020 00b0 00a2 00a3 00a7 2022 00b6 00df 00ae 00a9 2122 00b4 00a8 2260 00c6 00d8 221e 00b1 2264 2265 00a5 00b5 2202 2211 220f 03c0 222b 00aa 00ba 03a9 00e6 00f8 00bf 00a1 00ac 221a 0192 2248 2206 00ab 00bb 2026 00a0 00c0 00c3 00d5 0152 0153 2013 2014 201c 201d 2018 2019 00f7 25ca 00ff 0178 2044 00a4 2039 203a fb01 fb02 2021 00b7 201a 201e 2030 00c2 00ca 00c1 00cb 00c8 00cd 00ce 00cf 00cc 00d3 00d4 f8ff 00d2 00da 00db 00d9 0131 02c6 02dc 00af 02d8 02d9 02da 00b8 02dd 02db 02c7 charset CS_MAC_CROATIAN_OLD 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 00c4 00c5 00c7 00c9 00d1 00d6 00dc 00e1 00e0 00e2 00e4 00e3 00e5 00e7 00e9 00e8 00ea 00eb 00ed 00ec 00ee 00ef 00f1 00f3 00f2 00f4 00f6 00f5 00fa 00f9 00fb 00fc 2020 00b0 00a2 00a3 00a7 2022 00b6 00df 00ae 0160 2122 00b4 00a8 2260 017d 00d8 221e 00b1 2264 2265 2206 00b5 2202 2211 220f 0161 222b 00aa 00ba 03a9 017e 00f8 00bf 00a1 00ac 221a 0192 2248 0106 00ab 010c 2026 00a0 00c0 00c3 00d5 0152 0153 0110 2014 201c 201d 2018 2019 00f7 25ca f8ff 00a9 2044 00a4 2039 203a 00c6 00bb 2013 00b7 201a 201e 2030 00c2 0107 00c1 010d 00c8 00cd 00ce 00cf 00cc 00d3 00d4 0111 00d2 00da 00db 00d9 0131 02c6 02dc 00af 03c0 00cb 02da 00b8 00ca 00e6 02c7 charset CS_MAC_ICELAND_OLD 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 00c4 00c5 00c7 00c9 00d1 00d6 00dc 00e1 00e0 00e2 00e4 00e3 00e5 00e7 00e9 00e8 00ea 00eb 00ed 00ec 00ee 00ef 00f1 00f3 00f2 00f4 00f6 00f5 00fa 00f9 00fb 00fc 00dd 00b0 00a2 00a3 00a7 2022 00b6 00df 00ae 00a9 2122 00b4 00a8 2260 00c6 00d8 221e 00b1 2264 2265 00a5 00b5 2202 2211 220f 03c0 222b 00aa 00ba 03a9 00e6 00f8 00bf 00a1 00ac 221a 0192 2248 2206 00ab 00bb 2026 00a0 00c0 00c3 00d5 0152 0153 2013 2014 201c 201d 2018 2019 00f7 25ca 00ff 0178 2044 00a4 00d0 00f0 00de 00fe 00fd 00b7 201a 201e 2030 00c2 00ca 00c1 00cb 00c8 00cd 00ce 00cf 00cc 00d3 00d4 f8ff 00d2 00da 00db 00d9 0131 02c6 02dc 00af 02d8 02d9 02da 00b8 02dd 02db 02c7 charset CS_MAC_ROMANIAN_OLD 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 00c4 00c5 00c7 00c9 00d1 00d6 00dc 00e1 00e0 00e2 00e4 00e3 00e5 00e7 00e9 00e8 00ea 00eb 00ed 00ec 00ee 00ef 00f1 00f3 00f2 00f4 00f6 00f5 00fa 00f9 00fb 00fc 2020 00b0 00a2 00a3 00a7 2022 00b6 00df 00ae 00a9 2122 00b4 00a8 2260 0102 0218 221e 00b1 2264 2265 00a5 00b5 2202 2211 220f 03c0 222b 00aa 00ba 03a9 0103 0219 00bf 00a1 00ac 221a 0192 2248 2206 00ab 00bb 2026 00a0 00c0 00c3 00d5 0152 0153 2013 2014 201c 201d 2018 2019 00f7 25ca 00ff 0178 2044 00a4 2039 203a 021a 021b 2021 00b7 201a 201e 2030 00c2 00ca 00c1 00cb 00c8 00cd 00ce 00cf 00cc 00d3 00d4 f8ff 00d2 00da 00db 00d9 0131 02c6 02dc 00af 02d8 02d9 02da 00b8 02dd 02db 02c7 Mac OS Greek before Mac OS 9.2.2 (SOFT HYPHEN instead of EURO SIGN, and undefined instead of SOFT HYPHEN). charset CS_MAC_GREEK_OLD 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 00c4 00b9 00b2 00c9 00b3 00d6 00dc 0385 00e0 00e2 00e4 0384 00a8 00e7 00e9 00e8 00ea 00eb 00a3 2122 00ee 00ef 2022 00bd 2030 00f4 00f6 00a6 00ad 00f9 00fb 00fc 2020 0393 0394 0398 039b 039e 03a0 00df 00ae 00a9 03a3 03aa 00a7 2260 00b0 00b7 0391 00b1 2264 2265 00a5 0392 0395 0396 0397 0399 039a 039c 03a6 03ab 03a8 03a9 03ac 039d 00ac 039f 03a1 2248 03a4 00ab 00bb 2026 00a0 03a5 03a7 0386 0388 0153 2013 2015 201c 201d 2018 2019 00f7 0389 038a 038c 038e 03ad 03ae 03af 03cc 038f 03cd 03b1 03b2 03c8 03b4 03b5 03c6 03b3 03b7 03b9 03be 03ba 03bb 03bc 03bd 03bf 03c0 03ce 03c1 03c3 03c4 03b8 03c9 03c2 03c7 03c5 03b6 03ca 03cb 0390 03b0 XXXX Mac OS Cyrillic before Mac OS 9.0 (CENT SIGN instead of CYRILLIC CAPITAL LETTER GHE WITH UPTURN, PARTIAL DIFFERENTIAL instead of CYRILLIC SMALL LETTER GHE WITH UPTURN, CURRENCY SIGN instead of EURO SIGN): charset CS_MAC_CYRILLIC_OLD 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 0410 0411 0412 0413 0414 0415 0416 0417 0418 0419 041a 041b 041c 041d 041e 041f 0420 0421 0422 0423 0424 0425 0426 0427 0428 0429 042a 042b 042c 042d 042e 042f 2020 00b0 00a2 00a3 00a7 2022 00b6 0406 00ae 00a9 2122 0402 0452 2260 0403 0453 221e 00b1 2264 2265 0456 00b5 2022 0408 0404 0454 0407 0457 0409 0459 040a 045a 0458 0405 00ac 221a 0192 2248 2206 00ab 00bb 2026 00a0 040b 045b 040c 045c 0455 2013 2014 201c 201d 2018 2019 00f7 201e 040e 045e 040f 045f 2116 0401 0451 044f 0430 0431 0432 0433 0434 0435 0436 0437 0438 0439 043a 043b 043c 043d 043e 043f 0440 0441 0442 0443 0444 0445 0446 0447 0448 0449 044a 044b 044c 044d 044e 00a4 Mac OS Ukrainian (now Cyrillic) before Mac OS 9.0 (CURRENCY SIGN instead of EURO SIGN): charset CS_MAC_UKRAINE 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 0410 0411 0412 0413 0414 0415 0416 0417 0418 0419 041a 041b 041c 041d 041e 041f 0420 0421 0422 0423 0424 0425 0426 0427 0428 0429 042a 042b 042c 042d 042e 042f 2020 00b0 0490 00a3 00a7 2022 00b6 0406 00ae 00a9 2122 0402 0452 2260 0403 0453 221e 00b1 2264 2265 0456 00b5 0491 0408 0404 0454 0407 0457 0409 0459 040a 045a 0458 0405 00ac 221a 0192 2248 2206 00ab 00bb 2026 00a0 040b 045b 040c 045c 0455 2013 2014 201c 201d 2018 2019 00f7 201e 040e 045e 040f 045f 2116 0401 0451 044f 0430 0431 0432 0433 0434 0435 0436 0437 0438 0439 043a 043b 043c 043d 043e 043f 0440 0441 0442 0443 0444 0445 0446 0447 0448 0449 044a 044b 044c 044d 044e 00a4 Mac OS VT100 character set, as used by the "VT100" font. Basically Mac OS Roman hacked about to give it an almost-Latin1 repertoire and most of the VT100 line-drawing set too. Point CA is the backward question-mark used for silo overflows. This table was derived by pasting the relevant part of 'utom' 140 from the "Western Language Encodings" file shipped with TEC 1.5 and then manually fixing up the scan line characters to use the Unicode 3.2 HORIZONTAL SCAN LINE characters rather than UPPER ONE EIGHTH BLOCK and LOWER ONE EIGHTH BLOCK with transcoding hints. charset CS_MAC_VT100 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 240a 240b 240c 240d 240e 240f 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 241a 241b 241c 241d 241e 241f 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 2421 00c4 00c5 00c7 00c9 00d1 00d6 00dc 00e1 00e0 00e2 00e4 00e3 00e5 00e7 00e9 00e8 00ea 00eb 00ed 00ec 00ee 00ef 00f1 00f3 00f2 00f4 00f6 00f5 00fa 00f9 00fb 00fc 00dd 00b0 00a2 00a3 00a7 00b8 00b6 00df 00ae 00a9 2122 00b4 00a8 2260 00c6 00d8 00d7 00b1 2264 2265 00a5 00b5 00b9 00b2 00b3 03c0 00a6 00aa 00ba 2592 00e6 00f8 00bf 00a1 00ac 00bd 0192 00bc 00be 00ab 00bb 2026 XXXX 00c0 00c3 00d5 0152 0153 2013 2014 2518 2510 250c 2514 00f7 2022 00ff 0178 253c 20ac 00d0 00f0 00fe 00de 00fd 00b7 23ba 23bb 2500 00c2 00ca 00c1 00cb 00c8 00cd 00ce 00cf 00cc 00d3 00d4 XXXX 00d2 00da 00db 00d9 23bc 23bd 251c 2524 2534 252c 2502 XXXX XXXX XXXX XXXX As with so many others, before Mac OS 8.5 this font had CURRENCY SIGN rather than EURO SIGN. charset CS_MAC_VT100_OLD 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 240a 240b 240c 240d 240e 240f 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 241a 241b 241c 241d 241e 241f 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 2421 00c4 00c5 00c7 00c9 00d1 00d6 00dc 00e1 00e0 00e2 00e4 00e3 00e5 00e7 00e9 00e8 00ea 00eb 00ed 00ec 00ee 00ef 00f1 00f3 00f2 00f4 00f6 00f5 00fa 00f9 00fb 00fc 00dd 00b0 00a2 00a3 00a7 00b8 00b6 00df 00ae 00a9 2122 00b4 00a8 2260 00c6 00d8 00d7 00b1 2264 2265 00a5 00b5 00b9 00b2 00b3 03c0 00a6 00aa 00ba 2592 00e6 00f8 00bf 00a1 00ac 00bd 0192 00bc 00be 00ab 00bb 2026 XXXX 00c0 00c3 00d5 0152 0153 2013 2014 2518 2510 250c 2514 00f7 2022 00ff 0178 253c 00a4 00d0 00f0 00fe 00de 00fd 00b7 23ba 23bb 2500 00c2 00ca 00c1 00cb 00c8 00cd 00ce 00cf 00cc 00d3 00d4 XXXX 00d2 00da 00db 00d9 23bc 23bd 251c 2524 2534 252c 2502 XXXX XXXX XXXX XXXX Roman Czyborra's web site (http://czyborra.com/) has a variety of other useful mapping tables, in a slightly different format (and gzipped). Here's a shell/Perl function to generate an SBCS table from a Czyborra mapping table: gensbcs_c() { wget -q -O - "$1" | gzip -d | \ perl -ne '/^=(.*)\s+U\+(.*)\s+/ and $a[hex $1]=sprintf "%04x", hex $2;' \ -e 'BEGIN{for($i=0;$i<256;$i++){$a[$i]="XXXX";' \ -e 'if ($i < 32 or ($i >=127 and $i < 160)) {$a[$i]=sprintf "%04x", $i}}}' \ -e 'END{for($i=0;$i<256;$i++){printf"%s%s",$a[$i],$i%16==15?"\n":" "}}' } So here we have some character sets generated from Czyborra mapping tables: VISCII, HP-Roman8, and the DEC Multinational Character Set. { echo charset CS_VISCII; gensbcs_c http://czyborra.com/charsets/viscii.txt.gz; echo; echo charset CS_HP_ROMAN8; gensbcs_c http://czyborra.com/charsets/hp-roman8.txt.gz; echo; echo charset CS_DEC_MCS; gensbcs_c http://czyborra.com/charsets/dec-mcs.txt.gz; echo; } charset CS_VISCII 0000 0001 1eb2 0003 0004 1eb4 1eaa 0007 0008 0009 000a 000b 000c 000d 000e 000f 0010 0011 0012 0013 1ef6 0015 0016 0017 0018 1ef8 001a 001b 001c 001d 1ef4 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 1ea0 1eae 1eb0 1eb6 1ea4 1ea6 1ea8 1eac 1ebc 1eb8 1ebe 1ec0 1ec2 1ec4 1ec6 1ed0 1ed2 1ed4 1ed6 1ed8 1ee2 1eda 1edc 1ede 1eca 1ece 1ecc 1ec8 1ee6 0168 1ee4 1ef2 00d5 1eaf 1eb1 1eb7 1ea5 1ea7 1ea8 1ead 1ebd 1eb9 1ebf 1ec1 1ec3 1ec5 1ec7 1ed1 1ed3 1ed5 1ed7 1ee0 01a0 1ed9 1edd 1edf 1ecb 1ef0 1ee8 1eea 1eec 01a1 1edb 01af 00c0 00c1 00c2 00c3 1ea2 0102 1eb3 1eb5 00c8 00c9 00ca 1eba 00cc 00cd 0128 1ef3 0110 1ee9 00d2 00d3 00d4 1ea1 1ef7 1eeb 1eed 00d9 00da 1ef9 1ef5 00dd 1ee1 01b0 00e0 00e1 00e2 00e3 1ea3 0103 1eef 1eab 00e8 00e9 00ea 1ebb 00ec 00ed 0129 1ec9 0111 1ef1 00f2 00f3 00f4 00f5 1ecf 1ecd 1ee5 00f9 00fa 0169 1ee7 00fd 1ee3 1eee charset CS_HP_ROMAN8 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 0080 0081 0082 0083 0084 0085 0086 0087 0088 0089 008a 008b 008c 008d 008e 008f 0090 0091 0092 0093 0094 0095 0096 0097 0098 0099 009a 009b 009c 009d 009e 009f 00a0 00c0 00c2 00c8 00ca 00cb 00ce 00cf 00b4 02cb 02c6 00a8 02dc 00d9 00db 20a4 00af 00dd 00fd 00b0 00c7 00e7 00d1 00f1 00a1 00bf 00a4 00a3 00a5 00a7 0192 00a2 00e2 00ea 00f4 00fb 00e1 00e9 00f3 00fa 00e0 00e8 00f2 00f9 00e4 00eb 00f6 00fc 00c5 00ee 00d8 00c6 00e5 00ed 00f8 00e6 00c4 00ec 00d6 00dc 00c9 00ef 00df 00d4 00c1 00c3 00e3 00d0 00f0 00cd 00cc 00d3 00d2 00d5 00f5 0160 0161 00da 0178 00ff 00de 00fe 00b7 00b5 00b6 00be 2014 00bc 00bd 00aa 00ba 00ab 25a0 00bb 00b1 XXXX charset CS_DEC_MCS 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 0080 0081 0082 0083 0084 0085 0086 0087 0088 0089 008a 008b 008c 008d 008e 008f 0090 0091 0092 0093 0094 0095 0096 0097 0098 0099 009a 009b 009c 009d 009e 009f XXXX 00a1 00a2 00a3 XXXX 00a5 XXXX 00a7 00a4 00a9 00aa 00ab XXXX XXXX XXXX XXXX 00b0 00b1 00b2 00b3 XXXX 00b5 00b6 00b7 XXXX 00b9 00ba 00bb 00bc 00bd XXXX 00bf 00c0 00c1 00c2 00c3 00c4 00c5 00c6 00c7 00c8 00c9 00ca 00cb 00cc 00cd 00ce 00cf XXXX 00d1 00d2 00d3 00d4 00d5 00d6 0152 00d8 00d9 00da 00db 00dc 0178 XXXX 00df 00e0 00e1 00e2 00e3 00e4 00e5 00e6 00e7 00e8 00e9 00ea 00eb 00ec 00ed 00ee 00ef XXXX 00f1 00f2 00f3 00f4 00f5 00f6 0153 00f8 00f9 00fa 00fb 00fc 00ff XXXX XXXX putty-0.76/charset/sbcsgen.pl0000644000175000017500000000577314072266307013211 00000000000000#!/usr/bin/env perl -w # This script generates sbcsdat.c (the data for all the SBCSes) from its # source form sbcs.dat. $infile = "sbcs.dat"; $outfile = "sbcsdat.c"; open FOO, $infile; open BAR, ">$outfile"; select BAR; print "/*\n"; print " * sbcsdat.c - data definitions for single-byte character sets.\n"; print " *\n"; print " * Generated by sbcsgen.pl from sbcs.dat.\n"; print " * You should edit those files rather than editing this one.\n"; print " */\n"; print "\n"; print "#ifndef ENUM_CHARSETS\n"; print "\n"; print "#include \"charset.h\"\n"; print "#include \"internal.h\"\n"; print "\n"; my $charsetname = undef; my @vals = (); my @charsetnames = (); my @sortpriority = (); while () { chomp; if (/^charset (.*)$/) { $charsetname = $1; @vals = (); @sortpriority = map { 0 } 0..255; } elsif (/^sortpriority ([^-]*)-([^-]*) (.*)$/) { for ($i = hex $1; $i <= hex $2; $i++) { $sortpriority[$i] += $3; } } elsif (/^[0-9a-fA-FX]/) { push @vals, map { $_ eq "XXXX" ? -1 : hex $_ } split / +/, $_; if (scalar @vals > 256) { die "$infile:$.: charset $charsetname has more than 256 values\n"; } elsif (scalar @vals == 256) { &outcharset($charsetname, \@vals, \@sortpriority); push @charsetnames, $charsetname; $charsetname = undef; @vals = (); @sortpriority = map { 0 } 0..255; } } } print "#else /* ENUM_CHARSETS */\n"; print "\n"; foreach $i (@charsetnames) { print "ENUM_CHARSET($i)\n"; } print "\n"; print "#endif /* ENUM_CHARSETS */\n"; sub outcharset($$$) { my ($name, $vals, $sortpriority) = @_; my ($prefix, $i, @sorted); print "static const sbcs_data data_$name = {\n"; print " {\n"; $prefix = " "; @sorted = (); for ($i = 0; $i < 256; $i++) { if ($vals->[$i] < 0) { printf "%sERROR ", $prefix; } else { printf "%s0x%04x", $prefix, $vals->[$i]; die "ooh? $i\n" unless defined $sortpriority->[$i]; push @sorted, [$i, $vals->[$i], 0+$sortpriority->[$i]]; } if ($i % 8 == 7) { $prefix = ",\n "; } else { $prefix = ", "; } } print "\n },\n {\n"; @sorted = sort { ($a->[1] == $b->[1] ? $b->[2] <=> $a->[2] : $a->[1] <=> $b->[1]) || $a->[0] <=> $b->[0] } @sorted; $prefix = " "; $uval = -1; for ($i = $j = 0; $i < scalar @sorted; $i++) { next if ($uval == $sorted[$i]->[1]); # low-priority alternative $uval = $sorted[$i]->[1]; printf "%s0x%02x", $prefix, $sorted[$i]->[0]; if ($j % 8 == 7) { $prefix = ",\n "; } else { $prefix = ", "; } $j++; } printf "\n },\n %d\n", $j; print "};\n"; print "const charset_spec charset_$name = {\n" . " $name, read_sbcs, write_sbcs, &data_$name\n};\n\n"; } putty-0.76/charset/slookup.c0000644000175000017500000000103514072266307013053 00000000000000/* * slookup.c - static lookup of character sets. */ #include "charset.h" #include "internal.h" #define ENUM_CHARSET(x) extern charset_spec const charset_##x; #include "enum.c" #undef ENUM_CHARSET static charset_spec const *const cs_table[] = { #define ENUM_CHARSET(x) &charset_##x, #include "enum.c" #undef ENUM_CHARSET }; charset_spec const *charset_find_spec(int charset) { int i; for (i = 0; i < (int)lenof(cs_table); i++) if (cs_table[i]->charset == charset) return cs_table[i]; return NULL; } putty-0.76/charset/toucs.c0000644000175000017500000000434214072266307012520 00000000000000/* * toucs.c - convert charsets to Unicode. */ #include "charset.h" #include "internal.h" struct unicode_emit_param { wchar_t *output; int outlen; const wchar_t *errstr; int errlen; int stopped; }; static void unicode_emit(void *ctx, long int output) { struct unicode_emit_param *param = (struct unicode_emit_param *)ctx; wchar_t outval; wchar_t const *p; int outlen; if (output == ERROR) { if (param->errstr) { p = param->errstr; outlen = param->errlen; } else { outval = 0xFFFD; /* U+FFFD REPLACEMENT CHARACTER */ p = &outval; outlen = 1; } } else { outval = output; p = &outval; outlen = 1; } if (param->outlen >= outlen) { while (outlen > 0) { *param->output++ = *p++; param->outlen--; 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; param.output = output; param.outlen = outlen; param.errstr = errstr; param.errlen = errlen; param.stopped = 0; if (!state) { localstate.s0 = 0; } else { localstate = *state; /* structure copy */ } while (*inlen > 0) { int lenbefore = param.output - output; spec->read(spec, (unsigned char)**input, &localstate, unicode_emit, ¶m); if (param.stopped) { /* * The emit function has _tried_ to output some * characters, but ran up against the end of the * buffer. Leave immediately, and return what happened * _before_ attempting to process this character. */ return lenbefore; } if (state) *state = localstate; /* structure copy */ (*input)++; (*inlen)--; } return param.output - output; } putty-0.76/charset/utf8.c0000644000175000017500000011103414072266307012246 00000000000000/* * utf8.c - routines to handle UTF-8. */ #ifndef ENUM_CHARSETS #include "charset.h" #include "internal.h" /* * UTF-8 has no associated data, so `charset' may be ignored. */ static void read_utf8(charset_spec const *charset, long int input_chr, charset_state *state, void (*emit)(void *ctx, long int output), void *emitctx) { UNUSEDARG(charset); /* * For reading UTF-8, the `state' word contains: * * - in bits 29-31, the number of bytes expected to be in the * current multibyte character (which we can tell instantly * from the first byte, of course). * * - in bits 26-28, the number of bytes _seen so far_ in the * current multibyte character. * * - in the remainder of the word, the current value of the * character, which is shifted upwards by 6 bits to * accommodate each new byte. * * As required, the state is zero when we are not in the middle * of a multibyte character at all. * * For example, when reading E9 8D 8B, starting at state=0: * * - after E9, the state is 0x64000009 * - after 8D, the state is 0x6800024d * - after 8B, the state conceptually becomes 0x6c00934b, at * which point we notice we've got as many characters as we * were expecting, output U+934B, and reset the state to * zero. * * Note that the maximum number of bits we might need to store * in the character value field is 25 (U+7FFFFFFF contains 31 * bits, but we will never actually store its full value * because when we receive the last 6 bits in the final * continuation byte we will output it and revert the state to * zero). Hence the character value field never collides with * the byte counts. */ if (input_chr < 0x80) { /* * Single-byte character. If the state is nonzero before * coming here, output an error for an incomplete sequence. * Then output the character. */ if (state->s0 != 0) { emit(emitctx, ERROR); state->s0 = 0; } emit(emitctx, input_chr); } else if (input_chr == 0xFE || input_chr == 0xFF) { /* * FE and FF bytes should _never_ occur in UTF-8. They are * automatic errors; if the state was nonzero to start * with, output a further error for an incomplete sequence. */ if (state->s0 != 0) { emit(emitctx, ERROR); state->s0 = 0; } emit(emitctx, ERROR); } else if (input_chr >= 0x80 && input_chr < 0xC0) { /* * Continuation byte. Output an error for an unexpected * continuation byte, if the state is zero. */ if (state->s0 == 0) { emit(emitctx, ERROR); } else { unsigned long charval; unsigned long topstuff; int bytes; /* * Otherwise, accumulate more of the character value. */ charval = state->s0 & 0x03ffffffL; charval = (charval << 6) | (input_chr & 0x3F); /* * Check the byte counts; if we have not reached the * end of the character, update the state and return. */ topstuff = state->s0 & 0xfc000000L; topstuff += 0x04000000L; /* add one to the byte count */ if (((topstuff << 3) ^ topstuff) & 0xe0000000L) { state->s0 = topstuff | charval; return; } /* * Now we know we've reached the end of the character. * `charval' is the Unicode value. We should check for * various invalid things, and then either output * charval or an error. In all cases we reset the state * to zero. */ bytes = topstuff >> 29; state->s0 = 0; if (charval >= 0xD800 && charval < 0xE000) { /* * Surrogates (0xD800-0xDFFF) may never be encoded * in UTF-8. A surrogate pair in Unicode should * have been encoded as a single UTF-8 character * occupying more than three bytes. */ emit(emitctx, ERROR); } else if (charval == 0xFFFE || charval == 0xFFFF) { /* * U+FFFE and U+FFFF are invalid Unicode characters * and may never be encoded in UTF-8. (This is one * reason why U+FFFF is our way of signalling an * error to our `emit' function :-) */ emit(emitctx, ERROR); } else if ((charval <= 0x7FL /* && bytes > 1 */) || (charval <= 0x7FFL && bytes > 2) || (charval <= 0xFFFFL && bytes > 3) || (charval <= 0x1FFFFFL && bytes > 4) || (charval <= 0x3FFFFFFL && bytes > 5)) { /* * Overlong sequences are not to be tolerated, * under any circumstances. */ emit(emitctx, ERROR); } else { /* * Oh, all right. We'll let this one off. */ emit(emitctx, charval); } } } else { /* * Lead byte. First output an error for an incomplete * sequence, if the state is nonzero. */ if (state->s0 != 0) emit(emitctx, ERROR); /* * Now deal with the lead byte: work out the number of * bytes we expect to see in this character, and extract * the initial bits of it too. */ if (input_chr >= 0xC0 && input_chr < 0xE0) { state->s0 = 0x44000000L | (input_chr & 0x1F); } else if (input_chr >= 0xE0 && input_chr < 0xF0) { state->s0 = 0x64000000L | (input_chr & 0x0F); } else if (input_chr >= 0xF0 && input_chr < 0xF8) { state->s0 = 0x84000000L | (input_chr & 0x07); } else if (input_chr >= 0xF8 && input_chr < 0xFC) { state->s0 = 0xa4000000L | (input_chr & 0x03); } else if (input_chr >= 0xFC && input_chr < 0xFE) { state->s0 = 0xc4000000L | (input_chr & 0x01); } } } /* * UTF-8 is a stateless multi-byte encoding (in the sense that just * after any character has been completed, the state is always the * same); hence when writing it, there is no need to use the * charset_state. */ static void write_utf8(charset_spec const *charset, long int input_chr, charset_state *state, void (*emit)(void *ctx, long int output), void *emitctx) { UNUSEDARG(charset); UNUSEDARG(state); /* * Refuse to output any illegal code points. */ if (input_chr == 0xFFFE || input_chr == 0xFFFF || (input_chr >= 0xD800 && input_chr < 0xE000)) { emit(emitctx, ERROR); } else if (input_chr < 0x80) { /* one-byte character */ emit(emitctx, input_chr); } else if (input_chr < 0x800) { /* two-byte character */ emit(emitctx, 0xC0 | (0x1F & (input_chr >> 6))); emit(emitctx, 0x80 | (0x3F & (input_chr ))); } else if (input_chr < 0x10000) { /* three-byte character */ emit(emitctx, 0xE0 | (0x0F & (input_chr >> 12))); emit(emitctx, 0x80 | (0x3F & (input_chr >> 6))); emit(emitctx, 0x80 | (0x3F & (input_chr ))); } else if (input_chr < 0x200000) { /* four-byte character */ emit(emitctx, 0xF0 | (0x07 & (input_chr >> 18))); emit(emitctx, 0x80 | (0x3F & (input_chr >> 12))); emit(emitctx, 0x80 | (0x3F & (input_chr >> 6))); emit(emitctx, 0x80 | (0x3F & (input_chr ))); } else if (input_chr < 0x4000000) {/* five-byte character */ emit(emitctx, 0xF8 | (0x03 & (input_chr >> 24))); emit(emitctx, 0x80 | (0x3F & (input_chr >> 18))); emit(emitctx, 0x80 | (0x3F & (input_chr >> 12))); emit(emitctx, 0x80 | (0x3F & (input_chr >> 6))); emit(emitctx, 0x80 | (0x3F & (input_chr ))); } else { /* six-byte character */ emit(emitctx, 0xFC | (0x01 & (input_chr >> 30))); emit(emitctx, 0x80 | (0x3F & (input_chr >> 24))); emit(emitctx, 0x80 | (0x3F & (input_chr >> 18))); emit(emitctx, 0x80 | (0x3F & (input_chr >> 12))); emit(emitctx, 0x80 | (0x3F & (input_chr >> 6))); emit(emitctx, 0x80 | (0x3F & (input_chr ))); } } #ifdef TESTMODE #include #include int total_errs = 0; void utf8_emit(void *ctx, long output) { wchar_t **p = (wchar_t **)ctx; *(*p)++ = output; } void utf8_read_test(int line, char *input, int inlen, ...) { va_list ap; wchar_t *p, str[512]; int i; charset_state state; unsigned long l; state.s0 = 0; p = str; for (i = 0; i < inlen; i++) read_utf8(NULL, input[i] & 0xFF, &state, utf8_emit, &p); va_start(ap, inlen); l = 0; for (i = 0; i < p - str; i++) { l = va_arg(ap, long int); if (l == -1) { printf("%d: correct string shorter than output\n", line); total_errs++; break; } if (l != str[i]) { printf("%d: char %d came out as %08x, should be %08x\n", line, i, str[i], (unsigned)l); total_errs++; } } if (l != -1) { l = va_arg(ap, long int); if (l != -1) { printf("%d: correct string longer than output\n", line); total_errs++; } } va_end(ap); } void utf8_write_test(int line, const long *input, int inlen, ...) { va_list ap; wchar_t *p, str[512]; int i; charset_state state; unsigned long l; state.s0 = 0; p = str; for (i = 0; i < inlen; i++) write_utf8(NULL, input[i], &state, utf8_emit, &p); va_start(ap, inlen); l = 0; for (i = 0; i < p - str; i++) { l = va_arg(ap, long int); if (l == -1) { printf("%d: correct string shorter than output\n", line); total_errs++; break; } if (l != str[i]) { printf("%d: char %d came out as %08x, should be %08x\n", line, i, str[i], (unsigned)l); total_errs++; } } if (l != -1) { l = va_arg(ap, long int); if (l != -1) { printf("%d: correct string longer than output\n", line); total_errs++; } } va_end(ap); } /* Macro to concoct the first three parameters of utf8_read_test. */ #define TESTSTR(x) __LINE__, x, lenof(x) int main(void) { printf("read tests beginning\n"); utf8_read_test(TESTSTR("\xCE\xBA\xE1\xBD\xB9\xCF\x83\xCE\xBC\xCE\xB5"), 0x000003BA, /* GREEK SMALL LETTER KAPPA */ 0x00001F79, /* GREEK SMALL LETTER OMICRON WITH OXIA */ 0x000003C3, /* GREEK SMALL LETTER SIGMA */ 0x000003BC, /* GREEK SMALL LETTER MU */ 0x000003B5, /* GREEK SMALL LETTER EPSILON */ 0, -1); utf8_read_test(TESTSTR("\x00"), 0x00000000, /* */ 0, -1); utf8_read_test(TESTSTR("\xC2\x80"), 0x00000080, /* */ 0, -1); utf8_read_test(TESTSTR("\xE0\xA0\x80"), 0x00000800, /* */ 0, -1); utf8_read_test(TESTSTR("\xF0\x90\x80\x80"), 0x00010000, /* */ 0, -1); utf8_read_test(TESTSTR("\xF8\x88\x80\x80\x80"), 0x00200000, /* */ 0, -1); utf8_read_test(TESTSTR("\xFC\x84\x80\x80\x80\x80"), 0x04000000, /* */ 0, -1); utf8_read_test(TESTSTR("\x7F"), 0x0000007F, /* */ 0, -1); utf8_read_test(TESTSTR("\xDF\xBF"), 0x000007FF, /* */ 0, -1); utf8_read_test(TESTSTR("\xEF\xBF\xBD"), 0x0000FFFD, /* REPLACEMENT CHARACTER */ 0, -1); utf8_read_test(TESTSTR("\xEF\xBF\xBF"), ERROR, /* (invalid char) */ 0, -1); utf8_read_test(TESTSTR("\xF7\xBF\xBF\xBF"), 0x001FFFFF, /* */ 0, -1); utf8_read_test(TESTSTR("\xFB\xBF\xBF\xBF\xBF"), 0x03FFFFFF, /* */ 0, -1); utf8_read_test(TESTSTR("\xFD\xBF\xBF\xBF\xBF\xBF"), 0x7FFFFFFF, /* */ 0, -1); utf8_read_test(TESTSTR("\xED\x9F\xBF"), 0x0000D7FF, /* */ 0, -1); utf8_read_test(TESTSTR("\xEE\x80\x80"), 0x0000E000, /* */ 0, -1); utf8_read_test(TESTSTR("\xEF\xBF\xBD"), 0x0000FFFD, /* REPLACEMENT CHARACTER */ 0, -1); utf8_read_test(TESTSTR("\xF4\x8F\xBF\xBF"), 0x0010FFFF, /* */ 0, -1); utf8_read_test(TESTSTR("\xF4\x90\x80\x80"), 0x00110000, /* */ 0, -1); utf8_read_test(TESTSTR("\x80"), ERROR, /* (unexpected continuation byte) */ 0, -1); utf8_read_test(TESTSTR("\xBF"), ERROR, /* (unexpected continuation byte) */ 0, -1); utf8_read_test(TESTSTR("\x80\xBF"), ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ 0, -1); utf8_read_test(TESTSTR("\x80\xBF\x80"), ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ 0, -1); utf8_read_test(TESTSTR("\x80\xBF\x80\xBF"), ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ 0, -1); utf8_read_test(TESTSTR("\x80\xBF\x80\xBF\x80"), ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ 0, -1); utf8_read_test(TESTSTR("\x80\xBF\x80\xBF\x80\xBF"), ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ 0, -1); utf8_read_test(TESTSTR("\x80\xBF\x80\xBF\x80\xBF\x80"), ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ 0, -1); utf8_read_test(TESTSTR("\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF"), ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ ERROR, /* (unexpected continuation byte) */ 0, -1); utf8_read_test(TESTSTR("\xC0\x20\xC1\x20\xC2\x20\xC3\x20\xC4\x20\xC5\x20\xC6\x20\xC7\x20"), ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ 0, -1); utf8_read_test(TESTSTR("\xE0\x20\xE1\x20\xE2\x20\xE3\x20\xE4\x20\xE5\x20\xE6\x20\xE7\x20\xE8\x20\xE9\x20\xEA\x20\xEB\x20\xEC\x20\xED\x20\xEE\x20\xEF\x20"), ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ 0, -1); utf8_read_test(TESTSTR("\xF0\x20\xF1\x20\xF2\x20\xF3\x20\xF4\x20\xF5\x20\xF6\x20\xF7\x20"), ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ 0, -1); utf8_read_test(TESTSTR("\xF8\x20\xF9\x20\xFA\x20\xFB\x20"), ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ 0, -1); utf8_read_test(TESTSTR("\xFC\x20\xFD\x20"), ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ ERROR, /* (incomplete sequence) */ 0x00000020, /* SPACE */ 0, -1); utf8_read_test(TESTSTR("\xC0"), ERROR, /* (incomplete sequence) */ 0, -1); utf8_read_test(TESTSTR("\xE0\x80"), ERROR, /* (incomplete sequence) */ 0, -1); utf8_read_test(TESTSTR("\xF0\x80\x80"), ERROR, /* (incomplete sequence) */ 0, -1); utf8_read_test(TESTSTR("\xF8\x80\x80\x80"), ERROR, /* (incomplete sequence) */ 0, -1); utf8_read_test(TESTSTR("\xFC\x80\x80\x80\x80"), ERROR, /* (incomplete sequence) */ 0, -1); utf8_read_test(TESTSTR("\xDF"), ERROR, /* (incomplete sequence) */ 0, -1); utf8_read_test(TESTSTR("\xEF\xBF"), ERROR, /* (incomplete sequence) */ 0, -1); utf8_read_test(TESTSTR("\xF7\xBF\xBF"), ERROR, /* (incomplete sequence) */ 0, -1); utf8_read_test(TESTSTR("\xFB\xBF\xBF\xBF"), ERROR, /* (incomplete sequence) */ 0, -1); utf8_read_test(TESTSTR("\xFD\xBF\xBF\xBF\xBF"), ERROR, /* (incomplete sequence) */ 0, -1); utf8_read_test(TESTSTR("\xC0\xE0\x80\xF0\x80\x80\xF8\x80\x80\x80\xFC\x80\x80\x80\x80\xDF\xEF\xBF\xF7\xBF\xBF\xFB\xBF\xBF\xBF\xFD\xBF\xBF\xBF\xBF"), ERROR, /* (incomplete sequence) */ ERROR, /* (incomplete sequence) */ ERROR, /* (incomplete sequence) */ ERROR, /* (incomplete sequence) */ ERROR, /* (incomplete sequence) */ ERROR, /* (incomplete sequence) */ ERROR, /* (incomplete sequence) */ ERROR, /* (incomplete sequence) */ ERROR, /* (incomplete sequence) */ ERROR, /* (incomplete sequence) */ 0, -1); utf8_read_test(TESTSTR("\xFE"), ERROR, /* (invalid UTF-8 byte) */ 0, -1); utf8_read_test(TESTSTR("\xFF"), ERROR, /* (invalid UTF-8 byte) */ 0, -1); utf8_read_test(TESTSTR("\xFE\xFE\xFF\xFF"), ERROR, /* (invalid UTF-8 byte) */ ERROR, /* (invalid UTF-8 byte) */ ERROR, /* (invalid UTF-8 byte) */ ERROR, /* (invalid UTF-8 byte) */ 0, -1); utf8_read_test(TESTSTR("\xC0\xAF"), ERROR, /* SOLIDUS (overlong form of 2F) */ 0, -1); utf8_read_test(TESTSTR("\xE0\x80\xAF"), ERROR, /* SOLIDUS (overlong form of 2F) */ 0, -1); utf8_read_test(TESTSTR("\xF0\x80\x80\xAF"), ERROR, /* SOLIDUS (overlong form of 2F) */ 0, -1); utf8_read_test(TESTSTR("\xF8\x80\x80\x80\xAF"), ERROR, /* SOLIDUS (overlong form of 2F) */ 0, -1); utf8_read_test(TESTSTR("\xFC\x80\x80\x80\x80\xAF"), ERROR, /* SOLIDUS (overlong form of 2F) */ 0, -1); utf8_read_test(TESTSTR("\xC1\xBF"), ERROR, /* (overlong form of 7F) */ 0, -1); utf8_read_test(TESTSTR("\xE0\x9F\xBF"), ERROR, /* (overlong form of DF BF) */ 0, -1); utf8_read_test(TESTSTR("\xF0\x8F\xBF\xBF"), ERROR, /* (overlong form of EF BF BF) (invalid char) */ 0, -1); utf8_read_test(TESTSTR("\xF8\x87\xBF\xBF\xBF"), ERROR, /* (overlong form of F7 BF BF BF) */ 0, -1); utf8_read_test(TESTSTR("\xFC\x83\xBF\xBF\xBF\xBF"), ERROR, /* (overlong form of FB BF BF BF BF) */ 0, -1); utf8_read_test(TESTSTR("\xC0\x80"), ERROR, /* (overlong form of 00) */ 0, -1); utf8_read_test(TESTSTR("\xE0\x80\x80"), ERROR, /* (overlong form of 00) */ 0, -1); utf8_read_test(TESTSTR("\xF0\x80\x80\x80"), ERROR, /* (overlong form of 00) */ 0, -1); utf8_read_test(TESTSTR("\xF8\x80\x80\x80\x80"), ERROR, /* (overlong form of 00) */ 0, -1); utf8_read_test(TESTSTR("\xFC\x80\x80\x80\x80\x80"), ERROR, /* (overlong form of 00) */ 0, -1); utf8_read_test(TESTSTR("\xED\xA0\x80"), ERROR, /* (surrogate) */ 0, -1); utf8_read_test(TESTSTR("\xED\xAD\xBF"), ERROR, /* (surrogate) */ 0, -1); utf8_read_test(TESTSTR("\xED\xAE\x80"), ERROR, /* (surrogate) */ 0, -1); utf8_read_test(TESTSTR("\xED\xAF\xBF"), ERROR, /* (surrogate) */ 0, -1); utf8_read_test(TESTSTR("\xED\xB0\x80"), ERROR, /* (surrogate) */ 0, -1); utf8_read_test(TESTSTR("\xED\xBE\x80"), ERROR, /* (surrogate) */ 0, -1); utf8_read_test(TESTSTR("\xED\xBF\xBF"), ERROR, /* (surrogate) */ 0, -1); utf8_read_test(TESTSTR("\xED\xA0\x80\xED\xB0\x80"), ERROR, /* (surrogate) */ ERROR, /* (surrogate) */ 0, -1); utf8_read_test(TESTSTR("\xED\xA0\x80\xED\xBF\xBF"), ERROR, /* (surrogate) */ ERROR, /* (surrogate) */ 0, -1); utf8_read_test(TESTSTR("\xED\xAD\xBF\xED\xB0\x80"), ERROR, /* (surrogate) */ ERROR, /* (surrogate) */ 0, -1); utf8_read_test(TESTSTR("\xED\xAD\xBF\xED\xBF\xBF"), ERROR, /* (surrogate) */ ERROR, /* (surrogate) */ 0, -1); utf8_read_test(TESTSTR("\xED\xAE\x80\xED\xB0\x80"), ERROR, /* (surrogate) */ ERROR, /* (surrogate) */ 0, -1); utf8_read_test(TESTSTR("\xED\xAE\x80\xED\xBF\xBF"), ERROR, /* (surrogate) */ ERROR, /* (surrogate) */ 0, -1); utf8_read_test(TESTSTR("\xED\xAF\xBF\xED\xB0\x80"), ERROR, /* (surrogate) */ ERROR, /* (surrogate) */ 0, -1); utf8_read_test(TESTSTR("\xED\xAF\xBF\xED\xBF\xBF"), ERROR, /* (surrogate) */ ERROR, /* (surrogate) */ 0, -1); utf8_read_test(TESTSTR("\xEF\xBF\xBE"), ERROR, /* (invalid char) */ 0, -1); utf8_read_test(TESTSTR("\xEF\xBF\xBF"), ERROR, /* (invalid char) */ 0, -1); printf("read tests completed\n"); printf("write tests beginning\n"); { const static long str[] = {0x03BAL, 0x1F79L, 0x03C3L, 0x03BCL, 0x03B5L, 0}; utf8_write_test(TESTSTR(str), 0xCE, 0xBA, 0xE1, 0xBD, 0xB9, 0xCF, 0x83, 0xCE, 0xBC, 0xCE, 0xB5, 0, -1); } { const static long str[] = {0x0000L, 0}; utf8_write_test(TESTSTR(str), 0x00, 0, -1); } { const static long str[] = {0x0080L, 0}; utf8_write_test(TESTSTR(str), 0xC2, 0x80, 0, -1); } { const static long str[] = {0x0800L, 0}; utf8_write_test(TESTSTR(str), 0xE0, 0xA0, 0x80, 0, -1); } { const static long str[] = {0x00010000L, 0}; utf8_write_test(TESTSTR(str), 0xF0, 0x90, 0x80, 0x80, 0, -1); } { const static long str[] = {0x00200000L, 0}; utf8_write_test(TESTSTR(str), 0xF8, 0x88, 0x80, 0x80, 0x80, 0, -1); } { const static long str[] = {0x04000000L, 0}; utf8_write_test(TESTSTR(str), 0xFC, 0x84, 0x80, 0x80, 0x80, 0x80, 0, -1); } { const static long str[] = {0x007FL, 0}; utf8_write_test(TESTSTR(str), 0x7F, 0, -1); } { const static long str[] = {0x07FFL, 0}; utf8_write_test(TESTSTR(str), 0xDF, 0xBF, 0, -1); } { const static long str[] = {0xFFFDL, 0}; utf8_write_test(TESTSTR(str), 0xEF, 0xBF, 0xBD, 0, -1); } { const static long str[] = {0xFFFFL, 0}; utf8_write_test(TESTSTR(str), ERROR, 0, -1); } { const static long str[] = {0x001FFFFFL, 0}; utf8_write_test(TESTSTR(str), 0xF7, 0xBF, 0xBF, 0xBF, 0, -1); } { const static long str[] = {0x03FFFFFFL, 0}; utf8_write_test(TESTSTR(str), 0xFB, 0xBF, 0xBF, 0xBF, 0xBF, 0, -1); } { const static long str[] = {0x7FFFFFFFL, 0}; utf8_write_test(TESTSTR(str), 0xFD, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0, -1); } { const static long str[] = {0xD7FFL, 0}; utf8_write_test(TESTSTR(str), 0xED, 0x9F, 0xBF, 0, -1); } { const static long str[] = {0xD800L, 0}; utf8_write_test(TESTSTR(str), ERROR, 0, -1); } { const static long str[] = {0xD800L, 0xDC00L, 0}; utf8_write_test(TESTSTR(str), ERROR, ERROR, 0, -1); } { const static long str[] = {0xDFFFL, 0}; utf8_write_test(TESTSTR(str), ERROR, 0, -1); } { const static long str[] = {0xE000L, 0}; utf8_write_test(TESTSTR(str), 0xEE, 0x80, 0x80, 0, -1); } printf("write tests completed\n"); printf("total: %d errors\n", total_errs); return (total_errs != 0); } #endif /* TESTMODE */ const charset_spec charset_CS_UTF8 = { CS_UTF8, read_utf8, write_utf8, NULL }; #else /* ENUM_CHARSETS */ ENUM_CHARSET(CS_UTF8) #endif /* ENUM_CHARSETS */ putty-0.76/charset/xenc.c0000644000175000017500000000506414072266307012322 00000000000000/* * xenc.c - translate our internal character set codes to and from * X11 character encoding names. * */ #include #include "charset.h" #include "internal.h" static const struct { const char *name; int charset; } xencs[] = { /* * Officially registered encoding names. This list is derived * from the font encodings section of * * http://ftp.x.org/pub/DOCS/registry * * Where multiple encoding names map to the same encoding id * (such as iso8859-15 and fcd8859-15), the first is considered * canonical and will be returned when translating the id to a * string. */ { "iso8859-1", CS_ISO8859_1 }, { "iso8859-2", CS_ISO8859_2 }, { "iso8859-3", CS_ISO8859_3 }, { "iso8859-4", CS_ISO8859_4 }, { "iso8859-5", CS_ISO8859_5 }, { "iso8859-6", CS_ISO8859_6 }, { "iso8859-7", CS_ISO8859_7 }, { "iso8859-8", CS_ISO8859_8 }, { "iso8859-9", CS_ISO8859_9 }, { "iso8859-10", CS_ISO8859_10 }, { "iso8859-13", CS_ISO8859_13 }, { "iso8859-14", CS_ISO8859_14 }, { "iso8859-15", CS_ISO8859_15 }, { "fcd8859-15", CS_ISO8859_15 }, { "hp-roman8", CS_HP_ROMAN8 }, { "koi8-r", CS_KOI8_R }, /* * 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 }, { "microsoft-cp1256", CS_CP1256 }, { "microsoft-cp1257", CS_CP1257 }, { "microsoft-cp1258", CS_CP1258 }, { "mac-roman", CS_MAC_ROMAN }, { "viscii1.1-1", CS_VISCII }, { "viscii1-1", CS_VISCII }, }; const char *charset_to_xenc(int charset) { int i; for (i = 0; i < (int)lenof(xencs); i++) if (charset == xencs[i].charset) return xencs[i].name; return NULL; /* not found */ } int charset_from_xenc(const char *name) { int i; for (i = 0; i < (int)lenof(xencs); i++) { const char *p, *q; p = name; q = xencs[i].name; while (*p || *q) { if (tolower((unsigned char)*p) != tolower((unsigned char)*q)) break; p++; q++; } if (!*p && !*q) return xencs[i].charset; } return CS_NONE; /* not found */ } putty-0.76/charset/sbcsdat.c0000644000175000017500000065052414072266315013016 00000000000000/* * sbcsdat.c - data definitions for single-byte character sets. * * Generated by sbcsgen.pl from sbcs.dat. * You should edit those files rather than editing this one. */ #ifndef ENUM_CHARSETS #include "charset.h" #include "internal.h" static const sbcs_data data_CS_ISO8859_1 = { { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f, 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, 0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af, 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7, 0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf, 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf, 0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7, 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df, 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, 0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff }, { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff }, 256 }; const charset_spec charset_CS_ISO8859_1 = { CS_ISO8859_1, read_sbcs, write_sbcs, &data_CS_ISO8859_1 }; static const sbcs_data data_CS_ISO8859_2 = { { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f, 0x00a0, 0x0104, 0x02d8, 0x0141, 0x00a4, 0x013d, 0x015a, 0x00a7, 0x00a8, 0x0160, 0x015e, 0x0164, 0x0179, 0x00ad, 0x017d, 0x017b, 0x00b0, 0x0105, 0x02db, 0x0142, 0x00b4, 0x013e, 0x015b, 0x02c7, 0x00b8, 0x0161, 0x015f, 0x0165, 0x017a, 0x02dd, 0x017e, 0x017c, 0x0154, 0x00c1, 0x00c2, 0x0102, 0x00c4, 0x0139, 0x0106, 0x00c7, 0x010c, 0x00c9, 0x0118, 0x00cb, 0x011a, 0x00cd, 0x00ce, 0x010e, 0x0110, 0x0143, 0x0147, 0x00d3, 0x00d4, 0x0150, 0x00d6, 0x00d7, 0x0158, 0x016e, 0x00da, 0x0170, 0x00dc, 0x00dd, 0x0162, 0x00df, 0x0155, 0x00e1, 0x00e2, 0x0103, 0x00e4, 0x013a, 0x0107, 0x00e7, 0x010d, 0x00e9, 0x0119, 0x00eb, 0x011b, 0x00ed, 0x00ee, 0x010f, 0x0111, 0x0144, 0x0148, 0x00f3, 0x00f4, 0x0151, 0x00f6, 0x00f7, 0x0159, 0x016f, 0x00fa, 0x0171, 0x00fc, 0x00fd, 0x0163, 0x02d9 }, { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa4, 0xa7, 0xa8, 0xad, 0xb0, 0xb4, 0xb8, 0xc1, 0xc2, 0xc4, 0xc7, 0xc9, 0xcb, 0xcd, 0xce, 0xd3, 0xd4, 0xd6, 0xd7, 0xda, 0xdc, 0xdd, 0xdf, 0xe1, 0xe2, 0xe4, 0xe7, 0xe9, 0xeb, 0xed, 0xee, 0xf3, 0xf4, 0xf6, 0xf7, 0xfa, 0xfc, 0xfd, 0xc3, 0xe3, 0xa1, 0xb1, 0xc6, 0xe6, 0xc8, 0xe8, 0xcf, 0xef, 0xd0, 0xf0, 0xca, 0xea, 0xcc, 0xec, 0xc5, 0xe5, 0xa5, 0xb5, 0xa3, 0xb3, 0xd1, 0xf1, 0xd2, 0xf2, 0xd5, 0xf5, 0xc0, 0xe0, 0xd8, 0xf8, 0xa6, 0xb6, 0xaa, 0xba, 0xa9, 0xb9, 0xde, 0xfe, 0xab, 0xbb, 0xd9, 0xf9, 0xdb, 0xfb, 0xac, 0xbc, 0xaf, 0xbf, 0xae, 0xbe, 0xb7, 0xa2, 0xff, 0xb2, 0xbd }, 256 }; const charset_spec charset_CS_ISO8859_2 = { CS_ISO8859_2, read_sbcs, write_sbcs, &data_CS_ISO8859_2 }; static const sbcs_data data_CS_ISO8859_3 = { { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f, 0x00a0, 0x0126, 0x02d8, 0x00a3, 0x00a4, ERROR , 0x0124, 0x00a7, 0x00a8, 0x0130, 0x015e, 0x011e, 0x0134, 0x00ad, ERROR , 0x017b, 0x00b0, 0x0127, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x0125, 0x00b7, 0x00b8, 0x0131, 0x015f, 0x011f, 0x0135, 0x00bd, ERROR , 0x017c, 0x00c0, 0x00c1, 0x00c2, ERROR , 0x00c4, 0x010a, 0x0108, 0x00c7, 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf, ERROR , 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x0120, 0x00d6, 0x00d7, 0x011c, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x016c, 0x015c, 0x00df, 0x00e0, 0x00e1, 0x00e2, ERROR , 0x00e4, 0x010b, 0x0109, 0x00e7, 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, ERROR , 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x0121, 0x00f6, 0x00f7, 0x011d, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x016d, 0x015d, 0x02d9 }, { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa3, 0xa4, 0xa7, 0xa8, 0xad, 0xb0, 0xb2, 0xb3, 0xb4, 0xb5, 0xb7, 0xb8, 0xbd, 0xc0, 0xc1, 0xc2, 0xc4, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd1, 0xd2, 0xd3, 0xd4, 0xd6, 0xd7, 0xd9, 0xda, 0xdb, 0xdc, 0xdf, 0xe0, 0xe1, 0xe2, 0xe4, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf1, 0xf2, 0xf3, 0xf4, 0xf6, 0xf7, 0xf9, 0xfa, 0xfb, 0xfc, 0xc6, 0xe6, 0xc5, 0xe5, 0xd8, 0xf8, 0xab, 0xbb, 0xd5, 0xf5, 0xa6, 0xb6, 0xa1, 0xb1, 0xa9, 0xb9, 0xac, 0xbc, 0xde, 0xfe, 0xaa, 0xba, 0xdd, 0xfd, 0xaf, 0xbf, 0xa2, 0xff }, 249 }; const charset_spec charset_CS_ISO8859_3 = { CS_ISO8859_3, read_sbcs, write_sbcs, &data_CS_ISO8859_3 }; static const sbcs_data data_CS_ISO8859_4 = { { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f, 0x00a0, 0x0104, 0x0138, 0x0156, 0x00a4, 0x0128, 0x013b, 0x00a7, 0x00a8, 0x0160, 0x0112, 0x0122, 0x0166, 0x00ad, 0x017d, 0x00af, 0x00b0, 0x0105, 0x02db, 0x0157, 0x00b4, 0x0129, 0x013c, 0x02c7, 0x00b8, 0x0161, 0x0113, 0x0123, 0x0167, 0x014a, 0x017e, 0x014b, 0x0100, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x012e, 0x010c, 0x00c9, 0x0118, 0x00cb, 0x0116, 0x00cd, 0x00ce, 0x012a, 0x0110, 0x0145, 0x014c, 0x0136, 0x00d4, 0x00d5, 0x00d6, 0x00d7, 0x00d8, 0x0172, 0x00da, 0x00db, 0x00dc, 0x0168, 0x016a, 0x00df, 0x0101, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x012f, 0x010d, 0x00e9, 0x0119, 0x00eb, 0x0117, 0x00ed, 0x00ee, 0x012b, 0x0111, 0x0146, 0x014d, 0x0137, 0x00f4, 0x00f5, 0x00f6, 0x00f7, 0x00f8, 0x0173, 0x00fa, 0x00fb, 0x00fc, 0x0169, 0x016b, 0x02d9 }, { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa4, 0xa7, 0xa8, 0xad, 0xaf, 0xb0, 0xb4, 0xb8, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc9, 0xcb, 0xcd, 0xce, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xda, 0xdb, 0xdc, 0xdf, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe9, 0xeb, 0xed, 0xee, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xfa, 0xfb, 0xfc, 0xc0, 0xe0, 0xa1, 0xb1, 0xc8, 0xe8, 0xd0, 0xf0, 0xaa, 0xba, 0xcc, 0xec, 0xca, 0xea, 0xab, 0xbb, 0xa5, 0xb5, 0xcf, 0xef, 0xc7, 0xe7, 0xd3, 0xf3, 0xa2, 0xa6, 0xb6, 0xd1, 0xf1, 0xbd, 0xbf, 0xd2, 0xf2, 0xa3, 0xb3, 0xa9, 0xb9, 0xac, 0xbc, 0xdd, 0xfd, 0xde, 0xfe, 0xd9, 0xf9, 0xae, 0xbe, 0xb7, 0xff, 0xb2 }, 256 }; const charset_spec charset_CS_ISO8859_4 = { CS_ISO8859_4, read_sbcs, write_sbcs, &data_CS_ISO8859_4 }; static const sbcs_data data_CS_ISO8859_5 = { { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f, 0x00a0, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407, 0x0408, 0x0409, 0x040a, 0x040b, 0x040c, 0x00ad, 0x040e, 0x040f, 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, 0x041f, 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042a, 0x042b, 0x042c, 0x042d, 0x042e, 0x042f, 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e, 0x043f, 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044a, 0x044b, 0x044c, 0x044d, 0x044e, 0x044f, 0x2116, 0x0451, 0x0452, 0x0453, 0x0454, 0x0455, 0x0456, 0x0457, 0x0458, 0x0459, 0x045a, 0x045b, 0x045c, 0x00a7, 0x045e, 0x045f }, { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xfd, 0xad, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfe, 0xff, 0xf0 }, 256 }; const charset_spec charset_CS_ISO8859_5 = { CS_ISO8859_5, read_sbcs, write_sbcs, &data_CS_ISO8859_5 }; static const sbcs_data data_CS_ISO8859_6 = { { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f, 0x00a0, ERROR , ERROR , ERROR , 0x00a4, ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , 0x060c, 0x00ad, ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , 0x061b, ERROR , ERROR , ERROR , 0x061f, ERROR , 0x0621, 0x0622, 0x0623, 0x0624, 0x0625, 0x0626, 0x0627, 0x0628, 0x0629, 0x062a, 0x062b, 0x062c, 0x062d, 0x062e, 0x062f, 0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x0636, 0x0637, 0x0638, 0x0639, 0x063a, ERROR , ERROR , ERROR , ERROR , ERROR , 0x0640, 0x0641, 0x0642, 0x0643, 0x0644, 0x0645, 0x0646, 0x0647, 0x0648, 0x0649, 0x064a, 0x064b, 0x064c, 0x064d, 0x064e, 0x064f, 0x0650, 0x0651, 0x0652, ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR }, { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa4, 0xad, 0xac, 0xbb, 0xbf, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2 }, 211 }; const charset_spec charset_CS_ISO8859_6 = { CS_ISO8859_6, read_sbcs, write_sbcs, &data_CS_ISO8859_6 }; static const sbcs_data data_CS_ISO8859_7 = { { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f, 0x00a0, 0x2018, 0x2019, 0x00a3, ERROR , ERROR , 0x00a6, 0x00a7, 0x00a8, 0x00a9, ERROR , 0x00ab, 0x00ac, 0x00ad, ERROR , 0x2015, 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x0384, 0x0385, 0x0386, 0x00b7, 0x0388, 0x0389, 0x038a, 0x00bb, 0x038c, 0x00bd, 0x038e, 0x038f, 0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d, 0x039e, 0x039f, 0x03a0, 0x03a1, ERROR , 0x03a3, 0x03a4, 0x03a5, 0x03a6, 0x03a7, 0x03a8, 0x03a9, 0x03aa, 0x03ab, 0x03ac, 0x03ad, 0x03ae, 0x03af, 0x03b0, 0x03b1, 0x03b2, 0x03b3, 0x03b4, 0x03b5, 0x03b6, 0x03b7, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03be, 0x03bf, 0x03c0, 0x03c1, 0x03c2, 0x03c3, 0x03c4, 0x03c5, 0x03c6, 0x03c7, 0x03c8, 0x03c9, 0x03ca, 0x03cb, 0x03cc, 0x03cd, 0x03ce, ERROR }, { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa3, 0xa6, 0xa7, 0xa8, 0xa9, 0xab, 0xac, 0xad, 0xb0, 0xb1, 0xb2, 0xb3, 0xb7, 0xbb, 0xbd, 0xb4, 0xb5, 0xb6, 0xb8, 0xb9, 0xba, 0xbc, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xaf, 0xa1, 0xa2 }, 250 }; const charset_spec charset_CS_ISO8859_7 = { CS_ISO8859_7, read_sbcs, write_sbcs, &data_CS_ISO8859_7 }; static const sbcs_data data_CS_ISO8859_8 = { { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f, 0x00a0, ERROR , 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, 0x00a8, 0x00a9, 0x00d7, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af, 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7, 0x00b8, 0x00b9, 0x00f7, 0x00bb, 0x00bc, 0x00bd, 0x00be, ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , 0x2017, 0x05d0, 0x05d1, 0x05d2, 0x05d3, 0x05d4, 0x05d5, 0x05d6, 0x05d7, 0x05d8, 0x05d9, 0x05da, 0x05db, 0x05dc, 0x05dd, 0x05de, 0x05df, 0x05e0, 0x05e1, 0x05e2, 0x05e3, 0x05e4, 0x05e5, 0x05e6, 0x05e7, 0x05e8, 0x05e9, 0x05ea, ERROR , ERROR , 0x200e, 0x200f, ERROR }, { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xbb, 0xbc, 0xbd, 0xbe, 0xaa, 0xba, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfd, 0xfe, 0xdf }, 220 }; const charset_spec charset_CS_ISO8859_8 = { CS_ISO8859_8, read_sbcs, write_sbcs, &data_CS_ISO8859_8 }; static const sbcs_data data_CS_ISO8859_9 = { { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f, 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, 0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af, 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7, 0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf, 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf, 0x011e, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7, 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x0130, 0x015e, 0x00df, 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, 0x011f, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x0131, 0x015f, 0x00ff }, { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xff, 0xd0, 0xf0, 0xdd, 0xfd, 0xde, 0xfe }, 256 }; const charset_spec charset_CS_ISO8859_9 = { CS_ISO8859_9, read_sbcs, write_sbcs, &data_CS_ISO8859_9 }; static const sbcs_data data_CS_ISO8859_10 = { { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f, 0x00a0, 0x0104, 0x0112, 0x0122, 0x012a, 0x0128, 0x0136, 0x00a7, 0x013b, 0x0110, 0x0160, 0x0166, 0x017d, 0x00ad, 0x016a, 0x014a, 0x00b0, 0x0105, 0x0113, 0x0123, 0x012b, 0x0129, 0x0137, 0x00b7, 0x013c, 0x0111, 0x0161, 0x0167, 0x017e, 0x2015, 0x016b, 0x014b, 0x0100, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x012e, 0x010c, 0x00c9, 0x0118, 0x00cb, 0x0116, 0x00cd, 0x00ce, 0x00cf, 0x00d0, 0x0145, 0x014c, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x0168, 0x00d8, 0x0172, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df, 0x0101, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x012f, 0x010d, 0x00e9, 0x0119, 0x00eb, 0x0117, 0x00ed, 0x00ee, 0x00ef, 0x00f0, 0x0146, 0x014d, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x0169, 0x00f8, 0x0173, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x0138 }, { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa7, 0xad, 0xb0, 0xb7, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc9, 0xcb, 0xcd, 0xce, 0xcf, 0xd0, 0xd3, 0xd4, 0xd5, 0xd6, 0xd8, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe9, 0xeb, 0xed, 0xee, 0xef, 0xf0, 0xf3, 0xf4, 0xf5, 0xf6, 0xf8, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xc0, 0xe0, 0xa1, 0xb1, 0xc8, 0xe8, 0xa9, 0xb9, 0xa2, 0xb2, 0xcc, 0xec, 0xca, 0xea, 0xa3, 0xb3, 0xa5, 0xb5, 0xa4, 0xb4, 0xc7, 0xe7, 0xa6, 0xb6, 0xff, 0xa8, 0xb8, 0xd1, 0xf1, 0xaf, 0xbf, 0xd2, 0xf2, 0xaa, 0xba, 0xab, 0xbb, 0xd7, 0xf7, 0xae, 0xbe, 0xd9, 0xf9, 0xac, 0xbc, 0xbd }, 256 }; const charset_spec charset_CS_ISO8859_10 = { CS_ISO8859_10, read_sbcs, write_sbcs, &data_CS_ISO8859_10 }; static const sbcs_data data_CS_ISO8859_11 = { { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f, 0x00a0, 0x0e01, 0x0e02, 0x0e03, 0x0e04, 0x0e05, 0x0e06, 0x0e07, 0x0e08, 0x0e09, 0x0e0a, 0x0e0b, 0x0e0c, 0x0e0d, 0x0e0e, 0x0e0f, 0x0e10, 0x0e11, 0x0e12, 0x0e13, 0x0e14, 0x0e15, 0x0e16, 0x0e17, 0x0e18, 0x0e19, 0x0e1a, 0x0e1b, 0x0e1c, 0x0e1d, 0x0e1e, 0x0e1f, 0x0e20, 0x0e21, 0x0e22, 0x0e23, 0x0e24, 0x0e25, 0x0e26, 0x0e27, 0x0e28, 0x0e29, 0x0e2a, 0x0e2b, 0x0e2c, 0x0e2d, 0x0e2e, 0x0e2f, 0x0e30, 0x0e31, 0x0e32, 0x0e33, 0x0e34, 0x0e35, 0x0e36, 0x0e37, 0x0e38, 0x0e39, 0x0e3a, ERROR , ERROR , ERROR , ERROR , 0x0e3f, 0x0e40, 0x0e41, 0x0e42, 0x0e43, 0x0e44, 0x0e45, 0x0e46, 0x0e47, 0x0e48, 0x0e49, 0x0e4a, 0x0e4b, 0x0e4c, 0x0e4d, 0x0e4e, 0x0e4f, 0x0e50, 0x0e51, 0x0e52, 0x0e53, 0x0e54, 0x0e55, 0x0e56, 0x0e57, 0x0e58, 0x0e59, 0x0e5a, 0x0e5b, ERROR , ERROR , ERROR , ERROR }, { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb }, 248 }; const charset_spec charset_CS_ISO8859_11 = { CS_ISO8859_11, read_sbcs, write_sbcs, &data_CS_ISO8859_11 }; static const sbcs_data data_CS_ISO8859_13 = { { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f, 0x00a0, 0x201d, 0x00a2, 0x00a3, 0x00a4, 0x201e, 0x00a6, 0x00a7, 0x00d8, 0x00a9, 0x0156, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00c6, 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x201c, 0x00b5, 0x00b6, 0x00b7, 0x00f8, 0x00b9, 0x0157, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00e6, 0x0104, 0x012e, 0x0100, 0x0106, 0x00c4, 0x00c5, 0x0118, 0x0112, 0x010c, 0x00c9, 0x0179, 0x0116, 0x0122, 0x0136, 0x012a, 0x013b, 0x0160, 0x0143, 0x0145, 0x00d3, 0x014c, 0x00d5, 0x00d6, 0x00d7, 0x0172, 0x0141, 0x015a, 0x016a, 0x00dc, 0x017b, 0x017d, 0x00df, 0x0105, 0x012f, 0x0101, 0x0107, 0x00e4, 0x00e5, 0x0119, 0x0113, 0x010d, 0x00e9, 0x017a, 0x0117, 0x0123, 0x0137, 0x012b, 0x013c, 0x0161, 0x0144, 0x0146, 0x00f3, 0x014d, 0x00f5, 0x00f6, 0x00f7, 0x0173, 0x0142, 0x015b, 0x016b, 0x00fc, 0x017c, 0x017e, 0x2019 }, { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa2, 0xa3, 0xa4, 0xa6, 0xa7, 0xa9, 0xab, 0xac, 0xad, 0xae, 0xb0, 0xb1, 0xb2, 0xb3, 0xb5, 0xb6, 0xb7, 0xb9, 0xbb, 0xbc, 0xbd, 0xbe, 0xc4, 0xc5, 0xaf, 0xc9, 0xd3, 0xd5, 0xd6, 0xd7, 0xa8, 0xdc, 0xdf, 0xe4, 0xe5, 0xbf, 0xe9, 0xf3, 0xf5, 0xf6, 0xf7, 0xb8, 0xfc, 0xc2, 0xe2, 0xc0, 0xe0, 0xc3, 0xe3, 0xc8, 0xe8, 0xc7, 0xe7, 0xcb, 0xeb, 0xc6, 0xe6, 0xcc, 0xec, 0xce, 0xee, 0xc1, 0xe1, 0xcd, 0xed, 0xcf, 0xef, 0xd9, 0xf9, 0xd1, 0xf1, 0xd2, 0xf2, 0xd4, 0xf4, 0xaa, 0xba, 0xda, 0xfa, 0xd0, 0xf0, 0xdb, 0xfb, 0xd8, 0xf8, 0xca, 0xea, 0xdd, 0xfd, 0xde, 0xfe, 0xff, 0xb4, 0xa1, 0xa5 }, 256 }; const charset_spec charset_CS_ISO8859_13 = { CS_ISO8859_13, read_sbcs, write_sbcs, &data_CS_ISO8859_13 }; static const sbcs_data data_CS_ISO8859_14 = { { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f, 0x00a0, 0x1e02, 0x1e03, 0x00a3, 0x010a, 0x010b, 0x1e0a, 0x00a7, 0x1e80, 0x00a9, 0x1e82, 0x1e0b, 0x1ef2, 0x00ad, 0x00ae, 0x0178, 0x1e1e, 0x1e1f, 0x0120, 0x0121, 0x1e40, 0x1e41, 0x00b6, 0x1e56, 0x1e81, 0x1e57, 0x1e83, 0x1e60, 0x1ef3, 0x1e84, 0x1e85, 0x1e61, 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf, 0x0174, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x1e6a, 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x0176, 0x00df, 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, 0x0175, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x1e6b, 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x0177, 0x00ff }, { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa3, 0xa7, 0xa9, 0xad, 0xae, 0xb6, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xff, 0xa4, 0xa5, 0xb2, 0xb3, 0xd0, 0xf0, 0xde, 0xfe, 0xaf, 0xa1, 0xa2, 0xa6, 0xab, 0xb0, 0xb1, 0xb4, 0xb5, 0xb7, 0xb9, 0xbb, 0xbf, 0xd7, 0xf7, 0xa8, 0xb8, 0xaa, 0xba, 0xbd, 0xbe, 0xac, 0xbc }, 256 }; const charset_spec charset_CS_ISO8859_14 = { CS_ISO8859_14, read_sbcs, write_sbcs, &data_CS_ISO8859_14 }; static const sbcs_data data_CS_ISO8859_15 = { { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f, 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x20ac, 0x00a5, 0x0160, 0x00a7, 0x0161, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af, 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x017d, 0x00b5, 0x00b6, 0x00b7, 0x017e, 0x00b9, 0x00ba, 0x00bb, 0x0152, 0x0153, 0x0178, 0x00bf, 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf, 0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7, 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df, 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, 0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff }, { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa5, 0xa7, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb5, 0xb6, 0xb7, 0xb9, 0xba, 0xbb, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, 0xbc, 0xbd, 0xa6, 0xa8, 0xbe, 0xb4, 0xb8, 0xa4 }, 256 }; const charset_spec charset_CS_ISO8859_15 = { CS_ISO8859_15, read_sbcs, write_sbcs, &data_CS_ISO8859_15 }; static const sbcs_data data_CS_ISO8859_16 = { { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f, 0x00a0, 0x0104, 0x0105, 0x0141, 0x20ac, 0x201e, 0x0160, 0x00a7, 0x0161, 0x00a9, 0x0218, 0x00ab, 0x0179, 0x00ad, 0x017a, 0x017b, 0x00b0, 0x00b1, 0x010c, 0x0142, 0x017d, 0x201d, 0x00b6, 0x00b7, 0x017e, 0x010d, 0x0219, 0x00bb, 0x0152, 0x0153, 0x0178, 0x017c, 0x00c0, 0x00c1, 0x00c2, 0x0102, 0x00c4, 0x0106, 0x00c6, 0x00c7, 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf, 0x0110, 0x0143, 0x00d2, 0x00d3, 0x00d4, 0x0150, 0x00d6, 0x015a, 0x0170, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x0118, 0x021a, 0x00df, 0x00e0, 0x00e1, 0x00e2, 0x0103, 0x00e4, 0x0107, 0x00e6, 0x00e7, 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, 0x0111, 0x0144, 0x00f2, 0x00f3, 0x00f4, 0x0151, 0x00f6, 0x015b, 0x0171, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x0119, 0x021b, 0x00ff }, { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa7, 0xa9, 0xab, 0xad, 0xb0, 0xb1, 0xb6, 0xb7, 0xbb, 0xc0, 0xc1, 0xc2, 0xc4, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd2, 0xd3, 0xd4, 0xd6, 0xd9, 0xda, 0xdb, 0xdc, 0xdf, 0xe0, 0xe1, 0xe2, 0xe4, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf2, 0xf3, 0xf4, 0xf6, 0xf9, 0xfa, 0xfb, 0xfc, 0xff, 0xc3, 0xe3, 0xa1, 0xa2, 0xc5, 0xe5, 0xb2, 0xb9, 0xd0, 0xf0, 0xdd, 0xfd, 0xa3, 0xb3, 0xd1, 0xf1, 0xd5, 0xf5, 0xbc, 0xbd, 0xd7, 0xf7, 0xa6, 0xa8, 0xd8, 0xf8, 0xbe, 0xac, 0xae, 0xaf, 0xbf, 0xb4, 0xb8, 0xaa, 0xba, 0xde, 0xfe, 0xb5, 0xa5, 0xa4 }, 256 }; const charset_spec charset_CS_ISO8859_16 = { CS_ISO8859_16, read_sbcs, write_sbcs, &data_CS_ISO8859_16 }; static const sbcs_data data_CS_ISO8859_1_X11 = { { 0x0020, 0x2666, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0, 0x00b1, 0x2424, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c, 0x23ba, 0x23bb, 0x2500, 0x23bc, 0x23bd, 0x251c, 0x2524, 0x2534, 0x252c, 0x2502, 0x2264, 0x2265, 0x03c0, 0x2260, 0x00a3, 0x00b7, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f, 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, 0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af, 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7, 0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf, 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf, 0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7, 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df, 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, 0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff }, { 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, 0x1c, 0x1d, 0x1a, 0x1b, 0x10, 0x11, 0x13, 0x14, 0x03, 0x06, 0x0a, 0x04, 0x05, 0x09, 0x12, 0x19, 0x0d, 0x0c, 0x0e, 0x0b, 0x15, 0x16, 0x18, 0x17, 0x0f, 0x02, 0x01 }, 251 }; const charset_spec charset_CS_ISO8859_1_X11 = { CS_ISO8859_1_X11, read_sbcs, write_sbcs, &data_CS_ISO8859_1_X11 }; static const sbcs_data data_CS_CP437 = { { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7, 0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5, 0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9, 0x00ff, 0x00d6, 0x00dc, 0x00a2, 0x00a3, 0x00a5, 0x20a7, 0x0192, 0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00ba, 0x00bf, 0x2310, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb, 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510, 0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f, 0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567, 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256b, 0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258c, 0x2590, 0x2580, 0x03b1, 0x00df, 0x0393, 0x03c0, 0x03a3, 0x03c3, 0x00b5, 0x03c4, 0x03a6, 0x0398, 0x03a9, 0x03b4, 0x221e, 0x03c6, 0x03b5, 0x2229, 0x2261, 0x00b1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00f7, 0x2248, 0x00b0, 0x2219, 0x00b7, 0x221a, 0x207f, 0x00b2, 0x25a0, 0x00a0 }, { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0xff, 0xad, 0x9b, 0x9c, 0x9d, 0xa6, 0xae, 0xaa, 0xf8, 0xf1, 0xfd, 0xe6, 0xfa, 0xa7, 0xaf, 0xac, 0xab, 0xa8, 0x8e, 0x8f, 0x92, 0x80, 0x90, 0xa5, 0x99, 0x9a, 0xe1, 0x85, 0xa0, 0x83, 0x84, 0x86, 0x91, 0x87, 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b, 0xa4, 0x95, 0xa2, 0x93, 0x94, 0xf6, 0x97, 0xa3, 0x96, 0x81, 0x98, 0x9f, 0xe2, 0xe9, 0xe4, 0xe8, 0xea, 0xe0, 0xeb, 0xee, 0xe3, 0xe5, 0xe7, 0xed, 0xfc, 0x9e, 0xf9, 0xfb, 0xec, 0xef, 0xf7, 0xf0, 0xf3, 0xf2, 0xa9, 0xf4, 0xf5, 0xc4, 0xb3, 0xda, 0xbf, 0xc0, 0xd9, 0xc3, 0xb4, 0xc2, 0xc1, 0xc5, 0xcd, 0xba, 0xd5, 0xd6, 0xc9, 0xb8, 0xb7, 0xbb, 0xd4, 0xd3, 0xc8, 0xbe, 0xbd, 0xbc, 0xc6, 0xc7, 0xcc, 0xb5, 0xb6, 0xb9, 0xd1, 0xd2, 0xcb, 0xcf, 0xd0, 0xca, 0xd8, 0xd7, 0xce, 0xdf, 0xdc, 0xdb, 0xdd, 0xde, 0xb0, 0xb1, 0xb2, 0xfe }, 256 }; const charset_spec charset_CS_CP437 = { CS_CP437, read_sbcs, write_sbcs, &data_CS_CP437 }; static const sbcs_data data_CS_CP850 = { { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0, 0x00e5, 0x00e7, 0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec, 0x00c4, 0x00c5, 0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6, 0x00f2, 0x00fb, 0x00f9, 0x00ff, 0x00d6, 0x00dc, 0x00f8, 0x00a3, 0x00d8, 0x00d7, 0x0192, 0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1, 0x00d1, 0x00aa, 0x00ba, 0x00bf, 0x00ae, 0x00ac, 0x00bd, 0x00bc, 0x00a1, 0x00ab, 0x00bb, 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00c1, 0x00c2, 0x00c0, 0x00a9, 0x2563, 0x2551, 0x2557, 0x255d, 0x00a2, 0x00a5, 0x2510, 0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x00e3, 0x00c3, 0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x00a4, 0x00f0, 0x00d0, 0x00ca, 0x00cb, 0x00c8, 0x0131, 0x00cd, 0x00ce, 0x00cf, 0x2518, 0x250c, 0x2588, 0x2584, 0x00a6, 0x00cc, 0x2580, 0x00d3, 0x00df, 0x00d4, 0x00d2, 0x00f5, 0x00d5, 0x00b5, 0x00fe, 0x00de, 0x00da, 0x00db, 0x00d9, 0x00fd, 0x00dd, 0x00af, 0x00b4, 0x00ad, 0x00b1, 0x2017, 0x00be, 0x00b6, 0x00a7, 0x00f7, 0x00b8, 0x00b0, 0x00a8, 0x00b7, 0x00b9, 0x00b3, 0x00b2, 0x25a0, 0x00a0 }, { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0xff, 0xad, 0xbd, 0x9c, 0xcf, 0xbe, 0xdd, 0xf5, 0xf9, 0xb8, 0xa6, 0xae, 0xaa, 0xf0, 0xa9, 0xee, 0xf8, 0xf1, 0xfd, 0xfc, 0xef, 0xe6, 0xf4, 0xfa, 0xf7, 0xfb, 0xa7, 0xaf, 0xac, 0xab, 0xf3, 0xa8, 0xb7, 0xb5, 0xb6, 0xc7, 0x8e, 0x8f, 0x92, 0x80, 0xd4, 0x90, 0xd2, 0xd3, 0xde, 0xd6, 0xd7, 0xd8, 0xd1, 0xa5, 0xe3, 0xe0, 0xe2, 0xe5, 0x99, 0x9e, 0x9d, 0xeb, 0xe9, 0xea, 0x9a, 0xed, 0xe8, 0xe1, 0x85, 0xa0, 0x83, 0xc6, 0x84, 0x86, 0x91, 0x87, 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b, 0xd0, 0xa4, 0x95, 0xa2, 0x93, 0xe4, 0x94, 0xf6, 0x9b, 0x97, 0xa3, 0x96, 0x81, 0xec, 0xe7, 0x98, 0xd5, 0x9f, 0xf2, 0xc4, 0xb3, 0xda, 0xbf, 0xc0, 0xd9, 0xc3, 0xb4, 0xc2, 0xc1, 0xc5, 0xcd, 0xba, 0xc9, 0xbb, 0xc8, 0xbc, 0xcc, 0xb9, 0xcb, 0xca, 0xce, 0xdf, 0xdc, 0xdb, 0xb0, 0xb1, 0xb2, 0xfe }, 256 }; const charset_spec charset_CS_CP850 = { CS_CP850, read_sbcs, write_sbcs, &data_CS_CP850 }; static const sbcs_data data_CS_CP866 = { { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, 0x041f, 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042a, 0x042b, 0x042c, 0x042d, 0x042e, 0x042f, 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e, 0x043f, 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255d, 0x255c, 0x255b, 0x2510, 0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x255e, 0x255f, 0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x2567, 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256b, 0x256a, 0x2518, 0x250c, 0x2588, 0x2584, 0x258c, 0x2590, 0x2580, 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044a, 0x044b, 0x044c, 0x044d, 0x044e, 0x044f, 0x0401, 0x0451, 0x0404, 0x0454, 0x0407, 0x0457, 0x040e, 0x045e, 0x00b0, 0x2219, 0x00b7, 0x221a, 0x2116, 0x00a4, 0x25a0, 0x00a0 }, { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0xff, 0xfd, 0xf8, 0xfa, 0xf0, 0xf2, 0xf4, 0xf6, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf1, 0xf3, 0xf5, 0xf7, 0xfc, 0xf9, 0xfb, 0xc4, 0xb3, 0xda, 0xbf, 0xc0, 0xd9, 0xc3, 0xb4, 0xc2, 0xc1, 0xc5, 0xcd, 0xba, 0xd5, 0xd6, 0xc9, 0xb8, 0xb7, 0xbb, 0xd4, 0xd3, 0xc8, 0xbe, 0xbd, 0xbc, 0xc6, 0xc7, 0xcc, 0xb5, 0xb6, 0xb9, 0xd1, 0xd2, 0xcb, 0xcf, 0xd0, 0xca, 0xd8, 0xd7, 0xce, 0xdf, 0xdc, 0xdb, 0xdd, 0xde, 0xb0, 0xb1, 0xb2, 0xfe }, 256 }; const charset_spec charset_CS_CP866 = { CS_CP866, read_sbcs, write_sbcs, &data_CS_CP866 }; static const sbcs_data data_CS_CP852 = { { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x016f, 0x0107, 0x00e7, 0x0142, 0x00eb, 0x0150, 0x0151, 0x00ee, 0x0179, 0x00c4, 0x0106, 0x00c9, 0x0139, 0x013a, 0x00f4, 0x00f6, 0x013d, 0x013e, 0x015a, 0x015b, 0x00d6, 0x00dc, 0x0164, 0x0165, 0x0141, 0x00d7, 0x010d, 0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x0104, 0x0105, 0x017d, 0x017e, 0x0118, 0x0119, 0x00ac, 0x017a, 0x010c, 0x015f, 0x00ab, 0x00bb, 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x00c1, 0x00c2, 0x011a, 0x015e, 0x2563, 0x2551, 0x2557, 0x255d, 0x017b, 0x017c, 0x2510, 0x2514, 0x2534, 0x252c, 0x251c, 0x2500, 0x253c, 0x0102, 0x0103, 0x255a, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256c, 0x00a4, 0x0111, 0x0110, 0x010e, 0x00cb, 0x010f, 0x0147, 0x00cd, 0x00ce, 0x011b, 0x2518, 0x250c, 0x2588, 0x2584, 0x0162, 0x016e, 0x2580, 0x00d3, 0x00df, 0x00d4, 0x0143, 0x0144, 0x0148, 0x0160, 0x0161, 0x0154, 0x00da, 0x0155, 0x0170, 0x00fd, 0x00dd, 0x0163, 0x00b4, 0x00ad, 0x02dd, 0x02db, 0x02c7, 0x02d8, 0x00a7, 0x00f7, 0x00b8, 0x00b0, 0x00a8, 0x02d9, 0x0171, 0x0158, 0x0159, 0x25a0, 0x00a0 }, { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0xff, 0xcf, 0xf5, 0xf9, 0xae, 0xaa, 0xf0, 0xf8, 0xef, 0xf7, 0xaf, 0xb5, 0xb6, 0x8e, 0x80, 0x90, 0xd3, 0xd6, 0xd7, 0xe0, 0xe2, 0x99, 0x9e, 0xe9, 0x9a, 0xed, 0xe1, 0xa0, 0x83, 0x84, 0x87, 0x82, 0x89, 0xa1, 0x8c, 0xa2, 0x93, 0x94, 0xf6, 0xa3, 0x81, 0xec, 0xc6, 0xc7, 0xa4, 0xa5, 0x8f, 0x86, 0xac, 0x9f, 0xd2, 0xd4, 0xd1, 0xd0, 0xa8, 0xa9, 0xb7, 0xd8, 0x91, 0x92, 0x95, 0x96, 0x9d, 0x88, 0xe3, 0xe4, 0xd5, 0xe5, 0x8a, 0x8b, 0xe8, 0xea, 0xfc, 0xfd, 0x97, 0x98, 0xb8, 0xad, 0xe6, 0xe7, 0xdd, 0xee, 0x9b, 0x9c, 0xde, 0x85, 0xeb, 0xfb, 0x8d, 0xab, 0xbd, 0xbe, 0xa6, 0xa7, 0xf3, 0xf4, 0xfa, 0xf2, 0xf1, 0xc4, 0xb3, 0xda, 0xbf, 0xc0, 0xd9, 0xc3, 0xb4, 0xc2, 0xc1, 0xc5, 0xcd, 0xba, 0xc9, 0xbb, 0xc8, 0xbc, 0xcc, 0xb9, 0xcb, 0xca, 0xce, 0xdf, 0xdc, 0xdb, 0xb0, 0xb1, 0xb2, 0xfe }, 256 }; const charset_spec charset_CS_CP852 = { CS_CP852, read_sbcs, write_sbcs, &data_CS_CP852 }; static const sbcs_data data_CS_CP1250 = { { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x20ac, ERROR , 0x201a, ERROR , 0x201e, 0x2026, 0x2020, 0x2021, ERROR , 0x2030, 0x0160, 0x2039, 0x015a, 0x0164, 0x017d, 0x0179, ERROR , 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014, ERROR , 0x2122, 0x0161, 0x203a, 0x015b, 0x0165, 0x017e, 0x017a, 0x00a0, 0x02c7, 0x02d8, 0x0141, 0x00a4, 0x0104, 0x00a6, 0x00a7, 0x00a8, 0x00a9, 0x015e, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x017b, 0x00b0, 0x00b1, 0x02db, 0x0142, 0x00b4, 0x00b5, 0x00b6, 0x00b7, 0x00b8, 0x0105, 0x015f, 0x00bb, 0x013d, 0x02dd, 0x013e, 0x017c, 0x0154, 0x00c1, 0x00c2, 0x0102, 0x00c4, 0x0139, 0x0106, 0x00c7, 0x010c, 0x00c9, 0x0118, 0x00cb, 0x011a, 0x00cd, 0x00ce, 0x010e, 0x0110, 0x0143, 0x0147, 0x00d3, 0x00d4, 0x0150, 0x00d6, 0x00d7, 0x0158, 0x016e, 0x00da, 0x0170, 0x00dc, 0x00dd, 0x0162, 0x00df, 0x0155, 0x00e1, 0x00e2, 0x0103, 0x00e4, 0x013a, 0x0107, 0x00e7, 0x010d, 0x00e9, 0x0119, 0x00eb, 0x011b, 0x00ed, 0x00ee, 0x010f, 0x0111, 0x0144, 0x0148, 0x00f3, 0x00f4, 0x0151, 0x00f6, 0x00f7, 0x0159, 0x016f, 0x00fa, 0x0171, 0x00fc, 0x00fd, 0x0163, 0x02d9 }, { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0xa0, 0xa4, 0xa6, 0xa7, 0xa8, 0xa9, 0xab, 0xac, 0xad, 0xae, 0xb0, 0xb1, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xbb, 0xc1, 0xc2, 0xc4, 0xc7, 0xc9, 0xcb, 0xcd, 0xce, 0xd3, 0xd4, 0xd6, 0xd7, 0xda, 0xdc, 0xdd, 0xdf, 0xe1, 0xe2, 0xe4, 0xe7, 0xe9, 0xeb, 0xed, 0xee, 0xf3, 0xf4, 0xf6, 0xf7, 0xfa, 0xfc, 0xfd, 0xc3, 0xe3, 0xa5, 0xb9, 0xc6, 0xe6, 0xc8, 0xe8, 0xcf, 0xef, 0xd0, 0xf0, 0xca, 0xea, 0xcc, 0xec, 0xc5, 0xe5, 0xbc, 0xbe, 0xa3, 0xb3, 0xd1, 0xf1, 0xd2, 0xf2, 0xd5, 0xf5, 0xc0, 0xe0, 0xd8, 0xf8, 0x8c, 0x9c, 0xaa, 0xba, 0x8a, 0x9a, 0xde, 0xfe, 0x8d, 0x9d, 0xd9, 0xf9, 0xdb, 0xfb, 0x8f, 0x9f, 0xaf, 0xbf, 0x8e, 0x9e, 0xa1, 0xa2, 0xff, 0xb2, 0xbd, 0x96, 0x97, 0x91, 0x92, 0x82, 0x93, 0x94, 0x84, 0x86, 0x87, 0x95, 0x85, 0x89, 0x8b, 0x9b, 0x80, 0x99 }, 251 }; const charset_spec charset_CS_CP1250 = { CS_CP1250, read_sbcs, write_sbcs, &data_CS_CP1250 }; static const sbcs_data data_CS_CP1251 = { { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x0402, 0x0403, 0x201a, 0x0453, 0x201e, 0x2026, 0x2020, 0x2021, 0x20ac, 0x2030, 0x0409, 0x2039, 0x040a, 0x040c, 0x040b, 0x040f, 0x0452, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014, ERROR , 0x2122, 0x0459, 0x203a, 0x045a, 0x045c, 0x045b, 0x045f, 0x00a0, 0x040e, 0x045e, 0x0408, 0x00a4, 0x0490, 0x00a6, 0x00a7, 0x0401, 0x00a9, 0x0404, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x0407, 0x00b0, 0x00b1, 0x0406, 0x0456, 0x0491, 0x00b5, 0x00b6, 0x00b7, 0x0451, 0x2116, 0x0454, 0x00bb, 0x0458, 0x0405, 0x0455, 0x0457, 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, 0x041f, 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042a, 0x042b, 0x042c, 0x042d, 0x042e, 0x042f, 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e, 0x043f, 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044a, 0x044b, 0x044c, 0x044d, 0x044e, 0x044f }, { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0xa0, 0xa4, 0xa6, 0xa7, 0xa9, 0xab, 0xac, 0xad, 0xae, 0xb0, 0xb1, 0xb5, 0xb6, 0xb7, 0xbb, 0xa8, 0x80, 0x81, 0xaa, 0xbd, 0xb2, 0xaf, 0xa3, 0x8a, 0x8c, 0x8e, 0x8d, 0xa1, 0x8f, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, 0xb8, 0x90, 0x83, 0xba, 0xbe, 0xb3, 0xbf, 0xbc, 0x9a, 0x9c, 0x9e, 0x9d, 0xa2, 0x9f, 0xa5, 0xb4, 0x96, 0x97, 0x91, 0x92, 0x82, 0x93, 0x94, 0x84, 0x86, 0x87, 0x95, 0x85, 0x89, 0x8b, 0x9b, 0x88, 0xb9, 0x99 }, 255 }; const charset_spec charset_CS_CP1251 = { CS_CP1251, read_sbcs, write_sbcs, &data_CS_CP1251 }; static const sbcs_data data_CS_CP1252 = { { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x20ac, ERROR , 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021, 0x02c6, 0x2030, 0x0160, 0x2039, 0x0152, ERROR , 0x017d, ERROR , ERROR , 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014, 0x02dc, 0x2122, 0x0161, 0x203a, 0x0153, ERROR , 0x017e, 0x0178, 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, 0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af, 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7, 0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf, 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf, 0x00d0, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7, 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x00dd, 0x00de, 0x00df, 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, 0x00f0, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00fd, 0x00fe, 0x00ff }, { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, 0x8c, 0x9c, 0x8a, 0x9a, 0x9f, 0x8e, 0x9e, 0x83, 0x88, 0x98, 0x96, 0x97, 0x91, 0x92, 0x82, 0x93, 0x94, 0x84, 0x86, 0x87, 0x95, 0x85, 0x89, 0x8b, 0x9b, 0x80, 0x99 }, 251 }; const charset_spec charset_CS_CP1252 = { CS_CP1252, read_sbcs, write_sbcs, &data_CS_CP1252 }; static const sbcs_data data_CS_CP1253 = { { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x20ac, ERROR , 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021, ERROR , 0x2030, ERROR , 0x2039, ERROR , ERROR , ERROR , ERROR , ERROR , 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014, ERROR , 0x2122, ERROR , 0x203a, ERROR , ERROR , ERROR , ERROR , 0x00a0, 0x0385, 0x0386, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, 0x00a8, 0x00a9, ERROR , 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x2015, 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x0384, 0x00b5, 0x00b6, 0x00b7, 0x0388, 0x0389, 0x038a, 0x00bb, 0x038c, 0x00bd, 0x038e, 0x038f, 0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d, 0x039e, 0x039f, 0x03a0, 0x03a1, ERROR , 0x03a3, 0x03a4, 0x03a5, 0x03a6, 0x03a7, 0x03a8, 0x03a9, 0x03aa, 0x03ab, 0x03ac, 0x03ad, 0x03ae, 0x03af, 0x03b0, 0x03b1, 0x03b2, 0x03b3, 0x03b4, 0x03b5, 0x03b6, 0x03b7, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03be, 0x03bf, 0x03c0, 0x03c1, 0x03c2, 0x03c3, 0x03c4, 0x03c5, 0x03c6, 0x03c7, 0x03c8, 0x03c9, 0x03ca, 0x03cb, 0x03cc, 0x03cd, 0x03ce, ERROR }, { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0xa0, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xab, 0xac, 0xad, 0xae, 0xb0, 0xb1, 0xb2, 0xb3, 0xb5, 0xb6, 0xb7, 0xbb, 0xbd, 0x83, 0xb4, 0xa1, 0xa2, 0xb8, 0xb9, 0xba, 0xbc, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0x96, 0x97, 0xaf, 0x91, 0x92, 0x82, 0x93, 0x94, 0x84, 0x86, 0x87, 0x95, 0x85, 0x89, 0x8b, 0x9b, 0x80, 0x99 }, 239 }; const charset_spec charset_CS_CP1253 = { CS_CP1253, read_sbcs, write_sbcs, &data_CS_CP1253 }; static const sbcs_data data_CS_CP1254 = { { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x20ac, ERROR , 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021, 0x02c6, 0x2030, 0x0160, 0x2039, 0x0152, ERROR , ERROR , ERROR , ERROR , 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014, 0x02dc, 0x2122, 0x0161, 0x203a, 0x0153, ERROR , ERROR , 0x0178, 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, 0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af, 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7, 0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf, 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf, 0x011e, 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x00d7, 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x0130, 0x015e, 0x00df, 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, 0x011f, 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x00f7, 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x0131, 0x015f, 0x00ff }, { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xff, 0xd0, 0xf0, 0xdd, 0xfd, 0x8c, 0x9c, 0xde, 0xfe, 0x8a, 0x9a, 0x9f, 0x83, 0x88, 0x98, 0x96, 0x97, 0x91, 0x92, 0x82, 0x93, 0x94, 0x84, 0x86, 0x87, 0x95, 0x85, 0x89, 0x8b, 0x9b, 0x80, 0x99 }, 249 }; const charset_spec charset_CS_CP1254 = { CS_CP1254, read_sbcs, write_sbcs, &data_CS_CP1254 }; static const sbcs_data data_CS_CP1255 = { { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x20ac, ERROR , 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021, 0x02c6, 0x2030, ERROR , 0x2039, ERROR , ERROR , ERROR , ERROR , ERROR , 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014, 0x02dc, 0x2122, ERROR , 0x203a, ERROR , ERROR , ERROR , ERROR , 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x20aa, 0x00a5, 0x00a6, 0x00a7, 0x00a8, 0x00a9, 0x00d7, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af, 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7, 0x00b8, 0x00b9, 0x00f7, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf, 0x05b0, 0x05b1, 0x05b2, 0x05b3, 0x05b4, 0x05b5, 0x05b6, 0x05b7, 0x05b8, 0x05b9, ERROR , 0x05bb, 0x05bc, 0x05bd, 0x05be, 0x05bf, 0x05c0, 0x05c1, 0x05c2, 0x05c3, 0x05f0, 0x05f1, 0x05f2, 0x05f3, 0x05f4, ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , 0x05d0, 0x05d1, 0x05d2, 0x05d3, 0x05d4, 0x05d5, 0x05d6, 0x05d7, 0x05d8, 0x05d9, 0x05da, 0x05db, 0x05dc, 0x05dd, 0x05de, 0x05df, 0x05e0, 0x05e1, 0x05e2, 0x05e3, 0x05e4, 0x05e5, 0x05e6, 0x05e7, 0x05e8, 0x05e9, 0x05ea, ERROR , ERROR , 0x200e, 0x200f, ERROR }, { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xaa, 0xba, 0x83, 0x88, 0x98, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xfd, 0xfe, 0x96, 0x97, 0x91, 0x92, 0x82, 0x93, 0x94, 0x84, 0x86, 0x87, 0x95, 0x85, 0x89, 0x8b, 0x9b, 0xa4, 0x80, 0x99 }, 233 }; const charset_spec charset_CS_CP1255 = { CS_CP1255, read_sbcs, write_sbcs, &data_CS_CP1255 }; static const sbcs_data data_CS_CP1256 = { { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x20ac, 0x067e, 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021, 0x02c6, 0x2030, 0x0679, 0x2039, 0x0152, 0x0686, 0x0698, 0x0688, 0x06af, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014, 0x06a9, 0x2122, 0x0691, 0x203a, 0x0153, 0x200c, 0x200d, 0x06ba, 0x00a0, 0x060c, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, 0x00a8, 0x00a9, 0x06be, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af, 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7, 0x00b8, 0x00b9, 0x061b, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x061f, 0x06c1, 0x0621, 0x0622, 0x0623, 0x0624, 0x0625, 0x0626, 0x0627, 0x0628, 0x0629, 0x062a, 0x062b, 0x062c, 0x062d, 0x062e, 0x062f, 0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x0636, 0x00d7, 0x0637, 0x0638, 0x0639, 0x063a, 0x0640, 0x0641, 0x0642, 0x0643, 0x00e0, 0x0644, 0x00e2, 0x0645, 0x0646, 0x0647, 0x0648, 0x00e7, 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x0649, 0x064a, 0x00ee, 0x00ef, 0x064b, 0x064c, 0x064d, 0x064e, 0x00f4, 0x064f, 0x0650, 0x00f7, 0x0651, 0x00f9, 0x0652, 0x00fb, 0x00fc, 0x200e, 0x200f, 0x06d2 }, { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0xa0, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xbb, 0xbc, 0xbd, 0xbe, 0xd7, 0xe0, 0xe2, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xee, 0xef, 0xf4, 0xf7, 0xf9, 0xfb, 0xfc, 0x8c, 0x9c, 0x83, 0x88, 0xa1, 0xba, 0xbf, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe1, 0xe3, 0xe4, 0xe5, 0xe6, 0xec, 0xed, 0xf0, 0xf1, 0xf2, 0xf3, 0xf5, 0xf6, 0xf8, 0xfa, 0x8a, 0x81, 0x8d, 0x8f, 0x9a, 0x8e, 0x98, 0x90, 0x9f, 0xaa, 0xc0, 0xff, 0x9d, 0x9e, 0xfd, 0xfe, 0x96, 0x97, 0x91, 0x92, 0x82, 0x93, 0x94, 0x84, 0x86, 0x87, 0x95, 0x85, 0x89, 0x8b, 0x9b, 0x80, 0x99 }, 256 }; const charset_spec charset_CS_CP1256 = { CS_CP1256, read_sbcs, write_sbcs, &data_CS_CP1256 }; static const sbcs_data data_CS_CP1257 = { { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x20ac, ERROR , 0x201a, ERROR , 0x201e, 0x2026, 0x2020, 0x2021, ERROR , 0x2030, ERROR , 0x2039, ERROR , 0x00a8, 0x02c7, 0x00b8, ERROR , 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014, ERROR , 0x2122, ERROR , 0x203a, ERROR , 0x00af, 0x02db, ERROR , 0x00a0, ERROR , 0x00a2, 0x00a3, 0x00a4, ERROR , 0x00a6, 0x00a7, 0x00d8, 0x00a9, 0x0156, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00c6, 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7, 0x00f8, 0x00b9, 0x0157, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00e6, 0x0104, 0x012e, 0x0100, 0x0106, 0x00c4, 0x00c5, 0x0118, 0x0112, 0x010c, 0x00c9, 0x0179, 0x0116, 0x0122, 0x0136, 0x012a, 0x013b, 0x0160, 0x0143, 0x0145, 0x00d3, 0x014c, 0x00d5, 0x00d6, 0x00d7, 0x0172, 0x0141, 0x015a, 0x016a, 0x00dc, 0x017b, 0x017d, 0x00df, 0x0105, 0x012f, 0x0101, 0x0107, 0x00e4, 0x00e5, 0x0119, 0x0113, 0x010d, 0x00e9, 0x017a, 0x0117, 0x0123, 0x0137, 0x012b, 0x013c, 0x0161, 0x0144, 0x0146, 0x00f3, 0x014d, 0x00f5, 0x00f6, 0x00f7, 0x0173, 0x0142, 0x015b, 0x016b, 0x00fc, 0x017c, 0x017e, 0x02d9 }, { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0xa0, 0xa2, 0xa3, 0xa4, 0xa6, 0xa7, 0x8d, 0xa9, 0xab, 0xac, 0xad, 0xae, 0x9d, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0x8f, 0xb9, 0xbb, 0xbc, 0xbd, 0xbe, 0xc4, 0xc5, 0xaf, 0xc9, 0xd3, 0xd5, 0xd6, 0xd7, 0xa8, 0xdc, 0xdf, 0xe4, 0xe5, 0xbf, 0xe9, 0xf3, 0xf5, 0xf6, 0xf7, 0xb8, 0xfc, 0xc2, 0xe2, 0xc0, 0xe0, 0xc3, 0xe3, 0xc8, 0xe8, 0xc7, 0xe7, 0xcb, 0xeb, 0xc6, 0xe6, 0xcc, 0xec, 0xce, 0xee, 0xc1, 0xe1, 0xcd, 0xed, 0xcf, 0xef, 0xd9, 0xf9, 0xd1, 0xf1, 0xd2, 0xf2, 0xd4, 0xf4, 0xaa, 0xba, 0xda, 0xfa, 0xd0, 0xf0, 0xdb, 0xfb, 0xd8, 0xf8, 0xca, 0xea, 0xdd, 0xfd, 0xde, 0xfe, 0x8e, 0xff, 0x9e, 0x96, 0x97, 0x91, 0x92, 0x82, 0x93, 0x94, 0x84, 0x86, 0x87, 0x95, 0x85, 0x89, 0x8b, 0x9b, 0x80, 0x99 }, 244 }; const charset_spec charset_CS_CP1257 = { CS_CP1257, read_sbcs, write_sbcs, &data_CS_CP1257 }; static const sbcs_data data_CS_CP1258 = { { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x20ac, ERROR , 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021, 0x02c6, 0x2030, ERROR , 0x2039, 0x0152, ERROR , ERROR , ERROR , ERROR , 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014, 0x02dc, 0x2122, ERROR , 0x203a, 0x0153, ERROR , ERROR , 0x0178, 0x00a0, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, 0x00a8, 0x00a9, 0x00aa, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x00af, 0x00b0, 0x00b1, 0x00b2, 0x00b3, 0x00b4, 0x00b5, 0x00b6, 0x00b7, 0x00b8, 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, 0x00be, 0x00bf, 0x00c0, 0x00c1, 0x00c2, 0x0102, 0x00c4, 0x00c5, 0x00c6, 0x00c7, 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x0300, 0x00cd, 0x00ce, 0x00cf, 0x0110, 0x00d1, 0x0309, 0x00d3, 0x00d4, 0x01a0, 0x00d6, 0x00d7, 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x01af, 0x0303, 0x00df, 0x00e0, 0x00e1, 0x00e2, 0x0103, 0x00e4, 0x00e5, 0x00e6, 0x00e7, 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x0301, 0x00ed, 0x00ee, 0x00ef, 0x0111, 0x00f1, 0x0323, 0x00f3, 0x00f4, 0x01a1, 0x00f6, 0x00f7, 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x01b0, 0x20ab, 0x00ff }, { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcd, 0xce, 0xcf, 0xd1, 0xd3, 0xd4, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdf, 0xe0, 0xe1, 0xe2, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xed, 0xee, 0xef, 0xf1, 0xf3, 0xf4, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xff, 0xc3, 0xe3, 0xd0, 0xf0, 0x8c, 0x9c, 0x9f, 0x83, 0xd5, 0xf5, 0xdd, 0xfd, 0x88, 0x98, 0xcc, 0xec, 0xde, 0xd2, 0xf2, 0x96, 0x97, 0x91, 0x92, 0x82, 0x93, 0x94, 0x84, 0x86, 0x87, 0x95, 0x85, 0x89, 0x8b, 0x9b, 0xfe, 0x80, 0x99 }, 247 }; const charset_spec charset_CS_CP1258 = { CS_CP1258, read_sbcs, write_sbcs, &data_CS_CP1258 }; static const sbcs_data data_CS_KOI8_R = { { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x2500, 0x2502, 0x250c, 0x2510, 0x2514, 0x2518, 0x251c, 0x2524, 0x252c, 0x2534, 0x253c, 0x2580, 0x2584, 0x2588, 0x258c, 0x2590, 0x2591, 0x2592, 0x2593, 0x2320, 0x25a0, 0x2219, 0x221a, 0x2248, 0x2264, 0x2265, 0x00a0, 0x2321, 0x00b0, 0x00b2, 0x00b7, 0x00f7, 0x2550, 0x2551, 0x2552, 0x0451, 0x2553, 0x2554, 0x2555, 0x2556, 0x2557, 0x2558, 0x2559, 0x255a, 0x255b, 0x255c, 0x255d, 0x255e, 0x255f, 0x2560, 0x2561, 0x0401, 0x2562, 0x2563, 0x2564, 0x2565, 0x2566, 0x2567, 0x2568, 0x2569, 0x256a, 0x256b, 0x256c, 0x00a9, 0x044e, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433, 0x0445, 0x0438, 0x0439, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e, 0x043f, 0x044f, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432, 0x044c, 0x044b, 0x0437, 0x0448, 0x044d, 0x0449, 0x0447, 0x044a, 0x042e, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413, 0x0425, 0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, 0x041f, 0x042f, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412, 0x042c, 0x042b, 0x0417, 0x0428, 0x042d, 0x0429, 0x0427, 0x042a }, { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x9a, 0xbf, 0x9c, 0x9d, 0x9e, 0x9f, 0xb3, 0xe1, 0xe2, 0xf7, 0xe7, 0xe4, 0xe5, 0xf6, 0xfa, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf2, 0xf3, 0xf4, 0xf5, 0xe6, 0xe8, 0xe3, 0xfe, 0xfb, 0xfd, 0xff, 0xf9, 0xf8, 0xfc, 0xe0, 0xf1, 0xc1, 0xc2, 0xd7, 0xc7, 0xc4, 0xc5, 0xd6, 0xda, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd2, 0xd3, 0xd4, 0xd5, 0xc6, 0xc8, 0xc3, 0xde, 0xdb, 0xdd, 0xdf, 0xd9, 0xd8, 0xdc, 0xc0, 0xd1, 0xa3, 0x95, 0x96, 0x97, 0x98, 0x99, 0x93, 0x9b, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0xa0, 0xa1, 0xa2, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x94 }, 256 }; const charset_spec charset_CS_KOI8_R = { CS_KOI8_R, read_sbcs, write_sbcs, &data_CS_KOI8_R }; static const sbcs_data data_CS_KOI8_U = { { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x2500, 0x2502, 0x250c, 0x2510, 0x2514, 0x2518, 0x251c, 0x2524, 0x252c, 0x2534, 0x253c, 0x2580, 0x2584, 0x2588, 0x258c, 0x2590, 0x2591, 0x2592, 0x2593, 0x2320, 0x25a0, 0x2219, 0x221a, 0x2248, 0x2264, 0x2265, 0x00a0, 0x2321, 0x00b0, 0x00b2, 0x00b7, 0x00f7, 0x2550, 0x2551, 0x2552, 0x0451, 0x0454, 0x2554, 0x0456, 0x0457, 0x2557, 0x2558, 0x2559, 0x255a, 0x255b, 0x0491, 0x255d, 0x255e, 0x255f, 0x2560, 0x2561, 0x0401, 0x0404, 0x2563, 0x0406, 0x0407, 0x2566, 0x2567, 0x2568, 0x2569, 0x256a, 0x0490, 0x256c, 0x00a9, 0x044e, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433, 0x0445, 0x0438, 0x0439, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e, 0x043f, 0x044f, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432, 0x044c, 0x044b, 0x0437, 0x0448, 0x044d, 0x0449, 0x0447, 0x044a, 0x042e, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413, 0x0425, 0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, 0x041f, 0x042f, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412, 0x042c, 0x042b, 0x0417, 0x0428, 0x042d, 0x0429, 0x0427, 0x042a }, { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x9a, 0xbf, 0x9c, 0x9d, 0x9e, 0x9f, 0xb3, 0xb4, 0xb6, 0xb7, 0xe1, 0xe2, 0xf7, 0xe7, 0xe4, 0xe5, 0xf6, 0xfa, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf2, 0xf3, 0xf4, 0xf5, 0xe6, 0xe8, 0xe3, 0xfe, 0xfb, 0xfd, 0xff, 0xf9, 0xf8, 0xfc, 0xe0, 0xf1, 0xc1, 0xc2, 0xd7, 0xc7, 0xc4, 0xc5, 0xd6, 0xda, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd2, 0xd3, 0xd4, 0xd5, 0xc6, 0xc8, 0xc3, 0xde, 0xdb, 0xdd, 0xdf, 0xd9, 0xd8, 0xdc, 0xc0, 0xd1, 0xa3, 0xa4, 0xa6, 0xa7, 0xbd, 0xad, 0x95, 0x96, 0x97, 0x98, 0x99, 0x93, 0x9b, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0xa0, 0xa1, 0xa2, 0xa5, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb5, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbe, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x94 }, 256 }; const charset_spec charset_CS_KOI8_U = { CS_KOI8_U, read_sbcs, write_sbcs, &data_CS_KOI8_U }; static const sbcs_data data_CS_MAC_ROMAN = { { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x00c4, 0x00c5, 0x00c7, 0x00c9, 0x00d1, 0x00d6, 0x00dc, 0x00e1, 0x00e0, 0x00e2, 0x00e4, 0x00e3, 0x00e5, 0x00e7, 0x00e9, 0x00e8, 0x00ea, 0x00eb, 0x00ed, 0x00ec, 0x00ee, 0x00ef, 0x00f1, 0x00f3, 0x00f2, 0x00f4, 0x00f6, 0x00f5, 0x00fa, 0x00f9, 0x00fb, 0x00fc, 0x2020, 0x00b0, 0x00a2, 0x00a3, 0x00a7, 0x2022, 0x00b6, 0x00df, 0x00ae, 0x00a9, 0x2122, 0x00b4, 0x00a8, 0x2260, 0x00c6, 0x00d8, 0x221e, 0x00b1, 0x2264, 0x2265, 0x00a5, 0x00b5, 0x2202, 0x2211, 0x220f, 0x03c0, 0x222b, 0x00aa, 0x00ba, 0x03a9, 0x00e6, 0x00f8, 0x00bf, 0x00a1, 0x00ac, 0x221a, 0x0192, 0x2248, 0x2206, 0x00ab, 0x00bb, 0x2026, 0x00a0, 0x00c0, 0x00c3, 0x00d5, 0x0152, 0x0153, 0x2013, 0x2014, 0x201c, 0x201d, 0x2018, 0x2019, 0x00f7, 0x25ca, 0x00ff, 0x0178, 0x2044, 0x20ac, 0x2039, 0x203a, 0xfb01, 0xfb02, 0x2021, 0x00b7, 0x201a, 0x201e, 0x2030, 0x00c2, 0x00ca, 0x00c1, 0x00cb, 0x00c8, 0x00cd, 0x00ce, 0x00cf, 0x00cc, 0x00d3, 0x00d4, 0xf8ff, 0x00d2, 0x00da, 0x00db, 0x00d9, 0x0131, 0x02c6, 0x02dc, 0x00af, 0x02d8, 0x02d9, 0x02da, 0x00b8, 0x02dd, 0x02db, 0x02c7 }, { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0xca, 0xc1, 0xa2, 0xa3, 0xb4, 0xa4, 0xac, 0xa9, 0xbb, 0xc7, 0xc2, 0xa8, 0xf8, 0xa1, 0xb1, 0xab, 0xb5, 0xa6, 0xe1, 0xfc, 0xbc, 0xc8, 0xc0, 0xcb, 0xe7, 0xe5, 0xcc, 0x80, 0x81, 0xae, 0x82, 0xe9, 0x83, 0xe6, 0xe8, 0xed, 0xea, 0xeb, 0xec, 0x84, 0xf1, 0xee, 0xef, 0xcd, 0x85, 0xaf, 0xf4, 0xf2, 0xf3, 0x86, 0xa7, 0x88, 0x87, 0x89, 0x8b, 0x8a, 0x8c, 0xbe, 0x8d, 0x8f, 0x8e, 0x90, 0x91, 0x93, 0x92, 0x94, 0x95, 0x96, 0x98, 0x97, 0x99, 0x9b, 0x9a, 0xd6, 0xbf, 0x9d, 0x9c, 0x9e, 0x9f, 0xd8, 0xf5, 0xce, 0xcf, 0xd9, 0xc4, 0xf6, 0xff, 0xf9, 0xfa, 0xfb, 0xfe, 0xf7, 0xfd, 0xbd, 0xb9, 0xd0, 0xd1, 0xd4, 0xd5, 0xe2, 0xd2, 0xd3, 0xe3, 0xa0, 0xe0, 0xa5, 0xc9, 0xe4, 0xdc, 0xdd, 0xda, 0xdb, 0xaa, 0xb6, 0xc6, 0xb8, 0xb7, 0xc3, 0xb0, 0xba, 0xc5, 0xad, 0xb2, 0xb3, 0xd7, 0xf0, 0xde, 0xdf }, 256 }; const charset_spec charset_CS_MAC_ROMAN = { CS_MAC_ROMAN, read_sbcs, write_sbcs, &data_CS_MAC_ROMAN }; static const sbcs_data data_CS_MAC_TURKISH = { { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x00c4, 0x00c5, 0x00c7, 0x00c9, 0x00d1, 0x00d6, 0x00dc, 0x00e1, 0x00e0, 0x00e2, 0x00e4, 0x00e3, 0x00e5, 0x00e7, 0x00e9, 0x00e8, 0x00ea, 0x00eb, 0x00ed, 0x00ec, 0x00ee, 0x00ef, 0x00f1, 0x00f3, 0x00f2, 0x00f4, 0x00f6, 0x00f5, 0x00fa, 0x00f9, 0x00fb, 0x00fc, 0x2020, 0x00b0, 0x00a2, 0x00a3, 0x00a7, 0x2022, 0x00b6, 0x00df, 0x00ae, 0x00a9, 0x2122, 0x00b4, 0x00a8, 0x2260, 0x00c6, 0x00d8, 0x221e, 0x00b1, 0x2264, 0x2265, 0x00a5, 0x00b5, 0x2202, 0x2211, 0x220f, 0x03c0, 0x222b, 0x00aa, 0x00ba, 0x03a9, 0x00e6, 0x00f8, 0x00bf, 0x00a1, 0x00ac, 0x221a, 0x0192, 0x2248, 0x2206, 0x00ab, 0x00bb, 0x2026, 0x00a0, 0x00c0, 0x00c3, 0x00d5, 0x0152, 0x0153, 0x2013, 0x2014, 0x201c, 0x201d, 0x2018, 0x2019, 0x00f7, 0x25ca, 0x00ff, 0x0178, 0x011e, 0x011f, 0x0130, 0x0131, 0x015e, 0x015f, 0x2021, 0x00b7, 0x201a, 0x201e, 0x2030, 0x00c2, 0x00ca, 0x00c1, 0x00cb, 0x00c8, 0x00cd, 0x00ce, 0x00cf, 0x00cc, 0x00d3, 0x00d4, 0xf8ff, 0x00d2, 0x00da, 0x00db, 0x00d9, ERROR , 0x02c6, 0x02dc, 0x00af, 0x02d8, 0x02d9, 0x02da, 0x00b8, 0x02dd, 0x02db, 0x02c7 }, { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0xca, 0xc1, 0xa2, 0xa3, 0xb4, 0xa4, 0xac, 0xa9, 0xbb, 0xc7, 0xc2, 0xa8, 0xf8, 0xa1, 0xb1, 0xab, 0xb5, 0xa6, 0xe1, 0xfc, 0xbc, 0xc8, 0xc0, 0xcb, 0xe7, 0xe5, 0xcc, 0x80, 0x81, 0xae, 0x82, 0xe9, 0x83, 0xe6, 0xe8, 0xed, 0xea, 0xeb, 0xec, 0x84, 0xf1, 0xee, 0xef, 0xcd, 0x85, 0xaf, 0xf4, 0xf2, 0xf3, 0x86, 0xa7, 0x88, 0x87, 0x89, 0x8b, 0x8a, 0x8c, 0xbe, 0x8d, 0x8f, 0x8e, 0x90, 0x91, 0x93, 0x92, 0x94, 0x95, 0x96, 0x98, 0x97, 0x99, 0x9b, 0x9a, 0xd6, 0xbf, 0x9d, 0x9c, 0x9e, 0x9f, 0xd8, 0xda, 0xdb, 0xdc, 0xdd, 0xce, 0xcf, 0xde, 0xdf, 0xd9, 0xc4, 0xf6, 0xff, 0xf9, 0xfa, 0xfb, 0xfe, 0xf7, 0xfd, 0xbd, 0xb9, 0xd0, 0xd1, 0xd4, 0xd5, 0xe2, 0xd2, 0xd3, 0xe3, 0xa0, 0xe0, 0xa5, 0xc9, 0xe4, 0xaa, 0xb6, 0xc6, 0xb8, 0xb7, 0xc3, 0xb0, 0xba, 0xc5, 0xad, 0xb2, 0xb3, 0xd7, 0xf0 }, 255 }; const charset_spec charset_CS_MAC_TURKISH = { CS_MAC_TURKISH, read_sbcs, write_sbcs, &data_CS_MAC_TURKISH }; static const sbcs_data data_CS_MAC_CROATIAN = { { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x00c4, 0x00c5, 0x00c7, 0x00c9, 0x00d1, 0x00d6, 0x00dc, 0x00e1, 0x00e0, 0x00e2, 0x00e4, 0x00e3, 0x00e5, 0x00e7, 0x00e9, 0x00e8, 0x00ea, 0x00eb, 0x00ed, 0x00ec, 0x00ee, 0x00ef, 0x00f1, 0x00f3, 0x00f2, 0x00f4, 0x00f6, 0x00f5, 0x00fa, 0x00f9, 0x00fb, 0x00fc, 0x2020, 0x00b0, 0x00a2, 0x00a3, 0x00a7, 0x2022, 0x00b6, 0x00df, 0x00ae, 0x0160, 0x2122, 0x00b4, 0x00a8, 0x2260, 0x017d, 0x00d8, 0x221e, 0x00b1, 0x2264, 0x2265, 0x2206, 0x00b5, 0x2202, 0x2211, 0x220f, 0x0161, 0x222b, 0x00aa, 0x00ba, 0x03a9, 0x017e, 0x00f8, 0x00bf, 0x00a1, 0x00ac, 0x221a, 0x0192, 0x2248, 0x0106, 0x00ab, 0x010c, 0x2026, 0x00a0, 0x00c0, 0x00c3, 0x00d5, 0x0152, 0x0153, 0x0110, 0x2014, 0x201c, 0x201d, 0x2018, 0x2019, 0x00f7, 0x25ca, 0xf8ff, 0x00a9, 0x2044, 0x20ac, 0x2039, 0x203a, 0x00c6, 0x00bb, 0x2013, 0x00b7, 0x201a, 0x201e, 0x2030, 0x00c2, 0x0107, 0x00c1, 0x010d, 0x00c8, 0x00cd, 0x00ce, 0x00cf, 0x00cc, 0x00d3, 0x00d4, 0x0111, 0x00d2, 0x00da, 0x00db, 0x00d9, 0x0131, 0x02c6, 0x02dc, 0x00af, 0x03c0, 0x00cb, 0x02da, 0x00b8, 0x00ca, 0x00e6, 0x02c7 }, { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0xca, 0xc1, 0xa2, 0xa3, 0xa4, 0xac, 0xd9, 0xbb, 0xc7, 0xc2, 0xa8, 0xf8, 0xa1, 0xb1, 0xab, 0xb5, 0xa6, 0xe1, 0xfc, 0xbc, 0xdf, 0xc0, 0xcb, 0xe7, 0xe5, 0xcc, 0x80, 0x81, 0xde, 0x82, 0xe9, 0x83, 0xfd, 0xfa, 0xed, 0xea, 0xeb, 0xec, 0x84, 0xf1, 0xee, 0xef, 0xcd, 0x85, 0xaf, 0xf4, 0xf2, 0xf3, 0x86, 0xa7, 0x88, 0x87, 0x89, 0x8b, 0x8a, 0x8c, 0xfe, 0x8d, 0x8f, 0x8e, 0x90, 0x91, 0x93, 0x92, 0x94, 0x95, 0x96, 0x98, 0x97, 0x99, 0x9b, 0x9a, 0xd6, 0xbf, 0x9d, 0x9c, 0x9e, 0x9f, 0xc6, 0xe6, 0xc8, 0xe8, 0xd0, 0xf0, 0xf5, 0xce, 0xcf, 0xa9, 0xb9, 0xae, 0xbe, 0xc4, 0xf6, 0xff, 0xfb, 0xf7, 0xbd, 0xf9, 0xe0, 0xd1, 0xd4, 0xd5, 0xe2, 0xd2, 0xd3, 0xe3, 0xa0, 0xa5, 0xc9, 0xe4, 0xdc, 0xdd, 0xda, 0xdb, 0xaa, 0xb6, 0xb4, 0xb8, 0xb7, 0xc3, 0xb0, 0xba, 0xc5, 0xad, 0xb2, 0xb3, 0xd7, 0xd8 }, 256 }; const charset_spec charset_CS_MAC_CROATIAN = { CS_MAC_CROATIAN, read_sbcs, write_sbcs, &data_CS_MAC_CROATIAN }; static const sbcs_data data_CS_MAC_ICELAND = { { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x00c4, 0x00c5, 0x00c7, 0x00c9, 0x00d1, 0x00d6, 0x00dc, 0x00e1, 0x00e0, 0x00e2, 0x00e4, 0x00e3, 0x00e5, 0x00e7, 0x00e9, 0x00e8, 0x00ea, 0x00eb, 0x00ed, 0x00ec, 0x00ee, 0x00ef, 0x00f1, 0x00f3, 0x00f2, 0x00f4, 0x00f6, 0x00f5, 0x00fa, 0x00f9, 0x00fb, 0x00fc, 0x00dd, 0x00b0, 0x00a2, 0x00a3, 0x00a7, 0x2022, 0x00b6, 0x00df, 0x00ae, 0x00a9, 0x2122, 0x00b4, 0x00a8, 0x2260, 0x00c6, 0x00d8, 0x221e, 0x00b1, 0x2264, 0x2265, 0x00a5, 0x00b5, 0x2202, 0x2211, 0x220f, 0x03c0, 0x222b, 0x00aa, 0x00ba, 0x03a9, 0x00e6, 0x00f8, 0x00bf, 0x00a1, 0x00ac, 0x221a, 0x0192, 0x2248, 0x2206, 0x00ab, 0x00bb, 0x2026, 0x00a0, 0x00c0, 0x00c3, 0x00d5, 0x0152, 0x0153, 0x2013, 0x2014, 0x201c, 0x201d, 0x2018, 0x2019, 0x00f7, 0x25ca, 0x00ff, 0x0178, 0x2044, 0x20ac, 0x00d0, 0x00f0, 0x00de, 0x00fe, 0x00fd, 0x00b7, 0x201a, 0x201e, 0x2030, 0x00c2, 0x00ca, 0x00c1, 0x00cb, 0x00c8, 0x00cd, 0x00ce, 0x00cf, 0x00cc, 0x00d3, 0x00d4, 0xf8ff, 0x00d2, 0x00da, 0x00db, 0x00d9, 0x0131, 0x02c6, 0x02dc, 0x00af, 0x02d8, 0x02d9, 0x02da, 0x00b8, 0x02dd, 0x02db, 0x02c7 }, { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0xca, 0xc1, 0xa2, 0xa3, 0xb4, 0xa4, 0xac, 0xa9, 0xbb, 0xc7, 0xc2, 0xa8, 0xf8, 0xa1, 0xb1, 0xab, 0xb5, 0xa6, 0xe1, 0xfc, 0xbc, 0xc8, 0xc0, 0xcb, 0xe7, 0xe5, 0xcc, 0x80, 0x81, 0xae, 0x82, 0xe9, 0x83, 0xe6, 0xe8, 0xed, 0xea, 0xeb, 0xec, 0xdc, 0x84, 0xf1, 0xee, 0xef, 0xcd, 0x85, 0xaf, 0xf4, 0xf2, 0xf3, 0x86, 0xa0, 0xde, 0xa7, 0x88, 0x87, 0x89, 0x8b, 0x8a, 0x8c, 0xbe, 0x8d, 0x8f, 0x8e, 0x90, 0x91, 0x93, 0x92, 0x94, 0x95, 0xdd, 0x96, 0x98, 0x97, 0x99, 0x9b, 0x9a, 0xd6, 0xbf, 0x9d, 0x9c, 0x9e, 0x9f, 0xe0, 0xdf, 0xd8, 0xf5, 0xce, 0xcf, 0xd9, 0xc4, 0xf6, 0xff, 0xf9, 0xfa, 0xfb, 0xfe, 0xf7, 0xfd, 0xbd, 0xb9, 0xd0, 0xd1, 0xd4, 0xd5, 0xe2, 0xd2, 0xd3, 0xe3, 0xa5, 0xc9, 0xe4, 0xda, 0xdb, 0xaa, 0xb6, 0xc6, 0xb8, 0xb7, 0xc3, 0xb0, 0xba, 0xc5, 0xad, 0xb2, 0xb3, 0xd7, 0xf0 }, 256 }; const charset_spec charset_CS_MAC_ICELAND = { CS_MAC_ICELAND, read_sbcs, write_sbcs, &data_CS_MAC_ICELAND }; static const sbcs_data data_CS_MAC_ROMANIAN = { { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x00c4, 0x00c5, 0x00c7, 0x00c9, 0x00d1, 0x00d6, 0x00dc, 0x00e1, 0x00e0, 0x00e2, 0x00e4, 0x00e3, 0x00e5, 0x00e7, 0x00e9, 0x00e8, 0x00ea, 0x00eb, 0x00ed, 0x00ec, 0x00ee, 0x00ef, 0x00f1, 0x00f3, 0x00f2, 0x00f4, 0x00f6, 0x00f5, 0x00fa, 0x00f9, 0x00fb, 0x00fc, 0x2020, 0x00b0, 0x00a2, 0x00a3, 0x00a7, 0x2022, 0x00b6, 0x00df, 0x00ae, 0x00a9, 0x2122, 0x00b4, 0x00a8, 0x2260, 0x0102, 0x0218, 0x221e, 0x00b1, 0x2264, 0x2265, 0x00a5, 0x00b5, 0x2202, 0x2211, 0x220f, 0x03c0, 0x222b, 0x00aa, 0x00ba, 0x03a9, 0x0103, 0x0219, 0x00bf, 0x00a1, 0x00ac, 0x221a, 0x0192, 0x2248, 0x2206, 0x00ab, 0x00bb, 0x2026, 0x00a0, 0x00c0, 0x00c3, 0x00d5, 0x0152, 0x0153, 0x2013, 0x2014, 0x201c, 0x201d, 0x2018, 0x2019, 0x00f7, 0x25ca, 0x00ff, 0x0178, 0x2044, 0x20ac, 0x2039, 0x203a, 0x021a, 0x021b, 0x2021, 0x00b7, 0x201a, 0x201e, 0x2030, 0x00c2, 0x00ca, 0x00c1, 0x00cb, 0x00c8, 0x00cd, 0x00ce, 0x00cf, 0x00cc, 0x00d3, 0x00d4, 0xf8ff, 0x00d2, 0x00da, 0x00db, 0x00d9, 0x0131, 0x02c6, 0x02dc, 0x00af, 0x02d8, 0x02d9, 0x02da, 0x00b8, 0x02dd, 0x02db, 0x02c7 }, { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0xca, 0xc1, 0xa2, 0xa3, 0xb4, 0xa4, 0xac, 0xa9, 0xbb, 0xc7, 0xc2, 0xa8, 0xf8, 0xa1, 0xb1, 0xab, 0xb5, 0xa6, 0xe1, 0xfc, 0xbc, 0xc8, 0xc0, 0xcb, 0xe7, 0xe5, 0xcc, 0x80, 0x81, 0x82, 0xe9, 0x83, 0xe6, 0xe8, 0xed, 0xea, 0xeb, 0xec, 0x84, 0xf1, 0xee, 0xef, 0xcd, 0x85, 0xf4, 0xf2, 0xf3, 0x86, 0xa7, 0x88, 0x87, 0x89, 0x8b, 0x8a, 0x8c, 0x8d, 0x8f, 0x8e, 0x90, 0x91, 0x93, 0x92, 0x94, 0x95, 0x96, 0x98, 0x97, 0x99, 0x9b, 0x9a, 0xd6, 0x9d, 0x9c, 0x9e, 0x9f, 0xd8, 0xae, 0xbe, 0xf5, 0xce, 0xcf, 0xd9, 0xc4, 0xaf, 0xbf, 0xde, 0xdf, 0xf6, 0xff, 0xf9, 0xfa, 0xfb, 0xfe, 0xf7, 0xfd, 0xbd, 0xb9, 0xd0, 0xd1, 0xd4, 0xd5, 0xe2, 0xd2, 0xd3, 0xe3, 0xa0, 0xe0, 0xa5, 0xc9, 0xe4, 0xdc, 0xdd, 0xda, 0xdb, 0xaa, 0xb6, 0xc6, 0xb8, 0xb7, 0xc3, 0xb0, 0xba, 0xc5, 0xad, 0xb2, 0xb3, 0xd7, 0xf0 }, 256 }; const charset_spec charset_CS_MAC_ROMANIAN = { CS_MAC_ROMANIAN, read_sbcs, write_sbcs, &data_CS_MAC_ROMANIAN }; static const sbcs_data data_CS_MAC_GREEK = { { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x00c4, 0x00b9, 0x00b2, 0x00c9, 0x00b3, 0x00d6, 0x00dc, 0x0385, 0x00e0, 0x00e2, 0x00e4, 0x0384, 0x00a8, 0x00e7, 0x00e9, 0x00e8, 0x00ea, 0x00eb, 0x00a3, 0x2122, 0x00ee, 0x00ef, 0x2022, 0x00bd, 0x2030, 0x00f4, 0x00f6, 0x00a6, 0x20ac, 0x00f9, 0x00fb, 0x00fc, 0x2020, 0x0393, 0x0394, 0x0398, 0x039b, 0x039e, 0x03a0, 0x00df, 0x00ae, 0x00a9, 0x03a3, 0x03aa, 0x00a7, 0x2260, 0x00b0, 0x00b7, 0x0391, 0x00b1, 0x2264, 0x2265, 0x00a5, 0x0392, 0x0395, 0x0396, 0x0397, 0x0399, 0x039a, 0x039c, 0x03a6, 0x03ab, 0x03a8, 0x03a9, 0x03ac, 0x039d, 0x00ac, 0x039f, 0x03a1, 0x2248, 0x03a4, 0x00ab, 0x00bb, 0x2026, 0x00a0, 0x03a5, 0x03a7, 0x0386, 0x0388, 0x0153, 0x2013, 0x2015, 0x201c, 0x201d, 0x2018, 0x2019, 0x00f7, 0x0389, 0x038a, 0x038c, 0x038e, 0x03ad, 0x03ae, 0x03af, 0x03cc, 0x038f, 0x03cd, 0x03b1, 0x03b2, 0x03c8, 0x03b4, 0x03b5, 0x03c6, 0x03b3, 0x03b7, 0x03b9, 0x03be, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03bf, 0x03c0, 0x03ce, 0x03c1, 0x03c3, 0x03c4, 0x03b8, 0x03c9, 0x03c2, 0x03c7, 0x03c5, 0x03b6, 0x03ca, 0x03cb, 0x0390, 0x03b0, 0x00ad }, { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0xca, 0x92, 0xb4, 0x9b, 0xac, 0x8c, 0xa9, 0xc7, 0xc2, 0xff, 0xa8, 0xae, 0xb1, 0x82, 0x84, 0xaf, 0x81, 0xc8, 0x97, 0x80, 0x83, 0x85, 0x86, 0xa7, 0x88, 0x89, 0x8a, 0x8d, 0x8f, 0x8e, 0x90, 0x91, 0x94, 0x95, 0x99, 0x9a, 0xd6, 0x9d, 0x9e, 0x9f, 0xcf, 0x8b, 0x87, 0xcd, 0xce, 0xd7, 0xd8, 0xd9, 0xda, 0xdf, 0xfd, 0xb0, 0xb5, 0xa1, 0xa2, 0xb6, 0xb7, 0xb8, 0xa3, 0xb9, 0xba, 0xa4, 0xbb, 0xc1, 0xa5, 0xc3, 0xa6, 0xc4, 0xaa, 0xc6, 0xcb, 0xbc, 0xcc, 0xbe, 0xbf, 0xab, 0xbd, 0xc0, 0xdb, 0xdc, 0xdd, 0xfe, 0xe1, 0xe2, 0xe7, 0xe4, 0xe5, 0xfa, 0xe8, 0xf5, 0xe9, 0xeb, 0xec, 0xed, 0xee, 0xea, 0xef, 0xf0, 0xf2, 0xf7, 0xf3, 0xf4, 0xf9, 0xe6, 0xf8, 0xe3, 0xf6, 0xfb, 0xfc, 0xde, 0xe0, 0xf1, 0xd0, 0xd1, 0xd4, 0xd5, 0xd2, 0xd3, 0xa0, 0x96, 0xc9, 0x98, 0x9c, 0x93, 0xc5, 0xad, 0xb2, 0xb3 }, 256 }; const charset_spec charset_CS_MAC_GREEK = { CS_MAC_GREEK, read_sbcs, write_sbcs, &data_CS_MAC_GREEK }; static const sbcs_data data_CS_MAC_CYRILLIC = { { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, 0x041f, 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042a, 0x042b, 0x042c, 0x042d, 0x042e, 0x042f, 0x2020, 0x00b0, 0x0490, 0x00a3, 0x00a7, 0x2022, 0x00b6, 0x0406, 0x00ae, 0x00a9, 0x2122, 0x0402, 0x0452, 0x2260, 0x0403, 0x0453, 0x221e, 0x00b1, 0x2264, 0x2265, 0x0456, 0x00b5, 0x0491, 0x0408, 0x0404, 0x0454, 0x0407, 0x0457, 0x0409, 0x0459, 0x040a, 0x045a, 0x0458, 0x0405, 0x00ac, 0x221a, 0x0192, 0x2248, 0x2206, 0x00ab, 0x00bb, 0x2026, 0x00a0, 0x040b, 0x045b, 0x040c, 0x045c, 0x0455, 0x2013, 0x2014, 0x201c, 0x201d, 0x2018, 0x2019, 0x00f7, 0x201e, 0x040e, 0x045e, 0x040f, 0x045f, 0x2116, 0x0401, 0x0451, 0x044f, 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e, 0x043f, 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044a, 0x044b, 0x044c, 0x044d, 0x044e, 0x20ac }, { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0xca, 0xa3, 0xa4, 0xa9, 0xc7, 0xc2, 0xa8, 0xa1, 0xb1, 0xb5, 0xa6, 0xc8, 0xd6, 0xc4, 0xdd, 0xab, 0xae, 0xb8, 0xc1, 0xa7, 0xba, 0xb7, 0xbc, 0xbe, 0xcb, 0xcd, 0xd8, 0xda, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xdf, 0xde, 0xac, 0xaf, 0xb9, 0xcf, 0xb4, 0xbb, 0xc0, 0xbd, 0xbf, 0xcc, 0xce, 0xd9, 0xdb, 0xa2, 0xb6, 0xd0, 0xd1, 0xd4, 0xd5, 0xd2, 0xd3, 0xd7, 0xa0, 0xa5, 0xc9, 0xff, 0xdc, 0xaa, 0xc6, 0xc3, 0xb0, 0xc5, 0xad, 0xb2, 0xb3 }, 256 }; const charset_spec charset_CS_MAC_CYRILLIC = { CS_MAC_CYRILLIC, read_sbcs, write_sbcs, &data_CS_MAC_CYRILLIC }; static const sbcs_data data_CS_MAC_THAI = { { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x00ab, 0x00bb, 0x2026, 0x0e48, 0x0e49, 0x0e4a, 0x0e4b, 0x0e4c, 0x0e48, 0x0e49, 0x0e4a, 0x0e4b, 0x0e4c, 0x201c, 0x201d, 0x0e4d, ERROR , 0x2022, 0x0e31, 0x0e47, 0x0e34, 0x0e35, 0x0e36, 0x0e37, 0x0e48, 0x0e49, 0x0e4a, 0x0e4b, 0x0e4c, 0x2018, 0x2019, ERROR , 0x00a0, 0x0e01, 0x0e02, 0x0e03, 0x0e04, 0x0e05, 0x0e06, 0x0e07, 0x0e08, 0x0e09, 0x0e0a, 0x0e0b, 0x0e0c, 0x0e0d, 0x0e0e, 0x0e0f, 0x0e10, 0x0e11, 0x0e12, 0x0e13, 0x0e14, 0x0e15, 0x0e16, 0x0e17, 0x0e18, 0x0e19, 0x0e1a, 0x0e1b, 0x0e1c, 0x0e1d, 0x0e1e, 0x0e1f, 0x0e20, 0x0e21, 0x0e22, 0x0e23, 0x0e24, 0x0e25, 0x0e26, 0x0e27, 0x0e28, 0x0e29, 0x0e2a, 0x0e2b, 0x0e2c, 0x0e2d, 0x0e2e, 0x0e2f, 0x0e30, 0x0e31, 0x0e32, 0x0e33, 0x0e34, 0x0e35, 0x0e36, 0x0e37, 0x0e38, 0x0e39, 0x0e3a, 0x2060, 0x200b, 0x2013, 0x2014, 0x0e3f, 0x0e40, 0x0e41, 0x0e42, 0x0e43, 0x0e44, 0x0e45, 0x0e46, 0x0e47, 0x0e48, 0x0e49, 0x0e4a, 0x0e4b, 0x0e4c, 0x0e4d, 0x2122, 0x0e4f, 0x0e50, 0x0e51, 0x0e52, 0x0e53, 0x0e54, 0x0e55, 0x0e56, 0x0e57, 0x0e58, 0x0e59, 0x00ae, 0x00a9, ERROR , ERROR , ERROR , ERROR }, { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0xa0, 0xfb, 0x80, 0xfa, 0x81, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xdc, 0xdd, 0xde, 0x9d, 0x9e, 0x8d, 0x8e, 0x91, 0x82, 0xdb, 0xee }, 228 }; const charset_spec charset_CS_MAC_THAI = { CS_MAC_THAI, read_sbcs, write_sbcs, &data_CS_MAC_THAI }; static const sbcs_data data_CS_MAC_CENTEURO = { { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x00c4, 0x0100, 0x0101, 0x00c9, 0x0104, 0x00d6, 0x00dc, 0x00e1, 0x0105, 0x010c, 0x00e4, 0x010d, 0x0106, 0x0107, 0x00e9, 0x0179, 0x017a, 0x010e, 0x00ed, 0x010f, 0x0112, 0x0113, 0x0116, 0x00f3, 0x0117, 0x00f4, 0x00f6, 0x00f5, 0x00fa, 0x011a, 0x011b, 0x00fc, 0x2020, 0x00b0, 0x0118, 0x00a3, 0x00a7, 0x2022, 0x00b6, 0x00df, 0x00ae, 0x00a9, 0x2122, 0x0119, 0x00a8, 0x2260, 0x0123, 0x012e, 0x012f, 0x012a, 0x2264, 0x2265, 0x012b, 0x0136, 0x2202, 0x2211, 0x0142, 0x013b, 0x013c, 0x013d, 0x013e, 0x0139, 0x013a, 0x0145, 0x0146, 0x0143, 0x00ac, 0x221a, 0x0144, 0x0147, 0x2206, 0x00ab, 0x00bb, 0x2026, 0x00a0, 0x0148, 0x0150, 0x00d5, 0x0151, 0x014c, 0x2013, 0x2014, 0x201c, 0x201d, 0x2018, 0x2019, 0x00f7, 0x25ca, 0x014d, 0x0154, 0x0155, 0x0158, 0x2039, 0x203a, 0x0159, 0x0156, 0x0157, 0x0160, 0x201a, 0x201e, 0x0161, 0x015a, 0x015b, 0x00c1, 0x0164, 0x0165, 0x00cd, 0x017d, 0x017e, 0x016a, 0x00d3, 0x00d4, 0x016b, 0x016e, 0x00da, 0x016f, 0x0170, 0x0171, 0x0172, 0x0173, 0x00dd, 0x00fd, 0x0137, 0x017b, 0x0141, 0x017c, 0x0122, 0x02c7 }, { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0xca, 0xa3, 0xa4, 0xac, 0xa9, 0xc7, 0xc2, 0xa8, 0xa1, 0xa6, 0xc8, 0xe7, 0x80, 0x83, 0xea, 0xee, 0xef, 0xcd, 0x85, 0xf2, 0x86, 0xf8, 0xa7, 0x87, 0x8a, 0x8e, 0x92, 0x97, 0x99, 0x9b, 0x9a, 0xd6, 0x9c, 0x9f, 0xf9, 0x81, 0x82, 0x84, 0x88, 0x8c, 0x8d, 0x89, 0x8b, 0x91, 0x93, 0x94, 0x95, 0x96, 0x98, 0xa2, 0xab, 0x9d, 0x9e, 0xfe, 0xae, 0xb1, 0xb4, 0xaf, 0xb0, 0xb5, 0xfa, 0xbd, 0xbe, 0xb9, 0xba, 0xbb, 0xbc, 0xfc, 0xb8, 0xc1, 0xc4, 0xbf, 0xc0, 0xc5, 0xcb, 0xcf, 0xd8, 0xcc, 0xce, 0xd9, 0xda, 0xdf, 0xe0, 0xdb, 0xde, 0xe5, 0xe6, 0xe1, 0xe4, 0xe8, 0xe9, 0xed, 0xf0, 0xf1, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0x8f, 0x90, 0xfb, 0xfd, 0xeb, 0xec, 0xff, 0xd0, 0xd1, 0xd4, 0xd5, 0xe2, 0xd2, 0xd3, 0xe3, 0xa0, 0xa5, 0xc9, 0xdc, 0xdd, 0xaa, 0xb6, 0xc6, 0xb7, 0xc3, 0xad, 0xb2, 0xb3, 0xd7 }, 256 }; const charset_spec charset_CS_MAC_CENTEURO = { CS_MAC_CENTEURO, read_sbcs, write_sbcs, &data_CS_MAC_CENTEURO }; static const sbcs_data data_CS_MAC_SYMBOL = { { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x2200, 0x0023, 0x2203, 0x0025, 0x0026, 0x220d, 0x0028, 0x0029, 0x2217, 0x002b, 0x002c, 0x2212, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x2245, 0x0391, 0x0392, 0x03a7, 0x0394, 0x0395, 0x03a6, 0x0393, 0x0397, 0x0399, 0x03d1, 0x039a, 0x039b, 0x039c, 0x039d, 0x039f, 0x03a0, 0x0398, 0x03a1, 0x03a3, 0x03a4, 0x03a5, 0x03c2, 0x03a9, 0x039e, 0x03a8, 0x0396, 0x005b, 0x2234, 0x005d, 0x22a5, 0x005f, 0xf8e5, 0x03b1, 0x03b2, 0x03c7, 0x03b4, 0x03b5, 0x03c6, 0x03b3, 0x03b7, 0x03b9, 0x03d5, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03bf, 0x03c0, 0x03b8, 0x03c1, 0x03c3, 0x03c4, 0x03c5, 0x03d6, 0x03c9, 0x03be, 0x03c8, 0x03b6, 0x007b, 0x007c, 0x007d, 0x223c, 0x007f, ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , 0x20ac, 0x03d2, 0x2032, 0x2264, 0x2044, 0x221e, 0x0192, 0x2663, 0x2666, 0x2665, 0x2660, 0x2194, 0x2190, 0x2191, 0x2192, 0x2193, 0x00b0, 0x00b1, 0x2033, 0x2265, 0x00d7, 0x221d, 0x2202, 0x2022, 0x00f7, 0x2260, 0x2261, 0x2248, 0x2026, 0xf8e6, 0x23af, 0x21b5, 0x2135, 0x2111, 0x211c, 0x2118, 0x2297, 0x2295, 0x2205, 0x2229, 0x222a, 0x2283, 0x2287, 0x2284, 0x2282, 0x2286, 0x2208, 0x2209, 0x2220, 0x2207, 0x00ae, 0x00a9, 0x2122, 0x220f, 0x221a, 0x22c5, 0x00ac, 0x2227, 0x2228, 0x21d4, 0x21d0, 0x21d1, 0x21d2, 0x21d3, 0x22c4, 0x3008, 0x00ae, 0x00a9, 0x2122, 0x2211, 0x239b, 0x239c, 0x239d, 0x23a1, 0x23a2, 0x23a3, 0x23a7, 0x23a8, 0x23a9, 0x23aa, 0xf8ff, 0x3009, 0x222b, 0x2320, 0x23ae, 0x2321, 0x239e, 0x239f, 0x23a0, 0x23a4, 0x23a5, 0x23a6, 0x23ab, 0x23ac, 0x23ad, ERROR }, { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x23, 0x25, 0x26, 0x28, 0x29, 0x2b, 0x2c, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x5b, 0x5d, 0x5f, 0x7b, 0x7c, 0x7d, 0x7f, 0xd3, 0xd8, 0xd2, 0xb0, 0xb1, 0xb4, 0xb8, 0xa6, 0x41, 0x42, 0x47, 0x44, 0x45, 0x5a, 0x48, 0x51, 0x49, 0x4b, 0x4c, 0x4d, 0x4e, 0x58, 0x4f, 0x50, 0x52, 0x53, 0x54, 0x55, 0x46, 0x43, 0x59, 0x57, 0x61, 0x62, 0x67, 0x64, 0x65, 0x7a, 0x68, 0x71, 0x69, 0x6b, 0x6c, 0x6d, 0x6e, 0x78, 0x6f, 0x70, 0x72, 0x56, 0x73, 0x74, 0x75, 0x66, 0x63, 0x79, 0x77, 0x4a, 0xa1, 0x6a, 0x76, 0xb7, 0xbc, 0xa2, 0xb2, 0xa4, 0xa0, 0xc1, 0xc3, 0xc2, 0xd4, 0xc0, 0xac, 0xad, 0xae, 0xaf, 0xab, 0xbf, 0xdc, 0xdd, 0xde, 0xdf, 0xdb, 0x22, 0xb6, 0x24, 0xc6, 0xd1, 0xce, 0xcf, 0x27, 0xd5, 0xe5, 0x2d, 0x2a, 0xd6, 0xb5, 0xa5, 0xd0, 0xd9, 0xda, 0xc7, 0xc8, 0xf2, 0x5c, 0x7e, 0x40, 0xbb, 0xb9, 0xba, 0xa3, 0xb3, 0xcc, 0xc9, 0xcb, 0xcd, 0xca, 0xc5, 0xc4, 0x5e, 0xe0, 0xd7, 0xf3, 0xf5, 0xe6, 0xe7, 0xe8, 0xf6, 0xf7, 0xf8, 0xe9, 0xea, 0xeb, 0xf9, 0xfa, 0xfb, 0xec, 0xed, 0xee, 0xef, 0xfc, 0xfd, 0xfe, 0xf4, 0xbe, 0xaa, 0xa7, 0xa9, 0xa8, 0xe1, 0xf1, 0x60, 0xbd, 0xf0 }, 220 }; const charset_spec charset_CS_MAC_SYMBOL = { CS_MAC_SYMBOL, read_sbcs, write_sbcs, &data_CS_MAC_SYMBOL }; static const sbcs_data data_CS_MAC_DINGBATS = { { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x2701, 0x2702, 0x2703, 0x2704, 0x260e, 0x2706, 0x2707, 0x2708, 0x2709, 0x261b, 0x261e, 0x270c, 0x270d, 0x270e, 0x270f, 0x2710, 0x2711, 0x2712, 0x2713, 0x2714, 0x2715, 0x2716, 0x2717, 0x2718, 0x2719, 0x271a, 0x271b, 0x271c, 0x271d, 0x271e, 0x271f, 0x2720, 0x2721, 0x2722, 0x2723, 0x2724, 0x2725, 0x2726, 0x2727, 0x2605, 0x2729, 0x272a, 0x272b, 0x272c, 0x272d, 0x272e, 0x272f, 0x2730, 0x2731, 0x2732, 0x2733, 0x2734, 0x2735, 0x2736, 0x2737, 0x2738, 0x2739, 0x273a, 0x273b, 0x273c, 0x273d, 0x273e, 0x273f, 0x2740, 0x2741, 0x2742, 0x2743, 0x2744, 0x2745, 0x2746, 0x2747, 0x2748, 0x2749, 0x274a, 0x274b, 0x25cf, 0x274d, 0x25a0, 0x274f, 0x2750, 0x2751, 0x2752, 0x25b2, 0x25bc, 0x25c6, 0x2756, 0x25d7, 0x2758, 0x2759, 0x275a, 0x275b, 0x275c, 0x275d, 0x275e, 0x007f, 0x2768, 0x2769, 0x276a, 0x276b, 0x276c, 0x276d, 0x276e, 0x276f, 0x2770, 0x2771, 0x2772, 0x2773, 0x2774, 0x2775, ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , ERROR , 0x2761, 0x2762, 0x2763, 0x2764, 0x2765, 0x2766, 0x2767, 0x2663, 0x2666, 0x2665, 0x2660, 0x2460, 0x2461, 0x2462, 0x2463, 0x2464, 0x2465, 0x2466, 0x2467, 0x2468, 0x2469, 0x2776, 0x2777, 0x2778, 0x2779, 0x277a, 0x277b, 0x277c, 0x277d, 0x277e, 0x277f, 0x2780, 0x2781, 0x2782, 0x2783, 0x2784, 0x2785, 0x2786, 0x2787, 0x2788, 0x2789, 0x278a, 0x278b, 0x278c, 0x278d, 0x278e, 0x278f, 0x2790, 0x2791, 0x2792, 0x2793, 0x2794, 0x2192, 0x2194, 0x2195, 0x2798, 0x2799, 0x279a, 0x279b, 0x279c, 0x279d, 0x279e, 0x279f, 0x27a0, 0x27a1, 0x27a2, 0x27a3, 0x27a4, 0x27a5, 0x27a6, 0x27a7, 0x27a8, 0x27a9, 0x27aa, 0x27ab, 0x27ac, 0x27ad, 0x27ae, 0x27af, ERROR , 0x27b1, 0x27b2, 0x27b3, 0x27b4, 0x27b5, 0x27b6, 0x27b7, 0x27b8, 0x27b9, 0x27ba, 0x27bb, 0x27bc, 0x27bd, 0x27be, ERROR }, { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x7f, 0xd5, 0xd6, 0xd7, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0x6e, 0x73, 0x74, 0x75, 0x6c, 0x77, 0x48, 0x25, 0x2a, 0x2b, 0xab, 0xa8, 0xaa, 0xa9, 0x21, 0x22, 0x23, 0x24, 0x26, 0x27, 0x28, 0x29, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6d, 0x6f, 0x70, 0x71, 0x72, 0x76, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe }, 235 }; const charset_spec charset_CS_MAC_DINGBATS = { CS_MAC_DINGBATS, read_sbcs, write_sbcs, &data_CS_MAC_DINGBATS }; static const sbcs_data data_CS_MAC_ROMAN_OLD = { { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x00c4, 0x00c5, 0x00c7, 0x00c9, 0x00d1, 0x00d6, 0x00dc, 0x00e1, 0x00e0, 0x00e2, 0x00e4, 0x00e3, 0x00e5, 0x00e7, 0x00e9, 0x00e8, 0x00ea, 0x00eb, 0x00ed, 0x00ec, 0x00ee, 0x00ef, 0x00f1, 0x00f3, 0x00f2, 0x00f4, 0x00f6, 0x00f5, 0x00fa, 0x00f9, 0x00fb, 0x00fc, 0x2020, 0x00b0, 0x00a2, 0x00a3, 0x00a7, 0x2022, 0x00b6, 0x00df, 0x00ae, 0x00a9, 0x2122, 0x00b4, 0x00a8, 0x2260, 0x00c6, 0x00d8, 0x221e, 0x00b1, 0x2264, 0x2265, 0x00a5, 0x00b5, 0x2202, 0x2211, 0x220f, 0x03c0, 0x222b, 0x00aa, 0x00ba, 0x03a9, 0x00e6, 0x00f8, 0x00bf, 0x00a1, 0x00ac, 0x221a, 0x0192, 0x2248, 0x2206, 0x00ab, 0x00bb, 0x2026, 0x00a0, 0x00c0, 0x00c3, 0x00d5, 0x0152, 0x0153, 0x2013, 0x2014, 0x201c, 0x201d, 0x2018, 0x2019, 0x00f7, 0x25ca, 0x00ff, 0x0178, 0x2044, 0x00a4, 0x2039, 0x203a, 0xfb01, 0xfb02, 0x2021, 0x00b7, 0x201a, 0x201e, 0x2030, 0x00c2, 0x00ca, 0x00c1, 0x00cb, 0x00c8, 0x00cd, 0x00ce, 0x00cf, 0x00cc, 0x00d3, 0x00d4, 0xf8ff, 0x00d2, 0x00da, 0x00db, 0x00d9, 0x0131, 0x02c6, 0x02dc, 0x00af, 0x02d8, 0x02d9, 0x02da, 0x00b8, 0x02dd, 0x02db, 0x02c7 }, { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0xca, 0xc1, 0xa2, 0xa3, 0xdb, 0xb4, 0xa4, 0xac, 0xa9, 0xbb, 0xc7, 0xc2, 0xa8, 0xf8, 0xa1, 0xb1, 0xab, 0xb5, 0xa6, 0xe1, 0xfc, 0xbc, 0xc8, 0xc0, 0xcb, 0xe7, 0xe5, 0xcc, 0x80, 0x81, 0xae, 0x82, 0xe9, 0x83, 0xe6, 0xe8, 0xed, 0xea, 0xeb, 0xec, 0x84, 0xf1, 0xee, 0xef, 0xcd, 0x85, 0xaf, 0xf4, 0xf2, 0xf3, 0x86, 0xa7, 0x88, 0x87, 0x89, 0x8b, 0x8a, 0x8c, 0xbe, 0x8d, 0x8f, 0x8e, 0x90, 0x91, 0x93, 0x92, 0x94, 0x95, 0x96, 0x98, 0x97, 0x99, 0x9b, 0x9a, 0xd6, 0xbf, 0x9d, 0x9c, 0x9e, 0x9f, 0xd8, 0xf5, 0xce, 0xcf, 0xd9, 0xc4, 0xf6, 0xff, 0xf9, 0xfa, 0xfb, 0xfe, 0xf7, 0xfd, 0xbd, 0xb9, 0xd0, 0xd1, 0xd4, 0xd5, 0xe2, 0xd2, 0xd3, 0xe3, 0xa0, 0xe0, 0xa5, 0xc9, 0xe4, 0xdc, 0xdd, 0xda, 0xaa, 0xb6, 0xc6, 0xb8, 0xb7, 0xc3, 0xb0, 0xba, 0xc5, 0xad, 0xb2, 0xb3, 0xd7, 0xf0, 0xde, 0xdf }, 256 }; const charset_spec charset_CS_MAC_ROMAN_OLD = { CS_MAC_ROMAN_OLD, read_sbcs, write_sbcs, &data_CS_MAC_ROMAN_OLD }; static const sbcs_data data_CS_MAC_CROATIAN_OLD = { { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x00c4, 0x00c5, 0x00c7, 0x00c9, 0x00d1, 0x00d6, 0x00dc, 0x00e1, 0x00e0, 0x00e2, 0x00e4, 0x00e3, 0x00e5, 0x00e7, 0x00e9, 0x00e8, 0x00ea, 0x00eb, 0x00ed, 0x00ec, 0x00ee, 0x00ef, 0x00f1, 0x00f3, 0x00f2, 0x00f4, 0x00f6, 0x00f5, 0x00fa, 0x00f9, 0x00fb, 0x00fc, 0x2020, 0x00b0, 0x00a2, 0x00a3, 0x00a7, 0x2022, 0x00b6, 0x00df, 0x00ae, 0x0160, 0x2122, 0x00b4, 0x00a8, 0x2260, 0x017d, 0x00d8, 0x221e, 0x00b1, 0x2264, 0x2265, 0x2206, 0x00b5, 0x2202, 0x2211, 0x220f, 0x0161, 0x222b, 0x00aa, 0x00ba, 0x03a9, 0x017e, 0x00f8, 0x00bf, 0x00a1, 0x00ac, 0x221a, 0x0192, 0x2248, 0x0106, 0x00ab, 0x010c, 0x2026, 0x00a0, 0x00c0, 0x00c3, 0x00d5, 0x0152, 0x0153, 0x0110, 0x2014, 0x201c, 0x201d, 0x2018, 0x2019, 0x00f7, 0x25ca, 0xf8ff, 0x00a9, 0x2044, 0x00a4, 0x2039, 0x203a, 0x00c6, 0x00bb, 0x2013, 0x00b7, 0x201a, 0x201e, 0x2030, 0x00c2, 0x0107, 0x00c1, 0x010d, 0x00c8, 0x00cd, 0x00ce, 0x00cf, 0x00cc, 0x00d3, 0x00d4, 0x0111, 0x00d2, 0x00da, 0x00db, 0x00d9, 0x0131, 0x02c6, 0x02dc, 0x00af, 0x03c0, 0x00cb, 0x02da, 0x00b8, 0x00ca, 0x00e6, 0x02c7 }, { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0xca, 0xc1, 0xa2, 0xa3, 0xdb, 0xa4, 0xac, 0xd9, 0xbb, 0xc7, 0xc2, 0xa8, 0xf8, 0xa1, 0xb1, 0xab, 0xb5, 0xa6, 0xe1, 0xfc, 0xbc, 0xdf, 0xc0, 0xcb, 0xe7, 0xe5, 0xcc, 0x80, 0x81, 0xde, 0x82, 0xe9, 0x83, 0xfd, 0xfa, 0xed, 0xea, 0xeb, 0xec, 0x84, 0xf1, 0xee, 0xef, 0xcd, 0x85, 0xaf, 0xf4, 0xf2, 0xf3, 0x86, 0xa7, 0x88, 0x87, 0x89, 0x8b, 0x8a, 0x8c, 0xfe, 0x8d, 0x8f, 0x8e, 0x90, 0x91, 0x93, 0x92, 0x94, 0x95, 0x96, 0x98, 0x97, 0x99, 0x9b, 0x9a, 0xd6, 0xbf, 0x9d, 0x9c, 0x9e, 0x9f, 0xc6, 0xe6, 0xc8, 0xe8, 0xd0, 0xf0, 0xf5, 0xce, 0xcf, 0xa9, 0xb9, 0xae, 0xbe, 0xc4, 0xf6, 0xff, 0xfb, 0xf7, 0xbd, 0xf9, 0xe0, 0xd1, 0xd4, 0xd5, 0xe2, 0xd2, 0xd3, 0xe3, 0xa0, 0xa5, 0xc9, 0xe4, 0xdc, 0xdd, 0xda, 0xaa, 0xb6, 0xb4, 0xb8, 0xb7, 0xc3, 0xb0, 0xba, 0xc5, 0xad, 0xb2, 0xb3, 0xd7, 0xd8 }, 256 }; const charset_spec charset_CS_MAC_CROATIAN_OLD = { CS_MAC_CROATIAN_OLD, read_sbcs, write_sbcs, &data_CS_MAC_CROATIAN_OLD }; static const sbcs_data data_CS_MAC_ICELAND_OLD = { { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x00c4, 0x00c5, 0x00c7, 0x00c9, 0x00d1, 0x00d6, 0x00dc, 0x00e1, 0x00e0, 0x00e2, 0x00e4, 0x00e3, 0x00e5, 0x00e7, 0x00e9, 0x00e8, 0x00ea, 0x00eb, 0x00ed, 0x00ec, 0x00ee, 0x00ef, 0x00f1, 0x00f3, 0x00f2, 0x00f4, 0x00f6, 0x00f5, 0x00fa, 0x00f9, 0x00fb, 0x00fc, 0x00dd, 0x00b0, 0x00a2, 0x00a3, 0x00a7, 0x2022, 0x00b6, 0x00df, 0x00ae, 0x00a9, 0x2122, 0x00b4, 0x00a8, 0x2260, 0x00c6, 0x00d8, 0x221e, 0x00b1, 0x2264, 0x2265, 0x00a5, 0x00b5, 0x2202, 0x2211, 0x220f, 0x03c0, 0x222b, 0x00aa, 0x00ba, 0x03a9, 0x00e6, 0x00f8, 0x00bf, 0x00a1, 0x00ac, 0x221a, 0x0192, 0x2248, 0x2206, 0x00ab, 0x00bb, 0x2026, 0x00a0, 0x00c0, 0x00c3, 0x00d5, 0x0152, 0x0153, 0x2013, 0x2014, 0x201c, 0x201d, 0x2018, 0x2019, 0x00f7, 0x25ca, 0x00ff, 0x0178, 0x2044, 0x00a4, 0x00d0, 0x00f0, 0x00de, 0x00fe, 0x00fd, 0x00b7, 0x201a, 0x201e, 0x2030, 0x00c2, 0x00ca, 0x00c1, 0x00cb, 0x00c8, 0x00cd, 0x00ce, 0x00cf, 0x00cc, 0x00d3, 0x00d4, 0xf8ff, 0x00d2, 0x00da, 0x00db, 0x00d9, 0x0131, 0x02c6, 0x02dc, 0x00af, 0x02d8, 0x02d9, 0x02da, 0x00b8, 0x02dd, 0x02db, 0x02c7 }, { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0xca, 0xc1, 0xa2, 0xa3, 0xdb, 0xb4, 0xa4, 0xac, 0xa9, 0xbb, 0xc7, 0xc2, 0xa8, 0xf8, 0xa1, 0xb1, 0xab, 0xb5, 0xa6, 0xe1, 0xfc, 0xbc, 0xc8, 0xc0, 0xcb, 0xe7, 0xe5, 0xcc, 0x80, 0x81, 0xae, 0x82, 0xe9, 0x83, 0xe6, 0xe8, 0xed, 0xea, 0xeb, 0xec, 0xdc, 0x84, 0xf1, 0xee, 0xef, 0xcd, 0x85, 0xaf, 0xf4, 0xf2, 0xf3, 0x86, 0xa0, 0xde, 0xa7, 0x88, 0x87, 0x89, 0x8b, 0x8a, 0x8c, 0xbe, 0x8d, 0x8f, 0x8e, 0x90, 0x91, 0x93, 0x92, 0x94, 0x95, 0xdd, 0x96, 0x98, 0x97, 0x99, 0x9b, 0x9a, 0xd6, 0xbf, 0x9d, 0x9c, 0x9e, 0x9f, 0xe0, 0xdf, 0xd8, 0xf5, 0xce, 0xcf, 0xd9, 0xc4, 0xf6, 0xff, 0xf9, 0xfa, 0xfb, 0xfe, 0xf7, 0xfd, 0xbd, 0xb9, 0xd0, 0xd1, 0xd4, 0xd5, 0xe2, 0xd2, 0xd3, 0xe3, 0xa5, 0xc9, 0xe4, 0xda, 0xaa, 0xb6, 0xc6, 0xb8, 0xb7, 0xc3, 0xb0, 0xba, 0xc5, 0xad, 0xb2, 0xb3, 0xd7, 0xf0 }, 256 }; const charset_spec charset_CS_MAC_ICELAND_OLD = { CS_MAC_ICELAND_OLD, read_sbcs, write_sbcs, &data_CS_MAC_ICELAND_OLD }; static const sbcs_data data_CS_MAC_ROMANIAN_OLD = { { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x00c4, 0x00c5, 0x00c7, 0x00c9, 0x00d1, 0x00d6, 0x00dc, 0x00e1, 0x00e0, 0x00e2, 0x00e4, 0x00e3, 0x00e5, 0x00e7, 0x00e9, 0x00e8, 0x00ea, 0x00eb, 0x00ed, 0x00ec, 0x00ee, 0x00ef, 0x00f1, 0x00f3, 0x00f2, 0x00f4, 0x00f6, 0x00f5, 0x00fa, 0x00f9, 0x00fb, 0x00fc, 0x2020, 0x00b0, 0x00a2, 0x00a3, 0x00a7, 0x2022, 0x00b6, 0x00df, 0x00ae, 0x00a9, 0x2122, 0x00b4, 0x00a8, 0x2260, 0x0102, 0x0218, 0x221e, 0x00b1, 0x2264, 0x2265, 0x00a5, 0x00b5, 0x2202, 0x2211, 0x220f, 0x03c0, 0x222b, 0x00aa, 0x00ba, 0x03a9, 0x0103, 0x0219, 0x00bf, 0x00a1, 0x00ac, 0x221a, 0x0192, 0x2248, 0x2206, 0x00ab, 0x00bb, 0x2026, 0x00a0, 0x00c0, 0x00c3, 0x00d5, 0x0152, 0x0153, 0x2013, 0x2014, 0x201c, 0x201d, 0x2018, 0x2019, 0x00f7, 0x25ca, 0x00ff, 0x0178, 0x2044, 0x00a4, 0x2039, 0x203a, 0x021a, 0x021b, 0x2021, 0x00b7, 0x201a, 0x201e, 0x2030, 0x00c2, 0x00ca, 0x00c1, 0x00cb, 0x00c8, 0x00cd, 0x00ce, 0x00cf, 0x00cc, 0x00d3, 0x00d4, 0xf8ff, 0x00d2, 0x00da, 0x00db, 0x00d9, 0x0131, 0x02c6, 0x02dc, 0x00af, 0x02d8, 0x02d9, 0x02da, 0x00b8, 0x02dd, 0x02db, 0x02c7 }, { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0xca, 0xc1, 0xa2, 0xa3, 0xdb, 0xb4, 0xa4, 0xac, 0xa9, 0xbb, 0xc7, 0xc2, 0xa8, 0xf8, 0xa1, 0xb1, 0xab, 0xb5, 0xa6, 0xe1, 0xfc, 0xbc, 0xc8, 0xc0, 0xcb, 0xe7, 0xe5, 0xcc, 0x80, 0x81, 0x82, 0xe9, 0x83, 0xe6, 0xe8, 0xed, 0xea, 0xeb, 0xec, 0x84, 0xf1, 0xee, 0xef, 0xcd, 0x85, 0xf4, 0xf2, 0xf3, 0x86, 0xa7, 0x88, 0x87, 0x89, 0x8b, 0x8a, 0x8c, 0x8d, 0x8f, 0x8e, 0x90, 0x91, 0x93, 0x92, 0x94, 0x95, 0x96, 0x98, 0x97, 0x99, 0x9b, 0x9a, 0xd6, 0x9d, 0x9c, 0x9e, 0x9f, 0xd8, 0xae, 0xbe, 0xf5, 0xce, 0xcf, 0xd9, 0xc4, 0xaf, 0xbf, 0xde, 0xdf, 0xf6, 0xff, 0xf9, 0xfa, 0xfb, 0xfe, 0xf7, 0xfd, 0xbd, 0xb9, 0xd0, 0xd1, 0xd4, 0xd5, 0xe2, 0xd2, 0xd3, 0xe3, 0xa0, 0xe0, 0xa5, 0xc9, 0xe4, 0xdc, 0xdd, 0xda, 0xaa, 0xb6, 0xc6, 0xb8, 0xb7, 0xc3, 0xb0, 0xba, 0xc5, 0xad, 0xb2, 0xb3, 0xd7, 0xf0 }, 256 }; const charset_spec charset_CS_MAC_ROMANIAN_OLD = { CS_MAC_ROMANIAN_OLD, read_sbcs, write_sbcs, &data_CS_MAC_ROMANIAN_OLD }; static const sbcs_data data_CS_MAC_GREEK_OLD = { { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x00c4, 0x00b9, 0x00b2, 0x00c9, 0x00b3, 0x00d6, 0x00dc, 0x0385, 0x00e0, 0x00e2, 0x00e4, 0x0384, 0x00a8, 0x00e7, 0x00e9, 0x00e8, 0x00ea, 0x00eb, 0x00a3, 0x2122, 0x00ee, 0x00ef, 0x2022, 0x00bd, 0x2030, 0x00f4, 0x00f6, 0x00a6, 0x00ad, 0x00f9, 0x00fb, 0x00fc, 0x2020, 0x0393, 0x0394, 0x0398, 0x039b, 0x039e, 0x03a0, 0x00df, 0x00ae, 0x00a9, 0x03a3, 0x03aa, 0x00a7, 0x2260, 0x00b0, 0x00b7, 0x0391, 0x00b1, 0x2264, 0x2265, 0x00a5, 0x0392, 0x0395, 0x0396, 0x0397, 0x0399, 0x039a, 0x039c, 0x03a6, 0x03ab, 0x03a8, 0x03a9, 0x03ac, 0x039d, 0x00ac, 0x039f, 0x03a1, 0x2248, 0x03a4, 0x00ab, 0x00bb, 0x2026, 0x00a0, 0x03a5, 0x03a7, 0x0386, 0x0388, 0x0153, 0x2013, 0x2015, 0x201c, 0x201d, 0x2018, 0x2019, 0x00f7, 0x0389, 0x038a, 0x038c, 0x038e, 0x03ad, 0x03ae, 0x03af, 0x03cc, 0x038f, 0x03cd, 0x03b1, 0x03b2, 0x03c8, 0x03b4, 0x03b5, 0x03c6, 0x03b3, 0x03b7, 0x03b9, 0x03be, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03bf, 0x03c0, 0x03ce, 0x03c1, 0x03c3, 0x03c4, 0x03b8, 0x03c9, 0x03c2, 0x03c7, 0x03c5, 0x03b6, 0x03ca, 0x03cb, 0x0390, 0x03b0, ERROR }, { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0xca, 0x92, 0xb4, 0x9b, 0xac, 0x8c, 0xa9, 0xc7, 0xc2, 0x9c, 0xa8, 0xae, 0xb1, 0x82, 0x84, 0xaf, 0x81, 0xc8, 0x97, 0x80, 0x83, 0x85, 0x86, 0xa7, 0x88, 0x89, 0x8a, 0x8d, 0x8f, 0x8e, 0x90, 0x91, 0x94, 0x95, 0x99, 0x9a, 0xd6, 0x9d, 0x9e, 0x9f, 0xcf, 0x8b, 0x87, 0xcd, 0xce, 0xd7, 0xd8, 0xd9, 0xda, 0xdf, 0xfd, 0xb0, 0xb5, 0xa1, 0xa2, 0xb6, 0xb7, 0xb8, 0xa3, 0xb9, 0xba, 0xa4, 0xbb, 0xc1, 0xa5, 0xc3, 0xa6, 0xc4, 0xaa, 0xc6, 0xcb, 0xbc, 0xcc, 0xbe, 0xbf, 0xab, 0xbd, 0xc0, 0xdb, 0xdc, 0xdd, 0xfe, 0xe1, 0xe2, 0xe7, 0xe4, 0xe5, 0xfa, 0xe8, 0xf5, 0xe9, 0xeb, 0xec, 0xed, 0xee, 0xea, 0xef, 0xf0, 0xf2, 0xf7, 0xf3, 0xf4, 0xf9, 0xe6, 0xf8, 0xe3, 0xf6, 0xfb, 0xfc, 0xde, 0xe0, 0xf1, 0xd0, 0xd1, 0xd4, 0xd5, 0xd2, 0xd3, 0xa0, 0x96, 0xc9, 0x98, 0x93, 0xc5, 0xad, 0xb2, 0xb3 }, 255 }; const charset_spec charset_CS_MAC_GREEK_OLD = { CS_MAC_GREEK_OLD, read_sbcs, write_sbcs, &data_CS_MAC_GREEK_OLD }; static const sbcs_data data_CS_MAC_CYRILLIC_OLD = { { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, 0x041f, 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042a, 0x042b, 0x042c, 0x042d, 0x042e, 0x042f, 0x2020, 0x00b0, 0x00a2, 0x00a3, 0x00a7, 0x2022, 0x00b6, 0x0406, 0x00ae, 0x00a9, 0x2122, 0x0402, 0x0452, 0x2260, 0x0403, 0x0453, 0x221e, 0x00b1, 0x2264, 0x2265, 0x0456, 0x00b5, 0x2022, 0x0408, 0x0404, 0x0454, 0x0407, 0x0457, 0x0409, 0x0459, 0x040a, 0x045a, 0x0458, 0x0405, 0x00ac, 0x221a, 0x0192, 0x2248, 0x2206, 0x00ab, 0x00bb, 0x2026, 0x00a0, 0x040b, 0x045b, 0x040c, 0x045c, 0x0455, 0x2013, 0x2014, 0x201c, 0x201d, 0x2018, 0x2019, 0x00f7, 0x201e, 0x040e, 0x045e, 0x040f, 0x045f, 0x2116, 0x0401, 0x0451, 0x044f, 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e, 0x043f, 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044a, 0x044b, 0x044c, 0x044d, 0x044e, 0x00a4 }, { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0xca, 0xa2, 0xa3, 0xff, 0xa4, 0xa9, 0xc7, 0xc2, 0xa8, 0xa1, 0xb1, 0xb5, 0xa6, 0xc8, 0xd6, 0xc4, 0xdd, 0xab, 0xae, 0xb8, 0xc1, 0xa7, 0xba, 0xb7, 0xbc, 0xbe, 0xcb, 0xcd, 0xd8, 0xda, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xdf, 0xde, 0xac, 0xaf, 0xb9, 0xcf, 0xb4, 0xbb, 0xc0, 0xbd, 0xbf, 0xcc, 0xce, 0xd9, 0xdb, 0xd0, 0xd1, 0xd4, 0xd5, 0xd2, 0xd3, 0xd7, 0xa0, 0xa5, 0xc9, 0xdc, 0xaa, 0xc6, 0xc3, 0xb0, 0xc5, 0xad, 0xb2, 0xb3 }, 255 }; const charset_spec charset_CS_MAC_CYRILLIC_OLD = { CS_MAC_CYRILLIC_OLD, read_sbcs, write_sbcs, &data_CS_MAC_CYRILLIC_OLD }; static const sbcs_data data_CS_MAC_UKRAINE = { { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, 0x041f, 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042a, 0x042b, 0x042c, 0x042d, 0x042e, 0x042f, 0x2020, 0x00b0, 0x0490, 0x00a3, 0x00a7, 0x2022, 0x00b6, 0x0406, 0x00ae, 0x00a9, 0x2122, 0x0402, 0x0452, 0x2260, 0x0403, 0x0453, 0x221e, 0x00b1, 0x2264, 0x2265, 0x0456, 0x00b5, 0x0491, 0x0408, 0x0404, 0x0454, 0x0407, 0x0457, 0x0409, 0x0459, 0x040a, 0x045a, 0x0458, 0x0405, 0x00ac, 0x221a, 0x0192, 0x2248, 0x2206, 0x00ab, 0x00bb, 0x2026, 0x00a0, 0x040b, 0x045b, 0x040c, 0x045c, 0x0455, 0x2013, 0x2014, 0x201c, 0x201d, 0x2018, 0x2019, 0x00f7, 0x201e, 0x040e, 0x045e, 0x040f, 0x045f, 0x2116, 0x0401, 0x0451, 0x044f, 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e, 0x043f, 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044a, 0x044b, 0x044c, 0x044d, 0x044e, 0x00a4 }, { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0xca, 0xa3, 0xff, 0xa4, 0xa9, 0xc7, 0xc2, 0xa8, 0xa1, 0xb1, 0xb5, 0xa6, 0xc8, 0xd6, 0xc4, 0xdd, 0xab, 0xae, 0xb8, 0xc1, 0xa7, 0xba, 0xb7, 0xbc, 0xbe, 0xcb, 0xcd, 0xd8, 0xda, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xdf, 0xde, 0xac, 0xaf, 0xb9, 0xcf, 0xb4, 0xbb, 0xc0, 0xbd, 0xbf, 0xcc, 0xce, 0xd9, 0xdb, 0xa2, 0xb6, 0xd0, 0xd1, 0xd4, 0xd5, 0xd2, 0xd3, 0xd7, 0xa0, 0xa5, 0xc9, 0xdc, 0xaa, 0xc6, 0xc3, 0xb0, 0xc5, 0xad, 0xb2, 0xb3 }, 256 }; const charset_spec charset_CS_MAC_UKRAINE = { CS_MAC_UKRAINE, read_sbcs, write_sbcs, &data_CS_MAC_UKRAINE }; static const sbcs_data data_CS_MAC_VT100 = { { 0x2400, 0x2401, 0x2402, 0x2403, 0x2404, 0x2405, 0x2406, 0x2407, 0x2408, 0x2409, 0x240a, 0x240b, 0x240c, 0x240d, 0x240e, 0x240f, 0x2410, 0x2411, 0x2412, 0x2413, 0x2414, 0x2415, 0x2416, 0x2417, 0x2418, 0x2419, 0x241a, 0x241b, 0x241c, 0x241d, 0x241e, 0x241f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x2421, 0x00c4, 0x00c5, 0x00c7, 0x00c9, 0x00d1, 0x00d6, 0x00dc, 0x00e1, 0x00e0, 0x00e2, 0x00e4, 0x00e3, 0x00e5, 0x00e7, 0x00e9, 0x00e8, 0x00ea, 0x00eb, 0x00ed, 0x00ec, 0x00ee, 0x00ef, 0x00f1, 0x00f3, 0x00f2, 0x00f4, 0x00f6, 0x00f5, 0x00fa, 0x00f9, 0x00fb, 0x00fc, 0x00dd, 0x00b0, 0x00a2, 0x00a3, 0x00a7, 0x00b8, 0x00b6, 0x00df, 0x00ae, 0x00a9, 0x2122, 0x00b4, 0x00a8, 0x2260, 0x00c6, 0x00d8, 0x00d7, 0x00b1, 0x2264, 0x2265, 0x00a5, 0x00b5, 0x00b9, 0x00b2, 0x00b3, 0x03c0, 0x00a6, 0x00aa, 0x00ba, 0x2592, 0x00e6, 0x00f8, 0x00bf, 0x00a1, 0x00ac, 0x00bd, 0x0192, 0x00bc, 0x00be, 0x00ab, 0x00bb, 0x2026, ERROR , 0x00c0, 0x00c3, 0x00d5, 0x0152, 0x0153, 0x2013, 0x2014, 0x2518, 0x2510, 0x250c, 0x2514, 0x00f7, 0x2022, 0x00ff, 0x0178, 0x253c, 0x20ac, 0x00d0, 0x00f0, 0x00fe, 0x00de, 0x00fd, 0x00b7, 0x23ba, 0x23bb, 0x2500, 0x00c2, 0x00ca, 0x00c1, 0x00cb, 0x00c8, 0x00cd, 0x00ce, 0x00cf, 0x00cc, 0x00d3, 0x00d4, ERROR , 0x00d2, 0x00da, 0x00db, 0x00d9, 0x23bc, 0x23bd, 0x251c, 0x2524, 0x2534, 0x252c, 0x2502, ERROR , ERROR , ERROR , ERROR }, { 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0xc1, 0xa2, 0xa3, 0xb4, 0xba, 0xa4, 0xac, 0xa9, 0xbb, 0xc7, 0xc2, 0xa8, 0xa1, 0xb1, 0xb7, 0xb8, 0xab, 0xb5, 0xa6, 0xe1, 0xa5, 0xb6, 0xbc, 0xc8, 0xc5, 0xc3, 0xc6, 0xc0, 0xcb, 0xe7, 0xe5, 0xcc, 0x80, 0x81, 0xae, 0x82, 0xe9, 0x83, 0xe6, 0xe8, 0xed, 0xea, 0xeb, 0xec, 0xdc, 0x84, 0xf1, 0xee, 0xef, 0xcd, 0x85, 0xb0, 0xaf, 0xf4, 0xf2, 0xf3, 0x86, 0xa0, 0xdf, 0xa7, 0x88, 0x87, 0x89, 0x8b, 0x8a, 0x8c, 0xbe, 0x8d, 0x8f, 0x8e, 0x90, 0x91, 0x93, 0x92, 0x94, 0x95, 0xdd, 0x96, 0x98, 0x97, 0x99, 0x9b, 0x9a, 0xd6, 0xbf, 0x9d, 0x9c, 0x9e, 0x9f, 0xe0, 0xde, 0xd8, 0xce, 0xcf, 0xd9, 0xc4, 0xb9, 0xd0, 0xd1, 0xd7, 0xc9, 0xdb, 0xaa, 0xad, 0xb2, 0xb3, 0xe2, 0xe3, 0xf5, 0xf6, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x7f, 0xe4, 0xfb, 0xd4, 0xd3, 0xd5, 0xd2, 0xf7, 0xf8, 0xfa, 0xf9, 0xda, 0xbd }, 250 }; const charset_spec charset_CS_MAC_VT100 = { CS_MAC_VT100, read_sbcs, write_sbcs, &data_CS_MAC_VT100 }; static const sbcs_data data_CS_MAC_VT100_OLD = { { 0x2400, 0x2401, 0x2402, 0x2403, 0x2404, 0x2405, 0x2406, 0x2407, 0x2408, 0x2409, 0x240a, 0x240b, 0x240c, 0x240d, 0x240e, 0x240f, 0x2410, 0x2411, 0x2412, 0x2413, 0x2414, 0x2415, 0x2416, 0x2417, 0x2418, 0x2419, 0x241a, 0x241b, 0x241c, 0x241d, 0x241e, 0x241f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x2421, 0x00c4, 0x00c5, 0x00c7, 0x00c9, 0x00d1, 0x00d6, 0x00dc, 0x00e1, 0x00e0, 0x00e2, 0x00e4, 0x00e3, 0x00e5, 0x00e7, 0x00e9, 0x00e8, 0x00ea, 0x00eb, 0x00ed, 0x00ec, 0x00ee, 0x00ef, 0x00f1, 0x00f3, 0x00f2, 0x00f4, 0x00f6, 0x00f5, 0x00fa, 0x00f9, 0x00fb, 0x00fc, 0x00dd, 0x00b0, 0x00a2, 0x00a3, 0x00a7, 0x00b8, 0x00b6, 0x00df, 0x00ae, 0x00a9, 0x2122, 0x00b4, 0x00a8, 0x2260, 0x00c6, 0x00d8, 0x00d7, 0x00b1, 0x2264, 0x2265, 0x00a5, 0x00b5, 0x00b9, 0x00b2, 0x00b3, 0x03c0, 0x00a6, 0x00aa, 0x00ba, 0x2592, 0x00e6, 0x00f8, 0x00bf, 0x00a1, 0x00ac, 0x00bd, 0x0192, 0x00bc, 0x00be, 0x00ab, 0x00bb, 0x2026, ERROR , 0x00c0, 0x00c3, 0x00d5, 0x0152, 0x0153, 0x2013, 0x2014, 0x2518, 0x2510, 0x250c, 0x2514, 0x00f7, 0x2022, 0x00ff, 0x0178, 0x253c, 0x00a4, 0x00d0, 0x00f0, 0x00fe, 0x00de, 0x00fd, 0x00b7, 0x23ba, 0x23bb, 0x2500, 0x00c2, 0x00ca, 0x00c1, 0x00cb, 0x00c8, 0x00cd, 0x00ce, 0x00cf, 0x00cc, 0x00d3, 0x00d4, ERROR , 0x00d2, 0x00da, 0x00db, 0x00d9, 0x23bc, 0x23bd, 0x251c, 0x2524, 0x2534, 0x252c, 0x2502, ERROR , ERROR , ERROR , ERROR }, { 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0xc1, 0xa2, 0xa3, 0xdb, 0xb4, 0xba, 0xa4, 0xac, 0xa9, 0xbb, 0xc7, 0xc2, 0xa8, 0xa1, 0xb1, 0xb7, 0xb8, 0xab, 0xb5, 0xa6, 0xe1, 0xa5, 0xb6, 0xbc, 0xc8, 0xc5, 0xc3, 0xc6, 0xc0, 0xcb, 0xe7, 0xe5, 0xcc, 0x80, 0x81, 0xae, 0x82, 0xe9, 0x83, 0xe6, 0xe8, 0xed, 0xea, 0xeb, 0xec, 0xdc, 0x84, 0xf1, 0xee, 0xef, 0xcd, 0x85, 0xb0, 0xaf, 0xf4, 0xf2, 0xf3, 0x86, 0xa0, 0xdf, 0xa7, 0x88, 0x87, 0x89, 0x8b, 0x8a, 0x8c, 0xbe, 0x8d, 0x8f, 0x8e, 0x90, 0x91, 0x93, 0x92, 0x94, 0x95, 0xdd, 0x96, 0x98, 0x97, 0x99, 0x9b, 0x9a, 0xd6, 0xbf, 0x9d, 0x9c, 0x9e, 0x9f, 0xe0, 0xde, 0xd8, 0xce, 0xcf, 0xd9, 0xc4, 0xb9, 0xd0, 0xd1, 0xd7, 0xc9, 0xaa, 0xad, 0xb2, 0xb3, 0xe2, 0xe3, 0xf5, 0xf6, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x7f, 0xe4, 0xfb, 0xd4, 0xd3, 0xd5, 0xd2, 0xf7, 0xf8, 0xfa, 0xf9, 0xda, 0xbd }, 250 }; const charset_spec charset_CS_MAC_VT100_OLD = { CS_MAC_VT100_OLD, read_sbcs, write_sbcs, &data_CS_MAC_VT100_OLD }; static const sbcs_data data_CS_VISCII = { { 0x0000, 0x0001, 0x1eb2, 0x0003, 0x0004, 0x1eb4, 0x1eaa, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x1ef6, 0x0015, 0x0016, 0x0017, 0x0018, 0x1ef8, 0x001a, 0x001b, 0x001c, 0x001d, 0x1ef4, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x1ea0, 0x1eae, 0x1eb0, 0x1eb6, 0x1ea4, 0x1ea6, 0x1ea8, 0x1eac, 0x1ebc, 0x1eb8, 0x1ebe, 0x1ec0, 0x1ec2, 0x1ec4, 0x1ec6, 0x1ed0, 0x1ed2, 0x1ed4, 0x1ed6, 0x1ed8, 0x1ee2, 0x1eda, 0x1edc, 0x1ede, 0x1eca, 0x1ece, 0x1ecc, 0x1ec8, 0x1ee6, 0x0168, 0x1ee4, 0x1ef2, 0x00d5, 0x1eaf, 0x1eb1, 0x1eb7, 0x1ea5, 0x1ea7, 0x1ea8, 0x1ead, 0x1ebd, 0x1eb9, 0x1ebf, 0x1ec1, 0x1ec3, 0x1ec5, 0x1ec7, 0x1ed1, 0x1ed3, 0x1ed5, 0x1ed7, 0x1ee0, 0x01a0, 0x1ed9, 0x1edd, 0x1edf, 0x1ecb, 0x1ef0, 0x1ee8, 0x1eea, 0x1eec, 0x01a1, 0x1edb, 0x01af, 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x1ea2, 0x0102, 0x1eb3, 0x1eb5, 0x00c8, 0x00c9, 0x00ca, 0x1eba, 0x00cc, 0x00cd, 0x0128, 0x1ef3, 0x0110, 0x1ee9, 0x00d2, 0x00d3, 0x00d4, 0x1ea1, 0x1ef7, 0x1eeb, 0x1eed, 0x00d9, 0x00da, 0x1ef9, 0x1ef5, 0x00dd, 0x1ee1, 0x01b0, 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x1ea3, 0x0103, 0x1eef, 0x1eab, 0x00e8, 0x00e9, 0x00ea, 0x1ebb, 0x00ec, 0x00ed, 0x0129, 0x1ec9, 0x0111, 0x1ef1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x1ecf, 0x1ecd, 0x1ee5, 0x00f9, 0x00fa, 0x0169, 0x1ee7, 0x00fd, 0x1ee3, 0x1eee }, { 0x00, 0x01, 0x03, 0x04, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x15, 0x16, 0x17, 0x18, 0x1a, 0x1b, 0x1c, 0x1d, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0xc0, 0xc1, 0xc2, 0xc3, 0xc8, 0xc9, 0xca, 0xcc, 0xcd, 0xd2, 0xd3, 0xd4, 0xa0, 0xd9, 0xda, 0xdd, 0xe0, 0xe1, 0xe2, 0xe3, 0xe8, 0xe9, 0xea, 0xec, 0xed, 0xf2, 0xf3, 0xf4, 0xf5, 0xf9, 0xfa, 0xfd, 0xc5, 0xe5, 0xd0, 0xf0, 0xce, 0xee, 0x9d, 0xfb, 0xb4, 0xbd, 0xbf, 0xdf, 0x80, 0xd5, 0xc4, 0xe4, 0x84, 0xa4, 0x85, 0xa5, 0x86, 0x06, 0xe7, 0x87, 0xa7, 0x81, 0xa1, 0x82, 0xa2, 0x02, 0xc6, 0x05, 0xc7, 0x83, 0xa3, 0x89, 0xa9, 0xcb, 0xeb, 0x88, 0xa8, 0x8a, 0xaa, 0x8b, 0xab, 0x8c, 0xac, 0x8d, 0xad, 0x8e, 0xae, 0x9b, 0xef, 0x98, 0xb8, 0x9a, 0xf7, 0x99, 0xf6, 0x8f, 0xaf, 0x90, 0xb0, 0x91, 0xb1, 0x92, 0xb2, 0x93, 0xb5, 0x95, 0xbe, 0x96, 0xb6, 0x97, 0xb7, 0xb3, 0xde, 0x94, 0xfe, 0x9e, 0xf8, 0x9c, 0xfc, 0xba, 0xd1, 0xbb, 0xd7, 0xbc, 0xd8, 0xff, 0xe6, 0xb9, 0xf1, 0x9f, 0xcf, 0x1e, 0xdc, 0x14, 0xd6, 0x19, 0xdb }, 255 }; const charset_spec charset_CS_VISCII = { CS_VISCII, read_sbcs, write_sbcs, &data_CS_VISCII }; static const sbcs_data data_CS_HP_ROMAN8 = { { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f, 0x00a0, 0x00c0, 0x00c2, 0x00c8, 0x00ca, 0x00cb, 0x00ce, 0x00cf, 0x00b4, 0x02cb, 0x02c6, 0x00a8, 0x02dc, 0x00d9, 0x00db, 0x20a4, 0x00af, 0x00dd, 0x00fd, 0x00b0, 0x00c7, 0x00e7, 0x00d1, 0x00f1, 0x00a1, 0x00bf, 0x00a4, 0x00a3, 0x00a5, 0x00a7, 0x0192, 0x00a2, 0x00e2, 0x00ea, 0x00f4, 0x00fb, 0x00e1, 0x00e9, 0x00f3, 0x00fa, 0x00e0, 0x00e8, 0x00f2, 0x00f9, 0x00e4, 0x00eb, 0x00f6, 0x00fc, 0x00c5, 0x00ee, 0x00d8, 0x00c6, 0x00e5, 0x00ed, 0x00f8, 0x00e6, 0x00c4, 0x00ec, 0x00d6, 0x00dc, 0x00c9, 0x00ef, 0x00df, 0x00d4, 0x00c1, 0x00c3, 0x00e3, 0x00d0, 0x00f0, 0x00cd, 0x00cc, 0x00d3, 0x00d2, 0x00d5, 0x00f5, 0x0160, 0x0161, 0x00da, 0x0178, 0x00ff, 0x00de, 0x00fe, 0x00b7, 0x00b5, 0x00b6, 0x00be, 0x2014, 0x00bc, 0x00bd, 0x00aa, 0x00ba, 0x00ab, 0x25a0, 0x00bb, 0x00b1, ERROR }, { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xb8, 0xbf, 0xbb, 0xba, 0xbc, 0xbd, 0xab, 0xf9, 0xfb, 0xb0, 0xb3, 0xfe, 0xa8, 0xf3, 0xf4, 0xf2, 0xfa, 0xfd, 0xf7, 0xf8, 0xf5, 0xb9, 0xa1, 0xe0, 0xa2, 0xe1, 0xd8, 0xd0, 0xd3, 0xb4, 0xa3, 0xdc, 0xa4, 0xa5, 0xe6, 0xe5, 0xa6, 0xa7, 0xe3, 0xb6, 0xe8, 0xe7, 0xdf, 0xe9, 0xda, 0xd2, 0xad, 0xed, 0xae, 0xdb, 0xb1, 0xf0, 0xde, 0xc8, 0xc4, 0xc0, 0xe2, 0xcc, 0xd4, 0xd7, 0xb5, 0xc9, 0xc5, 0xc1, 0xcd, 0xd9, 0xd5, 0xd1, 0xdd, 0xe4, 0xb7, 0xca, 0xc6, 0xc2, 0xea, 0xce, 0xd6, 0xcb, 0xc7, 0xc3, 0xcf, 0xb2, 0xf1, 0xef, 0xeb, 0xec, 0xee, 0xbe, 0xaa, 0xa9, 0xac, 0xf6, 0xaf, 0xfc }, 255 }; const charset_spec charset_CS_HP_ROMAN8 = { CS_HP_ROMAN8, read_sbcs, write_sbcs, &data_CS_HP_ROMAN8 }; static const sbcs_data data_CS_DEC_MCS = { { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f, ERROR , 0x00a1, 0x00a2, 0x00a3, ERROR , 0x00a5, ERROR , 0x00a7, 0x00a4, 0x00a9, 0x00aa, 0x00ab, ERROR , ERROR , ERROR , ERROR , 0x00b0, 0x00b1, 0x00b2, 0x00b3, ERROR , 0x00b5, 0x00b6, 0x00b7, ERROR , 0x00b9, 0x00ba, 0x00bb, 0x00bc, 0x00bd, ERROR , 0x00bf, 0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x00c4, 0x00c5, 0x00c6, 0x00c7, 0x00c8, 0x00c9, 0x00ca, 0x00cb, 0x00cc, 0x00cd, 0x00ce, 0x00cf, ERROR , 0x00d1, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x00d6, 0x0152, 0x00d8, 0x00d9, 0x00da, 0x00db, 0x00dc, 0x0178, ERROR , 0x00df, 0x00e0, 0x00e1, 0x00e2, 0x00e3, 0x00e4, 0x00e5, 0x00e6, 0x00e7, 0x00e8, 0x00e9, 0x00ea, 0x00eb, 0x00ec, 0x00ed, 0x00ee, 0x00ef, ERROR , 0x00f1, 0x00f2, 0x00f3, 0x00f4, 0x00f5, 0x00f6, 0x0153, 0x00f8, 0x00f9, 0x00fa, 0x00fb, 0x00fc, 0x00ff, ERROR , ERROR }, { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa1, 0xa2, 0xa3, 0xa8, 0xa5, 0xa7, 0xa9, 0xaa, 0xab, 0xb0, 0xb1, 0xb2, 0xb3, 0xb5, 0xb6, 0xb7, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xd7, 0xf7, 0xdd }, 241 }; const charset_spec charset_CS_DEC_MCS = { CS_DEC_MCS, read_sbcs, write_sbcs, &data_CS_DEC_MCS }; #else /* ENUM_CHARSETS */ ENUM_CHARSET(CS_ISO8859_1) ENUM_CHARSET(CS_ISO8859_2) ENUM_CHARSET(CS_ISO8859_3) ENUM_CHARSET(CS_ISO8859_4) ENUM_CHARSET(CS_ISO8859_5) ENUM_CHARSET(CS_ISO8859_6) ENUM_CHARSET(CS_ISO8859_7) ENUM_CHARSET(CS_ISO8859_8) ENUM_CHARSET(CS_ISO8859_9) ENUM_CHARSET(CS_ISO8859_10) ENUM_CHARSET(CS_ISO8859_11) ENUM_CHARSET(CS_ISO8859_13) ENUM_CHARSET(CS_ISO8859_14) ENUM_CHARSET(CS_ISO8859_15) ENUM_CHARSET(CS_ISO8859_16) ENUM_CHARSET(CS_ISO8859_1_X11) ENUM_CHARSET(CS_CP437) ENUM_CHARSET(CS_CP850) ENUM_CHARSET(CS_CP866) ENUM_CHARSET(CS_CP852) ENUM_CHARSET(CS_CP1250) ENUM_CHARSET(CS_CP1251) ENUM_CHARSET(CS_CP1252) ENUM_CHARSET(CS_CP1253) ENUM_CHARSET(CS_CP1254) ENUM_CHARSET(CS_CP1255) ENUM_CHARSET(CS_CP1256) ENUM_CHARSET(CS_CP1257) ENUM_CHARSET(CS_CP1258) ENUM_CHARSET(CS_KOI8_R) ENUM_CHARSET(CS_KOI8_U) ENUM_CHARSET(CS_MAC_ROMAN) ENUM_CHARSET(CS_MAC_TURKISH) ENUM_CHARSET(CS_MAC_CROATIAN) ENUM_CHARSET(CS_MAC_ICELAND) ENUM_CHARSET(CS_MAC_ROMANIAN) ENUM_CHARSET(CS_MAC_GREEK) ENUM_CHARSET(CS_MAC_CYRILLIC) ENUM_CHARSET(CS_MAC_THAI) ENUM_CHARSET(CS_MAC_CENTEURO) ENUM_CHARSET(CS_MAC_SYMBOL) ENUM_CHARSET(CS_MAC_DINGBATS) ENUM_CHARSET(CS_MAC_ROMAN_OLD) ENUM_CHARSET(CS_MAC_CROATIAN_OLD) ENUM_CHARSET(CS_MAC_ICELAND_OLD) ENUM_CHARSET(CS_MAC_ROMANIAN_OLD) ENUM_CHARSET(CS_MAC_GREEK_OLD) ENUM_CHARSET(CS_MAC_CYRILLIC_OLD) ENUM_CHARSET(CS_MAC_UKRAINE) ENUM_CHARSET(CS_MAC_VT100) ENUM_CHARSET(CS_MAC_VT100_OLD) ENUM_CHARSET(CS_VISCII) ENUM_CHARSET(CS_HP_ROMAN8) ENUM_CHARSET(CS_DEC_MCS) #endif /* ENUM_CHARSETS */ putty-0.76/contrib/0000755000175000017500000000000014072266316011303 500000000000000putty-0.76/contrib/encodelib.py0000644000175000017500000000663014072266310013520 00000000000000# Python module to make it easy to manually encode SSH packets, by # supporting the various uint32, string, mpint primitives. # # The idea of this is that you can use it to manually construct key # exchange sequences of interesting kinds, for testing purposes. import sys import struct import random assert sys.version_info[:2] >= (3,0), "This is Python 3 code" def tobytes(s): return s if isinstance(s, bytes) else s.encode('ASCII') def boolean(b): return b"\1" if b else b"\0" def byte(b): assert 0 <= b < 0x100 return bytes([b]) def uint32(u): assert 0 <= u < 0x100000000 return struct.pack(">I", u) def uint64(u): assert 0 <= u < 0x10000000000000000 return struct.pack(">L", u) def string(s): return uint32(len(s)) + tobytes(s) def mpint(m): s = [] while m > 0: s.append(m & 0xFF) m >>= 8 if len(s) > 0 and (s[-1] & 0x80): s.append(0) s.reverse() return string(bytes(s)) def name_list(ns): s = b"" for n in map(tobytes, ns): assert b"," not in n if s != b"": s += b"," s += n return string(s) def ssh_rsa_key_blob(modulus, exponent): return string(string("ssh-rsa") + mpint(modulus) + mpint(exponent)) def ssh_rsa_signature_blob(signature): return string(string("ssh-rsa") + mpint(signature)) def greeting(string): # Greeting at the start of an SSH connection. return tobytes(string) + b"\r\n" # Packet types. SSH2_MSG_DISCONNECT = 1 SSH2_MSG_IGNORE = 2 SSH2_MSG_UNIMPLEMENTED = 3 SSH2_MSG_DEBUG = 4 SSH2_MSG_SERVICE_REQUEST = 5 SSH2_MSG_SERVICE_ACCEPT = 6 SSH2_MSG_KEXINIT = 20 SSH2_MSG_NEWKEYS = 21 SSH2_MSG_KEXDH_INIT = 30 SSH2_MSG_KEXDH_REPLY = 31 SSH2_MSG_KEX_DH_GEX_REQUEST_OLD = 30 SSH2_MSG_KEX_DH_GEX_GROUP = 31 SSH2_MSG_KEX_DH_GEX_INIT = 32 SSH2_MSG_KEX_DH_GEX_REPLY = 33 SSH2_MSG_KEX_DH_GEX_REQUEST = 34 SSH2_MSG_KEXRSA_PUBKEY = 30 SSH2_MSG_KEXRSA_SECRET = 31 SSH2_MSG_KEXRSA_DONE = 32 SSH2_MSG_USERAUTH_REQUEST = 50 SSH2_MSG_USERAUTH_FAILURE = 51 SSH2_MSG_USERAUTH_SUCCESS = 52 SSH2_MSG_USERAUTH_BANNER = 53 SSH2_MSG_USERAUTH_PK_OK = 60 SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ = 60 SSH2_MSG_USERAUTH_INFO_REQUEST = 60 SSH2_MSG_USERAUTH_INFO_RESPONSE = 61 SSH2_MSG_GLOBAL_REQUEST = 80 SSH2_MSG_REQUEST_SUCCESS = 81 SSH2_MSG_REQUEST_FAILURE = 82 SSH2_MSG_CHANNEL_OPEN = 90 SSH2_MSG_CHANNEL_OPEN_CONFIRMATION = 91 SSH2_MSG_CHANNEL_OPEN_FAILURE = 92 SSH2_MSG_CHANNEL_WINDOW_ADJUST = 93 SSH2_MSG_CHANNEL_DATA = 94 SSH2_MSG_CHANNEL_EXTENDED_DATA = 95 SSH2_MSG_CHANNEL_EOF = 96 SSH2_MSG_CHANNEL_CLOSE = 97 SSH2_MSG_CHANNEL_REQUEST = 98 SSH2_MSG_CHANNEL_SUCCESS = 99 SSH2_MSG_CHANNEL_FAILURE = 100 SSH2_MSG_USERAUTH_GSSAPI_RESPONSE = 60 SSH2_MSG_USERAUTH_GSSAPI_TOKEN = 61 SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE = 63 SSH2_MSG_USERAUTH_GSSAPI_ERROR = 64 SSH2_MSG_USERAUTH_GSSAPI_ERRTOK = 65 SSH2_MSG_USERAUTH_GSSAPI_MIC = 66 def clearpkt(msgtype, *stuff): # SSH-2 binary packet, in the cleartext format used for initial # setup and kex. s = byte(msgtype) for thing in stuff: s += thing padlen = 0 while padlen < 4 or len(s) % 8 != 3: padlen += 1 s += byte(random.randint(0,255)) s = byte(padlen) + s return string(s) def decode_uint32(s): assert len(s) == 4 return struct.unpack(">I", s)[0] def read_clearpkt(fh): length_field = fh.read(4) s = fh.read(decode_uint32(length_field)) padlen, msgtype = s[:2] return msgtype, s[2:-padlen] putty-0.76/contrib/gdb.py0000644000175000017500000002216514072266310012331 00000000000000import gdb import re import gdb.printing class PuTTYMpintPrettyPrinter(gdb.printing.PrettyPrinter): "Pretty-print PuTTY's mp_int type." name = "mp_int" def __init__(self, val): super(PuTTYMpintPrettyPrinter, self).__init__(self.name) self.val = val def to_string(self): type_BignumInt = gdb.lookup_type("BignumInt") type_BignumIntPtr = type_BignumInt.pointer() BIGNUM_INT_BITS = 8 * type_BignumInt.sizeof array = self.val["w"] aget = lambda i: int(array[i]) & ((1 << BIGNUM_INT_BITS)-1) try: length = int(self.val["nw"]) value = 0 for i in range(length): value |= aget(i) << (BIGNUM_INT_BITS * i) return "mp_int({:#x})".format(value) except gdb.MemoryError: address = int(self.val) if address == 0: return "mp_int(NULL)".format(address) return "mp_int(invalid @ {:#x})".format(address) class PuTTYPrinterSelector(gdb.printing.PrettyPrinter): def __init__(self): super(PuTTYPrinterSelector, self).__init__("PuTTY") def __call__(self, val): if str(val.type) == "mp_int *": return PuTTYMpintPrettyPrinter(val) return None gdb.printing.register_pretty_printer(None, PuTTYPrinterSelector()) class MemDumpCommand(gdb.Command): """Print a hex+ASCII dump of object EXP. EXP must be an expression whose value resides in memory. The contents of the memory it occupies are printed in a standard hex dump format, with each line showing an offset relative to the address of EXP, then the hex byte values of the memory at that offset, and then a translation into ASCII of the same bytes (with values outside the printable ASCII range translated as '.'). To dump a number of bytes from a particular address, it's useful to use the gdb expression extensions {TYPE} and @LENGTH. For example, if 'ptr' and 'len' are variables giving an address and a length in bytes, then the command memdump {char} ptr @ len will dump the range of memory described by those two variables.""" def __init__(self): super(MemDumpCommand, self).__init__( "memdump", gdb.COMMAND_DATA, gdb.COMPLETE_EXPRESSION) def invoke(self, cmdline, from_tty): expr = gdb.parse_and_eval(cmdline) try: start, size = int(expr.address), expr.type.sizeof except gdb.error as e: raise gdb.GdbError(str(e)) except (TypeError, AttributeError): raise gdb.GdbError("expression must identify an object in memory") return width = 16 line_ptr_type = gdb.lookup_type( "unsigned char").const().array(width).pointer() dumpaddr = 0 while size > 0: line = gdb.Value(start).cast(line_ptr_type).dereference() thislinelen = min(size, width) start += thislinelen size -= thislinelen dumpline = [None, " "] + [" "] * width + [" "] + [""] * width dumpline[0] = "{:08x}".format(dumpaddr) dumpaddr += thislinelen for i in range(thislinelen): ch = int(line[i]) & 0xFF dumpline[2+i] = " {:02x}".format(ch) dumpline[3+width+i] = chr(ch) if 0x20 <= ch < 0x7F else "." sys.stdout.write("".join(dumpline) + "\n") MemDumpCommand() class ContainerOf(gdb.Function): """Implement the container_of macro from PuTTY's defs.h. Arguments are an object or pointer to object; a type to convert it to; and, optionally the name of the structure member in the destination type that the pointer points to. (If the member name is not provided, then the default is whichever member of the destination structure type has the same type as the input object, provided there's only one.) Due to limitations of GDB's convenience function syntax, the type and member names must be provided as strings. """ def __init__(self): super(ContainerOf, self).__init__("container_of") def match_type(self, obj, typ): if obj.type == typ: return obj try: ref = obj.referenced_value() if ref.type == typ: return ref except gdb.error: pass return None def invoke(self, obj, dest_type_name_val, member_name_val=None): try: dest_type_name = dest_type_name_val.string() except gdb.error: raise gdb.GdbError("destination type name must be a string") try: dest_type = gdb.lookup_type(dest_type_name) except gdb.error: raise gdb.GdbError("no such type '{dt}'".format(dt=dest_type_name)) if member_name_val is not None: try: member_name = member_name_val.string() except gdb.error: raise gdb.GdbError("member name must be a string") for field in dest_type.fields(): if field.name == member_name: break else: raise gdb.GdbError( "type '{dt}' has no member called '{memb}'" .format(dt=dest_type_name, memb=member_name)) match_obj = self.match_type(obj, field.type) else: matches = [] for field in dest_type.fields(): this_match_obj = self.match_type(obj, field.type) if this_match_obj is not None: match_obj = this_match_obj matches.append(field) if len(matches) == 0: raise gdb.GdbError( "type '{dt}' has no member matching type '{ot}'" .format(dt=dest_type_name, ot=obj.type)) if len(matches) > 1: raise gdb.GdbError( "type '{dt}' has multiple members matching type '{ot}'" " ({memberlist})" .format(dt=dest_type_name, ot=obj.type, memberlist=", ".join(f.name for f in matches))) field = matches[0] if field.bitpos % 8 != 0: raise gdb.GdbError( "offset of field '{memb}' is a fractional number of bytes" .format(dt=dest_type_name, memb=member_name)) offset = field.bitpos // 8 if match_obj.type != field.type: raise gdb.GdbError( "value to convert does not have type '{ft}'" .format(ft=field.type)) try: addr = int(match_obj.address) except gdb.error: raise gdb.GdbError("cannot take address of value to convert") return gdb.Value(addr - offset).cast(dest_type.pointer()) ContainerOf() class List234(gdb.Function): """List the elements currently stored in a tree234. Arguments are a tree234, and optionally a value type. If no value type is given, the result is a list of the raw void * pointers stored in the tree. Othewise, each one is cast to a pointer to the value type and dereferenced. Due to limitations of GDB's convenience function syntax, the value type must be provided as a string. """ def __init__(self): super(List234, self).__init__("list234") def add_elements(self, node, destlist): kids = node["kids"] elems = node["elems"] for i in range(4): if int(kids[i]) != 0: add_elements(self, kids[i].dereference(), destlist) if i < 3 and int(elems[i]) != 0: destlist.append(elems[i]) def invoke(self, tree, value_type_name_val=None): if value_type_name_val is not None: try: value_type_name = value_type_name_val.string() except gdb.error: raise gdb.GdbError("value type name must be a string") try: value_type = gdb.lookup_type(value_type_name) except gdb.error: raise gdb.GdbError("no such type '{dt}'" .format(dt=value_type_name)) else: value_type = None try: tree = tree.dereference() except gdb.error: pass if tree.type == gdb.lookup_type("tree234"): tree = tree["root"].dereference() if tree.type != gdb.lookup_type("node234"): raise gdb.GdbError( "input value is not a tree234") if int(tree.address) == 0: # If you try to return {} for the empty list, gdb gives # the cryptic error "bad array bounds (0, -1)"! We return # NULL as the best approximation to 'sorry, list is # empty'. return gdb.parse_and_eval("((void *)0)") elems = [] self.add_elements(tree, elems) if value_type is not None: value_ptr_type_name = str(value_type.pointer()) elem_fmt = lambda p: "*({}){}".format(value_ptr_type_name, int(p)) else: elem_fmt = lambda p: "(void *){}".format(int(p)) elems_str = "{" + ",".join(elem_fmt(elem) for elem in elems) + "}" return gdb.parse_and_eval(elems_str) List234() putty-0.76/contrib/kh2reg.py0000755000175000017500000004000114072266310012747 00000000000000#!/usr/bin/env python3 # Convert OpenSSH known_hosts and known_hosts2 files to "new format" PuTTY # host keys. # usage: # kh2reg.py [ --win ] known_hosts1 2 3 4 ... > hosts.reg # Creates a Windows .REG file (double-click to install). # kh2reg.py --unix known_hosts1 2 3 4 ... > sshhostkeys # Creates data suitable for storing in ~/.putty/sshhostkeys (Unix). # Line endings are someone else's problem as is traditional. # Should run under either Python 2 or 3. import fileinput import base64 import struct import string import re import sys import argparse import itertools import collections import hashlib from functools import reduce def winmungestr(s): "Duplicate of PuTTY's mungestr() in winstore.c:1.10 for Registry keys" candot = 0 r = "" for c in s: if c in ' \*?%~' or ord(c){:d}B".format(len(s)), s) return reduce ((lambda a, b: (int(a) << 8) + int(b)), bytes) def strtoint_le(s): "Convert arbitrary-length little-endian binary data to a Python int" bytes = reversed(struct.unpack(">{:d}B".format(len(s)), s)) return reduce ((lambda a, b: (int(a) << 8) + int(b)), bytes) def inttohex(n): "Convert int to lower-case hex." return "0x{:x}".format(n) def warn(s): "Warning with file/line number" sys.stderr.write("%s:%d: %s\n" % (fileinput.filename(), fileinput.filelineno(), s)) class HMAC(object): def __init__(self, hashclass, blocksize): self.hashclass = hashclass self.blocksize = blocksize self.struct = struct.Struct(">{:d}B".format(self.blocksize)) def pad_key(self, key): return key + b'\0' * (self.blocksize - len(key)) def xor_key(self, key, xor): return self.struct.pack(*[b ^ xor for b in self.struct.unpack(key)]) def keyed_hash(self, key, padbyte, string): return self.hashclass(self.xor_key(key, padbyte) + string).digest() def compute(self, key, string): if len(key) > self.blocksize: key = self.hashclass(key).digest() key = self.pad_key(key) return self.keyed_hash(key, 0x5C, self.keyed_hash(key, 0x36, string)) def openssh_hashed_host_match(hashed_host, try_host): if hashed_host.startswith(b'|1|'): salt, expected = hashed_host[3:].split(b'|') salt = base64.decodebytes(salt) expected = base64.decodebytes(expected) mac = HMAC(hashlib.sha1, 64) else: return False # unrecognised magic number prefix return mac.compute(salt, try_host) == expected def invert(n, p): """Compute inverse mod p.""" if n % p == 0: raise ZeroDivisionError() a = n, 1, 0 b = p, 0, 1 while b[0]: q = a[0] // b[0] a = a[0] - q*b[0], a[1] - q*b[1], a[2] - q*b[2] b, a = a, b assert abs(a[0]) == 1 return a[1]*a[0] def jacobi(n,m): """Compute the Jacobi symbol. The special case of this when m is prime is the Legendre symbol, which is 0 if n is congruent to 0 mod m; 1 if n is congruent to a non-zero square number mod m; -1 if n is not congruent to any square mod m. """ assert m & 1 acc = 1 while True: n %= m if n == 0: return 0 while not (n & 1): n >>= 1 if (m & 7) not in {1,7}: acc *= -1 if n == 1: return acc if (n & 3) == 3 and (m & 3) == 3: acc *= -1 n, m = m, n class SqrtModP(object): """Class for finding square roots of numbers mod p. p must be an odd prime (but its primality is not checked).""" def __init__(self, p): p = abs(p) assert p & 1 self.p = p # Decompose p as 2^e k + 1 for odd k. self.k = p-1 self.e = 0 while not (self.k & 1): self.k >>= 1 self.e += 1 # Find a non-square mod p. for self.z in itertools.count(1): if jacobi(self.z, self.p) == -1: break self.zinv = invert(self.z, self.p) def sqrt_recurse(self, a): ak = pow(a, self.k, self.p) for i in range(self.e, -1, -1): if ak == 1: break ak = ak*ak % self.p assert i > 0 if i == self.e: return pow(a, (self.k+1) // 2, self.p) r_prime = self.sqrt_recurse(a * pow(self.z, 2**i, self.p)) return r_prime * pow(self.zinv, 2**(i-1), self.p) % self.p def sqrt(self, a): j = jacobi(a, self.p) if j == 0: return 0 if j < 0: raise ValueError("{} has no square root mod {}".format(a, self.p)) a %= self.p r = self.sqrt_recurse(a) assert r*r % self.p == a # Normalise to the smaller (or 'positive') one of the two roots. return min(r, self.p - r) def __str__(self): return "{}({})".format(type(self).__name__, self.p) def __repr__(self): return self.__str__() instances = {} @classmethod def make(cls, p): if p not in cls.instances: cls.instances[p] = cls(p) return cls.instances[p] @classmethod def root(cls, n, p): return cls.make(p).sqrt(n) NistCurve = collections.namedtuple("NistCurve", "p a b") nist_curves = { "ecdsa-sha2-nistp256": NistCurve(0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff, 0xffffffff00000001000000000000000000000000fffffffffffffffffffffffc, 0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b), "ecdsa-sha2-nistp384": NistCurve(0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000ffffffff, 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000fffffffc, 0xb3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef), "ecdsa-sha2-nistp521": NistCurve(0x01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, 0x01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc, 0x0051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00), } class BlankInputLine(Exception): pass class UnknownKeyType(Exception): def __init__(self, keytype): self.keytype = keytype class KeyFormatError(Exception): def __init__(self, msg): self.msg = msg def handle_line(line, output_formatter, try_hosts): try: # Remove leading/trailing whitespace (should zap CR and LF) line = line.strip() # Skip blanks and comments if line == '' or line[0] == '#': raise BlankInputLine # Split line on spaces. fields = line.split(' ') # Common fields hostpat = fields[0] keyparams = [] # placeholder keytype = "" # placeholder # Grotty heuristic to distinguish known_hosts from known_hosts2: # is second field entirely decimal digits? if re.match (r"\d*$", fields[1]): # Treat as SSH-1-type host key. # Format: hostpat bits10 exp10 mod10 comment... # (PuTTY doesn't store the number of bits.) keyparams = list(map(int, fields[2:4])) keytype = "rsa" else: # Treat as SSH-2-type host key. # Format: hostpat keytype keyblob64 comment... sshkeytype, blob = fields[1], base64.decodebytes( fields[2].encode("ASCII")) # 'blob' consists of a number of # uint32 N (big-endian) # uint8[N] field_data subfields = [] while blob: sizefmt = ">L" (size,) = struct.unpack (sizefmt, blob[0:4]) size = int(size) # req'd for slicage (data,) = struct.unpack (">%lus" % size, blob[4:size+4]) subfields.append(data) blob = blob [struct.calcsize(sizefmt) + size : ] # The first field is keytype again. if subfields[0].decode("ASCII") != sshkeytype: raise KeyFormatError(""" outer and embedded key types do not match: '%s', '%s' """ % (sshkeytype, subfields[1])) # Translate key type string into something PuTTY can use, and # munge the rest of the data. if sshkeytype == "ssh-rsa": keytype = "rsa2" # The rest of the subfields we can treat as an opaque list # of bignums (same numbers and order as stored by PuTTY). keyparams = list(map(strtoint, subfields[1:])) elif sshkeytype == "ssh-dss": keytype = "dss" # Same again. keyparams = list(map(strtoint, subfields[1:])) elif sshkeytype in nist_curves: keytype = sshkeytype # Have to parse this a bit. if len(subfields) > 3: raise KeyFormatError("too many subfields in blob") (curvename, Q) = subfields[1:] # First is yet another copy of the key name. if not re.match("ecdsa-sha2-" + re.escape( curvename.decode("ASCII")), sshkeytype): raise KeyFormatError("key type mismatch ('%s' vs '%s')" % (sshkeytype, curvename)) # Second contains key material X and Y (hopefully). # First a magic octet indicating point compression. point_type = struct.unpack_from("B", Q, 0)[0] Qrest = Q[1:] if point_type == 4: # Then two equal-length bignums (X and Y). bnlen = len(Qrest) if (bnlen % 1) != 0: raise KeyFormatError("odd-length X+Y") bnlen = bnlen // 2 x = strtoint(Qrest[:bnlen]) y = strtoint(Qrest[bnlen:]) elif 2 <= point_type <= 3: # A compressed point just specifies X, and leaves # Y implicit except for parity, so we have to # recover it from the curve equation. curve = nist_curves[sshkeytype] x = strtoint(Qrest) yy = (x*x*x + curve.a*x + curve.b) % curve.p y = SqrtModP.root(yy, curve.p) if y % 2 != point_type % 2: y = curve.p - y keyparams = [curvename, x, y] elif sshkeytype in { "ssh-ed25519", "ssh-ed448" }: keytype = sshkeytype if len(subfields) != 2: raise KeyFormatError("wrong number of subfields in blob") # Key material y, with the top bit being repurposed as # the expected parity of the associated x (point # compression). y = strtoint_le(subfields[1]) x_parity = y >> 255 y &= ~(1 << 255) # Curve parameters. p, d, a = { "ssh-ed25519": (2**255 - 19, 0x52036cee2b6ffe738cc740797779e89800700a4d4141d8ab75eb4dca135978a3, -1), "ssh-ed448": (2**448-2**224-1, -39081, +1), }[sshkeytype] # Recover x^2 = (y^2 - 1) / (d y^2 - a). xx = (y*y - 1) * invert(d*y*y - a, p) % p # Take the square root. x = SqrtModP.root(xx, p) # Pick the square root of the correct parity. if (x % 2) != x_parity: x = p - x keyparams = [x, y] else: raise UnknownKeyType(sshkeytype) # Now print out one line per host pattern, discarding wildcards. for host in hostpat.split(','): if re.search (r"[*?!]", host): warn("skipping wildcard host pattern '%s'" % host) continue if re.match (r"\|", host): for try_host in try_hosts: if openssh_hashed_host_match(host.encode('ASCII'), try_host.encode('UTF-8')): host = try_host break else: warn("unable to match hashed hostname '%s'" % host) continue m = re.match (r"\[([^]]*)\]:(\d*)$", host) if m: (host, port) = m.group(1,2) port = int(port) else: port = 22 # Slightly bizarre output key format: 'type@port:hostname' # XXX: does PuTTY do anything useful with literal IP[v4]s? key = keytype + ("@%d:%s" % (port, host)) # Most of these are numbers, but there's the occasional # string that needs passing through value = ",".join(map( lambda x: x if isinstance(x, str) else x.decode('ASCII') if isinstance(x, bytes) else inttohex(x), keyparams)) output_formatter.key(key, value) except UnknownKeyType as k: warn("unknown SSH key type '%s', skipping" % k.keytype) except KeyFormatError as k: warn("trouble parsing key (%s), skipping" % k.msg) except BlankInputLine: pass class OutputFormatter(object): def __init__(self, fh): self.fh = fh def header(self): pass def trailer(self): pass class WindowsOutputFormatter(OutputFormatter): def header(self): # Output REG file header. self.fh.write("""REGEDIT4 [HKEY_CURRENT_USER\Software\SimonTatham\PuTTY\SshHostKeys] """) def key(self, key, value): # XXX: worry about double quotes? self.fh.write("\"%s\"=\"%s\"\n" % (winmungestr(key), value)) def trailer(self): # The spec at http://support.microsoft.com/kb/310516 says we need # a blank line at the end of the reg file: # # Note the registry file should contain a blank line at the # bottom of the file. # self.fh.write("\n") class UnixOutputFormatter(OutputFormatter): def key(self, key, value): self.fh.write('%s %s\n' % (key, value)) def main(): parser = argparse.ArgumentParser( description="Convert OpenSSH known hosts files to PuTTY's format.") group = parser.add_mutually_exclusive_group() group.add_argument( "--windows", "--win", action='store_const', dest="output_formatter_class", const=WindowsOutputFormatter, help="Produce Windows .reg file output that regedit.exe can import" " (default).") group.add_argument( "--unix", action='store_const', dest="output_formatter_class", const=UnixOutputFormatter, help="Produce a file suitable for use as ~/.putty/sshhostkeys.") parser.add_argument("-o", "--output", type=argparse.FileType("w"), default=argparse.FileType("w")("-"), help="Output file to write to (default stdout).") parser.add_argument("--hostname", action="append", help="Host name(s) to try matching against hashed " "host entries in input.") parser.add_argument("infile", nargs="*", help="Input file(s) to read from (default stdin).") parser.set_defaults(output_formatter_class=WindowsOutputFormatter, hostname=[]) args = parser.parse_args() output_formatter = args.output_formatter_class(args.output) output_formatter.header() for line in fileinput.input(args.infile): handle_line(line, output_formatter, args.hostname) output_formatter.trailer() if __name__ == "__main__": main() putty-0.76/contrib/logparse.pl0000755000175000017500000014510114072266310013373 00000000000000#!/usr/bin/perl use Getopt::Long; use strict; use warnings; use FileHandle; my $dumpchannels = 0; my $dumpdata = 0; my $pass_through_events = 0; my $verbose_all; my %verbose_packet; GetOptions("dump-channels|c" => \$dumpchannels, "dump-data|d" => \$dumpdata, "verbose|v" => \$verbose_all, "full|f=s" => sub { $verbose_packet{$_[1]} = 1; }, "events|e" => \$pass_through_events, "help" => sub { &usage(\*STDOUT, 0); }) or &usage(\*STDERR, 1); sub usage { my ($fh, $exitstatus) = @_; print $fh <<'EOF'; usage: logparse.pl [ options ] [ input-log-file ] options: --dump-channels, -c dump the final state of every channel --dump-data, -d save data of every channel to ch0.i, ch0.o, ... --full=PKT, -f PKT print extra detail for packets of type PKT --verbose, -v print extra detail for all packets if available --events, -e copy Event Log messages from input log file EOF exit $exitstatus; } 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_KEXGSS_INIT 30 /* 0x1e */ 'SSH2_MSG_KEXGSS_INIT' => sub { my ($direction, $seq, $data) = @_; print "\n"; }, #define SSH2_MSG_KEXGSS_CONTINUE 31 /* 0x1f */ 'SSH2_MSG_KEXGSS_CONTINUE' => sub { my ($direction, $seq, $data) = @_; print "\n"; }, #define SSH2_MSG_KEXGSS_COMPLETE 32 /* 0x20 */ 'SSH2_MSG_KEXGSS_COMPLETE' => sub { my ($direction, $seq, $data) = @_; print "\n"; }, #define SSH2_MSG_KEXGSS_HOSTKEY 33 /* 0x21 */ 'SSH2_MSG_KEXGSS_HOSTKEY' => sub { my ($direction, $seq, $data) = @_; print "\n"; }, #define SSH2_MSG_KEXGSS_ERROR 34 /* 0x22 */ 'SSH2_MSG_KEXGSS_ERROR' => sub { my ($direction, $seq, $data) = @_; print "\n"; }, #define SSH2_MSG_KEXGSS_GROUPREQ 40 /* 0x28 */ 'SSH2_MSG_KEXGSS_GROUPREQ' => sub { my ($direction, $seq, $data) = @_; print "\n"; }, #define SSH2_MSG_KEXGSS_GROUP 41 /* 0x29 */ 'SSH2_MSG_KEXGSS_GROUP' => 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_KEX_ECDH_INIT 30 /* 0x1e */ 'SSH2_MSG_KEX_ECDH_INIT' => sub { my ($direction, $seq, $data) = @_; print "\n"; }, #define SSH2_MSG_KEX_ECDH_REPLY 31 /* 0x1f */ 'SSH2_MSG_KEX_ECDH_REPLY' => 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"; }, ); our %disc_reasons = ( 1 => "SSH_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT", 2 => "SSH_DISCONNECT_PROTOCOL_ERROR", 3 => "SSH_DISCONNECT_KEY_EXCHANGE_FAILED", 4 => "SSH_DISCONNECT_RESERVED", 5 => "SSH_DISCONNECT_MAC_ERROR", 6 => "SSH_DISCONNECT_COMPRESSION_ERROR", 7 => "SSH_DISCONNECT_SERVICE_NOT_AVAILABLE", 8 => "SSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED", 9 => "SSH_DISCONNECT_HOST_KEY_NOT_VERIFIABLE", 10 => "SSH_DISCONNECT_CONNECTION_LOST", 11 => "SSH_DISCONNECT_BY_APPLICATION", 12 => "SSH_DISCONNECT_TOO_MANY_CONNECTIONS", 13 => "SSH_DISCONNECT_AUTH_CANCELLED_BY_USER", 14 => "SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE", 15 => "SSH_DISCONNECT_ILLEGAL_USER_NAME", ); my %verbose_packet_dump_functions = ( 'SSH2_MSG_KEXINIT' => sub { my ($data) = @_; my ($cookie0, $cookie1, $cookie2, $cookie3, $kex, $hostkey, $cscipher, $sccipher, $csmac, $scmac, $cscompress, $sccompress, $cslang, $sclang, $guess, $reserved) = &parse("uuuussssssssssbu", $data); printf(" cookie: %08x%08x%08x%08x\n", $cookie0, $cookie1, $cookie2, $cookie3); my $print_namelist = sub { my @names = split /,/, $_[1]; printf " %s: name-list with %d items%s\n", $_[0], (scalar @names), join "", map { "\n $_" } @names; }; $print_namelist->("kex", $kex); $print_namelist->("host key", $hostkey); $print_namelist->("client->server cipher", $cscipher); $print_namelist->("server->client cipher", $sccipher); $print_namelist->("client->server MAC", $csmac); $print_namelist->("server->client MAC", $scmac); $print_namelist->("client->server compression", $cscompress); $print_namelist->("server->client compression", $sccompress); $print_namelist->("client->server language", $cslang); $print_namelist->("server->client language", $sclang); printf " first kex packet follows: %s\n", $guess; printf " reserved field: %#x\n", $reserved; }, 'SSH2_MSG_KEXDH_INIT' => sub { my ($data) = @_; my ($e) = &parse("m", $data); printf " e: %s\n", $e; }, 'SSH2_MSG_KEX_DH_GEX_REQUEST' => sub { my ($data) = @_; my ($min, $pref, $max) = &parse("uuu", $data); printf " min bits: %d\n", $min; printf " preferred bits: %d\n", $pref; printf " max bits: %d\n", $max; }, 'SSH2_MSG_KEX_DH_GEX_GROUP' => sub { my ($data) = @_; my ($p, $g) = &parse("mm", $data); printf " p: %s\n", $p; printf " g: %s\n", $g; }, 'SSH2_MSG_KEX_DH_GEX_INIT' => sub { my ($data) = @_; my ($e) = &parse("m", $data); printf " e: %s\n", $e; }, 'SSH2_MSG_KEX_ECDH_INIT' => sub { my ($data) = @_; my ($cpv) = &parse("s", $data); # Public values in ECDH depend for their interpretation on the # selected curve, and this script doesn't cross-analyse the # two KEXINIT packets to independently figure out what that # curve is. So the best we can do is just dump the raw data. printf " client public value: %s\n", (unpack "H*", $cpv); }, 'SSH2_MSG_KEXDH_REPLY' => sub { my ($data) = @_; my ($hostkeyblob, $f, $sigblob) = &parse("sms", $data); my ($hktype, @hostkey) = &parse_public_key($hostkeyblob); printf " host key: %s\n", $hktype; while (@hostkey) { my ($key, $value) = splice @hostkey, 0, 2; printf " $key: $value\n"; } printf " f: %s\n", $f; printf " signature:\n"; my @signature = &parse_signature($sigblob, $hktype); while (@signature) { my ($key, $value) = splice @signature, 0, 2; printf " $key: $value\n"; } }, 'SSH2_MSG_KEX_DH_GEX_REPLY' => sub { my ($data) = @_; my ($hostkeyblob, $f, $sigblob) = &parse("sms", $data); my ($hktype, @hostkey) = &parse_public_key($hostkeyblob); printf " host key: %s\n", $hktype; while (@hostkey) { my ($key, $value) = splice @hostkey, 0, 2; printf " $key: $value\n"; } printf " f: %s\n", $f; printf " signature:\n"; my @signature = &parse_signature($sigblob, $hktype); while (@signature) { my ($key, $value) = splice @signature, 0, 2; printf " $key: $value\n"; } }, 'SSH2_MSG_KEX_ECDH_REPLY' => sub { my ($data) = @_; my ($hostkeyblob, $spv, $sigblob) = &parse("sss", $data); my ($hktype, @hostkey) = &parse_public_key($hostkeyblob); printf " host key: %s\n", $hktype; while (@hostkey) { my ($key, $value) = splice @hostkey, 0, 2; printf " $key: $value\n"; } printf " server public value: %s\n", (unpack "H*", $spv); printf " signature:\n"; my @signature = &parse_signature($sigblob, $hktype); while (@signature) { my ($key, $value) = splice @signature, 0, 2; printf " $key: $value\n"; } }, 'SSH2_MSG_NEWKEYS' => sub {}, 'SSH2_MSG_SERVICE_REQUEST' => sub { my ($data) = @_; my ($servname) = &parse("s", $data); printf " service name: %s\n", $servname; }, 'SSH2_MSG_SERVICE_ACCEPT' => sub { my ($data) = @_; my ($servname) = &parse("s", $data); printf " service name: %s\n", $servname; }, 'SSH2_MSG_DISCONNECT' => sub { my ($data) = @_; my ($reason, $desc, $lang) = &parse("uss", $data); printf(" reason code: %d%s\n", $reason, defined $disc_reasons{$reason} ? " ($disc_reasons{$reason})" : "" ); printf " description: '%s'\n", $desc; printf " language tag: '%s'\n", $lang; }, 'SSH2_MSG_DEBUG' => sub { my ($data) = @_; my ($display, $desc, $lang) = &parse("bss", $data); printf " always display: %s\n", $display; printf " description: '%s'\n", $desc; printf " language tag: '%s'\n", $lang; }, 'SSH2_MSG_IGNORE' => sub { my ($data) = @_; my ($payload) = &parse("s", $data); printf " data: %s\n", unpack "H*", $payload; }, 'SSH2_MSG_UNIMPLEMENTED' => sub { my ($data) = @_; my ($seq) = &parse("u", $data); printf " sequence number: %d\n", $seq; }, 'SSH2_MSG_KEXGSS_INIT' => sub { my ($data) = @_; my ($token, $e) = &parse("sm", $data); printf " output token: %s\n", unpack "H*", $token; printf " e: %s\n", $e; }, 'SSH2_MSG_KEXGSS_CONTINUE' => sub { my ($data) = @_; my ($token) = &parse("s", $data); printf " output token: %s\n", unpack "H*", $token; }, 'SSH2_MSG_KEXGSS_COMPLETE' => sub { my ($data) = @_; my ($f, $permsgtoken, $got_output) = &parse("msb", $data); printf " f: %s\n", $f; printf " per-message token: %s\n", unpack "H*", $permsgtoken; printf " output token present: %s\n", $got_output; if ($got_output eq "yes") { my ($token) = &parse("s", $data); printf " output token: %s\n", unpack "H*", $token; } }, 'SSH2_MSG_KEXGSS_HOSTKEY' => sub { my ($data) = @_; my ($hostkey) = &parse("s", $data); printf " host key: %s\n", unpack "H*", $hostkey; }, 'SSH2_MSG_KEXGSS_ERROR' => sub { my ($data) = @_; my ($maj, $min, $msg, $lang) = &parse("uuss", $data); printf " major status: %d\n", $maj; printf " minor status: %d\n", $min; printf " message: '%s'\n", $msg; printf " language tag: '%s'\n", $lang; }, 'SSH2_MSG_KEXGSS_GROUPREQ' => sub { my ($data) = @_; my ($min, $pref, $max) = &parse("uuu", $data); printf " min bits: %d\n", $min; printf " preferred bits: %d\n", $pref; printf " max bits: %d\n", $max; }, 'SSH2_MSG_KEXGSS_GROUP' => sub { my ($data) = @_; my ($p, $g) = &parse("mm", $data); printf " p: %s\n", $p; printf " g: %s\n", $g; }, ); 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"); }, ); for my $type (keys %verbose_packet) { if (!defined $verbose_packet_dump_functions{$type}) { die "no verbose dump available for packet type $type\n"; } } 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 "; my ($verbose_dump, $verbose_data) = undef; if (defined $verbose_packet_dump_functions{$type} && ($verbose_all || defined $verbose_packet{$type})) { $verbose_dump = $verbose_packet_dump_functions{$type}; $verbose_data = [ @$data ]; } if (defined $packets{$type}) { $packets{$type}->($direction, $fullseq, $data); } else { printf "raw %s\n", join "", map { sprintf "%02x", $_ } @$data; } if (defined $verbose_dump) { $verbose_dump->($verbose_data); if (@$verbose_data) { printf(" trailing bytes: %s\n", unpack "H*", pack "C*", @$verbose_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 ($pass_through_events && m/^Event Log: ([^\n]*)$/) { printf "event: $1\n"; } } 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 format_unsigned_hex_integer { my $abs = join "", map { sprintf "%02x", $_ } @_; $abs =~ s!^0*!!g; $abs = "0" if $abs eq ""; return "0x" . $abs; } sub parseone { my ($type, $data) = @_; if ($type eq "u") { # uint32 my @bytes = splice @$data, 0, 4; return "" 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 "" 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 "" if !defined $byte or $byte < 0; return $byte ? "yes" : "no"; } elsif ($type eq "B") { # byte my $byte = shift @$data; return "" 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 "" if @bytes < 4 or grep { $_<0 } @bytes; my $len = unpack "N", pack "C*", @bytes; @bytes = splice @$data, 0, $len; return "" if @bytes < $len or grep { $_<0 } @bytes; if ($type eq "m") { 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 .= &format_unsigned_hex_integer(@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 ""; 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 parse_public_key { my ($blob) = @_; my $data = [ unpack "C*", $blob ]; my @toret; my ($type) = &parse("s", $data); push @toret, $type; if ($type eq "ssh-rsa") { my ($e, $n) = &parse("mm", $data); push @toret, "e", $e, "n", $n; } elsif ($type eq "ssh-dss") { my ($p, $q, $g, $y) = &parse("mmmm", $data); push @toret, "p", $p, "q", $q, "g", $g, "y", $y; } elsif ($type eq "ssh-ed25519") { my ($xyblob) = &parse("s", $data); my @y = unpack "C*", $xyblob; push @toret, "hibit(x)", $y[$#y] & 1; $y[$#y] &= ~1; push @toret, "y & ~1", &format_unsigned_hex_integer(@y); } elsif ($type =~ m!^ecdsa-sha2-nistp(256|384|521)$!) { my ($curvename, $blob) = &parse("ss", $data); push @toret, "curve name", $curvename; my @blobdata = unpack "C*", $blob; my ($fmt) = &parse("B", \@blobdata); push @toret, "format byte", $fmt; if ($fmt == 4) { push @toret, "x", &format_unsigned_hex_integer( @blobdata[0..($#blobdata+1)/2-1]); push @toret, "y", &format_unsigned_hex_integer( @blobdata[($#blobdata+1)/2..$#blobdata]); } } else { push @toret, "undecoded data", unpack "H*", pack "C*", @$data; } return @toret; }; sub parse_signature { my ($blob, $keytype) = @_; my $data = [ unpack "C*", $blob ]; my @toret; if ($keytype eq "ssh-rsa") { my ($type, $s) = &parse("ss", $data); push @toret, "sig type", $type; push @toret, "s", &format_unsigned_hex_integer(unpack "C*", $s); } elsif ($keytype eq "ssh-dss") { my ($type, $subblob) = &parse("ss", $data); push @toret, "sig type", $type; push @toret, "r", &format_unsigned_hex_integer( unpack "C*", substr($subblob, 0, 20)); push @toret, "s", &format_unsigned_hex_integer( unpack "C*", substr($subblob, 20, 40)); } elsif ($keytype eq "ssh-ed25519") { my ($type, $rsblob) = &parse("ss", $data); push @toret, "sig type", $type; my @ry = unpack "C*", $rsblob; my @sy = splice @ry, 32, 32; push @toret, "hibit(r.x)", $ry[$#ry] & 1; $ry[$#ry] &= ~1; push @toret, "r.y & ~1", &format_unsigned_hex_integer(@ry); push @toret, "hibit(s.x)", $sy[$#sy] & 1; $sy[$#sy] &= ~1; push @toret, "s.y & ~1", &format_unsigned_hex_integer(@sy); } elsif ($keytype =~ m!^ecdsa-sha2-nistp(256|384|521)$!) { my ($sigtype, $subblob) = &parse("ss", $data); push @toret, "sig type", $sigtype; my @sbdata = unpack "C*", $subblob; my ($r, $s) = &parse("mm", \@sbdata); push @toret, "r", $r, "s", $s; } else { push @toret, "undecoded data", unpack "H*", pack "C*", @$data; } return @toret; }; sub stringescape { my ($str) = @_; $str =~ s!\\!\\\\!g; $str =~ s![^ -~]!sprintf "\\x%02X", ord $&!eg; return $str; } putty-0.76/contrib/logrewrap.pl0000755000175000017500000000313114072266310013555 00000000000000#!/usr/bin/perl # Process a PuTTY SSH packet log that has gone through inappropriate # line wrapping, and try to make it legible again. # # Motivation: people often include PuTTY packet logs in email # messages, and if they're not careful, the sending MUA 'helpfully' # wraps the lines at 72 characters, corrupting all the hex dumps into # total unreadability. # # But as long as it's only the ASCII part of the dump at the end of # the line that gets wrapped, and the hex part is untouched, this is a # mechanically recoverable kind of corruption, because the ASCII is # redundant and can be reconstructed from the hex. Better still, you # can spot lines in which this has happened (because the ASCII at the # end of the line is a truncated version of what we think it should # say), and use that as a cue to remove the following line. use strict; use warnings; while (<<>>) { if (/^ ([0-9a-f]{8}) ((?:[0-9a-f]{2} ){0,15}(?:[0-9a-f]{2}))/) { my $addr = $1; my $hex = $2; my $ascii = ""; for (my $i = 0; $i < length($2); $i += 3) { my $byte = hex(substr($hex, $i, 2)); my $char = ($byte >= 32 && $byte < 127 ? chr($byte) : "."); $ascii .= $char; } $hex = substr($hex . (" " x 48), 0, 47); my $old_line = $_; chomp($old_line); my $new_line = " $addr $hex $ascii"; if ($old_line ne $new_line and $old_line eq substr($new_line, 0, length($old_line))) { print "$new_line\n"; <<>>; # eat the subsequent wrapped line next; } } print $_; } putty-0.76/contrib/make1305.py0000755000175000017500000003454114072266310013027 00000000000000#!/usr/bin/env python3 import sys import string from collections import namedtuple assert sys.version_info[:2] >= (3,0), "This is Python 3 code" class Multiprecision(object): def __init__(self, target, minval, maxval, words): self.target = target self.minval = minval self.maxval = maxval self.words = words assert 0 <= self.minval assert self.minval <= self.maxval assert self.target.nwords(self.maxval) == len(words) def getword(self, n): return self.words[n] if n < len(self.words) else "0" def __add__(self, rhs): newmin = self.minval + rhs.minval newmax = self.maxval + rhs.maxval nwords = self.target.nwords(newmax) words = [] addfn = self.target.add for i in range(nwords): words.append(addfn(self.getword(i), rhs.getword(i))) addfn = self.target.adc return Multiprecision(self.target, newmin, newmax, words) def __mul__(self, rhs): newmin = self.minval * rhs.minval newmax = self.maxval * rhs.maxval nwords = self.target.nwords(newmax) words = [] # There are basically two strategies we could take for # multiplying two multiprecision integers. One is to enumerate # the space of pairs of word indices in lexicographic order, # essentially computing a*b[i] for each i and adding them # together; the other is to enumerate in diagonal order, # computing everything together that belongs at a particular # output word index. # # For the moment, I've gone for the former. sprev = [] for i, sword in enumerate(self.words): rprev = None sthis = sprev[:i] for j, rword in enumerate(rhs.words): prevwords = [] if i+j < len(sprev): prevwords.append(sprev[i+j]) if rprev is not None: prevwords.append(rprev) vhi, vlo = self.target.muladd(sword, rword, *prevwords) sthis.append(vlo) rprev = vhi sthis.append(rprev) sprev = sthis # Remove unneeded words from the top of the output, if we can # prove by range analysis that they'll always be zero. sprev = sprev[:self.target.nwords(newmax)] return Multiprecision(self.target, newmin, newmax, sprev) def extract_bits(self, start, bits=None): if bits is None: bits = (self.maxval >> start).bit_length() # Overly thorough range analysis: if min and max have the same # *quotient* by 2^bits, then the result of reducing anything # in the range [min,max] mod 2^bits has to fall within the # obvious range. But if they have different quotients, then # you can wrap round the modulus and so any value mod 2^bits # is possible. newmin = self.minval >> start newmax = self.maxval >> start if (newmin >> bits) != (newmax >> bits): newmin = 0 newmax = (1 << bits) - 1 nwords = self.target.nwords(newmax) words = [] for i in range(nwords): srcpos = i * self.target.bits + start maxbits = min(self.target.bits, start + bits - srcpos) wordindex = srcpos // self.target.bits if srcpos % self.target.bits == 0: word = self.getword(srcpos // self.target.bits) elif (wordindex+1 >= len(self.words) or srcpos % self.target.bits + maxbits < self.target.bits): word = self.target.new_value( "(%%s) >> %d" % (srcpos % self.target.bits), self.getword(srcpos // self.target.bits)) else: word = self.target.new_value( "((%%s) >> %d) | ((%%s) << %d)" % ( srcpos % self.target.bits, self.target.bits - (srcpos % self.target.bits)), self.getword(srcpos // self.target.bits), self.getword(srcpos // self.target.bits + 1)) if maxbits < self.target.bits and maxbits < bits: word = self.target.new_value( "(%%s) & ((((BignumInt)1) << %d)-1)" % maxbits, word) words.append(word) return Multiprecision(self.target, newmin, newmax, words) # Each Statement has a list of variables it reads, and a list of ones # it writes. 'forms' is a list of multiple actual C statements it # could be generated as, depending on which of its output variables is # actually used (e.g. no point calling BignumADC if the generated # carry in a particular case is unused, or BignumMUL if nobody needs # the top half). It is indexed by a bitmap whose bits correspond to # the entries in wvars, with wvars[0] the MSB and wvars[-1] the LSB. Statement = namedtuple("Statement", "rvars wvars forms") class CodegenTarget(object): def __init__(self, bits): self.bits = bits self.valindex = 0 self.stmts = [] self.generators = {} self.bv_words = (130 + self.bits - 1) // self.bits self.carry_index = 0 def nwords(self, maxval): return (maxval.bit_length() + self.bits - 1) // self.bits def stmt(self, stmt, needed=False): index = len(self.stmts) self.stmts.append([needed, stmt]) for val in stmt.wvars: self.generators[val] = index def new_value(self, formatstr=None, *deps): name = "v%d" % self.valindex self.valindex += 1 if formatstr is not None: self.stmt(Statement( rvars=deps, wvars=[name], forms=[None, name + " = " + formatstr % deps])) return name def bigval_input(self, name, bits): words = (bits + self.bits - 1) // self.bits # Expect not to require an entire extra word assert words == self.bv_words return Multiprecision(self, 0, (1<w[%d]" % (name, i)) for i in range(words)]) def const(self, value): # We only support constants small enough to both fit in a # BignumInt (of any size supported) _and_ be expressible in C # with no weird integer literal syntax like a trailing LL. # # Supporting larger constants would be possible - you could # break 'value' up into word-sized pieces on the Python side, # and generate a legal C expression for each piece by # splitting it further into pieces within the # standards-guaranteed 'unsigned long' limit of 32 bits and # then casting those to BignumInt before combining them with # shifts. But it would be a lot of effort, and since the # application for this code doesn't even need it, there's no # point in bothering. assert value < 2**16 return Multiprecision(self, value, value, ["%d" % value]) def current_carry(self): return "carry%d" % self.carry_index def add(self, a1, a2): ret = self.new_value() adcform = "BignumADC(%s, carry, %s, %s, 0)" % (ret, a1, a2) plainform = "%s = %s + %s" % (ret, a1, a2) self.carry_index += 1 carryout = self.current_carry() self.stmt(Statement( rvars=[a1,a2], wvars=[ret,carryout], forms=[None, adcform, plainform, adcform])) return ret def adc(self, a1, a2): ret = self.new_value() adcform = "BignumADC(%s, carry, %s, %s, carry)" % (ret, a1, a2) plainform = "%s = %s + %s + carry" % (ret, a1, a2) carryin = self.current_carry() self.carry_index += 1 carryout = self.current_carry() self.stmt(Statement( rvars=[a1,a2,carryin], wvars=[ret,carryout], forms=[None, adcform, plainform, adcform])) return ret def muladd(self, m1, m2, *addends): rlo = self.new_value() rhi = self.new_value() wideform = "BignumMUL%s(%s)" % ( { 0:"", 1:"ADD", 2:"ADD2" }[len(addends)], ", ".join([rhi, rlo, m1, m2] + list(addends))) narrowform = " + ".join(["%s = %s * %s" % (rlo, m1, m2)] + list(addends)) self.stmt(Statement( rvars=[m1,m2]+list(addends), wvars=[rhi,rlo], forms=[None, narrowform, wideform, wideform])) return rhi, rlo def write_bigval(self, name, val): for i in range(self.bv_words): word = val.getword(i) self.stmt(Statement( rvars=[word], wvars=[], forms=["%s->w[%d] = %s" % (name, i, word)]), needed=True) def compute_needed(self): used_vars = set() self.queue = [stmt for (needed,stmt) in self.stmts if needed] while len(self.queue) > 0: stmt = self.queue.pop(0) deps = [] for var in stmt.rvars: if var[0] in string.digits: continue # constant deps.append(self.generators[var]) used_vars.add(var) for index in deps: if not self.stmts[index][0]: self.stmts[index][0] = True self.queue.append(self.stmts[index][1]) forms = [] for i, (needed, stmt) in enumerate(self.stmts): if needed: formindex = 0 for (j, var) in enumerate(stmt.wvars): formindex *= 2 if var in used_vars: formindex += 1 forms.append(stmt.forms[formindex]) # Now we must check whether this form of the statement # also writes some variables we _don't_ actually need # (e.g. if you only wanted the top half from a mul, or # only the carry from an adc, you'd be forced to # generate the other output too). Easiest way to do # this is to look for an identical statement form # later in the array. maxindex = max(i for i in range(len(stmt.forms)) if stmt.forms[i] == stmt.forms[formindex]) extra_vars = maxindex & ~formindex bitpos = 0 while extra_vars != 0: if extra_vars & (1 << bitpos): extra_vars &= ~(1 << bitpos) var = stmt.wvars[-1-bitpos] used_vars.add(var) # Also, write out a cast-to-void for each # subsequently unused value, to prevent gcc # warnings when the output code is compiled. forms.append("(void)" + var) bitpos += 1 used_carry = any(v.startswith("carry") for v in used_vars) used_vars = [v for v in used_vars if v.startswith("v")] used_vars.sort(key=lambda v: int(v[1:])) return used_carry, used_vars, forms def text(self): used_carry, values, forms = self.compute_needed() ret = "" while len(values) > 0: prefix, sep, suffix = " BignumInt ", ", ", ";" currline = values.pop(0) while (len(values) > 0 and len(prefix+currline+sep+values[0]+suffix) < 79): currline += sep + values.pop(0) ret += prefix + currline + suffix + "\n" if used_carry: ret += " BignumCarry carry;\n" if ret != "": ret += "\n" for stmtform in forms: ret += " %s;\n" % stmtform return ret def gen_add(target): # This is an addition _without_ reduction mod p, so that it can be # used both during accumulation of the polynomial and for adding # on the encrypted nonce at the end (which is mod 2^128, not mod # p). # # Because one of the inputs will have come from our # not-completely-reducing multiplication function, we expect up to # 3 extra bits of input. a = target.bigval_input("a", 133) b = target.bigval_input("b", 133) ret = a + b target.write_bigval("r", ret) return """\ static void bigval_add(bigval *r, const bigval *a, const bigval *b) { %s} \n""" % target.text() def gen_mul(target): # The inputs are not 100% reduced mod p. Specifically, we can get # a full 130-bit number from the pow5==0 pass, and then a 130-bit # number times 5 from the pow5==1 pass, plus a possible carry. The # total of that can be easily bounded above by 2^130 * 8, so we # need to assume we're multiplying two 133-bit numbers. a = target.bigval_input("a", 133) b = target.bigval_input("b", 133) ab = a * b ab0 = ab.extract_bits(0, 130) ab1 = ab.extract_bits(130, 130) ab2 = ab.extract_bits(260) ab1_5 = target.const(5) * ab1 ab2_25 = target.const(25) * ab2 ret = ab0 + ab1_5 + ab2_25 target.write_bigval("r", ret) return """\ static void bigval_mul_mod_p(bigval *r, const bigval *a, const bigval *b) { %s} \n""" % target.text() def gen_final_reduce(target): # Given our input number n, n >> 130 is usually precisely the # multiple of p that needs to be subtracted from n to reduce it to # strictly less than p, but it might be too low by 1 (but not more # than 1, given the range of our input is nowhere near the square # of the modulus). So we add another 5, which will push a carry # into the 130th bit if and only if that has happened, and then # use that to decide whether to subtract one more copy of p. a = target.bigval_input("n", 133) q = a.extract_bits(130) adjusted = a.extract_bits(0, 130) + target.const(5) * q final_subtract = (adjusted + target.const(5)).extract_bits(130) adjusted2 = adjusted + target.const(5) * final_subtract ret = adjusted2.extract_bits(0, 130) target.write_bigval("n", ret) return """\ static void bigval_final_reduce(bigval *n) { %s} \n""" % target.text() pp_keyword = "#if" for bits in [16, 32, 64]: sys.stdout.write("%s BIGNUM_INT_BITS == %d\n\n" % (pp_keyword, bits)) pp_keyword = "#elif" sys.stdout.write(gen_add(CodegenTarget(bits))) sys.stdout.write(gen_mul(CodegenTarget(bits))) sys.stdout.write(gen_final_reduce(CodegenTarget(bits))) sys.stdout.write("""#else #error Add another bit count to contrib/make1305.py and rerun it #endif """) putty-0.76/contrib/nice-ibeam.cur0000644000175000017500000000137614072266310013730 00000000000000 è( @€€€€€€€€€€€€€ÀÀÀÿÿÿÿÿÿÿÿÿÿÿÿÿÿðððððððððððððððððÿÿÿÿÿÿÿÿãÿÿÿãÿÿÿãÿÿÿãÿÿÿãÿÿÿãÿÿÿãÿÿÿãÿÿÿãÿÿÿãÿÿÿãÿÿÿãÿÿÿãÿÿÿãÿÿÿãÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿputty-0.76/contrib/plinkfs0000755000175000017500000000215114072266310012610 00000000000000#!/usr/bin/env python3 # Wrapper around the FUSE 'sshfs' client, which arranges to use Plink # as the SSH transport subcommand. # # This is not totally trivial because sshfs assumes slightly more of # OpenSSH's command-line syntax than Plink supports. So we actually # give sshfs a subcommand which is this script itself, re-invoked with # the --helper option. import sys import os import shlex if sys.argv[1:2] == ["--helper"]: # Helper mode. Strip OpenSSH-specific '-o' options from the # command line, and invoke Plink. plink_command = ["plink"] it = iter(sys.argv) next(it) # discard command name next(it) # discard --helper for arg in it: if arg == "-o": next(it) # discard -o option elif arg.startswith("-o"): pass else: plink_command.append(arg) os.execvp(plink_command[0], plink_command) else: # Normal mode, invoked by the user. sshfs_command = [ "sshfs", "-o", "ssh_command={} --helper".format( os.path.realpath(__file__)) ] + sys.argv[1:] os.execvp(sshfs_command[0], sshfs_command) putty-0.76/contrib/samplekex.py0000755000175000017500000001174614072266310013574 00000000000000#!/usr/bin/env python3 # Example Python script to synthesise the server end of an SSH key exchange. # This script expects to be run with its standard input and output # channels both connected to PuTTY. Run it by means of a command such # as # # rm -f test.log && ./plink -sshrawlog test.log -v -proxycmd './contrib/samplekex.py' dummy # # It will conduct the whole of an SSH connection setup, up to the # point where it ought to present a valid host key signature and # switch over to the encrypted protocol; but because this is a simple # script (and also because at that point PuTTY would annoyingly give a # host key prompt), it doesn't actually bother to do either, and will # instead present a nonsense signature and terminate. The above sample # command will log the whole of the exchange from PuTTY's point of # view in 'test.log'. # # The intention is that this forms example code that can be easily # adapted to demonstrate bugs in our SSH connection setup. With more # effort it could be expanded into some kind of a regression-testing # suite, although in order to reliably test particular corner cases # that would probably also need PuTTY-side modifications to make the # random numbers deterministic. import sys, random from encodelib import * assert sys.version_info[:2] >= (3,0), "This is Python 3 code" # A random Diffie-Hellman group, taken from an SSH server I made a # test connection to. groupgen = 5 group = 0xf5d3849d2092fd427b4ebd838ea4830397a55f80b644626320dbbe51e8f63ed88148d787c94e7e67e4f393f26c565e1992b0cff8a47a953439462a4d0ffa5763ef60ff908f8ee6c4f6ef9f32b9ba50f01ad56fe7ebe90876a5cf61813a4ad4ba7ec0704303c9bf887d36abbd6c2aa9545fc2263232927e731060f5c701c96dc34016636df438ce30973715f121d767cfb98b5d09ae7b86fa36a051ad3c2941a295a68e2f583a56bc69913ec9d25abef4fdf1e31ede827a02620db058b9f041da051c8c0f13b132c17ceb893fa7c4cd8d8feebd82c5f9120cb221b8e88c5fe4dc17ca020a535484c92c7d4bee69c7703e1fa9a652d444c80065342c6ec0fac23c24de246e3dee72ca8bc8beccdade2b36771efcc350558268f5352ae53f2f71db62249ad9ac4fabdd6dfb099c6cff8c05bdea894390f9860f011cca046dfeb2f6ef81094e7980be526742706d1f3db920db107409291bb4c11f9a7dcbfaf26d808e6f9fe636b26b939de419129e86b1e632c60ec23b65c815723c5d861af068fd0ac8b37f4c06ecbd5cb2ef069ca8daac5cbd67c6182a65fed656d0dfbbb8a430b1dbac7bd6303bec8de078fe69f443a7bc8131a284d25dc2844f096240bfc61b62e91a87802987659b884c094c68741d29aa5ca19b9457e1f9df61c7dbbb13a61a79e4670b086027f20da2af4f5b020725f8828726379f429178926a1f0ea03f # An RSA key, generated specially for this script. rsaexp = 0x10001 rsamod = 0xB98FE0C0BEE1E05B35FDDF5517B3E29D8A9A6A7834378B6783A19536968968F755E341B5822CAE15B465DECB80EE4116CF8D22DB5A6C85444A68D0D45D9D42008329619BE3CAC3B192EF83DD2B75C4BB6B567E11B841073BACE92108DA7E97E543ED7F032F454F7AC3C6D3F27DB34BC9974A85C7963C546662AE300A61CBABEE274481FD041C41D0145704F5FA9C77A5A442CD7A64827BB0F21FB56FDE388B596A20D7A7D1C5F22DA96C6C2171D90A673DABC66596CD99499D75AD82FEFDBE04DEC2CC7E1414A95388F668591B3F4D58249F80489646ED2C318E77D4F4E37EE8A588E79F2960620E6D28BF53653F1C974C91845F0BABFE5D167E1CA7044EE20D # 16 bytes of random data for the start of KEXINIT. cookie = bytes(random.randint(0,255) for i in range(16)) def expect(var, expr): expected_val = eval(expr) if var != expected_val: sys.stderr.write("Expected %s (%s), got %s\n" % ( expr, repr(expected_val), repr(var))) sys.exit(1) sys.stdout.buffer.write(greeting("SSH-2.0-Example KEX synthesis")) greeting = sys.stdin.buffer.readline() expect(greeting[:8].decode("ASCII"), '"SSH-2.0-"') sys.stdout.buffer.write( clearpkt(SSH2_MSG_KEXINIT, cookie, name_list(("diffie-hellman-group-exchange-sha256",)), # kex name_list(("ssh-rsa",)), # host keys name_list(("aes128-ctr",)), # client->server ciphers name_list(("aes128-ctr",)), # server->client ciphers name_list(("hmac-sha2-256",)), # client->server MACs name_list(("hmac-sha2-256",)), # server->client MACs name_list(("none",)), # client->server compression name_list(("none",)), # server->client compression name_list(()), # client->server languages name_list(()), # server->client languages boolean(False), # first kex packet does not follow uint32(0))) sys.stdout.buffer.flush() intype, inpkt = read_clearpkt(sys.stdin.buffer) expect(intype, "SSH2_MSG_KEXINIT") intype, inpkt = read_clearpkt(sys.stdin.buffer) expect(intype, "SSH2_MSG_KEX_DH_GEX_REQUEST") expect(inpkt, "uint32(0x400) + uint32(0x400) + uint32(0x2000)") sys.stdout.buffer.write( clearpkt(SSH2_MSG_KEX_DH_GEX_GROUP, mpint(group), mpint(groupgen))) sys.stdout.buffer.flush() intype, inpkt = read_clearpkt(sys.stdin.buffer) expect(intype, "SSH2_MSG_KEX_DH_GEX_INIT") sys.stdout.buffer.write( clearpkt(SSH2_MSG_KEX_DH_GEX_REPLY, ssh_rsa_key_blob(rsaexp, rsamod), mpint(random.randint(2, group-2)), ssh_rsa_signature_blob(random.randint(2, rsamod-2)))) sys.stdout.buffer.flush() putty-0.76/contrib/cygtermd/0000755000175000017500000000000014072266316013121 500000000000000putty-0.76/contrib/cygtermd/Makefile0000644000175000017500000000015514072266307014502 00000000000000cygtermd.exe: main.c sel.c telnet.c pty.c malloc.c gcc -o cygtermd.exe main.c sel.c telnet.c pty.c malloc.c putty-0.76/contrib/cygtermd/README0000644000175000017500000000101314072266310013706 00000000000000This directory contains 'cygtermd', a small and specialist Telnet server designed to act as middleware between PuTTY and a Cygwin shell session running on the same machine, so that PuTTY can act as an xterm-alike for Cygwin. To install it, you must compile it from source using Cygwin gcc, install it in Cygwin's /bin, and configure PuTTY to use it as a local proxy process. For detailed instructions, see the PuTTY Wishlist page at https://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/cygwin-terminal-window.html putty-0.76/contrib/cygtermd/main.c0000644000175000017500000000735214072266310014132 00000000000000/* * Main program. */ #include #include #include #include #include #include #include #include #include #include #include "sel.h" #include "pty.h" #include "telnet.h" int signalpipe[2]; sel *asel; sel_rfd *netr, *ptyr, *sigr; int ptyfd; sel_wfd *netw, *ptyw; Telnet *telnet; #define BUF 65536 void sigchld(int signum) { write(signalpipe[1], "C", 1); } void fatal(const char *fmt, ...) { va_list ap; fprintf(stderr, "cygtermd: "); va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); fprintf(stderr, "\n"); exit(1); } void net_readdata(sel_rfd *rfd, void *data, size_t len) { if (len == 0) exit(0); /* EOF on network - client went away */ telnet_from_net(telnet, data, len); if (sel_write(netw, NULL, 0) > BUF) sel_rfd_freeze(ptyr); if (sel_write(ptyw, NULL, 0) > BUF) sel_rfd_freeze(netr); } void net_readerr(sel_rfd *rfd, int error) { fprintf(stderr, "standard input: read: %s\n", strerror(errno)); exit(1); } void net_written(sel_wfd *wfd, size_t bufsize) { if (bufsize < BUF) sel_rfd_unfreeze(ptyr); } void net_writeerr(sel_wfd *wfd, int error) { fprintf(stderr, "standard input: write: %s\n", strerror(errno)); exit(1); } void pty_readdata(sel_rfd *rfd, void *data, size_t len) { if (len == 0) exit(0); /* EOF on pty */ telnet_from_pty(telnet, data, len); if (sel_write(netw, NULL, 0) > BUF) sel_rfd_freeze(ptyr); if (sel_write(ptyw, NULL, 0) > BUF) sel_rfd_freeze(netr); } void pty_readerr(sel_rfd *rfd, int error) { if (error == EIO) /* means EOF, on a pty */ exit(0); fprintf(stderr, "pty: read: %s\n", strerror(errno)); exit(1); } void pty_written(sel_wfd *wfd, size_t bufsize) { if (bufsize < BUF) sel_rfd_unfreeze(netr); } void pty_writeerr(sel_wfd *wfd, int error) { fprintf(stderr, "pty: write: %s\n", strerror(errno)); exit(1); } void sig_readdata(sel_rfd *rfd, void *data, size_t len) { char *p = data; while (len > 0) { if (*p == 'C') { int status; waitpid(-1, &status, WNOHANG); if (WIFEXITED(status) || WIFSIGNALED(status)) exit(0); /* child process vanished */ } } } void sig_readerr(sel_rfd *rfd, int error) { fprintf(stderr, "signal pipe: read: %s\n", strerror(errno)); exit(1); } int main(int argc, char **argv) { int ret; int shell_started = 0; char *directory = NULL; char **program_args = NULL; if (argc > 1 && argv[1][0]) { directory = argv[1]; argc--, argv++; } if (argc > 1) { program_args = argv + 1; } pty_preinit(); asel = sel_new(NULL); netr = sel_rfd_add(asel, 0, net_readdata, net_readerr, NULL); netw = sel_wfd_add(asel, 1, net_written, net_writeerr, NULL); ptyr = sel_rfd_add(asel, -1, pty_readdata, pty_readerr, NULL); ptyw = sel_wfd_add(asel, -1, pty_written, pty_writeerr, NULL); telnet = telnet_new(netw, ptyw); if (pipe(signalpipe) < 0) { perror("pipe"); return 1; } sigr = sel_rfd_add(asel, signalpipe[0], sig_readdata, sig_readerr, NULL); signal(SIGCHLD, sigchld); do { struct shell_data shdata; ret = sel_iterate(asel, -1); if (!shell_started && telnet_shell_ok(telnet, &shdata)) { ptyfd = run_program_in_pty(&shdata, directory, program_args); sel_rfd_setfd(ptyr, ptyfd); sel_wfd_setfd(ptyw, ptyfd); shell_started = 1; } } while (ret == 0); return 0; } putty-0.76/contrib/cygtermd/malloc.c0000644000175000017500000000120314072266310014442 00000000000000/* * malloc.c: implementation of malloc.h */ #include #include #include "malloc.h" extern void fatal(const char *, ...); void *smalloc(size_t size) { void *p; p = malloc(size); if (!p) { fatal("out of memory"); } return p; } void sfree(void *p) { if (p) { free(p); } } void *srealloc(void *p, size_t size) { void *q; if (p) { q = realloc(p, size); } else { q = malloc(size); } if (!q) fatal("out of memory"); return q; } char *dupstr(const char *s) { char *r = smalloc(1+strlen(s)); strcpy(r,s); return r; } putty-0.76/contrib/cygtermd/malloc.h0000644000175000017500000000271614072266310014461 00000000000000/* * malloc.h: safe wrappers around malloc, realloc, free, strdup */ #ifndef UMLWRAP_MALLOC_H #define UMLWRAP_MALLOC_H #include /* * smalloc should guarantee to return a useful pointer - Halibut * can do nothing except die when it's out of memory anyway. */ void *smalloc(size_t size); /* * srealloc should guaranteeably be able to realloc NULL */ void *srealloc(void *p, size_t size); /* * sfree should guaranteeably deal gracefully with freeing NULL */ void sfree(void *p); /* * dupstr is like strdup, but with the never-return-NULL property * of smalloc (and also reliably defined in all environments :-) */ char *dupstr(const char *s); /* * snew allocates one instance of a given type, and casts the * result so as to type-check that you're assigning it to the * right kind of pointer. Protects against allocation bugs * involving allocating the wrong size of thing. */ #define snew(type) \ ( (type *) smalloc (sizeof (type)) ) /* * snewn allocates n instances of a given type, for arrays. */ #define snewn(number, type) \ ( (type *) smalloc ((number) * sizeof (type)) ) /* * sresize wraps realloc so that you specify the new number of * elements and the type of the element, with the same type- * checking advantages. Also type-checks the input pointer. */ #define sresize(array, number, type) \ ( (void)sizeof((array)-(type *)0), \ (type *) srealloc ((array), (number) * sizeof (type)) ) #endif /* UMLWRAP_MALLOC_H */ putty-0.76/contrib/cygtermd/pty.c0000644000175000017500000001132614072266310014016 00000000000000/* * pty.c - pseudo-terminal handling */ #define _XOPEN_SOURCE 500 #include #include #include #include #include #include #include #include #include #include #include #include "pty.h" #include "malloc.h" static char ptyname[FILENAME_MAX]; int master = -1; void pty_preinit(void) { /* * Allocate the pty. */ master = open("/dev/ptmx", O_RDWR); if (master < 0) { perror("/dev/ptmx: open"); exit(1); } if (grantpt(master) < 0) { perror("grantpt"); exit(1); } if (unlockpt(master) < 0) { perror("unlockpt"); exit(1); } } void pty_resize(int w, int h) { struct winsize sz; assert(master >= 0); sz.ws_row = h; sz.ws_col = w; sz.ws_xpixel = sz.ws_ypixel = 0; ioctl(master, TIOCSWINSZ, &sz); } int run_program_in_pty(const struct shell_data *shdata, char *directory, char **program_args) { int slave, pid; char *fallback_args[2]; assert(master >= 0); ptyname[FILENAME_MAX-1] = '\0'; strncpy(ptyname, ptsname(master), FILENAME_MAX-1); #if 0 { struct winsize ws; struct termios ts; /* * FIXME: think up some good defaults here */ if (!ioctl(0, TIOCGWINSZ, &ws)) ioctl(master, TIOCSWINSZ, &ws); if (!tcgetattr(0, &ts)) tcsetattr(master, TCSANOW, &ts); } #endif slave = open(ptyname, O_RDWR | O_NOCTTY); if (slave < 0) { perror("slave pty: open"); return 1; } /* * Fork and execute the command. */ pid = fork(); if (pid < 0) { perror("fork"); return 1; } if (pid == 0) { int i, fd; /* * We are the child. */ close(master); fcntl(slave, F_SETFD, 0); /* don't close on exec */ dup2(slave, 0); dup2(slave, 1); if (slave != 0 && slave != 1) close(slave); dup2(1, 2); setsid(); setpgrp(); 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); if (directory) chdir(directory); /* * Use the provided shell program name, if the user gave * one. Failing that, use $SHELL; failing that, look up * the user's default shell in the password file; failing * _that_, revert to the bog-standard /bin/sh. */ if (!program_args) { char *shell; shell = getenv("SHELL"); if (!shell) { const char *login; uid_t uid; struct passwd *pwd; /* * For maximum generality in the face of multiple * /etc/passwd entries with different login names and * shells but a shared uid, we start by using * getpwnam(getlogin()) if it's available - but we * insist that its uid must match our real one, or we * give up and fall back to getpwuid(getuid()). */ uid = getuid(); login = getlogin(); if (login && (pwd = getpwnam(login)) && pwd->pw_uid == uid) shell = pwd->pw_shell; else if ((pwd = getpwuid(uid))) shell = pwd->pw_shell; } if (!shell) shell = "/bin/sh"; fallback_args[0] = shell; fallback_args[1] = NULL; program_args = fallback_args; } execv(program_args[0], program_args); /* * If we're here, exec has gone badly foom. */ perror("exec"); exit(127); } close(slave); return master; } putty-0.76/contrib/cygtermd/pty.h0000644000175000017500000000122414072266310014017 00000000000000/* * pty.h - declare functions for pty setup */ #ifndef CYGTERMD_PTY_H #define CYGTERMD_PTY_H #include "telnet.h" /* for struct shdata */ /* * Called at program startup to actually allocate a pty, so that * we can start passing in resize events as soon as they arrive. */ void pty_preinit(void); /* * Set the terminal size for the pty. */ void pty_resize(int w, int h); /* * Start a program in a subprocess running in the pty we allocated. * Returns the fd of the pty master. */ int run_program_in_pty(const struct shell_data *shdata, char *directory, char **program_args); #endif /* CYGTERMD_PTY_H */ putty-0.76/contrib/cygtermd/sel.c0000644000175000017500000002154514072266310013771 00000000000000/* * sel.c: implementation of sel.h. */ #include #include #include #include #include #include #include #include #include #include "sel.h" #include "malloc.h" /* ---------------------------------------------------------------------- * Chunk of code lifted from PuTTY's misc.c to manage buffers of * data to be written to an fd. */ #define BUFFER_GRANULE 512 typedef struct bufchain_tag { struct bufchain_granule *head, *tail; size_t buffersize; /* current amount of buffered data */ } bufchain; struct bufchain_granule { struct bufchain_granule *next; size_t buflen, bufpos; char buf[BUFFER_GRANULE]; }; static void bufchain_init(bufchain *ch) { ch->head = ch->tail = NULL; ch->buffersize = 0; } static void bufchain_clear(bufchain *ch) { struct bufchain_granule *b; while (ch->head) { b = ch->head; ch->head = ch->head->next; sfree(b); } ch->tail = NULL; ch->buffersize = 0; } static size_t bufchain_size(bufchain *ch) { return ch->buffersize; } static void bufchain_add(bufchain *ch, const void *data, size_t len) { const char *buf = (const char *)data; if (len == 0) return; ch->buffersize += len; if (ch->tail && ch->tail->buflen < BUFFER_GRANULE) { size_t copylen = BUFFER_GRANULE - ch->tail->buflen; if (copylen > len) copylen = len; memcpy(ch->tail->buf + ch->tail->buflen, buf, copylen); buf += copylen; len -= copylen; ch->tail->buflen += copylen; } while (len > 0) { struct bufchain_granule *newbuf; size_t grainlen = BUFFER_GRANULE; if (grainlen > len) grainlen = len; newbuf = snew(struct bufchain_granule); newbuf->bufpos = 0; newbuf->buflen = grainlen; memcpy(newbuf->buf, buf, grainlen); buf += grainlen; len -= grainlen; if (ch->tail) ch->tail->next = newbuf; else ch->head = ch->tail = newbuf; newbuf->next = NULL; ch->tail = newbuf; } } static void bufchain_consume(bufchain *ch, size_t len) { struct bufchain_granule *tmp; assert(ch->buffersize >= len); while (len > 0) { size_t remlen = len; assert(ch->head != NULL); if (remlen >= ch->head->buflen - ch->head->bufpos) { remlen = ch->head->buflen - ch->head->bufpos; tmp = ch->head; ch->head = tmp->next; sfree(tmp); if (!ch->head) ch->tail = NULL; } else ch->head->bufpos += remlen; ch->buffersize -= remlen; len -= remlen; } } static void bufchain_prefix(bufchain *ch, void **data, size_t *len) { *len = ch->head->buflen - ch->head->bufpos; *data = ch->head->buf + ch->head->bufpos; } /* ---------------------------------------------------------------------- * The actual implementation of the sel interface. */ struct sel { void *ctx; sel_rfd *rhead, *rtail; sel_wfd *whead, *wtail; }; struct sel_rfd { sel *parent; sel_rfd *prev, *next; sel_readdata_fn_t readdata; sel_readerr_fn_t readerr; void *ctx; int fd; int frozen; }; struct sel_wfd { sel *parent; sel_wfd *prev, *next; sel_written_fn_t written; sel_writeerr_fn_t writeerr; void *ctx; int fd; bufchain buf; }; sel *sel_new(void *ctx) { sel *sel = snew(struct sel); sel->ctx = ctx; sel->rhead = sel->rtail = NULL; sel->whead = sel->wtail = NULL; return sel; } sel_wfd *sel_wfd_add(sel *sel, int fd, sel_written_fn_t written, sel_writeerr_fn_t writeerr, void *ctx) { sel_wfd *wfd = snew(sel_wfd); wfd->written = written; wfd->writeerr = writeerr; wfd->ctx = ctx; wfd->fd = fd; bufchain_init(&wfd->buf); wfd->next = NULL; wfd->prev = sel->wtail; if (sel->wtail) sel->wtail->next = wfd; else sel->whead = wfd; sel->wtail = wfd; wfd->parent = sel; return wfd; } sel_rfd *sel_rfd_add(sel *sel, int fd, sel_readdata_fn_t readdata, sel_readerr_fn_t readerr, void *ctx) { sel_rfd *rfd = snew(sel_rfd); rfd->readdata = readdata; rfd->readerr = readerr; rfd->ctx = ctx; rfd->fd = fd; rfd->frozen = 0; rfd->next = NULL; rfd->prev = sel->rtail; if (sel->rtail) sel->rtail->next = rfd; else sel->rhead = rfd; sel->rtail = rfd; rfd->parent = sel; return rfd; } size_t sel_write(sel_wfd *wfd, const void *data, size_t len) { bufchain_add(&wfd->buf, data, len); return bufchain_size(&wfd->buf); } void sel_wfd_setfd(sel_wfd *wfd, int fd) { wfd->fd = fd; } void sel_rfd_setfd(sel_rfd *rfd, int fd) { rfd->fd = fd; } void sel_rfd_freeze(sel_rfd *rfd) { rfd->frozen = 1; } void sel_rfd_unfreeze(sel_rfd *rfd) { rfd->frozen = 0; } int sel_wfd_delete(sel_wfd *wfd) { sel *sel = wfd->parent; int ret; if (wfd->prev) wfd->prev->next = wfd->next; else sel->whead = wfd->next; if (wfd->next) wfd->next->prev = wfd->prev; else sel->wtail = wfd->prev; bufchain_clear(&wfd->buf); ret = wfd->fd; sfree(wfd); return ret; } int sel_rfd_delete(sel_rfd *rfd) { sel *sel = rfd->parent; int ret; if (rfd->prev) rfd->prev->next = rfd->next; else sel->rhead = rfd->next; if (rfd->next) rfd->next->prev = rfd->prev; else sel->rtail = rfd->prev; ret = rfd->fd; sfree(rfd); return ret; } void sel_free(sel *sel) { while (sel->whead) sel_wfd_delete(sel->whead); while (sel->rhead) sel_rfd_delete(sel->rhead); sfree(sel); } void *sel_get_ctx(sel *sel) { return sel->ctx; } void sel_set_ctx(sel *sel, void *ctx) { sel->ctx = ctx; } void *sel_wfd_get_ctx(sel_wfd *wfd) { return wfd->ctx; } void sel_wfd_set_ctx(sel_wfd *wfd, void *ctx) { wfd->ctx = ctx; } void *sel_rfd_get_ctx(sel_rfd *rfd) { return rfd->ctx; } void sel_rfd_set_ctx(sel_rfd *rfd, void *ctx) { rfd->ctx = ctx; } int sel_iterate(sel *sel, long timeout) { sel_rfd *rfd; sel_wfd *wfd; fd_set rset, wset; int maxfd = 0; struct timeval tv, *ptv; char buf[65536]; int ret; FD_ZERO(&rset); FD_ZERO(&wset); for (rfd = sel->rhead; rfd; rfd = rfd->next) { if (rfd->fd >= 0 && !rfd->frozen) { FD_SET(rfd->fd, &rset); if (maxfd < rfd->fd + 1) maxfd = rfd->fd + 1; } } for (wfd = sel->whead; wfd; wfd = wfd->next) { if (wfd->fd >= 0 && bufchain_size(&wfd->buf)) { FD_SET(wfd->fd, &wset); if (maxfd < wfd->fd + 1) maxfd = wfd->fd + 1; } } if (timeout < 0) { ptv = NULL; } else { ptv = &tv; tv.tv_sec = timeout / 1000; tv.tv_usec = 1000 * (timeout % 1000); } do { ret = select(maxfd, &rset, &wset, NULL, ptv); } while (ret < 0 && (errno == EINTR || errno == EAGAIN)); if (ret < 0) return errno; /* * Just in case one of the callbacks destroys an rfd or wfd we * had yet to get round to, we must loop from the start every * single time. Algorithmically irritating, but necessary * unless we want to store the rfd structures in a heavyweight * tree sorted by fd. And let's face it, if we cared about * good algorithmic complexity it's not at all clear we'd be * using select in the first place. */ do { for (wfd = sel->whead; wfd; wfd = wfd->next) if (wfd->fd >= 0 && FD_ISSET(wfd->fd, &wset)) { void *data; size_t len; FD_CLR(wfd->fd, &wset); bufchain_prefix(&wfd->buf, &data, &len); ret = write(wfd->fd, data, len); assert(ret != 0); if (ret < 0) { if (wfd->writeerr) wfd->writeerr(wfd, errno); } else { bufchain_consume(&wfd->buf, len); if (wfd->written) wfd->written(wfd, bufchain_size(&wfd->buf)); } break; } } while (wfd); do { for (rfd = sel->rhead; rfd; rfd = rfd->next) if (rfd->fd >= 0 && !rfd->frozen && FD_ISSET(rfd->fd, &rset)) { FD_CLR(rfd->fd, &rset); ret = read(rfd->fd, buf, sizeof(buf)); if (ret < 0) { if (rfd->readerr) rfd->readerr(rfd, errno); } else { if (rfd->readdata) rfd->readdata(rfd, buf, ret); } break; } } while (rfd); return 0; } putty-0.76/contrib/cygtermd/sel.h0000644000175000017500000001202114072266310013763 00000000000000/* * sel.h: subsystem to manage the grubby details of a select loop, * buffering data to write, and performing the actual writes and * reads. */ #ifndef CYGTERMD_SEL_H #define CYGTERMD_SEL_H typedef struct sel sel; typedef struct sel_wfd sel_wfd; typedef struct sel_rfd sel_rfd; /* * Callback called when some data is written to a wfd. "bufsize" * is the remaining quantity of data buffered in that wfd. */ typedef void (*sel_written_fn_t)(sel_wfd *wfd, size_t bufsize); /* * Callback called when an error occurs on a wfd, preventing * further writing to it. "error" is the errno value. */ typedef void (*sel_writeerr_fn_t)(sel_wfd *wfd, int error); /* * Callback called when some data is read from an rfd. On EOF, * this will be called with len==0. */ typedef void (*sel_readdata_fn_t)(sel_rfd *rfd, void *data, size_t len); /* * Callback called when an error occurs on an rfd, preventing * further reading from it. "error" is the errno value. */ typedef void (*sel_readerr_fn_t)(sel_rfd *rfd, int error); /* * Create a sel structure, which will oversee a select loop. * * "ctx" is user-supplied data stored in the sel structure; it can * be read and written with sel_get_ctx() and sel_set_ctx(). */ sel *sel_new(void *ctx); /* * Add a new fd for writing. Returns a sel_wfd which identifies * that fd in the sel structure, e.g. for putting data into its * output buffer. * * "ctx" is user-supplied data stored in the sel structure; it can * be read and written with sel_wfd_get_ctx() and sel_wfd_set_ctx(). * * "written" and "writeerr" are called from the event loop when * things happen. * * The fd passed in can be -1, in which case it will be assumed to * be unwritable at all times. An actual fd can be passed in later * using sel_wfd_setfd. */ sel_wfd *sel_wfd_add(sel *sel, int fd, sel_written_fn_t written, sel_writeerr_fn_t writeerr, void *ctx); /* * Add a new fd for reading. Returns a sel_rfd which identifies * that fd in the sel structure. * * "ctx" is user-supplied data stored in the sel structure; it can * be read and written with sel_rfd_get_ctx() and sel_rfd_set_ctx(). * * "readdata" and "readerr" are called from the event loop when * things happen. "ctx" is passed to both of them. */ sel_rfd *sel_rfd_add(sel *sel, int fd, sel_readdata_fn_t readdata, sel_readerr_fn_t readerr, void *ctx); /* * Write data into the output buffer of a wfd. Returns the new * size of the output buffer. (You can call it with len==0 if you * just want to know the buffer size; in that situation data==NULL * is also safe.) */ size_t sel_write(sel_wfd *wfd, const void *data, size_t len); /* * Freeze and unfreeze an rfd. When frozen, sel will temporarily * not attempt to read from it, but all its state is retained so * it can be conveniently unfrozen later. (You might use this * facility, for instance, if what you were doing with the * incoming data could only accept it at a certain rate: freeze * the rfd when you've got lots of backlog, and unfreeze it again * when things get calmer.) */ void sel_rfd_freeze(sel_rfd *rfd); void sel_rfd_unfreeze(sel_rfd *rfd); /* * Delete a wfd structure from its containing sel. Returns the * underlying fd, which the client may now consider itself to own * once more. */ int sel_wfd_delete(sel_wfd *wfd); /* * Delete an rfd structure from its containing sel. Returns the * underlying fd, which the client may now consider itself to own * once more. */ int sel_rfd_delete(sel_rfd *rfd); /* * NOT IMPLEMENTED YET: useful functions here might be ones which * enumerated all the wfds/rfds in a sel structure in some * fashion, so you could go through them and remove them all while * doing sensible things to them. Or, at the very least, just * return an arbitrary one of the wfds/rfds. */ /* * Free a sel structure and all its remaining wfds and rfds. */ void sel_free(sel *sel); /* * Read and write the ctx parameters in sel, sel_wfd and sel_rfd. */ void *sel_get_ctx(sel *sel); void sel_set_ctx(sel *sel, void *ctx); void *sel_wfd_get_ctx(sel_wfd *wfd); void sel_wfd_set_ctx(sel_wfd *wfd, void *ctx); void *sel_rfd_get_ctx(sel_rfd *rfd); void sel_rfd_set_ctx(sel_rfd *rfd, void *ctx); /* * Run one iteration of the sel event loop, calling callbacks as * necessary. Returns zero on success; in the event of a fatal * error, returns the errno value. * * "timeout" is a value in microseconds to limit the length of the * select call. Less than zero means to wait indefinitely. */ int sel_iterate(sel *sel, long timeout); /* * Change the underlying fd in a wfd. If set to -1, no write * attempts will take place and the wfd's buffer will simply store * everything passed to sel_write(). If later set to something * other than -1, all that buffered data will become eligible for * real writing. */ void sel_wfd_setfd(sel_wfd *wfd, int fd); /* * Change the underlying fd in a rfd. If set to -1, no read * attempts will take place. */ void sel_rfd_setfd(sel_rfd *rfd, int fd); #endif /* CYGTERMD_SEL_H */ putty-0.76/contrib/cygtermd/telnet.c0000644000175000017500000004577314072266310014512 00000000000000/* * Simple Telnet server code, adapted from PuTTY's own Telnet * client code for use as a Cygwin local pty proxy. */ #include #include #include #include #include "sel.h" #include "telnet.h" #include "malloc.h" #include "pty.h" #define IAC 255 /* interpret as command: */ #define DONT 254 /* you are not to use option */ #define DO 253 /* please, you use option */ #define WONT 252 /* I won't use option */ #define WILL 251 /* I will use option */ #define SB 250 /* interpret as subnegotiation */ #define SE 240 /* end sub negotiation */ #define GA 249 /* you may reverse the line */ #define EL 248 /* erase the current line */ #define EC 247 /* erase the current character */ #define AYT 246 /* are you there */ #define AO 245 /* abort output--but let prog finish */ #define IP 244 /* interrupt process--permanently */ #define BREAK 243 /* break */ #define DM 242 /* data mark--for connect. cleaning */ #define NOP 241 /* nop */ #define EOR 239 /* end of record (transparent mode) */ #define ABORT 238 /* Abort process */ #define SUSP 237 /* Suspend process */ #define xEOF 236 /* End of file: EOF is already used... */ #define TELOPTS(X) \ X(BINARY, 0) /* 8-bit data path */ \ X(ECHO, 1) /* echo */ \ X(RCP, 2) /* prepare to reconnect */ \ X(SGA, 3) /* suppress go ahead */ \ X(NAMS, 4) /* approximate message size */ \ X(STATUS, 5) /* give status */ \ X(TM, 6) /* timing mark */ \ X(RCTE, 7) /* remote controlled transmission and echo */ \ X(NAOL, 8) /* negotiate about output line width */ \ X(NAOP, 9) /* negotiate about output page size */ \ X(NAOCRD, 10) /* negotiate about CR disposition */ \ X(NAOHTS, 11) /* negotiate about horizontal tabstops */ \ X(NAOHTD, 12) /* negotiate about horizontal tab disposition */ \ X(NAOFFD, 13) /* negotiate about formfeed disposition */ \ X(NAOVTS, 14) /* negotiate about vertical tab stops */ \ X(NAOVTD, 15) /* negotiate about vertical tab disposition */ \ X(NAOLFD, 16) /* negotiate about output LF disposition */ \ X(XASCII, 17) /* extended ascic character set */ \ X(LOGOUT, 18) /* force logout */ \ X(BM, 19) /* byte macro */ \ X(DET, 20) /* data entry terminal */ \ X(SUPDUP, 21) /* supdup protocol */ \ X(SUPDUPOUTPUT, 22) /* supdup output */ \ X(SNDLOC, 23) /* send location */ \ X(TTYPE, 24) /* terminal type */ \ X(EOR, 25) /* end or record */ \ X(TUID, 26) /* TACACS user identification */ \ X(OUTMRK, 27) /* output marking */ \ X(TTYLOC, 28) /* terminal location number */ \ X(3270REGIME, 29) /* 3270 regime */ \ X(X3PAD, 30) /* X.3 PAD */ \ X(NAWS, 31) /* window size */ \ X(TSPEED, 32) /* terminal speed */ \ X(LFLOW, 33) /* remote flow control */ \ X(LINEMODE, 34) /* Linemode option */ \ X(XDISPLOC, 35) /* X Display Location */ \ X(OLD_ENVIRON, 36) /* Old - Environment variables */ \ X(AUTHENTICATION, 37) /* Authenticate */ \ X(ENCRYPT, 38) /* Encryption option */ \ X(NEW_ENVIRON, 39) /* New - Environment variables */ \ X(TN3270E, 40) /* TN3270 enhancements */ \ X(XAUTH, 41) \ X(CHARSET, 42) /* Character set */ \ X(RSP, 43) /* Remote serial port */ \ X(COM_PORT_OPTION, 44) /* Com port control */ \ X(SLE, 45) /* Suppress local echo */ \ X(STARTTLS, 46) /* Start TLS */ \ X(KERMIT, 47) /* Automatic Kermit file transfer */ \ X(SEND_URL, 48) \ X(FORWARD_X, 49) \ X(PRAGMA_LOGON, 138) \ X(SSPI_LOGON, 139) \ X(PRAGMA_HEARTBEAT, 140) \ X(EXOPL, 255) /* extended-options-list */ #define telnet_enum(x,y) TELOPT_##x = y, enum { TELOPTS(telnet_enum) dummy=0 }; #undef telnet_enum #define TELQUAL_IS 0 /* option is... */ #define TELQUAL_SEND 1 /* send option */ #define TELQUAL_INFO 2 /* ENVIRON: informational version of IS */ #define BSD_VAR 1 #define BSD_VALUE 0 #define RFC_VAR 0 #define RFC_VALUE 1 #define CR 13 #define LF 10 #define NUL 0 #define iswritable(x) ( (x) != IAC && (x) != CR ) static char *telopt(int opt) { #define telnet_str(x,y) case TELOPT_##x: return #x; switch (opt) { TELOPTS(telnet_str) default: return ""; } #undef telnet_str } static void telnet_size(void *handle, int width, int height); struct Opt { int send; /* what we initially send */ int nsend; /* -ve send if requested to stop it */ int ack, nak; /* +ve and -ve acknowledgements */ int option; /* the option code */ int index; /* index into telnet->opt_states[] */ enum { REQUESTED, ACTIVE, INACTIVE, REALLY_INACTIVE } initial_state; }; enum { OPTINDEX_NAWS, OPTINDEX_TSPEED, OPTINDEX_TTYPE, OPTINDEX_OENV, OPTINDEX_NENV, OPTINDEX_ECHO, OPTINDEX_WE_SGA, OPTINDEX_THEY_SGA, OPTINDEX_WE_BIN, OPTINDEX_THEY_BIN, NUM_OPTS }; static const struct Opt o_naws = { DO, DONT, WILL, WONT, TELOPT_NAWS, OPTINDEX_NAWS, REQUESTED }; static const struct Opt o_ttype = { DO, DONT, WILL, WONT, TELOPT_TTYPE, OPTINDEX_TTYPE, REQUESTED }; static const struct Opt o_oenv = { DO, DONT, WILL, WONT, TELOPT_OLD_ENVIRON, OPTINDEX_OENV, INACTIVE }; static const struct Opt o_nenv = { DO, DONT, WILL, WONT, TELOPT_NEW_ENVIRON, OPTINDEX_NENV, REQUESTED }; static const struct Opt o_echo = { WILL, WONT, DO, DONT, TELOPT_ECHO, OPTINDEX_ECHO, REQUESTED }; static const struct Opt o_they_sga = { DO, DONT, WILL, WONT, TELOPT_SGA, OPTINDEX_WE_SGA, REQUESTED }; static const struct Opt o_we_sga = { WILL, WONT, DO, DONT, TELOPT_SGA, OPTINDEX_THEY_SGA, REQUESTED }; static const struct Opt *const opts[] = { &o_echo, &o_we_sga, &o_they_sga, &o_naws, &o_ttype, &o_oenv, &o_nenv, NULL }; struct Telnet { int opt_states[NUM_OPTS]; 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; sel_wfd *net, *pty; /* * Options we must finish processing before launching the shell */ int old_environ_done, new_environ_done, ttype_done; /* * Ready to start shell? */ int shell_ok; int envvarsize; struct shell_data shdata; }; #define TELNET_MAX_BACKLOG 4096 #define SB_DELTA 1024 static void send_opt(Telnet *telnet, int cmd, int option) { unsigned char b[3]; b[0] = IAC; b[1] = cmd; b[2] = option; sel_write(telnet->net, b, 3); } static void deactivate_option(Telnet *telnet, const struct Opt *o) { if (telnet->opt_states[o->index] == REQUESTED || telnet->opt_states[o->index] == ACTIVE) send_opt(telnet, o->nsend, o->option); telnet->opt_states[o->index] = REALLY_INACTIVE; } /* * Generate side effects of enabling or disabling an option. */ static void option_side_effects(Telnet *telnet, const struct Opt *o, int enabled) { } static void activate_option(Telnet *telnet, const struct Opt *o) { if (o->option == TELOPT_NEW_ENVIRON || o->option == TELOPT_OLD_ENVIRON || o->option == TELOPT_TTYPE) { char buf[6]; buf[0] = IAC; buf[1] = SB; buf[2] = o->option; buf[3] = TELQUAL_SEND; buf[4] = IAC; buf[5] = SE; sel_write(telnet->net, buf, 6); } option_side_effects(telnet, o, 1); } static void done_option(Telnet *telnet, int option) { if (option == TELOPT_OLD_ENVIRON) telnet->old_environ_done = 1; else if (option == TELOPT_NEW_ENVIRON) telnet->new_environ_done = 1; else if (option == TELOPT_TTYPE) telnet->ttype_done = 1; if (telnet->old_environ_done && telnet->new_environ_done && telnet->ttype_done) { telnet->shell_ok = 1; } } static void refused_option(Telnet *telnet, const struct Opt *o) { done_option(telnet, o->option); if (o->send == WILL && o->option == TELOPT_NEW_ENVIRON && telnet->opt_states[o_oenv.index] == INACTIVE) { send_opt(telnet, WILL, TELOPT_OLD_ENVIRON); telnet->opt_states[o_oenv.index] = REQUESTED; telnet->old_environ_done = 0; } option_side_effects(telnet, o, 0); } static void proc_rec_opt(Telnet *telnet, int cmd, int option) { const struct Opt *const *o; for (o = opts; *o; o++) { if ((*o)->option == option && (*o)->ack == cmd) { switch (telnet->opt_states[(*o)->index]) { case REQUESTED: telnet->opt_states[(*o)->index] = ACTIVE; activate_option(telnet, *o); break; case ACTIVE: break; case INACTIVE: telnet->opt_states[(*o)->index] = ACTIVE; send_opt(telnet, (*o)->send, option); activate_option(telnet, *o); break; case REALLY_INACTIVE: send_opt(telnet, (*o)->nsend, option); break; } return; } else if ((*o)->option == option && (*o)->nak == cmd) { switch (telnet->opt_states[(*o)->index]) { case REQUESTED: telnet->opt_states[(*o)->index] = INACTIVE; refused_option(telnet, *o); break; case ACTIVE: telnet->opt_states[(*o)->index] = INACTIVE; send_opt(telnet, (*o)->nsend, option); option_side_effects(telnet, *o, 0); break; case INACTIVE: case REALLY_INACTIVE: break; } return; } } /* * If we reach here, the option was one we weren't prepared to * cope with. If the request was positive (WILL or DO), we send * a negative ack to indicate refusal. If the request was * negative (WONT / DONT), we must do nothing. */ if (cmd == WILL || cmd == DO) send_opt(telnet, (cmd == WILL ? DONT : WONT), option); } static void process_subneg(Telnet *telnet) { int var, value, n; switch (telnet->sb_opt) { case TELOPT_OLD_ENVIRON: case TELOPT_NEW_ENVIRON: if (telnet->sb_buf[0] == TELQUAL_IS) { if (telnet->sb_opt == TELOPT_NEW_ENVIRON) { var = RFC_VAR; value = RFC_VALUE; } else { if (telnet->sb_len > 1 && !(telnet->sb_buf[0] &~ 1)) { var = telnet->sb_buf[0]; value = BSD_VAR ^ BSD_VALUE ^ var; } else { var = BSD_VAR; value = BSD_VALUE; } } } n = 1; while (n < telnet->sb_len && telnet->sb_buf[n] == var) { int varpos, varlen, valpos, vallen; char *result; varpos = ++n; while (n < telnet->sb_len && telnet->sb_buf[n] != value) n++; if (n == telnet->sb_len) break; varlen = n - varpos; valpos = ++n; while (n < telnet->sb_len && telnet->sb_buf[n] != var) n++; vallen = n - valpos; result = snewn(varlen + vallen + 2, char); sprintf(result, "%.*s=%.*s", varlen, telnet->sb_buf+varpos, vallen, telnet->sb_buf+valpos); if (telnet->shdata.nenvvars >= telnet->envvarsize) { telnet->envvarsize = telnet->shdata.nenvvars * 3 / 2 + 16; telnet->shdata.envvars = sresize(telnet->shdata.envvars, telnet->envvarsize, char *); } telnet->shdata.envvars[telnet->shdata.nenvvars++] = result; } done_option(telnet, telnet->sb_opt); break; case TELOPT_TTYPE: if (telnet->sb_len >= 1 && telnet->sb_buf[0] == TELQUAL_IS) { telnet->shdata.termtype = snewn(5 + telnet->sb_len, char); strcpy(telnet->shdata.termtype, "TERM="); for (n = 0; n < telnet->sb_len-1; n++) { char c = telnet->sb_buf[n+1]; if (c >= 'A' && c <= 'Z') c = c + 'a' - 'A'; telnet->shdata.termtype[n+5] = c; } telnet->shdata.termtype[telnet->sb_len+5-1] = '\0'; } done_option(telnet, telnet->sb_opt); break; case TELOPT_NAWS: if (telnet->sb_len == 4) { int w, h; w = (unsigned char)telnet->sb_buf[0]; w = (w << 8) | (unsigned char)telnet->sb_buf[1]; h = (unsigned char)telnet->sb_buf[2]; h = (h << 8) | (unsigned char)telnet->sb_buf[3]; pty_resize(w, h); } break; } } void telnet_from_net(Telnet *telnet, char *buf, int len) { while (len--) { int c = (unsigned char) *buf++; switch (telnet->state) { case TOP_LEVEL: case SEENCR: /* * PuTTY sends Telnet's new line sequence (CR LF on * the wire) in response to the return key. We must * therefore treat that as equivalent to CR NUL, and * send CR to the pty. */ if ((c == NUL || c == '\n') && telnet->state == SEENCR) 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; else if (c == WILL) telnet->state = SEENWILL; else if (c == WONT) telnet->state = SEENWONT; else if (c == SB) telnet->state = SEENSB; else if (c == DM) telnet->state = TOP_LEVEL; else { /* ignore everything else; print it if it's IAC */ if (c == IAC) { char cc = c; sel_write(telnet->pty, &cc, 1); } telnet->state = TOP_LEVEL; } break; case SEENWILL: proc_rec_opt(telnet, WILL, c); telnet->state = TOP_LEVEL; break; case SEENWONT: proc_rec_opt(telnet, WONT, c); telnet->state = TOP_LEVEL; break; case SEENDO: proc_rec_opt(telnet, DO, c); telnet->state = TOP_LEVEL; break; case SEENDONT: proc_rec_opt(telnet, DONT, c); telnet->state = TOP_LEVEL; break; case SEENSB: telnet->sb_opt = c; telnet->sb_len = 0; telnet->state = SUBNEGOT; break; case SUBNEGOT: if (c == IAC) telnet->state = SUBNEG_IAC; else { subneg_addchar: if (telnet->sb_len >= telnet->sb_size) { telnet->sb_size += SB_DELTA; telnet->sb_buf = sresize(telnet->sb_buf, telnet->sb_size, unsigned char); } telnet->sb_buf[telnet->sb_len++] = c; telnet->state = SUBNEGOT; /* in case we came here by goto */ } break; case SUBNEG_IAC: if (c != SE) goto subneg_addchar; /* yes, it's a hack, I know, but... */ else { process_subneg(telnet); telnet->state = TOP_LEVEL; } break; } } } Telnet *telnet_new(sel_wfd *net, sel_wfd *pty) { Telnet *telnet; telnet = snew(Telnet); telnet->sb_buf = NULL; telnet->sb_size = 0; telnet->state = TOP_LEVEL; telnet->net = net; telnet->pty = pty; telnet->shdata.envvars = NULL; telnet->shdata.nenvvars = telnet->envvarsize = 0; telnet->shdata.termtype = NULL; /* * Initialise option states. */ { const struct Opt *const *o; for (o = opts; *o; o++) { telnet->opt_states[(*o)->index] = (*o)->initial_state; if (telnet->opt_states[(*o)->index] == REQUESTED) send_opt(telnet, (*o)->send, (*o)->option); } } telnet->old_environ_done = 1; /* initially don't want to bother */ telnet->new_environ_done = 0; telnet->ttype_done = 0; telnet->shell_ok = 0; return telnet; } void telnet_free(Telnet *telnet) { sfree(telnet->sb_buf); sfree(telnet); } void telnet_from_pty(Telnet *telnet, char *buf, int len) { unsigned char *p, *end; static const unsigned char iac[2] = { IAC, IAC }; static const unsigned char cr[2] = { CR, NUL }; #if 0 static const unsigned char nl[2] = { CR, LF }; #endif p = (unsigned char *)buf; end = (unsigned char *)(buf + len); while (p < end) { unsigned char *q = p; while (p < end && iswritable(*p)) p++; sel_write(telnet->net, q, p - q); while (p < end && !iswritable(*p)) { sel_write(telnet->net, *p == IAC ? iac : cr, 2); p++; } } } int telnet_shell_ok(Telnet *telnet, struct shell_data *shdata) { if (telnet->shell_ok) *shdata = telnet->shdata; /* structure copy */ return telnet->shell_ok; } putty-0.76/contrib/cygtermd/telnet.h0000644000175000017500000000163714072266310014506 00000000000000/* * Header declaring Telnet-handling functions. */ #ifndef CYGTERMD_TELNET_H #define CYGTERMD_TELNET_H #include "sel.h" typedef struct Telnet Telnet; struct shell_data { char **envvars; /* array of "VAR=value" terms */ int nenvvars; char *termtype; }; /* * Create and destroy a Telnet structure. */ Telnet *telnet_new(sel_wfd *net, sel_wfd *pty); void telnet_free(Telnet *telnet); /* * Process data read from the pty. */ void telnet_from_pty(Telnet *telnet, char *buf, int len); /* * Process Telnet protocol data received from the network. */ void telnet_from_net(Telnet *telnet, char *buf, int len); /* * Return true if pre-shell-startup negotiations are complete and * it's safe to start the shell subprocess now. On a true return, * also fills in the shell_data structure. */ int telnet_shell_ok(Telnet *telnet, struct shell_data *shdata); #endif /* CYGTERMD_TELNET_H */ putty-0.76/doc/0000755000175000017500000000000014072266316010410 500000000000000putty-0.76/doc/Makefile0000644000175000017500000000457714072266310011777 00000000000000all: man index.html # Decide on the versionid policy. # # If the user has passed in $(VERSION) on the command line (`make # VERSION="Release 0.56"'), we use that as an explicit version string. # Otherwise, we use `svnversion' to examine the checked-out # documentation source, and if that returns a single revision number # then we invent a version string reflecting just that number. Failing # _that_, we resort to versionids.but which gives 'version # unavailable'. # # So here, we define VERSION using svnversion if it isn't already # defined ... ifndef VERSION SVNVERSION=$(shell test -d .svn && svnversion .) BADCHARS=$(findstring :,$(SVNVERSION))$(findstring S,$(SVNVERSION)) ifeq ($(BADCHARS),) ifneq ($(SVNVERSION),) ifneq ($(SVNVERSION),exported) VERSION=Built from revision $(patsubst M,,$(SVNVERSION)) endif endif endif endif # ... and now, we condition our build behaviour on whether or not # VERSION _is_ defined. ifdef VERSION VERSIONIDS=vstr vstr.but: FORCE printf '\\versionid $(VERSION)\n' > vstr.but FORCE:; else VERSIONIDS=vids endif CHAPTERS := $(SITE) copy blurb intro gs using config pscp psftp plink CHAPTERS += pubkey pageant errors faq feedback pubkeyfmt licence udp CHAPTERS += pgpkeys sshnames CHAPTERS += index $(VERSIONIDS) INPUTS = $(patsubst %,%.but,$(CHAPTERS)) # This is temporary. Hack it locally or something. HALIBUT = halibut index.html: $(INPUTS) $(HALIBUT) --text --html --chm $(INPUTS) # During formal builds it's useful to be able to build this one alone. putty.chm: $(INPUTS) $(HALIBUT) --chm $(INPUTS) # We don't ship this any more. putty.hlp: $(INPUTS) $(HALIBUT) --winhelp $(INPUTS) putty.info: $(INPUTS) $(HALIBUT) --info $(INPUTS) MKMAN = $(HALIBUT) --man=$@ mancfg.but $< MANPAGES = putty.1 puttygen.1 plink.1 pscp.1 psftp.1 puttytel.1 pterm.1 \ pageant.1 psocks.1 psusan.1 man: $(MANPAGES) putty.1: man-putty.but mancfg.but; $(MKMAN) puttygen.1: man-puttygen.but mancfg.but; $(MKMAN) plink.1: man-plink.but mancfg.but; $(MKMAN) pscp.1: man-pscp.but mancfg.but; $(MKMAN) psftp.1: man-psftp.but mancfg.but; $(MKMAN) puttytel.1: man-puttytel.but mancfg.but; $(MKMAN) pterm.1: man-pterm.but mancfg.but; $(MKMAN) pageant.1: man-pageant.but mancfg.but; $(MKMAN) psocks.1: man-psocks.but mancfg.but; $(MKMAN) psusan.1: man-psusan.but mancfg.but; $(MKMAN) mostlyclean: rm -f *.html *.txt *.hlp *.cnt *.1 *.info vstr.but *.hh[pck] clean: mostlyclean rm -f *.chm putty-0.76/doc/blurb.but0000644000175000017500000000307314072266310012147 00000000000000\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}{

If you want to provide feedback on this manual or on the PuTTY tools themselves, see the Feedback page.

} \cfg{html-template-fragment}{%k}{%b} \cfg{info-max-file-size}{0} \cfg{chm-contents-filename}{index.html} \cfg{chm-template-filename}{%k.html} \cfg{chm-head-end}{} \cfg{chm-extra-file}{chm.css} \cfg{xhtml-contents-filename}{index.html} \cfg{text-filename}{puttydoc.txt} \cfg{winhelp-filename}{putty.hlp} \cfg{info-filename}{putty.info} \cfg{chm-filename}{putty.chm} PuTTY is a free (MIT-licensed) Windows Telnet and SSH client. This manual documents PuTTY, and its companion utilities PSCP, PSFTP, Plink, Pageant and PuTTYgen. \e{Note to Unix users:} this manual currently primarily documents the 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} and \cw{pageant} 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 \shortcopyrightdetails. All rights reserved. You may distribute this documentation under the MIT licence. See \k{licence} for the licence text in full. putty-0.76/doc/chm.css0000644000175000017500000000037614072266310011611 00000000000000/* Stylesheet for a Windows .CHM help file */ body { font-size: 75%; font-family: Verdana, Arial, Helvetica, Sans-Serif; } h1 { font-weight: bold; font-size: 150%; } h2 { font-weight: bold; font-size: 130%; } h3 { font-weight: bold; font-size: 120%; } putty-0.76/doc/config.but0000644000175000017500000051202514072266310012310 00000000000000\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 session, by selecting \q{Change Settings} from the window menu. \H{config-session} The Session panel The Session configuration panel contains the basic options you need to specify in order to open a session at all, and also allows you to save your settings to be reloaded later. \S{config-hostname} The \i{host name} section The top box on the Session panel, labelled \q{Specify the destination you want to connect to}, contains the details that need to be filled in before PuTTY can open a session at all. \b The \q{Host Name} box is where you type the name, or the \i{IP address}, of the server you want to connect to. \b The \q{Connection type} controls let you choose what type of connection you want to make: an \i{SSH} network connection, a connection to a local \i{serial line}, or various other kinds of network connection. \lcont{ \b See \k{which-one} for a summary of the differences between the network remote login protocols SSH, Telnet, Rlogin, and SUPDUP. \b See \k{using-serial} for information about using a serial line. \b See \k{using-rawprot} for an explanation of \q{raw} connections. \b See \k{using-telnet} for a little information about Telnet. \b See \k{using-rlogin} for information about using Rlogin. \b See \k{using-supdup} for information about using SUPDUP. \b The \q{Bare ssh-connection} option in the \q{Connection type} control is intended for specialist uses not involving network connections. See \k{config-psusan} for some information about it. } \b The \q{Port} box lets you specify which \i{port number} on the server to connect to. If you select Telnet, Rlogin, SUPDUP, or SSH, this box will be filled in automatically to the usual value, and you will only need to change it if you have an unusual server. If you select Raw mode, you will almost certainly need to fill in the \q{Port} box yourself. If you select \q{Serial} from the \q{Connection type} radio buttons, the \q{Host Name} and \q{Port} boxes are replaced by \q{Serial line} and \q{Speed}; see \k{config-serial} for more details of these. \S{config-saving} \ii{Loading and storing saved sessions} The next part of the Session configuration panel allows you to save your preferred PuTTY options so they will appear automatically the next time you start PuTTY. It also allows you to create \e{saved sessions}, which contain a full set of configuration options plus a host name and protocol. A saved session contains all the information PuTTY needs to start exactly the session you want. \b To save your default settings: first set up the settings the way you want them saved. Then come back to the Session panel. Select the \q{\i{Default Settings}} entry in the saved sessions list, with a single click. Then press the \q{Save} button. If there is a specific host you want to store the details of how to connect to, you should create a saved session, which will be separate from the Default Settings. \b To save a session: first go through the rest of the configuration box setting up all the options you want. Then come back to the Session panel. Enter a name for the saved session in the \q{Saved Sessions} input box. (The server name is often a good choice for a saved session name.) Then press the \q{Save} button. Your saved session name should now appear in the list box. \lcont{ You can also save settings in mid-session, from the \q{Change Settings} dialog. Settings changed since the start of the session will be saved with their current values; as well as settings changed through the dialog, this includes changes in window size, window title changes sent by the server, and so on. } \b To reload a saved session: single-click to select the session name in the list box, and then press the \q{Load} button. Your saved settings should all appear in the configuration panel. \b To modify a saved session: first load it as described above. Then make the changes you want. Come back to the Session panel, and press the \q{Save} button. The new settings will be saved over the top of the old ones. \lcont{ To save the new settings under a different name, you can enter the new name in the \q{Saved Sessions} box, or single-click to select a session name in the list box to overwrite that session. To save \q{Default Settings}, you must single-click the name before saving. } \b To start a saved session immediately: double-click on the session name in the list box. \b To delete a saved session: single-click to select the session name in the list box, and then press the \q{Delete} button. Each saved session is independent of the Default Settings configuration. If you change your preferences and update Default Settings, you must also update every saved session separately. Saved sessions are stored in the \i{Registry}, at the location \c HKEY_CURRENT_USER\Software\SimonTatham\PuTTY\Sessions If you need to store them in a file, you could try the method described in \k{config-file}. \S{config-closeonexit} \q{\ii{Close window} on exit} Finally in the Session panel, there is an option labelled \q{Close window on exit}. This controls whether the PuTTY \i{terminal window} disappears as soon as the session inside it terminates. If you are likely to want to copy and paste text out of the session after it has terminated, or restart the session, you should arrange for this option to be off. \q{Close window on exit} has three settings. \q{Always} means always close the window on exit; \q{Never} means never close on exit (always leave the window open, but \I{inactive window}inactive). The third setting, and the default one, is \q{Only on clean exit}. In this mode, a session which terminates normally will cause its window to close, but one which is aborted unexpectedly by network trouble or a confusing message from the server will leave the window up. \H{config-logging} The Logging panel The Logging configuration panel allows you to save \i{log file}s of your PuTTY sessions, for debugging, analysis or future reference. The main option is a radio-button set that specifies whether PuTTY will log anything at all. The options are: \b \q{None}. This is the default option; in this mode PuTTY will not create a log file at all. \b \q{Printable output}. In this mode, a log file will be created and written to, but only printable text will be saved into it. The various terminal control codes that are typically sent down an interactive session alongside the printable text will be omitted. This might be a useful mode if you want to read a log file in a text editor and hope to be able to make sense of it. \b \q{All session output}. In this mode, \e{everything} sent by the server into your terminal session is logged. If you view the log file in a text editor, therefore, you may well find it full of strange control characters. This is a particularly useful mode if you are experiencing problems with PuTTY's terminal handling: you can record everything that went to the terminal, so that someone else can replay the session later in slow motion and watch to see what went wrong. \b \I{SSH packet log}\q{SSH packets}. In this mode (which is only used by SSH connections), the SSH message packets sent over the encrypted connection are written to the log file (as well as \i{Event Log} entries). You might need this to debug a network-level problem, or more likely to send to the PuTTY authors as part of a bug report. \e{BE WARNED} that if you log in using a password, the password can appear in the log file; see \k{config-logssh} for options that may help to remove sensitive material from the log file before you send it to anyone else. \b \q{SSH packets and raw data}. In this mode, as well as the decrypted packets (as in the previous mode), the \e{raw} (encrypted, compressed, etc) packets are \e{also} logged. This could be useful to diagnose corruption in transit. (The same caveats as the previous mode apply, of course.) Note that the non-SSH logging options (\q{Printable output} and \q{All session output}) only work with PuTTY proper; in programs without terminal emulation (such as Plink), they will have no effect, even if enabled via saved settings. \S{config-logfilename} \q{Log file name} In this edit box you enter the name of the file you want to log the session to. The \q{Browse} button will let you look around your file system to find the right place to put the file; or if you already know exactly where you want it to go, you can just type a pathname into the edit box. There are a few special features in this box. If you use the \c{&} character in the file name box, PuTTY will insert details of the current session in the name of the file it actually opens. The precise replacements it will do are: \b \c{&Y} will be replaced by the current year, as four digits. \b \c{&M} will be replaced by the current month, as two digits. \b \c{&D} will be replaced by the current day of the month, as two digits. \b \c{&T} will be replaced by the current time, as six digits (HHMMSS) with no punctuation. \b \c{&H} will be replaced by the host name you are connecting to. \b \c{&P} will be replaced by the port number you are connecting to on the target host. For example, if you enter the host name \c{c:\\puttylogs\\log-&h-&y&m&d-&t.dat}, you will end up with files looking like \c log-server1.example.com-20010528-110859.dat \c log-unixbox.somewhere.org-20010611-221001.dat \S{config-logfileexists} \q{What to do if the log file already exists} This control allows you to specify what PuTTY should do if it tries to start writing to a log file and it finds the file already exists. You might want to automatically destroy the existing log file and start a new one with the same name. Alternatively, you might want to open the existing log file and add data to the \e{end} of it. Finally (the default option), you might not want to have any automatic behaviour, but to ask the user every time the problem comes up. \S{config-logflush} \I{log file, flushing}\q{Flush log file frequently} This option allows you to control how frequently logged data is flushed to disc. By default, PuTTY will flush data as soon as it is displayed, so that if you view the log file while a session is still open, it will be up to date; and if the client system crashes, there's a greater chance that the data will be preserved. However, this can incur a performance penalty. If PuTTY is running slowly with logging enabled, you could try unchecking this option. Be warned that the log file may not always be up to date as a result (although it will of course be flushed when it is closed, for instance at the end of a session). \S{config-logheader} \I{log file, header}\q{Include header} This option allows you to choose whether to include a header line with the date and time when the log file is opened. It may be useful to disable this if the log file is being used as realtime input to other programs that don't expect the header line. \S{config-logssh} Options specific to \i{SSH packet log}ging These options only apply if SSH packet data is being logged. The following options allow particularly sensitive portions of unencrypted packets to be automatically left out of the log file. They are only intended to deter casual nosiness; an attacker could glean a lot of useful information from even these obfuscated logs (e.g., length of password). \S2{config-logssh-omitpw} \q{Omit known password fields} When checked, decrypted password fields are removed from the log of transmitted packets. (This includes any user responses to challenge-response authentication methods such as \q{keyboard-interactive}.) This does not include X11 authentication data if using X11 forwarding. Note that this will only omit data that PuTTY \e{knows} to be a password. However, if you start another login session within your PuTTY session, for instance, any password used will appear in the clear in the packet log. The next option may be of use to protect against this. This option is enabled by default. \S2{config-logssh-omitdata} \q{Omit session data} When checked, all decrypted \q{session data} is omitted; this is defined as data in terminal sessions and in forwarded channels (TCP, X11, and authentication agent). This will usually substantially reduce the size of the resulting log file. This option is disabled by default. \H{config-terminal} The Terminal panel The Terminal configuration panel allows you to control the behaviour of PuTTY's \i{terminal emulation}. \S{config-autowrap} \q{Auto wrap mode initially on} \ii{Auto wrap mode} controls what happens when text printed in a PuTTY window reaches the right-hand edge of the window. With auto wrap mode on, if a long line of text reaches the right-hand edge, it will wrap over on to the next line so you can still see all the text. With auto wrap mode off, the cursor will stay at the right-hand edge of the screen, and all the characters in the line will be printed on top of each other. If you are running a full-screen application and you occasionally find the screen scrolling up when it looks as if it shouldn't, you could try turning this option off. Auto wrap mode can be turned on and off by \i{control sequence}s sent by the server. This configuration option controls the \e{default} state, which will be restored when you reset the terminal (see \k{reset-terminal}). However, if you modify this option in mid-session using \q{Change Settings}, it will take effect immediately. \S{config-decom} \q{DEC Origin Mode initially on} \i{DEC Origin Mode} is a minor option which controls how PuTTY interprets cursor-position \i{control sequence}s sent by the server. The server can send a control sequence that restricts the \i{scrolling region} of the display. For example, in an editor, the server might reserve a line at the top of the screen and a line at the bottom, and might send a control sequence that causes scrolling operations to affect only the remaining lines. With DEC Origin Mode on, \i{cursor coordinates} are counted from the top of the scrolling region. With it turned off, cursor coordinates are counted from the top of the whole screen regardless of the scrolling region. It is unlikely you would need to change this option, but if you find a full-screen application is displaying pieces of text in what looks like the wrong part of the screen, you could try turning DEC Origin Mode on to see whether that helps. DEC Origin Mode can be turned on and off by control sequences sent by the server. This configuration option controls the \e{default} state, which will be restored when you reset the terminal (see \k{reset-terminal}). However, if you modify this option in mid-session using \q{Change Settings}, it will take effect immediately. \S{config-crlf} \q{Implicit CR in every LF} Most servers send two control characters, \i{CR} and \i{LF}, to start a \i{new line} of the screen. The CR character makes the cursor return to the left-hand side of the screen. The LF character makes the cursor move one line down (and might make the screen scroll). Some servers only send LF, and expect the terminal to move the cursor over to the left automatically. If you come across a server that does this, you will see a \I{stair-stepping}stepped effect on the screen, like this: \c First line of text \c Second line \c Third line If this happens to you, try enabling the \q{Implicit CR in every LF} option, and things might go back to normal: \c First line of text \c Second line \c Third line \S{config-lfcr} \q{Implicit LF in every CR} Most servers send two control characters, \i{CR} and \i{LF}, to start a \i{new line} of the screen. The CR character makes the cursor return to the left-hand side of the screen. The LF character makes the cursor move one line down (and might make the screen scroll). Some servers only send CR, and so the newly written line is overwritten by the following line. This option causes a line feed so that all lines are displayed. \S{config-erase} \q{Use \i{background colour} to erase screen} Not all terminals agree on what colour to turn the screen when the server sends a \q{\i{clear screen}} sequence. Some terminals believe the screen should always be cleared to the \e{default} background colour. Others believe the screen should be cleared to whatever the server has selected as a background colour. There exist applications that expect both kinds of behaviour. Therefore, PuTTY can be configured to do either. With this option disabled, screen clearing is always done in the default background colour. With this option enabled, it is done in the \e{current} background colour. Background-colour erase can be turned on and off by \i{control sequences} sent by the server. This configuration option controls the \e{default} state, which will be restored when you reset the terminal (see \k{reset-terminal}). However, if you modify this option in mid-session using \q{Change Settings}, it will take effect immediately. \S{config-blink} \q{Enable \i{blinking text}} The server can ask PuTTY to display text that blinks on and off. This is very distracting, so PuTTY allows you to turn blinking text off completely. When blinking text is disabled and the server attempts to make some text blink, PuTTY will instead display the text with a \I{background colour, bright}bolded background colour. Blinking text can be turned on and off by \i{control sequence}s sent by the server. This configuration option controls the \e{default} state, which will be restored when you reset the terminal (see \k{reset-terminal}). However, if you modify this option in mid-session using \q{Change Settings}, it will take effect immediately. \S{config-answerback} \q{\ii{Answerback} to ^E} This option controls what PuTTY will send back to the server if the server sends it the ^E \i{enquiry character}. Normally it just sends the string \q{PuTTY}. If you accidentally write the contents of a binary file to your terminal, you will probably find that it contains more than one ^E character, and as a result your next command line will probably read \q{PuTTYPuTTYPuTTY...} as if you had typed the answerback string multiple times at the keyboard. If you set the answerback string to be empty, this problem should go away, but doing so might cause other problems. Note that this is \e{not} the feature of PuTTY which the server will typically use to determine your terminal type. That feature is the \q{\ii{Terminal-type} string} in the Connection panel; see \k{config-termtype} for details. You can include control characters in the answerback string using \c{^C} notation. (Use \c{^~} to get a literal \c{^}.) \S{config-localecho} \q{\ii{Local echo}} With local echo disabled, characters you type into the PuTTY window are not echoed in the window \e{by PuTTY}. They are simply sent to the server. (The \e{server} might choose to \I{remote echo}echo them back to you; this can't be controlled from the PuTTY control panel.) Some types of session need local echo, and many do not. In its default mode, PuTTY will automatically attempt to deduce whether or not local echo is appropriate for the session you are working in. If you find it has made the wrong decision, you can use this configuration option to override its choice: you can force local echo to be turned on, or force it to be turned off, instead of relying on the automatic detection. \S{config-localedit} \q{\ii{Local line editing}} Normally, every character you type into the PuTTY window is sent immediately to the server the moment you type it. If you enable local line editing, this changes. PuTTY will let you edit a whole line at a time locally, and the line will only be sent to the server when you press Return. If you make a mistake, you can use the Backspace key to correct it before you press Return, and the server will never see the mistake. Since it is hard to edit a line locally without being able to see it, local line editing is mostly used in conjunction with \i{local echo} (\k{config-localecho}). This makes it ideal for use in raw mode \#{FIXME} or when connecting to \i{MUD}s or \i{talker}s. (Although some more advanced MUDs do occasionally turn local line editing on and turn local echo off, in order to accept a password from the user.) Some types of session need local line editing, and many do not. In its default mode, PuTTY will automatically attempt to deduce whether or not local line editing is appropriate for the session you are working in. If you find it has made the wrong decision, you can use this configuration option to override its choice: you can force local line editing to be turned on, or force it to be turned off, instead of relying on the automatic detection. \S{config-printing} \ii{Remote-controlled printing} A lot of VT100-compatible terminals support printing under control of the remote server (sometimes called \q{passthrough printing}). PuTTY supports this feature as well, but it is turned off by default. To enable remote-controlled printing, choose a printer from the \q{Printer to send ANSI printer output to} drop-down list box. This should allow you to select from all the printers you have installed drivers for on your computer. Alternatively, you can type the network name of a networked printer (for example, \c{\\\\printserver\\printer1}) even if you haven't already installed a driver for it on your own machine. When the remote server attempts to print some data, PuTTY will send that data to the printer \e{raw} - without translating it, attempting to format it, or doing anything else to it. It is up to you to ensure your remote server knows what type of printer it is talking to. Since PuTTY sends data to the printer raw, it cannot offer options such as portrait versus landscape, print quality, or paper tray selection. All these things would be done by your PC printer driver (which PuTTY bypasses); if you need them done, you will have to find a way to configure your remote server to do them. To disable remote printing again, choose \q{None (printing disabled)} from the printer selection list. This is the default state. \H{config-keyboard} The Keyboard panel The Keyboard configuration panel allows you to control the behaviour of the \i{keyboard} in PuTTY. The correct state for many of these settings depends on what the server to which PuTTY is connecting expects. With a \i{Unix} server, this is likely to depend on the \i\c{termcap} or \i\c{terminfo} entry it uses, which in turn is likely to be controlled by the \q{\ii{Terminal-type} string} setting in the Connection panel; see \k{config-termtype} for details. If none of the settings here seems to help, you may find \k{faq-keyboard} to be useful. \S{config-backspace} Changing the action of the \ii{Backspace key} Some terminals believe that the Backspace key should send the same thing to the server as \i{Control-H} (ASCII code 8). Other terminals believe that the Backspace key should send ASCII code 127 (usually known as \i{Control-?}) so that it can be distinguished from Control-H. This option allows you to choose which code PuTTY generates when you press Backspace. If you are connecting over SSH, PuTTY by default tells the server the value of this option (see \k{config-ttymodes}), so you may find that the Backspace key does the right thing either way. Similarly, if you are connecting to a \i{Unix} system, you will probably find that the Unix \i\c{stty} command lets you configure which the server expects to see, so again you might not need to change which one PuTTY generates. On other systems, the server's expectation might be fixed and you might have no choice but to configure PuTTY. If you do have the choice, we recommend configuring PuTTY to generate Control-? and configuring the server to expect it, because that allows applications such as \c{emacs} to use Control-H for help. (Typing \i{Shift-Backspace} will cause PuTTY to send whichever code isn't configured here as the default.) \S{config-homeend} Changing the action of the \i{Home and End keys} The Unix terminal emulator \i\c{rxvt} disagrees with the rest of the world about what character sequences should be sent to the server by the Home and End keys. \i\c{xterm}, and other terminals, send \c{ESC [1~} for the Home key, and \c{ESC [4~} for the End key. \c{rxvt} sends \c{ESC [H} for the Home key and \c{ESC [Ow} for the End key. If you find an application on which the Home and End keys aren't working, you could try switching this option to see if it helps. \S{config-funkeys} Changing the action of the \i{function keys} and \i{keypad} This option affects the function keys (F1 to F12) and the top row of the numeric keypad. \b In the default mode, labelled \c{ESC [n~}, the function keys generate sequences like \c{ESC [11~}, \c{ESC [12~} and so on. This matches the general behaviour of Digital's terminals. \b In Linux mode, F6 to F12 behave just like the default mode, but F1 to F5 generate \c{ESC [[A} through to \c{ESC [[E}. This mimics the \i{Linux virtual console}. \b In \I{xterm}Xterm R6 mode, F5 to F12 behave like the default mode, but F1 to F4 generate \c{ESC OP} through to \c{ESC OS}, which are the sequences produced by the top row of the \e{keypad} on Digital's terminals. \b In \i{VT400} mode, all the function keys behave like the default mode, but the actual top row of the numeric keypad generates \c{ESC OP} through to \c{ESC OS}. \b In \i{VT100+} mode, the function keys generate \c{ESC OP} through to \c{ESC O[} \b In \i{SCO} mode, the function keys F1 to F12 generate \c{ESC [M} through to \c{ESC [X}. Together with shift, they generate \c{ESC [Y} through to \c{ESC [j}. With control they generate \c{ESC [k} through to \c{ESC [v}, and with shift and control together they generate \c{ESC [w} through to \c{ESC [\{}. If you don't know what any of this means, you probably don't need to fiddle with it. \S{config-appcursor} Controlling \i{Application Cursor Keys} mode Application Cursor Keys mode is a way for the server to change the control sequences sent by the arrow keys. In normal mode, the arrow keys send \c{ESC [A} through to \c{ESC [D}. In application mode, they send \c{ESC OA} through to \c{ESC OD}. Application Cursor Keys mode can be turned on and off by the server, depending on the application. PuTTY allows you to configure the initial state. You can also disable application cursor keys mode completely, using the \q{Features} configuration panel; see \k{config-features-application}. \S{config-appkeypad} Controlling \i{Application Keypad} mode Application Keypad mode is a way for the server to change the behaviour of the numeric keypad. In normal mode, the keypad behaves like a normal Windows keypad: with \i{NumLock} on, the number keys generate numbers, and with NumLock off they act like the arrow keys and Home, End etc. In application mode, all the keypad keys send special control sequences, \e{including} Num Lock. Num Lock stops behaving like Num Lock and becomes another function key. Depending on which version of Windows you run, you may find the Num Lock light still flashes on and off every time you press Num Lock, even when application mode is active and Num Lock is acting like a function key. This is unavoidable. Application keypad mode can be turned on and off by the server, depending on the application. PuTTY allows you to configure the initial state. You can also disable application keypad mode completely, using the \q{Features} configuration panel; see \k{config-features-application}. \S{config-nethack} Using \i{NetHack keypad mode} PuTTY has a special mode for playing NetHack. You can enable it by selecting \q{NetHack} in the \q{Initial state of numeric keypad} control. In this mode, the numeric keypad keys 1-9 generate the NetHack movement commands (\cw{hjklyubn}). The 5 key generates the \c{.} command (do nothing). In addition, pressing Shift or Ctrl with the keypad keys generate the Shift- or Ctrl-keys you would expect (e.g. keypad-7 generates \cq{y}, so Shift-keypad-7 generates \cq{Y} and Ctrl-keypad-7 generates Ctrl-Y); these commands tell NetHack to keep moving you in the same direction until you encounter something interesting. For some reason, this feature only works properly when \i{Num Lock} is on. We don't know why. \S{config-compose} Enabling a DEC-like \ii{Compose key} DEC terminals have a Compose key, which provides an easy-to-remember way of typing \i{accented characters}. You press Compose and then type two more characters. The two characters are \q{combined} to produce an accented character. The choices of character are designed to be easy to remember; for example, composing \q{e} and \q{`} produces the \q{\u00e8{e-grave}} character. If your keyboard has a Windows \i{Application key}, it acts as a Compose key in PuTTY. Alternatively, if you enable the \q{\i{AltGr} acts as Compose key} option, the AltGr key will become a Compose key. \S{config-ctrlalt} \q{Control-Alt is different from \i{AltGr}} Some old keyboards do not have an AltGr key, which can make it difficult to type some characters. PuTTY can be configured to treat the key combination Ctrl + Left Alt the same way as the AltGr key. By default, this checkbox is checked, and the key combination Ctrl + Left Alt does something completely different. PuTTY's usual handling of the left Alt key is to prefix the Escape (Control-\cw{[}) character to whatever character sequence the rest of the keypress would generate. For example, Alt-A generates Escape followed by \c{a}. So Alt-Ctrl-A would generate Escape, followed by Control-A. If you uncheck this box, Ctrl-Alt will become a synonym for AltGr, so you can use it to type extra graphic characters if your keyboard has any. (However, Ctrl-Alt will never act as a Compose key, regardless of the setting of \q{AltGr acts as Compose key} described in \k{config-compose}.) \H{config-bell} The Bell panel The Bell panel controls the \i{terminal bell} feature: the server's ability to cause PuTTY to beep at you. In the default configuration, when the server sends the character with ASCII code 7 (Control-G), PuTTY will play the \i{Windows Default Beep} sound. This is not always what you want the terminal bell feature to do; the Bell panel allows you to configure alternative actions. \S{config-bellstyle} \q{Set the style of bell} This control allows you to select various different actions to occur on a terminal bell: \b Selecting \q{None} \I{terminal bell, disabling}disables the bell completely. In this mode, the server can send as many Control-G characters as it likes and nothing at all will happen. \b \q{Make default system alert sound} is the default setting. It causes the Windows \q{Default Beep} sound to be played. To change what this sound is, or to test it if nothing seems to be happening, use the Sound configurer in the Windows Control Panel. \b \q{\ii{Visual bell}} is a silent alternative to a beeping computer. In this mode, when the server sends a Control-G, the whole PuTTY window will flash white for a fraction of a second. \b \q{Beep using the \i{PC speaker}} is self-explanatory. \b \q{Play a custom \i{sound file}} allows you to specify a particular sound file to be used by PuTTY alone, or even by a particular individual PuTTY session. This allows you to distinguish your PuTTY beeps from any other beeps on the system. If you select this option, you will also need to enter the name of your sound file in the edit control \q{Custom sound file to play as a bell}. \S{config-belltaskbar} \q{\ii{Taskbar}/\I{window caption}caption indication on bell} This feature controls what happens to the PuTTY window's entry in the Windows Taskbar if a bell occurs while the window does not have the input focus. In the default state (\q{Disabled}) nothing unusual happens. If you select \q{Steady}, then when a bell occurs and the window is not in focus, the window's Taskbar entry and its title bar will change colour to let you know that PuTTY session is asking for your attention. The change of colour will persist until you select the window, so you can leave several PuTTY windows minimised in your terminal, go away from your keyboard, and be sure not to have missed any important beeps when you get back. \q{Flashing} is even more eye-catching: the Taskbar entry will continuously flash on and off until you select the window. \S{config-bellovl} \q{Control the \i{bell overload} behaviour} A common user error in a terminal session is to accidentally run the Unix command \c{cat} (or equivalent) on an inappropriate file type, such as an executable, image file, or ZIP file. This produces a huge stream of non-text characters sent to the terminal, which typically includes a lot of bell characters. As a result of this the terminal often doesn't stop beeping for ten minutes, and everybody else in the office gets annoyed. To try to avoid this behaviour, or any other cause of excessive beeping, PuTTY includes a bell overload management feature. In the default configuration, receiving more than five bell characters in a two-second period will cause the overload feature to activate. Once the overload feature is active, further bells will \I{terminal bell, disabling} have no effect at all, so the rest of your binary file will be sent to the screen in silence. After a period of five seconds during which no further bells are received, the overload feature will turn itself off again and bells will be re-enabled. If you want this feature completely disabled, you can turn it off using the checkbox \q{Bell is temporarily disabled when over-used}. Alternatively, if you like the bell overload feature but don't agree with the settings, you can configure the details: how many bells constitute an overload, how short a time period they have to arrive in to do so, and how much silent time is required before the overload feature will deactivate itself. Bell overload mode is always deactivated by any keypress in the terminal. This means it can respond to large unexpected streams of data, but does not interfere with ordinary command-line activities that generate beeps (such as filename completion). \H{config-features} The Features panel PuTTY's \i{terminal emulation} is very highly featured, and can do a lot of things under remote server control. Some of these features can cause problems due to buggy or strangely configured server applications. The Features configuration panel allows you to disable some of PuTTY's more advanced terminal features, in case they cause trouble. \S{config-features-application} Disabling application keypad and cursor keys \I{Application Keypad}Application keypad mode (see \k{config-appkeypad}) and \I{Application Cursor Keys}application cursor keys mode (see \k{config-appcursor}) alter the behaviour of the keypad and cursor keys. Some applications enable these modes but then do not deal correctly with the modified keys. You can force these modes to be permanently disabled no matter what the server tries to do. \S{config-features-mouse} Disabling \cw{xterm}-style \i{mouse reporting} PuTTY allows the server to send \i{control codes} that let it take over the mouse and use it for purposes other than \i{copy and paste}. Applications which use this feature include the text-mode web browser \i\c{links}, the Usenet newsreader \i\c{trn} version 4, and the file manager \i\c{mc} (Midnight Commander). If you find this feature inconvenient, you can disable it using the \q{Disable xterm-style mouse reporting} control. With this box ticked, the mouse will \e{always} do copy and paste in the normal way. Note that even if the application takes over the mouse, you can still manage PuTTY's copy and paste by holding down the Shift key while you select and paste, unless you have deliberately turned this feature off (see \k{config-mouseshift}). \S{config-features-resize} Disabling remote \i{terminal resizing} PuTTY has the ability to change the terminal's size and position in response to commands from the server. If you find PuTTY is doing this unexpectedly or inconveniently, you can tell PuTTY not to respond to those server commands. \S{config-features-altscreen} Disabling switching to the \i{alternate screen} Many terminals, including PuTTY, support an \q{alternate screen}. This is the same size as the ordinary terminal screen, but separate. Typically a screen-based program such as a text editor might switch the terminal to the alternate screen before starting up. Then at the end of the run, it switches back to the primary screen, and you see the screen contents just as they were before starting the editor. Some people prefer this not to happen. If you want your editor to run in the same screen as the rest of your terminal activity, you can disable the alternate screen feature completely. \S{config-features-retitle} Disabling remote \i{window title} changing PuTTY has the ability to change the window title in response to commands from the server. If you find PuTTY is doing this unexpectedly or inconveniently, you can tell PuTTY not to respond to those server commands. \S{config-features-qtitle} Response to remote \i{window title} querying PuTTY can optionally provide the xterm service of allowing server applications to find out the local window title. This feature is disabled by default, but you can turn it on if you really want it. NOTE that this feature is a \e{potential \i{security hazard}}. If a malicious application can write data to your terminal (for example, if you merely \c{cat} a file owned by someone else on the server machine), it can change your window title (unless you have disabled this as mentioned in \k{config-features-retitle}) and then use this service to have the new window title sent back to the server as if typed at the keyboard. This allows an attacker to fake keypresses and potentially cause your server-side applications to do things you didn't want. Therefore this feature is disabled by default, and we recommend you do not set it to \q{Window title} unless you \e{really} know what you are doing. There are three settings for this option: \dt \q{None} \dd PuTTY makes no response whatsoever to the relevant escape sequence. This may upset server-side software that is expecting some sort of response. \dt \q{Empty string} \dd PuTTY makes a well-formed response, but leaves it blank. Thus, server-side software that expects a response is kept happy, but an attacker cannot influence the response string. This is probably the setting you want if you have no better ideas. \dt \q{Window title} \dd PuTTY responds with the actual window title. This is dangerous for the reasons described above. \S{config-features-clearscroll} Disabling remote \i{scrollback clearing} PuTTY has the ability to clear the terminal's scrollback buffer in response to a command from the server. If you find PuTTY is doing this unexpectedly or inconveniently, you can tell PuTTY not to respond to that server command. \S{config-features-dbackspace} Disabling \i{destructive backspace} Normally, when PuTTY receives character 127 (^?) from the server, it will perform a \q{destructive backspace}: move the cursor one space left and delete the character under it. This can apparently cause problems in some applications, so PuTTY provides the ability to configure character 127 to perform a normal backspace (without deleting a character) instead. \S{config-features-charset} Disabling remote \i{character set} configuration PuTTY has the ability to change its character set configuration in response to commands from the server. Some programs send these commands unexpectedly or inconveniently. In particular, \i{BitchX} (an IRC client) seems to have a habit of reconfiguring the character set to something other than the user intended. If you find that accented characters are not showing up the way you expect them to, particularly if you're running BitchX, you could try disabling the remote character set configuration commands. \S{config-features-shaping} Disabling \i{Arabic text shaping} PuTTY supports shaping of Arabic text, which means that if your server sends text written in the basic \i{Unicode} Arabic alphabet then it will convert it to the correct display forms before printing it on the screen. If you are using full-screen software which was not expecting this to happen (especially if you are not an Arabic speaker and you unexpectedly find yourself dealing with Arabic text files in applications which are not Arabic-aware), you might find that the \i{display becomes corrupted}. By ticking this box, you can disable Arabic text shaping so that PuTTY displays precisely the characters it is told to display. You may also find you need to disable bidirectional text display; see \k{config-features-bidi}. \S{config-features-bidi} Disabling \i{bidirectional text} display PuTTY supports bidirectional text display, which means that if your server sends text written in a language which is usually displayed from right to left (such as \i{Arabic} or \i{Hebrew}) then PuTTY will automatically flip it round so that it is displayed in the right direction on the screen. If you are using full-screen software which was not expecting this to happen (especially if you are not an Arabic speaker and you unexpectedly find yourself dealing with Arabic text files in applications which are not Arabic-aware), you might find that the \i{display becomes corrupted}. By ticking this box, you can disable bidirectional text display, so that PuTTY displays text from left to right in all situations. You may also find you need to disable Arabic text shaping; see \k{config-features-shaping}. \H{config-window} The Window panel The Window configuration panel allows you to control aspects of the \i{PuTTY window}. \S{config-winsize} Setting the \I{window size}size of the PuTTY window The \q{\ii{Columns}} and \q{\ii{Rows}} boxes let you set the PuTTY window to a precise size. Of course you can also \I{window resizing}drag the window to a new size while a session is running. \S{config-winsizelock} What to do when the window is resized These options allow you to control what happens when the user tries to \I{window resizing}resize the PuTTY window using its window furniture. There are four options here: \b \q{Change the number of rows and columns}: the font size will not change. (This is the default.) \b \q{Change the size of the font}: the number of rows and columns in the terminal will stay the same, and the \i{font size} will change. \b \q{Change font size when maximised}: when the window is resized, the number of rows and columns will change, \e{except} when the window is \i{maximise}d (or restored), when the font size will change. (In this mode, holding down the Alt key while resizing will also cause the font size to change.) \b \q{Forbid resizing completely}: the terminal will refuse to be resized at all. \S{config-scrollback} Controlling \i{scrollback} These options let you configure the way PuTTY keeps text after it scrolls off the top of the screen (see \k{using-scrollback}). The \q{Lines of scrollback} box lets you configure how many lines of text PuTTY keeps. The \q{Display scrollbar} options allow you to hide the \i{scrollbar} (although you can still view the scrollback using the keyboard as described in \k{using-scrollback}). You can separately configure whether the scrollbar is shown in \i{full-screen} mode and in normal modes. If you are viewing part of the scrollback when the server sends more text to PuTTY, the screen will revert to showing the current terminal contents. You can disable this behaviour by turning off \q{Reset scrollback on display activity}. You can also make the screen revert when you press a key, by turning on \q{Reset scrollback on keypress}. \S{config-erasetoscrollback} \q{Push erased text into scrollback} When this option is enabled, the contents of the terminal screen will be pushed into the scrollback when a server-side application clears the screen, so that your scrollback will contain a better record of what was on your screen in the past. If the application switches to the \i{alternate screen} (see \k{config-features-altscreen} for more about this), then the contents of the primary screen will be visible in the scrollback until the application switches back again. This option is enabled by default. \H{config-appearance} The Appearance panel The Appearance configuration panel allows you to control aspects of the appearance of \I{PuTTY window}PuTTY's window. \S{config-cursor} Controlling the appearance of the \i{cursor} The \q{Cursor appearance} option lets you configure the cursor to be a block, an underline, or a vertical line. A block cursor becomes an empty box when the window loses focus; an underline or a vertical line becomes dotted. The \q{\ii{Cursor blinks}} option makes the cursor blink on and off. This works in any of the cursor modes. \S{config-font} Controlling the \i{font} used in the terminal window This option allows you to choose what font, in what \I{font size}size, the PuTTY terminal window uses to display the text in the session. By default, you will be offered a choice from all the fixed-width fonts installed on the system, since VT100-style terminal handling expects a fixed-width font. If you tick the box marked \q{Allow selection of variable-pitch fonts}, however, PuTTY will offer variable-width fonts as well: if you select one of these, the font will be coerced into fixed-size character cells, which will probably not look very good (but can work OK with some fonts). \S{config-mouseptr} \q{Hide \i{mouse pointer} when typing in window} If you enable this option, the mouse pointer will disappear if the PuTTY window is selected and you press a key. This way, it will not obscure any of the text in the window while you work in your session. As soon as you move the mouse, the pointer will reappear. This option is disabled by default, so the mouse pointer remains visible at all times. \S{config-winborder} Controlling the \i{window border} PuTTY allows you to configure the appearance of the window border to some extent. The checkbox marked \q{Sunken-edge border} changes the appearance of the window border to something more like a DOS box: the inside edge of the border is highlighted as if it sank down to meet the surface inside the window. This makes the border a little bit thicker as well. It's hard to describe well. Try it and see if you like it. You can also configure a completely blank gap between the text in the window and the border, using the \q{Gap between text and window edge} control. By default this is set at one pixel. You can reduce it to zero, or increase it further. \H{config-behaviour} The Behaviour panel The Behaviour configuration panel allows you to control aspects of the behaviour of \I{PuTTY window}PuTTY's window. \S{config-title} Controlling the \i{window title} The \q{Window title} edit box allows you to set the title of the PuTTY window. By default the window title will contain the \i{host name} followed by \q{PuTTY}, for example \c{server1.example.com - PuTTY}. If you want a different window title, this is where to set it. PuTTY allows the server to send \c{xterm} \i{control sequence}s which modify the title of the window in mid-session (unless this is disabled - see \k{config-features-retitle}); the title string set here is therefore only the \e{initial} window title. As well as the \e{window} title, there is also an \c{xterm} sequence to modify the \I{icon title}title of the window's \e{icon}. This makes sense in a windowing system where the window becomes an icon when minimised, such as Windows 3.1 or most X Window System setups; but in the Windows 95-like user interface it isn't as applicable. By default, PuTTY only uses the server-supplied \e{window} title, and ignores the icon title entirely. If for some reason you want to see both titles, check the box marked \q{Separate window and icon titles}. If you do this, PuTTY's window title and Taskbar \I{window caption}caption will change into the server-supplied icon title if you \i{minimise} the PuTTY window, and change back to the server-supplied window title if you restore it. (If the server has not bothered to supply a window or icon title, none of this will happen.) \S{config-warnonclose} \q{Warn before \i{closing window}} If you press the \i{Close button} in a PuTTY window that contains a running session, PuTTY will put up a warning window asking if you really meant to close the window. A window whose session has already terminated can always be closed without a warning. If you want to be able to close a window quickly, you can disable the \q{Warn before closing window} option. \S{config-altf4} \q{Window closes on \i{ALT-F4}} By default, pressing ALT-F4 causes the \I{closing window}window to close (or a warning box to appear; see \k{config-warnonclose}). If you disable the \q{Window closes on ALT-F4} option, then pressing ALT-F4 will simply send a key sequence to the server. \S{config-altspace} \q{\ii{System menu} appears on \i{ALT-Space}} If this option is enabled, then pressing ALT-Space will bring up the PuTTY window's menu, like clicking on the top left corner. If it is disabled, then pressing ALT-Space will just send \c{ESC SPACE} to the server. Some \i{accessibility} programs for Windows may need this option enabling to be able to control PuTTY's window successfully. For instance, \i{Dragon NaturallySpeaking} requires it both to open the system menu via voice, and to close, minimise, maximise and restore the window. \S{config-altonly} \q{\ii{System menu} appears on \i{Alt} alone} If this option is enabled, then pressing and releasing ALT will bring up the PuTTY window's menu, like clicking on the top left corner. If it is disabled, then pressing and releasing ALT will have no effect. \S{config-alwaysontop} \q{Ensure window is \i{always on top}} If this option is enabled, the PuTTY window will stay on top of all other windows. \S{config-fullscreen} \q{\ii{Full screen} on Alt-Enter} If this option is enabled, then pressing Alt-Enter will cause the PuTTY window to become full-screen. Pressing Alt-Enter again will restore the previous window size. The full-screen feature is also available from the \ii{System menu}, even when it is configured not to be available on the Alt-Enter key. See \k{using-fullscreen}. \H{config-translation} The Translation panel The Translation configuration panel allows you to control the translation between the \i{character set} understood by the server and the character set understood by PuTTY. \S{config-charset} Controlling character set translation During an interactive session, PuTTY receives a stream of 8-bit bytes from the server, and in order to display them on the screen it 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} There are \I{East Asian Ambiguous characters}some Unicode characters whose \I{character width}width is not well-defined. In most contexts, such characters should be treated as single-width for the purposes of \I{wrapping, terminal}wrapping and so on; however, in some CJK contexts, they are better treated as double-width for historical reasons, and some server-side applications may expect them to be displayed as such. Setting this option will cause PuTTY to take the double-width interpretation. If you use legacy CJK applications, and you find your lines are wrapping in the wrong places, or you are having other display problems, you might want to play with this setting. This option only has any effect in \i{UTF-8} mode (see \k{config-charset}). \S{config-cyr} \q{\i{Caps Lock} acts as \i{Cyrillic} switch} This feature allows you to switch between a US/UK keyboard layout and a Cyrillic keyboard layout by using the Caps Lock key, if you need to type (for example) \i{Russian} and English side by side in the same document. Currently this feature is not expected to work properly if your native keyboard layout is not US or UK. \S{config-linedraw} Controlling display of \i{line-drawing characters} VT100-series terminals allow the server to send \i{control sequence}s that shift temporarily into a separate character set for drawing simple lines and boxes. However, there are a variety of ways in which PuTTY can attempt to find appropriate characters, and the right one to use depends on the locally configured \i{font}. In general you should probably try lots of options until you find one that your particular font supports. \b \q{Use Unicode line drawing code points} tries to use the box characters that are present in \i{Unicode}. For good Unicode-supporting fonts this is probably the most reliable and functional option. \b \q{Poor man's line drawing} assumes that the font \e{cannot} generate the line and box characters at all, so it will use the \c{+}, \c{-} and \c{|} characters to draw approximations to boxes. You should use this option if none of the other options works. \b \q{Font has XWindows encoding} is for use with fonts that have a special encoding, where the lowest 32 character positions (below the ASCII printable range) contain the line-drawing characters. This is unlikely to be the case with any standard Windows font; it will probably only apply to custom-built fonts or fonts that have been automatically converted from the X Window System. \b \q{Use font in both ANSI and OEM modes} tries to use the same font in two different character sets, to obtain a wider range of characters. This doesn't always work; some fonts claim to be a different size depending on which character set you try to use. \b \q{Use font in OEM mode only} is more reliable than that, but can miss out other characters from the main character set. \S{config-linedrawpaste} Controlling \i{copy and paste} of line drawing characters By default, when you copy and paste a piece of the PuTTY screen that contains VT100 line and box drawing characters, PuTTY will paste them in the form they appear on the screen: either \i{Unicode} line drawing code points, or the \q{poor man's} line-drawing characters \c{+}, \c{-} and \c{|}. The checkbox \q{Copy and paste VT100 line drawing chars as lqqqk} disables this feature, so line-drawing characters will be pasted as the \i{ASCII} characters that were printed to produce them. This will typically mean they come out mostly as \c{q} and \c{x}, with a scattering of \c{jklmntuvw} at the corners. This might be useful if you were trying to recreate the same box layout in another program, for example. Note that this option only applies to line-drawing characters which \e{were} printed by using the VT100 mechanism. Line-drawing characters that were received as Unicode code points will paste as Unicode always. \S{config-utf8linedraw} Combining VT100 line-drawing with UTF-8 If PuTTY is configured to treat data from the server as encoded in UTF-8, then by default it disables the older VT100-style system of control sequences that cause the lower-case letters to be temporarily replaced by line drawing characters. The rationale is that in UTF-8 mode you don't need those control sequences anyway, because all the line-drawing characters they access are available as Unicode characters already, so there's no need for applications to put the terminal into a special state to get at them. Also, it removes a risk of the terminal \e{accidentally} getting into that state: if you accidentally write uncontrolled binary data to a non-UTF-8 terminal, it can be surprisingly common to find that your next shell prompt appears as a sequence of line-drawing characters and then you have to remember or look up how to get out of that mode. So by default, UTF-8 mode simply doesn't \e{have} a confusing mode like that to get into, accidentally or on purpose. However, not all applications will see it that way. Even UTF-8 terminal users will still sometimes have to run software that tries to print line-drawing characters in the old-fashioned way. So the configuration option \q{Enable VT100 line drawing even in UTF-8 mode} puts PuTTY into a hybrid mode in which it understands the VT100-style control sequences that change the meaning of the ASCII lower case letters, \e{and} understands UTF-8. \H{config-selection} The Selection panel The Selection panel allows you to control the way \i{copy and paste} work in the PuTTY window. \S{config-mouse} Changing the actions of the mouse buttons PuTTY's copy and paste mechanism is by default modelled on the Unix \i\c{xterm} application. The X Window System uses a three-button mouse, and the convention in that system is that the \i{left button} \I{selecting text}selects, the \i{right button} extends an existing selection, and the \i{middle button} pastes. Windows often only has two mouse buttons, so when run on Windows, PuTTY is configurable. In PuTTY's default configuration (\q{Compromise}), the \e{right} button pastes, and the \e{middle} button (if you have one) \I{adjusting a selection}extends a selection. If you have a \i{three-button mouse} and you are already used to the \c{xterm} arrangement, you can select it using the \q{Action of mouse buttons} control. Alternatively, with the \q{Windows} option selected, the middle button extends, and the right button brings up a \i{context menu} (on which one of the options is \q{Paste}). (This context menu is always available by holding down Ctrl and right-clicking, regardless of the setting of this option.) (When PuTTY iself is running on Unix, it follows the X Window System convention.) \S{config-mouseshift} \q{Shift overrides application's use of mouse} PuTTY allows the server to send \i{control codes} that let it \I{mouse reporting}take over the mouse and use it for purposes other than \i{copy and paste}. Applications which use this feature include the text-mode web browser \c{links}, the Usenet newsreader \c{trn} version 4, and the file manager \c{mc} (Midnight Commander). When running one of these applications, pressing the mouse buttons no longer performs copy and paste. If you do need to copy and paste, you can still do so if you hold down Shift while you do your mouse clicks. However, it is possible in theory for applications to even detect and make use of Shift + mouse clicks. We don't know of any applications that do this, but in case someone ever writes one, unchecking the \q{Shift overrides application's use of mouse} checkbox will cause Shift + mouse clicks to go to the server as well (so that mouse-driven copy and paste will be completely disabled). If you want to prevent the application from taking over the mouse at all, you can do this using the Features control panel; see \k{config-features-mouse}. \S{config-rectselect} Default selection mode As described in \k{using-selection}, PuTTY has two modes of selecting text to be copied to the clipboard. In the default mode (\q{Normal}), dragging the mouse from point A to point B selects to the end of the line containing A, all the lines in between, and from the very beginning of the line containing B. In the other mode (\q{Rectangular block}), dragging the mouse between two points defines a rectangle, and everything within that rectangle is copied. Normally, you have to hold down Alt while dragging the mouse to select a rectangular block. Using the \q{Default selection mode} control, you can set \i{rectangular selection} as the default, and then you have to hold down Alt to get the \e{normal} behaviour. \S{config-clipboards} Assigning copy and paste actions to clipboards Here you can configure which clipboard(s) are written or read by PuTTY's various copy and paste actions. Most platforms, including Windows, have a single system clipboard. On these platforms, PuTTY provides a second clipboard-like facility by permitting you to paste the text you last selected in \e{this window}, whether or not it is currently also in the system clipboard. This is not enabled by default. The X Window System (which underlies most Unix graphical interfaces) provides multiple clipboards (or \q{\i{selections}}), and many applications support more than one of them by a different user interface mechanism. When PuTTY itself is running on Unix, it has more configurability relating to these selections. The two most commonly used selections are called \cq{\i{PRIMARY}} and \cq{\I{CLIPBOARD selection}CLIPBOARD}; in applications supporting both, the usual behaviour is that \cw{PRIMARY} is used by mouse-only actions (selecting text automatically copies it to \cw{PRIMARY}, and \i{middle-clicking} pastes from \cw{PRIMARY}), whereas \cw{CLIPBOARD} is used by explicit Copy and Paste menu items or keypresses such as \i{Ctrl-C} and \i{Ctrl-V}. \S2{config-selection-autocopy} \q{Auto-copy selected text} The checkbox \q{Auto-copy selected text to system clipboard} controls whether or not selecting text in the PuTTY terminal window automatically has the side effect of copying it to the system clipboard, without requiring a separate user interface action. On X, the wording of this option is changed slightly so that \cq{CLIPBOARD} is mentioned in place of the \q{system clipboard}. Text selected in the terminal window will \e{always} be automatically placed in the \cw{PRIMARY} selection, as is conventional, but if you tick this box, it will \e{also} be placed in \cq{CLIPBOARD} at the same time. \S2{config-selection-clipactions} Choosing a clipboard for UI actions PuTTY has three user-interface actions which can be configured to paste into the terminal (not counting menu items). You can click whichever mouse button (if any) is configured to paste (see \k{config-mouse}); you can press \i{Shift-Ins}; or you can press \i{Ctrl-Shift-V}, although that action is not enabled by default. You can configure which of the available clipboards each of these actions pastes from (including turning the paste action off completely). On platforms with a single system clipboard (such as Windows), the available options are to paste from that clipboard or to paste from PuTTY's internal memory of the \i{last selected text} within that window. On X, the standard options are \cw{CLIPBOARD} or \cw{PRIMARY}. (\cw{PRIMARY} is conceptually similar in that it \e{also} refers to the last selected text \dash just across all applications instead of just this window.) The two keyboard options each come with a corresponding key to copy \e{to} the same clipboard. Whatever you configure Shift-Ins to paste from, \i{Ctrl-Ins} will copy to the same location; similarly, \i{Ctrl-Shift-C} will copy to whatever Ctrl-Shift-V pastes from. On X, you can also enter a selection name of your choice. For example, there is a rarely-used standard selection called \cq{\i{SECONDARY}}, which Emacs (for example) can work with if you hold down the Meta key while dragging to select or clicking to paste; if you configure a PuTTY keyboard action to access this clipboard, then you can interoperate with other applications' use of it. Another thing you could do would be to invent a clipboard name yourself, to create a special clipboard shared \e{only} between instances of PuTTY, or between just instances configured in that particular way. \S{config-paste-ctrl-char} \q{Permit control characters in pasted text} It is possible for the clipboard to contain not just text (with newlines and tabs) but also control characters such as ESC which could have surprising effects if pasted into a terminal session, depending on what program is running on the server side. Copying text from a mischievous web page could put such characters onto the clipboard. By default, PuTTY filters out the more unusual control characters, only letting through the more obvious text-formatting characters (newlines, tab, backspace, and DEL). Setting this option stops this filtering; on paste, any character on the clipboard is sent to the session uncensored. This might be useful if you are deliberately using control character pasting as a simple form of scripting, for instance. \H{config-selection-copy} The Copy panel The Copy configuration panel controls behaviour specifically related to copying from the terminal window to the clipboard. \S{config-charclasses} Character classes PuTTY will \I{word-by-word selection}select a word at a time in the terminal window if you \i{double-click} to begin the drag. This section allows you to control precisely what is considered to be a word. Each character is given a \e{class}, which is a small number (typically 0, 1 or 2). PuTTY considers a single word to be any number of adjacent characters in the same class. So by modifying the assignment of characters to classes, you can modify the word-by-word selection behaviour. In the default configuration, the \i{character classes} are: \b Class 0 contains \i{white space} and control characters. \b Class 1 contains most \i{punctuation}. \b Class 2 contains letters, numbers and a few pieces of punctuation (the double quote, minus sign, period, forward slash and underscore). So, for example, if you assign the \c{@} symbol into character class 2, you will be able to select an e-mail address with just a double click. In order to adjust these assignments, you start by selecting a group of characters in the list box. Then enter a class number in the edit box below, and press the \q{Set} button. This mechanism currently only covers ASCII characters, because it isn't feasible to expand the list to cover the whole of Unicode. Character class definitions can be modified by \i{control sequence}s sent by the server. This configuration option controls the \e{default} state, which will be restored when you reset the terminal (see \k{reset-terminal}). However, if you modify this option in mid-session using \q{Change Settings}, it will take effect immediately. \S{config-rtfcopy} Copying in \i{Rich Text Format} If you enable \q{Copy to clipboard in RTF as well as plain text}, PuTTY will write formatting information to the clipboard as well as the actual text you copy. The effect of this is that if you paste into (say) a word processor, the text will appear in the word processor in the same \i{font}, \i{colour}, and style (e.g. bold, underline) PuTTY was using to display it. This option can easily be inconvenient, so by default it is disabled. \H{config-colours} The Colours panel The Colours panel allows you to control PuTTY's use of \i{colour}. \S{config-ansicolour} \q{Allow terminal to specify \i{ANSI colours}} This option is enabled by default. If it is disabled, PuTTY will ignore any \i{control sequence}s sent by the server to request coloured text. If you have a particularly garish application, you might want to turn this option off and make PuTTY only use the default foreground and background colours. \S{config-xtermcolour} \q{Allow terminal to use xterm \i{256-colour mode}} This option is enabled by default. If it is disabled, PuTTY will ignore any control sequences sent by the server which use the extended 256-colour mode supported by recent versions of \cw{xterm}. If you have an application which is supposed to use 256-colour mode and it isn't working, you may find you need to tell your server that your terminal supports 256 colours. On Unix, you do this by ensuring that the setting of \i\cw{TERM} describes a 256-colour-capable terminal. You can check this using a command such as \c{infocmp}: \c $ infocmp | grep colors \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-truecolour} \q{Allow terminal to use 24-bit colour} This option is enabled by default. If it is disabled, PuTTY will ignore any control sequences sent by the server which use the control sequences supported by modern terminals to specify arbitrary 24-bit RGB colour value. \S{config-boldcolour} \q{Indicate bolded text by changing...} 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. Some applications rely on \q{\i{bold black}} being distinguishable from a black background; if you choose \q{The font}, their text may become invisible. \S{config-logpalette} \q{Attempt to use \i{logical palettes}} Logical palettes are a mechanism by which a Windows application running on an \i{8-bit colour} display can select precisely the colours it wants instead of going with the Windows standard defaults. If you are not getting the colours you ask for on an 8-bit display, you can try enabling this option. However, be warned that it's never worked very well. \S{config-syscolour} \q{Use \i{system colours}} Enabling this option will cause PuTTY to ignore the configured colours for \I{default background}\I{default foreground}\q{Default Background/Foreground} and \I{cursor colour}\q{Cursor Colour/Text} (see \k{config-colourcfg}), instead going with the system-wide defaults. Note that non-bold and \i{bold text} will be the same colour if this option is enabled. You might want to change to indicating bold text by font changes (see \k{config-boldcolour}). \S{config-colourcfg} Adjusting the colours in the \i{terminal window} The main colour control allows you to specify exactly what colours things should be displayed in. To modify one of the PuTTY colours, use the list box to select which colour you want to modify. The \i{RGB values} for that colour will appear on the right-hand side of the list box. Now, if you press the \q{Modify} button, you will be presented with a colour selector, in which you can choose a new colour to go in place of the old one. (You may also edit the RGB values directly in the edit boxes, if you wish; each value is an integer from 0 to 255.) PuTTY allows you to set the \i{cursor colour}, the \i{default foreground} and \I{default background}background, and the precise shades of all the \I{ANSI colours}ANSI configurable colours (black, red, green, yellow, blue, magenta, cyan, and white). You can also modify the precise shades used for the \i{bold} versions of these colours; these are used to display bold text if you have chosen to indicate that by colour (see \k{config-boldcolour}), and can also be used if the server asks specifically to use them. (Note that \q{Default Bold Background} is \e{not} the background colour used for bold text; it is only used if the server specifically asks for a bold background.) \H{config-connection} The Connection panel The Connection panel allows you to configure options that apply to more than one type of \i{connection}. \S{config-keepalive} Using \i{keepalives} to prevent disconnection If you find your sessions are closing unexpectedly (most often with \q{Connection reset by peer}) after they have been idle for a while, you might want to try using this option. Some network \i{routers} and \i{firewalls} need to keep track of all connections through them. Usually, these firewalls will assume a connection is dead if no data is transferred in either direction after a certain time interval. This can cause PuTTY sessions to be unexpectedly closed by the firewall if no traffic is seen in the session for some time. The keepalive option (\q{Seconds between keepalives}) allows you to configure PuTTY to send data through the session at regular intervals, in a way that does not disrupt the actual terminal session. If you find your firewall is cutting \i{idle connections} off, you can try entering a non-zero value in this field. The value is measured in seconds; so, for example, if your firewall cuts connections off after ten minutes then you might want to enter 300 seconds (5 minutes) in the box. Note that keepalives are not always helpful. They help if you have a firewall which drops your connection after an idle period; but if the network between you and the server suffers from \i{breaks in connectivity} then keepalives can actually make things worse. If a session is idle, and connectivity is temporarily lost between the endpoints, but the connectivity is restored before either side tries to send anything, then there will be no problem - neither endpoint will notice that anything was wrong. However, if one side does send something during the break, it will repeatedly try to re-send, and eventually give up and abandon the connection. Then when connectivity is restored, the other side will find that the first side doesn't believe there is an open connection any more. Keepalives can make this sort of problem worse, because they increase the probability that PuTTY will attempt to send data during a break in connectivity. (Other types of periodic network activity can cause this behaviour; in particular, SSH-2 re-keys can have this effect. See \k{config-ssh-kex-rekey}.) Therefore, you might find that keepalives help connection loss, or you might find they make it worse, depending on what \e{kind} of network problems you have between you and the server. Keepalives are only supported in Telnet and SSH; the Rlogin, SUPDUP, and Raw protocols offer no way of implementing them. (For an alternative, see \k{config-tcp-keepalives}.) Note that if you are using SSH-1 and the server has a bug that makes it unable to deal with SSH-1 ignore messages (see \k{config-ssh-bug-ignore1}), enabling keepalives will have no effect. \S{config-nodelay} \q{Disable \i{Nagle's algorithm}} Nagle's algorithm is a detail of TCP/IP implementations that tries to minimise the number of small data packets sent down a network connection. With Nagle's algorithm enabled, PuTTY's \i{bandwidth} usage will be slightly more efficient; with it disabled, you may find you get a faster response to your keystrokes when connecting to some types of server. The Nagle algorithm is disabled by default for \i{interactive connections}. \S{config-tcp-keepalives} \q{Enable \i{TCP keepalives}} \e{NOTE:} TCP keepalives should not be confused with the application-level keepalives described in \k{config-keepalive}. If in doubt, you probably want application-level keepalives; TCP keepalives are provided for completeness. The idea of TCP keepalives is similar to application-level keepalives, and the same caveats apply. The main differences are: \b TCP keepalives are available on \e{all} network connection types, including Raw, Rlogin, and SUPDUP. \b The interval between TCP keepalives is usually much longer, typically two hours; this is set by the operating system, and cannot be configured within PuTTY. \b If the operating system does not receive a response to a keepalive, it may send out more in quick succession and terminate the connection if no response is received. TCP keepalives may be more useful for ensuring that \i{half-open connections} are terminated than for keeping a connection alive. TCP keepalives are disabled by default. \S{config-address-family} \q{\i{Internet protocol version}} This option allows the user to select between the old and new Internet protocols and addressing schemes (\i{IPv4} and \i{IPv6}). The selected protocol will be used for most outgoing network connections (including connections to \I{proxy}proxies); however, tunnels have their own configuration, for which see \k{config-ssh-portfwd-address-family}. The default setting is \q{Auto}, which means PuTTY will do something sensible and try to guess which protocol you wanted. (If you specify a literal \i{Internet address}, it will use whichever protocol that address implies. If you provide a \i{hostname}, it will see what kinds of address exist for that hostname; it will use IPv6 if there is an IPv6 address available, and fall back to IPv4 if not.) If you need to force PuTTY to use a particular protocol, you can explicitly set this to \q{IPv4} or \q{IPv6}. \S{config-loghost} \I{logical host name}\q{Logical name of remote host} This allows you to tell PuTTY that the host it will really end up connecting to is different from where it thinks it is making a network connection. You might use this, for instance, if you had set up an SSH port forwarding in one PuTTY session so that connections to some arbitrary port (say, \cw{localhost} port 10022) were forwarded to a second machine's SSH port (say, \cw{foovax} port 22), and then started a second PuTTY connecting to the forwarded port. In normal usage, the second PuTTY will access the \i{host key cache} under the host name and port it actually connected to (i.e. \cw{localhost} port 10022 in this example). Using the logical host name option, however, you can configure the second PuTTY to cache the host key under the name of the host \e{you} know that it's \e{really} going to end up talking to (here \c{foovax}). This can be useful if you expect to connect to the same actual server through many different channels (perhaps because your port forwarding arrangements keep changing): by consistently setting the logical host name, you can arrange that PuTTY will not keep asking you to reconfirm its host key. Conversely, if you expect to use the same local port number for port forwardings to lots of different servers, you probably didn't want any particular server's host key cached under that local port number. (For this latter case, you could instead explicitly configure host keys in the relevant sessions; see \k{config-ssh-kex-manual-hostkeys}.) If you just enter a host name for this option, PuTTY will cache the SSH host key under the default SSH port for that host, irrespective of the port you really connected to (since the typical scenario is like the above example: you connect to a silly real port number and your connection ends up forwarded to the normal port-22 SSH server of some other machine). To override this, you can append a port number to the logical host name, separated by a colon. E.g. entering \cq{foovax:2200} as the logical host name will cause the host key to be cached as if you had connected to port 2200 of \c{foovax}. If you provide a host name using this option, it is also displayed in other locations which contain the remote host name, such as the default window title and the default SSH password prompt. This reflects the fact that this is the host you're \e{really} connecting to, which is more important than the mere means you happen to be using to contact that host. (This applies even if you're using a protocol other than SSH.) \H{config-data} The Data panel The Data panel allows you to configure various pieces of data which can be sent to the server to affect your connection at the far end. Each option on this panel applies to more than one protocol. Options which apply to only one protocol appear on that protocol's configuration panels. \S{config-username} \q{\ii{Auto-login username}} All three of the SSH, Telnet, and Rlogin protocols allow you to specify what user name you want to log in as, without having to type it explicitly every time. (Some Telnet servers don't support this.) In this box you can type that user name. \S{config-username-from-env} Use of system username When the previous box (\k{config-username}) is left blank, by default, PuTTY will prompt for a username at the time you make a connection. In some environments, such as the networks of large organisations implementing \i{single sign-on}, a more sensible default may be to use the name of the user logged in to the local operating system (if any); this is particularly likely to be useful with \i{GSSAPI} key exchange and user authentication (see \k{config-ssh-auth-gssapi} and \k{config-ssh-gssapi-kex}). This control allows you to change the default behaviour. The current system username is displayed in the dialog as a convenience. It is not saved in the configuration; if a saved session is later used by a different user, that user's name will be used. \S{config-termtype} \q{\ii{Terminal-type} string} Most servers you might connect to with PuTTY are designed to be connected to from lots of different types of terminal. In order to send the right \i{control sequence}s to each one, the server will need to know what type of terminal it is dealing with. Therefore, each of the SSH, Telnet, and Rlogin protocols allow a text string to be sent down the connection describing the terminal. On a \i{Unix} server, this selects an entry from the \i\c{termcap} or \i\c{terminfo} database that tells applications what \i{control sequences} to send to the terminal, and what character sequences to expect the \i{keyboard} to generate. PuTTY attempts to emulate the Unix \i\c{xterm} program, and by default it reflects this by sending \c{xterm} as a terminal-type string. If you find this is not doing what you want - perhaps the remote system reports \q{Unknown terminal type} - you could try setting this to something different, such as \i\c{vt220}. If you're not sure whether a problem is due to the terminal type setting or not, you probably need to consult the manual for your application or your server. \S{config-termspeed} \q{\ii{Terminal speed}s} The Telnet, Rlogin, and SSH protocols allow the client to specify terminal speeds to the server. This parameter does \e{not} affect the actual speed of the connection, which is always \q{as fast as possible}; it is just a hint that is sometimes used by server software to modify its behaviour. For instance, if a slow speed is indicated, the server may switch to a less \i{bandwidth}-hungry display mode. The value is usually meaningless in a network environment, but PuTTY lets you configure it, in case you find the server is reacting badly to the default value. The format is a pair of numbers separated by a comma, for instance, \c{38400,38400}. The first number represents the output speed (\e{from} the server) in bits per second, and the second is the input speed (\e{to} the server). (Only the first is used in the Rlogin protocol.) This option has no effect on Raw connections. \S{config-environ} Setting \i{environment variables} on the server The Telnet protocol provides a means for the client to pass environment variables to the server. Many Telnet servers have stopped supporting this feature due to security flaws, but PuTTY still supports it for the benefit of any servers which have found other ways around the security problems than just disabling the whole mechanism. Version 2 of the SSH protocol also provides a similar mechanism, which is easier to implement without security flaws. Newer \i{SSH-2} servers are more likely to support it than older ones. This configuration data is not used in the SSH-1, rlogin or raw protocols. To add an environment variable to the list transmitted down the connection, you enter the variable name in the \q{Variable} box, enter its value in the \q{Value} box, and press the \q{Add} button. To remove one from the list, select it in the list box and press \q{Remove}. \H{config-proxy} The Proxy panel The \ii{Proxy} panel allows you to configure PuTTY to use various types of proxy in order to make its network connections. The settings in this panel affect the primary network connection forming your PuTTY session, and also any extra connections made as a result of SSH \i{port forwarding} (see \k{using-port-forwarding}). Note that unlike some software (such as web browsers), PuTTY does not attempt to automatically determine whether to use a proxy and (if so) which one to use for a given destination. If you need to use a proxy, it must always be explicitly configured. \S{config-proxy-type} Setting the proxy type The \q{Proxy type} radio buttons allow you to configure what type of proxy you want PuTTY to use for its network connections. The default setting is \q{None}; in this mode no proxy is used for any connection. \b Selecting \I{HTTP proxy}\q{HTTP} allows you to proxy your connections through a web server supporting the HTTP \cw{CONNECT} command, as documented in \W{http://www.ietf.org/rfc/rfc2817.txt}{RFC 2817}. \b Selecting \q{SOCKS 4} or \q{SOCKS 5} allows you to proxy your connections through a \i{SOCKS server}. \b Many firewalls implement a less formal type of proxy in which a user can make a Telnet connection directly to the firewall machine and enter a command such as \c{connect myhost.com 22} to connect through to an external host. Selecting \I{Telnet proxy}\q{Telnet} allows you to tell PuTTY to use this type of proxy. \b Selecting \I{Local proxy}\q{Local} allows you to specify an arbitrary command on the local machine to act as a proxy. When the session is started, instead of creating a TCP connection, PuTTY runs the command (specified in \k{config-proxy-command}), and uses its standard input and output streams. \lcont{ This could be used, for instance, to talk to some kind of network proxy that PuTTY does not natively support; or you could tunnel a connection over something other than TCP/IP entirely. If you want your local proxy command to make a secondary SSH connection to a proxy host and then tunnel the primary connection over that, you might well want the \c{-nc} command-line option in Plink. See \k{using-cmdline-ncmode} for more information. You can also enable this mode on the command line; see \k{using-cmdline-proxycmd}. } \S{config-proxy-exclude} Excluding parts of the network from proxying Typically you will only need to use a proxy to connect to non-local parts of your network; for example, your proxy might be required for connections outside your company's internal network. In the \q{Exclude Hosts/IPs} box you can enter ranges of IP addresses, or ranges of DNS names, for which PuTTY will avoid using the proxy and make a direct connection instead. The \q{Exclude Hosts/IPs} box may contain more than one exclusion range, separated by commas. Each range can be an IP address or a DNS name, with a \c{*} character allowing wildcards. For example: \c *.example.com This excludes any host with a name ending in \c{.example.com} from proxying. \c 192.168.88.* This excludes any host with an IP address starting with 192.168.88 from proxying. \c 192.168.88.*,*.example.com This excludes both of the above ranges at once. Connections to the local host (the host name \i\c{localhost}, and any \i{loopback IP address}) are never proxied, even if the proxy exclude list does not explicitly contain them. It is very unlikely that this behaviour would ever cause problems, but if it does you can change it by enabling \q{Consider proxying local host connections}. Note that if you are doing \I{proxy DNS}DNS at the proxy (see \k{config-proxy-dns}), you should make sure that your proxy exclusion settings do not depend on knowing the IP address of a host. If the name is passed on to the proxy without PuTTY looking it up, it will never know the IP address and cannot check it against your list. \S{config-proxy-dns} \I{proxy DNS}\ii{Name resolution} when using a proxy If you are using a proxy to access a private network, it can make a difference whether \i{DNS} name resolution is performed by PuTTY itself (on the client machine) or performed by the proxy. The \q{Do DNS name lookup at proxy end} configuration option allows you to control this. If you set it to \q{No}, PuTTY will always do its own DNS, and will always pass an IP address to the proxy. If you set it to \q{Yes}, PuTTY will always pass host names straight to the proxy without trying to look them up first. If you set this option to \q{Auto} (the default), PuTTY will do something it considers appropriate for each type of proxy. Telnet, HTTP, and SOCKS5 proxies will have host names passed straight to them; SOCKS4 proxies will not. Note that if you are doing DNS at the proxy, you should make sure that your proxy exclusion settings (see \k{config-proxy-exclude}) do not depend on knowing the IP address of a host. If the name is passed on to the proxy without PuTTY looking it up, it will never know the IP address and cannot check it against your list. The original SOCKS 4 protocol does not support proxy-side DNS. There is a protocol extension (SOCKS 4A) which does support it, but not all SOCKS 4 servers provide this extension. If you enable proxy DNS and your SOCKS 4 server cannot deal with it, this might be why. \S{config-proxy-auth} \I{proxy username}Username and \I{proxy password}password If your proxy requires \I{proxy authentication}authentication, you can enter a username and a password in the \q{Username} and \q{Password} boxes. \I{security hazard}Note that if you save your session, the proxy password will be saved in plain text, so anyone who can access your PuTTY configuration data will be able to discover it. Authentication is not fully supported for all forms of proxy: \b Username and password authentication is supported for HTTP proxies and SOCKS 5 proxies. \lcont{ \b With SOCKS 5, authentication is via \i{CHAP} if the proxy supports it (this is not supported in \i{PuTTYtel}); otherwise the password is sent to the proxy in \I{plaintext password}plain text. \b With HTTP proxying, the only currently supported authentication method is \I{HTTP basic}\q{basic}, where the password is sent to the proxy in \I{plaintext password}plain text. } \b SOCKS 4 can use the \q{Username} field, but does not support passwords. \b You can specify a way to include a username and password in the Telnet/Local proxy command (see \k{config-proxy-command}). \S{config-proxy-command} Specifying the Telnet or Local proxy command If you are using the \i{Telnet proxy} type, the usual command required by the firewall's Telnet server is \c{connect}, followed by a host name and a port number. If your proxy needs a different command, you can enter an alternative here. If you are using the \i{Local proxy} type, the local command to run is specified here. In this string, you can use \c{\\n} to represent a new-line, \c{\\r} to represent a carriage return, \c{\\t} to represent a tab character, and \c{\\x} followed by two hex digits to represent any other character. \c{\\\\} is used to encode the \c{\\} character itself. Also, the special strings \c{%host} and \c{%port} will be replaced by the host name and port number you want to connect to. The strings \c{%user} and \c{%pass} will be replaced by the proxy username and password you specify. The strings \c{%proxyhost} and \c{%proxyport} will be replaced by the host details specified on the \e{Proxy} panel, if any (this is most likely to be useful for the Local proxy type). To get a literal \c{%} sign, enter \c{%%}. If a Telnet proxy server prompts for a username and password before commands can be sent, you can use a command such as: \c %user\n%pass\nconnect %host %port\n This will send your username and password as the first two lines to the proxy, followed by a command to connect to the desired host and port. Note that if you do not include the \c{%user} or \c{%pass} tokens in the Telnet command, then the \q{Username} and \q{Password} configuration fields will be ignored. \S{config-proxy-logging} Controlling \i{proxy logging} Often the proxy interaction has its own diagnostic output; this is particularly the case for local proxy commands. The setting \q{Print proxy diagnostics in the terminal window} lets you control how much of the proxy's diagnostics are printed to the main terminal window, along with output from your main session. By default (\q{No}), proxy diagnostics are only sent to the Event Log; with \q{Yes} they are also printed to the terminal, where they may get mixed up with your main session. \q{Only until session starts} is a compromise; proxy messages will go to the terminal window until the main session is deemed to have started (in a protocol-dependent way), which is when they're most likely to be interesting; any further proxy-related messages during the session will only go to the Event Log. \H{config-ssh} The SSH panel The \i{SSH} panel allows you to configure options that only apply to SSH sessions. \S{config-command} Executing a specific command on the server In SSH, you don't have to run a general shell session on the server. Instead, you can choose to run a single specific command (such as a mail user agent, for example). If you want to do this, enter the command in the \q{\ii{Remote command}} box. Note that most servers will close the session after executing the command. \S{config-ssh-noshell} \q{Don't start a \I{remote shell}shell or \I{remote command}command at all} If you tick this box, PuTTY will not attempt to run a shell or command after connecting to the remote server. You might want to use this option if you are only using the SSH connection for \i{port forwarding}, and your user account on the server does not have the ability to run a shell. This feature is only available in \i{SSH protocol version 2} (since the version 1 protocol assumes you will always want to run a shell). This feature can also be enabled using the \c{-N} command-line option; see \k{using-cmdline-noshell}. If you use this feature in Plink, you will not be able to terminate the Plink process by any graceful means; the only way to kill it will be by pressing Control-C or sending a kill signal from another program. \S{config-ssh-comp} \q{Enable \i{compression}} This enables data compression in the SSH connection: data sent by the server is compressed before sending, and decompressed at the client end. Likewise, data sent by PuTTY to the server is compressed first and the server decompresses it at the other end. This can help make the most of a low-\i{bandwidth} connection. \S{config-ssh-prot} \q{\i{SSH protocol version}} This allows you to select whether to use \i{SSH protocol version 2} or the older \I{SSH-1}version 1. You should normally leave this at the default of \q{2}. As well as having fewer features, the older SSH-1 protocol is no longer developed, has many known cryptographic weaknesses, and is generally not considered to be secure. PuTTY's protocol 1 implementation is provided mainly for compatibility, and is no longer being enhanced. If a server offers both versions, prefer \q{2}. If you have some server or piece of equipment that only talks SSH-1, select \q{1} here, and do not treat the resulting connection as secure. PuTTY will not automatically fall back to the other version of the protocol if the server turns out not to match your selection here; instead, it will put up an error message and abort the connection. This prevents an active attacker downgrading an intended SSH-2 connection to SSH-1. \S{config-ssh-sharing} Sharing an SSH connection between PuTTY tools 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. It is possible to test programmatically for the existence of a live upstream using Plink. See \k{plink-option-shareexists}. \H{config-ssh-kex} The Kex panel The Kex panel (short for \q{\i{key exchange}}) allows you to configure options related to SSH-2 key exchange. Key exchange occurs at the start of an SSH connection (and occasionally thereafter); it establishes a \i{shared secret} that is used as the basis for all of SSH's security features. It is therefore very important for the security of the connection that the key exchange is secure. Key exchange is a cryptographically intensive process; if either the client or the server is a relatively slow machine, the slower methods may take several tens of seconds to complete. If connection startup is too slow, or the connection hangs periodically, you may want to try changing these settings. If you don't understand what any of this means, it's safe to leave these settings alone. This entire panel is only relevant to SSH protocol version 2; none of these settings affect SSH-1 at all. \S{config-ssh-kex-order} \ii{Key exchange algorithm} selection PuTTY supports a variety of SSH-2 key exchange methods, and allows you to choose which one you prefer to use; configuration is similar to cipher selection (see \k{config-ssh-encryption}). PuTTY currently supports the following key exchange methods: \b \q{ECDH}: \i{elliptic curve} \i{Diffie-Hellman key exchange}. \b \q{Group 14}: Diffie-Hellman key exchange with a well-known 2048-bit group. \b \q{Group 1}: Diffie-Hellman key exchange with a well-known 1024-bit group. We no longer recommend using this method, and it's not used by default in new installations; however, it may be the only method supported by very old server software. \b \q{\ii{Group exchange}}: with this method, instead of using a fixed group, PuTTY requests that the server suggest a group to use for key exchange; the server can avoid groups known to be weak, and possibly invent new ones over time, without any changes required to PuTTY's configuration. We recommend use of this method instead of the well-known groups, if possible. \b \q{\i{RSA key exchange}}: this requires much less computational effort on the part of the client, and somewhat less on the part of the server, than Diffie-Hellman key exchange. \b \q{GSSAPI key exchange}: see \k{config-ssh-gssapi-kex}. If the first algorithm PuTTY finds is below the \q{warn below here} line, you will see a warning box when you make the connection, similar to that for cipher selection (see \k{config-ssh-encryption}). \S2{config-ssh-gssapi-kex} GSSAPI-based key exchange PuTTY supports a set of key exchange methods that also incorporates GSSAPI-based authentication. They are enabled with the \q{Attempt GSSAPI key exchange} checkbox (which also appears on the \q{GSSAPI} panel). PuTTY can only perform the GSSAPI-authenticated key exchange methods when using Kerberos V5, and not other GSSAPI mechanisms. If the user running PuTTY has current Kerberos V5 credentials, then PuTTY will select the GSSAPI key exchange methods in preference to any of the ordinary SSH key exchange methods configured in the preference list. The advantage of doing GSSAPI authentication as part of the SSH key exchange is apparent when you are using credential delegation (see \k{config-ssh-auth-gssapi-delegation}). The SSH key exchange can be repeated later in the session, and this allows your Kerberos V5 credentials (which are typically short-lived) to be automatically re-delegated to the server when they are refreshed on the client. (This feature is commonly referred to as \q{\i{cascading credentials}}.) If your server doesn't support GSSAPI key exchange, it may still support GSSAPI in the SSH user authentication phase. This will still let you log in using your Kerberos credentials, but will only allow you to delegate the credentials that are active at the beginning of the session; they can't be refreshed automatically later, in a long-running session. Another effect of GSSAPI key exchange is that it replaces the usual SSH mechanism of permanent host keys described in \k{gs-hostkey}. So if you use this method, then you won't be asked any interactive questions about whether to accept the server's host key. Instead, the Kerberos exchange will verify the identity of the host you connect to, at the same time as verifying your identity to it. \S{config-ssh-kex-rekey} \ii{Repeat key exchange} If the session key negotiated at connection startup is used too much or for too long, it may become feasible to mount attacks against the SSH connection. Therefore, the SSH-2 protocol specifies that a new key exchange should take place every so often; this can be initiated by either the client or the server. While this renegotiation is taking place, no data can pass through the SSH connection, so it may appear to \q{freeze}. (The occurrence of repeat key exchange is noted in the Event Log; see \k{using-eventlog}.) Usually the same algorithm is used as at the start of the connection, with a similar overhead. These options control how often PuTTY will initiate a repeat key exchange (\q{rekey}). You can also force a key exchange at any time from the Special Commands menu (see \k{using-specials}). \# FIXME: do we have any additions to the SSH-2 specs' advice on these values? Do we want to enforce any limits? \b \q{Max minutes before rekey} specifies the amount of time that is allowed to elapse before a rekey is initiated. If this is set to zero, PuTTY will not rekey due to elapsed time. The SSH-2 protocol specification recommends a timeout of at most 60 minutes. You might have a need to disable time-based rekeys completely for the same reasons that \i{keepalives} aren't always helpful. If you anticipate suffering a network dropout of several hours in the middle of an SSH connection, but were not actually planning to send \e{data} down that connection during those hours, then an attempted rekey in the middle of the dropout will probably cause the connection to be abandoned, whereas if rekeys are disabled then the connection should in principle survive (in the absence of interfering \i{firewalls}). See \k{config-keepalive} for more discussion of these issues; for these purposes, rekeys have much the same properties as keepalives. (Except that rekeys have cryptographic value in themselves, so you should bear that in mind when deciding whether to turn them off.) Note, however, the the SSH \e{server} can still initiate rekeys. \b \q{Minutes between GSSAPI checks}, if you're using GSSAPI key exchange, specifies how often the GSSAPI credential cache is checked to see whether new tickets are available for delegation, or current ones are near expiration. If forwarding of GSSAPI credentials is enabled, PuTTY will try to rekey as necessary to keep the delegated credentials from expiring. Frequent checks are recommended; rekeying only happens when needed. \b \q{Max data before rekey} specifies the amount of data (in bytes) that is permitted to flow in either direction before a rekey is initiated. If this is set to zero, PuTTY will not rekey due to transferred data. The SSH-2 protocol specification recommends a limit of at most 1 gigabyte. \lcont{ As well as specifying a value in bytes, the following shorthand can be used: \b \cq{1k} specifies 1 kilobyte (1024 bytes). \b \cq{1M} specifies 1 megabyte (1024 kilobytes). \b \cq{1G} specifies 1 gigabyte (1024 megabytes). } 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 occurring 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-hostkey} The Host Keys panel The Host Keys panel allows you to configure options related to SSH-2 \i{host key management}. Host keys are used to prove the server's identity, and assure you that the server is not being spoofed (either by a man-in-the-middle attack or by completely replacing it on the network). See \k{gs-hostkey} for a basic introduction to host keys. This entire panel is only relevant to SSH protocol version 2; none of these settings affect SSH-1 at all. \S{config-ssh-hostkey-order} \ii{Host key type} selection PuTTY supports a variety of SSH-2 host key types, and allows you to choose which one you prefer to use to identify the server. Configuration is similar to cipher selection (see \k{config-ssh-encryption}). PuTTY currently supports the following host key types: \b \q{\i{Ed25519}}: \I{EdDSA}Edwards-curve DSA using a twisted Edwards curve with modulus \cw{2^255-19}. \b \q{\i{Ed448}}: another \I{EdDSA}Edwards-curve DSA type, using a larger elliptic curve with a 448-bit instead of 255-bit modulus (so it has a higher security level than Ed25519). \b \q{ECDSA}: \i{elliptic curve} \i{DSA} using one of the NIST-standardised elliptic curves. \b \q{DSA}: straightforward \i{DSA} using modular exponentiation. \b \q{RSA}: the ordinary \i{RSA} algorithm. If PuTTY already has one or more host keys stored for the server, it will by default prefer to use one of those, even if the server has a key type that is higher in the preference order. You can add such a key to PuTTY's cache from within an existing session using the \q{Special Commands} menu; see \k{using-specials}. Otherwise, PuTTY will choose a key type based purely on the preference order you specify in the configuration. If the first key type PuTTY finds is below the \q{warn below here} line, you will see a warning box when you make the connection, similar to that for cipher selection (see \k{config-ssh-encryption}). \S{config-ssh-prefer-known-hostkeys} Preferring known host keys By default, PuTTY will adjust the preference order for host key algorithms so that any host keys it already knows are moved to the top of the list. This prevents you from having to check and confirm a new host key for a server you already had one for (e.g. because the server has generated an alternative key of a type higher in PuTTY's preference order, or because you changed the preference order itself). However, on the other hand, it can leak information to a listener in the network about \e{whether} you already know a host key for this server. For this reason, this policy is configurable. By turning this checkbox off, you can reset PuTTY to always use the exact order of host key algorithms configured in the preference list described in \k{config-ssh-hostkey-order}, so that a listener will find out nothing about what keys you had stored. \S{config-ssh-kex-manual-hostkeys} \ii{Manually configuring host keys} In some situations, if PuTTY's automated host key management is not doing what you need, you might need to manually configure PuTTY to accept a specific host key, or one of a specific set of host keys. One reason why you might want to do this is because the host name PuTTY is connecting to is using round-robin DNS to return one of multiple actual servers, and they all have different host keys. In that situation, you might need to configure PuTTY to accept any of a list of host keys for the possible servers, while still rejecting any key not in that list. Another reason is if PuTTY's automated host key management is completely unavailable, e.g. because PuTTY (or Plink or PSFTP, etc) is running in a Windows environment without access to the Registry. In that situation, you will probably want to use the \cw{-hostkey} command-line option to configure the expected host key(s); see \k{using-cmdline-hostkey}. For situations where PuTTY's automated host key management simply picks the wrong host name to store a key under, you may want to consider setting a \q{logical host name} instead; see \k{config-loghost}. To configure manual host keys via the GUI, enter some text describing the host key into the edit box in the \q{Manually configure host keys for this connection} container, and press the \q{Add} button. The text will appear in the \q{Host keys or fingerprints to accept} list box. You can remove keys again with the \q{Remove} button. The text describing a host key can be in one of the following formats: \b An \I{SHA256 fingerprint}SHA-256-based host key fingerprint of the form displayed in PuTTY's Event Log and host key dialog boxes, i.e. \cq{SHA256:} followed by 43 case-sensitive characters. \b An \I{MD5 fingerprint}MD5-based host key fingerprint, i.e. sixteen 2-digit hex numbers separated by colons, optionally preceded by the prefix \cq{MD5:}. (The case of the characters does not matter.) \b A base64-encoded blob describing an SSH-2 public key in OpenSSH's one-line public key format. How you acquire a public key in this format is server-dependent; on an OpenSSH server it can typically be found in a location like \c{/etc/ssh/ssh_host_rsa_key.pub}. If this box contains at least one host key or fingerprint when PuTTY makes an SSH connection, then PuTTY's automated host key management is completely bypassed: the connection will be permitted if and only if the host key presented by the server is one of the keys listed in this box, and the \I{host key cache}host key store in the Registry will be neither read \e{nor written}, unless you explicitly do so. If the box is empty (as it usually is), then PuTTY's automated host key management will work as normal. \H{config-ssh-encryption} The Cipher panel 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{ChaCha20-Poly1305}, a combined cipher and \i{MAC} (SSH-2 only) \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-banner} \q{Display pre-authentication banner} SSH-2 servers can provide a message for clients to display to the prospective user before the user logs in; this is sometimes known as a pre-authentication \q{\i{banner}}. Typically this is used to provide information about the server and legal notices. By default, PuTTY displays this message before prompting for a password or similar credentials (although, unfortunately, not before prompting for a login name, due to the nature of the protocol design). By unchecking this option, display of the banner can be suppressed entirely. \S{config-ssh-noauth} \q{Bypass authentication entirely} In SSH-2, it is in principle possible to establish a connection without using SSH's mechanisms to identify or prove who you are to the server. An SSH server could prefer to handle authentication in the data channel, for instance, or simply require no user authentication whatsoever. By default, PuTTY assumes the server requires authentication (we've never heard of one that doesn't), and thus must start this process with a username. If you find you are getting username prompts that you cannot answer, you could try enabling this option. However, most SSH servers will reject this. This is not the option you want if you have a username and just want PuTTY to remember it; for that see \k{config-username}. It's also probably not what if you're trying to set up passwordless login to a mainstream SSH server; depending on the server, you probably wanted public-key authentication (\k{pubkey}) or perhaps GSSAPI authentication (\k{config-ssh-auth-gssapi}). (These are still forms of authentication, even if you don't have to interact with them.) This option only affects SSH-2 connections. SSH-1 connections always require an authentication step. \S{config-ssh-notrivialauth} \q{Disconnect if authentication succeeds trivially} This option causes PuTTY to abandon an SSH session and disconnect from the server, if the server accepted authentication without ever having asked for any kind of password or signature or token. This might be used as a security measure. There are some forms of attack against an SSH client user which work by terminating the SSH authentication stage early, and then doing something in the main part of the SSH session which \e{looks} like part of the authentication, but isn't really. For example, instead of demanding a signature from your public key, for which PuTTY would ask for your key's passphrase, a compromised or malicious server might allow you to log in with no signature or password at all, and then print a message that \e{imitates} PuTTY's request for your passphrase, in the hope that you would type it in. (In fact, the passphrase for your public key should not be sent to any server.) PuTTY's main defence against attacks of this type is the \q{trust sigil} system: messages in the PuTTY window that are truly originated by PuTTY itself are shown next to a small copy of the PuTTY icon, which the server cannot fake when it tries to imitate the same message using terminal output. However, if you think you might be at risk of this kind of thing anyway (if you don't watch closely for the trust sigils, or if you think you're at extra risk of one of your servers being malicious), then you could enable this option as an extra defence. Then, if the server tries any of these attacks involving letting you through the authentication stage, PuTTY will disconnect from the server before it can send a follow-up fake prompt or other type of attack. On the other hand, some servers \e{legitimately} let you through the SSH authentication phase trivially, either because they are genuinely public, or because the important authentication step happens during the terminal session. (An example might be an SSH server that connects you directly to the terminal login prompt of a legacy mainframe.) So enabling this option might cause some kinds of session to stop working. It's up to you. \S{config-ssh-tryagent} \q{Attempt authentication using Pageant} If this option is enabled, then PuTTY will look for Pageant (the SSH private-key storage agent) and attempt to authenticate with any suitable public keys Pageant currently holds. This behaviour is almost always desirable, and is therefore enabled by default. In rare cases you might need to turn it off in order to force authentication by some non-public-key method such as passwords. This option can also be controlled using the \c{-noagent} command-line option. See \k{using-cmdline-agentauth}. See \k{pageant} for more information about Pageant in general. \S{config-ssh-tis} \q{Attempt \I{TIS authentication}TIS or \i{CryptoCard authentication}} TIS and CryptoCard authentication are (despite their names) generic forms of simple \I{challenge/response authentication}challenge/response authentication available in SSH protocol version 1 only. You might use them if you were using \i{S/Key} \i{one-time passwords}, for example, or if you had a physical \i{security token} that generated responses to authentication challenges. They can even be used to prompt for simple passwords. With this switch enabled, PuTTY will attempt these forms of authentication if the server is willing to try them. You will be presented with a challenge string (which may be different every time) and must supply the correct response in order to log in. If your server supports this, you should talk to your system administrator about precisely what form these challenges and responses take. \S{config-ssh-ki} \q{Attempt \i{keyboard-interactive authentication}} The SSH-2 equivalent of TIS authentication is called \q{keyboard-interactive}. It is a flexible authentication method using an arbitrary sequence of requests and responses; so it is not only useful for \I{challenge/response authentication}challenge/response mechanisms such as \i{S/Key}, but it can also be used for (for example) asking the user for a \I{password expiry}new password when the old one has expired. PuTTY leaves this option enabled by default, but supplies a switch to turn it off in case you should have trouble with it. \S{config-ssh-agentfwd} \q{Allow \i{agent forwarding}} This option allows the SSH server to open forwarded connections back to your local copy of \i{Pageant}. If you are not running Pageant, this option will do nothing. See \k{pageant} for general information on Pageant, and \k{pageant-forward} for information on agent forwarding. Note that there is a security risk involved with enabling this option; see \k{pageant-security} for details. \S{config-ssh-changeuser} \q{Allow attempted \i{changes of username} in SSH-2} In the SSH-1 protocol, it is impossible to change username after failing to authenticate. So if you mis-type your username at the PuTTY \q{login as:} prompt, you will not be able to change it except by restarting PuTTY. The SSH-2 protocol \e{does} allow changes of username, in principle, but does not make it mandatory for SSH-2 servers to accept them. In particular, \i{OpenSSH} does not accept a change of username; once you have sent one username, it will reject attempts to try to authenticate as another user. (Depending on the version of OpenSSH, it may quietly return failure for all login attempts, or it may send an error message.) For this reason, PuTTY will by default not prompt you for your username more than once, in case the server complains. If you know your server can cope with it, you can enable the \q{Allow attempted changes of username} option to modify PuTTY's behaviour. \S{config-ssh-privkey} \q{\ii{Private key} file for authentication} This box is where you enter the name of your private key file if you are using \i{public key authentication}. See \k{pubkey} for information about public key authentication in SSH. This key must be in PuTTY's native format (\c{*.\i{PPK}}). If you have a private key in another format that you want to use with PuTTY, see \k{puttygen-conversions}. You can use the authentication agent \i{Pageant} so that you do not need to explicitly configure a key here; see \k{pageant}. If a private key file is specified here with Pageant running, PuTTY will first try asking Pageant to authenticate with that key, and ignore any other keys Pageant may have. If that fails, PuTTY will ask for a passphrase as normal. You can also specify a \e{public} key file in this case (in RFC 4716 or OpenSSH format), as that's sufficient to identify the key to Pageant, but of course if Pageant isn't present PuTTY can't fall back to using this file itself. \H{config-ssh-auth-gssapi} The \i{GSSAPI} panel The \q{GSSAPI} subpanel of the \q{Auth} panel controls the use of GSSAPI authentication. This is a mechanism which delegates the authentication exchange to a library elsewhere on the client machine, which in principle can authenticate in many different ways but in practice is usually used with the \i{Kerberos} \i{single sign-on} protocol to implement \i{passwordless login}. GSSAPI authentication is only available in the SSH-2 protocol. PuTTY supports two forms of GSSAPI-based authentication. In one of them, the SSH key exchange happens in the normal way, and GSSAPI is only involved in authenticating the user. The checkbox labelled \q{Attempt GSSAPI authentication} controls this form. In the other method, GSSAPI-based authentication is combined with the SSH key exchange phase. If this succeeds, then the SSH authentication step has nothing left to do. See \k{config-ssh-gssapi-kex} for more information about this method. The checkbox labelled \q{Attempt GSSAPI key exchange} controls this form. (The same checkbox appears on the \q{Kex} panel.) If one or both of these controls is enabled, then GSSAPI authentication will be attempted in one form or the other, and (typically) if your client machine has valid Kerberos credentials loaded, then PuTTY should be able to authenticate automatically to servers that support Kerberos logins. If both of those checkboxes are disabled, PuTTY will not try any form of GSSAPI at all, and the rest of this panel will be unused. \S{config-ssh-auth-gssapi-delegation} \q{Allow GSSAPI credential delegation} \i{GSSAPI credential delegation} is a mechanism for passing on your Kerberos (or other) identity to the session on the SSH server. If you enable this option, then not only will PuTTY be able to log in automatically to a server that accepts your Kerberos credentials, but also you will be able to connect out from that server to other Kerberos-supporting services and use the same credentials just as automatically. (This option is the Kerberos analogue of SSH agent forwarding; see \k{pageant-forward} for some information on that.) Note that, like SSH agent forwarding, there is a security implication in the use of this option: the administrator of the server you connect to, or anyone else who has cracked the administrator account on that server, could fake your identity when connecting to further Kerberos-supporting services. However, Kerberos sites are typically run by a central authority, so the administrator of one server is likely to already have access to the other services too; so this would typically be less of a risk than SSH agent forwarding. If your connection is not using GSSAPI key exchange, it is possible for the delegation to expire during your session. See \k{config-ssh-gssapi-kex} for more information. \S{config-ssh-auth-gssapi-libraries} Preference order for GSSAPI libraries GSSAPI is a mechanism which allows more than one authentication method to be accessed through the same interface. Therefore, more than one authentication library may exist on your system which can be accessed using GSSAPI. PuTTY contains native support for a few well-known such libraries (including Windows' \i{SSPI}), and will look for all of them on your system and use whichever it finds. If more than one exists on your system and you need to use a specific one, you can adjust the order in which it will search using this preference list control. One of the options in the preference list is to use a user-specified GSSAPI library. If the library you want to use is not mentioned by name in PuTTY's list of options, you can enter its full pathname in the \q{User-supplied GSSAPI library path} field, and move the \q{User-supplied GSSAPI library} option in the preference list to make sure it is selected before anything else. On Windows, such libraries are files with a \I{DLL}\cw{.dll} extension, and must have been built in the same way as the PuTTY executable you're running; if you have a 32-bit DLL, you must run a 32-bit version of PuTTY, and the same with 64-bit (see \k{faq-32bit-64bit}). On Unix, shared libraries generally have a \cw{.so} extension. \H{config-ssh-tty} The TTY panel The TTY panel lets you configure the remote pseudo-terminal. \S{config-ssh-pty} \I{pseudo-terminal allocation}\q{Don't allocate a pseudo-terminal} When connecting to a \i{Unix} system, most \I{interactive connections}interactive shell sessions are run in a \e{pseudo-terminal}, which allows the Unix system to pretend it's talking to a real physical terminal device but allows the SSH server to catch all the data coming from that fake device and send it back to the client. Occasionally you might find you have a need to run a session \e{not} in a pseudo-terminal. In PuTTY, this is generally only useful for very specialist purposes; although in Plink (see \k{plink}) it is the usual way of working. \S{config-ttymodes} Sending \i{terminal modes} The SSH protocol allows the client to send \q{terminal modes} for the remote pseudo-terminal. These usually control the server's expectation of the local terminal's behaviour. If your server does not have sensible defaults for these modes, you may find that changing them here helps, although the server is at liberty to ignore your changes. If you don't understand any of this, it's safe to leave these settings alone. (None of these settings will have any effect if no pseudo-terminal is requested or allocated.) You can change what happens for a particular mode by selecting it in the list, choosing one of the options and specifying the exact value if necessary, and hitting \q{Set}. The effect of the options is as follows: \b If the \q{Auto} option is selected, the PuTTY tools will decide whether to specify that mode to the server, and if so, will send a sensible value. \lcont{ PuTTY proper will send modes that it has an opinion on (currently only the code for the Backspace key, \cw{ERASE}, and whether the character set is UTF-8, \cw{IUTF8}). Plink on Unix will propagate appropriate modes from the local terminal, if any. } \b If \q{Nothing} is selected, no value for the mode will be specified to the server under any circumstances. \b If a value is specified, it will be sent to the server under all circumstances. The precise syntax of the value box depends on the mode. By default, all of the available modes are listed as \q{Auto}, which should do the right thing in most circumstances. The precise effect of each setting, if any, is up to the server. Their names come from \i{POSIX} and other Unix systems, and they are most likely to have a useful effect on such systems. (These are the same settings that can usually be changed using the \i\c{stty} command once logged in to such servers.) Some notable modes are described below; for fuller explanations, see your server documentation. \b \I{ERASE special character}\cw{ERASE} is the character that when typed by the user will delete one space to the left. When set to \q{Auto} (the default setting), this follows the setting of the local Backspace key in PuTTY (see \k{config-backspace}). \lcont{ This and other \i{special character}s are specified using \c{^C} notation for Ctrl-C, and so on. Use \c{^<27>} or \c{^<0x1B>} to specify a character numerically, and \c{^~} to get a literal \c{^}. Other non-control characters are denoted by themselves. Leaving the box entirely blank indicates that \e{no} character should be assigned to the specified function, although this may not be supported by all servers. } \b \I{QUIT special character}\cw{QUIT} is a special character that usually forcefully ends the current process on the server (\cw{SIGQUIT}). On many servers its default setting is Ctrl-backslash (\c{^\\}), which is easy to accidentally invoke on many keyboards. If this is getting in your way, you may want to change it to another character or turn it off entirely. \b Boolean modes such as \cw{ECHO} and \cw{ICANON} can be specified in PuTTY in a variety of ways, such as \cw{true}/\cw{false}, \cw{yes}/\cw{no}, and \cw{0}/\cw{1}. (Explicitly specifying a value of \cw{no} is different from not sending the mode at all.) \b The boolean mode \I{IUTF8 terminal mode}\cw{IUTF8} signals to the server whether the terminal character set is \i{UTF-8} or not, for purposes such as basic line editing; if this is set incorrectly, the backspace key may erase the wrong amount of text, for instance. However, simply setting this is not usually sufficient for the server to use UTF-8; POSIX servers will generally also require the locale to be set (by some server-dependent means), although many newer installations default to UTF-8. Also, since this mode was added to the SSH protocol much later than the others, \#{circa 2016} many servers (particularly older servers) do not honour this mode sent over SSH; indeed, a few poorly-written servers object to its mere presence, so you may find you need to set it to not be sent at all. When set to \q{Auto}, this follows the local configured character set (see \k{config-charset}). \b Terminal speeds are configured elsewhere; see \k{config-termspeed}. \H{config-ssh-x11} The X11 panel The X11 panel allows you to configure \i{forwarding of X11} over an SSH connection. If your server lets you run X Window System \i{graphical applications}, X11 forwarding allows you to securely give those applications access to a local X display on your PC. To enable X11 forwarding, check the \q{Enable X11 forwarding} box. If your X display is somewhere unusual, you will need to enter its location in the \q{X display location} box; if this is left blank, PuTTY will try to find a sensible default in the environment, or use the primary local display (\c{:0}) if that fails. See \k{using-x-forwarding} for more information about X11 forwarding. \S{config-ssh-x11auth} Remote \i{X11 authentication} If you are using X11 forwarding, the virtual X server created on the SSH server machine will be protected by authorisation data. This data is invented, and checked, by PuTTY. The usual authorisation method used for this is called \i\cw{MIT-MAGIC-COOKIE-1}. This is a simple password-style protocol: the X client sends some cookie data to the server, and the server checks that it matches the real cookie. The cookie data is sent over an unencrypted X11 connection; so if you allow a client on a third machine to access the virtual X server, then the cookie will be sent in the clear. PuTTY offers the alternative protocol \i\cw{XDM-AUTHORIZATION-1}. This is a cryptographically authenticated protocol: the data sent by the X client is different every time, and it depends on the IP address and port of the client's end of the connection and is also stamped with the current time. So an eavesdropper who captures an \cw{XDM-AUTHORIZATION-1} string cannot immediately re-use it for their own X connection. PuTTY's support for \cw{XDM-AUTHORIZATION-1} is a somewhat experimental feature, and may encounter several problems: \b Some X clients probably do not even support \cw{XDM-AUTHORIZATION-1}, so they will not know what to do with the data PuTTY has provided. \b This authentication mechanism will only work in SSH-2. In SSH-1, the SSH server does not tell the client the source address of a forwarded connection in a machine-readable format, so it's impossible to verify the \cw{XDM-AUTHORIZATION-1} data. \b You may find this feature causes problems with some SSH servers, which will not clean up \cw{XDM-AUTHORIZATION-1} data after a session, so that if you then connect to the same server using a client which only does \cw{MIT-MAGIC-COOKIE-1} and are allocated the same remote display number, you might find that out-of-date authentication data is still present on your server and your X connections fail. PuTTY's default is \cw{MIT-MAGIC-COOKIE-1}. If you change it, you should be sure you know what you're doing. \S{config-ssh-xauthority} X authority file for local display If you are using X11 forwarding, the local X server to which your forwarded connections are eventually directed may itself require authorisation. Some Windows X servers do not require this: they do authorisation by simpler means, such as accepting any connection from the local machine but not from anywhere else. However, if your X server does require authorisation, then PuTTY needs to know what authorisation is required. One way in which this data might be made available is for the X server to store it somewhere in a file which has the same format as the Unix \c{.Xauthority} file. If this is how your Windows X server works, then you can tell PuTTY where to find this file by configuring this option. By default, PuTTY will not attempt to find any authorisation for your local display. \H{config-ssh-portfwd} \I{port forwarding}The Tunnels panel The Tunnels panel allows you to configure tunnelling of arbitrary connection types through an SSH connection. Port forwarding allows you to tunnel other types of \i{network connection} down an SSH session. See \k{using-port-forwarding} for a general discussion of port forwarding and how it works. The port forwarding section in the Tunnels panel shows a list of all the port forwardings that PuTTY will try to set up when it connects to the server. By default no port forwardings are set up, so this list is empty. To add a port forwarding: \b Set one of the \q{Local} or \q{Remote} radio buttons, depending on whether you want to \I{local port forwarding}forward a local port to a remote destination (\q{Local}) or \I{remote port forwarding}forward a remote port to a local destination (\q{Remote}). Alternatively, select \q{Dynamic} if you want PuTTY to \I{dynamic port forwarding}provide a local SOCKS 4/4A/5 proxy on a local port (note that this proxy only supports TCP connections; the SSH protocol does not support forwarding \i{UDP}). \b Enter a source \i{port number} into the \q{Source port} box. For local forwardings, PuTTY will listen on this port of your PC. For remote forwardings, your SSH server will listen on this port of the remote machine. Note that most servers will not allow you to listen on \I{privileged port}port numbers less than 1024. \b If you have selected \q{Local} or \q{Remote} (this step is not needed with \q{Dynamic}), enter a hostname and port number separated by a colon, in the \q{Destination} box. Connections received on the source port will be directed to this destination. For example, to connect to a POP-3 server, you might enter \c{popserver.example.com:110}. (If you need to enter a literal \i{IPv6 address}, enclose it in square brackets, for instance \cq{[::1]:2200}.) \b Click the \q{Add} button. Your forwarding details should appear in the list box. To remove a port forwarding, simply select its details in the list box, and click the \q{Remove} button. In the \q{Source port} box, you can also optionally enter an \I{listen address}IP address to listen on, by specifying (for instance) \c{127.0.0.5:79}. See \k{using-port-forwarding} for more information on how this works and its restrictions. In place of port numbers, you can enter \i{service names}, if they are known to the local system. For instance, in the \q{Destination} box, you could enter \c{popserver.example.com:pop3}. You can \I{port forwarding, changing mid-session}modify the currently active set of port forwardings in mid-session using \q{Change Settings} (see \k{using-changesettings}). If you delete a local or dynamic port forwarding in mid-session, PuTTY will stop listening for connections on that port, so it can be re-used by another program. If you delete a remote port forwarding, note that: \b The SSH-1 protocol contains no mechanism for asking the server to stop listening on a remote port. \b The SSH-2 protocol does contain such a mechanism, but not all SSH servers support it. (In particular, \i{OpenSSH} does not support it in any version earlier than 3.9.) If you ask to delete a remote port forwarding and PuTTY cannot make the server actually stop listening on the port, it will instead just start refusing incoming connections on that port. Therefore, although the port cannot be reused by another program, you can at least be reasonably sure that server-side programs can no longer access the service at your end of the port forwarding. If you delete a forwarding, any existing connections established using that forwarding remain open. Similarly, changes to global settings such as \q{Local ports accept connections from other hosts} only take effect on new forwardings. If the connection you are forwarding over SSH is itself a second SSH connection made by another copy of PuTTY, you might find the \q{logical host name} configuration option useful to warn PuTTY of which host key it should be expecting. See \k{config-loghost} for details of this. \S{config-ssh-portfwd-localhost} Controlling the visibility of forwarded ports The source port for a forwarded connection usually does not accept connections from any machine except the \I{localhost}SSH client or server machine itself (for local and remote forwardings respectively). There are controls in the Tunnels panel to change this: \b The \q{Local ports accept connections from other hosts} option allows you to set up local-to-remote port forwardings in such a way that machines other than your client PC can connect to the forwarded port. (This also applies to dynamic SOCKS forwarding.) \b The \q{Remote ports do the same} option does the same thing for remote-to-local port forwardings (so that machines other than the SSH server machine can connect to the forwarded port.) Note that this feature is only available in the SSH-2 protocol, and not all SSH-2 servers support it (\i{OpenSSH} 3.0 does not, for example). \S{config-ssh-portfwd-address-family} Selecting \i{Internet protocol version} for forwarded ports This switch allows you to select a specific Internet protocol (\i{IPv4} or \i{IPv6}) for the local end of a forwarded port. By default, it is set on \q{Auto}, which means that: \b for a local-to-remote port forwarding, PuTTY will listen for incoming connections in both IPv4 and (if available) IPv6 \b for a remote-to-local port forwarding, PuTTY will choose a sensible protocol for the outgoing connection. This overrides the general Internet protocol version preference on the Connection panel (see \k{config-address-family}). Note that some operating systems may listen for incoming connections in IPv4 even if you specifically asked for IPv6, because their IPv4 and IPv6 protocol stacks are linked together. Apparently \i{Linux} does this, and Windows does not. So if you're running PuTTY on Windows and you tick \q{IPv6} for a local or dynamic port forwarding, it will \e{only} be usable by connecting to it using IPv6; whereas if you do the same on Linux, you can also use it with IPv4. However, ticking \q{Auto} should always give you a port which you can connect to using either protocol. \H{config-ssh-bugs} \I{SSH server bugs}The Bugs and More Bugs panels Not all SSH servers work properly. Various existing servers have bugs in them, which can make it impossible for a client to talk to them unless it knows about the bug and works around it. Since most servers announce their software version number at the beginning of the SSH connection, PuTTY will attempt to detect which bugs it can expect to see in the server and automatically enable workarounds. However, sometimes it will make mistakes; if the server has been deliberately configured to conceal its version number, or if the server is a version which PuTTY's bug database does not know about, then PuTTY will not know what bugs to expect. The Bugs and More Bugs panels (there are two because we have so many bug compatibility modes) allow you to manually configure the bugs PuTTY expects to see in the server. Each bug can be configured in three states: \b \q{Off}: PuTTY will assume the server does not have the bug. \b \q{On}: PuTTY will assume the server \e{does} have the bug. \b \q{Auto}: PuTTY will use the server's version number announcement to try to guess whether or not the server has the bug. \S{config-ssh-bug-ignore2} \q{Chokes on SSH-2 \i{ignore message}s} An ignore message (SSH_MSG_IGNORE) is a message in the SSH protocol which can be sent from the client to the server, or from the server to the client, at any time. Either side is required to ignore the message whenever it receives it. PuTTY uses ignore messages in SSH-2 to confuse the encrypted data stream and make it harder to cryptanalyse. It also uses ignore messages for connection \i{keepalives} (see \k{config-keepalive}). If it believes the server to have this bug, PuTTY will stop using ignore messages. If this bug is enabled when talking to a correct server, the session will succeed, but keepalives will not work and the session might be less cryptographically secure than it could be. \S{config-ssh-bug-rekey} \q{Handles SSH-2 key re-exchange badly} Some SSH servers cannot cope with \i{repeat key exchange} at all, and will ignore attempts by the client to start one. Since PuTTY pauses the session while performing a repeat key exchange, the effect of this would be to cause the session to hang after an hour (unless you have your rekey timeout set differently; see \k{config-ssh-kex-rekey} for more about rekeys). Other, very old, SSH servers handle repeat key exchange even more badly, and disconnect upon receiving a repeat key exchange request. If this bug is detected, PuTTY will never initiate a repeat key exchange. If this bug is enabled when talking to a correct server, the session should still function, but may be less secure than you would expect. This is an SSH-2-specific bug. \S{config-ssh-bug-winadj} \q{Chokes on PuTTY's SSH-2 \cq{winadj} requests} 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. \S{config-ssh-bug-chanreq} \q{Replies to requests on closed channels} The SSH protocol as published in RFC 4254 has an ambiguity which arises if one side of a connection tries to close a channel, while the other side simultaneously sends a request within the channel and asks for a reply. RFC 4254 leaves it unclear whether the closing side should reply to the channel request after having announced its intention to close the channel. Discussion on the \cw{ietf-ssh} mailing list in April 2014 formed a clear consensus that the right answer is no. However, because of the ambiguity in the specification, some SSH servers have implemented the other policy; for example, \W{https://bugzilla.mindrot.org/show_bug.cgi?id=1818}{OpenSSH used to} until it was fixed. Because PuTTY sends channel requests with the \q{want reply} flag throughout channels' lifetime (see \k{config-ssh-bug-winadj}), it's possible that when connecting to such a server it might receive a reply to a request after it thinks the channel has entirely closed, and terminate with an error along the lines of \q{Received \cw{SSH2_MSG_CHANNEL_FAILURE} for nonexistent channel 256}. \S{config-ssh-bug-maxpkt2} \q{Ignores SSH-2 \i{maximum packet size}} When an SSH-2 channel is set up, each end announces the maximum size of data packet that it is willing to receive for that channel. Some servers ignore PuTTY's announcement and send packets larger than PuTTY is willing to accept, causing it to report \q{Incoming packet was garbled on decryption}. If this bug is detected, PuTTY never allows the channel's \i{flow-control window} to grow large enough to allow the server to send an over-sized packet. If this bug is enabled when talking to a correct server, the session will work correctly, but download performance will be less than it could be. \S{config-ssh-bug-sig} \q{Requires padding on SSH-2 \i{RSA} \i{signatures}} Versions below 3.3 of \i{OpenSSH} require SSH-2 RSA signatures to be padded with zero bytes to the same length as the RSA key modulus. The SSH-2 specification says that an unpadded signature MUST be accepted, so this is a bug. A typical symptom of this problem is that PuTTY mysteriously fails RSA authentication once in every few hundred attempts, and falls back to passwords. If this bug is detected, PuTTY will pad its signatures in the way OpenSSH expects. If this bug is enabled when talking to a correct server, it is likely that no damage will be done, since correct servers usually still accept padded signatures because they're used to talking to OpenSSH. This is an SSH-2-specific bug. \S{config-ssh-bug-oldgex2} \q{Only supports pre-RFC4419 SSH-2 DH GEX} The SSH key exchange method that uses Diffie-Hellman group exchange was redesigned after its original release, to use a slightly more sophisticated setup message. Almost all SSH implementations switched over to the new version. (PuTTY was one of the last.) A few old servers still only support the old one. If this bug is detected, and the client and server negotiate Diffie-Hellman group exchange, then PuTTY will send the old message now known as \cw{SSH2_MSG_KEX_DH_GEX_REQUEST_OLD} in place of the new \cw{SSH2_MSG_KEX_DH_GEX_REQUEST}. This is an SSH-2-specific bug. \S{config-ssh-bug-hmac2} \q{Miscomputes SSH-2 HMAC keys} Versions 2.3.0 and below of the SSH server software from \cw{ssh.com} compute the keys for their \i{HMAC} \i{message authentication code}s incorrectly. A typical symptom of this problem is that PuTTY dies unexpectedly at the beginning of the session, saying \q{Incorrect MAC received on packet}. If this bug is detected, PuTTY will compute its HMAC keys in the same way as the buggy server, so that communication will still be possible. If this bug is enabled when talking to a correct server, communication will fail. This is an SSH-2-specific bug. \S{config-ssh-bug-pksessid2} \q{Misuses the \i{session ID} in SSH-2 PK auth} Versions below 2.3 of \i{OpenSSH} require SSH-2 \i{public-key authentication} to be done slightly differently: the data to be signed by the client contains the session ID formatted in a different way. If public-key authentication mysteriously does not work but the Event Log (see \k{using-eventlog}) thinks it has successfully sent a signature, it might be worth enabling the workaround for this bug to see if it helps. If this bug is detected, PuTTY will sign data in the way OpenSSH expects. If this bug is enabled when talking to a correct server, SSH-2 public-key authentication will fail. This is an SSH-2-specific bug. \S{config-ssh-bug-derivekey2} \q{Miscomputes SSH-2 \i{encryption} keys} Versions below 2.0.11 of the SSH server software from \i\cw{ssh.com} compute the keys for the session encryption incorrectly. This problem can cause various error messages, such as \q{Incoming packet was garbled on decryption}, or possibly even \q{Out of memory}. If this bug is detected, PuTTY will compute its encryption keys in the same way as the buggy server, so that communication will still be possible. If this bug is enabled when talking to a correct server, communication will fail. This is an SSH-2-specific bug. \S{config-ssh-bug-ignore1} \q{Chokes on SSH-1 \i{ignore message}s} An ignore message (SSH_MSG_IGNORE) is a message in the SSH protocol which can be sent from the client to the server, or from the server to the client, at any time. Either side is required to ignore the message whenever it receives it. PuTTY uses ignore messages to \I{password camouflage}hide the password packet in SSH-1, so that a listener cannot tell the length of the user's password; it also uses ignore messages for connection \i{keepalives} (see \k{config-keepalive}). If this bug is detected, PuTTY will stop using ignore messages. This means that keepalives will stop working, and PuTTY will have to fall back to a secondary defence against SSH-1 password-length eavesdropping. See \k{config-ssh-bug-plainpw1}. If this bug is enabled when talking to a correct server, the session will succeed, but keepalives will not work and the session might be more vulnerable to eavesdroppers than it could be. \S{config-ssh-bug-plainpw1} \q{Refuses all SSH-1 \i{password camouflage}} When talking to an SSH-1 server which cannot deal with ignore messages (see \k{config-ssh-bug-ignore1}), PuTTY will attempt to disguise the length of the user's password by sending additional padding \e{within} the password packet. This is technically a violation of the SSH-1 specification, and so PuTTY will only do it when it cannot use standards-compliant ignore messages as camouflage. In this sense, for a server to refuse to accept a padded password packet is not really a bug, but it does make life inconvenient if the server can also not handle ignore messages. If this \q{bug} is detected, PuTTY will assume that neither ignore messages nor padding are acceptable, and that it thus has no choice but to send the user's password with no form of camouflage, so that an eavesdropping user will be easily able to find out the exact length of the password. If this bug is enabled when talking to a correct server, the session will succeed, but will be more vulnerable to eavesdroppers than it could be. This is an SSH-1-specific bug. SSH-2 is secure against this type of attack. \S{config-ssh-bug-rsa1} \q{Chokes on SSH-1 \i{RSA} authentication} Some SSH-1 servers cannot deal with RSA authentication messages at all. If \i{Pageant} is running and contains any SSH-1 keys, PuTTY will normally automatically try RSA authentication before falling back to passwords, so these servers will crash when they see the RSA attempt. If this bug is detected, PuTTY will go straight to password authentication. If this bug is enabled when talking to a correct server, the session will succeed, but of course RSA authentication will be impossible. This is an SSH-1-specific bug. \H{config-psusan} The \q{Bare \cw{\i{ssh-connection}}} protocol In addition to SSH itself, PuTTY also supports a second protocol that is derived from SSH. It's listed in the PuTTY GUI under the name \q{Bare \cw{ssh-connection}}. This protocol consists of just the innermost of SSH-2's three layers: it leaves out the cryptography layer providing network security, and it leaves out the authentication layer where you provide a username and prove you're allowed to log in as that user. It is therefore \s{completely unsuited to any network connection}. Don't try to use it over a network! The purpose of this protocol is for various specialist circumstances in which the \q{connection} is not over a real network, but is a pipe or IPC channel between different processes running on the \e{same} computer. In these contexts, the operating system will already have guaranteed that each of the two communicating processes is owned by the expected user (so that no authentication is necessary), and that the communications channel cannot be tapped by a hostile user on the same machine (so that no cryptography is necessary either). Examples of possible uses involve communicating with a strongly separated context such as the inside of a container, or a VM, or a different network namespace. Explicit support for this protocol is new in PuTTY 0.75. As of 2021-04, the only known server for the bare \cw{ssh-connection} protocol is the Unix program \cq{\i{psusan}} that is also part of the PuTTY tool suite. (However, this protocol is also the same one used between instances of PuTTY to implement connection sharing: see \k{config-ssh-sharing}. In fact, in the Unix version of PuTTY, when a sharing upstream records \q{Sharing this connection at [pathname]} in the Event Log, it's possible to connect another instance of PuTTY directly to that Unix socket, by entering its pathname in the host name box and selecting \q{Bare \cw{ssh-connection}} as the protocol!) Many of the options under the SSH panel also affect this protocol, although options to do with cryptography and authentication do not, for obvious reasons. I repeat, \s{DON'T TRY TO USE THIS PROTOCOL FOR NETWORK CONNECTIONS!} That's not what it's for, and it's not at all safe to do it. \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 The \q{Serial line to connect to} box allows you to choose which serial line you want PuTTY to talk to, if your computer has more than one serial port. On Windows, the first serial line is called \i\cw{COM1}, and if there is a second it is called \cw{COM2}, and so on. This configuration setting is also visible on the Session panel, where it replaces the \q{Host Name} box (see \k{config-hostname}) if the connection type is set to \q{Serial}. \S{config-serial-speed} Selecting the speed of your serial line The \q{Speed} box allows you to choose the speed (or \q{baud rate}) at which to talk to the serial line. Typical values might be 9600, 19200, 38400 or 57600. Which one you need will depend on the device at the other end of the serial cable; consult the manual for that device if you are in doubt. This configuration setting is also visible on the Session panel, where it replaces the \q{Port} box (see \k{config-hostname}) if the connection type is set to \q{Serial}. \S{config-serial-databits} Selecting the number of data bits The \q{Data bits} box allows you to choose how many data bits are transmitted in each byte sent or received through the serial line. Typical values are 7 or 8. \S{config-serial-stopbits} Selecting the number of stop bits The \q{Stop bits} box allows you to choose how many stop bits are used in the serial line protocol. Typical values are 1, 1.5 or 2. \S{config-serial-parity} Selecting the serial parity checking scheme The \q{Parity} box allows you to choose what type of parity checking is used on the serial line. The settings are: \b \q{None}: no parity bit is sent at all. \b \q{Odd}: an extra parity bit is sent alongside each byte, and arranged so that the total number of 1 bits is odd. \b \q{Even}: an extra parity bit is sent alongside each byte, and arranged so that the total number of 1 bits is even. \b \q{Mark}: an extra parity bit is sent alongside each byte, and always set to 1. \b \q{Space}: an extra parity bit is sent alongside each byte, and always set to 0. \S{config-serial-flow} Selecting the serial flow control scheme The \q{Flow control} box allows you to choose what type of flow control checking is used on the serial line. The settings are: \b \q{None}: no flow control is done. Data may be lost if either side attempts to send faster than the serial line permits. \b \q{XON/XOFF}: flow control is done by sending XON and XOFF characters within the data stream. \b \q{RTS/CTS}: flow control is done using the RTS and CTS wires on the serial line. \b \q{DSR/DTR}: flow control is done using the DSR and DTR wires on the serial line. \H{config-telnet} The \i{Telnet} panel The Telnet panel allows you to configure options that only apply to Telnet sessions. \S{config-oldenviron} \q{Handling of OLD_ENVIRON ambiguity} The original Telnet mechanism for passing \i{environment variables} was badly specified. At the time the standard (RFC 1408) was written, BSD telnet implementations were already supporting the feature, and the intention of the standard was to describe the behaviour the BSD implementations were already using. Sadly there was a typing error in the standard when it was issued, and two vital function codes were specified the wrong way round. BSD implementations did not change, and the standard was not corrected. Therefore, it's possible you might find either \i{BSD} or \i{RFC}-compliant implementations out there. This switch allows you to choose which one PuTTY claims to be. The problem was solved by issuing a second standard, defining a new Telnet mechanism called \i\cw{NEW_ENVIRON}, which behaved exactly like the original \i\cw{OLD_ENVIRON} but was not encumbered by existing implementations. Most Telnet servers now support this, and it's unambiguous. This feature should only be needed if you have trouble passing environment variables to quite an old server. \S{config-ptelnet} Passive and active \i{Telnet negotiation} modes In a Telnet connection, there are two types of data passed between the client and the server: actual text, and \e{negotiations} about which Telnet extra features to use. PuTTY can use two different strategies for negotiation: \b In \I{active Telnet negotiation}\e{active} mode, PuTTY starts to send negotiations as soon as the connection is opened. \b In \I{passive Telnet negotiation}\e{passive} mode, PuTTY will wait to negotiate until it sees a negotiation from the server. The obvious disadvantage of passive mode is that if the server is also operating in a passive mode, then negotiation will never begin at all. For this reason PuTTY defaults to active mode. However, sometimes passive mode is required in order to successfully get through certain types of firewall and \i{Telnet proxy} server. If you have confusing trouble with a \i{firewall}, you could try enabling passive mode to see if it helps. \S{config-telnetkey} \q{Keyboard sends \i{Telnet special commands}} If this box is checked, several key sequences will have their normal actions modified: \b the Backspace key on the keyboard will send the \I{Erase Character, Telnet special command}Telnet special backspace code; \b Control-C will send the Telnet special \I{Interrupt Process, Telnet special command}Interrupt Process code; \b Control-Z will send the Telnet special \I{Suspend Process, Telnet special command}Suspend Process code. You probably shouldn't enable this unless you know what you're doing. \S{config-telnetnl} \q{Return key sends \i{Telnet New Line} instead of ^M} Unlike most other remote login protocols, the Telnet protocol has a special \q{\i{new line}} code that is not the same as the usual line endings of Control-M or Control-J. By default, PuTTY sends the Telnet New Line code when you press Return, instead of sending Control-M as it does in most other protocols. Most Unix-style Telnet servers don't mind whether they receive Telnet New Line or Control-M; some servers do expect New Line, and some servers prefer to see ^M. If you are seeing surprising behaviour when you press Return in a Telnet session, you might try turning this option off to see if it helps. \H{config-rlogin} The Rlogin panel The \i{Rlogin} panel allows you to configure options that only apply to Rlogin sessions. \S{config-rlogin-localuser} \I{local username in Rlogin}\q{Local username} Rlogin allows an automated (password-free) form of login by means of a file called \i\c{.rhosts} on the server. You put a line in your \c{.rhosts} file saying something like \c{jbloggs@pc1.example.com}, and then when you make an Rlogin connection the client transmits the username of the user running the Rlogin client. The server checks the username and hostname against \c{.rhosts}, and if they match it \I{passwordless login}does not ask for a password. This only works because Unix systems contain a safeguard to stop a user from pretending to be another user in an Rlogin connection. Rlogin connections have to come from \I{privileged port}port numbers below 1024, and Unix systems prohibit this to unprivileged processes; so when the server sees a connection from a low-numbered port, it assumes the client end of the connection is held by a privileged (and therefore trusted) process, so it believes the claim of who the user is. Windows does not have this restriction: \e{any} user can initiate an outgoing connection from a low-numbered port. Hence, the Rlogin \c{.rhosts} mechanism is completely useless for securely distinguishing several different users on a Windows machine. If you have a \c{.rhosts} entry pointing at a Windows PC, you should assume that \e{anyone} using that PC can \i{spoof} your username in an Rlogin connection and access your account on the server. The \q{Local username} control allows you to specify what user name PuTTY should claim you have, in case it doesn't match your \i{Windows user name} (or in case you didn't bother to set up a Windows user name). \H{config-supdup} The \i{SUPDUP} panel The SUPDUP panel allows you to configure options that only apply to SUPDUP sessions. See \k{using-supdup} for more about the SUPDUP protocol. \S{supdup-location} \q{Location string} In SUPDUP, the client sends a piece of text of its choice to the server giving the user's location. This is typically displayed in lists of logged-in users. By default, PuTTY just defaults this to "The Internet". If you want your location to show up as something more specific, you can configure it here. \S{supdup-ascii} \q{Extended ASCII Character set} This declares what kind of character set extension your terminal supports. If the server supports it, it will send text using that character set. \q{None} means the standard 95 printable ASCII characters. \q{ITS} means ASCII extended with printable characters in the control character range. This character set is documented in the SUPDUP protocol definition. \q{WAITS} is similar to \q{ITS} but uses some alternative characters in the extended set: most prominently, it will display arrows instead of \c{^} and \c{_}, and \c{\}} instead of \c{~}. \q{ITS} extended ASCII is used by ITS and Lisp machines, whilst \q{WAITS} is only used by the WAITS operating system from the Stanford AI Laboratory. \S{supdup-more} \q{**MORE** processing} When **MORE** processing is enabled, the server causes output to pause at the bottom of the screen, until a space is typed. \S{supdup-scroll} \q{Terminal scrolling} This controls whether the terminal will perform scrolling then the cursor goes below the last line, or if the cursor will return to the first line. \H{config-file} \ii{Storing configuration in a file} PuTTY does not currently support storing its configuration in a file instead of the \i{Registry}. However, you can work around this with a couple of \i{batch file}s. You will need a file called (say) \c{PUTTY.BAT} which imports the contents of a file into the Registry, then runs PuTTY, exports the contents of the Registry back into the file, and deletes the Registry entries. This can all be done using the Regedit command line options, so it's all automatic. Here is what you need in \c{PUTTY.BAT}: \c @ECHO OFF \c regedit /s putty.reg \c regedit /s puttyrnd.reg \c start /w putty.exe \c regedit /ea new.reg HKEY_CURRENT_USER\Software\SimonTatham\PuTTY \c copy new.reg putty.reg \c del new.reg \c regedit /s puttydel.reg This batch file needs two auxiliary files: \c{PUTTYRND.REG} which sets up an initial safe location for the \c{PUTTY.RND} random seed file, and \c{PUTTYDEL.REG} which destroys everything in the Registry once it's been successfully saved back to the file. Here is \c{PUTTYDEL.REG}: \c REGEDIT4 \c \c [-HKEY_CURRENT_USER\Software\SimonTatham\PuTTY] Here is an example \c{PUTTYRND.REG} file: \c REGEDIT4 \c \c [HKEY_CURRENT_USER\Software\SimonTatham\PuTTY] \c "RandSeedFile"="a:\\putty.rnd" You should replace \c{a:\\putty.rnd} with the location where you want to store your random number data. If the aim is to carry around PuTTY and its settings on one USB stick, you probably want to store it on the USB stick. putty-0.76/doc/errors.but0000644000175000017500000004100414072266310012351 00000000000000\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. We do not attempt to list \e{all} error messages here: there are many which should never occur, and some which should be self-explanatory. If you get an error message which is not listed in this chapter and which you don't understand, report it to us as a bug (see \k{feedback}) and we will add documentation for it. \H{errors-hostkey-absent} \q{The server's host key is not cached in the registry} This error message occurs when PuTTY connects to a new SSH server. Every server identifies itself by means of a host key; once PuTTY knows the host key for a server, it will be able to detect if a malicious attacker redirects your connection to another machine. If you see this message, it means that PuTTY has not seen this host key before, and has no way of knowing whether it is correct or not. You should attempt to verify the host key by other means, such as asking the machine's administrator. If you see this message and you know that your installation of PuTTY \e{has} connected to the same server before, it may have been recently upgraded to SSH protocol version 2. SSH protocols 1 and 2 use separate host keys, so when you first use \i{SSH-2} with a server you have only used SSH-1 with before, you will see this message again. You should verify the correctness of the key as before. See \k{gs-hostkey} for more information on host keys. \H{errors-hostkey-wrong} \q{WARNING - POTENTIAL SECURITY BREACH!} This message, followed by \q{The server's host key does not match the one PuTTY has cached in the registry}, means that PuTTY has connected to the SSH server before, knows what its host key \e{should} be, but has found a different one. This may mean that a malicious attacker has replaced your server with a different one, or has redirected your network connection to their own machine. On the other hand, it may simply mean that the administrator of your server has accidentally changed the key while upgrading the SSH software; this \e{shouldn't} happen but it is unfortunately possible. 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-ssh-protocol} \q{SSH protocol version 2 required by our configuration but remote only provides (old, insecure) SSH-1} By default, PuTTY only supports connecting to SSH servers that implement \i{SSH protocol version 2}. If you see this message, the server you're trying to connect to only supports the older SSH-1 protocol. If the server genuinely only supports SSH-1, then you need to either change the \q{SSH protocol version} setting (see \k{config-ssh-prot}), or use the \c{-1} command-line option; in any case, you should not treat the resulting connection as secure. You might start seeing this message with new versions of PuTTY (from 0.68 onwards) where you didn't before, because it used to be possible to configure PuTTY to automatically fall back from SSH-2 to SSH-1. This is no longer supported, to prevent the possibility of a downgrade attack. \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 \i{Blowfish}, \ii{single-DES}, and \i{Arcfour} encryption. See \k{config-ssh-encryption} for more information on this message. (There are similar messages for other cryptographic primitives, such as host key algorithms.) \H{errors-toomanyauth} \q{Remote side sent disconnect message type 2 (protocol error): "Too many authentication failures for root"} This message is produced by an \i{OpenSSH} (or \i{Sun SSH}) server if it receives more failed authentication attempts than it is willing to tolerate. This can easily happen if you are using Pageant and have a large number of keys loaded into it, since these servers count each offer of a public key as an authentication attempt. This can be worked around by specifying the key that's required for the authentication in the PuTTY configuration (see \k{config-ssh-privkey}); PuTTY will ignore any other keys Pageant may have, but will ask Pageant to do the authentication, so that you don't have to type your passphrase. On the server, this can be worked around by disabling public-key authentication or (for Sun SSH only) by increasing \c{MaxAuthTries} in \c{sshd_config}. \H{errors-memory} \q{\ii{Out of memory}} This occurs when PuTTY tries to allocate more memory than the system can give it. This \e{may} happen for genuine reasons: if the computer really has run out of memory, or if you have configured an extremely large number of lines of scrollback in your terminal. PuTTY is not able to recover from running out of memory; it will terminate immediately after giving this error. However, this error can also occur when memory is not running out at all, because PuTTY receives data in the wrong format. In SSH-2 and also in SFTP, the server sends the length of each message before the message itself; so PuTTY will receive the length, try to allocate space for the message, and then receive the rest of the message. If the length PuTTY receives is garbage, it will try to allocate a ridiculous amount of memory, and will terminate with an \q{Out of memory} error. This can happen in SSH-2, if PuTTY and the server have not enabled encryption in the same way (see \k{faq-outofmem} in the FAQ). This can also happen in PSCP or PSFTP, if your \i{login scripts} on the server generate output: the client program will be expecting an SFTP message starting with a length, and if it receives some text from your login scripts instead it will try to interpret them as a message length. See \k{faq-outofmem2} for details of this. \H{errors-internal} \q{\ii{Internal error}}, \q{\ii{Internal fault}}, \q{\ii{Assertion failed}} Any error beginning with the word \q{Internal} should \e{never} occur. If it does, there is a bug in PuTTY by definition; please see \k{feedback} and report it to us. Similarly, any error message starting with \q{Assertion failed} is a bug in PuTTY. Please report it to us, and include the exact text from the error message box. \H{errors-cant-load-key} \q{Unable to use key file}, \q{Couldn't load private key}, \q{Couldn't load this key} Various forms of this error are printed in the PuTTY window, or written to the PuTTY Event Log (see \k{using-eventlog}) when trying public-key authentication, or given by Pageant when trying to load a private key. If you see one of these messages, it often indicates that you've tried to load a key of an inappropriate type into PuTTY, Plink, PSCP, PSFTP, or Pageant. You may have tried to load an SSH-2 key in a \q{foreign} format (OpenSSH or \cw{ssh.com}) directly into one of the PuTTY tools, in which case you need to import it into PuTTY's native format (\c{*.PPK}) using PuTTYgen \dash see \k{puttygen-conversions}. Alternatively, you may have specified a key that's inappropriate for the connection you're making. The SSH-2 and the old SSH-1 protocols require different private key formats, and a SSH-1 key can't be used for a SSH-2 connection (or vice versa). \H{errors-refused} \q{Server refused our key}, \q{Server refused our public key}, \q{Key refused} Various forms of this error are printed in the PuTTY window, or written to the PuTTY Event Log (see \k{using-eventlog}) when trying public-key authentication. If you see one of these messages, it means that PuTTY has sent a public key to the server and offered to authenticate with it, and the server has refused to accept authentication. This usually means that the server is not configured to accept this key to authenticate this user. This is almost certainly not a problem with PuTTY. If you see this type of message, the first thing you should do is check your \e{server} configuration carefully. Common errors include having the wrong permissions or ownership set on the public key or the user's home directory on the server. Also, read the PuTTY Event Log; the server may have sent diagnostic messages explaining exactly what problem it had with your setup. \K{pubkey-gettingready} has some hints on server-side public key setup. \H{errors-access-denied} \q{Access denied}, \q{Authentication refused} Various forms of this error are printed in the PuTTY window, or written to the PuTTY Event Log (see \k{using-eventlog}) during authentication. If you see one of these messages, it means that the server has refused all the forms of authentication PuTTY has tried and it has no further ideas. It may be worth checking the Event Log for diagnostic messages from the server giving more detail. This error can be caused by buggy SSH-1 servers that fail to cope with the various strategies we use for camouflaging passwords in transit. Upgrade your server, or use the workarounds described in \k{config-ssh-bug-ignore1} and possibly \k{config-ssh-bug-plainpw1}. \H{errors-no-auth} \q{No supported authentication methods available} This error indicates that PuTTY has run out of ways to authenticate you to an SSH server. This may be because PuTTY has TIS or keyboard-interactive authentication disabled, in which case see \k{config-ssh-tis} and \k{config-ssh-ki}. \H{errors-crc} \q{Incorrect \i{MAC} received on packet} or \q{Incorrect \i{CRC} received on packet} This error occurs when PuTTY decrypts an SSH packet and its checksum is not correct. This probably means something has gone wrong in the encryption or decryption process. It's difficult to tell from this error message whether the problem is in the client, in the server, or in between. In particular, if the network is corrupting data at the TCP level, it may only be obvious with cryptographic protocols such as SSH, which explicitly check the integrity of the transferred data and complain loudly if the checks fail. Corruption of protocols without integrity protection (such as HTTP) will manifest in more subtle failures (such as misdisplayed text or images in a web browser) which may not be noticed. Occasionally this has been caused by server bugs. An example is the bug described at \k{config-ssh-bug-hmac2}, although you're very unlikely to encounter that one these days. In this context MAC stands for \ii{Message Authentication Code}. It's a cryptographic term, and it has nothing at all to do with Ethernet MAC (Media Access Control) addresses, or with the Apple computer. \H{errors-garbled} \q{Incoming packet was garbled on decryption} This error occurs when PuTTY decrypts an SSH packet and the decrypted data makes no sense. This probably means something has gone wrong in the encryption or decryption process. It's difficult to tell from this error message whether the problem is in the client, in the server, or in between. If you get this error, one thing you could try would be to fiddle with the setting of \q{Miscomputes SSH-2 encryption keys} (see \k{config-ssh-bug-derivekey2}) or \q{Ignores SSH-2 maximum packet size} (see \k{config-ssh-bug-maxpkt2}) on the Bugs panel. \H{errors-x11-proxy} \q{PuTTY X11 proxy: \e{various errors}} This family of errors are reported when PuTTY is doing X forwarding. They are sent back to the X application running on the SSH server, which will usually report the error to the user. When PuTTY enables X forwarding (see \k{using-x-forwarding}) it creates a virtual X display running on the SSH server. This display requires authentication to connect to it (this is how PuTTY prevents other users on your server machine from connecting through the PuTTY proxy to your real X display). PuTTY also sends the server the details it needs to enable clients to connect, and the server should put this mechanism in place automatically, so your X applications should just work. A common reason why people see one of these messages is because they used SSH to log in as one user (let's say \q{fred}), and then used the Unix \c{su} command to become another user (typically \q{root}). The original user, \q{fred}, has access to the X authentication data provided by the SSH server, and can run X applications which are forwarded over the SSH connection. However, the second user (\q{root}) does not automatically have the authentication data passed on to it, so attempting to run an X application as that user often fails with this error. If this happens, \e{it is not a problem with PuTTY}. You need to arrange for your X authentication data to be passed from the user you logged in as to the user you used \c{su} to become. How you do this depends on your particular system; in fact many modern versions of \c{su} do it automatically. \H{errors-connaborted} \q{Network error: Software caused connection abort} This is a generic error produced by the Windows network code when it kills an established connection for some reason. For example, it might happen if you pull the network cable out of the back of an Ethernet-connected computer, or if Windows has any other similar reason to believe the entire network has become unreachable. Windows also generates this error if it has given up on the machine at the other end of the connection ever responding to it. If the network between your client and server goes down and your client then tries to send some data, Windows will make several attempts to send the data and will then give up and kill the connection. In particular, this can occur even if you didn't type anything, if you are using SSH-2 and PuTTY attempts a key re-exchange. (See \k{config-ssh-kex-rekey} for more about key re-exchange.) (It can also occur if you are using keepalives in your connection. Other people have reported that keepalives \e{fix} this error for them. See \k{config-keepalive} for a discussion of the pros and cons of keepalives.) We are not aware of any reason why this error might occur that would represent a bug in PuTTY. The problem is between you, your Windows system, your network and the remote system. \H{errors-connreset} \q{Network error: Connection reset by peer} This error occurs when the machines at each end of a network connection lose track of the state of the connection between them. For example, you might see it if your SSH server crashes, and manages to reboot fully before you next attempt to send data to it. However, the most common reason to see this message is if you are connecting through a \i{firewall} or a \i{NAT router} which has timed the connection out. See \k{faq-idleout} in the FAQ for more details. You may be able to improve the situation by using keepalives; see \k{config-keepalive} for details on this. Note that Windows can produce this error in some circumstances without seeing a connection reset from the server, for instance if the connection to the network is lost. \H{errors-connrefused} \q{Network error: Connection refused} This error means that the network connection PuTTY tried to make to your server was rejected by the server. Usually this happens because the server does not provide the service which PuTTY is trying to access. Check that you are connecting with the correct protocol (SSH, Telnet, etc), and check that the port number is correct. If that fails, consult the administrator of your server. \H{errors-conntimedout} \q{Network error: Connection timed out} This error means that the network connection PuTTY tried to make to your server received no response at all from the server. Usually this happens because the server machine is completely isolated from the network, or because it is turned off. Check that you have correctly entered the host name or IP address of your server machine. If that fails, consult the administrator of your server. \i{Unix} also generates this error when it tries to send data down a connection and contact with the server has been completely lost during a connection. (There is a delay of minutes before Unix gives up on receiving a reply from the server.) This can occur if you type things into PuTTY while the network is down, but it can also occur if PuTTY decides of its own accord to send data: due to a repeat key exchange in SSH-2 (see \k{config-ssh-kex-rekey}) or due to keepalives (\k{config-keepalive}). \H{errors-cannotassignaddress} \q{Network error: Cannot assign requested address} This means that the operating system rejected the parameters of the network connection PuTTY tried to make, usually without actually trying to connect to anything, because they were simply invalid. A common way to provoke this error is to accidentally try to connect to port 0, which is not a valid port number. putty-0.76/doc/faq.but0000644000175000017500000021754714072266310011625 00000000000000\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 \S{faq-what}{Question} What is PuTTY? PuTTY is a client program for the SSH, Telnet, Rlogin, and SUPDUP network protocols. These protocols are all used to run a remote session on a computer, over a network. PuTTY implements the client end of that session: the end at which the session is displayed, rather than the end at which it runs. In really simple terms: you run PuTTY on a Windows machine, and tell it to connect to (for example) a Unix machine. PuTTY opens a window. Then, anything you type into that window is sent straight to the Unix machine, and everything the Unix machine sends back is displayed in the window. So you can work on the Unix machine as if you were sitting at its console, while actually sitting somewhere else. \H{faq-support} Features supported in PuTTY \I{supported features}In general, if you want to know if PuTTY supports a particular feature, you should look for it on the \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/}{PuTTY web site}. In particular: \b try the \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/changes.html}{changes page}, and see if you can find the feature on there. If a feature is listed there, it's been implemented. If it's listed as a change made \e{since} the latest version, it should be available in the development snapshots, in which case testing will be very welcome. \b try the \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/}{Wishlist page}, and see if you can find the feature there. If it's on there, and not in the \q{Recently fixed} section, it probably \e{hasn't} been implemented. \S{faq-ssh2}{Question} Does PuTTY support SSH-2? Yes. SSH-2 support has been available in PuTTY since version 0.50 in 2000. Public key authentication (both RSA and DSA) in SSH-2 was new in version 0.52 in 2002. \S{faq-ssh2-keyfmt}{Question} Does PuTTY support reading OpenSSH or \cw{ssh.com} SSH-2 private key files? PuTTY doesn't support this natively (see \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/key-formats-natively.html}{the wishlist entry} for reasons why not), but as of 0.53 PuTTYgen can convert both OpenSSH and \cw{ssh.com} private key files into PuTTY's format. \S{faq-ssh1}{Question} Does PuTTY support SSH-1? Yes. SSH-1 support has always been available in PuTTY. However, the SSH-1 protocol has many weaknesses and is no longer considered secure; you should use SSH-2 instead if at all possible. As of 0.68, PuTTY will no longer fall back to SSH-1 if the server doesn't appear to support SSH-2; you must explicitly ask for SSH-1. \S{faq-localecho}{Question} Does PuTTY support \i{local echo}? Yes. Version 0.52 has proper support for local echo. In version 0.51 and before, local echo could not be separated from local line editing (where you type a line of text locally, and it is not sent to the server until you press Return, so you have the chance to edit it and correct mistakes \e{before} the server sees it). New in version 0.52, local echo and local line editing are separate options, and by default PuTTY will try to determine automatically whether to enable them or not, based on which protocol you have selected and also based on hints from the server. If you have a problem with PuTTY's default choice, you can force each option to be enabled or disabled as you choose. The controls are in the Terminal panel, in the section marked \q{Line discipline options}. \S{faq-savedsettings}{Question} Does PuTTY support storing settings, so I don't have to change them every time? Yes, all of PuTTY's settings can be saved in named session profiles. You can also change the default settings that are used for new sessions. See \k{config-saving} in the documentation for how to do this. \S{faq-disksettings}{Question} Does PuTTY support storing its settings in a disk file? Not at present, although \k{config-file} in the documentation gives a method of achieving the same effect. \S{faq-fullscreen}{Question} Does PuTTY support full-screen mode, like a DOS box? Yes; this was added in version 0.52, in 2002. \S{faq-password-remember}{Question} Does PuTTY have the ability to \i{remember my password} so I don't have to type it every time? No, it doesn't. Remembering your password is a bad plan for obvious security reasons: anyone who gains access to your machine while you're away from your desk can find out the remembered password, and use it, abuse it or change it. In addition, it's not even \e{possible} for PuTTY to automatically send your password in a Telnet session, because Telnet doesn't give the client software any indication of which part of the login process is the password prompt. PuTTY would have to guess, by looking for words like \q{password} in the session data; and if your login program is written in something other than English, this won't work. In SSH, remembering your password would be possible in theory, but there doesn't seem to be much point since SSH supports public key authentication, which is more flexible and more secure. See \k{pubkey} in the documentation for a full discussion of public key authentication. \S{faq-hostkeys}{Question} Is there an option to turn off the \I{verifying the host key}annoying host key prompts? No, there isn't. And there won't be. Even if you write it yourself and send us the patch, we won't accept it. Those annoying host key prompts are the \e{whole point} of SSH. Without them, all the cryptographic technology SSH uses to secure your session is doing nothing more than making an attacker's job slightly harder; instead of sitting between you and the server with a packet sniffer, the attacker must actually subvert a router and start modifying the packets going back and forth. But that's not all that much harder than just sniffing; and without host key checking, it will go completely undetected by client or server. Host key checking is your guarantee that the encryption you put on your data at the client end is the \e{same} encryption taken off the data at the server end; it's your guarantee that it hasn't been removed and replaced somewhere on the way. Host key checking makes the attacker's job \e{astronomically} hard, compared to packet sniffing, and even compared to subverting a router. Instead of applying a little intelligence and keeping an eye on Bugtraq, the attacker must now perform a brute-force attack against at least one military-strength cipher. That insignificant host key prompt really does make \e{that} much difference. If you're having a specific problem with host key checking - perhaps you want an automated batch job to make use of PSCP or Plink, and the interactive host key prompt is hanging the batch process - then the right way to fix it is to add the correct host key to the Registry in advance, or if the Registry is not available, to use the \cw{-hostkey} command-line option. That way, you retain the \e{important} feature of host key checking: the right key will be accepted and the wrong ones will not. Adding an option to turn host key checking off completely is the wrong solution and we will not do it. If you have host keys available in the common \i\c{known_hosts} format, we have a script called \W{https://git.tartarus.org/?p=simon/putty.git;a=blob;f=contrib/kh2reg.py;hb=HEAD}\c{kh2reg.py} to convert them to a Windows .REG file, which can be installed ahead of time by double-clicking or using \c{REGEDIT}. \S{faq-server}{Question} Will you write an SSH server for the PuTTY suite, to go with the client? Not one that you'd want to use. While much of the protocol and networking code can be made common between a client and server, to make a \e{useful} general-purpose server requires all sorts of fiddly new code like interacting with OS authentication databases and the like. A special-purpose SSH server (called \i{Uppity}) can now be built from the PuTTY source code, and indeed it is not usable as a general-purpose server; it exists mainly as a test harness. If someone else wants to use this as a basis for writing a general-purpose SSH server, they'd be perfectly welcome to of course; but we don't have time, and we don't have motivation. The code is available if anyone else wants to try it. \S{faq-pscp-ascii}{Question} Can PSCP or PSFTP transfer files in \i{ASCII} mode? Unfortunately not. This was a limitation of the file transfer protocols as originally specified: the SCP and SFTP protocols had no notion of transferring a file in anything other than binary mode. (This is still true of SCP.) The current draft protocol spec of SFTP proposes a means of implementing ASCII transfer. At some point PSCP/PSFTP may implement this proposal. \H{faq-ports} Ports to other operating systems The eventual goal is for PuTTY to be a multi-platform program, able to run on at least Windows, Mac OS and Unix. PuTTY has been gaining a generalised porting layer, drawing a clear line between platform-dependent and platform-independent code. The general intention was for this porting layer to evolve naturally as part of the process of doing the first port; a Unix port has now been released and the plan seems to be working so far. \S{faq-ports-general}{Question} What ports of PuTTY exist? Currently, release versions of PuTTY tools only run on Windows systems and Unix. As of 0.68, the supplied PuTTY executables run on versions of Windows from XP onwards, up to and including Windows 10; and we know of no reason why PuTTY should not continue to work on future versions of Windows. We provide 32-bit and 64-bit Windows executables for the common x86 processor family; see \k{faq-32bit-64bit} for discussion of the compatibility issues around that. The 32-bit executables require a \i{Pentium 4} or newer processor. We also provide executables for Windows on Arm processors. (We used to also provide executables for Windows for the Alpha processor, but stopped after 0.58 due to lack of interest.) In the development code, a partial port to Mac OS exists (see \k{faq-mac-port}). Currently PuTTY does \e{not} run on Windows CE (see \k{faq-wince}). We do not have release-quality ports for any other systems at the present time. If anyone told you we had an Android port, or an iOS port, or any other port of PuTTY, they were mistaken. We don't. There are some third-party ports to various platforms, mentioned on the \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/links.html}{Links page of our website}. \S{faq-unix}{Question} \I{Unix version}Is there a port to Unix? There are Unix ports of most of the traditional PuTTY tools, and also one entirely new application. If you look at the source release, you should find a \c{unix} subdirectory. There are a couple of ways of building it, including the usual \c{configure}/\c{make}; see the file \c{README} in the source distribution. This should build you: \b Unix ports of PuTTY, Plink, PSCP, and PSFTP, which work pretty much the same as their Windows counterparts; \b Command-line versions of PuTTYgen and Pageant, whose user interface is quite different to the Windows GUI versions; \b \i\c{pterm} - an \cw{xterm}-type program which supports the same terminal emulation as PuTTY. If you don't have \i{Gtk}, you should still be able to build the command-line tools. \S{faq-unix-why}{Question} What's the point of the Unix port? Unix has OpenSSH. All sorts of little things. \c{pterm} is directly useful to anyone who prefers PuTTY's terminal emulation to \c{xterm}'s, which at least some people do. Unix Plink has apparently found a niche among people who find the complexity of OpenSSL makes OpenSSH hard to install (and who don't mind Plink not having as many features). Some users want to generate a large number of SSH keys on Unix and then copy them all into PuTTY, and the Unix PuTTYgen should allow them to automate that conversion process. There were development advantages as well; porting PuTTY to Unix was a valuable path-finding effort for other future ports, and also allowed us to use the excellent Linux tool \W{http://valgrind.kde.org/}{Valgrind} to help with debugging, which has already improved PuTTY's stability on \e{all} platforms. However, if you're a Unix user and you can see no reason to switch from OpenSSH to PuTTY/Plink, then you're probably right. We don't expect our Unix port to be the right thing for everybody. \S{faq-wince}{Question} Will there be a port to Windows CE or PocketPC? We once did some work on such a port, but it only reached an early stage, and certainly not a useful one. It's no longer being actively worked on. \S{faq-win31}{Question} Is there a port to \i{Windows 3.1}? PuTTY is a 32-bit application from the ground up, so it won't run on Windows 3.1 as a native 16-bit program; and it would be \e{very} hard to port it to do so, because of Windows 3.1's vile memory allocation mechanisms. However, it is possible in theory to compile the existing PuTTY source in such a way that it will run under \i{Win32s} (an extension to Windows 3.1 to let you run 32-bit programs). In order to do this you'll need the right kind of C compiler - modern versions of Visual C at least have stopped being backwards compatible to Win32s. Also, the last time we tried this it didn't work very well. \S{faq-mac-port}{Question} Will there be a port to the \I{Mac OS}Mac? We hope so! We attempted one around 2005, written as a native Cocoa application, but it turned out to be very slow to redraw its window for some reason we never got to the bottom of. In 2015, after porting the GTK front end to work with GTK 3, we began another attempt based on making small changes to the GTK code and building it against the OS X Quartz version of GTK 3. This doesn't seem to have the window redrawing problem any more, so it's already got further than the last effort, but it is still substantially unfinished. If any OS X and/or GTK programming experts are keen to have a finished version of this, we urge them to help out with some of the remaining problems! See the TODO list in \c{unix/gtkapp.c} in the source code. \S{faq-epoc}{Question} Will there be a port to EPOC? I hope so, but given that ports aren't really progressing very fast even on systems the developers \e{do} already know how to program for, it might be a long time before any of us get round to learning a new system and doing the port for that. However, some of the work has been done by other people; see the \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/links.html}{Links page of our website} for various third-party ports. \S{faq-iphone}{Question} Will there be a port to the iPhone? We have no plans to write such a port ourselves; none of us has an iPhone, and developing and publishing applications for it looks awkward and expensive. However, there is a third-party SSH client for the iPhone and iPod\_Touch called \W{http://www.instantcocoa.com/products/pTerm/}{pTerm}, which is apparently based on PuTTY. (This is nothing to do with our similarly-named \c{pterm}, which is a standalone terminal emulator for Unix systems; see \k{faq-unix}.) \H{faq-embedding} Embedding PuTTY in other programs \S{faq-dll}{Question} Is the SSH or Telnet code available as a DLL? No, it isn't. It would take a reasonable amount of rewriting for this to be possible, and since the PuTTY project itself doesn't believe in DLLs (they make installation more error-prone) none of us has taken the time to do it. Most of the code cleanup work would be a good thing to happen in general, so if anyone feels like helping, we wouldn't say no. See also \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/dll-frontend.html}{the wishlist entry}. \S{faq-vb}{Question} Is the SSH or Telnet code available as a Visual Basic component? No, it isn't. None of the PuTTY team uses Visual Basic, and none of us has any particular need to make SSH connections from a Visual Basic application. In addition, all the preliminary work to turn it into a DLL would be necessary first; and furthermore, we don't even know how to write VB components. If someone offers to do some of this work for us, we might consider it, but unless that happens I can't see VB integration being anywhere other than the very bottom of our priority list. \S{faq-ipc}{Question} How can I use PuTTY to make an SSH connection from within another program? Probably your best bet is to use Plink, the command-line connection tool. If you can start Plink as a second Windows process, and arrange for your primary process to be able to send data to the Plink process, and receive data from it, through pipes, then you should be able to make SSH connections from your program. This is what CVS for Windows does, for example. \H{faq-details} Details of PuTTY's operation \S{faq-term}{Question} What \i{terminal type} does PuTTY use? For most purposes, PuTTY can be considered to be an \cw{xterm} terminal. PuTTY also supports some terminal \i{control sequences} not supported by the real \cw{xterm}: notably the Linux console sequences that reconfigure the colour palette, and the title bar control sequences used by \i\cw{DECterm} (which are different from the \cw{xterm} ones; PuTTY supports both). By default, PuTTY announces its terminal type to the server as \c{xterm}. If you have a problem with this, you can reconfigure it to say something else; \c{vt220} might help if you have trouble. \S{faq-settings}{Question} Where does PuTTY store its data? On Windows, PuTTY stores most of its data (saved sessions, SSH host keys) in the \i{Registry}. The precise location is \c HKEY_CURRENT_USER\Software\SimonTatham\PuTTY and within that area, saved sessions are stored under \c{Sessions} while host keys are stored under \c{SshHostKeys}. PuTTY also requires a random number seed file, to improve the unpredictability of randomly chosen data needed as part of the SSH cryptography. This is stored by default in a file called \i\c{PUTTY.RND}; this is stored by default in the \q{Application Data} directory, or failing that, one of a number of fallback locations. If you want to change the location of the random number seed file, you can put your chosen pathname in the Registry, at \c HKEY_CURRENT_USER\Software\SimonTatham\PuTTY\RandSeedFile You can ask PuTTY to delete all this data; see \k{faq-cleanup}. On Unix, PuTTY stores all of this data in a directory \cw{~/.putty} by default. \S{faq-trust-sigils} Why do small \i{PuTTY icon}s appear next to the login prompts? As of PuTTY 0.71, some lines of text in the terminal window are marked with a small copy of the PuTTY icon (as far as pixels allow). This is to show trustworthiness. When the PuTTY icon appears next to a line of text, it indicates that that line of text was generated by PuTTY itself, and not generated by the server and sent to PuTTY. Text that comes from the server does not have this icon, and we've arranged that the server should not be able to fake it. (There's no control sequence the server can send which will make PuTTY draw its own icon, and if the server tries to move the cursor back up to a line that \e{already} has an icon and overwrite the text, the icon will disappear.) This lets you tell the difference between (for example) a legitimate prompt in which PuTTY itself asks you for your private key passphrase, and a fake prompt in which the server tries to send the identical text to trick you into telling \e{it} your private key passphrase. \S{faq-plink-pause} Why has Plink started saying \q{Press Return to begin session}? As of PuTTY 0.71, if you use Plink for an interactive SSH session, then after the login phase has finished, it will present a final interactive prompt saying \q{Access granted. Press Return to begin session}. This is another defence against servers trying to mimic the real authentication prompts after the session has started. When you pass through that prompt, you know that everything after it is generated by the server and not by Plink itself, so any request for your private key passphrase should be treated with suspicion. In Plink, we can't use the defence described in \k{faq-trust-sigils}: Plink is running \e{in} the terminal, so anything it can write into the terminal, the server could write in the same way after the session starts. And we can't just print a separator line without a pause, because then the server could simply move the cursor back up to it and overwrite it (probably with a brief flicker, but you might easily miss that). The only robust defence anyone has come up with involves this pause. If you trust your server not to be abusive, you can turn this off. It will also not appear in various other circumstances where Plink can be confident it isn't necessary. See \k{plink-option-antispoof} for details. \H{faq-howto} HOWTO questions \S{faq-login}{Question} What login name / password should I use? This is not a question you should be asking \e{us}. PuTTY is a communications tool, for making connections to other computers. We maintain the tool; we \e{don't} administer any computers that you're likely to be able to use, in the same way that the people who make web browsers aren't responsible for most of the content you can view in them. \#{FIXME: less technical analogy?} We cannot help with questions of this sort. If you know the name of the computer you want to connect to, but don't know what login name or password to use, you should talk to whoever administers that computer. If you don't know who that is, see the next question for some possible ways to find out. \# FIXME: some people ask us to provide them with a login name apparently as random members of the public rather than in the belief that we run a server belonging to an organisation they already have some relationship with. Not sure what to say to such people. \S{faq-commands}{Question} \I{commands on the server}What commands can I type into my PuTTY terminal window? Again, this is not a question you should be asking \e{us}. You need to read the manuals, or ask the administrator, of \e{the computer you have connected to}. PuTTY does not process the commands you type into it. It's only a communications tool. It makes a connection to another computer; it passes the commands you type to that other computer; and it passes the other computer's responses back to you. Therefore, the precise range of commands you can use will not depend on PuTTY, but on what kind of computer you have connected to and what software is running on it. The PuTTY team cannot help you with that. (Think of PuTTY as being a bit like a telephone. If you phone somebody up and you don't know what language to speak to make them understand you, it isn't \e{the telephone company}'s job to find that out for you. We just provide the means for you to get in touch; making yourself understood is somebody else's problem.) If you are unsure of where to start looking for the administrator of your server, a good place to start might be to remember how you found out the host name in the PuTTY configuration. If you were given that host name by e-mail, for example, you could try asking the person who sent you that e-mail. If your company's IT department provided you with ready-made PuTTY saved sessions, then that IT department can probably also tell you something about what commands you can type during those sessions. But the PuTTY maintainer team does not administer any server you are likely to be connecting to, and cannot help you with questions of this type. \S{faq-startmax}{Question} How can I make PuTTY start up \i{maximise}d? Create a Windows shortcut to start PuTTY from, and set it as \q{Run Maximized}. \S{faq-startsess}{Question} How can I create a \i{Windows shortcut} to start a particular saved session directly? To run a PuTTY session saved under the name \q{\cw{mysession}}, create a Windows shortcut that invokes PuTTY with a command line like \c \path\name\to\putty.exe -load "mysession" (Note: prior to 0.53, the syntax was \c{@session}. This is now deprecated and may be removed at some point.) \S{faq-startssh}{Question} How can I start an SSH session straight from the command line? Use the command line \c{putty -ssh host.name}. Alternatively, create a saved session that specifies the SSH protocol, and start the saved session as shown in \k{faq-startsess}. \S{faq-cutpaste}{Question} How do I \i{copy and paste} between PuTTY and other Windows applications? Copy and paste works similarly to the X Window System. You use the left mouse button to select text in the PuTTY window. The act of selection \e{automatically} copies the text to the clipboard: there is no need to press Ctrl-Ins or Ctrl-C or anything else. In fact, pressing Ctrl-C will send a Ctrl-C character to the other end of your connection (just like it does the rest of the time), which may have unpleasant effects. The \e{only} thing you need to do, to copy text to the clipboard, is to select it. To paste the clipboard contents into a PuTTY window, by default you click the right mouse button. If you have a three-button mouse and are used to X applications, you can configure pasting to be done by the middle button instead, but this is not the default because most Windows users don't have a middle button at all. You can also paste by pressing Shift-Ins. \S{faq-options}{Question} How do I use all PuTTY's features (public keys, proxying, cipher selection, etc.) in PSCP, PSFTP and Plink? Most major features (e.g., public keys, port forwarding) are available through command line options. See the documentation. Not all features are accessible from the command line yet, although we'd like to fix this. In the meantime, you can use most of PuTTY's features if you create a PuTTY saved session, and then use the name of the saved session on the command line in place of a hostname. This works for PSCP, PSFTP and Plink (but don't expect port forwarding in the file transfer applications!). \S{faq-pscp}{Question} How do I use PSCP.EXE? When I double-click it gives me a command prompt window which then closes instantly. PSCP is a command-line application, not a GUI application. If you run it without arguments, it will simply print a help message and terminate. To use PSCP properly, run it from a Command Prompt window. See \k{pscp} in the documentation for more details. \S{faq-pscp-spaces}{Question} \I{spaces in filenames}How do I use PSCP to copy a file whose name has spaces in? If PSCP is using the traditional SCP protocol, this is confusing. If you're specifying a file at the local end, you just use one set of quotes as you would normally do: \c pscp "local filename with spaces" user@host: \c pscp user@host:myfile "local filename with spaces" But if the filename you're specifying is on the \e{remote} side, you have to use backslashes and two sets of quotes: \c pscp user@host:"\"remote filename with spaces\"" local_filename \c pscp local_filename user@host:"\"remote filename with spaces\"" Worse still, in a remote-to-local copy you have to specify the local file name explicitly, otherwise PSCP will complain that they don't match (unless you specified the \c{-unsafe} option). The following command will give an error message: \c c:\>pscp user@host:"\"oo er\"" . \c warning: remote host tried to write to a file called 'oo er' \c when we requested a file called '"oo er"'. Instead, you need to specify the local file name in full: \c c:\>pscp user@host:"\"oo er\"" "oo er" If PSCP is using the newer SFTP protocol, none of this is a problem, and all filenames with spaces in are specified using a single pair of quotes in the obvious way: \c pscp "local file" user@host: \c pscp user@host:"remote file" . \S{faq-32bit-64bit}{Question} Should I run the 32-bit or the 64-bit version? If you're not sure, the \I{32-bit Windows}32-bit version is generally the safe option. It will run perfectly well on all processors and on all versions of Windows that PuTTY supports. PuTTY doesn't require to run as a 64-bit application to work well, and having a 32-bit PuTTY on a 64-bit system isn't likely to cause you any trouble. The 64-bit version (first released in 0.68) will only run if you have a 64-bit processor \e{and} a \I{64-bit Windows}64-bit edition of Windows (both of these things are likely to be true of any recent Windows PC). It will run somewhat faster (in particular, the cryptography will be faster, especially during link setup), but it will consume slightly more memory. If you need to use an external \i{DLL} for GSSAPI authentication, that DLL may only be available in a 32-bit or 64-bit form, and that will dictate the version of PuTTY you need to use. (You will probably know if you're doing this; see \k{config-ssh-auth-gssapi-libraries} in the documentation.) \H{faq-trouble} Troubleshooting \S{faq-pscp-protocol}{Question} Why do I see \q{Fatal: Protocol error: Expected control record} in PSCP? This happens because PSCP was expecting to see data from the server that was part of the PSCP protocol exchange, and instead it saw data that it couldn't make any sense of at all. This almost always happens because the \i{startup scripts} in your account on the server machine are generating output. This is impossible for PSCP, or any other SCP client, to work around. You should never use startup files (\c{.bashrc}, \c{.cshrc} and so on) which generate output in non-interactive sessions. This is not actually a PuTTY problem. If PSCP fails in this way, then all other SCP clients are likely to fail in exactly the same way. The problem is at the server end. \S{faq-colours}{Question} I clicked on a colour in the \ii{Colours} panel, and the colour didn't change in my terminal. That isn't how you're supposed to use the Colours panel. During the course of a session, PuTTY potentially uses \e{all} the colours listed in the Colours panel. It's not a question of using only one of them and you choosing which one; PuTTY will use them \e{all}. The purpose of the Colours panel is to let you adjust the appearance of all the colours. So to change the colour of the cursor, for example, you would select \q{Cursor Colour}, press the \q{Modify} button, and select a new colour from the dialog box that appeared. Similarly, if you want your session to appear in green, you should select \q{Default Foreground} and press \q{Modify}. Clicking on \q{ANSI Green} won't turn your session green; it will only allow you to adjust the \e{shade} of green used when PuTTY is instructed by the server to display green text. \S{faq-outofmem}{Question} After trying to establish an SSH-2 connection, PuTTY says \q{\ii{Out of memory}} and dies. If this happens just while the connection is starting up, this often indicates that for some reason the client and server have failed to establish a session encryption key. Somehow, they have performed calculations that should have given each of them the same key, but have ended up with different keys; so data encrypted by one and decrypted by the other looks like random garbage. This causes an \q{out of memory} error because the first encrypted data PuTTY expects to see is the length of an SSH message. Normally this will be something well under 100 bytes. If the decryption has failed, PuTTY will see a completely random length in the region of two \e{gigabytes}, and will try to allocate enough memory to store this non-existent message. This will immediately lead to it thinking it doesn't have enough memory, and panicking. If this happens to you, it is quite likely to still be a PuTTY bug and you should report it (although it might be a bug in your SSH server instead); but it doesn't necessarily mean you've actually run out of memory. \S{faq-outofmem2}{Question} When attempting a file transfer, either PSCP or PSFTP says \q{\ii{Out of memory}} and dies. This is almost always caused by your \i{login scripts} on the server generating output. PSCP or PSFTP will receive that output when they were expecting to see the start of a file transfer protocol, and they will attempt to interpret the output as file-transfer protocol. This will usually lead to an \q{out of memory} error for much the same reasons as given in \k{faq-outofmem}. This is a setup problem in your account on your server, \e{not} a PSCP/PSFTP bug. Your login scripts should \e{never} generate output during non-interactive sessions; secure file transfer is not the only form of remote access that will break if they do. On Unix, a simple fix is to ensure that all the parts of your login script that might generate output are in \c{.profile} (if you use a Bourne shell derivative) or \c{.login} (if you use a C shell). Putting them in more general files such as \c{.bashrc} or \c{.cshrc} is liable to lead to problems. \S{faq-psftp-slow}{Question} PSFTP transfers files much slower than PSCP. The throughput of PSFTP 0.54 should be much better than 0.53b and prior; we've added code to the SFTP backend to queue several blocks of data rather than waiting for an acknowledgement for each. (The SCP backend did not suffer from this performance issue because SCP is a much simpler protocol.) \S{faq-bce}{Question} When I run full-colour applications, I see areas of black space where colour ought to be, or vice versa. You almost certainly need to change the \q{Use \i{background colour} to erase screen} setting in the Terminal panel. If there is too much black space (the commoner situation), you should enable it, while if there is too much colour, you should disable it. (See \k{config-erase}.) In old versions of PuTTY, this was disabled by default, and would not take effect until you reset the terminal (see \k{faq-resetterm}). Since 0.54, it is enabled by default, and changes take effect immediately. \S{faq-resetterm}{Question} When I change some terminal settings, nothing happens. Some of the terminal options (notably \ii{Auto Wrap} and background-colour screen erase) actually represent the \e{default} setting, rather than the currently active setting. The server can send sequences that modify these options in mid-session, but when the terminal is reset (by server action, or by you choosing \q{Reset Terminal} from the System menu) the defaults are restored. In versions 0.53b and prior, if you change one of these options in the middle of a session, you will find that the change does not immediately take effect. It will only take effect once you reset the terminal. In version 0.54, the behaviour has changed - changes to these settings take effect immediately. \S{faq-idleout}{Question} My PuTTY sessions unexpectedly close after they are \I{idle connections}idle for a while. Some types of \i{firewall}, and almost any router doing Network Address Translation (\i{NAT}, also known as IP masquerading), will forget about a connection through them if the connection does nothing for too long. This will cause the connection to be rudely cut off when contact is resumed. You can try to combat this by telling PuTTY to send \e{keepalives}: packets of data which have no effect on the actual session, but which reassure the router or firewall that the network connection is still active and worth remembering about. Keepalives don't solve everything, unfortunately; although they cause greater robustness against this sort of router, they can also cause a \e{loss} of robustness against network dropouts. See \k{config-keepalive} in the documentation for more discussion of this. \S{faq-timeout}{Question} PuTTY's network connections time out too quickly when \I{breaks in connectivity}network connectivity is temporarily lost. This is a Windows problem, not a PuTTY problem. The timeout value can't be set on per application or per session basis. To increase the TCP timeout globally, you need to tinker with the Registry. On Windows 95, 98 or ME, the registry key you need to create or change is \c HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\VxD\ \c MSTCP\MaxDataRetries (it must be of type DWORD in Win95, or String in Win98/ME). (See MS Knowledge Base article \W{http://support.microsoft.com/default.aspx?scid=kb;en-us;158474}{158474} for more information.) On Windows NT, 2000, or XP, the registry key to create or change is \c HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\ \c Parameters\TcpMaxDataRetransmissions and it must be of type DWORD. (See MS Knowledge Base articles \W{http://support.microsoft.com/default.aspx?scid=kb;en-us;120642}{120642} and \W{http://support.microsoft.com/default.aspx?scid=kb;en-us;314053}{314053} for more information.) Set the key's value to something like 10. This will cause Windows to try harder to keep connections alive instead of abandoning them. \S{faq-puttyputty}{Question} When I \cw{cat} a binary file, I get \q{PuTTYPuTTYPuTTY} on my command line. Don't do that, then. This is designed behaviour; when PuTTY receives the character Control-E from the remote server, it interprets it as a request to identify itself, and so it sends back the string \q{\cw{PuTTY}} as if that string had been entered at the keyboard. Control-E should only be sent by programs that are prepared to deal with the response. Writing a binary file to your terminal is likely to output many Control-E characters, and cause this behaviour. Don't do it. It's a bad plan. To mitigate the effects, you could configure the answerback string to be empty (see \k{config-answerback}); but writing binary files to your terminal is likely to cause various other unpleasant behaviour, so this is only a small remedy. \S{faq-wintitle}{Question} When I \cw{cat} a binary file, my \i{window title} changes to a nonsense string. Don't do that, then. It is designed behaviour that PuTTY should have the ability to adjust the window title on instructions from the server. Normally the control sequence that does this should only be sent deliberately, by programs that know what they are doing and intend to put meaningful text in the window title. Writing a binary file to your terminal runs the risk of sending the same control sequence by accident, and cause unexpected changes in the window title. Don't do it. \S{faq-password-fails}{Question} My \i{keyboard} stops working once PuTTY displays the \i{password prompt}. No, it doesn't. PuTTY just doesn't display the password you type, so that someone looking at your screen can't see what it is. Unlike the Windows login prompts, PuTTY doesn't display the password as a row of asterisks either. This is so that someone looking at your screen can't even tell how \e{long} your password is, which might be valuable information. \S{faq-keyboard}{Question} One or more \I{keyboard}\i{function keys} don't do what I expected in a server-side application. If you've already tried all the relevant options in the PuTTY Keyboard panel, you may need to mail the PuTTY maintainers and ask. It is \e{not} usually helpful just to tell us which application, which server operating system, and which key isn't working; in order to replicate the problem we would need to have a copy of every operating system, and every application, that anyone has ever complained about. PuTTY responds to function key presses by sending a sequence of control characters to the server. If a function key isn't doing what you expect, it's likely that the character sequence your application is expecting to receive is not the same as the one PuTTY is sending. Therefore what we really need to know is \e{what} sequence the application is expecting. The simplest way to investigate this is to find some other terminal environment, in which that function key \e{does} work; and then investigate what sequence the function key is sending in that situation. One reasonably easy way to do this on a \i{Unix} system is to type the command \i\c{cat}, and then press the function key. This is likely to produce output of the form \c{^[[11~}. You can also do this in PuTTY, to find out what sequence the function key is producing in that. Then you can mail the PuTTY maintainers and tell us \q{I wanted the F1 key to send \c{^[[11~}, but instead it's sending \c{^[OP}, can this be done?}, or something similar. You should still read the \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/feedback.html}{Feedback page} on the PuTTY website (also provided as \k{feedback} in the manual), and follow the guidelines contained in that. \S{faq-ssh2key-ssh1conn}{Question} Why do I see \q{Couldn't load private key from ...}? Why can PuTTYgen load my key but not PuTTY? It's likely that you've generated an SSH protocol 2 key with PuTTYgen, but you're trying to use it in an SSH-1 connection. SSH-1 and SSH-2 keys have different formats, and (at least in 0.52) PuTTY's reporting of a key in the wrong format isn't optimal. To connect using SSH-2 to a server that supports both versions, you need to change the configuration from the default (see \k{faq-ssh2}). \S{faq-rh8-utf8}{Question} When I'm connected to a \i{Red Hat Linux} 8.0 system, some characters don't display properly. A common complaint is that hyphens in man pages show up as a-acute. With release 8.0, Red Hat appear to have made \i{UTF-8} the default character set. There appears to be no way for terminal emulators such as PuTTY to know this (as far as we know, the appropriate escape sequence to switch into UTF-8 mode isn't sent). A fix is to configure sessions to RH8 systems to use UTF-8 translation - see \k{config-charset} in the documentation. (Note that if you use \q{Change Settings}, changes may not take place immediately - see \k{faq-resetterm}.) If you really want to change the character set used by the server, the right place is \c{/etc/sysconfig/i18n}, but this shouldn't be necessary. \S{faq-screen}{Question} Since I upgraded to PuTTY 0.54, the scrollback has stopped working when I run \c{screen}. PuTTY's terminal emulator has always had the policy that when the \q{\i{alternate screen}} is in use, nothing is added to the scrollback. This is because the usual sorts of programs which use the alternate screen are things like text editors, which tend to scroll back and forth in the same document a lot; so (a) they would fill up the scrollback with a large amount of unhelpfully disordered text, and (b) they contain their \e{own} method for the user to scroll back to the bit they were interested in. We have generally found this policy to do the Right Thing in almost all situations. Unfortunately, \c{screen} is one exception: it uses the alternate screen, but it's still usually helpful to have PuTTY's scrollback continue working. The simplest solution is to go to the Features control panel and tick \q{Disable switching to alternate terminal screen}. (See \k{config-features-altscreen} for more details.) Alternatively, you can tell \c{screen} itself not to use the alternate screen: the \W{http://www4.informatik.uni-erlangen.de/~jnweiger/screen-faq.html}{\c{screen} FAQ} suggests adding the line \cq{termcapinfo xterm ti@:te@} to your \cw{.screenrc} file. The reason why this only started to be a problem in 0.54 is because \c{screen} typically uses an unusual control sequence to switch to the alternate screen, and previous versions of PuTTY did not support this sequence. \S{faq-alternate-localhost}{Question} Since I upgraded \i{Windows XP} to Service Pack 2, I can't use addresses like \cw{127.0.0.2}. Some people who ask PuTTY to listen on \i{localhost} addresses other than \cw{127.0.0.1} to forward services such as \i{SMB} and \i{Windows Terminal Services} have found that doing so no longer works since they upgraded to WinXP SP2. This is apparently an issue with SP2 that is acknowledged by Microsoft in MS Knowledge Base article \W{http://support.microsoft.com/default.aspx?scid=kb;en-us;884020}{884020}. The article links to a fix you can download. (\e{However}, we've been told that SP2 \e{also} fixes the bug that means you need to use non-\cw{127.0.0.1} addresses to forward Terminal Services in the first place.) \S{faq-missing-slash}{Question} PSFTP commands seem to be missing a directory separator (slash). Some people have reported the following incorrect behaviour with PSFTP: \c psftp> pwd \e iii \c Remote directory is /dir1/dir2 \c psftp> get filename.ext \e iiiiiiiiiiiiiiii \c /dir1/dir2filename.ext: no such file or directory This is not a bug in PSFTP. There is a known bug in some versions of portable \i{OpenSSH} (\W{http://bugzilla.mindrot.org/show_bug.cgi?id=697}{bug 697}) that causes these symptoms; it appears to have been introduced around 3.7.x. It manifests only on certain platforms (AIX is what has been reported to us). There is a patch for OpenSSH attached to that bug; it's also fixed in recent versions of portable OpenSSH (from around 3.8). \S{faq-connaborted}{Question} Do you want to hear about \q{Software caused connection abort}? In the documentation for PuTTY 0.53 and 0.53b, we mentioned that we'd like to hear about any occurrences of this error. Since the release of PuTTY 0.54, however, we've been convinced that this error doesn't indicate that PuTTY's doing anything wrong, and we don't need to hear about further occurrences. See \k{errors-connaborted} for our current documentation of this error. \S{faq-rekey}{Question} My SSH-2 session \I{locking up, SSH-2 sessions}locks up for a few seconds every so often. Recent versions of PuTTY automatically initiate \i{repeat key exchange} once per hour, to improve session security. If your client or server machine is slow, you may experience this as a delay of anything up to thirty seconds or so. These \I{delays, in SSH-2 sessions}delays are inconvenient, but they are there for your protection. If they really cause you a problem, you can choose to turn off periodic rekeying using the \q{Kex} configuration panel (see \k{config-ssh-kex}), but be aware that you will be sacrificing security for this. (Falling back to SSH-1 would also remove the delays, but would lose a \e{lot} more security still. We do not recommend it.) \S{faq-xpwontrun}{Question} PuTTY fails to start up. Windows claims that \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{https://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 32-bit 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 PuTTY executable 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. \S{faq-iutf8}{Question} After I upgraded PuTTY to 0.68, I can no longer connect to my embedded device or appliance. If your SSH server has started unexpectedly closing SSH connections after you enter your password, and it worked before 0.68, you may have a buggy server that objects to certain SSH protocol extensions. The SSH protocol recently gained a new \q{terminal mode}, \cw{IUTF8}, which PuTTY sends by default; see \k{config-ttymodes}. This is the first new terminal mode since the SSH-2 protocol was defined. While servers are supposed to ignore modes they don't know about, some buggy servers will unceremoniously close the connection if they see anything they don't recognise. SSH servers in embedded devices, network appliances, and the like seem to disproportionately have this bug. If you think you have such a server, from 0.69 onwards you can disable sending of the \cw{IUTF8} mode: on the SSH / TTY panel, select \cw{IUTF8} on the list, select \q{Nothing}, and press \q{Set}. (It's not possible to disable sending this mode in 0.68.) \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 you plan to type passwords into either. It might be watching your keystrokes, or it might tamper with the PuTTY binary you download. There is \e{no} program safe enough that you can run it on an actively malicious PC and get away with typing passwords into it. If you do trust the PC, then it's probably OK to use PuTTY on it (but if you don't trust the network, then the PuTTY download might be tampered with, so it would be better to carry PuTTY with you on a USB stick). \S{faq-cleanup}{Question} What does PuTTY leave on a system? How can I \i{clean up} after it? PuTTY will leave some Registry entries, and a random seed file, on the PC (see \k{faq-settings}). Windows 7 and up also remember some information about recently launched sessions for the \q{jump list} feature. If you are using PuTTY on a public PC, or somebody else's PC, you might want to clean this information up when you leave. You can do that automatically, by running the command \c{putty -cleanup}. See \k{using-cleanup} in the documentation for more detail. (Note that this only removes settings for the currently logged-in user on \i{multi-user systems}.) If PuTTY was installed from the installer package, it will also appear in \q{Add/Remove Programs}. Current versions of the installer do not offer to remove the above-mentioned items, so if you want them removed you should run \c{putty -cleanup} before uninstalling. \S{faq-dsa}{Question} How come PuTTY now supports \i{DSA}, when the website used to say how insecure it was? DSA has a major weakness \e{if badly implemented}: it relies on a random number generator to far too great an extent. If the random number generator produces a number an attacker can predict, the DSA private key is exposed - meaning that the attacker can log in as you on all systems that accept that key. The PuTTY policy changed because the developers were informed of ways to implement DSA which do not suffer nearly as badly from this weakness, and indeed which don't need to rely on random numbers at all. For this reason we now believe PuTTY's DSA implementation is probably OK. The recently added elliptic-curve signature methods are also DSA-style algorithms, so they have this same weakness in principle. Our ECDSA implementation uses the same defence as DSA, while our Ed25519 implementation uses the similar system (but different in details) that the Ed25519 spec mandates. \S{faq-virtuallock}{Question} Couldn't Pageant use \cw{VirtualLock()} to stop private keys being written to disk? Unfortunately not. The \cw{VirtualLock()} function in the Windows API doesn't do a proper job: it may prevent small pieces of a process's memory from being paged to disk while the process is running, but it doesn't stop the process's memory as a whole from being swapped completely out to disk when the process is long-term inactive. And Pageant spends most of its time inactive. \H{faq-admin} Administrative questions \S{faq-putty-org}{Question} Is \cw{putty.org} your website? No, it isn't. \cw{putty.org} is run by an opportunist who uses it to advertise their own commercial SSH implementation to people looking for our free one. We don't own that site, we can't control it, and we don't advise anyone to use it in preference to our own site. The real PuTTY web site, run by the PuTTY team, has always been at \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/}\cw{https://www.chiark.greenend.org.uk/~sgtatham/putty/}. \S{faq-domain}{Question} Would you like me to register you a nicer domain name? No, thank you. Even if you can find one (most of them seem to have been registered already, by people who didn't ask whether we actually wanted it before they applied), we're happy with the PuTTY web site being exactly where it is. It's not hard to find (just type \q{putty} into \W{http://www.google.com/}{google.com} and we're the first link returned), and we don't believe the administrative hassle of moving the site would be worth the benefit. In addition, if we \e{did} want a custom domain name, we would want to run it ourselves, so we knew for certain that it would continue to point where we wanted it, and wouldn't suddenly change or do strange things. Having it registered for us by a third party who we don't even know is not the best way to achieve this. \S{faq-webhosting}{Question} Would you like free web hosting for the PuTTY web site? We already have some, thanks. \S{faq-link}{Question} Would you link to my web site from the PuTTY web site? Only if the content of your web page is of definite direct interest to PuTTY users. If your content is unrelated, or only tangentially related, to PuTTY, then the link would simply be advertising for you. One very nice effect of the Google ranking mechanism is that by and large, the most popular web sites get the highest rankings. This means that when an ordinary person does a search, the top item in the search is very likely to be a high-quality site or the site they actually wanted, rather than the site which paid the most money for its ranking. The PuTTY web site is held in high esteem by Google, for precisely this reason: lots of people have linked to it simply because they like PuTTY, without us ever having to ask anyone to link to us. We feel that it would be an abuse of this esteem to use it to boost the ranking of random advertisers' web sites. If you want your web site to have a high Google ranking, we'd prefer that you achieve this the way we did - by being good enough at what you do that people will link to you simply because they like you. In particular, we aren't interested in trading links for money (see above), and we \e{certainly} aren't interested in trading links for other links (since we have no advertising on our web site, our Google ranking is not even directly worth anything to us). If we don't want to link to you for free, then we probably won't want to link to you at all. If you have software based on PuTTY, or specifically designed to interoperate with PuTTY, or in some other way of genuine interest to PuTTY users, then we will probably be happy to add a link to you on our Links page. And if you're running a particularly valuable mirror of the PuTTY web site, we might be interested in linking to you from our Mirrors page. \S{faq-sourceforge}{Question} Why don't you move PuTTY to SourceForge? Partly, because we don't want to move the web site location (see \k{faq-domain}). Also, security reasons. PuTTY is a security product, and as such it is particularly important to guard the code and the web site against unauthorised modifications which might introduce subtle security flaws. Therefore, we prefer that the Git repository, web site and FTP site remain where they are, under the direct control of system administrators we know and trust personally, rather than being run by a large organisation full of people we've never met and which is known to have had breakins in the past. No offence to SourceForge; I think they do a wonderful job. But they're not ideal for everyone, and in particular they're not ideal for us. \S{faq-mailinglist1}{Question} Why can't I subscribe to the putty-bugs mailing list? Because you're not a member of the PuTTY core development team. The putty-bugs mailing list is not a general newsgroup-like discussion forum; it's a contact address for the core developers, and an \e{internal} mailing list for us to discuss things among ourselves. If we opened it up for everybody to subscribe to, it would turn into something more like a newsgroup and we would be completely overwhelmed by the volume of traffic. It's hard enough to keep up with the list as it is. \S{faq-mailinglist2}{Question} If putty-bugs isn't a general-subscription mailing list, what is? There isn't one, that we know of. If someone else wants to set up a mailing list or other forum for PuTTY users to help each other with common problems, that would be fine with us, though the PuTTY team would almost certainly not have the time to read it. It's probably better to use one of the established newsgroups for this purpose (see \k{feedback-other-fora}). \S{faq-donations}{Question} How can I donate to PuTTY development? Please, \e{please} don't feel you have to. PuTTY is completely free software, and not shareware. We think it's very important that \e{everybody} who wants to use PuTTY should be able to, whether they have any money or not; so the last thing we would want is for a PuTTY user to feel guilty because they haven't paid us any money. If you want to keep your money, please do keep it. We wouldn't dream of asking for any. Having said all that, if you still really \e{want} to give us money, we won't argue :-) The easiest way for us to accept donations is if you send money to \cw{} using PayPal (\W{http://www.paypal.com/}\cw{www.paypal.com}). If you don't like PayPal, talk to us; we can probably arrange some alternative means. Small donations (tens of dollars or tens of euros) will probably be spent on beer or curry, which helps motivate our volunteer team to continue doing this for the world. Larger donations will be spent on something that actually helps development, if we can find anything (perhaps new hardware, or a copy of Windows XP), but if we can't find anything then we'll just distribute the money among the developers. If you want to be sure your donation is going towards something worthwhile, ask us first. If you don't like these terms, feel perfectly free not to donate. We don't mind. \S{faq-permission}{Question} Can I have permission to put PuTTY on a cover disk / distribute it with other software / etc? Yes. For most things, you need not bother asking us explicitly for permission; our licence already grants you permission. See \k{feedback-permission} for more details. \S{faq-indemnity}{Question} Can you sign an agreement indemnifying us against security problems in PuTTY? No! A vendor of physical security products (e.g. locks) might plausibly be willing to accept financial liability for a product that failed to perform as advertised and resulted in damage (e.g. valuables being stolen). The reason they can afford to do this is because they sell a \e{lot} of units, and only a small proportion of them will fail; so they can meet their financial liability out of the income from all the rest of their sales, and still have enough left over to make a profit. Financial liability is intrinsically linked to selling your product for money. There are two reasons why PuTTY is not analogous to a physical lock in this context. One is that software products don't exhibit random variation: \e{if} PuTTY has a security hole (which does happen, although we do our utmost to prevent it and to respond quickly when it does), every copy of PuTTY will have the same hole, so it's likely to affect all the users at the same time. So even if our users were all paying us to use PuTTY, we wouldn't be able to \e{simultaneously} pay every affected user compensation in excess of the amount they had paid us in the first place. It just wouldn't work. The second, much more important, reason is that PuTTY users \e{don't} pay us. The PuTTY team does not have an income; it's a volunteer effort composed of people spending their spare time to try to write useful software. We aren't even a company or any kind of legally recognised organisation. We're just a bunch of people who happen to do some stuff in our spare time. Therefore, to ask us to assume financial liability is to ask us to assume a risk of having to pay it out of our own \e{personal} pockets: out of the same budget from which we buy food and clothes and pay our rent. That's more than we're willing to give. We're already giving a lot of our spare \e{time} to developing software for free; if we had to pay our own \e{money} to do it as well, we'd start to wonder why we were bothering. Free software fundamentally does not work on the basis of financial guarantees. Your guarantee of the software functioning correctly is simply that you have the source code and can check it before you use it. If you want to be sure there aren't any security holes, do a security audit of the PuTTY code, or hire a security engineer if you don't have the necessary skills yourself: instead of trying to ensure you can get compensation in the event of a disaster, try to ensure there isn't a disaster in the first place. If you \e{really} want financial security, see if you can find a security engineer who will take financial responsibility for the correctness of their review. (This might be less likely to suffer from the everything-failing-at-once problem mentioned above, because such an engineer would probably be reviewing a lot of \e{different} products which would tend to fail independently.) Failing that, see if you can persuade an insurance company to insure you against security incidents, and if the insurer demands it as a condition then get our code reviewed by a security engineer they're happy with. \S{faq-permission-form}{Question} Can you sign this form granting us permission to use/distribute PuTTY? If your form contains any clause along the lines of \q{the undersigned represents and warrants}, we're not going to sign it. This is particularly true if it asks us to warrant that PuTTY is secure; see \k{faq-indemnity} for more discussion of this. But it doesn't really matter what we're supposed to be warranting: even if it's something we already believe is true, such as that we don't infringe any third-party copyright, we will not sign a document accepting any legal or financial liability. This is simply because the PuTTY development project has no income out of which to satisfy that liability, or pay legal costs, should it become necessary. We cannot afford to be sued. We are assuring you that \e{we have done our best}; if that isn't good enough for you, tough. The existing PuTTY licence document already gives you permission to use or distribute PuTTY in pretty much any way which does not involve pretending you wrote it or suing us if it goes wrong. We think that really ought to be enough for anybody. See also \k{faq-permission-general} for another reason why we don't want to do this sort of thing. \S{faq-permission-future}{Question} Can you write us a formal notice of permission to use PuTTY? We could, in principle, but it isn't clear what use it would be. If you think there's a serious chance of one of the PuTTY copyright holders suing you (which we don't!), you would presumably want a signed notice from \e{all} of them; and we couldn't provide that even if we wanted to, because many of the copyright holders are people who contributed some code in the past and with whom we subsequently lost contact. Therefore the best we would be able to do \e{even in theory} would be to have the core development team sign the document, which wouldn't guarantee you that some other copyright holder might not sue. See also \k{faq-permission-general} for another reason why we don't want to do this sort of thing. \S{faq-permission-general}{Question} Can you sign \e{anything} for us? Not unless there's an incredibly good reason. We are generally unwilling to set a precedent that involves us having to enter into individual agreements with PuTTY users. We estimate that we have literally \e{millions} of users, and we absolutely would not have time to go round signing specific agreements with every one of them. So if you want us to sign something specific for you, you might usefully stop to consider whether there's anything special that distinguishes you from 999,999 other users, and therefore any reason we should be willing to sign something for you without it setting such a precedent. If your company policy requires you to have an individual agreement with the supplier of any software you use, then your company policy is simply not well suited to using popular free software, and we urge you to consider this as a flaw in your policy. \S{faq-permission-assurance}{Question} If you won't sign anything, can you give us some sort of assurance that you won't make PuTTY closed-source in future? Yes and no. If what you want is an assurance that some \e{current version} of PuTTY which you've already downloaded will remain free, then you already have that assurance: it's called the PuTTY Licence. It grants you permission to use, distribute and copy the software to which it applies; once we've granted that permission (which we have), we can't just revoke it. On the other hand, if you want an assurance that \e{future} versions of PuTTY won't be closed-source, that's more difficult. We could in principle sign a document stating that we would never release a closed-source PuTTY, but that wouldn't assure you that we \e{would} keep releasing \e{open}-source PuTTYs: we would still have the option of ceasing to develop PuTTY at all, which would surely be even worse for you than making it closed-source! (And we almost certainly wouldn't \e{want} to sign a document guaranteeing that we would actually continue to do development work on PuTTY; we certainly wouldn't sign it for free. Documents like that are called contracts of employment, and are generally not signed except in return for a sizeable salary.) If we \e{were} to stop developing PuTTY, or to decide to make all future releases closed-source, then you would still be free to copy the last open release in accordance with the current licence, and in particular you could start your own fork of the project from that release. If this happened, I confidently predict that \e{somebody} would do that, and that some kind of a free PuTTY would continue to be developed. There's already precedent for that sort of thing happening in free software. We can't guarantee that somebody \e{other than you} would do it, of course; you might have to do it yourself. But we can assure you that there would be nothing \e{preventing} anyone from continuing free development if we stopped. (Finally, we can also confidently predict that if we made PuTTY closed-source and someone made an open-source fork, most people would switch to the latter. Therefore, it would be pretty stupid of us to try it.) \S{faq-export-cert}{Question} Can you provide us with export control information / FIPS certification for PuTTY? Some people have asked us for an Export Control Classification Number (ECCN) for PuTTY. We don't know whether we have one, and as a team of free software developers based in the UK we don't have the time, money, or effort to deal with US bureaucracy to investigate any further. We believe that PuTTY falls under 5D002 on the US Commerce Control List, but that shouldn't be taken as definitive. If you need to know more you should seek professional legal advice. The same applies to any other country's legal requirements and restrictions. Similarly, some people have asked us for FIPS certification of the PuTTY tools. Unless someone else is prepared to do the necessary work and pay any costs, we can't provide this. \S{faq-vendor}{Question} As one of our existing software vendors, can you just fill in this questionnaire for us? We periodically receive requests like this, from organisations which have apparently sent out a form letter to everyone listed in their big spreadsheet of \q{software vendors} requiring them all to answer some long list of questions about supported OS versions, paid support arrangements, compliance with assorted local regulations we haven't heard of, contact phone numbers, and other such administrivia. Many of the questions are obviously meaningless when applied to PuTTY (we don't provide any paid support in the first place!), most of the rest could have been answered with only a very quick look at our website, and some we are actively unwilling to answer (we are private individuals, why would we want to give out our home phone numbers to large corporations?). We don't make a habit of responding in full to these questionnaires, because \e{we are not a software vendor}. A software \e{vendor} is a company to which you are paying lots of money in return for some software. They know who you are, and they know you're paying them money; so they have an incentive to fill in your forms and questionnaires, to research any local regulations you cite if they don't already know about them, and generally to provide every scrap of information you might possibly need in the most convenient manner for you, because they want to keep being paid. But we are a team of free software developers, and that means your relationship with us is nothing like that at all. If you once downloaded our software from our website, that's great and we hope you found it useful, but it doesn't mean we have the least idea who you are, or any incentive to do lots of unpaid work to support our \q{relationship} with you. It's not that we are unwilling to \e{provide information}. We put as much of it as we can on our website for your convenience, and if you actually need to know some fact about PuTTY which you haven't been able to find on the website (and which is not obviously inapplicable to free software in the first place) then please do ask us, and we'll try to answer as best we can. But we put up the website and this FAQ precisely so that we \e{don't} have to keep answering the same questions over and over again, so we aren't prepared to fill in completely generic form-letter questionnaires for people who haven't done their best to find the answers here first. If you work for an organisation which you think might be at risk of making this mistake, we urge you to reorganise your list of software suppliers so that it clearly distinguishes paid vendors who know about you from free software developers who don't have any idea who you are. Then, only send out these mass mailings to the former. \S{faq-checksums}{Question} The \c{sha1sums} / \c{sha256sums} / etc files on your download page don't match the binaries. People report this every so often, and usually the reason turns out to be that they've matched up the wrong checksums file with the wrong binaries. The PuTTY download page contains more than one version of the software. There's a \e{latest release} version; there are the \e{development snapshots}; and when we're in the run-up to making a release, there are also \e{pre-release} builds of the upcoming new version. Each one has its own collection of binaries, and its own collection of checksums files to go with them. So if you've downloaded the release version of the actual program, you need the release version of the checksums too, otherwise you will see a mismatch. Similarly, the development snapshot binaries go with the development snapshot checksums, and so on. (We've colour-coded the download page in an effort to reduce this confusion a bit.) Another thing to watch out for: as of 0.71, executables like \c{putty.exe} come in two flavours for each platform: the standalone versions on the website, each of which contains embedded help, and the versions installed by the installer, which use a separate help file also in the installer. We provide checksums for both; the latter are indicated with \cq{(installer version)} after the filename. If you have double-checked all that, and you still think there's a real mismatch, then please send us a report carefully quoting everything relevant: \b the exact URL you got your binary from \b the checksum of the binary after you downloaded \b the exact URL you got your checksums file from \b the checksum that file says the binary should have. \H{faq-misc} Miscellaneous questions \S{faq-openssh}{Question} Is PuTTY a port of \i{OpenSSH}, or based on OpenSSH or OpenSSL? No, it isn't. PuTTY is almost completely composed of code written from scratch for PuTTY. The only code we share with OpenSSH is the detector for SSH-1 CRC compensation attacks, written by CORE SDI S.A; we share no code at all with OpenSSL. \S{faq-sillyputty}{Question} Where can I buy silly putty? You're looking at the wrong web site; the only PuTTY we know about here is the name of a computer program. If you want the kind of putty you can buy as an executive toy, the PuTTY team can personally recommend Thinking Putty, which you can buy from Crazy Aaron's Putty World, at \W{http://www.puttyworld.com}\cw{www.puttyworld.com}. \S{faq-meaning}{Question} What does \q{PuTTY} mean? It's the name of a popular SSH and Telnet client. Any other meaning is in the eye of the beholder. It's been rumoured that \q{PuTTY} is the antonym of \q{\cw{getty}}, or that it's the stuff that makes your Windows useful, or that it's a kind of plutonium Teletype. We couldn't possibly comment on such allegations. \S{faq-pronounce}{Question} How do I pronounce \q{PuTTY}? Exactly like the English word \q{putty}, which we pronounce /\u02C8{'}p\u028C{V}ti/. putty-0.76/doc/feedback.but0000644000175000017500000005104514072266310012567 00000000000000\A{feedback} \ii{Feedback} and \i{bug reporting} This is a guide to providing feedback to the PuTTY development team. It is provided as both a web page on the PuTTY site, and an appendix in the PuTTY manual. \K{feedback-general} gives some general guidelines for sending any kind of e-mail to the development team. Following sections give more specific guidelines for particular types of e-mail, such as bug reports and feature requests. \H{feedback-general} General guidelines The PuTTY development team gets a \e{lot} of mail. If you can possibly solve your own problem by reading the manual, reading the FAQ, reading the web site, asking a fellow user, perhaps posting to a newsgroup (see \k{feedback-other-fora}), or some other means, then it would make our lives much easier. We get so much e-mail that we literally do not have time to answer it all. We regret this, but there's nothing we can do about it. So if you can \e{possibly} avoid sending mail to the PuTTY team, we recommend you do so. In particular, support requests (\k{feedback-support}) are probably better sent to newsgroups, or passed to a local expert if possible. The PuTTY contact email address is a private \i{mailing list} containing four or five core developers. Don't be put off by it being a mailing list: if you need to send confidential data as part of a bug report, you can trust the people on the list to respect that confidence. Also, the archives aren't publicly available, so you shouldn't be letting yourself in for any spam by sending us mail. Please use a meaningful subject line on your message. We get a lot of mail, and it's hard to find the message we're looking for if they all have subject lines like \q{PuTTY bug}. \S{feedback-largefiles} Sending large attachments Since the PuTTY contact address is a mailing list, e-mails larger than 40Kb will be held for inspection by the list administrator, and will not be allowed through unless they really appear to be worth their large size. If you are considering sending any kind of large data file to the PuTTY team, it's almost always a bad idea, or at the very least it would be better to ask us first whether we actually need the file. Alternatively, you could put the file on a web site and just send us the URL; that way, we don't have to download it unless we decide we actually need it, and only one of us needs to download it instead of it being automatically copied to all the developers. (If the file contains confidential information, then you could encrypt it with our Secure Contact Key; see \k{pgpkeys-pubkey} for details. Please \e{only} use this for information that \e{needs} to be confidential.) Some people like to send mail in MS Word format. Please \e{don't} send us bug reports, or any other mail, as a Word document. Word documents are roughly fifty times larger than writing the same report in plain text. In addition, most of the PuTTY team read their e-mail on Unix machines, so copying the file to a Windows box to run Word is very inconvenient. Not only that, but several of us don't even \e{have} a copy of Word! Some people like to send us screen shots when demonstrating a problem. Please don't do this without checking with us first - we almost never actually need the information in the screen shot. Sending a screen shot of an error box is almost certainly unnecessary when you could just tell us in plain text what the error was. (On some versions of Windows, pressing Ctrl-C when the error box is displayed will copy the text of the message to the clipboard.) Sending a full-screen shot is \e{occasionally} useful, but it's probably still wise to check whether we need it before sending it. If you \e{must} mail a screen shot, don't send it as a \cw{.BMP} file. \cw{BMP}s have no compression and they are \e{much} larger than other image formats such as PNG, TIFF and GIF. Convert the file to a properly compressed image format before sending it. Please don't mail us executables, at all. Our mail server blocks all incoming e-mail containing executables, as a defence against the vast numbers of e-mail viruses we receive every day. If you mail us an executable, it will just bounce. If you have made a tiny modification to the PuTTY code, please send us a \e{patch} to the source code if possible, rather than sending us a huge \cw{.ZIP} file containing the complete sources plus your modification. If you've only changed 10 lines, we'd prefer to receive a mail that's 30 lines long than one containing multiple megabytes of data we already have. \S{feedback-other-fora} Other places to ask for help There are two Usenet newsgroups that are particularly relevant to the PuTTY tools: \b \W{news:comp.security.ssh}\c{comp.security.ssh}, for questions specific to using the SSH protocol; \b \W{news:comp.terminals}\c{comp.terminals}, for issues relating to terminal emulation (for instance, keyboard problems). Please use the newsgroup most appropriate to your query, and remember that these are general newsgroups, not specifically about PuTTY. If you don't have direct access to Usenet, you can access these newsgroups through Google Groups (\W{http://groups.google.com/}\cw{groups.google.com}). \H{feedback-bugs} Reporting bugs If you think you have found a bug in PuTTY, your first steps should be: \b Check the \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/}{Wishlist page} on the PuTTY website, and see if we already know about the problem. If we do, it is almost certainly not necessary to mail us about it, unless you think you have extra information that might be helpful to us in fixing it. (Of course, if we actually \e{need} specific extra information about a particular bug, the Wishlist page will say so.) \b Check the \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/changes.html}{Change Log} on the PuTTY website, and see if we have already fixed the bug in the \i{development snapshots}. \b Check the \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/faq.html}{FAQ} on the PuTTY website (also provided as \k{faq} in the manual), and see if it answers your question. The FAQ lists the most common things which people think are bugs, but which aren't bugs. \b Download the latest development snapshot and see if the problem still happens with that. This really is worth doing. As a general rule we aren't very interested in bugs that appear in the release version but not in the development version, because that usually means they are bugs we have \e{already fixed}. On the other hand, if you can find a bug in the development version that doesn't appear in the release, that's likely to be a new bug we've introduced since the release and we're definitely interested in it. If none of those options solved your problem, and you still need to report a bug to us, it is useful if you include some general information: \b Tell us what \i{version of PuTTY} you are running. To find this out, use the \q{About PuTTY} option from the System menu. Please \e{do not} just tell us \q{I'm running the latest version}; e-mail can be delayed and it may not be obvious which version was the latest at the time you sent the message. \b PuTTY is a multi-platform application; tell us what version of what OS you are running PuTTY on. (If you're running on Unix, or Windows for Arm, tell us, or we'll assume you're running on Windows for Intel as this is overwhelmingly the case.) \b Tell us what protocol you are connecting with: SSH, Telnet, Rlogin, SUPDUP, or Raw mode, or a serial connection. \b Tell us what kind of server you are connecting to; what OS, and if possible what SSH server (if you're using SSH). You can get some of this information from the PuTTY Event Log (see \k{using-eventlog} in the manual). \b Send us the contents of the PuTTY Event Log, unless you have a specific reason not to (for example, if it contains confidential information that you think we should be able to solve your problem without needing to know). \b Try to give us as much information as you can to help us see the problem for ourselves. If possible, give us a step-by-step sequence of \e{precise} instructions for reproducing the fault. \b Don't just tell us that PuTTY \q{does the wrong thing}; tell us exactly and precisely what it did, and also tell us exactly and precisely what you think it should have done instead. Some people tell us PuTTY does the wrong thing, and it turns out that it was doing the right thing and their expectations were wrong. Help to avoid this problem by telling us exactly what you think it should have done, and exactly what it did do. \b If you think you can, you're welcome to try to fix the problem yourself. A \i{patch} to the code which fixes a bug is an excellent addition to a bug report. However, a patch is never a \e{substitute} for a good bug report; if your patch is wrong or inappropriate, and you haven't supplied us with full information about the actual bug, then we won't be able to find a better solution. \b \W{https://www.chiark.greenend.org.uk/~sgtatham/bugs.html}\cw{https://www.chiark.greenend.org.uk/~sgtatham/bugs.html} is an article on how to report bugs effectively in general. If your bug report is \e{particularly} unclear, we may ask you to go away, read this article, and then report the bug again. It is reasonable to report bugs in PuTTY's documentation, if you think the documentation is unclear or unhelpful. But we do need to be given exact details of \e{what} you think the documentation has failed to tell you, or \e{how} you think it could be made clearer. If your problem is simply that you don't \e{understand} the documentation, we suggest posting to a newsgroup (see \k{feedback-other-fora}) and seeing if someone will explain what you need to know. \e{Then}, if you think the documentation could usefully have told you that, send us a bug report and explain how you think we should change it. \H{feedback-vulns} Reporting security vulnerabilities If you've found a security vulnerability in PuTTY, you might well want to notify us using an encrypted communications channel, to avoid disclosing information about the vulnerability before a fixed release is available. For this purpose, we provide a GPG key suitable for encryption: the Secure Contact Key. See \k{pgpkeys-pubkey} for details of this. (Of course, vulnerabilities are also bugs, so please do include as much information as possible about them, the same way you would with any other bug report.) \H{feedback-features} Requesting extra features If you want to request a new feature in PuTTY, the very first things you should do are: \b Check the \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/}{Wishlist page} on the PuTTY website, and see if your feature is already on the list. If it is, it probably won't achieve very much to repeat the request. (But see \k{feedback-feature-priority} if you want to persuade us to give your particular feature higher priority.) \b Check the Wishlist and \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/changes.html}{Change Log} on the PuTTY website, and see if we have already added your feature in the development snapshots. If it isn't clear, download the latest development snapshot and see if the feature is present. If it is, then it will also be in the next release and there is no need to mail us at all. If you can't find your feature in either the development snapshots \e{or} the Wishlist, then you probably do need to submit a feature request. Since the PuTTY authors are very busy, it helps if you try to do some of the work for us: \b Do as much of the design as you can. Think about \q{corner cases}; think about how your feature interacts with other existing features. Think about the user interface; if you can't come up with a simple and intuitive interface to your feature, you shouldn't be surprised if we can't either. Always imagine whether it's possible for there to be more than one, or less than one, of something you'd assumed there would be one of. (For example, if you were to want PuTTY to put an icon in the System tray rather than the Taskbar, you should think about what happens if there's more than one PuTTY active; how would the user tell which was which?) \b If you can program, it may be worth offering to write the feature yourself and send us a patch. However, it is likely to be helpful if you confer with us first; there may be design issues you haven't thought of, or we may be about to make big changes to the code which your patch would clash with, or something. If you check with the maintainers first, there is a better chance of your code actually being usable. Also, read the design principles listed in \k{udp}: if you do not conform to them, we will probably not be able to accept your patch. \H{feedback-feature-priority} Requesting features that have already been requested If a feature is already listed on the Wishlist, then it usually means we would like to add it to PuTTY at some point. However, this may not be in the near future. If there's a feature on the Wishlist which you would like to see in the \e{near} future, there are several things you can do to try to increase its priority level: \b Mail us and vote for it. (Be sure to mention that you've seen it on the Wishlist, or we might think you haven't even \e{read} the Wishlist). This probably won't have very \e{much} effect; if a huge number of people vote for something then it may make a difference, but one or two extra votes for a particular feature are unlikely to change our priority list immediately. Offering a new and compelling justification might help. Also, don't expect a reply. \b Offer us money if we do the work sooner rather than later. This sometimes works, but not always. The PuTTY team all have full-time jobs and we're doing all of this work in our free time; we may sometimes be willing to give up some more of our free time in exchange for some money, but if you try to bribe us for a \e{big} feature it's entirely possible that we simply won't have the time to spare - whether you pay us or not. (Also, we don't accept bribes to add \e{bad} features to the Wishlist, because our desire to provide high-quality software to the users comes first.) \b Offer to help us write the code. This is probably the \e{only} way to get a feature implemented quickly, if it's a big one that we don't have time to do ourselves. \H{feedback-support} \ii{Support requests} If you're trying to make PuTTY do something for you and it isn't working, but you're not sure whether it's a bug or not, then \e{please} consider looking for help somewhere else. This is one of the most common types of mail the PuTTY team receives, and we simply don't have time to answer all the questions. Questions of this type include: \b If you want to do something with PuTTY but have no idea where to start, and reading the manual hasn't helped, try posting to a newsgroup (see \k{feedback-other-fora}) and see if someone can explain it to you. \b If you have tried to do something with PuTTY but it hasn't worked, and you aren't sure whether it's a bug in PuTTY or a bug in your SSH server or simply that you're not doing it right, then try posting to a newsgroup (see \k{feedback-other-fora}) and see if someone can solve your problem. Or try doing the same thing with a different SSH client and see if it works with that. Please do not report it as a PuTTY bug unless you are really sure it \e{is} a bug in PuTTY. \b If someone else installed PuTTY for you, or you're using PuTTY on someone else's computer, try asking them for help first. They're more likely to understand how they installed it and what they expected you to use it for than we are. \b If you have successfully made a connection to your server and now need to know what to type at the server's command prompt, or other details of how to use the server-end software, talk to your server's system administrator. This is not the PuTTY team's problem. PuTTY is only a communications tool, like a telephone; if you can't speak the same language as the person at the other end of the phone, it isn't the telephone company's job to teach it to you. If you absolutely cannot get a support question answered any other way, you can try mailing it to us, but we can't guarantee to have time to answer it. \H{feedback-webadmin} Web server administration If the PuTTY \i{web site} is down (Connection Timed Out), please don't bother mailing us to tell us about it. Most of us read our e-mail on the same machines that host the web site, so if those machines are down then we will notice \e{before} we read our e-mail. So there's no point telling us our servers are down. Of course, if the web site has some other error (Connection Refused, 404 Not Found, 403 Forbidden, or something else) then we might \e{not} have noticed and it might still be worth telling us about it. If you want to report a problem with our web site, check that you're looking at our \e{real} web site and not a mirror. The real web site is at \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/}\c{https://www.chiark.greenend.org.uk/~sgtatham/putty/}; if that's not where you're reading this, then don't report the problem to us until you've checked that it's really a problem with the main site. If it's only a problem with the mirror, you should try to contact the administrator of that mirror site first, and only contact us if that doesn't solve the problem (in case we need to remove the mirror from our list). \H{feedback-permission} Asking permission for things PuTTY is distributed under the MIT Licence (see \k{licence} for details). This means you can do almost \e{anything} you like with our software, our source code, and our documentation. The only things you aren't allowed to do are to remove our copyright notices or the licence text itself, or to hold us legally responsible if something goes wrong. So if you want permission to include PuTTY on a magazine cover disk, or as part of a collection of useful software on a CD or a web site, then \e{permission is already granted}. You don't have to mail us and ask. Just go ahead and do it. We don't mind. (If you want to distribute PuTTY alongside your own application for use with that application, or if you want to distribute PuTTY within your own organisation, then we recommend, but do not insist, that you offer your own first-line technical support, to answer questions about the interaction of PuTTY with your environment. If your users mail us directly, we won't be able to tell them anything useful about your specific setup.) If you want to use parts of the PuTTY source code in another program, then it might be worth mailing us to talk about technical details, but if all you want is to ask permission then you don't need to bother. You already have permission. If you just want to link to our web site, just go ahead. (It's not clear that we \e{could} stop you doing this, even if we wanted to!) \H{feedback-mirrors} Mirroring the PuTTY web site \# the next two paragraphs also on the Mirrors page itself, with \# minor context changes If you want to set up a mirror of the PuTTY website, go ahead and set one up. Please don't bother asking us for permission before setting up a mirror. You already have permission. If the mirror is in a country where we don't already have plenty of mirrors, we may be willing to add it to the list on our \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/mirrors.html}{mirrors page}. Read the guidelines on that page, make sure your mirror works, and email us the information listed at the bottom of the page. Note that we do not \e{promise} to list your mirror: we get a lot of mirror notifications and yours may not happen to find its way to the top of the list. Also note that we link to all our mirror sites using the \c{rel="nofollow"} attribute. Running a PuTTY mirror is not intended to be a cheap way to gain search rankings. If you have technical questions about the process of mirroring, then you might want to mail us before setting up the mirror (see also the \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/mirrors.html#guidelines}{guidelines on the Mirrors page}); but if you just want to ask for permission, you don't need to. You already have permission. \H{feedback-compliments} Praise and compliments One of the most rewarding things about maintaining free software is getting e-mails that just say \q{thanks}. We are always happy to receive e-mails of this type. Regrettably we don't have time to answer them all in person. If you mail us a compliment and don't receive a reply, \e{please} don't think we've ignored you. We did receive it and we were happy about it; we just didn't have time to tell you so personally. To everyone who's ever sent us praise and compliments, in the past and the future: \e{you're welcome}! \H{feedback-address} E-mail address The actual address to mail is \cw{<\W{mailto:putty@projects.tartarus.org}{putty@projects.tartarus.org}>}. putty-0.76/doc/gs.but0000644000175000017500000002156614072266310011461 00000000000000\C{gs} Getting started with PuTTY This chapter gives a quick guide to the simplest types of interactive login session using PuTTY. \H{gs-insecure} \ii{Starting a session} When you start PuTTY, you will see a \i{dialog box}. This dialog box allows you to control everything PuTTY can do. See \k{config} for details of all the things you can control. You don't usually need to change most of the configuration options. To start the simplest kind of session, all you need to do is to enter a few basic parameters. In the \q{Host Name} box, enter the Internet \i{host name} of the server you want to connect to. You should have been told this by the provider of your login account. Now select a login \i{protocol} to use, from the \q{Connection type} controls. For a login session, you should select \i{SSH}, \i{Telnet}, \i{Rlogin}, or \i{SUPDUP}. See \k{which-one} for a description of the differences between these protocols, and advice on which one to use. The \I{raw protocol}\e{Raw} protocol is not used for interactive login sessions; you would usually use this for debugging other Internet services (see \k{using-rawprot}). The \e{Serial} option is used for connecting to a local serial line, and works somewhat differently: see \k{using-serial} for more information on this. \#{FIXME: describe bare ssh-connection} When you change the selected protocol, the number in the \q{Port} box will change. This is normal: it happens because the various login services are usually provided on different network ports by the server machine. Most servers will use the standard port numbers, so you will not need to change the port setting. If your server provides login services on a non-standard port, your system administrator should have told you which one. (For example, many \i{MUDs} run Telnet service on a port other than 23.) Once you have filled in the \q{Host Name}, \q{Connection type}, and possibly \q{Port} settings, you are ready to connect. Press the \q{Open} button at the bottom of the dialog box, and PuTTY will begin trying to connect you to the server. \H{gs-hostkey} \ii{Verifying the host key} (SSH only) If you are not using the \i{SSH} protocol, you can skip this section. If you are using SSH to connect to a server for the first time, you will probably see a message looking something like this: \c The server's host key is not cached in the registry. You have no \c guarantee that the server is the computer you think it is. \c The server's ssh-ed25519 key fingerprint is: \c ssh-ed25519 255 SHA256:TddlQk20DVs4LRcAsIfDN9pInKpY06D+h4kSHwWAj4w \c If you trust this host, press "Accept" to add the key to PuTTY's \c cache and carry on connecting. \c If you want to carry on connecting just once, without adding the key \c to the cache, press "Connect Once". \c If you do not trust this host, press "Cancel" to abandon the connection. This is a feature of the SSH protocol. It is designed to protect you against a network attack known as \i\e{spoofing}: secretly redirecting your connection to a different computer, so that you send your password to the wrong machine. Using this technique, an attacker would be able to learn the password that guards your login account, and could then log in as if they were you and use the account for their own purposes. To prevent this attack, each server has a unique identifying code, called a \e{host key}. These keys are created in a way that prevents one server from forging another server's key. So if you connect to a server and it sends you a different host key from the one you were expecting, PuTTY can warn you that the server may have been switched and that a spoofing attack might be in progress. PuTTY \I{host key cache}records the host key for each server you connect to, in the Windows \i{Registry}. Every time you connect to a server, it checks that the host key presented by the server is the same host key as it was the last time you connected. If it is not, you will see a warning, and you will have the chance to abandon your connection before you type any private information (such as a password) into it. (See \k{errors-hostkey-wrong} for what that looks like.) However, when you connect to a server you have not connected to before, PuTTY has no way of telling whether the host key is the right one or not. So it gives the warning shown above, and asks you whether you want to \I{trusting host keys}trust this host key or not. Whether or not to trust the host key is your choice. If you are connecting within a company network, you might feel that all the network users are on the same side and spoofing attacks are unlikely, so you might choose to trust the key without checking it. If you are connecting across a hostile network (such as the Internet), you should check with your system administrator, perhaps by telephone or in person. (When verifying the fingerprint, be careful with letters and numbers that can be confused with each other: \c{0}/\c{O}, \c{1}/\c{I}/\c{l}, and so on.) Many servers have more than one host key. If the system administrator sends you more than one \I{host key fingerprint}fingerprint, you should make sure the one PuTTY shows you is on the list, but it doesn't matter which one it is. If you don't have any fingerprints that look like the example (\I{SHA256 fingerprint}\c{SHA256:} followed by a long string of characters), but instead have pairs of characters separated by colons like \c{a4:db:96:a7:...}, try pressing the \q{More info...} button and see if you have a fingerprint matching the \q{\i{MD5 fingerprint}} there. This is an older and less secure way to summarise the same underlying host key; it's possible for an attacker to create their own host key with the same fingerprint; so you should avoid relying on this fingerprint format unless you have no choice. The \q{More info...} dialog box also shows the full host public key, in case that is easier to compare than a fingerprint. See \k{config-ssh-hostkey} for advanced options for managing host keys. \# FIXME: this is all very fine but of course in practice the world doesn't work that way. Ask the team if they have any good ideas for changes to this section! \H{gs-login} \ii{Logging in} After you have connected, and perhaps verified the server's host key, you will be asked to log in, probably using a \i{username} and a \i{password}. Your system administrator should have provided you with these. (If, instead, your system administrator has asked you to provide, or provided you with, a \q{public key} or \q{key file}, see \k{pubkey}.) PuTTY will display a text window (the \q{\i{terminal window}} \dash it will have a black background unless you've changed the defaults), and prompt you to type your username and password into that window. (These prompts will include the \i{PuTTY icon}, to distinguish them from any text sent by the server in the same window.) Enter the username and the password, and the server should grant you access and begin your session. If you have \I{mistyping a password}mistyped your password, most servers will give you several chances to get it right. While you are typing your password, you will not usually see the cursor moving in the window, but PuTTY \e{is} registering what you type, and will send it when you press Return. (It works this way to avoid revealing the length of your password to anyone watching your screen.) If you are using SSH, be careful not to type your username wrongly, because you will not have a chance to correct it after you press Return; many SSH servers do not permit you to make two login attempts using \i{different usernames}. If you type your username wrongly, you must close PuTTY and start again. If your password is refused but you are sure you have typed it correctly, check that Caps Lock is not enabled. Many login servers, particularly Unix computers, treat upper case and lower case as different when checking your password; so if Caps Lock is on, your password will probably be refused. \H{gs-session} After logging in After you log in to the server, what happens next is up to the server! Most servers will print some sort of login message and then present a \i{prompt}, at which you can type \I{commands on the server}commands which the server will carry out. Some servers will offer you on-line help; others might not. If you are in doubt about what to do next, consult your system administrator. \H{gs-logout} \ii{Logging out} When you have finished your session, you should log out by typing the server's own logout command. This might vary between servers; if in doubt, try \c{logout} or \c{exit}, or consult a manual or your system administrator. When the server processes your logout command, the PuTTY window should close itself automatically. You \e{can} close a PuTTY session using the \i{Close button} in the window border, but this might confuse the server - a bit like hanging up a telephone unexpectedly in the middle of a conversation. We recommend you do not do this unless the server has stopped responding to you and you cannot close the window any other way. putty-0.76/doc/index.but0000644000175000017500000007273614072266310012164 00000000000000\IM{Unix version} Unix version of PuTTY tools \IM{Unix version} Linux version of PuTTY tools \IM{Unix} Unix \IM{Unix} Linux \IM{Command Prompt}{command prompt window}{MS-DOS Prompt}{console window} Command Prompt \IM{Command Prompt}{command prompt window}{MS-DOS Prompt}{console window} MS-DOS Prompt \IM{Command Prompt}{command prompt window}{MS-DOS Prompt}{console window} console window \IM{spoof}{spoofed}{spoofing} spoofing \IM{verifying the host key} verifying the host key \IM{verifying the host key} host key, verifying \IM{trusting host keys} trusting host keys \IM{trusting host keys} host keys, trusting \IM{host key fingerprint} fingerprint, of SSH host key \IM{host key fingerprint} host key fingerprint (SSH) \IM{host key fingerprint} SSH host key fingerprint \IM{MD5 fingerprint} MD5 fingerprint, of SSH host key \IM{MD5 fingerprint} fingerprint, MD5, of SSH host key \IM{SHA256 fingerprint} SHA-256 fingerprint, of SSH host key \IM{SHA256 fingerprint} fingerprint, SHA-256, of SSH host key \IM{manually configuring host keys} manually configuring host keys \IM{manually configuring host keys} overriding host keys \IM{manually configuring host keys} host keys, manually configuring \IM{starting a session} starting a session \IM{starting a session} session, starting \IM{commands on the server}{remote command} commands on the server \IM{commands on the server}{remote command} remote commands \IM{commands on the server}{remote command} server, commands on \IM{mistyping a password} mistyping a password \IM{mistyping a password} password, mistyping \IM{different usernames}{changes of username} different user names \IM{different usernames}{changes of username} changing user names \IM{different usernames}{changes of username} user names, different \IM{different usernames}{changes of username} login names, different \IM{different usernames}{changes of username} account names, different \IM{differences between SSH, Telnet, Rlogin, and SUPDUP} differences between SSH, Telnet, Rlogin, and SUPDUP \IM{differences between SSH, Telnet, Rlogin, and SUPDUP} protocols, differences between \IM{differences between SSH, Telnet, Rlogin, and SUPDUP} SSH, differences from other protocols \IM{differences between SSH, Telnet, Rlogin, and SUPDUP} Telnet, differences from other protocols \IM{differences between SSH, Telnet, Rlogin, and SUPDUP} Rlogin, differences from other protocols \IM{differences between SSH, Telnet, Rlogin, and SUPDUP} SUPDUP, differences from other protocols \IM{differences between SSH, Telnet, Rlogin, and SUPDUP} selecting a protocol \IM{differences between SSH, Telnet, Rlogin, and SUPDUP} choosing a protocol \IM{MUD}{MUDs} MUDs \IM{talker}{talker systems} talker systems \IM{security hazard}{security risk} security hazard \IM{SSH-2}{SSH protocol version 2} SSH-2 \IM{terminal window}{PuTTY window} terminal window \IM{terminal window}{PuTTY window} PuTTY terminal window \IM{terminal window}{PuTTY window} window, terminal \IM{copy and paste} copy and paste \IM{copy and paste} cut and paste \IM{copy and paste} paste, copy and \IM{three-button mouse} three-button mouse \IM{three-button mouse} mouse, three-button \IM{left mouse button}{left button} left mouse button \IM{middle mouse button}{middle button}{middle-clicking} middle mouse button \IM{right mouse button}{right button} right mouse button \IM{selecting words}{word-by-word selection} selecting whole words \IM{selecting words}{word-by-word selection} words, selecting \IM{selecting lines} selecting whole lines \IM{selecting lines} lines, selecting \IM{rectangular selection} rectangular selection \IM{rectangular selection} selection, rectangular \IM{adjusting a selection} adjusting a selection \IM{adjusting a selection} extending a selection \IM{adjusting a selection} selection, adjusting \IM{right mouse button, with Ctrl} right mouse button, with Ctrl \IM{right mouse button, with Ctrl} Ctrl, with right mouse button \IM{selections} selections, multiple \IM{selections} clipboards, multiple \IM{PRIMARY} \c{PRIMARY} selection \IM{PRIMARY} selection, \c{PRIMARY} \IM{CLIPBOARD selection} \c{CLIPBOARD} selection \IM{CLIPBOARD selection} selection, \c{CLIPBOARD} \IM{SECONDARY} \c{SECONDARY} selection \IM{SECONDARY} selection, \c{SECONDARY} \IM{system menu} system menu \IM{system menu} menu, system \IM{system menu} window menu \IM{context menu} context menu \IM{context menu} menu, context \IM{context menu} right mouse button menu \IM{Event Log} Event Log \IM{Event Log} PuTTY Event Log \IM{Event Log} Log, Event \IM{Telnet special commands} Telnet special commands \IM{Telnet special commands} special commands, in Telnet \IM{SSH special commands} SSH special commands \IM{SSH special commands} special commands, in SSH \IM{Repeat key exchange, SSH special command} Repeat key exchange, SSH special command \IM{Repeat key exchange, SSH special command} key exchange, forcing repeat \IM{Repeat key exchange, SSH special command} SSH key exchange, forcing repeat \IM{accented characters} accented characters \IM{accented characters} characters, accented \IM{line-drawing characters} line-drawing characters \IM{line-drawing characters} box-drawing characters \IM{line-drawing characters} characters, line-drawing \IM{line-drawing characters} ANSI graphics \IM{port forwarding}{port forwardings} port forwarding in SSH \IM{port forwarding}{port forwardings} SSH port forwarding \IM{port forwarding}{port forwardings} forwarding ports in SSH \IM{port forwarding}{port forwardings} tunnelling using SSH \IM{port forwarding}{port forwardings} SSH tunnelling \IM{port forwarding, changing mid-session} port forwarding in SSH, changing mid-session \IM{port forwarding, changing mid-session} SSH port forwarding, changing mid-session \IM{port forwarding, changing mid-session} forwarding ports in SSH, changing mid-session \IM{port forwarding, changing mid-session} tunnelling using SSH, changing mid-session \IM{port forwarding, changing mid-session} SSH tunnelling, changing mid-session \IM{local port forwarding} local-to-remote port forwarding \IM{remote port forwarding} remote-to-local port forwarding \IM{dynamic port forwarding} dynamic port forwarding \IM{dynamic port forwarding} SOCKS port forwarding \IM{debugging Internet protocols} debugging Internet protocols \IM{debugging Internet protocols} Internet protocols, debugging \IM{debugging Internet protocols} protocols, debugging \IM{Internet protocol version} Internet Protocol version \IM{Internet protocol version} version, of Internet Protocol \IM{raw TCP connections} raw TCP connections \IM{raw TCP connections} TCP connections, raw \IM{command-line arguments} command-line arguments \IM{command-line arguments} arguments, command-line \IM{command-line arguments} options, command-line \IM{command-line arguments} switches, command-line \IM{Windows shortcut} Windows shortcut \IM{Windows shortcut} shortcut, Windows \IM{telnet URLs} Telnet URLs \IM{telnet URLs} URLs, Telnet \IM{saved sessions, loading from command line} saved sessions, loading from command line \IM{saved sessions, loading from command line} loading saved sessions from command line \IM{saved sessions, loading from command line} command line, loading saved sessions from \IM{putty @sessionname} \c{putty @sessionname} \IM{putty @sessionname} \c{@sessionname} command-line argument \IM{protocol selection} protocol selection \IM{protocol selection} selecting a protocol \IM{protocol selection} choosing a protocol \IM{ssh-connection} bare \cw{ssh-connection} protocol \IM{ssh-connection} \cw{ssh-connection} protocol, bare \IM{psusan} \cq{psusan} program \IM{login name}{username} login name \IM{login name}{username} user name \IM{login name}{username} account name \IM{reading commands from a file} reading commands from a file \IM{reading commands from a file} commands, reading from a file \IM{agent forwarding} agent forwarding \IM{agent forwarding} authentication agent forwarding \IM{agent forwarding} SSH agent forwarding \IM{agent forwarding} forwarding, SSH agent \IM{X11 forwarding}{forwarding of X11} X11 forwarding \IM{X11 forwarding}{forwarding of X11} SSH X11 forwarding \IM{X11 forwarding}{forwarding of X11} forwarding, of X11 \IM{X11 authentication} X11 authentication \IM{X11 authentication} authentication, X11 \IM{pseudo-terminal allocation} pseudo-terminal allocation \IM{pseudo-terminal allocation} pty allocation \IM{pseudo-terminal allocation} allocation, of pseudo-terminal \IM{ERASE special character} \cw{ERASE}, special character \IM{ERASE special character} \cw{VERASE}, special character \IM{QUIT special character} \cw{QUIT}, special character \IM{QUIT special character} \cw{VQUIT}, special character \IM{-telnet} \c{-telnet} command-line option \IM{-raw} \c{-raw} command-line option \IM{-rlogin} \c{-rlogin} command-line option \IM{-supdup} \c{-supdup} command-line option \IM{-ssh} \c{-ssh} command-line option \IM{-ssh-connection} \c{-ssh-connection} command-line option \IM{-serial} \c{-serial} command-line option \IM{-cleanup} \c{-cleanup} command-line option \IM{-load} \c{-load} command-line option \IM{-v} \c{-v} command-line option \IM{-l} \c{-l} command-line option \IM{-L-upper} \c{-L} command-line option \IM{-R-upper} \c{-R} command-line option \IM{-D-upper} \c{-D} command-line option \IM{-m} \c{-m} command-line option \IM{-P-upper} \c{-P} command-line option \IM{-pw} \c{-pw} command-line option \IM{-A-upper} \c{-A} command-line option \IM{-a} \c{-a} command-line option \IM{-X-upper} \c{-X} command-line option \IM{-x} \c{-x} command-line option \IM{-T-upper} \c{-T} command-line option \IM{-t} \c{-t} command-line option \IM{-C-upper} \c{-C} command-line option \IM{-N-upper} \c{-N} command-line option \IM{-1} \c{-1} command-line option \IM{-2} \c{-2} command-line option \IM{-i} \c{-i} command-line option \IM{-pgpfp} \c{-pgpfp} command-line option \IM{-sercfg} \c{-sercfg} command-line option \IM{removing registry entries} removing registry entries \IM{removing registry entries} registry entries, removing \IM{random seed file} random seed file \IM{random seed file} \c{putty.rnd} (random seed file) \IM{putty.rnd} \c{putty.rnd} (random seed file) \IM{suppressing remote shell} remote shell, suppressing \IM{suppressing remote shell} shell, remote, suppressing \IM{SSH protocol version} SSH protocol version \IM{SSH protocol version} protocol version, SSH \IM{SSH protocol version} version, of SSH protocol \IM{PPK} \cw{PPK} file \IM{PPK} private key file, PuTTY \IM{Argon2} Argon2 passphrase hashing function \IM{passphrase hashing} passphrase hashing, for private key files \IM{passphrase hashing} password hashing, for private key files \IM{PGP key fingerprint} PGP key fingerprint \IM{PGP key fingerprint} fingerprint, of PGP key \IM{verifying new versions} verifying new versions of PuTTY \IM{verifying new versions} new version, verifying \IM{verifying new versions} upgraded version, verifying \IM{connection}{network connection} network connection \IM{connection}{network connection} connection, network \IM{host name}{hostname} host name \IM{host name}{hostname} DNS name \IM{host name}{hostname} server name \IM{IP address}{Internet address} IP address \IM{IP address}{Internet address} address, IP \IM{localhost} \c{localhost} \IM{loopback IP address}{loopback address} loopback IP address \IM{loopback IP address}{loopback address} IP address, loopback \IM{listen address} listen address \IM{listen address} bind address \IM{DNS} DNS \IM{DNS} Domain Name System \IM{name resolution} name resolution \IM{name resolution} DNS resolution \IM{name resolution} host name resolution \IM{name resolution} server name resolution \IM{loading and storing saved sessions} sessions, loading and storing \IM{loading and storing saved sessions} settings, loading and storing \IM{loading and storing saved sessions} saving settings \IM{loading and storing saved sessions} storing settings \IM{loading and storing saved sessions} loading settings \IM{Default Settings} Default Settings \IM{Default Settings} settings, default \IM{Registry} Registry (Windows) \IM{Registry} Windows Registry \IM{inactive window} inactive window \IM{inactive window} window, inactive \IM{inactive window} terminal window, inactive \IM{SSH packet log} SSH packet log \IM{SSH packet log} packet log, SSH \IM{auto wrap mode}{auto wrap} auto wrap mode \IM{auto wrap mode}{auto wrap} wrapping, automatic \IM{auto wrap mode}{auto wrap} line wrapping, automatic \IM{control sequence}{control codes} control sequences \IM{control sequence}{control codes} terminal control sequences \IM{control sequence}{control codes} escape sequences \IM{cursor coordinates} cursor coordinates \IM{cursor coordinates} coordinates, cursor \IM{CR} CR (Carriage Return) \IM{CR} Carriage Return \IM{LF} LF (Line Feed) \IM{LF} Line Feed \IM{clear screen} clear screen \IM{clear screen} erase screen \IM{clear screen} screen, clearing \IM{blinking text} blinking text \IM{blinking text} flashing text \IM{answerback} answerback string \IM{local echo} local echo \IM{local echo} echo, local \IM{remote echo} remote echo \IM{remote echo} echo, remote \IM{local line editing} local line editing \IM{local line editing} line editing, local \IM{remote-controlled printing} ANSI printing \IM{remote-controlled printing} remote-controlled printing \IM{remote-controlled printing} printing, remote-controlled \IM{remote-controlled printing} passthrough printing \IM{Control-H} Control-H \IM{Control-H} Ctrl-H \IM{Control-?} Control-? \IM{Control-?} Ctrl-? \IM{Home and End keys} Home key \IM{Home and End keys} End key \IM{keypad} keypad, numeric \IM{keypad} numeric keypad \IM{Application Cursor Keys} Application Cursor Keys \IM{Application Cursor Keys} cursor keys, \q{Application} mode \IM{Application Keypad} Application Keypad \IM{Application Keypad} keypad, \q{Application} mode \IM{Application Keypad} numeric keypad, \q{Application} mode \IM{Num Lock}{NumLock} Num Lock \IM{NetHack keypad mode} NetHack keypad mode \IM{NetHack keypad mode} keypad, NetHack mode \IM{compose key} Compose key \IM{compose key} DEC Compose key \IM{terminal bell} terminal bell \IM{terminal bell} bell, terminal \IM{terminal bell} beep, terminal \IM{terminal bell} feep \IM{Windows Default Beep} Windows Default Beep sound \IM{Windows Default Beep} Default Beep sound, Windows \IM{terminal bell, disabling} terminal bell, disabling \IM{terminal bell, disabling} bell, disabling \IM{visual bell} visual bell \IM{visual bell} bell, visual \IM{PC speaker} PC speaker \IM{PC speaker} beep, with PC speaker \IM{sound file} sound file \IM{sound file} \cw{WAV} file \IM{bell overload} bell overload mode \IM{bell overload} terminal bell overload mode \IM{mouse reporting} mouse reporting \IM{mouse reporting} \c{xterm} mouse reporting \IM{links} \c{links} (web browser) \IM{mc} \c{mc} \IM{mc} Midnight Commander \IM{terminal resizing}{window resizing} terminal resizing \IM{terminal resizing}{window resizing} window resizing \IM{terminal resizing}{window resizing} resizing, terminal \IM{destructive backspace} destructive backspace \IM{destructive backspace} non-destructive backspace \IM{destructive backspace} backspace, destructive \IM{Arabic text shaping} Arabic text shaping \IM{Arabic text shaping} shaping, of Arabic text \IM{Unicode} Unicode \IM{Unicode} ISO-10646 (Unicode) \IM{ASCII} ASCII \IM{ASCII} US-ASCII \IM{bidirectional text} bidirectional text \IM{bidirectional text} right-to-left text \IM{display becomes corrupted} display corruption \IM{display becomes corrupted} corruption, of display \IM{rows} rows, in terminal window \IM{columns} columns, in terminal window \IM{window size} window size \IM{window size} size, of window \IM{font size} font size \IM{font size} size, of font \IM{full screen}{full-screen} full-screen mode \IM{cursor blinks} blinking cursor \IM{cursor blinks} flashing cursor \IM{cursor blinks} cursor, blinking \IM{font} font \IM{font} typeface \IM{minimise} minimise window \IM{minimise} window, minimising \IM{maximise} maximise window \IM{maximise} window, maximising \IM{closing window}{close window} closing window \IM{closing window}{close window} window, closing \IM{Dragon NaturallySpeaking} Dragon NaturallySpeaking \IM{Dragon NaturallySpeaking} NaturallySpeaking \IM{AltGr} \q{AltGr} key \IM{Alt} \q{Alt} key \IM{CJK} CJK \IM{CJK} Chinese \IM{CJK} Japanese \IM{CJK} Korean \IM{East Asian Ambiguous characters} East Asian Ambiguous characters \IM{East Asian Ambiguous characters} CJK ambiguous characters \IM{character width} character width \IM{character width} single-width character \IM{character width} double-width character \IM{Rich Text Format} Rich Text Format \IM{Rich Text Format} RTF \IM{bold}{bold text} bold text \IM{colour}{colours} colour \IM{8-bit colour} 8-bit colour \IM{8-bit colour} colour, 8-bit \IM{system colours} system colours \IM{system colours} colours, system \IM{ANSI colours} ANSI colours \IM{ANSI colours} colours, ANSI \IM{cursor colour} cursor colour \IM{cursor colour} colour, of cursor \IM{default background} background colour, default \IM{default background} colour, background, default \IM{default foreground} foreground colour, default \IM{default foreground} colour, foreground, default \IM{bold black} bold black \IM{bold black} black, bold \IM{bold black} bright black \IM{TERM} \cw{TERM} environment variable \IM{logical palettes} logical palettes \IM{logical palettes} palettes, logical \IM{breaks in connectivity} connectivity, breaks in \IM{breaks in connectivity} intermittent connectivity \IM{idle connections} idle connections \IM{idle connections} timeout, of connections \IM{idle connections} connections, idle \IM{interactive connections}{interactive session} interactive connections \IM{interactive connections}{interactive session} connections, interactive \IM{keepalives} keepalives, application \IM{Nagle's algorithm} Nagle's algorithm \IM{Nagle's algorithm} \cw{TCP_NODELAY} \IM{TCP keepalives} TCP keepalives \IM{TCP keepalives} keepalives, TCP \IM{TCP keepalives} \cw{SO_KEEPALIVE} \IM{half-open connections} half-open connections \IM{half-open connections} connections, half-open \IM{auto-login username} user name, for auto-login \IM{auto-login username} login name, for auto-login \IM{auto-login username} account name, for auto-login \IM{terminal emulation}{terminal-type} terminal emulation \IM{terminal emulation}{terminal-type} emulation, terminal \IM{terminal speed} terminal speed \IM{terminal speed} speed, terminal \IM{terminal speed} baud rate, of terminal \IM{environment variables} environment variables \IM{environment variables} variables, environment \IM{proxy} proxy server \IM{proxy} server, proxy \IM{HTTP proxy} HTTP proxy \IM{HTTP proxy} proxy, HTTP \IM{HTTP proxy} server, HTTP \IM{HTTP proxy} \cw{CONNECT} proxy (HTTP) \IM{SOCKS server} SOCKS proxy \IM{SOCKS server} server, SOCKS \IM{SOCKS server} proxy, SOCKS \IM{Telnet proxy} Telnet proxy \IM{Telnet proxy} TCP proxy \IM{Telnet proxy} ad-hoc proxy \IM{Telnet proxy} proxy, Telnet \IM{Local proxy} local proxy \IM{Local proxy} proxy command \IM{Local proxy} command, proxy \IM{proxy DNS} proxy DNS \IM{proxy DNS} DNS, with proxy \IM{proxy DNS} name resolution, with proxy \IM{proxy DNS} host name resolution, with proxy \IM{proxy DNS} server name resolution, with proxy \IM{proxy username} proxy user name \IM{proxy username} user name, for proxy \IM{proxy username} login name, for proxy \IM{proxy username} account name, for proxy \IM{proxy password} proxy password \IM{proxy password} password, for proxy \IM{proxy authentication} proxy authentication \IM{proxy authentication} authentication, to proxy \IM{HTTP basic} HTTP \q{basic} authentication \IM{HTTP basic} \q{basic} authentication (HTTP) \IM{plaintext password} plain text password \IM{plaintext password} password, plain text \IM{Telnet negotiation} Telnet option negotiation \IM{Telnet negotiation} option negotiation, Telnet \IM{Telnet negotiation} negotiation, of Telnet options \IM{firewall}{firewalls} firewalls \IM{NAT router}{NAT} NAT routers \IM{NAT router}{NAT} routers, NAT \IM{NAT router}{NAT} Network Address Translation \IM{NAT router}{NAT} IP masquerading \IM{Telnet New Line} Telnet New Line \IM{Telnet New Line} new line, in Telnet \IM{.rhosts} \c{.rhosts} file \IM{.rhosts} \q{rhosts} file \IM{passwordless login} passwordless login \IM{passwordless login} login, passwordless \IM{Windows user name} local user name, in Windows \IM{Windows user name} user name, local, in Windows \IM{Windows user name} login name, local, in Windows \IM{Windows user name} account name, local, in Windows \IM{local username in Rlogin} local user name, in Rlogin \IM{local username in Rlogin} user name, local, in Rlogin \IM{local username in Rlogin} login name, local, in Rlogin \IM{local username in Rlogin} account name, local, in Rlogin \IM{privileged port} privileged port \IM{privileged port} low-numbered port \IM{privileged port} port, privileged \IM{remote shell} shell, remote \IM{remote shell} remote shell \IM{encryption}{encrypted}{encrypt} encryption \IM{encryption algorithm} encryption algorithm \IM{encryption algorithm} cipher algorithm \IM{encryption algorithm} symmetric-key algorithm \IM{encryption algorithm} algorithm, encryption \IM{AES} AES \IM{AES} Advanced Encryption Standard \IM{AES} Rijndael \IM{Arcfour} Arcfour \IM{Arcfour} RC4 \IM{triple-DES} triple-DES \IM{single-DES} single-DES \IM{single-DES} DES \IM{key exchange} key exchange \IM{key exchange} kex \IM{shared secret} shared secret \IM{shared secret} secret, shared \IM{key exchange algorithm} key exchange algorithm \IM{key exchange algorithm} algorithm, key exchange \IM{Diffie-Hellman key exchange} Diffie-Hellman key exchange \IM{Diffie-Hellman key exchange} key exchange, Diffie-Hellman \IM{group exchange} Diffie-Hellman group exchange \IM{group exchange} group exchange, Diffie-Hellman \IM{repeat key exchange} repeat key exchange \IM{repeat key exchange} key exchange, repeat \IM{challenge/response authentication} challenge/response authentication \IM{challenge/response authentication} authentication, challenge/response \IM{security token} security token \IM{security token} token, security \IM{one-time passwords} one-time passwords \IM{one-time passwords} password, one-time \IM{keyboard-interactive authentication} keyboard-interactive authentication \IM{keyboard-interactive authentication} authentication, keyboard-interactive \IM{password expiry} password expiry \IM{password expiry} expiry, of passwords \IM{public key authentication}{public-key authentication} public key authentication \IM{public key authentication}{public-key authentication} RSA authentication \IM{public key authentication}{public-key authentication} DSA authentication \IM{public key authentication}{public-key authentication} authentication, public key \IM{MIT-MAGIC-COOKIE-1} \cw{MIT-MAGIC-COOKIE-1} \IM{MIT-MAGIC-COOKIE-1} magic cookie \IM{MIT-MAGIC-COOKIE-1} cookie, magic \IM{SSH server bugs} SSH server bugs \IM{SSH server bugs} bugs, in SSH servers \IM{ignore message} SSH \q{ignore} messages \IM{ignore message} \q{ignore} messages, in SSH \IM{message authentication code}{MAC} message authentication code (MAC) \IM{message authentication code}{MAC} MAC (message authentication code) \IM{signatures} signature \IM{signatures} digital signature \IM{storing configuration in a file} storing settings in a file \IM{storing configuration in a file} saving settings in a file \IM{storing configuration in a file} loading settings from a file \IM{transferring files} transferring files \IM{transferring files} files, transferring \IM{receiving files}{download a file} receiving files \IM{receiving files}{download a file} files, receiving \IM{receiving files}{download a file} downloading files \IM{sending files}{upload a file} sending files \IM{sending files}{upload a file} files, sending \IM{sending files}{upload a file} uploading files \IM{listing files} listing files \IM{listing files} files, listing \IM{wildcard}{wildcards} wildcards \IM{wildcard}{wildcards} glob (wildcard) \IM{PATH} \c{PATH} environment variable \IM{SFTP} SFTP \IM{SFTP} SSH file transfer protocol \IM{-unsafe} \c{-unsafe} PSCP command-line option \IM{-ls-PSCP} \c{-ls} PSCP command-line option \IM{-p-PSCP} \c{-p} PSCP command-line option \IM{-q-PSCP} \c{-q} PSCP command-line option \IM{-r-PSCP} \c{-r} PSCP command-line option \IM{-batch-PSCP} \c{-batch} PSCP command-line option \IM{-sftp} \c{-sftp} PSCP command-line option \IM{-scp} \c{-scp} PSCP command-line option \IM{return value} return value \IM{return value} exit value \IM{-b-PSFTP} \c{-b} PSFTP command-line option \IM{-bc-PSFTP} \c{-bc} PSFTP command-line option \IM{-be-PSFTP} \c{-be} PSFTP command-line option \IM{-batch-PSFTP} \c{-batch} PSFTP command-line option \IM{spaces in filenames} spaces in filenames \IM{spaces in filenames} filenames containing spaces \IM{working directory} working directory \IM{working directory} current working directory \IM{resuming file transfers} resuming file transfers \IM{resuming file transfers} files, resuming transfer of \IM{changing permissions on files} changing permissions on files \IM{changing permissions on files} permissions on files, changing \IM{changing permissions on files} files, changing permissions on \IM{changing permissions on files} modes of files, changing \IM{changing permissions on files} access to files, changing \IM{deleting files} deleting files \IM{deleting files} files, deleting \IM{deleting files} removing files \IM{create a directory} creating directories \IM{create a directory} directories, creating \IM{remove a directory} removing directories \IM{remove a directory} directories, removing \IM{remove a directory} deleting directories \IM{rename remote files} renaming files \IM{rename remote files} files, renaming and moving \IM{rename remote files} moving files \IM{local Windows command} local Windows command \IM{local Windows command} Windows command \IM{PLINK_PROTOCOL} \c{PLINK_PROTOCOL} environment variable \IM{-batch-plink} \c{-batch} Plink command-line option \IM{-s-plink} \c{-s} Plink command-line option \IM{-shareexists-plink} \c{-shareexists} Plink command-line option \IM{subsystem} subsystem, SSH \IM{subsystem} SSH subsystem \IM{batch file}{batch files} batch files \IM{CVS_RSH} \c{CVS_RSH} environment variable \IM{DSA} DSA \IM{DSA} Digital Signature Standard \IM{EdDSA} EdDSA \IM{EdDSA} Edwards-curve DSA \IM{public-key algorithm} public-key algorithm \IM{public-key algorithm} asymmetric key algorithm \IM{public-key algorithm} algorithm, public-key \IM{generating keys} generating key pairs \IM{generating keys} creating key pairs \IM{generating keys} key pairs, generating \IM{generating keys} public keys, generating \IM{generating keys} private keys, generating \IM{probable primes} probable primes \IM{probable primes} primes, probable \IM{proven primes} proven primes \IM{proven primes} primes, proven \IM{authorized_keys file}{authorized_keys} \cw{authorized_keys} file \IM{key fingerprint} fingerprint, of SSH authentication key \IM{key fingerprint} public key fingerprint (SSH) \IM{key fingerprint} SSH public key fingerprint \IM{SSH-2 public key format} SSH-2 public key file format \IM{SSH-2 public key format} public key file, SSH-2 \IM{OpenSSH private key format} OpenSSH private key file format \IM{OpenSSH private key format} private key file, OpenSSH \IM{ssh.com private key format} \cw{ssh.com} private key file format \IM{ssh.com private key format} private key file, \cw{ssh.com} \IM{importing keys} importing private keys \IM{importing keys} loading private keys \IM{export private keys} exporting private keys \IM{export private keys} saving private keys \IM{.ssh} \c{.ssh} directory \IM{.ssh2} \c{.ssh2} directory \IM{authentication agent} authentication agent \IM{authentication agent} agent, authentication \IM{-c-pageant} \c{-c} Pageant command-line option \IM{FAQ} FAQ \IM{FAQ} Frequently Asked Questions \IM{supported features} supported features \IM{supported features} features, supported \IM{remember my password} storing passwords \IM{remember my password} password, storing \IM{login scripts}{startup scripts} login scripts \IM{login scripts}{startup scripts} startup scripts \IM{Red Hat Linux} Red Hat Linux \IM{Red Hat Linux} Linux, Red Hat \IM{SMB} SMB \IM{SMB} Windows file sharing \IM{clean up} clean up after PuTTY \IM{clean up} uninstalling \IM{version of PuTTY} version, of PuTTY \IM{GPG signatures} PGP signatures, of PuTTY binaries \IM{GPG signatures} GPG signatures, of PuTTY binaries \IM{GPG signatures} signatures, of PuTTY binaries \IM{logical host name} logical host name \IM{logical host name} host name, logical \IM{host key cache}{host key management} host key management \IM{host key cache}{host key management} cache, of SSH host keys \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{cascading credentials} cascading credentials \IM{cascading credentials} credentials, cascading \IM{SYSTEM32} \cw{SYSTEM32} directory, on Windows \IM{32-bit Windows} 32-bit Windows \IM{32-bit Windows} Windows, 32-bit \IM{32-bit Windows} x86 (32-bit processor architecture) \IM{64-bit Windows} 64-bit Windows \IM{64-bit Windows} Windows, 64-bit \IM{Windows process ACL} Windows process ACL \IM{Windows process ACL} process ACL (Windows) \IM{Windows process ACL} ACL, process (Windows) \IM{proxy logging} proxy logging \IM{proxy logging} logging, proxy \IM{proxy logging} diagnostic, proxy \IM{proxy logging} standard error, proxy \IM{PuTTY icon} PuTTY icon \IM{PuTTY icon} icon, PuTTY's \IM{PuTTY icon} logo, PuTTY's \IM{system tray} system tray, Windows \IM{system tray} notification area, Windows (aka system tray) \IM{system tray} taskbar notification area, Windows (aka system tray) putty-0.76/doc/intro.but0000644000175000017500000000745414072266310012203 00000000000000\C{intro} Introduction to PuTTY PuTTY is a free SSH, Telnet, Rlogin, and SUPDUP client for Windows systems. \H{you-what} What are SSH, Telnet, Rlogin, and SUPDUP? If you already know what SSH, Telnet, Rlogin, and SUPDUP are, you can safely skip on to the next section. SSH, Telnet, Rlogin, and SUPDUP are four ways of doing the same thing: logging in to a multi-user computer from another computer, over a network. Multi-user operating systems, typically of the Unix family (such as Linux, MacOS, and the BSD family), usually present a \i{command-line interface} to the user, much like the \q{\i{Command Prompt}} or \q{\i{MS-DOS Prompt}} in Windows. The system prints a prompt, and you type commands which the system will obey. Using this type of interface, there is no need for you to be sitting at the same machine you are typing commands to. The commands, and responses, can be sent over a network, so you can sit at one computer and give commands to another one, or even to more than one. SSH, Telnet, Rlogin, and SUPDUP are \i\e{network protocols} that allow you to do this. On the computer you sit at, you run a \i\e{client}, which makes a network connection to the other computer (the \i\e{server}). The network connection carries your keystrokes and commands from the client to the server, and carries the server's responses back to you. These protocols can also be used for other types of keyboard-based interactive session. In particular, there are a lot of bulletin boards, \i{talker systems} and \i{MUDs} (Multi-User Dungeons) which support access using Telnet. There are even a few that support SSH. You might want to use SSH, Telnet, Rlogin, or SUPDUP if: \b you have an account on a Unix system (or some other multi-user OS such as VMS or ITS) which you want to be able to access from somewhere else \b your Internet Service Provider provides you with a login account on a \i{web server}. (This might also be known as a \i\e{shell account}. A \e{shell} is the program that runs on the server and interprets your commands for you.) \b you want to use a \i{bulletin board system}, talker or MUD which can be accessed using Telnet. You probably do \e{not} want to use SSH, Telnet, Rlogin, or SUPDUP if: \b you only use Windows. Windows computers have their own ways of networking between themselves, and unless you are doing something fairly unusual, you will not need to use any of these remote login protocols. \H{which-one} How do SSH, Telnet, Rlogin, and SUPDUP differ? This list summarises some of the \i{differences between SSH, Telnet, Rlogin, and SUPDUP}. \b SSH (which stands for \q{\i{secure shell}}) is a recently designed, high-security protocol. It uses strong cryptography to protect your connection against eavesdropping, hijacking and other attacks. Telnet, Rlogin, and SUPDUP are all older protocols offering minimal security. \b SSH and Rlogin both allow you to \I{passwordless login}log in to the server without having to type a password. (Rlogin's method of doing this is insecure, and can allow an attacker to access your account on the server. SSH's method is much more secure, and typically breaking the security requires the attacker to have gained access to your actual client machine.) \b SSH allows you to connect to the server and automatically send a command, so that the server will run that command and then disconnect. So you can use it in automated processing. The Internet is a hostile environment and security is everybody's responsibility. If you are connecting across the open Internet, then we recommend you use SSH. If the server you want to connect to doesn't support SSH, it might be worth trying to persuade the administrator to install it. If your client and server are both behind the same (good) firewall, it is more likely to be safe to use Telnet, Rlogin, or SUPDUP, but we still recommend you use SSH. putty-0.76/doc/man-pageant.but0000644000175000017500000003510514072266310013232 00000000000000\cfg{man-identity}{pageant}{1}{2015-05-19}{PuTTY tool suite}{PuTTY tool suite} \H{pageant-manpage} Man page for Pageant \S{pageant-manpage-name} NAME \cw{pageant} - PuTTY SSH authentication agent \S{pageant-manpage-synopsis} SYNOPSIS \c pageant ( -X | -T | --permanent | --debug ) [ [ --encrypted ] key-file... ] \e bbbbbbb bb bb bbbbbbbbbbb bbbbbbb bbbbbbbbbbb iiiiiiii \c pageant [ [ --encrypted ] key-file... ] --exec command [ args... ] \e bbbbbbb bbbbbbbbb iiiiiiii bbbbbb iiiiiii iiii \c pageant -a [ --encrypted ] key-file... \e bbbbbbb bb bbbbbbbbbbb iiiiiiii \c pageant ( -d | -r | --public | --public-openssh ) key-identifier... \e bbbbbbb bb bb bbbbbbbb bbbbbbbbbbbbbbbb iiiiiiiiiiiiii \c pageant ( -D | -R ) \e bbbbbbb bb bb \c pageant -l [ --fptype format ] \e bbbbbbb bb bbbbbbbb iiiiii \c pageant --askpass prompt \e bbbbbbb bbbbbbbbb iiiiii \S{pageant-manpage-description} DESCRIPTION \c{pageant} is both an SSH authentication agent, and also a tool for communicating with an already-running agent. When running as an SSH agent, it listens on a Unix-domain socket for connections from client processes running under your user id. Clients can load SSH private keys into the agent, or request signatures on a given message from a key already in the agent. This permits one-touch authentication by SSH client programs, if Pageant is holding a key that the server they are connecting to will accept. \c{pageant} can also act as a client program itself, communicating with an already-running agent to add or remove keys, list the keys, or extract their public half. The agent protocol used by \c{pageant} is compatible with the PuTTY tools and also with other implementations such as OpenSSH's SSH client and \e{ssh-agent(1)}. Some \c{pageant} features are implemented with protocol extensions, so will only work if \c{pageant} is on both ends. To run \c{pageant} as an agent, you must provide an option to tell it what its \e{lifetime} should be. Typically you would probably want Pageant to last for the duration of a login session, in which case you should use either \cw{-X} or \cw{-T}, depending on whether your login session is GUI or purely terminal-based respectively. For example, in your X session startup script you might write \c eval $(pageant -X) \e bbbbbbbbbbbbbbbbbb which will cause Pageant to start running, monitor the X server to notice when your session terminates (and then it will terminate too), and print on standard output some shell commands to set environment variables that client processes will need to find the running agent. In a terminal-based login, you could do almost exactly the same thing but with \cw{-T}: \c eval $(pageant -T) \e bbbbbbbbbbbbbbbbbb This will cause Pageant to tie its lifetime to that of your controlling terminal: when you log out, and the terminal device ceases to be associated with your session, Pageant will notice that it has no controlling terminal any more, and will terminate automatically. In either of these modes, you can also add one or more private keys as extra command-line arguments, e.g. \c eval $(pageant -T ~/.ssh/key.ppk) \e bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb in which case Pageant will immediately prompt for the keys' passphrases (if any) and start the agent with those keys already loaded in cleartext form. Passphrase prompts will use the controlling terminal if one is available, or failing that the GUI if one of those is available. (The prompt method can be overridden with the \cw{--gui-prompt} or \cw{--tty-prompt} options.) If neither is available, no passphrase prompting can be done. Alternatively, you can start an agent with keys stored in encrypted form: \c eval $(pageant -T --encrypted ~/.ssh/key.ppk) \e bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb In this case, Pageant will not prompt for a passphrase at startup; instead, it will prompt the first time a client tries to use the key. (Pageant will need access to a GUI so that it can pop up a passphrase prompt when required, unless it's running in \cw{--debug} mode.) To use Pageant to talk to an existing agent, you can add new keys using \cw{-a}, list the current set of keys' fingerprints and comments with \cw{-l}, extract the full public half of any key using \cw{--public} or \cw{--public-openssh}, delete a specific key or all keys using \cw{-d} or \cw{-D} respectively, or request re-encryption of a specific key or all keys using \cw{-r} or \cw{-R} respectively. \S{pageant-manpage-lifetime} LIFETIME The following options are called \e{lifetime modes}. They all request Pageant to operate in agent mode; each one specifies a different method for Pageant to start up and know when to shut down. \dt \cw{-X} \dd Pageant will open a connection to your X display, and when that connection is lost, it will terminate. This gives it the same lifetime as your GUI login session, so in this mode it is suitable for running from a startup script such as \cw{.xsession}. The actual agent will be a subprocess; the main Pageant process will terminate immediately, after printing environment-variable setting commands on standard output which should be installed in any process wanting to communicate with the agent. \lcont{ The usual approach would be to run \c eval $(pageant -X) \e bbbbbbbbbbbbbbbbbb in an X session startup script. However, other possibilities exist, such as directing the standard output of \cq{pageant -X} to a file which is then sourced by any new shell. } \dt \cw{-T} \dd Pageant will tie its lifetime to that of the login session running on its controlling terminal, by noticing when it ceases to have a controlling terminal (which will automatically happen as a side effect of the session leader process terminating). Like \cw{-X}, Pageant will print environment-variable commands on standard output. \dt \cw{--exec} \e{command} \dd Pageant will run the provided command as a subprocess, preloaded with the appropriate environment variables to access the agent it starts up. When the subprocess terminates, Pageant will terminate as well. \lcont{ All arguments on Pageant's command line after \cw{--exec} will be treated as part of the command to run, even if they look like other valid Pageant options or key files. } \dt \cw{--permanent} \dd Pageant will fork off a subprocess to be the agent, and print environment-variable commands on standard output, like \cw{-X} and \cw{-T}. However, in this case, it will make no effort to limit its lifetime in any way; it will simply run permanently, unless manually killed. The environment variable \cw{SSH_AGENT_PID}, set by the commands printed by Pageant, permits the agent process to be found for this purpose. \lcont{ This option is not recommended, because any method of manually killing the agent carries the risk of the session terminating unexpectedly before it manages to happen. } \dt \cw{--debug} \dd Pageant will run in the foreground, without forking. It will print its environment variable setup commands on standard output, and then it will log all agent activity to standard output as well; any passphrase prompts will need to be answered on standard input. This is useful for debugging what Pageant itself is doing, or what another process is doing to it. \S{pageant-manpage-client} CLIENT OPTIONS The following options tell Pageant to operate in client mode, contacting an existing agent via environment variables that it should already have set. \dt \cw{-a} \e{key-files} \dd Load the specified private key file(s) and add them to the already-running agent. Unless \cw{--encrypted} is also specified, \c{pageant} will decrypt them if necessary by prompting for their passphrases (with the same choice of user interfaces as in agent mode). \lcont{ The private key files must be in PuTTY's \cw{.ppk} file format. } \dt \cw{-l} \dd List the keys currently in the running agent. Each key's fingerprint and comment string will be shown. (Use the \cw{-E} option to change the fingerprint format.) \lcont{ Keys that will require a passphrase on their next use are listed as \q{encrypted}. Keys that can be returned to this state with \cw{-r} are listed as \q{re-encryptable}. } \dt \cw{--public} \e{key-identifiers} \dd Print the public half of each specified key, in the RFC 4716 standard format (multiple lines, starting with \cq{---- BEGIN SSH2 PUBLIC KEY ----}). \lcont{ Each \e{key-identifier} can be any of the following: \b The name of a file containing the key, either the whole key (again in \cw{.ppk} format) or just its public half. \b The key's comment string, as shown by \cw{pageant -l}. \b Enough of one of the key's fingerprint formats to be unique among keys currently loaded into the agent. If Pageant can uniquely identify one key by interpreting the \e{key-identifier} in any of these ways, it will assume that key was the one you meant. If it cannot, you will have to specify more detail. If you find that your desired \e{key-identifier} string can be validly interpreted as more than one of the above \e{kinds} of identification, you can disambiguate by prefixing it as follows: \dt \cq{file:} \dd to indicate that it is a filename \dt \cq{comment:} \dd to indicate that it is a comment string \dt \cq{fp:} \dd to indicate that it is a fingerprint; any fingerprint format will be matched \dt \cq{sha256:} or \cq{md5:} \dd to indicate that it is a fingerprint of a specific format } \dt \cw{--public-openssh} \e{key-identifiers}, \cw{-L} \e{key-identifiers} \dd Print the public half of each specified key, in the one-line format used by OpenSSH, suitable for putting in \cw{.ssh/authorized_keys} files. \dt \cw{-d} \e{key-identifiers} \dd Delete each specified key from the agent's memory, so that the agent will no longer serve it to clients unless it is loaded in again using \cw{pageant -a}. \dt \cw{-D} \dd Delete all keys from the agent's memory, leaving it completely empty. \dt \cw{-r} \e{key-identifiers} \dd \q{Re-encrypt} each specified key in the agent's memory - that is, forget any cleartext version, so that the user will be prompted for a passphrase again next time the key is used. (For this to be possible, the key must previously have been added with the \cw{--encrypted} option.) \lcont{ (Holding encrypted keys is a Pageant extension, so this option and \cw{-R} are unlikely to work with other agents.) } \dt \cw{-R} \dd \q{Re-encrypt} all possible keys in the agent's memory. (This may leave some keys in cleartext, if they were not previously added with the \cw{--encrypted} option.) \dt \cw{--test-sign} \e{key-identifier} \dt \cw{--test-sign-with-flags=}\e{flags} \e{key-identifier} \dd Sign arbitrary data with the given key. This mode is only likely to be useful when testing \c{pageant} itself. \lcont{ The data to sign is taken from standard input, signed by the agent with the key identified by \e{key-identifier}, and the resulting signature emitted on standard output (as a binary blob in the format defined by the SSH specifications). \e{flags} is a number representing a combination of flag bits defined by the SSH agent protocol. } \S{pageant-manpage-askpass} SSH-ASKPASS REPLACEMENT \dt \cw{--askpass} \e{prompt} \dd With this option, \c{pageant} acts as an \e{ssh-askpass(1)} replacement, rather than performing any SSH agent functionality. This may be useful if you prefer Pageant's GUI prompt style, which minimises information leakage about your passphrase length in its visual feedback, compared to other \e{ssh-askpass(1)} implementations. \lcont{ \c{pageant --askpass} implements the standard \e{ssh-askpass(1)} interface: it can be passed a prompt to display (as a single argument) and, if successful, prints the passphrase on standard output and returns a zero exit status. Typically you would use the environment variable \cw{SSH_ASKPASS} to tell other programs to use \c{pageant} in this way. } \S{pageant-manpage-options} OPTIONS \dt \cw{-v} \dd Verbose mode. When Pageant runs in agent mode, this option causes it to log all agent activity to its standard error. For example, you might run \lcont{ \c eval $(pageant -X -v 2>~/.pageant.log) \e bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb and expect a list of all signatures requested by agent clients to build up in that log file. The log information is the same as that produced by the \cw{--debug} lifetime option, but \cw{--debug} sends it to standard output (since that is the main point of debugging mode) whereas \cw{-v} in all other lifetime modes sends the same log data to standard error (being a by-product of the program's main purpose). Using \cw{-v} in \cw{--debug} mode has no effect: the log still goes to standard output. } \dt \cw{-s}, \cw{-c} \dd Force Pageant to output its environment setup commands in the style of POSIX / Bourne shells (\cw{-s}) or C shells (\cw{-c}) respectively. If neither option is given, Pageant will guess based on whether the environment variable \cw{SHELL} has a value ending in \cq{csh}. \dt \cw{--symlink} \e{fixed-path} \dd When operating in agent mode, as well as creating a uniquely named listening socket, \c{pageant} will also create (or update) a symbolic link at \e{fixed-path} pointing to that socket. \lcont{ This allows access to an agent instance by setting the \c{SSH_AUTH_SOCK} environment variable to \e{fixed-path}, rather than having to use the value invented by \c{pageant} when it starts. It's mainly expected to be useful for debugging. } \dt \cw{--encrypted}, \cw{--no-decrypt} \dd When adding keys to the agent (at startup or later), keep them in encrypted form until the first attempt to use them; the user will be prompted for a passphrase then. Once decrypted, a key that was added in this way can be \q{re-encrypted} with the \cw{-r} or \cw{-R} client options. \lcont{ The \cw{--encrypted} option makes no difference for key files which do not have a passphrase. (Storing keys in encrypted form is a Pageant extension; other agent implementations are unlikely to support it.) } \dt \cw{-E} \e{fingerprint-type}, \cw{--fptype} \e{fingerprint-type} \dd Specify the fingerprint format to print. Only applicable when listing fingerprints with \cw{-l}. The available formats are \cw{sha256} (the default) and \cw{md5}. \dt \cw{--gui-prompt}, \cw{--tty-prompt} \dd Force Pageant to prompt for key passphrases with a particular method (GUI or terminal) rather than trying to guess the most appropriate method as described above. (These options are relevant whenever a key file is specified to \c{pageant} that needs immediate decryption, and in \c{--askpass} mode.) \dt \cw{--help} \dd Print a brief summary of command-line options and terminate. \dt \cw{--version}, \cw{-V} \dd Print the version of Pageant. \dt \cw{--} \dd Cause all subsequent arguments to be treated as key file names, even if they look like options. putty-0.76/doc/man-plink.but0000644000175000017500000002072214072266310012727 00000000000000\cfg{man-identity}{plink}{1}{2004-03-24}{PuTTY tool suite}{PuTTY tool suite} \H{plink-manpage} Man page for Plink \S{plink-manpage-name} NAME \cw{plink} \- PuTTY link, command line network connection tool \S{plink-manpage-synopsis} SYNOPSIS \c plink [options] [user@]host [command] \e bbbbb iiiiiii iiiib iiii iiiiiii \S{plink-manpage-description} DESCRIPTION \cw{plink} is a network connection tool supporting several protocols. \S{plink-manpage-options} OPTIONS The command-line options supported by \cw{plink} are: \dt \cw{-V} \dd Show version information and exit. \dt \cw{-pgpfp} \dd Display the fingerprints of the PuTTY PGP Master Keys and exit, to aid in verifying new files released by the PuTTY team. \dt \cw{-v} \dd Show verbose messages. \dt \cw{-load} \e{session} \dd Load settings from saved session. \dt \cw{-ssh} \dd Force use of SSH protocol (default). \dt \cw{-telnet} \dd Force use of Telnet protocol. \dt \cw{-rlogin} \dd Force use of rlogin protocol. \dt \cw{-raw} \dd Force raw mode. \dt \cw{-serial} \dd Force serial mode. \dt \cw{-ssh-connection} \dd Force use of the \q{bare \cw{ssh-connection}} protocol. This is only likely to be useful when connecting to a \e{psusan(1)} server, most likely with an absolute path to a Unix-domain socket in place of \e{host}. \dt \cw{\-proxycmd} \e{command} \dd Instead of making a TCP connection, use \e{command} as a proxy; network traffic will be redirected to the standard input and output of \e{command}. \e{command} must be a single word, so is likely to need quoting by the shell. \lcont{ The special strings \cw{%host} and \cw{%port} in \e{command} will be replaced by the hostname and port number you want to connect to; to get a literal \c{%} sign, enter \c{%%}. Backslash escapes are also supported, such as sequences like \c{\\n} being replaced by a literal newline; to get a literal backslash, enter \c{\\\\}. (Further escaping may be required by the shell.) (See the main PuTTY manual for full details of the supported \cw{%}- and backslash-delimited tokens, although most of them are probably not very useful in this context.) } \dt \cw{-P} \e{port} \dd Connect to port \e{port}. \dt \cw{-l} \e{user} \dd Set remote username to \e{user}. \dt \cw{-m} \e{path} \dd Read remote command(s) from local file \e{path}. \dt \cw{-batch} \dd Disable interactive prompts. \dt \cw{-sanitise-stderr} \dt \cw{-sanitise-stdout} \dt \cw{-no-sanitise-stderr} \dt \cw{-no-sanitise-stdout} \dd By default, Plink can choose to filter control characters if that seems appropriate, to prevent remote processes sending confusing escape sequences. These options override Plink's default behaviour to enable or disabling such filtering on the standard error and standard output channels. \dt \cw{-pw} \e{password} \dd Set remote password to \e{password}. \e{CAUTION:} this will likely make the password visible to other users of the local machine (via commands such as \q{\c{w}}). \dt \cw{\-L} \cw{[}\e{srcaddr}\cw{:]}\e{srcport}\cw{:}\e{desthost}\cw{:}\e{destport} \dd Set up a local port forwarding: listen on \e{srcport} (or \e{srcaddr}:\e{srcport} if specified), and forward any connections over the SSH connection to the destination address \e{desthost}:\e{destport}. Only works in SSH. \dt \cw{\-R} \cw{[}\e{srcaddr}\cw{:]}\e{srcport}\cw{:}\e{desthost}\cw{:}\e{destport} \dd Set up a remote port forwarding: ask the SSH server to listen on \e{srcport} (or \e{srcaddr}:\e{srcport} if specified), and to forward any connections back over the SSH connection where the client will pass them on to the destination address \e{desthost}:\e{destport}. Only works in SSH. \dt \cw{\-D} [\e{srcaddr}:]\e{srcport} \dd Set up dynamic port forwarding. The client listens on \e{srcport} (or \e{srcaddr}:\e{srcport} if specified), and implements a SOCKS server. So you can point SOCKS-aware applications at this port and they will automatically use the SSH connection to tunnel all their connections. Only works in SSH. \dt \cw{-X} \dd Enable X11 forwarding. \dt \cw{-x} \dd Disable X11 forwarding (default). \dt \cw{-A} \dd Enable agent forwarding. \dt \cw{-a} \dd Disable agent forwarding (default). \dt \cw{-t} \dd Enable pty allocation (default if a command is NOT specified). \dt \cw{-T} \dd Disable pty allocation (default if a command is specified). \dt \cw{-1} \dd Force use of SSH protocol version 1. \dt \cw{-2} \dd Force use of SSH protocol version 2. \dt \cw{-4}, \cw{-6} \dd Force use of IPv4 or IPv6 for network connections. \dt \cw{-C} \dd Enable SSH compression. \dt \cw{-i} \e{keyfile} \dd Private key file for user authentication. For SSH-2 keys, this key file must be in PuTTY's PPK format, not OpenSSH's format or anyone else's. \lcont{ If you are using an authentication agent, you can also specify a \e{public} key here (in RFC 4716 or OpenSSH format), to identify which of the agent's keys to use. } \dt \cw{\-noagent} \dd Don't try to use an authentication agent for local authentication. (This doesn't affect agent forwarding.) \dt \cw{\-agent} \dd Allow use of an authentication agent. (This option is only necessary to override a setting in a saved session.) \dt \cw{\-no\-trivial\-auth} \dd Disconnect from any SSH server which accepts authentication without ever having asked for any kind of password or signature or token. (You might want to enable this for a server you always expect to challenge you, for instance to ensure you don't accidentally type your key file's passphrase into a compromised server spoofing Plink's passphrase prompt.) \dt \cw{\-noshare} \dd Don't test and try to share an existing connection, always make a new connection. \dt \cw{\-share} \dd Test and try to share an existing connection. \dt \cw{\-hostkey} \e{key} \dd Specify an acceptable host public key. This option may be specified multiple times; each key can be either a fingerprint (\cw{SHA256:AbCdE...}, \cw{99:aa:bb:...}, etc) or a base64-encoded blob in OpenSSH's one-line format. \lcont{ Specifying this option overrides automated host key management; \e{only} the key(s) specified on the command-line will be accepted (unless a saved session also overrides host keys, in which case those will be added to), and the host key cache will not be written. } \dt \cw{-s} \dd Remote command is SSH subsystem (SSH-2 only). \dt \cw{-N} \dd Don't start a remote command or shell at all (SSH-2 only). \dt \cw{\-nc} \e{host}:\e{port} \dd Make a remote network connection from the server instead of starting a shell or command. \dt \cw{\-sercfg} \e{configuration-string} \dd Specify the configuration parameters for the serial port, in \cw{-serial} mode. \e{configuration-string} should be a comma-separated list of configuration parameters as follows: \lcont{ \b Any single digit from 5 to 9 sets the number of data bits. \b \cq{1}, \cq{1.5} or \cq{2} sets the number of stop bits. \b Any other numeric string is interpreted as a baud rate. \b A single lower-case letter specifies the parity: \cq{n} for none, \cq{o} for odd, \cq{e} for even, \cq{m} for mark and \cq{s} for space. \b A single upper-case letter specifies the flow control: \cq{N} for none, \cq{X} for XON/XOFF, \cq{R} for RTS/CTS and \cq{D} for DSR/DTR. } \dt \cw{\-sshlog} \e{logfile} \dt \cw{\-sshrawlog} \e{logfile} \dd For SSH connections, these options make \cw{plink} log protocol details to a file. (Some of these may be sensitive, although by default an effort is made to suppress obvious passwords.) \lcont{ \cw{\-sshlog} logs decoded SSH packets and other events (those that \cw{\-v} would print). \cw{\-sshrawlog} additionally logs the raw encrypted packet data. } \dt \cw{\-logoverwrite} \dd If Plink is configured to write to a log file that already exists, discard the existing file. \dt \cw{\-logappend} \dd If Plink is configured to write to a log file that already exists, append new log data to the existing file. \dt \cw{\-shareexists} \dd Instead of making a new connection, test for the presence of an existing connection that can be shared. The desired session can be specified in any of the usual ways. \lcont{ Returns immediately with a zero exit status if a suitable \q{upstream} exists, nonzero otherwise. } \S{plink-manpage-more-information} MORE INFORMATION For more information on plink, it's probably best to go and look at the manual on the PuTTY web page: \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/}\cw{https://www.chiark.greenend.org.uk/~sgtatham/putty/} \S{plink-manpage-bugs} BUGS This man page isn't terribly complete. See the above web link for better documentation. putty-0.76/doc/man-pscp.but0000644000175000017500000001366114072266310012563 00000000000000\cfg{man-identity}{pscp}{1}{2004-03-24}{PuTTY tool suite}{PuTTY tool suite} \H{pscp-manpage} Man page for PSCP \S{pscp-manpage-name} NAME \cw{pscp} \- command-line SCP (secure copy) / SFTP client \S{pscp-manpage-synopsis} SYNOPSIS \c pscp [options] [user@]host:source target \e bbbb iiiiiii iiiib iiiibiiiiii iiiiii \c pscp [options] source [source...] [user@]host:target \e bbbb iiiiiii iiiiii iiiiii iiiib iiiibiiiiii \c pscp [options] -ls [user@]host:filespec \e bbbb iiiiiii bbb iiiib iiiibiiiiiiii \S{pscp-manpage-description} DESCRIPTION \cw{pscp} is a command-line client for the SSH-based SCP (secure copy) and SFTP (secure file transfer protocol) protocols. \S{pscp-manpage-options} OPTIONS The command-line options supported by \e{pscp} are: \dt \cw{-V} \dd Show version information and exit. \dt \cw{-pgpfp} \dd Display the fingerprints of the PuTTY PGP Master Keys and exit, to aid in verifying new files released by the PuTTY team. \dt \cw{-ls} \dd Remote directory listing. \dt \cw{-p} \dd Preserve file attributes. \dt \cw{-q} \dd Quiet, don't show statistics. \dt \cw{-r} \dd Copy directories recursively. \dt \cw{-unsafe} \dd Allow server-side wildcards (DANGEROUS). \dt \cw{-v} \dd Show verbose messages. \dt \cw{-load} \e{session} \dd Load settings from saved session. \dt \cw{-P} \e{port} \dd Connect to port \e{port}. \dt \cw{\-proxycmd} \e{command} \dd Instead of making a TCP connection, use \e{command} as a proxy; network traffic will be redirected to the standard input and output of \e{command}. \e{command} must be a single word, so is likely to need quoting by the shell. \lcont{ The special strings \cw{%host} and \cw{%port} in \e{command} will be replaced by the hostname and port number you want to connect to; to get a literal \c{%} sign, enter \c{%%}. Backslash escapes are also supported, such as sequences like \c{\\n} being replaced by a literal newline; to get a literal backslash, enter \c{\\\\}. (Further escaping may be required by the shell.) (See the main PuTTY manual for full details of the supported \cw{%}- and backslash-delimited tokens, although most of them are probably not very useful in this context.) } \dt \cw{-l} \e{user} \dd Set remote username to \e{user}. \dt \cw{-batch} \dd Disable interactive prompts. \dt \cw{-no-sanitise-stderr} \dd By default, PSCP will filter control characters from the standard error channel from the server, to prevent remote processes sending confusing escape sequences. This option forces the standard error channel to not be filtered. \dt \cw{-pw} \e{password} \dd Set remote password to \e{password}. \e{CAUTION:} this will likely make the password visible to other users of the local machine (via commands such as \q{\c{w}}). \dt \cw{-1} \dd Force use of SSH protocol version 1. \dt \cw{-2} \dd Force use of SSH protocol version 2. \dt \cw{-ssh-connection} \dd Force use of the \q{bare \cw{ssh-connection}} protocol. This is only likely to be useful when connecting to a \e{psusan(1)} server, most likely with an absolute path to a Unix-domain socket in place of \e{host}. \dt \cw{-ssh} \dd Force use of the SSH protocol. (This is usually not needed; it's only likely to be useful if you need to override some other configuration of the \q{bare \cw{ssh-connection}} protocol.) \dt \cw{-4}, \cw{-6} \dd Force use of IPv4 or IPv6 for network connections. \dt \cw{-C} \dd Enable SSH compression. \dt \cw{-i} \e{keyfile} \dd Private key file for user authentication. For SSH-2 keys, this key file must be in PuTTY's PPK format, not OpenSSH's format or anyone else's. \lcont{ If you are using an authentication agent, you can also specify a \e{public} key here (in RFC 4716 or OpenSSH format), to identify which of the agent's keys to use. } \dt \cw{\-noagent} \dd Don't try to use an authentication agent. \dt \cw{\-agent} \dd Allow use of an authentication agent. (This option is only necessary to override a setting in a saved session.) \dt \cw{\-no\-trivial\-auth} \dd Disconnect from any SSH server which accepts authentication without ever having asked for any kind of password or signature or token. (You might want to enable this for a server you always expect to challenge you, for instance to ensure you don't accidentally type your key file's passphrase into a compromised server spoofing PSCP's passphrase prompt.) \dt \cw{\-hostkey} \e{key} \dd Specify an acceptable host public key. This option may be specified multiple times; each key can be either a fingerprint (\cw{SHA256:AbCdE...}, \cw{99:aa:bb:...}, etc) or a base64-encoded blob in OpenSSH's one-line format. \lcont{ Specifying this option overrides automated host key management; \e{only} the key(s) specified on the command-line will be accepted (unless a saved session also overrides host keys, in which case those will be added to), and the host key cache will not be written. } \dt \cw{-scp} \dd Force use of SCP protocol. \dt \cw{-sftp} \dd Force use of SFTP protocol. \dt \cw{\-sshlog} \e{logfile} \dt \cw{\-sshrawlog} \e{logfile} \dd These options make \cw{pscp} log protocol details to a file. (Some of these may be sensitive, although by default an effort is made to suppress obvious passwords.) \lcont{ \cw{\-sshlog} logs decoded SSH packets and other events (those that \cw{\-v} would print). \cw{\-sshrawlog} additionally logs the raw encrypted packet data. } \dt \cw{\-logoverwrite} \dd If PSCP is configured to write to a log file that already exists, discard the existing file. \dt \cw{\-logappend} \dd If PSCP is configured to write to a log file that already exists, append new log data to the existing file. \S{pscp-manpage-more-information} MORE INFORMATION For more information on \cw{pscp} it's probably best to go and look at the manual on the PuTTY web page: \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/}\cw{https://www.chiark.greenend.org.uk/~sgtatham/putty/} \S{pscp-manpage-bugs} BUGS This man page isn't terribly complete. See the above web link for better documentation. putty-0.76/doc/man-psftp.but0000644000175000017500000001316314072266310012747 00000000000000\cfg{man-identity}{psftp}{1}{2004-03-24}{PuTTY tool suite}{PuTTY tool suite} \H{psftp-manpage} Man page for PSFTP \S{psftp-manpage-name} NAME \cw{psftp} \- interactive SFTP (secure file transfer protocol) client \S{psftp-manpage-synopsis} SYNOPSIS \c psftp [options] [user@]host \e bbbbb iiiiiii iiiib iiii \S{psftp-manpage-description} DESCRIPTION \cw{psftp} is an interactive text-based client for the SSH-based SFTP (secure file transfer) protocol. \S{psftp-manpage-options} OPTIONS The command-line options supported by \cw{psftp} are: \dt \cw{-V} \dd Show version information and exit. \dt \cw{-pgpfp} \dd Display the fingerprints of the PuTTY PGP Master Keys and exit, to aid in verifying new files released by the PuTTY team. \dt \cw{-b} \e{batchfile} \dd Use specified batchfile. \dt \cw{-bc} \dd Output batchfile commands. \dt \cw{-be} \dd Don't stop batchfile processing on errors. \dt \cw{-v} \dd Show verbose messages. \dt \cw{-load} \e{session} \dd Load settings from saved session. \dt \cw{-P} \e{port} \dd Connect to port \e{port}. \dt \cw{\-proxycmd} \e{command} \dd Instead of making a TCP connection, use \e{command} as a proxy; network traffic will be redirected to the standard input and output of \e{command}. \e{command} must be a single word, so is likely to need quoting by the shell. \lcont{ The special strings \cw{%host} and \cw{%port} in \e{command} will be replaced by the hostname and port number you want to connect to; to get a literal \c{%} sign, enter \c{%%}. Backslash escapes are also supported, such as sequences like \c{\\n} being replaced by a literal newline; to get a literal backslash, enter \c{\\\\}. (Further escaping may be required by the shell.) (See the main PuTTY manual for full details of the supported \cw{%}- and backslash-delimited tokens, although most of them are probably not very useful in this context.) } \dt \cw{-l} \e{user} \dd Set remote username to \e{user}. \dt \cw{-batch} \dd Disable interactive prompts. \dt \cw{-no-sanitise-stderr} \dd By default, PSFTP will filter control characters from the standard error channel from the server, to prevent remote processes sending confusing escape sequences. This option forces the standard error channel to not be filtered. \dt \cw{-pw} \e{password} \dd Set remote password to \e{password}. \e{CAUTION:} this will likely make the password visible to other users of the local machine (via commands such as \q{\c{w}}). \dt \cw{-1} \dd Force use of SSH protocol version 1. \dt \cw{-2} \dd Force use of SSH protocol version 2. \dt \cw{-ssh-connection} \dd Force use of the \q{bare \cw{ssh-connection}} protocol. This is only likely to be useful when connecting to a \e{psusan(1)} server, most likely with an absolute path to a Unix-domain socket in place of \e{host}. \dt \cw{-ssh} \dd Force use of the SSH protocol. (This is usually not needed; it's only likely to be useful if you need to override some other configuration of the \q{bare \cw{ssh-connection}} protocol.) \dt \cw{-4}, \cw{-6} \dd Force use of IPv4 or IPv6 for network connections. \dt \cw{-C} \dd Enable SSH compression. \dt \cw{-i} \e{keyfile} \dd Private key file for user authentication. For SSH-2 keys, this key file must be in PuTTY's PPK format, not OpenSSH's format or anyone else's. \lcont{ If you are using an authentication agent, you can also specify a \e{public} key here (in RFC 4716 or OpenSSH format), to identify which of the agent's keys to use. } \dt \cw{\-noagent} \dd Don't try to use an authentication agent. \dt \cw{\-agent} \dd Allow use of an authentication agent. (This option is only necessary to override a setting in a saved session.) \dt \cw{\-no\-trivial\-auth} \dd Disconnect from any SSH server which accepts authentication without ever having asked for any kind of password or signature or token. (You might want to enable this for a server you always expect to challenge you, for instance to ensure you don't accidentally type your key file's passphrase into a compromised server spoofing PSFTP's passphrase prompt.) \dt \cw{\-hostkey} \e{key} \dd Specify an acceptable host public key. This option may be specified multiple times; each key can be either a fingerprint (\cw{SHA256:AbCdE...}, \cw{99:aa:bb:...}, etc) or a base64-encoded blob in OpenSSH's one-line format. \lcont{ Specifying this option overrides automated host key management; \e{only} the key(s) specified on the command-line will be accepted (unless a saved session also overrides host keys, in which case those will be added to), and the host key cache will not be written. } \dt \cw{\-sshlog} \e{logfile} \dt \cw{\-sshrawlog} \e{logfile} \dd These options make \cw{psftp} log protocol details to a file. (Some of these may be sensitive, although by default an effort is made to suppress obvious passwords.) \lcont{ \cw{\-sshlog} logs decoded SSH packets and other events (those that \cw{\-v} would print). \cw{\-sshrawlog} additionally logs the raw encrypted packet data. } \dt \cw{\-logoverwrite} \dd If PSFTP is configured to write to a log file that already exists, discard the existing file. \dt \cw{\-logappend} \dd If PSFTP is configured to write to a log file that already exists, append new log data to the existing file. \S{psftp-manpage-commands} COMMANDS For a list of commands available inside \cw{psftp}, type \cw{help} at the \cw{psftp>} prompt. \S{psftp-manpage-more-information} MORE INFORMATION For more information on \cw{psftp} it's probably best to go and look at the manual on the PuTTY web page: \cw{https://www.chiark.greenend.org.uk/~sgtatham/putty/} \S{psftp-manpage-bugs} BUGS This man page isn't terribly complete. See the above web link for better documentation. putty-0.76/doc/man-psocks.but0000644000175000017500000000551714072266310013121 00000000000000\cfg{man-identity}{psocks}{1}{2021-04-08}{PuTTY tool suite}{PuTTY tool suite} \H{psocks-manpage} Man page for \cw{psocks} \S{psocks-manpage-name} NAME \cw{psocks} \- simple SOCKS proxy server \S{psocks-manpage-synopsis} SYNOPSIS \c psocks [ -d ] [ -f | -p pipe-cmd ] [ -g ] [ port-number ] \e bbbbbb bb bb bb iiiiiiii bb iiiiiiiiiii \S{psocks-manpage-description} DESCRIPTION \cw{psocks} is a simple SOCKS4/5 proxy server. It supports proxying IPv4 and IPv6 connections. It does not support requiring authentication of its clients. \cw{psocks} can be used together with an SSH client such as \cw{putty(1)} to implement a reverse dynamic SSH tunnel. It can also be used for network protocol debugging, as it can record all the traffic passing through it in various ways. By default, \cw{psocks} listens to connections from localhost only, on TCP port 1080. A different \e{port-number} can optionally be supplied, and with \cw{-g} it will listen to connections from any host. \cw{psocks} will emit log messages about connections it receives on standard error. With \cw{-d}, it will log the contents of those connections too. \S{psocks-manpage-options} OPTIONS The command-line options supported by \cw{psocks} are: \dt \cw{-g} \dd Accept connections from anywhere. By default, \cw{psocks} only accepts connections on the loopback interface. \dt \cw{--exec} \e{command} \dd \cw{psocks} will run the provided command as a subprocess. When the subprocess terminates, \cw{psocks} will terminate as well. \lcont{ All arguments on the \cw{psocks} command line after \cw{--exec} will be treated as part of the command to run, even if they look like other valid \cw{psocks} options. } \dt \cw{-d} \dd Log all traffic to standard error, in a more or less human-readable form (in addition to messages about connections being opened and closed, which are always logged). \dt \cw{-f} \dd Record all traffic to files. For every incoming connection, two files are created, \cw{sockout.NNNN} and \cw{sockin.NNNN}, where \e{NNNN} is a decimal index starting at 0 identifying the proxied connection. These record, respectively, traffic from the SOCKS client, and from the server it connected to through the proxy. \dt \cw{-p} \e{pipe-cmd} \dd Pipe all traffic to a command. For every incoming connection, \e{pipe-cmd} is invoked twice: \lcont{ \c pipe-cmd out N \e iiiiiiii bbb i \c pipe-cmd in N \e iiiiiiii bb i Each command will run for the direction of a proxied connection, and have the connection's traffic piped into it, similar to \cw{-f}. } \S{psocks-manpage-examples} EXAMPLES In combination with the \e{plink(1)} SSH client, to set up a reverse dynamic SSH tunnel, in which the remote listening port 1080 on remote host \cw{myhost} acts as a SOCKS server giving access to your local network: \c psocks 12345 --exec plink -R 1080:localhost:12345 user@myhost putty-0.76/doc/man-psusan.but0000644000175000017500000003713114072266310013125 00000000000000\cfg{man-identity}{psusan}{1}{2020-12-13}{PuTTY tool suite}{PuTTY tool suite} \H{psusan-manpage} Man page for \cw{psusan} \S{psusan-manpage-name} NAME \cw{psusan} \- pseudo-SSH for untappable, separately authenticated networks \S{psusan-manpage-synopsis} SYNOPSIS \c psusan [ options ] \e bbbbbb iiiiiii \S{psusan-manpage-description} DESCRIPTION \cw{psusan} is a server program that behaves like the innermost \q{connection} layer of an SSH session, without the two outer security layers of encryption and authentication. It provides all the post-authentication features of an SSH connection: \b choosing whether to run an interactive terminal session or a single specified command \b multiple terminal sessions at once (or a mixture of those and specified commands) \b SFTP file transfer \b all the standard SSH port-forwarding options \b X11 forwarding \b SSH agent forwarding The catch is that, because it lacks the outer layers of SSH, you have to run it over some kind of data channel that is already authenticated as the right user, and that is already protected to your satisfaction against eavesdropping and session hijacking. A good rule of thumb is that any channel that you were prepared to run a \e{bare} shell session over, you can run \cw{psusan} over instead, which adds all the above conveniences without changing the security properties. The protocol that \cw{psusan} speaks is also spoken by PuTTY, Plink, PSCP, and PSFTP, if you select the protocol type \q{Bare ssh-connection} or the command-line option \cw{-ssh-connection} and specify the absolute path to the appropriate Unix-domain socket in place of a hostname. \S{psusan-manpage-examples} EXAMPLES The idea of a secure, pre-authenticated data channel seems strange to people thinking about \e{network} connections. But there are lots of examples within the context of a single Unix system, and that's where \cw{psusan} is typically useful. \S2{psusan-manpage-examples-docker} Docker A good example is the console or standard I/O channel leading into a container or virtualisation system. Docker is a familiar example. If you want to start a Docker container and run a shell directly within it, you might say something like \c docker run -i -t some:image \e iiiiiiiiii which will allow you to run a single shell session inside the container, in the same terminal you started Docker from. Suppose that you'd prefer to run \e{multiple} shell sessions in the same container at once (perhaps so that one of them can use debugging tools to poke at what another is doing). And perhaps inside that container you're going to run a program that you don't trust with full access to your network, but are prepared to let it make one or two specific network connections of the kind you could set up with an SSH port forwarding. In that case, you could remove the \cw{-t} option from that Docker command line (which means \q{allocate a terminal device}), and tell it to run \cw{psusan} inside the container: \c docker run -i some:image /some/path/to/psusan \e iiiiiiiiii iiiiiiiiiiii (Of course, you'll need to ensure that \cw{psusan} is installed somewhere inside the container image.) If you do that from a shell command line, you'll see a banner line looking something like this: \c SSHCONNECTION@putty.projects.tartarus.org-2.0-PSUSAN_Release_0.75 which isn't particularly helpful except that it tells you that \cw{psusan} has started up successfully. To talk to this server \e{usefully}, you can set up a PuTTY saved session as follows: \b Set the protocol to \q{Bare ssh-connection} (the \cw{psusan} protocol). \b Write \e{something} in the hostname box. It will appear in PuTTY's window title (if you run GUI PuTTY), so you might want to write something that will remind you what kind of window it is. If you have no opinion, something generic like \cq{dummy} will do. \b In the \q{Proxy} configuration panel, set the proxy type to \q{Local}, and enter the above \cq{docker run} command in the \q{Telnet command, or local proxy command} edit box. \b In the \q{SSH} configuration panel, you will very likely want to turn on connection sharing. (See below.) This arranges that when PuTTY starts up, it will run the Docker command as shown above in place of making a network connection, and talk to that command using the \cw{psusan} SSH-like protocol. The effect is that you will still get a shell session in the context of a Docker container. But this time, it's got all the SSH amenities. If you also turn on connection sharing in the \q{SSH} configuration panel, then the \q{Duplicate Session} option will get you a second shell in the \e{same} Docker container (instead of a primary shell in a separate instance). You can transfer files in and out of the container while it's running using PSCP or PSFTP; you can forward network ports, X11 programs, and/or an SSH agent to the container. Of course, another way to do all of this would be to run the \e{full} SSH protocol over the same channel. This involves more setup: you have to invent an SSH host key for the container, accept it in the client, and deal with it being left behind in your client's host key cache when the container is discarded. And you have to set up some login details in the container: either configure a password, and type it in the client, or copy in the public half of some SSH key you already had. And all this inconvenience is \e{unnecessary}, because these are all precautions you need to take when the connection between two systems is going over a hostile network. In this case, it's only going over a kernel IPC channel that's guaranteed to go to the right place, so those safety precautions are redundant, and they only add awkwardness. \S2{psusan-manpage-examples-uml} User-mode Linux User-mode Linux is another container type you can talk to in the same way. Here's a small worked example. The \e{easiest} way to run UML is to use its \cq{hostfs} file system type to give the guest kernel access to the same virtual filesystem as you have on the host. For example, a command line like this gets you a shell prompt inside a UML instance sharing your existing filesystem: \c linux mem=512M rootfstype=hostfs rootflags=/ rw init=/bin/bash If you run this at a command line (assuming you have a UML kernel available on your path under the name \cq{linux}), then you should see a lot of kernel startup messages, followed by a shell prompt along the lines of \c root@(none):/# To convert this into a \cw{psusan}-based UML session, we need to adjust the command line so that instead of running \cw{bash} it runs \cw{psusan}. But running \cw{psusan} directly isn't quite enough, because \cw{psusan} will depend on a small amount of setup, such as having \cw{/proc} mounted. So instead, we set the init process to a shell script which will do the necessary setup and \e{then} invoke \cw{psusan}. Also, running \cw{psusan} directly over the UML console device is a bad idea, because then the \cw{psusan} binary protocol will be mixed with textual console messages. So a better plan is to redirect UML's console to the standard error of the \cw{linux} process, and map its standard input and output to a serial port. So the replacement UML command line might look something like this: \c linux mem=512M rootfstype=hostfs rootflags=/ rw \ \c con=fd:2,fd:2 ssl0=fd:0,fd:1 init=/some/path/to/uml-psusan.sh \e iiiiiiiiiiiiiiiiiiiiiiiiiii And the setup script \cw{uml-psusan.sh} might look like this: \c #!/bin/bash \c # Set up vital pseudo-filesystems \e iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii \c mount -t proc none /proc \c mount -t devpts none /dev/pts \c # Redirect I/O to the serial port, but stderr to the console \e iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii \c exec 0<>/dev/ttyS0 1>&0 2>/dev/console \c # Set the serial port into raw mode, to run a binary protocol \e iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii \c stty raw -echo \c # Choose what shell you want to run inside psusan \e iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii \c export SHELL=/bin/bash \c # And now run psusan over the serial port \e iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii \c exec /home/simon/src/putty/misc/psusan Now set up a PuTTY saved session as in the Docker example above, using that \cw{linux} command as the local proxy command, and you'll have a PuTTY session that starts up a clean UML instance when you run it, and (if you enabled connection sharing) further instances of the same session will connect to the same instance again. \S2{psusan-manpage-examples-wsl} Windows Subsystem for Linux On Windows, the default way to use WSL is to run the \cw{wsl} program, or one of its aliases, in a Windows console, either by launching it from an existing command prompt, or by using a shortcut that opens it in a fresh console. This gives you a Linux terminal environment, but in a Windows console window. If you'd prefer to interact with the same environment using PuTTY as the terminal (for example, if you prefer PuTTY's mouse shortcuts for copy and paste), you can set it up by installing \cw{psusan} in the Linux environment, and then setting up a PuTTY saved session that talks to it. A nice way to do this is to use the name of the WSL distribution as the \q{host name}: \b set the local proxy command to \cq{wsl -d %host /usr/local/bin/psusan} (or wherever you installed \cw{psusan} in the Linux system) \b enter the name of a particular WSL distribution in the host name box. (For example, if you installed WSL Debian in the standard way from the Windows store, this will just be \q{Debian}.) \b set the protocol to \q{Bare ssh-connection}, as usual. Like all the other examples here, this also permits you to forward ports in and out of the WSL environment (e.g. expose a WSL2 network service through the hypervisor's internal NAT), forward Pageant into it, and so on. \S2{psusan-manpage-examples-schroot} \cw{schroot} Another example of a container-like environment is the alternative filesystem layout set up by \cw{schroot}(\e{1}). \cw{schroot} is another program that defaults to running an interactive shell session in the terminal you launched it from. But again, you can get a \cw{psusan} connection into the \cw{schroot} environment by setting up a PuTTY saved session whose local proxy command is along the lines of \c schroot -c chroot-name /some/path/to/psusan \e iiiiiiiiiii iiiiiiiiiiii Depending on how much of the chroot environment is copied from your main one, you might find this makes it easier to (for example) run X11 programs inside the chroot that open windows on your main X display, or transfer files in and out of the chroot. \S2{psusan-manpage-examples-namespace} Between network namespaces If you've set up multiple network namespaces on a Linux system, with different TCP/IP configurations, then \cw{psusan} can be a convenient unprivileged-user gateway between them, if you run it as a non-root user in the non-default one of your namespaces, listening for connections on a Unix-domain socket. If you do that, then it gives you convenient control over which of your outgoing network connections use which TCP/IP configuration: you can use PuTTY to run a shell session in the context of the other namespace if you want to run commands like \cw{ping}, or you can set up individual port forwardings or even a SOCKS server so that processes running in one namespace can send their network connections via the other one. For this application, it's probably most convenient to use the \cw{--listen} option in \cw{psusan}, which makes it run as a server and listen for connections on a Unix-domain socket. Then you can enter that socket name in PuTTY's host name configuration field (and also still select the \q{Bare ssh-connection} protocol option), to connect to that socket as if it were an SSH client. Provided the Unix-domain socket is inside a directory that only the right user has access to, this will ensure that authentication is done implicitly by the Linux kernel. \S2{psusan-manpage-examples-userv} Between user ids, via GNU userv If you use multiple user ids on the same machine, say for purposes of privilege separation (running some less-trusted program with limited abilities to access all your stuff), then you probably have a \q{default} or most privileged account where you run your main login session, and sometimes need to run a shell in another account. \cw{psusan} can be used as an access channel between the accounts, using GNU \cw{userv}(\e{1}) as the transport. In the account you want to access, write a \cw{userv} configuration stanza along the lines of \c if (glob service psusan & glob calling-user my-main-account-name) \e iiiiiiiiiiiiiiiiiiii \c reset \c execute /some/path/to/psusan \e iiiiiiiiiiii \c fi This gives your main account the right to run the command \c userv my-sub-account-name psusan \e iiiiiiiiiiiiiiiiiii and you can configure that command name as a PuTTY local proxy command, in the same way as most of the previous examples. Of course, there are plenty of ways already to access one local account from another, such as \cw{sudo}. One advantage of doing it this way is that you don't need the system administrator to intervene when you want to change the access controls (e.g. change which of your accounts have access to another): as long as you have \e{some} means of getting into each account in the first place, and \cw{userv} is installed, you can make further configuration changes without having to bother root about it. Another advantage is that it might make file transfer between the accounts easier. If you're the kind of person who keeps your home directories private, then it's awkward to copy a file from one of your accounts to another just by using the \cw{cp} command, because there's nowhere convenient that you can leave it in one account where the other one can read it. But with \cw{psusan} over \cw{userv}, you don't need any shared piece of filesystem: you can \cw{scp} files back and forth without any difficulty. \S{psusan-manpage-options} OPTIONS The command-line options supported by \cw{psusan} are: \dt \cw{--listen} \e{unix-socket-name} \dd Run \cw{psusan} in listening mode. \e{unix-socket-name} is the pathname of a Unix-domain socket to listen on. You should ensure that this pathname is inside a directory whose read and exec permissions are restricted to only the user(s) you want to be able to access the environment that \cw{psusan} is running in. \lcont{ The listening socket has to be a Unix-domain socket. \cw{psusan} does not provide an option to run over TCP/IP, because the unauthenticated nature of the protocol would make it inherently insecure. } \dt \cw{--listen-once} \dd In listening mode, this option causes \cw{psusan} to listen for only one connection, and exit immediately after that connection terminates. \dt \cw{--sessiondir} \e{pathname} \dd This option sets the directory that shell sessions and subprocesses will start in. By default it is \cw{psusan}'s own working directory, but in some situations it's easier to change it with a command-line option than by wrapping \cw{psusan} in a script that changes directory before starting it. \dt \cw{-v}, \cw{--verbose} \dd This option causes \cw{psusan} to print verbose log messages on its standard error. This is probably most useful in listening mode. \dt \cw{\-sshlog} \e{logfile} \dt \cw{\-sshrawlog} \e{logfile} \dd These options cause \cw{psusan} to log protocol details to a file, similarly to the logging options in PuTTY and Plink. \lcont{ \cw{\-sshlog} logs decoded SSH packets and other events (those that \cw{\-v} would print). \cw{\-sshrawlog} additionally logs the raw wire data, including the outer packet format and the initial greetings. } putty-0.76/doc/man-pterm.but0000644000175000017500000006000614072266310012740 00000000000000\cfg{man-identity}{pterm}{1}{2004-03-24}{PuTTY tool suite}{PuTTY tool suite} \H{pterm-manpage} Man page for pterm \S{pterm-manpage-name} NAME pterm \- yet another X terminal emulator \S{pterm-manpage-synopsis} SYNOPSIS \c pterm [ options ] \e bbbbb iiiiiii \S{pterm-manpage-description} DESCRIPTION \cw{pterm} is a terminal emulator for X. It is based on a port of the terminal emulation engine in the Windows SSH client PuTTY. \S{pterm-manpage-options} OPTIONS The command-line options supported by \cw{pterm} are: \dt \cw{\-e} \e{command} [ \e{arguments} ] \dd Specify a command to be executed in the new terminal. Everything on the command line after this option will be passed straight to the \cw{execvp} system call; so if you need the command to redirect its input or output, you will have to use \cw{sh}: \lcont{ \c pterm -e sh -c 'mycommand < inputfile' } \dt \cw{\-\-display} \e{display\-name} \dd Specify the X display on which to open \cw{pterm}. (Note this option has a double minus sign, even though none of the others do. This is because this option is supplied automatically by GTK. Sorry.) \dt \cw{\-name} \e{name} \dd Specify the name under which \cw{pterm} looks up X resources. Normally it will look them up as (for example) \cw{pterm.Font}. If you specify \q{\cw{\-name xyz}}, it will look them up as \cw{xyz.Font} instead. This allows you to set up several different sets of defaults and choose between them. \dt \cw{\-fn} \e{font-name} \dd Specify the font to use for normal text displayed in the terminal. For example, \cw{\-fn\_fixed}, \cw{\-fn\_"Monospace\_12"}. \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. \dt \cw{\-sl} \e{lines} \dd Specify the number of lines of scrollback to save off the top of the terminal. \dt \cw{\-fg} \e{colour} \dd Specify the foreground colour to use for normal text. \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. \dt \cw{\-cbg} \e{colour} \dd Specify the background colour to use for text covered by the cursor. In other words, this is the main colour of the cursor. \dt \cw{\-title} \e{title} \dd Specify the initial title of the terminal window. (This can be changed under control of the server.) \dt \cw{\-ut\-} or \cw{+ut} \dd Tells \cw{pterm} not to record your login in the \cw{utmp}, \cw{wtmp} and \cw{lastlog} system log files; so you will not show up on \cw{finger} or \cw{who} listings, for example. \dt \cw{\-ut} \dd Tells \cw{pterm} to record your login in \cw{utmp}, \cw{wtmp} and \cw{lastlog}: this is the opposite of \cw{\-ut\-}. This is the default option: you will probably only need to specify it explicitly if you have changed the default using the \cw{StampUtmp} resource. \dt \cw{\-ls\-} or \cw{+ls} \dd Tells \cw{pterm} not to execute your shell as a login shell. \dt \cw{\-ls} \dd Tells \cw{pterm} to execute your shell as a login shell: this is the opposite of \cw{\-ls\-}. This is the default option: you will probably only need to specify it explicitly if you have changed the default using the \cw{LoginShell} resource. \dt \cw{\-sb\-} or \cw{+sb} \dd Tells \cw{pterm} not to display a scroll bar. \dt \cw{\-sb} \dd Tells \cw{pterm} to display a scroll bar: this is the opposite of \cw{\-sb\-}. This is the default option: you will probably only need to specify it explicitly if you have changed the default using the \cw{ScrollBar} resource. \dt \cw{\-log} \e{logfile}, \cw{\-sessionlog} \e{logfile} \dd This option makes \cw{pterm} log all the terminal output to a file as well as displaying it in the terminal. \dt \cw{\-cs} \e{charset} \dd This option specifies the character set in which \cw{pterm} should assume the session is operating. This character set will be used to interpret all the data received from the session, and all input you type or paste into \cw{pterm} will be converted into this character set before being sent to the session. \lcont{ Any character set name which is valid in a MIME header (and supported by \cw{pterm}) should be valid here (examples are \q{\cw{ISO-8859-1}}, \q{\cw{windows-1252}} or \q{\cw{UTF-8}}). Also, any character encoding which is valid in an X logical font description should be valid (\q{\cw{ibm-cp437}}, for example). \cw{pterm}'s default behaviour is to use the same character encoding as its primary font. If you supply a Unicode (\cw{iso10646-1}) font, it will default to the UTF-8 character set. Character set names are case-insensitive. } \dt \cw{\-nethack} \dd Tells \cw{pterm} to enable NetHack keypad mode, in which the numeric keypad generates the NetHack \c{hjklyubn} direction keys. This enables you to play NetHack with the numeric keypad without having to use the NetHack \c{number_pad} option (which requires you to press \q{\cw{n}} before any repeat count). So you can move with the numeric keypad, and enter repeat counts with the normal number keys. \dt \cw{\-xrm} \e{resource-string} \dd This option specifies an X resource string. Useful for setting resources which do not have their own command-line options. For example: \lcont{ \c pterm -xrm 'ScrollbarOnLeft: 1' } \dt \cw{\-help}, \cw{\-\-help} \dd Display a message summarizing the available options. \dt \cw{\-pgpfp} \dd Display the fingerprints of the PuTTY PGP Master Keys, to aid in verifying new files released by the PuTTY team. \S{pterm-manpage-x-resources} X RESOURCES \cw{pterm} can be more completely configured by means of X resources. All of these resources are of the form \cw{pterm.FOO} for some \cw{FOO}; you can make \cw{pterm} look them up under another name, such as \cw{xyz.FOO}, by specifying the command-line option \q{\cw{\-name xyz}}. \dt \cw{pterm.CloseOnExit} \dd This option should be set to 0, 1 or 2; the default is 2. It controls what \cw{pterm} does when the process running inside it terminates. When set to 2 (the default), \cw{pterm} will close its window as soon as the process inside it terminates. When set to 0, \cw{pterm} will print the process's exit status, and the window will remain present until a key is pressed (allowing you to inspect the scrollback, and copy and paste text out of it). \lcont{ When this setting is set to 1, \cw{pterm} will close immediately if the process exits cleanly (with an exit status of zero), but the window will stay around if the process exits with a non-zero code or on a signal. This enables you to see what went wrong if the process suffers an error, but not to have to bother closing the window in normal circumstances. } \dt \cw{pterm.WarnOnClose} \dd This option should be set to either 0 or 1; the default is 1. When set to 1, \cw{pterm} will ask for confirmation before closing its window when you press the close button. \dt \cw{pterm.TerminalType} \dd This controls the value set in the \cw{TERM} environment variable inside the new terminal. The default is \q{\cw{xterm}}. \dt \cw{pterm.BackspaceIsDelete} \dd This option should be set to either 0 or 1; the default is 1. When set to 0, the ordinary Backspace key generates the Backspace character (\cw{^H}); when set to 1, it generates the Delete character (\cw{^?}). Whichever one you set, the terminal device inside \cw{pterm} will be set up to expect it. \dt \cw{pterm.RXVTHomeEnd} \dd This option should be set to either 0 or 1; the default is 0. When it is set to 1, the Home and End keys generate the control sequences they would generate in the \cw{rxvt} terminal emulator, instead of the more usual ones generated by other emulators. \dt \cw{pterm.LinuxFunctionKeys} \dd This option can be set to any number between 0 and 5 inclusive; the default is 0. The modes vary the control sequences sent by the function keys; for more complete documentation, it is probably simplest to try each option in \q{\cw{pterm \-e cat}}, and press the keys to see what they generate. \dt \cw{pterm.NoApplicationKeys} \dd This option should be set to either 0 or 1; the default is 0. When set to 1, it stops the server from ever switching the numeric keypad into application mode (where the keys send function-key-like sequences instead of numbers or arrow keys). You probably only need this if some application is making a nuisance of itself. \dt \cw{pterm.NoApplicationCursors} \dd This option should be set to either 0 or 1; the default is 0. When set to 1, it stops the server from ever switching the cursor keys into application mode (where the keys send slightly different sequences). You probably only need this if some application is making a nuisance of itself. \dt \cw{pterm.NoMouseReporting} \dd This option should be set to either 0 or 1; the default is 0. When set to 1, it stops the server from ever enabling mouse reporting mode (where mouse clicks are sent to the application instead of controlling cut and paste). \dt \cw{pterm.NoRemoteResize} \dd This option should be set to either 0 or 1; the default is 0. When set to 1, it stops the server from being able to remotely control the size of the \cw{pterm} window. \dt \cw{pterm.NoAltScreen} \dd This option should be set to either 0 or 1; the default is 0. When set to 1, it stops the server from using the \q{alternate screen} terminal feature, which lets full-screen applications leave the screen exactly the way they found it. \dt \cw{pterm.NoRemoteWinTitle} \dd This option should be set to either 0 or 1; the default is 0. When set to 1, it stops the server from remotely controlling the title of the \cw{pterm} window. \dt \cw{pterm.NoRemoteQTitle} \dd This option should be set to either 0 or 1; the default is 1. When set to 1, it stops the server from remotely requesting the title of the \cw{pterm} window. \lcont{ This feature is a \e{POTENTIAL SECURITY HAZARD}. If a malicious application can write data to your terminal (for example, if you merely \cw{cat} a file owned by someone else on the server machine), it can change your window title (unless you have disabled this using the \cw{NoRemoteWinTitle} resource) and then use this service to have the new window title sent back to the server as if typed at the keyboard. This allows an attacker to fake keypresses and potentially cause your server-side applications to do things you didn't want. Therefore this feature is disabled by default, and we recommend you do not turn it on unless you \e{really} know what you are doing. } \dt \cw{pterm.NoDBackspace} \dd This option should be set to either 0 or 1; the default is 0. When set to 1, it disables the normal action of the Delete (\cw{^?}) character when sent from the server to the terminal, which is to move the cursor left by one space and erase the character now under it. \dt \cw{pterm.ApplicationCursorKeys} \dd This option should be set to either 0 or 1; the default is 0. When set to 1, the default initial state of the cursor keys are application mode (where the keys send function-key-like sequences instead of numbers or arrow keys). When set to 0, the default state is the normal one. \dt \cw{pterm.ApplicationKeypad} \dd This option should be set to either 0 or 1; the default is 0. When set to 1, the default initial state of the numeric keypad is application mode (where the keys send function-key-like sequences instead of numbers or arrow keys). When set to 0, the default state is the normal one. \dt \cw{pterm.NetHackKeypad} \dd This option should be set to either 0 or 1; the default is 0. When set to 1, the numeric keypad operates in NetHack mode. This is equivalent to the \cw{\-nethack} command-line option. \dt \cw{pterm.Answerback} \dd This option controls the string which the terminal sends in response to receiving the \cw{^E} character (\q{tell me about yourself}). By default this string is \q{\cw{PuTTY}}. \dt \cw{pterm.HideMousePtr} \dd This option should be set to either 0 or 1; the default is 0. When it is set to 1, the mouse pointer will disappear if it is over the \cw{pterm} window and you press a key. It will reappear as soon as you move it. \dt \cw{pterm.WindowBorder} \dd This option controls the number of pixels of space between the text in the \cw{pterm} window and the window frame. The default is 1. You can increase this value, but decreasing it to 0 is not recommended because it can cause the window manager's size hints to work incorrectly. \dt \cw{pterm.CurType} \dd This option should be set to either 0, 1 or 2; the default is 0. When set to 0, the text cursor displayed in the window is a rectangular block. When set to 1, the cursor is an underline; when set to 2, it is a vertical line. \dt \cw{pterm.BlinkCur} \dd This option should be set to either 0 or 1; the default is 0. When it is set to 1, the text cursor will blink when the window is active. \dt \cw{pterm.Beep} \dd This option should be set to either 0 or 2 (yes, 2); the default is 0. When it is set to 2, \cw{pterm} will respond to a bell character (\cw{^G}) by flashing the window instead of beeping. \dt \cw{pterm.BellOverload} \dd This option should be set to either 0 or 1; the default is 0. When it is set to 1, \cw{pterm} will watch out for large numbers of bells arriving in a short time and will temporarily disable the bell until they stop. The idea is that if you \cw{cat} a binary file, the frantic beeping will mostly be silenced by this feature and will not drive you crazy. \lcont{ The bell overload mode is activated by receiving N bells in time T; after a further time S without any bells, overload mode will turn itself off again. Bell overload mode is always deactivated by any keypress in the terminal. This means it can respond to large unexpected streams of data, but does not interfere with ordinary command-line activities that generate beeps (such as filename completion). } \dt \cw{pterm.BellOverloadN} \dd This option counts the number of bell characters which will activate bell overload if they are received within a length of time T. The default is 5. \dt \cw{pterm.BellOverloadT} \dd This option specifies the time period in which receiving N or more bells will activate bell overload mode. It is measured in microseconds, so (for example) set it to 1000000 for one second. The default is 2000000 (two seconds). \dt \cw{pterm.BellOverloadS} \dd This option specifies the time period of silence required to turn off bell overload mode. It is measured in microseconds, so (for example) set it to 1000000 for one second. The default is 5000000 (five seconds of silence). \dt \cw{pterm.ScrollbackLines} \dd This option specifies how many lines of scrollback to save above the visible terminal screen. The default is 200. This resource is equivalent to the \cw{\-sl} command-line option. \dt \cw{pterm.DECOriginMode} \dd This option should be set to either 0 or 1; the default is 0. It specifies the default state of DEC Origin Mode. (If you don't know what that means, you probably don't need to mess with it.) \dt \cw{pterm.AutoWrapMode} \dd This option should be set to either 0 or 1; the default is 1. It specifies the default state of auto wrap mode. When set to 1, very long lines will wrap over to the next line on the terminal; when set to 0, long lines will be squashed against the right-hand edge of the screen. \dt \cw{pterm.LFImpliesCR} \dd This option should be set to either 0 or 1; the default is 0. When set to 1, the terminal will return the cursor to the left side of the screen when it receives a line feed character. \dt \cw{pterm.WinTitle} \dd This resource is the same as the \cw{\-T} command-line option: it controls the initial title of the window. The default is \q{\cw{pterm}}. \dt \cw{pterm.TermWidth} \dd This resource is the same as the width part of the \cw{\-geometry} command-line option: it controls the number of columns of text in the window. The default is 80. \dt \cw{pterm.TermHeight} \dd This resource is the same as the width part of the \cw{\-geometry} command-line option: it controls the number of columns of text in the window. The defaults is 24. \dt \cw{pterm.Font} \dd This resource is the same as the \cw{\-fn} command-line option: it 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 \q{\cw{255,0,0}} and so on. \lcont{ Colours 0 and 1 specify the foreground colour and its bold equivalent (the \cw{\-fg} and \cw{\-bfg} command-line options). Colours 2 and 3 specify the background colour and its bold equivalent (the \cw{\-bg} and \cw{\-bbg} command-line options). Colours 4 and 5 specify the text and block colours used for the cursor (the \cw{\-cfg} and \cw{\-cbg} command-line options). Each even number from 6 to 20 inclusive specifies the colour to be used for one of the ANSI primary colour specifications (black, red, green, yellow, blue, magenta, cyan, white, in that order); the odd numbers from 7 to 21 inclusive specify the bold version of each colour, in the same order. The defaults are: \c pterm.Colour0: 187,187,187 \c pterm.Colour1: 255,255,255 \c pterm.Colour2: 0,0,0 \c pterm.Colour3: 85,85,85 \c pterm.Colour4: 0,0,0 \c pterm.Colour5: 0,255,0 \c pterm.Colour6: 0,0,0 \c pterm.Colour7: 85,85,85 \c pterm.Colour8: 187,0,0 \c pterm.Colour9: 255,85,85 \c pterm.Colour10: 0,187,0 \c pterm.Colour11: 85,255,85 \c pterm.Colour12: 187,187,0 \c pterm.Colour13: 255,255,85 \c pterm.Colour14: 0,0,187 \c pterm.Colour15: 85,85,255 \c pterm.Colour16: 187,0,187 \c pterm.Colour17: 255,85,255 \c pterm.Colour18: 0,187,187 \c pterm.Colour19: 85,255,255 \c pterm.Colour20: 187,187,187 \c pterm.Colour21: 255,255,255 } \dt \cw{pterm.RectSelect} \dd This option should be set to either 0 or 1; the default is 0. When set to 0, dragging the mouse over several lines selects to the end of each line and from the beginning of the next; when set to 1, dragging the mouse over several lines selects a rectangular region. In each case, holding down Alt while dragging gives the other behaviour. \dt \cw{pterm.MouseOverride} \dd This option should be set to either 0 or 1; the default is 1. When set to 1, if the application requests mouse tracking (so that mouse clicks are sent to it instead of doing selection), holding down Shift will revert the mouse to normal selection. When set to 0, mouse tracking completely disables selection. \dt \cw{pterm.Printer} \dd This option is unset by default. If you set it, then server-controlled printing is enabled: the server can send control sequences to request data to be sent to a printer. That data will be piped into the command you specify here; so you might want to set it to \q{\cw{lpr}}, for example, or \q{\cw{lpr \-Pmyprinter}}. \dt \cw{pterm.ScrollBar} \dd This option should be set to either 0 or 1; the default is 1. When set to 0, the scrollbar is hidden (although Shift-PageUp and Shift-PageDown still work). This is the same as the \cw{\-sb} command-line option. \dt \cw{pterm.ScrollbarOnLeft} \dd This option should be set to either 0 or 1; the default is 0. When set to 1, the scrollbar will be displayed on the left of the terminal instead of on the right. \dt \cw{pterm.ScrollOnKey} \dd This option should be set to either 0 or 1; the default is 0. When set to 1, any keypress causes the position of the scrollback to be reset to the very bottom. \dt \cw{pterm.ScrollOnDisp} \dd This option should be set to either 0 or 1; the default is 1. When set to 1, any activity in the display causes the position of the scrollback to be reset to the very bottom. \dt \cw{pterm.LineCodePage} \dd This option specifies the character set to be used for the session. This is the same as the \cw{\-cs} command-line option. \dt \cw{pterm.NoRemoteCharset} \dd This option disables the terminal's ability to change its character set when it receives escape sequences telling it to. You might need to do this to interoperate with programs which incorrectly change the character set to something they think is sensible. \dt \cw{pterm.BCE} \dd This option should be set to either 0 or 1; the default is 1. When set to 1, the various control sequences that erase parts of the terminal display will erase in whatever the current background colour is; when set to 0, they will erase in black always. \dt \cw{pterm.BlinkText} \dd This option should be set to either 0 or 1; the default is 0. When set to 1, text specified as blinking by the server will actually blink on and off; when set to 0, \cw{pterm} will use the less distracting approach of making the text's background colour bold. \dt \cw{pterm.StampUtmp} \dd This option should be set to either 0 or 1; the default is 1. When set to 1, \cw{pterm} will log the login in the various system log files. This resource is equivalent to the \cw{\-ut} command-line option. \dt \cw{pterm.LoginShell} \dd This option should be set to either 0 or 1; the default is 1. When set to 1, \cw{pterm} will execute your shell as a login shell. This resource is equivalent to the \cw{\-ls} command-line option. \S{pterm-manpage-bugs} BUGS Most of the X resources have silly names. (Historical reasons from PuTTY, mostly.) putty-0.76/doc/man-putty.but0000644000175000017500000002634214072266310013003 00000000000000\cfg{man-identity}{putty}{1}{2004-03-24}{PuTTY tool suite}{PuTTY tool suite} \H{putty-manpage} Man page for PuTTY \S{putty-manpage-name} NAME \cw{putty} - GUI SSH, Telnet, Rlogin, and SUPDUP client for X \S{putty-manpage-synopsis} SYNOPSIS \c putty [ options ] [ host ] \e bbbbb iiiiiii iiii \S{putty-manpage-description} DESCRIPTION \cw{putty} is a graphical SSH, Telnet, Rlogin, and SUPDUP client for X. It is a direct port of the Windows SSH client of the same name. \S{putty-manpage-options} OPTIONS The command-line options supported by \cw{putty} are: \dt \cw{\-\-display} \e{display\-name} \dd Specify the X display on which to open \cw{putty}. (Note this option has a double minus sign, even though none of the others do. This is because this option is supplied automatically by GTK. Sorry.) \dt \cw{\-fn} \e{font-name} \dd Specify the font to use for normal text displayed in the terminal. For example, \cw{\-fn\_fixed}, \cw{\-fn\_"Monospace\_12"}. \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. \dt \cw{\-sl} \e{lines} \dd Specify the number of lines of scrollback to save off the top of the terminal. \dt \cw{\-fg} \e{colour} \dd Specify the foreground colour to use for normal text. \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. \dt \cw{\-cbg} \e{colour} \dd Specify the background colour to use for text covered by the cursor. In other words, this is the main colour of the cursor. \dt \cw{\-title} \e{title} \dd Specify the initial title of the terminal window. (This can be changed under control of the server.) \dt \cw{\-sb\-} or \cw{+sb} \dd Tells \cw{putty} not to display a scroll bar. \dt \cw{\-sb} \dd Tells \cw{putty} to display a scroll bar: this is the opposite of \cw{\-sb\-}. This is the default option: you will probably only need to specify it explicitly if you have changed the default using the \cw{ScrollBar} resource. \dt \cw{\-log} \e{logfile}, \cw{\-sessionlog} \e{logfile} \dd This option makes \cw{putty} log all the terminal output to a file as well as displaying it in the terminal. \dt \cw{\-sshlog} \e{logfile} \dt \cw{\-sshrawlog} \e{logfile} \dd For SSH connections, these options make \cw{putty} log protocol details to a file. (Some of these may be sensitive, although by default an effort is made to suppress obvious passwords.) \lcont{ \cw{\-sshlog} logs decoded SSH packets and other events (those that \cw{\-v} would print). \cw{\-sshrawlog} additionally logs the raw encrypted packet data. } \dt \cw{\-logoverwrite} \dd If \cw{putty} is configured to write to a log file that already exists, discard the existing file. \dt \cw{\-logappend} \dd If \cw{putty} is configured to write to a log file that already exists, append new log data to the existing file. \dt \cw{\-cs} \e{charset} \dd This option specifies the character set in which \cw{putty} should assume the session is operating. This character set will be used to interpret all the data received from the session, and all input you type or paste into \cw{putty} will be converted into this character set before being sent to the session. \lcont{ Any character set name which is valid in a MIME header (and supported by \cw{putty}) should be valid here (examples are \q{\cw{ISO-8859-1}}, \q{\cw{windows-1252}} or \q{\cw{UTF-8}}). Also, any character encoding which is valid in an X logical font description should be valid (\q{\cw{ibm-cp437}}, for example). \cw{putty}'s default behaviour is to use the same character encoding as its primary font. If you supply a Unicode (\cw{iso10646-1}) font, it will default to the UTF-8 character set. Character set names are case-insensitive. } \dt \cw{\-nethack} \dd Tells \cw{putty} to enable NetHack keypad mode, in which the numeric keypad generates the NetHack \c{hjklyubn} direction keys. This enables you to play NetHack with the numeric keypad without having to use the NetHack \c{number_pad} option (which requires you to press \q{\cw{n}} before any repeat count). So you can move with the numeric keypad, and enter repeat counts with the normal number keys. \dt \cw{\-help}, \cw{\-\-help} \dd Display a message summarizing the available options. \dt \cw{\-pgpfp} \dd Display the fingerprints of the PuTTY PGP Master Keys, to aid in verifying new files released by the PuTTY team. \dt \cw{\-load} \e{session} \dd Load a saved session by name. This allows you to run a saved session straight from the command line without having to go through the configuration box first. \dt \cw{\-ssh}, \cw{\-telnet}, \cw{\-rlogin}, \cw{\-supdup}, \cw{\-raw}, \cw{-ssh-connection}, \cw{\-serial} \dd Select the protocol \cw{putty} will use to make the connection. \dt \cw{\-proxycmd} \e{command} \dd Instead of making a TCP connection, use \e{command} as a proxy; network traffic will be redirected to the standard input and output of \e{command}. \e{command} must be a single word, so is likely to need quoting by the shell. \lcont{ The special strings \cw{%host} and \cw{%port} in \e{command} will be replaced by the hostname and port number you want to connect to; to get a literal \c{%} sign, enter \c{%%}. Backslash escapes are also supported, such as sequences like \c{\\n} being replaced by a literal newline; to get a literal backslash, enter \c{\\\\}. (Further escaping may be required by the shell.) (See the main PuTTY manual for full details of the supported \cw{%}- and backslash-delimited tokens, although most of them are probably not very useful in this context.) } \dt \cw{\-l} \e{username} \dd Specify the username to use when logging in to the server. \dt \cw{\-L} \cw{[}\e{srcaddr}\cw{:]}\e{srcport}\cw{:}\e{desthost}\cw{:}\e{destport} \dd Set up a local port forwarding: listen on \e{srcport} (or \e{srcaddr}:\e{srcport} if specified), and forward any connections over the SSH connection to the destination address \e{desthost}:\e{destport}. Only works in SSH. \dt \cw{\-R} \cw{[}\e{srcaddr}\cw{:]}\e{srcport}\cw{:}\e{desthost}\cw{:}\e{destport} \dd Set up a remote port forwarding: ask the SSH server to listen on \e{srcport} (or \e{srcaddr}:\e{srcport} if specified), and to forward any connections back over the SSH connection where the client will pass them on to the destination address \e{desthost}:\e{destport}. Only works in SSH. \dt \cw{\-D} [\e{srcaddr}:]\e{srcport} \dd Set up dynamic port forwarding. The client listens on \e{srcport} (or \e{srcaddr}:\e{srcport} if specified), and implements a SOCKS server. So you can point SOCKS-aware applications at this port and they will automatically use the SSH connection to tunnel all their connections. Only works in SSH. \dt \cw{\-P} \e{port} \dd Specify the port to connect to the server on. \dt \cw{\-A}, \cw{\-a} \dd Enable (\cw{\-A}) or disable (\cw{\-a}) SSH agent forwarding. Currently this only works with OpenSSH and SSH-1. \dt \cw{\-X}, \cw{\-x} \dd Enable (\cw{\-X}) or disable (\cw{\-x}) X11 forwarding. \dt \cw{\-T}, \cw{\-t} \dd Enable (\cw{\-t}) or disable (\cw{\-T}) the allocation of a pseudo-terminal at the server end. \dt \cw{\-C} \dd Enable zlib-style compression on the connection. \dt \cw{\-1}, \cw{\-2} \dd Select SSH protocol version 1 or 2. \dt \cw{-4}, \cw{-6} \dd Force use of IPv4 or IPv6 for network connections. \dt \cw{\-i} \e{keyfile} \dd Private key file for user authentication. For SSH-2 keys, this key file must be in PuTTY's PPK format, not OpenSSH's format or anyone else's. \lcont{ If you are using an authentication agent, you can also specify a \e{public} key here (in RFC 4716 or OpenSSH format), to identify which of the agent's keys to use. } \dt \cw{\-noagent} \dd Don't try to use an authentication agent for local authentication. (This doesn't affect agent forwarding.) \dt \cw{\-agent} \dd Allow use of an authentication agent. (This option is only necessary to override a setting in a saved session.) \dt \cw{\-no\-trivial\-auth} \dd Disconnect from any SSH server which accepts authentication without ever having asked for any kind of password or signature or token. (You might want to enable this for a server you always expect to challenge you, for instance to ensure you don't accidentally type your key file's passphrase into a compromised server spoofing PuTTY's passphrase prompt.) \dt \cw{\-hostkey} \e{key} \dd Specify an acceptable host public key. This option may be specified multiple times; each key can be either a fingerprint (\cw{SHA256:AbCdE...}, \cw{99:aa:bb:...}, etc) or a base64-encoded blob in OpenSSH's one-line format. \lcont{ Specifying this option overrides automated host key management; \e{only} the key(s) specified on the command-line will be accepted (unless a saved session also overrides host keys, in which case those will be added to), and the host key cache will not be written. } \dt \cw{\-sercfg} \e{configuration-string} \dd Specify the configuration parameters for the serial port, in \cw{-serial} mode. \e{configuration-string} should be a comma-separated list of configuration parameters as follows: \lcont{ \b Any single digit from 5 to 9 sets the number of data bits. \b \cq{1}, \cq{1.5} or \cq{2} sets the number of stop bits. \b Any other numeric string is interpreted as a baud rate. \b A single lower-case letter specifies the parity: \cq{n} for none, \cq{o} for odd, \cq{e} for even, \cq{m} for mark and \cq{s} for space. \b A single upper-case letter specifies the flow control: \cq{N} for none, \cq{X} for XON/XOFF, \cq{R} for RTS/CTS and \cq{D} for DSR/DTR. } \S{putty-manpage-saved-sessions} SAVED SESSIONS Saved sessions are stored in a \cw{.putty/sessions} subdirectory in your home directory. \S{putty-manpage-more-information} MORE INFORMATION For more information on PuTTY, it's probably best to go and look at the manual on the web page: \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/}\cw{https://www.chiark.greenend.org.uk/~sgtatham/putty/} \S{putty-manpage-bugs} BUGS This man page isn't terribly complete. putty-0.76/doc/man-puttygen.but0000644000175000017500000002717614072266310013503 00000000000000\cfg{man-identity}{puttygen}{1}{2004-03-24}{PuTTY tool suite}{PuTTY tool suite} \H{puttygen-manpage} Man page for PuTTYgen \S{puttygen-manpage-name} NAME \cw{puttygen} - public-key generator for the PuTTY tools \S{puttygen-manpage-synopsis} SYNOPSIS \c puttygen ( keyfile | -t keytype [ -b bits ] [ --primes method ] [ -q ] ) \e bbbbbbbb iiiiiii bb iiiiiii bb iiii bbbbbbbb iiiiii bb \c [ -C new-comment ] [ -P ] [ --reencrypt ] \e bb iiiiiiiiiii bb bbbbbbbbbbb \c [ -O output-type | -l | -L | -p | --dump ] [ -E fptype ] \e bb iiiiiiiiiii bb bb bb bbbbbb bb iiiiii \c [ --ppk-param key=value,... ] \e bbbbbbbbbbb iiibiiiiib \c [ -o output-file ] \e bb iiiiiiiiiii \S{puttygen-manpage-description} DESCRIPTION \c{puttygen} is a tool to generate and manipulate SSH public and private key pairs. It is part of the PuTTY suite, although it can also interoperate with the key formats used by some other SSH clients. When you run \c{puttygen}, it does three things. Firstly, it either loads an existing key file (if you specified \e{keyfile}), or generates a new key (if you specified \e{keytype}). Then, it optionally makes modifications to the key (such as changing the comment and/or the passphrase); finally, it outputs the key, or some information about the key, to a file. All three of these phases are controlled by the options described in the following section. \S{puttygen-manpage-options} OPTIONS In the first phase, \c{puttygen} either loads or generates a key. Note that generating a key requires random data, which can cause \c{puttygen} to pause, possibly for some time if your system does not have much randomness available. The options to control this phase are: \dt \e{keyfile} \dd Specify a key file to be loaded. (Use \cq{-} to read a key file from standard input.) \lcont{ Usually this will be a private key, which can be in the (de facto standard) SSH-1 key format, or in PuTTY's SSH-2 key format, or in either of the SSH-2 private key formats used by OpenSSH and ssh.com's implementation. You can also specify a file containing only a \e{public} key here. The operations you can do are limited to outputting another public key format or a fingerprint. Public keys can be in RFC 4716 or OpenSSH format, or the standard SSH-1 format. } \dt \cw{\-t} \e{keytype} \dd Specify a type of key to generate. The acceptable values here are \c{rsa}, \c{dsa}, \c{ecdsa}, \c{eddsa}, \c{ed25519}, and \c{ed448} (to generate SSH-2 keys), and \c{rsa1} (to generate SSH-1 keys). \dt \cw{\-b} \e{bits} \dd Specify the size of the key to generate, in bits. Default for \c{rsa} and \c{dsa} keys is 2048. \dt \cw{\-\-primes} \e{method} \dd Method for generating prime numbers. The acceptable values here are \c{probable} (the default), \c{proven}, and \c{proven-even}; the later methods are slower. (Various synonyms for these method names are also accepted.) \lcont{ The \q{probable primes} method sounds unsafe, but it's the most commonly used prime-generation strategy. There is in theory a possibility that it might accidentally generate a number that isn't prime, but the software does enough checking to make that probability vanishingly small (less than 1 in 2^80, or 1 in 10^24). So, in practice, nobody worries about it very much. The other methods cause PuTTYgen to use numbers that it is \e{sure} are prime, because it generates the output number together with a proof of its primality. This takes more effort, but it eliminates that theoretical risk in the probabilistic method. You might choose to switch from probable to proven primes if you have a local security standard that demands it, or if you don't trust the probabilistic argument for the safety of the usual method. } \dt \cw{\-\-strong-rsa} \dd When generating an RSA key, make sure the prime factors of the key modulus are \q{strong primes}. A strong prime is a prime number chosen to have a particular structure that makes certain factoring algorithms more difficult to apply, so some security standards recommend their use. However, the most modern factoring algorithms are unaffected, so this option is probably not worth turning on \e{unless} you have a local standard that recommends it. \dt \cw{\-q} \dd Suppress the progress display when generating a new key. \dt \cw{\-\-old\-passphrase} \e{file} \dd Specify a file name; the first line will be read from this file (removing any trailing newline) and used as the old passphrase. \s{CAUTION:} If the passphrase is important, the file should be stored on a temporary filesystem or else securely erased after use. \dt \cw{\-\-random\-device} \e{device} \dd Specify device to read entropy from. By default, \c{puttygen} uses \c{/dev/urandom}, falling back to \c{/dev/random} if it has to. In the second phase, \c{puttygen} optionally alters properties of the key it has loaded or generated. The options to control this are: \dt \cw{\-C} \e{new\-comment} \dd Specify a comment string to describe the key. This comment string will be used by PuTTY to identify the key to you (when asking you to enter the passphrase, for example, so that you know which passphrase to type). \dt \cw{\-P} \dd Indicate that you want to change the key's passphrase. This is automatic when you are generating a new key, but not when you are modifying an existing key. \dt \cw{\-\-reencrypt} \dd For an existing private key saved with a passphrase, refresh the encryption without changing the passphrase. \lcont{ This is most likely to be useful with the \cw{\-\-ppk-param} option, to change some aspect of the key file's format or encryption. } \dt \cw{\-\-ppk-param} \e{key}\cw{=}\e{value}\cw{,}... \dd When saving a PPK file (the default \cw{private} output type for SSH-2 keys), adjust details of the on-disk format. \lcont{ Aspects to change are specified as a series of \e{key}\cw{=}\e{value} pairs separated by commas. The \e{key}s are: \dt \cw{version} \dd The PPK format version. Possible values are \cw{3} (the default) and \cw{2} (which is less resistant to brute-force decryption, but which you might need if your key needs to be used by old versions of PuTTY tools, or other PPK consumers). \lcont{ The following \e{key}s only affect PPK version 3 files. } \dt \cw{kdf} \dd The variant of the Argon2 key derivation function to use. Options are \cw{argon2id} (default, and recommended), \cw{argon2i}, and \cw{argon2d}. \lcont{ You might change this if you consider your exposure to side-channel attacks to be different to the norm. } \dt \cw{memory} \dd The amount of memory needed to decrypt the key, in Kbyte. Default is 8192 (i.e., 8 Mbyte). \dt \cw{time} \dd Approximate time, on this machine, required to attempt decrypting the key, in milliseconds. Default is 100 (ms). \dt \cw{passes} \dd Alternative to \cw{time}: explicitly specify the number of hash passes required to attempt decrypting the key. \dt \cw{parallelism} \dd Number of parallelisable threads that can be used to decrypt the key. Default is 1 (force decryption to run single-threaded). } In the third phase, \c{puttygen} saves the key or information about it. The options to control this are: \dt \cw{\-O} \e{output\-type} \dd Specify the type of output you want \c{puttygen} to produce. Acceptable options are: \lcont{ \dt \cw{private} \dd Save the private key in a format usable by PuTTY. This will either be the standard SSH-1 key format, or PuTTY's own SSH-2 key format (\q{PPK}). This is the default. \dt \cw{public} \dd Save the public key only. For SSH-1 keys, the standard public key format will be used (\q{\cw{1024 37 5698745}...}). For SSH-2 keys, the public key will be output in the format specified by RFC 4716, which is a multi-line text file beginning with the line \q{\cw{---- BEGIN SSH2 PUBLIC KEY ----}}. \dt \cw{public-openssh} \dd Save the public key only, in a format usable by OpenSSH. For SSH-1 keys, this output format behaves identically to \c{public}. For SSH-2 keys, the public key will be output in the OpenSSH format, which is a single line (\q{\cw{ssh-rsa AAAAB3NzaC1yc2}...}). \dt \cw{fingerprint} \dd Print a fingerprint of the public key. The \cw{-E} option lets you specify which fingerprinting algorithm to use. All algorithms are believed compatible with OpenSSH. \dt \cw{private-openssh} \dd Save an SSH-2 private key in OpenSSH's format, using the oldest format available to maximise backward compatibility. This option is not permitted for SSH-1 keys. \dt \cw{private-openssh-new} \dd As \c{private-openssh}, except that it forces the use of OpenSSH's newer format even for RSA, DSA, and ECDSA keys. \dt \cw{private-sshcom} \dd Save an SSH-2 private key in ssh.com's format. This option is not permitted for SSH-1 keys. \dt \cw{text} \dd Save a textual dump of the numeric components comprising the key (both the public and private parts, if present). Useful for debugging, or for using PuTTYgen as a key generator for applications other than SSH. \lcont{ The output consists of a series of \cw{name=value} lines, where each \c{value} is either a C-like string literal in double quotes, or a hexadecimal number starting with \cw{0x...} } If no output type is specified, the default is \c{private}. } \dt \cw{\-o} \e{output\-file} \dd Specify the file where \c{puttygen} should write its output. If this option is not specified, \c{puttygen} will assume you want to overwrite the original file if the input and output file types are the same (changing a comment or passphrase), and will assume you want to output to stdout if you are asking for a public key or fingerprint. Otherwise, the \c{\-o} option is required. \dt \cw{\-l} \dd Synonym for \q{\cw{-O fingerprint}}. \dt \cw{\-L} \dd Synonym for \q{\cw{-O public-openssh}}. \dt \cw{\-p} \dd Synonym for \q{\cw{-O public}}. \dt \cw{\-\-dump} \dd Synonym for \q{\cw{-O text}}. \dt \cw{-E} \e{fptype} \dd Specify the algorithm to use if generating a fingerprint. The options are \cw{sha256} (the default) and \cw{md5}. \dt \cw{\-\-new\-passphrase} \e{file} \dd Specify a file name; the first line will be read from this file (removing any trailing newline) and used as the new passphrase. If the file is empty then the saved key will be unencrypted. \s{CAUTION:} If the passphrase is important, the file should be stored on a temporary filesystem or else securely erased after use. The following options do not run PuTTYgen as normal, but print informational messages and then quit: \dt \cw{\-h}, \cw{\-\-help} \dd Display a message summarizing the available options. \dt \cw{\-V}, \cw{\-\-version} \dd Display the version of PuTTYgen. \dt \cw{\-\-pgpfp} \dd Display the fingerprints of the PuTTY PGP Master Keys, to aid in verifying new files released by the PuTTY team. \S{puttygen-manpage-examples} EXAMPLES To generate an SSH-2 RSA key pair and save it in PuTTY's own format (you will be prompted for the passphrase): \c puttygen -t rsa -C "my home key" -o mykey.ppk To generate a larger (4096-bit) key: \c puttygen -t rsa -b 4096 -C "my home key" -o mykey.ppk To change the passphrase on a key (you will be prompted for the old and new passphrases): \c puttygen -P mykey.ppk To change the comment on a key: \c puttygen -C "new comment" mykey.ppk To convert a key into OpenSSH's private key format: \c puttygen mykey.ppk -O private-openssh -o my-openssh-key To convert a key \e{from} another format (\c{puttygen} will automatically detect the input key type): \c puttygen my-ssh.com-key -o mykey.ppk To display the SHA-256 fingerprint of a key (some key types require a passphrase to extract even this much information): \c puttygen -l mykey.ppk To add the OpenSSH-format public half of a key to your authorised keys file: \c puttygen -L mykey.ppk >> $HOME/.ssh/authorized_keys putty-0.76/doc/man-puttytel.but0000644000175000017500000001575114072266310013512 00000000000000\cfg{man-identity}{puttytel}{1}{2004-03-24}{PuTTY tool suite}{PuTTY tool suite} \H{puttytel-manpage} Man page for PuTTYtel \S{puttytel-manpage-name} NAME \cw{puttytel} \- GUI Telnet, Rlogin, and SUPDUP client for X \S{puttytel-manpage-synopsis} SYNOPSIS \c puttytel [ options ] [ host ] \e bbbbbbbb iiiiiii iiii \S{puttytel-manpage-description} DESCRIPTION \cw{puttytel} is a graphical Telnet, Rlogin, and SUPDUP client for X. It is a direct port of the Windows Telnet, Rlogin, and SUPDUP client of the same name, and a cut-down cryptography-free version of PuTTY. \S{puttytel-manpage-options} OPTIONS The command-line options supported by \cw{puttytel} are: \dt \cw{\-\-display} \e{display\-name} \dd Specify the X display on which to open \cw{puttytel}. (Note this option has a double minus sign, even though none of the others do. This is because this option is supplied automatically by GTK. Sorry.) \dt \cw{\-fn} \e{font-name} \dd Specify the font to use for normal text displayed in the terminal. For example, \cw{\-fn\_fixed}, \cw{\-fn\_"Monospace\_12"}. \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. \dt \cw{\-sl} \e{lines} \dd Specify the number of lines of scrollback to save off the top of the terminal. \dt \cw{\-fg} \e{colour} \dd Specify the foreground colour to use for normal text. \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. \dt \cw{\-cbg} \e{colour} \dd Specify the background colour to use for text covered by the cursor. In other words, this is the main colour of the cursor. \dt \cw{\-title} \e{title} \dd Specify the initial title of the terminal window. (This can be changed under control of the server.) \dt \cw{\-sb\-} or \cw{+sb} \dd Tells \cw{puttytel} not to display a scroll bar. \dt \cw{\-sb} \dd Tells \cw{puttytel} to display a scroll bar: this is the opposite of \cw{\-sb\-}. This is the default option: you will probably only need to specify it explicitly if you have changed the default using the \cw{ScrollBar} resource. \dt \cw{\-log} \e{logfile}, \cw{\-sessionlog} \e{logfile} \dd This option makes \cw{puttytel} log all the terminal output to a file as well as displaying it in the terminal. \dt \cw{\-cs} \e{charset} \dd This option specifies the character set in which \cw{puttytel} should assume the session is operating. This character set will be used to interpret all the data received from the session, and all input you type or paste into \cw{puttytel} will be converted into this character set before being sent to the session. \lcont{ Any character set name which is valid in a MIME header (and supported by \cw{puttytel}) should be valid here (examples are \q{\cw{ISO-8859-1}}, \q{\cw{windows-1252}} or \q{\cw{UTF-8}}). Also, any character encoding which is valid in an X logical font description should be valid (\q{\cw{ibm-cp437}}, for example). \cw{puttytel}'s default behaviour is to use the same character encoding as its primary font. If you supply a Unicode (\cw{iso10646-1}) font, it will default to the UTF-8 character set. Character set names are case-insensitive. } \dt \cw{\-nethack} \dd Tells \cw{puttytel} to enable NetHack keypad mode, in which the numeric keypad generates the NetHack \c{hjklyubn} direction keys. This enables you to play NetHack with the numeric keypad without having to use the NetHack \c{number_pad} option (which requires you to press \q{\cw{n}} before any repeat count). So you can move with the numeric keypad, and enter repeat counts with the normal number keys. \dt \cw{\-help}, \cw{\-\-help} \dd Display a message summarizing the available options. \dt \cw{\-pgpfp} \dd Display the fingerprints of the PuTTY PGP Master Keys, to aid in verifying new files released by the PuTTY team. \dt \cw{\-load} \e{session} \dd Load a saved session by name. This allows you to run a saved session straight from the command line without having to go through the configuration box first. \dt \cw{\-telnet}, \cw{\-rlogin}, \cw{\-supdup}, \cw{\-raw} \dd Select the protocol \cw{puttytel} will use to make the connection. \dt \cw{\-proxycmd} \e{command} \dd Instead of making a TCP connection, use \e{command} as a proxy; network traffic will be redirected to the standard input and output of \e{command}. \e{command} must be a single word, so is likely to need quoting by the shell. \lcont{ The special strings \cw{%host} and \cw{%port} in \e{command} will be replaced by the hostname and port number you want to connect to; to get a literal \c{%} sign, enter \c{%%}. Backslash escapes are also supported, such as sequences like \c{\\n} being replaced by a literal newline; to get a literal backslash, enter \c{\\\\}. (Further escaping may be required by the shell.) (See the main PuTTY manual for full details of the supported \cw{%}- and backslash-delimited tokens, although most of them are probably not very useful in this context.) } \dt \cw{\-l} \e{username} \dd Specify the username to use when logging in to the server. \dt \cw{\-P} \e{port} \dd Specify the port to connect to the server on. \dt \cw{-4}, \cw{-6} \dd Force use of IPv4 or IPv6 for network connections. \S{puttytel-manpage-saved-sessions} SAVED SESSIONS Saved sessions are stored in a \cw{.putty/sessions} subdirectory in your home directory. \S{puttytel-manpage-more-information} MORE INFORMATION For more information on PuTTY and PuTTYtel, it's probably best to go and look at the manual on the web page: \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/}\cw{https://www.chiark.greenend.org.uk/~sgtatham/putty/} \S{puttytel-manpage-bugs} BUGS This man page isn't terribly complete. putty-0.76/doc/mancfg.but0000644000175000017500000000010614072266310012266 00000000000000\cfg{man-mindepth}{2} \C{not-shown} Chapter title which is not shown putty-0.76/doc/manpages.but0000644000175000017500000000014114072266310012625 00000000000000\A{man-pages} Man pages for Unix PuTTY This appendix contains all the man pages for Unix PuTTY. putty-0.76/doc/pageant.but0000644000175000017500000003533214072266310012463 00000000000000\C{pageant} Using \i{Pageant} for authentication Pageant is an SSH \i{authentication agent}. It holds your \i{private key}s in memory, already decoded, so that you can use them often \I{passwordless login}without needing to type a \i{passphrase}. \H{pageant-start} Getting started with Pageant Before you run Pageant, you need to have a private key in \c{*.\i{PPK}} format. See \k{pubkey} to find out how to generate and use one. When you run Pageant, it will put an icon of a computer wearing a hat into the \ii{System tray}. It will then sit and do nothing, until you load a private key into it. (You may need to use Windows' \q{Show hidden icons} arrow to see the Pageant icon.) If you click the Pageant icon with the right mouse button, you will see a menu. Select \q{View Keys} from this menu. The Pageant main window will appear. (You can also bring this window up by double-clicking on the Pageant icon.) The Pageant window contains a list box. This shows the private keys Pageant is holding. When you start Pageant, it has no keys, so the list box will be empty. After you add one or more keys, they will show up in the list box. To add a key to Pageant, press the \q{Add Key} button. Pageant will bring up a file dialog, labelled \q{Select Private Key File}. Find your private key file in this dialog, and press \q{Open}. Pageant will now load the private key. If the key is protected by a passphrase, Pageant will ask you to type the passphrase. When the key has been loaded, it will appear in the list in the Pageant window. Now start PuTTY and open an SSH session to a site that accepts your key. PuTTY will notice that Pageant is running, retrieve the key automatically from Pageant, and use it to authenticate. You can now open as many PuTTY sessions as you like without having to type your passphrase again. (PuTTY can be configured not to try to use Pageant, but it will try by default. See \k{config-ssh-tryagent} and \k{using-cmdline-agentauth} for more information.) When you want to shut down Pageant, click the right button on the Pageant icon in the System tray, and select \q{Exit} from the menu. Closing the Pageant main window does \e{not} shut down Pageant. If you want Pageant to stay running but forget all the keys it has acquired, select \q{Remove All Keys} from the System tray menu. \H{pageant-mainwin} The Pageant main window The Pageant main window appears when you left-click on the Pageant system tray icon, or alternatively right-click and select \q{View Keys} from the menu. You can use it to keep track of what keys are currently loaded into Pageant, and to add new ones or remove the existing keys. \S{pageant-mainwin-keylist} The key list box The large list box in the Pageant main window lists the private keys that are currently loaded into Pageant. The list might look something like this: \c ssh-ed25519 SHA256:TddlQk20DVs4LRcAsIfDN9pInKpY06D+h4kSHwWAj4w \c ssh-rsa 2048 SHA256:8DFtyHm3kQihgy52nzX96qMcEVOq7/yJmmwQQhBWYFg For each key, the list box will tell you: \b The type of the key. Currently, this can be \c{ssh-rsa} (an RSA key for use with the SSH-2 protocol), \c{ssh-dss} (a DSA key for use with the SSH-2 protocol), \c{ecdsa-sha2-*} (an ECDSA key for use with the SSH-2 protocol), \c{ssh-ed25519} (an Ed25519 key for use with the SSH-2 protocol), \c{ssh-ed448} (an Ed448 key for use with the SSH-2 protocol), or \c{ssh1} (an RSA key for use with the old SSH-1 protocol). \b The size (in bits) of the key, for key types that come in different sizes. \b The \I{key fingerprint}fingerprint for the public key. This should be the same fingerprint given by PuTTYgen, and (hopefully) also the same fingerprint shown by remote utilities such as \i\c{ssh-keygen} when applied to your \c{authorized_keys} file. \lcont{ By default this is shown in the \q{SHA256} format. You can change to the older \q{MD5} format (which looks like \c{aa:bb:cc:...}) with the \q{Fingerprint type} drop-down, but bear in mind that this format is less secure and should be avoided for comparison purposes where possible. } \b The comment attached to the key. \b The state of deferred decryption, if enabled for this key. See \k{pageant-deferred-decryption}. \S{pageant-mainwin-addkey} The \q{Add Key} button To add a key to Pageant by reading it out of a local disk file, press the \q{Add Key} button in the Pageant main window, or alternatively right-click on the Pageant icon in the system tray and select \q{Add Key} from there. Pageant will bring up a file dialog, labelled \q{Select Private Key File}. Find your private key file in this dialog, and press \q{Open}. If you want to add more than one key at once, you can select multiple files using Shift-click (to select several adjacent files) or Ctrl-click (to select non-adjacent files). Pageant will now load the private key(s). If a key is protected by a passphrase, Pageant will ask you to type the passphrase. (This is not the only way to add a private key to Pageant. You can also add one from a remote system by using agent forwarding; see \k{pageant-forward} for details.) \S{pageant-mainwin-remkey} The \q{Remove Key} button If you need to remove a key from Pageant, select that key in the list box, and press the \q{Remove Key} button. Pageant will remove the key from its memory. You can apply this to keys you added using the \q{Add Key} button, or to keys you added remotely using agent forwarding (see \k{pageant-forward}); it makes no difference. \H{pageant-cmdline} The Pageant command line Pageant can be made to do things automatically when it starts up, by \I{command-line arguments}specifying instructions on its command line. If you're starting Pageant from the Windows GUI, you can arrange this by editing the properties of the \i{Windows shortcut} that it was started from. If Pageant is already running, invoking it again with the options below causes actions to be performed with the existing instance, not a new one. \S{pageant-cmdline-loadkey} Making Pageant automatically load keys on startup Pageant can automatically load one or more private keys when it starts up, if you provide them on the Pageant command line. Your command line might then look like: \c C:\PuTTY\pageant.exe d:\main.ppk d:\secondary.ppk If the keys are stored encrypted, Pageant will request the passphrases on startup. If Pageant is already running, this syntax loads keys into the existing Pageant. You can specify the \cq{--encrypted} option to defer decryption of these keys; see \k{pageant-deferred-decryption}. \S{pageant-cmdline-command} Making Pageant run another program You can arrange for Pageant to start another program once it has initialised itself and loaded any keys specified on its command line. This program (perhaps a PuTTY, or a WinCVS making use of Plink, or whatever) will then be able to use the keys Pageant has loaded. You do this by specifying the \I{-c-pageant}\c{-c} option followed by the command, like this: \c C:\PuTTY\pageant.exe d:\main.ppk -c C:\PuTTY\putty.exe \S{pageant-cmdline-keylist} Starting with the key list visible Start Pageant with the \i\c{--keylist} option to show the main window as soon as it starts up. \S{pageant-cmdline-restrict-acl} Restricting the \i{Windows process ACL} Pageant supports the same \i\c{-restrict-acl} option as the other PuTTY utilities to lock down the Pageant process's access control; see \k{using-cmdline-restrict-acl} for why you might want to do this. By default, if Pageant is started with \c{-restrict-acl}, it won't pass this to any PuTTY sessions started from its System Tray submenu. Use \c{-restrict-putty-acl} to change this. (Again, see \k{using-cmdline-restrict-acl} for details.) \H{pageant-forward} Using \i{agent forwarding} Agent forwarding is a mechanism that allows applications on your SSH server machine to talk to the agent on your client machine. Note that at present, whether agent forwarding in SSH-2 is available depends on your server. Pageant's protocol is compatible with the \i{OpenSSH} server, but the \i\cw{ssh.com} server uses a different agent protocol, which PuTTY does not yet support. To enable agent forwarding, first start Pageant. Then set up a PuTTY SSH session in which \q{Allow agent forwarding} is enabled (see \k{config-ssh-agentfwd}). Open the session as normal. (Alternatively, you can use the \c{-A} command line option; see \k{using-cmdline-agent} for details.) If this has worked, your applications on the server should now have access to a Unix domain socket which the SSH server will forward back to PuTTY, and PuTTY will forward on to the agent. To check that this has actually happened, you can try this command on Unix server machines: \c unixbox:~$ echo $SSH_AUTH_SOCK \c /tmp/ssh-XXNP18Jz/agent.28794 \c unixbox:~$ If the result line comes up blank, agent forwarding has not been enabled at all. Now if you run \c{ssh} on the server and use it to connect through to another server that accepts one of the keys in Pageant, you should be able to log in without a password: \c unixbox:~$ ssh -v otherunixbox \c [...] \c debug: next auth method to try is publickey \c debug: userauth_pubkey_agent: trying agent key my-putty-key \c debug: ssh-userauth2 successful: method publickey \c [...] If you enable agent forwarding on \e{that} SSH connection as well (see the manual for your server-side SSH client to find out how to do this), your authentication keys will still be available on the next machine you connect to - two SSH connections away from where they're actually stored. In addition, if you have a private key on one of the SSH servers, you can send it all the way back to Pageant using the local \i\c{ssh-add} command: \c unixbox:~$ ssh-add ~/.ssh/id_rsa \c Need passphrase for /home/fred/.ssh/id_rsa \c Enter passphrase for /home/fred/.ssh/id_rsa: \c Identity added: /home/fred/.ssh/id_rsa (/home/simon/.ssh/id_rsa) \c unixbox:~$ and then it's available to every machine that has agent forwarding available (not just the ones downstream of the place you added it). \H{pageant-deferred-decryption} Loading keys without decrypting them You can add keys to Pageant \e{without} decrypting them. The key file will be held in Pageant's memory still encrypted, and when a client program first tries to use the key, Pageant will display a dialog box prompting for the passphrase so that the key can be decrypted. This works the same way whether the key is used by an instance of PuTTY running locally, or a remote client connecting to Pageant through agent forwarding. To add a key to Pageant in this encrypted form, press the \q{Add Key (encrypted)} button in the Pageant main window, or alternatively right-click on the Pageant icon in the system tray and select \q{Add Key (encrypted)} from there. Pageant will bring up a file dialog, in just the same way as it would for the plain \q{Add Key} button. But it won't ask for a passphrase. Instead, the key will be listed in the main window with \q{(encrypted)} after it. To start Pageant up in the first place with encrypted keys loaded into it, you can use the \cq{--encrypted} option on the command line. For example: \c C:\PuTTY\pageant.exe --encrypted d:\main.ppk After a key has been decrypted for the first use, it remains decrypted, so that it can be used again. The main window will list the key with \q{(\i{re-encryptable})} after it. You can revert it to the previous state, where a passphrase is required, using the \q{\i{Re-encrypt}} button in the Pageant main window. You can also \q{re-encrypt} all keys that were added encrypted by choosing \q{Re-encrypt All Keys} from the System tray menu. (Note that this does \e{not} discard cleartext keys that were not previously added encrypted!) \s{CAUTION}: When Pageant displays a prompt to decrypt an already-loaded key, it cannot give keyboard focus to the prompt dialog box. As far as I know this is a deliberate defensive measure by Windows, against malicious software. So make sure you click in the prompt window before typing your passphrase, or else the passphrase might be sent to somewhere you didn't want to trust with it! \H{pageant-security} Security considerations \I{security risk}Using Pageant for public-key authentication gives you the convenience of being able to open multiple SSH sessions without having to type a passphrase every time, but also gives you the security benefit of never storing a decrypted private key on disk. Many people feel this is a good compromise between security and convenience. It \e{is} a compromise, however. Holding your decrypted private keys in Pageant is better than storing them in easy-to-find disk files, but still less secure than not storing them anywhere at all. This is for two reasons: \b Windows unfortunately provides no way to protect pieces of memory from being written to the system \i{swap file}. So if Pageant is holding your private keys for a long period of time, it's possible that decrypted private key data may be written to the system swap file, and an attacker who gained access to your hard disk later on might be able to recover that data. (However, if you stored an unencrypted key in a disk file they would \e{certainly} be able to recover it.) \b Although, like most modern operating systems, Windows prevents programs from accidentally accessing one another's memory space, it does allow programs to access one another's memory space deliberately, for special purposes such as debugging. This means that if you allow a virus, trojan, or other malicious program on to your Windows system while Pageant is running, it could access the memory of the Pageant process, extract your decrypted authentication keys, and send them back to its master. Similarly, use of agent \e{forwarding} is a security improvement on other methods of one-touch authentication, but not perfect. Holding your keys in Pageant on your Windows box has a security advantage over holding them on the remote server machine itself (either in an agent or just unencrypted on disk), because if the server machine ever sees your unencrypted private key then the sysadmin or anyone who cracks the machine can steal the keys and pretend to be you for as long as they want. However, the sysadmin of the server machine can always pretend to be you \e{on that machine}. So if you forward your agent to a server machine, then the sysadmin of that machine can access the forwarded agent connection and request signatures from any of your private keys, and can therefore log in to other machines as you. They can only do this to a limited extent - when the agent forwarding disappears they lose the ability - but using Pageant doesn't actually \e{prevent} the sysadmin (or hackers) on the server from doing this. Therefore, if you don't trust the sysadmin of a server machine, you should \e{never} use agent forwarding to that machine. (Of course you also shouldn't store private keys on that machine, type passphrases into it, or log into other machines from it in any way at all; Pageant is hardly unique in this respect.) putty-0.76/doc/pgpkeys.but0000644000175000017500000002265514072266310012532 00000000000000\A{pgpkeys} PuTTY download keys and signatures \I{verifying new versions}We create \i{GPG signatures} for all the PuTTY files distributed from our web site, so that users can be confident that the files have not been tampered with. Here we identify our public keys, and explain our signature policy so you can have an accurate idea of what each signature guarantees. This description is provided as both a web page on the PuTTY site, and an appendix in the PuTTY manual. As of release 0.58, all of the PuTTY executables contain fingerprint material (usually accessed via the \i\c{-pgpfp} command-line option), such that if you have an executable you trust, you can use it to establish a trust path, for instance to a newer version downloaded from the Internet. As of release 0.67, the Windows executables and installer also contain built-in signatures that are automatically verified by Windows' own mechanism (\q{\i{Authenticode}}). The keys used for that are different, and are not covered here. (Note that none of the keys, signatures, etc mentioned here have anything to do with keys used with SSH - they are purely for verifying the origin of files distributed by the PuTTY team.) \H{pgpkeys-pubkey} Public keys We maintain multiple keys, stored with different levels of security due to being used in different ways. See \k{pgpkeys-security} below for details. The keys we provide are: \dt Snapshot Key \dd Used to sign routine development builds of PuTTY: nightly snapshots, pre-releases, and sometimes also custom diagnostic builds we send to particular users. \dt Release Key \dd Used to sign manually released versions of PuTTY. \dt Secure Contact Key \dd An encryption-capable key suitable for people to send confidential messages to the PuTTY team, e.g. reports of vulnerabilities. \dt Master Key \dd Used to tie all the above keys into the GPG web of trust. The Master Key signs all the other keys, and other GPG users have signed it in turn. The current issue of those keys are available for download from the PuTTY website, and are also available on PGP keyservers using the key IDs listed below. \dt \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/keys/master-2018.asc}{\s{Master Key} (2018)} \dd RSA, 4096-bit. Key ID: \cw{76BC7FE4EBFD2D9E}. Fingerprint: \cw{24E1\_B1C5\_75EA\_3C9F\_F752\_\_A922\_76BC\_7FE4\_EBFD\_2D9E} \dt \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/keys/release-2018.asc}{\s{Release Key} (2018)} \dd RSA, 3072-bit. Key ID: \cw{6289A25F4AE8DA82}. Fingerprint: \cw{E273\_94AC\_A3F9\_D904\_9522\_\_E054\_6289\_A25F\_4AE8\_DA82} \dt \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/keys/snapshot-2018.asc}{\s{Snapshot Key} (2018)} \dd RSA, 3072-bit. Key ID: \cw{38BA7229B7588FD1}. Fingerprint: \cw{C92B\_52E9\_9AB6\_1DDA\_33DB\_\_2B7A\_38BA\_7229\_B758\_8FD1} \dt \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/keys/contact-2018.asc}{\s{Secure Contact Key} (2018)} \dd RSA, 3072-bit. Key ID: \cw{657D487977F95C98}. Fingerprint: \cw{A680\_0082\_2998\_6E46\_22CA\_\_0E43\_657D\_4879\_77F9\_5C98} \H{pgpkeys-security} Security details The various keys have various different security levels. This section explains what those security levels are, and how far you can expect to trust each key. \S{pgpkeys-snapshot} The Development Snapshots key The Development Snapshots private key is stored \e{without a passphrase}. This is necessary, because the snapshots are generated every night without human intervention, so nobody would be able to type a passphrase. The snapshots are built and signed on a team member's home computers, before being uploaded to the web server from which you download them. Therefore, a signature from the Development Snapshots key \e{DOES} protect you against: \b People tampering with the PuTTY binaries between the PuTTY web site and you. \b The maintainers of our web server attempting to abuse their root privilege to tamper with the binaries. But it \e{DOES NOT} protect you against: \b People tampering with the binaries before they are uploaded to our download servers. \b People tampering with the build machines so that the next set of binaries they build will be malicious in some way. \b People stealing the unencrypted private key from the build machine it lives on. Of course, we take all reasonable precautions to guard the build machines. But when you see a signature, you should always be certain of precisely what it guarantees and precisely what it does not. \S{pgpkeys-release} The Releases key The Releases key is more secure: because it is only used at release time, to sign each release by hand, we can store it encrypted. The Releases private key is kept encrypted on the developers' own local machines. So an attacker wanting to steal it would have to also steal the passphrase. \S{pgpkeys-contact} The Secure Contact Key The Secure Contact Key is stored with a similar level of security to the Release Key: it is stored with a passphrase, and no automated script has access to it. \S{pgpkeys-master} The Master Keys The Master Key signs almost nothing. Its purpose is to bind the other keys together and certify that they are all owned by the same people and part of the same integrated setup. The only signatures produced by the Master Key, \e{ever}, should be the signatures on the other keys. The Master Key is especially long, and its private key and passphrase are stored with special care. We have collected some third-party signatures on the Master Key, in order to increase the chances that you can find a suitable trust path to them. We have uploaded our various keys to public keyservers, so that even if you don't know any of the people who have signed our keys, you can still be reasonably confident that an attacker would find it hard to substitute fake keys on all the public keyservers at once. \H{pgpkeys-rollover} Key rollover Our current keys were generated in August 2018. Each new Master Key is signed with the old one, to show that it really is owned by the same people and not substituted by an attacker. Each new Master Key also signs the previous Release Keys, in case you're trying to verify the signatures on a release prior to the rollover and can find a chain of trust to those keys from any of the people who have signed our new Master Key. Each release is signed with the Release Key that was current at the time of release. We don't go back and re-sign old releases with newly generated keys. The details of all previous keys are given here. \s{Key generated in 2016} (when we first introduced the Secure Contact Key) \dt \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/keys/contact-2016.asc}{\s{Secure Contact Key} (2016)} \dd RSA, 2048-bit. Main key ID: \cw{2048R/8A0AF00B} (long version: \cw{2048R/C4FCAAD08A0AF00B}). Encryption subkey ID: \cw{2048R/50C2CF5C} (long version: \cw{2048R/9EB39CC150C2CF5C}). Fingerprint: \cw{8A26\_250E\_763F\_E359\_75F3\_\_118F\_C4FC\_AAD0\_8A0A\_F00B} \s{Keys generated in the 2015 rollover} \dt \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/keys/master-2015.asc}{\s{Master Key} (2015)} \dd RSA, 4096-bit. Key ID: \cw{4096R/04676F7C} (long version: \cw{4096R/AB585DC604676F7C}). Fingerprint: \cw{440D\_E3B5\_B7A1\_CA85\_B3CC\_\_1718\_AB58\_5DC6\_0467\_6F7C} \dt \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/keys/release-2015.asc}{\s{Release Key} (2015)} \dd RSA, 2048-bit. Key ID: \cw{2048R/B43434E4} (long version: \cw{2048R/9DFE2648B43434E4}). Fingerprint: \cw{0054\_DDAA\_8ADA\_15D2\_768A\_\_6DE7\_9DFE\_2648\_B434\_34E4} \dt \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/keys/snapshot-2015.asc}{\s{Snapshot Key} (2015)} \dd RSA, 2048-bit. Key ID: \cw{2048R/D15F7E8A} (long version: \cw{2048R/EEF20295D15F7E8A}). Fingerprint: \cw{0A3B\_0048\_FE49\_9B67\_A234\_\_FEB6\_EEF2\_0295\_D15F\_7E8A} \s{Original keys generated in 2000} (two sets, RSA and DSA) \dt \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/keys/master-rsa.asc}{\s{Master Key} (original RSA)} \dd RSA, 1024-bit. Key ID: \cw{1024R/1E34AC41} (long version: \cw{1024R/9D5877BF1E34AC41}). Fingerprint: \cw{8F\_15\_97\_DA\_25\_30\_AB\_0D\_\_88\_D1\_92\_54\_11\_CF\_0C\_4C} \dt \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/keys/master-dsa.asc}{\s{Master Key} (original DSA)} \dd DSA, 1024-bit. Key ID: \cw{1024D/6A93B34E} (long version: \cw{1024D/4F5E6DF56A93B34E}). Fingerprint: \cw{313C\_3E76\_4B74\_C2C5\_F2AE\_\_83A8\_4F5E\_6DF5\_6A93\_B34E} \dt \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/keys/release-rsa.asc}{\s{Release Key} (original RSA)} \dd RSA, 1024-bit. Key ID: \cw{1024R/B41CAE29} (long version: \cw{1024R/EF39CCC0B41CAE29}). Fingerprint: \cw{AE\_65\_D3\_F7\_85\_D3\_18\_E0\_\_3B\_0C\_9B\_02\_FF\_3A\_81\_FE} \dt \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/keys/release-dsa.asc}{\s{Release Key} (original DSA)} \dd DSA, 1024-bit. Key ID: \cw{1024D/08B0A90B} (long version: \cw{1024D/FECD6F3F08B0A90B}). Fingerprint: \cw{00B1\_1009\_38E6\_9800\_6518\_\_F0AB\_FECD\_6F3F\_08B0\_A90B} \dt \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/keys/snapshot-rsa.asc}{\s{Snapshot Key} (original RSA)} \dd RSA, 1024-bit. Key ID: \cw{1024R/32B903A9} (long version: \cw{1024R/FAAED21532B903A9}). Fingerprint: \cw{86\_8B\_1F\_79\_9C\_F4\_7F\_BD\_\_8B\_1B\_D7\_8E\_C6\_4E\_4C\_03} \dt \W{https://www.chiark.greenend.org.uk/~sgtatham/putty/keys/snapshot-dsa.asc}{\s{Snapshot Key} (original DSA)} \dd DSA, 1024-bit. Key ID: \cw{1024D/7D3E4A00} (long version: \cw{1024D/165E56F77D3E4A00}). Fingerprint: \cw{63DD\_8EF8\_32F5\_D777\_9FF0\_\_2947\_165E\_56F7\_7D3E\_4A00} putty-0.76/doc/plink.but0000644000175000017500000004263114072266310012161 00000000000000\C{plink} Using the command-line connection tool \i{Plink} \i{Plink} 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. Plink is probably not what you want if you want to run an \i{interactive session} in a console window. \H{plink-starting} Starting Plink Plink is a command line application. This means that you cannot just double-click on its icon to run it and instead you have to bring up a \i{console window}. In Windows 95, 98, and ME, this is called an \q{MS-DOS Prompt}, and in Windows NT, 2000, and XP, it is called a \q{Command Prompt}. It should be available from the Programs section of your Start Menu. In order to use Plink, the file \c{plink.exe} will need either to be on your \i{\c{PATH}} or in your current directory. To add the directory containing Plink to your \c{PATH} environment variable, type into the console window: \c set PATH=C:\path\to\putty\directory;%PATH% This will only work for the lifetime of that particular console window. To set your \c{PATH} more permanently on Windows NT, 2000, and XP, use the Environment tab of the System Control Panel. On Windows 95, 98, and ME, you will need to edit your \i\c{AUTOEXEC.BAT} to include a \c{set} command like the one above. \H{plink-usage} Using Plink This section describes the basics of how to use Plink for interactive logins and for automated processes. 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 C:\>plink \c Plink: command-line connection utility \c Release 0.76 \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 \c -ssh -telnet -rlogin -raw -serial \c force use of a particular protocol \c -ssh-connection \c force use of the bare ssh-connection protocol \c -P port connect to specified port \c -l user connect with specified username \c -batch disable all interactive prompts \c -proxycmd command \c use 'command' as local proxy \c -sercfg configuration-string (e.g. 19200,8,n,1,X) \c Specify the serial configuration (serial only) \c The following options only apply to SSH connections: \c -pw passw login with specified password \c -D [listen-IP:]listen-port \c Dynamic SOCKS-based port forwarding \c -L [listen-IP:]listen-port:host:port \c Forward local port to remote address \c -R [listen-IP:]listen-port:host:port \c Forward remote port to local address \c -X -x enable / disable X11 forwarding \c -A -a enable / disable agent forwarding \c -t -T enable / disable pty allocation \c -1 -2 force use of particular SSH protocol version \c -4 -6 force use of IPv4 or IPv6 \c -C enable compression \c -i key private key file for user authentication \c -noagent disable use of Pageant \c -agent enable use of Pageant \c -no-trivial-auth \c disconnect if SSH authentication succeeds trivially \c -noshare disable use of connection sharing \c -share enable use of connection sharing \c -hostkey keyid \c manually specify a host key (may be repeated) \c -sanitise-stderr, -sanitise-stdout, -no-sanitise-stderr, -no-sanitise-stdout \c do/don't strip control chars from standard output/error \c -no-antispoof omit anti-spoofing prompt after authentication \c -m file read remote command(s) from file \c -s remote command is an SSH subsystem (SSH-2 only) \c -N don't start a shell/command (SSH-2 only) \c -nc host:port \c open tunnel in place of session (SSH-2 only) \c -sshlog file \c -sshrawlog file \c log protocol details to a file \c -logoverwrite \c -logappend \c control what happens when a log file already exists \c -shareexists \c test whether a connection-sharing upstream exists Once this works, you are ready to use Plink. \S{plink-usage-interactive} Using Plink for interactive logins To make a simple interactive connection to a remote server, just type \c{plink} and then the host name: \c C:\>plink login.example.com \c \c Debian GNU/Linux 2.2 flunky.example.com \c flunky login: You should then be able to log in as normal and run a session. The output sent by the server will be written straight to your command prompt window, which will most likely not interpret terminal \i{control codes} in the way the server expects it to. So if you run any full-screen applications, for example, you can expect to see strange characters appearing in your window. Interactive connections like this are not the main point of Plink. In order to connect with a different protocol, you can give the command line options \c{-ssh}, \c{-ssh-connection}, \c{-telnet}, \c{-rlogin}, or \c{-raw}. To make an SSH connection, for example: \c C:\>plink -ssh login.example.com \c login as: If you have already set up a PuTTY saved session, then instead of supplying a host name, you can give the saved session name. This allows you to use public-key authentication, specify a user name, and use most of the other features of PuTTY: \c C:\>plink my-ssh-session \c Sent username "fred" \c Authenticating with public key "fred@winbox" \c Last login: Thu Dec 6 19:25:33 2001 from :0.0 \c fred@flunky:~$ (You can also use the \c{-load} command-line option to load a saved session; see \k{using-cmdline-load}. If you use \c{-load}, the saved session exists, and it specifies a hostname, you cannot also specify a \c{host} or \c{user@host} argument - it will be treated as part of the remote command.) \S{plink-usage-batch} Using Plink for automated connections More typically Plink is used with the SSH protocol, to enable you to talk directly to a program running on the server. To do this you have to ensure Plink is \e{using} the SSH protocol. You can do this in several ways: \b Use the \c{-ssh} option as described in \k{plink-usage-interactive}. \b Set up a PuTTY saved session that describes the server you are connecting to, and that also specifies the protocol as SSH. \b Set the Windows environment variable \i\c{PLINK_PROTOCOL} to the word \c{ssh}. Usually Plink is not invoked directly by a user, but run automatically by another process. Therefore you typically do not want Plink to prompt you for a user name or a password. Next, you are likely to need to avoid the various interactive prompts Plink can produce. You might be prompted to verify the host key of the server you're connecting to, to enter a user name, or to enter a password. To avoid being prompted for the server host key when using Plink for an automated connection, you can first make a \e{manual} connection (using either of PuTTY or Plink) to the same server, verify the host key (see \k{gs-hostkey} for more information), and select \q{Accept} to add the host key to the Registry. After that, Plink commands connecting to that server should not give a host key prompt unless the host key changes. Alternatively, you can specify the appropriate host key(s) on Plink's command line every time you use it; see \k{using-cmdline-hostkey}. To avoid being prompted for a user name, you can: \b Use the \c{-l} option to specify a user name on the command line. For example, \c{plink login.example.com -l fred}. \b Set up a PuTTY saved session that describes the server you are connecting to, and that also specifies the username to log in as (see \k{config-username}). To avoid being prompted for a password, you should almost certainly set up \i{public-key authentication}. (See \k{pubkey} for a general introduction to public-key authentication.) Again, you can do this in two ways: \b Set up a PuTTY saved session that describes the server you are connecting to, and that also specifies a private key file (see \k{config-ssh-privkey}). For this to work without prompting, your private key will need to have no passphrase. \b Store the private key in Pageant. See \k{pageant} for further information. Once you have done all this, you should be able to run a remote command on the SSH server machine and have it execute automatically with no prompting: \c C:\>plink login.example.com -l fred echo hello, world \c hello, world \c \c C:\> Or, if you have set up a saved session with all the connection details: \c C:\>plink mysession echo hello, world \c hello, world \c \c C:\> Then you can set up other programs to run this Plink command and talk to it as if it were a process on the server machine. \S{plink-options} Plink command line options Plink accepts all the general command line options supported by the PuTTY tools. See \k{using-general-opts} for a description of these options. Plink also supports some of its own options. The following sections describe Plink's specific command-line options. \S2{plink-option-batch} \I{-batch-plink}\c{-batch}: disable all interactive prompts If you use the \c{-batch} option, Plink will never give an interactive prompt while establishing the connection. If the server's host key is invalid, for example (see \k{gs-hostkey}), then the connection will simply be abandoned instead of asking you what to do next. This may help Plink's behaviour when it is used in automated scripts: using \c{-batch}, if something goes wrong at connection time, the batch job will fail rather than hang. \S2{plink-option-s} \I{-s-plink}\c{-s}: remote command is SSH subsystem If you specify the \c{-s} option, Plink passes the specified command as the name of an SSH \q{\i{subsystem}} rather than an ordinary command line. (This option is only meaningful with the SSH-2 protocol.) \S2{plink-option-share} \I{-share-plink}\c{-share}: Test and try to share an existing connection. This option tris to detect if an existing connection can be shared (See \k{config-ssh-sharing} for more information about SSH connection sharing.) and reuses that connection. A Plink invocation of the form: \c plink -share \e iiiiiiiii will test whether there is currently a viable \q{upstream} for the session in question, which can be specified using any syntax you'd normally use with Plink to make an actual connection (a host/port number, a bare saved session name, \c{-load}, etc). If no \q{upstream} viable session is found and \c{-share} is specified, this connection will be become the \q{upstream} connection for subsequent connection sharing tries. (This option is only meaningful with the SSH-2 protocol.) \S2{plink-option-shareexists} \I{-shareexists-plink}\c{-shareexists}: test for connection-sharing upstream This option does not make a new connection; instead it allows testing for the presence of an existing connection that can be shared. (See \k{config-ssh-sharing} for more information about SSH connection sharing.) A Plink invocation of the form: \c plink -shareexists \e iiiiiiiii will test whether there is currently a viable \q{upstream} for the session in question, which can be specified using any syntax you'd normally use with Plink to make an actual connection (a host/port number, a bare saved session name, \c{-load}, etc). It returns a zero exit status if a usable \q{upstream} exists, nonzero otherwise. (This option is only meaningful with the SSH-2 protocol.) \S2{plink-option-sanitise} \I{-sanitise-stderr}\I{-sanitise-stdout}\I{-no-sanitise-stderr}\I{-no-sanitise-stdout}\c{-sanitise-}\e{stream}: control output sanitisation In some situations, Plink applies a sanitisation pass to the output received from the server, to strip out control characters such as backspace and the escape character. The idea of this is to prevent remote processes from sending confusing escape sequences through the standard error channel when Plink is being used as a transport for something like \cw{git} or CVS. If the server actually wants to send an error message, it will probably be plain text; if the server abuses that channel to try to write over unexpected parts of your terminal display, Plink will try to stop it. By default, this only happens for output channels which are sent to a Windows console device, or a Unix terminal device. (Any output stream going somewhere else is likely to be needed by an 8-bit protocol and must not be tampered with at all.) It also stops happening if you tell Plink to allocate a remote pseudo-terminal (see \k{using-cmdline-pty} and \k{config-ssh-pty}), on the basis that in that situation you often \e{want} escape sequences from the server to go to your terminal. But in case Plink guesses wrong about whether you want this sanitisation, you can override it in either direction, using one of these options: \dt \c{-sanitise-stderr} \dd Sanitise server data written to Plink's standard error channel, regardless of terminals and consoles and remote ptys. \dt \c{-no-sanitise-stderr} \dd Do not sanitise server data written to Plink's standard error channel. \dt \c{-sanitise-stdout} \dd Sanitise server data written to Plink's standard output channel. \dt \c{-no-sanitise-stdout} \dd Do not sanitise server data written to Plink's standard output channel. \S2{plink-option-antispoof} \i{-no-antispoof}: turn off authentication spoofing protection prompt In SSH, some possible server authentication methods require user input (for example, password authentication, or entering a private key passphrase), and others do not (e.g. a private key held in Pageant). If you use Plink to run an interactive login session, and if Plink authenticates without needing any user interaction, and if the server is malicious or compromised, it could try to trick you into giving it authentication data that should not go to the server (such as your private key passphrase), by sending what \e{looks} like one of Plink's local prompts, as if Plink had not already authenticated. To protect against this, Plink's default policy is to finish the authentication phase with a final trivial prompt looking like this: \c Access granted. Press Return to begin session. so that if you saw anything that looked like an authentication prompt \e{after} that line, you would know it was not from Plink. That extra interactive step is inconvenient. So Plink will turn it off in as many situations as it can: \b If Plink's standard input is not pointing at a console or terminal device \dash for example, if you're using Plink as a transport for some automated application like version control \dash then you \e{can't} type passphrases into the server anyway. In that situation, Plink won't try to protect you from the server trying to fool you into doing so. \b If Plink is in batch mode (see \k{plink-usage-batch}), then it \e{never} does any interactive authentication. So anything looking like an interactive authentication prompt is automatically suspect, and so Plink omits the anti-spoofing prompt. But if you still find the protective prompt inconvenient, and you trust the server not to try a trick like this, you can turn it off using the \cq{-no-antispoof} option. \H{plink-batch} Using Plink in \i{batch files} and \i{scripts} Once you have set up Plink to be able to log in to a remote server without any interactive prompting (see \k{plink-usage-batch}), you can use it for lots of scripting and batch purposes. For example, to start a backup on a remote machine, you might use a command like: \c plink root@myserver /etc/backups/do-backup.sh Or perhaps you want to fetch all system log lines relating to a particular web area: \c plink mysession grep /~fred/ /var/log/httpd/access.log > fredlog Any non-interactive command you could usefully run on the server command line, you can run in a batch file using Plink in this way. \H{plink-cvs} Using Plink with \i{CVS} To use Plink with CVS, you need to set the environment variable \i\c{CVS_RSH} to point to Plink: \c set CVS_RSH=\path\to\plink.exe You also need to arrange to be able to connect to a remote host without any interactive prompts, as described in \k{plink-usage-batch}. You should then be able to run CVS as follows: \c cvs -d :ext:user@sessionname:/path/to/repository co module If you specified a username in your saved session, you don't even need to specify the \q{user} part of this, and you can just say: \c cvs -d :ext:sessionname:/path/to/repository co module \H{plink-wincvs} Using Plink with \i{WinCVS} Plink can also be used with WinCVS. Firstly, arrange for Plink to be able to connect to a remote host non-interactively, as described in \k{plink-usage-batch}. Then, in WinCVS, bring up the \q{Preferences} dialogue box from the \e{Admin} menu, and switch to the \q{Ports} tab. Tick the box there labelled \q{Check for an alternate \cw{rsh} name} and in the text entry field to the right enter the full path to \c{plink.exe}. Select \q{OK} on the \q{Preferences} dialogue box. Next, select \q{Command Line} from the WinCVS \q{Admin} menu, and type a CVS command as in \k{plink-cvs}, for example: \c cvs -d :ext:user@hostname:/path/to/repository co module or (if you're using a saved session): \c cvs -d :ext:user@sessionname:/path/to/repository co module Select the folder you want to check out to with the \q{Change Folder} button, and click \q{OK} to check out your module. Once you've got modules checked out, WinCVS will happily invoke plink from the GUI for CVS operations. \# \H{plink-whatelse} Using Plink with... ? putty-0.76/doc/pscp.but0000644000175000017500000003420314072266310012005 00000000000000\#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. If you have an SSH-2 server, you might prefer PSFTP (see \k{psftp}) for interactive use. PSFTP does not in general work with SSH-1 servers, however. \H{pscp-starting} Starting PSCP PSCP is a command line application. This means that you cannot just double-click on its icon to run it and instead you have to bring up a \i{console window}. With Windows 95, 98, and ME, this is called an \q{MS-DOS Prompt} and with Windows NT, 2000, and XP, it is called a \q{Command Prompt}. It should be available from the Programs section of your \i{Start Menu}. To start PSCP it will need either to be on your \i{\c{PATH}} or in your current directory. To add the directory containing PSCP to your \c{PATH} environment variable, type into the console window: \c set PATH=C:\path\to\putty\directory;%PATH% This will only work for the lifetime of that particular console window. To set your \c{PATH} more permanently on Windows NT, 2000, and XP, use the Environment tab of the System Control Panel. On Windows 95, 98, and ME, you will need to edit your \i\c{AUTOEXEC.BAT} to include a \c{set} command like the one above. \H{pscp-usage} PSCP Usage 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 C:\>pscp \c PuTTY Secure Copy client \c Release 0.76 \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 \c -q quiet, don't show statistics \c -r copy directories recursively \c -v show verbose messages \c -load sessname Load settings from saved session \c -P port connect to specified port \c -l user connect with specified username \c -pw passw login with specified password \c -1 -2 force use of particular SSH protocol version \c -ssh -ssh-connection \c force use of particular SSH protocol variant \c -4 -6 force use of IPv4 or IPv6 \c -C enable compression \c -i key private key file for user authentication \c -noagent disable use of Pageant \c -agent enable use of Pageant \c -no-trivial-auth \c disconnect if SSH authentication succeeds trivially \c -hostkey keyid \c manually specify a host key (may be repeated) \c -batch disable all interactive prompts \c -no-sanitise-stderr don't strip control chars from standard error \c -proxycmd command \c use 'command' as local proxy \c -unsafe allow server-side wildcards (DANGEROUS) \c -sftp force use of SFTP protocol \c -scp force use of SCP protocol \c -sshlog file \c -sshrawlog file \c log protocol details to a file \c -logoverwrite \c -logappend \c control what happens when a log file already exists (PSCP's interface is much like the Unix \c{scp} command, if you're familiar with that.) \S{pscp-usage-basics} The basics To \I{receiving files}receive (a) file(s) from a remote server: \c pscp [options] [user@]host:source target So to copy the file \c{/etc/hosts} from the server \c{example.com} as user \c{fred} to the file \c{c:\\temp\\example-hosts.txt}, you would type: \c pscp fred@example.com:/etc/hosts c:\temp\example-hosts.txt To \I{sending files}send (a) file(s) to a remote server: \c pscp [options] source [source...] [user@]host:target So to copy the local file \c{c:\\documents\\foo.txt} to the server \c{example.com} as user \c{fred} to the file \c{/tmp/foo} you would type: \c pscp c:\documents\foo.txt fred@example.com:/tmp/foo You can use \i{wildcards} to transfer multiple files in either direction, like this: \c pscp c:\documents\*.doc fred@example.com:docfiles \c pscp fred@example.com:source/*.c c:\source However, in the second case (using a wildcard for multiple remote files) you may see a warning saying something like \q{warning: remote host tried to write to a file called \cq{terminal.c} when we requested a file called \cq{*.c}. If this is a wildcard, consider upgrading to SSH-2 or using the \cq{-unsafe} option. Renaming of this file has been disallowed}. This is due to a \I{security risk}fundamental insecurity in the old-style \i{SCP protocol}: the client sends the wildcard string (\c{*.c}) to the server, and the server sends back a sequence of file names that match the wildcard pattern. However, there is nothing to stop the server sending back a \e{different} pattern and writing over one of your other files: if you request \c{*.c}, the server might send back the file name \c{AUTOEXEC.BAT} and install a virus for you. Since the wildcard matching rules are decided by the server, the client cannot reliably verify that the filenames sent back match the pattern. PSCP will attempt to use the newer \i{SFTP} protocol (part of SSH-2) where possible, which does not suffer from this security flaw. If you are talking to an SSH-2 server which supports SFTP, you will never see this warning. (You can force use of the SFTP protocol, if available, with \c{-sftp} - see \k{pscp-usage-options-backend}.) If you really need to use a server-side wildcard with an SSH-1 server, you can use the \i\c{-unsafe} command line option with PSCP: \c pscp -unsafe fred@example.com:source/*.c c:\source This will suppress the warning message and the file transfer will happen. However, you should be aware that by using this option you are giving the server the ability to write to \e{any} file in the target directory, so you should only use this option if you trust the server administrator not to be malicious (and not to let the server machine be cracked by malicious people). Alternatively, do any such download in a newly created empty directory. (Even in \q{unsafe} mode, PSCP will still protect you against the server trying to get out of that directory using pathnames including \cq{..}.) \S2{pscp-usage-basics-user} \c{user} The \i{login name} on the remote server. If this is omitted, and \c{host} is a PuTTY saved session, PSCP will use any username specified by that saved session. Otherwise, PSCP will attempt to use the local Windows username. \S2{pscp-usage-basics-host} \I{hostname}\c{host} The name of the remote server, or the name of an existing PuTTY saved session. In the latter case, the session's settings for hostname, port number, cipher type and username will be used. \S2{pscp-usage-basics-source} \c{source} One or more source files. \ii{Wildcards} are allowed. The syntax of wildcards depends on the system to which they apply, so if you are copying \e{from} a Windows system \e{to} a UNIX system, you should use Windows wildcard syntax (e.g. \c{*.*}), but if you are copying \e{from} a UNIX system \e{to} a Windows system, you would use the wildcard syntax allowed by your UNIX shell (e.g. \c{*}). If the source is a remote server and you do not specify a full pathname (in UNIX, a pathname beginning with a \c{/} (slash) character), what you specify as a source will be interpreted relative to your \i{home directory} on the remote server. \S2{pscp-usage-basics-target} \c{target} The filename or directory to put the file(s). When copying from a remote server to a local host, you may wish simply to place the file(s) in the current directory. To do this, you should specify a target of \c{.}. For example: \c pscp fred@example.com:/home/tom/.emacs . ...would copy \c{/home/tom/.emacs} on the remote server to the current directory. As with the \c{source} parameter, if the target is on a remote server and is not a full path name, it is interpreted relative to your home directory on the remote server. \S{pscp-usage-options} Options PSCP accepts all the general command line options supported by the PuTTY tools, except the ones which make no sense in a file transfer utility. See \k{using-general-opts} for a description of these options. (The ones not supported by PSCP are clearly marked.) PSCP also supports some of its own options. The following sections describe PSCP's specific command-line options. \S2{pscp-usage-options-ls}\I{-ls-PSCP}\c{-ls} \I{listing files}list remote files If the \c{-ls} option is given, no files are transferred; instead, remote files are listed. Only a hostname specification and optional remote file specification need be given. For example: \c pscp -ls fred@example.com:dir1 The SCP protocol does not contain within itself a means of listing files. If SCP is in use, this option therefore assumes that the server responds appropriately to the command \c{ls\_-la}; this may not work with all servers. If SFTP is in use, this option should work with all servers. \S2{pscp-usage-options-p}\I{-p-PSCP}\c{-p} \i{preserve file attributes} By default, files copied with PSCP are \i{timestamp}ed with the date and time they were copied. The \c{-p} option preserves the original timestamp on copied files. \S2{pscp-usage-options-q}\I{-q-PSCP}\c{-q} quiet, don't show \i{statistics} By default, PSCP displays a meter displaying the progress of the current transfer: \c mibs.tar | 168 kB | 84.0 kB/s | ETA: 00:00:13 | 13% The fields in this display are (from left to right), filename, size (in kilobytes) of file transferred so far, estimate of how fast the file is being transferred (in kilobytes per second), estimated time that the transfer will be complete, and percentage of the file so far transferred. The \c{-q} option to PSCP suppresses the printing of these statistics. \S2{pscp-usage-options-r}\I{-r-PSCP}\c{-r} copies directories \i{recursive}ly By default, PSCP will only copy files. Any directories you specify to copy will be skipped, as will their contents. The \c{-r} option tells PSCP to descend into any directories you specify, and to copy them and their contents. This allows you to use PSCP to transfer whole directory structures between machines. \S2{pscp-usage-options-batch}\I{-batch-PSCP}\c{-batch} avoid interactive prompts If you use the \c{-batch} option, PSCP will never give an interactive prompt while establishing the connection. If the server's host key is invalid, for example (see \k{gs-hostkey}), then the connection will simply be abandoned instead of asking you what to do next. This may help PSCP's behaviour when it is used in automated scripts: using \c{-batch}, if something goes wrong at connection time, the batch job will fail rather than hang. \S2{pscp-usage-options-backend}\i\c{-sftp}, \i\c{-scp} force use of particular file transfer protocol As mentioned in \k{pscp-usage-basics}, there are two different file transfer protocols in use with SSH. Despite its name, PSCP (like many other ostensible \cw{scp} clients) can use either of these protocols. The older \i{SCP protocol} does not have a written specification and leaves a lot of detail to the server platform. \ii{Wildcards} are expanded on the server. The simple design means that any wildcard specification supported by the server platform (such as brace expansion) can be used, but also leads to interoperability issues such as with filename quoting (for instance, where filenames contain spaces), and also the security issue described in \k{pscp-usage-basics}. The newer \i{SFTP} protocol, which is usually associated with SSH-2 servers, is specified in a more platform independent way, and leaves issues such as wildcard syntax up to the client. (PuTTY's SFTP wildcard syntax is described in \k{psftp-wildcards}.) This makes it more consistent across platforms, more suitable for scripting and automation, and avoids security issues with wildcard matching. Normally PSCP will attempt to use the SFTP protocol, and only fall back to the SCP protocol if SFTP is not available on the server. The \c{-scp} option forces PSCP to use the SCP protocol or quit. The \c{-sftp} option forces PSCP to use the SFTP protocol or quit. When this option is specified, PSCP looks harder for an SFTP server, which may allow use of SFTP with SSH-1 depending on server setup. \S2{pscp-option-sanitise} \I{-sanitise-stderr}\I{-no-sanitise-stderr}\c{-no-sanitise-stderr}: control error message sanitisation The \c{-no-sanitise-stderr} option will cause PSCP to pass through the server's standard-error stream literally, without stripping control characters from it first. This might be useful if the server were sending coloured error messages, but it also gives the server the ability to have unexpected effects on your terminal display. For more discussion, see \k{plink-option-sanitise}. \S{pscp-retval} \ii{Return value} PSCP returns an \i\cw{ERRORLEVEL} of zero (success) only if the files were correctly transferred. You can test for this in a \i{batch file}, using code such as this: \c pscp file*.* user@hostname: \c if errorlevel 1 echo There was an error \S{pscp-pubkey} Using \i{public key authentication} with PSCP Like PuTTY, PSCP can authenticate using a public key instead of a password. There are three ways you can do this. Firstly, PSCP can use PuTTY saved sessions in place of hostnames (see \k{pscp-usage-basics-host}). So you would do this: \b Run PuTTY, and create a PuTTY saved session (see \k{config-saving}) which specifies your private key file (see \k{config-ssh-privkey}). You will probably also want to specify a username to log in as (see \k{config-username}). \b In PSCP, you can now use the name of the session instead of a hostname: type \c{pscp sessionname:file localfile}, where \c{sessionname} is replaced by the name of your saved session. Secondly, you can supply the name of a private key file on the command line, with the \c{-i} option. See \k{using-cmdline-identity} for more information. Thirdly, PSCP will attempt to authenticate using Pageant if Pageant is running (see \k{pageant}). So you would do this: \b Ensure Pageant is running, and has your private key stored in it. \b Specify a user and host name to PSCP as normal. PSCP will automatically detect Pageant and try to use the keys within it. For more general information on public-key authentication, see \k{pubkey}. putty-0.76/doc/psftp.but0000644000175000017500000005351214072266310012200 00000000000000\C{psftp} Using \i{PSFTP} to transfer files securely \i{PSFTP}, the PuTTY SFTP client, is a tool for \i{transferring files} securely between computers using an SSH connection. PSFTP differs from PSCP in the following ways: \b PSCP should work on virtually every SSH server. PSFTP uses the new \i{SFTP} protocol, which is a feature of SSH-2 only. (PSCP will also use this protocol if it can, but there is an SSH-1 equivalent it can fall back to if it cannot.) \b PSFTP allows you to run an interactive file transfer session, much like the Windows \i\c{ftp} program. You can list the contents of directories, browse around the file system, issue multiple \c{get} and \c{put} commands, and eventually log out. By contrast, PSCP is designed to do a single file transfer operation and immediately terminate. \H{psftp-starting} Starting PSFTP The usual way to start PSFTP is from a command prompt, much like PSCP. To do this, it will need either to be on your \i{\c{PATH}} or in your current directory. To add the directory containing PSFTP to your \c{PATH} environment variable, type into the console window: \c set PATH=C:\path\to\putty\directory;%PATH% Unlike PSCP, however, PSFTP has no complex command-line syntax; you just specify a host name and perhaps a user name: \c psftp server.example.com or perhaps \c psftp fred@server.example.com Alternatively, if you just type \c{psftp} on its own (or double-click the PSFTP icon in the Windows GUI), you will see the PSFTP prompt, and a message telling you PSFTP has not connected to any server: \c C:\>psftp \c psftp: no hostname specified; use "open host.name" to connect \c psftp> At this point you can type \c{open server.example.com} or \c{open fred@server.example.com} to start a session. PSFTP accepts all the general command line options supported by the PuTTY tools, except the ones which make no sense in a file transfer utility. See \k{using-general-opts} for a description of these options. (The ones not supported by PSFTP are clearly marked.) PSFTP also supports some of its own options. The following sections describe PSFTP's specific command-line options. \S{psftp-option-b} \I{-b-PSFTP}\c{-b}: specify a file containing batch commands In normal operation, PSFTP is an interactive program which displays a command line and accepts commands from the keyboard. If you need to do automated tasks with PSFTP, you would probably prefer to \I{batch scripts in PSFTP}specify a set of commands in advance and have them executed automatically. The \c{-b} option allows you to do this. You use it with a file name containing batch commands. For example, you might create a file called \c{myscript.scr} containing lines like this: \c cd /home/ftp/users/jeff \c del jam-old.tar.gz \c ren jam.tar.gz jam-old.tar.gz \c put jam.tar.gz \c chmod a+r jam.tar.gz and then you could run the script by typing \c psftp user@hostname -b myscript.scr When you run a batch script in this way, PSFTP will abort the script if any command fails to complete successfully. To change this behaviour, you can add the \c{-be} option (\k{psftp-option-be}). PSFTP will terminate after it finishes executing the batch script. \S{psftp-option-bc} \I{-bc-PSFTP}\c{-bc}: display batch commands as they are run The \c{-bc} option alters what PSFTP displays while processing a batch script specified with \c{-b}. With the \c{-bc} option, PSFTP will display prompts and commands just as if the commands had been typed at the keyboard. So instead of seeing this: \c C:\>psftp fred@hostname -b batchfile \c Sent username "fred" \c Remote working directory is /home/fred \c Listing directory /home/fred/lib \c drwxrwsr-x 4 fred fred 1024 Sep 6 10:42 . \c drwxr-sr-x 25 fred fred 2048 Dec 14 09:36 .. \c drwxrwsr-x 3 fred fred 1024 Apr 17 2000 jed \c lrwxrwxrwx 1 fred fred 24 Apr 17 2000 timber \c drwxrwsr-x 2 fred fred 1024 Mar 13 2000 trn you might see this: \c C:\>psftp fred@hostname -bc -b batchfile \c Sent username "fred" \c Remote working directory is /home/fred \c psftp> dir lib \c Listing directory /home/fred/lib \c drwxrwsr-x 4 fred fred 1024 Sep 6 10:42 . \c drwxr-sr-x 25 fred fred 2048 Dec 14 09:36 .. \c drwxrwsr-x 3 fred fred 1024 Apr 17 2000 jed \c lrwxrwxrwx 1 fred fred 24 Apr 17 2000 timber \c drwxrwsr-x 2 fred fred 1024 Mar 13 2000 trn \c psftp> quit \S{psftp-option-be} \I{-be-PSFTP}\c{-be}: continue batch processing on errors When running a batch file, this additional option causes PSFTP to continue processing even if a command fails to complete successfully. You might want this to happen if you wanted to delete a file and didn't care if it was already not present, for example. \S{psftp-usage-options-batch} \I{-batch-PSFTP}\c{-batch}: avoid interactive prompts If you use the \c{-batch} option, PSFTP will never give an interactive prompt while establishing the connection. If the server's host key is invalid, for example (see \k{gs-hostkey}), then the connection will simply be abandoned instead of asking you what to do next. This may help PSFTP's behaviour when it is used in automated scripts: using \c{-batch}, if something goes wrong at connection time, the batch job will fail rather than hang. \S2{psftp-option-sanitise} \I{-sanitise-stderr}\I{-no-sanitise-stderr}\c{-no-sanitise-stderr}: control error message sanitisation The \c{-no-sanitise-stderr} option will cause PSFTP to pass through the server's standard-error stream literally, without stripping control characters from it first. This might be useful if the server were sending coloured error messages, but it also gives the server the ability to have unexpected effects on your terminal display. For more discussion, see \k{plink-option-sanitise}. \H{psftp-commands} Running PSFTP Once you have started your PSFTP session, you will see a \c{psftp>} prompt. You can now type commands to perform file-transfer functions. This section lists all the available commands. Any line starting with a \cw{#} will be treated as a \i{comment} and ignored. \S{psftp-quoting} \I{quoting, in PSFTP}General quoting rules for PSFTP commands Most PSFTP commands are considered by the PSFTP command interpreter as a sequence of words, separated by spaces. For example, the command \c{ren oldfilename newfilename} splits up into three words: \c{ren} (the command name), \c{oldfilename} (the name of the file to be renamed), and \c{newfilename} (the new name to give the file). Sometimes you will need to specify \I{spaces in filenames}file names that \e{contain} spaces. In order to do this, you can surround the file name with double quotes. This works equally well for local file names and remote file names: \c psftp> get "spacey file name.txt" "save it under this name.txt" The double quotes themselves will not appear as part of the file names; they are removed by PSFTP and their only effect is to stop the spaces inside them from acting as word separators. If you need to \e{use} a double quote (on some types of remote system, such as Unix, you are allowed to use double quotes in file names), you can do this by doubling it. This works both inside and outside double quotes. For example, this command \c psftp> ren ""this"" "a file with ""quotes"" in it" will take a file whose current name is \c{"this"} (with a double quote character at the beginning and the end) and rename it to a file whose name is \c{a file with "quotes" in it}. (The one exception to the PSFTP quoting rules is the \c{!} command, which passes its command line straight to Windows without splitting it up into words at all. See \k{psftp-cmd-pling}.) \S{psftp-wildcards} Wildcards in PSFTP Several commands in PSFTP support \q{\i{wildcards}} to select multiple files. For \e{local} file specifications (such as the first argument to \c{put}), wildcard rules for the local operating system are used. For instance, PSFTP running on Windows might require the use of \c{*.*} where PSFTP on Unix would need \c{*}. For \e{remote} file specifications (such as the first argument to \c{get}), PSFTP uses a standard wildcard syntax (similar to \i{POSIX} wildcards): \b \c{*} matches any sequence of characters (including a zero-length sequence). \b \c{?} matches exactly one character. \b \c{[abc]} matches exactly one character which can be \cw{a}, \cw{b}, or \cw{c}. \lcont{ \c{[a-z]} matches any character in the range \cw{a} to \cw{z}. \c{[^abc]} matches a single character that is \e{not} \cw{a}, \cw{b}, or \cw{c}. Special cases: \c{[-a]} matches a literal hyphen (\cw{-}) or \cw{a}; \c{[^-a]} matches all other characters. \c{[a^]} matches a literal caret (\cw{^}) or \cw{a}. } \b \c{\\} (backslash) before any of the above characters (or itself) removes that character's special meaning. A leading period (\cw{.}) on a filename is not treated specially, unlike in some Unix contexts; \c{get *} will fetch all files, whether or not they start with a leading period. \S{psftp-cmd-open} The \c{open} command: start a session If you started PSFTP by double-clicking in the GUI, or just by typing \c{psftp} at the command line, you will need to open a connection to an SFTP server before you can issue any other commands (except \c{help} and \c{quit}). To create a connection, type \c{open host.name}, or if you need to specify a user name as well you can type \c{open user@host.name}. You can optionally specify a port as well: \c{open user@host.name 22}. Once you have issued this command, you will not be able to issue it again, \e{even} if the command fails (for example, if you mistype the host name or the connection times out). So if the connection is not opened successfully, PSFTP will terminate immediately. \S{psftp-cmd-quit} The \c{quit} command: end your session When you have finished your session, type the command \c{quit} to close the connection, terminate PSFTP and return to the command line (or just close the PSFTP console window if you started it from the GUI). You can also use the \c{bye} and \c{exit} commands, which have exactly the same effect. \S{psftp-cmd-close} The \c{close} command: close your connection If you just want to close the network connection but keep PSFTP running, you can use the \c{close} command. You can then use the \c{open} command to open a new connection. \S{psftp-cmd-help} The \c{help} command: get quick online help If you type \c{help}, PSFTP will give a short list of the available commands. If you type \c{help} with a command name - for example, \c{help get} - then PSFTP will give a short piece of help on that particular command. \S{psftp-cmd-cd} The \c{cd} and \c{pwd} commands: changing the remote \i{working directory} PSFTP maintains a notion of your \q{working directory} on the server. This is the default directory that other commands will operate on. For example, if you type \c{get filename.dat} then PSFTP will look for \c{filename.dat} in your remote working directory on the server. To change your remote working directory, use the \c{cd} command. If you don't provide an argument, \c{cd} will return you to your home directory on the server (more precisely, the remote directory you were in at the start of the connection). To display your current remote working directory, type \c{pwd}. \S{psftp-cmd-lcd} The \c{lcd} and \c{lpwd} commands: changing the local \i{working directory} As well as having a working directory on the remote server, PSFTP also has a working directory on your local machine (just like any other Windows process). This is the default local directory that other commands will operate on. For example, if you type \c{get filename.dat} then PSFTP will save the resulting file as \c{filename.dat} in your local working directory. To change your local working directory, use the \c{lcd} command. To display your current local working directory, type \c{lpwd}. \S{psftp-cmd-get} The \c{get} command: fetch a file from the server To \i{download a file} from the server and store it on your local PC, you use the \c{get} command. In its simplest form, you just use this with a file name: \c get myfile.dat If you want to store the file locally under a different name, specify the local file name after the remote one: \c get myfile.dat newname.dat This will fetch the file on the server called \c{myfile.dat}, but will save it to your local machine under the name \c{newname.dat}. To fetch an entire directory \i{recursive}ly, you can use the \c{-r} option: \c get -r mydir \c get -r mydir newname (If you want to fetch a file whose name starts with a hyphen, you may have to use the \c{--} special argument, which stops \c{get} from interpreting anything as a switch after it. For example, \cq{get -- -silly-name-}.) \S{psftp-cmd-put} The \c{put} command: send a file to the server To \i{upload a file} to the server from your local PC, you use the \c{put} command. In its simplest form, you just use this with a file name: \c put myfile.dat If you want to store the file remotely under a different name, specify the remote file name after the local one: \c put myfile.dat newname.dat This will send the local file called \c{myfile.dat}, but will store it on the server under the name \c{newname.dat}. To send an entire directory \i{recursive}ly, you can use the \c{-r} option: \c put -r mydir \c put -r mydir newname (If you want to send a file whose name starts with a hyphen, you may have to use the \c{--} special argument, which stops \c{put} from interpreting anything as a switch after it. For example, \cq{put -- -silly-name-}.) \S{psftp-cmd-mgetput} The \c{mget} and \c{mput} commands: fetch or send multiple files \c{mget} works almost exactly like \c{get}, except that it allows you to specify more than one file to fetch at once. You can do this in two ways: \b by giving two or more explicit file names (\cq{mget file1.txt file2.txt}) \b by using a wildcard (\cq{mget *.txt}). Every argument to \c{mget} is treated as the name of a file to fetch (unlike \c{get}, which will interpret at most one argument like that, and a second argument will be treated as an alternative name under which to store the retrieved file), or a \i{wildcard} expression matching more than one file. The \c{-r} and \c{--} options from \c{get} are also available with \c{mget}. \c{mput} is similar to \c{put}, with the same differences. \S{psftp-cmd-regetput} The \c{reget} and \c{reput} commands: \i{resuming file transfers} If a file transfer fails half way through, and you end up with half the file stored on your disk, you can resume the file transfer using the \c{reget} and \c{reput} commands. These work exactly like the \c{get} and \c{put} commands, but they check for the presence of the half-written destination file and start transferring from where the last attempt left off. The syntax of \c{reget} and \c{reput} is exactly the same as the syntax of \c{get} and \c{put}: \c reget myfile.dat \c reget myfile.dat newname.dat \c reget -r mydir These commands are intended mainly for resuming interrupted transfers. They assume that the remote file or directory structure has not changed in any way; if there have been changes, you may end up with corrupted files. In particular, the \c{-r} option will not pick up changes to files or directories already transferred in full. \S{psftp-cmd-dir} The \c{dir} command: \I{listing files}list remote files To list the files in your remote working directory, just type \c{dir}. You can also list the contents of a different directory by typing \c{dir} followed by the directory name: \c dir /home/fred \c dir sources And you can list a subset of the contents of a directory by providing a wildcard: \c dir /home/fred/*.txt \c dir sources/*.c The \c{ls} command works exactly the same way as \c{dir}. \S{psftp-cmd-chmod} The \c{chmod} command: change permissions on remote files \I{changing permissions on files}PSFTP allows you to modify the file permissions on files and directories on the server. You do this using the \c{chmod} command, which works very much like the Unix \c{chmod} command. The basic syntax is \c{chmod modes file}, where \c{modes} represents a modification to the file permissions, and \c{file} is the filename to modify. You can specify multiple files or wildcards. For example: \c chmod go-rwx,u+w privatefile \c chmod a+r public* \c chmod 640 groupfile1 groupfile2 The \c{modes} parameter can be a set of octal digits in the Unix style. (If you don't know what this means, you probably don't want to be using it!) Alternatively, it can be a list of permission modifications, separated by commas. Each modification consists of: \b The people affected by the modification. This can be \c{u} (the owning user), \c{g} (members of the owning group), or \c{o} (everybody else - \q{others}), or some combination of those. It can also be \c{a} (\q{all}) to affect everybody at once. \b A \c{+} or \c{-} sign, indicating whether permissions are to be added or removed. \b The actual permissions being added or removed. These can be \I{read permission}\c{r} (permission to read the file), \I{write permission}\c{w} (permission to write to the file), and \I{execute permission}\c{x} (permission to execute the file, or in the case of a directory, permission to access files within the directory). So the above examples would do: \b The first example: \c{go-rwx} removes read, write and execute permissions for members of the owning group and everybody else (so the only permissions left are the ones for the file owner). \c{u+w} adds write permission for the file owner. \b The second example: \c{a+r} adds read permission for everybody to all files and directories starting with \q{public}. In addition to all this, there are a few extra special cases for \i{Unix} systems. On non-Unix systems these are unlikely to be useful: \b You can specify \c{u+s} and \c{u-s} to add or remove the Unix \i{set-user-ID bit}. This is typically only useful for special purposes; refer to your Unix documentation if you're not sure about it. \b You can specify \c{g+s} and \c{g-s} to add or remove the Unix \i{set-group-ID bit}. On a file, this works similarly to the set-user-ID bit (see your Unix documentation again); on a directory it ensures that files created in the directory are accessible by members of the group that owns the directory. \b You can specify \c{+t} and \c{-t} to add or remove the Unix \q{\i{sticky bit}}. When applied to a directory, this means that the owner of a file in that directory can delete the file (whereas normally only the owner of the \e{directory} would be allowed to). \S{psftp-cmd-del} The \c{del} command: delete remote files To \I{deleting files}delete a file on the server, type \c{del} and then the filename or filenames: \c del oldfile.dat \c del file1.txt file2.txt \c del *.o Files will be deleted without further prompting, even if multiple files are specified. \c{del} will only delete files. You cannot use it to delete directories; use \c{rmdir} for that. The \c{rm} command works exactly the same way as \c{del}. \S{psftp-cmd-mkdir} The \c{mkdir} command: create remote directories To \i{create a directory} on the server, type \c{mkdir} and then the directory name: \c mkdir newstuff You can specify multiple directories to create at once: \c mkdir dir1 dir2 dir3 \S{psftp-cmd-rmdir} The \c{rmdir} command: remove remote directories To \i{remove a directory} on the server, type \c{rmdir} and then the directory name or names: \c rmdir oldstuff \c rmdir *.old ancient Directories will be deleted without further prompting, even if multiple directories are specified. Most SFTP servers will probably refuse to remove a directory if the directory has anything in it, so you will need to delete the contents first. \S{psftp-cmd-mv} The \c{mv} command: move and \i{rename remote files} To rename a single file on the server, type \c{mv}, then the current file name, and then the new file name: \c mv oldfile newname You can also move the file into a different directory and change the name: \c mv oldfile dir/newname To move one or more files into an existing subdirectory, specify the files (using wildcards if desired), and then the destination directory: \c mv file dir \c mv file1 dir1/file2 dir2 \c mv *.c *.h .. The \c{rename} and \c{ren} commands work exactly the same way as \c{mv}. \S{psftp-cmd-pling} The \c{!} command: run a \i{local Windows command} You can run local Windows commands using the \c{!} command. This is the only PSFTP command that is not subject to the command quoting rules given in \k{psftp-quoting}. If any command line begins with the \c{!} character, then the rest of the line will be passed straight to Windows without further translation. For example, if you want to move an existing copy of a file out of the way before downloading an updated version, you might type: \c psftp> !ren myfile.dat myfile.bak \c psftp> get myfile.dat using the Windows \c{ren} command to rename files on your local PC. \H{psftp-pubkey} Using \i{public key authentication} with PSFTP Like PuTTY, PSFTP can authenticate using a public key instead of a password. There are three ways you can do this. Firstly, PSFTP can use PuTTY saved sessions in place of hostnames. So you might do this: \b Run PuTTY, and create a PuTTY saved session (see \k{config-saving}) which specifies your private key file (see \k{config-ssh-privkey}). You will probably also want to specify a username to log in as (see \k{config-username}). \b In PSFTP, you can now use the name of the session instead of a hostname: type \c{psftp sessionname}, where \c{sessionname} is replaced by the name of your saved session. Secondly, you can supply the name of a private key file on the command line, with the \c{-i} option. See \k{using-cmdline-identity} for more information. Thirdly, PSFTP will attempt to authenticate using Pageant if Pageant is running (see \k{pageant}). So you would do this: \b Ensure Pageant is running, and has your private key stored in it. \b Specify a user and host name to PSFTP as normal. PSFTP will automatically detect Pageant and try to use the keys within it. For more general information on public-key authentication, see \k{pubkey}. putty-0.76/doc/pubkey.but0000644000175000017500000005744414072266310012353 00000000000000\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 secure and more flexible, but more difficult to set up. In conventional password authentication, you prove you are who you claim to be by proving that you know the correct password. The only way to prove you know the password is to tell the server what you think the password is. This means that if the server has been hacked, or \i\e{spoofed} (see \k{gs-hostkey}), an attacker can learn your password. Public key authentication solves this problem. You generate a \i\e{key pair}, consisting of a \i{public key} (which everybody is allowed to know) and a \i{private key} (which you keep secret and do not give to anybody). The private key is able to generate \i\e{signatures}. A signature created using your private key cannot be forged by anybody who does not have that key; but anybody who has your public key can verify that a particular signature is genuine. So you generate a key pair on your own computer, and you copy the public key to the server. Then, when the server asks you to prove who you are, PuTTY can generate a signature using your private key. The server can verify that signature (since it has your public key) and allow you to log in. Now if the server is hacked or spoofed, the attacker does not gain your private key or password; they only gain one signature. And signatures cannot be re-used, so they have gained nothing. There is a problem with this: if your private key is stored unprotected on your own computer, then anybody who gains access to \e{that} will be able to generate signatures as if they were you. So they will be able to log in to your server under your account. For this reason, your private key is usually \i\e{encrypted} when it is stored on your local machine, using a \i{passphrase} of your choice. In order to generate a signature, PuTTY must decrypt the key, so you have to type your passphrase. This can make public-key authentication less convenient than password authentication: every time you log in to the server, instead of typing a short password, you have to type a longer passphrase. One solution to this is to use an \i\e{authentication agent}, a separate program which holds decrypted private keys and generates signatures on request. PuTTY's authentication agent is called \i{Pageant}. When you begin a Windows session, you start Pageant and load your private key into it (typing your passphrase once). For the rest of your session, you can start PuTTY any number of times and Pageant will automatically generate signatures without you having to do anything. When you close your Windows session, Pageant shuts down, without ever having stored your decrypted private key on disk. Many people feel this is a good compromise between security and convenience. See \k{pageant} for further details. There is more than one \i{public-key algorithm} available. The most common are \i{RSA} and \i{ECDSA}, but others exist, notably \i{DSA} (otherwise known as DSS), the USA's federal Digital Signature Standard. The key types supported by PuTTY are described in \k{puttygen-keytype}. \H{pubkey-puttygen} Using \i{PuTTYgen}, the PuTTY key generator PuTTYgen is a key generator. It \I{generating keys}generates pairs of public and private keys to be used with PuTTY, PSCP, and Plink, as well as the PuTTY authentication agent, Pageant (see \k{pageant}). PuTTYgen generates RSA, DSA, ECDSA, and EdDSA keys. When you run PuTTYgen you will see a window where you have two main choices: \q{Generate}, to generate a new public/private key pair, or \q{Load} to load in an existing private key. \S{puttygen-generating} Generating a new key This is a general outline of the procedure for generating a new key pair. The following sections describe the process in more detail. \b First, you need to select which type of key you want to generate, and also select the strength of the key. This is described in more detail in \k{puttygen-keytype} and \k{puttygen-strength}. \b Then press the \q{Generate} button, to actually generate the key. \K{puttygen-generate} describes this step. \b Once you have generated the key, select a comment field (\k{puttygen-comment}) and a passphrase (\k{puttygen-passphrase}). \b Now you're ready to save the private key to disk; press the \q{Save private key} button. (See \k{puttygen-savepriv}). Your key pair is now ready for use. You may also want to copy the public key to your server, either by copying it out of the \q{Public key for pasting into OpenSSH authorized_keys file} box (see \k{puttygen-pastekey}), or by using the \q{Save public key} button (\k{puttygen-savepub}). However, you don't need to do this immediately; if you want, you can load the private key back into PuTTYgen later (see \k{puttygen-load}) and the public key will be available for copying and pasting again. \K{pubkey-gettingready} describes the typical process of configuring PuTTY to attempt public-key authentication, and configuring your SSH server to accept it. \S{puttygen-keytype} Selecting the type of key Before generating a key pair using PuTTYgen, you need to select which type of key you need. The current version of the SSH protocol, SSH-2, supports several different key types, although specific servers may not support all of them. PuTTYgen can generate: \b An \i{RSA} key for use with the SSH-2 protocol. \b A \i{DSA} key for use with the SSH-2 protocol. \b An \i{ECDSA} (\i{elliptic curve} DSA) key for use with the SSH-2 protocol. \b An \i{EdDSA} key (Edwards-curve DSA, another elliptic curve algorithm) for use with the SSH-2 protocol. PuTTYgen can also generate an RSA key suitable for use with the old SSH-1 protocol (which only supports RSA); for this, you need to select the \q{SSH-1 (RSA)} option. Since the SSH-1 protocol is no longer considered secure, it's rare to need this option. \S{puttygen-strength} Selecting the size (strength) of the key The \q{Number of bits} input box allows you to choose the strength of the key PuTTYgen will generate. \b For RSA and DSA, 2048 bits should currently be sufficient for most purposes. \b For ECDSA, only 256, 384, and 521 bits are supported. (ECDSA offers equivalent security to RSA with smaller key sizes.) \b For EdDSA, the only valid sizes are 255 bits (these keys are also known as \q{\i{Ed25519}} and are commonly used) and 448 bits (\q{\i{Ed448}}, which is much less common at the time of writing). (256 is also accepted for backward compatibility, but the effect is the same as 255.) \S{puttygen-primes} Selecting the \i{prime generation method} On the \q{Key} menu, you can also optionally change the method for generating the prime numbers used in the generated key. This is used for RSA and DSA keys only. (The other key types don't require generating prime numbers at all.) The prime-generation method does not affect compatibility: a key generated with any of these methods will still work with all the same SSH servers. If you don't care about this, it's entirely sensible to leave it on the default setting. The available methods are: \b Use \i{probable primes} (fast) \b Use \i{proven primes} (slower) \b Use proven primes with even distribution (slowest) The \q{probable primes} method sounds unsafe, but it's the most commonly used prime-generation strategy. There is in theory a possibility that it might accidentally generate a number that isn't prime, but the software does enough checking to make that probability vanishingly small (less than 1 in 2^80, or 1 in 10^24). So, in practice, nobody worries about it very much. The other methods cause PuTTYgen to use numbers that it is \e{sure} are prime, because it generates the output number together with a proof of its primality. This takes more effort, but it eliminates that theoretical risk in the probabilistic method. You might choose to switch from probable to proven primes if you have a local security standard that demands it, or if you don't trust the probabilistic argument for the safety of the usual method. For RSA keys, there's also an option on the \q{Key} menu to use \i{\q{strong} primes} as the prime factors of the public key. A \q{strong} prime is a prime number chosen to have a particular structure that makes certain factoring algorithms more difficult to apply, so some security standards recommend their use. However, the most modern factoring algorithms are unaffected, so this option is probably not worth turning on \e{unless} you have a local standard that recommends it. \S{puttygen-generate} The \q{Generate} button 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. First, a progress bar will appear and PuTTYgen will ask you to move the mouse around to generate randomness. Wave the mouse in circles over the blank area in the PuTTYgen window, and the progress bar will gradually fill up as PuTTYgen collects enough randomness. You don't need to wave the mouse in particularly imaginative patterns (although it can't hurt); PuTTYgen will collect enough randomness just from the fine detail of \e{exactly} how far the mouse has moved each time Windows samples its position. When the progress bar reaches the end, PuTTYgen will begin creating the key. The progress bar will reset to the start, and gradually move up again to track the progress of the key generation. It will not move evenly, and may occasionally slow down to a stop; this is unfortunately unavoidable, because key generation is a random process and it is impossible to reliably predict how long it will take. When the key generation is complete, a new set of controls will appear in the window to indicate this. \S{puttygen-fingerprint} The \q{\ii{Key fingerprint}} box The \q{Key fingerprint} box shows you a fingerprint value for the generated key. This is derived cryptographically from the \e{public} key value, so it doesn't need to be kept secret; it is supposed to be more manageable for human beings than the public key itself. The fingerprint value is intended to be cryptographically secure, in the sense that it is computationally infeasible for someone to invent a second key with the same fingerprint, or to find a key with a particular fingerprint. So some utilities, such as the Pageant key list box (see \k{pageant-mainwin-keylist}) and the Unix \c{ssh-add} utility, will list key fingerprints rather than the whole public key. By default, PuTTYgen will display fingerprints in the \q{SHA256} format. If you need to see the fingerprint in the older \q{MD5} format (which looks like \c{aa:bb:cc:...}), you can choose \q{Show fingerprint as MD5} from the \q{Key} menu, but bear in mind that this is less cryptographically secure; it may be feasible for an attacker to create a key with the same fingerprint as yours. \S{puttygen-comment} Setting a comment for your key If you have more than one key and use them for different purposes, you don't need to memorise the key fingerprints in order to tell them apart. PuTTYgen allows you to enter a \e{comment} for your key, which will be displayed whenever PuTTY or Pageant asks you for the passphrase. The default comment format, if you don't specify one, contains the key type and the date of generation, such as \c{rsa-key-20011212}. Another commonly used approach is to use your name and the name of the computer the key will be used on, such as \c{simon@simons-pc}. To alter the key comment, just type your comment text into the \q{Key comment} box before saving the private key. If you want to change the comment later, you can load the private key back into PuTTYgen, change the comment, and save it again. \S{puttygen-passphrase} Setting a \i{passphrase} for your key The \q{Key passphrase} and \q{Confirm passphrase} boxes allow you to choose a passphrase for your key. The passphrase will be used to \i{encrypt} the key on disk, so you will not be able to use the key without first entering the passphrase. When you save the key, PuTTYgen will check that the \q{Key passphrase} and \q{Confirm passphrase} boxes both contain exactly the same passphrase, and will refuse to save the key otherwise. If you leave the passphrase fields blank, the key will be saved unencrypted. You should \e{not} do this without good reason; if you do, your private key file on disk will be all an attacker needs to gain access to any machine configured to accept that key. If you want to be able to \I{passwordless login}log in without having to type a passphrase every time, you should consider using Pageant (\k{pageant}) so that your decrypted key is only held in memory rather than on disk. Under special circumstances you may genuinely \e{need} to use a key with no passphrase; for example, if you need to run an automated batch script that needs to make an SSH connection, you can't be there to type the passphrase. In this case we recommend you generate a special key for each specific batch script (or whatever) that needs one, and on the server side you should arrange that each key is \e{restricted} so that it can only be used for that specific purpose. The documentation for your SSH server should explain how to do this (it will probably vary between servers). Choosing a good passphrase is difficult. Just as you shouldn't use a dictionary word as a password because it's easy for an attacker to run through a whole dictionary, you should not use a song lyric, quotation or other well-known sentence as a passphrase. \i{DiceWare} (\W{http://www.diceware.com/}\cw{www.diceware.com}) recommends using at least five words each generated randomly by rolling five dice, which gives over 2^64 possible passphrases and is probably not a bad scheme. If you want your passphrase to make grammatical sense, this cuts down the possibilities a lot and you should use a longer one as a result. \e{Do not forget your passphrase}. There is no way to recover it. \S{puttygen-savepriv} Saving your private key to a disk file Once you have generated a key, set a comment field and set a passphrase, you are ready to save your private key to disk. Press the \q{Save private key} button. PuTTYgen will put up a dialog box asking you where to save the file. Select a directory, type in a file name, and press \q{Save}. This file is in PuTTY's native format (\c{*.\i{PPK}}); it is the one you will need to tell PuTTY to use for authentication (see \k{config-ssh-privkey}) or tell Pageant to load (see \k{pageant-mainwin-addkey}). (You can optionally change some details of the PPK format for your saved key files; see \k{puttygen-save-params}. But The defaults should be fine for most purposes.) \S{puttygen-savepub} Saving your public key to a disk file RFC 4716 specifies a \I{SSH-2 public key format}standard format for storing SSH-2 public keys on disk. Some SSH servers (such as \i\cw{ssh.com}'s) require a public key in this format in order to accept authentication with the corresponding private key. (Others, such as OpenSSH, use a different format; see \k{puttygen-pastekey}.) To save your public key in the SSH-2 standard format, press the \q{Save public key} button in PuTTYgen. PuTTYgen will put up a dialog box asking you where to save the file. Select a directory, type in a file name, and press \q{Save}. You will then probably want to copy the public key file to your SSH server machine. See \k{pubkey-gettingready} for general instructions on configuring public-key authentication once you have generated a key. If you use this option with an SSH-1 key, the file PuTTYgen saves will contain exactly the same text that appears in the \q{Public key for pasting} box. This is the only existing standard for SSH-1 public keys. \S{puttygen-pastekey} \q{Public key for pasting into OpenSSH \i{authorized_keys file}} The \i{OpenSSH} server, among others, requires your public key to be given to it in a one-line format before it will accept authentication with your private key. (SSH-1 servers also used this method.) The \q{Public key for pasting into OpenSSH authorized_keys file} gives the public-key data in the correct one-line format. Typically you will want to select the entire contents of the box using the mouse, press Ctrl+C to copy it to the clipboard, and then paste the data into a PuTTY session which is already connected to the server. See \k{pubkey-gettingready} for general instructions on configuring public-key authentication once you have generated a key. \S{puttygen-save-params} Parameters for saving key files Selecting \q{Parameters for saving key files...} from the \q{Key} menu lets you adjust some aspects of PPK-format private key files stored on disk. None of these options affect compatibility with SSH servers. In most cases, it's entirely sensible to leave all of these at their default settings. \S2{puttygen-save-ppk-version} PPK file version This defaults to version 3, which is fine for most uses. You might need to select PPK version 2 if you need your private key file to be loadable in older versions of PuTTY (0.74 and older), or in other tools which do not yet support the version 3 format (which was introduced in 2021). The version 2 format is less resistant to brute-force decryption, and doesn't support any of the following options to control that. \S2{puttygen-save-passphrase-hashing} Options affecting \i{passphrase hashing} All of the following options only affect keys saved with passphrases. They control how much work is required to decrypt the key (which happens every time you type its passphrase). This allows you to trade off the cost of legitimate use of the key against the resistance of the encrypted key to password-guessing attacks. These options only affect PPK version 3. \dt Key derivation function \dd The variant of the \i{Argon2} key derivation function to use. You might change this if you consider your exposure to side-channel attacks to be different to the norm. \dt Memory to use for passphrase hash \dd The amount of memory needed to decrypt the key, in Kbyte. \dt Time to use for passphrase hash \dd Controls how much time is required to attempt decrypting the key. You can either specify an approximate time in milliseconds (on this machine), or explicitly specify a number of hash passes (which is what the time is turned into during encryption). \dt Parallelism for passphrase hash \dd Number of parallelisable threads that can be used to decrypt the key. The default, 1, forces the process to run single-threaded, even on machines with multiple cores. \S{puttygen-load} Reloading a private key PuTTYgen allows you to load an existing private key file into memory. If you do this, you can then change the passphrase and comment before saving it again; you can also make extra copies of the public key. To load an existing key, press the \q{Load} button. PuTTYgen will put up a dialog box where you can browse around the file system and find your key file. Once you select the file, PuTTYgen will ask you for a passphrase (if necessary) and will then display the key details in the same way as if it had just generated the key. If you use the Load command to load a foreign key format, it will work, but you will see a message box warning you that the key you have loaded is not a PuTTY native key. See \k{puttygen-conversions} for information about importing foreign key formats. \S{puttygen-conversions} Dealing with private keys in other formats SSH-2 private keys have no standard format. \I{OpenSSH private key format}OpenSSH and \I{ssh.com private key format}\cw{ssh.com} have different formats, and PuTTY's is different again. So a key generated with one client cannot immediately be used with another. Using the \I{importing keys}\q{Import} command from the \q{Conversions} menu, PuTTYgen can load SSH-2 private keys in OpenSSH's format and \cw{ssh.com}'s format. Once you have loaded one of these key types, you can then save it back out as a PuTTY-format key (\c{*.\i{PPK}}) so that you can use it with the PuTTY suite. The passphrase will be unchanged by this process (unless you deliberately change it). You may want to change the key comment before you save the key, since some OpenSSH key formats contained no space for a comment, and \cw{ssh.com}'s default comment format is long and verbose. PuTTYgen can also \i{export private keys} in OpenSSH format and in \cw{ssh.com} format. To do so, select one of the \q{Export} options from the \q{Conversions} menu. Exporting a key works exactly like saving it (see \k{puttygen-savepriv}) - you need to have typed your passphrase in beforehand, and you will be warned if you are about to save a key without a passphrase. For OpenSSH there are two options. Modern OpenSSH actually has two formats it uses for storing private keys. \q{Export OpenSSH key} will automatically choose the oldest format supported for the key type, for maximum backward compatibility with older versions of OpenSSH; for newer key types like Ed25519, it will use the newer format as that is the only legal option. If you have some specific reason for wanting to use OpenSSH's newer format even for RSA, DSA, or ECDSA keys, you can choose \q{Export OpenSSH key (force new file format)}. Most clients for the older SSH-1 protocol use a standard format for storing private keys on disk. PuTTY uses this format as well; so if you have generated an SSH-1 private key using OpenSSH or \cw{ssh.com}'s client, you can use it with PuTTY, and vice versa. Hence, the export options are not available if you have generated an SSH-1 key. \H{pubkey-gettingready} Getting ready for public key authentication Connect to your SSH server using PuTTY with the SSH protocol. When the connection succeeds you will be prompted for your user name and password to login. Once logged in, you must configure the server to accept your public key for authentication: \b If your server is \i{OpenSSH}, you should change into the \i\c{.ssh} directory under your home directory, and open the file \i\c{authorized_keys} with your favourite editor. (You may have to create this file, if this is the first key you have put in it.) Then switch to the PuTTYgen window, select all of the text in the \q{Public key for pasting into OpenSSH authorized_keys file} box (see \k{puttygen-pastekey}), and copy it to the clipboard (\c{Ctrl+C}). Then, switch back to the PuTTY window and insert the data into the open file, making sure it ends up all on one line. Save the file. \lcont{ (In very old versions of OpenSSH, SSH-2 keys had to be put in a separate file called \c{authorized_keys2}. In all current versions, the same \c{authorized_keys} file is used for both SSH-1 and SSH-2 keys.) } \b If your server is \i\cw{ssh.com}'s product and is using SSH-2, you need to save a \e{public} key file from PuTTYgen (see \k{puttygen-savepub}), and copy that into the \i\c{.ssh2} directory on the server. Then you should go into that \c{.ssh2} directory, and edit (or create) a file called \c{authorization}. In this file you should put a line like \c{Key mykey.pub}, with \c{mykey.pub} replaced by the name of your key file. \b For other SSH server software, you should refer to the manual for that server. You may also need to ensure that your home directory, your \c{.ssh} directory, and any other files involved (such as \c{authorized_keys}, \c{authorized_keys2} or \c{authorization}) are not group-writable or world-writable; servers will typically ignore the keys unless this is done. You can typically do this by using a command such as \c chmod go-w $HOME $HOME/.ssh $HOME/.ssh/authorized_keys Your server should now be configured to accept authentication using your private key. Now you need to configure PuTTY to \e{attempt} authentication using your private key. You can do this in any of three ways: \b Select the private key in PuTTY's configuration. See \k{config-ssh-privkey} for details. \b Specify the key file on the command line with the \c{-i} option. See \k{using-cmdline-identity} for details. \b Load the private key into Pageant (see \k{pageant}). In this case PuTTY will automatically try to use it for authentication if it can. putty-0.76/doc/pubkeyfmt.but0000644000175000017500000003670614072266310013060 00000000000000\A{ppk} PPK file format This appendix documents the file format used by PuTTY to store private keys. In this appendix, binary data structures are described using data type representations such as \cq{uint32}, \cq{string} and \cq{mpint} as used in the SSH protocol standards themselves. These are defined authoritatively by \W{https://tools.ietf.org/html/rfc4251#section-5}{RFC 4251 section 5}, \q{Data Type Representations Used in the SSH Protocols}. \H{ppk-overview} Overview A PPK file stores a private key, and the corresponding public key. Both are contained in the same file. The file format can be completely unencrypted, or it can encrypt the private key. The \e{public} key is stored in cleartext in both cases. (This enables PuTTY to send the public key to an SSH server to see whether it will accept it, and not bother prompting for the passphrase unless the server says yes.) When the key file is encrypted, the encryption key is derived from a passphrase. An encrypted PPK file is also tamper-proofed using a MAC (authentication code), also derived from the same passphrase. The MAC protects the encrypted private key data, but it also covers the cleartext parts of the file. So you can't edit the public half of the key without invalidating the MAC and causing the key file as a whole to become useless. This MAC protects the key file against active cryptographic attacks in which the public half of a key pair is modified in a controlled way that allows an attacker to deduce information about the private half from the resulting corrupted signatures. Any attempt to do that to a PPK file should be reliably caught by the MAC failing to validate. (Such an attack would only be useful if the key file was stored in a location where the attacker could modify it without also having full access to the process that you type passphrases into. But that's not impossible; for example, if your home directory was on a network file server, then the file server's administrator could access the key file but not processes on the client machine.) The MAC also covers the \e{comment} on the key. This stops an attacker from swapping keys with each other and editing the comments to disguise the fact. As a consequence, PuTTYgen cannot edit the comment on a key unless you decrypt the key with your passphrase first. (The circumstances in which \e{that} attack would be useful are even more restricted. One example might be that the different keys trigger specific actions on the server you're connecting to and one of those actions is more useful to the attacker than the other. But once you have a MAC at all, it's no extra effort to make it cover as much as possible, and usually sensible.) \H{ppk-outer} Outer layer The outer layer of a PPK file is text-based. The PuTTY tools will always use LF line termination when writing PPK files, but will tolerate CR+LF and CR-only on input. The first few lines identify it as a PPK, and give some initial data about what's stored in it and how. They look like this: \c PuTTY-User-Key-File-version: algorithm-name \e bbbbbbb bbbbbbbbbbbbbb \c Encryption: encryption-type \e bbbbbbbbbbbbbbb \c Comment: key-comment-string \e bbbbbbbbbbbbbbbbbb \s{version} is a decimal number giving the version number of the file format itself. The current file format version is 3. \s{algorithm-name} is the SSH protocol identifier for the public key algorithm that this key is used for (such as \cq{ssh-dss} or \cq{ecdsa-sha2-nistp384}). \s{encryption-type} indicates whether this key is stored encrypted, and if so, by what method. Currently the only supported encryption types are \cq{aes256-cbc} and \cq{none}. \s{key-comment-string} is a free text field giving the comment. This can contain any byte values other than 13 and 10 (CR and LF). The next part of the file gives the public key. This is stored unencrypted but base64-encoded (\W{https://tools.ietf.org/html/rfc4648}{RFC 4648}), and is preceded by a header line saying how many lines of base64 data are shown, looking like this: \c Public-Lines: number-of-lines \e bbbbbbbbbbbbbbb \c that many lines of base64 data \e bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb The base64-encoded data in this blob is formatted in exactly the same way as an SSH public key sent over the wire in the SSH protocol itself. That is also the same format as the base64 data stored in OpenSSH's \c{authorized_keys} file, except that in a PPK file the base64 data is split across multiple lines. But if you remove the newlines from the middle of this section, the resulting base64 blob is in the right format to go in an \c{authorized_keys} line. If the key is encrypted (i.e. \s{encryption-type} is not \cq{none}), then the next thing that appears is a sequence of lines specifying how the keys for encrypting the file are to be derived from the passphrase: \c Key-Derivation: argon2-flavour \e bbbbbbbbbbbbbb \c Argon2-Memory: decimal-integer \e bbbbbbbbbbbbbbb \c Argon2-Passes: decimal-integer \e bbbbbbbbbbbbbbb \c Argon2-Parallelism: decimal-integer \e bbbbbbbbbbbbbbb \c Argon2-Salt: hex-string \e bbbbbbbbbb \s{argon2-flavour} is one of the identifiers \cq{Argon2d}, \cq{Argon2i} or \cq{Argon2id}, all describing variants of the Argon2 password-hashing function. The three integer values are used as parameters for Argon2, which allows you to configure the amount of memory used (in Kbyte), the number of passes of the algorithm to run (to tune its running time), and the degree of parallelism required by the hash function. The salt is decoded into a sequence of binary bytes and used as an additional input to Argon2. (It is chosen randomly when the key file is written, so that a guessing attack can't be mounted in parallel against multiple key files.) The next part of the file gives the private key. This is base64-encoded in the same way: \c Private-Lines: number-of-lines \e bbbbbbbbbbbbbbb \c that many lines of base64 data \e bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb The binary data represented in this base64 blob may be encrypted, depending on the \e{encryption-type} field in the key file header shown above: \b If \s{encryption-type} is \cq{none}, then this data is stored in plain text. \b If \s{encryption-type} is \cq{aes256-cbc}, then this data is encrypted using AES, with a 256-bit key length, in the CBC cipher mode. The key and initialisation vector are derived from the passphrase: see \k{ppk-keys}. \lcont{ In order to encrypt the private key data with AES, it must be a multiple of 16 bytes (the AES cipher block length). This is achieved by appending random padding to the data before encrypting it. When decoding it after decryption, the random data can be ignored: the internal structure of the data is enough to tell you when you've reached the end of the meaningful part. } Unlike public keys, the binary encoding of private keys is not specified at all in the SSH standards. See \k{ppk-privkeys} for details of the private key format for each key type supported by PuTTY. The final thing in the key file is the MAC: \c Private-MAC: hex-mac-data \e bbbbbbbbbbbb \s{hex-mac-data} is a hexadecimal-encoded value, 64 digits long (i.e. 32 bytes), generated using the HMAC-SHA-256 algorithm with the following binary data as input: \b \cw{string}: the \s{algorithm-name} header field. \b \cw{string}: the \s{encryption-type} header field. \b \cw{string}: the \s{key-comment-string} header field. \b \cw{string}: the binary public key data, as decoded from the base64 lines after the \cq{Public-Lines} header. \b \cw{string}: the plaintext of the binary private key data, as decoded from the base64 lines after the \cq{Private-Lines} header. If that data was stored encrypted, then the decrypted version of it is used in this MAC preimage, \e{including} the random padding mentioned above. The MAC key is derived from the passphrase: see \k{ppk-keys}. \H{ppk-privkeys} Private key encodings This section describes the private key format for each key type supported by PuTTY. Because the PPK format also contains the public key (and both public and private key are protected by the same MAC to ensure they can't be made inconsistent), there is no need for the private key section of the file to repeat data from the public section. So some of these formats are very short. In all cases, a decoding application can begin reading from the start of the decrypted private key data, and know when it has read all that it needs. This allows random padding after the meaningful data to be safely ignored. \S{ppk-privkey-rsa} RSA RSA keys are stored using an \s{algorithm-name} of \cq{ssh-rsa}. (Keys stored like this are also used by the updated RSA signature schemes that use hashes other than SHA-1.) The public key data has already provided the key modulus and the public encoding exponent. The private data stores: \b \cw{mpint}: the private decoding exponent of the key. \b \cw{mpint}: one prime factor \e{p} of the key. \b \cw{mpint}: the other prime factor \e{q} of the key. (RSA keys stored in this format are expected to have exactly two prime factors.) \b \cw{mpint}: the multiplicative inverse of \e{q} modulo \e{p}. \S{ppk-privkey-dsa} DSA DSA keys are stored using an \s{algorithm-name} of \cq{ssh-dss}. The public key data has already provided the key parameters (the large prime \e{p}, the small prime \e{q} and the group generator \e{g}), and the public key \e{y}. The private key stores: \b \cw{mpint}: the private key \e{x}, which is the discrete logarithm of \e{y} in the group generated by \e{g} mod \e{p}. \S{ppk-privkey-ecdsa} NIST elliptic-curve keys NIST elliptic-curve keys are stored using one of the following \s{algorithm-name} values, each corresponding to a different elliptic curve and key size: \b \cq{ecdsa-sha2-nistp256} \b \cq{ecdsa-sha2-nistp384} \b \cq{ecdsa-sha2-nistp521} The public key data has already provided the public elliptic curve point. The private key stores: \b \cw{mpint}: the private exponent, which is the discrete log of the public point. \S{ppk-privkey-eddsa} EdDSA elliptic-curve keys (Ed25519 and Ed448) EdDSA elliptic-curve keys are stored using one of the following \s{algorithm-name} values, each corresponding to a different elliptic curve and key size: \b \cq{ssh-ed25519} \b \cq{ssh-ed448} The public key data has already provided the public elliptic curve point. The private key stores: \b \cw{mpint}: the private exponent, which is the discrete log of the public point. \H{ppk-keys} Key derivation When a key file is encrypted, there are three pieces of key material that need to be computed from the passphrase: \b the key for the symmetric cipher used to encrypt the private key \b the initialisation vector for that cipher encryption \b the key for the MAC. If \s{encryption-type} is \cq{aes256-cbc}, then the symmetric cipher key is 32 bytes long, and the initialisation vector is 16 bytes (one cipher block). The length of the MAC key is also chosen to be 32 bytes. If \s{encryption-type} is \cq{none}, then all three of these pieces of data have zero length. (The MAC is still generated and checked in the key file format, but it has a zero-length key.) If the amount of key material required is not zero, then the passphrase is fed to the Argon2 key derivation function, in whichever mode is described in the \cq{Key-Derivation} header in the key file, with parameters derived from the various \q{\cw{Argon2-}\e{Parameter}\cw{:}} headers. (If the key is unencrypted, then all those headers are omitted, and Argon2 is not run at all.) Argon2 takes two extra string inputs in addition to the passphrase and the salt: a secret key, and some \q{associated data}. In PPK's use of Argon2, these are both set to the empty string. The \q{tag length} parameter to Argon2 (i.e. the amount of data it is asked to output) is set to the sum of the lengths of all of the data items required, i.e. (cipher key length + IV length + MAC key length). The output data is interpreted as the concatenation of the cipher key, the IV and the MAC key, in that order. So, for \cq{aes256-cbc}, the tag length will be 32+16+32\_=\_80 bytes; of the 80 bytes of output data, the first 32 bytes are used as the 256-bit AES key, the next 16 as the CBC IV, and the final 32 bytes as the HMAC-SHA-256 key. \H{ppk-old} Older versions of the PPK format \S{ppk-v2} Version 2 PPK version 2 was used by PuTTY 0.52 to 0.74 inclusive. In PPK version 2, the MAC algorithm used was HMAC-SHA-1 (so the \cw{Private-MAC} line contained only 40 hex digits). The \cq{Key-Derivation:} header and all the \q{\cw{Argon2-}\e{Parameter}\cw{:}} headers were absent. Instead of using Argon2, the key material for encrypting the private blob was derived from the passphrase in a totally different way, as follows. The cipher key for \cq{aes256-cbc} was constructed by generating two SHA-1 hashes, concatenating them, and taking the first 32 bytes of the result. (So you'd get all 20 bytes of the first hash output, and the first 12 of the second). Each hash preimage was as follows: \b \cw{uint32}: a sequence number. This is 0 in the first hash, and 1 in the second. (The idea was to extend this mechanism to further hashes by continuing to increment the sequence number, if future changes required even longer keys.) \b the passphrase, without any prefix length field. In PPK v2, the CBC initialisation vector was all zeroes. The MAC key was 20 bytes long, and was a single SHA-1 hash of the following data: \b the fixed string \cq{putty-private-key-file-mac-key}, without any prefix length field. \b the passphrase, without any prefix length field. (If the key is stored unencrypted, the passphrase was taken to be the empty string for these purposes.) \S{ppk-v1} Version 1 PPK version 1 was a badly designed format, only used during initial development, and not recommended for production use. PPK version 1 was never used by a released version of PuTTY. It was only emitted by some early development snapshots between version 0.51 (which did not support SSH-2 public keys at all) and 0.52 (which already used version 2 of this file format). I \e{hope} there are no PPK v1 files in use anywhere. But just in case, the old badly designed format is documented here anyway. In PPK version 1, the input to the MAC does not include any of the header fields or the public key. It is simply the private key data (still in plaintext and including random padding), all by itself (without a wrapping \cw{string}). PPK version 1 keys must therefore be rigorously validated after loading, to ensure that the public and private parts of the key were consistent with each other. PPK version 1 only supported the RSA and DSA key types. For RSA, this validation can be done using only the provided data (since the private key blob contains enough information to reconstruct the public values anyway). But for DSA, that isn't quite enough. Hence, PPK version 1 DSA keys extended the private data so that immediately after \e{x} was stored an extra value: \b \cw{string}: a SHA-1 hash of the public key data, whose preimage consists of \lcont{ \b \cw{string}: the large prime \e{p} \b \cw{string}: the small prime \e{q} \b \cw{string}: the group generator \e{g} } The idea was that checking this hash would verify that the key parameters had not been tampered with, and then the loading application could directly verify that \e{g}\cw{^}\e{x}\cw{\_=\_}\e{y}. In an \e{unencrypted} version 1 key file, the MAC is replaced by a plain SHA-1 hash of the private key data. This is indicated by the \cq{Private-MAC:} header being replaced with \cq{Private-Hash:} instead. putty-0.76/doc/site.but0000644000175000017500000000030214072266310011775 00000000000000\# Additional configuration for the version of the PuTTY docs \# actually published as HTML on the website. \cfg{xhtml-head-end}{} putty-0.76/doc/sshnames.but0000644000175000017500000001136314072266310012663 00000000000000\A{sshnames} SSH-2 names specified for PuTTY There are various parts of the SSH-2 protocol where things are specified using a textual name. Names ending in \cw{@putty.projects.tartarus.org} are reserved for allocation by the PuTTY team. Allocated names are documented here. \H{sshnames-channel} Connection protocol channel request names These names can be sent in a \cw{SSH_MSG_CHANNEL_REQUEST} message. \dt \cw{simple@putty.projects.tartarus.org} \dd This is sent by a client to announce that it will not have more than one channel open at a time in the current connection (that one being the one the request is sent on). The intention is that the server, knowing this, can set the window on that one channel to something very large, and leave flow control to TCP. There is no message-specific data. \dt \cw{winadj@putty.projects.tartarus.org} \dd PuTTY sends this request along with some \cw{SSH_MSG_CHANNEL_WINDOW_ADJUST} messages as part of its window-size tuning. It can be sent on any type of channel. There is no message-specific data. Servers MUST treat it as an unrecognised request and respond with \cw{SSH_MSG_CHANNEL_FAILURE}. \lcont{ (Some SSH servers get confused by this message, so there is a bug-compatibility mode for disabling it. See \k{config-ssh-bug-winadj}.) } \H{sshnames-kex} Key exchange method names \dt \cw{rsa-sha1-draft-00@putty.projects.tartarus.org} \dt \cw{rsa-sha256-draft-00@putty.projects.tartarus.org} \dt \cw{rsa1024-sha1-draft-01@putty.projects.tartarus.org} \dt \cw{rsa1024-sha256-draft-01@putty.projects.tartarus.org} \dt \cw{rsa2048-sha256-draft-01@putty.projects.tartarus.org} \dt \cw{rsa1024-sha1-draft-02@putty.projects.tartarus.org} \dt \cw{rsa2048-sha512-draft-02@putty.projects.tartarus.org} \dt \cw{rsa1024-sha1-draft-03@putty.projects.tartarus.org} \dt \cw{rsa2048-sha256-draft-03@putty.projects.tartarus.org} \dt \cw{rsa1024-sha1-draft-04@putty.projects.tartarus.org} \dt \cw{rsa2048-sha256-draft-04@putty.projects.tartarus.org} \dd These appeared in various drafts of what eventually became RFC\_4432. They have been superseded by \cw{rsa1024-sha1} and \cw{rsa2048-sha256}. \H{sshnames-encrypt} Encryption algorithm names \dt \cw{arcfour128-draft-00@putty.projects.tartarus.org} \dt \cw{arcfour256-draft-00@putty.projects.tartarus.org} \dd These were used in drafts of what eventually became RFC\_4345. They have been superseded by \cw{arcfour128} and \cw{arcfour256}. \H{sshnames-agent} Agent extension request names The SSH agent protocol, which is only specified in an Internet-Draft at the time of writing (\W{https://tools.ietf.org/html/draft-miller-ssh-agent}\cw{draft-miller-ssh-agent}), defines an extension mechanism. These names can be sent in an \cw{SSH_AGENTC_EXTENSION} message. \dt \cw{add-ppk@putty.projects.tartarus.org} \dd The payload is a single SSH-2 \cw{string} containing a keypair in the PPK format defined in \k{ppk}. Compared to the standard \cw{SSH_AGENTC_ADD_IDENTITY}, this extension allows adding keys in encrypted form, with the agent requesting a decryption passphrase from the user on demand, and able to revert the key to encrypted form. \dt \cw{reencrypt@putty.projects.tartarus.org} \dd The payload is a single SSH-2 \cw{string} specifying a public key blob, as in \cw{SSH_AGENTC_REMOVE_IDENTITY}. Requests that the agent forget any cleartext form of a specific key. \lcont{ Returns \cw{SSH_AGENT_SUCCESS} if the agent ended up holding the key only in encrypted form (even if it was already encrypted); returns \cw{SSH_AGENT_EXTENSION_FAILURE} if not (if it wasn't held by the agent at all, or only in cleartext form). } \dt \cw{reencrypt-all@putty.projects.tartarus.org} \dd No payload. Requests that the agent forget the cleartext form of any keys for which it holds an encrypted form. \lcont{ If the agent holds any keys with an encrypted form (or no keys at all), returns \cw{SSH_AGENT_SUCCESS} to indicate that no such keys are now held in cleartext form, followed by a \cw{uint32} specifying how many keys remain in cleartext form (because the agent didn't hold an encrypted form for them). If the agent holds nothing but keys in cleartext form, returns \cw{SSH_AGENT_EXTENSION_FAILURE}. } \dt \cw{list-extended@putty.projects.tartarus.org} \dd No payload. Returns \cw{SSH_AGENT_SUCCESS} followed by a list of identities similar to \cw{SSH_AGENT_IDENTITIES_ANSWER}, except that each key has an extra SSH-2 \cw{string} at the end. Currently that \cw{string} contains a single \cw{uint32} flags word, with the following bits defined: \lcont{ \dt Bit 0 \dd If set, key is held with an encrypted form (so that the \c{reencrypt} extension can do something useful with it). \dt Bit 1 \dd If set, key's cleartext form is not currently held (so the user will have to supply a passphrase before the key can be used). } putty-0.76/doc/udp.but0000644000175000017500000011730114072266310011631 00000000000000\# This file is so named for tradition's sake: it contains what we \# always used to refer to, before they were written down, as \# PuTTY's `unwritten design principles'. It has nothing to do with \# the User Datagram Protocol. \A{udp} PuTTY hacking guide This appendix lists a selection of the design principles applying to the PuTTY source code. If you are planning to send code contributions, you should read this first. \H{udp-portability} Cross-OS portability Despite Windows being its main area of fame, PuTTY is no longer a Windows-only application suite. It has a working Unix port; a Mac port is in progress; more ports may or may not happen at a later date. Therefore, embedding Windows-specific code in core modules such as \cw{ssh.c} is not acceptable. We went to great lengths to \e{remove} all the Windows-specific stuff from our core modules, and to shift it out into Windows-specific modules. Adding large amounts of Windows-specific stuff in parts of the code that should be portable is almost guaranteed to make us reject a contribution. The PuTTY source base is divided into platform-specific modules and platform-generic modules. The Unix-specific modules are all in the \c{unix} subdirectory; the Windows-specific modules are in the \c{windows} subdirectory. All the modules in the main source directory - notably \e{all} of the code for the various back ends - are platform-generic. We want to keep them that way. This also means you should stick to the C semantics guaranteed by the C standard: try not to make assumptions about the precise size of basic types such as \c{int} and \c{long int}; don't use pointer casts to do endianness-dependent operations, and so on. (Even \e{within} a platform front end you should still be careful of some of these portability issues. The Windows front end compiles on both 32- and 64-bit x86 and also Arm.) Our current choice of C standards version is \e{mostly} C99. With a couple of exceptions, you can assume that C99 features are available (in particular \cw{}, \cw{} and \c{inline}), but you shouldn't use things that are new in C11 (such as \cw{} or \cw{_Generic}). The exceptions to that rule are due to the need for Visual Studio compatibility: \b Don't use variable-length arrays. Visual Studio doesn't support them even now that it's adopted the rest of C99. We use \cw{-Wvla} when building with gcc and clang, to make it easier to avoid accidentally breaking that rule. \b For historical reasons, we still build with one older VS version which lacks \cw{}. So that file is included centrally in \c{defs.h}, and has a set of workaround definitions for the \cw{PRIx64}-type macros we use. If you need to use another one of those macros, you need to add a workaround definition in \c{defs.h}, and don't casually re-include \cw{} anywhere else in the source file. Here are a few portability assumptions that we \e{do} currently allow (because we'd already have to thoroughly vet the existing code if they ever needed to change, and it doesn't seem worth doing that unless we really have to): \b You can assume \c{int} is \e{at least} 32 bits wide. (We've never tried to port PuTTY to a platform with 16-bit \cw{int}, and it doesn't look likely to be necessary in future.) \b Similarly, you can assume \c{char} is exactly 8 bits. (Exceptions to that are even less likely to be relevant to us than short \cw{int}.) \b You can assume that using \c{memset} to write zero bytes over a whole structure will have the effect of setting all its pointer fields to \cw{NULL}. (The standard itself guarantees this for \e{integer} fields, but not for pointers.) \b You can assume that \c{time_t} has POSIX semantics, i.e. that it represents an integer number of non-leap seconds since 1970-01-01 00:00:00 UTC. (Times in this format are used in X authorisation, but we could work around that by carefully distinguishing local \c{time_t} from time values used in the wire protocol; but these semantics of \c{time_t} are also baked into the shared library API used by the GSSAPI authentication code, which would be much harder to change.) \b You can assume that the execution character encoding is a superset of the printable characters of ASCII. (In particular, it's fine to do arithmetic on a \c{char} value representing a Latin alphabetic character, without bothering to allow for EBCDIC or other non-consecutive encodings of the alphabet.) On the other hand, here are some particular things \e{not} to assume: \b Don't assume anything about the \e{signedness} of \c{char}. In particular, you \e{must} cast \c{char} values to \c{unsigned char} before passing them to any \cw{} function (because those expect a non-negative character value, or \cw{EOF}). If you need a particular signedness, explicitly specify \c{signed char} or \c{unsigned char}, or use C99 \cw{int8_t} or \cw{uint8_t}. \b From past experience with MacOS, we're still a bit nervous about \cw{'\\n'} and \cw{'\\r'} potentially having unusual meanings on a given platform. So it's fine to say \c{\\n} in a string you're passing to \c{printf}, but in any context where those characters appear in a standardised wire protocol or a binary file format, they should be spelled \cw{'\\012'} and \cw{'\\015'} respectively. \H{udp-multi-backend} Multiple backends treated equally PuTTY is not an SSH client with some other stuff tacked on the side. PuTTY is a generic, multiple-backend, remote VT-terminal client which happens to support one backend which is larger, more popular and more useful than the rest. Any extra feature which can possibly be general across all backends should be so: localising features unnecessarily into the SSH back end is a design error. (For example, we had several code submissions for proxy support which worked by hacking \cw{ssh.c}. Clearly this is completely wrong: the \cw{network.h} abstraction is the place to put it, so that it will apply to all back ends equally, and indeed we eventually put it there after another contributor sent a better patch.) The rest of PuTTY should try to avoid knowing anything about specific back ends if at all possible. To support a feature which is only available in one network protocol, for example, the back end interface should be extended in a general manner such that \e{any} back end which is able to provide that feature can do so. If it so happens that only one back end actually does, that's just the way it is, but it shouldn't be relied upon by any code. \H{udp-globals} Multiple sessions per process on some platforms Some ports of PuTTY - notably the in-progress Mac port - are constrained by the operating system to run as a single process potentially managing multiple sessions. Therefore, the platform-independent parts of PuTTY never use global variables to store per-session data. The global variables that do exist are tolerated because they are not specific to a particular login session. The random number state in \cw{sshrand.c}, the timer list in \cw{timing.c} and the queue of top-level callbacks in \cw{callback.c} serve all sessions equally. But most data is specific to a particular network session, and is therefore stored in dynamically allocated data structures, and pointers to these structures are passed around between functions. Platform-specific code can reverse this decision if it likes. The Windows code, for historical reasons, stores most of its data as global variables. That's OK, because \e{on Windows} we know there is only one session per PuTTY process, so it's safe to do that. But changes to the platform-independent code should avoid introducing global variables, unless they are genuinely cross-session. \H{udp-pure-c} C, not C++ PuTTY is written entirely in C, not in C++. We have made \e{some} effort to make it easy to compile our code using a C++ compiler: notably, our \c{snew}, \c{snewn} and \c{sresize} macros explicitly cast the return values of \cw{malloc} and \cw{realloc} to the target type. (This has type checking advantages even in C: it means you never accidentally allocate the wrong size piece of memory for the pointer type you're assigning it to. C++ friendliness is really a side benefit.) We want PuTTY to continue being pure C, at least in the platform-independent parts and the currently existing ports. Patches which switch the Makefiles to compile it as C++ and start using classes will not be accepted. Also, in particular, we disapprove of \cw{//} comments, at least for the moment. (Perhaps once C99 becomes genuinely widespread we might be more lenient.) The one exception: a port to a new platform may use languages other than C if they are necessary to code on that platform. If your favourite PDA has a GUI with a C++ API, then there's no way you can do a port of PuTTY without using C++, so go ahead and use it. But keep the C++ restricted to that platform's subdirectory; if your changes force the Unix or Windows ports to be compiled as C++, they will be unacceptable to us. \H{udp-security} Security-conscious coding PuTTY is a network application and a security application. Assume your code will end up being fed deliberately malicious data by attackers, and try to code in a way that makes it unlikely to be a security risk. In particular, try not to use fixed-size buffers for variable-size data such as strings received from the network (or even the user). We provide functions such as \cw{dupcat} and \cw{dupprintf}, which dynamically allocate buffers of the right size for the string they construct. Use these wherever possible. \H{udp-multi-compiler} Independence of specific compiler Windows PuTTY can currently be compiled with any of three Windows compilers: MS Visual C, the Cygwin / \cw{mingw32} GNU tools, and \cw{clang} (in MS compatibility mode). This is a really useful property of PuTTY, because it means people who want to contribute to the coding don't depend on having a specific compiler; so they don't have to fork out money for MSVC if they don't already have it, but on the other hand if they \e{do} have it they also don't have to spend effort installing \cw{gcc} alongside it. They can use whichever compiler they happen to have available, or install whichever is cheapest and easiest if they don't have one. Therefore, we don't want PuTTY to start depending on which compiler you're using. Using GNU extensions to the C language, for example, would ruin this useful property (not that anyone's ever tried it!); and more realistically, depending on an MS-specific library function supplied by the MSVC C library (\cw{_snprintf}, for example) is a mistake, because that function won't be available under the other compilers. Any function supplied in an official Windows DLL as part of the Windows API is fine, and anything defined in the C library standard is also fine, because those should be available irrespective of compilation environment. But things in between, available as non-standard library and language extensions in only one compiler, are disallowed. (\cw{_snprintf} in particular should be unnecessary, since we provide \cw{dupprintf}; see \k{udp-security}.) Compiler independence should apply on all platforms, of course, not just on Windows. \H{udp-small} Small code size PuTTY is tiny, compared to many other Windows applications. And it's easy to install: it depends on no DLLs, no other applications, no service packs or system upgrades. It's just one executable. You install that executable wherever you want to, and run it. We want to keep both these properties - the small size, and the ease of installation - if at all possible. So code contributions that depend critically on external DLLs, or that add a huge amount to the code size for a feature which is only useful to a small minority of users, are likely to be thrown out immediately. We do vaguely intend to introduce a DLL plugin interface for PuTTY, whereby seriously large extra features can be implemented in plugin modules. The important thing, though, is that those DLLs will be \e{optional}; if PuTTY can't find them on startup, it should run perfectly happily and just won't provide those particular features. A full installation of PuTTY might one day contain ten or twenty little DLL plugins, which would cut down a little on the ease of installation - but if you really needed ease of installation you \e{could} still just install the one PuTTY binary, or just the DLLs you really needed, and it would still work fine. Depending on \e{external} DLLs is something we'd like to avoid if at all possible (though for some purposes, such as complex SSH authentication mechanisms, it may be unavoidable). If it can't be avoided, the important thing is to follow the same principle of graceful degradation: if a DLL can't be found, then PuTTY should run happily and just not supply the feature that depended on it. \H{udp-single-threaded} Single-threaded code PuTTY and its supporting tools, or at least the vast majority of them, run in only one OS thread. This means that if you're devising some piece of internal mechanism, there's no need to use locks to make sure it doesn't get called by two threads at once. The only way code can be called re-entrantly is by recursion. That said, most of Windows PuTTY's network handling is triggered off Windows messages requested by \cw{WSAAsyncSelect()}, so if you call \cw{MessageBox()} deep within some network event handling code you should be aware that you might be re-entered if a network event comes in and is passed on to our window procedure by the \cw{MessageBox()} message loop. Also, the front ends (in particular Windows Plink) can use multiple threads if they like. However, Windows Plink keeps \e{very} tight control of its auxiliary threads, and uses them pretty much exclusively as a form of \cw{select()}. Pretty much all the code outside \cw{windows/winplink.c} is \e{only} ever called from the one primary thread; the others just loop round blocking on file handles and send messages to the main thread when some real work needs doing. This is not considered a portability hazard because that bit of \cw{windows/winplink.c} will need rewriting on other platforms in any case. One important consequence of this: PuTTY has only one thread in which to do everything. That \q{everything} may include managing more than one login session (\k{udp-globals}), managing multiple data channels within an SSH session, responding to GUI events even when nothing is happening on the network, and responding to network requests from the server (such as repeat key exchange) even when the program is dealing with complex user interaction such as the re-configuration dialog box. This means that \e{almost none} of the PuTTY code can safely block. \H{udp-keystrokes} Keystrokes sent to the server wherever possible In almost all cases, PuTTY sends keystrokes to the server. Even weird keystrokes that you think should be hot keys controlling PuTTY. Even Alt-F4 or Alt-Space, for example. If a keystroke has a well-defined escape sequence that it could usefully be sending to the server, then it should do so, or at the very least it should be configurably able to do so. To unconditionally turn a key combination into a hot key to control PuTTY is almost always a design error. If a hot key is really truly required, then try to find a key combination for it which \e{isn't} already used in existing PuTTYs (either it sends nothing to the server, or it sends the same thing as some other combination). Even then, be prepared for the possibility that one day that key combination might end up being needed to send something to the server - so make sure that there's an alternative way to invoke whatever PuTTY feature it controls. \H{udp-640x480} 640\u00D7{x}480 friendliness in configuration panels There's a reason we have lots of tiny configuration panels instead of a few huge ones, and that reason is that not everyone has a 1600\u00D7{x}1200 desktop. 640\u00D7{x}480 is still a viable resolution for running Windows (and indeed it's still the default if you start up in safe mode), so it's still a resolution we care about. Accordingly, the PuTTY configuration box, and the PuTTYgen control window, are deliberately kept just small enough to fit comfortably on a 640\u00D7{x}480 display. If you're adding controls to either of these boxes and you find yourself wanting to increase the size of the whole box, \e{don't}. Split it into more panels instead. \H{udp-makefiles-auto} Automatically generated \cw{Makefile}s PuTTY is intended to compile on multiple platforms, and with multiple compilers. It would be horrifying to try to maintain a single \cw{Makefile} which handled all possible situations, and just as painful to try to directly maintain a set of matching \cw{Makefile}s for each different compilation environment. Therefore, we have moved the problem up by one level. In the PuTTY source archive is a file called \c{Recipe}, which lists which source files combine to produce which binaries; and there is also a script called \cw{mkfiles.pl}, which reads \c{Recipe} and writes out the real \cw{Makefile}s. (The script also reads all the source files and analyses their dependencies on header files, so we get an extra benefit from doing it this way, which is that we can supply correct dependency information even in environments where it's difficult to set up an automated \c{make depend} phase.) You should \e{never} edit any of the PuTTY \cw{Makefile}s directly. They are not stored in our source repository at all. They are automatically generated by \cw{mkfiles.pl} from the file \c{Recipe}. If you need to add a new object file to a particular binary, the right thing to do is to edit \c{Recipe} and re-run \cw{mkfiles.pl}. This will cause the new object file to be added in every tool that requires it, on every platform where it matters, in every \cw{Makefile} to which it is relevant, \e{and} to get all the dependency data right. If you send us a patch that modifies one of the \cw{Makefile}s, you just waste our time, because we will have to convert it into a change to \c{Recipe}. If you send us a patch that modifies \e{all} of the \cw{Makefile}s, you will have wasted a lot of \e{your} time as well! (There is a comment at the top of every \cw{Makefile} in the PuTTY source archive saying this, but many people don't seem to read it, so it's worth repeating here.) \H{udp-ssh-coroutines} Coroutines in the SSH code Large parts of the code in the various SSH modules (in fact most of the protocol layers) are structured using a set of macros that implement (something close to) Donald Knuth's \q{coroutines} concept in C. Essentially, the purpose of these macros are to arrange that a function can call \cw{crReturn()} to return to its caller, and the next time it is called control will resume from just after that \cw{crReturn} statement. This means that any local (automatic) variables declared in such a function will be corrupted every time you call \cw{crReturn}. If you need a variable to persist for longer than that, you \e{must} make it a field in some appropriate structure containing the persistent state of the coroutine \dash typically the main state structure for an SSH protocol layer. See \W{https://www.chiark.greenend.org.uk/~sgtatham/coroutines.html}\c{https://www.chiark.greenend.org.uk/~sgtatham/coroutines.html} for a more in-depth discussion of what these macros are for and how they work. Another caveat: most of these coroutines are not \e{guaranteed} to run to completion, because the SSH connection (or whatever) that they're part of might be interrupted at any time by an unexpected network event or user action. So whenever a coroutine-managed variable refers to a resource that needs releasing, you should also ensure that the cleanup function for its containing state structure can reliably release it even if the coroutine is aborted at an arbitrary point. For example, if an SSH packet protocol layer has to have a field that sometimes points to a piece of allocated memory, then you should ensure that when you free that memory you reset the pointer field to \cw{NULL}. Then, no matter when the protocol layer's cleanup function is called, it can reliably free the memory if there is any, and not crash if there isn't. \H{udp-traits} Explicit vtable structures to implement traits A lot of PuTTY's code is written in a style that looks structurally rather like an object-oriented language, in spite of PuTTY being a pure C program. For example, there's a single data type called \cw{ssh_hash}, which is an abstraction of a secure hash function, and a bunch of functions called things like \cw{ssh_hash_}\e{foo} that do things with those data types. But in fact, PuTTY supports many different hash functions, and each one has to provide its own implementation of those functions. In C++ terms, this is rather like having a single abstract base class, and multiple concrete subclasses of it, each of which fills in all the pure virtual methods in a way that's compatible with the data fields of the subclass. The implementation is more or less the same, as well: in C, we do explicitly in the source code what the C++ compiler will be doing behind the scenes at compile time. But perhaps a closer analogy in functional terms is the Rust concept of a \q{trait}, or the Java idea of an \q{interface}. C++ supports a multi-level hierarchy of inheritance, whereas PuTTY's system \dash like traits or interfaces \dash has only two levels, one describing a generic object of a type (e.g. a hash function) and another describing a specific implementation of that type (e.g. SHA-256). The PuTTY code base has a standard idiom for doing this in C, as follows. Firstly, we define two \cw{struct} types for our trait. One of them describes a particular \e{kind} of implementation of that trait, and it's full of (mostly) function pointers. The other describes a specific \e{instance} of an implementation of that trait, and it will contain a pointer to a \cw{const} instance of the first type. For example: \c typedef struct MyAbstraction MyAbstraction; \c typedef struct MyAbstractionVtable MyAbstractionVtable; \c \c struct MyAbstractionVtable { \c MyAbstraction *(*new)(const MyAbstractionVtable *vt); \c void (*free)(MyAbstraction *); \c void (*modify)(MyAbstraction *, unsigned some_parameter); \c unsigned (*query)(MyAbstraction *, unsigned some_parameter); \c }; \c \c struct MyAbstraction { \c const MyAbstractionVtable *vt; \c }; Here, we imagine that \cw{MyAbstraction} might be some kind of object that contains mutable state. The associated vtable structure shows what operations you can perform on a \cw{MyAbstraction}: you can create one (dynamically allocated), free one you already have, or call the example methods \q{modify} (to change the state of the object in some way) and \q{query} (to return some value derived from the object's current state). (In most cases, the vtable structure has a name ending in \cq{vtable}. But for historical reasons a lot of the crypto primitives that use this scheme \dash ciphers, hash functions, public key methods and so on \dash instead have names ending in \cq{alg}, on the basis that the primitives they implement are often referred to as \q{encryption algorithms}, \q{hash algorithms} and so forth.) Now, to define a concrete instance of this trait, you'd define a \cw{struct} that contains a \cw{MyAbstraction} field, plus any other data it might need: \c struct MyImplementation { \c unsigned internal_data[16]; \c SomeOtherType *dynamic_subthing; \c \c MyAbstraction myabs; \c }; Next, you'd implement all the necessary methods for that implementation of the trait, in this kind of style: \c static MyAbstraction *myimpl_new(const MyAbstractionVtable *vt) \c { \c MyImplementation *impl = snew(MyImplementation); \c memset(impl, 0, sizeof(*impl)); \c impl->dynamic_subthing = allocate_some_other_type(); \c impl->myabs.vt = vt; \c return &impl->myabs; \c } \c \c static void myimpl_free(MyAbstraction *myabs) \c { \c MyImplementation *impl = container_of(myabs, MyImplementation, myabs); \c free_other_type(impl->dynamic_subthing); \c sfree(impl); \c } \c \c static void myimpl_modify(MyAbstraction *myabs, unsigned param) \c { \c MyImplementation *impl = container_of(myabs, MyImplementation, myabs); \c impl->internal_data[param] += do_something_with(impl->dynamic_subthing); \c } \c \c static unsigned myimpl_query(MyAbstraction *myabs, unsigned param) \c { \c MyImplementation *impl = container_of(myabs, MyImplementation, myabs); \c return impl->internal_data[param]; \c } Having defined those methods, now we can define a \cw{const} instance of the vtable structure containing pointers to them: \c const MyAbstractionVtable MyImplementation_vt = { \c .new = myimpl_new, \c .free = myimpl_free, \c .modify = myimpl_modify, \c .query = myimpl_query, \c }; \e{In principle}, this is all you need. Client code can construct a new instance of a particular implementation of \cw{MyAbstraction} by digging out the \cw{new} method from the vtable and calling it (with the vtable itself as a parameter), which returns a \cw{MyAbstraction *} pointer that identifies a newly created instance, in which the \cw{vt} field will contain a pointer to the same vtable structure you passed in. And once you have an instance object, say \cw{MyAbstraction *myabs}, you can dig out one of the other method pointers from the vtable it points to, and call that, passing the object itself as a parameter. But in fact, we don't do that, because it looks pretty ugly at all the call sites. Instead, what we generally do in this code base is to write a set of \cw{static inline} wrapper functions in the same header file that defined the \cw{MyAbstraction} structure types, like this: \c static MyAbstraction *myabs_new(const MyAbstractionVtable *vt) \c { return vt->new(vt); } \c static void myabs_free(MyAbstraction *myabs) \c { myabs->vt->free(myabs); } \c static void myimpl_modify(MyAbstraction *myabs, unsigned param) \c { myabs->vt->modify(myabs, param); } \c static unsigned myimpl_query(MyAbstraction *myabs, unsigned param) \c { return myabs->vt->query(myabs, param); } And now call sites can use those reasonably clean-looking wrapper functions, and shouldn't ever have to directly refer to the \cw{vt} field inside any \cw{myabs} object they're holding. For example, you might write something like this: \c MyAbstraction *myabs = myabs_new(&MyImplementation_vtable); \c myabs_update(myabs, 10); \c unsigned output = myabs_query(myabs, 2); \c myabs_free(myabs); and then all this code can use a different implementation of the same abstraction by just changing which vtable pointer it passed in in the first line. Some things to note about this system: \b The implementation instance type (here \cq{MyImplementation} contains the abstraction type (\cq{MyAbstraction}) as one of its fields. But that field is not necessarily at the start of the structure. So you can't just \e{cast} pointers back and forth between the two types. Instead: \lcont{ \b You \q{up-cast} from implementation to abstraction by taking the address of the \cw{MyAbstraction} field. You can see the example \cw{new} method above doing this, returning \cw{&impl->myabs}. All \cw{new} methods do this on return. \b Going in the other direction, each method that was passed a generic \cw{MyAbstraction *myabs} parameter has to recover a pointer to the specific implementation type \cw{MyImplementation *impl}. The idiom for doing that is to use the \cq{container_of} macro, also seen in the Linux kernel code. Generally, \cw{container_of(p, Type, field)} says: \q{I'm confident that the pointer value \cq{p} is pointing to the field called \cq{field} within a larger \cw{struct} of type \cw{Type}. Please return me the pointer to the containing structure.} So in this case, we take the \cq{myabs} pointer passed to the function, and \q{down-cast} it into a pointer to the larger and more specific structure type \cw{MyImplementation}, by adjusting the pointer value based on the offset within that structure of the field called \cq{myabs}. This system is flexible enough to permit \q{multiple inheritance}, or rather, multiple \e{implementation}: having one object type implement more than one trait. For example, the \cw{Proxy} type implements both the \cw{Socket} trait and the \cw{Plug} trait that connects to it, because it has to act as an adapter between another instance of each of those types. It's also perfectly possible to have the same object implement the \e{same} trait in two different ways. At the time of writing this I can't think of any case where we actually do this, but a theoretical example might be if you needed to support a trait like \cw{Comparable} in two ways that sorted by different criteria. There would be no difficulty doing this in the PuTTY system: simply have your implementation \cw{struct} contain two (or more) fields of the same abstraction type. The fields will have different names, which makes it easy to explicitly specify which one you're returning a pointer to during up-casting, or which one you're down-casting from using \cw{container_of}. And then both sets of implementation methods can recover a pointer to the same containing structure. } \b Unlike in C++, all objects in PuTTY that use this system are dynamically allocated. The \q{constructor} functions (whether they're virtualised across the whole abstraction or specific to each implementation) always allocate memory and return a pointer to it. The \q{free} method (our analogue of a destructor) always expects the input pointer to be dynamically allocated, and frees it. As a result, client code doesn't need to know how large the implementing object type is, because it will never need to allocate it (on the stack or anywhere else). \b Unlike in C++, the abstraction's \q{vtable} structure does not only hold methods that you can call on an instance object. It can also hold several other kinds of thing: \lcont{ \b Methods that you can call \e{without} an instance object, given only the vtable structure identifying a particular implementation of the trait. You might think of these as \q{static methods}, as in C++, except that they're \e{virtual} \dash the same code can call the static method of a different \q{class} given a different vtable pointer. So they're more like \q{virtual static methods}, which is a concept C++ doesn't have. An example is the \cw{pubkey_bits} method in \cw{ssh_keyalg}. \b The most important case of a \q{virtual static method} is the \cw{new} method that allocates and returns a new object. You can think of it as a \q{virtual constructor} \dash another concept C++ doesn't have. (However, not all types need one of these: see below.) \b The vtable can also contain constant data relevant to the class as a whole \dash \q{virtual constant data}. For example, a cryptographic hash function will contain an integer field giving the length of the output hash, and most crypto primitives will contain a string field giving the identifier used in the SSH protocol that describes that primitive. The effect of all of this is that you can make other pieces of code able to use any instance of one of these types, by passing it an actual vtable as a parameter. For example, the \cw{hash_simple} function takes an \cw{ssh_hashalg} vtable pointer specifying any hash algorithm you like, and internally, it creates an object of that type, uses it, and frees it. In C++, you'd probably do this using a template, which would mean you had multiple specialisations of \cw{hash_simple} \dash and then it would be much more difficult to decide \e{at run time} which one you needed to use. Here, \cw{hash_simple} is still just one function, and you can decide as late as you like which vtable to pass to it. } \b The abstract \e{instance} structure can also contain publicly visible data fields (this time, usually treated as mutable) which are common to all implementations of the trait. For example, \cw{BinaryPacketProtocol} has lots of these. \b Not all abstractions of this kind want virtual constructors. It depends on how different the implementations are. \lcont{ With a crypto primitive like a hash algorithm, the constructor call looks the same for every implementing type, so it makes sense to have a standardised virtual constructor in the vtable and a \cw{ssh_hash_new} wrapper function which can make an instance of whatever vtable you pass it. And then you make all the vtable objects themselves globally visible throughout the source code, so that any module can call (for example) \cw{ssh_hash_new(&ssh_sha256)}. But with other kinds of object, the constructor for each implementing type has to take a different set of parameters. For example, implementations of \cw{Socket} are not generally interchangeable at construction time, because constructing different kinds of socket require totally different kinds of address parameter. In that situation, it makes more sense to keep the vtable structure itself private to the implementing source file, and instead, publish an ordinary constructing function that allocates and returns an instance of that particular subtype, taking whatever parameters are appropriate to that subtype. } \b If you do have virtual constructors, you can choose whether they take a vtable pointer as a parameter (as shown above), or an \e{existing} instance object. In the latter case, they can refer to the object itself as well as the vtable. For example, you could have a trait come with a virtual constructor called \q{clone}, meaning \q{Make a copy of this object, no matter which implementation it is.} \b Sometimes, a single vtable structure type can be shared between two completely different object types, and contain all the methods for both. For example, \cw{ssh_compression_alg} contains methods to create, use and free \cw{ssh_compressor} and \cw{ssh_decompressor} objects, which are not interchangeable \dash but putting their methods in the same vtable means that it's easy to create a matching pair of objects that are compatible with each other. \b Passing the vtable itself as an argument to the \cw{new} method is not compulsory: if a given \cw{new} implementation is only used by a single vtable, then that function can simply hard-code the vtable pointer that it writes into the object it constructs. But passing the vtable is more flexible, because it allows a single constructor function to be shared between multiple slightly different object types. For example, SHA-384 and SHA-512 share the same \cw{new} method and the same implementation data type, because they're very nearly the same hash algorithm \dash but a couple of the other methods in their vtables are different, because the \q{reset} function has to set up the initial algorithm state differently, and the \q{digest} method has to write out a different amount of data. \lcont{ One practical advantage of having the \cw{myabs_}\e{foo} family of inline wrapper functions in the header file is that if you change your mind later about whether the vtable needs to be passed to \cw{new}, you only have to update the \cw{myabs_new} wrapper, and then the existing call sites won't need changing. } \b Another piece of \q{stunt object orientation} made possible by this scheme is that you can write two vtables that both use the same structure layout for the implementation object, and have an object \e{transform from one to the other} part way through its lifetime, by overwriting its own vtable pointer field. For example, the \cw{sesschan} type that handles the server side of an SSH terminal session will sometimes transform in mid-lifetime into an SCP or SFTP file-transfer channel in this way, at the point where the client sends an \cq{exec} or \cq{subsystem} request that indicates that that's what it wants to do with the channel. \lcont{ This concept would be difficult to arrange in C++. In Rust, it wouldn't even \e{make sense}, because in Rust, objects implementing a trait don't even contain a vtable pointer at all \dash instead, the \q{trait object} type (identifying a specific instance of some implementation of a given trait) consists of a pair of pointers, one to the object itself and one to the vtable. In that model, the only way you could make an existing object turn into a different trait would be to know where all the pointers to it were stored elsewhere in the program, and persuade all their owners to rewrite them. } \b Another stunt you can do is to have a vtable that doesn't have a corresponding implementation structure at all, because the only methods implemented in it are the constructors, and they always end up returning an implementation of some other vtable. For example, some of PuTTY's crypto primitives have a hardware-accelerated version and a pure software version, and decide at run time which one to use (based on whether the CPU they're running on supports the necessary acceleration instructions). So, for example, there are vtables for \cw{ssh_sha256_sw} and \cw{ssh_sha256_hw}, each of which has its own data layout and its own implementations of all the methods; and then there's a top-level vtable \cw{ssh_sha256}, which only provides the \q{new} method, and implements it by calling the \q{new} method on one or other of the subtypes depending on what it finds out about the machine it's running on. That top-level selector vtable is nearly always the one used by client code. (Except for the test suite, which has to instantiate both of the subtypes in order to make sure they both pass the tests.) \lcont{ As a result, the top-level selector vtable \cw{ssh_sha256} doesn't need to implement any method that takes an \cw{ssh_cipher *} parameter, because no \cw{ssh_cipher} object is ever constructed whose \cw{vt} field points to \cw{&ssh_sha256}: they all point to one of the other two full implementation vtables. } \H{udp-compile-once} Single compilation of each source file The PuTTY build system for any given platform works on the following very simple model: \b Each source file is compiled precisely once, to produce a single object file. \b Each binary is created by linking together some combination of those object files. Therefore, if you need to introduce functionality to a particular module which is only available in some of the tool binaries (for example, a cryptographic proxy authentication mechanism which needs to be left out of PuTTYtel to maintain its usability in crypto-hostile jurisdictions), the \e{wrong} way to do it is by adding \cw{#ifdef}s in (say) \cw{proxy.c}. This would require separate compilation of \cw{proxy.c} for PuTTY and PuTTYtel, which means that the entire \cw{Makefile}-generation architecture (see \k{udp-makefiles-auto}) would have to be significantly redesigned. Unless you are prepared to do that redesign yourself, \e{and} guarantee that it will still port to any future platforms we might decide to run on, you should not attempt this! The \e{right} way to introduce a feature like this is to put the new code in a separate source file, and (if necessary) introduce a second new source file defining the same set of functions, but defining them as stubs which don't provide the feature. Then the module whose behaviour needs to vary (\cw{proxy.c} in this example) can call the functions defined in these two modules, and it will either provide the new feature or not provide it according to which of your new modules it is linked with. Of course, object files are never shared \e{between} platforms; so it is allowable to use \cw{#ifdef} to select between platforms. This happens in \cw{puttyps.h} (choosing which of the platform-specific include files to use), and also in \cw{misc.c} (the Windows-specific \q{Minefield} memory diagnostic system). It should be used sparingly, though, if at all. \H{udp-perfection} Do as we say, not as we do The current PuTTY code probably does not conform strictly to \e{all} of the principles listed above. There may be the occasional SSH-specific piece of code in what should be a backend-independent module, or the occasional dependence on a non-standard X library function under Unix. This should not be taken as a licence to go ahead and violate the rules. Where we violate them ourselves, we're not happy about it, and we would welcome patches that fix any existing problems. Please try to help us make our code better, not worse! putty-0.76/doc/using.but0000644000175000017500000014163214072266310012172 00000000000000\C{using} Using PuTTY This chapter provides a general introduction to some more advanced features of PuTTY. For extreme detail and reference purposes, \k{config} is likely to contain more information. \H{using-session} During your session A lot of PuTTY's complexity and features are in the configuration panel. Once you have worked your way through that and started a session, things should be reasonably simple after that. Nevertheless, there are a few more useful features available. \S{using-selection} Copying and pasting text \I{copy and paste}Often in a PuTTY session you will find text on your terminal screen which you want to type in again. Like most other terminal emulators, PuTTY allows you to copy and paste the text rather than having to type it again. Also, copy and paste uses the \I{Windows clipboard}Windows \i{clipboard}, so that you can paste (for example) URLs into a web browser, or paste from a word processor or spreadsheet into your terminal session. By default, PuTTY's copy and paste works entirely with the \i{mouse}. (This will be familiar to people who have used \i\c{xterm} on Unix.) In order to copy text to the clipboard, you just click the \i{left mouse button} in the \i{terminal window}, and drag to \I{selecting text}select text. When you let go of the button, the text is \e{automatically} copied to the clipboard. You do not need to press \i{Ctrl-C} or \i{Ctrl-Ins}; in fact, if you do press Ctrl-C, PuTTY will send a Ctrl-C character down your session to the server where it will probably cause a process to be interrupted. Pasting into PuTTY is done using the right button (or the middle mouse button, if you have a \i{three-button mouse} and have set it up; see \k{config-mouse}). (Pressing \i{Shift-Ins}, or selecting \q{Paste} from the \I{right mouse button, with Ctrl}Ctrl+right-click \i{context menu}, have the same effect.) When you click the \i{right mouse button}, PuTTY will read whatever is in the Windows clipboard and paste it into your session. By default, this behaves \e{exactly} as if the clipboard contents had been typed at the keyboard; therefore, be careful of pasting formatted text into an editor that does automatic \i{indenting}, as you may find that the spaces pasted from the clipboard plus the spaces added by the editor add up to too many spaces and ruin the formatting. (Some remote applications can ask PuTTY to identify text that is being pasted, to avoid this sort of problem; but if your application does not, there is nothing PuTTY can do to avoid this.) If you \i{double-click} the left mouse button, PuTTY will \I{selecting words}select a whole word. If you double-click, hold down the second click, and drag the mouse, PuTTY will select a sequence of whole words. (You can adjust precisely what PuTTY considers to be part of a word; see \k{config-charclasses}.) If you \e{triple}-click, or \i{triple-click} and drag, then PuTTY will \I{selecting lines}select a whole line or sequence of lines. If you want to select a \I{rectangular selection}rectangular region instead of selecting to the end of each line, you can do this by holding down Alt when you make your selection. You can also configure rectangular selection to be the default, and then holding down Alt gives the normal behaviour instead: see \k{config-rectselect} for details. (In some Unix environments, Alt+drag is intercepted by the window manager. Shift+Alt+drag should work for rectangular selection as well, so you could try that instead.) If you have a \i{middle mouse button}, then you can use it to \I{adjusting a selection}adjust an existing selection if you selected something slightly wrong. (If you have configured the middle mouse button to paste, then the right mouse button does this instead.) Click the button on the screen, and you can pick up the nearest end of the selection and drag it to somewhere else. If you are running PuTTY itself on Unix (not just using it to connect to a Unix system from Windows), by default you will likely have to use similar mouse actions in other applications to paste the text you copied from PuTTY, and to copy text for pasting into PuTTY; actions like \i{Ctrl-C} and Ctrl-V will likely not behave as you expect. \K{config-clipboards} explains why this is, and how you can change the behaviour. (On Windows there is only a single selection shared with other applications, so this confusion does not arise.) It's possible for the server to ask to \I{mouse reporting}handle mouse clicks in the PuTTY window itself. If this happens, the \i{mouse pointer} will turn into an arrow, and using the mouse to copy and paste will only work if you hold down Shift. See \k{config-features-mouse} and \k{config-mouseshift} for details of this feature and how to configure it. You can customise much of this behaviour, for instance to enable copy and paste from the keyboard; see \k{config-selection}. \S{using-scrollback} \I{scrollback}Scrolling the screen back PuTTY keeps track of text that has scrolled up off the top of the terminal. So if something appears on the screen that you want to read, but it scrolls too fast and it's gone by the time you try to look for it, you can use the \i{scrollbar} on the right side of the window to look back up the session \i{history} and find it again. As well as using the scrollbar, you can also page the scrollback up and down by pressing \i{Shift-PgUp} and \i{Shift-PgDn}. You can scroll a line at a time using \i{Ctrl-PgUp} and \i{Ctrl-PgDn}, or to the top/bottom of the scrollback with \i{Ctrl-Shift-PgUp} and \i{Ctrl-Shift-PgDn}. These are still available if you configure the scrollbar to be invisible. By default the last 2000 lines scrolled off the top are preserved for you to look at. You can increase (or decrease) this value using the configuration box; see \k{config-scrollback}. \S{using-sysmenu} The \ii{System menu} If you click the left mouse button on the icon in the top left corner of PuTTY's terminal window, or click the right mouse button on the title bar, you will see the standard Windows system menu containing items like Minimise, Move, Size and Close. PuTTY's system menu contains extra program features in addition to the Windows standard options. These extra menu commands are described below. (These options are also available in a \i{context menu} brought up by holding Ctrl and clicking with the right mouse button anywhere in the \i{PuTTY window}.) \S2{using-eventlog} The PuTTY \i{Event Log} If you choose \q{Event Log} from the system menu, a small window will pop up in which PuTTY logs significant events during the connection. Most of the events in the log will probably take place during session startup, but a few can occur at any point in the session, and one or two occur right at the end. You can use the mouse to select one or more lines of the Event Log, and hit the Copy button to copy them to the \i{clipboard}. If you are reporting a bug, it's often useful to paste the contents of the Event Log into your bug report. (The Event Log is not the same as the facility to create a log file of your session; that's described in \k{using-logging}.) \S2{using-specials} \ii{Special commands} Depending on the protocol used for the current session, there may be a submenu of \q{special commands}. These are protocol-specific tokens, such as a \q{break} signal, that can be sent down a connection in addition to normal data. Their precise effect is usually up to the server. Currently only Telnet, SSH, and serial connections have special commands. The \q{break} signal can also be invoked from the keyboard with \i{Ctrl-Break}. In an SSH connection, the following \I{SSH special commands}special commands are available: \b \I{IGNORE message, SSH special command}\I{No-op, in SSH}\ii{IGNORE message} \lcont{ Should have no effect. } \b \I{Repeat key exchange, SSH special command}Repeat key exchange \lcont{ Only available in SSH-2. Forces a \i{repeat key exchange} immediately (and resets associated timers and counters). For more information about repeat key exchanges, see \k{config-ssh-kex-rekey}. } \b \I{host key cache}Cache new host key type \lcont{ Only available in SSH-2. This submenu appears only if the server has host keys of a type that PuTTY doesn't already have cached, and so won't consider. Selecting a key here will allow PuTTY to use that key now and in future: PuTTY will do a fresh key-exchange with the selected key, and immediately add that key to its permanent cache (relying on the host key used at the start of the connection to cross-certify the new key). That key will be used for the rest of the current session; it may not actually be used for future sessions, depending on your preferences (see \k{config-ssh-hostkey-order}). Normally, PuTTY will carry on using a host key it already knows, even if the server offers key formats that PuTTY would otherwise prefer, to avoid host key prompts. As a result, if you've been using a server for some years, you may still be using an older key than a new user would use, due to server upgrades in the meantime. The SSH protocol unfortunately does not have organised facilities for host key migration and rollover, but this allows you to \I{host keys, upgrading}manually upgrade. } \b \I{Break, SSH special command}Break \lcont{ Only available in SSH-2, and only during a session. Optional extension; may not be supported by server. PuTTY requests the server's default break length. } \b \I{Signal, SSH special command}Signals (SIGINT, SIGTERM etc) \lcont{ Only available in SSH-2, and only during a session. Sends various POSIX signals. Not honoured by all servers. } The following \I{Telnet special commands}special commands are available in Telnet: \b \I{Are You There, Telnet special command}Are You There \b \I{Break, Telnet special command}Break \b \I{Synch, Telnet special command}Synch \b \I{Erase Character, Telnet special command}Erase Character \lcont{ PuTTY can also be configured to send this when the Backspace key is pressed; see \k{config-telnetkey}. } \b \I{Erase Line, Telnet special command}Erase Line \b \I{Go Ahead, Telnet special command}Go Ahead \b \I{No Operation, Telnet special command}No Operation \lcont{ Should have no effect. } \b \I{Abort Process, Telnet special command}Abort Process \b \I{Abort Output, Telnet special command}Abort Output \b \I{Interrupt Process, Telnet special command}Interrupt Process \lcont{ PuTTY can also be configured to send this when Ctrl-C is typed; see \k{config-telnetkey}. } \b \I{Suspend Process, Telnet special command}Suspend Process \lcont{ PuTTY can also be configured to send this when Ctrl-Z is typed; see \k{config-telnetkey}. } \b \I{End Of Record, Telnet special command}End Of Record \b \I{End Of File, Telnet special command}End Of File With a serial connection, the only available special command is \I{Break, serial special command}\q{Break}. \S2{using-newsession} Starting new sessions PuTTY's system menu provides some shortcut ways to start new sessions: \b Selecting \i{\q{New Session}} will start a completely new instance of PuTTY, and bring up the configuration box as normal. \b Selecting \i{\q{Duplicate Session}} will start a session in a new window with precisely the same options as your current one - connecting to the same host using the same protocol, with all the same terminal settings and everything. \b In an inactive window, selecting \i{\q{Restart Session}} will do the same as \q{Duplicate Session}, but in the current window. \b The \i{\q{Saved Sessions} submenu} gives you quick access to any sets of stored session details you have previously saved. See \k{config-saving} for details of how to create saved sessions. \S2{using-changesettings} \I{settings, changing}Changing your session settings If you select \i{\q{Change Settings}} from the system menu, PuTTY will display a cut-down version of its initial configuration box. This allows you to adjust most properties of your current session. You can change the terminal size, the font, the actions of various keypresses, the colours, and so on. Some of the options that are available in the main configuration box are not shown in the cut-down Change Settings box. These are usually options which don't make sense to change in the middle of a session (for example, you can't switch from SSH to Telnet in mid-session). You can save the current settings to a saved session for future use from this dialog box. See \k{config-saving} for more on saved sessions. \S2{using-copyall} \i{Copy All to Clipboard} This system menu option provides a convenient way to copy the whole contents of the terminal screen (up to the last nonempty line) and scrollback to the \i{clipboard} in one go. \S2{reset-terminal} \I{scrollback, clearing}Clearing and \I{terminal, resetting}resetting the terminal The \i{\q{Clear Scrollback}} option on the system menu tells PuTTY to discard all the lines of text that have been kept after they scrolled off the top of the screen. This might be useful, for example, if you displayed sensitive information and wanted to make sure nobody could look over your shoulder and see it. (Note that this only prevents a casual user from using the scrollbar to view the information; the text is not guaranteed not to still be in PuTTY's memory.) The \i{\q{Reset Terminal}} option causes a full reset of the \i{terminal emulation}. A VT-series terminal is a complex piece of software and can easily get into a state where all the text printed becomes unreadable. (This can happen, for example, if you accidentally output a binary file to your terminal.) If this happens, selecting Reset Terminal should sort it out. \S2{using-fullscreen} \ii{Full screen} mode If you find the title bar on a maximised window to be ugly or distracting, you can select Full Screen mode to maximise PuTTY \q{even more}. When you select this, PuTTY will expand to fill the whole screen and its borders, title bar and scrollbar will disappear. (You can configure the scrollbar not to disappear in full-screen mode if you want to keep it; see \k{config-scrollback}.) When you are in full-screen mode, you can still access the \i{system menu} if you click the left mouse button in the \e{extreme} top left corner of the screen. \H{using-logging} Creating a \i{log file} of your \I{session log}session For some purposes you may find you want to log everything that appears on your screen. You can do this using the \q{Logging} panel in the configuration box. To begin a session log, select \q{Change Settings} from the system menu and go to the Logging panel. Enter a log file name, and select a logging mode. (You can log all session output including the terminal \i{control sequence}s, or you can just log the printable text. It depends what you want the log for.) Click \q{Apply} and your log will be started. Later on, you can go back to the Logging panel and select \q{Logging turned off completely} to stop logging; then PuTTY will close the log file and you can safely read it. See \k{config-logging} for more details and options. \H{using-translation} Altering your \i{character set} configuration If you find that special characters (\i{accented characters}, for example, or \i{line-drawing characters}) are not being displayed correctly in your PuTTY session, it may be that PuTTY is interpreting the characters sent by the server according to the wrong \e{character set}. There are a lot of different character sets available, and no good way for PuTTY to know which to use, so it's entirely possible for this to happen. If you click \q{Change Settings} and look at the \q{Translation} panel, you should see a large number of character sets which you can select, and other related options. Now all you need is to find out which of them you want! (See \k{config-translation} for more information.) \H{using-x-forwarding} Using \i{X11 forwarding} in SSH The SSH protocol has the ability to securely forward X Window System \i{graphical applications} over your encrypted SSH connection, so that you can run an application on the SSH server machine and have it put its windows up on your local machine without sending any X network traffic in the clear. In order to use this feature, you will need an X display server for your Windows machine, such as Cygwin/X, X-Win32, or Exceed. This will probably install itself as display number 0 on your local machine; if it doesn't, the manual for the \i{X server} should tell you what it does do. You should then tick the \q{Enable X11 forwarding} box in the X11 panel (see \k{config-ssh-x11}) before starting your SSH session. The \i{\q{X display location}} box is blank by default, which means that PuTTY will try to use a sensible default such as \c{:0}, which is the usual display location where your X server will be installed. If that needs changing, then change it. Now you should be able to log in to the SSH server as normal. To check that X forwarding has been successfully negotiated during connection startup, you can check the PuTTY Event Log (see \k{using-eventlog}). It should say something like this: \c 2001-12-05 17:22:01 Requesting X11 forwarding \c 2001-12-05 17:22:02 X11 forwarding enabled If the remote system is Unix or Unix-like, you should also be able to see that the \i{\c{DISPLAY} environment variable} has been set to point at display 10 or above on the SSH server machine itself: \c fred@unixbox:~$ echo $DISPLAY \c unixbox:10.0 If this works, you should then be able to run X applications in the remote session and have them display their windows on your PC. For more options relating to X11 forwarding, see \k{config-ssh-x11}. \H{using-port-forwarding} Using \i{port forwarding} in SSH The SSH protocol has the ability to forward arbitrary \I{network connection}network (TCP) connections over your encrypted SSH connection, to avoid the network traffic being sent in clear. For example, you could use this to connect from your home computer to a \i{POP-3} server on a remote machine without your POP-3 password being visible to network sniffers. In order to use port forwarding to \I{local port forwarding}connect from your local machine to a port on a remote server, you need to: \b Choose a \i{port number} on your local machine where PuTTY should listen for incoming connections. There are likely to be plenty of unused port numbers above 3000. (You can also use a local loopback address here; see below for more details.) \b Now, before you start your SSH connection, go to the Tunnels panel (see \k{config-ssh-portfwd}). Make sure the \q{Local} radio button is set. Enter the local port number into the \q{Source port} box. Enter the destination host name and port number into the \q{Destination} box, separated by a colon (for example, \c{popserver.example.com:110} to connect to a POP-3 server). \b Now click the \q{Add} button. The details of your port forwarding should appear in the list box. Now start your session and log in. (Port forwarding will not be enabled until after you have logged in; otherwise it would be easy to perform completely anonymous network attacks, and gain access to anyone's virtual private network.) To check that PuTTY has set up the port forwarding correctly, you can look at the PuTTY Event Log (see \k{using-eventlog}). It should say something like this: \c 2001-12-05 17:22:10 Local port 3110 forwarding to \c popserver.example.com:110 Now if you connect to the source port number on your local PC, you should find that it answers you exactly as if it were the service running on the destination machine. So in this example, you could then configure an e-mail client to use \c{localhost:3110} as a POP-3 server instead of \c{popserver.example.com:110}. (Of course, the forwarding will stop happening when your PuTTY session closes down.) You can also forward ports in the other direction: arrange for a particular port number on the \e{server} machine to be \I{remote port forwarding}forwarded back to your PC as a connection to a service on your PC or near it. To do this, just select the \q{Remote} radio button instead of the \q{Local} one. The \q{Source port} box will now specify a port number on the \e{server} (note that most servers will not allow you to use \I{privileged port}port numbers under 1024 for this purpose). An alternative way to forward local connections to remote hosts is to use \I{dynamic port forwarding}dynamic SOCKS proxying. In this mode, PuTTY acts as a SOCKS server, which SOCKS-aware programs can connect to and open forwarded connections to the destination of their choice, so this can be an alternative to long lists of static forwardings. To use this mode, you will need to select the \q{Dynamic} radio button instead of \q{Local}, and then you should not enter anything into the \q{Destination} box (it will be ignored). PuTTY will then listen for SOCKS connections on the port you have specified. Most \i{web browsers} can be configured to connect to this SOCKS proxy service; also, you can forward other PuTTY connections through it by setting up the Proxy control panel (see \k{config-proxy} for details). The source port for a forwarded connection usually does not accept connections from any machine except the \I{localhost}SSH client or server machine itself (for local and remote forwardings respectively). There are controls in the Tunnels panel to change this: \b The \q{Local ports accept connections from other hosts} option allows you to set up local-to-remote port forwardings (including dynamic port forwardings) in such a way that machines other than your client PC can connect to the forwarded port. \b The \q{Remote ports do the same} option does the same thing for remote-to-local port forwardings (so that machines other than the SSH server machine can connect to the forwarded port.) Note that this feature is only available in the SSH-2 protocol, and not all SSH-2 servers honour it (in \i{OpenSSH}, for example, it's usually disabled by default). You can also specify an \i{IP address} to \I{listen address}listen on. Typically a Windows machine can be asked to listen on any single IP address in the \cw{127.*.*.*} range, and all of these are \i{loopback address}es available only to the local machine. So if you forward (for example) \c{127.0.0.5:79} to a remote machine's \i\cw{finger} port, then you should be able to run commands such as \c{finger fred@127.0.0.5}. This can be useful if the program connecting to the forwarded port doesn't allow you to change the port number it uses. This feature is available for local-to-remote forwarded ports; SSH-1 is unable to support it for remote-to-local ports, while SSH-2 can support it in theory but servers will not necessarily cooperate. (Note that if you're using Windows XP Service Pack 2, you may need to obtain a fix from Microsoft in order to use addresses like \cw{127.0.0.5} - see \k{faq-alternate-localhost}.) For more options relating to port forwarding, see \k{config-ssh-portfwd}. If the connection you are forwarding over SSH is itself a second SSH connection made by another copy of PuTTY, you might find the \q{logical host name} configuration option useful to warn PuTTY of which host key it should be expecting. See \k{config-loghost} for details of this. \H{using-serial} Connecting to a local serial line PuTTY can connect directly to a local serial line as an alternative to making a network connection. In this mode, text typed into the PuTTY window will be sent straight out of your computer's serial port, and data received through that port will be displayed in the PuTTY window. You might use this mode, for example, if your serial port is connected to another computer which has a serial connection. To make a connection of this type, simply select \q{Serial} from the \q{Connection type} radio buttons on the \q{Session} configuration panel (see \k{config-hostname}). The \q{Host Name} and \q{Port} boxes will transform into \q{Serial line} and \q{Speed}, allowing you to specify which serial line to use (if your computer has more than one) and what speed (baud rate) to use when transferring data. For further configuration options (data bits, stop bits, parity, flow control), you can use the \q{Serial} configuration panel (see \k{config-serial}). After you start up PuTTY in serial mode, you might find that you have to make the first move, by sending some data out of the serial line in order to notify the device at the other end that someone is there for it to talk to. This probably depends on the device. If you start up a PuTTY serial session and nothing appears in the window, try pressing Return a few times and see if that helps. A serial line provides no well defined means for one end of the connection to notify the other that the connection is finished. Therefore, PuTTY in serial mode will remain connected until you close the window using the close button. \H{using-rawprot} Making \i{raw TCP connections} A lot of \I{debugging Internet protocols}Internet protocols are composed of commands and responses in plain text. For example, \i{SMTP} (the protocol used to transfer e-mail), \i{NNTP} (the protocol used to transfer Usenet news), and \i{HTTP} (the protocol used to serve Web pages) all consist of commands in readable plain text. Sometimes it can be useful to connect directly to one of these services and speak the protocol \q{by hand}, by typing protocol commands and watching the responses. On Unix machines, you can do this using the system's \c{telnet} command to connect to the right port number. For example, \c{telnet mailserver.example.com 25} might enable you to talk directly to the SMTP service running on a mail server. Although the Unix \c{telnet} program provides this functionality, the protocol being used is not really Telnet. Really there is no actual protocol at all; the bytes sent down the connection are exactly the ones you type, and the bytes shown on the screen are exactly the ones sent by the server. Unix \c{telnet} will attempt to detect or guess whether the service it is talking to is a real Telnet service or not; PuTTY prefers to be told for certain. In order to make a debugging connection to a service of this type, you simply select the fourth protocol name, \I{\q{Raw} protocol}\q{Raw}, from the \q{Protocol} buttons in the \q{Session} configuration panel. (See \k{config-hostname}.) You can then enter a host name and a port number, and make the connection. \H{using-telnet} Connecting using the \i{Telnet} protocol PuTTY can use the Telnet protocol to connect to a server. Telnet was perhaps the most popular remote login protocol before SSH was introduced. It was general enough to be used by multiple server operating systems (Unix and VMS in particular), and supported many optional protocol extensions providing extra support for particular server features. Unlike SSH, Telnet runs over an unsecured network connection, so it is a very bad idea to use it over the hostile Internet (though it is still used to some extent as of 2020). \H{using-rlogin} Connecting using the \i{Rlogin} protocol PuTTY can use the Rlogin protocol to connect to a server. Rlogin was similar to Telnet in concept, but more focused on connections between Unix machines. It supported a feature for passwordless login, based on use of \q{privileged ports} (ports with numbers below 1024, which Unix traditionally does not allow users other than \cw{root} to allocate). Ultimately, based on the server trusting that the client's IP address was owned by the Unix machine it claimed to be, and that that machine would guard its privileged ports appropriately. Like Telnet, Rlogin runs over an unsecured network connection. \H{using-supdup} Connecting using the \i{SUPDUP} protocol PuTTY can use the SUPDUP protocol to connect to a server. SUPDUP is a login protocol used mainly by PDP-10 and Lisp machines during the period 1975-1990. Like Telnet and Rlogin, it is unsecured, so modern systems almost never support it. To make a connection of this type, select \q{SUPDUP} from the \q{Connection type} radio buttons on the \q{Session} panel (see \k{config-hostname}). For further configuration options (character set, more processing, scrolling), you can use the \q{SUPDUP} configuration panel (see \k{config-supdup}). In SUPDUP, terminal emulation is more integrated with the network protocol than in other protocols such as SSH. The SUPDUP protocol can thus only be used with PuTTY proper, not with the command-line tool Plink. The SUPDUP protocol does not support changing the terminal dimensions, so this capability is disabled during a SUPDUP session. SUPDUP provides no well defined means for one end of the connection to notify the other that the connection is finished. Therefore, PuTTY in SUPDUP mode will remain connected until you close the window using the close button. \H{using-cmdline} The PuTTY command line PuTTY can be made to do various things without user intervention by supplying \i{command-line arguments} (e.g., from a \i{command prompt window}, or a \i{Windows shortcut}). \S{using-cmdline-session} Starting a session from the command line \I\c{-ssh}\I\c{-ssh-connection}\I\c{-telnet}\I\c{-rlogin}\I\c{-supdup}\I\c{-raw}\I\c{-serial}These options allow you to bypass the configuration window and launch straight into a session. To start a connection to a server called \c{host}: \c putty.exe [-ssh | -ssh-connection | -telnet | -rlogin | -supdup | -raw] [user@]host If this syntax is used, settings are taken from the \i{Default Settings} (see \k{config-saving}); \c{user} overrides these settings if supplied. Also, you can specify a protocol, which will override the default protocol (see \k{using-cmdline-protocol}). For telnet sessions, the following alternative syntax is supported (this makes PuTTY suitable for use as a URL handler for \i{telnet URLs} in \i{web browsers}): \c putty.exe telnet://host[:port]/ To start a connection to a serial port, e.g. COM1: \c putty.exe -serial com1 In order to start an existing saved session called \c{sessionname}, use the \c{-load} option (described in \k{using-cmdline-load}). \c putty.exe -load "session name" \S{using-cleanup} \i\c{-cleanup} If invoked with the \c{-cleanup} option, rather than running as normal, PuTTY will remove its \I{removing registry entries}registry entries and \i{random seed file} from the local machine (after confirming with the user). It will also attempt to remove information about recently launched sessions stored in the \q{jump list} on Windows 7 and up. Note that on \i{multi-user systems}, \c{-cleanup} only removes registry entries and files associated with the currently logged-in user. \S{using-general-opts} Standard command-line options PuTTY and its associated tools support a range of command-line options, most of which are consistent across all the tools. This section lists the available options in all tools. Options which are specific to a particular tool are covered in the chapter about that tool. \S2{using-cmdline-load} \i\c{-load}: load a saved session \I{saved sessions, loading from command line}The \c{-load} option causes PuTTY to load configuration details out of a saved session. If these details include a host name, then this option is all you need to make PuTTY start a session. You need double quotes around the session name if it contains spaces. If you want to create a \i{Windows shortcut} to start a PuTTY saved session, this is the option you should use: your shortcut should call something like \c d:\path\to\putty.exe -load "my session" (Note that PuTTY itself supports an alternative form of this option, for backwards compatibility. If you execute \i\c{putty @sessionname} it will have the same effect as \c{putty -load "sessionname"}. With the \c{@} form, no double quotes are required, and the \c{@} sign must be the very first thing on the command line. This form of the option is deprecated.) \S2{using-cmdline-protocol} Selecting a protocol: \c{-ssh}, \c{-ssh-connection}, \c{-telnet}, \c{-rlogin}, \c{-supdup}, \c{-raw}, \c{-serial} To choose which protocol you want to connect with, you can use one of these options: \b \i\c{-ssh} selects the SSH protocol. \b \i\c{-ssh-connection} selects the bare ssh-connection protocol. (This is only useful in specialised circumstances; see \k{config-psusan} for more information.) \b \i\c{-telnet} selects the Telnet protocol. \b \i\c{-rlogin} selects the Rlogin protocol. \b \i\c{-supdup} selects the SUPDUP protocol. \b \i\c{-raw} selects the raw protocol. \b \i\c{-serial} selects a serial connection. Most of these options are not available in the file transfer tools PSCP and PSFTP (which only work with the SSH protocol and the bare ssh-connection protocol). These options are equivalent to the \i{protocol selection} buttons in the Session panel of the PuTTY configuration box (see \k{config-hostname}). \S2{using-cmdline-v} \i\c{-v}: increase verbosity \I{verbose mode}Most of the PuTTY tools can be made to tell you more about what they are doing by supplying the \c{-v} option. If you are having trouble when making a connection, or you're simply curious, you can turn this switch on and hope to find out more about what is happening. \S2{using-cmdline-l} \i\c{-l}: specify a \i{login name} You can specify the user name to log in as on the remote server using the \c{-l} option. For example, \c{plink login.example.com -l fred}. These options are equivalent to the username selection box in the Connection panel of the PuTTY configuration box (see \k{config-username}). \S2{using-cmdline-portfwd} \I{-L-upper}\c{-L}, \I{-R-upper}\c{-R} and \I{-D-upper}\c{-D}: set up \i{port forwardings} As well as setting up port forwardings in the PuTTY configuration (see \k{config-ssh-portfwd}), you can also set up forwardings on the command line. The command-line options work just like the ones in Unix \c{ssh} programs. To \I{local port forwarding}forward a local port (say 5110) to a remote destination (say \cw{popserver.example.com} port 110), you can write something like one of these: \c putty -L 5110:popserver.example.com:110 -load mysession \c plink mysession -L 5110:popserver.example.com:110 To forward a \I{remote port forwarding}remote port to a local destination, just use the \c{-R} option instead of \c{-L}: \c putty -R 5023:mytelnetserver.myhouse.org:23 -load mysession \c plink mysession -R 5023:mytelnetserver.myhouse.org:23 To \I{listen address}specify an IP address for the listening end of the tunnel, prepend it to the argument: \c plink -L 127.0.0.5:23:localhost:23 myhost To set up \I{dynamic port forwarding}SOCKS-based dynamic port forwarding on a local port, use the \c{-D} option. For this one you only have to pass the port number: \c putty -D 4096 -load mysession For general information on port forwarding, see \k{using-port-forwarding}. These options are not available in the file transfer tools PSCP and PSFTP. \S2{using-cmdline-m} \i\c{-m}: \I{reading commands from a file}read a remote command or script from a file The \i\c{-m} option performs a similar function to the \q{\ii{Remote command}} box in the SSH panel of the PuTTY configuration box (see \k{config-command}). However, the \c{-m} option expects to be given a local file name, and it will read a command from that file. With some servers (particularly Unix systems), you can even put multiple lines in this file and execute more than one command in sequence, or a whole shell script; but this is arguably an abuse, and cannot be expected to work on all servers. In particular, it is known \e{not} to work with certain \q{embedded} servers, such as \i{Cisco} routers. This option is not available in the file transfer tools PSCP and PSFTP. \S2{using-cmdline-p} \I{-P-upper}\c{-P}: specify a \i{port number} The \c{-P} option is used to specify the port number to connect to. If you have a Telnet server running on port 9696 of a machine instead of port 23, for example: \c putty -telnet -P 9696 host.name \c plink -telnet -P 9696 host.name (Note that this option is more useful in Plink than in PuTTY, because in PuTTY you can write \c{putty -telnet host.name 9696} in any case.) This option is equivalent to the port number control in the Session panel of the PuTTY configuration box (see \k{config-hostname}). \S2{using-cmdline-pw} \i\c{-pw}: specify a \i{password} A simple way to automate a remote login is to supply your password on the command line. This is \e{not recommended} for reasons of security. If you possibly can, we recommend you set up public-key authentication instead. See \k{pubkey} for details. Note that the \c{-pw} option only works when you are using the SSH protocol. Due to fundamental limitations of Telnet, Rlogin, and SUPDUP, these protocols do not support automated password authentication. \S2{using-cmdline-agentauth} \i\c{-agent} and \i\c{-noagent}: control use of Pageant for authentication The \c{-agent} option turns on SSH authentication using Pageant, and \c{-noagent} turns it off. These options are only meaningful if you are using SSH. See \k{pageant} for general information on \i{Pageant}. These options are equivalent to the agent authentication checkbox in the Auth panel of the PuTTY configuration box (see \k{config-ssh-tryagent}). \S2{using-cmdline-agent} \I{-A-upper}\c{-A} and \i\c{-a}: control \i{agent forwarding} The \c{-A} option turns on SSH agent forwarding, and \c{-a} turns it off. These options are only meaningful if you are using SSH. See \k{pageant} for general information on \i{Pageant}, and \k{pageant-forward} for information on agent forwarding. Note that there is a security risk involved with enabling this option; see \k{pageant-security} for details. These options are equivalent to the agent forwarding checkbox in the Auth panel of the PuTTY configuration box (see \k{config-ssh-agentfwd}). These options are not available in the file transfer tools PSCP and PSFTP. \S2{using-cmdline-x11} \I{-X-upper}\c{-X} and \i\c{-x}: control \i{X11 forwarding} The \c{-X} option turns on X11 forwarding in SSH, and \c{-x} turns it off. These options are only meaningful if you are using SSH. For information on X11 forwarding, see \k{using-x-forwarding}. These options are equivalent to the X11 forwarding checkbox in the X11 panel of the PuTTY configuration box (see \k{config-ssh-x11}). These options are not available in the file transfer tools PSCP and PSFTP. \S2{using-cmdline-pty} \i\c{-t} and \I{-T-upper}\c{-T}: control \i{pseudo-terminal allocation} The \c{-t} option ensures PuTTY attempts to allocate a pseudo-terminal at the server, and \c{-T} stops it from allocating one. These options are only meaningful if you are using SSH. These options are equivalent to the \q{Don't allocate a pseudo-terminal} checkbox in the SSH panel of the PuTTY configuration box (see \k{config-ssh-pty}). These options are not available in the file transfer tools PSCP and PSFTP. \S2{using-cmdline-noshell} \I{-N-upper}\c{-N}: suppress starting a \I{suppressing remote shell}shell or command The \c{-N} option prevents PuTTY from attempting to start a shell or command on the remote server. You might want to use this option if you are only using the SSH connection for port forwarding, and your user account on the server does not have the ability to run a shell. This feature is only available in SSH protocol version 2 (since the version 1 protocol assumes you will always want to run a shell). This option is equivalent to the \q{Don't start a shell or command at all} checkbox in the SSH panel of the PuTTY configuration box (see \k{config-ssh-noshell}). This option is not available in the file transfer tools PSCP and PSFTP. \S2{using-cmdline-ncmode} \I{-nc}\c{-nc}: make a \i{remote network connection} in place of a remote shell or command The \c{-nc} option prevents Plink (or PuTTY) from attempting to start a shell or command on the remote server. Instead, it will instruct the remote server to open a network connection to a host name and port number specified by you, and treat that network connection as if it were the main session. You specify a host and port as an argument to the \c{-nc} option, with a colon separating the host name from the port number, like this: \c plink host1.example.com -nc host2.example.com:1234 You might want to use this feature if you needed to make an SSH connection to a target host which you can only reach by going through a proxy host, and rather than using port forwarding you prefer to use the local proxy feature (see \k{config-proxy-type} for more about local proxies). In this situation you might select \q{Local} proxy type, set your local proxy command to be \cq{plink %proxyhost -nc %host:%port}, enter the target host name on the Session panel, and enter the directly reachable proxy host name on the Proxy panel. This feature is only available in SSH protocol version 2 (since the version 1 protocol assumes you will always want to run a shell). It is not available in the file transfer tools PSCP and PSFTP. It is available in PuTTY itself, although it is unlikely to be very useful in any tool other than Plink. Also, \c{-nc} uses the same server functionality as port forwarding, so it will not work if your server administrator has disabled port forwarding. (The option is named \c{-nc} after the Unix program \W{http://www.vulnwatch.org/netcat/}\c{nc}, short for \q{netcat}. The command \cq{plink host1 -nc host2:port} is very similar in functionality to \cq{plink host1 nc host2 port}, which invokes \c{nc} on the server and tells it to connect to the specified destination. However, Plink's built-in \c{-nc} option does not depend on the \c{nc} program being installed on the server.) \S2{using-cmdline-compress} \I{-C-upper}\c{-C}: enable \i{compression} The \c{-C} option enables compression of the data sent across the network. This option is only meaningful if you are using SSH. This option is equivalent to the \q{Enable compression} checkbox in the SSH panel of the PuTTY configuration box (see \k{config-ssh-comp}). \S2{using-cmdline-sshprot} \i\c{-1} and \i\c{-2}: specify an \i{SSH protocol version} The \c{-1} and \c{-2} options force PuTTY to use version \I{SSH-1}1 or version \I{SSH-2}2 of the SSH protocol. These options are only meaningful if you are using SSH. These options are equivalent to selecting the SSH protocol version in the SSH panel of the PuTTY configuration box (see \k{config-ssh-prot}). \S2{using-cmdline-ipversion} \i\c{-4} and \i\c{-6}: specify an \i{Internet protocol version} The \c{-4} and \c{-6} options force PuTTY to use the older Internet protocol \i{IPv4} or the newer \i{IPv6} for most outgoing connections. These options are equivalent to selecting your preferred Internet protocol version as \q{IPv4} or \q{IPv6} in the Connection panel of the PuTTY configuration box (see \k{config-address-family}). \S2{using-cmdline-identity} \i\c{-i}: specify an SSH \i{private key} The \c{-i} option allows you to specify the name of a private key file in \c{*.\i{PPK}} format which PuTTY will use to authenticate with the server. This option is only meaningful if you are using SSH. If you are using Pageant, you can also specify a \e{public} key file (in RFC 4716 or OpenSSH format) to identify a specific key file to use. (This won't work if you're not running Pageant, of course.) For general information on \i{public-key authentication}, see \k{pubkey}. This option is equivalent to the \q{Private key file for authentication} box in the Auth panel of the PuTTY configuration box (see \k{config-ssh-privkey}). \S2{using-cmdline-no-trivial-auth} \i\c{-no-trivial-auth}: disconnect if SSH authentication succeeds trivially This option causes PuTTY to abandon an SSH session if the server accepts authentication without ever having asked for any kind of password or signature or token. See \k{config-ssh-notrivialauth} for why you might want this. \S2{using-cmdline-loghost} \i\c{-loghost}: specify a \i{logical host name} This option overrides PuTTY's normal SSH \I{host key cache}host key caching policy by telling it the name of the host you expect your connection to end up at (in cases where this differs from the location PuTTY thinks it's connecting to). It can be a plain host name, or a host name followed by a colon and a port number. See \k{config-loghost} for more detail on this. \S2{using-cmdline-hostkey} \i\c{-hostkey}: \I{manually configuring host keys}manually specify an expected host key This option overrides PuTTY's normal SSH \I{host key cache}host key caching policy by telling it exactly what host key to expect, which can be useful if the normal automatic host key store in the Registry is unavailable. The argument to this option should be either a host key fingerprint, or an SSH-2 public key blob. See \k{config-ssh-kex-manual-hostkeys} for more information. You can specify this option more than once if you want to configure more than one key to be accepted. \S2{using-cmdline-pgpfp} \i\c{-pgpfp}: display \i{PGP key fingerprint}s This option causes the PuTTY tools not to run as normal, but instead to display the fingerprints of the PuTTY PGP Master Keys, in order to aid with \i{verifying new versions}. See \k{pgpkeys} for more information. \S2{using-cmdline-sercfg} \i\c{-sercfg}: specify serial port \i{configuration} This option specifies the configuration parameters for the serial port (baud rate, stop bits etc). Its argument is interpreted as a comma-separated list of configuration options, which can be as follows: \b Any single digit from 5 to 9 sets the number of data bits. \b \cq{1}, \cq{1.5} or \cq{2} sets the number of stop bits. \b Any other numeric string is interpreted as a baud rate. \b A single lower-case letter specifies the parity: \cq{n} for none, \cq{o} for odd, \cq{e} for even, \cq{m} for mark and \cq{s} for space. \b A single upper-case letter specifies the flow control: \cq{N} for none, \cq{X} for XON/XOFF, \cq{R} for RTS/CTS and \cq{D} for DSR/DTR. For example, \cq{-sercfg 19200,8,n,1,N} denotes a baud rate of 19200, 8 data bits, no parity, 1 stop bit and no flow control. \S2{using-cmdline-sshlog} \i\c{-sessionlog}, \i\c{-sshlog}, \i\c{-sshrawlog}: enable session logging These options cause the PuTTY network tools to write out a \i{log file}. Each of them expects a file name as an argument, e.g. \cq{-sshlog putty.log} causes an SSH packet log to be written to a file called \cq{putty.log}. The three different options select different logging modes, all available from the GUI too: \b \c{-sessionlog} selects \q{All session output} logging mode. \b \c{-sshlog} selects \q{SSH packets} logging mode. \b \c{-sshrawlog} selects \q{SSH packets and raw data} logging mode. For more information on logging configuration, see \k{config-logging}. \S2{using-cmdline-logfileexists} \i\c{-logoverwrite}, \i\c{-logappend}: control behaviour with existing log file If logging has been enabled (in the saved configuration, or by another command-line option), and the specified log file already exists, these options tell the PuTTY network tools what to do so that they don't have to ask the user. See \k{config-logfileexists} for details. \S2{using-cmdline-proxycmd} \i\c{-proxycmd}: specify a local proxy command This option enables PuTTY's mode for running a \I{Local proxy}command on the local machine and using it as a proxy for the network connection. It expects a shell command string as an argument. See \k{config-proxy-type} for more information on this, and on other proxy settings. In particular, note that since the special sequences described there are understood in the argument string, literal backslashes must be doubled (if you want \c{\\} in your command, you must put \c{\\\\} on the command line). \S2{using-cmdline-restrict-acl} \i\c{-restrict-acl}: restrict the \i{Windows process ACL} This option (on Windows only) causes PuTTY (or another PuTTY tool) to try to lock down the operating system's access control on its own process. If this succeeds, it should present an extra obstacle to malware that has managed to run under the same user id as the PuTTY process, by preventing it from attaching to PuTTY using the same interfaces debuggers use and either reading sensitive information out of its memory or hijacking its network session. This option is not enabled by default, because this form of interaction between Windows programs has many legitimate uses, including accessibility software such as screen readers. Also, it cannot provide full security against this class of attack in any case, because PuTTY can only lock down its own ACL \e{after} it has started up, and malware could still get in if it attacks the process between startup and lockdown. So it trades away noticeable convenience, and delivers less real security than you might want. However, if you do want to make that tradeoff anyway, the option is available. A PuTTY process started with \c{-restrict-acl} will pass that on to any processes started with Duplicate Session, New Session etc. (However, if you're invoking PuTTY tools explicitly, for instance as a proxy command, you'll need to arrange to pass them the \c{-restrict-acl} option yourself, if that's what you want.) If Pageant is started with the \c{-restrict-acl} option, and you use it to launch a PuTTY session from its \ii{System Tray} submenu, then Pageant will \e{not} default to starting the PuTTY subprocess with a restricted ACL. This is because PuTTY is more likely to suffer reduced functionality as a result of restricted ACLs (e.g. screen reader software will have a greater need to interact with it), whereas Pageant stores the more critical information (hence benefits more from the extra protection), so it's reasonable to want to run Pageant but not PuTTY with the ACL restrictions. You can force Pageant to start subsidiary PuTTY processes with a restricted ACL if you also pass the \i\c{-restrict-putty-acl} option. putty-0.76/doc/vids.but0000644000175000017500000000017714072266310012010 00000000000000\# Fallback versionid for use when the build system hasn't provided a better one. \versionid no version information available putty-0.76/doc/licence.but0000644000175000017500000000312114072266315012442 00000000000000\# Generated by licence.pl from LICENCE. \# You should edit those files rather than editing this one. \A{licence} PuTTY \ii{Licence} PuTTY is \i{copyright} 1997-2021 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, Christopher Staite, Lorenz Diener, Christian Brabandt, Jeff Smith, Pavel Kryukov, Maxim Kuznetsov, Svyatoslav Kuzmich, Nico Williams, Viktor Dukhovni, Josh Dersch, Lars Brinkhoff, 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 \q{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: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED \q{AS IS}, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. putty-0.76/doc/copy.but0000644000175000017500000000023614072266315012016 00000000000000\# Generated by licence.pl from LICENCE. \# You should edit those files rather than editing this one. \define{shortcopyrightdetails} 1997-2021 Simon Tatham putty-0.76/doc/putty.10000644000175000017500000002634114072266315011604 00000000000000.ie \n(.g .ds Aq \(aq .el .ds Aq ' .TH "putty" "1" "2004\(hy03\(hy24" "PuTTY\ tool\ suite" "PuTTY\ tool\ suite" .SH "NAME" .PP \fBputty\fP - GUI SSH, Telnet, Rlogin, and SUPDUP client for X .SH "SYNOPSIS" .PP .nf \fBputty\fP\ [\ \fIoptions\fP\ ]\ [\ \fIhost\fP\ ] .fi .SH "DESCRIPTION" .PP \fBputty\fP is a graphical SSH, Telnet, Rlogin, and SUPDUP client for X. It is a direct port of the Windows SSH client of the same name. .SH "OPTIONS" .PP The command-line options supported by \fBputty\fP are: .IP "\fB\-\-display\fP \fIdisplay\-name\fP" Specify the X display on which to open \fBputty\fP. (Note this option has a double minus sign, even though none of the others do. This is because this option is supplied automatically by GTK. Sorry.) .IP "\fB\-fn\fP \fIfont-name\fP" Specify the font to use for normal text displayed in the terminal. For example, \fB\-fn\ fixed\fP, \fB\-fn\ "Monospace\ 12"\fP. .IP "\fB\-fb\fP \fIfont-name\fP" Specify the font to use for bold text displayed in the terminal. If the \fBBoldAsColour\fP 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 \fBBoldAsColour\fP is set to 0 or 2 and you do not specify a bold font, \fBputty\fP will overprint the normal font to make it look bolder. .IP "\fB\-fw\fP \fIfont-name\fP" Specify the font to use for double-width characters (typically Chinese, Japanese and Korean text) displayed in the terminal. .IP "\fB\-fwb\fP \fIfont-name\fP" Specify the font to use for bold double-width characters (typically Chinese, Japanese and Korean text). Like \fB-fb\fP, this will be ignored unless the \fBBoldAsColour\fP resource is set to 0 or 2. .IP "\fB\-geometry\fP \fIgeometry\fP" Specify the size of the terminal, in rows and columns of text. See \fIX(7)\fP for more information on the syntax of geometry specifications. .IP "\fB\-sl\fP \fIlines\fP" Specify the number of lines of scrollback to save off the top of the terminal. .IP "\fB\-fg\fP \fIcolour\fP" Specify the foreground colour to use for normal text. .IP "\fB\-bg\fP \fIcolour\fP" Specify the background colour to use for normal text. .IP "\fB\-bfg\fP \fIcolour\fP" Specify the foreground colour to use for bold text, if the \fBBoldAsColour\fP resource is set to 1 (the default) or 2. .IP "\fB\-bbg\fP \fIcolour\fP" Specify the foreground colour to use for bold reverse-video text, if the \fBBoldAsColour\fP 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 \fIin\fP the background colour.) .IP "\fB\-cfg\fP \fIcolour\fP" Specify the foreground colour to use for text covered by the cursor. .IP "\fB\-cbg\fP \fIcolour\fP" Specify the background colour to use for text covered by the cursor. In other words, this is the main colour of the cursor. .IP "\fB\-title\fP \fItitle\fP" Specify the initial title of the terminal window. (This can be changed under control of the server.) .IP "\fB\-sb\-\fP or \fB+sb\fP" Tells \fBputty\fP not to display a scroll bar. .IP "\fB\-sb\fP" Tells \fBputty\fP to display a scroll bar: this is the opposite of \fB\-sb\-\fP. This is the default option: you will probably only need to specify it explicitly if you have changed the default using the \fBScrollBar\fP resource. .IP "\fB\-log\fP \fIlogfile\fP, \fB\-sessionlog\fP \fIlogfile\fP" This option makes \fBputty\fP log all the terminal output to a file as well as displaying it in the terminal. .IP "\fB\-sshlog\fP \fIlogfile\fP" .IP "\fB\-sshrawlog\fP \fIlogfile\fP" For SSH connections, these options make \fBputty\fP log protocol details to a file. (Some of these may be sensitive, although by default an effort is made to suppress obvious passwords.) .RS .PP \fB\-sshlog\fP logs decoded SSH packets and other events (those that \fB\-v\fP would print). \fB\-sshrawlog\fP additionally logs the raw encrypted packet data. .RE .IP "\fB\-logoverwrite\fP" If \fBputty\fP is configured to write to a log file that already exists, discard the existing file. .IP "\fB\-logappend\fP" If \fBputty\fP is configured to write to a log file that already exists, append new log data to the existing file. .IP "\fB\-cs\fP \fIcharset\fP" This option specifies the character set in which \fBputty\fP should assume the session is operating. This character set will be used to interpret all the data received from the session, and all input you type or paste into \fBputty\fP will be converted into this character set before being sent to the session. .RS .PP Any character set name which is valid in a MIME header (and supported by \fBputty\fP) should be valid here (examples are `\fBISO-8859-1\fP', `\fBwindows-1252\fP' or `\fBUTF-8\fP'). Also, any character encoding which is valid in an X logical font description should be valid (`\fBibm-cp437\fP', for example). .PP \fBputty\fP\*(Aqs default behaviour is to use the same character encoding as its primary font. If you supply a Unicode (\fBiso10646-1\fP) font, it will default to the UTF-8 character set. .PP Character set names are case-insensitive. .RE .IP "\fB\-nethack\fP" Tells \fBputty\fP to enable NetHack keypad mode, in which the numeric keypad generates the NetHack \fBhjklyubn\fP direction keys. This enables you to play NetHack with the numeric keypad without having to use the NetHack \fBnumber_pad\fP option (which requires you to press `\fBn\fP' before any repeat count). So you can move with the numeric keypad, and enter repeat counts with the normal number keys. .IP "\fB\-help\fP, \fB\-\-help\fP" Display a message summarizing the available options. .IP "\fB\-pgpfp\fP" Display the fingerprints of the PuTTY PGP Master Keys, to aid in verifying new files released by the PuTTY team. .IP "\fB\-load\fP \fIsession\fP" Load a saved session by name. This allows you to run a saved session straight from the command line without having to go through the configuration box first. .IP "\fB\-ssh\fP, \fB\-telnet\fP, \fB\-rlogin\fP, \fB\-supdup\fP, \fB\-raw\fP, \fB-ssh-connection\fP, \fB\-serial\fP" Select the protocol \fBputty\fP will use to make the connection. .IP "\fB\-proxycmd\fP \fIcommand\fP" Instead of making a TCP connection, use \fIcommand\fP as a proxy; network traffic will be redirected to the standard input and output of \fIcommand\fP. \fIcommand\fP must be a single word, so is likely to need quoting by the shell. .RS .PP The special strings \fB%host\fP and \fB%port\fP in \fIcommand\fP will be replaced by the hostname and port number you want to connect to; to get a literal \fB%\fP sign, enter \fB%%\fP. .PP Backslash escapes are also supported, such as sequences like \fB\en\fP being replaced by a literal newline; to get a literal backslash, enter \fB\e\e\fP. (Further escaping may be required by the shell.) .PP (See the main PuTTY manual for full details of the supported \fB%\fP- and backslash-delimited tokens, although most of them are probably not very useful in this context.) .RE .IP "\fB\-l\fP \fIusername\fP" Specify the username to use when logging in to the server. .IP "\fB\-L\fP \fB[\fP\fIsrcaddr\fP\fB:]\fP\fIsrcport\fP\fB:\fP\fIdesthost\fP\fB:\fP\fIdestport\fP" Set up a local port forwarding: listen on \fIsrcport\fP (or \fIsrcaddr\fP:\fIsrcport\fP if specified), and forward any connections over the SSH connection to the destination address \fIdesthost\fP:\fIdestport\fP. Only works in SSH. .IP "\fB\-R\fP \fB[\fP\fIsrcaddr\fP\fB:]\fP\fIsrcport\fP\fB:\fP\fIdesthost\fP\fB:\fP\fIdestport\fP" Set up a remote port forwarding: ask the SSH server to listen on \fIsrcport\fP (or \fIsrcaddr\fP:\fIsrcport\fP if specified), and to forward any connections back over the SSH connection where the client will pass them on to the destination address \fIdesthost\fP:\fIdestport\fP. Only works in SSH. .IP "\fB\-D\fP [\fIsrcaddr\fP:]\fIsrcport\fP" Set up dynamic port forwarding. The client listens on \fIsrcport\fP (or \fIsrcaddr\fP:\fIsrcport\fP if specified), and implements a SOCKS server. So you can point SOCKS-aware applications at this port and they will automatically use the SSH connection to tunnel all their connections. Only works in SSH. .IP "\fB\-P\fP \fIport\fP" Specify the port to connect to the server on. .IP "\fB\-A\fP, \fB\-a\fP" Enable (\fB\-A\fP) or disable (\fB\-a\fP) SSH agent forwarding. Currently this only works with OpenSSH and SSH-1. .IP "\fB\-X\fP, \fB\-x\fP" Enable (\fB\-X\fP) or disable (\fB\-x\fP) X11 forwarding. .IP "\fB\-T\fP, \fB\-t\fP" Enable (\fB\-t\fP) or disable (\fB\-T\fP) the allocation of a pseudo-terminal at the server end. .IP "\fB\-C\fP" Enable zlib-style compression on the connection. .IP "\fB\-1\fP, \fB\-2\fP" Select SSH protocol version 1 or 2. .IP "\fB-4\fP, \fB-6\fP" Force use of IPv4 or IPv6 for network connections. .IP "\fB\-i\fP \fIkeyfile\fP" Private key file for user authentication. For SSH-2 keys, this key file must be in PuTTY's PPK format, not OpenSSH's format or anyone else's. .RS .PP If you are using an authentication agent, you can also specify a \fIpublic\fP key here (in RFC 4716 or OpenSSH format), to identify which of the agent's keys to use. .RE .IP "\fB\-noagent\fP" Don't try to use an authentication agent for local authentication. (This doesn't affect agent forwarding.) .IP "\fB\-agent\fP" Allow use of an authentication agent. (This option is only necessary to override a setting in a saved session.) .IP "\fB\-no\-trivial\-auth\fP" Disconnect from any SSH server which accepts authentication without ever having asked for any kind of password or signature or token. (You might want to enable this for a server you always expect to challenge you, for instance to ensure you don't accidentally type your key file's passphrase into a compromised server spoofing PuTTY's passphrase prompt.) .IP "\fB\-hostkey\fP \fIkey\fP" Specify an acceptable host public key. This option may be specified multiple times; each key can be either a fingerprint (\fBSHA256:AbCdE...\fP, \fB99:aa:bb:...\fP, etc) or a base64-encoded blob in OpenSSH\*(Aqs one-line format. .RS .PP Specifying this option overrides automated host key management; \fIonly\fP the key(s) specified on the command-line will be accepted (unless a saved session also overrides host keys, in which case those will be added to), and the host key cache will not be written. .RE .IP "\fB\-sercfg\fP \fIconfiguration-string\fP" Specify the configuration parameters for the serial port, in \fB-serial\fP mode. \fIconfiguration-string\fP should be a comma-separated list of configuration parameters as follows: .RS .IP "\fB\(bu\fP" Any single digit from 5 to 9 sets the number of data bits. .IP "\fB\(bu\fP" `\fB1\fP', `\fB1.5\fP' or `\fB2\fP' sets the number of stop bits. .IP "\fB\(bu\fP" Any other numeric string is interpreted as a baud rate. .IP "\fB\(bu\fP" A single lower-case letter specifies the parity: `\fBn\fP' for none, `\fBo\fP' for odd, `\fBe\fP' for even, `\fBm\fP' for mark and `\fBs\fP' for space. .IP "\fB\(bu\fP" A single upper-case letter specifies the flow control: `\fBN\fP' for none, `\fBX\fP' for XON/XOFF, `\fBR\fP' for RTS/CTS and `\fBD\fP' for DSR/DTR. .RE .SH "SAVED SESSIONS" .PP Saved sessions are stored in a \fB.putty/sessions\fP subdirectory in your home directory. .SH "MORE INFORMATION" .PP For more information on PuTTY, it's probably best to go and look at the manual on the web page: .PP \fBhttps://www.chiark.greenend.org.uk/~sgtatham/putty/\fP .SH "BUGS" .PP This man page isn't terribly complete. putty-0.76/doc/puttygen.10000644000175000017500000002732514072266315012301 00000000000000.ie \n(.g .ds Aq \(aq .el .ds Aq ' .TH "puttygen" "1" "2004\(hy03\(hy24" "PuTTY\ tool\ suite" "PuTTY\ tool\ suite" .SH "NAME" .PP \fBputtygen\fP - public-key generator for the PuTTY tools .SH "SYNOPSIS" .PP .nf \fBputtygen\fP\ (\ \fIkeyfile\fP\ |\ \fB\-t\fP\ \fIkeytype\fP\ [\ \fB\-b\fP\ \fIbits\fP\ ]\ [\ \fB\-\-primes\fP\ \fImethod\fP\ ]\ [\ \fB\-q\fP\ ]\ ) \ \ \ \ \ \ \ \ \ [\ \fB\-C\fP\ \fInew\-comment\fP\ ]\ [\ \fB\-P\fP\ ]\ [\ \fB\-\-reencrypt\fP\ ] \ \ \ \ \ \ \ \ \ [\ \fB\-O\fP\ \fIoutput\-type\fP\ |\ \fB\-l\fP\ |\ \fB\-L\fP\ |\ \fB\-p\fP\ |\ \fB\-\-dump\fP\ ]\ [\ \fB\-E\fP\ \fIfptype\fP\ ] \ \ \ \ \ \ \ \ \ \ \ \ [\ \fB\-\-ppk\-param\fP\ \fIkey\fP\fB=\fP\fIvalue\fP\fB,\fP...\ ] \ \ \ \ \ \ \ \ \ [\ \fB\-o\fP\ \fIoutput\-file\fP\ ] .fi .SH "DESCRIPTION" .PP \fBputtygen\fP is a tool to generate and manipulate SSH public and private key pairs. It is part of the PuTTY suite, although it can also interoperate with the key formats used by some other SSH clients. .PP When you run \fBputtygen\fP, it does three things. Firstly, it either loads an existing key file (if you specified \fIkeyfile\fP), or generates a new key (if you specified \fIkeytype\fP). Then, it optionally makes modifications to the key (such as changing the comment and/or the passphrase); finally, it outputs the key, or some information about the key, to a file. .PP All three of these phases are controlled by the options described in the following section. .SH "OPTIONS" .PP In the first phase, \fBputtygen\fP either loads or generates a key. Note that generating a key requires random data, which can cause \fBputtygen\fP to pause, possibly for some time if your system does not have much randomness available. .PP The options to control this phase are: .IP "\fIkeyfile\fP" Specify a key file to be loaded. (Use `\fB-\fP' to read a key file from standard input.) .RS .PP Usually this will be a private key, which can be in the (de facto standard) SSH-1 key format, or in PuTTY's SSH-2 key format, or in either of the SSH-2 private key formats used by OpenSSH and ssh.com's implementation. .PP You can also specify a file containing only a \fIpublic\fP key here. The operations you can do are limited to outputting another public key format or a fingerprint. Public keys can be in RFC 4716 or OpenSSH format, or the standard SSH-1 format. .RE .IP "\fB\-t\fP \fIkeytype\fP" Specify a type of key to generate. The acceptable values here are \fBrsa\fP, \fBdsa\fP, \fBecdsa\fP, \fBeddsa\fP, \fBed25519\fP, and \fBed448\fP (to generate SSH-2 keys), and \fBrsa1\fP (to generate SSH-1 keys). .IP "\fB\-b\fP \fIbits\fP" Specify the size of the key to generate, in bits. Default for \fBrsa\fP and \fBdsa\fP keys is 2048. .IP "\fB\-\-primes\fP \fImethod\fP" Method for generating prime numbers. The acceptable values here are \fBprobable\fP (the default), \fBproven\fP, and \fBproven-even\fP; the later methods are slower. (Various synonyms for these method names are also accepted.) .RS .PP The `probable primes' method sounds unsafe, but it's the most commonly used prime-generation strategy. There is in theory a possibility that it might accidentally generate a number that isn't prime, but the software does enough checking to make that probability vanishingly small (less than 1 in 2^80, or 1 in 10^24). So, in practice, nobody worries about it very much. .PP The other methods cause PuTTYgen to use numbers that it is \fIsure\fP are prime, because it generates the output number together with a proof of its primality. This takes more effort, but it eliminates that theoretical risk in the probabilistic method. .PP You might choose to switch from probable to proven primes if you have a local security standard that demands it, or if you don't trust the probabilistic argument for the safety of the usual method. .RE .IP "\fB\-\-strong-rsa\fP" When generating an RSA key, make sure the prime factors of the key modulus are `strong primes'. A strong prime is a prime number chosen to have a particular structure that makes certain factoring algorithms more difficult to apply, so some security standards recommend their use. However, the most modern factoring algorithms are unaffected, so this option is probably not worth turning on \fIunless\fP you have a local standard that recommends it. .IP "\fB\-q\fP" Suppress the progress display when generating a new key. .IP "\fB\-\-old\-passphrase\fP \fIfile\fP" Specify a file name; the first line will be read from this file (removing any trailing newline) and used as the old passphrase. \fBCAUTION:\fP If the passphrase is important, the file should be stored on a temporary filesystem or else securely erased after use. .IP "\fB\-\-random\-device\fP \fIdevice\fP" Specify device to read entropy from. By default, \fBputtygen\fP uses \fB/dev/urandom\fP, falling back to \fB/dev/random\fP if it has to. .PP In the second phase, \fBputtygen\fP optionally alters properties of the key it has loaded or generated. The options to control this are: .IP "\fB\-C\fP \fInew\-comment\fP" Specify a comment string to describe the key. This comment string will be used by PuTTY to identify the key to you (when asking you to enter the passphrase, for example, so that you know which passphrase to type). .IP "\fB\-P\fP" Indicate that you want to change the key's passphrase. This is automatic when you are generating a new key, but not when you are modifying an existing key. .IP "\fB\-\-reencrypt\fP" For an existing private key saved with a passphrase, refresh the encryption without changing the passphrase. .RS .PP This is most likely to be useful with the \fB\-\-ppk-param\fP option, to change some aspect of the key file\*(Aqs format or encryption. .RE .IP "\fB\-\-ppk-param\fP \fIkey\fP\fB=\fP\fIvalue\fP\fB,\fP..." When saving a PPK file (the default \fBprivate\fP output type for SSH-2 keys), adjust details of the on-disk format. .RS .PP Aspects to change are specified as a series of \fIkey\fP\fB=\fP\fIvalue\fP pairs separated by commas. The \fIkey\fPs are: .IP "\fBversion\fP" The PPK format version. Possible values are \fB3\fP (the default) and \fB2\fP (which is less resistant to brute-force decryption, but which you might need if your key needs to be used by old versions of PuTTY tools, or other PPK consumers). .RS .PP The following \fIkey\fPs only affect PPK version 3 files. .RE .IP "\fBkdf\fP" The variant of the Argon2 key derivation function to use. Options are \fBargon2id\fP (default, and recommended), \fBargon2i\fP, and \fBargon2d\fP. .RS .PP You might change this if you consider your exposure to side-channel attacks to be different to the norm. .RE .IP "\fBmemory\fP" The amount of memory needed to decrypt the key, in Kbyte. Default is 8192 (i.e., 8 Mbyte). .IP "\fBtime\fP" Approximate time, on this machine, required to attempt decrypting the key, in milliseconds. Default is 100 (ms). .IP "\fBpasses\fP" Alternative to \fBtime\fP: explicitly specify the number of hash passes required to attempt decrypting the key. .IP "\fBparallelism\fP" Number of parallelisable threads that can be used to decrypt the key. Default is 1 (force decryption to run single-threaded). .RE .PP In the third phase, \fBputtygen\fP saves the key or information about it. The options to control this are: .IP "\fB\-O\fP \fIoutput\-type\fP" Specify the type of output you want \fBputtygen\fP to produce. Acceptable options are: .RS .IP "\fBprivate\fP" Save the private key in a format usable by PuTTY. This will either be the standard SSH-1 key format, or PuTTY's own SSH-2 key format (`PPK'). This is the default. .IP "\fBpublic\fP" Save the public key only. For SSH-1 keys, the standard public key format will be used (`\fB1024 37 5698745\fP...'). For SSH-2 keys, the public key will be output in the format specified by RFC 4716, which is a multi-line text file beginning with the line `\fB---- BEGIN SSH2 PUBLIC KEY ----\fP'. .IP "\fBpublic-openssh\fP" Save the public key only, in a format usable by OpenSSH. For SSH-1 keys, this output format behaves identically to \fBpublic\fP. For SSH-2 keys, the public key will be output in the OpenSSH format, which is a single line (`\fBssh-rsa AAAAB3NzaC1yc2\fP...'). .IP "\fBfingerprint\fP" Print a fingerprint of the public key. The \fB-E\fP option lets you specify which fingerprinting algorithm to use. All algorithms are believed compatible with OpenSSH. .IP "\fBprivate-openssh\fP" Save an SSH-2 private key in OpenSSH's format, using the oldest format available to maximise backward compatibility. This option is not permitted for SSH-1 keys. .IP "\fBprivate-openssh-new\fP" As \fBprivate-openssh\fP, except that it forces the use of OpenSSH\*(Aqs newer format even for RSA, DSA, and ECDSA keys. .IP "\fBprivate-sshcom\fP" Save an SSH-2 private key in ssh.com's format. This option is not permitted for SSH-1 keys. .IP "\fBtext\fP" Save a textual dump of the numeric components comprising the key (both the public and private parts, if present). Useful for debugging, or for using PuTTYgen as a key generator for applications other than SSH. .RS .PP The output consists of a series of \fBname=value\fP lines, where each \fBvalue\fP is either a C-like string literal in double quotes, or a hexadecimal number starting with \fB0x...\fP .RE .PP If no output type is specified, the default is \fBprivate\fP. .RE .IP "\fB\-o\fP \fIoutput\-file\fP" Specify the file where \fBputtygen\fP should write its output. If this option is not specified, \fBputtygen\fP will assume you want to overwrite the original file if the input and output file types are the same (changing a comment or passphrase), and will assume you want to output to stdout if you are asking for a public key or fingerprint. Otherwise, the \fB\-o\fP option is required. .IP "\fB\-l\fP" Synonym for `\fB-O fingerprint\fP'. .IP "\fB\-L\fP" Synonym for `\fB-O public-openssh\fP'. .IP "\fB\-p\fP" Synonym for `\fB-O public\fP'. .IP "\fB\-\-dump\fP" Synonym for `\fB-O text\fP'. .IP "\fB-E\fP \fIfptype\fP" Specify the algorithm to use if generating a fingerprint. The options are \fBsha256\fP (the default) and \fBmd5\fP. .IP "\fB\-\-new\-passphrase\fP \fIfile\fP" Specify a file name; the first line will be read from this file (removing any trailing newline) and used as the new passphrase. If the file is empty then the saved key will be unencrypted. \fBCAUTION:\fP If the passphrase is important, the file should be stored on a temporary filesystem or else securely erased after use. .PP The following options do not run PuTTYgen as normal, but print informational messages and then quit: .IP "\fB\-h\fP, \fB\-\-help\fP" Display a message summarizing the available options. .IP "\fB\-V\fP, \fB\-\-version\fP" Display the version of PuTTYgen. .IP "\fB\-\-pgpfp\fP" Display the fingerprints of the PuTTY PGP Master Keys, to aid in verifying new files released by the PuTTY team. .SH "EXAMPLES" .PP To generate an SSH-2 RSA key pair and save it in PuTTY's own format (you will be prompted for the passphrase): .PP .nf puttygen\ \-t\ rsa\ \-C\ "my\ home\ key"\ \-o\ mykey.ppk .fi .PP To generate a larger (4096-bit) key: .PP .nf puttygen\ \-t\ rsa\ \-b\ 4096\ \-C\ "my\ home\ key"\ \-o\ mykey.ppk .fi .PP To change the passphrase on a key (you will be prompted for the old and new passphrases): .PP .nf puttygen\ \-P\ mykey.ppk .fi .PP To change the comment on a key: .PP .nf puttygen\ \-C\ "new\ comment"\ mykey.ppk .fi .PP To convert a key into OpenSSH's private key format: .PP .nf puttygen\ mykey.ppk\ \-O\ private\-openssh\ \-o\ my\-openssh\-key .fi .PP To convert a key \fIfrom\fP another format (\fBputtygen\fP will automatically detect the input key type): .PP .nf puttygen\ my\-ssh.com\-key\ \-o\ mykey.ppk .fi .PP To display the SHA-256 fingerprint of a key (some key types require a passphrase to extract even this much information): .PP .nf puttygen\ \-l\ mykey.ppk .fi .PP To add the OpenSSH-format public half of a key to your authorised keys file: .PP .nf puttygen\ \-L\ mykey.ppk\ >>\ $HOME/.ssh/authorized_keys .fi putty-0.76/doc/plink.10000644000175000017500000002065314072266315011534 00000000000000.ie \n(.g .ds Aq \(aq .el .ds Aq ' .TH "plink" "1" "2004\(hy03\(hy24" "PuTTY\ tool\ suite" "PuTTY\ tool\ suite" .SH "NAME" .PP \fBplink\fP \- PuTTY link, command line network connection tool .SH "SYNOPSIS" .PP .nf \fBplink\fP\ [\fIoptions\fP]\ [\fIuser\fP\fB@\fP]\fIhost\fP\ [\fIcommand\fP] .fi .SH "DESCRIPTION" .PP \fBplink\fP is a network connection tool supporting several protocols. .SH "OPTIONS" .PP The command-line options supported by \fBplink\fP are: .IP "\fB-V\fP" Show version information and exit. .IP "\fB-pgpfp\fP" Display the fingerprints of the PuTTY PGP Master Keys and exit, to aid in verifying new files released by the PuTTY team. .IP "\fB-v\fP" Show verbose messages. .IP "\fB-load\fP \fIsession\fP" Load settings from saved session. .IP "\fB-ssh\fP" Force use of SSH protocol (default). .IP "\fB-telnet\fP" Force use of Telnet protocol. .IP "\fB-rlogin\fP" Force use of rlogin protocol. .IP "\fB-raw\fP" Force raw mode. .IP "\fB-serial\fP" Force serial mode. .IP "\fB-ssh-connection\fP" Force use of the `bare \fBssh-connection\fP' protocol. This is only likely to be useful when connecting to a \fIpsusan(1)\fP server, most likely with an absolute path to a Unix-domain socket in place of \fIhost\fP. .IP "\fB\-proxycmd\fP \fIcommand\fP" Instead of making a TCP connection, use \fIcommand\fP as a proxy; network traffic will be redirected to the standard input and output of \fIcommand\fP. \fIcommand\fP must be a single word, so is likely to need quoting by the shell. .RS .PP The special strings \fB%host\fP and \fB%port\fP in \fIcommand\fP will be replaced by the hostname and port number you want to connect to; to get a literal \fB%\fP sign, enter \fB%%\fP. .PP Backslash escapes are also supported, such as sequences like \fB\en\fP being replaced by a literal newline; to get a literal backslash, enter \fB\e\e\fP. (Further escaping may be required by the shell.) .PP (See the main PuTTY manual for full details of the supported \fB%\fP- and backslash-delimited tokens, although most of them are probably not very useful in this context.) .RE .IP "\fB-P\fP \fIport\fP" Connect to port \fIport\fP. .IP "\fB-l\fP \fIuser\fP" Set remote username to \fIuser\fP. .IP "\fB-m\fP \fIpath\fP" Read remote command(s) from local file \fIpath\fP. .IP "\fB-batch\fP" Disable interactive prompts. .IP "\fB-sanitise-stderr\fP" .IP "\fB-sanitise-stdout\fP" .IP "\fB-no-sanitise-stderr\fP" .IP "\fB-no-sanitise-stdout\fP" By default, Plink can choose to filter control characters if that seems appropriate, to prevent remote processes sending confusing escape sequences. These options override Plink's default behaviour to enable or disabling such filtering on the standard error and standard output channels. .IP "\fB-pw\fP \fIpassword\fP" Set remote password to \fIpassword\fP. \fICAUTION:\fP this will likely make the password visible to other users of the local machine (via commands such as `\fBw\fP'). .IP "\fB\-L\fP \fB[\fP\fIsrcaddr\fP\fB:]\fP\fIsrcport\fP\fB:\fP\fIdesthost\fP\fB:\fP\fIdestport\fP" Set up a local port forwarding: listen on \fIsrcport\fP (or \fIsrcaddr\fP:\fIsrcport\fP if specified), and forward any connections over the SSH connection to the destination address \fIdesthost\fP:\fIdestport\fP. Only works in SSH. .IP "\fB\-R\fP \fB[\fP\fIsrcaddr\fP\fB:]\fP\fIsrcport\fP\fB:\fP\fIdesthost\fP\fB:\fP\fIdestport\fP" Set up a remote port forwarding: ask the SSH server to listen on \fIsrcport\fP (or \fIsrcaddr\fP:\fIsrcport\fP if specified), and to forward any connections back over the SSH connection where the client will pass them on to the destination address \fIdesthost\fP:\fIdestport\fP. Only works in SSH. .IP "\fB\-D\fP [\fIsrcaddr\fP:]\fIsrcport\fP" Set up dynamic port forwarding. The client listens on \fIsrcport\fP (or \fIsrcaddr\fP:\fIsrcport\fP if specified), and implements a SOCKS server. So you can point SOCKS-aware applications at this port and they will automatically use the SSH connection to tunnel all their connections. Only works in SSH. .IP "\fB-X\fP" Enable X11 forwarding. .IP "\fB-x\fP" Disable X11 forwarding (default). .IP "\fB-A\fP" Enable agent forwarding. .IP "\fB-a\fP" Disable agent forwarding (default). .IP "\fB-t\fP" Enable pty allocation (default if a command is NOT specified). .IP "\fB-T\fP" Disable pty allocation (default if a command is specified). .IP "\fB-1\fP" Force use of SSH protocol version 1. .IP "\fB-2\fP" Force use of SSH protocol version 2. .IP "\fB-4\fP, \fB-6\fP" Force use of IPv4 or IPv6 for network connections. .IP "\fB-C\fP" Enable SSH compression. .IP "\fB-i\fP \fIkeyfile\fP" Private key file for user authentication. For SSH-2 keys, this key file must be in PuTTY's PPK format, not OpenSSH's format or anyone else's. .RS .PP If you are using an authentication agent, you can also specify a \fIpublic\fP key here (in RFC 4716 or OpenSSH format), to identify which of the agent's keys to use. .RE .IP "\fB\-noagent\fP" Don't try to use an authentication agent for local authentication. (This doesn't affect agent forwarding.) .IP "\fB\-agent\fP" Allow use of an authentication agent. (This option is only necessary to override a setting in a saved session.) .IP "\fB\-no\-trivial\-auth\fP" Disconnect from any SSH server which accepts authentication without ever having asked for any kind of password or signature or token. (You might want to enable this for a server you always expect to challenge you, for instance to ensure you don't accidentally type your key file's passphrase into a compromised server spoofing Plink's passphrase prompt.) .IP "\fB\-noshare\fP" Don't test and try to share an existing connection, always make a new connection. .IP "\fB\-share\fP" Test and try to share an existing connection. .IP "\fB\-hostkey\fP \fIkey\fP" Specify an acceptable host public key. This option may be specified multiple times; each key can be either a fingerprint (\fBSHA256:AbCdE...\fP, \fB99:aa:bb:...\fP, etc) or a base64-encoded blob in OpenSSH\*(Aqs one-line format. .RS .PP Specifying this option overrides automated host key management; \fIonly\fP the key(s) specified on the command-line will be accepted (unless a saved session also overrides host keys, in which case those will be added to), and the host key cache will not be written. .RE .IP "\fB-s\fP" Remote command is SSH subsystem (SSH-2 only). .IP "\fB-N\fP" Don't start a remote command or shell at all (SSH-2 only). .IP "\fB\-nc\fP \fIhost\fP:\fIport\fP" Make a remote network connection from the server instead of starting a shell or command. .IP "\fB\-sercfg\fP \fIconfiguration-string\fP" Specify the configuration parameters for the serial port, in \fB-serial\fP mode. \fIconfiguration-string\fP should be a comma-separated list of configuration parameters as follows: .RS .IP "\fB\(bu\fP" Any single digit from 5 to 9 sets the number of data bits. .IP "\fB\(bu\fP" `\fB1\fP', `\fB1.5\fP' or `\fB2\fP' sets the number of stop bits. .IP "\fB\(bu\fP" Any other numeric string is interpreted as a baud rate. .IP "\fB\(bu\fP" A single lower-case letter specifies the parity: `\fBn\fP' for none, `\fBo\fP' for odd, `\fBe\fP' for even, `\fBm\fP' for mark and `\fBs\fP' for space. .IP "\fB\(bu\fP" A single upper-case letter specifies the flow control: `\fBN\fP' for none, `\fBX\fP' for XON/XOFF, `\fBR\fP' for RTS/CTS and `\fBD\fP' for DSR/DTR. .RE .IP "\fB\-sshlog\fP \fIlogfile\fP" .IP "\fB\-sshrawlog\fP \fIlogfile\fP" For SSH connections, these options make \fBplink\fP log protocol details to a file. (Some of these may be sensitive, although by default an effort is made to suppress obvious passwords.) .RS .PP \fB\-sshlog\fP logs decoded SSH packets and other events (those that \fB\-v\fP would print). \fB\-sshrawlog\fP additionally logs the raw encrypted packet data. .RE .IP "\fB\-logoverwrite\fP" If Plink is configured to write to a log file that already exists, discard the existing file. .IP "\fB\-logappend\fP" If Plink is configured to write to a log file that already exists, append new log data to the existing file. .IP "\fB\-shareexists\fP" Instead of making a new connection, test for the presence of an existing connection that can be shared. The desired session can be specified in any of the usual ways. .RS .PP Returns immediately with a zero exit status if a suitable `upstream' exists, nonzero otherwise. .RE .SH "MORE INFORMATION" .PP For more information on plink, it's probably best to go and look at the manual on the PuTTY web page: .PP \fBhttps://www.chiark.greenend.org.uk/~sgtatham/putty/\fP .SH "BUGS" .PP This man page isn't terribly complete. See the above web link for better documentation. putty-0.76/doc/pscp.10000644000175000017500000001344114072266315011361 00000000000000.ie \n(.g .ds Aq \(aq .el .ds Aq ' .TH "pscp" "1" "2004\(hy03\(hy24" "PuTTY\ tool\ suite" "PuTTY\ tool\ suite" .SH "NAME" .PP \fBpscp\fP \- command-line SCP (secure copy) / SFTP client .SH "SYNOPSIS" .PP .nf \fBpscp\fP\ [\fIoptions\fP]\ [\fIuser\fP\fB@\fP]\fIhost\fP\fB:\fP\fIsource\fP\ \fItarget\fP \fBpscp\fP\ [\fIoptions\fP]\ \fIsource\fP\ [\fIsource\fP...]\ [\fIuser\fP\fB@\fP]\fIhost\fP\fB:\fP\fItarget\fP \fBpscp\fP\ [\fIoptions\fP]\ \fB\-ls\fP\ [\fIuser\fP\fB@\fP]\fIhost\fP\fB:\fP\fIfilespec\fP .fi .SH "DESCRIPTION" .PP \fBpscp\fP is a command-line client for the SSH-based SCP (secure copy) and SFTP (secure file transfer protocol) protocols. .SH "OPTIONS" .PP The command-line options supported by \fIpscp\fP are: .IP "\fB-V\fP" Show version information and exit. .IP "\fB-pgpfp\fP" Display the fingerprints of the PuTTY PGP Master Keys and exit, to aid in verifying new files released by the PuTTY team. .IP "\fB-ls\fP" Remote directory listing. .IP "\fB-p\fP" Preserve file attributes. .IP "\fB-q\fP" Quiet, don't show statistics. .IP "\fB-r\fP" Copy directories recursively. .IP "\fB-unsafe\fP" Allow server-side wildcards (DANGEROUS). .IP "\fB-v\fP" Show verbose messages. .IP "\fB-load\fP \fIsession\fP" Load settings from saved session. .IP "\fB-P\fP \fIport\fP" Connect to port \fIport\fP. .IP "\fB\-proxycmd\fP \fIcommand\fP" Instead of making a TCP connection, use \fIcommand\fP as a proxy; network traffic will be redirected to the standard input and output of \fIcommand\fP. \fIcommand\fP must be a single word, so is likely to need quoting by the shell. .RS .PP The special strings \fB%host\fP and \fB%port\fP in \fIcommand\fP will be replaced by the hostname and port number you want to connect to; to get a literal \fB%\fP sign, enter \fB%%\fP. .PP Backslash escapes are also supported, such as sequences like \fB\en\fP being replaced by a literal newline; to get a literal backslash, enter \fB\e\e\fP. (Further escaping may be required by the shell.) .PP (See the main PuTTY manual for full details of the supported \fB%\fP- and backslash-delimited tokens, although most of them are probably not very useful in this context.) .RE .IP "\fB-l\fP \fIuser\fP" Set remote username to \fIuser\fP. .IP "\fB-batch\fP" Disable interactive prompts. .IP "\fB-no-sanitise-stderr\fP" By default, PSCP will filter control characters from the standard error channel from the server, to prevent remote processes sending confusing escape sequences. This option forces the standard error channel to not be filtered. .IP "\fB-pw\fP \fIpassword\fP" Set remote password to \fIpassword\fP. \fICAUTION:\fP this will likely make the password visible to other users of the local machine (via commands such as `\fBw\fP'). .IP "\fB-1\fP" Force use of SSH protocol version 1. .IP "\fB-2\fP" Force use of SSH protocol version 2. .IP "\fB-ssh-connection\fP" Force use of the `bare \fBssh-connection\fP' protocol. This is only likely to be useful when connecting to a \fIpsusan(1)\fP server, most likely with an absolute path to a Unix-domain socket in place of \fIhost\fP. .IP "\fB-ssh\fP" Force use of the SSH protocol. (This is usually not needed; it's only likely to be useful if you need to override some other configuration of the `bare \fBssh-connection\fP' protocol.) .IP "\fB-4\fP, \fB-6\fP" Force use of IPv4 or IPv6 for network connections. .IP "\fB-C\fP" Enable SSH compression. .IP "\fB-i\fP \fIkeyfile\fP" Private key file for user authentication. For SSH-2 keys, this key file must be in PuTTY's PPK format, not OpenSSH's format or anyone else's. .RS .PP If you are using an authentication agent, you can also specify a \fIpublic\fP key here (in RFC 4716 or OpenSSH format), to identify which of the agent's keys to use. .RE .IP "\fB\-noagent\fP" Don't try to use an authentication agent. .IP "\fB\-agent\fP" Allow use of an authentication agent. (This option is only necessary to override a setting in a saved session.) .IP "\fB\-no\-trivial\-auth\fP" Disconnect from any SSH server which accepts authentication without ever having asked for any kind of password or signature or token. (You might want to enable this for a server you always expect to challenge you, for instance to ensure you don't accidentally type your key file's passphrase into a compromised server spoofing PSCP's passphrase prompt.) .IP "\fB\-hostkey\fP \fIkey\fP" Specify an acceptable host public key. This option may be specified multiple times; each key can be either a fingerprint (\fBSHA256:AbCdE...\fP, \fB99:aa:bb:...\fP, etc) or a base64-encoded blob in OpenSSH\*(Aqs one-line format. .RS .PP Specifying this option overrides automated host key management; \fIonly\fP the key(s) specified on the command-line will be accepted (unless a saved session also overrides host keys, in which case those will be added to), and the host key cache will not be written. .RE .IP "\fB-scp\fP" Force use of SCP protocol. .IP "\fB-sftp\fP" Force use of SFTP protocol. .IP "\fB\-sshlog\fP \fIlogfile\fP" .IP "\fB\-sshrawlog\fP \fIlogfile\fP" These options make \fBpscp\fP log protocol details to a file. (Some of these may be sensitive, although by default an effort is made to suppress obvious passwords.) .RS .PP \fB\-sshlog\fP logs decoded SSH packets and other events (those that \fB\-v\fP would print). \fB\-sshrawlog\fP additionally logs the raw encrypted packet data. .RE .IP "\fB\-logoverwrite\fP" If PSCP is configured to write to a log file that already exists, discard the existing file. .IP "\fB\-logappend\fP" If PSCP is configured to write to a log file that already exists, append new log data to the existing file. .SH "MORE INFORMATION" .PP For more information on \fBpscp\fP it\*(Aqs probably best to go and look at the manual on the PuTTY web page: .PP \fBhttps://www.chiark.greenend.org.uk/~sgtatham/putty/\fP .SH "BUGS" .PP This man page isn't terribly complete. See the above web link for better documentation. putty-0.76/doc/psftp.10000644000175000017500000001300414072266315011543 00000000000000.ie \n(.g .ds Aq \(aq .el .ds Aq ' .TH "psftp" "1" "2004\(hy03\(hy24" "PuTTY\ tool\ suite" "PuTTY\ tool\ suite" .SH "NAME" .PP \fBpsftp\fP \- interactive SFTP (secure file transfer protocol) client .SH "SYNOPSIS" .PP .nf \fBpsftp\fP\ [\fIoptions\fP]\ [\fIuser\fP\fB@\fP]\fIhost\fP .fi .SH "DESCRIPTION" .PP \fBpsftp\fP is an interactive text-based client for the SSH-based SFTP (secure file transfer) protocol. .SH "OPTIONS" .PP The command-line options supported by \fBpsftp\fP are: .IP "\fB-V\fP" Show version information and exit. .IP "\fB-pgpfp\fP" Display the fingerprints of the PuTTY PGP Master Keys and exit, to aid in verifying new files released by the PuTTY team. .IP "\fB-b\fP \fIbatchfile\fP" Use specified batchfile. .IP "\fB-bc\fP" Output batchfile commands. .IP "\fB-be\fP" Don't stop batchfile processing on errors. .IP "\fB-v\fP" Show verbose messages. .IP "\fB-load\fP \fIsession\fP" Load settings from saved session. .IP "\fB-P\fP \fIport\fP" Connect to port \fIport\fP. .IP "\fB\-proxycmd\fP \fIcommand\fP" Instead of making a TCP connection, use \fIcommand\fP as a proxy; network traffic will be redirected to the standard input and output of \fIcommand\fP. \fIcommand\fP must be a single word, so is likely to need quoting by the shell. .RS .PP The special strings \fB%host\fP and \fB%port\fP in \fIcommand\fP will be replaced by the hostname and port number you want to connect to; to get a literal \fB%\fP sign, enter \fB%%\fP. .PP Backslash escapes are also supported, such as sequences like \fB\en\fP being replaced by a literal newline; to get a literal backslash, enter \fB\e\e\fP. (Further escaping may be required by the shell.) .PP (See the main PuTTY manual for full details of the supported \fB%\fP- and backslash-delimited tokens, although most of them are probably not very useful in this context.) .RE .IP "\fB-l\fP \fIuser\fP" Set remote username to \fIuser\fP. .IP "\fB-batch\fP" Disable interactive prompts. .IP "\fB-no-sanitise-stderr\fP" By default, PSFTP will filter control characters from the standard error channel from the server, to prevent remote processes sending confusing escape sequences. This option forces the standard error channel to not be filtered. .IP "\fB-pw\fP \fIpassword\fP" Set remote password to \fIpassword\fP. \fICAUTION:\fP this will likely make the password visible to other users of the local machine (via commands such as `\fBw\fP'). .IP "\fB-1\fP" Force use of SSH protocol version 1. .IP "\fB-2\fP" Force use of SSH protocol version 2. .IP "\fB-ssh-connection\fP" Force use of the `bare \fBssh-connection\fP' protocol. This is only likely to be useful when connecting to a \fIpsusan(1)\fP server, most likely with an absolute path to a Unix-domain socket in place of \fIhost\fP. .IP "\fB-ssh\fP" Force use of the SSH protocol. (This is usually not needed; it's only likely to be useful if you need to override some other configuration of the `bare \fBssh-connection\fP' protocol.) .IP "\fB-4\fP, \fB-6\fP" Force use of IPv4 or IPv6 for network connections. .IP "\fB-C\fP" Enable SSH compression. .IP "\fB-i\fP \fIkeyfile\fP" Private key file for user authentication. For SSH-2 keys, this key file must be in PuTTY's PPK format, not OpenSSH's format or anyone else's. .RS .PP If you are using an authentication agent, you can also specify a \fIpublic\fP key here (in RFC 4716 or OpenSSH format), to identify which of the agent's keys to use. .RE .IP "\fB\-noagent\fP" Don't try to use an authentication agent. .IP "\fB\-agent\fP" Allow use of an authentication agent. (This option is only necessary to override a setting in a saved session.) .IP "\fB\-no\-trivial\-auth\fP" Disconnect from any SSH server which accepts authentication without ever having asked for any kind of password or signature or token. (You might want to enable this for a server you always expect to challenge you, for instance to ensure you don't accidentally type your key file's passphrase into a compromised server spoofing PSFTP's passphrase prompt.) .IP "\fB\-hostkey\fP \fIkey\fP" Specify an acceptable host public key. This option may be specified multiple times; each key can be either a fingerprint (\fBSHA256:AbCdE...\fP, \fB99:aa:bb:...\fP, etc) or a base64-encoded blob in OpenSSH\*(Aqs one-line format. .RS .PP Specifying this option overrides automated host key management; \fIonly\fP the key(s) specified on the command-line will be accepted (unless a saved session also overrides host keys, in which case those will be added to), and the host key cache will not be written. .RE .IP "\fB\-sshlog\fP \fIlogfile\fP" .IP "\fB\-sshrawlog\fP \fIlogfile\fP" These options make \fBpsftp\fP log protocol details to a file. (Some of these may be sensitive, although by default an effort is made to suppress obvious passwords.) .RS .PP \fB\-sshlog\fP logs decoded SSH packets and other events (those that \fB\-v\fP would print). \fB\-sshrawlog\fP additionally logs the raw encrypted packet data. .RE .IP "\fB\-logoverwrite\fP" If PSFTP is configured to write to a log file that already exists, discard the existing file. .IP "\fB\-logappend\fP" If PSFTP is configured to write to a log file that already exists, append new log data to the existing file. .SH "COMMANDS" .PP For a list of commands available inside \fBpsftp\fP, type \fBhelp\fP at the \fBpsftp>\fP prompt. .SH "MORE INFORMATION" .PP For more information on \fBpsftp\fP it\*(Aqs probably best to go and look at the manual on the PuTTY web page: .PP \fBhttps://www.chiark.greenend.org.uk/~sgtatham/putty/\fP .SH "BUGS" .PP This man page isn't terribly complete. See the above web link for better documentation. putty-0.76/doc/puttytel.10000644000175000017500000001546314072266315012314 00000000000000.ie \n(.g .ds Aq \(aq .el .ds Aq ' .TH "puttytel" "1" "2004\(hy03\(hy24" "PuTTY\ tool\ suite" "PuTTY\ tool\ suite" .SH "NAME" .PP \fBputtytel\fP \- GUI Telnet, Rlogin, and SUPDUP client for X .SH "SYNOPSIS" .PP .nf \fBputtytel\fP\ [\ \fIoptions\fP\ ]\ [\ \fIhost\fP\ ] .fi .SH "DESCRIPTION" .PP \fBputtytel\fP is a graphical Telnet, Rlogin, and SUPDUP client for X. It is a direct port of the Windows Telnet, Rlogin, and SUPDUP client of the same name, and a cut-down cryptography-free version of PuTTY. .SH "OPTIONS" .PP The command-line options supported by \fBputtytel\fP are: .IP "\fB\-\-display\fP \fIdisplay\-name\fP" Specify the X display on which to open \fBputtytel\fP. (Note this option has a double minus sign, even though none of the others do. This is because this option is supplied automatically by GTK. Sorry.) .IP "\fB\-fn\fP \fIfont-name\fP" Specify the font to use for normal text displayed in the terminal. For example, \fB\-fn\ fixed\fP, \fB\-fn\ "Monospace\ 12"\fP. .IP "\fB\-fb\fP \fIfont-name\fP" Specify the font to use for bold text displayed in the terminal. If the \fBBoldAsColour\fP 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 \fBBoldAsColour\fP is set to 0 or 2 and you do not specify a bold font, \fBputtytel\fP will overprint the normal font to make it look bolder. .IP "\fB\-fw\fP \fIfont-name\fP" Specify the font to use for double-width characters (typically Chinese, Japanese and Korean text) displayed in the terminal. .IP "\fB\-fwb\fP \fIfont-name\fP" Specify the font to use for bold double-width characters (typically Chinese, Japanese and Korean text). Like \fB-fb\fP, this will be ignored unless the \fBBoldAsColour\fP resource is set to 0 or 2. .IP "\fB\-geometry\fP \fIgeometry\fP" Specify the size of the terminal, in rows and columns of text. See \fIX(7)\fP for more information on the syntax of geometry specifications. .IP "\fB\-sl\fP \fIlines\fP" Specify the number of lines of scrollback to save off the top of the terminal. .IP "\fB\-fg\fP \fIcolour\fP" Specify the foreground colour to use for normal text. .IP "\fB\-bg\fP \fIcolour\fP" Specify the background colour to use for normal text. .IP "\fB\-bfg\fP \fIcolour\fP" Specify the foreground colour to use for bold text, if the \fBBoldAsColour\fP resource is set to 1 (the default) or 2. .IP "\fB\-bbg\fP \fIcolour\fP" Specify the foreground colour to use for bold reverse-video text, if the \fBBoldAsColour\fP 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 \fIin\fP the background colour.) .IP "\fB\-cfg\fP \fIcolour\fP" Specify the foreground colour to use for text covered by the cursor. .IP "\fB\-cbg\fP \fIcolour\fP" Specify the background colour to use for text covered by the cursor. In other words, this is the main colour of the cursor. .IP "\fB\-title\fP \fItitle\fP" Specify the initial title of the terminal window. (This can be changed under control of the server.) .IP "\fB\-sb\-\fP or \fB+sb\fP" Tells \fBputtytel\fP not to display a scroll bar. .IP "\fB\-sb\fP" Tells \fBputtytel\fP to display a scroll bar: this is the opposite of \fB\-sb\-\fP. This is the default option: you will probably only need to specify it explicitly if you have changed the default using the \fBScrollBar\fP resource. .IP "\fB\-log\fP \fIlogfile\fP, \fB\-sessionlog\fP \fIlogfile\fP" This option makes \fBputtytel\fP log all the terminal output to a file as well as displaying it in the terminal. .IP "\fB\-cs\fP \fIcharset\fP" This option specifies the character set in which \fBputtytel\fP should assume the session is operating. This character set will be used to interpret all the data received from the session, and all input you type or paste into \fBputtytel\fP will be converted into this character set before being sent to the session. .RS .PP Any character set name which is valid in a MIME header (and supported by \fBputtytel\fP) should be valid here (examples are `\fBISO-8859-1\fP', `\fBwindows-1252\fP' or `\fBUTF-8\fP'). Also, any character encoding which is valid in an X logical font description should be valid (`\fBibm-cp437\fP', for example). .PP \fBputtytel\fP\*(Aqs default behaviour is to use the same character encoding as its primary font. If you supply a Unicode (\fBiso10646-1\fP) font, it will default to the UTF-8 character set. .PP Character set names are case-insensitive. .RE .IP "\fB\-nethack\fP" Tells \fBputtytel\fP to enable NetHack keypad mode, in which the numeric keypad generates the NetHack \fBhjklyubn\fP direction keys. This enables you to play NetHack with the numeric keypad without having to use the NetHack \fBnumber_pad\fP option (which requires you to press `\fBn\fP' before any repeat count). So you can move with the numeric keypad, and enter repeat counts with the normal number keys. .IP "\fB\-help\fP, \fB\-\-help\fP" Display a message summarizing the available options. .IP "\fB\-pgpfp\fP" Display the fingerprints of the PuTTY PGP Master Keys, to aid in verifying new files released by the PuTTY team. .IP "\fB\-load\fP \fIsession\fP" Load a saved session by name. This allows you to run a saved session straight from the command line without having to go through the configuration box first. .IP "\fB\-telnet\fP, \fB\-rlogin\fP, \fB\-supdup\fP, \fB\-raw\fP" Select the protocol \fBputtytel\fP will use to make the connection. .IP "\fB\-proxycmd\fP \fIcommand\fP" Instead of making a TCP connection, use \fIcommand\fP as a proxy; network traffic will be redirected to the standard input and output of \fIcommand\fP. \fIcommand\fP must be a single word, so is likely to need quoting by the shell. .RS .PP The special strings \fB%host\fP and \fB%port\fP in \fIcommand\fP will be replaced by the hostname and port number you want to connect to; to get a literal \fB%\fP sign, enter \fB%%\fP. .PP Backslash escapes are also supported, such as sequences like \fB\en\fP being replaced by a literal newline; to get a literal backslash, enter \fB\e\e\fP. (Further escaping may be required by the shell.) .PP (See the main PuTTY manual for full details of the supported \fB%\fP- and backslash-delimited tokens, although most of them are probably not very useful in this context.) .RE .IP "\fB\-l\fP \fIusername\fP" Specify the username to use when logging in to the server. .IP "\fB\-P\fP \fIport\fP" Specify the port to connect to the server on. .IP "\fB-4\fP, \fB-6\fP" Force use of IPv4 or IPv6 for network connections. .SH "SAVED SESSIONS" .PP Saved sessions are stored in a \fB.putty/sessions\fP subdirectory in your home directory. .SH "MORE INFORMATION" .PP For more information on PuTTY and PuTTYtel, it's probably best to go and look at the manual on the web page: .PP \fBhttps://www.chiark.greenend.org.uk/~sgtatham/putty/\fP .SH "BUGS" .PP This man page isn't terribly complete. putty-0.76/doc/pterm.10000644000175000017500000005753014072266315011552 00000000000000.ie \n(.g .ds Aq \(aq .el .ds Aq ' .TH "pterm" "1" "2004\(hy03\(hy24" "PuTTY\ tool\ suite" "PuTTY\ tool\ suite" .SH "NAME" .PP pterm \(hy yet another X terminal emulator .SH "SYNOPSIS" .PP .nf \fBpterm\fP\ [\ \fIoptions\fP\ ] .fi .SH "DESCRIPTION" .PP \fBpterm\fP is a terminal emulator for X. It is based on a port of the terminal emulation engine in the Windows SSH client PuTTY. .SH "OPTIONS" .PP The command-line options supported by \fBpterm\fP are: .IP "\fB\-e\fP \fIcommand\fP [ \fIarguments\fP ]" Specify a command to be executed in the new terminal. Everything on the command line after this option will be passed straight to the \fBexecvp\fP system call; so if you need the command to redirect its input or output, you will have to use \fBsh\fP: .RS .PP .nf pterm\ \-e\ sh\ \-c\ \*(Aqmycommand\ <\ inputfile\*(Aq .fi .RE .IP "\fB\-\-display\fP \fIdisplay\-name\fP" Specify the X display on which to open \fBpterm\fP. (Note this option has a double minus sign, even though none of the others do. This is because this option is supplied automatically by GTK. Sorry.) .IP "\fB\-name\fP \fIname\fP" Specify the name under which \fBpterm\fP looks up X resources. Normally it will look them up as (for example) \fBpterm.Font\fP. If you specify `\fB\-name xyz\fP', it will look them up as \fBxyz.Font\fP instead. This allows you to set up several different sets of defaults and choose between them. .IP "\fB\-fn\fP \fIfont-name\fP" Specify the font to use for normal text displayed in the terminal. For example, \fB\-fn\ fixed\fP, \fB\-fn\ "Monospace\ 12"\fP. .IP "\fB\-fb\fP \fIfont-name\fP" Specify the font to use for bold text displayed in the terminal. If the \fBBoldAsColour\fP 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 \fBBoldAsColour\fP is set to 0 or 2 and you do not specify a bold font, \fBpterm\fP will overprint the normal font to make it look bolder. .IP "\fB\-fw\fP \fIfont-name\fP" Specify the font to use for double-width characters (typically Chinese, Japanese and Korean text) displayed in the terminal. .IP "\fB\-fwb\fP \fIfont-name\fP" Specify the font to use for bold double-width characters (typically Chinese, Japanese and Korean text). Like \fB-fb\fP, this will be ignored unless the \fBBoldAsColour\fP resource is set to 0 or 2. .IP "\fB\-geometry\fP \fIgeometry\fP" Specify the size of the terminal, in rows and columns of text. See \fIX(7)\fP for more information on the syntax of geometry specifications. .IP "\fB\-sl\fP \fIlines\fP" Specify the number of lines of scrollback to save off the top of the terminal. .IP "\fB\-fg\fP \fIcolour\fP" Specify the foreground colour to use for normal text. .IP "\fB\-bg\fP \fIcolour\fP" Specify the background colour to use for normal text. .IP "\fB\-bfg\fP \fIcolour\fP" Specify the foreground colour to use for bold text, if the \fBBoldAsColour\fP resource is set to 1 (the default) or 2. .IP "\fB\-bbg\fP \fIcolour\fP" Specify the foreground colour to use for bold reverse-video text, if the \fBBoldAsColour\fP 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 \fIin\fP the background colour.) .IP "\fB\-cfg\fP \fIcolour\fP" Specify the foreground colour to use for text covered by the cursor. .IP "\fB\-cbg\fP \fIcolour\fP" Specify the background colour to use for text covered by the cursor. In other words, this is the main colour of the cursor. .IP "\fB\-title\fP \fItitle\fP" Specify the initial title of the terminal window. (This can be changed under control of the server.) .IP "\fB\-ut\-\fP or \fB+ut\fP" Tells \fBpterm\fP not to record your login in the \fButmp\fP, \fBwtmp\fP and \fBlastlog\fP system log files; so you will not show up on \fBfinger\fP or \fBwho\fP listings, for example. .IP "\fB\-ut\fP" Tells \fBpterm\fP to record your login in \fButmp\fP, \fBwtmp\fP and \fBlastlog\fP: this is the opposite of \fB\-ut\-\fP. This is the default option: you will probably only need to specify it explicitly if you have changed the default using the \fBStampUtmp\fP resource. .IP "\fB\-ls\-\fP or \fB+ls\fP" Tells \fBpterm\fP not to execute your shell as a login shell. .IP "\fB\-ls\fP" Tells \fBpterm\fP to execute your shell as a login shell: this is the opposite of \fB\-ls\-\fP. This is the default option: you will probably only need to specify it explicitly if you have changed the default using the \fBLoginShell\fP resource. .IP "\fB\-sb\-\fP or \fB+sb\fP" Tells \fBpterm\fP not to display a scroll bar. .IP "\fB\-sb\fP" Tells \fBpterm\fP to display a scroll bar: this is the opposite of \fB\-sb\-\fP. This is the default option: you will probably only need to specify it explicitly if you have changed the default using the \fBScrollBar\fP resource. .IP "\fB\-log\fP \fIlogfile\fP, \fB\-sessionlog\fP \fIlogfile\fP" This option makes \fBpterm\fP log all the terminal output to a file as well as displaying it in the terminal. .IP "\fB\-cs\fP \fIcharset\fP" This option specifies the character set in which \fBpterm\fP should assume the session is operating. This character set will be used to interpret all the data received from the session, and all input you type or paste into \fBpterm\fP will be converted into this character set before being sent to the session. .RS .PP Any character set name which is valid in a MIME header (and supported by \fBpterm\fP) should be valid here (examples are `\fBISO-8859-1\fP', `\fBwindows-1252\fP' or `\fBUTF-8\fP'). Also, any character encoding which is valid in an X logical font description should be valid (`\fBibm-cp437\fP', for example). .PP \fBpterm\fP\*(Aqs default behaviour is to use the same character encoding as its primary font. If you supply a Unicode (\fBiso10646-1\fP) font, it will default to the UTF-8 character set. .PP Character set names are case-insensitive. .RE .IP "\fB\-nethack\fP" Tells \fBpterm\fP to enable NetHack keypad mode, in which the numeric keypad generates the NetHack \fBhjklyubn\fP direction keys. This enables you to play NetHack with the numeric keypad without having to use the NetHack \fBnumber_pad\fP option (which requires you to press `\fBn\fP' before any repeat count). So you can move with the numeric keypad, and enter repeat counts with the normal number keys. .IP "\fB\-xrm\fP \fIresource-string\fP" This option specifies an X resource string. Useful for setting resources which do not have their own command-line options. For example: .RS .PP .nf pterm\ \-xrm\ \*(AqScrollbarOnLeft:\ 1\*(Aq .fi .RE .IP "\fB\-help\fP, \fB\-\-help\fP" Display a message summarizing the available options. .IP "\fB\-pgpfp\fP" Display the fingerprints of the PuTTY PGP Master Keys, to aid in verifying new files released by the PuTTY team. .SH "X RESOURCES" .PP \fBpterm\fP can be more completely configured by means of X resources. All of these resources are of the form \fBpterm.FOO\fP for some \fBFOO\fP; you can make \fBpterm\fP look them up under another name, such as \fBxyz.FOO\fP, by specifying the command-line option `\fB\-name xyz\fP'. .IP "\fBpterm.CloseOnExit\fP" This option should be set to 0, 1 or 2; the default is 2. It controls what \fBpterm\fP does when the process running inside it terminates. When set to 2 (the default), \fBpterm\fP will close its window as soon as the process inside it terminates. When set to 0, \fBpterm\fP will print the process\*(Aqs exit status, and the window will remain present until a key is pressed (allowing you to inspect the scrollback, and copy and paste text out of it). .RS .PP When this setting is set to 1, \fBpterm\fP will close immediately if the process exits cleanly (with an exit status of zero), but the window will stay around if the process exits with a non-zero code or on a signal. This enables you to see what went wrong if the process suffers an error, but not to have to bother closing the window in normal circumstances. .RE .IP "\fBpterm.WarnOnClose\fP" This option should be set to either 0 or 1; the default is 1. When set to 1, \fBpterm\fP will ask for confirmation before closing its window when you press the close button. .IP "\fBpterm.TerminalType\fP" This controls the value set in the \fBTERM\fP environment variable inside the new terminal. The default is `\fBxterm\fP'. .IP "\fBpterm.BackspaceIsDelete\fP" This option should be set to either 0 or 1; the default is 1. When set to 0, the ordinary Backspace key generates the Backspace character (\fB^H\fP); when set to 1, it generates the Delete character (\fB^?\fP). Whichever one you set, the terminal device inside \fBpterm\fP will be set up to expect it. .IP "\fBpterm.RXVTHomeEnd\fP" This option should be set to either 0 or 1; the default is 0. When it is set to 1, the Home and End keys generate the control sequences they would generate in the \fBrxvt\fP terminal emulator, instead of the more usual ones generated by other emulators. .IP "\fBpterm.LinuxFunctionKeys\fP" This option can be set to any number between 0 and 5 inclusive; the default is 0. The modes vary the control sequences sent by the function keys; for more complete documentation, it is probably simplest to try each option in `\fBpterm \-e cat\fP', and press the keys to see what they generate. .IP "\fBpterm.NoApplicationKeys\fP" This option should be set to either 0 or 1; the default is 0. When set to 1, it stops the server from ever switching the numeric keypad into application mode (where the keys send function-key-like sequences instead of numbers or arrow keys). You probably only need this if some application is making a nuisance of itself. .IP "\fBpterm.NoApplicationCursors\fP" This option should be set to either 0 or 1; the default is 0. When set to 1, it stops the server from ever switching the cursor keys into application mode (where the keys send slightly different sequences). You probably only need this if some application is making a nuisance of itself. .IP "\fBpterm.NoMouseReporting\fP" This option should be set to either 0 or 1; the default is 0. When set to 1, it stops the server from ever enabling mouse reporting mode (where mouse clicks are sent to the application instead of controlling cut and paste). .IP "\fBpterm.NoRemoteResize\fP" This option should be set to either 0 or 1; the default is 0. When set to 1, it stops the server from being able to remotely control the size of the \fBpterm\fP window. .IP "\fBpterm.NoAltScreen\fP" This option should be set to either 0 or 1; the default is 0. When set to 1, it stops the server from using the `alternate screen' terminal feature, which lets full-screen applications leave the screen exactly the way they found it. .IP "\fBpterm.NoRemoteWinTitle\fP" This option should be set to either 0 or 1; the default is 0. When set to 1, it stops the server from remotely controlling the title of the \fBpterm\fP window. .IP "\fBpterm.NoRemoteQTitle\fP" This option should be set to either 0 or 1; the default is 1. When set to 1, it stops the server from remotely requesting the title of the \fBpterm\fP window. .RS .PP This feature is a \fIPOTENTIAL SECURITY HAZARD\fP. If a malicious application can write data to your terminal (for example, if you merely \fBcat\fP a file owned by someone else on the server machine), it can change your window title (unless you have disabled this using the \fBNoRemoteWinTitle\fP resource) and then use this service to have the new window title sent back to the server as if typed at the keyboard. This allows an attacker to fake keypresses and potentially cause your server-side applications to do things you didn\*(Aqt want. Therefore this feature is disabled by default, and we recommend you do not turn it on unless you \fIreally\fP know what you are doing. .RE .IP "\fBpterm.NoDBackspace\fP" This option should be set to either 0 or 1; the default is 0. When set to 1, it disables the normal action of the Delete (\fB^?\fP) character when sent from the server to the terminal, which is to move the cursor left by one space and erase the character now under it. .IP "\fBpterm.ApplicationCursorKeys\fP" This option should be set to either 0 or 1; the default is 0. When set to 1, the default initial state of the cursor keys are application mode (where the keys send function-key-like sequences instead of numbers or arrow keys). When set to 0, the default state is the normal one. .IP "\fBpterm.ApplicationKeypad\fP" This option should be set to either 0 or 1; the default is 0. When set to 1, the default initial state of the numeric keypad is application mode (where the keys send function-key-like sequences instead of numbers or arrow keys). When set to 0, the default state is the normal one. .IP "\fBpterm.NetHackKeypad\fP" This option should be set to either 0 or 1; the default is 0. When set to 1, the numeric keypad operates in NetHack mode. This is equivalent to the \fB\-nethack\fP command-line option. .IP "\fBpterm.Answerback\fP" This option controls the string which the terminal sends in response to receiving the \fB^E\fP character (`tell me about yourself'). By default this string is `\fBPuTTY\fP'. .IP "\fBpterm.HideMousePtr\fP" This option should be set to either 0 or 1; the default is 0. When it is set to 1, the mouse pointer will disappear if it is over the \fBpterm\fP window and you press a key. It will reappear as soon as you move it. .IP "\fBpterm.WindowBorder\fP" This option controls the number of pixels of space between the text in the \fBpterm\fP window and the window frame. The default is 1. You can increase this value, but decreasing it to 0 is not recommended because it can cause the window manager\*(Aqs size hints to work incorrectly. .IP "\fBpterm.CurType\fP" This option should be set to either 0, 1 or 2; the default is 0. When set to 0, the text cursor displayed in the window is a rectangular block. When set to 1, the cursor is an underline; when set to 2, it is a vertical line. .IP "\fBpterm.BlinkCur\fP" This option should be set to either 0 or 1; the default is 0. When it is set to 1, the text cursor will blink when the window is active. .IP "\fBpterm.Beep\fP" This option should be set to either 0 or 2 (yes, 2); the default is 0. When it is set to 2, \fBpterm\fP will respond to a bell character (\fB^G\fP) by flashing the window instead of beeping. .IP "\fBpterm.BellOverload\fP" This option should be set to either 0 or 1; the default is 0. When it is set to 1, \fBpterm\fP will watch out for large numbers of bells arriving in a short time and will temporarily disable the bell until they stop. The idea is that if you \fBcat\fP a binary file, the frantic beeping will mostly be silenced by this feature and will not drive you crazy. .RS .PP The bell overload mode is activated by receiving N bells in time T; after a further time S without any bells, overload mode will turn itself off again. .PP Bell overload mode is always deactivated by any keypress in the terminal. This means it can respond to large unexpected streams of data, but does not interfere with ordinary command-line activities that generate beeps (such as filename completion). .RE .IP "\fBpterm.BellOverloadN\fP" This option counts the number of bell characters which will activate bell overload if they are received within a length of time T. The default is 5. .IP "\fBpterm.BellOverloadT\fP" This option specifies the time period in which receiving N or more bells will activate bell overload mode. It is measured in microseconds, so (for example) set it to 1000000 for one second. The default is 2000000 (two seconds). .IP "\fBpterm.BellOverloadS\fP" This option specifies the time period of silence required to turn off bell overload mode. It is measured in microseconds, so (for example) set it to 1000000 for one second. The default is 5000000 (five seconds of silence). .IP "\fBpterm.ScrollbackLines\fP" This option specifies how many lines of scrollback to save above the visible terminal screen. The default is 200. This resource is equivalent to the \fB\-sl\fP command-line option. .IP "\fBpterm.DECOriginMode\fP" This option should be set to either 0 or 1; the default is 0. It specifies the default state of DEC Origin Mode. (If you don't know what that means, you probably don't need to mess with it.) .IP "\fBpterm.AutoWrapMode\fP" This option should be set to either 0 or 1; the default is 1. It specifies the default state of auto wrap mode. When set to 1, very long lines will wrap over to the next line on the terminal; when set to 0, long lines will be squashed against the right-hand edge of the screen. .IP "\fBpterm.LFImpliesCR\fP" This option should be set to either 0 or 1; the default is 0. When set to 1, the terminal will return the cursor to the left side of the screen when it receives a line feed character. .IP "\fBpterm.WinTitle\fP" This resource is the same as the \fB\-T\fP command-line option: it controls the initial title of the window. The default is `\fBpterm\fP'. .IP "\fBpterm.TermWidth\fP" This resource is the same as the width part of the \fB\-geometry\fP command-line option: it controls the number of columns of text in the window. The default is 80. .IP "\fBpterm.TermHeight\fP" This resource is the same as the width part of the \fB\-geometry\fP command-line option: it controls the number of columns of text in the window. The defaults is 24. .IP "\fBpterm.Font\fP" This resource is the same as the \fB\-fn\fP command-line option: it controls the font used to display normal text. The default is `\fBfixed\fP'. .IP "\fBpterm.BoldFont\fP" This resource is the same as the \fB\-fb\fP command-line option: it controls the font used to display bold text when \fBBoldAsColour\fP is set to 0 or 2. The default is unset (the font will be bolded by printing it twice at a one-pixel offset). .IP "\fBpterm.WideFont\fP" This resource is the same as the \fB\-fw\fP command-line option: it controls the font used to display double-width characters. The default is unset (double-width characters cannot be displayed). .IP "\fBpterm.WideBoldFont\fP" This resource is the same as the \fB\-fwb\fP command-line option: it controls the font used to display double-width characters in bold, when \fBBoldAsColour\fP 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). .IP "\fBpterm.ShadowBoldOffset\fP" This resource can be set to an integer; the default is \(hy1. It specifies the offset at which text is overprinted when using `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 \(hy1, 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). .IP "\fBpterm.BoldAsColour\fP" 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 \fIand\fP a brighter colour). .IP "\fBpterm.Colour0\fP, \fBpterm.Colour1\fP, ..., \fBpterm.Colour21\fP" These options control the various colours used to display text in the \fBpterm\fP window. Each one should be specified as a triple of decimal numbers giving red, green and blue values: so that black is `\fB0,0,0\fP', white is `\fB255,255,255\fP', red is `\fB255,0,0\fP' and so on. .RS .PP Colours 0 and 1 specify the foreground colour and its bold equivalent (the \fB\-fg\fP and \fB\-bfg\fP command-line options). Colours 2 and 3 specify the background colour and its bold equivalent (the \fB\-bg\fP and \fB\-bbg\fP command-line options). Colours 4 and 5 specify the text and block colours used for the cursor (the \fB\-cfg\fP and \fB\-cbg\fP command-line options). Each even number from 6 to 20 inclusive specifies the colour to be used for one of the ANSI primary colour specifications (black, red, green, yellow, blue, magenta, cyan, white, in that order); the odd numbers from 7 to 21 inclusive specify the bold version of each colour, in the same order. The defaults are: .PP .nf pterm.Colour0:\ 187,187,187 pterm.Colour1:\ 255,255,255 pterm.Colour2:\ 0,0,0 pterm.Colour3:\ 85,85,85 pterm.Colour4:\ 0,0,0 pterm.Colour5:\ 0,255,0 pterm.Colour6:\ 0,0,0 pterm.Colour7:\ 85,85,85 pterm.Colour8:\ 187,0,0 pterm.Colour9:\ 255,85,85 pterm.Colour10:\ 0,187,0 pterm.Colour11:\ 85,255,85 pterm.Colour12:\ 187,187,0 pterm.Colour13:\ 255,255,85 pterm.Colour14:\ 0,0,187 pterm.Colour15:\ 85,85,255 pterm.Colour16:\ 187,0,187 pterm.Colour17:\ 255,85,255 pterm.Colour18:\ 0,187,187 pterm.Colour19:\ 85,255,255 pterm.Colour20:\ 187,187,187 pterm.Colour21:\ 255,255,255 .fi .RE .IP "\fBpterm.RectSelect\fP" This option should be set to either 0 or 1; the default is 0. When set to 0, dragging the mouse over several lines selects to the end of each line and from the beginning of the next; when set to 1, dragging the mouse over several lines selects a rectangular region. In each case, holding down Alt while dragging gives the other behaviour. .IP "\fBpterm.MouseOverride\fP" This option should be set to either 0 or 1; the default is 1. When set to 1, if the application requests mouse tracking (so that mouse clicks are sent to it instead of doing selection), holding down Shift will revert the mouse to normal selection. When set to 0, mouse tracking completely disables selection. .IP "\fBpterm.Printer\fP" This option is unset by default. If you set it, then server-controlled printing is enabled: the server can send control sequences to request data to be sent to a printer. That data will be piped into the command you specify here; so you might want to set it to `\fBlpr\fP', for example, or `\fBlpr \-Pmyprinter\fP'. .IP "\fBpterm.ScrollBar\fP" This option should be set to either 0 or 1; the default is 1. When set to 0, the scrollbar is hidden (although Shift-PageUp and Shift-PageDown still work). This is the same as the \fB\-sb\fP command-line option. .IP "\fBpterm.ScrollbarOnLeft\fP" This option should be set to either 0 or 1; the default is 0. When set to 1, the scrollbar will be displayed on the left of the terminal instead of on the right. .IP "\fBpterm.ScrollOnKey\fP" This option should be set to either 0 or 1; the default is 0. When set to 1, any keypress causes the position of the scrollback to be reset to the very bottom. .IP "\fBpterm.ScrollOnDisp\fP" This option should be set to either 0 or 1; the default is 1. When set to 1, any activity in the display causes the position of the scrollback to be reset to the very bottom. .IP "\fBpterm.LineCodePage\fP" This option specifies the character set to be used for the session. This is the same as the \fB\-cs\fP command-line option. .IP "\fBpterm.NoRemoteCharset\fP" This option disables the terminal's ability to change its character set when it receives escape sequences telling it to. You might need to do this to interoperate with programs which incorrectly change the character set to something they think is sensible. .IP "\fBpterm.BCE\fP" This option should be set to either 0 or 1; the default is 1. When set to 1, the various control sequences that erase parts of the terminal display will erase in whatever the current background colour is; when set to 0, they will erase in black always. .IP "\fBpterm.BlinkText\fP" This option should be set to either 0 or 1; the default is 0. When set to 1, text specified as blinking by the server will actually blink on and off; when set to 0, \fBpterm\fP will use the less distracting approach of making the text\*(Aqs background colour bold. .IP "\fBpterm.StampUtmp\fP" This option should be set to either 0 or 1; the default is 1. When set to 1, \fBpterm\fP will log the login in the various system log files. This resource is equivalent to the \fB\-ut\fP command-line option. .IP "\fBpterm.LoginShell\fP" This option should be set to either 0 or 1; the default is 1. When set to 1, \fBpterm\fP will execute your shell as a login shell. This resource is equivalent to the \fB\-ls\fP command-line option. .SH "BUGS" .PP Most of the X resources have silly names. (Historical reasons from PuTTY, mostly.) putty-0.76/doc/pageant.10000644000175000017500000003473614072266315012045 00000000000000.ie \n(.g .ds Aq \(aq .el .ds Aq ' .TH "pageant" "1" "2015\(hy05\(hy19" "PuTTY\ tool\ suite" "PuTTY\ tool\ suite" .SH "NAME" .PP \fBpageant\fP - PuTTY SSH authentication agent .SH "SYNOPSIS" .PP .nf \fBpageant\fP\ (\ \fB\-X\fP\ |\ \fB\-T\fP\ |\ \fB\-\-permanent\fP\ |\ \fB\-\-debug\fP\ )\ [\ [\ \fB\-\-encrypted\fP\ ]\ \fIkey\-file\fP...\ ] \fBpageant\fP\ [\ [\ \-\-\fBencrypted\fP\ ]\ \fIkey\-file\fP...\ ]\ \fB\-\-exec\fP\ \fIcommand\fP\ [\ \fIargs\fP...\ ] \fBpageant\fP\ \fB\-a\fP\ [\ \fB\-\-encrypted\fP\ ]\ \fIkey\-file\fP... \fBpageant\fP\ (\ \fB\-d\fP\ |\ \fB\-r\fP\ |\ \fB\-\-public\fP\ |\ \fB\-\-public\-openssh\fP\ )\ \fIkey\-identifier\fP... \fBpageant\fP\ (\ \fB\-D\fP\ |\ \fB\-R\fP\ ) \fBpageant\fP\ \fB\-l\fP\ [\ \fB\-\-fptype\fP\ \fIformat\fP\ ] \fBpageant\fP\ \fB\-\-askpass\fP\ \fIprompt\fP .fi .SH "DESCRIPTION" .PP \fBpageant\fP is both an SSH authentication agent, and also a tool for communicating with an already-running agent. .PP When running as an SSH agent, it listens on a Unix-domain socket for connections from client processes running under your user id. Clients can load SSH private keys into the agent, or request signatures on a given message from a key already in the agent. This permits one-touch authentication by SSH client programs, if Pageant is holding a key that the server they are connecting to will accept. .PP \fBpageant\fP can also act as a client program itself, communicating with an already-running agent to add or remove keys, list the keys, or extract their public half. .PP The agent protocol used by \fBpageant\fP is compatible with the PuTTY tools and also with other implementations such as OpenSSH\*(Aqs SSH client and \fIssh-agent(1)\fP. Some \fBpageant\fP features are implemented with protocol extensions, so will only work if \fBpageant\fP is on both ends. .PP To run \fBpageant\fP as an agent, you must provide an option to tell it what its \fIlifetime\fP should be. Typically you would probably want Pageant to last for the duration of a login session, in which case you should use either \fB-X\fP or \fB-T\fP, depending on whether your login session is GUI or purely terminal-based respectively. For example, in your X session startup script you might write .PP .nf \fBeval\ $(pageant\ \-X)\fP .fi .PP which will cause Pageant to start running, monitor the X server to notice when your session terminates (and then it will terminate too), and print on standard output some shell commands to set environment variables that client processes will need to find the running agent. .PP In a terminal-based login, you could do almost exactly the same thing but with \fB-T\fP: .PP .nf \fBeval\ $(pageant\ \-T)\fP .fi .PP This will cause Pageant to tie its lifetime to that of your controlling terminal: when you log out, and the terminal device ceases to be associated with your session, Pageant will notice that it has no controlling terminal any more, and will terminate automatically. .PP In either of these modes, you can also add one or more private keys as extra command-line arguments, e.g. .PP .nf \fBeval\ $(pageant\ \-T\ ~/.ssh/key.ppk)\fP .fi .PP in which case Pageant will immediately prompt for the keys' passphrases (if any) and start the agent with those keys already loaded in cleartext form. Passphrase prompts will use the controlling terminal if one is available, or failing that the GUI if one of those is available. (The prompt method can be overridden with the \fB--gui-prompt\fP or \fB--tty-prompt\fP options.) If neither is available, no passphrase prompting can be done. .PP Alternatively, you can start an agent with keys stored in encrypted form: .PP .nf \fBeval\ $(pageant\ \-T\ \-\-encrypted\ ~/.ssh/key.ppk)\fP .fi .PP In this case, Pageant will not prompt for a passphrase at startup; instead, it will prompt the first time a client tries to use the key. (Pageant will need access to a GUI so that it can pop up a passphrase prompt when required, unless it's running in \fB--debug\fP mode.) .PP To use Pageant to talk to an existing agent, you can add new keys using \fB-a\fP, list the current set of keys\*(Aq fingerprints and comments with \fB-l\fP, extract the full public half of any key using \fB--public\fP or \fB--public-openssh\fP, delete a specific key or all keys using \fB-d\fP or \fB-D\fP respectively, or request re-encryption of a specific key or all keys using \fB-r\fP or \fB-R\fP respectively. .SH "LIFETIME" .PP The following options are called \fIlifetime modes\fP. They all request Pageant to operate in agent mode; each one specifies a different method for Pageant to start up and know when to shut down. .IP "\fB-X\fP" Pageant will open a connection to your X display, and when that connection is lost, it will terminate. This gives it the same lifetime as your GUI login session, so in this mode it is suitable for running from a startup script such as \fB.xsession\fP. The actual agent will be a subprocess; the main Pageant process will terminate immediately, after printing environment-variable setting commands on standard output which should be installed in any process wanting to communicate with the agent. .RS .PP The usual approach would be to run .PP .nf \fBeval\ $(pageant\ \-X)\fP .fi .PP in an X session startup script. However, other possibilities exist, such as directing the standard output of `\fBpageant -X\fP' to a file which is then sourced by any new shell. .RE .IP "\fB-T\fP" Pageant will tie its lifetime to that of the login session running on its controlling terminal, by noticing when it ceases to have a controlling terminal (which will automatically happen as a side effect of the session leader process terminating). Like \fB-X\fP, Pageant will print environment-variable commands on standard output. .IP "\fB--exec\fP \fIcommand\fP" Pageant will run the provided command as a subprocess, preloaded with the appropriate environment variables to access the agent it starts up. When the subprocess terminates, Pageant will terminate as well. .RS .PP All arguments on Pageant's command line after \fB--exec\fP will be treated as part of the command to run, even if they look like other valid Pageant options or key files. .RE .IP "\fB--permanent\fP" Pageant will fork off a subprocess to be the agent, and print environment-variable commands on standard output, like \fB-X\fP and \fB-T\fP. However, in this case, it will make no effort to limit its lifetime in any way; it will simply run permanently, unless manually killed. The environment variable \fBSSH_AGENT_PID\fP, set by the commands printed by Pageant, permits the agent process to be found for this purpose. .RS .PP This option is not recommended, because any method of manually killing the agent carries the risk of the session terminating unexpectedly before it manages to happen. .RE .IP "\fB--debug\fP" Pageant will run in the foreground, without forking. It will print its environment variable setup commands on standard output, and then it will log all agent activity to standard output as well; any passphrase prompts will need to be answered on standard input. This is useful for debugging what Pageant itself is doing, or what another process is doing to it. .SH "CLIENT OPTIONS" .PP The following options tell Pageant to operate in client mode, contacting an existing agent via environment variables that it should already have set. .IP "\fB-a\fP \fIkey-files\fP" Load the specified private key file(s) and add them to the already-running agent. Unless \fB--encrypted\fP is also specified, \fBpageant\fP will decrypt them if necessary by prompting for their passphrases (with the same choice of user interfaces as in agent mode). .RS .PP The private key files must be in PuTTY's \fB.ppk\fP file format. .RE .IP "\fB-l\fP" List the keys currently in the running agent. Each key's fingerprint and comment string will be shown. (Use the \fB-E\fP option to change the fingerprint format.) .RS .PP Keys that will require a passphrase on their next use are listed as `encrypted'. Keys that can be returned to this state with \fB-r\fP are listed as `re-encryptable'. .RE .IP "\fB--public\fP \fIkey-identifiers\fP" Print the public half of each specified key, in the RFC 4716 standard format (multiple lines, starting with `\fB---- BEGIN SSH2 PUBLIC KEY ----\fP'). .RS .PP Each \fIkey-identifier\fP can be any of the following: .IP "\fB\(bu\fP" The name of a file containing the key, either the whole key (again in \fB.ppk\fP format) or just its public half. .IP "\fB\(bu\fP" The key's comment string, as shown by \fBpageant -l\fP. .IP "\fB\(bu\fP" Enough of one of the key's fingerprint formats to be unique among keys currently loaded into the agent. .PP If Pageant can uniquely identify one key by interpreting the \fIkey-identifier\fP in any of these ways, it will assume that key was the one you meant. If it cannot, you will have to specify more detail. .PP If you find that your desired \fIkey-identifier\fP string can be validly interpreted as more than one of the above \fIkinds\fP of identification, you can disambiguate by prefixing it as follows: .IP "`\fBfile:\fP'" to indicate that it is a filename .IP "`\fBcomment:\fP'" to indicate that it is a comment string .IP "`\fBfp:\fP'" to indicate that it is a fingerprint; any fingerprint format will be matched .IP "`\fBsha256:\fP' or `\fBmd5:\fP'" to indicate that it is a fingerprint of a specific format .RE .IP "\fB--public-openssh\fP \fIkey-identifiers\fP, \fB-L\fP \fIkey-identifiers\fP" Print the public half of each specified key, in the one-line format used by OpenSSH, suitable for putting in \fB.ssh/authorized_keys\fP files. .IP "\fB-d\fP \fIkey-identifiers\fP" Delete each specified key from the agent's memory, so that the agent will no longer serve it to clients unless it is loaded in again using \fBpageant -a\fP. .IP "\fB-D\fP" Delete all keys from the agent's memory, leaving it completely empty. .IP "\fB-r\fP \fIkey-identifiers\fP" `Re-encrypt' each specified key in the agent's memory - that is, forget any cleartext version, so that the user will be prompted for a passphrase again next time the key is used. (For this to be possible, the key must previously have been added with the \fB--encrypted\fP option.) .RS .PP (Holding encrypted keys is a Pageant extension, so this option and \fB-R\fP are unlikely to work with other agents.) .RE .IP "\fB-R\fP" `Re-encrypt' all possible keys in the agent's memory. (This may leave some keys in cleartext, if they were not previously added with the \fB--encrypted\fP option.) .IP "\fB--test-sign\fP \fIkey-identifier\fP" .IP "\fB--test-sign-with-flags=\fP\fIflags\fP \fIkey-identifier\fP" Sign arbitrary data with the given key. This mode is only likely to be useful when testing \fBpageant\fP itself. .RS .PP The data to sign is taken from standard input, signed by the agent with the key identified by \fIkey-identifier\fP, and the resulting signature emitted on standard output (as a binary blob in the format defined by the SSH specifications). .PP \fIflags\fP is a number representing a combination of flag bits defined by the SSH agent protocol. .RE .SH "SSH-ASKPASS REPLACEMENT" .IP "\fB--askpass\fP \fIprompt\fP" With this option, \fBpageant\fP acts as an \fIssh-askpass(1)\fP replacement, rather than performing any SSH agent functionality. This may be useful if you prefer Pageant\*(Aqs GUI prompt style, which minimises information leakage about your passphrase length in its visual feedback, compared to other \fIssh-askpass(1)\fP implementations. .RS .PP \fBpageant --askpass\fP implements the standard \fIssh-askpass(1)\fP interface: it can be passed a prompt to display (as a single argument) and, if successful, prints the passphrase on standard output and returns a zero exit status. Typically you would use the environment variable \fBSSH_ASKPASS\fP to tell other programs to use \fBpageant\fP in this way. .RE .SH "OPTIONS" .IP "\fB-v\fP" Verbose mode. When Pageant runs in agent mode, this option causes it to log all agent activity to its standard error. For example, you might run .RS .PP .nf \fBeval\ $(pageant\ \-X\ \-v\ 2>~/.pageant.log)\fP .fi .PP and expect a list of all signatures requested by agent clients to build up in that log file. .PP The log information is the same as that produced by the \fB--debug\fP lifetime option, but \fB--debug\fP sends it to standard output (since that is the main point of debugging mode) whereas \fB-v\fP in all other lifetime modes sends the same log data to standard error (being a by-product of the program\*(Aqs main purpose). Using \fB-v\fP in \fB--debug\fP mode has no effect: the log still goes to standard output. .RE .IP "\fB-s\fP, \fB-c\fP" Force Pageant to output its environment setup commands in the style of POSIX / Bourne shells (\fB-s\fP) or C shells (\fB-c\fP) respectively. If neither option is given, Pageant will guess based on whether the environment variable \fBSHELL\fP has a value ending in `\fBcsh\fP'. .IP "\fB--symlink\fP \fIfixed-path\fP" When operating in agent mode, as well as creating a uniquely named listening socket, \fBpageant\fP will also create (or update) a symbolic link at \fIfixed-path\fP pointing to that socket. .RS .PP This allows access to an agent instance by setting the \fBSSH_AUTH_SOCK\fP environment variable to \fIfixed-path\fP, rather than having to use the value invented by \fBpageant\fP when it starts. It\*(Aqs mainly expected to be useful for debugging. .RE .IP "\fB--encrypted\fP, \fB--no-decrypt\fP" When adding keys to the agent (at startup or later), keep them in encrypted form until the first attempt to use them; the user will be prompted for a passphrase then. Once decrypted, a key that was added in this way can be `re-encrypted' with the \fB-r\fP or \fB-R\fP client options. .RS .PP The \fB--encrypted\fP option makes no difference for key files which do not have a passphrase. .PP (Storing keys in encrypted form is a Pageant extension; other agent implementations are unlikely to support it.) .RE .IP "\fB-E\fP \fIfingerprint-type\fP, \fB--fptype\fP \fIfingerprint-type\fP" Specify the fingerprint format to print. Only applicable when listing fingerprints with \fB-l\fP. The available formats are \fBsha256\fP (the default) and \fBmd5\fP. .IP "\fB--gui-prompt\fP, \fB--tty-prompt\fP" Force Pageant to prompt for key passphrases with a particular method (GUI or terminal) rather than trying to guess the most appropriate method as described above. (These options are relevant whenever a key file is specified to \fBpageant\fP that needs immediate decryption, and in \fB--askpass\fP mode.) .IP "\fB--help\fP" Print a brief summary of command-line options and terminate. .IP "\fB--version\fP, \fB-V\fP" Print the version of Pageant. .IP "\fB--\fP" Cause all subsequent arguments to be treated as key file names, even if they look like options. putty-0.76/doc/psocks.10000644000175000017500000000545114072266315011720 00000000000000.ie \n(.g .ds Aq \(aq .el .ds Aq ' .TH "psocks" "1" "2021\(hy04\(hy08" "PuTTY\ tool\ suite" "PuTTY\ tool\ suite" .SH "NAME" .PP \fBpsocks\fP \- simple SOCKS proxy server .SH "SYNOPSIS" .PP .nf \fBpsocks\fP\ [\ \fB\-d\fP\ ]\ [\ \fB\-f\fP\ |\ \fB\-p\fP\ \fIpipe\-cmd\fP\ ]\ [\ \fB\-g\fP\ ]\ [\ \fIport\-number\fP\ ] .fi .SH "DESCRIPTION" .PP \fBpsocks\fP is a simple SOCKS4/5 proxy server. It supports proxying IPv4 and IPv6 connections. It does not support requiring authentication of its clients. .PP \fBpsocks\fP can be used together with an SSH client such as \fBputty(1)\fP to implement a reverse dynamic SSH tunnel. It can also be used for network protocol debugging, as it can record all the traffic passing through it in various ways. .PP By default, \fBpsocks\fP listens to connections from localhost only, on TCP port 1080. A different \fIport-number\fP can optionally be supplied, and with \fB-g\fP it will listen to connections from any host. .PP \fBpsocks\fP will emit log messages about connections it receives on standard error. With \fB-d\fP, it will log the contents of those connections too. .SH "OPTIONS" .PP The command-line options supported by \fBpsocks\fP are: .IP "\fB-g\fP" Accept connections from anywhere. By default, \fBpsocks\fP only accepts connections on the loopback interface. .IP "\fB--exec\fP \fIcommand\fP" \fBpsocks\fP will run the provided command as a subprocess. When the subprocess terminates, \fBpsocks\fP will terminate as well. .RS .PP All arguments on the \fBpsocks\fP command line after \fB--exec\fP will be treated as part of the command to run, even if they look like other valid \fBpsocks\fP options. .RE .IP "\fB-d\fP" Log all traffic to standard error, in a more or less human-readable form (in addition to messages about connections being opened and closed, which are always logged). .IP "\fB-f\fP" Record all traffic to files. For every incoming connection, two files are created, \fBsockout.NNNN\fP and \fBsockin.NNNN\fP, where \fINNNN\fP is a decimal index starting at 0 identifying the proxied connection. These record, respectively, traffic from the SOCKS client, and from the server it connected to through the proxy. .IP "\fB-p\fP \fIpipe-cmd\fP" Pipe all traffic to a command. For every incoming connection, \fIpipe-cmd\fP is invoked twice: .RS .PP .nf \fIpipe\-cmd\fP\ \fBout\fP\ \fIN\fP \fIpipe\-cmd\fP\ \fBin\fP\ \fIN\fP .fi .PP Each command will run for the direction of a proxied connection, and have the connection's traffic piped into it, similar to \fB-f\fP. .RE .SH "EXAMPLES" .PP In combination with the \fIplink(1)\fP SSH client, to set up a reverse dynamic SSH tunnel, in which the remote listening port 1080 on remote host \fBmyhost\fP acts as a SOCKS server giving access to your local network: .PP .nf psocks\ 12345\ \-\-exec\ plink\ \-R\ 1080:localhost:12345\ user@myhost .fi putty-0.76/doc/psusan.10000644000175000017500000003655514072266315011740 00000000000000.ie \n(.g .ds Aq \(aq .el .ds Aq ' .TH "psusan" "1" "2020\(hy12\(hy13" "PuTTY\ tool\ suite" "PuTTY\ tool\ suite" .SH "NAME" .PP \fBpsusan\fP \- pseudo-SSH for untappable, separately authenticated networks .SH "SYNOPSIS" .PP .nf \fBpsusan\fP\ [\ \fIoptions\fP\ ] .fi .SH "DESCRIPTION" .PP \fBpsusan\fP is a server program that behaves like the innermost `connection' layer of an SSH session, without the two outer security layers of encryption and authentication. It provides all the post-authentication features of an SSH connection: .IP "\fB\(bu\fP" choosing whether to run an interactive terminal session or a single specified command .IP "\fB\(bu\fP" multiple terminal sessions at once (or a mixture of those and specified commands) .IP "\fB\(bu\fP" SFTP file transfer .IP "\fB\(bu\fP" all the standard SSH port-forwarding options .IP "\fB\(bu\fP" X11 forwarding .IP "\fB\(bu\fP" SSH agent forwarding .PP The catch is that, because it lacks the outer layers of SSH, you have to run it over some kind of data channel that is already authenticated as the right user, and that is already protected to your satisfaction against eavesdropping and session hijacking. A good rule of thumb is that any channel that you were prepared to run a \fIbare\fP shell session over, you can run \fBpsusan\fP over instead, which adds all the above conveniences without changing the security properties. .PP The protocol that \fBpsusan\fP speaks is also spoken by PuTTY, Plink, PSCP, and PSFTP, if you select the protocol type `Bare ssh-connection' or the command-line option \fB-ssh-connection\fP and specify the absolute path to the appropriate Unix-domain socket in place of a hostname. .SH "EXAMPLES" .PP The idea of a secure, pre-authenticated data channel seems strange to people thinking about \fInetwork\fP connections. But there are lots of examples within the context of a single Unix system, and that's where \fBpsusan\fP is typically useful. .SS "Docker" .PP A good example is the console or standard I/O channel leading into a container or virtualisation system. Docker is a familiar example. If you want to start a Docker container and run a shell directly within it, you might say something like .PP .nf docker\ run\ \-i\ \-t\ \fIsome:image\fP .fi .PP which will allow you to run a single shell session inside the container, in the same terminal you started Docker from. .PP Suppose that you'd prefer to run \fImultiple\fP shell sessions in the same container at once (perhaps so that one of them can use debugging tools to poke at what another is doing). And perhaps inside that container you're going to run a program that you don't trust with full access to your network, but are prepared to let it make one or two specific network connections of the kind you could set up with an SSH port forwarding. .PP In that case, you could remove the \fB-t\fP option from that Docker command line (which means `allocate a terminal device'), and tell it to run \fBpsusan\fP inside the container: .PP .nf docker\ run\ \-i\ \fIsome:image\fP\ /\fIsome/path/to\fP/psusan .fi .PP (Of course, you'll need to ensure that \fBpsusan\fP is installed somewhere inside the container image.) .PP If you do that from a shell command line, you'll see a banner line looking something like this: .PP .nf SSHCONNECTION@putty.projects.tartarus.org\-2.0\-PSUSAN_Release_0.75 .fi .PP which isn't particularly helpful except that it tells you that \fBpsusan\fP has started up successfully. .PP To talk to this server \fIusefully\fP, you can set up a PuTTY saved session as follows: .IP "\fB\(bu\fP" Set the protocol to `Bare ssh-connection' (the \fBpsusan\fP protocol). .IP "\fB\(bu\fP" Write \fIsomething\fP in the hostname box. It will appear in PuTTY's window title (if you run GUI PuTTY), so you might want to write something that will remind you what kind of window it is. If you have no opinion, something generic like `\fBdummy\fP' will do. .IP "\fB\(bu\fP" In the `Proxy' configuration panel, set the proxy type to `Local', and enter the above `\fBdocker run\fP' command in the `Telnet command, or local proxy command' edit box. .IP "\fB\(bu\fP" In the `SSH' configuration panel, you will very likely want to turn on connection sharing. (See below.) .PP This arranges that when PuTTY starts up, it will run the Docker command as shown above in place of making a network connection, and talk to that command using the \fBpsusan\fP SSH-like protocol. .PP The effect is that you will still get a shell session in the context of a Docker container. But this time, it's got all the SSH amenities. If you also turn on connection sharing in the `SSH' configuration panel, then the `Duplicate Session' option will get you a second shell in the \fIsame\fP Docker container (instead of a primary shell in a separate instance). You can transfer files in and out of the container while it's running using PSCP or PSFTP; you can forward network ports, X11 programs, and/or an SSH agent to the container. .PP Of course, another way to do all of this would be to run the \fIfull\fP SSH protocol over the same channel. This involves more setup: you have to invent an SSH host key for the container, accept it in the client, and deal with it being left behind in your client's host key cache when the container is discarded. And you have to set up some login details in the container: either configure a password, and type it in the client, or copy in the public half of some SSH key you already had. And all this inconvenience is \fIunnecessary\fP, because these are all precautions you need to take when the connection between two systems is going over a hostile network. In this case, it's only going over a kernel IPC channel that's guaranteed to go to the right place, so those safety precautions are redundant, and they only add awkwardness. .SS "User-mode Linux" .PP User-mode Linux is another container type you can talk to in the same way. Here's a small worked example. .PP The \fIeasiest\fP way to run UML is to use its `\fBhostfs\fP' file system type to give the guest kernel access to the same virtual filesystem as you have on the host. For example, a command line like this gets you a shell prompt inside a UML instance sharing your existing filesystem: .PP .nf linux\ mem=512M\ rootfstype=hostfs\ rootflags=/\ rw\ init=/bin/bash .fi .PP If you run this at a command line (assuming you have a UML kernel available on your path under the name `\fBlinux\fP'), then you should see a lot of kernel startup messages, followed by a shell prompt along the lines of .PP .nf root@(none):/# .fi .PP To convert this into a \fBpsusan\fP-based UML session, we need to adjust the command line so that instead of running \fBbash\fP it runs \fBpsusan\fP. But running \fBpsusan\fP directly isn\*(Aqt quite enough, because \fBpsusan\fP will depend on a small amount of setup, such as having \fB/proc\fP mounted. So instead, we set the init process to a shell script which will do the necessary setup and \fIthen\fP invoke \fBpsusan\fP. .PP Also, running \fBpsusan\fP directly over the UML console device is a bad idea, because then the \fBpsusan\fP binary protocol will be mixed with textual console messages. So a better plan is to redirect UML\*(Aqs console to the standard error of the \fBlinux\fP process, and map its standard input and output to a serial port. So the replacement UML command line might look something like this: .PP .nf linux\ mem=512M\ rootfstype=hostfs\ rootflags=/\ rw\ \e \ \ \ \ con=fd:2,fd:2\ ssl0=fd:0,fd:1\ init=\fI/some/path/to/uml\-psusan.sh\fP .fi .PP And the setup script \fBuml-psusan.sh\fP might look like this: .PP .nf #!/bin/bash \fI#\ Set\ up\ vital\ pseudo\-filesystems\fP mount\ \-t\ proc\ none\ /proc mount\ \-t\ devpts\ none\ /dev/pts \fI#\ Redirect\ I/O\ to\ the\ serial\ port,\ but\ stderr\ to\ the\ console\fP exec\ 0<>/dev/ttyS0\ 1>&0\ 2>/dev/console \fI#\ Set\ the\ serial\ port\ into\ raw\ mode,\ to\ run\ a\ binary\ protocol\fP stty\ raw\ \-echo \fI#\ Choose\ what\ shell\ you\ want\ to\ run\ inside\ psusan\fP export\ SHELL=/bin/bash \fI#\ And\ now\ run\ psusan\ over\ the\ serial\ port\fP exec\ /home/simon/src/putty/misc/psusan .fi .PP Now set up a PuTTY saved session as in the Docker example above, using that \fBlinux\fP command as the local proxy command, and you\*(Aqll have a PuTTY session that starts up a clean UML instance when you run it, and (if you enabled connection sharing) further instances of the same session will connect to the same instance again. .SS "Windows Subsystem for Linux" .PP On Windows, the default way to use WSL is to run the \fBwsl\fP program, or one of its aliases, in a Windows console, either by launching it from an existing command prompt, or by using a shortcut that opens it in a fresh console. This gives you a Linux terminal environment, but in a Windows console window. .PP If you'd prefer to interact with the same environment using PuTTY as the terminal (for example, if you prefer PuTTY's mouse shortcuts for copy and paste), you can set it up by installing \fBpsusan\fP in the Linux environment, and then setting up a PuTTY saved session that talks to it. A nice way to do this is to use the name of the WSL distribution as the `host name': .IP "\fB\(bu\fP" set the local proxy command to `\fBwsl -d %host /usr/local/bin/psusan\fP' (or wherever you installed \fBpsusan\fP in the Linux system) .IP "\fB\(bu\fP" enter the name of a particular WSL distribution in the host name box. (For example, if you installed WSL Debian in the standard way from the Windows store, this will just be `Debian'.) .IP "\fB\(bu\fP" set the protocol to `Bare ssh-connection', as usual. .PP Like all the other examples here, this also permits you to forward ports in and out of the WSL environment (e.g. expose a WSL2 network service through the hypervisor's internal NAT), forward Pageant into it, and so on. .SS "\fBschroot\fP" .PP Another example of a container-like environment is the alternative filesystem layout set up by \fBschroot\fP(\fI1\fP). .PP \fBschroot\fP is another program that defaults to running an interactive shell session in the terminal you launched it from. But again, you can get a \fBpsusan\fP connection into the \fBschroot\fP environment by setting up a PuTTY saved session whose local proxy command is along the lines of .PP .nf schroot\ \-c\ \fIchroot\-name\fP\ /\fIsome/path/to\fP/psusan .fi .PP Depending on how much of the chroot environment is copied from your main one, you might find this makes it easier to (for example) run X11 programs inside the chroot that open windows on your main X display, or transfer files in and out of the chroot. .SS "Between network namespaces" .PP If you've set up multiple network namespaces on a Linux system, with different TCP/IP configurations, then \fBpsusan\fP can be a convenient unprivileged-user gateway between them, if you run it as a non-root user in the non-default one of your namespaces, listening for connections on a Unix-domain socket. .PP If you do that, then it gives you convenient control over which of your outgoing network connections use which TCP/IP configuration: you can use PuTTY to run a shell session in the context of the other namespace if you want to run commands like \fBping\fP, or you can set up individual port forwardings or even a SOCKS server so that processes running in one namespace can send their network connections via the other one. .PP For this application, it's probably most convenient to use the \fB--listen\fP option in \fBpsusan\fP, which makes it run as a server and listen for connections on a Unix-domain socket. Then you can enter that socket name in PuTTY\*(Aqs host name configuration field (and also still select the `Bare ssh-connection' protocol option), to connect to that socket as if it were an SSH client. .PP Provided the Unix-domain socket is inside a directory that only the right user has access to, this will ensure that authentication is done implicitly by the Linux kernel. .SS "Between user ids, via GNU userv" .PP If you use multiple user ids on the same machine, say for purposes of privilege separation (running some less-trusted program with limited abilities to access all your stuff), then you probably have a `default' or most privileged account where you run your main login session, and sometimes need to run a shell in another account. .PP \fBpsusan\fP can be used as an access channel between the accounts, using GNU \fBuserv\fP(\fI1\fP) as the transport. In the account you want to access, write a \fBuserv\fP configuration stanza along the lines of .PP .nf if\ (glob\ service\ psusan\ &\ glob\ calling\-user\ \fImy\-main\-account\-name\fP) \ \ \ reset \ \ \ execute\ /\fIsome/path/to\fP/psusan fi .fi .PP This gives your main account the right to run the command .PP .nf userv\ \fImy\-sub\-account\-name\fP\ psusan .fi .PP and you can configure that command name as a PuTTY local proxy command, in the same way as most of the previous examples. .PP Of course, there are plenty of ways already to access one local account from another, such as \fBsudo\fP. One advantage of doing it this way is that you don\*(Aqt need the system administrator to intervene when you want to change the access controls (e.g. change which of your accounts have access to another): as long as you have \fIsome\fP means of getting into each account in the first place, and \fBuserv\fP is installed, you can make further configuration changes without having to bother root about it. .PP Another advantage is that it might make file transfer between the accounts easier. If you're the kind of person who keeps your home directories private, then it's awkward to copy a file from one of your accounts to another just by using the \fBcp\fP command, because there\*(Aqs nowhere convenient that you can leave it in one account where the other one can read it. But with \fBpsusan\fP over \fBuserv\fP, you don\*(Aqt need any shared piece of filesystem: you can \fBscp\fP files back and forth without any difficulty. .SH "OPTIONS" .PP The command-line options supported by \fBpsusan\fP are: .IP "\fB--listen\fP \fIunix-socket-name\fP" Run \fBpsusan\fP in listening mode. \fIunix-socket-name\fP is the pathname of a Unix-domain socket to listen on. You should ensure that this pathname is inside a directory whose read and exec permissions are restricted to only the user(s) you want to be able to access the environment that \fBpsusan\fP is running in. .RS .PP The listening socket has to be a Unix-domain socket. \fBpsusan\fP does not provide an option to run over TCP/IP, because the unauthenticated nature of the protocol would make it inherently insecure. .RE .IP "\fB--listen-once\fP" In listening mode, this option causes \fBpsusan\fP to listen for only one connection, and exit immediately after that connection terminates. .IP "\fB--sessiondir\fP \fIpathname\fP" This option sets the directory that shell sessions and subprocesses will start in. By default it is \fBpsusan\fP\*(Aqs own working directory, but in some situations it\*(Aqs easier to change it with a command-line option than by wrapping \fBpsusan\fP in a script that changes directory before starting it. .IP "\fB-v\fP, \fB--verbose\fP" This option causes \fBpsusan\fP to print verbose log messages on its standard error. This is probably most useful in listening mode. .IP "\fB\-sshlog\fP \fIlogfile\fP" .IP "\fB\-sshrawlog\fP \fIlogfile\fP" These options cause \fBpsusan\fP to log protocol details to a file, similarly to the logging options in PuTTY and Plink. .RS .PP \fB\-sshlog\fP logs decoded SSH packets and other events (those that \fB\-v\fP would print). \fB\-sshrawlog\fP additionally logs the raw wire data, including the outer packet format and the initial greetings. .RE putty-0.76/doc/vstr.but0000644000175000017500000000003614072266315012040 00000000000000\versionid PuTTY release 0.76 putty-0.76/doc/puttydoc.txt0000644000175000017500000204252714072266315012757 00000000000000 PuTTY User Manual ================= PuTTY is a free (MIT-licensed) Windows Telnet and SSH client. This manual documents PuTTY, and its companion utilities PSCP, PSFTP, Plink, Pageant and PuTTYgen. _Note to Unix users:_ this manual currently primarily documents the Windows versions of the PuTTY utilities. Some options are therefore mentioned that are absent from the Unix version; the Unix version has features not described here; and the pterm and command-line puttygen and pageant utilities are not described at all. The only Unix-specific documentation that currently exists is the man pages. This manual is copyright 1997-2021 Simon Tatham. All rights reserved. You may distribute this documentation under the MIT licence. See appendix D for the licence text in full. Chapter 1: Introduction to PuTTY -------------------------------- PuTTY is a free SSH, Telnet, Rlogin, and SUPDUP client for Windows systems. 1.1 What are SSH, Telnet, Rlogin, and SUPDUP? If you already know what SSH, Telnet, Rlogin, and SUPDUP are, you can safely skip on to the next section. SSH, Telnet, Rlogin, and SUPDUP are four ways of doing the same thing: logging in to a multi-user computer from another computer, over a network. Multi-user operating systems, typically of the Unix family (such as Linux, MacOS, and the BSD family), usually present a command- line interface to the user, much like the `Command Prompt' or `MS- DOS Prompt' in Windows. The system prints a prompt, and you type commands which the system will obey. Using this type of interface, there is no need for you to be sitting at the same machine you are typing commands to. The commands, and responses, can be sent over a network, so you can sit at one computer and give commands to another one, or even to more than one. SSH, Telnet, Rlogin, and SUPDUP are _network protocols_ that allow you to do this. On the computer you sit at, you run a _client_, which makes a network connection to the other computer (the _server_). The network connection carries your keystrokes and commands from the client to the server, and carries the server's responses back to you. These protocols can also be used for other types of keyboard-based interactive session. In particular, there are a lot of bulletin boards, talker systems and MUDs (Multi-User Dungeons) which support access using Telnet. There are even a few that support SSH. You might want to use SSH, Telnet, Rlogin, or SUPDUP if: - you have an account on a Unix system (or some other multi-user OS such as VMS or ITS) which you want to be able to access from somewhere else - your Internet Service Provider provides you with a login account on a web server. (This might also be known as a _shell account_. A _shell_ is the program that runs on the server and interprets your commands for you.) - you want to use a bulletin board system, talker or MUD which can be accessed using Telnet. You probably do _not_ want to use SSH, Telnet, Rlogin, or SUPDUP if: - you only use Windows. Windows computers have their own ways of networking between themselves, and unless you are doing something fairly unusual, you will not need to use any of these remote login protocols. 1.2 How do SSH, Telnet, Rlogin, and SUPDUP differ? This list summarises some of the differences between SSH, Telnet, Rlogin, and SUPDUP. - SSH (which stands for `secure shell') is a recently designed, high-security protocol. It uses strong cryptography to protect your connection against eavesdropping, hijacking and other attacks. Telnet, Rlogin, and SUPDUP are all older protocols offering minimal security. - SSH and Rlogin both allow you to log in to the server without having to type a password. (Rlogin's method of doing this is insecure, and can allow an attacker to access your account on the server. SSH's method is much more secure, and typically breaking the security requires the attacker to have gained access to your actual client machine.) - SSH allows you to connect to the server and automatically send a command, so that the server will run that command and then disconnect. So you can use it in automated processing. The Internet is a hostile environment and security is everybody's responsibility. If you are connecting across the open Internet, then we recommend you use SSH. If the server you want to connect to doesn't support SSH, it might be worth trying to persuade the administrator to install it. If your client and server are both behind the same (good) firewall, it is more likely to be safe to use Telnet, Rlogin, or SUPDUP, but we still recommend you use SSH. Chapter 2: Getting started with PuTTY ------------------------------------- This chapter gives a quick guide to the simplest types of interactive login session using PuTTY. 2.1 Starting a session When you start PuTTY, you will see a dialog box. This dialog box allows you to control everything PuTTY can do. See chapter 4 for details of all the things you can control. You don't usually need to change most of the configuration options. To start the simplest kind of session, all you need to do is to enter a few basic parameters. In the `Host Name' box, enter the Internet host name of the server you want to connect to. You should have been told this by the provider of your login account. Now select a login protocol to use, from the `Connection type' controls. For a login session, you should select SSH, Telnet, Rlogin, or SUPDUP. See section 1.2 for a description of the differences between these protocols, and advice on which one to use. The _Raw_ protocol is not used for interactive login sessions; you would usually use this for debugging other Internet services (see section 3.7). The _Serial_ option is used for connecting to a local serial line, and works somewhat differently: see section 3.6 for more information on this. When you change the selected protocol, the number in the `Port' box will change. This is normal: it happens because the various login services are usually provided on different network ports by the server machine. Most servers will use the standard port numbers, so you will not need to change the port setting. If your server provides login services on a non-standard port, your system administrator should have told you which one. (For example, many MUDs run Telnet service on a port other than 23.) Once you have filled in the `Host Name', `Connection type', and possibly `Port' settings, you are ready to connect. Press the `Open' button at the bottom of the dialog box, and PuTTY will begin trying to connect you to the server. 2.2 Verifying the host key (SSH only) If you are not using the SSH protocol, you can skip this section. If you are using SSH to connect to a server for the first time, you will probably see a message looking something like this: The server's host key is not cached in the registry. You have no guarantee that the server is the computer you think it is. The server's ssh-ed25519 key fingerprint is: ssh-ed25519 255 SHA256:TddlQk20DVs4LRcAsIfDN9pInKpY06D+h4kSHwWAj4w If you trust this host, press "Accept" to add the key to PuTTY's cache and carry on connecting. If you want to carry on connecting just once, without adding the key to the cache, press "Connect Once". If you do not trust this host, press "Cancel" to abandon the connection. This is a feature of the SSH protocol. It is designed to protect you against a network attack known as _spoofing_: secretly redirecting your connection to a different computer, so that you send your password to the wrong machine. Using this technique, an attacker would be able to learn the password that guards your login account, and could then log in as if they were you and use the account for their own purposes. To prevent this attack, each server has a unique identifying code, called a _host key_. These keys are created in a way that prevents one server from forging another server's key. So if you connect to a server and it sends you a different host key from the one you were expecting, PuTTY can warn you that the server may have been switched and that a spoofing attack might be in progress. PuTTY records the host key for each server you connect to, in the Windows Registry. Every time you connect to a server, it checks that the host key presented by the server is the same host key as it was the last time you connected. If it is not, you will see a warning, and you will have the chance to abandon your connection before you type any private information (such as a password) into it. (See section 10.2 for what that looks like.) However, when you connect to a server you have not connected to before, PuTTY has no way of telling whether the host key is the right one or not. So it gives the warning shown above, and asks you whether you want to trust this host key or not. Whether or not to trust the host key is your choice. If you are connecting within a company network, you might feel that all the network users are on the same side and spoofing attacks are unlikely, so you might choose to trust the key without checking it. If you are connecting across a hostile network (such as the Internet), you should check with your system administrator, perhaps by telephone or in person. (When verifying the fingerprint, be careful with letters and numbers that can be confused with each other: `0'/`O', `1'/`I'/`l', and so on.) Many servers have more than one host key. If the system administrator sends you more than one fingerprint, you should make sure the one PuTTY shows you is on the list, but it doesn't matter which one it is. If you don't have any fingerprints that look like the example (`SHA256:' followed by a long string of characters), but instead have pairs of characters separated by colons like `a4:db:96:a7:...', try pressing the `More info...' button and see if you have a fingerprint matching the `MD5 fingerprint' there. This is an older and less secure way to summarise the same underlying host key; it's possible for an attacker to create their own host key with the same fingerprint; so you should avoid relying on this fingerprint format unless you have no choice. The `More info...' dialog box also shows the full host public key, in case that is easier to compare than a fingerprint. See section 4.19 for advanced options for managing host keys. 2.3 Logging in After you have connected, and perhaps verified the server's host key, you will be asked to log in, probably using a username and a password. Your system administrator should have provided you with these. (If, instead, your system administrator has asked you to provide, or provided you with, a `public key' or `key file', see chapter 8.) PuTTY will display a text window (the `terminal window' - it will have a black background unless you've changed the defaults), and prompt you to type your username and password into that window. (These prompts will include the PuTTY icon, to distinguish them from any text sent by the server in the same window.) Enter the username and the password, and the server should grant you access and begin your session. If you have mistyped your password, most servers will give you several chances to get it right. While you are typing your password, you will not usually see the cursor moving in the window, but PuTTY _is_ registering what you type, and will send it when you press Return. (It works this way to avoid revealing the length of your password to anyone watching your screen.) If you are using SSH, be careful not to type your username wrongly, because you will not have a chance to correct it after you press Return; many SSH servers do not permit you to make two login attempts using different usernames. If you type your username wrongly, you must close PuTTY and start again. If your password is refused but you are sure you have typed it correctly, check that Caps Lock is not enabled. Many login servers, particularly Unix computers, treat upper case and lower case as different when checking your password; so if Caps Lock is on, your password will probably be refused. 2.4 After logging in After you log in to the server, what happens next is up to the server! Most servers will print some sort of login message and then present a prompt, at which you can type commands which the server will carry out. Some servers will offer you on-line help; others might not. If you are in doubt about what to do next, consult your system administrator. 2.5 Logging out When you have finished your session, you should log out by typing the server's own logout command. This might vary between servers; if in doubt, try `logout' or `exit', or consult a manual or your system administrator. When the server processes your logout command, the PuTTY window should close itself automatically. You _can_ close a PuTTY session using the Close button in the window border, but this might confuse the server - a bit like hanging up a telephone unexpectedly in the middle of a conversation. We recommend you do not do this unless the server has stopped responding to you and you cannot close the window any other way. Chapter 3: Using PuTTY ---------------------- This chapter provides a general introduction to some more advanced features of PuTTY. For extreme detail and reference purposes, chapter 4 is likely to contain more information. 3.1 During your session A lot of PuTTY's complexity and features are in the configuration panel. Once you have worked your way through that and started a session, things should be reasonably simple after that. Nevertheless, there are a few more useful features available. 3.1.1 Copying and pasting text Often in a PuTTY session you will find text on your terminal screen which you want to type in again. Like most other terminal emulators, PuTTY allows you to copy and paste the text rather than having to type it again. Also, copy and paste uses the Windows clipboard, so that you can paste (for example) URLs into a web browser, or paste from a word processor or spreadsheet into your terminal session. By default, PuTTY's copy and paste works entirely with the mouse. (This will be familiar to people who have used `xterm' on Unix.) In order to copy text to the clipboard, you just click the left mouse button in the terminal window, and drag to select text. When you let go of the button, the text is _automatically_ copied to the clipboard. You do not need to press Ctrl-C or Ctrl-Ins; in fact, if you do press Ctrl-C, PuTTY will send a Ctrl-C character down your session to the server where it will probably cause a process to be interrupted. Pasting into PuTTY is done using the right button (or the middle mouse button, if you have a three-button mouse and have set it up; see section 4.11.1). (Pressing Shift-Ins, or selecting `Paste' from the Ctrl+right-click context menu, have the same effect.) When you click the right mouse button, PuTTY will read whatever is in the Windows clipboard and paste it into your session. By default, this behaves _exactly_ as if the clipboard contents had been typed at the keyboard; therefore, be careful of pasting formatted text into an editor that does automatic indenting, as you may find that the spaces pasted from the clipboard plus the spaces added by the editor add up to too many spaces and ruin the formatting. (Some remote applications can ask PuTTY to identify text that is being pasted, to avoid this sort of problem; but if your application does not, there is nothing PuTTY can do to avoid this.) If you double-click the left mouse button, PuTTY will select a whole word. If you double-click, hold down the second click, and drag the mouse, PuTTY will select a sequence of whole words. (You can adjust precisely what PuTTY considers to be part of a word; see section 4.12.1.) If you _triple_-click, or triple-click and drag, then PuTTY will select a whole line or sequence of lines. If you want to select a rectangular region instead of selecting to the end of each line, you can do this by holding down Alt when you make your selection. You can also configure rectangular selection to be the default, and then holding down Alt gives the normal behaviour instead: see section 4.11.3 for details. (In some Unix environments, Alt+drag is intercepted by the window manager. Shift+Alt+drag should work for rectangular selection as well, so you could try that instead.) If you have a middle mouse button, then you can use it to adjust an existing selection if you selected something slightly wrong. (If you have configured the middle mouse button to paste, then the right mouse button does this instead.) Click the button on the screen, and you can pick up the nearest end of the selection and drag it to somewhere else. If you are running PuTTY itself on Unix (not just using it to connect to a Unix system from Windows), by default you will likely have to use similar mouse actions in other applications to paste the text you copied from PuTTY, and to copy text for pasting into PuTTY; actions like Ctrl-C and Ctrl-V will likely not behave as you expect. Section 4.11.4 explains why this is, and how you can change the behaviour. (On Windows there is only a single selection shared with other applications, so this confusion does not arise.) It's possible for the server to ask to handle mouse clicks in the PuTTY window itself. If this happens, the mouse pointer will turn into an arrow, and using the mouse to copy and paste will only work if you hold down Shift. See section 4.6.2 and section 4.11.2 for details of this feature and how to configure it. You can customise much of this behaviour, for instance to enable copy and paste from the keyboard; see section 4.11. 3.1.2 Scrolling the screen back PuTTY keeps track of text that has scrolled up off the top of the terminal. So if something appears on the screen that you want to read, but it scrolls too fast and it's gone by the time you try to look for it, you can use the scrollbar on the right side of the window to look back up the session history and find it again. As well as using the scrollbar, you can also page the scrollback up and down by pressing Shift-PgUp and Shift-PgDn. You can scroll a line at a time using Ctrl-PgUp and Ctrl-PgDn, or to the top/bottom of the scrollback with Ctrl-Shift-PgUp and Ctrl-Shift-PgDn. These are still available if you configure the scrollbar to be invisible. By default the last 2000 lines scrolled off the top are preserved for you to look at. You can increase (or decrease) this value using the configuration box; see section 4.7.3. 3.1.3 The System menu If you click the left mouse button on the icon in the top left corner of PuTTY's terminal window, or click the right mouse button on the title bar, you will see the standard Windows system menu containing items like Minimise, Move, Size and Close. PuTTY's system menu contains extra program features in addition to the Windows standard options. These extra menu commands are described below. (These options are also available in a context menu brought up by holding Ctrl and clicking with the right mouse button anywhere in the PuTTY window.) 3.1.3.1 The PuTTY Event Log If you choose `Event Log' from the system menu, a small window will pop up in which PuTTY logs significant events during the connection. Most of the events in the log will probably take place during session startup, but a few can occur at any point in the session, and one or two occur right at the end. You can use the mouse to select one or more lines of the Event Log, and hit the Copy button to copy them to the clipboard. If you are reporting a bug, it's often useful to paste the contents of the Event Log into your bug report. (The Event Log is not the same as the facility to create a log file of your session; that's described in section 3.2.) 3.1.3.2 Special commands Depending on the protocol used for the current session, there may be a submenu of `special commands'. These are protocol-specific tokens, such as a `break' signal, that can be sent down a connection in addition to normal data. Their precise effect is usually up to the server. Currently only Telnet, SSH, and serial connections have special commands. The `break' signal can also be invoked from the keyboard with Ctrl- Break. In an SSH connection, the following special commands are available: - IGNORE message Should have no effect. - Repeat key exchange Only available in SSH-2. Forces a repeat key exchange immediately (and resets associated timers and counters). For more information about repeat key exchanges, see section 4.18.2. - Cache new host key type Only available in SSH-2. This submenu appears only if the server has host keys of a type that PuTTY doesn't already have cached, and so won't consider. Selecting a key here will allow PuTTY to use that key now and in future: PuTTY will do a fresh key- exchange with the selected key, and immediately add that key to its permanent cache (relying on the host key used at the start of the connection to cross-certify the new key). That key will be used for the rest of the current session; it may not actually be used for future sessions, depending on your preferences (see section 4.19.1). Normally, PuTTY will carry on using a host key it already knows, even if the server offers key formats that PuTTY would otherwise prefer, to avoid host key prompts. As a result, if you've been using a server for some years, you may still be using an older key than a new user would use, due to server upgrades in the meantime. The SSH protocol unfortunately does not have organised facilities for host key migration and rollover, but this allows you to manually upgrade. - Break Only available in SSH-2, and only during a session. Optional extension; may not be supported by server. PuTTY requests the server's default break length. - Signals (SIGINT, SIGTERM etc) Only available in SSH-2, and only during a session. Sends various POSIX signals. Not honoured by all servers. The following special commands are available in Telnet: - Are You There - Break - Synch - Erase Character PuTTY can also be configured to send this when the Backspace key is pressed; see section 4.29.3. - Erase Line - Go Ahead - No Operation Should have no effect. - Abort Process - Abort Output - Interrupt Process PuTTY can also be configured to send this when Ctrl-C is typed; see section 4.29.3. - Suspend Process PuTTY can also be configured to send this when Ctrl-Z is typed; see section 4.29.3. - End Of Record - End Of File With a serial connection, the only available special command is `Break'. 3.1.3.3 Starting new sessions PuTTY's system menu provides some shortcut ways to start new sessions: - Selecting `New Session' will start a completely new instance of PuTTY, and bring up the configuration box as normal. - Selecting `Duplicate Session' will start a session in a new window with precisely the same options as your current one - connecting to the same host using the same protocol, with all the same terminal settings and everything. - In an inactive window, selecting `Restart Session' will do the same as `Duplicate Session', but in the current window. - The `Saved Sessions' submenu gives you quick access to any sets of stored session details you have previously saved. See section 4.1.2 for details of how to create saved sessions. 3.1.3.4 Changing your session settings If you select `Change Settings' from the system menu, PuTTY will display a cut-down version of its initial configuration box. This allows you to adjust most properties of your current session. You can change the terminal size, the font, the actions of various keypresses, the colours, and so on. Some of the options that are available in the main configuration box are not shown in the cut-down Change Settings box. These are usually options which don't make sense to change in the middle of a session (for example, you can't switch from SSH to Telnet in mid-session). You can save the current settings to a saved session for future use from this dialog box. See section 4.1.2 for more on saved sessions. 3.1.3.5 Copy All to Clipboard This system menu option provides a convenient way to copy the whole contents of the terminal screen (up to the last nonempty line) and scrollback to the clipboard in one go. 3.1.3.6 Clearing and resetting the terminal The `Clear Scrollback' option on the system menu tells PuTTY to discard all the lines of text that have been kept after they scrolled off the top of the screen. This might be useful, for example, if you displayed sensitive information and wanted to make sure nobody could look over your shoulder and see it. (Note that this only prevents a casual user from using the scrollbar to view the information; the text is not guaranteed not to still be in PuTTY's memory.) The `Reset Terminal' option causes a full reset of the terminal emulation. A VT-series terminal is a complex piece of software and can easily get into a state where all the text printed becomes unreadable. (This can happen, for example, if you accidentally output a binary file to your terminal.) If this happens, selecting Reset Terminal should sort it out. 3.1.3.7 Full screen mode If you find the title bar on a maximised window to be ugly or distracting, you can select Full Screen mode to maximise PuTTY `even more'. When you select this, PuTTY will expand to fill the whole screen and its borders, title bar and scrollbar will disappear. (You can configure the scrollbar not to disappear in full-screen mode if you want to keep it; see section 4.7.3.) When you are in full-screen mode, you can still access the system menu if you click the left mouse button in the _extreme_ top left corner of the screen. 3.2 Creating a log file of your session For some purposes you may find you want to log everything that appears on your screen. You can do this using the `Logging' panel in the configuration box. To begin a session log, select `Change Settings' from the system menu and go to the Logging panel. Enter a log file name, and select a logging mode. (You can log all session output including the terminal control sequences, or you can just log the printable text. It depends what you want the log for.) Click `Apply' and your log will be started. Later on, you can go back to the Logging panel and select `Logging turned off completely' to stop logging; then PuTTY will close the log file and you can safely read it. See section 4.2 for more details and options. 3.3 Altering your character set configuration If you find that special characters (accented characters, for example, or line-drawing characters) are not being displayed correctly in your PuTTY session, it may be that PuTTY is interpreting the characters sent by the server according to the wrong _character set_. There are a lot of different character sets available, and no good way for PuTTY to know which to use, so it's entirely possible for this to happen. If you click `Change Settings' and look at the `Translation' panel, you should see a large number of character sets which you can select, and other related options. Now all you need is to find out which of them you want! (See section 4.10 for more information.) 3.4 Using X11 forwarding in SSH The SSH protocol has the ability to securely forward X Window System graphical applications over your encrypted SSH connection, so that you can run an application on the SSH server machine and have it put its windows up on your local machine without sending any X network traffic in the clear. In order to use this feature, you will need an X display server for your Windows machine, such as Cygwin/X, X-Win32, or Exceed. This will probably install itself as display number 0 on your local machine; if it doesn't, the manual for the X server should tell you what it does do. You should then tick the `Enable X11 forwarding' box in the X11 panel (see section 4.24) before starting your SSH session. The `X display location' box is blank by default, which means that PuTTY will try to use a sensible default such as `:0', which is the usual display location where your X server will be installed. If that needs changing, then change it. Now you should be able to log in to the SSH server as normal. To check that X forwarding has been successfully negotiated during connection startup, you can check the PuTTY Event Log (see section 3.1.3.1). It should say something like this: 2001-12-05 17:22:01 Requesting X11 forwarding 2001-12-05 17:22:02 X11 forwarding enabled If the remote system is Unix or Unix-like, you should also be able to see that the `DISPLAY' environment variable has been set to point at display 10 or above on the SSH server machine itself: fred@unixbox:~$ echo $DISPLAY unixbox:10.0 If this works, you should then be able to run X applications in the remote session and have them display their windows on your PC. For more options relating to X11 forwarding, see section 4.24. 3.5 Using port forwarding in SSH The SSH protocol has the ability to forward arbitrary network (TCP) connections over your encrypted SSH connection, to avoid the network traffic being sent in clear. For example, you could use this to connect from your home computer to a POP-3 server on a remote machine without your POP-3 password being visible to network sniffers. In order to use port forwarding to connect from your local machine to a port on a remote server, you need to: - Choose a port number on your local machine where PuTTY should listen for incoming connections. There are likely to be plenty of unused port numbers above 3000. (You can also use a local loopback address here; see below for more details.) - Now, before you start your SSH connection, go to the Tunnels panel (see section 4.25). Make sure the `Local' radio button is set. Enter the local port number into the `Source port' box. Enter the destination host name and port number into the `Destination' box, separated by a colon (for example, `popserver.example.com:110' to connect to a POP-3 server). - Now click the `Add' button. The details of your port forwarding should appear in the list box. Now start your session and log in. (Port forwarding will not be enabled until after you have logged in; otherwise it would be easy to perform completely anonymous network attacks, and gain access to anyone's virtual private network.) To check that PuTTY has set up the port forwarding correctly, you can look at the PuTTY Event Log (see section 3.1.3.1). It should say something like this: 2001-12-05 17:22:10 Local port 3110 forwarding to popserver.example.com:110 Now if you connect to the source port number on your local PC, you should find that it answers you exactly as if it were the service running on the destination machine. So in this example, you could then configure an e-mail client to use `localhost:3110' as a POP- 3 server instead of `popserver.example.com:110'. (Of course, the forwarding will stop happening when your PuTTY session closes down.) You can also forward ports in the other direction: arrange for a particular port number on the _server_ machine to be forwarded back to your PC as a connection to a service on your PC or near it. To do this, just select the `Remote' radio button instead of the `Local' one. The `Source port' box will now specify a port number on the _server_ (note that most servers will not allow you to use port numbers under 1024 for this purpose). An alternative way to forward local connections to remote hosts is to use dynamic SOCKS proxying. In this mode, PuTTY acts as a SOCKS server, which SOCKS-aware programs can connect to and open forwarded connections to the destination of their choice, so this can be an alternative to long lists of static forwardings. To use this mode, you will need to select the `Dynamic' radio button instead of `Local', and then you should not enter anything into the `Destination' box (it will be ignored). PuTTY will then listen for SOCKS connections on the port you have specified. Most web browsers can be configured to connect to this SOCKS proxy service; also, you can forward other PuTTY connections through it by setting up the Proxy control panel (see section 4.16 for details). The source port for a forwarded connection usually does not accept connections from any machine except the SSH client or server machine itself (for local and remote forwardings respectively). There are controls in the Tunnels panel to change this: - The `Local ports accept connections from other hosts' option allows you to set up local-to-remote port forwardings (including dynamic port forwardings) in such a way that machines other than your client PC can connect to the forwarded port. - The `Remote ports do the same' option does the same thing for remote-to-local port forwardings (so that machines other than the SSH server machine can connect to the forwarded port.) Note that this feature is only available in the SSH-2 protocol, and not all SSH-2 servers honour it (in OpenSSH, for example, it's usually disabled by default). You can also specify an IP address to listen on. Typically a Windows machine can be asked to listen on any single IP address in the 127.*.*.* range, and all of these are loopback addresses available only to the local machine. So if you forward (for example) `127.0.0.5:79' to a remote machine's finger port, then you should be able to run commands such as `finger fred@127.0.0.5'. This can be useful if the program connecting to the forwarded port doesn't allow you to change the port number it uses. This feature is available for local-to-remote forwarded ports; SSH-1 is unable to support it for remote-to-local ports, while SSH-2 can support it in theory but servers will not necessarily cooperate. (Note that if you're using Windows XP Service Pack 2, you may need to obtain a fix from Microsoft in order to use addresses like 127.0.0.5 - see question A.7.17.) For more options relating to port forwarding, see section 4.25. If the connection you are forwarding over SSH is itself a second SSH connection made by another copy of PuTTY, you might find the `logical host name' configuration option useful to warn PuTTY of which host key it should be expecting. See section 4.14.5 for details of this. 3.6 Connecting to a local serial line PuTTY can connect directly to a local serial line as an alternative to making a network connection. In this mode, text typed into the PuTTY window will be sent straight out of your computer's serial port, and data received through that port will be displayed in the PuTTY window. You might use this mode, for example, if your serial port is connected to another computer which has a serial connection. To make a connection of this type, simply select `Serial' from the `Connection type' radio buttons on the `Session' configuration panel (see section 4.1.1). The `Host Name' and `Port' boxes will transform into `Serial line' and `Speed', allowing you to specify which serial line to use (if your computer has more than one) and what speed (baud rate) to use when transferring data. For further configuration options (data bits, stop bits, parity, flow control), you can use the `Serial' configuration panel (see section 4.28). After you start up PuTTY in serial mode, you might find that you have to make the first move, by sending some data out of the serial line in order to notify the device at the other end that someone is there for it to talk to. This probably depends on the device. If you start up a PuTTY serial session and nothing appears in the window, try pressing Return a few times and see if that helps. A serial line provides no well defined means for one end of the connection to notify the other that the connection is finished. Therefore, PuTTY in serial mode will remain connected until you close the window using the close button. 3.7 Making raw TCP connections A lot of Internet protocols are composed of commands and responses in plain text. For example, SMTP (the protocol used to transfer e- mail), NNTP (the protocol used to transfer Usenet news), and HTTP (the protocol used to serve Web pages) all consist of commands in readable plain text. Sometimes it can be useful to connect directly to one of these services and speak the protocol `by hand', by typing protocol commands and watching the responses. On Unix machines, you can do this using the system's `telnet' command to connect to the right port number. For example, `telnet mailserver.example.com 25' might enable you to talk directly to the SMTP service running on a mail server. Although the Unix `telnet' program provides this functionality, the protocol being used is not really Telnet. Really there is no actual protocol at all; the bytes sent down the connection are exactly the ones you type, and the bytes shown on the screen are exactly the ones sent by the server. Unix `telnet' will attempt to detect or guess whether the service it is talking to is a real Telnet service or not; PuTTY prefers to be told for certain. In order to make a debugging connection to a service of this type, you simply select the fourth protocol name, `Raw', from the `Protocol' buttons in the `Session' configuration panel. (See section 4.1.1.) You can then enter a host name and a port number, and make the connection. 3.8 Connecting using the Telnet protocol PuTTY can use the Telnet protocol to connect to a server. Telnet was perhaps the most popular remote login protocol before SSH was introduced. It was general enough to be used by multiple server operating systems (Unix and VMS in particular), and supported many optional protocol extensions providing extra support for particular server features. Unlike SSH, Telnet runs over an unsecured network connection, so it is a very bad idea to use it over the hostile Internet (though it is still used to some extent as of 2020). 3.9 Connecting using the Rlogin protocol PuTTY can use the Rlogin protocol to connect to a server. Rlogin was similar to Telnet in concept, but more focused on connections between Unix machines. It supported a feature for passwordless login, based on use of `privileged ports' (ports with numbers below 1024, which Unix traditionally does not allow users other than root to allocate). Ultimately, based on the server trusting that the client's IP address was owned by the Unix machine it claimed to be, and that that machine would guard its privileged ports appropriately. Like Telnet, Rlogin runs over an unsecured network connection. 3.10 Connecting using the SUPDUP protocol PuTTY can use the SUPDUP protocol to connect to a server. SUPDUP is a login protocol used mainly by PDP-10 and Lisp machines during the period 1975-1990. Like Telnet and Rlogin, it is unsecured, so modern systems almost never support it. To make a connection of this type, select `SUPDUP' from the `Connection type' radio buttons on the `Session' panel (see section 4.1.1). For further configuration options (character set, more processing, scrolling), you can use the `SUPDUP' configuration panel (see section 4.31). In SUPDUP, terminal emulation is more integrated with the network protocol than in other protocols such as SSH. The SUPDUP protocol can thus only be used with PuTTY proper, not with the command-line tool Plink. The SUPDUP protocol does not support changing the terminal dimensions, so this capability is disabled during a SUPDUP session. SUPDUP provides no well defined means for one end of the connection to notify the other that the connection is finished. Therefore, PuTTY in SUPDUP mode will remain connected until you close the window using the close button. 3.11 The PuTTY command line PuTTY can be made to do various things without user intervention by supplying command-line arguments (e.g., from a command prompt window, or a Windows shortcut). 3.11.1 Starting a session from the command line These options allow you to bypass the configuration window and launch straight into a session. To start a connection to a server called `host': putty.exe [-ssh | -ssh-connection | -telnet | -rlogin | -supdup | -raw] [user@]host If this syntax is used, settings are taken from the Default Settings (see section 4.1.2); `user' overrides these settings if supplied. Also, you can specify a protocol, which will override the default protocol (see section 3.11.3.2). For telnet sessions, the following alternative syntax is supported (this makes PuTTY suitable for use as a URL handler for telnet URLs in web browsers): putty.exe telnet://host[:port]/ To start a connection to a serial port, e.g. COM1: putty.exe -serial com1 In order to start an existing saved session called `sessionname', use the `-load' option (described in section 3.11.3.1). putty.exe -load "session name" 3.11.2 `-cleanup' If invoked with the `-cleanup' option, rather than running as normal, PuTTY will remove its registry entries and random seed file from the local machine (after confirming with the user). It will also attempt to remove information about recently launched sessions stored in the `jump list' on Windows 7 and up. Note that on multi-user systems, `-cleanup' only removes registry entries and files associated with the currently logged-in user. 3.11.3 Standard command-line options PuTTY and its associated tools support a range of command-line options, most of which are consistent across all the tools. This section lists the available options in all tools. Options which are specific to a particular tool are covered in the chapter about that tool. 3.11.3.1 `-load': load a saved session The `-load' option causes PuTTY to load configuration details out of a saved session. If these details include a host name, then this option is all you need to make PuTTY start a session. You need double quotes around the session name if it contains spaces. If you want to create a Windows shortcut to start a PuTTY saved session, this is the option you should use: your shortcut should call something like d:\path\to\putty.exe -load "my session" (Note that PuTTY itself supports an alternative form of this option, for backwards compatibility. If you execute `putty @sessionname' it will have the same effect as `putty -load "sessionname"'. With the `@' form, no double quotes are required, and the `@' sign must be the very first thing on the command line. This form of the option is deprecated.) 3.11.3.2 Selecting a protocol: `-ssh', `-ssh-connection', `-telnet', `- rlogin', `-supdup', `-raw', `-serial' To choose which protocol you want to connect with, you can use one of these options: - `-ssh' selects the SSH protocol. - `-ssh-connection' selects the bare ssh-connection protocol. (This is only useful in specialised circumstances; see section 4.27 for more information.) - `-telnet' selects the Telnet protocol. - `-rlogin' selects the Rlogin protocol. - `-supdup' selects the SUPDUP protocol. - `-raw' selects the raw protocol. - `-serial' selects a serial connection. Most of these options are not available in the file transfer tools PSCP and PSFTP (which only work with the SSH protocol and the bare ssh-connection protocol). These options are equivalent to the protocol selection buttons in the Session panel of the PuTTY configuration box (see section 4.1.1). 3.11.3.3 `-v': increase verbosity Most of the PuTTY tools can be made to tell you more about what they are doing by supplying the `-v' option. If you are having trouble when making a connection, or you're simply curious, you can turn this switch on and hope to find out more about what is happening. 3.11.3.4 `-l': specify a login name You can specify the user name to log in as on the remote server using the `-l' option. For example, `plink login.example.com - l fred'. These options are equivalent to the username selection box in the Connection panel of the PuTTY configuration box (see section 4.15.1). 3.11.3.5 `-L', `-R' and `-D': set up port forwardings As well as setting up port forwardings in the PuTTY configuration (see section 4.25), you can also set up forwardings on the command line. The command-line options work just like the ones in Unix `ssh' programs. To forward a local port (say 5110) to a remote destination (say popserver.example.com port 110), you can write something like one of these: putty -L 5110:popserver.example.com:110 -load mysession plink mysession -L 5110:popserver.example.com:110 To forward a remote port to a local destination, just use the `-R' option instead of `-L': putty -R 5023:mytelnetserver.myhouse.org:23 -load mysession plink mysession -R 5023:mytelnetserver.myhouse.org:23 To specify an IP address for the listening end of the tunnel, prepend it to the argument: plink -L 127.0.0.5:23:localhost:23 myhost To set up SOCKS-based dynamic port forwarding on a local port, use the `-D' option. For this one you only have to pass the port number: putty -D 4096 -load mysession For general information on port forwarding, see section 3.5. These options are not available in the file transfer tools PSCP and PSFTP. 3.11.3.6 `-m': read a remote command or script from a file The `-m' option performs a similar function to the `Remote command' box in the SSH panel of the PuTTY configuration box (see section 4.17.1). However, the `-m' option expects to be given a local file name, and it will read a command from that file. With some servers (particularly Unix systems), you can even put multiple lines in this file and execute more than one command in sequence, or a whole shell script; but this is arguably an abuse, and cannot be expected to work on all servers. In particular, it is known _not_ to work with certain `embedded' servers, such as Cisco routers. This option is not available in the file transfer tools PSCP and PSFTP. 3.11.3.7 `-P': specify a port number The `-P' option is used to specify the port number to connect to. If you have a Telnet server running on port 9696 of a machine instead of port 23, for example: putty -telnet -P 9696 host.name plink -telnet -P 9696 host.name (Note that this option is more useful in Plink than in PuTTY, because in PuTTY you can write `putty -telnet host.name 9696' in any case.) This option is equivalent to the port number control in the Session panel of the PuTTY configuration box (see section 4.1.1). 3.11.3.8 `-pw': specify a password A simple way to automate a remote login is to supply your password on the command line. This is _not recommended_ for reasons of security. If you possibly can, we recommend you set up public-key authentication instead. See chapter 8 for details. Note that the `-pw' option only works when you are using the SSH protocol. Due to fundamental limitations of Telnet, Rlogin, and SUPDUP, these protocols do not support automated password authentication. 3.11.3.9 `-agent' and `-noagent': control use of Pageant for authentication The `-agent' option turns on SSH authentication using Pageant, and `-noagent' turns it off. These options are only meaningful if you are using SSH. See chapter 9 for general information on Pageant. These options are equivalent to the agent authentication checkbox in the Auth panel of the PuTTY configuration box (see section 4.21.4). 3.11.3.10 `-A' and `-a': control agent forwarding The `-A' option turns on SSH agent forwarding, and `-a' turns it off. These options are only meaningful if you are using SSH. See chapter 9 for general information on Pageant, and section 9.4 for information on agent forwarding. Note that there is a security risk involved with enabling this option; see section 9.6 for details. These options are equivalent to the agent forwarding checkbox in the Auth panel of the PuTTY configuration box (see section 4.21.7). These options are not available in the file transfer tools PSCP and PSFTP. 3.11.3.11 `-X' and `-x': control X11 forwarding The `-X' option turns on X11 forwarding in SSH, and `-x' turns it off. These options are only meaningful if you are using SSH. For information on X11 forwarding, see section 3.4. These options are equivalent to the X11 forwarding checkbox in the X11 panel of the PuTTY configuration box (see section 4.24). These options are not available in the file transfer tools PSCP and PSFTP. 3.11.3.12 `-t' and `-T': control pseudo-terminal allocation The `-t' option ensures PuTTY attempts to allocate a pseudo-terminal at the server, and `-T' stops it from allocating one. These options are only meaningful if you are using SSH. These options are equivalent to the `Don't allocate a pseudo- terminal' checkbox in the SSH panel of the PuTTY configuration box (see section 4.23.1). These options are not available in the file transfer tools PSCP and PSFTP. 3.11.3.13 `-N': suppress starting a shell or command The `-N' option prevents PuTTY from attempting to start a shell or command on the remote server. You might want to use this option if you are only using the SSH connection for port forwarding, and your user account on the server does not have the ability to run a shell. This feature is only available in SSH protocol version 2 (since the version 1 protocol assumes you will always want to run a shell). This option is equivalent to the `Don't start a shell or command at all' checkbox in the SSH panel of the PuTTY configuration box (see section 4.17.2). This option is not available in the file transfer tools PSCP and PSFTP. 3.11.3.14 `-nc': make a remote network connection in place of a remote shell or command The `-nc' option prevents Plink (or PuTTY) from attempting to start a shell or command on the remote server. Instead, it will instruct the remote server to open a network connection to a host name and port number specified by you, and treat that network connection as if it were the main session. You specify a host and port as an argument to the `-nc' option, with a colon separating the host name from the port number, like this: plink host1.example.com -nc host2.example.com:1234 You might want to use this feature if you needed to make an SSH connection to a target host which you can only reach by going through a proxy host, and rather than using port forwarding you prefer to use the local proxy feature (see section 4.16.1 for more about local proxies). In this situation you might select `Local' proxy type, set your local proxy command to be `plink %proxyhost - nc %host:%port', enter the target host name on the Session panel, and enter the directly reachable proxy host name on the Proxy panel. This feature is only available in SSH protocol version 2 (since the version 1 protocol assumes you will always want to run a shell). It is not available in the file transfer tools PSCP and PSFTP. It is available in PuTTY itself, although it is unlikely to be very useful in any tool other than Plink. Also, `-nc' uses the same server functionality as port forwarding, so it will not work if your server administrator has disabled port forwarding. (The option is named `-nc' after the Unix program `nc', short for `netcat'. The command `plink host1 -nc host2:port' is very similar in functionality to `plink host1 nc host2 port', which invokes `nc' on the server and tells it to connect to the specified destination. However, Plink's built-in `-nc' option does not depend on the `nc' program being installed on the server.) 3.11.3.15 `-C': enable compression The `-C' option enables compression of the data sent across the network. This option is only meaningful if you are using SSH. This option is equivalent to the `Enable compression' checkbox in the SSH panel of the PuTTY configuration box (see section 4.17.3). 3.11.3.16 `-1' and `-2': specify an SSH protocol version The `-1' and `-2' options force PuTTY to use version 1 or version 2 of the SSH protocol. These options are only meaningful if you are using SSH. These options are equivalent to selecting the SSH protocol version in the SSH panel of the PuTTY configuration box (see section 4.17.4). 3.11.3.17 `-4' and `-6': specify an Internet protocol version The `-4' and `-6' options force PuTTY to use the older Internet protocol IPv4 or the newer IPv6 for most outgoing connections. These options are equivalent to selecting your preferred Internet protocol version as `IPv4' or `IPv6' in the Connection panel of the PuTTY configuration box (see section 4.14.4). 3.11.3.18 `-i': specify an SSH private key The `-i' option allows you to specify the name of a private key file in `*.PPK' format which PuTTY will use to authenticate with the server. This option is only meaningful if you are using SSH. If you are using Pageant, you can also specify a _public_ key file (in RFC 4716 or OpenSSH format) to identify a specific key file to use. (This won't work if you're not running Pageant, of course.) For general information on public-key authentication, see chapter 8. This option is equivalent to the `Private key file for authentication' box in the Auth panel of the PuTTY configuration box (see section 4.21.9). 3.11.3.19 `-no-trivial-auth': disconnect if SSH authentication succeeds trivially This option causes PuTTY to abandon an SSH session if the server accepts authentication without ever having asked for any kind of password or signature or token. See section 4.21.3 for why you might want this. 3.11.3.20 `-loghost': specify a logical host name This option overrides PuTTY's normal SSH host key caching policy by telling it the name of the host you expect your connection to end up at (in cases where this differs from the location PuTTY thinks it's connecting to). It can be a plain host name, or a host name followed by a colon and a port number. See section 4.14.5 for more detail on this. 3.11.3.21 `-hostkey': manually specify an expected host key This option overrides PuTTY's normal SSH host key caching policy by telling it exactly what host key to expect, which can be useful if the normal automatic host key store in the Registry is unavailable. The argument to this option should be either a host key fingerprint, or an SSH-2 public key blob. See section 4.19.3 for more information. You can specify this option more than once if you want to configure more than one key to be accepted. 3.11.3.22 `-pgpfp': display PGP key fingerprints This option causes the PuTTY tools not to run as normal, but instead to display the fingerprints of the PuTTY PGP Master Keys, in order to aid with verifying new versions. See appendix F for more information. 3.11.3.23 `-sercfg': specify serial port configuration This option specifies the configuration parameters for the serial port (baud rate, stop bits etc). Its argument is interpreted as a comma-separated list of configuration options, which can be as follows: - Any single digit from 5 to 9 sets the number of data bits. - `1', `1.5' or `2' sets the number of stop bits. - Any other numeric string is interpreted as a baud rate. - A single lower-case letter specifies the parity: `n' for none, `o' for odd, `e' for even, `m' for mark and `s' for space. - A single upper-case letter specifies the flow control: `N' for none, `X' for XON/XOFF, `R' for RTS/CTS and `D' for DSR/DTR. For example, `-sercfg 19200,8,n,1,N' denotes a baud rate of 19200, 8 data bits, no parity, 1 stop bit and no flow control. 3.11.3.24 `-sessionlog', `-sshlog', `-sshrawlog': enable session logging These options cause the PuTTY network tools to write out a log file. Each of them expects a file name as an argument, e.g. `- sshlog putty.log' causes an SSH packet log to be written to a file called `putty.log'. The three different options select different logging modes, all available from the GUI too: - `-sessionlog' selects `All session output' logging mode. - `-sshlog' selects `SSH packets' logging mode. - `-sshrawlog' selects `SSH packets and raw data' logging mode. For more information on logging configuration, see section 4.2. 3.11.3.25 `-logoverwrite', `-logappend': control behaviour with existing log file If logging has been enabled (in the saved configuration, or by another command-line option), and the specified log file already exists, these options tell the PuTTY network tools what to do so that they don't have to ask the user. See section 4.2.2 for details. 3.11.3.26 `-proxycmd': specify a local proxy command This option enables PuTTY's mode for running a command on the local machine and using it as a proxy for the network connection. It expects a shell command string as an argument. See section 4.16.1 for more information on this, and on other proxy settings. In particular, note that since the special sequences described there are understood in the argument string, literal backslashes must be doubled (if you want `\' in your command, you must put `\\' on the command line). 3.11.3.27 `-restrict-acl': restrict the Windows process ACL This option (on Windows only) causes PuTTY (or another PuTTY tool) to try to lock down the operating system's access control on its own process. If this succeeds, it should present an extra obstacle to malware that has managed to run under the same user id as the PuTTY process, by preventing it from attaching to PuTTY using the same interfaces debuggers use and either reading sensitive information out of its memory or hijacking its network session. This option is not enabled by default, because this form of interaction between Windows programs has many legitimate uses, including accessibility software such as screen readers. Also, it cannot provide full security against this class of attack in any case, because PuTTY can only lock down its own ACL _after_ it has started up, and malware could still get in if it attacks the process between startup and lockdown. So it trades away noticeable convenience, and delivers less real security than you might want. However, if you do want to make that tradeoff anyway, the option is available. A PuTTY process started with `-restrict-acl' will pass that on to any processes started with Duplicate Session, New Session etc. (However, if you're invoking PuTTY tools explicitly, for instance as a proxy command, you'll need to arrange to pass them the `-restrict- acl' option yourself, if that's what you want.) If Pageant is started with the `-restrict-acl' option, and you use it to launch a PuTTY session from its System Tray submenu, then Pageant will _not_ default to starting the PuTTY subprocess with a restricted ACL. This is because PuTTY is more likely to suffer reduced functionality as a result of restricted ACLs (e.g. screen reader software will have a greater need to interact with it), whereas Pageant stores the more critical information (hence benefits more from the extra protection), so it's reasonable to want to run Pageant but not PuTTY with the ACL restrictions. You can force Pageant to start subsidiary PuTTY processes with a restricted ACL if you also pass the `-restrict-putty-acl' option. Chapter 4: Configuring PuTTY ---------------------------- This chapter describes all the 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 session, by selecting `Change Settings' from the window menu. 4.1 The Session panel The Session configuration panel contains the basic options you need to specify in order to open a session at all, and also allows you to save your settings to be reloaded later. 4.1.1 The host name section The top box on the Session panel, labelled `Specify the destination you want to connect to', contains the details that need to be filled in before PuTTY can open a session at all. - The `Host Name' box is where you type the name, or the IP address, of the server you want to connect to. - The `Connection type' controls let you choose what type of connection you want to make: an SSH network connection, a connection to a local serial line, or various other kinds of network connection. - See section 1.2 for a summary of the differences between the network remote login protocols SSH, Telnet, Rlogin, and SUPDUP. - See section 3.6 for information about using a serial line. - See section 3.7 for an explanation of `raw' connections. - See section 3.8 for a little information about Telnet. - See section 3.9 for information about using Rlogin. - See section 3.10 for information about using SUPDUP. - The `Bare ssh-connection' option in the `Connection type' control is intended for specialist uses not involving network connections. See section 4.27 for some information about it. - The `Port' box lets you specify which port number on the server to connect to. If you select Telnet, Rlogin, SUPDUP, or SSH, this box will be filled in automatically to the usual value, and you will only need to change it if you have an unusual server. If you select Raw mode, you will almost certainly need to fill in the `Port' box yourself. If you select `Serial' from the `Connection type' radio buttons, the `Host Name' and `Port' boxes are replaced by `Serial line' and `Speed'; see section 4.28 for more details of these. 4.1.2 Loading and storing saved sessions The next part of the Session configuration panel allows you to save your preferred PuTTY options so they will appear automatically the next time you start PuTTY. It also allows you to create _saved sessions_, which contain a full set of configuration options plus a host name and protocol. A saved session contains all the information PuTTY needs to start exactly the session you want. - To save your default settings: first set up the settings the way you want them saved. Then come back to the Session panel. Select the `Default Settings' entry in the saved sessions list, with a single click. Then press the `Save' button. If there is a specific host you want to store the details of how to connect to, you should create a saved session, which will be separate from the Default Settings. - To save a session: first go through the rest of the configuration box setting up all the options you want. Then come back to the Session panel. Enter a name for the saved session in the `Saved Sessions' input box. (The server name is often a good choice for a saved session name.) Then press the `Save' button. Your saved session name should now appear in the list box. You can also save settings in mid-session, from the `Change Settings' dialog. Settings changed since the start of the session will be saved with their current values; as well as settings changed through the dialog, this includes changes in window size, window title changes sent by the server, and so on. - To reload a saved session: single-click to select the session name in the list box, and then press the `Load' button. Your saved settings should all appear in the configuration panel. - To modify a saved session: first load it as described above. Then make the changes you want. Come back to the Session panel, and press the `Save' button. The new settings will be saved over the top of the old ones. To save the new settings under a different name, you can enter the new name in the `Saved Sessions' box, or single-click to select a session name in the list box to overwrite that session. To save `Default Settings', you must single-click the name before saving. - To start a saved session immediately: double-click on the session name in the list box. - To delete a saved session: single-click to select the session name in the list box, and then press the `Delete' button. Each saved session is independent of the Default Settings configuration. If you change your preferences and update Default Settings, you must also update every saved session separately. Saved sessions are stored in the Registry, at the location HKEY_CURRENT_USER\Software\SimonTatham\PuTTY\Sessions If you need to store them in a file, you could try the method described in section 4.32. 4.1.3 `Close window on exit' Finally in the Session panel, there is an option labelled `Close window on exit'. This controls whether the PuTTY terminal window disappears as soon as the session inside it terminates. If you are likely to want to copy and paste text out of the session after it has terminated, or restart the session, you should arrange for this option to be off. `Close window on exit' has three settings. `Always' means always close the window on exit; `Never' means never close on exit (always leave the window open, but inactive). The third setting, and the default one, is `Only on clean exit'. In this mode, a session which terminates normally will cause its window to close, but one which is aborted unexpectedly by network trouble or a confusing message from the server will leave the window up. 4.2 The Logging panel The Logging configuration panel allows you to save log files of your PuTTY sessions, for debugging, analysis or future reference. The main option is a radio-button set that specifies whether PuTTY will log anything at all. The options are: - `None'. This is the default option; in this mode PuTTY will not create a log file at all. - `Printable output'. In this mode, a log file will be created and written to, but only printable text will be saved into it. The various terminal control codes that are typically sent down an interactive session alongside the printable text will be omitted. This might be a useful mode if you want to read a log file in a text editor and hope to be able to make sense of it. - `All session output'. In this mode, _everything_ sent by the server into your terminal session is logged. If you view the log file in a text editor, therefore, you may well find it full of strange control characters. This is a particularly useful mode if you are experiencing problems with PuTTY's terminal handling: you can record everything that went to the terminal, so that someone else can replay the session later in slow motion and watch to see what went wrong. - `SSH packets'. In this mode (which is only used by SSH connections), the SSH message packets sent over the encrypted connection are written to the log file (as well as Event Log entries). You might need this to debug a network-level problem, or more likely to send to the PuTTY authors as part of a bug report. _BE WARNED_ that if you log in using a password, the password can appear in the log file; see section 4.2.5 for options that may help to remove sensitive material from the log file before you send it to anyone else. - `SSH packets and raw data'. In this mode, as well as the decrypted packets (as in the previous mode), the _raw_ (encrypted, compressed, etc) packets are _also_ logged. This could be useful to diagnose corruption in transit. (The same caveats as the previous mode apply, of course.) Note that the non-SSH logging options (`Printable output' and `All session output') only work with PuTTY proper; in programs without terminal emulation (such as Plink), they will have no effect, even if enabled via saved settings. 4.2.1 `Log file name' In this edit box you enter the name of the file you want to log the session to. The `Browse' button will let you look around your file system to find the right place to put the file; or if you already know exactly where you want it to go, you can just type a pathname into the edit box. There are a few special features in this box. If you use the `&' character in the file name box, PuTTY will insert details of the current session in the name of the file it actually opens. The precise replacements it will do are: - `&Y' will be replaced by the current year, as four digits. - `&M' will be replaced by the current month, as two digits. - `&D' will be replaced by the current day of the month, as two digits. - `&T' will be replaced by the current time, as six digits (HHMMSS) with no punctuation. - `&H' will be replaced by the host name you are connecting to. - `&P' will be replaced by the port number you are connecting to on the target host. For example, if you enter the host name `c:\puttylogs\log-&h-&y&m&d- &t.dat', you will end up with files looking like log-server1.example.com-20010528-110859.dat log-unixbox.somewhere.org-20010611-221001.dat 4.2.2 `What to do if the log file already exists' This control allows you to specify what PuTTY should do if it tries to start writing to a log file and it finds the file already exists. You might want to automatically destroy the existing log file and start a new one with the same name. Alternatively, you might want to open the existing log file and add data to the _end_ of it. Finally (the default option), you might not want to have any automatic behaviour, but to ask the user every time the problem comes up. 4.2.3 `Flush log file frequently' This option allows you to control how frequently logged data is flushed to disc. By default, PuTTY will flush data as soon as it is displayed, so that if you view the log file while a session is still open, it will be up to date; and if the client system crashes, there's a greater chance that the data will be preserved. However, this can incur a performance penalty. If PuTTY is running slowly with logging enabled, you could try unchecking this option. Be warned that the log file may not always be up to date as a result (although it will of course be flushed when it is closed, for instance at the end of a session). 4.2.4 `Include header' This option allows you to choose whether to include a header line with the date and time when the log file is opened. It may be useful to disable this if the log file is being used as realtime input to other programs that don't expect the header line. 4.2.5 Options specific to SSH packet logging These options only apply if SSH packet data is being logged. The following options allow particularly sensitive portions of unencrypted packets to be automatically left out of the log file. They are only intended to deter casual nosiness; an attacker could glean a lot of useful information from even these obfuscated logs (e.g., length of password). 4.2.5.1 `Omit known password fields' When checked, decrypted password fields are removed from the log of transmitted packets. (This includes any user responses to challenge- response authentication methods such as `keyboard-interactive'.) This does not include X11 authentication data if using X11 forwarding. Note that this will only omit data that PuTTY _knows_ to be a password. However, if you start another login session within your PuTTY session, for instance, any password used will appear in the clear in the packet log. The next option may be of use to protect against this. This option is enabled by default. 4.2.5.2 `Omit session data' When checked, all decrypted `session data' is omitted; this is defined as data in terminal sessions and in forwarded channels (TCP, X11, and authentication agent). This will usually substantially reduce the size of the resulting log file. This option is disabled by default. 4.3 The Terminal panel The Terminal configuration panel allows you to control the behaviour of PuTTY's terminal emulation. 4.3.1 `Auto wrap mode initially on' Auto wrap mode controls what happens when text printed in a PuTTY window reaches the right-hand edge of the window. With auto wrap mode on, if a long line of text reaches the right- hand edge, it will wrap over on to the next line so you can still see all the text. With auto wrap mode off, the cursor will stay at the right-hand edge of the screen, and all the characters in the line will be printed on top of each other. If you are running a full-screen application and you occasionally find the screen scrolling up when it looks as if it shouldn't, you could try turning this option off. Auto wrap mode can be turned on and off by control sequences sent by the server. This configuration option controls the _default_ state, which will be restored when you reset the terminal (see section 3.1.3.6). However, if you modify this option in mid-session using `Change Settings', it will take effect immediately. 4.3.2 `DEC Origin Mode initially on' DEC Origin Mode is a minor option which controls how PuTTY interprets cursor-position control sequences sent by the server. The server can send a control sequence that restricts the scrolling region of the display. For example, in an editor, the server might reserve a line at the top of the screen and a line at the bottom, and might send a control sequence that causes scrolling operations to affect only the remaining lines. With DEC Origin Mode on, cursor coordinates are counted from the top of the scrolling region. With it turned off, cursor coordinates are counted from the top of the whole screen regardless of the scrolling region. It is unlikely you would need to change this option, but if you find a full-screen application is displaying pieces of text in what looks like the wrong part of the screen, you could try turning DEC Origin Mode on to see whether that helps. DEC Origin Mode can be turned on and off by control sequences sent by the server. This configuration option controls the _default_ state, which will be restored when you reset the terminal (see section 3.1.3.6). However, if you modify this option in mid-session using `Change Settings', it will take effect immediately. 4.3.3 `Implicit CR in every LF' Most servers send two control characters, CR and LF, to start a new line of the screen. The CR character makes the cursor return to the left-hand side of the screen. The LF character makes the cursor move one line down (and might make the screen scroll). Some servers only send LF, and expect the terminal to move the cursor over to the left automatically. If you come across a server that does this, you will see a stepped effect on the screen, like this: First line of text Second line Third line If this happens to you, try enabling the `Implicit CR in every LF' option, and things might go back to normal: First line of text Second line Third line 4.3.4 `Implicit LF in every CR' Most servers send two control characters, CR and LF, to start a new line of the screen. The CR character makes the cursor return to the left-hand side of the screen. The LF character makes the cursor move one line down (and might make the screen scroll). Some servers only send CR, and so the newly written line is overwritten by the following line. This option causes a line feed so that all lines are displayed. 4.3.5 `Use background colour to erase screen' Not all terminals agree on what colour to turn the screen when the server sends a `clear screen' sequence. Some terminals believe the screen should always be cleared to the _default_ background colour. Others believe the screen should be cleared to whatever the server has selected as a background colour. There exist applications that expect both kinds of behaviour. Therefore, PuTTY can be configured to do either. With this option disabled, screen clearing is always done in the default background colour. With this option enabled, it is done in the _current_ background colour. Background-colour erase can be turned on and off by control sequences sent by the server. This configuration option controls the _default_ state, which will be restored when you reset the terminal (see section 3.1.3.6). However, if you modify this option in mid- session using `Change Settings', it will take effect immediately. 4.3.6 `Enable blinking text' The server can ask PuTTY to display text that blinks on and off. This is very distracting, so PuTTY allows you to turn blinking text off completely. When blinking text is disabled and the server attempts to make some text blink, PuTTY will instead display the text with a bolded background colour. Blinking text can be turned on and off by control sequences sent by the server. This configuration option controls the _default_ state, which will be restored when you reset the terminal (see section 3.1.3.6). However, if you modify this option in mid-session using `Change Settings', it will take effect immediately. 4.3.7 `Answerback to ^E' This option controls what PuTTY will send back to the server if the server sends it the ^E enquiry character. Normally it just sends the string `PuTTY'. If you accidentally write the contents of a binary file to your terminal, you will probably find that it contains more than one ^E character, and as a result your next command line will probably read `PuTTYPuTTYPuTTY...' as if you had typed the answerback string multiple times at the keyboard. If you set the answerback string to be empty, this problem should go away, but doing so might cause other problems. Note that this is _not_ the feature of PuTTY which the server will typically use to determine your terminal type. That feature is the `Terminal-type string' in the Connection panel; see section 4.15.3 for details. You can include control characters in the answerback string using `^C' notation. (Use `^~' to get a literal `^'.) 4.3.8 `Local echo' With local echo disabled, characters you type into the PuTTY window are not echoed in the window _by PuTTY_. They are simply sent to the server. (The _server_ might choose to echo them back to you; this can't be controlled from the PuTTY control panel.) Some types of session need local echo, and many do not. In its default mode, PuTTY will automatically attempt to deduce whether or not local echo is appropriate for the session you are working in. If you find it has made the wrong decision, you can use this configuration option to override its choice: you can force local echo to be turned on, or force it to be turned off, instead of relying on the automatic detection. 4.3.9 `Local line editing' Normally, every character you type into the PuTTY window is sent immediately to the server the moment you type it. If you enable local line editing, this changes. PuTTY will let you edit a whole line at a time locally, and the line will only be sent to the server when you press Return. If you make a mistake, you can use the Backspace key to correct it before you press Return, and the server will never see the mistake. Since it is hard to edit a line locally without being able to see it, local line editing is mostly used in conjunction with local echo (section 4.3.8). This makes it ideal for use in raw mode or when connecting to MUDs or talkers. (Although some more advanced MUDs do occasionally turn local line editing on and turn local echo off, in order to accept a password from the user.) Some types of session need local line editing, and many do not. In its default mode, PuTTY will automatically attempt to deduce whether or not local line editing is appropriate for the session you are working in. If you find it has made the wrong decision, you can use this configuration option to override its choice: you can force local line editing to be turned on, or force it to be turned off, instead of relying on the automatic detection. 4.3.10 Remote-controlled printing A lot of VT100-compatible terminals support printing under control of the remote server (sometimes called `passthrough printing'). PuTTY supports this feature as well, but it is turned off by default. To enable remote-controlled printing, choose a printer from the `Printer to send ANSI printer output to' drop-down list box. This should allow you to select from all the printers you have installed drivers for on your computer. Alternatively, you can type the network name of a networked printer (for example, `\\printserver\printer1') even if you haven't already installed a driver for it on your own machine. When the remote server attempts to print some data, PuTTY will send that data to the printer _raw_ - without translating it, attempting to format it, or doing anything else to it. It is up to you to ensure your remote server knows what type of printer it is talking to. Since PuTTY sends data to the printer raw, it cannot offer options such as portrait versus landscape, print quality, or paper tray selection. All these things would be done by your PC printer driver (which PuTTY bypasses); if you need them done, you will have to find a way to configure your remote server to do them. To disable remote printing again, choose `None (printing disabled)' from the printer selection list. This is the default state. 4.4 The Keyboard panel The Keyboard configuration panel allows you to control the behaviour of the keyboard in PuTTY. The correct state for many of these settings depends on what the server to which PuTTY is connecting expects. With a Unix server, this is likely to depend on the `termcap' or `terminfo' entry it uses, which in turn is likely to be controlled by the `Terminal-type string' setting in the Connection panel; see section 4.15.3 for details. If none of the settings here seems to help, you may find question A.7.13 to be useful. 4.4.1 Changing the action of the Backspace key Some terminals believe that the Backspace key should send the same thing to the server as Control-H (ASCII code 8). Other terminals believe that the Backspace key should send ASCII code 127 (usually known as Control-?) so that it can be distinguished from Control-H. This option allows you to choose which code PuTTY generates when you press Backspace. If you are connecting over SSH, PuTTY by default tells the server the value of this option (see section 4.23.2), so you may find that the Backspace key does the right thing either way. Similarly, if you are connecting to a Unix system, you will probably find that the Unix `stty' command lets you configure which the server expects to see, so again you might not need to change which one PuTTY generates. On other systems, the server's expectation might be fixed and you might have no choice but to configure PuTTY. If you do have the choice, we recommend configuring PuTTY to generate Control-? and configuring the server to expect it, because that allows applications such as `emacs' to use Control-H for help. (Typing Shift-Backspace will cause PuTTY to send whichever code isn't configured here as the default.) 4.4.2 Changing the action of the Home and End keys The Unix terminal emulator `rxvt' disagrees with the rest of the world about what character sequences should be sent to the server by the Home and End keys. `xterm', and other terminals, send `ESC [1~' for the Home key, and `ESC [4~' for the End key. `rxvt' sends `ESC [H' for the Home key and `ESC [Ow' for the End key. If you find an application on which the Home and End keys aren't working, you could try switching this option to see if it helps. 4.4.3 Changing the action of the function keys and keypad This option affects the function keys (F1 to F12) and the top row of the numeric keypad. - In the default mode, labelled `ESC [n~', the function keys generate sequences like `ESC [11~', `ESC [12~' and so on. This matches the general behaviour of Digital's terminals. - In Linux mode, F6 to F12 behave just like the default mode, but F1 to F5 generate `ESC [[A' through to `ESC [[E'. This mimics the Linux virtual console. - In Xterm R6 mode, F5 to F12 behave like the default mode, but F1 to F4 generate `ESC OP' through to `ESC OS', which are the sequences produced by the top row of the _keypad_ on Digital's terminals. - In VT400 mode, all the function keys behave like the default mode, but the actual top row of the numeric keypad generates `ESC OP' through to `ESC OS'. - In VT100+ mode, the function keys generate `ESC OP' through to `ESC O[' - In SCO mode, the function keys F1 to F12 generate `ESC [M' through to `ESC [X'. Together with shift, they generate `ESC [Y' through to `ESC [j'. With control they generate `ESC [k' through to `ESC [v', and with shift and control together they generate `ESC [w' through to `ESC [{'. If you don't know what any of this means, you probably don't need to fiddle with it. 4.4.4 Controlling Application Cursor Keys mode Application Cursor Keys mode is a way for the server to change the control sequences sent by the arrow keys. In normal mode, the arrow keys send `ESC [A' through to `ESC [D'. In application mode, they send `ESC OA' through to `ESC OD'. Application Cursor Keys mode can be turned on and off by the server, depending on the application. PuTTY allows you to configure the initial state. You can also disable application cursor keys mode completely, using the `Features' configuration panel; see section 4.6.1. 4.4.5 Controlling Application Keypad mode Application Keypad mode is a way for the server to change the behaviour of the numeric keypad. In normal mode, the keypad behaves like a normal Windows keypad: with NumLock on, the number keys generate numbers, and with NumLock off they act like the arrow keys and Home, End etc. In application mode, all the keypad keys send special control sequences, _including_ Num Lock. Num Lock stops behaving like Num Lock and becomes another function key. Depending on which version of Windows you run, you may find the Num Lock light still flashes on and off every time you press Num Lock, even when application mode is active and Num Lock is acting like a function key. This is unavoidable. Application keypad mode can be turned on and off by the server, depending on the application. PuTTY allows you to configure the initial state. You can also disable application keypad mode completely, using the `Features' configuration panel; see section 4.6.1. 4.4.6 Using NetHack keypad mode PuTTY has a special mode for playing NetHack. You can enable it by selecting `NetHack' in the `Initial state of numeric keypad' control. In this mode, the numeric keypad keys 1-9 generate the NetHack movement commands (hjklyubn). The 5 key generates the `.' command (do nothing). In addition, pressing Shift or Ctrl with the keypad keys generate the Shift- or Ctrl-keys you would expect (e.g. keypad-7 generates `y', so Shift-keypad-7 generates `Y' and Ctrl-keypad-7 generates Ctrl-Y); these commands tell NetHack to keep moving you in the same direction until you encounter something interesting. For some reason, this feature only works properly when Num Lock is on. We don't know why. 4.4.7 Enabling a DEC-like Compose key DEC terminals have a Compose key, which provides an easy-to-remember way of typing accented characters. You press Compose and then type two more characters. The two characters are `combined' to produce an accented character. The choices of character are designed to be easy to remember; for example, composing `e' and ``' produces the `e-grave' character. If your keyboard has a Windows Application key, it acts as a Compose key in PuTTY. Alternatively, if you enable the `AltGr acts as Compose key' option, the AltGr key will become a Compose key. 4.4.8 `Control-Alt is different from AltGr' Some old keyboards do not have an AltGr key, which can make it difficult to type some characters. PuTTY can be configured to treat the key combination Ctrl + Left Alt the same way as the AltGr key. By default, this checkbox is checked, and the key combination Ctrl + Left Alt does something completely different. PuTTY's usual handling of the left Alt key is to prefix the Escape (Control-[) character to whatever character sequence the rest of the keypress would generate. For example, Alt-A generates Escape followed by `a'. So Alt-Ctrl-A would generate Escape, followed by Control-A. If you uncheck this box, Ctrl-Alt will become a synonym for AltGr, so you can use it to type extra graphic characters if your keyboard has any. (However, Ctrl-Alt will never act as a Compose key, regardless of the setting of `AltGr acts as Compose key' described in section 4.4.7.) 4.5 The Bell panel The Bell panel controls the terminal bell feature: the server's ability to cause PuTTY to beep at you. In the default configuration, when the server sends the character with ASCII code 7 (Control-G), PuTTY will play the Windows Default Beep sound. This is not always what you want the terminal bell feature to do; the Bell panel allows you to configure alternative actions. 4.5.1 `Set the style of bell' This control allows you to select various different actions to occur on a terminal bell: - Selecting `None' disables the bell completely. In this mode, the server can send as many Control-G characters as it likes and nothing at all will happen. - `Make default system alert sound' is the default setting. It causes the Windows `Default Beep' sound to be played. To change what this sound is, or to test it if nothing seems to be happening, use the Sound configurer in the Windows Control Panel. - `Visual bell' is a silent alternative to a beeping computer. In this mode, when the server sends a Control-G, the whole PuTTY window will flash white for a fraction of a second. - `Beep using the PC speaker' is self-explanatory. - `Play a custom sound file' allows you to specify a particular sound file to be used by PuTTY alone, or even by a particular individual PuTTY session. This allows you to distinguish your PuTTY beeps from any other beeps on the system. If you select this option, you will also need to enter the name of your sound file in the edit control `Custom sound file to play as a bell'. 4.5.2 `Taskbar/caption indication on bell' This feature controls what happens to the PuTTY window's entry in the Windows Taskbar if a bell occurs while the window does not have the input focus. In the default state (`Disabled') nothing unusual happens. If you select `Steady', then when a bell occurs and the window is not in focus, the window's Taskbar entry and its title bar will change colour to let you know that PuTTY session is asking for your attention. The change of colour will persist until you select the window, so you can leave several PuTTY windows minimised in your terminal, go away from your keyboard, and be sure not to have missed any important beeps when you get back. `Flashing' is even more eye-catching: the Taskbar entry will continuously flash on and off until you select the window. 4.5.3 `Control the bell overload behaviour' A common user error in a terminal session is to accidentally run the Unix command `cat' (or equivalent) on an inappropriate file type, such as an executable, image file, or ZIP file. This produces a huge stream of non-text characters sent to the terminal, which typically includes a lot of bell characters. As a result of this the terminal often doesn't stop beeping for ten minutes, and everybody else in the office gets annoyed. To try to avoid this behaviour, or any other cause of excessive beeping, PuTTY includes a bell overload management feature. In the default configuration, receiving more than five bell characters in a two-second period will cause the overload feature to activate. Once the overload feature is active, further bells will have no effect at all, so the rest of your binary file will be sent to the screen in silence. After a period of five seconds during which no further bells are received, the overload feature will turn itself off again and bells will be re-enabled. If you want this feature completely disabled, you can turn it off using the checkbox `Bell is temporarily disabled when over-used'. Alternatively, if you like the bell overload feature but don't agree with the settings, you can configure the details: how many bells constitute an overload, how short a time period they have to arrive in to do so, and how much silent time is required before the overload feature will deactivate itself. Bell overload mode is always deactivated by any keypress in the terminal. This means it can respond to large unexpected streams of data, but does not interfere with ordinary command-line activities that generate beeps (such as filename completion). 4.6 The Features panel PuTTY's terminal emulation is very highly featured, and can do a lot of things under remote server control. Some of these features can cause problems due to buggy or strangely configured server applications. The Features configuration panel allows you to disable some of PuTTY's more advanced terminal features, in case they cause trouble. 4.6.1 Disabling application keypad and cursor keys Application keypad mode (see section 4.4.5) and application cursor keys mode (see section 4.4.4) alter the behaviour of the keypad and cursor keys. Some applications enable these modes but then do not deal correctly with the modified keys. You can force these modes to be permanently disabled no matter what the server tries to do. 4.6.2 Disabling xterm-style mouse reporting PuTTY allows the server to send control codes that let it take over the mouse and use it for purposes other than copy and paste. Applications which use this feature include the text-mode web browser `links', the Usenet newsreader `trn' version 4, and the file manager `mc' (Midnight Commander). If you find this feature inconvenient, you can disable it using the `Disable xterm-style mouse reporting' control. With this box ticked, the mouse will _always_ do copy and paste in the normal way. Note that even if the application takes over the mouse, you can still manage PuTTY's copy and paste by holding down the Shift key while you select and paste, unless you have deliberately turned this feature off (see section 4.11.2). 4.6.3 Disabling remote terminal resizing PuTTY has the ability to change the terminal's size and position in response to commands from the server. If you find PuTTY is doing this unexpectedly or inconveniently, you can tell PuTTY not to respond to those server commands. 4.6.4 Disabling switching to the alternate screen Many terminals, including PuTTY, support an `alternate screen'. This is the same size as the ordinary terminal screen, but separate. Typically a screen-based program such as a text editor might switch the terminal to the alternate screen before starting up. Then at the end of the run, it switches back to the primary screen, and you see the screen contents just as they were before starting the editor. Some people prefer this not to happen. If you want your editor to run in the same screen as the rest of your terminal activity, you can disable the alternate screen feature completely. 4.6.5 Disabling remote window title changing PuTTY has the ability to change the window title in response to commands from the server. If you find PuTTY is doing this unexpectedly or inconveniently, you can tell PuTTY not to respond to those server commands. 4.6.6 Response to remote window title querying PuTTY can optionally provide the xterm service of allowing server applications to find out the local window title. This feature is disabled by default, but you can turn it on if you really want it. NOTE that this feature is a _potential security hazard_. If a malicious application can write data to your terminal (for example, if you merely `cat' a file owned by someone else on the server machine), it can change your window title (unless you have disabled this as mentioned in section 4.6.5) and then use this service to have the new window title sent back to the server as if typed at the keyboard. This allows an attacker to fake keypresses and potentially cause your server-side applications to do things you didn't want. Therefore this feature is disabled by default, and we recommend you do not set it to `Window title' unless you _really_ know what you are doing. There are three settings for this option: `None' PuTTY makes no response whatsoever to the relevant escape sequence. This may upset server-side software that is expecting some sort of response. `Empty string' PuTTY makes a well-formed response, but leaves it blank. Thus, server-side software that expects a response is kept happy, but an attacker cannot influence the response string. This is probably the setting you want if you have no better ideas. `Window title' PuTTY responds with the actual window title. This is dangerous for the reasons described above. 4.6.7 Disabling remote scrollback clearing PuTTY has the ability to clear the terminal's scrollback buffer in response to a command from the server. If you find PuTTY is doing this unexpectedly or inconveniently, you can tell PuTTY not to respond to that server command. 4.6.8 Disabling destructive backspace Normally, when PuTTY receives character 127 (^?) from the server, it will perform a `destructive backspace': move the cursor one space left and delete the character under it. This can apparently cause problems in some applications, so PuTTY provides the ability to configure character 127 to perform a normal backspace (without deleting a character) instead. 4.6.9 Disabling remote character set configuration PuTTY has the ability to change its character set configuration in response to commands from the server. Some programs send these commands unexpectedly or inconveniently. In particular, BitchX (an IRC client) seems to have a habit of reconfiguring the character set to something other than the user intended. If you find that accented characters are not showing up the way you expect them to, particularly if you're running BitchX, you could try disabling the remote character set configuration commands. 4.6.10 Disabling Arabic text shaping PuTTY supports shaping of Arabic text, which means that if your server sends text written in the basic Unicode Arabic alphabet then it will convert it to the correct display forms before printing it on the screen. If you are using full-screen software which was not expecting this to happen (especially if you are not an Arabic speaker and you unexpectedly find yourself dealing with Arabic text files in applications which are not Arabic-aware), you might find that the display becomes corrupted. By ticking this box, you can disable Arabic text shaping so that PuTTY displays precisely the characters it is told to display. You may also find you need to disable bidirectional text display; see section 4.6.11. 4.6.11 Disabling bidirectional text display PuTTY supports bidirectional text display, which means that if your server sends text written in a language which is usually displayed from right to left (such as Arabic or Hebrew) then PuTTY will automatically flip it round so that it is displayed in the right direction on the screen. If you are using full-screen software which was not expecting this to happen (especially if you are not an Arabic speaker and you unexpectedly find yourself dealing with Arabic text files in applications which are not Arabic-aware), you might find that the display becomes corrupted. By ticking this box, you can disable bidirectional text display, so that PuTTY displays text from left to right in all situations. You may also find you need to disable Arabic text shaping; see section 4.6.10. 4.7 The Window panel The Window configuration panel allows you to control aspects of the PuTTY window. 4.7.1 Setting the size of the PuTTY window The `Columns' and `Rows' boxes let you set the PuTTY window to a precise size. Of course you can also drag the window to a new size while a session is running. 4.7.2 What to do when the window is resized These options allow you to control what happens when the user tries to resize the PuTTY window using its window furniture. There are four options here: - `Change the number of rows and columns': the font size will not change. (This is the default.) - `Change the size of the font': the number of rows and columns in the terminal will stay the same, and the font size will change. - `Change font size when maximised': when the window is resized, the number of rows and columns will change, _except_ when the window is maximised (or restored), when the font size will change. (In this mode, holding down the Alt key while resizing will also cause the font size to change.) - `Forbid resizing completely': the terminal will refuse to be resized at all. 4.7.3 Controlling scrollback These options let you configure the way PuTTY keeps text after it scrolls off the top of the screen (see section 3.1.2). The `Lines of scrollback' box lets you configure how many lines of text PuTTY keeps. The `Display scrollbar' options allow you to hide the scrollbar (although you can still view the scrollback using the keyboard as described in section 3.1.2). You can separately configure whether the scrollbar is shown in full-screen mode and in normal modes. If you are viewing part of the scrollback when the server sends more text to PuTTY, the screen will revert to showing the current terminal contents. You can disable this behaviour by turning off `Reset scrollback on display activity'. You can also make the screen revert when you press a key, by turning on `Reset scrollback on keypress'. 4.7.4 `Push erased text into scrollback' When this option is enabled, the contents of the terminal screen will be pushed into the scrollback when a server-side application clears the screen, so that your scrollback will contain a better record of what was on your screen in the past. If the application switches to the alternate screen (see section 4.6.4 for more about this), then the contents of the primary screen will be visible in the scrollback until the application switches back again. This option is enabled by default. 4.8 The Appearance panel The Appearance configuration panel allows you to control aspects of the appearance of PuTTY's window. 4.8.1 Controlling the appearance of the cursor The `Cursor appearance' option lets you configure the cursor to be a block, an underline, or a vertical line. A block cursor becomes an empty box when the window loses focus; an underline or a vertical line becomes dotted. The `Cursor blinks' option makes the cursor blink on and off. This works in any of the cursor modes. 4.8.2 Controlling the font used in the terminal window This option allows you to choose what font, in what size, the PuTTY terminal window uses to display the text in the session. By default, you will be offered a choice from all the fixed-width fonts installed on the system, since VT100-style terminal handling expects a fixed-width font. If you tick the box marked `Allow selection of variable-pitch fonts', however, PuTTY will offer variable-width fonts as well: if you select one of these, the font will be coerced into fixed-size character cells, which will probably not look very good (but can work OK with some fonts). 4.8.3 `Hide mouse pointer when typing in window' If you enable this option, the mouse pointer will disappear if the PuTTY window is selected and you press a key. This way, it will not obscure any of the text in the window while you work in your session. As soon as you move the mouse, the pointer will reappear. This option is disabled by default, so the mouse pointer remains visible at all times. 4.8.4 Controlling the window border PuTTY allows you to configure the appearance of the window border to some extent. The checkbox marked `Sunken-edge border' changes the appearance of the window border to something more like a DOS box: the inside edge of the border is highlighted as if it sank down to meet the surface inside the window. This makes the border a little bit thicker as well. It's hard to describe well. Try it and see if you like it. You can also configure a completely blank gap between the text in the window and the border, using the `Gap between text and window edge' control. By default this is set at one pixel. You can reduce it to zero, or increase it further. 4.9 The Behaviour panel The Behaviour configuration panel allows you to control aspects of the behaviour of PuTTY's window. 4.9.1 Controlling the window title The `Window title' edit box allows you to set the title of the PuTTY window. By default the window title will contain the host name followed by `PuTTY', for example `server1.example.com - PuTTY'. If you want a different window title, this is where to set it. PuTTY allows the server to send `xterm' control sequences which modify the title of the window in mid-session (unless this is disabled - see section 4.6.5); the title string set here is therefore only the _initial_ window title. As well as the _window_ title, there is also an `xterm' sequence to modify the title of the window's _icon_. This makes sense in a windowing system where the window becomes an icon when minimised, such as Windows 3.1 or most X Window System setups; but in the Windows 95-like user interface it isn't as applicable. By default, PuTTY only uses the server-supplied _window_ title, and ignores the icon title entirely. If for some reason you want to see both titles, check the box marked `Separate window and icon titles'. If you do this, PuTTY's window title and Taskbar caption will change into the server-supplied icon title if you minimise the PuTTY window, and change back to the server-supplied window title if you restore it. (If the server has not bothered to supply a window or icon title, none of this will happen.) 4.9.2 `Warn before closing window' If you press the Close button in a PuTTY window that contains a running session, PuTTY will put up a warning window asking if you really meant to close the window. A window whose session has already terminated can always be closed without a warning. If you want to be able to close a window quickly, you can disable the `Warn before closing window' option. 4.9.3 `Window closes on ALT-F4' By default, pressing ALT-F4 causes the window to close (or a warning box to appear; see section 4.9.2). If you disable the `Window closes on ALT-F4' option, then pressing ALT-F4 will simply send a key sequence to the server. 4.9.4 `System menu appears on ALT-Space' If this option is enabled, then pressing ALT-Space will bring up the PuTTY window's menu, like clicking on the top left corner. If it is disabled, then pressing ALT-Space will just send `ESC SPACE' to the server. Some accessibility programs for Windows may need this option enabling to be able to control PuTTY's window successfully. For instance, Dragon NaturallySpeaking requires it both to open the system menu via voice, and to close, minimise, maximise and restore the window. 4.9.5 `System menu appears on Alt alone' If this option is enabled, then pressing and releasing ALT will bring up the PuTTY window's menu, like clicking on the top left corner. If it is disabled, then pressing and releasing ALT will have no effect. 4.9.6 `Ensure window is always on top' If this option is enabled, the PuTTY window will stay on top of all other windows. 4.9.7 `Full screen on Alt-Enter' If this option is enabled, then pressing Alt-Enter will cause the PuTTY window to become full-screen. Pressing Alt-Enter again will restore the previous window size. The full-screen feature is also available from the System menu, even when it is configured not to be available on the Alt-Enter key. See section 3.1.3.7. 4.10 The Translation panel The Translation configuration panel allows you to control the translation between the character set understood by the server and the character set understood by PuTTY. 4.10.1 Controlling character set translation During an interactive session, PuTTY receives a stream of 8-bit bytes from the server, and in order to display them on the screen it 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 `Remote character set' option lets you select one. By default PuTTY will use the UTF-8 encoding of 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: - The ISO-8859 series are all standard character sets that include various accented characters appropriate for different sets of languages. - The 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. - If you want the old IBM PC character set with block graphics and line-drawing characters, you can select `CP437'. If you need support for a numeric code page which is not listed in the drop-down list, such as code page 866, then you can try entering its name manually (`CP866' for example) in the list box. If the underlying version of Windows has the appropriate translation table installed, PuTTY will use it. 4.10.2 `Treat CJK ambiguous characters as wide' There are some Unicode characters whose width is not well-defined. In most contexts, such characters should be treated as single- width for the purposes of wrapping and so on; however, in some CJK contexts, they are better treated as double-width for historical reasons, and some server-side applications may expect them to be displayed as such. Setting this option will cause PuTTY to take the double-width interpretation. If you use legacy CJK applications, and you find your lines are wrapping in the wrong places, or you are having other display problems, you might want to play with this setting. This option only has any effect in UTF-8 mode (see section 4.10.1). 4.10.3 `Caps Lock acts as Cyrillic switch' This feature allows you to switch between a US/UK keyboard layout and a Cyrillic keyboard layout by using the Caps Lock key, if you need to type (for example) Russian and English side by side in the same document. Currently this feature is not expected to work properly if your native keyboard layout is not US or UK. 4.10.4 Controlling display of line-drawing characters VT100-series terminals allow the server to send control sequences that shift temporarily into a separate character set for drawing simple lines and boxes. However, there are a variety of ways in which PuTTY can attempt to find appropriate characters, and the right one to use depends on the locally configured font. In general you should probably try lots of options until you find one that your particular font supports. - `Use Unicode line drawing code points' tries to use the box characters that are present in Unicode. For good Unicode- supporting fonts this is probably the most reliable and functional option. - `Poor man's line drawing' assumes that the font _cannot_ generate the line and box characters at all, so it will use the `+', `-' and `|' characters to draw approximations to boxes. You should use this option if none of the other options works. - `Font has XWindows encoding' is for use with fonts that have a special encoding, where the lowest 32 character positions (below the ASCII printable range) contain the line-drawing characters. This is unlikely to be the case with any standard Windows font; it will probably only apply to custom-built fonts or fonts that have been automatically converted from the X Window System. - `Use font in both ANSI and OEM modes' tries to use the same font in two different character sets, to obtain a wider range of characters. This doesn't always work; some fonts claim to be a different size depending on which character set you try to use. - `Use font in OEM mode only' is more reliable than that, but can miss out other characters from the main character set. 4.10.5 Controlling copy and paste of line drawing characters By default, when you copy and paste a piece of the PuTTY screen that contains VT100 line and box drawing characters, PuTTY will paste them in the form they appear on the screen: either Unicode line drawing code points, or the `poor man's' line-drawing characters `+', `-' and `|'. The checkbox `Copy and paste VT100 line drawing chars as lqqqk' disables this feature, so line-drawing characters will be pasted as the ASCII characters that were printed to produce them. This will typically mean they come out mostly as `q' and `x', with a scattering of `jklmntuvw' at the corners. This might be useful if you were trying to recreate the same box layout in another program, for example. Note that this option only applies to line-drawing characters which _were_ printed by using the VT100 mechanism. Line-drawing characters that were received as Unicode code points will paste as Unicode always. 4.10.6 Combining VT100 line-drawing with UTF-8 If PuTTY is configured to treat data from the server as encoded in UTF-8, then by default it disables the older VT100-style system of control sequences that cause the lower-case letters to be temporarily replaced by line drawing characters. The rationale is that in UTF-8 mode you don't need those control sequences anyway, because all the line-drawing characters they access are available as Unicode characters already, so there's no need for applications to put the terminal into a special state to get at them. Also, it removes a risk of the terminal _accidentally_ getting into that state: if you accidentally write uncontrolled binary data to a non-UTF-8 terminal, it can be surprisingly common to find that your next shell prompt appears as a sequence of line-drawing characters and then you have to remember or look up how to get out of that mode. So by default, UTF-8 mode simply doesn't _have_ a confusing mode like that to get into, accidentally or on purpose. However, not all applications will see it that way. Even UTF-8 terminal users will still sometimes have to run software that tries to print line-drawing characters in the old-fashioned way. So the configuration option `Enable VT100 line drawing even in UTF-8 mode' puts PuTTY into a hybrid mode in which it understands the VT100- style control sequences that change the meaning of the ASCII lower case letters, _and_ understands UTF-8. 4.11 The Selection panel The Selection panel allows you to control the way copy and paste work in the PuTTY window. 4.11.1 Changing the actions of the mouse buttons PuTTY's copy and paste mechanism is by default modelled on the Unix `xterm' application. The X Window System uses a three-button mouse, and the convention in that system is that the left button selects, the right button extends an existing selection, and the middle button pastes. Windows often only has two mouse buttons, so when run on Windows, PuTTY is configurable. In PuTTY's default configuration (`Compromise'), the _right_ button pastes, and the _middle_ button (if you have one) extends a selection. If you have a three-button mouse and you are already used to the `xterm' arrangement, you can select it using the `Action of mouse buttons' control. Alternatively, with the `Windows' option selected, the middle button extends, and the right button brings up a context menu (on which one of the options is `Paste'). (This context menu is always available by holding down Ctrl and right-clicking, regardless of the setting of this option.) (When PuTTY iself is running on Unix, it follows the X Window System convention.) 4.11.2 `Shift overrides application's use of mouse' PuTTY allows the server to send control codes that let it take over the mouse and use it for purposes other than copy and paste. Applications which use this feature include the text-mode web browser `links', the Usenet newsreader `trn' version 4, and the file manager `mc' (Midnight Commander). When running one of these applications, pressing the mouse buttons no longer performs copy and paste. If you do need to copy and paste, you can still do so if you hold down Shift while you do your mouse clicks. However, it is possible in theory for applications to even detect and make use of Shift + mouse clicks. We don't know of any applications that do this, but in case someone ever writes one, unchecking the `Shift overrides application's use of mouse' checkbox will cause Shift + mouse clicks to go to the server as well (so that mouse-driven copy and paste will be completely disabled). If you want to prevent the application from taking over the mouse at all, you can do this using the Features control panel; see section 4.6.2. 4.11.3 Default selection mode As described in section 3.1.1, PuTTY has two modes of selecting text to be copied to the clipboard. In the default mode (`Normal'), dragging the mouse from point A to point B selects to the end of the line containing A, all the lines in between, and from the very beginning of the line containing B. In the other mode (`Rectangular block'), dragging the mouse between two points defines a rectangle, and everything within that rectangle is copied. Normally, you have to hold down Alt while dragging the mouse to select a rectangular block. Using the `Default selection mode' control, you can set rectangular selection as the default, and then you have to hold down Alt to get the _normal_ behaviour. 4.11.4 Assigning copy and paste actions to clipboards Here you can configure which clipboard(s) are written or read by PuTTY's various copy and paste actions. Most platforms, including Windows, have a single system clipboard. On these platforms, PuTTY provides a second clipboard-like facility by permitting you to paste the text you last selected in _this window_, whether or not it is currently also in the system clipboard. This is not enabled by default. The X Window System (which underlies most Unix graphical interfaces) provides multiple clipboards (or `selections'), and many applications support more than one of them by a different user interface mechanism. When PuTTY itself is running on Unix, it has more configurability relating to these selections. The two most commonly used selections are called `PRIMARY' and `CLIPBOARD'; in applications supporting both, the usual behaviour is that PRIMARY is used by mouse-only actions (selecting text automatically copies it to PRIMARY, and middle-clicking pastes from PRIMARY), whereas CLIPBOARD is used by explicit Copy and Paste menu items or keypresses such as Ctrl-C and Ctrl-V. 4.11.4.1 `Auto-copy selected text' The checkbox `Auto-copy selected text to system clipboard' controls whether or not selecting text in the PuTTY terminal window automatically has the side effect of copying it to the system clipboard, without requiring a separate user interface action. On X, the wording of this option is changed slightly so that `CLIPBOARD' is mentioned in place of the `system clipboard'. Text selected in the terminal window will _always_ be automatically placed in the PRIMARY selection, as is conventional, but if you tick this box, it will _also_ be placed in `CLIPBOARD' at the same time. 4.11.4.2 Choosing a clipboard for UI actions PuTTY has three user-interface actions which can be configured to paste into the terminal (not counting menu items). You can click whichever mouse button (if any) is configured to paste (see section 4.11.1); you can press Shift-Ins; or you can press Ctrl-Shift-V, although that action is not enabled by default. You can configure which of the available clipboards each of these actions pastes from (including turning the paste action off completely). On platforms with a single system clipboard (such as Windows), the available options are to paste from that clipboard or to paste from PuTTY's internal memory of the last selected text within that window. On X, the standard options are CLIPBOARD or PRIMARY. (PRIMARY is conceptually similar in that it _also_ refers to the last selected text - just across all applications instead of just this window.) The two keyboard options each come with a corresponding key to copy _to_ the same clipboard. Whatever you configure Shift-Ins to paste from, Ctrl-Ins will copy to the same location; similarly, Ctrl- Shift-C will copy to whatever Ctrl-Shift-V pastes from. On X, you can also enter a selection name of your choice. For example, there is a rarely-used standard selection called `SECONDARY', which Emacs (for example) can work with if you hold down the Meta key while dragging to select or clicking to paste; if you configure a PuTTY keyboard action to access this clipboard, then you can interoperate with other applications' use of it. Another thing you could do would be to invent a clipboard name yourself, to create a special clipboard shared _only_ between instances of PuTTY, or between just instances configured in that particular way. 4.11.5 `Permit control characters in pasted text' It is possible for the clipboard to contain not just text (with newlines and tabs) but also control characters such as ESC which could have surprising effects if pasted into a terminal session, depending on what program is running on the server side. Copying text from a mischievous web page could put such characters onto the clipboard. By default, PuTTY filters out the more unusual control characters, only letting through the more obvious text-formatting characters (newlines, tab, backspace, and DEL). Setting this option stops this filtering; on paste, any character on the clipboard is sent to the session uncensored. This might be useful if you are deliberately using control character pasting as a simple form of scripting, for instance. 4.12 The Copy panel The Copy configuration panel controls behaviour specifically related to copying from the terminal window to the clipboard. 4.12.1 Character classes PuTTY will select a word at a time in the terminal window if you double-click to begin the drag. This section allows you to control precisely what is considered to be a word. Each character is given a _class_, which is a small number (typically 0, 1 or 2). PuTTY considers a single word to be any number of adjacent characters in the same class. So by modifying the assignment of characters to classes, you can modify the word-by-word selection behaviour. In the default configuration, the character classes are: - Class 0 contains white space and control characters. - Class 1 contains most punctuation. - Class 2 contains letters, numbers and a few pieces of punctuation (the double quote, minus sign, period, forward slash and underscore). So, for example, if you assign the `@' symbol into character class 2, you will be able to select an e-mail address with just a double click. In order to adjust these assignments, you start by selecting a group of characters in the list box. Then enter a class number in the edit box below, and press the `Set' button. This mechanism currently only covers ASCII characters, because it isn't feasible to expand the list to cover the whole of Unicode. Character class definitions can be modified by control sequences sent by the server. This configuration option controls the _default_ state, which will be restored when you reset the terminal (see section 3.1.3.6). However, if you modify this option in mid-session using `Change Settings', it will take effect immediately. 4.12.2 Copying in Rich Text Format If you enable `Copy to clipboard in RTF as well as plain text', PuTTY will write formatting information to the clipboard as well as the actual text you copy. The effect of this is that if you paste into (say) a word processor, the text will appear in the word processor in the same font, colour, and style (e.g. bold, underline) PuTTY was using to display it. This option can easily be inconvenient, so by default it is disabled. 4.13 The Colours panel The Colours panel allows you to control PuTTY's use of colour. 4.13.1 `Allow terminal to specify ANSI colours' This option is enabled by default. If it is disabled, PuTTY will ignore any control sequences sent by the server to request coloured text. If you have a particularly garish application, you might want to turn this option off and make PuTTY only use the default foreground and background colours. 4.13.2 `Allow terminal to use xterm 256-colour mode' This option is enabled by default. If it is disabled, PuTTY will ignore any control sequences sent by the server which use the extended 256-colour mode supported by recent versions of xterm. If you have an application which is supposed to use 256-colour mode and it isn't working, you may find you need to tell your server that your terminal supports 256 colours. On Unix, you do this by ensuring that the setting of TERM describes a 256-colour-capable terminal. You can check this using a command such as `infocmp': $ infocmp | grep colors colors#256, cols#80, it#8, lines#24, pairs#256, If you do not see `colors#256' in the output, you may need to change your terminal setting. On modern Linux machines, you could try `xterm-256color'. 4.13.3 `Allow terminal to use 24-bit colour' This option is enabled by default. If it is disabled, PuTTY will ignore any control sequences sent by the server which use the control sequences supported by modern terminals to specify arbitrary 24-bit RGB colour value. 4.13.4 `Indicate bolded text by changing...' When the server sends a control sequence indicating that some text should be displayed in bold, PuTTY can handle this in several ways. It can either change the font for a bold version, or use the same font in a brighter colour, or it can do both (brighten the colour _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 `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 `Both', the font and the colour will both change. Some applications rely on `bold black' being distinguishable from a black background; if you choose `The font', their text may become invisible. 4.13.5 `Attempt to use logical palettes' Logical palettes are a mechanism by which a Windows application running on an 8-bit colour display can select precisely the colours it wants instead of going with the Windows standard defaults. If you are not getting the colours you ask for on an 8-bit display, you can try enabling this option. However, be warned that it's never worked very well. 4.13.6 `Use system colours' Enabling this option will cause PuTTY to ignore the configured colours for `Default Background/Foreground' and `Cursor Colour/Text' (see section 4.13.7), instead going with the system-wide defaults. Note that non-bold and bold text will be the same colour if this option is enabled. You might want to change to indicating bold text by font changes (see section 4.13.4). 4.13.7 Adjusting the colours in the terminal window The main colour control allows you to specify exactly what colours things should be displayed in. To modify one of the PuTTY colours, use the list box to select which colour you want to modify. The RGB values for that colour will appear on the right-hand side of the list box. Now, if you press the `Modify' button, you will be presented with a colour selector, in which you can choose a new colour to go in place of the old one. (You may also edit the RGB values directly in the edit boxes, if you wish; each value is an integer from 0 to 255.) PuTTY allows you to set the cursor colour, the default foreground and background, and the precise shades of all the ANSI configurable colours (black, red, green, yellow, blue, magenta, cyan, and white). You can also modify the precise shades used for the bold versions of these colours; these are used to display bold text if you have chosen to indicate that by colour (see section 4.13.4), and can also be used if the server asks specifically to use them. (Note that `Default Bold Background' is _not_ the background colour used for bold text; it is only used if the server specifically asks for a bold background.) 4.14 The Connection panel The Connection panel allows you to configure options that apply to more than one type of connection. 4.14.1 Using keepalives to prevent disconnection If you find your sessions are closing unexpectedly (most often with `Connection reset by peer') after they have been idle for a while, you might want to try using this option. Some network routers and firewalls need to keep track of all connections through them. Usually, these firewalls will assume a connection is dead if no data is transferred in either direction after a certain time interval. This can cause PuTTY sessions to be unexpectedly closed by the firewall if no traffic is seen in the session for some time. The keepalive option (`Seconds between keepalives') allows you to configure PuTTY to send data through the session at regular intervals, in a way that does not disrupt the actual terminal session. If you find your firewall is cutting idle connections off, you can try entering a non-zero value in this field. The value is measured in seconds; so, for example, if your firewall cuts connections off after ten minutes then you might want to enter 300 seconds (5 minutes) in the box. Note that keepalives are not always helpful. They help if you have a firewall which drops your connection after an idle period; but if the network between you and the server suffers from breaks in connectivity then keepalives can actually make things worse. If a session is idle, and connectivity is temporarily lost between the endpoints, but the connectivity is restored before either side tries to send anything, then there will be no problem - neither endpoint will notice that anything was wrong. However, if one side does send something during the break, it will repeatedly try to re-send, and eventually give up and abandon the connection. Then when connectivity is restored, the other side will find that the first side doesn't believe there is an open connection any more. Keepalives can make this sort of problem worse, because they increase the probability that PuTTY will attempt to send data during a break in connectivity. (Other types of periodic network activity can cause this behaviour; in particular, SSH-2 re-keys can have this effect. See section 4.18.2.) Therefore, you might find that keepalives help connection loss, or you might find they make it worse, depending on what _kind_ of network problems you have between you and the server. Keepalives are only supported in Telnet and SSH; the Rlogin, SUPDUP, and Raw protocols offer no way of implementing them. (For an alternative, see section 4.14.3.) Note that if you are using SSH-1 and the server has a bug that makes it unable to deal with SSH-1 ignore messages (see section 4.26.11), enabling keepalives will have no effect. 4.14.2 `Disable Nagle's algorithm' Nagle's algorithm is a detail of TCP/IP implementations that tries to minimise the number of small data packets sent down a network connection. With Nagle's algorithm enabled, PuTTY's bandwidth usage will be slightly more efficient; with it disabled, you may find you get a faster response to your keystrokes when connecting to some types of server. The Nagle algorithm is disabled by default for interactive connections. 4.14.3 `Enable TCP keepalives' _NOTE:_ TCP keepalives should not be confused with the application- level keepalives described in section 4.14.1. If in doubt, you probably want application-level keepalives; TCP keepalives are provided for completeness. The idea of TCP keepalives is similar to application-level keepalives, and the same caveats apply. The main differences are: - TCP keepalives are available on _all_ network connection types, including Raw, Rlogin, and SUPDUP. - The interval between TCP keepalives is usually much longer, typically two hours; this is set by the operating system, and cannot be configured within PuTTY. - If the operating system does not receive a response to a keepalive, it may send out more in quick succession and terminate the connection if no response is received. TCP keepalives may be more useful for ensuring that half-open connections are terminated than for keeping a connection alive. TCP keepalives are disabled by default. 4.14.4 `Internet protocol version' This option allows the user to select between the old and new Internet protocols and addressing schemes (IPv4 and IPv6). The selected protocol will be used for most outgoing network connections (including connections to proxies); however, tunnels have their own configuration, for which see section 4.25.2. The default setting is `Auto', which means PuTTY will do something sensible and try to guess which protocol you wanted. (If you specify a literal Internet address, it will use whichever protocol that address implies. If you provide a hostname, it will see what kinds of address exist for that hostname; it will use IPv6 if there is an IPv6 address available, and fall back to IPv4 if not.) If you need to force PuTTY to use a particular protocol, you can explicitly set this to `IPv4' or `IPv6'. 4.14.5 `Logical name of remote host' This allows you to tell PuTTY that the host it will really end up connecting to is different from where it thinks it is making a network connection. You might use this, for instance, if you had set up an SSH port forwarding in one PuTTY session so that connections to some arbitrary port (say, localhost port 10022) were forwarded to a second machine's SSH port (say, foovax port 22), and then started a second PuTTY connecting to the forwarded port. In normal usage, the second PuTTY will access the host key cache under the host name and port it actually connected to (i.e. localhost port 10022 in this example). Using the logical host name option, however, you can configure the second PuTTY to cache the host key under the name of the host _you_ know that it's _really_ going to end up talking to (here `foovax'). This can be useful if you expect to connect to the same actual server through many different channels (perhaps because your port forwarding arrangements keep changing): by consistently setting the logical host name, you can arrange that PuTTY will not keep asking you to reconfirm its host key. Conversely, if you expect to use the same local port number for port forwardings to lots of different servers, you probably didn't want any particular server's host key cached under that local port number. (For this latter case, you could instead explicitly configure host keys in the relevant sessions; see section 4.19.3.) If you just enter a host name for this option, PuTTY will cache the SSH host key under the default SSH port for that host, irrespective of the port you really connected to (since the typical scenario is like the above example: you connect to a silly real port number and your connection ends up forwarded to the normal port-22 SSH server of some other machine). To override this, you can append a port number to the logical host name, separated by a colon. E.g. entering `foovax:2200' as the logical host name will cause the host key to be cached as if you had connected to port 2200 of `foovax'. If you provide a host name using this option, it is also displayed in other locations which contain the remote host name, such as the default window title and the default SSH password prompt. This reflects the fact that this is the host you're _really_ connecting to, which is more important than the mere means you happen to be using to contact that host. (This applies even if you're using a protocol other than SSH.) 4.15 The Data panel The Data panel allows you to configure various pieces of data which can be sent to the server to affect your connection at the far end. Each option on this panel applies to more than one protocol. Options which apply to only one protocol appear on that protocol's configuration panels. 4.15.1 `Auto-login username' All three of the SSH, Telnet, and Rlogin protocols allow you to specify what user name you want to log in as, without having to type it explicitly every time. (Some Telnet servers don't support this.) In this box you can type that user name. 4.15.2 Use of system username When the previous box (section 4.15.1) is left blank, by default, PuTTY will prompt for a username at the time you make a connection. In some environments, such as the networks of large organisations implementing single sign-on, a more sensible default may be to use the name of the user logged in to the local operating system (if any); this is particularly likely to be useful with GSSAPI key exchange and user authentication (see section 4.22 and section 4.18.1.1). This control allows you to change the default behaviour. The current system username is displayed in the dialog as a convenience. It is not saved in the configuration; if a saved session is later used by a different user, that user's name will be used. 4.15.3 `Terminal-type string' Most servers you might connect to with PuTTY are designed to be connected to from lots of different types of terminal. In order to send the right control sequences to each one, the server will need to know what type of terminal it is dealing with. Therefore, each of the SSH, Telnet, and Rlogin protocols allow a text string to be sent down the connection describing the terminal. On a Unix server, this selects an entry from the `termcap' or `terminfo' database that tells applications what control sequences to send to the terminal, and what character sequences to expect the keyboard to generate. PuTTY attempts to emulate the Unix `xterm' program, and by default it reflects this by sending `xterm' as a terminal-type string. If you find this is not doing what you want - perhaps the remote system reports `Unknown terminal type' - you could try setting this to something different, such as `vt220'. If you're not sure whether a problem is due to the terminal type setting or not, you probably need to consult the manual for your application or your server. 4.15.4 `Terminal speeds' The Telnet, Rlogin, and SSH protocols allow the client to specify terminal speeds to the server. This parameter does _not_ affect the actual speed of the connection, which is always `as fast as possible'; it is just a hint that is sometimes used by server software to modify its behaviour. For instance, if a slow speed is indicated, the server may switch to a less bandwidth-hungry display mode. The value is usually meaningless in a network environment, but PuTTY lets you configure it, in case you find the server is reacting badly to the default value. The format is a pair of numbers separated by a comma, for instance, `38400,38400'. The first number represents the output speed (_from_ the server) in bits per second, and the second is the input speed (_to_ the server). (Only the first is used in the Rlogin protocol.) This option has no effect on Raw connections. 4.15.5 Setting environment variables on the server The Telnet protocol provides a means for the client to pass environment variables to the server. Many Telnet servers have stopped supporting this feature due to security flaws, but PuTTY still supports it for the benefit of any servers which have found other ways around the security problems than just disabling the whole mechanism. Version 2 of the SSH protocol also provides a similar mechanism, which is easier to implement without security flaws. Newer SSH-2 servers are more likely to support it than older ones. This configuration data is not used in the SSH-1, rlogin or raw protocols. To add an environment variable to the list transmitted down the connection, you enter the variable name in the `Variable' box, enter its value in the `Value' box, and press the `Add' button. To remove one from the list, select it in the list box and press `Remove'. 4.16 The Proxy panel The Proxy panel allows you to configure PuTTY to use various types of proxy in order to make its network connections. The settings in this panel affect the primary network connection forming your PuTTY session, and also any extra connections made as a result of SSH port forwarding (see section 3.5). Note that unlike some software (such as web browsers), PuTTY does not attempt to automatically determine whether to use a proxy and (if so) which one to use for a given destination. If you need to use a proxy, it must always be explicitly configured. 4.16.1 Setting the proxy type The `Proxy type' radio buttons allow you to configure what type of proxy you want PuTTY to use for its network connections. The default setting is `None'; in this mode no proxy is used for any connection. - Selecting `HTTP' allows you to proxy your connections through a web server supporting the HTTP CONNECT command, as documented in RFC 2817. - Selecting `SOCKS 4' or `SOCKS 5' allows you to proxy your connections through a SOCKS server. - Many firewalls implement a less formal type of proxy in which a user can make a Telnet connection directly to the firewall machine and enter a command such as `connect myhost.com 22' to connect through to an external host. Selecting `Telnet' allows you to tell PuTTY to use this type of proxy. - Selecting `Local' allows you to specify an arbitrary command on the local machine to act as a proxy. When the session is started, instead of creating a TCP connection, PuTTY runs the command (specified in section 4.16.5), and uses its standard input and output streams. This could be used, for instance, to talk to some kind of network proxy that PuTTY does not natively support; or you could tunnel a connection over something other than TCP/IP entirely. If you want your local proxy command to make a secondary SSH connection to a proxy host and then tunnel the primary connection over that, you might well want the `-nc' command-line option in Plink. See section 3.11.3.14 for more information. You can also enable this mode on the command line; see section 3.11.3.26. 4.16.2 Excluding parts of the network from proxying Typically you will only need to use a proxy to connect to non-local parts of your network; for example, your proxy might be required for connections outside your company's internal network. In the `Exclude Hosts/IPs' box you can enter ranges of IP addresses, or ranges of DNS names, for which PuTTY will avoid using the proxy and make a direct connection instead. The `Exclude Hosts/IPs' box may contain more than one exclusion range, separated by commas. Each range can be an IP address or a DNS name, with a `*' character allowing wildcards. For example: *.example.com This excludes any host with a name ending in `.example.com' from proxying. 192.168.88.* This excludes any host with an IP address starting with 192.168.88 from proxying. 192.168.88.*,*.example.com This excludes both of the above ranges at once. Connections to the local host (the host name `localhost', and any loopback IP address) are never proxied, even if the proxy exclude list does not explicitly contain them. It is very unlikely that this behaviour would ever cause problems, but if it does you can change it by enabling `Consider proxying local host connections'. Note that if you are doing DNS at the proxy (see section 4.16.3), you should make sure that your proxy exclusion settings do not depend on knowing the IP address of a host. If the name is passed on to the proxy without PuTTY looking it up, it will never know the IP address and cannot check it against your list. 4.16.3 Name resolution when using a proxy If you are using a proxy to access a private network, it can make a difference whether DNS name resolution is performed by PuTTY itself (on the client machine) or performed by the proxy. The `Do DNS name lookup at proxy end' configuration option allows you to control this. If you set it to `No', PuTTY will always do its own DNS, and will always pass an IP address to the proxy. If you set it to `Yes', PuTTY will always pass host names straight to the proxy without trying to look them up first. If you set this option to `Auto' (the default), PuTTY will do something it considers appropriate for each type of proxy. Telnet, HTTP, and SOCKS5 proxies will have host names passed straight to them; SOCKS4 proxies will not. Note that if you are doing DNS at the proxy, you should make sure that your proxy exclusion settings (see section 4.16.2) do not depend on knowing the IP address of a host. If the name is passed on to the proxy without PuTTY looking it up, it will never know the IP address and cannot check it against your list. The original SOCKS 4 protocol does not support proxy-side DNS. There is a protocol extension (SOCKS 4A) which does support it, but not all SOCKS 4 servers provide this extension. If you enable proxy DNS and your SOCKS 4 server cannot deal with it, this might be why. 4.16.4 Username and password If your proxy requires authentication, you can enter a username and a password in the `Username' and `Password' boxes. Note that if you save your session, the proxy password will be saved in plain text, so anyone who can access your PuTTY configuration data will be able to discover it. Authentication is not fully supported for all forms of proxy: - Username and password authentication is supported for HTTP proxies and SOCKS 5 proxies. - With SOCKS 5, authentication is via CHAP if the proxy supports it (this is not supported in PuTTYtel); otherwise the password is sent to the proxy in plain text. - With HTTP proxying, the only currently supported authentication method is `basic', where the password is sent to the proxy in plain text. - SOCKS 4 can use the `Username' field, but does not support passwords. - You can specify a way to include a username and password in the Telnet/Local proxy command (see section 4.16.5). 4.16.5 Specifying the Telnet or Local proxy command If you are using the Telnet proxy type, the usual command required by the firewall's Telnet server is `connect', followed by a host name and a port number. If your proxy needs a different command, you can enter an alternative here. If you are using the Local proxy type, the local command to run is specified here. In this string, you can use `\n' to represent a new-line, `\r' to represent a carriage return, `\t' to represent a tab character, and `\x' followed by two hex digits to represent any other character. `\\' is used to encode the `\' character itself. Also, the special strings `%host' and `%port' will be replaced by the host name and port number you want to connect to. The strings `%user' and `%pass' will be replaced by the proxy username and password you specify. The strings `%proxyhost' and `%proxyport' will be replaced by the host details specified on the _Proxy_ panel, if any (this is most likely to be useful for the Local proxy type). To get a literal `%' sign, enter `%%'. If a Telnet proxy server prompts for a username and password before commands can be sent, you can use a command such as: %user\n%pass\nconnect %host %port\n This will send your username and password as the first two lines to the proxy, followed by a command to connect to the desired host and port. Note that if you do not include the `%user' or `%pass' tokens in the Telnet command, then the `Username' and `Password' configuration fields will be ignored. 4.16.6 Controlling proxy logging Often the proxy interaction has its own diagnostic output; this is particularly the case for local proxy commands. The setting `Print proxy diagnostics in the terminal window' lets you control how much of the proxy's diagnostics are printed to the main terminal window, along with output from your main session. By default (`No'), proxy diagnostics are only sent to the Event Log; with `Yes' they are also printed to the terminal, where they may get mixed up with your main session. `Only until session starts' is a compromise; proxy messages will go to the terminal window until the main session is deemed to have started (in a protocol-dependent way), which is when they're most likely to be interesting; any further proxy-related messages during the session will only go to the Event Log. 4.17 The SSH panel The SSH panel allows you to configure options that only apply to SSH sessions. 4.17.1 Executing a specific command on the server In SSH, you don't have to run a general shell session on the server. Instead, you can choose to run a single specific command (such as a mail user agent, for example). If you want to do this, enter the command in the `Remote command' box. Note that most servers will close the session after executing the command. 4.17.2 `Don't start a shell or command at all' If you tick this box, PuTTY will not attempt to run a shell or command after connecting to the remote server. You might want to use this option if you are only using the SSH connection for port forwarding, and your user account on the server does not have the ability to run a shell. This feature is only available in SSH protocol version 2 (since the version 1 protocol assumes you will always want to run a shell). This feature can also be enabled using the `-N' command-line option; see section 3.11.3.13. If you use this feature in Plink, you will not be able to terminate the Plink process by any graceful means; the only way to kill it will be by pressing Control-C or sending a kill signal from another program. 4.17.3 `Enable compression' This enables data compression in the SSH connection: data sent by the server is compressed before sending, and decompressed at the client end. Likewise, data sent by PuTTY to the server is compressed first and the server decompresses it at the other end. This can help make the most of a low-bandwidth connection. 4.17.4 `SSH protocol version' This allows you to select whether to use SSH protocol version 2 or the older version 1. You should normally leave this at the default of `2'. As well as having fewer features, the older SSH-1 protocol is no longer developed, has many known cryptographic weaknesses, and is generally not considered to be secure. PuTTY's protocol 1 implementation is provided mainly for compatibility, and is no longer being enhanced. If a server offers both versions, prefer `2'. If you have some server or piece of equipment that only talks SSH-1, select `1' here, and do not treat the resulting connection as secure. PuTTY will not automatically fall back to the other version of the protocol if the server turns out not to match your selection here; instead, it will put up an error message and abort the connection. This prevents an active attacker downgrading an intended SSH-2 connection to SSH-1. 4.17.5 Sharing an SSH connection between PuTTY tools 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 `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 `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 `upstream', which means that it is the one managing the real SSH connection. All subsequent PuTTYs which reuse the connection are referred to as `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, _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 `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 _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 `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 _never_ act as upstreams. It is possible to test programmatically for the existence of a live upstream using Plink. See section 7.2.3.4. 4.18 The Kex panel The Kex panel (short for `key exchange') allows you to configure options related to SSH-2 key exchange. Key exchange occurs at the start of an SSH connection (and occasionally thereafter); it establishes a shared secret that is used as the basis for all of SSH's security features. It is therefore very important for the security of the connection that the key exchange is secure. Key exchange is a cryptographically intensive process; if either the client or the server is a relatively slow machine, the slower methods may take several tens of seconds to complete. If connection startup is too slow, or the connection hangs periodically, you may want to try changing these settings. If you don't understand what any of this means, it's safe to leave these settings alone. This entire panel is only relevant to SSH protocol version 2; none of these settings affect SSH-1 at all. 4.18.1 Key exchange algorithm selection PuTTY supports a variety of SSH-2 key exchange methods, and allows you to choose which one you prefer to use; configuration is similar to cipher selection (see section 4.20). PuTTY currently supports the following key exchange methods: - `ECDH': elliptic curve Diffie-Hellman key exchange. - `Group 14': Diffie-Hellman key exchange with a well-known 2048- bit group. - `Group 1': Diffie-Hellman key exchange with a well-known 1024- bit group. We no longer recommend using this method, and it's not used by default in new installations; however, it may be the only method supported by very old server software. - `Group exchange': with this method, instead of using a fixed group, PuTTY requests that the server suggest a group to use for key exchange; the server can avoid groups known to be weak, and possibly invent new ones over time, without any changes required to PuTTY's configuration. We recommend use of this method instead of the well-known groups, if possible. - `RSA key exchange': this requires much less computational effort on the part of the client, and somewhat less on the part of the server, than Diffie-Hellman key exchange. - `GSSAPI key exchange': see section 4.18.1.1. If the first algorithm PuTTY finds is below the `warn below here' line, you will see a warning box when you make the connection, similar to that for cipher selection (see section 4.20). 4.18.1.1 GSSAPI-based key exchange PuTTY supports a set of key exchange methods that also incorporates GSSAPI-based authentication. They are enabled with the `Attempt GSSAPI key exchange' checkbox (which also appears on the `GSSAPI' panel). PuTTY can only perform the GSSAPI-authenticated key exchange methods when using Kerberos V5, and not other GSSAPI mechanisms. If the user running PuTTY has current Kerberos V5 credentials, then PuTTY will select the GSSAPI key exchange methods in preference to any of the ordinary SSH key exchange methods configured in the preference list. The advantage of doing GSSAPI authentication as part of the SSH key exchange is apparent when you are using credential delegation (see section 4.22.1). The SSH key exchange can be repeated later in the session, and this allows your Kerberos V5 credentials (which are typically short-lived) to be automatically re-delegated to the server when they are refreshed on the client. (This feature is commonly referred to as `cascading credentials'.) If your server doesn't support GSSAPI key exchange, it may still support GSSAPI in the SSH user authentication phase. This will still let you log in using your Kerberos credentials, but will only allow you to delegate the credentials that are active at the beginning of the session; they can't be refreshed automatically later, in a long- running session. Another effect of GSSAPI key exchange is that it replaces the usual SSH mechanism of permanent host keys described in section 2.2. So if you use this method, then you won't be asked any interactive questions about whether to accept the server's host key. Instead, the Kerberos exchange will verify the identity of the host you connect to, at the same time as verifying your identity to it. 4.18.2 Repeat key exchange If the session key negotiated at connection startup is used too much or for too long, it may become feasible to mount attacks against the SSH connection. Therefore, the SSH-2 protocol specifies that a new key exchange should take place every so often; this can be initiated by either the client or the server. While this renegotiation is taking place, no data can pass through the SSH connection, so it may appear to `freeze'. (The occurrence of repeat key exchange is noted in the Event Log; see section 3.1.3.1.) Usually the same algorithm is used as at the start of the connection, with a similar overhead. These options control how often PuTTY will initiate a repeat key exchange (`rekey'). You can also force a key exchange at any time from the Special Commands menu (see section 3.1.3.2). - `Max minutes before rekey' specifies the amount of time that is allowed to elapse before a rekey is initiated. If this is set to zero, PuTTY will not rekey due to elapsed time. The SSH- 2 protocol specification recommends a timeout of at most 60 minutes. You might have a need to disable time-based rekeys completely for the same reasons that keepalives aren't always helpful. If you anticipate suffering a network dropout of several hours in the middle of an SSH connection, but were not actually planning to send _data_ down that connection during those hours, then an attempted rekey in the middle of the dropout will probably cause the connection to be abandoned, whereas if rekeys are disabled then the connection should in principle survive (in the absence of interfering firewalls). See section 4.14.1 for more discussion of these issues; for these purposes, rekeys have much the same properties as keepalives. (Except that rekeys have cryptographic value in themselves, so you should bear that in mind when deciding whether to turn them off.) Note, however, the the SSH _server_ can still initiate rekeys. - `Minutes between GSSAPI checks', if you're using GSSAPI key exchange, specifies how often the GSSAPI credential cache is checked to see whether new tickets are available for delegation, or current ones are near expiration. If forwarding of GSSAPI credentials is enabled, PuTTY will try to rekey as necessary to keep the delegated credentials from expiring. Frequent checks are recommended; rekeying only happens when needed. - `Max data before rekey' specifies the amount of data (in bytes) that is permitted to flow in either direction before a rekey is initiated. If this is set to zero, PuTTY will not rekey due to transferred data. The SSH-2 protocol specification recommends a limit of at most 1 gigabyte. As well as specifying a value in bytes, the following shorthand can be used: - `1k' specifies 1 kilobyte (1024 bytes). - `1M' specifies 1 megabyte (1024 kilobytes). - `1G' specifies 1 gigabyte (1024 megabytes). Disabling data-based rekeys entirely is a bad idea. The integrity, and to a lesser extent, confidentiality of the SSH-2 protocol depend in part on rekeys occurring 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. 4.19 The Host Keys panel The Host Keys panel allows you to configure options related to SSH-2 host key management. Host keys are used to prove the server's identity, and assure you that the server is not being spoofed (either by a man-in-the-middle attack or by completely replacing it on the network). See section 2.2 for a basic introduction to host keys. This entire panel is only relevant to SSH protocol version 2; none of these settings affect SSH-1 at all. 4.19.1 Host key type selection PuTTY supports a variety of SSH-2 host key types, and allows you to choose which one you prefer to use to identify the server. Configuration is similar to cipher selection (see section 4.20). PuTTY currently supports the following host key types: - `Ed25519': Edwards-curve DSA using a twisted Edwards curve with modulus 2^255-19. - `Ed448': another Edwards-curve DSA type, using a larger elliptic curve with a 448-bit instead of 255-bit modulus (so it has a higher security level than Ed25519). - `ECDSA': elliptic curve DSA using one of the NIST-standardised elliptic curves. - `DSA': straightforward DSA using modular exponentiation. - `RSA': the ordinary RSA algorithm. If PuTTY already has one or more host keys stored for the server, it will by default prefer to use one of those, even if the server has a key type that is higher in the preference order. You can add such a key to PuTTY's cache from within an existing session using the `Special Commands' menu; see section 3.1.3.2. Otherwise, PuTTY will choose a key type based purely on the preference order you specify in the configuration. If the first key type PuTTY finds is below the `warn below here' line, you will see a warning box when you make the connection, similar to that for cipher selection (see section 4.20). 4.19.2 Preferring known host keys By default, PuTTY will adjust the preference order for host key algorithms so that any host keys it already knows are moved to the top of the list. This prevents you from having to check and confirm a new host key for a server you already had one for (e.g. because the server has generated an alternative key of a type higher in PuTTY's preference order, or because you changed the preference order itself). However, on the other hand, it can leak information to a listener in the network about _whether_ you already know a host key for this server. For this reason, this policy is configurable. By turning this checkbox off, you can reset PuTTY to always use the exact order of host key algorithms configured in the preference list described in section 4.19.1, so that a listener will find out nothing about what keys you had stored. 4.19.3 Manually configuring host keys In some situations, if PuTTY's automated host key management is not doing what you need, you might need to manually configure PuTTY to accept a specific host key, or one of a specific set of host keys. One reason why you might want to do this is because the host name PuTTY is connecting to is using round-robin DNS to return one of multiple actual servers, and they all have different host keys. In that situation, you might need to configure PuTTY to accept any of a list of host keys for the possible servers, while still rejecting any key not in that list. Another reason is if PuTTY's automated host key management is completely unavailable, e.g. because PuTTY (or Plink or PSFTP, etc) is running in a Windows environment without access to the Registry. In that situation, you will probably want to use the -hostkey command-line option to configure the expected host key(s); see section 3.11.3.21. For situations where PuTTY's automated host key management simply picks the wrong host name to store a key under, you may want to consider setting a `logical host name' instead; see section 4.14.5. To configure manual host keys via the GUI, enter some text describing the host key into the edit box in the `Manually configure host keys for this connection' container, and press the `Add' button. The text will appear in the `Host keys or fingerprints to accept' list box. You can remove keys again with the `Remove' button. The text describing a host key can be in one of the following formats: - An SHA-256-based host key fingerprint of the form displayed in PuTTY's Event Log and host key dialog boxes, i.e. `SHA256:' followed by 43 case-sensitive characters. - An MD5-based host key fingerprint, i.e. sixteen 2-digit hex numbers separated by colons, optionally preceded by the prefix `MD5:'. (The case of the characters does not matter.) - A base64-encoded blob describing an SSH-2 public key in OpenSSH's one-line public key format. How you acquire a public key in this format is server-dependent; on an OpenSSH server it can typically be found in a location like `/etc/ssh/ssh_host_rsa_key.pub'. If this box contains at least one host key or fingerprint when PuTTY makes an SSH connection, then PuTTY's automated host key management is completely bypassed: the connection will be permitted if and only if the host key presented by the server is one of the keys listed in this box, and the host key store in the Registry will be neither read _nor written_, unless you explicitly do so. If the box is empty (as it usually is), then PuTTY's automated host key management will work as normal. 4.20 The Cipher panel PuTTY supports a variety of different encryption algorithms, 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: - ChaCha20-Poly1305, a combined cipher and MAC (SSH-2 only) - AES (Rijndael) - 256, 192, or 128-bit SDCTR or CBC (SSH-2 only) - Arcfour (RC4) - 256 or 128-bit stream cipher (SSH-2 only) - Blowfish - 256-bit SDCTR (SSH-2 only) or 128-bit CBC - Triple-DES - 168-bit SDCTR (SSH-2 only) or CBC - Single-DES - 56-bit CBC (see below for SSH-2) If the algorithm PuTTY finds is below the `warn below here' line, you will see a warning box when you make the connection: The first cipher supported by the server is single-DES, which is below the configured warning threshold. 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 `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 `Enable legacy use of single-DES in SSH-2' option; by default this is disabled and PuTTY will stick to recommended ciphers. 4.21 The Auth panel The Auth panel allows you to configure authentication options for SSH sessions. 4.21.1 `Display pre-authentication banner' SSH-2 servers can provide a message for clients to display to the prospective user before the user logs in; this is sometimes known as a pre-authentication `banner'. Typically this is used to provide information about the server and legal notices. By default, PuTTY displays this message before prompting for a password or similar credentials (although, unfortunately, not before prompting for a login name, due to the nature of the protocol design). By unchecking this option, display of the banner can be suppressed entirely. 4.21.2 `Bypass authentication entirely' In SSH-2, it is in principle possible to establish a connection without using SSH's mechanisms to identify or prove who you are to the server. An SSH server could prefer to handle authentication in the data channel, for instance, or simply require no user authentication whatsoever. By default, PuTTY assumes the server requires authentication (we've never heard of one that doesn't), and thus must start this process with a username. If you find you are getting username prompts that you cannot answer, you could try enabling this option. However, most SSH servers will reject this. This is not the option you want if you have a username and just want PuTTY to remember it; for that see section 4.15.1. It's also probably not what if you're trying to set up passwordless login to a mainstream SSH server; depending on the server, you probably wanted public-key authentication (chapter 8) or perhaps GSSAPI authentication (section 4.22). (These are still forms of authentication, even if you don't have to interact with them.) This option only affects SSH-2 connections. SSH-1 connections always require an authentication step. 4.21.3 `Disconnect if authentication succeeds trivially' This option causes PuTTY to abandon an SSH session and disconnect from the server, if the server accepted authentication without ever having asked for any kind of password or signature or token. This might be used as a security measure. There are some forms of attack against an SSH client user which work by terminating the SSH authentication stage early, and then doing something in the main part of the SSH session which _looks_ like part of the authentication, but isn't really. For example, instead of demanding a signature from your public key, for which PuTTY would ask for your key's passphrase, a compromised or malicious server might allow you to log in with no signature or password at all, and then print a message that _imitates_ PuTTY's request for your passphrase, in the hope that you would type it in. (In fact, the passphrase for your public key should not be sent to any server.) PuTTY's main defence against attacks of this type is the `trust sigil' system: messages in the PuTTY window that are truly originated by PuTTY itself are shown next to a small copy of the PuTTY icon, which the server cannot fake when it tries to imitate the same message using terminal output. However, if you think you might be at risk of this kind of thing anyway (if you don't watch closely for the trust sigils, or if you think you're at extra risk of one of your servers being malicious), then you could enable this option as an extra defence. Then, if the server tries any of these attacks involving letting you through the authentication stage, PuTTY will disconnect from the server before it can send a follow-up fake prompt or other type of attack. On the other hand, some servers _legitimately_ let you through the SSH authentication phase trivially, either because they are genuinely public, or because the important authentication step happens during the terminal session. (An example might be an SSH server that connects you directly to the terminal login prompt of a legacy mainframe.) So enabling this option might cause some kinds of session to stop working. It's up to you. 4.21.4 `Attempt authentication using Pageant' If this option is enabled, then PuTTY will look for Pageant (the SSH private-key storage agent) and attempt to authenticate with any suitable public keys Pageant currently holds. This behaviour is almost always desirable, and is therefore enabled by default. In rare cases you might need to turn it off in order to force authentication by some non-public-key method such as passwords. This option can also be controlled using the `-noagent' command-line option. See section 3.11.3.9. See chapter 9 for more information about Pageant in general. 4.21.5 `Attempt TIS or CryptoCard authentication' TIS and CryptoCard authentication are (despite their names) generic forms of simple challenge/response authentication available in SSH protocol version 1 only. You might use them if you were using S/Key one-time passwords, for example, or if you had a physical security token that generated responses to authentication challenges. They can even be used to prompt for simple passwords. With this switch enabled, PuTTY will attempt these forms of authentication if the server is willing to try them. You will be presented with a challenge string (which may be different every time) and must supply the correct response in order to log in. If your server supports this, you should talk to your system administrator about precisely what form these challenges and responses take. 4.21.6 `Attempt keyboard-interactive authentication' The SSH-2 equivalent of TIS authentication is called `keyboard- interactive'. It is a flexible authentication method using an arbitrary sequence of requests and responses; so it is not only useful for challenge/response mechanisms such as S/Key, but it can also be used for (for example) asking the user for a new password when the old one has expired. PuTTY leaves this option enabled by default, but supplies a switch to turn it off in case you should have trouble with it. 4.21.7 `Allow agent forwarding' This option allows the SSH server to open forwarded connections back to your local copy of Pageant. If you are not running Pageant, this option will do nothing. See chapter 9 for general information on Pageant, and section 9.4 for information on agent forwarding. Note that there is a security risk involved with enabling this option; see section 9.6 for details. 4.21.8 `Allow attempted changes of username in SSH-2' In the SSH-1 protocol, it is impossible to change username after failing to authenticate. So if you mis-type your username at the PuTTY `login as:' prompt, you will not be able to change it except by restarting PuTTY. The SSH-2 protocol _does_ allow changes of username, in principle, but does not make it mandatory for SSH-2 servers to accept them. In particular, OpenSSH does not accept a change of username; once you have sent one username, it will reject attempts to try to authenticate as another user. (Depending on the version of OpenSSH, it may quietly return failure for all login attempts, or it may send an error message.) For this reason, PuTTY will by default not prompt you for your username more than once, in case the server complains. If you know your server can cope with it, you can enable the `Allow attempted changes of username' option to modify PuTTY's behaviour. 4.21.9 `Private key file for authentication' This box is where you enter the name of your private key file if you are using public key authentication. See chapter 8 for information about public key authentication in SSH. This key must be in PuTTY's native format (`*.PPK'). If you have a private key in another format that you want to use with PuTTY, see section 8.2.14. You can use the authentication agent Pageant so that you do not need to explicitly configure a key here; see chapter 9. If a private key file is specified here with Pageant running, PuTTY will first try asking Pageant to authenticate with that key, and ignore any other keys Pageant may have. If that fails, PuTTY will ask for a passphrase as normal. You can also specify a _public_ key file in this case (in RFC 4716 or OpenSSH format), as that's sufficient to identify the key to Pageant, but of course if Pageant isn't present PuTTY can't fall back to using this file itself. 4.22 The GSSAPI panel The `GSSAPI' subpanel of the `Auth' panel controls the use of GSSAPI authentication. This is a mechanism which delegates the authentication exchange to a library elsewhere on the client machine, which in principle can authenticate in many different ways but in practice is usually used with the Kerberos single sign-on protocol to implement passwordless login. GSSAPI authentication is only available in the SSH-2 protocol. PuTTY supports two forms of GSSAPI-based authentication. In one of them, the SSH key exchange happens in the normal way, and GSSAPI is only involved in authenticating the user. The checkbox labelled `Attempt GSSAPI authentication' controls this form. In the other method, GSSAPI-based authentication is combined with the SSH key exchange phase. If this succeeds, then the SSH authentication step has nothing left to do. See section 4.18.1.1 for more information about this method. The checkbox labelled `Attempt GSSAPI key exchange' controls this form. (The same checkbox appears on the `Kex' panel.) If one or both of these controls is enabled, then GSSAPI authentication will be attempted in one form or the other, and (typically) if your client machine has valid Kerberos credentials loaded, then PuTTY should be able to authenticate automatically to servers that support Kerberos logins. If both of those checkboxes are disabled, PuTTY will not try any form of GSSAPI at all, and the rest of this panel will be unused. 4.22.1 `Allow GSSAPI credential delegation' GSSAPI credential delegation is a mechanism for passing on your Kerberos (or other) identity to the session on the SSH server. If you enable this option, then not only will PuTTY be able to log in automatically to a server that accepts your Kerberos credentials, but also you will be able to connect out from that server to other Kerberos-supporting services and use the same credentials just as automatically. (This option is the Kerberos analogue of SSH agent forwarding; see section 9.4 for some information on that.) Note that, like SSH agent forwarding, there is a security implication in the use of this option: the administrator of the server you connect to, or anyone else who has cracked the administrator account on that server, could fake your identity when connecting to further Kerberos-supporting services. However, Kerberos sites are typically run by a central authority, so the administrator of one server is likely to already have access to the other services too; so this would typically be less of a risk than SSH agent forwarding. If your connection is not using GSSAPI key exchange, it is possible for the delegation to expire during your session. See section 4.18.1.1 for more information. 4.22.2 Preference order for GSSAPI libraries GSSAPI is a mechanism which allows more than one authentication method to be accessed through the same interface. Therefore, more than one authentication library may exist on your system which can be accessed using GSSAPI. PuTTY contains native support for a few well-known such libraries (including Windows' SSPI), and will look for all of them on your system and use whichever it finds. If more than one exists on your system and you need to use a specific one, you can adjust the order in which it will search using this preference list control. One of the options in the preference list is to use a user-specified GSSAPI library. If the library you want to use is not mentioned by name in PuTTY's list of options, you can enter its full pathname in the `User-supplied GSSAPI library path' field, and move the `User- supplied GSSAPI library' option in the preference list to make sure it is selected before anything else. On Windows, such libraries are files with a .dll extension, and must have been built in the same way as the PuTTY executable you're running; if you have a 32-bit DLL, you must run a 32-bit version of PuTTY, and the same with 64-bit (see question A.6.10). On Unix, shared libraries generally have a .so extension. 4.23 The TTY panel The TTY panel lets you configure the remote pseudo-terminal. 4.23.1 `Don't allocate a pseudo-terminal' When connecting to a Unix system, most interactive shell sessions are run in a _pseudo-terminal_, which allows the Unix system to pretend it's talking to a real physical terminal device but allows the SSH server to catch all the data coming from that fake device and send it back to the client. Occasionally you might find you have a need to run a session _not_ in a pseudo-terminal. In PuTTY, this is generally only useful for very specialist purposes; although in Plink (see chapter 7) it is the usual way of working. 4.23.2 Sending terminal modes The SSH protocol allows the client to send `terminal modes' for the remote pseudo-terminal. These usually control the server's expectation of the local terminal's behaviour. If your server does not have sensible defaults for these modes, you may find that changing them here helps, although the server is at liberty to ignore your changes. If you don't understand any of this, it's safe to leave these settings alone. (None of these settings will have any effect if no pseudo-terminal is requested or allocated.) You can change what happens for a particular mode by selecting it in the list, choosing one of the options and specifying the exact value if necessary, and hitting `Set'. The effect of the options is as follows: - If the `Auto' option is selected, the PuTTY tools will decide whether to specify that mode to the server, and if so, will send a sensible value. PuTTY proper will send modes that it has an opinion on (currently only the code for the Backspace key, ERASE, and whether the character set is UTF-8, IUTF8). Plink on Unix will propagate appropriate modes from the local terminal, if any. - If `Nothing' is selected, no value for the mode will be specified to the server under any circumstances. - If a value is specified, it will be sent to the server under all circumstances. The precise syntax of the value box depends on the mode. By default, all of the available modes are listed as `Auto', which should do the right thing in most circumstances. The precise effect of each setting, if any, is up to the server. Their names come from POSIX and other Unix systems, and they are most likely to have a useful effect on such systems. (These are the same settings that can usually be changed using the `stty' command once logged in to such servers.) Some notable modes are described below; for fuller explanations, see your server documentation. - ERASE is the character that when typed by the user will delete one space to the left. When set to `Auto' (the default setting), this follows the setting of the local Backspace key in PuTTY (see section 4.4.1). This and other special characters are specified using `^C' notation for Ctrl-C, and so on. Use `^<27>' or `^<0x1B>' to specify a character numerically, and `^~' to get a literal `^'. Other non-control characters are denoted by themselves. Leaving the box entirely blank indicates that _no_ character should be assigned to the specified function, although this may not be supported by all servers. - QUIT is a special character that usually forcefully ends the current process on the server (SIGQUIT). On many servers its default setting is Ctrl-backslash (`^\'), which is easy to accidentally invoke on many keyboards. If this is getting in your way, you may want to change it to another character or turn it off entirely. - Boolean modes such as ECHO and ICANON can be specified in PuTTY in a variety of ways, such as true/false, yes/no, and 0/1. (Explicitly specifying a value of no is different from not sending the mode at all.) - The boolean mode IUTF8 signals to the server whether the terminal character set is UTF-8 or not, for purposes such as basic line editing; if this is set incorrectly, the backspace key may erase the wrong amount of text, for instance. However, simply setting this is not usually sufficient for the server to use UTF-8; POSIX servers will generally also require the locale to be set (by some server-dependent means), although many newer installations default to UTF-8. Also, since this mode was added to the SSH protocol much later than the others, many servers (particularly older servers) do not honour this mode sent over SSH; indeed, a few poorly-written servers object to its mere presence, so you may find you need to set it to not be sent at all. When set to `Auto', this follows the local configured character set (see section 4.10.1). - Terminal speeds are configured elsewhere; see section 4.15.4. 4.24 The X11 panel The X11 panel allows you to configure forwarding of X11 over an SSH connection. If your server lets you run X Window System graphical applications, X11 forwarding allows you to securely give those applications access to a local X display on your PC. To enable X11 forwarding, check the `Enable X11 forwarding' box. If your X display is somewhere unusual, you will need to enter its location in the `X display location' box; if this is left blank, PuTTY will try to find a sensible default in the environment, or use the primary local display (`:0') if that fails. See section 3.4 for more information about X11 forwarding. 4.24.1 Remote X11 authentication If you are using X11 forwarding, the virtual X server created on the SSH server machine will be protected by authorisation data. This data is invented, and checked, by PuTTY. The usual authorisation method used for this is called MIT-MAGIC- COOKIE-1. This is a simple password-style protocol: the X client sends some cookie data to the server, and the server checks that it matches the real cookie. The cookie data is sent over an unencrypted X11 connection; so if you allow a client on a third machine to access the virtual X server, then the cookie will be sent in the clear. PuTTY offers the alternative protocol XDM-AUTHORIZATION-1. This is a cryptographically authenticated protocol: the data sent by the X client is different every time, and it depends on the IP address and port of the client's end of the connection and is also stamped with the current time. So an eavesdropper who captures an XDM- AUTHORIZATION-1 string cannot immediately re-use it for their own X connection. PuTTY's support for XDM-AUTHORIZATION-1 is a somewhat experimental feature, and may encounter several problems: - Some X clients probably do not even support XDM-AUTHORIZATION- 1, so they will not know what to do with the data PuTTY has provided. - This authentication mechanism will only work in SSH-2. In SSH- 1, the SSH server does not tell the client the source address of a forwarded connection in a machine-readable format, so it's impossible to verify the XDM-AUTHORIZATION-1 data. - You may find this feature causes problems with some SSH servers, which will not clean up XDM-AUTHORIZATION-1 data after a session, so that if you then connect to the same server using a client which only does MIT-MAGIC-COOKIE-1 and are allocated the same remote display number, you might find that out-of-date authentication data is still present on your server and your X connections fail. PuTTY's default is MIT-MAGIC-COOKIE-1. If you change it, you should be sure you know what you're doing. 4.24.2 X authority file for local display If you are using X11 forwarding, the local X server to which your forwarded connections are eventually directed may itself require authorisation. Some Windows X servers do not require this: they do authorisation by simpler means, such as accepting any connection from the local machine but not from anywhere else. However, if your X server does require authorisation, then PuTTY needs to know what authorisation is required. One way in which this data might be made available is for the X server to store it somewhere in a file which has the same format as the Unix `.Xauthority' file. If this is how your Windows X server works, then you can tell PuTTY where to find this file by configuring this option. By default, PuTTY will not attempt to find any authorisation for your local display. 4.25 The Tunnels panel The Tunnels panel allows you to configure tunnelling of arbitrary connection types through an SSH connection. Port forwarding allows you to tunnel other types of network connection down an SSH session. See section 3.5 for a general discussion of port forwarding and how it works. The port forwarding section in the Tunnels panel shows a list of all the port forwardings that PuTTY will try to set up when it connects to the server. By default no port forwardings are set up, so this list is empty. To add a port forwarding: - Set one of the `Local' or `Remote' radio buttons, depending on whether you want to forward a local port to a remote destination (`Local') or forward a remote port to a local destination (`Remote'). Alternatively, select `Dynamic' if you want PuTTY to provide a local SOCKS 4/4A/5 proxy on a local port (note that this proxy only supports TCP connections; the SSH protocol does not support forwarding UDP). - Enter a source port number into the `Source port' box. For local forwardings, PuTTY will listen on this port of your PC. For remote forwardings, your SSH server will listen on this port of the remote machine. Note that most servers will not allow you to listen on port numbers less than 1024. - If you have selected `Local' or `Remote' (this step is not needed with `Dynamic'), enter a hostname and port number separated by a colon, in the `Destination' box. Connections received on the source port will be directed to this destination. For example, to connect to a POP-3 server, you might enter `popserver.example.com:110'. (If you need to enter a literal IPv6 address, enclose it in square brackets, for instance `[::1]:2200'.) - Click the `Add' button. Your forwarding details should appear in the list box. To remove a port forwarding, simply select its details in the list box, and click the `Remove' button. In the `Source port' box, you can also optionally enter an IP address to listen on, by specifying (for instance) `127.0.0.5:79'. See section 3.5 for more information on how this works and its restrictions. In place of port numbers, you can enter service names, if they are known to the local system. For instance, in the `Destination' box, you could enter `popserver.example.com:pop3'. You can modify the currently active set of port forwardings in mid- session using `Change Settings' (see section 3.1.3.4). If you delete a local or dynamic port forwarding in mid-session, PuTTY will stop listening for connections on that port, so it can be re-used by another program. If you delete a remote port forwarding, note that: - The SSH-1 protocol contains no mechanism for asking the server to stop listening on a remote port. - The SSH-2 protocol does contain such a mechanism, but not all SSH servers support it. (In particular, OpenSSH does not support it in any version earlier than 3.9.) If you ask to delete a remote port forwarding and PuTTY cannot make the server actually stop listening on the port, it will instead just start refusing incoming connections on that port. Therefore, although the port cannot be reused by another program, you can at least be reasonably sure that server-side programs can no longer access the service at your end of the port forwarding. If you delete a forwarding, any existing connections established using that forwarding remain open. Similarly, changes to global settings such as `Local ports accept connections from other hosts' only take effect on new forwardings. If the connection you are forwarding over SSH is itself a second SSH connection made by another copy of PuTTY, you might find the `logical host name' configuration option useful to warn PuTTY of which host key it should be expecting. See section 4.14.5 for details of this. 4.25.1 Controlling the visibility of forwarded ports The source port for a forwarded connection usually does not accept connections from any machine except the SSH client or server machine itself (for local and remote forwardings respectively). There are controls in the Tunnels panel to change this: - The `Local ports accept connections from other hosts' option allows you to set up local-to-remote port forwardings in such a way that machines other than your client PC can connect to the forwarded port. (This also applies to dynamic SOCKS forwarding.) - The `Remote ports do the same' option does the same thing for remote-to-local port forwardings (so that machines other than the SSH server machine can connect to the forwarded port.) Note that this feature is only available in the SSH-2 protocol, and not all SSH-2 servers support it (OpenSSH 3.0 does not, for example). 4.25.2 Selecting Internet protocol version for forwarded ports This switch allows you to select a specific Internet protocol (IPv4 or IPv6) for the local end of a forwarded port. By default, it is set on `Auto', which means that: - for a local-to-remote port forwarding, PuTTY will listen for incoming connections in both IPv4 and (if available) IPv6 - for a remote-to-local port forwarding, PuTTY will choose a sensible protocol for the outgoing connection. This overrides the general Internet protocol version preference on the Connection panel (see section 4.14.4). Note that some operating systems may listen for incoming connections in IPv4 even if you specifically asked for IPv6, because their IPv4 and IPv6 protocol stacks are linked together. Apparently Linux does this, and Windows does not. So if you're running PuTTY on Windows and you tick `IPv6' for a local or dynamic port forwarding, it will _only_ be usable by connecting to it using IPv6; whereas if you do the same on Linux, you can also use it with IPv4. However, ticking `Auto' should always give you a port which you can connect to using either protocol. 4.26 The Bugs and More Bugs panels Not all SSH servers work properly. Various existing servers have bugs in them, which can make it impossible for a client to talk to them unless it knows about the bug and works around it. Since most servers announce their software version number at the beginning of the SSH connection, PuTTY will attempt to detect which bugs it can expect to see in the server and automatically enable workarounds. However, sometimes it will make mistakes; if the server has been deliberately configured to conceal its version number, or if the server is a version which PuTTY's bug database does not know about, then PuTTY will not know what bugs to expect. The Bugs and More Bugs panels (there are two because we have so many bug compatibility modes) allow you to manually configure the bugs PuTTY expects to see in the server. Each bug can be configured in three states: - `Off': PuTTY will assume the server does not have the bug. - `On': PuTTY will assume the server _does_ have the bug. - `Auto': PuTTY will use the server's version number announcement to try to guess whether or not the server has the bug. 4.26.1 `Chokes on SSH-2 ignore messages' An ignore message (SSH_MSG_IGNORE) is a message in the SSH protocol which can be sent from the client to the server, or from the server to the client, at any time. Either side is required to ignore the message whenever it receives it. PuTTY uses ignore messages in SSH-2 to confuse the encrypted data stream and make it harder to cryptanalyse. It also uses ignore messages for connection keepalives (see section 4.14.1). If it believes the server to have this bug, PuTTY will stop using ignore messages. If this bug is enabled when talking to a correct server, the session will succeed, but keepalives will not work and the session might be less cryptographically secure than it could be. 4.26.2 `Handles SSH-2 key re-exchange badly' Some SSH servers cannot cope with repeat key exchange at all, and will ignore attempts by the client to start one. Since PuTTY pauses the session while performing a repeat key exchange, the effect of this would be to cause the session to hang after an hour (unless you have your rekey timeout set differently; see section 4.18.2 for more about rekeys). Other, very old, SSH servers handle repeat key exchange even more badly, and disconnect upon receiving a repeat key exchange request. If this bug is detected, PuTTY will never initiate a repeat key exchange. If this bug is enabled when talking to a correct server, the session should still function, but may be less secure than you would expect. This is an SSH-2-specific bug. 4.26.3 `Chokes on PuTTY's SSH-2 `winadj' requests' PuTTY sometimes sends a special request to SSH servers in the middle of channel data, with the name winadj@putty.projects.tartarus.org (see section G.1). 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 _understand_ the message; it is expected to send back a SSH_MSG_CHANNEL_FAILURE message indicating that it didn't understand it. (All PuTTY needs for its timing calculations is _some_ kind of response.) It has been known for some SSH servers to get confused by this message in one way or another - 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 `winadj@putty.projects.tartarus.org' request, and will make do without its timing data. 4.26.4 `Replies to requests on closed channels' The SSH protocol as published in RFC 4254 has an ambiguity which arises if one side of a connection tries to close a channel, while the other side simultaneously sends a request within the channel and asks for a reply. RFC 4254 leaves it unclear whether the closing side should reply to the channel request after having announced its intention to close the channel. Discussion on the ietf-ssh mailing list in April 2014 formed a clear consensus that the right answer is no. However, because of the ambiguity in the specification, some SSH servers have implemented the other policy; for example, OpenSSH used to until it was fixed. Because PuTTY sends channel requests with the `want reply' flag throughout channels' lifetime (see section 4.26.3), it's possible that when connecting to such a server it might receive a reply to a request after it thinks the channel has entirely closed, and terminate with an error along the lines of `Received SSH2_MSG_CHANNEL_FAILURE for nonexistent channel 256'. 4.26.5 `Ignores SSH-2 maximum packet size' When an SSH-2 channel is set up, each end announces the maximum size of data packet that it is willing to receive for that channel. Some servers ignore PuTTY's announcement and send packets larger than PuTTY is willing to accept, causing it to report `Incoming packet was garbled on decryption'. If this bug is detected, PuTTY never allows the channel's 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. 4.26.6 `Requires padding on SSH-2 RSA signatures' Versions below 3.3 of OpenSSH require SSH-2 RSA signatures to be padded with zero bytes to the same length as the RSA key modulus. The SSH-2 specification says that an unpadded signature MUST be accepted, so this is a bug. A typical symptom of this problem is that PuTTY mysteriously fails RSA authentication once in every few hundred attempts, and falls back to passwords. If this bug is detected, PuTTY will pad its signatures in the way OpenSSH expects. If this bug is enabled when talking to a correct server, it is likely that no damage will be done, since correct servers usually still accept padded signatures because they're used to talking to OpenSSH. This is an SSH-2-specific bug. 4.26.7 `Only supports pre-RFC4419 SSH-2 DH GEX' The SSH key exchange method that uses Diffie-Hellman group exchange was redesigned after its original release, to use a slightly more sophisticated setup message. Almost all SSH implementations switched over to the new version. (PuTTY was one of the last.) A few old servers still only support the old one. If this bug is detected, and the client and server negotiate Diffie- Hellman group exchange, then PuTTY will send the old message now known as SSH2_MSG_KEX_DH_GEX_REQUEST_OLD in place of the new SSH2_MSG_KEX_DH_GEX_REQUEST. This is an SSH-2-specific bug. 4.26.8 `Miscomputes SSH-2 HMAC keys' Versions 2.3.0 and below of the SSH server software from ssh.com compute the keys for their HMAC message authentication codes incorrectly. A typical symptom of this problem is that PuTTY dies unexpectedly at the beginning of the session, saying `Incorrect MAC received on packet'. If this bug is detected, PuTTY will compute its HMAC keys in the same way as the buggy server, so that communication will still be possible. If this bug is enabled when talking to a correct server, communication will fail. This is an SSH-2-specific bug. 4.26.9 `Misuses the session ID in SSH-2 PK auth' Versions below 2.3 of OpenSSH require SSH-2 public-key authentication to be done slightly differently: the data to be signed by the client contains the session ID formatted in a different way. If public-key authentication mysteriously does not work but the Event Log (see section 3.1.3.1) thinks it has successfully sent a signature, it might be worth enabling the workaround for this bug to see if it helps. If this bug is detected, PuTTY will sign data in the way OpenSSH expects. If this bug is enabled when talking to a correct server, SSH-2 public-key authentication will fail. This is an SSH-2-specific bug. 4.26.10 `Miscomputes SSH-2 encryption keys' Versions below 2.0.11 of the SSH server software from ssh.com compute the keys for the session encryption incorrectly. This problem can cause various error messages, such as `Incoming packet was garbled on decryption', or possibly even `Out of memory'. If this bug is detected, PuTTY will compute its encryption keys in the same way as the buggy server, so that communication will still be possible. If this bug is enabled when talking to a correct server, communication will fail. This is an SSH-2-specific bug. 4.26.11 `Chokes on SSH-1 ignore messages' An ignore message (SSH_MSG_IGNORE) is a message in the SSH protocol which can be sent from the client to the server, or from the server to the client, at any time. Either side is required to ignore the message whenever it receives it. PuTTY uses ignore messages to hide the password packet in SSH-1, so that a listener cannot tell the length of the user's password; it also uses ignore messages for connection keepalives (see section 4.14.1). If this bug is detected, PuTTY will stop using ignore messages. This means that keepalives will stop working, and PuTTY will have to fall back to a secondary defence against SSH-1 password- length eavesdropping. See section 4.26.12. If this bug is enabled when talking to a correct server, the session will succeed, but keepalives will not work and the session might be more vulnerable to eavesdroppers than it could be. 4.26.12 `Refuses all SSH-1 password camouflage' When talking to an SSH-1 server which cannot deal with ignore messages (see section 4.26.11), PuTTY will attempt to disguise the length of the user's password by sending additional padding _within_ the password packet. This is technically a violation of the SSH- 1 specification, and so PuTTY will only do it when it cannot use standards-compliant ignore messages as camouflage. In this sense, for a server to refuse to accept a padded password packet is not really a bug, but it does make life inconvenient if the server can also not handle ignore messages. If this `bug' is detected, PuTTY will assume that neither ignore messages nor padding are acceptable, and that it thus has no choice but to send the user's password with no form of camouflage, so that an eavesdropping user will be easily able to find out the exact length of the password. If this bug is enabled when talking to a correct server, the session will succeed, but will be more vulnerable to eavesdroppers than it could be. This is an SSH-1-specific bug. SSH-2 is secure against this type of attack. 4.26.13 `Chokes on SSH-1 RSA authentication' Some SSH-1 servers cannot deal with RSA authentication messages at all. If Pageant is running and contains any SSH-1 keys, PuTTY will normally automatically try RSA authentication before falling back to passwords, so these servers will crash when they see the RSA attempt. If this bug is detected, PuTTY will go straight to password authentication. If this bug is enabled when talking to a correct server, the session will succeed, but of course RSA authentication will be impossible. This is an SSH-1-specific bug. 4.27 The `Bare ssh-connection' protocol In addition to SSH itself, PuTTY also supports a second protocol that is derived from SSH. It's listed in the PuTTY GUI under the name `Bare ssh-connection'. This protocol consists of just the innermost of SSH-2's three layers: it leaves out the cryptography layer providing network security, and it leaves out the authentication layer where you provide a username and prove you're allowed to log in as that user. It is therefore *completely unsuited to any network connection*. Don't try to use it over a network! The purpose of this protocol is for various specialist circumstances in which the `connection' is not over a real network, but is a pipe or IPC channel between different processes running on the _same_ computer. In these contexts, the operating system will already have guaranteed that each of the two communicating processes is owned by the expected user (so that no authentication is necessary), and that the communications channel cannot be tapped by a hostile user on the same machine (so that no cryptography is necessary either). Examples of possible uses involve communicating with a strongly separated context such as the inside of a container, or a VM, or a different network namespace. Explicit support for this protocol is new in PuTTY 0.75. As of 2021- 04, the only known server for the bare ssh-connection protocol is the Unix program `psusan' that is also part of the PuTTY tool suite. (However, this protocol is also the same one used between instances of PuTTY to implement connection sharing: see section 4.17.5. In fact, in the Unix version of PuTTY, when a sharing upstream records `Sharing this connection at [pathname]' in the Event Log, it's possible to connect another instance of PuTTY directly to that Unix socket, by entering its pathname in the host name box and selecting `Bare ssh-connection' as the protocol!) Many of the options under the SSH panel also affect this protocol, although options to do with cryptography and authentication do not, for obvious reasons. I repeat, *DON'T TRY TO USE THIS PROTOCOL FOR NETWORK CONNECTIONS!* That's not what it's for, and it's not at all safe to do it. 4.28 The Serial panel The Serial panel allows you to configure options that only apply when PuTTY is connecting to a local serial line. 4.28.1 Selecting a serial line to connect to The `Serial line to connect to' box allows you to choose which serial line you want PuTTY to talk to, if your computer has more than one serial port. On Windows, the first serial line is called COM1, and if there is a second it is called COM2, and so on. This configuration setting is also visible on the Session panel, where it replaces the `Host Name' box (see section 4.1.1) if the connection type is set to `Serial'. 4.28.2 Selecting the speed of your serial line The `Speed' box allows you to choose the speed (or `baud rate') at which to talk to the serial line. Typical values might be 9600, 19200, 38400 or 57600. Which one you need will depend on the device at the other end of the serial cable; consult the manual for that device if you are in doubt. This configuration setting is also visible on the Session panel, where it replaces the `Port' box (see section 4.1.1) if the connection type is set to `Serial'. 4.28.3 Selecting the number of data bits The `Data bits' box allows you to choose how many data bits are transmitted in each byte sent or received through the serial line. Typical values are 7 or 8. 4.28.4 Selecting the number of stop bits The `Stop bits' box allows you to choose how many stop bits are used in the serial line protocol. Typical values are 1, 1.5 or 2. 4.28.5 Selecting the serial parity checking scheme The `Parity' box allows you to choose what type of parity checking is used on the serial line. The settings are: - `None': no parity bit is sent at all. - `Odd': an extra parity bit is sent alongside each byte, and arranged so that the total number of 1 bits is odd. - `Even': an extra parity bit is sent alongside each byte, and arranged so that the total number of 1 bits is even. - `Mark': an extra parity bit is sent alongside each byte, and always set to 1. - `Space': an extra parity bit is sent alongside each byte, and always set to 0. 4.28.6 Selecting the serial flow control scheme The `Flow control' box allows you to choose what type of flow control checking is used on the serial line. The settings are: - `None': no flow control is done. Data may be lost if either side attempts to send faster than the serial line permits. - `XON/XOFF': flow control is done by sending XON and XOFF characters within the data stream. - `RTS/CTS': flow control is done using the RTS and CTS wires on the serial line. - `DSR/DTR': flow control is done using the DSR and DTR wires on the serial line. 4.29 The Telnet panel The Telnet panel allows you to configure options that only apply to Telnet sessions. 4.29.1 `Handling of OLD_ENVIRON ambiguity' The original Telnet mechanism for passing environment variables was badly specified. At the time the standard (RFC 1408) was written, BSD telnet implementations were already supporting the feature, and the intention of the standard was to describe the behaviour the BSD implementations were already using. Sadly there was a typing error in the standard when it was issued, and two vital function codes were specified the wrong way round. BSD implementations did not change, and the standard was not corrected. Therefore, it's possible you might find either BSD or RFC-compliant implementations out there. This switch allows you to choose which one PuTTY claims to be. The problem was solved by issuing a second standard, defining a new Telnet mechanism called NEW_ENVIRON, which behaved exactly like the original OLD_ENVIRON but was not encumbered by existing implementations. Most Telnet servers now support this, and it's unambiguous. This feature should only be needed if you have trouble passing environment variables to quite an old server. 4.29.2 Passive and active Telnet negotiation modes In a Telnet connection, there are two types of data passed between the client and the server: actual text, and _negotiations_ about which Telnet extra features to use. PuTTY can use two different strategies for negotiation: - In _active_ mode, PuTTY starts to send negotiations as soon as the connection is opened. - In _passive_ mode, PuTTY will wait to negotiate until it sees a negotiation from the server. The obvious disadvantage of passive mode is that if the server is also operating in a passive mode, then negotiation will never begin at all. For this reason PuTTY defaults to active mode. However, sometimes passive mode is required in order to successfully get through certain types of firewall and Telnet proxy server. If you have confusing trouble with a firewall, you could try enabling passive mode to see if it helps. 4.29.3 `Keyboard sends Telnet special commands' If this box is checked, several key sequences will have their normal actions modified: - the Backspace key on the keyboard will send the Telnet special backspace code; - Control-C will send the Telnet special Interrupt Process code; - Control-Z will send the Telnet special Suspend Process code. You probably shouldn't enable this unless you know what you're doing. 4.29.4 `Return key sends Telnet New Line instead of ^M' Unlike most other remote login protocols, the Telnet protocol has a special `new line' code that is not the same as the usual line endings of Control-M or Control-J. By default, PuTTY sends the Telnet New Line code when you press Return, instead of sending Control-M as it does in most other protocols. Most Unix-style Telnet servers don't mind whether they receive Telnet New Line or Control-M; some servers do expect New Line, and some servers prefer to see ^M. If you are seeing surprising behaviour when you press Return in a Telnet session, you might try turning this option off to see if it helps. 4.30 The Rlogin panel The Rlogin panel allows you to configure options that only apply to Rlogin sessions. 4.30.1 `Local username' Rlogin allows an automated (password-free) form of login by means of a file called `.rhosts' on the server. You put a line in your `.rhosts' file saying something like `jbloggs@pc1.example.com', and then when you make an Rlogin connection the client transmits the username of the user running the Rlogin client. The server checks the username and hostname against `.rhosts', and if they match it does not ask for a password. This only works because Unix systems contain a safeguard to stop a user from pretending to be another user in an Rlogin connection. Rlogin connections have to come from port numbers below 1024, and Unix systems prohibit this to unprivileged processes; so when the server sees a connection from a low-numbered port, it assumes the client end of the connection is held by a privileged (and therefore trusted) process, so it believes the claim of who the user is. Windows does not have this restriction: _any_ user can initiate an outgoing connection from a low-numbered port. Hence, the Rlogin `.rhosts' mechanism is completely useless for securely distinguishing several different users on a Windows machine. If you have a `.rhosts' entry pointing at a Windows PC, you should assume that _anyone_ using that PC can spoof your username in an Rlogin connection and access your account on the server. The `Local username' control allows you to specify what user name PuTTY should claim you have, in case it doesn't match your Windows user name (or in case you didn't bother to set up a Windows user name). 4.31 The SUPDUP panel The SUPDUP panel allows you to configure options that only apply to SUPDUP sessions. See section 3.10 for more about the SUPDUP protocol. 4.31.1 `Location string' In SUPDUP, the client sends a piece of text of its choice to the server giving the user's location. This is typically displayed in lists of logged-in users. By default, PuTTY just defaults this to "The Internet". If you want your location to show up as something more specific, you can configure it here. 4.31.2 `Extended ASCII Character set' This declares what kind of character set extension your terminal supports. If the server supports it, it will send text using that character set. `None' means the standard 95 printable ASCII characters. `ITS' means ASCII extended with printable characters in the control character range. This character set is documented in the SUPDUP protocol definition. `WAITS' is similar to `ITS' but uses some alternative characters in the extended set: most prominently, it will display arrows instead of `^' and `_', and `}' instead of `~'. `ITS' extended ASCII is used by ITS and Lisp machines, whilst `WAITS' is only used by the WAITS operating system from the Stanford AI Laboratory. 4.31.3 `**MORE** processing' When **MORE** processing is enabled, the server causes output to pause at the bottom of the screen, until a space is typed. 4.31.4 `Terminal scrolling' This controls whether the terminal will perform scrolling then the cursor goes below the last line, or if the cursor will return to the first line. 4.32 Storing configuration in a file PuTTY does not currently support storing its configuration in a file instead of the Registry. However, you can work around this with a couple of batch files. You will need a file called (say) `PUTTY.BAT' which imports the contents of a file into the Registry, then runs PuTTY, exports the contents of the Registry back into the file, and deletes the Registry entries. This can all be done using the Regedit command line options, so it's all automatic. Here is what you need in `PUTTY.BAT': @ECHO OFF regedit /s putty.reg regedit /s puttyrnd.reg start /w putty.exe regedit /ea new.reg HKEY_CURRENT_USER\Software\SimonTatham\PuTTY copy new.reg putty.reg del new.reg regedit /s puttydel.reg This batch file needs two auxiliary files: `PUTTYRND.REG' which sets up an initial safe location for the `PUTTY.RND' random seed file, and `PUTTYDEL.REG' which destroys everything in the Registry once it's been successfully saved back to the file. Here is `PUTTYDEL.REG': REGEDIT4 [-HKEY_CURRENT_USER\Software\SimonTatham\PuTTY] Here is an example `PUTTYRND.REG' file: REGEDIT4 [HKEY_CURRENT_USER\Software\SimonTatham\PuTTY] "RandSeedFile"="a:\\putty.rnd" You should replace `a:\putty.rnd' with the location where you want to store your random number data. If the aim is to carry around PuTTY and its settings on one USB stick, you probably want to store it on the USB stick. Chapter 5: Using PSCP to transfer files securely ------------------------------------------------ PSCP, the PuTTY Secure Copy client, is a tool for transferring files securely between computers using an SSH connection. If you have an SSH-2 server, you might prefer PSFTP (see chapter 6) for interactive use. PSFTP does not in general work with SSH-1 servers, however. 5.1 Starting PSCP PSCP is a command line application. This means that you cannot just double-click on its icon to run it and instead you have to bring up a console window. With Windows 95, 98, and ME, this is called an `MS-DOS Prompt' and with Windows NT, 2000, and XP, it is called a `Command Prompt'. It should be available from the Programs section of your Start Menu. To start PSCP it will need either to be on your `PATH' or in your current directory. To add the directory containing PSCP to your `PATH' environment variable, type into the console window: set PATH=C:\path\to\putty\directory;%PATH% This will only work for the lifetime of that particular console window. To set your `PATH' more permanently on Windows NT, 2000, and XP, use the Environment tab of the System Control Panel. On Windows 95, 98, and ME, you will need to edit your `AUTOEXEC.BAT' to include a `set' command like the one above. 5.2 PSCP Usage Once you've got a console window to type into, you can just type `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:\>pscp PuTTY Secure Copy client Release 0.76 Usage: pscp [options] [user@]host:source target pscp [options] source [source...] [user@]host:target pscp [options] -ls [user@]host:filespec Options: -V print version information and exit -pgpfp print PGP key fingerprints and exit -p preserve file attributes -q quiet, don't show statistics -r copy directories recursively -v show verbose messages -load sessname Load settings from saved session -P port connect to specified port -l user connect with specified username -pw passw login with specified password -1 -2 force use of particular SSH protocol version -ssh -ssh-connection force use of particular SSH protocol variant -4 -6 force use of IPv4 or IPv6 -C enable compression -i key private key file for user authentication -noagent disable use of Pageant -agent enable use of Pageant -no-trivial-auth disconnect if SSH authentication succeeds trivially -hostkey keyid manually specify a host key (may be repeated) -batch disable all interactive prompts -no-sanitise-stderr don't strip control chars from standard error -proxycmd command use 'command' as local proxy -unsafe allow server-side wildcards (DANGEROUS) -sftp force use of SFTP protocol -scp force use of SCP protocol -sshlog file -sshrawlog file log protocol details to a file -logoverwrite -logappend control what happens when a log file already exists (PSCP's interface is much like the Unix `scp' command, if you're familiar with that.) 5.2.1 The basics To receive (a) file(s) from a remote server: pscp [options] [user@]host:source target So to copy the file `/etc/hosts' from the server `example.com' as user `fred' to the file `c:\temp\example-hosts.txt', you would type: pscp fred@example.com:/etc/hosts c:\temp\example-hosts.txt To send (a) file(s) to a remote server: pscp [options] source [source...] [user@]host:target So to copy the local file `c:\documents\foo.txt' to the server `example.com' as user `fred' to the file `/tmp/foo' you would type: pscp c:\documents\foo.txt fred@example.com:/tmp/foo You can use wildcards to transfer multiple files in either direction, like this: pscp c:\documents\*.doc fred@example.com:docfiles pscp fred@example.com:source/*.c c:\source However, in the second case (using a wildcard for multiple remote files) you may see a warning saying something like `warning: remote host tried to write to a file called `terminal.c' when we requested a file called `*.c'. If this is a wildcard, consider upgrading to SSH-2 or using the `-unsafe' option. Renaming of this file has been disallowed'. This is due to a fundamental insecurity in the old-style SCP protocol: the client sends the wildcard string (`*.c') to the server, and the server sends back a sequence of file names that match the wildcard pattern. However, there is nothing to stop the server sending back a _different_ pattern and writing over one of your other files: if you request `*.c', the server might send back the file name `AUTOEXEC.BAT' and install a virus for you. Since the wildcard matching rules are decided by the server, the client cannot reliably verify that the filenames sent back match the pattern. PSCP will attempt to use the newer SFTP protocol (part of SSH-2) where possible, which does not suffer from this security flaw. If you are talking to an SSH-2 server which supports SFTP, you will never see this warning. (You can force use of the SFTP protocol, if available, with `-sftp' - see section 5.2.2.6.) If you really need to use a server-side wildcard with an SSH-1 server, you can use the `-unsafe' command line option with PSCP: pscp -unsafe fred@example.com:source/*.c c:\source This will suppress the warning message and the file transfer will happen. However, you should be aware that by using this option you are giving the server the ability to write to _any_ file in the target directory, so you should only use this option if you trust the server administrator not to be malicious (and not to let the server machine be cracked by malicious people). Alternatively, do any such download in a newly created empty directory. (Even in `unsafe' mode, PSCP will still protect you against the server trying to get out of that directory using pathnames including `..'.) 5.2.1.1 `user' The login name on the remote server. If this is omitted, and `host' is a PuTTY saved session, PSCP will use any username specified by that saved session. Otherwise, PSCP will attempt to use the local Windows username. 5.2.1.2 `host' The name of the remote server, or the name of an existing PuTTY saved session. In the latter case, the session's settings for hostname, port number, cipher type and username will be used. 5.2.1.3 `source' One or more source files. Wildcards are allowed. The syntax of wildcards depends on the system to which they apply, so if you are copying _from_ a Windows system _to_ a UNIX system, you should use Windows wildcard syntax (e.g. `*.*'), but if you are copying _from_ a UNIX system _to_ a Windows system, you would use the wildcard syntax allowed by your UNIX shell (e.g. `*'). If the source is a remote server and you do not specify a full pathname (in UNIX, a pathname beginning with a `/' (slash) character), what you specify as a source will be interpreted relative to your home directory on the remote server. 5.2.1.4 `target' The filename or directory to put the file(s). When copying from a remote server to a local host, you may wish simply to place the file(s) in the current directory. To do this, you should specify a target of `.'. For example: pscp fred@example.com:/home/tom/.emacs . ...would copy `/home/tom/.emacs' on the remote server to the current directory. As with the `source' parameter, if the target is on a remote server and is not a full path name, it is interpreted relative to your home directory on the remote server. 5.2.2 Options PSCP accepts all the general command line options supported by the PuTTY tools, except the ones which make no sense in a file transfer utility. See section 3.11.3 for a description of these options. (The ones not supported by PSCP are clearly marked.) PSCP also supports some of its own options. The following sections describe PSCP's specific command-line options. 5.2.2.1 `-ls' list remote files If the `-ls' option is given, no files are transferred; instead, remote files are listed. Only a hostname specification and optional remote file specification need be given. For example: pscp -ls fred@example.com:dir1 The SCP protocol does not contain within itself a means of listing files. If SCP is in use, this option therefore assumes that the server responds appropriately to the command `ls -la'; this may not work with all servers. If SFTP is in use, this option should work with all servers. 5.2.2.2 `-p' preserve file attributes By default, files copied with PSCP are timestamped with the date and time they were copied. The `-p' option preserves the original timestamp on copied files. 5.2.2.3 `-q' quiet, don't show statistics By default, PSCP displays a meter displaying the progress of the current transfer: mibs.tar | 168 kB | 84.0 kB/s | ETA: 00:00:13 | 13% The fields in this display are (from left to right), filename, size (in kilobytes) of file transferred so far, estimate of how fast the file is being transferred (in kilobytes per second), estimated time that the transfer will be complete, and percentage of the file so far transferred. The `-q' option to PSCP suppresses the printing of these statistics. 5.2.2.4 `-r' copies directories recursively By default, PSCP will only copy files. Any directories you specify to copy will be skipped, as will their contents. The `-r' option tells PSCP to descend into any directories you specify, and to copy them and their contents. This allows you to use PSCP to transfer whole directory structures between machines. 5.2.2.5 `-batch' avoid interactive prompts If you use the `-batch' option, PSCP will never give an interactive prompt while establishing the connection. If the server's host key is invalid, for example (see section 2.2), then the connection will simply be abandoned instead of asking you what to do next. This may help PSCP's behaviour when it is used in automated scripts: using `-batch', if something goes wrong at connection time, the batch job will fail rather than hang. 5.2.2.6 `-sftp', `-scp' force use of particular file transfer protocol As mentioned in section 5.2.1, there are two different file transfer protocols in use with SSH. Despite its name, PSCP (like many other ostensible scp clients) can use either of these protocols. The older SCP protocol does not have a written specification and leaves a lot of detail to the server platform. Wildcards are expanded on the server. The simple design means that any wildcard specification supported by the server platform (such as brace expansion) can be used, but also leads to interoperability issues such as with filename quoting (for instance, where filenames contain spaces), and also the security issue described in section 5.2.1. The newer SFTP protocol, which is usually associated with SSH- 2 servers, is specified in a more platform independent way, and leaves issues such as wildcard syntax up to the client. (PuTTY's SFTP wildcard syntax is described in section 6.2.2.) This makes it more consistent across platforms, more suitable for scripting and automation, and avoids security issues with wildcard matching. Normally PSCP will attempt to use the SFTP protocol, and only fall back to the SCP protocol if SFTP is not available on the server. The `-scp' option forces PSCP to use the SCP protocol or quit. The `-sftp' option forces PSCP to use the SFTP protocol or quit. When this option is specified, PSCP looks harder for an SFTP server, which may allow use of SFTP with SSH-1 depending on server setup. 5.2.2.7 `-no-sanitise-stderr': control error message sanitisation The `-no-sanitise-stderr' option will cause PSCP to pass through the server's standard-error stream literally, without stripping control characters from it first. This might be useful if the server were sending coloured error messages, but it also gives the server the ability to have unexpected effects on your terminal display. For more discussion, see section 7.2.3.5. 5.2.3 Return value PSCP returns an ERRORLEVEL of zero (success) only if the files were correctly transferred. You can test for this in a batch file, using code such as this: pscp file*.* user@hostname: if errorlevel 1 echo There was an error 5.2.4 Using public key authentication with PSCP Like PuTTY, PSCP can authenticate using a public key instead of a password. There are three ways you can do this. Firstly, PSCP can use PuTTY saved sessions in place of hostnames (see section 5.2.1.2). So you would do this: - Run PuTTY, and create a PuTTY saved session (see section 4.1.2) which specifies your private key file (see section 4.21.9). You will probably also want to specify a username to log in as (see section 4.15.1). - In PSCP, you can now use the name of the session instead of a hostname: type `pscp sessionname:file localfile', where `sessionname' is replaced by the name of your saved session. Secondly, you can supply the name of a private key file on the command line, with the `-i' option. See section 3.11.3.18 for more information. Thirdly, PSCP will attempt to authenticate using Pageant if Pageant is running (see chapter 9). So you would do this: - Ensure Pageant is running, and has your private key stored in it. - Specify a user and host name to PSCP as normal. PSCP will automatically detect Pageant and try to use the keys within it. For more general information on public-key authentication, see chapter 8. Chapter 6: Using PSFTP to transfer files securely ------------------------------------------------- PSFTP, the PuTTY SFTP client, is a tool for transferring files securely between computers using an SSH connection. PSFTP differs from PSCP in the following ways: - PSCP should work on virtually every SSH server. PSFTP uses the new SFTP protocol, which is a feature of SSH-2 only. (PSCP will also use this protocol if it can, but there is an SSH-1 equivalent it can fall back to if it cannot.) - PSFTP allows you to run an interactive file transfer session, much like the Windows `ftp' program. You can list the contents of directories, browse around the file system, issue multiple `get' and `put' commands, and eventually log out. By contrast, PSCP is designed to do a single file transfer operation and immediately terminate. 6.1 Starting PSFTP The usual way to start PSFTP is from a command prompt, much like PSCP. To do this, it will need either to be on your `PATH' or in your current directory. To add the directory containing PSFTP to your `PATH' environment variable, type into the console window: set PATH=C:\path\to\putty\directory;%PATH% Unlike PSCP, however, PSFTP has no complex command-line syntax; you just specify a host name and perhaps a user name: psftp server.example.com or perhaps psftp fred@server.example.com Alternatively, if you just type `psftp' on its own (or double-click the PSFTP icon in the Windows GUI), you will see the PSFTP prompt, and a message telling you PSFTP has not connected to any server: C:\>psftp psftp: no hostname specified; use "open host.name" to connect psftp> At this point you can type `open server.example.com' or `open fred@server.example.com' to start a session. PSFTP accepts all the general command line options supported by the PuTTY tools, except the ones which make no sense in a file transfer utility. See section 3.11.3 for a description of these options. (The ones not supported by PSFTP are clearly marked.) PSFTP also supports some of its own options. The following sections describe PSFTP's specific command-line options. 6.1.1 `-b': specify a file containing batch commands In normal operation, PSFTP is an interactive program which displays a command line and accepts commands from the keyboard. If you need to do automated tasks with PSFTP, you would probably prefer to specify a set of commands in advance and have them executed automatically. The `-b' option allows you to do this. You use it with a file name containing batch commands. For example, you might create a file called `myscript.scr' containing lines like this: cd /home/ftp/users/jeff del jam-old.tar.gz ren jam.tar.gz jam-old.tar.gz put jam.tar.gz chmod a+r jam.tar.gz and then you could run the script by typing psftp user@hostname -b myscript.scr When you run a batch script in this way, PSFTP will abort the script if any command fails to complete successfully. To change this behaviour, you can add the `-be' option (section 6.1.3). PSFTP will terminate after it finishes executing the batch script. 6.1.2 `-bc': display batch commands as they are run The `-bc' option alters what PSFTP displays while processing a batch script specified with `-b'. With the `-bc' option, PSFTP will display prompts and commands just as if the commands had been typed at the keyboard. So instead of seeing this: C:\>psftp fred@hostname -b batchfile Sent username "fred" Remote working directory is /home/fred Listing directory /home/fred/lib drwxrwsr-x 4 fred fred 1024 Sep 6 10:42 . drwxr-sr-x 25 fred fred 2048 Dec 14 09:36 .. drwxrwsr-x 3 fred fred 1024 Apr 17 2000 jed lrwxrwxrwx 1 fred fred 24 Apr 17 2000 timber drwxrwsr-x 2 fred fred 1024 Mar 13 2000 trn you might see this: C:\>psftp fred@hostname -bc -b batchfile Sent username "fred" Remote working directory is /home/fred psftp> dir lib Listing directory /home/fred/lib drwxrwsr-x 4 fred fred 1024 Sep 6 10:42 . drwxr-sr-x 25 fred fred 2048 Dec 14 09:36 .. drwxrwsr-x 3 fred fred 1024 Apr 17 2000 jed lrwxrwxrwx 1 fred fred 24 Apr 17 2000 timber drwxrwsr-x 2 fred fred 1024 Mar 13 2000 trn psftp> quit 6.1.3 `-be': continue batch processing on errors When running a batch file, this additional option causes PSFTP to continue processing even if a command fails to complete successfully. You might want this to happen if you wanted to delete a file and didn't care if it was already not present, for example. 6.1.4 `-batch': avoid interactive prompts If you use the `-batch' option, PSFTP will never give an interactive prompt while establishing the connection. If the server's host key is invalid, for example (see section 2.2), then the connection will simply be abandoned instead of asking you what to do next. This may help PSFTP's behaviour when it is used in automated scripts: using `-batch', if something goes wrong at connection time, the batch job will fail rather than hang. 6.1.4.1 `-no-sanitise-stderr': control error message sanitisation The `-no-sanitise-stderr' option will cause PSFTP to pass through the server's standard-error stream literally, without stripping control characters from it first. This might be useful if the server were sending coloured error messages, but it also gives the server the ability to have unexpected effects on your terminal display. For more discussion, see section 7.2.3.5. 6.2 Running PSFTP Once you have started your PSFTP session, you will see a `psftp>' prompt. You can now type commands to perform file-transfer functions. This section lists all the available commands. Any line starting with a # will be treated as a comment and ignored. 6.2.1 General quoting rules for PSFTP commands Most PSFTP commands are considered by the PSFTP command interpreter as a sequence of words, separated by spaces. For example, the command `ren oldfilename newfilename' splits up into three words: `ren' (the command name), `oldfilename' (the name of the file to be renamed), and `newfilename' (the new name to give the file). Sometimes you will need to specify file names that _contain_ spaces. In order to do this, you can surround the file name with double quotes. This works equally well for local file names and remote file names: psftp> get "spacey file name.txt" "save it under this name.txt" The double quotes themselves will not appear as part of the file names; they are removed by PSFTP and their only effect is to stop the spaces inside them from acting as word separators. If you need to _use_ a double quote (on some types of remote system, such as Unix, you are allowed to use double quotes in file names), you can do this by doubling it. This works both inside and outside double quotes. For example, this command psftp> ren ""this"" "a file with ""quotes"" in it" will take a file whose current name is `"this"' (with a double quote character at the beginning and the end) and rename it to a file whose name is `a file with "quotes" in it'. (The one exception to the PSFTP quoting rules is the `!' command, which passes its command line straight to Windows without splitting it up into words at all. See section 6.2.19.) 6.2.2 Wildcards in PSFTP Several commands in PSFTP support `wildcards' to select multiple files. For _local_ file specifications (such as the first argument to `put'), wildcard rules for the local operating system are used. For instance, PSFTP running on Windows might require the use of `*.*' where PSFTP on Unix would need `*'. For _remote_ file specifications (such as the first argument to `get'), PSFTP uses a standard wildcard syntax (similar to POSIX wildcards): - `*' matches any sequence of characters (including a zero-length sequence). - `?' matches exactly one character. - `[abc]' matches exactly one character which can be a, b, or c. `[a-z]' matches any character in the range a to z. `[^abc]' matches a single character that is _not_ a, b, or c. Special cases: `[-a]' matches a literal hyphen (-) or a; `[^-a]' matches all other characters. `[a^]' matches a literal caret (^) or a. - `\' (backslash) before any of the above characters (or itself) removes that character's special meaning. A leading period (.) on a filename is not treated specially, unlike in some Unix contexts; `get *' will fetch all files, whether or not they start with a leading period. 6.2.3 The `open' command: start a session If you started PSFTP by double-clicking in the GUI, or just by typing `psftp' at the command line, you will need to open a connection to an SFTP server before you can issue any other commands (except `help' and `quit'). To create a connection, type `open host.name', or if you need to specify a user name as well you can type `open user@host.name'. You can optionally specify a port as well: `open user@host.name 22'. Once you have issued this command, you will not be able to issue it again, _even_ if the command fails (for example, if you mistype the host name or the connection times out). So if the connection is not opened successfully, PSFTP will terminate immediately. 6.2.4 The `quit' command: end your session When you have finished your session, type the command `quit' to close the connection, terminate PSFTP and return to the command line (or just close the PSFTP console window if you started it from the GUI). You can also use the `bye' and `exit' commands, which have exactly the same effect. 6.2.5 The `close' command: close your connection If you just want to close the network connection but keep PSFTP running, you can use the `close' command. You can then use the `open' command to open a new connection. 6.2.6 The `help' command: get quick online help If you type `help', PSFTP will give a short list of the available commands. If you type `help' with a command name - for example, `help get' - then PSFTP will give a short piece of help on that particular command. 6.2.7 The `cd' and `pwd' commands: changing the remote working directory PSFTP maintains a notion of your `working directory' on the server. This is the default directory that other commands will operate on. For example, if you type `get filename.dat' then PSFTP will look for `filename.dat' in your remote working directory on the server. To change your remote working directory, use the `cd' command. If you don't provide an argument, `cd' will return you to your home directory on the server (more precisely, the remote directory you were in at the start of the connection). To display your current remote working directory, type `pwd'. 6.2.8 The `lcd' and `lpwd' commands: changing the local working directory As well as having a working directory on the remote server, PSFTP also has a working directory on your local machine (just like any other Windows process). This is the default local directory that other commands will operate on. For example, if you type `get filename.dat' then PSFTP will save the resulting file as `filename.dat' in your local working directory. To change your local working directory, use the `lcd' command. To display your current local working directory, type `lpwd'. 6.2.9 The `get' command: fetch a file from the server To download a file from the server and store it on your local PC, you use the `get' command. In its simplest form, you just use this with a file name: get myfile.dat If you want to store the file locally under a different name, specify the local file name after the remote one: get myfile.dat newname.dat This will fetch the file on the server called `myfile.dat', but will save it to your local machine under the name `newname.dat'. To fetch an entire directory recursively, you can use the `-r' option: get -r mydir get -r mydir newname (If you want to fetch a file whose name starts with a hyphen, you may have to use the `--' special argument, which stops `get' from interpreting anything as a switch after it. For example, `get -- - silly-name-'.) 6.2.10 The `put' command: send a file to the server To upload a file to the server from your local PC, you use the `put' command. In its simplest form, you just use this with a file name: put myfile.dat If you want to store the file remotely under a different name, specify the remote file name after the local one: put myfile.dat newname.dat This will send the local file called `myfile.dat', but will store it on the server under the name `newname.dat'. To send an entire directory recursively, you can use the `-r' option: put -r mydir put -r mydir newname (If you want to send a file whose name starts with a hyphen, you may have to use the `--' special argument, which stops `put' from interpreting anything as a switch after it. For example, `put -- - silly-name-'.) 6.2.11 The `mget' and `mput' commands: fetch or send multiple files `mget' works almost exactly like `get', except that it allows you to specify more than one file to fetch at once. You can do this in two ways: - by giving two or more explicit file names (`mget file1.txt file2.txt') - by using a wildcard (`mget *.txt'). Every argument to `mget' is treated as the name of a file to fetch (unlike `get', which will interpret at most one argument like that, and a second argument will be treated as an alternative name under which to store the retrieved file), or a wildcard expression matching more than one file. The `-r' and `--' options from `get' are also available with `mget'. `mput' is similar to `put', with the same differences. 6.2.12 The `reget' and `reput' commands: resuming file transfers If a file transfer fails half way through, and you end up with half the file stored on your disk, you can resume the file transfer using the `reget' and `reput' commands. These work exactly like the `get' and `put' commands, but they check for the presence of the half- written destination file and start transferring from where the last attempt left off. The syntax of `reget' and `reput' is exactly the same as the syntax of `get' and `put': reget myfile.dat reget myfile.dat newname.dat reget -r mydir These commands are intended mainly for resuming interrupted transfers. They assume that the remote file or directory structure has not changed in any way; if there have been changes, you may end up with corrupted files. In particular, the `-r' option will not pick up changes to files or directories already transferred in full. 6.2.13 The `dir' command: list remote files To list the files in your remote working directory, just type `dir'. You can also list the contents of a different directory by typing `dir' followed by the directory name: dir /home/fred dir sources And you can list a subset of the contents of a directory by providing a wildcard: dir /home/fred/*.txt dir sources/*.c The `ls' command works exactly the same way as `dir'. 6.2.14 The `chmod' command: change permissions on remote files PSFTP allows you to modify the file permissions on files and directories on the server. You do this using the `chmod' command, which works very much like the Unix `chmod' command. The basic syntax is `chmod modes file', where `modes' represents a modification to the file permissions, and `file' is the filename to modify. You can specify multiple files or wildcards. For example: chmod go-rwx,u+w privatefile chmod a+r public* chmod 640 groupfile1 groupfile2 The `modes' parameter can be a set of octal digits in the Unix style. (If you don't know what this means, you probably don't want to be using it!) Alternatively, it can be a list of permission modifications, separated by commas. Each modification consists of: - The people affected by the modification. This can be `u' (the owning user), `g' (members of the owning group), or `o' (everybody else - `others'), or some combination of those. It can also be `a' (`all') to affect everybody at once. - A `+' or `-' sign, indicating whether permissions are to be added or removed. - The actual permissions being added or removed. These can be `r' (permission to read the file), `w' (permission to write to the file), and `x' (permission to execute the file, or in the case of a directory, permission to access files within the directory). So the above examples would do: - The first example: `go-rwx' removes read, write and execute permissions for members of the owning group and everybody else (so the only permissions left are the ones for the file owner). `u+w' adds write permission for the file owner. - The second example: `a+r' adds read permission for everybody to all files and directories starting with `public'. In addition to all this, there are a few extra special cases for Unix systems. On non-Unix systems these are unlikely to be useful: - You can specify `u+s' and `u-s' to add or remove the Unix set- user-ID bit. This is typically only useful for special purposes; refer to your Unix documentation if you're not sure about it. - You can specify `g+s' and `g-s' to add or remove the Unix set- group-ID bit. On a file, this works similarly to the set-user- ID bit (see your Unix documentation again); on a directory it ensures that files created in the directory are accessible by members of the group that owns the directory. - You can specify `+t' and `-t' to add or remove the Unix `sticky bit'. When applied to a directory, this means that the owner of a file in that directory can delete the file (whereas normally only the owner of the _directory_ would be allowed to). 6.2.15 The `del' command: delete remote files To delete a file on the server, type `del' and then the filename or filenames: del oldfile.dat del file1.txt file2.txt del *.o Files will be deleted without further prompting, even if multiple files are specified. `del' will only delete files. You cannot use it to delete directories; use `rmdir' for that. The `rm' command works exactly the same way as `del'. 6.2.16 The `mkdir' command: create remote directories To create a directory on the server, type `mkdir' and then the directory name: mkdir newstuff You can specify multiple directories to create at once: mkdir dir1 dir2 dir3 6.2.17 The `rmdir' command: remove remote directories To remove a directory on the server, type `rmdir' and then the directory name or names: rmdir oldstuff rmdir *.old ancient Directories will be deleted without further prompting, even if multiple directories are specified. Most SFTP servers will probably refuse to remove a directory if the directory has anything in it, so you will need to delete the contents first. 6.2.18 The `mv' command: move and rename remote files To rename a single file on the server, type `mv', then the current file name, and then the new file name: mv oldfile newname You can also move the file into a different directory and change the name: mv oldfile dir/newname To move one or more files into an existing subdirectory, specify the files (using wildcards if desired), and then the destination directory: mv file dir mv file1 dir1/file2 dir2 mv *.c *.h .. The `rename' and `ren' commands work exactly the same way as `mv'. 6.2.19 The `!' command: run a local Windows command You can run local Windows commands using the `!' command. This is the only PSFTP command that is not subject to the command quoting rules given in section 6.2.1. If any command line begins with the `!' character, then the rest of the line will be passed straight to Windows without further translation. For example, if you want to move an existing copy of a file out of the way before downloading an updated version, you might type: psftp> !ren myfile.dat myfile.bak psftp> get myfile.dat using the Windows `ren' command to rename files on your local PC. 6.3 Using public key authentication with PSFTP Like PuTTY, PSFTP can authenticate using a public key instead of a password. There are three ways you can do this. Firstly, PSFTP can use PuTTY saved sessions in place of hostnames. So you might do this: - Run PuTTY, and create a PuTTY saved session (see section 4.1.2) which specifies your private key file (see section 4.21.9). You will probably also want to specify a username to log in as (see section 4.15.1). - In PSFTP, you can now use the name of the session instead of a hostname: type `psftp sessionname', where `sessionname' is replaced by the name of your saved session. Secondly, you can supply the name of a private key file on the command line, with the `-i' option. See section 3.11.3.18 for more information. Thirdly, PSFTP will attempt to authenticate using Pageant if Pageant is running (see chapter 9). So you would do this: - Ensure Pageant is running, and has your private key stored in it. - Specify a user and host name to PSFTP as normal. PSFTP will automatically detect Pageant and try to use the keys within it. For more general information on public-key authentication, see chapter 8. Chapter 7: Using the command-line connection tool Plink ------------------------------------------------------- Plink is a command-line connection tool similar to UNIX `ssh'. It is mostly used for automated operations, such as making CVS access a repository on a remote server. Plink is probably not what you want if you want to run an interactive session in a console window. 7.1 Starting Plink Plink is a command line application. This means that you cannot just double-click on its icon to run it and instead you have to bring up a console window. In Windows 95, 98, and ME, this is called an `MS-DOS Prompt', and in Windows NT, 2000, and XP, it is called a `Command Prompt'. It should be available from the Programs section of your Start Menu. In order to use Plink, the file `plink.exe' will need either to be on your `PATH' or in your current directory. To add the directory containing Plink to your `PATH' environment variable, type into the console window: set PATH=C:\path\to\putty\directory;%PATH% This will only work for the lifetime of that particular console window. To set your `PATH' more permanently on Windows NT, 2000, and XP, use the Environment tab of the System Control Panel. On Windows 95, 98, and ME, you will need to edit your `AUTOEXEC.BAT' to include a `set' command like the one above. 7.2 Using Plink This section describes the basics of how to use Plink for interactive logins and for automated processes. Once you've got a console window to type into, you can just type `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:\>plink Plink: command-line connection utility Release 0.76 Usage: plink [options] [user@]host [command] ("host" can also be a PuTTY saved session name) Options: -V print version information and exit -pgpfp print PGP key fingerprints and exit -v show verbose messages -load sessname Load settings from saved session -ssh -telnet -rlogin -raw -serial force use of a particular protocol -ssh-connection force use of the bare ssh-connection protocol -P port connect to specified port -l user connect with specified username -batch disable all interactive prompts -proxycmd command use 'command' as local proxy -sercfg configuration-string (e.g. 19200,8,n,1,X) Specify the serial configuration (serial only) The following options only apply to SSH connections: -pw passw login with specified password -D [listen-IP:]listen-port Dynamic SOCKS-based port forwarding -L [listen-IP:]listen-port:host:port Forward local port to remote address -R [listen-IP:]listen-port:host:port Forward remote port to local address -X -x enable / disable X11 forwarding -A -a enable / disable agent forwarding -t -T enable / disable pty allocation -1 -2 force use of particular SSH protocol version -4 -6 force use of IPv4 or IPv6 -C enable compression -i key private key file for user authentication -noagent disable use of Pageant -agent enable use of Pageant -no-trivial-auth disconnect if SSH authentication succeeds trivially -noshare disable use of connection sharing -share enable use of connection sharing -hostkey keyid manually specify a host key (may be repeated) -sanitise-stderr, -sanitise-stdout, -no-sanitise-stderr, -no-sanitise-stdout do/don't strip control chars from standard output/error -no-antispoof omit anti-spoofing prompt after authentication -m file read remote command(s) from file -s remote command is an SSH subsystem (SSH-2 only) -N don't start a shell/command (SSH-2 only) -nc host:port open tunnel in place of session (SSH-2 only) -sshlog file -sshrawlog file log protocol details to a file -logoverwrite -logappend control what happens when a log file already exists -shareexists test whether a connection-sharing upstream exists Once this works, you are ready to use Plink. 7.2.1 Using Plink for interactive logins To make a simple interactive connection to a remote server, just type `plink' and then the host name: C:\>plink login.example.com Debian GNU/Linux 2.2 flunky.example.com flunky login: You should then be able to log in as normal and run a session. The output sent by the server will be written straight to your command prompt window, which will most likely not interpret terminal control codes in the way the server expects it to. So if you run any full- screen applications, for example, you can expect to see strange characters appearing in your window. Interactive connections like this are not the main point of Plink. In order to connect with a different protocol, you can give the command line options `-ssh', `-ssh-connection', `-telnet', `- rlogin', or `-raw'. To make an SSH connection, for example: C:\>plink -ssh login.example.com login as: If you have already set up a PuTTY saved session, then instead of supplying a host name, you can give the saved session name. This allows you to use public-key authentication, specify a user name, and use most of the other features of PuTTY: C:\>plink my-ssh-session Sent username "fred" Authenticating with public key "fred@winbox" Last login: Thu Dec 6 19:25:33 2001 from :0.0 fred@flunky:~$ (You can also use the `-load' command-line option to load a saved session; see section 3.11.3.1. If you use `-load', the saved session exists, and it specifies a hostname, you cannot also specify a `host' or `user@host' argument - it will be treated as part of the remote command.) 7.2.2 Using Plink for automated connections More typically Plink is used with the SSH protocol, to enable you to talk directly to a program running on the server. To do this you have to ensure Plink is _using_ the SSH protocol. You can do this in several ways: - Use the `-ssh' option as described in section 7.2.1. - Set up a PuTTY saved session that describes the server you are connecting to, and that also specifies the protocol as SSH. - Set the Windows environment variable `PLINK_PROTOCOL' to the word `ssh'. Usually Plink is not invoked directly by a user, but run automatically by another process. Therefore you typically do not want Plink to prompt you for a user name or a password. Next, you are likely to need to avoid the various interactive prompts Plink can produce. You might be prompted to verify the host key of the server you're connecting to, to enter a user name, or to enter a password. To avoid being prompted for the server host key when using Plink for an automated connection, you can first make a _manual_ connection (using either of PuTTY or Plink) to the same server, verify the host key (see section 2.2 for more information), and select `Accept' to add the host key to the Registry. After that, Plink commands connecting to that server should not give a host key prompt unless the host key changes. Alternatively, you can specify the appropriate host key(s) on Plink's command line every time you use it; see section 3.11.3.21. To avoid being prompted for a user name, you can: - Use the `-l' option to specify a user name on the command line. For example, `plink login.example.com -l fred'. - Set up a PuTTY saved session that describes the server you are connecting to, and that also specifies the username to log in as (see section 4.15.1). To avoid being prompted for a password, you should almost certainly set up public-key authentication. (See chapter 8 for a general introduction to public-key authentication.) Again, you can do this in two ways: - Set up a PuTTY saved session that describes the server you are connecting to, and that also specifies a private key file (see section 4.21.9). For this to work without prompting, your private key will need to have no passphrase. - Store the private key in Pageant. See chapter 9 for further information. Once you have done all this, you should be able to run a remote command on the SSH server machine and have it execute automatically with no prompting: C:\>plink login.example.com -l fred echo hello, world hello, world C:\> Or, if you have set up a saved session with all the connection details: C:\>plink mysession echo hello, world hello, world C:\> Then you can set up other programs to run this Plink command and talk to it as if it were a process on the server machine. 7.2.3 Plink command line options Plink accepts all the general command line options supported by the PuTTY tools. See section 3.11.3 for a description of these options. Plink also supports some of its own options. The following sections describe Plink's specific command-line options. 7.2.3.1 `-batch': disable all interactive prompts If you use the `-batch' option, Plink will never give an interactive prompt while establishing the connection. If the server's host key is invalid, for example (see section 2.2), then the connection will simply be abandoned instead of asking you what to do next. This may help Plink's behaviour when it is used in automated scripts: using `-batch', if something goes wrong at connection time, the batch job will fail rather than hang. 7.2.3.2 `-s': remote command is SSH subsystem If you specify the `-s' option, Plink passes the specified command as the name of an SSH `subsystem' rather than an ordinary command line. (This option is only meaningful with the SSH-2 protocol.) 7.2.3.3 `-share': Test and try to share an existing connection. This option tris to detect if an existing connection can be shared (See section 4.17.5 for more information about SSH connection sharing.) and reuses that connection. A Plink invocation of the form: plink -share will test whether there is currently a viable `upstream' for the session in question, which can be specified using any syntax you'd normally use with Plink to make an actual connection (a host/port number, a bare saved session name, `-load', etc). If no `upstream' viable session is found and `-share' is specified, this connection will be become the `upstream' connection for subsequent connection sharing tries. (This option is only meaningful with the SSH-2 protocol.) 7.2.3.4 `-shareexists': test for connection-sharing upstream This option does not make a new connection; instead it allows testing for the presence of an existing connection that can be shared. (See section 4.17.5 for more information about SSH connection sharing.) A Plink invocation of the form: plink -shareexists will test whether there is currently a viable `upstream' for the session in question, which can be specified using any syntax you'd normally use with Plink to make an actual connection (a host/port number, a bare saved session name, `-load', etc). It returns a zero exit status if a usable `upstream' exists, nonzero otherwise. (This option is only meaningful with the SSH-2 protocol.) 7.2.3.5 `-sanitise-'_stream_: control output sanitisation In some situations, Plink applies a sanitisation pass to the output received from the server, to strip out control characters such as backspace and the escape character. The idea of this is to prevent remote processes from sending confusing escape sequences through the standard error channel when Plink is being used as a transport for something like git or CVS. If the server actually wants to send an error message, it will probably be plain text; if the server abuses that channel to try to write over unexpected parts of your terminal display, Plink will try to stop it. By default, this only happens for output channels which are sent to a Windows console device, or a Unix terminal device. (Any output stream going somewhere else is likely to be needed by an 8- bit protocol and must not be tampered with at all.) It also stops happening if you tell Plink to allocate a remote pseudo-terminal (see section 3.11.3.12 and section 4.23.1), on the basis that in that situation you often _want_ escape sequences from the server to go to your terminal. But in case Plink guesses wrong about whether you want this sanitisation, you can override it in either direction, using one of these options: `-sanitise-stderr' Sanitise server data written to Plink's standard error channel, regardless of terminals and consoles and remote ptys. `-no-sanitise-stderr' Do not sanitise server data written to Plink's standard error channel. `-sanitise-stdout' Sanitise server data written to Plink's standard output channel. `-no-sanitise-stdout' Do not sanitise server data written to Plink's standard output channel. 7.2.3.6 -no-antispoof: turn off authentication spoofing protection prompt In SSH, some possible server authentication methods require user input (for example, password authentication, or entering a private key passphrase), and others do not (e.g. a private key held in Pageant). If you use Plink to run an interactive login session, and if Plink authenticates without needing any user interaction, and if the server is malicious or compromised, it could try to trick you into giving it authentication data that should not go to the server (such as your private key passphrase), by sending what _looks_ like one of Plink's local prompts, as if Plink had not already authenticated. To protect against this, Plink's default policy is to finish the authentication phase with a final trivial prompt looking like this: Access granted. Press Return to begin session. so that if you saw anything that looked like an authentication prompt _after_ that line, you would know it was not from Plink. That extra interactive step is inconvenient. So Plink will turn it off in as many situations as it can: - If Plink's standard input is not pointing at a console or terminal device - for example, if you're using Plink as a transport for some automated application like version control - then you _can't_ type passphrases into the server anyway. In that situation, Plink won't try to protect you from the server trying to fool you into doing so. - If Plink is in batch mode (see section 7.2.2), then it _never_ does any interactive authentication. So anything looking like an interactive authentication prompt is automatically suspect, and so Plink omits the anti-spoofing prompt. But if you still find the protective prompt inconvenient, and you trust the server not to try a trick like this, you can turn it off using the `-no-antispoof' option. 7.3 Using Plink in batch files and scripts Once you have set up Plink to be able to log in to a remote server without any interactive prompting (see section 7.2.2), you can use it for lots of scripting and batch purposes. For example, to start a backup on a remote machine, you might use a command like: plink root@myserver /etc/backups/do-backup.sh Or perhaps you want to fetch all system log lines relating to a particular web area: plink mysession grep /~fred/ /var/log/httpd/access.log > fredlog Any non-interactive command you could usefully run on the server command line, you can run in a batch file using Plink in this way. 7.4 Using Plink with CVS To use Plink with CVS, you need to set the environment variable `CVS_RSH' to point to Plink: set CVS_RSH=\path\to\plink.exe You also need to arrange to be able to connect to a remote host without any interactive prompts, as described in section 7.2.2. You should then be able to run CVS as follows: cvs -d :ext:user@sessionname:/path/to/repository co module If you specified a username in your saved session, you don't even need to specify the `user' part of this, and you can just say: cvs -d :ext:sessionname:/path/to/repository co module 7.5 Using Plink with WinCVS Plink can also be used with WinCVS. Firstly, arrange for Plink to be able to connect to a remote host non-interactively, as described in section 7.2.2. Then, in WinCVS, bring up the `Preferences' dialogue box from the _Admin_ menu, and switch to the `Ports' tab. Tick the box there labelled `Check for an alternate rsh name' and in the text entry field to the right enter the full path to `plink.exe'. Select `OK' on the `Preferences' dialogue box. Next, select `Command Line' from the WinCVS `Admin' menu, and type a CVS command as in section 7.4, for example: cvs -d :ext:user@hostname:/path/to/repository co module or (if you're using a saved session): cvs -d :ext:user@sessionname:/path/to/repository co module Select the folder you want to check out to with the `Change Folder' button, and click `OK' to check out your module. Once you've got modules checked out, WinCVS will happily invoke plink from the GUI for CVS operations. Chapter 8: Using public keys for SSH authentication --------------------------------------------------- 8.1 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 secure and more flexible, but more difficult to set up. In conventional password authentication, you prove you are who you claim to be by proving that you know the correct password. The only way to prove you know the password is to tell the server what you think the password is. This means that if the server has been hacked, or _spoofed_ (see section 2.2), an attacker can learn your password. Public key authentication solves this problem. You generate a _key pair_, consisting of a public key (which everybody is allowed to know) and a private key (which you keep secret and do not give to anybody). The private key is able to generate _signatures_. A signature created using your private key cannot be forged by anybody who does not have that key; but anybody who has your public key can verify that a particular signature is genuine. So you generate a key pair on your own computer, and you copy the public key to the server. Then, when the server asks you to prove who you are, PuTTY can generate a signature using your private key. The server can verify that signature (since it has your public key) and allow you to log in. Now if the server is hacked or spoofed, the attacker does not gain your private key or password; they only gain one signature. And signatures cannot be re-used, so they have gained nothing. There is a problem with this: if your private key is stored unprotected on your own computer, then anybody who gains access to _that_ will be able to generate signatures as if they were you. So they will be able to log in to your server under your account. For this reason, your private key is usually _encrypted_ when it is stored on your local machine, using a passphrase of your choice. In order to generate a signature, PuTTY must decrypt the key, so you have to type your passphrase. This can make public-key authentication less convenient than password authentication: every time you log in to the server, instead of typing a short password, you have to type a longer passphrase. One solution to this is to use an _authentication agent_, a separate program which holds decrypted private keys and generates signatures on request. PuTTY's authentication agent is called Pageant. When you begin a Windows session, you start Pageant and load your private key into it (typing your passphrase once). For the rest of your session, you can start PuTTY any number of times and Pageant will automatically generate signatures without you having to do anything. When you close your Windows session, Pageant shuts down, without ever having stored your decrypted private key on disk. Many people feel this is a good compromise between security and convenience. See chapter 9 for further details. There is more than one public-key algorithm available. The most common are RSA and ECDSA, but others exist, notably DSA (otherwise known as DSS), the USA's federal Digital Signature Standard. The key types supported by PuTTY are described in section 8.2.2. 8.2 Using PuTTYgen, the PuTTY key generator PuTTYgen is a key generator. It generates pairs of public and private keys to be used with PuTTY, PSCP, and Plink, as well as the PuTTY authentication agent, Pageant (see chapter 9). PuTTYgen generates RSA, DSA, ECDSA, and EdDSA keys. When you run PuTTYgen you will see a window where you have two main choices: `Generate', to generate a new public/private key pair, or `Load' to load in an existing private key. 8.2.1 Generating a new key This is a general outline of the procedure for generating a new key pair. The following sections describe the process in more detail. - First, you need to select which type of key you want to generate, and also select the strength of the key. This is described in more detail in section 8.2.2 and section 8.2.3. - Then press the `Generate' button, to actually generate the key. Section 8.2.5 describes this step. - Once you have generated the key, select a comment field (section 8.2.7) and a passphrase (section 8.2.8). - Now you're ready to save the private key to disk; press the `Save private key' button. (See section 8.2.9). Your key pair is now ready for use. You may also want to copy the public key to your server, either by copying it out of the `Public key for pasting into OpenSSH authorized_keys file' box (see section 8.2.11), or by using the `Save public key' button (section 8.2.10). However, you don't need to do this immediately; if you want, you can load the private key back into PuTTYgen later (see section 8.2.13) and the public key will be available for copying and pasting again. Section 8.3 describes the typical process of configuring PuTTY to attempt public-key authentication, and configuring your SSH server to accept it. 8.2.2 Selecting the type of key Before generating a key pair using PuTTYgen, you need to select which type of key you need. The current version of the SSH protocol, SSH-2, supports several different key types, although specific servers may not support all of them. PuTTYgen can generate: - An RSA key for use with the SSH-2 protocol. - A DSA key for use with the SSH-2 protocol. - An ECDSA (elliptic curve DSA) key for use with the SSH-2 protocol. - An EdDSA key (Edwards-curve DSA, another elliptic curve algorithm) for use with the SSH-2 protocol. PuTTYgen can also generate an RSA key suitable for use with the old SSH-1 protocol (which only supports RSA); for this, you need to select the `SSH-1 (RSA)' option. Since the SSH-1 protocol is no longer considered secure, it's rare to need this option. 8.2.3 Selecting the size (strength) of the key The `Number of bits' input box allows you to choose the strength of the key PuTTYgen will generate. - For RSA and DSA, 2048 bits should currently be sufficient for most purposes. - For ECDSA, only 256, 384, and 521 bits are supported. (ECDSA offers equivalent security to RSA with smaller key sizes.) - For EdDSA, the only valid sizes are 255 bits (these keys are also known as `Ed25519' and are commonly used) and 448 bits (`Ed448', which is much less common at the time of writing). (256 is also accepted for backward compatibility, but the effect is the same as 255.) 8.2.4 Selecting the prime generation method On the `Key' menu, you can also optionally change the method for generating the prime numbers used in the generated key. This is used for RSA and DSA keys only. (The other key types don't require generating prime numbers at all.) The prime-generation method does not affect compatibility: a key generated with any of these methods will still work with all the same SSH servers. If you don't care about this, it's entirely sensible to leave it on the default setting. The available methods are: - Use probable primes (fast) - Use proven primes (slower) - Use proven primes with even distribution (slowest) The `probable primes' method sounds unsafe, but it's the most commonly used prime-generation strategy. There is in theory a possibility that it might accidentally generate a number that isn't prime, but the software does enough checking to make that probability vanishingly small (less than 1 in 2^80, or 1 in 10^24). So, in practice, nobody worries about it very much. The other methods cause PuTTYgen to use numbers that it is _sure_ are prime, because it generates the output number together with a proof of its primality. This takes more effort, but it eliminates that theoretical risk in the probabilistic method. You might choose to switch from probable to proven primes if you have a local security standard that demands it, or if you don't trust the probabilistic argument for the safety of the usual method. For RSA keys, there's also an option on the `Key' menu to use `strong' primes as the prime factors of the public key. A `strong' prime is a prime number chosen to have a particular structure that makes certain factoring algorithms more difficult to apply, so some security standards recommend their use. However, the most modern factoring algorithms are unaffected, so this option is probably not worth turning on _unless_ you have a local standard that recommends it. 8.2.5 The `Generate' button Once you have chosen the type of key you want, and the strength of the key, press the `Generate' button and PuTTYgen will begin the process of actually generating the key. First, a progress bar will appear and PuTTYgen will ask you to move the mouse around to generate randomness. Wave the mouse in circles over the blank area in the PuTTYgen window, and the progress bar will gradually fill up as PuTTYgen collects enough randomness. You don't need to wave the mouse in particularly imaginative patterns (although it can't hurt); PuTTYgen will collect enough randomness just from the fine detail of _exactly_ how far the mouse has moved each time Windows samples its position. When the progress bar reaches the end, PuTTYgen will begin creating the key. The progress bar will reset to the start, and gradually move up again to track the progress of the key generation. It will not move evenly, and may occasionally slow down to a stop; this is unfortunately unavoidable, because key generation is a random process and it is impossible to reliably predict how long it will take. When the key generation is complete, a new set of controls will appear in the window to indicate this. 8.2.6 The `Key fingerprint' box The `Key fingerprint' box shows you a fingerprint value for the generated key. This is derived cryptographically from the _public_ key value, so it doesn't need to be kept secret; it is supposed to be more manageable for human beings than the public key itself. The fingerprint value is intended to be cryptographically secure, in the sense that it is computationally infeasible for someone to invent a second key with the same fingerprint, or to find a key with a particular fingerprint. So some utilities, such as the Pageant key list box (see section 9.2.1) and the Unix `ssh-add' utility, will list key fingerprints rather than the whole public key. By default, PuTTYgen will display fingerprints in the `SHA256' format. If you need to see the fingerprint in the older `MD5' format (which looks like `aa:bb:cc:...'), you can choose `Show fingerprint as MD5' from the `Key' menu, but bear in mind that this is less cryptographically secure; it may be feasible for an attacker to create a key with the same fingerprint as yours. 8.2.7 Setting a comment for your key If you have more than one key and use them for different purposes, you don't need to memorise the key fingerprints in order to tell them apart. PuTTYgen allows you to enter a _comment_ for your key, which will be displayed whenever PuTTY or Pageant asks you for the passphrase. The default comment format, if you don't specify one, contains the key type and the date of generation, such as `rsa-key-20011212'. Another commonly used approach is to use your name and the name of the computer the key will be used on, such as `simon@simons-pc'. To alter the key comment, just type your comment text into the `Key comment' box before saving the private key. If you want to change the comment later, you can load the private key back into PuTTYgen, change the comment, and save it again. 8.2.8 Setting a passphrase for your key The `Key passphrase' and `Confirm passphrase' boxes allow you to choose a passphrase for your key. The passphrase will be used to encrypt the key on disk, so you will not be able to use the key without first entering the passphrase. When you save the key, PuTTYgen will check that the `Key passphrase' and `Confirm passphrase' boxes both contain exactly the same passphrase, and will refuse to save the key otherwise. If you leave the passphrase fields blank, the key will be saved unencrypted. You should _not_ do this without good reason; if you do, your private key file on disk will be all an attacker needs to gain access to any machine configured to accept that key. If you want to be able to log in without having to type a passphrase every time, you should consider using Pageant (chapter 9) so that your decrypted key is only held in memory rather than on disk. Under special circumstances you may genuinely _need_ to use a key with no passphrase; for example, if you need to run an automated batch script that needs to make an SSH connection, you can't be there to type the passphrase. In this case we recommend you generate a special key for each specific batch script (or whatever) that needs one, and on the server side you should arrange that each key is _restricted_ so that it can only be used for that specific purpose. The documentation for your SSH server should explain how to do this (it will probably vary between servers). Choosing a good passphrase is difficult. Just as you shouldn't use a dictionary word as a password because it's easy for an attacker to run through a whole dictionary, you should not use a song lyric, quotation or other well-known sentence as a passphrase. DiceWare (www.diceware.com) recommends using at least five words each generated randomly by rolling five dice, which gives over 2^64 possible passphrases and is probably not a bad scheme. If you want your passphrase to make grammatical sense, this cuts down the possibilities a lot and you should use a longer one as a result. _Do not forget your passphrase_. There is no way to recover it. 8.2.9 Saving your private key to a disk file Once you have generated a key, set a comment field and set a passphrase, you are ready to save your private key to disk. Press the `Save private key' button. PuTTYgen will put up a dialog box asking you where to save the file. Select a directory, type in a file name, and press `Save'. This file is in PuTTY's native format (`*.PPK'); it is the one you will need to tell PuTTY to use for authentication (see section 4.21.9) or tell Pageant to load (see section 9.2.2). (You can optionally change some details of the PPK format for your saved key files; see section 8.2.12. But The defaults should be fine for most purposes.) 8.2.10 Saving your public key to a disk file RFC 4716 specifies a standard format for storing SSH-2 public keys on disk. Some SSH servers (such as ssh.com's) require a public key in this format in order to accept authentication with the corresponding private key. (Others, such as OpenSSH, use a different format; see section 8.2.11.) To save your public key in the SSH-2 standard format, press the `Save public key' button in PuTTYgen. PuTTYgen will put up a dialog box asking you where to save the file. Select a directory, type in a file name, and press `Save'. You will then probably want to copy the public key file to your SSH server machine. See section 8.3 for general instructions on configuring public-key authentication once you have generated a key. If you use this option with an SSH-1 key, the file PuTTYgen saves will contain exactly the same text that appears in the `Public key for pasting' box. This is the only existing standard for SSH-1 public keys. 8.2.11 `Public key for pasting into OpenSSH authorized_keys file' The OpenSSH server, among others, requires your public key to be given to it in a one-line format before it will accept authentication with your private key. (SSH-1 servers also used this method.) The `Public key for pasting into OpenSSH authorized_keys file' gives the public-key data in the correct one-line format. Typically you will want to select the entire contents of the box using the mouse, press Ctrl+C to copy it to the clipboard, and then paste the data into a PuTTY session which is already connected to the server. See section 8.3 for general instructions on configuring public-key authentication once you have generated a key. 8.2.12 Parameters for saving key files Selecting `Parameters for saving key files...' from the `Key' menu lets you adjust some aspects of PPK-format private key files stored on disk. None of these options affect compatibility with SSH servers. In most cases, it's entirely sensible to leave all of these at their default settings. 8.2.12.1 PPK file version This defaults to version 3, which is fine for most uses. You might need to select PPK version 2 if you need your private key file to be loadable in older versions of PuTTY (0.74 and older), or in other tools which do not yet support the version 3 format (which was introduced in 2021). The version 2 format is less resistant to brute-force decryption, and doesn't support any of the following options to control that. 8.2.12.2 Options affecting passphrase hashing All of the following options only affect keys saved with passphrases. They control how much work is required to decrypt the key (which happens every time you type its passphrase). This allows you to trade off the cost of legitimate use of the key against the resistance of the encrypted key to password-guessing attacks. These options only affect PPK version 3. Key derivation function The variant of the Argon2 key derivation function to use. You might change this if you consider your exposure to side-channel attacks to be different to the norm. Memory to use for passphrase hash The amount of memory needed to decrypt the key, in Kbyte. Time to use for passphrase hash Controls how much time is required to attempt decrypting the key. You can either specify an approximate time in milliseconds (on this machine), or explicitly specify a number of hash passes (which is what the time is turned into during encryption). Parallelism for passphrase hash Number of parallelisable threads that can be used to decrypt the key. The default, 1, forces the process to run single-threaded, even on machines with multiple cores. 8.2.13 Reloading a private key PuTTYgen allows you to load an existing private key file into memory. If you do this, you can then change the passphrase and comment before saving it again; you can also make extra copies of the public key. To load an existing key, press the `Load' button. PuTTYgen will put up a dialog box where you can browse around the file system and find your key file. Once you select the file, PuTTYgen will ask you for a passphrase (if necessary) and will then display the key details in the same way as if it had just generated the key. If you use the Load command to load a foreign key format, it will work, but you will see a message box warning you that the key you have loaded is not a PuTTY native key. See section 8.2.14 for information about importing foreign key formats. 8.2.14 Dealing with private keys in other formats SSH-2 private keys have no standard format. OpenSSH and ssh.com have different formats, and PuTTY's is different again. So a key generated with one client cannot immediately be used with another. Using the `Import' command from the `Conversions' menu, PuTTYgen can load SSH-2 private keys in OpenSSH's format and ssh.com's format. Once you have loaded one of these key types, you can then save it back out as a PuTTY-format key (`*.PPK') so that you can use it with the PuTTY suite. The passphrase will be unchanged by this process (unless you deliberately change it). You may want to change the key comment before you save the key, since some OpenSSH key formats contained no space for a comment, and ssh.com's default comment format is long and verbose. PuTTYgen can also export private keys in OpenSSH format and in ssh.com format. To do so, select one of the `Export' options from the `Conversions' menu. Exporting a key works exactly like saving it (see section 8.2.9) - you need to have typed your passphrase in beforehand, and you will be warned if you are about to save a key without a passphrase. For OpenSSH there are two options. Modern OpenSSH actually has two formats it uses for storing private keys. `Export OpenSSH key' will automatically choose the oldest format supported for the key type, for maximum backward compatibility with older versions of OpenSSH; for newer key types like Ed25519, it will use the newer format as that is the only legal option. If you have some specific reason for wanting to use OpenSSH's newer format even for RSA, DSA, or ECDSA keys, you can choose `Export OpenSSH key (force new file format)'. Most clients for the older SSH-1 protocol use a standard format for storing private keys on disk. PuTTY uses this format as well; so if you have generated an SSH-1 private key using OpenSSH or ssh.com's client, you can use it with PuTTY, and vice versa. Hence, the export options are not available if you have generated an SSH-1 key. 8.3 Getting ready for public key authentication Connect to your SSH server using PuTTY with the SSH protocol. When the connection succeeds you will be prompted for your user name and password to login. Once logged in, you must configure the server to accept your public key for authentication: - If your server is OpenSSH, you should change into the `.ssh' directory under your home directory, and open the file `authorized_keys' with your favourite editor. (You may have to create this file, if this is the first key you have put in it.) Then switch to the PuTTYgen window, select all of the text in the `Public key for pasting into OpenSSH authorized_keys file' box (see section 8.2.11), and copy it to the clipboard (`Ctrl+C'). Then, switch back to the PuTTY window and insert the data into the open file, making sure it ends up all on one line. Save the file. (In very old versions of OpenSSH, SSH-2 keys had to be put in a separate file called `authorized_keys2'. In all current versions, the same `authorized_keys' file is used for both SSH-1 and SSH-2 keys.) - If your server is ssh.com's product and is using SSH-2, you need to save a _public_ key file from PuTTYgen (see section 8.2.10), and copy that into the `.ssh2' directory on the server. Then you should go into that `.ssh2' directory, and edit (or create) a file called `authorization'. In this file you should put a line like `Key mykey.pub', with `mykey.pub' replaced by the name of your key file. - For other SSH server software, you should refer to the manual for that server. You may also need to ensure that your home directory, your `.ssh' directory, and any other files involved (such as `authorized_keys', `authorized_keys2' or `authorization') are not group-writable or world-writable; servers will typically ignore the keys unless this is done. You can typically do this by using a command such as chmod go-w $HOME $HOME/.ssh $HOME/.ssh/authorized_keys Your server should now be configured to accept authentication using your private key. Now you need to configure PuTTY to _attempt_ authentication using your private key. You can do this in any of three ways: - Select the private key in PuTTY's configuration. See section 4.21.9 for details. - Specify the key file on the command line with the `-i' option. See section 3.11.3.18 for details. - Load the private key into Pageant (see chapter 9). In this case PuTTY will automatically try to use it for authentication if it can. Chapter 9: Using Pageant for authentication ------------------------------------------- Pageant is an SSH authentication agent. It holds your private keys in memory, already decoded, so that you can use them often without needing to type a passphrase. 9.1 Getting started with Pageant Before you run Pageant, you need to have a private key in `*.PPK' format. See chapter 8 to find out how to generate and use one. When you run Pageant, it will put an icon of a computer wearing a hat into the System tray. It will then sit and do nothing, until you load a private key into it. (You may need to use Windows' `Show hidden icons' arrow to see the Pageant icon.) If you click the Pageant icon with the right mouse button, you will see a menu. Select `View Keys' from this menu. The Pageant main window will appear. (You can also bring this window up by double- clicking on the Pageant icon.) The Pageant window contains a list box. This shows the private keys Pageant is holding. When you start Pageant, it has no keys, so the list box will be empty. After you add one or more keys, they will show up in the list box. To add a key to Pageant, press the `Add Key' button. Pageant will bring up a file dialog, labelled `Select Private Key File'. Find your private key file in this dialog, and press `Open'. Pageant will now load the private key. If the key is protected by a passphrase, Pageant will ask you to type the passphrase. When the key has been loaded, it will appear in the list in the Pageant window. Now start PuTTY and open an SSH session to a site that accepts your key. PuTTY will notice that Pageant is running, retrieve the key automatically from Pageant, and use it to authenticate. You can now open as many PuTTY sessions as you like without having to type your passphrase again. (PuTTY can be configured not to try to use Pageant, but it will try by default. See section 4.21.4 and section 3.11.3.9 for more information.) When you want to shut down Pageant, click the right button on the Pageant icon in the System tray, and select `Exit' from the menu. Closing the Pageant main window does _not_ shut down Pageant. If you want Pageant to stay running but forget all the keys it has acquired, select `Remove All Keys' from the System tray menu. 9.2 The Pageant main window The Pageant main window appears when you left-click on the Pageant system tray icon, or alternatively right-click and select `View Keys' from the menu. You can use it to keep track of what keys are currently loaded into Pageant, and to add new ones or remove the existing keys. 9.2.1 The key list box The large list box in the Pageant main window lists the private keys that are currently loaded into Pageant. The list might look something like this: ssh-ed25519 SHA256:TddlQk20DVs4LRcAsIfDN9pInKpY06D+h4kSHwWAj4w ssh-rsa 2048 SHA256:8DFtyHm3kQihgy52nzX96qMcEVOq7/yJmmwQQhBWYFg For each key, the list box will tell you: - The type of the key. Currently, this can be `ssh-rsa' (an RSA key for use with the SSH-2 protocol), `ssh-dss' (a DSA key for use with the SSH-2 protocol), `ecdsa-sha2-*' (an ECDSA key for use with the SSH-2 protocol), `ssh-ed25519' (an Ed25519 key for use with the SSH-2 protocol), `ssh-ed448' (an Ed448 key for use with the SSH-2 protocol), or `ssh1' (an RSA key for use with the old SSH-1 protocol). - The size (in bits) of the key, for key types that come in different sizes. - The fingerprint for the public key. This should be the same fingerprint given by PuTTYgen, and (hopefully) also the same fingerprint shown by remote utilities such as `ssh-keygen' when applied to your `authorized_keys' file. By default this is shown in the `SHA256' format. You can change to the older `MD5' format (which looks like `aa:bb:cc:...') with the `Fingerprint type' drop-down, but bear in mind that this format is less secure and should be avoided for comparison purposes where possible. - The comment attached to the key. - The state of deferred decryption, if enabled for this key. See section 9.5. 9.2.2 The `Add Key' button To add a key to Pageant by reading it out of a local disk file, press the `Add Key' button in the Pageant main window, or alternatively right-click on the Pageant icon in the system tray and select `Add Key' from there. Pageant will bring up a file dialog, labelled `Select Private Key File'. Find your private key file in this dialog, and press `Open'. If you want to add more than one key at once, you can select multiple files using Shift-click (to select several adjacent files) or Ctrl-click (to select non-adjacent files). Pageant will now load the private key(s). If a key is protected by a passphrase, Pageant will ask you to type the passphrase. (This is not the only way to add a private key to Pageant. You can also add one from a remote system by using agent forwarding; see section 9.4 for details.) 9.2.3 The `Remove Key' button If you need to remove a key from Pageant, select that key in the list box, and press the `Remove Key' button. Pageant will remove the key from its memory. You can apply this to keys you added using the `Add Key' button, or to keys you added remotely using agent forwarding (see section 9.4); it makes no difference. 9.3 The Pageant command line Pageant can be made to do things automatically when it starts up, by specifying instructions on its command line. If you're starting Pageant from the Windows GUI, you can arrange this by editing the properties of the Windows shortcut that it was started from. If Pageant is already running, invoking it again with the options below causes actions to be performed with the existing instance, not a new one. 9.3.1 Making Pageant automatically load keys on startup Pageant can automatically load one or more private keys when it starts up, if you provide them on the Pageant command line. Your command line might then look like: C:\PuTTY\pageant.exe d:\main.ppk d:\secondary.ppk If the keys are stored encrypted, Pageant will request the passphrases on startup. If Pageant is already running, this syntax loads keys into the existing Pageant. You can specify the `--encrypted' option to defer decryption of these keys; see section 9.5. 9.3.2 Making Pageant run another program You can arrange for Pageant to start another program once it has initialised itself and loaded any keys specified on its command line. This program (perhaps a PuTTY, or a WinCVS making use of Plink, or whatever) will then be able to use the keys Pageant has loaded. You do this by specifying the `-c' option followed by the command, like this: C:\PuTTY\pageant.exe d:\main.ppk -c C:\PuTTY\putty.exe 9.3.3 Starting with the key list visible Start Pageant with the `--keylist' option to show the main window as soon as it starts up. 9.3.4 Restricting the Windows process ACL Pageant supports the same `-restrict-acl' option as the other PuTTY utilities to lock down the Pageant process's access control; see section 3.11.3.27 for why you might want to do this. By default, if Pageant is started with `-restrict-acl', it won't pass this to any PuTTY sessions started from its System Tray submenu. Use `-restrict-putty-acl' to change this. (Again, see section 3.11.3.27 for details.) 9.4 Using agent forwarding Agent forwarding is a mechanism that allows applications on your SSH server machine to talk to the agent on your client machine. Note that at present, whether agent forwarding in SSH-2 is available depends on your server. Pageant's protocol is compatible with the OpenSSH server, but the ssh.com server uses a different agent protocol, which PuTTY does not yet support. To enable agent forwarding, first start Pageant. Then set up a PuTTY SSH session in which `Allow agent forwarding' is enabled (see section 4.21.7). Open the session as normal. (Alternatively, you can use the `-A' command line option; see section 3.11.3.10 for details.) If this has worked, your applications on the server should now have access to a Unix domain socket which the SSH server will forward back to PuTTY, and PuTTY will forward on to the agent. To check that this has actually happened, you can try this command on Unix server machines: unixbox:~$ echo $SSH_AUTH_SOCK /tmp/ssh-XXNP18Jz/agent.28794 unixbox:~$ If the result line comes up blank, agent forwarding has not been enabled at all. Now if you run `ssh' on the server and use it to connect through to another server that accepts one of the keys in Pageant, you should be able to log in without a password: unixbox:~$ ssh -v otherunixbox [...] debug: next auth method to try is publickey debug: userauth_pubkey_agent: trying agent key my-putty-key debug: ssh-userauth2 successful: method publickey [...] If you enable agent forwarding on _that_ SSH connection as well (see the manual for your server-side SSH client to find out how to do this), your authentication keys will still be available on the next machine you connect to - two SSH connections away from where they're actually stored. In addition, if you have a private key on one of the SSH servers, you can send it all the way back to Pageant using the local `ssh- add' command: unixbox:~$ ssh-add ~/.ssh/id_rsa Need passphrase for /home/fred/.ssh/id_rsa Enter passphrase for /home/fred/.ssh/id_rsa: Identity added: /home/fred/.ssh/id_rsa (/home/simon/.ssh/id_rsa) unixbox:~$ and then it's available to every machine that has agent forwarding available (not just the ones downstream of the place you added it). 9.5 Loading keys without decrypting them You can add keys to Pageant _without_ decrypting them. The key file will be held in Pageant's memory still encrypted, and when a client program first tries to use the key, Pageant will display a dialog box prompting for the passphrase so that the key can be decrypted. This works the same way whether the key is used by an instance of PuTTY running locally, or a remote client connecting to Pageant through agent forwarding. To add a key to Pageant in this encrypted form, press the `Add Key (encrypted)' button in the Pageant main window, or alternatively right-click on the Pageant icon in the system tray and select `Add Key (encrypted)' from there. Pageant will bring up a file dialog, in just the same way as it would for the plain `Add Key' button. But it won't ask for a passphrase. Instead, the key will be listed in the main window with `(encrypted)' after it. To start Pageant up in the first place with encrypted keys loaded into it, you can use the `--encrypted' option on the command line. For example: C:\PuTTY\pageant.exe --encrypted d:\main.ppk After a key has been decrypted for the first use, it remains decrypted, so that it can be used again. The main window will list the key with `(re-encryptable)' after it. You can revert it to the previous state, where a passphrase is required, using the `Re- encrypt' button in the Pageant main window. You can also `re-encrypt' all keys that were added encrypted by choosing `Re-encrypt All Keys' from the System tray menu. (Note that this does _not_ discard cleartext keys that were not previously added encrypted!) *CAUTION*: When Pageant displays a prompt to decrypt an already- loaded key, it cannot give keyboard focus to the prompt dialog box. As far as I know this is a deliberate defensive measure by Windows, against malicious software. So make sure you click in the prompt window before typing your passphrase, or else the passphrase might be sent to somewhere you didn't want to trust with it! 9.6 Security considerations Using Pageant for public-key authentication gives you the convenience of being able to open multiple SSH sessions without having to type a passphrase every time, but also gives you the security benefit of never storing a decrypted private key on disk. Many people feel this is a good compromise between security and convenience. It _is_ a compromise, however. Holding your decrypted private keys in Pageant is better than storing them in easy-to-find disk files, but still less secure than not storing them anywhere at all. This is for two reasons: - Windows unfortunately provides no way to protect pieces of memory from being written to the system swap file. So if Pageant is holding your private keys for a long period of time, it's possible that decrypted private key data may be written to the system swap file, and an attacker who gained access to your hard disk later on might be able to recover that data. (However, if you stored an unencrypted key in a disk file they would _certainly_ be able to recover it.) - Although, like most modern operating systems, Windows prevents programs from accidentally accessing one another's memory space, it does allow programs to access one another's memory space deliberately, for special purposes such as debugging. This means that if you allow a virus, trojan, or other malicious program on to your Windows system while Pageant is running, it could access the memory of the Pageant process, extract your decrypted authentication keys, and send them back to its master. Similarly, use of agent _forwarding_ is a security improvement on other methods of one-touch authentication, but not perfect. Holding your keys in Pageant on your Windows box has a security advantage over holding them on the remote server machine itself (either in an agent or just unencrypted on disk), because if the server machine ever sees your unencrypted private key then the sysadmin or anyone who cracks the machine can steal the keys and pretend to be you for as long as they want. However, the sysadmin of the server machine can always pretend to be you _on that machine_. So if you forward your agent to a server machine, then the sysadmin of that machine can access the forwarded agent connection and request signatures from any of your private keys, and can therefore log in to other machines as you. They can only do this to a limited extent - when the agent forwarding disappears they lose the ability - but using Pageant doesn't actually _prevent_ the sysadmin (or hackers) on the server from doing this. Therefore, if you don't trust the sysadmin of a server machine, you should _never_ use agent forwarding to that machine. (Of course you also shouldn't store private keys on that machine, type passphrases into it, or log into other machines from it in any way at all; Pageant is hardly unique in this respect.) Chapter 10: Common 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. We do not attempt to list _all_ error messages here: there are many which should never occur, and some which should be self-explanatory. If you get an error message which is not listed in this chapter and which you don't understand, report it to us as a bug (see appendix B) and we will add documentation for it. 10.1 `The server's host key is not cached in the registry' This error message occurs when PuTTY connects to a new SSH server. Every server identifies itself by means of a host key; once PuTTY knows the host key for a server, it will be able to detect if a malicious attacker redirects your connection to another machine. If you see this message, it means that PuTTY has not seen this host key before, and has no way of knowing whether it is correct or not. You should attempt to verify the host key by other means, such as asking the machine's administrator. If you see this message and you know that your installation of PuTTY _has_ connected to the same server before, it may have been recently upgraded to SSH protocol version 2. SSH protocols 1 and 2 use separate host keys, so when you first use SSH-2 with a server you have only used SSH-1 with before, you will see this message again. You should verify the correctness of the key as before. See section 2.2 for more information on host keys. 10.2 `WARNING - POTENTIAL SECURITY BREACH!' This message, followed by `The server's host key does not match the one PuTTY has cached in the registry', means that PuTTY has connected to the SSH server before, knows what its host key _should_ be, but has found a different one. This may mean that a malicious attacker has replaced your server with a different one, or has redirected your network connection to their own machine. On the other hand, it may simply mean that the administrator of your server has accidentally changed the key while upgrading the SSH software; this _shouldn't_ happen but it is unfortunately possible. 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 section 2.2 for more information on host keys. 10.3 `SSH protocol version 2 required by our configuration but remote only provides (old, insecure) SSH-1' By default, PuTTY only supports connecting to SSH servers that implement SSH protocol version 2. If you see this message, the server you're trying to connect to only supports the older SSH-1 protocol. If the server genuinely only supports SSH-1, then you need to either change the `SSH protocol version' setting (see section 4.17.4), or use the `-1' command-line option; in any case, you should not treat the resulting connection as secure. You might start seeing this message with new versions of PuTTY (from 0.68 onwards) where you didn't before, because it used to be possible to configure PuTTY to automatically fall back from SSH-2 to SSH-1. This is no longer supported, to prevent the possibility of a downgrade attack. 10.4 `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 Blowfish, single-DES, and Arcfour encryption. See section 4.20 for more information on this message. (There are similar messages for other cryptographic primitives, such as host key algorithms.) 10.5 `Remote side sent disconnect message type 2 (protocol error): "Too many authentication failures for root"' This message is produced by an OpenSSH (or Sun SSH) server if it receives more failed authentication attempts than it is willing to tolerate. This can easily happen if you are using Pageant and have a large number of keys loaded into it, since these servers count each offer of a public key as an authentication attempt. This can be worked around by specifying the key that's required for the authentication in the PuTTY configuration (see section 4.21.9); PuTTY will ignore any other keys Pageant may have, but will ask Pageant to do the authentication, so that you don't have to type your passphrase. On the server, this can be worked around by disabling public-key authentication or (for Sun SSH only) by increasing `MaxAuthTries' in `sshd_config'. 10.6 `Out of memory' This occurs when PuTTY tries to allocate more memory than the system can give it. This _may_ happen for genuine reasons: if the computer really has run out of memory, or if you have configured an extremely large number of lines of scrollback in your terminal. PuTTY is not able to recover from running out of memory; it will terminate immediately after giving this error. However, this error can also occur when memory is not running out at all, because PuTTY receives data in the wrong format. In SSH-2 and also in SFTP, the server sends the length of each message before the message itself; so PuTTY will receive the length, try to allocate space for the message, and then receive the rest of the message. If the length PuTTY receives is garbage, it will try to allocate a ridiculous amount of memory, and will terminate with an `Out of memory' error. This can happen in SSH-2, if PuTTY and the server have not enabled encryption in the same way (see question A.7.3 in the FAQ). This can also happen in PSCP or PSFTP, if your login scripts on the server generate output: the client program will be expecting an SFTP message starting with a length, and if it receives some text from your login scripts instead it will try to interpret them as a message length. See question A.7.4 for details of this. 10.7 `Internal error', `Internal fault', `Assertion failed' Any error beginning with the word `Internal' should _never_ occur. If it does, there is a bug in PuTTY by definition; please see appendix B and report it to us. Similarly, any error message starting with `Assertion failed' is a bug in PuTTY. Please report it to us, and include the exact text from the error message box. 10.8 `Unable to use key file', `Couldn't load private key', `Couldn't load this key' Various forms of this error are printed in the PuTTY window, or written to the PuTTY Event Log (see section 3.1.3.1) when trying public-key authentication, or given by Pageant when trying to load a private key. If you see one of these messages, it often indicates that you've tried to load a key of an inappropriate type into PuTTY, Plink, PSCP, PSFTP, or Pageant. You may have tried to load an SSH-2 key in a `foreign' format (OpenSSH or ssh.com) directly into one of the PuTTY tools, in which case you need to import it into PuTTY's native format (`*.PPK') using PuTTYgen - see section 8.2.14. Alternatively, you may have specified a key that's inappropriate for the connection you're making. The SSH-2 and the old SSH-1 protocols require different private key formats, and a SSH-1 key can't be used for a SSH-2 connection (or vice versa). 10.9 `Server refused our key', `Server refused our public key', `Key refused' Various forms of this error are printed in the PuTTY window, or written to the PuTTY Event Log (see section 3.1.3.1) when trying public-key authentication. If you see one of these messages, it means that PuTTY has sent a public key to the server and offered to authenticate with it, and the server has refused to accept authentication. This usually means that the server is not configured to accept this key to authenticate this user. This is almost certainly not a problem with PuTTY. If you see this type of message, the first thing you should do is check your _server_ configuration carefully. Common errors include having the wrong permissions or ownership set on the public key or the user's home directory on the server. Also, read the PuTTY Event Log; the server may have sent diagnostic messages explaining exactly what problem it had with your setup. Section 8.3 has some hints on server-side public key setup. 10.10 `Access denied', `Authentication refused' Various forms of this error are printed in the PuTTY window, or written to the PuTTY Event Log (see section 3.1.3.1) during authentication. If you see one of these messages, it means that the server has refused all the forms of authentication PuTTY has tried and it has no further ideas. It may be worth checking the Event Log for diagnostic messages from the server giving more detail. This error can be caused by buggy SSH-1 servers that fail to cope with the various strategies we use for camouflaging passwords in transit. Upgrade your server, or use the workarounds described in section 4.26.11 and possibly section 4.26.12. 10.11 `No supported authentication methods available' This error indicates that PuTTY has run out of ways to authenticate you to an SSH server. This may be because PuTTY has TIS or keyboard- interactive authentication disabled, in which case see section 4.21.5 and section 4.21.6. 10.12 `Incorrect MAC received on packet' or `Incorrect CRC received on packet' This error occurs when PuTTY decrypts an SSH packet and its checksum is not correct. This probably means something has gone wrong in the encryption or decryption process. It's difficult to tell from this error message whether the problem is in the client, in the server, or in between. In particular, if the network is corrupting data at the TCP level, it may only be obvious with cryptographic protocols such as SSH, which explicitly check the integrity of the transferred data and complain loudly if the checks fail. Corruption of protocols without integrity protection (such as HTTP) will manifest in more subtle failures (such as misdisplayed text or images in a web browser) which may not be noticed. Occasionally this has been caused by server bugs. An example is the bug described at section 4.26.8, although you're very unlikely to encounter that one these days. In this context MAC stands for Message Authentication Code. It's a cryptographic term, and it has nothing at all to do with Ethernet MAC (Media Access Control) addresses, or with the Apple computer. 10.13 `Incoming packet was garbled on decryption' This error occurs when PuTTY decrypts an SSH packet and the decrypted data makes no sense. This probably means something has gone wrong in the encryption or decryption process. It's difficult to tell from this error message whether the problem is in the client, in the server, or in between. If you get this error, one thing you could try would be to fiddle with the setting of `Miscomputes SSH-2 encryption keys' (see section 4.26.10) or `Ignores SSH-2 maximum packet size' (see section 4.26.5) on the Bugs panel. 10.14 `PuTTY X11 proxy: _various errors_' This family of errors are reported when PuTTY is doing X forwarding. They are sent back to the X application running on the SSH server, which will usually report the error to the user. When PuTTY enables X forwarding (see section 3.4) it creates a virtual X display running on the SSH server. This display requires authentication to connect to it (this is how PuTTY prevents other users on your server machine from connecting through the PuTTY proxy to your real X display). PuTTY also sends the server the details it needs to enable clients to connect, and the server should put this mechanism in place automatically, so your X applications should just work. A common reason why people see one of these messages is because they used SSH to log in as one user (let's say `fred'), and then used the Unix `su' command to become another user (typically `root'). The original user, `fred', has access to the X authentication data provided by the SSH server, and can run X applications which are forwarded over the SSH connection. However, the second user (`root') does not automatically have the authentication data passed on to it, so attempting to run an X application as that user often fails with this error. If this happens, _it is not a problem with PuTTY_. You need to arrange for your X authentication data to be passed from the user you logged in as to the user you used `su' to become. How you do this depends on your particular system; in fact many modern versions of `su' do it automatically. 10.15 `Network error: Software caused connection abort' This is a generic error produced by the Windows network code when it kills an established connection for some reason. For example, it might happen if you pull the network cable out of the back of an Ethernet-connected computer, or if Windows has any other similar reason to believe the entire network has become unreachable. Windows also generates this error if it has given up on the machine at the other end of the connection ever responding to it. If the network between your client and server goes down and your client then tries to send some data, Windows will make several attempts to send the data and will then give up and kill the connection. In particular, this can occur even if you didn't type anything, if you are using SSH-2 and PuTTY attempts a key re-exchange. (See section 4.18.2 for more about key re-exchange.) (It can also occur if you are using keepalives in your connection. Other people have reported that keepalives _fix_ this error for them. See section 4.14.1 for a discussion of the pros and cons of keepalives.) We are not aware of any reason why this error might occur that would represent a bug in PuTTY. The problem is between you, your Windows system, your network and the remote system. 10.16 `Network error: Connection reset by peer' This error occurs when the machines at each end of a network connection lose track of the state of the connection between them. For example, you might see it if your SSH server crashes, and manages to reboot fully before you next attempt to send data to it. However, the most common reason to see this message is if you are connecting through a firewall or a NAT router which has timed the connection out. See question A.7.8 in the FAQ for more details. You may be able to improve the situation by using keepalives; see section 4.14.1 for details on this. Note that Windows can produce this error in some circumstances without seeing a connection reset from the server, for instance if the connection to the network is lost. 10.17 `Network error: Connection refused' This error means that the network connection PuTTY tried to make to your server was rejected by the server. Usually this happens because the server does not provide the service which PuTTY is trying to access. Check that you are connecting with the correct protocol (SSH, Telnet, etc), and check that the port number is correct. If that fails, consult the administrator of your server. 10.18 `Network error: Connection timed out' This error means that the network connection PuTTY tried to make to your server received no response at all from the server. Usually this happens because the server machine is completely isolated from the network, or because it is turned off. Check that you have correctly entered the host name or IP address of your server machine. If that fails, consult the administrator of your server. Unix also generates this error when it tries to send data down a connection and contact with the server has been completely lost during a connection. (There is a delay of minutes before Unix gives up on receiving a reply from the server.) This can occur if you type things into PuTTY while the network is down, but it can also occur if PuTTY decides of its own accord to send data: due to a repeat key exchange in SSH-2 (see section 4.18.2) or due to keepalives (section 4.14.1). 10.19 `Network error: Cannot assign requested address' This means that the operating system rejected the parameters of the network connection PuTTY tried to make, usually without actually trying to connect to anything, because they were simply invalid. A common way to provoke this error is to accidentally try to connect to port 0, which is not a valid port number. Appendix A: PuTTY FAQ --------------------- This FAQ is published on the PuTTY web site, and also provided as an appendix in the manual. A.1 Introduction A.1.1 What is PuTTY? PuTTY is a client program for the SSH, Telnet, Rlogin, and SUPDUP network protocols. These protocols are all used to run a remote session on a computer, over a network. PuTTY implements the client end of that session: the end at which the session is displayed, rather than the end at which it runs. In really simple terms: you run PuTTY on a Windows machine, and tell it to connect to (for example) a Unix machine. PuTTY opens a window. Then, anything you type into that window is sent straight to the Unix machine, and everything the Unix machine sends back is displayed in the window. So you can work on the Unix machine as if you were sitting at its console, while actually sitting somewhere else. A.2 Features supported in PuTTY In general, if you want to know if PuTTY supports a particular feature, you should look for it on the PuTTY web site. In particular: - try the changes page, and see if you can find the feature on there. If a feature is listed there, it's been implemented. If it's listed as a change made _since_ the latest version, it should be available in the development snapshots, in which case testing will be very welcome. - try the Wishlist page, and see if you can find the feature there. If it's on there, and not in the `Recently fixed' section, it probably _hasn't_ been implemented. A.2.1 Does PuTTY support SSH-2? Yes. SSH-2 support has been available in PuTTY since version 0.50 in 2000. Public key authentication (both RSA and DSA) in SSH-2 was new in version 0.52 in 2002. A.2.2 Does PuTTY support reading OpenSSH or ssh.com SSH-2 private key files? PuTTY doesn't support this natively (see the wishlist entry for reasons why not), but as of 0.53 PuTTYgen can convert both OpenSSH and ssh.com private key files into PuTTY's format. A.2.3 Does PuTTY support SSH-1? Yes. SSH-1 support has always been available in PuTTY. However, the SSH-1 protocol has many weaknesses and is no longer considered secure; you should use SSH-2 instead if at all possible. As of 0.68, PuTTY will no longer fall back to SSH-1 if the server doesn't appear to support SSH-2; you must explicitly ask for SSH-1. A.2.4 Does PuTTY support local echo? Yes. Version 0.52 has proper support for local echo. In version 0.51 and before, local echo could not be separated from local line editing (where you type a line of text locally, and it is not sent to the server until you press Return, so you have the chance to edit it and correct mistakes _before_ the server sees it). New in version 0.52, local echo and local line editing are separate options, and by default PuTTY will try to determine automatically whether to enable them or not, based on which protocol you have selected and also based on hints from the server. If you have a problem with PuTTY's default choice, you can force each option to be enabled or disabled as you choose. The controls are in the Terminal panel, in the section marked `Line discipline options'. A.2.5 Does PuTTY support storing settings, so I don't have to change them every time? Yes, all of PuTTY's settings can be saved in named session profiles. You can also change the default settings that are used for new sessions. See section 4.1.2 in the documentation for how to do this. A.2.6 Does PuTTY support storing its settings in a disk file? Not at present, although section 4.32 in the documentation gives a method of achieving the same effect. A.2.7 Does PuTTY support full-screen mode, like a DOS box? Yes; this was added in version 0.52, in 2002. A.2.8 Does PuTTY have the ability to remember my password so I don't have to type it every time? No, it doesn't. Remembering your password is a bad plan for obvious security reasons: anyone who gains access to your machine while you're away from your desk can find out the remembered password, and use it, abuse it or change it. In addition, it's not even _possible_ for PuTTY to automatically send your password in a Telnet session, because Telnet doesn't give the client software any indication of which part of the login process is the password prompt. PuTTY would have to guess, by looking for words like `password' in the session data; and if your login program is written in something other than English, this won't work. In SSH, remembering your password would be possible in theory, but there doesn't seem to be much point since SSH supports public key authentication, which is more flexible and more secure. See chapter 8 in the documentation for a full discussion of public key authentication. A.2.9 Is there an option to turn off the annoying host key prompts? No, there isn't. And there won't be. Even if you write it yourself and send us the patch, we won't accept it. Those annoying host key prompts are the _whole point_ of SSH. Without them, all the cryptographic technology SSH uses to secure your session is doing nothing more than making an attacker's job slightly harder; instead of sitting between you and the server with a packet sniffer, the attacker must actually subvert a router and start modifying the packets going back and forth. But that's not all that much harder than just sniffing; and without host key checking, it will go completely undetected by client or server. Host key checking is your guarantee that the encryption you put on your data at the client end is the _same_ encryption taken off the data at the server end; it's your guarantee that it hasn't been removed and replaced somewhere on the way. Host key checking makes the attacker's job _astronomically_ hard, compared to packet sniffing, and even compared to subverting a router. Instead of applying a little intelligence and keeping an eye on Bugtraq, the attacker must now perform a brute-force attack against at least one military-strength cipher. That insignificant host key prompt really does make _that_ much difference. If you're having a specific problem with host key checking - perhaps you want an automated batch job to make use of PSCP or Plink, and the interactive host key prompt is hanging the batch process - then the right way to fix it is to add the correct host key to the Registry in advance, or if the Registry is not available, to use the -hostkey command-line option. That way, you retain the _important_ feature of host key checking: the right key will be accepted and the wrong ones will not. Adding an option to turn host key checking off completely is the wrong solution and we will not do it. If you have host keys available in the common `known_hosts' format, we have a script called `kh2reg.py' to convert them to a Windows .REG file, which can be installed ahead of time by double-clicking or using `REGEDIT'. A.2.10 Will you write an SSH server for the PuTTY suite, to go with the client? Not one that you'd want to use. While much of the protocol and networking code can be made common between a client and server, to make a _useful_ general-purpose server requires all sorts of fiddly new code like interacting with OS authentication databases and the like. A special-purpose SSH server (called Uppity) can now be built from the PuTTY source code, and indeed it is not usable as a general- purpose server; it exists mainly as a test harness. If someone else wants to use this as a basis for writing a general- purpose SSH server, they'd be perfectly welcome to of course; but we don't have time, and we don't have motivation. The code is available if anyone else wants to try it. A.2.11 Can PSCP or PSFTP transfer files in ASCII mode? Unfortunately not. This was a limitation of the file transfer protocols as originally specified: the SCP and SFTP protocols had no notion of transferring a file in anything other than binary mode. (This is still true of SCP.) The current draft protocol spec of SFTP proposes a means of implementing ASCII transfer. At some point PSCP/PSFTP may implement this proposal. A.3 Ports to other operating systems The eventual goal is for PuTTY to be a multi-platform program, able to run on at least Windows, Mac OS and Unix. PuTTY has been gaining a generalised porting layer, drawing a clear line between platform-dependent and platform-independent code. The general intention was for this porting layer to evolve naturally as part of the process of doing the first port; a Unix port has now been released and the plan seems to be working so far. A.3.1 What ports of PuTTY exist? Currently, release versions of PuTTY tools only run on Windows systems and Unix. As of 0.68, the supplied PuTTY executables run on versions of Windows from XP onwards, up to and including Windows 10; and we know of no reason why PuTTY should not continue to work on future versions of Windows. We provide 32-bit and 64-bit Windows executables for the common x86 processor family; see question A.6.10 for discussion of the compatibility issues around that. The 32-bit executables require a Pentium 4 or newer processor. We also provide executables for Windows on Arm processors. (We used to also provide executables for Windows for the Alpha processor, but stopped after 0.58 due to lack of interest.) In the development code, a partial port to Mac OS exists (see question A.3.6). Currently PuTTY does _not_ run on Windows CE (see question A.3.4). We do not have release-quality ports for any other systems at the present time. If anyone told you we had an Android port, or an iOS port, or any other port of PuTTY, they were mistaken. We don't. There are some third-party ports to various platforms, mentioned on the Links page of our website. A.3.2 Is there a port to Unix? There are Unix ports of most of the traditional PuTTY tools, and also one entirely new application. If you look at the source release, you should find a `unix' subdirectory. There are a couple of ways of building it, including the usual `configure'/`make'; see the file `README' in the source distribution. This should build you: - Unix ports of PuTTY, Plink, PSCP, and PSFTP, which work pretty much the same as their Windows counterparts; - Command-line versions of PuTTYgen and Pageant, whose user interface is quite different to the Windows GUI versions; - `pterm' - an xterm-type program which supports the same terminal emulation as PuTTY. If you don't have Gtk, you should still be able to build the command-line tools. A.3.3 What's the point of the Unix port? Unix has OpenSSH. All sorts of little things. `pterm' is directly useful to anyone who prefers PuTTY's terminal emulation to `xterm''s, which at least some people do. Unix Plink has apparently found a niche among people who find the complexity of OpenSSL makes OpenSSH hard to install (and who don't mind Plink not having as many features). Some users want to generate a large number of SSH keys on Unix and then copy them all into PuTTY, and the Unix PuTTYgen should allow them to automate that conversion process. There were development advantages as well; porting PuTTY to Unix was a valuable path-finding effort for other future ports, and also allowed us to use the excellent Linux tool Valgrind to help with debugging, which has already improved PuTTY's stability on _all_ platforms. However, if you're a Unix user and you can see no reason to switch from OpenSSH to PuTTY/Plink, then you're probably right. We don't expect our Unix port to be the right thing for everybody. A.3.4 Will there be a port to Windows CE or PocketPC? We once did some work on such a port, but it only reached an early stage, and certainly not a useful one. It's no longer being actively worked on. A.3.5 Is there a port to Windows 3.1? PuTTY is a 32-bit application from the ground up, so it won't run on Windows 3.1 as a native 16-bit program; and it would be _very_ hard to port it to do so, because of Windows 3.1's vile memory allocation mechanisms. However, it is possible in theory to compile the existing PuTTY source in such a way that it will run under Win32s (an extension to Windows 3.1 to let you run 32-bit programs). In order to do this you'll need the right kind of C compiler - modern versions of Visual C at least have stopped being backwards compatible to Win32s. Also, the last time we tried this it didn't work very well. A.3.6 Will there be a port to the Mac? We hope so! We attempted one around 2005, written as a native Cocoa application, but it turned out to be very slow to redraw its window for some reason we never got to the bottom of. In 2015, after porting the GTK front end to work with GTK 3, we began another attempt based on making small changes to the GTK code and building it against the OS X Quartz version of GTK 3. This doesn't seem to have the window redrawing problem any more, so it's already got further than the last effort, but it is still substantially unfinished. If any OS X and/or GTK programming experts are keen to have a finished version of this, we urge them to help out with some of the remaining problems! See the TODO list in `unix/gtkapp.c' in the source code. A.3.7 Will there be a port to EPOC? I hope so, but given that ports aren't really progressing very fast even on systems the developers _do_ already know how to program for, it might be a long time before any of us get round to learning a new system and doing the port for that. However, some of the work has been done by other people; see the Links page of our website for various third-party ports. A.3.8 Will there be a port to the iPhone? We have no plans to write such a port ourselves; none of us has an iPhone, and developing and publishing applications for it looks awkward and expensive. However, there is a third-party SSH client for the iPhone and iPod Touch called pTerm, which is apparently based on PuTTY. (This is nothing to do with our similarly-named `pterm', which is a standalone terminal emulator for Unix systems; see question A.3.2.) A.4 Embedding PuTTY in other programs A.4.1 Is the SSH or Telnet code available as a DLL? No, it isn't. It would take a reasonable amount of rewriting for this to be possible, and since the PuTTY project itself doesn't believe in DLLs (they make installation more error-prone) none of us has taken the time to do it. Most of the code cleanup work would be a good thing to happen in general, so if anyone feels like helping, we wouldn't say no. See also the wishlist entry. A.4.2 Is the SSH or Telnet code available as a Visual Basic component? No, it isn't. None of the PuTTY team uses Visual Basic, and none of us has any particular need to make SSH connections from a Visual Basic application. In addition, all the preliminary work to turn it into a DLL would be necessary first; and furthermore, we don't even know how to write VB components. If someone offers to do some of this work for us, we might consider it, but unless that happens I can't see VB integration being anywhere other than the very bottom of our priority list. A.4.3 How can I use PuTTY to make an SSH connection from within another program? Probably your best bet is to use Plink, the command-line connection tool. If you can start Plink as a second Windows process, and arrange for your primary process to be able to send data to the Plink process, and receive data from it, through pipes, then you should be able to make SSH connections from your program. This is what CVS for Windows does, for example. A.5 Details of PuTTY's operation A.5.1 What terminal type does PuTTY use? For most purposes, PuTTY can be considered to be an xterm terminal. PuTTY also supports some terminal control sequences not supported by the real xterm: notably the Linux console sequences that reconfigure the colour palette, and the title bar control sequences used by DECterm (which are different from the xterm ones; PuTTY supports both). By default, PuTTY announces its terminal type to the server as `xterm'. If you have a problem with this, you can reconfigure it to say something else; `vt220' might help if you have trouble. A.5.2 Where does PuTTY store its data? On Windows, PuTTY stores most of its data (saved sessions, SSH host keys) in the Registry. The precise location is HKEY_CURRENT_USER\Software\SimonTatham\PuTTY and within that area, saved sessions are stored under `Sessions' while host keys are stored under `SshHostKeys'. PuTTY also requires a random number seed file, to improve the unpredictability of randomly chosen data needed as part of the SSH cryptography. This is stored by default in a file called `PUTTY.RND'; this is stored by default in the `Application Data' directory, or failing that, one of a number of fallback locations. If you want to change the location of the random number seed file, you can put your chosen pathname in the Registry, at HKEY_CURRENT_USER\Software\SimonTatham\PuTTY\RandSeedFile You can ask PuTTY to delete all this data; see question A.8.2. On Unix, PuTTY stores all of this data in a directory ~/.putty by default. A.5.3 Why do small PuTTY icons appear next to the login prompts? As of PuTTY 0.71, some lines of text in the terminal window are marked with a small copy of the PuTTY icon (as far as pixels allow). This is to show trustworthiness. When the PuTTY icon appears next to a line of text, it indicates that that line of text was generated by PuTTY itself, and not generated by the server and sent to PuTTY. Text that comes from the server does not have this icon, and we've arranged that the server should not be able to fake it. (There's no control sequence the server can send which will make PuTTY draw its own icon, and if the server tries to move the cursor back up to a line that _already_ has an icon and overwrite the text, the icon will disappear.) This lets you tell the difference between (for example) a legitimate prompt in which PuTTY itself asks you for your private key passphrase, and a fake prompt in which the server tries to send the identical text to trick you into telling _it_ your private key passphrase. A.5.4 Why has Plink started saying `Press Return to begin session'? As of PuTTY 0.71, if you use Plink for an interactive SSH session, then after the login phase has finished, it will present a final interactive prompt saying `Access granted. Press Return to begin session'. This is another defence against servers trying to mimic the real authentication prompts after the session has started. When you pass through that prompt, you know that everything after it is generated by the server and not by Plink itself, so any request for your private key passphrase should be treated with suspicion. In Plink, we can't use the defence described in section A.5.3: Plink is running _in_ the terminal, so anything it can write into the terminal, the server could write in the same way after the session starts. And we can't just print a separator line without a pause, because then the server could simply move the cursor back up to it and overwrite it (probably with a brief flicker, but you might easily miss that). The only robust defence anyone has come up with involves this pause. If you trust your server not to be abusive, you can turn this off. It will also not appear in various other circumstances where Plink can be confident it isn't necessary. See section 7.2.3.6 for details. A.6 HOWTO questions A.6.1 What login name / password should I use? This is not a question you should be asking _us_. PuTTY is a communications tool, for making connections to other computers. We maintain the tool; we _don't_ administer any computers that you're likely to be able to use, in the same way that the people who make web browsers aren't responsible for most of the content you can view in them. We cannot help with questions of this sort. If you know the name of the computer you want to connect to, but don't know what login name or password to use, you should talk to whoever administers that computer. If you don't know who that is, see the next question for some possible ways to find out. A.6.2 What commands can I type into my PuTTY terminal window? Again, this is not a question you should be asking _us_. You need to read the manuals, or ask the administrator, of _the computer you have connected to_. PuTTY does not process the commands you type into it. It's only a communications tool. It makes a connection to another computer; it passes the commands you type to that other computer; and it passes the other computer's responses back to you. Therefore, the precise range of commands you can use will not depend on PuTTY, but on what kind of computer you have connected to and what software is running on it. The PuTTY team cannot help you with that. (Think of PuTTY as being a bit like a telephone. If you phone somebody up and you don't know what language to speak to make them understand you, it isn't _the telephone company_'s job to find that out for you. We just provide the means for you to get in touch; making yourself understood is somebody else's problem.) If you are unsure of where to start looking for the administrator of your server, a good place to start might be to remember how you found out the host name in the PuTTY configuration. If you were given that host name by e-mail, for example, you could try asking the person who sent you that e-mail. If your company's IT department provided you with ready-made PuTTY saved sessions, then that IT department can probably also tell you something about what commands you can type during those sessions. But the PuTTY maintainer team does not administer any server you are likely to be connecting to, and cannot help you with questions of this type. A.6.3 How can I make PuTTY start up maximised? Create a Windows shortcut to start PuTTY from, and set it as `Run Maximized'. A.6.4 How can I create a Windows shortcut to start a particular saved session directly? To run a PuTTY session saved under the name `mysession', create a Windows shortcut that invokes PuTTY with a command line like \path\name\to\putty.exe -load "mysession" (Note: prior to 0.53, the syntax was `@session'. This is now deprecated and may be removed at some point.) A.6.5 How can I start an SSH session straight from the command line? Use the command line `putty -ssh host.name'. Alternatively, create a saved session that specifies the SSH protocol, and start the saved session as shown in question A.6.4. A.6.6 How do I copy and paste between PuTTY and other Windows applications? Copy and paste works similarly to the X Window System. You use the left mouse button to select text in the PuTTY window. The act of selection _automatically_ copies the text to the clipboard: there is no need to press Ctrl-Ins or Ctrl-C or anything else. In fact, pressing Ctrl-C will send a Ctrl-C character to the other end of your connection (just like it does the rest of the time), which may have unpleasant effects. The _only_ thing you need to do, to copy text to the clipboard, is to select it. To paste the clipboard contents into a PuTTY window, by default you click the right mouse button. If you have a three-button mouse and are used to X applications, you can configure pasting to be done by the middle button instead, but this is not the default because most Windows users don't have a middle button at all. You can also paste by pressing Shift-Ins. A.6.7 How do I use all PuTTY's features (public keys, proxying, cipher selection, etc.) in PSCP, PSFTP and Plink? Most major features (e.g., public keys, port forwarding) are available through command line options. See the documentation. Not all features are accessible from the command line yet, although we'd like to fix this. In the meantime, you can use most of PuTTY's features if you create a PuTTY saved session, and then use the name of the saved session on the command line in place of a hostname. This works for PSCP, PSFTP and Plink (but don't expect port forwarding in the file transfer applications!). A.6.8 How do I use PSCP.EXE? When I double-click it gives me a command prompt window which then closes instantly. PSCP is a command-line application, not a GUI application. If you run it without arguments, it will simply print a help message and terminate. To use PSCP properly, run it from a Command Prompt window. See chapter 5 in the documentation for more details. A.6.9 How do I use PSCP to copy a file whose name has spaces in? If PSCP is using the traditional SCP protocol, this is confusing. If you're specifying a file at the local end, you just use one set of quotes as you would normally do: pscp "local filename with spaces" user@host: pscp user@host:myfile "local filename with spaces" But if the filename you're specifying is on the _remote_ side, you have to use backslashes and two sets of quotes: pscp user@host:"\"remote filename with spaces\"" local_filename pscp local_filename user@host:"\"remote filename with spaces\"" Worse still, in a remote-to-local copy you have to specify the local file name explicitly, otherwise PSCP will complain that they don't match (unless you specified the `-unsafe' option). The following command will give an error message: c:\>pscp user@host:"\"oo er\"" . warning: remote host tried to write to a file called 'oo er' when we requested a file called '"oo er"'. Instead, you need to specify the local file name in full: c:\>pscp user@host:"\"oo er\"" "oo er" If PSCP is using the newer SFTP protocol, none of this is a problem, and all filenames with spaces in are specified using a single pair of quotes in the obvious way: pscp "local file" user@host: pscp user@host:"remote file" . A.6.10 Should I run the 32-bit or the 64-bit version? If you're not sure, the 32-bit version is generally the safe option. It will run perfectly well on all processors and on all versions of Windows that PuTTY supports. PuTTY doesn't require to run as a 64- bit application to work well, and having a 32-bit PuTTY on a 64-bit system isn't likely to cause you any trouble. The 64-bit version (first released in 0.68) will only run if you have a 64-bit processor _and_ a 64-bit edition of Windows (both of these things are likely to be true of any recent Windows PC). It will run somewhat faster (in particular, the cryptography will be faster, especially during link setup), but it will consume slightly more memory. If you need to use an external DLL for GSSAPI authentication, that DLL may only be available in a 32-bit or 64-bit form, and that will dictate the version of PuTTY you need to use. (You will probably know if you're doing this; see section 4.22.2 in the documentation.) A.7 Troubleshooting A.7.1 Why do I see `Fatal: Protocol error: Expected control record' in PSCP? This happens because PSCP was expecting to see data from the server that was part of the PSCP protocol exchange, and instead it saw data that it couldn't make any sense of at all. This almost always happens because the startup scripts in your account on the server machine are generating output. This is impossible for PSCP, or any other SCP client, to work around. You should never use startup files (`.bashrc', `.cshrc' and so on) which generate output in non-interactive sessions. This is not actually a PuTTY problem. If PSCP fails in this way, then all other SCP clients are likely to fail in exactly the same way. The problem is at the server end. A.7.2 I clicked on a colour in the Colours panel, and the colour didn't change in my terminal. That isn't how you're supposed to use the Colours panel. During the course of a session, PuTTY potentially uses _all_ the colours listed in the Colours panel. It's not a question of using only one of them and you choosing which one; PuTTY will use them _all_. The purpose of the Colours panel is to let you adjust the appearance of all the colours. So to change the colour of the cursor, for example, you would select `Cursor Colour', press the `Modify' button, and select a new colour from the dialog box that appeared. Similarly, if you want your session to appear in green, you should select `Default Foreground' and press `Modify'. Clicking on `ANSI Green' won't turn your session green; it will only allow you to adjust the _shade_ of green used when PuTTY is instructed by the server to display green text. A.7.3 After trying to establish an SSH-2 connection, PuTTY says `Out of memory' and dies. If this happens just while the connection is starting up, this often indicates that for some reason the client and server have failed to establish a session encryption key. Somehow, they have performed calculations that should have given each of them the same key, but have ended up with different keys; so data encrypted by one and decrypted by the other looks like random garbage. This causes an `out of memory' error because the first encrypted data PuTTY expects to see is the length of an SSH message. Normally this will be something well under 100 bytes. If the decryption has failed, PuTTY will see a completely random length in the region of two _gigabytes_, and will try to allocate enough memory to store this non-existent message. This will immediately lead to it thinking it doesn't have enough memory, and panicking. If this happens to you, it is quite likely to still be a PuTTY bug and you should report it (although it might be a bug in your SSH server instead); but it doesn't necessarily mean you've actually run out of memory. A.7.4 When attempting a file transfer, either PSCP or PSFTP says `Out of memory' and dies. This is almost always caused by your login scripts on the server generating output. PSCP or PSFTP will receive that output when they were expecting to see the start of a file transfer protocol, and they will attempt to interpret the output as file-transfer protocol. This will usually lead to an `out of memory' error for much the same reasons as given in question A.7.3. This is a setup problem in your account on your server, _not_ a PSCP/PSFTP bug. Your login scripts should _never_ generate output during non-interactive sessions; secure file transfer is not the only form of remote access that will break if they do. On Unix, a simple fix is to ensure that all the parts of your login script that might generate output are in `.profile' (if you use a Bourne shell derivative) or `.login' (if you use a C shell). Putting them in more general files such as `.bashrc' or `.cshrc' is liable to lead to problems. A.7.5 PSFTP transfers files much slower than PSCP. The throughput of PSFTP 0.54 should be much better than 0.53b and prior; we've added code to the SFTP backend to queue several blocks of data rather than waiting for an acknowledgement for each. (The SCP backend did not suffer from this performance issue because SCP is a much simpler protocol.) A.7.6 When I run full-colour applications, I see areas of black space where colour ought to be, or vice versa. You almost certainly need to change the `Use background colour to erase screen' setting in the Terminal panel. If there is too much black space (the commoner situation), you should enable it, while if there is too much colour, you should disable it. (See section 4.3.5.) In old versions of PuTTY, this was disabled by default, and would not take effect until you reset the terminal (see question A.7.7). Since 0.54, it is enabled by default, and changes take effect immediately. A.7.7 When I change some terminal settings, nothing happens. Some of the terminal options (notably Auto Wrap and background- colour screen erase) actually represent the _default_ setting, rather than the currently active setting. The server can send sequences that modify these options in mid-session, but when the terminal is reset (by server action, or by you choosing `Reset Terminal' from the System menu) the defaults are restored. In versions 0.53b and prior, if you change one of these options in the middle of a session, you will find that the change does not immediately take effect. It will only take effect once you reset the terminal. In version 0.54, the behaviour has changed - changes to these settings take effect immediately. A.7.8 My PuTTY sessions unexpectedly close after they are idle for a while. Some types of firewall, and almost any router doing Network Address Translation (NAT, also known as IP masquerading), will forget about a connection through them if the connection does nothing for too long. This will cause the connection to be rudely cut off when contact is resumed. You can try to combat this by telling PuTTY to send _keepalives_: packets of data which have no effect on the actual session, but which reassure the router or firewall that the network connection is still active and worth remembering about. Keepalives don't solve everything, unfortunately; although they cause greater robustness against this sort of router, they can also cause a _loss_ of robustness against network dropouts. See section 4.14.1 in the documentation for more discussion of this. A.7.9 PuTTY's network connections time out too quickly when network connectivity is temporarily lost. This is a Windows problem, not a PuTTY problem. The timeout value can't be set on per application or per session basis. To increase the TCP timeout globally, you need to tinker with the Registry. On Windows 95, 98 or ME, the registry key you need to create or change is HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\VxD\ MSTCP\MaxDataRetries (it must be of type DWORD in Win95, or String in Win98/ME). (See MS Knowledge Base article 158474 for more information.) On Windows NT, 2000, or XP, the registry key to create or change is HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\ Parameters\TcpMaxDataRetransmissions and it must be of type DWORD. (See MS Knowledge Base articles 120642 and 314053 for more information.) Set the key's value to something like 10. This will cause Windows to try harder to keep connections alive instead of abandoning them. A.7.10 When I cat a binary file, I get `PuTTYPuTTYPuTTY' on my command line. Don't do that, then. This is designed behaviour; when PuTTY receives the character Control-E from the remote server, it interprets it as a request to identify itself, and so it sends back the string `PuTTY' as if that string had been entered at the keyboard. Control-E should only be sent by programs that are prepared to deal with the response. Writing a binary file to your terminal is likely to output many Control-E characters, and cause this behaviour. Don't do it. It's a bad plan. To mitigate the effects, you could configure the answerback string to be empty (see section 4.3.7); but writing binary files to your terminal is likely to cause various other unpleasant behaviour, so this is only a small remedy. A.7.11 When I cat a binary file, my window title changes to a nonsense string. Don't do that, then. It is designed behaviour that PuTTY should have the ability to adjust the window title on instructions from the server. Normally the control sequence that does this should only be sent deliberately, by programs that know what they are doing and intend to put meaningful text in the window title. Writing a binary file to your terminal runs the risk of sending the same control sequence by accident, and cause unexpected changes in the window title. Don't do it. A.7.12 My keyboard stops working once PuTTY displays the password prompt. No, it doesn't. PuTTY just doesn't display the password you type, so that someone looking at your screen can't see what it is. Unlike the Windows login prompts, PuTTY doesn't display the password as a row of asterisks either. This is so that someone looking at your screen can't even tell how _long_ your password is, which might be valuable information. A.7.13 One or more function keys don't do what I expected in a server-side application. If you've already tried all the relevant options in the PuTTY Keyboard panel, you may need to mail the PuTTY maintainers and ask. It is _not_ usually helpful just to tell us which application, which server operating system, and which key isn't working; in order to replicate the problem we would need to have a copy of every operating system, and every application, that anyone has ever complained about. PuTTY responds to function key presses by sending a sequence of control characters to the server. If a function key isn't doing what you expect, it's likely that the character sequence your application is expecting to receive is not the same as the one PuTTY is sending. Therefore what we really need to know is _what_ sequence the application is expecting. The simplest way to investigate this is to find some other terminal environment, in which that function key _does_ work; and then investigate what sequence the function key is sending in that situation. One reasonably easy way to do this on a Unix system is to type the command `cat', and then press the function key. This is likely to produce output of the form `^[[11~'. You can also do this in PuTTY, to find out what sequence the function key is producing in that. Then you can mail the PuTTY maintainers and tell us `I wanted the F1 key to send `^[[11~', but instead it's sending `^[OP', can this be done?', or something similar. You should still read the Feedback page on the PuTTY website (also provided as appendix B in the manual), and follow the guidelines contained in that. A.7.14 Why do I see `Couldn't load private key from ...'? Why can PuTTYgen load my key but not PuTTY? It's likely that you've generated an SSH protocol 2 key with PuTTYgen, but you're trying to use it in an SSH-1 connection. SSH- 1 and SSH-2 keys have different formats, and (at least in 0.52) PuTTY's reporting of a key in the wrong format isn't optimal. To connect using SSH-2 to a server that supports both versions, you need to change the configuration from the default (see question A.2.1). A.7.15 When I'm connected to a Red Hat Linux 8.0 system, some characters don't display properly. A common complaint is that hyphens in man pages show up as a-acute. With release 8.0, Red Hat appear to have made UTF-8 the default character set. There appears to be no way for terminal emulators such as PuTTY to know this (as far as we know, the appropriate escape sequence to switch into UTF-8 mode isn't sent). A fix is to configure sessions to RH8 systems to use UTF-8 translation - see section 4.10.1 in the documentation. (Note that if you use `Change Settings', changes may not take place immediately - see question A.7.7.) If you really want to change the character set used by the server, the right place is `/etc/sysconfig/i18n', but this shouldn't be necessary. A.7.16 Since I upgraded to PuTTY 0.54, the scrollback has stopped working when I run `screen'. PuTTY's terminal emulator has always had the policy that when the `alternate screen' is in use, nothing is added to the scrollback. This is because the usual sorts of programs which use the alternate screen are things like text editors, which tend to scroll back and forth in the same document a lot; so (a) they would fill up the scrollback with a large amount of unhelpfully disordered text, and (b) they contain their _own_ method for the user to scroll back to the bit they were interested in. We have generally found this policy to do the Right Thing in almost all situations. Unfortunately, `screen' is one exception: it uses the alternate screen, but it's still usually helpful to have PuTTY's scrollback continue working. The simplest solution is to go to the Features control panel and tick `Disable switching to alternate terminal screen'. (See section 4.6.4 for more details.) Alternatively, you can tell `screen' itself not to use the alternate screen: the `screen' FAQ suggests adding the line `termcapinfo xterm ti@:te@' to your .screenrc file. The reason why this only started to be a problem in 0.54 is because `screen' typically uses an unusual control sequence to switch to the alternate screen, and previous versions of PuTTY did not support this sequence. A.7.17 Since I upgraded Windows XP to Service Pack 2, I can't use addresses like 127.0.0.2. Some people who ask PuTTY to listen on localhost addresses other than 127.0.0.1 to forward services such as SMB and Windows Terminal Services have found that doing so no longer works since they upgraded to WinXP SP2. This is apparently an issue with SP2 that is acknowledged by Microsoft in MS Knowledge Base article 884020. The article links to a fix you can download. (_However_, we've been told that SP2 _also_ fixes the bug that means you need to use non-127.0.0.1 addresses to forward Terminal Services in the first place.) A.7.18 PSFTP commands seem to be missing a directory separator (slash). Some people have reported the following incorrect behaviour with PSFTP: psftp> pwd Remote directory is /dir1/dir2 psftp> get filename.ext /dir1/dir2filename.ext: no such file or directory This is not a bug in PSFTP. There is a known bug in some versions of portable OpenSSH (bug 697) that causes these symptoms; it appears to have been introduced around 3.7.x. It manifests only on certain platforms (AIX is what has been reported to us). There is a patch for OpenSSH attached to that bug; it's also fixed in recent versions of portable OpenSSH (from around 3.8). A.7.19 Do you want to hear about `Software caused connection abort'? In the documentation for PuTTY 0.53 and 0.53b, we mentioned that we'd like to hear about any occurrences of this error. Since the release of PuTTY 0.54, however, we've been convinced that this error doesn't indicate that PuTTY's doing anything wrong, and we don't need to hear about further occurrences. See section 10.15 for our current documentation of this error. A.7.20 My SSH-2 session locks up for a few seconds every so often. Recent versions of PuTTY automatically initiate repeat key exchange once per hour, to improve session security. If your client or server machine is slow, you may experience this as a delay of anything up to thirty seconds or so. These delays are inconvenient, but they are there for your protection. If they really cause you a problem, you can choose to turn off periodic rekeying using the `Kex' configuration panel (see section 4.18), but be aware that you will be sacrificing security for this. (Falling back to SSH-1 would also remove the delays, but would lose a _lot_ more security still. We do not recommend it.) A.7.21 PuTTY fails to start up. Windows claims that `the application configuration is incorrect'. This is caused by a bug in certain versions of Windows XP which is triggered by PuTTY 0.58. This was fixed in 0.59. The `xp-wont-run' entry in PuTTY's wishlist has more details. A.7.22 When I put 32-bit PuTTY in C:\WINDOWS\SYSTEM32 on my 64-bit Windows system, `Duplicate Session' doesn't work. The short answer is not to put the PuTTY executables in that location. On 64-bit systems, C:\WINDOWS\SYSTEM32 is intended to contain only 64-bit binaries; Windows' 32-bit binaries live in C:\WINDOWS\SYSWOW64. When a 32-bit PuTTY executable runs on a 64- bit system, it cannot by default see the `real' C:\WINDOWS\SYSTEM32 at all, because the File System Redirector arranges that the running program sees the appropriate kind of binaries in SYSTEM32. Thus, operations in the PuTTY suite that involve it accessing its own executables, such as `New Session' and `Duplicate Session', will not work. A.7.23 After I upgraded PuTTY to 0.68, I can no longer connect to my embedded device or appliance. If your SSH server has started unexpectedly closing SSH connections after you enter your password, and it worked before 0.68, you may have a buggy server that objects to certain SSH protocol extensions. The SSH protocol recently gained a new `terminal mode', IUTF8, which PuTTY sends by default; see section 4.23.2. This is the first new terminal mode since the SSH-2 protocol was defined. While servers are supposed to ignore modes they don't know about, some buggy servers will unceremoniously close the connection if they see anything they don't recognise. SSH servers in embedded devices, network appliances, and the like seem to disproportionately have this bug. If you think you have such a server, from 0.69 onwards you can disable sending of the IUTF8 mode: on the SSH / TTY panel, select IUTF8 on the list, select `Nothing', and press `Set'. (It's not possible to disable sending this mode in 0.68.) A.8 Security questions A.8.1 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 you plan to type passwords into either. It might be watching your keystrokes, or it might tamper with the PuTTY binary you download. There is _no_ program safe enough that you can run it on an actively malicious PC and get away with typing passwords into it. If you do trust the PC, then it's probably OK to use PuTTY on it (but if you don't trust the network, then the PuTTY download might be tampered with, so it would be better to carry PuTTY with you on a USB stick). A.8.2 What does PuTTY leave on a system? How can I clean up after it? PuTTY will leave some Registry entries, and a random seed file, on the PC (see question A.5.2). Windows 7 and up also remember some information about recently launched sessions for the `jump list' feature. If you are using PuTTY on a public PC, or somebody else's PC, you might want to clean this information up when you leave. You can do that automatically, by running the command `putty -cleanup'. See section 3.11.2 in the documentation for more detail. (Note that this only removes settings for the currently logged-in user on multi-user systems.) If PuTTY was installed from the installer package, it will also appear in `Add/Remove Programs'. Current versions of the installer do not offer to remove the above-mentioned items, so if you want them removed you should run `putty -cleanup' before uninstalling. A.8.3 How come PuTTY now supports DSA, when the website used to say how insecure it was? DSA has a major weakness _if badly implemented_: it relies on a random number generator to far too great an extent. If the random number generator produces a number an attacker can predict, the DSA private key is exposed - meaning that the attacker can log in as you on all systems that accept that key. The PuTTY policy changed because the developers were informed of ways to implement DSA which do not suffer nearly as badly from this weakness, and indeed which don't need to rely on random numbers at all. For this reason we now believe PuTTY's DSA implementation is probably OK. The recently added elliptic-curve signature methods are also DSA- style algorithms, so they have this same weakness in principle. Our ECDSA implementation uses the same defence as DSA, while our Ed25519 implementation uses the similar system (but different in details) that the Ed25519 spec mandates. A.8.4 Couldn't Pageant use VirtualLock() to stop private keys being written to disk? Unfortunately not. The VirtualLock() function in the Windows API doesn't do a proper job: it may prevent small pieces of a process's memory from being paged to disk while the process is running, but it doesn't stop the process's memory as a whole from being swapped completely out to disk when the process is long-term inactive. And Pageant spends most of its time inactive. A.9 Administrative questions A.9.1 Is putty.org your website? No, it isn't. putty.org is run by an opportunist who uses it to advertise their own commercial SSH implementation to people looking for our free one. We don't own that site, we can't control it, and we don't advise anyone to use it in preference to our own site. The real PuTTY web site, run by the PuTTY team, has always been at https://www.chiark.greenend.org.uk/~sgtatham/putty/. A.9.2 Would you like me to register you a nicer domain name? No, thank you. Even if you can find one (most of them seem to have been registered already, by people who didn't ask whether we actually wanted it before they applied), we're happy with the PuTTY web site being exactly where it is. It's not hard to find (just type `putty' into google.com and we're the first link returned), and we don't believe the administrative hassle of moving the site would be worth the benefit. In addition, if we _did_ want a custom domain name, we would want to run it ourselves, so we knew for certain that it would continue to point where we wanted it, and wouldn't suddenly change or do strange things. Having it registered for us by a third party who we don't even know is not the best way to achieve this. A.9.3 Would you like free web hosting for the PuTTY web site? We already have some, thanks. A.9.4 Would you link to my web site from the PuTTY web site? Only if the content of your web page is of definite direct interest to PuTTY users. If your content is unrelated, or only tangentially related, to PuTTY, then the link would simply be advertising for you. One very nice effect of the Google ranking mechanism is that by and large, the most popular web sites get the highest rankings. This means that when an ordinary person does a search, the top item in the search is very likely to be a high-quality site or the site they actually wanted, rather than the site which paid the most money for its ranking. The PuTTY web site is held in high esteem by Google, for precisely this reason: lots of people have linked to it simply because they like PuTTY, without us ever having to ask anyone to link to us. We feel that it would be an abuse of this esteem to use it to boost the ranking of random advertisers' web sites. If you want your web site to have a high Google ranking, we'd prefer that you achieve this the way we did - by being good enough at what you do that people will link to you simply because they like you. In particular, we aren't interested in trading links for money (see above), and we _certainly_ aren't interested in trading links for other links (since we have no advertising on our web site, our Google ranking is not even directly worth anything to us). If we don't want to link to you for free, then we probably won't want to link to you at all. If you have software based on PuTTY, or specifically designed to interoperate with PuTTY, or in some other way of genuine interest to PuTTY users, then we will probably be happy to add a link to you on our Links page. And if you're running a particularly valuable mirror of the PuTTY web site, we might be interested in linking to you from our Mirrors page. A.9.5 Why don't you move PuTTY to SourceForge? Partly, because we don't want to move the web site location (see question A.9.2). Also, security reasons. PuTTY is a security product, and as such it is particularly important to guard the code and the web site against unauthorised modifications which might introduce subtle security flaws. Therefore, we prefer that the Git repository, web site and FTP site remain where they are, under the direct control of system administrators we know and trust personally, rather than being run by a large organisation full of people we've never met and which is known to have had breakins in the past. No offence to SourceForge; I think they do a wonderful job. But they're not ideal for everyone, and in particular they're not ideal for us. A.9.6 Why can't I subscribe to the putty-bugs mailing list? Because you're not a member of the PuTTY core development team. The putty-bugs mailing list is not a general newsgroup-like discussion forum; it's a contact address for the core developers, and an _internal_ mailing list for us to discuss things among ourselves. If we opened it up for everybody to subscribe to, it would turn into something more like a newsgroup and we would be completely overwhelmed by the volume of traffic. It's hard enough to keep up with the list as it is. A.9.7 If putty-bugs isn't a general-subscription mailing list, what is? There isn't one, that we know of. If someone else wants to set up a mailing list or other forum for PuTTY users to help each other with common problems, that would be fine with us, though the PuTTY team would almost certainly not have the time to read it. It's probably better to use one of the established newsgroups for this purpose (see section B.1.2). A.9.8 How can I donate to PuTTY development? Please, _please_ don't feel you have to. PuTTY is completely free software, and not shareware. We think it's very important that _everybody_ who wants to use PuTTY should be able to, whether they have any money or not; so the last thing we would want is for a PuTTY user to feel guilty because they haven't paid us any money. If you want to keep your money, please do keep it. We wouldn't dream of asking for any. Having said all that, if you still really _want_ to give us money, we won't argue :-) The easiest way for us to accept donations is if you send money to using PayPal (www.paypal.com). If you don't like PayPal, talk to us; we can probably arrange some alternative means. Small donations (tens of dollars or tens of euros) will probably be spent on beer or curry, which helps motivate our volunteer team to continue doing this for the world. Larger donations will be spent on something that actually helps development, if we can find anything (perhaps new hardware, or a copy of Windows XP), but if we can't find anything then we'll just distribute the money among the developers. If you want to be sure your donation is going towards something worthwhile, ask us first. If you don't like these terms, feel perfectly free not to donate. We don't mind. A.9.9 Can I have permission to put PuTTY on a cover disk / distribute it with other software / etc? Yes. For most things, you need not bother asking us explicitly for permission; our licence already grants you permission. See section B.8 for more details. A.9.10 Can you sign an agreement indemnifying us against security problems in PuTTY? No! A vendor of physical security products (e.g. locks) might plausibly be willing to accept financial liability for a product that failed to perform as advertised and resulted in damage (e.g. valuables being stolen). The reason they can afford to do this is because they sell a _lot_ of units, and only a small proportion of them will fail; so they can meet their financial liability out of the income from all the rest of their sales, and still have enough left over to make a profit. Financial liability is intrinsically linked to selling your product for money. There are two reasons why PuTTY is not analogous to a physical lock in this context. One is that software products don't exhibit random variation: _if_ PuTTY has a security hole (which does happen, although we do our utmost to prevent it and to respond quickly when it does), every copy of PuTTY will have the same hole, so it's likely to affect all the users at the same time. So even if our users were all paying us to use PuTTY, we wouldn't be able to _simultaneously_ pay every affected user compensation in excess of the amount they had paid us in the first place. It just wouldn't work. The second, much more important, reason is that PuTTY users _don't_ pay us. The PuTTY team does not have an income; it's a volunteer effort composed of people spending their spare time to try to write useful software. We aren't even a company or any kind of legally recognised organisation. We're just a bunch of people who happen to do some stuff in our spare time. Therefore, to ask us to assume financial liability is to ask us to assume a risk of having to pay it out of our own _personal_ pockets: out of the same budget from which we buy food and clothes and pay our rent. That's more than we're willing to give. We're already giving a lot of our spare _time_ to developing software for free; if we had to pay our own _money_ to do it as well, we'd start to wonder why we were bothering. Free software fundamentally does not work on the basis of financial guarantees. Your guarantee of the software functioning correctly is simply that you have the source code and can check it before you use it. If you want to be sure there aren't any security holes, do a security audit of the PuTTY code, or hire a security engineer if you don't have the necessary skills yourself: instead of trying to ensure you can get compensation in the event of a disaster, try to ensure there isn't a disaster in the first place. If you _really_ want financial security, see if you can find a security engineer who will take financial responsibility for the correctness of their review. (This might be less likely to suffer from the everything-failing-at-once problem mentioned above, because such an engineer would probably be reviewing a lot of _different_ products which would tend to fail independently.) Failing that, see if you can persuade an insurance company to insure you against security incidents, and if the insurer demands it as a condition then get our code reviewed by a security engineer they're happy with. A.9.11 Can you sign this form granting us permission to use/distribute PuTTY? If your form contains any clause along the lines of `the undersigned represents and warrants', we're not going to sign it. This is particularly true if it asks us to warrant that PuTTY is secure; see question A.9.10 for more discussion of this. But it doesn't really matter what we're supposed to be warranting: even if it's something we already believe is true, such as that we don't infringe any third-party copyright, we will not sign a document accepting any legal or financial liability. This is simply because the PuTTY development project has no income out of which to satisfy that liability, or pay legal costs, should it become necessary. We cannot afford to be sued. We are assuring you that _we have done our best_; if that isn't good enough for you, tough. The existing PuTTY licence document already gives you permission to use or distribute PuTTY in pretty much any way which does not involve pretending you wrote it or suing us if it goes wrong. We think that really ought to be enough for anybody. See also question A.9.13 for another reason why we don't want to do this sort of thing. A.9.12 Can you write us a formal notice of permission to use PuTTY? We could, in principle, but it isn't clear what use it would be. If you think there's a serious chance of one of the PuTTY copyright holders suing you (which we don't!), you would presumably want a signed notice from _all_ of them; and we couldn't provide that even if we wanted to, because many of the copyright holders are people who contributed some code in the past and with whom we subsequently lost contact. Therefore the best we would be able to do _even in theory_ would be to have the core development team sign the document, which wouldn't guarantee you that some other copyright holder might not sue. See also question A.9.13 for another reason why we don't want to do this sort of thing. A.9.13 Can you sign _anything_ for us? Not unless there's an incredibly good reason. We are generally unwilling to set a precedent that involves us having to enter into individual agreements with PuTTY users. We estimate that we have literally _millions_ of users, and we absolutely would not have time to go round signing specific agreements with every one of them. So if you want us to sign something specific for you, you might usefully stop to consider whether there's anything special that distinguishes you from 999,999 other users, and therefore any reason we should be willing to sign something for you without it setting such a precedent. If your company policy requires you to have an individual agreement with the supplier of any software you use, then your company policy is simply not well suited to using popular free software, and we urge you to consider this as a flaw in your policy. A.9.14 If you won't sign anything, can you give us some sort of assurance that you won't make PuTTY closed-source in future? Yes and no. If what you want is an assurance that some _current version_ of PuTTY which you've already downloaded will remain free, then you already have that assurance: it's called the PuTTY Licence. It grants you permission to use, distribute and copy the software to which it applies; once we've granted that permission (which we have), we can't just revoke it. On the other hand, if you want an assurance that _future_ versions of PuTTY won't be closed-source, that's more difficult. We could in principle sign a document stating that we would never release a closed-source PuTTY, but that wouldn't assure you that we _would_ keep releasing _open_-source PuTTYs: we would still have the option of ceasing to develop PuTTY at all, which would surely be even worse for you than making it closed-source! (And we almost certainly wouldn't _want_ to sign a document guaranteeing that we would actually continue to do development work on PuTTY; we certainly wouldn't sign it for free. Documents like that are called contracts of employment, and are generally not signed except in return for a sizeable salary.) If we _were_ to stop developing PuTTY, or to decide to make all future releases closed-source, then you would still be free to copy the last open release in accordance with the current licence, and in particular you could start your own fork of the project from that release. If this happened, I confidently predict that _somebody_ would do that, and that some kind of a free PuTTY would continue to be developed. There's already precedent for that sort of thing happening in free software. We can't guarantee that somebody _other than you_ would do it, of course; you might have to do it yourself. But we can assure you that there would be nothing _preventing_ anyone from continuing free development if we stopped. (Finally, we can also confidently predict that if we made PuTTY closed-source and someone made an open-source fork, most people would switch to the latter. Therefore, it would be pretty stupid of us to try it.) A.9.15 Can you provide us with export control information / FIPS certification for PuTTY? Some people have asked us for an Export Control Classification Number (ECCN) for PuTTY. We don't know whether we have one, and as a team of free software developers based in the UK we don't have the time, money, or effort to deal with US bureaucracy to investigate any further. We believe that PuTTY falls under 5D002 on the US Commerce Control List, but that shouldn't be taken as definitive. If you need to know more you should seek professional legal advice. The same applies to any other country's legal requirements and restrictions. Similarly, some people have asked us for FIPS certification of the PuTTY tools. Unless someone else is prepared to do the necessary work and pay any costs, we can't provide this. A.9.16 As one of our existing software vendors, can you just fill in this questionnaire for us? We periodically receive requests like this, from organisations which have apparently sent out a form letter to everyone listed in their big spreadsheet of `software vendors' requiring them all to answer some long list of questions about supported OS versions, paid support arrangements, compliance with assorted local regulations we haven't heard of, contact phone numbers, and other such administrivia. Many of the questions are obviously meaningless when applied to PuTTY (we don't provide any paid support in the first place!), most of the rest could have been answered with only a very quick look at our website, and some we are actively unwilling to answer (we are private individuals, why would we want to give out our home phone numbers to large corporations?). We don't make a habit of responding in full to these questionnaires, because _we are not a software vendor_. A software _vendor_ is a company to which you are paying lots of money in return for some software. They know who you are, and they know you're paying them money; so they have an incentive to fill in your forms and questionnaires, to research any local regulations you cite if they don't already know about them, and generally to provide every scrap of information you might possibly need in the most convenient manner for you, because they want to keep being paid. But we are a team of free software developers, and that means your relationship with us is nothing like that at all. If you once downloaded our software from our website, that's great and we hope you found it useful, but it doesn't mean we have the least idea who you are, or any incentive to do lots of unpaid work to support our `relationship' with you. It's not that we are unwilling to _provide information_. We put as much of it as we can on our website for your convenience, and if you actually need to know some fact about PuTTY which you haven't been able to find on the website (and which is not obviously inapplicable to free software in the first place) then please do ask us, and we'll try to answer as best we can. But we put up the website and this FAQ precisely so that we _don't_ have to keep answering the same questions over and over again, so we aren't prepared to fill in completely generic form-letter questionnaires for people who haven't done their best to find the answers here first. If you work for an organisation which you think might be at risk of making this mistake, we urge you to reorganise your list of software suppliers so that it clearly distinguishes paid vendors who know about you from free software developers who don't have any idea who you are. Then, only send out these mass mailings to the former. A.9.17 The `sha1sums' / `sha256sums' / etc files on your download page don't match the binaries. People report this every so often, and usually the reason turns out to be that they've matched up the wrong checksums file with the wrong binaries. The PuTTY download page contains more than one version of the software. There's a _latest release_ version; there are the _development snapshots_; and when we're in the run-up to making a release, there are also _pre-release_ builds of the upcoming new version. Each one has its own collection of binaries, and its own collection of checksums files to go with them. So if you've downloaded the release version of the actual program, you need the release version of the checksums too, otherwise you will see a mismatch. Similarly, the development snapshot binaries go with the development snapshot checksums, and so on. (We've colour- coded the download page in an effort to reduce this confusion a bit.) Another thing to watch out for: as of 0.71, executables like `putty.exe' come in two flavours for each platform: the standalone versions on the website, each of which contains embedded help, and the versions installed by the installer, which use a separate help file also in the installer. We provide checksums for both; the latter are indicated with `(installer version)' after the filename. If you have double-checked all that, and you still think there's a real mismatch, then please send us a report carefully quoting everything relevant: - the exact URL you got your binary from - the checksum of the binary after you downloaded - the exact URL you got your checksums file from - the checksum that file says the binary should have. A.10 Miscellaneous questions A.10.1 Is PuTTY a port of OpenSSH, or based on OpenSSH or OpenSSL? No, it isn't. PuTTY is almost completely composed of code written from scratch for PuTTY. The only code we share with OpenSSH is the detector for SSH-1 CRC compensation attacks, written by CORE SDI S.A; we share no code at all with OpenSSL. A.10.2 Where can I buy silly putty? You're looking at the wrong web site; the only PuTTY we know about here is the name of a computer program. If you want the kind of putty you can buy as an executive toy, the PuTTY team can personally recommend Thinking Putty, which you can buy from Crazy Aaron's Putty World, at www.puttyworld.com. A.10.3 What does `PuTTY' mean? It's the name of a popular SSH and Telnet client. Any other meaning is in the eye of the beholder. It's been rumoured that `PuTTY' is the antonym of `getty', or that it's the stuff that makes your Windows useful, or that it's a kind of plutonium Teletype. We couldn't possibly comment on such allegations. A.10.4 How do I pronounce `PuTTY'? Exactly like the English word `putty', which we pronounce /'pVti/. Appendix B: Feedback and bug reporting -------------------------------------- This is a guide to providing feedback to the PuTTY development team. It is provided as both a web page on the PuTTY site, and an appendix in the PuTTY manual. Section B.1 gives some general guidelines for sending any kind of e- mail to the development team. Following sections give more specific guidelines for particular types of e-mail, such as bug reports and feature requests. B.1 General guidelines The PuTTY development team gets a _lot_ of mail. If you can possibly solve your own problem by reading the manual, reading the FAQ, reading the web site, asking a fellow user, perhaps posting to a newsgroup (see section B.1.2), or some other means, then it would make our lives much easier. We get so much e-mail that we literally do not have time to answer it all. We regret this, but there's nothing we can do about it. So if you can _possibly_ avoid sending mail to the PuTTY team, we recommend you do so. In particular, support requests (section B.6) are probably better sent to newsgroups, or passed to a local expert if possible. The PuTTY contact email address is a private mailing list containing four or five core developers. Don't be put off by it being a mailing list: if you need to send confidential data as part of a bug report, you can trust the people on the list to respect that confidence. Also, the archives aren't publicly available, so you shouldn't be letting yourself in for any spam by sending us mail. Please use a meaningful subject line on your message. We get a lot of mail, and it's hard to find the message we're looking for if they all have subject lines like `PuTTY bug'. B.1.1 Sending large attachments Since the PuTTY contact address is a mailing list, e-mails larger than 40Kb will be held for inspection by the list administrator, and will not be allowed through unless they really appear to be worth their large size. If you are considering sending any kind of large data file to the PuTTY team, it's almost always a bad idea, or at the very least it would be better to ask us first whether we actually need the file. Alternatively, you could put the file on a web site and just send us the URL; that way, we don't have to download it unless we decide we actually need it, and only one of us needs to download it instead of it being automatically copied to all the developers. (If the file contains confidential information, then you could encrypt it with our Secure Contact Key; see section F.1 for details. Please _only_ use this for information that _needs_ to be confidential.) Some people like to send mail in MS Word format. Please _don't_ send us bug reports, or any other mail, as a Word document. Word documents are roughly fifty times larger than writing the same report in plain text. In addition, most of the PuTTY team read their e-mail on Unix machines, so copying the file to a Windows box to run Word is very inconvenient. Not only that, but several of us don't even _have_ a copy of Word! Some people like to send us screen shots when demonstrating a problem. Please don't do this without checking with us first - we almost never actually need the information in the screen shot. Sending a screen shot of an error box is almost certainly unnecessary when you could just tell us in plain text what the error was. (On some versions of Windows, pressing Ctrl-C when the error box is displayed will copy the text of the message to the clipboard.) Sending a full-screen shot is _occasionally_ useful, but it's probably still wise to check whether we need it before sending it. If you _must_ mail a screen shot, don't send it as a .BMP file. BMPs have no compression and they are _much_ larger than other image formats such as PNG, TIFF and GIF. Convert the file to a properly compressed image format before sending it. Please don't mail us executables, at all. Our mail server blocks all incoming e-mail containing executables, as a defence against the vast numbers of e-mail viruses we receive every day. If you mail us an executable, it will just bounce. If you have made a tiny modification to the PuTTY code, please send us a _patch_ to the source code if possible, rather than sending us a huge .ZIP file containing the complete sources plus your modification. If you've only changed 10 lines, we'd prefer to receive a mail that's 30 lines long than one containing multiple megabytes of data we already have. B.1.2 Other places to ask for help There are two Usenet newsgroups that are particularly relevant to the PuTTY tools: - `comp.security.ssh', for questions specific to using the SSH protocol; - `comp.terminals', for issues relating to terminal emulation (for instance, keyboard problems). Please use the newsgroup most appropriate to your query, and remember that these are general newsgroups, not specifically about PuTTY. If you don't have direct access to Usenet, you can access these newsgroups through Google Groups (groups.google.com). B.2 Reporting bugs If you think you have found a bug in PuTTY, your first steps should be: - Check the Wishlist page on the PuTTY website, and see if we already know about the problem. If we do, it is almost certainly not necessary to mail us about it, unless you think you have extra information that might be helpful to us in fixing it. (Of course, if we actually _need_ specific extra information about a particular bug, the Wishlist page will say so.) - Check the Change Log on the PuTTY website, and see if we have already fixed the bug in the development snapshots. - Check the FAQ on the PuTTY website (also provided as appendix A in the manual), and see if it answers your question. The FAQ lists the most common things which people think are bugs, but which aren't bugs. - Download the latest development snapshot and see if the problem still happens with that. This really is worth doing. As a general rule we aren't very interested in bugs that appear in the release version but not in the development version, because that usually means they are bugs we have _already fixed_. On the other hand, if you can find a bug in the development version that doesn't appear in the release, that's likely to be a new bug we've introduced since the release and we're definitely interested in it. If none of those options solved your problem, and you still need to report a bug to us, it is useful if you include some general information: - Tell us what version of PuTTY you are running. To find this out, use the `About PuTTY' option from the System menu. Please _do not_ just tell us `I'm running the latest version'; e-mail can be delayed and it may not be obvious which version was the latest at the time you sent the message. - PuTTY is a multi-platform application; tell us what version of what OS you are running PuTTY on. (If you're running on Unix, or Windows for Arm, tell us, or we'll assume you're running on Windows for Intel as this is overwhelmingly the case.) - Tell us what protocol you are connecting with: SSH, Telnet, Rlogin, SUPDUP, or Raw mode, or a serial connection. - Tell us what kind of server you are connecting to; what OS, and if possible what SSH server (if you're using SSH). You can get some of this information from the PuTTY Event Log (see section 3.1.3.1 in the manual). - Send us the contents of the PuTTY Event Log, unless you have a specific reason not to (for example, if it contains confidential information that you think we should be able to solve your problem without needing to know). - Try to give us as much information as you can to help us see the problem for ourselves. If possible, give us a step-by-step sequence of _precise_ instructions for reproducing the fault. - Don't just tell us that PuTTY `does the wrong thing'; tell us exactly and precisely what it did, and also tell us exactly and precisely what you think it should have done instead. Some people tell us PuTTY does the wrong thing, and it turns out that it was doing the right thing and their expectations were wrong. Help to avoid this problem by telling us exactly what you think it should have done, and exactly what it did do. - If you think you can, you're welcome to try to fix the problem yourself. A patch to the code which fixes a bug is an excellent addition to a bug report. However, a patch is never a _substitute_ for a good bug report; if your patch is wrong or inappropriate, and you haven't supplied us with full information about the actual bug, then we won't be able to find a better solution. - https://www.chiark.greenend.org.uk/~sgtatham/bugs.html is an article on how to report bugs effectively in general. If your bug report is _particularly_ unclear, we may ask you to go away, read this article, and then report the bug again. It is reasonable to report bugs in PuTTY's documentation, if you think the documentation is unclear or unhelpful. But we do need to be given exact details of _what_ you think the documentation has failed to tell you, or _how_ you think it could be made clearer. If your problem is simply that you don't _understand_ the documentation, we suggest posting to a newsgroup (see section B.1.2) and seeing if someone will explain what you need to know. _Then_, if you think the documentation could usefully have told you that, send us a bug report and explain how you think we should change it. B.3 Reporting security vulnerabilities If you've found a security vulnerability in PuTTY, you might well want to notify us using an encrypted communications channel, to avoid disclosing information about the vulnerability before a fixed release is available. For this purpose, we provide a GPG key suitable for encryption: the Secure Contact Key. See section F.1 for details of this. (Of course, vulnerabilities are also bugs, so please do include as much information as possible about them, the same way you would with any other bug report.) B.4 Requesting extra features If you want to request a new feature in PuTTY, the very first things you should do are: - Check the Wishlist page on the PuTTY website, and see if your feature is already on the list. If it is, it probably won't achieve very much to repeat the request. (But see section B.5 if you want to persuade us to give your particular feature higher priority.) - Check the Wishlist and Change Log on the PuTTY website, and see if we have already added your feature in the development snapshots. If it isn't clear, download the latest development snapshot and see if the feature is present. If it is, then it will also be in the next release and there is no need to mail us at all. If you can't find your feature in either the development snapshots _or_ the Wishlist, then you probably do need to submit a feature request. Since the PuTTY authors are very busy, it helps if you try to do some of the work for us: - Do as much of the design as you can. Think about `corner cases'; think about how your feature interacts with other existing features. Think about the user interface; if you can't come up with a simple and intuitive interface to your feature, you shouldn't be surprised if we can't either. Always imagine whether it's possible for there to be more than one, or less than one, of something you'd assumed there would be one of. (For example, if you were to want PuTTY to put an icon in the System tray rather than the Taskbar, you should think about what happens if there's more than one PuTTY active; how would the user tell which was which?) - If you can program, it may be worth offering to write the feature yourself and send us a patch. However, it is likely to be helpful if you confer with us first; there may be design issues you haven't thought of, or we may be about to make big changes to the code which your patch would clash with, or something. If you check with the maintainers first, there is a better chance of your code actually being usable. Also, read the design principles listed in appendix E: if you do not conform to them, we will probably not be able to accept your patch. B.5 Requesting features that have already been requested If a feature is already listed on the Wishlist, then it usually means we would like to add it to PuTTY at some point. However, this may not be in the near future. If there's a feature on the Wishlist which you would like to see in the _near_ future, there are several things you can do to try to increase its priority level: - Mail us and vote for it. (Be sure to mention that you've seen it on the Wishlist, or we might think you haven't even _read_ the Wishlist). This probably won't have very _much_ effect; if a huge number of people vote for something then it may make a difference, but one or two extra votes for a particular feature are unlikely to change our priority list immediately. Offering a new and compelling justification might help. Also, don't expect a reply. - Offer us money if we do the work sooner rather than later. This sometimes works, but not always. The PuTTY team all have full- time jobs and we're doing all of this work in our free time; we may sometimes be willing to give up some more of our free time in exchange for some money, but if you try to bribe us for a _big_ feature it's entirely possible that we simply won't have the time to spare - whether you pay us or not. (Also, we don't accept bribes to add _bad_ features to the Wishlist, because our desire to provide high-quality software to the users comes first.) - Offer to help us write the code. This is probably the _only_ way to get a feature implemented quickly, if it's a big one that we don't have time to do ourselves. B.6 Support requests If you're trying to make PuTTY do something for you and it isn't working, but you're not sure whether it's a bug or not, then _please_ consider looking for help somewhere else. This is one of the most common types of mail the PuTTY team receives, and we simply don't have time to answer all the questions. Questions of this type include: - If you want to do something with PuTTY but have no idea where to start, and reading the manual hasn't helped, try posting to a newsgroup (see section B.1.2) and see if someone can explain it to you. - If you have tried to do something with PuTTY but it hasn't worked, and you aren't sure whether it's a bug in PuTTY or a bug in your SSH server or simply that you're not doing it right, then try posting to a newsgroup (see section B.1.2) and see if someone can solve your problem. Or try doing the same thing with a different SSH client and see if it works with that. Please do not report it as a PuTTY bug unless you are really sure it _is_ a bug in PuTTY. - If someone else installed PuTTY for you, or you're using PuTTY on someone else's computer, try asking them for help first. They're more likely to understand how they installed it and what they expected you to use it for than we are. - If you have successfully made a connection to your server and now need to know what to type at the server's command prompt, or other details of how to use the server-end software, talk to your server's system administrator. This is not the PuTTY team's problem. PuTTY is only a communications tool, like a telephone; if you can't speak the same language as the person at the other end of the phone, it isn't the telephone company's job to teach it to you. If you absolutely cannot get a support question answered any other way, you can try mailing it to us, but we can't guarantee to have time to answer it. B.7 Web server administration If the PuTTY web site is down (Connection Timed Out), please don't bother mailing us to tell us about it. Most of us read our e-mail on the same machines that host the web site, so if those machines are down then we will notice _before_ we read our e-mail. So there's no point telling us our servers are down. Of course, if the web site has some other error (Connection Refused, 404 Not Found, 403 Forbidden, or something else) then we might _not_ have noticed and it might still be worth telling us about it. If you want to report a problem with our web site, check that you're looking at our _real_ web site and not a mirror. The real web site is at `https://www.chiark.greenend.org.uk/~sgtatham/putty/'; if that's not where you're reading this, then don't report the problem to us until you've checked that it's really a problem with the main site. If it's only a problem with the mirror, you should try to contact the administrator of that mirror site first, and only contact us if that doesn't solve the problem (in case we need to remove the mirror from our list). B.8 Asking permission for things PuTTY is distributed under the MIT Licence (see appendix D for details). This means you can do almost _anything_ you like with our software, our source code, and our documentation. The only things you aren't allowed to do are to remove our copyright notices or the licence text itself, or to hold us legally responsible if something goes wrong. So if you want permission to include PuTTY on a magazine cover disk, or as part of a collection of useful software on a CD or a web site, then _permission is already granted_. You don't have to mail us and ask. Just go ahead and do it. We don't mind. (If you want to distribute PuTTY alongside your own application for use with that application, or if you want to distribute PuTTY within your own organisation, then we recommend, but do not insist, that you offer your own first-line technical support, to answer questions about the interaction of PuTTY with your environment. If your users mail us directly, we won't be able to tell them anything useful about your specific setup.) If you want to use parts of the PuTTY source code in another program, then it might be worth mailing us to talk about technical details, but if all you want is to ask permission then you don't need to bother. You already have permission. If you just want to link to our web site, just go ahead. (It's not clear that we _could_ stop you doing this, even if we wanted to!) B.9 Mirroring the PuTTY web site If you want to set up a mirror of the PuTTY website, go ahead and set one up. Please don't bother asking us for permission before setting up a mirror. You already have permission. If the mirror is in a country where we don't already have plenty of mirrors, we may be willing to add it to the list on our mirrors page. Read the guidelines on that page, make sure your mirror works, and email us the information listed at the bottom of the page. Note that we do not _promise_ to list your mirror: we get a lot of mirror notifications and yours may not happen to find its way to the top of the list. Also note that we link to all our mirror sites using the `rel="nofollow"' attribute. Running a PuTTY mirror is not intended to be a cheap way to gain search rankings. If you have technical questions about the process of mirroring, then you might want to mail us before setting up the mirror (see also the guidelines on the Mirrors page); but if you just want to ask for permission, you don't need to. You already have permission. B.10 Praise and compliments One of the most rewarding things about maintaining free software is getting e-mails that just say `thanks'. We are always happy to receive e-mails of this type. Regrettably we don't have time to answer them all in person. If you mail us a compliment and don't receive a reply, _please_ don't think we've ignored you. We did receive it and we were happy about it; we just didn't have time to tell you so personally. To everyone who's ever sent us praise and compliments, in the past and the future: _you're welcome_! B.11 E-mail address The actual address to mail is . Appendix C: PPK file format --------------------------- This appendix documents the file format used by PuTTY to store private keys. In this appendix, binary data structures are described using data type representations such as `uint32', `string' and `mpint' as used in the SSH protocol standards themselves. These are defined authoritatively by RFC 4251 section 5, `Data Type Representations Used in the SSH Protocols'. C.1 Overview A PPK file stores a private key, and the corresponding public key. Both are contained in the same file. The file format can be completely unencrypted, or it can encrypt the private key. The _public_ key is stored in cleartext in both cases. (This enables PuTTY to send the public key to an SSH server to see whether it will accept it, and not bother prompting for the passphrase unless the server says yes.) When the key file is encrypted, the encryption key is derived from a passphrase. An encrypted PPK file is also tamper-proofed using a MAC (authentication code), also derived from the same passphrase. The MAC protects the encrypted private key data, but it also covers the cleartext parts of the file. So you can't edit the public half of the key without invalidating the MAC and causing the key file as a whole to become useless. This MAC protects the key file against active cryptographic attacks in which the public half of a key pair is modified in a controlled way that allows an attacker to deduce information about the private half from the resulting corrupted signatures. Any attempt to do that to a PPK file should be reliably caught by the MAC failing to validate. (Such an attack would only be useful if the key file was stored in a location where the attacker could modify it without also having full access to the process that you type passphrases into. But that's not impossible; for example, if your home directory was on a network file server, then the file server's administrator could access the key file but not processes on the client machine.) The MAC also covers the _comment_ on the key. This stops an attacker from swapping keys with each other and editing the comments to disguise the fact. As a consequence, PuTTYgen cannot edit the comment on a key unless you decrypt the key with your passphrase first. (The circumstances in which _that_ attack would be useful are even more restricted. One example might be that the different keys trigger specific actions on the server you're connecting to and one of those actions is more useful to the attacker than the other. But once you have a MAC at all, it's no extra effort to make it cover as much as possible, and usually sensible.) C.2 Outer layer The outer layer of a PPK file is text-based. The PuTTY tools will always use LF line termination when writing PPK files, but will tolerate CR+LF and CR-only on input. The first few lines identify it as a PPK, and give some initial data about what's stored in it and how. They look like this: PuTTY-User-Key-File-version: algorithm-name Encryption: encryption-type Comment: key-comment-string *version* is a decimal number giving the version number of the file format itself. The current file format version is 3. *algorithm-name* is the SSH protocol identifier for the public key algorithm that this key is used for (such as `ssh-dss' or `ecdsa- sha2-nistp384'). *encryption-type* indicates whether this key is stored encrypted, and if so, by what method. Currently the only supported encryption types are `aes256-cbc' and `none'. *key-comment-string* is a free text field giving the comment. This can contain any byte values other than 13 and 10 (CR and LF). The next part of the file gives the public key. This is stored unencrypted but base64-encoded (RFC 4648), and is preceded by a header line saying how many lines of base64 data are shown, looking like this: Public-Lines: number-of-lines that many lines of base64 data The base64-encoded data in this blob is formatted in exactly the same way as an SSH public key sent over the wire in the SSH protocol itself. That is also the same format as the base64 data stored in OpenSSH's `authorized_keys' file, except that in a PPK file the base64 data is split across multiple lines. But if you remove the newlines from the middle of this section, the resulting base64 blob is in the right format to go in an `authorized_keys' line. If the key is encrypted (i.e. *encryption-type* is not `none'), then the next thing that appears is a sequence of lines specifying how the keys for encrypting the file are to be derived from the passphrase: Key-Derivation: argon2-flavour Argon2-Memory: decimal-integer Argon2-Passes: decimal-integer Argon2-Parallelism: decimal-integer Argon2-Salt: hex-string *argon2-flavour* is one of the identifiers `Argon2d', `Argon2i' or `Argon2id', all describing variants of the Argon2 password-hashing function. The three integer values are used as parameters for Argon2, which allows you to configure the amount of memory used (in Kbyte), the number of passes of the algorithm to run (to tune its running time), and the degree of parallelism required by the hash function. The salt is decoded into a sequence of binary bytes and used as an additional input to Argon2. (It is chosen randomly when the key file is written, so that a guessing attack can't be mounted in parallel against multiple key files.) The next part of the file gives the private key. This is base64- encoded in the same way: Private-Lines: number-of-lines that many lines of base64 data The binary data represented in this base64 blob may be encrypted, depending on the _encryption-type_ field in the key file header shown above: - If *encryption-type* is `none', then this data is stored in plain text. - If *encryption-type* is `aes256-cbc', then this data is encrypted using AES, with a 256-bit key length, in the CBC cipher mode. The key and initialisation vector are derived from the passphrase: see section C.4. In order to encrypt the private key data with AES, it must be a multiple of 16 bytes (the AES cipher block length). This is achieved by appending random padding to the data before encrypting it. When decoding it after decryption, the random data can be ignored: the internal structure of the data is enough to tell you when you've reached the end of the meaningful part. Unlike public keys, the binary encoding of private keys is not specified at all in the SSH standards. See section C.3 for details of the private key format for each key type supported by PuTTY. The final thing in the key file is the MAC: Private-MAC: hex-mac-data *hex-mac-data* is a hexadecimal-encoded value, 64 digits long (i.e. 32 bytes), generated using the HMAC-SHA-256 algorithm with the following binary data as input: - string: the *algorithm-name* header field. - string: the *encryption-type* header field. - string: the *key-comment-string* header field. - string: the binary public key data, as decoded from the base64 lines after the `Public-Lines' header. - string: the plaintext of the binary private key data, as decoded from the base64 lines after the `Private-Lines' header. If that data was stored encrypted, then the decrypted version of it is used in this MAC preimage, _including_ the random padding mentioned above. The MAC key is derived from the passphrase: see section C.4. C.3 Private key encodings This section describes the private key format for each key type supported by PuTTY. Because the PPK format also contains the public key (and both public and private key are protected by the same MAC to ensure they can't be made inconsistent), there is no need for the private key section of the file to repeat data from the public section. So some of these formats are very short. In all cases, a decoding application can begin reading from the start of the decrypted private key data, and know when it has read all that it needs. This allows random padding after the meaningful data to be safely ignored. C.3.1 RSA RSA keys are stored using an *algorithm-name* of `ssh-rsa'. (Keys stored like this are also used by the updated RSA signature schemes that use hashes other than SHA-1.) The public key data has already provided the key modulus and the public encoding exponent. The private data stores: - mpint: the private decoding exponent of the key. - mpint: one prime factor _p_ of the key. - mpint: the other prime factor _q_ of the key. (RSA keys stored in this format are expected to have exactly two prime factors.) - mpint: the multiplicative inverse of _q_ modulo _p_. C.3.2 DSA DSA keys are stored using an *algorithm-name* of `ssh-dss'. The public key data has already provided the key parameters (the large prime _p_, the small prime _q_ and the group generator _g_), and the public key _y_. The private key stores: - mpint: the private key _x_, which is the discrete logarithm of _y_ in the group generated by _g_ mod _p_. C.3.3 NIST elliptic-curve keys NIST elliptic-curve keys are stored using one of the following *algorithm-name* values, each corresponding to a different elliptic curve and key size: - `ecdsa-sha2-nistp256' - `ecdsa-sha2-nistp384' - `ecdsa-sha2-nistp521' The public key data has already provided the public elliptic curve point. The private key stores: - mpint: the private exponent, which is the discrete log of the public point. C.3.4 EdDSA elliptic-curve keys (Ed25519 and Ed448) EdDSA elliptic-curve keys are stored using one of the following *algorithm-name* values, each corresponding to a different elliptic curve and key size: - `ssh-ed25519' - `ssh-ed448' The public key data has already provided the public elliptic curve point. The private key stores: - mpint: the private exponent, which is the discrete log of the public point. C.4 Key derivation When a key file is encrypted, there are three pieces of key material that need to be computed from the passphrase: - the key for the symmetric cipher used to encrypt the private key - the initialisation vector for that cipher encryption - the key for the MAC. If *encryption-type* is `aes256-cbc', then the symmetric cipher key is 32 bytes long, and the initialisation vector is 16 bytes (one cipher block). The length of the MAC key is also chosen to be 32 bytes. If *encryption-type* is `none', then all three of these pieces of data have zero length. (The MAC is still generated and checked in the key file format, but it has a zero-length key.) If the amount of key material required is not zero, then the passphrase is fed to the Argon2 key derivation function, in whichever mode is described in the `Key-Derivation' header in the key file, with parameters derived from the various `Argon2- _Parameter_:' headers. (If the key is unencrypted, then all those headers are omitted, and Argon2 is not run at all.) Argon2 takes two extra string inputs in addition to the passphrase and the salt: a secret key, and some `associated data'. In PPK's use of Argon2, these are both set to the empty string. The `tag length' parameter to Argon2 (i.e. the amount of data it is asked to output) is set to the sum of the lengths of all of the data items required, i.e. (cipher key length + IV length + MAC key length). The output data is interpreted as the concatenation of the cipher key, the IV and the MAC key, in that order. So, for `aes256-cbc', the tag length will be 32+16+32 = 80 bytes; of the 80 bytes of output data, the first 32 bytes are used as the 256- bit AES key, the next 16 as the CBC IV, and the final 32 bytes as the HMAC-SHA-256 key. C.5 Older versions of the PPK format C.5.1 Version 2 PPK version 2 was used by PuTTY 0.52 to 0.74 inclusive. In PPK version 2, the MAC algorithm used was HMAC-SHA-1 (so the Private-MAC line contained only 40 hex digits). The `Key-Derivation:' header and all the `Argon2-_Parameter_:' headers were absent. Instead of using Argon2, the key material for encrypting the private blob was derived from the passphrase in a totally different way, as follows. The cipher key for `aes256-cbc' was constructed by generating two SHA-1 hashes, concatenating them, and taking the first 32 bytes of the result. (So you'd get all 20 bytes of the first hash output, and the first 12 of the second). Each hash preimage was as follows: - uint32: a sequence number. This is 0 in the first hash, and 1 in the second. (The idea was to extend this mechanism to further hashes by continuing to increment the sequence number, if future changes required even longer keys.) - the passphrase, without any prefix length field. In PPK v2, the CBC initialisation vector was all zeroes. The MAC key was 20 bytes long, and was a single SHA-1 hash of the following data: - the fixed string `putty-private-key-file-mac-key', without any prefix length field. - the passphrase, without any prefix length field. (If the key is stored unencrypted, the passphrase was taken to be the empty string for these purposes.) C.5.2 Version 1 PPK version 1 was a badly designed format, only used during initial development, and not recommended for production use. PPK version 1 was never used by a released version of PuTTY. It was only emitted by some early development snapshots between version 0.51 (which did not support SSH-2 public keys at all) and 0.52 (which already used version 2 of this file format). I _hope_ there are no PPK v1 files in use anywhere. But just in case, the old badly designed format is documented here anyway. In PPK version 1, the input to the MAC does not include any of the header fields or the public key. It is simply the private key data (still in plaintext and including random padding), all by itself (without a wrapping string). PPK version 1 keys must therefore be rigorously validated after loading, to ensure that the public and private parts of the key were consistent with each other. PPK version 1 only supported the RSA and DSA key types. For RSA, this validation can be done using only the provided data (since the private key blob contains enough information to reconstruct the public values anyway). But for DSA, that isn't quite enough. Hence, PPK version 1 DSA keys extended the private data so that immediately after _x_ was stored an extra value: - string: a SHA-1 hash of the public key data, whose preimage consists of - string: the large prime _p_ - string: the small prime _q_ - string: the group generator _g_ The idea was that checking this hash would verify that the key parameters had not been tampered with, and then the loading application could directly verify that _g_^_x_ = _y_. In an _unencrypted_ version 1 key file, the MAC is replaced by a plain SHA-1 hash of the private key data. This is indicated by the `Private-MAC:' header being replaced with `Private-Hash:' instead. Appendix D: PuTTY Licence ------------------------- PuTTY is copyright 1997-2021 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, Christopher Staite, Lorenz Diener, Christian Brabandt, Jeff Smith, Pavel Kryukov, Maxim Kuznetsov, Svyatoslav Kuzmich, Nico Williams, Viktor Dukhovni, Josh Dersch, Lars Brinkhoff, 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: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED `AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Appendix E: PuTTY hacking guide ------------------------------- This appendix lists a selection of the design principles applying to the PuTTY source code. If you are planning to send code contributions, you should read this first. E.1 Cross-OS portability Despite Windows being its main area of fame, PuTTY is no longer a Windows-only application suite. It has a working Unix port; a Mac port is in progress; more ports may or may not happen at a later date. Therefore, embedding Windows-specific code in core modules such as ssh.c is not acceptable. We went to great lengths to _remove_ all the Windows-specific stuff from our core modules, and to shift it out into Windows-specific modules. Adding large amounts of Windows- specific stuff in parts of the code that should be portable is almost guaranteed to make us reject a contribution. The PuTTY source base is divided into platform-specific modules and platform-generic modules. The Unix-specific modules are all in the `unix' subdirectory; the Windows-specific modules are in the `windows' subdirectory. All the modules in the main source directory - notably _all_ of the code for the various back ends - are platform-generic. We want to keep them that way. This also means you should stick to the C semantics guaranteed by the C standard: try not to make assumptions about the precise size of basic types such as `int' and `long int'; don't use pointer casts to do endianness-dependent operations, and so on. (Even _within_ a platform front end you should still be careful of some of these portability issues. The Windows front end compiles on both 32- and 64-bit x86 and also Arm.) Our current choice of C standards version is _mostly_ C99. With a couple of exceptions, you can assume that C99 features are available (in particular , and `inline'), but you shouldn't use things that are new in C11 (such as or _Generic). The exceptions to that rule are due to the need for Visual Studio compatibility: - Don't use variable-length arrays. Visual Studio doesn't support them even now that it's adopted the rest of C99. We use -Wvla when building with gcc and clang, to make it easier to avoid accidentally breaking that rule. - For historical reasons, we still build with one older VS version which lacks . So that file is included centrally in `defs.h', and has a set of workaround definitions for the PRIx64-type macros we use. If you need to use another one of those macros, you need to add a workaround definition in `defs.h', and don't casually re-include anywhere else in the source file. Here are a few portability assumptions that we _do_ currently allow (because we'd already have to thoroughly vet the existing code if they ever needed to change, and it doesn't seem worth doing that unless we really have to): - You can assume `int' is _at least_ 32 bits wide. (We've never tried to port PuTTY to a platform with 16-bit int, and it doesn't look likely to be necessary in future.) - Similarly, you can assume `char' is exactly 8 bits. (Exceptions to that are even less likely to be relevant to us than short int.) - You can assume that using `memset' to write zero bytes over a whole structure will have the effect of setting all its pointer fields to NULL. (The standard itself guarantees this for _integer_ fields, but not for pointers.) - You can assume that `time_t' has POSIX semantics, i.e. that it represents an integer number of non-leap seconds since 1970-01-01 00:00:00 UTC. (Times in this format are used in X authorisation, but we could work around that by carefully distinguishing local `time_t' from time values used in the wire protocol; but these semantics of `time_t' are also baked into the shared library API used by the GSSAPI authentication code, which would be much harder to change.) - You can assume that the execution character encoding is a superset of the printable characters of ASCII. (In particular, it's fine to do arithmetic on a `char' value representing a Latin alphabetic character, without bothering to allow for EBCDIC or other non-consecutive encodings of the alphabet.) On the other hand, here are some particular things _not_ to assume: - Don't assume anything about the _signedness_ of `char'. In particular, you _must_ cast `char' values to `unsigned char' before passing them to any function (because those expect a non-negative character value, or EOF). If you need a particular signedness, explicitly specify `signed char' or `unsigned char', or use C99 int8_t or uint8_t. - From past experience with MacOS, we're still a bit nervous about '\n' and '\r' potentially having unusual meanings on a given platform. So it's fine to say `\n' in a string you're passing to `printf', but in any context where those characters appear in a standardised wire protocol or a binary file format, they should be spelled '\012' and '\015' respectively. E.2 Multiple backends treated equally PuTTY is not an SSH client with some other stuff tacked on the side. PuTTY is a generic, multiple-backend, remote VT-terminal client which happens to support one backend which is larger, more popular and more useful than the rest. Any extra feature which can possibly be general across all backends should be so: localising features unnecessarily into the SSH back end is a design error. (For example, we had several code submissions for proxy support which worked by hacking ssh.c. Clearly this is completely wrong: the network.h abstraction is the place to put it, so that it will apply to all back ends equally, and indeed we eventually put it there after another contributor sent a better patch.) The rest of PuTTY should try to avoid knowing anything about specific back ends if at all possible. To support a feature which is only available in one network protocol, for example, the back end interface should be extended in a general manner such that _any_ back end which is able to provide that feature can do so. If it so happens that only one back end actually does, that's just the way it is, but it shouldn't be relied upon by any code. E.3 Multiple sessions per process on some platforms Some ports of PuTTY - notably the in-progress Mac port - are constrained by the operating system to run as a single process potentially managing multiple sessions. Therefore, the platform-independent parts of PuTTY never use global variables to store per-session data. The global variables that do exist are tolerated because they are not specific to a particular login session. The random number state in sshrand.c, the timer list in timing.c and the queue of top-level callbacks in callback.c serve all sessions equally. But most data is specific to a particular network session, and is therefore stored in dynamically allocated data structures, and pointers to these structures are passed around between functions. Platform-specific code can reverse this decision if it likes. The Windows code, for historical reasons, stores most of its data as global variables. That's OK, because _on Windows_ we know there is only one session per PuTTY process, so it's safe to do that. But changes to the platform-independent code should avoid introducing global variables, unless they are genuinely cross-session. E.4 C, not C++ PuTTY is written entirely in C, not in C++. We have made _some_ effort to make it easy to compile our code using a C++ compiler: notably, our `snew', `snewn' and `sresize' macros explicitly cast the return values of malloc and realloc to the target type. (This has type checking advantages even in C: it means you never accidentally allocate the wrong size piece of memory for the pointer type you're assigning it to. C++ friendliness is really a side benefit.) We want PuTTY to continue being pure C, at least in the platform- independent parts and the currently existing ports. Patches which switch the Makefiles to compile it as C++ and start using classes will not be accepted. Also, in particular, we disapprove of // comments, at least for the moment. (Perhaps once C99 becomes genuinely widespread we might be more lenient.) The one exception: a port to a new platform may use languages other than C if they are necessary to code on that platform. If your favourite PDA has a GUI with a C++ API, then there's no way you can do a port of PuTTY without using C++, so go ahead and use it. But keep the C++ restricted to that platform's subdirectory; if your changes force the Unix or Windows ports to be compiled as C++, they will be unacceptable to us. E.5 Security-conscious coding PuTTY is a network application and a security application. Assume your code will end up being fed deliberately malicious data by attackers, and try to code in a way that makes it unlikely to be a security risk. In particular, try not to use fixed-size buffers for variable-size data such as strings received from the network (or even the user). We provide functions such as dupcat and dupprintf, which dynamically allocate buffers of the right size for the string they construct. Use these wherever possible. E.6 Independence of specific compiler Windows PuTTY can currently be compiled with any of three Windows compilers: MS Visual C, the Cygwin / mingw32 GNU tools, and clang (in MS compatibility mode). This is a really useful property of PuTTY, because it means people who want to contribute to the coding don't depend on having a specific compiler; so they don't have to fork out money for MSVC if they don't already have it, but on the other hand if they _do_ have it they also don't have to spend effort installing gcc alongside it. They can use whichever compiler they happen to have available, or install whichever is cheapest and easiest if they don't have one. Therefore, we don't want PuTTY to start depending on which compiler you're using. Using GNU extensions to the C language, for example, would ruin this useful property (not that anyone's ever tried it!); and more realistically, depending on an MS-specific library function supplied by the MSVC C library (_snprintf, for example) is a mistake, because that function won't be available under the other compilers. Any function supplied in an official Windows DLL as part of the Windows API is fine, and anything defined in the C library standard is also fine, because those should be available irrespective of compilation environment. But things in between, available as non-standard library and language extensions in only one compiler, are disallowed. (_snprintf in particular should be unnecessary, since we provide dupprintf; see section E.5.) Compiler independence should apply on all platforms, of course, not just on Windows. E.7 Small code size PuTTY is tiny, compared to many other Windows applications. And it's easy to install: it depends on no DLLs, no other applications, no service packs or system upgrades. It's just one executable. You install that executable wherever you want to, and run it. We want to keep both these properties - the small size, and the ease of installation - if at all possible. So code contributions that depend critically on external DLLs, or that add a huge amount to the code size for a feature which is only useful to a small minority of users, are likely to be thrown out immediately. We do vaguely intend to introduce a DLL plugin interface for PuTTY, whereby seriously large extra features can be implemented in plugin modules. The important thing, though, is that those DLLs will be _optional_; if PuTTY can't find them on startup, it should run perfectly happily and just won't provide those particular features. A full installation of PuTTY might one day contain ten or twenty little DLL plugins, which would cut down a little on the ease of installation - but if you really needed ease of installation you _could_ still just install the one PuTTY binary, or just the DLLs you really needed, and it would still work fine. Depending on _external_ DLLs is something we'd like to avoid if at all possible (though for some purposes, such as complex SSH authentication mechanisms, it may be unavoidable). If it can't be avoided, the important thing is to follow the same principle of graceful degradation: if a DLL can't be found, then PuTTY should run happily and just not supply the feature that depended on it. E.8 Single-threaded code PuTTY and its supporting tools, or at least the vast majority of them, run in only one OS thread. This means that if you're devising some piece of internal mechanism, there's no need to use locks to make sure it doesn't get called by two threads at once. The only way code can be called re-entrantly is by recursion. That said, most of Windows PuTTY's network handling is triggered off Windows messages requested by WSAAsyncSelect(), so if you call MessageBox() deep within some network event handling code you should be aware that you might be re-entered if a network event comes in and is passed on to our window procedure by the MessageBox() message loop. Also, the front ends (in particular Windows Plink) can use multiple threads if they like. However, Windows Plink keeps _very_ tight control of its auxiliary threads, and uses them pretty much exclusively as a form of select(). Pretty much all the code outside windows/winplink.c is _only_ ever called from the one primary thread; the others just loop round blocking on file handles and send messages to the main thread when some real work needs doing. This is not considered a portability hazard because that bit of windows/winplink.c will need rewriting on other platforms in any case. One important consequence of this: PuTTY has only one thread in which to do everything. That `everything' may include managing more than one login session (section E.3), managing multiple data channels within an SSH session, responding to GUI events even when nothing is happening on the network, and responding to network requests from the server (such as repeat key exchange) even when the program is dealing with complex user interaction such as the re- configuration dialog box. This means that _almost none_ of the PuTTY code can safely block. E.9 Keystrokes sent to the server wherever possible In almost all cases, PuTTY sends keystrokes to the server. Even weird keystrokes that you think should be hot keys controlling PuTTY. Even Alt-F4 or Alt-Space, for example. If a keystroke has a well-defined escape sequence that it could usefully be sending to the server, then it should do so, or at the very least it should be configurably able to do so. To unconditionally turn a key combination into a hot key to control PuTTY is almost always a design error. If a hot key is really truly required, then try to find a key combination for it which _isn't_ already used in existing PuTTYs (either it sends nothing to the server, or it sends the same thing as some other combination). Even then, be prepared for the possibility that one day that key combination might end up being needed to send something to the server - so make sure that there's an alternative way to invoke whatever PuTTY feature it controls. E.10 640x480 friendliness in configuration panels There's a reason we have lots of tiny configuration panels instead of a few huge ones, and that reason is that not everyone has a 1600x1200 desktop. 640x480 is still a viable resolution for running Windows (and indeed it's still the default if you start up in safe mode), so it's still a resolution we care about. Accordingly, the PuTTY configuration box, and the PuTTYgen control window, are deliberately kept just small enough to fit comfortably on a 640x480 display. If you're adding controls to either of these boxes and you find yourself wanting to increase the size of the whole box, _don't_. Split it into more panels instead. E.11 Automatically generated Makefiles PuTTY is intended to compile on multiple platforms, and with multiple compilers. It would be horrifying to try to maintain a single Makefile which handled all possible situations, and just as painful to try to directly maintain a set of matching Makefiles for each different compilation environment. Therefore, we have moved the problem up by one level. In the PuTTY source archive is a file called `Recipe', which lists which source files combine to produce which binaries; and there is also a script called mkfiles.pl, which reads `Recipe' and writes out the real Makefiles. (The script also reads all the source files and analyses their dependencies on header files, so we get an extra benefit from doing it this way, which is that we can supply correct dependency information even in environments where it's difficult to set up an automated `make depend' phase.) You should _never_ edit any of the PuTTY Makefiles directly. They are not stored in our source repository at all. They are automatically generated by mkfiles.pl from the file `Recipe'. If you need to add a new object file to a particular binary, the right thing to do is to edit `Recipe' and re-run mkfiles.pl. This will cause the new object file to be added in every tool that requires it, on every platform where it matters, in every Makefile to which it is relevant, _and_ to get all the dependency data right. If you send us a patch that modifies one of the Makefiles, you just waste our time, because we will have to convert it into a change to `Recipe'. If you send us a patch that modifies _all_ of the Makefiles, you will have wasted a lot of _your_ time as well! (There is a comment at the top of every Makefile in the PuTTY source archive saying this, but many people don't seem to read it, so it's worth repeating here.) E.12 Coroutines in the SSH code Large parts of the code in the various SSH modules (in fact most of the protocol layers) are structured using a set of macros that implement (something close to) Donald Knuth's `coroutines' concept in C. Essentially, the purpose of these macros are to arrange that a function can call crReturn() to return to its caller, and the next time it is called control will resume from just after that crReturn statement. This means that any local (automatic) variables declared in such a function will be corrupted every time you call crReturn. If you need a variable to persist for longer than that, you _must_ make it a field in some appropriate structure containing the persistent state of the coroutine - typically the main state structure for an SSH protocol layer. See `https://www.chiark.greenend.org.uk/~sgtatham/coroutines.html' for a more in-depth discussion of what these macros are for and how they work. Another caveat: most of these coroutines are not _guaranteed_ to run to completion, because the SSH connection (or whatever) that they're part of might be interrupted at any time by an unexpected network event or user action. So whenever a coroutine-managed variable refers to a resource that needs releasing, you should also ensure that the cleanup function for its containing state structure can reliably release it even if the coroutine is aborted at an arbitrary point. For example, if an SSH packet protocol layer has to have a field that sometimes points to a piece of allocated memory, then you should ensure that when you free that memory you reset the pointer field to NULL. Then, no matter when the protocol layer's cleanup function is called, it can reliably free the memory if there is any, and not crash if there isn't. E.13 Explicit vtable structures to implement traits A lot of PuTTY's code is written in a style that looks structurally rather like an object-oriented language, in spite of PuTTY being a pure C program. For example, there's a single data type called ssh_hash, which is an abstraction of a secure hash function, and a bunch of functions called things like ssh_hash__foo_ that do things with those data types. But in fact, PuTTY supports many different hash functions, and each one has to provide its own implementation of those functions. In C++ terms, this is rather like having a single abstract base class, and multiple concrete subclasses of it, each of which fills in all the pure virtual methods in a way that's compatible with the data fields of the subclass. The implementation is more or less the same, as well: in C, we do explicitly in the source code what the C++ compiler will be doing behind the scenes at compile time. But perhaps a closer analogy in functional terms is the Rust concept of a `trait', or the Java idea of an `interface'. C++ supports a multi-level hierarchy of inheritance, whereas PuTTY's system - like traits or interfaces - has only two levels, one describing a generic object of a type (e.g. a hash function) and another describing a specific implementation of that type (e.g. SHA-256). The PuTTY code base has a standard idiom for doing this in C, as follows. Firstly, we define two struct types for our trait. One of them describes a particular _kind_ of implementation of that trait, and it's full of (mostly) function pointers. The other describes a specific _instance_ of an implementation of that trait, and it will contain a pointer to a const instance of the first type. For example: typedef struct MyAbstraction MyAbstraction; typedef struct MyAbstractionVtable MyAbstractionVtable; struct MyAbstractionVtable { MyAbstraction *(*new)(const MyAbstractionVtable *vt); void (*free)(MyAbstraction *); void (*modify)(MyAbstraction *, unsigned some_parameter); unsigned (*query)(MyAbstraction *, unsigned some_parameter); }; struct MyAbstraction { const MyAbstractionVtable *vt; }; Here, we imagine that MyAbstraction might be some kind of object that contains mutable state. The associated vtable structure shows what operations you can perform on a MyAbstraction: you can create one (dynamically allocated), free one you already have, or call the example methods `modify' (to change the state of the object in some way) and `query' (to return some value derived from the object's current state). (In most cases, the vtable structure has a name ending in `vtable'. But for historical reasons a lot of the crypto primitives that use this scheme - ciphers, hash functions, public key methods and so on - instead have names ending in `alg', on the basis that the primitives they implement are often referred to as `encryption algorithms', `hash algorithms' and so forth.) Now, to define a concrete instance of this trait, you'd define a struct that contains a MyAbstraction field, plus any other data it might need: struct MyImplementation { unsigned internal_data[16]; SomeOtherType *dynamic_subthing; MyAbstraction myabs; }; Next, you'd implement all the necessary methods for that implementation of the trait, in this kind of style: static MyAbstraction *myimpl_new(const MyAbstractionVtable *vt) { MyImplementation *impl = snew(MyImplementation); memset(impl, 0, sizeof(*impl)); impl->dynamic_subthing = allocate_some_other_type(); impl->myabs.vt = vt; return &impl->myabs; } static void myimpl_free(MyAbstraction *myabs) { MyImplementation *impl = container_of(myabs, MyImplementation, myabs); free_other_type(impl->dynamic_subthing); sfree(impl); } static void myimpl_modify(MyAbstraction *myabs, unsigned param) { MyImplementation *impl = container_of(myabs, MyImplementation, myabs); impl->internal_data[param] += do_something_with(impl->dynamic_subthing); } static unsigned myimpl_query(MyAbstraction *myabs, unsigned param) { MyImplementation *impl = container_of(myabs, MyImplementation, myabs); return impl->internal_data[param]; } Having defined those methods, now we can define a const instance of the vtable structure containing pointers to them: const MyAbstractionVtable MyImplementation_vt = { .new = myimpl_new, .free = myimpl_free, .modify = myimpl_modify, .query = myimpl_query, }; _In principle_, this is all you need. Client code can construct a new instance of a particular implementation of MyAbstraction by digging out the new method from the vtable and calling it (with the vtable itself as a parameter), which returns a MyAbstraction * pointer that identifies a newly created instance, in which the vt field will contain a pointer to the same vtable structure you passed in. And once you have an instance object, say MyAbstraction *myabs, you can dig out one of the other method pointers from the vtable it points to, and call that, passing the object itself as a parameter. But in fact, we don't do that, because it looks pretty ugly at all the call sites. Instead, what we generally do in this code base is to write a set of static inline wrapper functions in the same header file that defined the MyAbstraction structure types, like this: static MyAbstraction *myabs_new(const MyAbstractionVtable *vt) { return vt->new(vt); } static void myabs_free(MyAbstraction *myabs) { myabs->vt->free(myabs); } static void myimpl_modify(MyAbstraction *myabs, unsigned param) { myabs->vt->modify(myabs, param); } static unsigned myimpl_query(MyAbstraction *myabs, unsigned param) { return myabs->vt->query(myabs, param); } And now call sites can use those reasonably clean-looking wrapper functions, and shouldn't ever have to directly refer to the vt field inside any myabs object they're holding. For example, you might write something like this: MyAbstraction *myabs = myabs_new(&MyImplementation_vtable); myabs_update(myabs, 10); unsigned output = myabs_query(myabs, 2); myabs_free(myabs); and then all this code can use a different implementation of the same abstraction by just changing which vtable pointer it passed in in the first line. Some things to note about this system: - The implementation instance type (here `MyImplementation' contains the abstraction type (`MyAbstraction') as one of its fields. But that field is not necessarily at the start of the structure. So you can't just _cast_ pointers back and forth between the two types. Instead: - You `up-cast' from implementation to abstraction by taking the address of the MyAbstraction field. You can see the example new method above doing this, returning &impl->myabs. All new methods do this on return. - Going in the other direction, each method that was passed a generic MyAbstraction *myabs parameter has to recover a pointer to the specific implementation type MyImplementation *impl. The idiom for doing that is to use the `container_of' macro, also seen in the Linux kernel code. Generally, container_of(p, Type, field) says: `I'm confident that the pointer value `p' is pointing to the field called `field' within a larger struct of type Type. Please return me the pointer to the containing structure.' So in this case, we take the `myabs' pointer passed to the function, and `down-cast' it into a pointer to the larger and more specific structure type MyImplementation, by adjusting the pointer value based on the offset within that structure of the field called `myabs'. This system is flexible enough to permit `multiple inheritance', or rather, multiple _implementation_: having one object type implement more than one trait. For example, the Proxy type implements both the Socket trait and the Plug trait that connects to it, because it has to act as an adapter between another instance of each of those types. It's also perfectly possible to have the same object implement the _same_ trait in two different ways. At the time of writing this I can't think of any case where we actually do this, but a theoretical example might be if you needed to support a trait like Comparable in two ways that sorted by different criteria. There would be no difficulty doing this in the PuTTY system: simply have your implementation struct contain two (or more) fields of the same abstraction type. The fields will have different names, which makes it easy to explicitly specify which one you're returning a pointer to during up-casting, or which one you're down-casting from using container_of. And then both sets of implementation methods can recover a pointer to the same containing structure. - Unlike in C++, all objects in PuTTY that use this system are dynamically allocated. The `constructor' functions (whether they're virtualised across the whole abstraction or specific to each implementation) always allocate memory and return a pointer to it. The `free' method (our analogue of a destructor) always expects the input pointer to be dynamically allocated, and frees it. As a result, client code doesn't need to know how large the implementing object type is, because it will never need to allocate it (on the stack or anywhere else). - Unlike in C++, the abstraction's `vtable' structure does not only hold methods that you can call on an instance object. It can also hold several other kinds of thing: - Methods that you can call _without_ an instance object, given only the vtable structure identifying a particular implementation of the trait. You might think of these as `static methods', as in C++, except that they're _virtual_ - the same code can call the static method of a different `class' given a different vtable pointer. So they're more like `virtual static methods', which is a concept C++ doesn't have. An example is the pubkey_bits method in ssh_keyalg. - The most important case of a `virtual static method' is the new method that allocates and returns a new object. You can think of it as a `virtual constructor' - another concept C++ doesn't have. (However, not all types need one of these: see below.) - The vtable can also contain constant data relevant to the class as a whole - `virtual constant data'. For example, a cryptographic hash function will contain an integer field giving the length of the output hash, and most crypto primitives will contain a string field giving the identifier used in the SSH protocol that describes that primitive. The effect of all of this is that you can make other pieces of code able to use any instance of one of these types, by passing it an actual vtable as a parameter. For example, the hash_simple function takes an ssh_hashalg vtable pointer specifying any hash algorithm you like, and internally, it creates an object of that type, uses it, and frees it. In C++, you'd probably do this using a template, which would mean you had multiple specialisations of hash_simple - and then it would be much more difficult to decide _at run time_ which one you needed to use. Here, hash_simple is still just one function, and you can decide as late as you like which vtable to pass to it. - The abstract _instance_ structure can also contain publicly visible data fields (this time, usually treated as mutable) which are common to all implementations of the trait. For example, BinaryPacketProtocol has lots of these. - Not all abstractions of this kind want virtual constructors. It depends on how different the implementations are. With a crypto primitive like a hash algorithm, the constructor call looks the same for every implementing type, so it makes sense to have a standardised virtual constructor in the vtable and a ssh_hash_new wrapper function which can make an instance of whatever vtable you pass it. And then you make all the vtable objects themselves globally visible throughout the source code, so that any module can call (for example) ssh_hash_new(&ssh_sha256). But with other kinds of object, the constructor for each implementing type has to take a different set of parameters. For example, implementations of Socket are not generally interchangeable at construction time, because constructing different kinds of socket require totally different kinds of address parameter. In that situation, it makes more sense to keep the vtable structure itself private to the implementing source file, and instead, publish an ordinary constructing function that allocates and returns an instance of that particular subtype, taking whatever parameters are appropriate to that subtype. - If you do have virtual constructors, you can choose whether they take a vtable pointer as a parameter (as shown above), or an _existing_ instance object. In the latter case, they can refer to the object itself as well as the vtable. For example, you could have a trait come with a virtual constructor called `clone', meaning `Make a copy of this object, no matter which implementation it is.' - Sometimes, a single vtable structure type can be shared between two completely different object types, and contain all the methods for both. For example, ssh_compression_alg contains methods to create, use and free ssh_compressor and ssh_decompressor objects, which are not interchangeable - but putting their methods in the same vtable means that it's easy to create a matching pair of objects that are compatible with each other. - Passing the vtable itself as an argument to the new method is not compulsory: if a given new implementation is only used by a single vtable, then that function can simply hard-code the vtable pointer that it writes into the object it constructs. But passing the vtable is more flexible, because it allows a single constructor function to be shared between multiple slightly different object types. For example, SHA-384 and SHA- 512 share the same new method and the same implementation data type, because they're very nearly the same hash algorithm - but a couple of the other methods in their vtables are different, because the `reset' function has to set up the initial algorithm state differently, and the `digest' method has to write out a different amount of data. One practical advantage of having the myabs__foo_ family of inline wrapper functions in the header file is that if you change your mind later about whether the vtable needs to be passed to new, you only have to update the myabs_new wrapper, and then the existing call sites won't need changing. - Another piece of `stunt object orientation' made possible by this scheme is that you can write two vtables that both use the same structure layout for the implementation object, and have an object _transform from one to the other_ part way through its lifetime, by overwriting its own vtable pointer field. For example, the sesschan type that handles the server side of an SSH terminal session will sometimes transform in mid-lifetime into an SCP or SFTP file-transfer channel in this way, at the point where the client sends an `exec' or `subsystem' request that indicates that that's what it wants to do with the channel. This concept would be difficult to arrange in C++. In Rust, it wouldn't even _make sense_, because in Rust, objects implementing a trait don't even contain a vtable pointer at all - instead, the `trait object' type (identifying a specific instance of some implementation of a given trait) consists of a pair of pointers, one to the object itself and one to the vtable. In that model, the only way you could make an existing object turn into a different trait would be to know where all the pointers to it were stored elsewhere in the program, and persuade all their owners to rewrite them. - Another stunt you can do is to have a vtable that doesn't have a corresponding implementation structure at all, because the only methods implemented in it are the constructors, and they always end up returning an implementation of some other vtable. For example, some of PuTTY's crypto primitives have a hardware- accelerated version and a pure software version, and decide at run time which one to use (based on whether the CPU they're running on supports the necessary acceleration instructions). So, for example, there are vtables for ssh_sha256_sw and ssh_sha256_hw, each of which has its own data layout and its own implementations of all the methods; and then there's a top-level vtable ssh_sha256, which only provides the `new' method, and implements it by calling the `new' method on one or other of the subtypes depending on what it finds out about the machine it's running on. That top-level selector vtable is nearly always the one used by client code. (Except for the test suite, which has to instantiate both of the subtypes in order to make sure they both pass the tests.) As a result, the top-level selector vtable ssh_sha256 doesn't need to implement any method that takes an ssh_cipher * parameter, because no ssh_cipher object is ever constructed whose vt field points to &ssh_sha256: they all point to one of the other two full implementation vtables. E.14 Single compilation of each source file The PuTTY build system for any given platform works on the following very simple model: - Each source file is compiled precisely once, to produce a single object file. - Each binary is created by linking together some combination of those object files. Therefore, if you need to introduce functionality to a particular module which is only available in some of the tool binaries (for example, a cryptographic proxy authentication mechanism which needs to be left out of PuTTYtel to maintain its usability in crypto- hostile jurisdictions), the _wrong_ way to do it is by adding #ifdefs in (say) proxy.c. This would require separate compilation of proxy.c for PuTTY and PuTTYtel, which means that the entire Makefile-generation architecture (see section E.11) would have to be significantly redesigned. Unless you are prepared to do that redesign yourself, _and_ guarantee that it will still port to any future platforms we might decide to run on, you should not attempt this! The _right_ way to introduce a feature like this is to put the new code in a separate source file, and (if necessary) introduce a second new source file defining the same set of functions, but defining them as stubs which don't provide the feature. Then the module whose behaviour needs to vary (proxy.c in this example) can call the functions defined in these two modules, and it will either provide the new feature or not provide it according to which of your new modules it is linked with. Of course, object files are never shared _between_ platforms; so it is allowable to use #ifdef to select between platforms. This happens in puttyps.h (choosing which of the platform-specific include files to use), and also in misc.c (the Windows-specific `Minefield' memory diagnostic system). It should be used sparingly, though, if at all. E.15 Do as we say, not as we do The current PuTTY code probably does not conform strictly to _all_ of the principles listed above. There may be the occasional SSH- specific piece of code in what should be a backend-independent module, or the occasional dependence on a non-standard X library function under Unix. This should not be taken as a licence to go ahead and violate the rules. Where we violate them ourselves, we're not happy about it, and we would welcome patches that fix any existing problems. Please try to help us make our code better, not worse! Appendix F: PuTTY download keys and signatures ---------------------------------------------- We create GPG signatures for all the PuTTY files distributed from our web site, so that users can be confident that the files have not been tampered with. Here we identify our public keys, and explain our signature policy so you can have an accurate idea of what each signature guarantees. This description is provided as both a web page on the PuTTY site, and an appendix in the PuTTY manual. As of release 0.58, all of the PuTTY executables contain fingerprint material (usually accessed via the `-pgpfp' command-line option), such that if you have an executable you trust, you can use it to establish a trust path, for instance to a newer version downloaded from the Internet. As of release 0.67, the Windows executables and installer also contain built-in signatures that are automatically verified by Windows' own mechanism (`Authenticode'). The keys used for that are different, and are not covered here. (Note that none of the keys, signatures, etc mentioned here have anything to do with keys used with SSH - they are purely for verifying the origin of files distributed by the PuTTY team.) F.1 Public keys We maintain multiple keys, stored with different levels of security due to being used in different ways. See section F.2 below for details. The keys we provide are: Snapshot Key Used to sign routine development builds of PuTTY: nightly snapshots, pre-releases, and sometimes also custom diagnostic builds we send to particular users. Release Key Used to sign manually released versions of PuTTY. Secure Contact Key An encryption-capable key suitable for people to send confidential messages to the PuTTY team, e.g. reports of vulnerabilities. Master Key Used to tie all the above keys into the GPG web of trust. The Master Key signs all the other keys, and other GPG users have signed it in turn. The current issue of those keys are available for download from the PuTTY website, and are also available on PGP keyservers using the key IDs listed below. *Master Key* (2018) RSA, 4096-bit. Key ID: 76BC7FE4EBFD2D9E. Fingerprint: 24E1 B1C5 75EA 3C9F F752 A922 76BC 7FE4 EBFD 2D9E *Release Key* (2018) RSA, 3072-bit. Key ID: 6289A25F4AE8DA82. Fingerprint: E273 94AC A3F9 D904 9522 E054 6289 A25F 4AE8 DA82 *Snapshot Key* (2018) RSA, 3072-bit. Key ID: 38BA7229B7588FD1. Fingerprint: C92B 52E9 9AB6 1DDA 33DB 2B7A 38BA 7229 B758 8FD1 *Secure Contact Key* (2018) RSA, 3072-bit. Key ID: 657D487977F95C98. Fingerprint: A680 0082 2998 6E46 22CA 0E43 657D 4879 77F9 5C98 F.2 Security details The various keys have various different security levels. This section explains what those security levels are, and how far you can expect to trust each key. F.2.1 The Development Snapshots key The Development Snapshots private key is stored _without a passphrase_. This is necessary, because the snapshots are generated every night without human intervention, so nobody would be able to type a passphrase. The snapshots are built and signed on a team member's home computers, before being uploaded to the web server from which you download them. Therefore, a signature from the Development Snapshots key _DOES_ protect you against: - People tampering with the PuTTY binaries between the PuTTY web site and you. - The maintainers of our web server attempting to abuse their root privilege to tamper with the binaries. But it _DOES NOT_ protect you against: - People tampering with the binaries before they are uploaded to our download servers. - People tampering with the build machines so that the next set of binaries they build will be malicious in some way. - People stealing the unencrypted private key from the build machine it lives on. Of course, we take all reasonable precautions to guard the build machines. But when you see a signature, you should always be certain of precisely what it guarantees and precisely what it does not. F.2.2 The Releases key The Releases key is more secure: because it is only used at release time, to sign each release by hand, we can store it encrypted. The Releases private key is kept encrypted on the developers' own local machines. So an attacker wanting to steal it would have to also steal the passphrase. F.2.3 The Secure Contact Key The Secure Contact Key is stored with a similar level of security to the Release Key: it is stored with a passphrase, and no automated script has access to it. F.2.4 The Master Keys The Master Key signs almost nothing. Its purpose is to bind the other keys together and certify that they are all owned by the same people and part of the same integrated setup. The only signatures produced by the Master Key, _ever_, should be the signatures on the other keys. The Master Key is especially long, and its private key and passphrase are stored with special care. We have collected some third-party signatures on the Master Key, in order to increase the chances that you can find a suitable trust path to them. We have uploaded our various keys to public keyservers, so that even if you don't know any of the people who have signed our keys, you can still be reasonably confident that an attacker would find it hard to substitute fake keys on all the public keyservers at once. F.3 Key rollover Our current keys were generated in August 2018. Each new Master Key is signed with the old one, to show that it really is owned by the same people and not substituted by an attacker. Each new Master Key also signs the previous Release Keys, in case you're trying to verify the signatures on a release prior to the rollover and can find a chain of trust to those keys from any of the people who have signed our new Master Key. Each release is signed with the Release Key that was current at the time of release. We don't go back and re-sign old releases with newly generated keys. The details of all previous keys are given here. *Key generated in 2016* (when we first introduced the Secure Contact Key) *Secure Contact Key* (2016) RSA, 2048-bit. Main key ID: 2048R/8A0AF00B (long version: 2048R/C4FCAAD08A0AF00B). Encryption subkey ID: 2048R/50C2CF5C (long version: 2048R/9EB39CC150C2CF5C). Fingerprint: 8A26 250E 763F E359 75F3 118F C4FC AAD0 8A0A F00B *Keys generated in the 2015 rollover* *Master Key* (2015) RSA, 4096-bit. Key ID: 4096R/04676F7C (long version: 4096R/AB585DC604676F7C). Fingerprint: 440D E3B5 B7A1 CA85 B3CC 1718 AB58 5DC6 0467 6F7C *Release Key* (2015) RSA, 2048-bit. Key ID: 2048R/B43434E4 (long version: 2048R/9DFE2648B43434E4). Fingerprint: 0054 DDAA 8ADA 15D2 768A 6DE7 9DFE 2648 B434 34E4 *Snapshot Key* (2015) RSA, 2048-bit. Key ID: 2048R/D15F7E8A (long version: 2048R/EEF20295D15F7E8A). Fingerprint: 0A3B 0048 FE49 9B67 A234 FEB6 EEF2 0295 D15F 7E8A *Original keys generated in 2000* (two sets, RSA and DSA) *Master Key* (original RSA) RSA, 1024-bit. Key ID: 1024R/1E34AC41 (long version: 1024R/9D5877BF1E34AC41). Fingerprint: 8F 15 97 DA 25 30 AB 0D 88 D1 92 54 11 CF 0C 4C *Master Key* (original DSA) DSA, 1024-bit. Key ID: 1024D/6A93B34E (long version: 1024D/4F5E6DF56A93B34E). Fingerprint: 313C 3E76 4B74 C2C5 F2AE 83A8 4F5E 6DF5 6A93 B34E *Release Key* (original RSA) RSA, 1024-bit. Key ID: 1024R/B41CAE29 (long version: 1024R/EF39CCC0B41CAE29). Fingerprint: AE 65 D3 F7 85 D3 18 E0 3B 0C 9B 02 FF 3A 81 FE *Release Key* (original DSA) DSA, 1024-bit. Key ID: 1024D/08B0A90B (long version: 1024D/FECD6F3F08B0A90B). Fingerprint: 00B1 1009 38E6 9800 6518 F0AB FECD 6F3F 08B0 A90B *Snapshot Key* (original RSA) RSA, 1024-bit. Key ID: 1024R/32B903A9 (long version: 1024R/FAAED21532B903A9). Fingerprint: 86 8B 1F 79 9C F4 7F BD 8B 1B D7 8E C6 4E 4C 03 *Snapshot Key* (original DSA) DSA, 1024-bit. Key ID: 1024D/7D3E4A00 (long version: 1024D/165E56F77D3E4A00). Fingerprint: 63DD 8EF8 32F5 D777 9FF0 2947 165E 56F7 7D3E 4A00 Appendix G: SSH-2 names specified for PuTTY ------------------------------------------- There are various parts of the SSH-2 protocol where things are specified using a textual name. Names ending in @putty.projects.tartarus.org are reserved for allocation by the PuTTY team. Allocated names are documented here. G.1 Connection protocol channel request names These names can be sent in a SSH_MSG_CHANNEL_REQUEST message. simple@putty.projects.tartarus.org This is sent by a client to announce that it will not have more than one channel open at a time in the current connection (that one being the one the request is sent on). The intention is that the server, knowing this, can set the window on that one channel to something very large, and leave flow control to TCP. There is no message-specific data. winadj@putty.projects.tartarus.org PuTTY sends this request along with some SSH_MSG_CHANNEL_WINDOW_ADJUST messages as part of its window- size tuning. It can be sent on any type of channel. There is no message-specific data. Servers MUST treat it as an unrecognised request and respond with SSH_MSG_CHANNEL_FAILURE. (Some SSH servers get confused by this message, so there is a bug-compatibility mode for disabling it. See section 4.26.3.) G.2 Key exchange method names rsa-sha1-draft-00@putty.projects.tartarus.org rsa-sha256-draft-00@putty.projects.tartarus.org rsa1024-sha1-draft-01@putty.projects.tartarus.org rsa1024-sha256-draft-01@putty.projects.tartarus.org rsa2048-sha256-draft-01@putty.projects.tartarus.org rsa1024-sha1-draft-02@putty.projects.tartarus.org rsa2048-sha512-draft-02@putty.projects.tartarus.org rsa1024-sha1-draft-03@putty.projects.tartarus.org rsa2048-sha256-draft-03@putty.projects.tartarus.org rsa1024-sha1-draft-04@putty.projects.tartarus.org rsa2048-sha256-draft-04@putty.projects.tartarus.org These appeared in various drafts of what eventually became RFC 4432. They have been superseded by rsa1024-sha1 and rsa2048- sha256. G.3 Encryption algorithm names arcfour128-draft-00@putty.projects.tartarus.org arcfour256-draft-00@putty.projects.tartarus.org These were used in drafts of what eventually became RFC 4345. They have been superseded by arcfour128 and arcfour256. G.4 Agent extension request names The SSH agent protocol, which is only specified in an Internet- Draft at the time of writing (draft-miller-ssh-agent), defines an extension mechanism. These names can be sent in an SSH_AGENTC_EXTENSION message. add-ppk@putty.projects.tartarus.org The payload is a single SSH-2 string containing a keypair in the PPK format defined in appendix C. Compared to the standard SSH_AGENTC_ADD_IDENTITY, this extension allows adding keys in encrypted form, with the agent requesting a decryption passphrase from the user on demand, and able to revert the key to encrypted form. reencrypt@putty.projects.tartarus.org The payload is a single SSH-2 string specifying a public key blob, as in SSH_AGENTC_REMOVE_IDENTITY. Requests that the agent forget any cleartext form of a specific key. Returns SSH_AGENT_SUCCESS if the agent ended up holding the key only in encrypted form (even if it was already encrypted); returns SSH_AGENT_EXTENSION_FAILURE if not (if it wasn't held by the agent at all, or only in cleartext form). reencrypt-all@putty.projects.tartarus.org No payload. Requests that the agent forget the cleartext form of any keys for which it holds an encrypted form. If the agent holds any keys with an encrypted form (or no keys at all), returns SSH_AGENT_SUCCESS to indicate that no such keys are now held in cleartext form, followed by a uint32 specifying how many keys remain in cleartext form (because the agent didn't hold an encrypted form for them). If the agent holds nothing but keys in cleartext form, returns SSH_AGENT_EXTENSION_FAILURE. list-extended@putty.projects.tartarus.org No payload. Returns SSH_AGENT_SUCCESS followed by a list of identities similar to SSH_AGENT_IDENTITIES_ANSWER, except that each key has an extra SSH-2 string at the end. Currently that string contains a single uint32 flags word, with the following bits defined: Bit 0 If set, key is held with an encrypted form (so that the `reencrypt' extension can do something useful with it). Bit 1 If set, key's cleartext form is not currently held (so the user will have to supply a passphrase before the key can be used). [PuTTY release 0.76] putty-0.76/doc/index.html0000644000175000017500000003574714072266315012344 00000000000000 PuTTY User Manual

Previous | Contents | Index | Next

PuTTY User Manual

PuTTY is a free (MIT-licensed) Windows Telnet and SSH client. This manual documents PuTTY, and its companion utilities PSCP, PSFTP, Plink, Pageant and PuTTYgen.

Note to Unix users: this manual currently primarily documents the Windows versions of the PuTTY utilities. Some options are therefore mentioned that are absent from the Unix version; the Unix version has features not described here; and the pterm and command-line puttygen and pageant utilities are not described at all. The only Unix-specific documentation that currently exists is the man pages.

This manual is copyright 1997-2021 Simon Tatham. All rights reserved. You may distribute this documentation under the MIT licence. See appendix D for the licence text in full.


If you want to provide feedback on this manual or on the PuTTY tools themselves, see the Feedback page.

[PuTTY release 0.76]
putty-0.76/doc/Chapter1.html0000644000175000017500000001256114072266315012671 00000000000000 Introduction to PuTTY

Previous | Contents | Index | Next

Chapter 1: Introduction to PuTTY

PuTTY is a free SSH, Telnet, Rlogin, and SUPDUP client for Windows systems.

1.1 What are SSH, Telnet, Rlogin, and SUPDUP?

If you already know what SSH, Telnet, Rlogin, and SUPDUP are, you can safely skip on to the next section.

SSH, Telnet, Rlogin, and SUPDUP are four ways of doing the same thing: logging in to a multi-user computer from another computer, over a network.

Multi-user operating systems, typically of the Unix family (such as Linux, MacOS, and the BSD family), usually present a command-line interface to the user, much like the ‘Command Prompt’ or ‘MS-DOS Prompt’ in Windows. The system prints a prompt, and you type commands which the system will obey.

Using this type of interface, there is no need for you to be sitting at the same machine you are typing commands to. The commands, and responses, can be sent over a network, so you can sit at one computer and give commands to another one, or even to more than one.

SSH, Telnet, Rlogin, and SUPDUP are network protocols that allow you to do this. On the computer you sit at, you run a client, which makes a network connection to the other computer (the server). The network connection carries your keystrokes and commands from the client to the server, and carries the server's responses back to you.

These protocols can also be used for other types of keyboard-based interactive session. In particular, there are a lot of bulletin boards, talker systems and MUDs (Multi-User Dungeons) which support access using Telnet. There are even a few that support SSH.

You might want to use SSH, Telnet, Rlogin, or SUPDUP if:

  • you have an account on a Unix system (or some other multi-user OS such as VMS or ITS) which you want to be able to access from somewhere else
  • your Internet Service Provider provides you with a login account on a web server. (This might also be known as a shell account. A shell is the program that runs on the server and interprets your commands for you.)
  • you want to use a bulletin board system, talker or MUD which can be accessed using Telnet.

You probably do not want to use SSH, Telnet, Rlogin, or SUPDUP if:

  • you only use Windows. Windows computers have their own ways of networking between themselves, and unless you are doing something fairly unusual, you will not need to use any of these remote login protocols.

1.2 How do SSH, Telnet, Rlogin, and SUPDUP differ?

This list summarises some of the differences between SSH, Telnet, Rlogin, and SUPDUP.

  • SSH (which stands for ‘secure shell’) is a recently designed, high-security protocol. It uses strong cryptography to protect your connection against eavesdropping, hijacking and other attacks. Telnet, Rlogin, and SUPDUP are all older protocols offering minimal security.
  • SSH and Rlogin both allow you to log in to the server without having to type a password. (Rlogin's method of doing this is insecure, and can allow an attacker to access your account on the server. SSH's method is much more secure, and typically breaking the security requires the attacker to have gained access to your actual client machine.)
  • SSH allows you to connect to the server and automatically send a command, so that the server will run that command and then disconnect. So you can use it in automated processing.

The Internet is a hostile environment and security is everybody's responsibility. If you are connecting across the open Internet, then we recommend you use SSH. If the server you want to connect to doesn't support SSH, it might be worth trying to persuade the administrator to install it.

If your client and server are both behind the same (good) firewall, it is more likely to be safe to use Telnet, Rlogin, or SUPDUP, but we still recommend you use SSH.


If you want to provide feedback on this manual or on the PuTTY tools themselves, see the Feedback page.

[PuTTY release 0.76]
putty-0.76/doc/Chapter2.html0000644000175000017500000002606114072266315012672 00000000000000 Getting started with PuTTY

Previous | Contents | Index | Next

Chapter 2: Getting started with PuTTY

This chapter gives a quick guide to the simplest types of interactive login session using PuTTY.

2.1 Starting a session

When you start PuTTY, you will see a dialog box. This dialog box allows you to control everything PuTTY can do. See chapter 4 for details of all the things you can control.

You don't usually need to change most of the configuration options. To start the simplest kind of session, all you need to do is to enter a few basic parameters.

In the ‘Host Name’ box, enter the Internet host name of the server you want to connect to. You should have been told this by the provider of your login account.

Now select a login protocol to use, from the ‘Connection type’ controls. For a login session, you should select SSH, Telnet, Rlogin, or SUPDUP. See section 1.2 for a description of the differences between these protocols, and advice on which one to use. The Raw protocol is not used for interactive login sessions; you would usually use this for debugging other Internet services (see section 3.7). The Serial option is used for connecting to a local serial line, and works somewhat differently: see section 3.6 for more information on this.

When you change the selected protocol, the number in the ‘Port’ box will change. This is normal: it happens because the various login services are usually provided on different network ports by the server machine. Most servers will use the standard port numbers, so you will not need to change the port setting. If your server provides login services on a non-standard port, your system administrator should have told you which one. (For example, many MUDs run Telnet service on a port other than 23.)

Once you have filled in the ‘Host Name’, ‘Connection type’, and possibly ‘Port’ settings, you are ready to connect. Press the ‘Open’ button at the bottom of the dialog box, and PuTTY will begin trying to connect you to the server.

2.2 Verifying the host key (SSH only)

If you are not using the SSH protocol, you can skip this section.

If you are using SSH to connect to a server for the first time, you will probably see a message looking something like this:

The server's host key is not cached in the registry. You have no
guarantee that the server is the computer you think it is.
The server's ssh-ed25519 key fingerprint is:
 ssh-ed25519 255 SHA256:TddlQk20DVs4LRcAsIfDN9pInKpY06D+h4kSHwWAj4w
If you trust this host, press "Accept" to add the key to PuTTY's
cache and carry on connecting.
If you want to carry on connecting just once, without adding the key
to the cache, press "Connect Once".
If you do not trust this host, press "Cancel" to abandon the connection.

This is a feature of the SSH protocol. It is designed to protect you against a network attack known as spoofing: secretly redirecting your connection to a different computer, so that you send your password to the wrong machine. Using this technique, an attacker would be able to learn the password that guards your login account, and could then log in as if they were you and use the account for their own purposes.

To prevent this attack, each server has a unique identifying code, called a host key. These keys are created in a way that prevents one server from forging another server's key. So if you connect to a server and it sends you a different host key from the one you were expecting, PuTTY can warn you that the server may have been switched and that a spoofing attack might be in progress.

PuTTY records the host key for each server you connect to, in the Windows Registry. Every time you connect to a server, it checks that the host key presented by the server is the same host key as it was the last time you connected. If it is not, you will see a warning, and you will have the chance to abandon your connection before you type any private information (such as a password) into it. (See section 10.2 for what that looks like.)

However, when you connect to a server you have not connected to before, PuTTY has no way of telling whether the host key is the right one or not. So it gives the warning shown above, and asks you whether you want to trust this host key or not.

Whether or not to trust the host key is your choice. If you are connecting within a company network, you might feel that all the network users are on the same side and spoofing attacks are unlikely, so you might choose to trust the key without checking it. If you are connecting across a hostile network (such as the Internet), you should check with your system administrator, perhaps by telephone or in person. (When verifying the fingerprint, be careful with letters and numbers that can be confused with each other: 0/O, 1/I/l, and so on.)

Many servers have more than one host key. If the system administrator sends you more than one fingerprint, you should make sure the one PuTTY shows you is on the list, but it doesn't matter which one it is.

If you don't have any fingerprints that look like the example (SHA256: followed by a long string of characters), but instead have pairs of characters separated by colons like a4:db:96:a7:..., try pressing the ‘More info...’ button and see if you have a fingerprint matching the ‘MD5 fingerprint’ there. This is an older and less secure way to summarise the same underlying host key; it's possible for an attacker to create their own host key with the same fingerprint; so you should avoid relying on this fingerprint format unless you have no choice. The ‘More info...’ dialog box also shows the full host public key, in case that is easier to compare than a fingerprint.

See section 4.19 for advanced options for managing host keys.

2.3 Logging in

After you have connected, and perhaps verified the server's host key, you will be asked to log in, probably using a username and a password. Your system administrator should have provided you with these. (If, instead, your system administrator has asked you to provide, or provided you with, a ‘public key’ or ‘key file’, see chapter 8.)

PuTTY will display a text window (the ‘terminal window’ – it will have a black background unless you've changed the defaults), and prompt you to type your username and password into that window. (These prompts will include the PuTTY icon, to distinguish them from any text sent by the server in the same window.)

Enter the username and the password, and the server should grant you access and begin your session. If you have mistyped your password, most servers will give you several chances to get it right.

While you are typing your password, you will not usually see the cursor moving in the window, but PuTTY is registering what you type, and will send it when you press Return. (It works this way to avoid revealing the length of your password to anyone watching your screen.)

If you are using SSH, be careful not to type your username wrongly, because you will not have a chance to correct it after you press Return; many SSH servers do not permit you to make two login attempts using different usernames. If you type your username wrongly, you must close PuTTY and start again.

If your password is refused but you are sure you have typed it correctly, check that Caps Lock is not enabled. Many login servers, particularly Unix computers, treat upper case and lower case as different when checking your password; so if Caps Lock is on, your password will probably be refused.

2.4 After logging in

After you log in to the server, what happens next is up to the server! Most servers will print some sort of login message and then present a prompt, at which you can type commands which the server will carry out. Some servers will offer you on-line help; others might not. If you are in doubt about what to do next, consult your system administrator.

2.5 Logging out

When you have finished your session, you should log out by typing the server's own logout command. This might vary between servers; if in doubt, try logout or exit, or consult a manual or your system administrator. When the server processes your logout command, the PuTTY window should close itself automatically.

You can close a PuTTY session using the Close button in the window border, but this might confuse the server - a bit like hanging up a telephone unexpectedly in the middle of a conversation. We recommend you do not do this unless the server has stopped responding to you and you cannot close the window any other way.


If you want to provide feedback on this manual or on the PuTTY tools themselves, see the Feedback page.

[PuTTY release 0.76]
putty-0.76/doc/Chapter3.html0000644000175000017500000017340314072266315012676 00000000000000 Using PuTTY

Previous | Contents | Index | Next

Chapter 3: Using PuTTY

This chapter provides a general introduction to some more advanced features of PuTTY. For extreme detail and reference purposes, chapter 4 is likely to contain more information.

3.1 During your session

A lot of PuTTY's complexity and features are in the configuration panel. Once you have worked your way through that and started a session, things should be reasonably simple after that. Nevertheless, there are a few more useful features available.

3.1.1 Copying and pasting text

Often in a PuTTY session you will find text on your terminal screen which you want to type in again. Like most other terminal emulators, PuTTY allows you to copy and paste the text rather than having to type it again. Also, copy and paste uses the Windows clipboard, so that you can paste (for example) URLs into a web browser, or paste from a word processor or spreadsheet into your terminal session.

By default, PuTTY's copy and paste works entirely with the mouse. (This will be familiar to people who have used xterm on Unix.) In order to copy text to the clipboard, you just click the left mouse button in the terminal window, and drag to select text. When you let go of the button, the text is automatically copied to the clipboard. You do not need to press Ctrl-C or Ctrl-Ins; in fact, if you do press Ctrl-C, PuTTY will send a Ctrl-C character down your session to the server where it will probably cause a process to be interrupted.

Pasting into PuTTY is done using the right button (or the middle mouse button, if you have a three-button mouse and have set it up; see section 4.11.1). (Pressing Shift-Ins, or selecting ‘Paste’ from the Ctrl+right-click context menu, have the same effect.) When you click the right mouse button, PuTTY will read whatever is in the Windows clipboard and paste it into your session. By default, this behaves exactly as if the clipboard contents had been typed at the keyboard; therefore, be careful of pasting formatted text into an editor that does automatic indenting, as you may find that the spaces pasted from the clipboard plus the spaces added by the editor add up to too many spaces and ruin the formatting. (Some remote applications can ask PuTTY to identify text that is being pasted, to avoid this sort of problem; but if your application does not, there is nothing PuTTY can do to avoid this.)

If you double-click the left mouse button, PuTTY will select a whole word. If you double-click, hold down the second click, and drag the mouse, PuTTY will select a sequence of whole words. (You can adjust precisely what PuTTY considers to be part of a word; see section 4.12.1.) If you triple-click, or triple-click and drag, then PuTTY will select a whole line or sequence of lines.

If you want to select a rectangular region instead of selecting to the end of each line, you can do this by holding down Alt when you make your selection. You can also configure rectangular selection to be the default, and then holding down Alt gives the normal behaviour instead: see section 4.11.3 for details.

(In some Unix environments, Alt+drag is intercepted by the window manager. Shift+Alt+drag should work for rectangular selection as well, so you could try that instead.)

If you have a middle mouse button, then you can use it to adjust an existing selection if you selected something slightly wrong. (If you have configured the middle mouse button to paste, then the right mouse button does this instead.) Click the button on the screen, and you can pick up the nearest end of the selection and drag it to somewhere else.

If you are running PuTTY itself on Unix (not just using it to connect to a Unix system from Windows), by default you will likely have to use similar mouse actions in other applications to paste the text you copied from PuTTY, and to copy text for pasting into PuTTY; actions like Ctrl-C and Ctrl-V will likely not behave as you expect. Section 4.11.4 explains why this is, and how you can change the behaviour. (On Windows there is only a single selection shared with other applications, so this confusion does not arise.)

It's possible for the server to ask to handle mouse clicks in the PuTTY window itself. If this happens, the mouse pointer will turn into an arrow, and using the mouse to copy and paste will only work if you hold down Shift. See section 4.6.2 and section 4.11.2 for details of this feature and how to configure it.

You can customise much of this behaviour, for instance to enable copy and paste from the keyboard; see section 4.11.

3.1.2 Scrolling the screen back

PuTTY keeps track of text that has scrolled up off the top of the terminal. So if something appears on the screen that you want to read, but it scrolls too fast and it's gone by the time you try to look for it, you can use the scrollbar on the right side of the window to look back up the session history and find it again.

As well as using the scrollbar, you can also page the scrollback up and down by pressing Shift-PgUp and Shift-PgDn. You can scroll a line at a time using Ctrl-PgUp and Ctrl-PgDn, or to the top/bottom of the scrollback with Ctrl-Shift-PgUp and Ctrl-Shift-PgDn. These are still available if you configure the scrollbar to be invisible.

By default the last 2000 lines scrolled off the top are preserved for you to look at. You can increase (or decrease) this value using the configuration box; see section 4.7.3.

3.1.3 The System menu

If you click the left mouse button on the icon in the top left corner of PuTTY's terminal window, or click the right mouse button on the title bar, you will see the standard Windows system menu containing items like Minimise, Move, Size and Close.

PuTTY's system menu contains extra program features in addition to the Windows standard options. These extra menu commands are described below.

(These options are also available in a context menu brought up by holding Ctrl and clicking with the right mouse button anywhere in the PuTTY window.)

3.1.3.1 The PuTTY Event Log

If you choose ‘Event Log’ from the system menu, a small window will pop up in which PuTTY logs significant events during the connection. Most of the events in the log will probably take place during session startup, but a few can occur at any point in the session, and one or two occur right at the end.

You can use the mouse to select one or more lines of the Event Log, and hit the Copy button to copy them to the clipboard. If you are reporting a bug, it's often useful to paste the contents of the Event Log into your bug report.

(The Event Log is not the same as the facility to create a log file of your session; that's described in section 3.2.)

3.1.3.2 Special commands

Depending on the protocol used for the current session, there may be a submenu of ‘special commands’. These are protocol-specific tokens, such as a ‘break’ signal, that can be sent down a connection in addition to normal data. Their precise effect is usually up to the server. Currently only Telnet, SSH, and serial connections have special commands.

The ‘break’ signal can also be invoked from the keyboard with Ctrl-Break.

In an SSH connection, the following special commands are available:

  • IGNORE message

    Should have no effect.

  • Repeat key exchange

    Only available in SSH-2. Forces a repeat key exchange immediately (and resets associated timers and counters). For more information about repeat key exchanges, see section 4.18.2.

  • Cache new host key type

    Only available in SSH-2. This submenu appears only if the server has host keys of a type that PuTTY doesn't already have cached, and so won't consider. Selecting a key here will allow PuTTY to use that key now and in future: PuTTY will do a fresh key-exchange with the selected key, and immediately add that key to its permanent cache (relying on the host key used at the start of the connection to cross-certify the new key). That key will be used for the rest of the current session; it may not actually be used for future sessions, depending on your preferences (see section 4.19.1).

    Normally, PuTTY will carry on using a host key it already knows, even if the server offers key formats that PuTTY would otherwise prefer, to avoid host key prompts. As a result, if you've been using a server for some years, you may still be using an older key than a new user would use, due to server upgrades in the meantime. The SSH protocol unfortunately does not have organised facilities for host key migration and rollover, but this allows you to manually upgrade.

  • Break

    Only available in SSH-2, and only during a session. Optional extension; may not be supported by server. PuTTY requests the server's default break length.

  • Signals (SIGINT, SIGTERM etc)

    Only available in SSH-2, and only during a session. Sends various POSIX signals. Not honoured by all servers.

The following special commands are available in Telnet:

  • Are You There
  • Break
  • Synch
  • Erase Character

    PuTTY can also be configured to send this when the Backspace key is pressed; see section 4.29.3.

  • Erase Line
  • Go Ahead
  • No Operation

    Should have no effect.

  • Abort Process
  • Abort Output
  • Interrupt Process

    PuTTY can also be configured to send this when Ctrl-C is typed; see section 4.29.3.

  • Suspend Process

    PuTTY can also be configured to send this when Ctrl-Z is typed; see section 4.29.3.

  • End Of Record
  • End Of File

With a serial connection, the only available special command is ‘Break’.

3.1.3.3 Starting new sessions

PuTTY's system menu provides some shortcut ways to start new sessions:

  • Selecting ‘New Session’ will start a completely new instance of PuTTY, and bring up the configuration box as normal.
  • Selecting ‘Duplicate Session’ will start a session in a new window with precisely the same options as your current one - connecting to the same host using the same protocol, with all the same terminal settings and everything.
  • In an inactive window, selecting ‘Restart Session’ will do the same as ‘Duplicate Session’, but in the current window.
  • The ‘Saved Sessions’ submenu gives you quick access to any sets of stored session details you have previously saved. See section 4.1.2 for details of how to create saved sessions.

3.1.3.4 Changing your session settings

If you select ‘Change Settings’ from the system menu, PuTTY will display a cut-down version of its initial configuration box. This allows you to adjust most properties of your current session. You can change the terminal size, the font, the actions of various keypresses, the colours, and so on.

Some of the options that are available in the main configuration box are not shown in the cut-down Change Settings box. These are usually options which don't make sense to change in the middle of a session (for example, you can't switch from SSH to Telnet in mid-session).

You can save the current settings to a saved session for future use from this dialog box. See section 4.1.2 for more on saved sessions.

3.1.3.5 Copy All to Clipboard

This system menu option provides a convenient way to copy the whole contents of the terminal screen (up to the last nonempty line) and scrollback to the clipboard in one go.

3.1.3.6 Clearing and resetting the terminal

The ‘Clear Scrollback’ option on the system menu tells PuTTY to discard all the lines of text that have been kept after they scrolled off the top of the screen. This might be useful, for example, if you displayed sensitive information and wanted to make sure nobody could look over your shoulder and see it. (Note that this only prevents a casual user from using the scrollbar to view the information; the text is not guaranteed not to still be in PuTTY's memory.)

The ‘Reset Terminal’ option causes a full reset of the terminal emulation. A VT-series terminal is a complex piece of software and can easily get into a state where all the text printed becomes unreadable. (This can happen, for example, if you accidentally output a binary file to your terminal.) If this happens, selecting Reset Terminal should sort it out.

3.1.3.7 Full screen mode

If you find the title bar on a maximised window to be ugly or distracting, you can select Full Screen mode to maximise PuTTY ‘even more’. When you select this, PuTTY will expand to fill the whole screen and its borders, title bar and scrollbar will disappear. (You can configure the scrollbar not to disappear in full-screen mode if you want to keep it; see section 4.7.3.)

When you are in full-screen mode, you can still access the system menu if you click the left mouse button in the extreme top left corner of the screen.

3.2 Creating a log file of your session

For some purposes you may find you want to log everything that appears on your screen. You can do this using the ‘Logging’ panel in the configuration box.

To begin a session log, select ‘Change Settings’ from the system menu and go to the Logging panel. Enter a log file name, and select a logging mode. (You can log all session output including the terminal control sequences, or you can just log the printable text. It depends what you want the log for.) Click ‘Apply’ and your log will be started. Later on, you can go back to the Logging panel and select ‘Logging turned off completely’ to stop logging; then PuTTY will close the log file and you can safely read it.

See section 4.2 for more details and options.

3.3 Altering your character set configuration

If you find that special characters (accented characters, for example, or line-drawing characters) are not being displayed correctly in your PuTTY session, it may be that PuTTY is interpreting the characters sent by the server according to the wrong character set. There are a lot of different character sets available, and no good way for PuTTY to know which to use, so it's entirely possible for this to happen.

If you click ‘Change Settings’ and look at the ‘Translation’ panel, you should see a large number of character sets which you can select, and other related options. Now all you need is to find out which of them you want! (See section 4.10 for more information.)

3.4 Using X11 forwarding in SSH

The SSH protocol has the ability to securely forward X Window System graphical applications over your encrypted SSH connection, so that you can run an application on the SSH server machine and have it put its windows up on your local machine without sending any X network traffic in the clear.

In order to use this feature, you will need an X display server for your Windows machine, such as Cygwin/X, X-Win32, or Exceed. This will probably install itself as display number 0 on your local machine; if it doesn't, the manual for the X server should tell you what it does do.

You should then tick the ‘Enable X11 forwarding’ box in the X11 panel (see section 4.24) before starting your SSH session. The ‘X display location’ box is blank by default, which means that PuTTY will try to use a sensible default such as :0, which is the usual display location where your X server will be installed. If that needs changing, then change it.

Now you should be able to log in to the SSH server as normal. To check that X forwarding has been successfully negotiated during connection startup, you can check the PuTTY Event Log (see section 3.1.3.1). It should say something like this:

2001-12-05 17:22:01 Requesting X11 forwarding
2001-12-05 17:22:02 X11 forwarding enabled

If the remote system is Unix or Unix-like, you should also be able to see that the DISPLAY environment variable has been set to point at display 10 or above on the SSH server machine itself:

fred@unixbox:~$ echo $DISPLAY
unixbox:10.0

If this works, you should then be able to run X applications in the remote session and have them display their windows on your PC.

For more options relating to X11 forwarding, see section 4.24.

3.5 Using port forwarding in SSH

The SSH protocol has the ability to forward arbitrary network (TCP) connections over your encrypted SSH connection, to avoid the network traffic being sent in clear. For example, you could use this to connect from your home computer to a POP-3 server on a remote machine without your POP-3 password being visible to network sniffers.

In order to use port forwarding to connect from your local machine to a port on a remote server, you need to:

  • Choose a port number on your local machine where PuTTY should listen for incoming connections. There are likely to be plenty of unused port numbers above 3000. (You can also use a local loopback address here; see below for more details.)
  • Now, before you start your SSH connection, go to the Tunnels panel (see section 4.25). Make sure the ‘Local’ radio button is set. Enter the local port number into the ‘Source port’ box. Enter the destination host name and port number into the ‘Destination’ box, separated by a colon (for example, popserver.example.com:110 to connect to a POP-3 server).
  • Now click the ‘Add’ button. The details of your port forwarding should appear in the list box.

Now start your session and log in. (Port forwarding will not be enabled until after you have logged in; otherwise it would be easy to perform completely anonymous network attacks, and gain access to anyone's virtual private network.) To check that PuTTY has set up the port forwarding correctly, you can look at the PuTTY Event Log (see section 3.1.3.1). It should say something like this:

2001-12-05 17:22:10 Local port 3110 forwarding to
         popserver.example.com:110

Now if you connect to the source port number on your local PC, you should find that it answers you exactly as if it were the service running on the destination machine. So in this example, you could then configure an e-mail client to use localhost:3110 as a POP-3 server instead of popserver.example.com:110. (Of course, the forwarding will stop happening when your PuTTY session closes down.)

You can also forward ports in the other direction: arrange for a particular port number on the server machine to be forwarded back to your PC as a connection to a service on your PC or near it. To do this, just select the ‘Remote’ radio button instead of the ‘Local’ one. The ‘Source port’ box will now specify a port number on the server (note that most servers will not allow you to use port numbers under 1024 for this purpose).

An alternative way to forward local connections to remote hosts is to use dynamic SOCKS proxying. In this mode, PuTTY acts as a SOCKS server, which SOCKS-aware programs can connect to and open forwarded connections to the destination of their choice, so this can be an alternative to long lists of static forwardings. To use this mode, you will need to select the ‘Dynamic’ radio button instead of ‘Local’, and then you should not enter anything into the ‘Destination’ box (it will be ignored). PuTTY will then listen for SOCKS connections on the port you have specified. Most web browsers can be configured to connect to this SOCKS proxy service; also, you can forward other PuTTY connections through it by setting up the Proxy control panel (see section 4.16 for details).

The source port for a forwarded connection usually does not accept connections from any machine except the SSH client or server machine itself (for local and remote forwardings respectively). There are controls in the Tunnels panel to change this:

  • The ‘Local ports accept connections from other hosts’ option allows you to set up local-to-remote port forwardings (including dynamic port forwardings) in such a way that machines other than your client PC can connect to the forwarded port.
  • The ‘Remote ports do the same’ option does the same thing for remote-to-local port forwardings (so that machines other than the SSH server machine can connect to the forwarded port.) Note that this feature is only available in the SSH-2 protocol, and not all SSH-2 servers honour it (in OpenSSH, for example, it's usually disabled by default).

You can also specify an IP address to listen on. Typically a Windows machine can be asked to listen on any single IP address in the 127.*.*.* range, and all of these are loopback addresses available only to the local machine. So if you forward (for example) 127.0.0.5:79 to a remote machine's finger port, then you should be able to run commands such as finger fred@127.0.0.5. This can be useful if the program connecting to the forwarded port doesn't allow you to change the port number it uses. This feature is available for local-to-remote forwarded ports; SSH-1 is unable to support it for remote-to-local ports, while SSH-2 can support it in theory but servers will not necessarily cooperate.

(Note that if you're using Windows XP Service Pack 2, you may need to obtain a fix from Microsoft in order to use addresses like 127.0.0.5 - see question A.7.17.)

For more options relating to port forwarding, see section 4.25.

If the connection you are forwarding over SSH is itself a second SSH connection made by another copy of PuTTY, you might find the ‘logical host name’ configuration option useful to warn PuTTY of which host key it should be expecting. See section 4.14.5 for details of this.

3.6 Connecting to a local serial line

PuTTY can connect directly to a local serial line as an alternative to making a network connection. In this mode, text typed into the PuTTY window will be sent straight out of your computer's serial port, and data received through that port will be displayed in the PuTTY window. You might use this mode, for example, if your serial port is connected to another computer which has a serial connection.

To make a connection of this type, simply select ‘Serial’ from the ‘Connection type’ radio buttons on the ‘Session’ configuration panel (see section 4.1.1). The ‘Host Name’ and ‘Port’ boxes will transform into ‘Serial line’ and ‘Speed’, allowing you to specify which serial line to use (if your computer has more than one) and what speed (baud rate) to use when transferring data. For further configuration options (data bits, stop bits, parity, flow control), you can use the ‘Serial’ configuration panel (see section 4.28).

After you start up PuTTY in serial mode, you might find that you have to make the first move, by sending some data out of the serial line in order to notify the device at the other end that someone is there for it to talk to. This probably depends on the device. If you start up a PuTTY serial session and nothing appears in the window, try pressing Return a few times and see if that helps.

A serial line provides no well defined means for one end of the connection to notify the other that the connection is finished. Therefore, PuTTY in serial mode will remain connected until you close the window using the close button.

3.7 Making raw TCP connections

A lot of Internet protocols are composed of commands and responses in plain text. For example, SMTP (the protocol used to transfer e-mail), NNTP (the protocol used to transfer Usenet news), and HTTP (the protocol used to serve Web pages) all consist of commands in readable plain text.

Sometimes it can be useful to connect directly to one of these services and speak the protocol ‘by hand’, by typing protocol commands and watching the responses. On Unix machines, you can do this using the system's telnet command to connect to the right port number. For example, telnet mailserver.example.com 25 might enable you to talk directly to the SMTP service running on a mail server.

Although the Unix telnet program provides this functionality, the protocol being used is not really Telnet. Really there is no actual protocol at all; the bytes sent down the connection are exactly the ones you type, and the bytes shown on the screen are exactly the ones sent by the server. Unix telnet will attempt to detect or guess whether the service it is talking to is a real Telnet service or not; PuTTY prefers to be told for certain.

In order to make a debugging connection to a service of this type, you simply select the fourth protocol name, ‘Raw’, from the ‘Protocol’ buttons in the ‘Session’ configuration panel. (See section 4.1.1.) You can then enter a host name and a port number, and make the connection.

3.8 Connecting using the Telnet protocol

PuTTY can use the Telnet protocol to connect to a server.

Telnet was perhaps the most popular remote login protocol before SSH was introduced. It was general enough to be used by multiple server operating systems (Unix and VMS in particular), and supported many optional protocol extensions providing extra support for particular server features.

Unlike SSH, Telnet runs over an unsecured network connection, so it is a very bad idea to use it over the hostile Internet (though it is still used to some extent as of 2020).

3.9 Connecting using the Rlogin protocol

PuTTY can use the Rlogin protocol to connect to a server.

Rlogin was similar to Telnet in concept, but more focused on connections between Unix machines. It supported a feature for passwordless login, based on use of ‘privileged ports’ (ports with numbers below 1024, which Unix traditionally does not allow users other than root to allocate). Ultimately, based on the server trusting that the client's IP address was owned by the Unix machine it claimed to be, and that that machine would guard its privileged ports appropriately.

Like Telnet, Rlogin runs over an unsecured network connection.

3.10 Connecting using the SUPDUP protocol

PuTTY can use the SUPDUP protocol to connect to a server.

SUPDUP is a login protocol used mainly by PDP-10 and Lisp machines during the period 1975-1990. Like Telnet and Rlogin, it is unsecured, so modern systems almost never support it.

To make a connection of this type, select ‘SUPDUP’ from the ‘Connection type’ radio buttons on the ‘Session’ panel (see section 4.1.1). For further configuration options (character set, more processing, scrolling), you can use the ‘SUPDUP’ configuration panel (see section 4.31).

In SUPDUP, terminal emulation is more integrated with the network protocol than in other protocols such as SSH. The SUPDUP protocol can thus only be used with PuTTY proper, not with the command-line tool Plink.

The SUPDUP protocol does not support changing the terminal dimensions, so this capability is disabled during a SUPDUP session.

SUPDUP provides no well defined means for one end of the connection to notify the other that the connection is finished. Therefore, PuTTY in SUPDUP mode will remain connected until you close the window using the close button.

3.11 The PuTTY command line

PuTTY can be made to do various things without user intervention by supplying command-line arguments (e.g., from a command prompt window, or a Windows shortcut).

3.11.1 Starting a session from the command line

These options allow you to bypass the configuration window and launch straight into a session.

To start a connection to a server called host:

putty.exe [-ssh | -ssh-connection | -telnet | -rlogin | -supdup | -raw] [user@]host

If this syntax is used, settings are taken from the Default Settings (see section 4.1.2); user overrides these settings if supplied. Also, you can specify a protocol, which will override the default protocol (see section 3.11.3.2).

For telnet sessions, the following alternative syntax is supported (this makes PuTTY suitable for use as a URL handler for telnet URLs in web browsers):

putty.exe telnet://host[:port]/

To start a connection to a serial port, e.g. COM1:

putty.exe -serial com1

In order to start an existing saved session called sessionname, use the -load option (described in section 3.11.3.1).

putty.exe -load "session name"

3.11.2 -cleanup

If invoked with the -cleanup option, rather than running as normal, PuTTY will remove its registry entries and random seed file from the local machine (after confirming with the user). It will also attempt to remove information about recently launched sessions stored in the ‘jump list’ on Windows 7 and up.

Note that on multi-user systems, -cleanup only removes registry entries and files associated with the currently logged-in user.

3.11.3 Standard command-line options

PuTTY and its associated tools support a range of command-line options, most of which are consistent across all the tools. This section lists the available options in all tools. Options which are specific to a particular tool are covered in the chapter about that tool.

3.11.3.1 -load: load a saved session

The -load option causes PuTTY to load configuration details out of a saved session. If these details include a host name, then this option is all you need to make PuTTY start a session.

You need double quotes around the session name if it contains spaces.

If you want to create a Windows shortcut to start a PuTTY saved session, this is the option you should use: your shortcut should call something like

d:\path\to\putty.exe -load "my session"

(Note that PuTTY itself supports an alternative form of this option, for backwards compatibility. If you execute putty @sessionname it will have the same effect as putty -load "sessionname". With the @ form, no double quotes are required, and the @ sign must be the very first thing on the command line. This form of the option is deprecated.)

3.11.3.2 Selecting a protocol: -ssh, -ssh-connection, -telnet, -rlogin, -supdup, -raw, -serial

To choose which protocol you want to connect with, you can use one of these options:

  • -ssh selects the SSH protocol.
  • -ssh-connection selects the bare ssh-connection protocol. (This is only useful in specialised circumstances; see section 4.27 for more information.)
  • -telnet selects the Telnet protocol.
  • -rlogin selects the Rlogin protocol.
  • -supdup selects the SUPDUP protocol.
  • -raw selects the raw protocol.
  • -serial selects a serial connection.

Most of these options are not available in the file transfer tools PSCP and PSFTP (which only work with the SSH protocol and the bare ssh-connection protocol).

These options are equivalent to the protocol selection buttons in the Session panel of the PuTTY configuration box (see section 4.1.1).

3.11.3.3 -v: increase verbosity

Most of the PuTTY tools can be made to tell you more about what they are doing by supplying the -v option. If you are having trouble when making a connection, or you're simply curious, you can turn this switch on and hope to find out more about what is happening.

3.11.3.4 -l: specify a login name

You can specify the user name to log in as on the remote server using the -l option. For example, plink login.example.com -l fred.

These options are equivalent to the username selection box in the Connection panel of the PuTTY configuration box (see section 4.15.1).

3.11.3.5 -L, -R and -D: set up port forwardings

As well as setting up port forwardings in the PuTTY configuration (see section 4.25), you can also set up forwardings on the command line. The command-line options work just like the ones in Unix ssh programs.

To forward a local port (say 5110) to a remote destination (say popserver.example.com port 110), you can write something like one of these:

putty -L 5110:popserver.example.com:110 -load mysession
plink mysession -L 5110:popserver.example.com:110

To forward a remote port to a local destination, just use the -R option instead of -L:

putty -R 5023:mytelnetserver.myhouse.org:23 -load mysession
plink mysession -R 5023:mytelnetserver.myhouse.org:23

To specify an IP address for the listening end of the tunnel, prepend it to the argument:

plink -L 127.0.0.5:23:localhost:23 myhost

To set up SOCKS-based dynamic port forwarding on a local port, use the -D option. For this one you only have to pass the port number:

putty -D 4096 -load mysession

For general information on port forwarding, see section 3.5.

These options are not available in the file transfer tools PSCP and PSFTP.

3.11.3.6 -m: read a remote command or script from a file

The -m option performs a similar function to the ‘Remote command’ box in the SSH panel of the PuTTY configuration box (see section 4.17.1). However, the -m option expects to be given a local file name, and it will read a command from that file.

With some servers (particularly Unix systems), you can even put multiple lines in this file and execute more than one command in sequence, or a whole shell script; but this is arguably an abuse, and cannot be expected to work on all servers. In particular, it is known not to work with certain ‘embedded’ servers, such as Cisco routers.

This option is not available in the file transfer tools PSCP and PSFTP.

3.11.3.7 -P: specify a port number

The -P option is used to specify the port number to connect to. If you have a Telnet server running on port 9696 of a machine instead of port 23, for example:

putty -telnet -P 9696 host.name
plink -telnet -P 9696 host.name

(Note that this option is more useful in Plink than in PuTTY, because in PuTTY you can write putty -telnet host.name 9696 in any case.)

This option is equivalent to the port number control in the Session panel of the PuTTY configuration box (see section 4.1.1).

3.11.3.8 -pw: specify a password

A simple way to automate a remote login is to supply your password on the command line. This is not recommended for reasons of security. If you possibly can, we recommend you set up public-key authentication instead. See chapter 8 for details.

Note that the -pw option only works when you are using the SSH protocol. Due to fundamental limitations of Telnet, Rlogin, and SUPDUP, these protocols do not support automated password authentication.

3.11.3.9 -agent and -noagent: control use of Pageant for authentication

The -agent option turns on SSH authentication using Pageant, and -noagent turns it off. These options are only meaningful if you are using SSH.

See chapter 9 for general information on Pageant.

These options are equivalent to the agent authentication checkbox in the Auth panel of the PuTTY configuration box (see section 4.21.4).

3.11.3.10 -A and -a: control agent forwarding

The -A option turns on SSH agent forwarding, and -a turns it off. These options are only meaningful if you are using SSH.

See chapter 9 for general information on Pageant, and section 9.4 for information on agent forwarding. Note that there is a security risk involved with enabling this option; see section 9.6 for details.

These options are equivalent to the agent forwarding checkbox in the Auth panel of the PuTTY configuration box (see section 4.21.7).

These options are not available in the file transfer tools PSCP and PSFTP.

3.11.3.11 -X and -x: control X11 forwarding

The -X option turns on X11 forwarding in SSH, and -x turns it off. These options are only meaningful if you are using SSH.

For information on X11 forwarding, see section 3.4.

These options are equivalent to the X11 forwarding checkbox in the X11 panel of the PuTTY configuration box (see section 4.24).

These options are not available in the file transfer tools PSCP and PSFTP.

3.11.3.12 -t and -T: control pseudo-terminal allocation

The -t option ensures PuTTY attempts to allocate a pseudo-terminal at the server, and -T stops it from allocating one. These options are only meaningful if you are using SSH.

These options are equivalent to the ‘Don't allocate a pseudo-terminal’ checkbox in the SSH panel of the PuTTY configuration box (see section 4.23.1).

These options are not available in the file transfer tools PSCP and PSFTP.

3.11.3.13 -N: suppress starting a shell or command

The -N option prevents PuTTY from attempting to start a shell or command on the remote server. You might want to use this option if you are only using the SSH connection for port forwarding, and your user account on the server does not have the ability to run a shell.

This feature is only available in SSH protocol version 2 (since the version 1 protocol assumes you will always want to run a shell).

This option is equivalent to the ‘Don't start a shell or command at all’ checkbox in the SSH panel of the PuTTY configuration box (see section 4.17.2).

This option is not available in the file transfer tools PSCP and PSFTP.

3.11.3.14 -nc: make a remote network connection in place of a remote shell or command

The -nc option prevents Plink (or PuTTY) from attempting to start a shell or command on the remote server. Instead, it will instruct the remote server to open a network connection to a host name and port number specified by you, and treat that network connection as if it were the main session.

You specify a host and port as an argument to the -nc option, with a colon separating the host name from the port number, like this:

plink host1.example.com -nc host2.example.com:1234

You might want to use this feature if you needed to make an SSH connection to a target host which you can only reach by going through a proxy host, and rather than using port forwarding you prefer to use the local proxy feature (see section 4.16.1 for more about local proxies). In this situation you might select ‘Local’ proxy type, set your local proxy command to be ‘plink %proxyhost -nc %host:%port’, enter the target host name on the Session panel, and enter the directly reachable proxy host name on the Proxy panel.

This feature is only available in SSH protocol version 2 (since the version 1 protocol assumes you will always want to run a shell). It is not available in the file transfer tools PSCP and PSFTP. It is available in PuTTY itself, although it is unlikely to be very useful in any tool other than Plink. Also, -nc uses the same server functionality as port forwarding, so it will not work if your server administrator has disabled port forwarding.

(The option is named -nc after the Unix program nc, short for ‘netcat’. The command ‘plink host1 -nc host2:port’ is very similar in functionality to ‘plink host1 nc host2 port’, which invokes nc on the server and tells it to connect to the specified destination. However, Plink's built-in -nc option does not depend on the nc program being installed on the server.)

3.11.3.15 -C: enable compression

The -C option enables compression of the data sent across the network. This option is only meaningful if you are using SSH.

This option is equivalent to the ‘Enable compression’ checkbox in the SSH panel of the PuTTY configuration box (see section 4.17.3).

3.11.3.16 -1 and -2: specify an SSH protocol version

The -1 and -2 options force PuTTY to use version 1 or version 2 of the SSH protocol. These options are only meaningful if you are using SSH.

These options are equivalent to selecting the SSH protocol version in the SSH panel of the PuTTY configuration box (see section 4.17.4).

3.11.3.17 -4 and -6: specify an Internet protocol version

The -4 and -6 options force PuTTY to use the older Internet protocol IPv4 or the newer IPv6 for most outgoing connections.

These options are equivalent to selecting your preferred Internet protocol version as ‘IPv4’ or ‘IPv6’ in the Connection panel of the PuTTY configuration box (see section 4.14.4).

3.11.3.18 -i: specify an SSH private key

The -i option allows you to specify the name of a private key file in *.PPK format which PuTTY will use to authenticate with the server. This option is only meaningful if you are using SSH.

If you are using Pageant, you can also specify a public key file (in RFC 4716 or OpenSSH format) to identify a specific key file to use. (This won't work if you're not running Pageant, of course.)

For general information on public-key authentication, see chapter 8.

This option is equivalent to the ‘Private key file for authentication’ box in the Auth panel of the PuTTY configuration box (see section 4.21.9).

3.11.3.19 -no-trivial-auth: disconnect if SSH authentication succeeds trivially

This option causes PuTTY to abandon an SSH session if the server accepts authentication without ever having asked for any kind of password or signature or token.

See section 4.21.3 for why you might want this.

3.11.3.20 -loghost: specify a logical host name

This option overrides PuTTY's normal SSH host key caching policy by telling it the name of the host you expect your connection to end up at (in cases where this differs from the location PuTTY thinks it's connecting to). It can be a plain host name, or a host name followed by a colon and a port number. See section 4.14.5 for more detail on this.

3.11.3.21 -hostkey: manually specify an expected host key

This option overrides PuTTY's normal SSH host key caching policy by telling it exactly what host key to expect, which can be useful if the normal automatic host key store in the Registry is unavailable. The argument to this option should be either a host key fingerprint, or an SSH-2 public key blob. See section 4.19.3 for more information.

You can specify this option more than once if you want to configure more than one key to be accepted.

3.11.3.22 -pgpfp: display PGP key fingerprints

This option causes the PuTTY tools not to run as normal, but instead to display the fingerprints of the PuTTY PGP Master Keys, in order to aid with verifying new versions. See appendix F for more information.

3.11.3.23 -sercfg: specify serial port configuration

This option specifies the configuration parameters for the serial port (baud rate, stop bits etc). Its argument is interpreted as a comma-separated list of configuration options, which can be as follows:

  • Any single digit from 5 to 9 sets the number of data bits.
  • 1’, ‘1.5’ or ‘2’ sets the number of stop bits.
  • Any other numeric string is interpreted as a baud rate.
  • A single lower-case letter specifies the parity: ‘n’ for none, ‘o’ for odd, ‘e’ for even, ‘m’ for mark and ‘s’ for space.
  • A single upper-case letter specifies the flow control: ‘N’ for none, ‘X’ for XON/XOFF, ‘R’ for RTS/CTS and ‘D’ for DSR/DTR.

For example, ‘-sercfg 19200,8,n,1,N’ denotes a baud rate of 19200, 8 data bits, no parity, 1 stop bit and no flow control.

3.11.3.24 -sessionlog, -sshlog, -sshrawlog: enable session logging

These options cause the PuTTY network tools to write out a log file. Each of them expects a file name as an argument, e.g. ‘-sshlog putty.log’ causes an SSH packet log to be written to a file called ‘putty.log’. The three different options select different logging modes, all available from the GUI too:

  • -sessionlog selects ‘All session output’ logging mode.
  • -sshlog selects ‘SSH packets’ logging mode.
  • -sshrawlog selects ‘SSH packets and raw data’ logging mode.

For more information on logging configuration, see section 4.2.

3.11.3.25 -logoverwrite, -logappend: control behaviour with existing log file

If logging has been enabled (in the saved configuration, or by another command-line option), and the specified log file already exists, these options tell the PuTTY network tools what to do so that they don't have to ask the user. See section 4.2.2 for details.

3.11.3.26 -proxycmd: specify a local proxy command

This option enables PuTTY's mode for running a command on the local machine and using it as a proxy for the network connection. It expects a shell command string as an argument.

See section 4.16.1 for more information on this, and on other proxy settings. In particular, note that since the special sequences described there are understood in the argument string, literal backslashes must be doubled (if you want \ in your command, you must put \\ on the command line).

3.11.3.27 -restrict-acl: restrict the Windows process ACL

This option (on Windows only) causes PuTTY (or another PuTTY tool) to try to lock down the operating system's access control on its own process. If this succeeds, it should present an extra obstacle to malware that has managed to run under the same user id as the PuTTY process, by preventing it from attaching to PuTTY using the same interfaces debuggers use and either reading sensitive information out of its memory or hijacking its network session.

This option is not enabled by default, because this form of interaction between Windows programs has many legitimate uses, including accessibility software such as screen readers. Also, it cannot provide full security against this class of attack in any case, because PuTTY can only lock down its own ACL after it has started up, and malware could still get in if it attacks the process between startup and lockdown. So it trades away noticeable convenience, and delivers less real security than you might want. However, if you do want to make that tradeoff anyway, the option is available.

A PuTTY process started with -restrict-acl will pass that on to any processes started with Duplicate Session, New Session etc. (However, if you're invoking PuTTY tools explicitly, for instance as a proxy command, you'll need to arrange to pass them the -restrict-acl option yourself, if that's what you want.)

If Pageant is started with the -restrict-acl option, and you use it to launch a PuTTY session from its System Tray submenu, then Pageant will not default to starting the PuTTY subprocess with a restricted ACL. This is because PuTTY is more likely to suffer reduced functionality as a result of restricted ACLs (e.g. screen reader software will have a greater need to interact with it), whereas Pageant stores the more critical information (hence benefits more from the extra protection), so it's reasonable to want to run Pageant but not PuTTY with the ACL restrictions. You can force Pageant to start subsidiary PuTTY processes with a restricted ACL if you also pass the -restrict-putty-acl option.


If you want to provide feedback on this manual or on the PuTTY tools themselves, see the Feedback page.

[PuTTY release 0.76]
putty-0.76/doc/Chapter4.html0000644000175000017500000064317314072266315012705 00000000000000 Configuring PuTTY

Previous | Contents | Index | Next

Chapter 4: Configuring PuTTY

This chapter describes all the 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 session, by selecting ‘Change Settings’ from the window menu.

4.1 The Session panel

The Session configuration panel contains the basic options you need to specify in order to open a session at all, and also allows you to save your settings to be reloaded later.

4.1.1 The host name section

The top box on the Session panel, labelled ‘Specify the destination you want to connect to’, contains the details that need to be filled in before PuTTY can open a session at all.

  • The ‘Host Name’ box is where you type the name, or the IP address, of the server you want to connect to.
  • The ‘Connection type’ controls let you choose what type of connection you want to make: an SSH network connection, a connection to a local serial line, or various other kinds of network connection.
    • See section 1.2 for a summary of the differences between the network remote login protocols SSH, Telnet, Rlogin, and SUPDUP.
    • See section 3.6 for information about using a serial line.
    • See section 3.7 for an explanation of ‘raw’ connections.
    • See section 3.8 for a little information about Telnet.
    • See section 3.9 for information about using Rlogin.
    • See section 3.10 for information about using SUPDUP.
    • The ‘Bare ssh-connection’ option in the ‘Connection type’ control is intended for specialist uses not involving network connections. See section 4.27 for some information about it.
  • The ‘Port’ box lets you specify which port number on the server to connect to. If you select Telnet, Rlogin, SUPDUP, or SSH, this box will be filled in automatically to the usual value, and you will only need to change it if you have an unusual server. If you select Raw mode, you will almost certainly need to fill in the ‘Port’ box yourself.

If you select ‘Serial’ from the ‘Connection type’ radio buttons, the ‘Host Name’ and ‘Port’ boxes are replaced by ‘Serial line’ and ‘Speed’; see section 4.28 for more details of these.

4.1.2 Loading and storing saved sessions

The next part of the Session configuration panel allows you to save your preferred PuTTY options so they will appear automatically the next time you start PuTTY. It also allows you to create saved sessions, which contain a full set of configuration options plus a host name and protocol. A saved session contains all the information PuTTY needs to start exactly the session you want.

  • To save your default settings: first set up the settings the way you want them saved. Then come back to the Session panel. Select the ‘Default Settings’ entry in the saved sessions list, with a single click. Then press the ‘Save’ button.

If there is a specific host you want to store the details of how to connect to, you should create a saved session, which will be separate from the Default Settings.

  • To save a session: first go through the rest of the configuration box setting up all the options you want. Then come back to the Session panel. Enter a name for the saved session in the ‘Saved Sessions’ input box. (The server name is often a good choice for a saved session name.) Then press the ‘Save’ button. Your saved session name should now appear in the list box.

    You can also save settings in mid-session, from the ‘Change Settings’ dialog. Settings changed since the start of the session will be saved with their current values; as well as settings changed through the dialog, this includes changes in window size, window title changes sent by the server, and so on.

  • To reload a saved session: single-click to select the session name in the list box, and then press the ‘Load’ button. Your saved settings should all appear in the configuration panel.
  • To modify a saved session: first load it as described above. Then make the changes you want. Come back to the Session panel, and press the ‘Save’ button. The new settings will be saved over the top of the old ones.

    To save the new settings under a different name, you can enter the new name in the ‘Saved Sessions’ box, or single-click to select a session name in the list box to overwrite that session. To save ‘Default Settings’, you must single-click the name before saving.

  • To start a saved session immediately: double-click on the session name in the list box.
  • To delete a saved session: single-click to select the session name in the list box, and then press the ‘Delete’ button.

Each saved session is independent of the Default Settings configuration. If you change your preferences and update Default Settings, you must also update every saved session separately.

Saved sessions are stored in the Registry, at the location

HKEY_CURRENT_USER\Software\SimonTatham\PuTTY\Sessions

If you need to store them in a file, you could try the method described in section 4.32.

4.1.3 ‘Close window on exit’

Finally in the Session panel, there is an option labelled ‘Close window on exit’. This controls whether the PuTTY terminal window disappears as soon as the session inside it terminates. If you are likely to want to copy and paste text out of the session after it has terminated, or restart the session, you should arrange for this option to be off.

‘Close window on exit’ has three settings. ‘Always’ means always close the window on exit; ‘Never’ means never close on exit (always leave the window open, but inactive). The third setting, and the default one, is ‘Only on clean exit’. In this mode, a session which terminates normally will cause its window to close, but one which is aborted unexpectedly by network trouble or a confusing message from the server will leave the window up.

4.2 The Logging panel

The Logging configuration panel allows you to save log files of your PuTTY sessions, for debugging, analysis or future reference.

The main option is a radio-button set that specifies whether PuTTY will log anything at all. The options are:

  • ‘None’. This is the default option; in this mode PuTTY will not create a log file at all.
  • ‘Printable output’. In this mode, a log file will be created and written to, but only printable text will be saved into it. The various terminal control codes that are typically sent down an interactive session alongside the printable text will be omitted. This might be a useful mode if you want to read a log file in a text editor and hope to be able to make sense of it.
  • ‘All session output’. In this mode, everything sent by the server into your terminal session is logged. If you view the log file in a text editor, therefore, you may well find it full of strange control characters. This is a particularly useful mode if you are experiencing problems with PuTTY's terminal handling: you can record everything that went to the terminal, so that someone else can replay the session later in slow motion and watch to see what went wrong.
  • ‘SSH packets’. In this mode (which is only used by SSH connections), the SSH message packets sent over the encrypted connection are written to the log file (as well as Event Log entries). You might need this to debug a network-level problem, or more likely to send to the PuTTY authors as part of a bug report. BE WARNED that if you log in using a password, the password can appear in the log file; see section 4.2.5 for options that may help to remove sensitive material from the log file before you send it to anyone else.
  • ‘SSH packets and raw data’. In this mode, as well as the decrypted packets (as in the previous mode), the raw (encrypted, compressed, etc) packets are also logged. This could be useful to diagnose corruption in transit. (The same caveats as the previous mode apply, of course.)

Note that the non-SSH logging options (‘Printable output’ and ‘All session output’) only work with PuTTY proper; in programs without terminal emulation (such as Plink), they will have no effect, even if enabled via saved settings.

4.2.1 ‘Log file name’

In this edit box you enter the name of the file you want to log the session to. The ‘Browse’ button will let you look around your file system to find the right place to put the file; or if you already know exactly where you want it to go, you can just type a pathname into the edit box.

There are a few special features in this box. If you use the & character in the file name box, PuTTY will insert details of the current session in the name of the file it actually opens. The precise replacements it will do are:

  • &Y will be replaced by the current year, as four digits.
  • &M will be replaced by the current month, as two digits.
  • &D will be replaced by the current day of the month, as two digits.
  • &T will be replaced by the current time, as six digits (HHMMSS) with no punctuation.
  • &H will be replaced by the host name you are connecting to.
  • &P will be replaced by the port number you are connecting to on the target host.

For example, if you enter the host name c:\puttylogs\log-&h-&y&m&d-&t.dat, you will end up with files looking like

log-server1.example.com-20010528-110859.dat
log-unixbox.somewhere.org-20010611-221001.dat

4.2.2 ‘What to do if the log file already exists’

This control allows you to specify what PuTTY should do if it tries to start writing to a log file and it finds the file already exists. You might want to automatically destroy the existing log file and start a new one with the same name. Alternatively, you might want to open the existing log file and add data to the end of it. Finally (the default option), you might not want to have any automatic behaviour, but to ask the user every time the problem comes up.

4.2.3 ‘Flush log file frequently’

This option allows you to control how frequently logged data is flushed to disc. By default, PuTTY will flush data as soon as it is displayed, so that if you view the log file while a session is still open, it will be up to date; and if the client system crashes, there's a greater chance that the data will be preserved.

However, this can incur a performance penalty. If PuTTY is running slowly with logging enabled, you could try unchecking this option. Be warned that the log file may not always be up to date as a result (although it will of course be flushed when it is closed, for instance at the end of a session).

4.2.4 ‘Include header’

This option allows you to choose whether to include a header line with the date and time when the log file is opened. It may be useful to disable this if the log file is being used as realtime input to other programs that don't expect the header line.

4.2.5 Options specific to SSH packet logging

These options only apply if SSH packet data is being logged.

The following options allow particularly sensitive portions of unencrypted packets to be automatically left out of the log file. They are only intended to deter casual nosiness; an attacker could glean a lot of useful information from even these obfuscated logs (e.g., length of password).

4.2.5.1 ‘Omit known password fields’

When checked, decrypted password fields are removed from the log of transmitted packets. (This includes any user responses to challenge-response authentication methods such as ‘keyboard-interactive’.) This does not include X11 authentication data if using X11 forwarding.

Note that this will only omit data that PuTTY knows to be a password. However, if you start another login session within your PuTTY session, for instance, any password used will appear in the clear in the packet log. The next option may be of use to protect against this.

This option is enabled by default.

4.2.5.2 ‘Omit session data’

When checked, all decrypted ‘session data’ is omitted; this is defined as data in terminal sessions and in forwarded channels (TCP, X11, and authentication agent). This will usually substantially reduce the size of the resulting log file.

This option is disabled by default.

4.3 The Terminal panel

The Terminal configuration panel allows you to control the behaviour of PuTTY's terminal emulation.

4.3.1 ‘Auto wrap mode initially on’

Auto wrap mode controls what happens when text printed in a PuTTY window reaches the right-hand edge of the window.

With auto wrap mode on, if a long line of text reaches the right-hand edge, it will wrap over on to the next line so you can still see all the text. With auto wrap mode off, the cursor will stay at the right-hand edge of the screen, and all the characters in the line will be printed on top of each other.

If you are running a full-screen application and you occasionally find the screen scrolling up when it looks as if it shouldn't, you could try turning this option off.

Auto wrap mode can be turned on and off by control sequences sent by the server. This configuration option controls the default state, which will be restored when you reset the terminal (see section 3.1.3.6). However, if you modify this option in mid-session using ‘Change Settings’, it will take effect immediately.

4.3.2 ‘DEC Origin Mode initially on’

DEC Origin Mode is a minor option which controls how PuTTY interprets cursor-position control sequences sent by the server.

The server can send a control sequence that restricts the scrolling region of the display. For example, in an editor, the server might reserve a line at the top of the screen and a line at the bottom, and might send a control sequence that causes scrolling operations to affect only the remaining lines.

With DEC Origin Mode on, cursor coordinates are counted from the top of the scrolling region. With it turned off, cursor coordinates are counted from the top of the whole screen regardless of the scrolling region.

It is unlikely you would need to change this option, but if you find a full-screen application is displaying pieces of text in what looks like the wrong part of the screen, you could try turning DEC Origin Mode on to see whether that helps.

DEC Origin Mode can be turned on and off by control sequences sent by the server. This configuration option controls the default state, which will be restored when you reset the terminal (see section 3.1.3.6). However, if you modify this option in mid-session using ‘Change Settings’, it will take effect immediately.

4.3.3 ‘Implicit CR in every LF’

Most servers send two control characters, CR and LF, to start a new line of the screen. The CR character makes the cursor return to the left-hand side of the screen. The LF character makes the cursor move one line down (and might make the screen scroll).

Some servers only send LF, and expect the terminal to move the cursor over to the left automatically. If you come across a server that does this, you will see a stepped effect on the screen, like this:

First line of text
                  Second line
                             Third line

If this happens to you, try enabling the ‘Implicit CR in every LF’ option, and things might go back to normal:

First line of text
Second line
Third line

4.3.4 ‘Implicit LF in every CR’

Most servers send two control characters, CR and LF, to start a new line of the screen. The CR character makes the cursor return to the left-hand side of the screen. The LF character makes the cursor move one line down (and might make the screen scroll).

Some servers only send CR, and so the newly written line is overwritten by the following line. This option causes a line feed so that all lines are displayed.

4.3.5 ‘Use background colour to erase screen’

Not all terminals agree on what colour to turn the screen when the server sends a ‘clear screen’ sequence. Some terminals believe the screen should always be cleared to the default background colour. Others believe the screen should be cleared to whatever the server has selected as a background colour.

There exist applications that expect both kinds of behaviour. Therefore, PuTTY can be configured to do either.

With this option disabled, screen clearing is always done in the default background colour. With this option enabled, it is done in the current background colour.

Background-colour erase can be turned on and off by control sequences sent by the server. This configuration option controls the default state, which will be restored when you reset the terminal (see section 3.1.3.6). However, if you modify this option in mid-session using ‘Change Settings’, it will take effect immediately.

4.3.6 ‘Enable blinking text’

The server can ask PuTTY to display text that blinks on and off. This is very distracting, so PuTTY allows you to turn blinking text off completely.

When blinking text is disabled and the server attempts to make some text blink, PuTTY will instead display the text with a bolded background colour.

Blinking text can be turned on and off by control sequences sent by the server. This configuration option controls the default state, which will be restored when you reset the terminal (see section 3.1.3.6). However, if you modify this option in mid-session using ‘Change Settings’, it will take effect immediately.

4.3.7 ‘Answerback to ^E’

This option controls what PuTTY will send back to the server if the server sends it the ^E enquiry character. Normally it just sends the string ‘PuTTY’.

If you accidentally write the contents of a binary file to your terminal, you will probably find that it contains more than one ^E character, and as a result your next command line will probably read ‘PuTTYPuTTYPuTTY...’ as if you had typed the answerback string multiple times at the keyboard. If you set the answerback string to be empty, this problem should go away, but doing so might cause other problems.

Note that this is not the feature of PuTTY which the server will typically use to determine your terminal type. That feature is the ‘Terminal-type string’ in the Connection panel; see section 4.15.3 for details.

You can include control characters in the answerback string using ^C notation. (Use ^~ to get a literal ^.)

4.3.8 ‘Local echo’

With local echo disabled, characters you type into the PuTTY window are not echoed in the window by PuTTY. They are simply sent to the server. (The server might choose to echo them back to you; this can't be controlled from the PuTTY control panel.)

Some types of session need local echo, and many do not. In its default mode, PuTTY will automatically attempt to deduce whether or not local echo is appropriate for the session you are working in. If you find it has made the wrong decision, you can use this configuration option to override its choice: you can force local echo to be turned on, or force it to be turned off, instead of relying on the automatic detection.

4.3.9 ‘Local line editing’

Normally, every character you type into the PuTTY window is sent immediately to the server the moment you type it.

If you enable local line editing, this changes. PuTTY will let you edit a whole line at a time locally, and the line will only be sent to the server when you press Return. If you make a mistake, you can use the Backspace key to correct it before you press Return, and the server will never see the mistake.

Since it is hard to edit a line locally without being able to see it, local line editing is mostly used in conjunction with local echo (section 4.3.8). This makes it ideal for use in raw mode or when connecting to MUDs or talkers. (Although some more advanced MUDs do occasionally turn local line editing on and turn local echo off, in order to accept a password from the user.)

Some types of session need local line editing, and many do not. In its default mode, PuTTY will automatically attempt to deduce whether or not local line editing is appropriate for the session you are working in. If you find it has made the wrong decision, you can use this configuration option to override its choice: you can force local line editing to be turned on, or force it to be turned off, instead of relying on the automatic detection.

4.3.10 Remote-controlled printing

A lot of VT100-compatible terminals support printing under control of the remote server (sometimes called ‘passthrough printing’). PuTTY supports this feature as well, but it is turned off by default.

To enable remote-controlled printing, choose a printer from the ‘Printer to send ANSI printer output to’ drop-down list box. This should allow you to select from all the printers you have installed drivers for on your computer. Alternatively, you can type the network name of a networked printer (for example, \\printserver\printer1) even if you haven't already installed a driver for it on your own machine.

When the remote server attempts to print some data, PuTTY will send that data to the printer raw - without translating it, attempting to format it, or doing anything else to it. It is up to you to ensure your remote server knows what type of printer it is talking to.

Since PuTTY sends data to the printer raw, it cannot offer options such as portrait versus landscape, print quality, or paper tray selection. All these things would be done by your PC printer driver (which PuTTY bypasses); if you need them done, you will have to find a way to configure your remote server to do them.

To disable remote printing again, choose ‘None (printing disabled)’ from the printer selection list. This is the default state.

4.4 The Keyboard panel

The Keyboard configuration panel allows you to control the behaviour of the keyboard in PuTTY. The correct state for many of these settings depends on what the server to which PuTTY is connecting expects. With a Unix server, this is likely to depend on the termcap or terminfo entry it uses, which in turn is likely to be controlled by the ‘Terminal-type string’ setting in the Connection panel; see section 4.15.3 for details. If none of the settings here seems to help, you may find question A.7.13 to be useful.

4.4.1 Changing the action of the Backspace key

Some terminals believe that the Backspace key should send the same thing to the server as Control-H (ASCII code 8). Other terminals believe that the Backspace key should send ASCII code 127 (usually known as Control-?) so that it can be distinguished from Control-H. This option allows you to choose which code PuTTY generates when you press Backspace.

If you are connecting over SSH, PuTTY by default tells the server the value of this option (see section 4.23.2), so you may find that the Backspace key does the right thing either way. Similarly, if you are connecting to a Unix system, you will probably find that the Unix stty command lets you configure which the server expects to see, so again you might not need to change which one PuTTY generates. On other systems, the server's expectation might be fixed and you might have no choice but to configure PuTTY.

If you do have the choice, we recommend configuring PuTTY to generate Control-? and configuring the server to expect it, because that allows applications such as emacs to use Control-H for help.

(Typing Shift-Backspace will cause PuTTY to send whichever code isn't configured here as the default.)

4.4.2 Changing the action of the Home and End keys

The Unix terminal emulator rxvt disagrees with the rest of the world about what character sequences should be sent to the server by the Home and End keys.

xterm, and other terminals, send ESC [1~ for the Home key, and ESC [4~ for the End key. rxvt sends ESC [H for the Home key and ESC [Ow for the End key.

If you find an application on which the Home and End keys aren't working, you could try switching this option to see if it helps.

4.4.3 Changing the action of the function keys and keypad

This option affects the function keys (F1 to F12) and the top row of the numeric keypad.

  • In the default mode, labelled ESC [n~, the function keys generate sequences like ESC [11~, ESC [12~ and so on. This matches the general behaviour of Digital's terminals.
  • In Linux mode, F6 to F12 behave just like the default mode, but F1 to F5 generate ESC [[A through to ESC [[E. This mimics the Linux virtual console.
  • In Xterm R6 mode, F5 to F12 behave like the default mode, but F1 to F4 generate ESC OP through to ESC OS, which are the sequences produced by the top row of the keypad on Digital's terminals.
  • In VT400 mode, all the function keys behave like the default mode, but the actual top row of the numeric keypad generates ESC OP through to ESC OS.
  • In VT100+ mode, the function keys generate ESC OP through to ESC O[
  • In SCO mode, the function keys F1 to F12 generate ESC [M through to ESC [X. Together with shift, they generate ESC [Y through to ESC [j. With control they generate ESC [k through to ESC [v, and with shift and control together they generate ESC [w through to ESC [{.

If you don't know what any of this means, you probably don't need to fiddle with it.

4.4.4 Controlling Application Cursor Keys mode

Application Cursor Keys mode is a way for the server to change the control sequences sent by the arrow keys. In normal mode, the arrow keys send ESC [A through to ESC [D. In application mode, they send ESC OA through to ESC OD.

Application Cursor Keys mode can be turned on and off by the server, depending on the application. PuTTY allows you to configure the initial state.

You can also disable application cursor keys mode completely, using the ‘Features’ configuration panel; see section 4.6.1.

4.4.5 Controlling Application Keypad mode

Application Keypad mode is a way for the server to change the behaviour of the numeric keypad.

In normal mode, the keypad behaves like a normal Windows keypad: with NumLock on, the number keys generate numbers, and with NumLock off they act like the arrow keys and Home, End etc.

In application mode, all the keypad keys send special control sequences, including Num Lock. Num Lock stops behaving like Num Lock and becomes another function key.

Depending on which version of Windows you run, you may find the Num Lock light still flashes on and off every time you press Num Lock, even when application mode is active and Num Lock is acting like a function key. This is unavoidable.

Application keypad mode can be turned on and off by the server, depending on the application. PuTTY allows you to configure the initial state.

You can also disable application keypad mode completely, using the ‘Features’ configuration panel; see section 4.6.1.

4.4.6 Using NetHack keypad mode

PuTTY has a special mode for playing NetHack. You can enable it by selecting ‘NetHack’ in the ‘Initial state of numeric keypad’ control.

In this mode, the numeric keypad keys 1-9 generate the NetHack movement commands (hjklyubn). The 5 key generates the . command (do nothing).

In addition, pressing Shift or Ctrl with the keypad keys generate the Shift- or Ctrl-keys you would expect (e.g. keypad-7 generates ‘y’, so Shift-keypad-7 generates ‘Y’ and Ctrl-keypad-7 generates Ctrl-Y); these commands tell NetHack to keep moving you in the same direction until you encounter something interesting.

For some reason, this feature only works properly when Num Lock is on. We don't know why.

4.4.7 Enabling a DEC-like Compose key

DEC terminals have a Compose key, which provides an easy-to-remember way of typing accented characters. You press Compose and then type two more characters. The two characters are ‘combined’ to produce an accented character. The choices of character are designed to be easy to remember; for example, composing ‘e’ and ‘`’ produces the ‘è’ character.

If your keyboard has a Windows Application key, it acts as a Compose key in PuTTY. Alternatively, if you enable the ‘AltGr acts as Compose key’ option, the AltGr key will become a Compose key.

4.4.8 ‘Control-Alt is different from AltGr’

Some old keyboards do not have an AltGr key, which can make it difficult to type some characters. PuTTY can be configured to treat the key combination Ctrl + Left Alt the same way as the AltGr key.

By default, this checkbox is checked, and the key combination Ctrl + Left Alt does something completely different. PuTTY's usual handling of the left Alt key is to prefix the Escape (Control-[) character to whatever character sequence the rest of the keypress would generate. For example, Alt-A generates Escape followed by a. So Alt-Ctrl-A would generate Escape, followed by Control-A.

If you uncheck this box, Ctrl-Alt will become a synonym for AltGr, so you can use it to type extra graphic characters if your keyboard has any.

(However, Ctrl-Alt will never act as a Compose key, regardless of the setting of ‘AltGr acts as Compose key’ described in section 4.4.7.)

4.5 The Bell panel

The Bell panel controls the terminal bell feature: the server's ability to cause PuTTY to beep at you.

In the default configuration, when the server sends the character with ASCII code 7 (Control-G), PuTTY will play the Windows Default Beep sound. This is not always what you want the terminal bell feature to do; the Bell panel allows you to configure alternative actions.

4.5.1 ‘Set the style of bell’

This control allows you to select various different actions to occur on a terminal bell:

  • Selecting ‘None’ disables the bell completely. In this mode, the server can send as many Control-G characters as it likes and nothing at all will happen.
  • ‘Make default system alert sound’ is the default setting. It causes the Windows ‘Default Beep’ sound to be played. To change what this sound is, or to test it if nothing seems to be happening, use the Sound configurer in the Windows Control Panel.
  • Visual bell’ is a silent alternative to a beeping computer. In this mode, when the server sends a Control-G, the whole PuTTY window will flash white for a fraction of a second.
  • ‘Beep using the PC speaker’ is self-explanatory.
  • ‘Play a custom sound file’ allows you to specify a particular sound file to be used by PuTTY alone, or even by a particular individual PuTTY session. This allows you to distinguish your PuTTY beeps from any other beeps on the system. If you select this option, you will also need to enter the name of your sound file in the edit control ‘Custom sound file to play as a bell’.

4.5.2 ‘Taskbar/caption indication on bell’

This feature controls what happens to the PuTTY window's entry in the Windows Taskbar if a bell occurs while the window does not have the input focus.

In the default state (‘Disabled’) nothing unusual happens.

If you select ‘Steady’, then when a bell occurs and the window is not in focus, the window's Taskbar entry and its title bar will change colour to let you know that PuTTY session is asking for your attention. The change of colour will persist until you select the window, so you can leave several PuTTY windows minimised in your terminal, go away from your keyboard, and be sure not to have missed any important beeps when you get back.

‘Flashing’ is even more eye-catching: the Taskbar entry will continuously flash on and off until you select the window.

4.5.3 ‘Control the bell overload behaviour’

A common user error in a terminal session is to accidentally run the Unix command cat (or equivalent) on an inappropriate file type, such as an executable, image file, or ZIP file. This produces a huge stream of non-text characters sent to the terminal, which typically includes a lot of bell characters. As a result of this the terminal often doesn't stop beeping for ten minutes, and everybody else in the office gets annoyed.

To try to avoid this behaviour, or any other cause of excessive beeping, PuTTY includes a bell overload management feature. In the default configuration, receiving more than five bell characters in a two-second period will cause the overload feature to activate. Once the overload feature is active, further bells will have no effect at all, so the rest of your binary file will be sent to the screen in silence. After a period of five seconds during which no further bells are received, the overload feature will turn itself off again and bells will be re-enabled.

If you want this feature completely disabled, you can turn it off using the checkbox ‘Bell is temporarily disabled when over-used’.

Alternatively, if you like the bell overload feature but don't agree with the settings, you can configure the details: how many bells constitute an overload, how short a time period they have to arrive in to do so, and how much silent time is required before the overload feature will deactivate itself.

Bell overload mode is always deactivated by any keypress in the terminal. This means it can respond to large unexpected streams of data, but does not interfere with ordinary command-line activities that generate beeps (such as filename completion).

4.6 The Features panel

PuTTY's terminal emulation is very highly featured, and can do a lot of things under remote server control. Some of these features can cause problems due to buggy or strangely configured server applications.

The Features configuration panel allows you to disable some of PuTTY's more advanced terminal features, in case they cause trouble.

4.6.1 Disabling application keypad and cursor keys

Application keypad mode (see section 4.4.5) and application cursor keys mode (see section 4.4.4) alter the behaviour of the keypad and cursor keys. Some applications enable these modes but then do not deal correctly with the modified keys. You can force these modes to be permanently disabled no matter what the server tries to do.

4.6.2 Disabling xterm-style mouse reporting

PuTTY allows the server to send control codes that let it take over the mouse and use it for purposes other than copy and paste. Applications which use this feature include the text-mode web browser links, the Usenet newsreader trn version 4, and the file manager mc (Midnight Commander).

If you find this feature inconvenient, you can disable it using the ‘Disable xterm-style mouse reporting’ control. With this box ticked, the mouse will always do copy and paste in the normal way.

Note that even if the application takes over the mouse, you can still manage PuTTY's copy and paste by holding down the Shift key while you select and paste, unless you have deliberately turned this feature off (see section 4.11.2).

4.6.3 Disabling remote terminal resizing

PuTTY has the ability to change the terminal's size and position in response to commands from the server. If you find PuTTY is doing this unexpectedly or inconveniently, you can tell PuTTY not to respond to those server commands.

4.6.4 Disabling switching to the alternate screen

Many terminals, including PuTTY, support an ‘alternate screen’. This is the same size as the ordinary terminal screen, but separate. Typically a screen-based program such as a text editor might switch the terminal to the alternate screen before starting up. Then at the end of the run, it switches back to the primary screen, and you see the screen contents just as they were before starting the editor.

Some people prefer this not to happen. If you want your editor to run in the same screen as the rest of your terminal activity, you can disable the alternate screen feature completely.

4.6.5 Disabling remote window title changing

PuTTY has the ability to change the window title in response to commands from the server. If you find PuTTY is doing this unexpectedly or inconveniently, you can tell PuTTY not to respond to those server commands.

4.6.6 Response to remote window title querying

PuTTY can optionally provide the xterm service of allowing server applications to find out the local window title. This feature is disabled by default, but you can turn it on if you really want it.

NOTE that this feature is a potential security hazard. If a malicious application can write data to your terminal (for example, if you merely cat a file owned by someone else on the server machine), it can change your window title (unless you have disabled this as mentioned in section 4.6.5) and then use this service to have the new window title sent back to the server as if typed at the keyboard. This allows an attacker to fake keypresses and potentially cause your server-side applications to do things you didn't want. Therefore this feature is disabled by default, and we recommend you do not set it to ‘Window title’ unless you really know what you are doing.

There are three settings for this option:

‘None’
PuTTY makes no response whatsoever to the relevant escape sequence. This may upset server-side software that is expecting some sort of response.
‘Empty string’
PuTTY makes a well-formed response, but leaves it blank. Thus, server-side software that expects a response is kept happy, but an attacker cannot influence the response string. This is probably the setting you want if you have no better ideas.
‘Window title’
PuTTY responds with the actual window title. This is dangerous for the reasons described above.

4.6.7 Disabling remote scrollback clearing

PuTTY has the ability to clear the terminal's scrollback buffer in response to a command from the server. If you find PuTTY is doing this unexpectedly or inconveniently, you can tell PuTTY not to respond to that server command.

4.6.8 Disabling destructive backspace

Normally, when PuTTY receives character 127 (^?) from the server, it will perform a ‘destructive backspace’: move the cursor one space left and delete the character under it. This can apparently cause problems in some applications, so PuTTY provides the ability to configure character 127 to perform a normal backspace (without deleting a character) instead.

4.6.9 Disabling remote character set configuration

PuTTY has the ability to change its character set configuration in response to commands from the server. Some programs send these commands unexpectedly or inconveniently. In particular, BitchX (an IRC client) seems to have a habit of reconfiguring the character set to something other than the user intended.

If you find that accented characters are not showing up the way you expect them to, particularly if you're running BitchX, you could try disabling the remote character set configuration commands.

4.6.10 Disabling Arabic text shaping

PuTTY supports shaping of Arabic text, which means that if your server sends text written in the basic Unicode Arabic alphabet then it will convert it to the correct display forms before printing it on the screen.

If you are using full-screen software which was not expecting this to happen (especially if you are not an Arabic speaker and you unexpectedly find yourself dealing with Arabic text files in applications which are not Arabic-aware), you might find that the display becomes corrupted. By ticking this box, you can disable Arabic text shaping so that PuTTY displays precisely the characters it is told to display.

You may also find you need to disable bidirectional text display; see section 4.6.11.

4.6.11 Disabling bidirectional text display

PuTTY supports bidirectional text display, which means that if your server sends text written in a language which is usually displayed from right to left (such as Arabic or Hebrew) then PuTTY will automatically flip it round so that it is displayed in the right direction on the screen.

If you are using full-screen software which was not expecting this to happen (especially if you are not an Arabic speaker and you unexpectedly find yourself dealing with Arabic text files in applications which are not Arabic-aware), you might find that the display becomes corrupted. By ticking this box, you can disable bidirectional text display, so that PuTTY displays text from left to right in all situations.

You may also find you need to disable Arabic text shaping; see section 4.6.10.

4.7 The Window panel

The Window configuration panel allows you to control aspects of the PuTTY window.

4.7.1 Setting the size of the PuTTY window

The ‘Columns’ and ‘Rows’ boxes let you set the PuTTY window to a precise size. Of course you can also drag the window to a new size while a session is running.

4.7.2 What to do when the window is resized

These options allow you to control what happens when the user tries to resize the PuTTY window using its window furniture.

There are four options here:

  • ‘Change the number of rows and columns’: the font size will not change. (This is the default.)
  • ‘Change the size of the font’: the number of rows and columns in the terminal will stay the same, and the font size will change.
  • ‘Change font size when maximised’: when the window is resized, the number of rows and columns will change, except when the window is maximised (or restored), when the font size will change. (In this mode, holding down the Alt key while resizing will also cause the font size to change.)
  • ‘Forbid resizing completely’: the terminal will refuse to be resized at all.

4.7.3 Controlling scrollback

These options let you configure the way PuTTY keeps text after it scrolls off the top of the screen (see section 3.1.2).

The ‘Lines of scrollback’ box lets you configure how many lines of text PuTTY keeps. The ‘Display scrollbar’ options allow you to hide the scrollbar (although you can still view the scrollback using the keyboard as described in section 3.1.2). You can separately configure whether the scrollbar is shown in full-screen mode and in normal modes.

If you are viewing part of the scrollback when the server sends more text to PuTTY, the screen will revert to showing the current terminal contents. You can disable this behaviour by turning off ‘Reset scrollback on display activity’. You can also make the screen revert when you press a key, by turning on ‘Reset scrollback on keypress’.

4.7.4 ‘Push erased text into scrollback’

When this option is enabled, the contents of the terminal screen will be pushed into the scrollback when a server-side application clears the screen, so that your scrollback will contain a better record of what was on your screen in the past.

If the application switches to the alternate screen (see section 4.6.4 for more about this), then the contents of the primary screen will be visible in the scrollback until the application switches back again.

This option is enabled by default.

4.8 The Appearance panel

The Appearance configuration panel allows you to control aspects of the appearance of PuTTY's window.

4.8.1 Controlling the appearance of the cursor

The ‘Cursor appearance’ option lets you configure the cursor to be a block, an underline, or a vertical line. A block cursor becomes an empty box when the window loses focus; an underline or a vertical line becomes dotted.

The ‘Cursor blinks’ option makes the cursor blink on and off. This works in any of the cursor modes.

4.8.2 Controlling the font used in the terminal window

This option allows you to choose what font, in what size, the PuTTY terminal window uses to display the text in the session.

By default, you will be offered a choice from all the fixed-width fonts installed on the system, since VT100-style terminal handling expects a fixed-width font. If you tick the box marked ‘Allow selection of variable-pitch fonts’, however, PuTTY will offer variable-width fonts as well: if you select one of these, the font will be coerced into fixed-size character cells, which will probably not look very good (but can work OK with some fonts).

4.8.3 ‘Hide mouse pointer when typing in window’

If you enable this option, the mouse pointer will disappear if the PuTTY window is selected and you press a key. This way, it will not obscure any of the text in the window while you work in your session. As soon as you move the mouse, the pointer will reappear.

This option is disabled by default, so the mouse pointer remains visible at all times.

4.8.4 Controlling the window border

PuTTY allows you to configure the appearance of the window border to some extent.

The checkbox marked ‘Sunken-edge border’ changes the appearance of the window border to something more like a DOS box: the inside edge of the border is highlighted as if it sank down to meet the surface inside the window. This makes the border a little bit thicker as well. It's hard to describe well. Try it and see if you like it.

You can also configure a completely blank gap between the text in the window and the border, using the ‘Gap between text and window edge’ control. By default this is set at one pixel. You can reduce it to zero, or increase it further.

4.9 The Behaviour panel

The Behaviour configuration panel allows you to control aspects of the behaviour of PuTTY's window.

4.9.1 Controlling the window title

The ‘Window title’ edit box allows you to set the title of the PuTTY window. By default the window title will contain the host name followed by ‘PuTTY’, for example server1.example.com - PuTTY. If you want a different window title, this is where to set it.

PuTTY allows the server to send xterm control sequences which modify the title of the window in mid-session (unless this is disabled - see section 4.6.5); the title string set here is therefore only the initial window title.

As well as the window title, there is also an xterm sequence to modify the title of the window's icon. This makes sense in a windowing system where the window becomes an icon when minimised, such as Windows 3.1 or most X Window System setups; but in the Windows 95-like user interface it isn't as applicable.

By default, PuTTY only uses the server-supplied window title, and ignores the icon title entirely. If for some reason you want to see both titles, check the box marked ‘Separate window and icon titles’. If you do this, PuTTY's window title and Taskbar caption will change into the server-supplied icon title if you minimise the PuTTY window, and change back to the server-supplied window title if you restore it. (If the server has not bothered to supply a window or icon title, none of this will happen.)

4.9.2 ‘Warn before closing window’

If you press the Close button in a PuTTY window that contains a running session, PuTTY will put up a warning window asking if you really meant to close the window. A window whose session has already terminated can always be closed without a warning.

If you want to be able to close a window quickly, you can disable the ‘Warn before closing window’ option.

4.9.3 ‘Window closes on ALT-F4’

By default, pressing ALT-F4 causes the window to close (or a warning box to appear; see section 4.9.2). If you disable the ‘Window closes on ALT-F4’ option, then pressing ALT-F4 will simply send a key sequence to the server.

4.9.4 ‘System menu appears on ALT-Space’

If this option is enabled, then pressing ALT-Space will bring up the PuTTY window's menu, like clicking on the top left corner. If it is disabled, then pressing ALT-Space will just send ESC SPACE to the server.

Some accessibility programs for Windows may need this option enabling to be able to control PuTTY's window successfully. For instance, Dragon NaturallySpeaking requires it both to open the system menu via voice, and to close, minimise, maximise and restore the window.

4.9.5 ‘System menu appears on Alt alone’

If this option is enabled, then pressing and releasing ALT will bring up the PuTTY window's menu, like clicking on the top left corner. If it is disabled, then pressing and releasing ALT will have no effect.

4.9.6 ‘Ensure window is always on top’

If this option is enabled, the PuTTY window will stay on top of all other windows.

4.9.7 ‘Full screen on Alt-Enter’

If this option is enabled, then pressing Alt-Enter will cause the PuTTY window to become full-screen. Pressing Alt-Enter again will restore the previous window size.

The full-screen feature is also available from the System menu, even when it is configured not to be available on the Alt-Enter key. See section 3.1.3.7.

4.10 The Translation panel

The Translation configuration panel allows you to control the translation between the character set understood by the server and the character set understood by PuTTY.

4.10.1 Controlling character set translation

During an interactive session, PuTTY receives a stream of 8-bit bytes from the server, and in order to display them on the screen it 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 ‘Remote character set’ option lets you select one.

By default PuTTY will use the UTF-8 encoding of 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:

  • The ISO-8859 series are all standard character sets that include various accented characters appropriate for different sets of languages.
  • The 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.
  • If you want the old IBM PC character set with block graphics and line-drawing characters, you can select ‘CP437’.

If you need support for a numeric code page which is not listed in the drop-down list, such as code page 866, then you can try entering its name manually (CP866 for example) in the list box. If the underlying version of Windows has the appropriate translation table installed, PuTTY will use it.

4.10.2 ‘Treat CJK ambiguous characters as wide’

There are some Unicode characters whose width is not well-defined. In most contexts, such characters should be treated as single-width for the purposes of wrapping and so on; however, in some CJK contexts, they are better treated as double-width for historical reasons, and some server-side applications may expect them to be displayed as such. Setting this option will cause PuTTY to take the double-width interpretation.

If you use legacy CJK applications, and you find your lines are wrapping in the wrong places, or you are having other display problems, you might want to play with this setting.

This option only has any effect in UTF-8 mode (see section 4.10.1).

4.10.3 ‘Caps Lock acts as Cyrillic switch’

This feature allows you to switch between a US/UK keyboard layout and a Cyrillic keyboard layout by using the Caps Lock key, if you need to type (for example) Russian and English side by side in the same document.

Currently this feature is not expected to work properly if your native keyboard layout is not US or UK.

4.10.4 Controlling display of line-drawing characters

VT100-series terminals allow the server to send control sequences that shift temporarily into a separate character set for drawing simple lines and boxes. However, there are a variety of ways in which PuTTY can attempt to find appropriate characters, and the right one to use depends on the locally configured font. In general you should probably try lots of options until you find one that your particular font supports.

  • ‘Use Unicode line drawing code points’ tries to use the box characters that are present in Unicode. For good Unicode-supporting fonts this is probably the most reliable and functional option.
  • ‘Poor man's line drawing’ assumes that the font cannot generate the line and box characters at all, so it will use the +, - and | characters to draw approximations to boxes. You should use this option if none of the other options works.
  • ‘Font has XWindows encoding’ is for use with fonts that have a special encoding, where the lowest 32 character positions (below the ASCII printable range) contain the line-drawing characters. This is unlikely to be the case with any standard Windows font; it will probably only apply to custom-built fonts or fonts that have been automatically converted from the X Window System.
  • ‘Use font in both ANSI and OEM modes’ tries to use the same font in two different character sets, to obtain a wider range of characters. This doesn't always work; some fonts claim to be a different size depending on which character set you try to use.
  • ‘Use font in OEM mode only’ is more reliable than that, but can miss out other characters from the main character set.

4.10.5 Controlling copy and paste of line drawing characters

By default, when you copy and paste a piece of the PuTTY screen that contains VT100 line and box drawing characters, PuTTY will paste them in the form they appear on the screen: either Unicode line drawing code points, or the ‘poor man's’ line-drawing characters +, - and |. The checkbox ‘Copy and paste VT100 line drawing chars as lqqqk’ disables this feature, so line-drawing characters will be pasted as the ASCII characters that were printed to produce them. This will typically mean they come out mostly as q and x, with a scattering of jklmntuvw at the corners. This might be useful if you were trying to recreate the same box layout in another program, for example.

Note that this option only applies to line-drawing characters which were printed by using the VT100 mechanism. Line-drawing characters that were received as Unicode code points will paste as Unicode always.

4.10.6 Combining VT100 line-drawing with UTF-8

If PuTTY is configured to treat data from the server as encoded in UTF-8, then by default it disables the older VT100-style system of control sequences that cause the lower-case letters to be temporarily replaced by line drawing characters.

The rationale is that in UTF-8 mode you don't need those control sequences anyway, because all the line-drawing characters they access are available as Unicode characters already, so there's no need for applications to put the terminal into a special state to get at them.

Also, it removes a risk of the terminal accidentally getting into that state: if you accidentally write uncontrolled binary data to a non-UTF-8 terminal, it can be surprisingly common to find that your next shell prompt appears as a sequence of line-drawing characters and then you have to remember or look up how to get out of that mode. So by default, UTF-8 mode simply doesn't have a confusing mode like that to get into, accidentally or on purpose.

However, not all applications will see it that way. Even UTF-8 terminal users will still sometimes have to run software that tries to print line-drawing characters in the old-fashioned way. So the configuration option ‘Enable VT100 line drawing even in UTF-8 mode’ puts PuTTY into a hybrid mode in which it understands the VT100-style control sequences that change the meaning of the ASCII lower case letters, and understands UTF-8.

4.11 The Selection panel

The Selection panel allows you to control the way copy and paste work in the PuTTY window.

4.11.1 Changing the actions of the mouse buttons

PuTTY's copy and paste mechanism is by default modelled on the Unix xterm application. The X Window System uses a three-button mouse, and the convention in that system is that the left button selects, the right button extends an existing selection, and the middle button pastes.

Windows often only has two mouse buttons, so when run on Windows, PuTTY is configurable. In PuTTY's default configuration (‘Compromise’), the right button pastes, and the middle button (if you have one) extends a selection.

If you have a three-button mouse and you are already used to the xterm arrangement, you can select it using the ‘Action of mouse buttons’ control.

Alternatively, with the ‘Windows’ option selected, the middle button extends, and the right button brings up a context menu (on which one of the options is ‘Paste’). (This context menu is always available by holding down Ctrl and right-clicking, regardless of the setting of this option.)

(When PuTTY iself is running on Unix, it follows the X Window System convention.)

4.11.2 ‘Shift overrides application's use of mouse’

PuTTY allows the server to send control codes that let it take over the mouse and use it for purposes other than copy and paste. Applications which use this feature include the text-mode web browser links, the Usenet newsreader trn version 4, and the file manager mc (Midnight Commander).

When running one of these applications, pressing the mouse buttons no longer performs copy and paste. If you do need to copy and paste, you can still do so if you hold down Shift while you do your mouse clicks.

However, it is possible in theory for applications to even detect and make use of Shift + mouse clicks. We don't know of any applications that do this, but in case someone ever writes one, unchecking the ‘Shift overrides application's use of mouse’ checkbox will cause Shift + mouse clicks to go to the server as well (so that mouse-driven copy and paste will be completely disabled).

If you want to prevent the application from taking over the mouse at all, you can do this using the Features control panel; see section 4.6.2.

4.11.3 Default selection mode

As described in section 3.1.1, PuTTY has two modes of selecting text to be copied to the clipboard. In the default mode (‘Normal’), dragging the mouse from point A to point B selects to the end of the line containing A, all the lines in between, and from the very beginning of the line containing B. In the other mode (‘Rectangular block’), dragging the mouse between two points defines a rectangle, and everything within that rectangle is copied.

Normally, you have to hold down Alt while dragging the mouse to select a rectangular block. Using the ‘Default selection mode’ control, you can set rectangular selection as the default, and then you have to hold down Alt to get the normal behaviour.

4.11.4 Assigning copy and paste actions to clipboards

Here you can configure which clipboard(s) are written or read by PuTTY's various copy and paste actions.

Most platforms, including Windows, have a single system clipboard. On these platforms, PuTTY provides a second clipboard-like facility by permitting you to paste the text you last selected in this window, whether or not it is currently also in the system clipboard. This is not enabled by default.

The X Window System (which underlies most Unix graphical interfaces) provides multiple clipboards (or ‘selections’), and many applications support more than one of them by a different user interface mechanism. When PuTTY itself is running on Unix, it has more configurability relating to these selections.

The two most commonly used selections are called ‘PRIMARY’ and ‘CLIPBOARD’; in applications supporting both, the usual behaviour is that PRIMARY is used by mouse-only actions (selecting text automatically copies it to PRIMARY, and middle-clicking pastes from PRIMARY), whereas CLIPBOARD is used by explicit Copy and Paste menu items or keypresses such as Ctrl-C and Ctrl-V.

4.11.4.1 ‘Auto-copy selected text’

The checkbox ‘Auto-copy selected text to system clipboard’ controls whether or not selecting text in the PuTTY terminal window automatically has the side effect of copying it to the system clipboard, without requiring a separate user interface action.

On X, the wording of this option is changed slightly so that ‘CLIPBOARD’ is mentioned in place of the ‘system clipboard’. Text selected in the terminal window will always be automatically placed in the PRIMARY selection, as is conventional, but if you tick this box, it will also be placed in ‘CLIPBOARD’ at the same time.

4.11.4.2 Choosing a clipboard for UI actions

PuTTY has three user-interface actions which can be configured to paste into the terminal (not counting menu items). You can click whichever mouse button (if any) is configured to paste (see section 4.11.1); you can press Shift-Ins; or you can press Ctrl-Shift-V, although that action is not enabled by default.

You can configure which of the available clipboards each of these actions pastes from (including turning the paste action off completely). On platforms with a single system clipboard (such as Windows), the available options are to paste from that clipboard or to paste from PuTTY's internal memory of the last selected text within that window. On X, the standard options are CLIPBOARD or PRIMARY.

(PRIMARY is conceptually similar in that it also refers to the last selected text – just across all applications instead of just this window.)

The two keyboard options each come with a corresponding key to copy to the same clipboard. Whatever you configure Shift-Ins to paste from, Ctrl-Ins will copy to the same location; similarly, Ctrl-Shift-C will copy to whatever Ctrl-Shift-V pastes from.

On X, you can also enter a selection name of your choice. For example, there is a rarely-used standard selection called ‘SECONDARY’, which Emacs (for example) can work with if you hold down the Meta key while dragging to select or clicking to paste; if you configure a PuTTY keyboard action to access this clipboard, then you can interoperate with other applications' use of it. Another thing you could do would be to invent a clipboard name yourself, to create a special clipboard shared only between instances of PuTTY, or between just instances configured in that particular way.

4.11.5 ‘Permit control characters in pasted text’

It is possible for the clipboard to contain not just text (with newlines and tabs) but also control characters such as ESC which could have surprising effects if pasted into a terminal session, depending on what program is running on the server side. Copying text from a mischievous web page could put such characters onto the clipboard.

By default, PuTTY filters out the more unusual control characters, only letting through the more obvious text-formatting characters (newlines, tab, backspace, and DEL).

Setting this option stops this filtering; on paste, any character on the clipboard is sent to the session uncensored. This might be useful if you are deliberately using control character pasting as a simple form of scripting, for instance.

4.12 The Copy panel

The Copy configuration panel controls behaviour specifically related to copying from the terminal window to the clipboard.

4.12.1 Character classes

PuTTY will select a word at a time in the terminal window if you double-click to begin the drag. This section allows you to control precisely what is considered to be a word.

Each character is given a class, which is a small number (typically 0, 1 or 2). PuTTY considers a single word to be any number of adjacent characters in the same class. So by modifying the assignment of characters to classes, you can modify the word-by-word selection behaviour.

In the default configuration, the character classes are:

  • Class 0 contains white space and control characters.
  • Class 1 contains most punctuation.
  • Class 2 contains letters, numbers and a few pieces of punctuation (the double quote, minus sign, period, forward slash and underscore).

So, for example, if you assign the @ symbol into character class 2, you will be able to select an e-mail address with just a double click.

In order to adjust these assignments, you start by selecting a group of characters in the list box. Then enter a class number in the edit box below, and press the ‘Set’ button.

This mechanism currently only covers ASCII characters, because it isn't feasible to expand the list to cover the whole of Unicode.

Character class definitions can be modified by control sequences sent by the server. This configuration option controls the default state, which will be restored when you reset the terminal (see section 3.1.3.6). However, if you modify this option in mid-session using ‘Change Settings’, it will take effect immediately.

4.12.2 Copying in Rich Text Format

If you enable ‘Copy to clipboard in RTF as well as plain text’, PuTTY will write formatting information to the clipboard as well as the actual text you copy. The effect of this is that if you paste into (say) a word processor, the text will appear in the word processor in the same font, colour, and style (e.g. bold, underline) PuTTY was using to display it.

This option can easily be inconvenient, so by default it is disabled.

4.13 The Colours panel

The Colours panel allows you to control PuTTY's use of colour.

4.13.1 ‘Allow terminal to specify ANSI colours’

This option is enabled by default. If it is disabled, PuTTY will ignore any control sequences sent by the server to request coloured text.

If you have a particularly garish application, you might want to turn this option off and make PuTTY only use the default foreground and background colours.

4.13.2 ‘Allow terminal to use xterm 256-colour mode’

This option is enabled by default. If it is disabled, PuTTY will ignore any control sequences sent by the server which use the extended 256-colour mode supported by recent versions of xterm.

If you have an application which is supposed to use 256-colour mode and it isn't working, you may find you need to tell your server that your terminal supports 256 colours. On Unix, you do this by ensuring that the setting of TERM describes a 256-colour-capable terminal. You can check this using a command such as infocmp:

$ infocmp | grep colors
        colors#256, cols#80, it#8, lines#24, pairs#256,

If you do not see ‘colors#256’ in the output, you may need to change your terminal setting. On modern Linux machines, you could try ‘xterm-256color’.

4.13.3 ‘Allow terminal to use 24-bit colour’

This option is enabled by default. If it is disabled, PuTTY will ignore any control sequences sent by the server which use the control sequences supported by modern terminals to specify arbitrary 24-bit RGB colour value.

4.13.4 ‘Indicate bolded text by changing...’

When the server sends a control sequence indicating that some text should be displayed in bold, PuTTY can handle this in several ways. It can either change the font for a bold version, or use the same font in a brighter colour, or it can do both (brighten the colour 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 ‘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 ‘Both’, the font and the colour will both change.

Some applications rely on ‘bold black’ being distinguishable from a black background; if you choose ‘The font’, their text may become invisible.

4.13.5 ‘Attempt to use logical palettes’

Logical palettes are a mechanism by which a Windows application running on an 8-bit colour display can select precisely the colours it wants instead of going with the Windows standard defaults.

If you are not getting the colours you ask for on an 8-bit display, you can try enabling this option. However, be warned that it's never worked very well.

4.13.6 ‘Use system colours’

Enabling this option will cause PuTTY to ignore the configured colours for ‘Default Background/Foreground’ and ‘Cursor Colour/Text’ (see section 4.13.7), instead going with the system-wide defaults.

Note that non-bold and bold text will be the same colour if this option is enabled. You might want to change to indicating bold text by font changes (see section 4.13.4).

4.13.7 Adjusting the colours in the terminal window

The main colour control allows you to specify exactly what colours things should be displayed in. To modify one of the PuTTY colours, use the list box to select which colour you want to modify. The RGB values for that colour will appear on the right-hand side of the list box. Now, if you press the ‘Modify’ button, you will be presented with a colour selector, in which you can choose a new colour to go in place of the old one. (You may also edit the RGB values directly in the edit boxes, if you wish; each value is an integer from 0 to 255.)

PuTTY allows you to set the cursor colour, the default foreground and background, and the precise shades of all the ANSI configurable colours (black, red, green, yellow, blue, magenta, cyan, and white). You can also modify the precise shades used for the bold versions of these colours; these are used to display bold text if you have chosen to indicate that by colour (see section 4.13.4), and can also be used if the server asks specifically to use them. (Note that ‘Default Bold Background’ is not the background colour used for bold text; it is only used if the server specifically asks for a bold background.)

4.14 The Connection panel

The Connection panel allows you to configure options that apply to more than one type of connection.

4.14.1 Using keepalives to prevent disconnection

If you find your sessions are closing unexpectedly (most often with ‘Connection reset by peer’) after they have been idle for a while, you might want to try using this option.

Some network routers and firewalls need to keep track of all connections through them. Usually, these firewalls will assume a connection is dead if no data is transferred in either direction after a certain time interval. This can cause PuTTY sessions to be unexpectedly closed by the firewall if no traffic is seen in the session for some time.

The keepalive option (‘Seconds between keepalives’) allows you to configure PuTTY to send data through the session at regular intervals, in a way that does not disrupt the actual terminal session. If you find your firewall is cutting idle connections off, you can try entering a non-zero value in this field. The value is measured in seconds; so, for example, if your firewall cuts connections off after ten minutes then you might want to enter 300 seconds (5 minutes) in the box.

Note that keepalives are not always helpful. They help if you have a firewall which drops your connection after an idle period; but if the network between you and the server suffers from breaks in connectivity then keepalives can actually make things worse. If a session is idle, and connectivity is temporarily lost between the endpoints, but the connectivity is restored before either side tries to send anything, then there will be no problem - neither endpoint will notice that anything was wrong. However, if one side does send something during the break, it will repeatedly try to re-send, and eventually give up and abandon the connection. Then when connectivity is restored, the other side will find that the first side doesn't believe there is an open connection any more. Keepalives can make this sort of problem worse, because they increase the probability that PuTTY will attempt to send data during a break in connectivity. (Other types of periodic network activity can cause this behaviour; in particular, SSH-2 re-keys can have this effect. See section 4.18.2.)

Therefore, you might find that keepalives help connection loss, or you might find they make it worse, depending on what kind of network problems you have between you and the server.

Keepalives are only supported in Telnet and SSH; the Rlogin, SUPDUP, and Raw protocols offer no way of implementing them. (For an alternative, see section 4.14.3.)

Note that if you are using SSH-1 and the server has a bug that makes it unable to deal with SSH-1 ignore messages (see section 4.26.11), enabling keepalives will have no effect.

4.14.2 ‘Disable Nagle's algorithm’

Nagle's algorithm is a detail of TCP/IP implementations that tries to minimise the number of small data packets sent down a network connection. With Nagle's algorithm enabled, PuTTY's bandwidth usage will be slightly more efficient; with it disabled, you may find you get a faster response to your keystrokes when connecting to some types of server.

The Nagle algorithm is disabled by default for interactive connections.

4.14.3 ‘Enable TCP keepalives’

NOTE: TCP keepalives should not be confused with the application-level keepalives described in section 4.14.1. If in doubt, you probably want application-level keepalives; TCP keepalives are provided for completeness.

The idea of TCP keepalives is similar to application-level keepalives, and the same caveats apply. The main differences are:

  • TCP keepalives are available on all network connection types, including Raw, Rlogin, and SUPDUP.
  • The interval between TCP keepalives is usually much longer, typically two hours; this is set by the operating system, and cannot be configured within PuTTY.
  • If the operating system does not receive a response to a keepalive, it may send out more in quick succession and terminate the connection if no response is received.

TCP keepalives may be more useful for ensuring that half-open connections are terminated than for keeping a connection alive.

TCP keepalives are disabled by default.

4.14.4 ‘Internet protocol version’

This option allows the user to select between the old and new Internet protocols and addressing schemes (IPv4 and IPv6). The selected protocol will be used for most outgoing network connections (including connections to proxies); however, tunnels have their own configuration, for which see section 4.25.2.

The default setting is ‘Auto’, which means PuTTY will do something sensible and try to guess which protocol you wanted. (If you specify a literal Internet address, it will use whichever protocol that address implies. If you provide a hostname, it will see what kinds of address exist for that hostname; it will use IPv6 if there is an IPv6 address available, and fall back to IPv4 if not.)

If you need to force PuTTY to use a particular protocol, you can explicitly set this to ‘IPv4’ or ‘IPv6’.

4.14.5 ‘Logical name of remote host’

This allows you to tell PuTTY that the host it will really end up connecting to is different from where it thinks it is making a network connection.

You might use this, for instance, if you had set up an SSH port forwarding in one PuTTY session so that connections to some arbitrary port (say, localhost port 10022) were forwarded to a second machine's SSH port (say, foovax port 22), and then started a second PuTTY connecting to the forwarded port.

In normal usage, the second PuTTY will access the host key cache under the host name and port it actually connected to (i.e. localhost port 10022 in this example). Using the logical host name option, however, you can configure the second PuTTY to cache the host key under the name of the host you know that it's really going to end up talking to (here foovax).

This can be useful if you expect to connect to the same actual server through many different channels (perhaps because your port forwarding arrangements keep changing): by consistently setting the logical host name, you can arrange that PuTTY will not keep asking you to reconfirm its host key. Conversely, if you expect to use the same local port number for port forwardings to lots of different servers, you probably didn't want any particular server's host key cached under that local port number. (For this latter case, you could instead explicitly configure host keys in the relevant sessions; see section 4.19.3.)

If you just enter a host name for this option, PuTTY will cache the SSH host key under the default SSH port for that host, irrespective of the port you really connected to (since the typical scenario is like the above example: you connect to a silly real port number and your connection ends up forwarded to the normal port-22 SSH server of some other machine). To override this, you can append a port number to the logical host name, separated by a colon. E.g. entering ‘foovax:2200’ as the logical host name will cause the host key to be cached as if you had connected to port 2200 of foovax.

If you provide a host name using this option, it is also displayed in other locations which contain the remote host name, such as the default window title and the default SSH password prompt. This reflects the fact that this is the host you're really connecting to, which is more important than the mere means you happen to be using to contact that host. (This applies even if you're using a protocol other than SSH.)

4.15 The Data panel

The Data panel allows you to configure various pieces of data which can be sent to the server to affect your connection at the far end.

Each option on this panel applies to more than one protocol. Options which apply to only one protocol appear on that protocol's configuration panels.

4.15.1 ‘Auto-login username’

All three of the SSH, Telnet, and Rlogin protocols allow you to specify what user name you want to log in as, without having to type it explicitly every time. (Some Telnet servers don't support this.)

In this box you can type that user name.

4.15.2 Use of system username

When the previous box (section 4.15.1) is left blank, by default, PuTTY will prompt for a username at the time you make a connection.

In some environments, such as the networks of large organisations implementing single sign-on, a more sensible default may be to use the name of the user logged in to the local operating system (if any); this is particularly likely to be useful with GSSAPI key exchange and user authentication (see section 4.22 and section 4.18.1.1). This control allows you to change the default behaviour.

The current system username is displayed in the dialog as a convenience. It is not saved in the configuration; if a saved session is later used by a different user, that user's name will be used.

4.15.3 ‘Terminal-type string’

Most servers you might connect to with PuTTY are designed to be connected to from lots of different types of terminal. In order to send the right control sequences to each one, the server will need to know what type of terminal it is dealing with. Therefore, each of the SSH, Telnet, and Rlogin protocols allow a text string to be sent down the connection describing the terminal. On a Unix server, this selects an entry from the termcap or terminfo database that tells applications what control sequences to send to the terminal, and what character sequences to expect the keyboard to generate.

PuTTY attempts to emulate the Unix xterm program, and by default it reflects this by sending xterm as a terminal-type string. If you find this is not doing what you want - perhaps the remote system reports ‘Unknown terminal type’ - you could try setting this to something different, such as vt220.

If you're not sure whether a problem is due to the terminal type setting or not, you probably need to consult the manual for your application or your server.

4.15.4 ‘Terminal speeds’

The Telnet, Rlogin, and SSH protocols allow the client to specify terminal speeds to the server.

This parameter does not affect the actual speed of the connection, which is always ‘as fast as possible’; it is just a hint that is sometimes used by server software to modify its behaviour. For instance, if a slow speed is indicated, the server may switch to a less bandwidth-hungry display mode.

The value is usually meaningless in a network environment, but PuTTY lets you configure it, in case you find the server is reacting badly to the default value.

The format is a pair of numbers separated by a comma, for instance, 38400,38400. The first number represents the output speed (from the server) in bits per second, and the second is the input speed (to the server). (Only the first is used in the Rlogin protocol.)

This option has no effect on Raw connections.

4.15.5 Setting environment variables on the server

The Telnet protocol provides a means for the client to pass environment variables to the server. Many Telnet servers have stopped supporting this feature due to security flaws, but PuTTY still supports it for the benefit of any servers which have found other ways around the security problems than just disabling the whole mechanism.

Version 2 of the SSH protocol also provides a similar mechanism, which is easier to implement without security flaws. Newer SSH-2 servers are more likely to support it than older ones.

This configuration data is not used in the SSH-1, rlogin or raw protocols.

To add an environment variable to the list transmitted down the connection, you enter the variable name in the ‘Variable’ box, enter its value in the ‘Value’ box, and press the ‘Add’ button. To remove one from the list, select it in the list box and press ‘Remove’.

4.16 The Proxy panel

The Proxy panel allows you to configure PuTTY to use various types of proxy in order to make its network connections. The settings in this panel affect the primary network connection forming your PuTTY session, and also any extra connections made as a result of SSH port forwarding (see section 3.5).

Note that unlike some software (such as web browsers), PuTTY does not attempt to automatically determine whether to use a proxy and (if so) which one to use for a given destination. If you need to use a proxy, it must always be explicitly configured.

4.16.1 Setting the proxy type

The ‘Proxy type’ radio buttons allow you to configure what type of proxy you want PuTTY to use for its network connections. The default setting is ‘None’; in this mode no proxy is used for any connection.

  • Selecting ‘HTTP’ allows you to proxy your connections through a web server supporting the HTTP CONNECT command, as documented in RFC 2817.
  • Selecting ‘SOCKS 4’ or ‘SOCKS 5’ allows you to proxy your connections through a SOCKS server.
  • Many firewalls implement a less formal type of proxy in which a user can make a Telnet connection directly to the firewall machine and enter a command such as connect myhost.com 22 to connect through to an external host. Selecting ‘Telnet’ allows you to tell PuTTY to use this type of proxy.
  • Selecting ‘Local’ allows you to specify an arbitrary command on the local machine to act as a proxy. When the session is started, instead of creating a TCP connection, PuTTY runs the command (specified in section 4.16.5), and uses its standard input and output streams.

    This could be used, for instance, to talk to some kind of network proxy that PuTTY does not natively support; or you could tunnel a connection over something other than TCP/IP entirely.

    If you want your local proxy command to make a secondary SSH connection to a proxy host and then tunnel the primary connection over that, you might well want the -nc command-line option in Plink. See section 3.11.3.14 for more information.

    You can also enable this mode on the command line; see section 3.11.3.26.

4.16.2 Excluding parts of the network from proxying

Typically you will only need to use a proxy to connect to non-local parts of your network; for example, your proxy might be required for connections outside your company's internal network. In the ‘Exclude Hosts/IPs’ box you can enter ranges of IP addresses, or ranges of DNS names, for which PuTTY will avoid using the proxy and make a direct connection instead.

The ‘Exclude Hosts/IPs’ box may contain more than one exclusion range, separated by commas. Each range can be an IP address or a DNS name, with a * character allowing wildcards. For example:

*.example.com

This excludes any host with a name ending in .example.com from proxying.

192.168.88.*

This excludes any host with an IP address starting with 192.168.88 from proxying.

192.168.88.*,*.example.com

This excludes both of the above ranges at once.

Connections to the local host (the host name localhost, and any loopback IP address) are never proxied, even if the proxy exclude list does not explicitly contain them. It is very unlikely that this behaviour would ever cause problems, but if it does you can change it by enabling ‘Consider proxying local host connections’.

Note that if you are doing DNS at the proxy (see section 4.16.3), you should make sure that your proxy exclusion settings do not depend on knowing the IP address of a host. If the name is passed on to the proxy without PuTTY looking it up, it will never know the IP address and cannot check it against your list.

4.16.3 Name resolution when using a proxy

If you are using a proxy to access a private network, it can make a difference whether DNS name resolution is performed by PuTTY itself (on the client machine) or performed by the proxy.

The ‘Do DNS name lookup at proxy end’ configuration option allows you to control this. If you set it to ‘No’, PuTTY will always do its own DNS, and will always pass an IP address to the proxy. If you set it to ‘Yes’, PuTTY will always pass host names straight to the proxy without trying to look them up first.

If you set this option to ‘Auto’ (the default), PuTTY will do something it considers appropriate for each type of proxy. Telnet, HTTP, and SOCKS5 proxies will have host names passed straight to them; SOCKS4 proxies will not.

Note that if you are doing DNS at the proxy, you should make sure that your proxy exclusion settings (see section 4.16.2) do not depend on knowing the IP address of a host. If the name is passed on to the proxy without PuTTY looking it up, it will never know the IP address and cannot check it against your list.

The original SOCKS 4 protocol does not support proxy-side DNS. There is a protocol extension (SOCKS 4A) which does support it, but not all SOCKS 4 servers provide this extension. If you enable proxy DNS and your SOCKS 4 server cannot deal with it, this might be why.

4.16.4 Username and password

If your proxy requires authentication, you can enter a username and a password in the ‘Username’ and ‘Password’ boxes.

Note that if you save your session, the proxy password will be saved in plain text, so anyone who can access your PuTTY configuration data will be able to discover it.

Authentication is not fully supported for all forms of proxy:

  • Username and password authentication is supported for HTTP proxies and SOCKS 5 proxies.
    • With SOCKS 5, authentication is via CHAP if the proxy supports it (this is not supported in PuTTYtel); otherwise the password is sent to the proxy in plain text.
    • With HTTP proxying, the only currently supported authentication method is ‘basic’, where the password is sent to the proxy in plain text.
  • SOCKS 4 can use the ‘Username’ field, but does not support passwords.
  • You can specify a way to include a username and password in the Telnet/Local proxy command (see section 4.16.5).

4.16.5 Specifying the Telnet or Local proxy command

If you are using the Telnet proxy type, the usual command required by the firewall's Telnet server is connect, followed by a host name and a port number. If your proxy needs a different command, you can enter an alternative here.

If you are using the Local proxy type, the local command to run is specified here.

In this string, you can use \n to represent a new-line, \r to represent a carriage return, \t to represent a tab character, and \x followed by two hex digits to represent any other character. \\ is used to encode the \ character itself.

Also, the special strings %host and %port will be replaced by the host name and port number you want to connect to. The strings %user and %pass will be replaced by the proxy username and password you specify. The strings %proxyhost and %proxyport will be replaced by the host details specified on the Proxy panel, if any (this is most likely to be useful for the Local proxy type). To get a literal % sign, enter %%.

If a Telnet proxy server prompts for a username and password before commands can be sent, you can use a command such as:

%user\n%pass\nconnect %host %port\n

This will send your username and password as the first two lines to the proxy, followed by a command to connect to the desired host and port. Note that if you do not include the %user or %pass tokens in the Telnet command, then the ‘Username’ and ‘Password’ configuration fields will be ignored.

4.16.6 Controlling proxy logging

Often the proxy interaction has its own diagnostic output; this is particularly the case for local proxy commands.

The setting ‘Print proxy diagnostics in the terminal window’ lets you control how much of the proxy's diagnostics are printed to the main terminal window, along with output from your main session.

By default (‘No’), proxy diagnostics are only sent to the Event Log; with ‘Yes’ they are also printed to the terminal, where they may get mixed up with your main session. ‘Only until session starts’ is a compromise; proxy messages will go to the terminal window until the main session is deemed to have started (in a protocol-dependent way), which is when they're most likely to be interesting; any further proxy-related messages during the session will only go to the Event Log.

4.17 The SSH panel

The SSH panel allows you to configure options that only apply to SSH sessions.

4.17.1 Executing a specific command on the server

In SSH, you don't have to run a general shell session on the server. Instead, you can choose to run a single specific command (such as a mail user agent, for example). If you want to do this, enter the command in the ‘Remote command’ box.

Note that most servers will close the session after executing the command.

4.17.2 ‘Don't start a shell or command at all’

If you tick this box, PuTTY will not attempt to run a shell or command after connecting to the remote server. You might want to use this option if you are only using the SSH connection for port forwarding, and your user account on the server does not have the ability to run a shell.

This feature is only available in SSH protocol version 2 (since the version 1 protocol assumes you will always want to run a shell).

This feature can also be enabled using the -N command-line option; see section 3.11.3.13.

If you use this feature in Plink, you will not be able to terminate the Plink process by any graceful means; the only way to kill it will be by pressing Control-C or sending a kill signal from another program.

4.17.3 ‘Enable compression’

This enables data compression in the SSH connection: data sent by the server is compressed before sending, and decompressed at the client end. Likewise, data sent by PuTTY to the server is compressed first and the server decompresses it at the other end. This can help make the most of a low-bandwidth connection.

4.17.4 ‘SSH protocol version’

This allows you to select whether to use SSH protocol version 2 or the older version 1.

You should normally leave this at the default of ‘2’. As well as having fewer features, the older SSH-1 protocol is no longer developed, has many known cryptographic weaknesses, and is generally not considered to be secure. PuTTY's protocol 1 implementation is provided mainly for compatibility, and is no longer being enhanced.

If a server offers both versions, prefer ‘2’. If you have some server or piece of equipment that only talks SSH-1, select ‘1’ here, and do not treat the resulting connection as secure.

PuTTY will not automatically fall back to the other version of the protocol if the server turns out not to match your selection here; instead, it will put up an error message and abort the connection. This prevents an active attacker downgrading an intended SSH-2 connection to SSH-1.

4.17.5 Sharing an SSH connection between PuTTY tools

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 ‘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 ‘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 ‘upstream’, which means that it is the one managing the real SSH connection. All subsequent PuTTYs which reuse the connection are referred to as ‘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, 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 ‘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 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 ‘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 never act as upstreams.

It is possible to test programmatically for the existence of a live upstream using Plink. See section 7.2.3.4.

4.18 The Kex panel

The Kex panel (short for ‘key exchange’) allows you to configure options related to SSH-2 key exchange.

Key exchange occurs at the start of an SSH connection (and occasionally thereafter); it establishes a shared secret that is used as the basis for all of SSH's security features. It is therefore very important for the security of the connection that the key exchange is secure.

Key exchange is a cryptographically intensive process; if either the client or the server is a relatively slow machine, the slower methods may take several tens of seconds to complete.

If connection startup is too slow, or the connection hangs periodically, you may want to try changing these settings.

If you don't understand what any of this means, it's safe to leave these settings alone.

This entire panel is only relevant to SSH protocol version 2; none of these settings affect SSH-1 at all.

4.18.1 Key exchange algorithm selection

PuTTY supports a variety of SSH-2 key exchange methods, and allows you to choose which one you prefer to use; configuration is similar to cipher selection (see section 4.20).

PuTTY currently supports the following key exchange methods:

  • ‘ECDH’: elliptic curve Diffie-Hellman key exchange.
  • ‘Group 14’: Diffie-Hellman key exchange with a well-known 2048-bit group.
  • ‘Group 1’: Diffie-Hellman key exchange with a well-known 1024-bit group. We no longer recommend using this method, and it's not used by default in new installations; however, it may be the only method supported by very old server software.
  • Group exchange’: with this method, instead of using a fixed group, PuTTY requests that the server suggest a group to use for key exchange; the server can avoid groups known to be weak, and possibly invent new ones over time, without any changes required to PuTTY's configuration. We recommend use of this method instead of the well-known groups, if possible.
  • RSA key exchange’: this requires much less computational effort on the part of the client, and somewhat less on the part of the server, than Diffie-Hellman key exchange.
  • ‘GSSAPI key exchange’: see section 4.18.1.1.

If the first algorithm PuTTY finds is below the ‘warn below here’ line, you will see a warning box when you make the connection, similar to that for cipher selection (see section 4.20).

4.18.1.1 GSSAPI-based key exchange

PuTTY supports a set of key exchange methods that also incorporates GSSAPI-based authentication. They are enabled with the ‘Attempt GSSAPI key exchange’ checkbox (which also appears on the ‘GSSAPI’ panel).

PuTTY can only perform the GSSAPI-authenticated key exchange methods when using Kerberos V5, and not other GSSAPI mechanisms. If the user running PuTTY has current Kerberos V5 credentials, then PuTTY will select the GSSAPI key exchange methods in preference to any of the ordinary SSH key exchange methods configured in the preference list.

The advantage of doing GSSAPI authentication as part of the SSH key exchange is apparent when you are using credential delegation (see section 4.22.1). The SSH key exchange can be repeated later in the session, and this allows your Kerberos V5 credentials (which are typically short-lived) to be automatically re-delegated to the server when they are refreshed on the client. (This feature is commonly referred to as ‘cascading credentials’.)

If your server doesn't support GSSAPI key exchange, it may still support GSSAPI in the SSH user authentication phase. This will still let you log in using your Kerberos credentials, but will only allow you to delegate the credentials that are active at the beginning of the session; they can't be refreshed automatically later, in a long-running session.

Another effect of GSSAPI key exchange is that it replaces the usual SSH mechanism of permanent host keys described in section 2.2. So if you use this method, then you won't be asked any interactive questions about whether to accept the server's host key. Instead, the Kerberos exchange will verify the identity of the host you connect to, at the same time as verifying your identity to it.

4.18.2 Repeat key exchange

If the session key negotiated at connection startup is used too much or for too long, it may become feasible to mount attacks against the SSH connection. Therefore, the SSH-2 protocol specifies that a new key exchange should take place every so often; this can be initiated by either the client or the server.

While this renegotiation is taking place, no data can pass through the SSH connection, so it may appear to ‘freeze’. (The occurrence of repeat key exchange is noted in the Event Log; see section 3.1.3.1.) Usually the same algorithm is used as at the start of the connection, with a similar overhead.

These options control how often PuTTY will initiate a repeat key exchange (‘rekey’). You can also force a key exchange at any time from the Special Commands menu (see section 3.1.3.2).

  • ‘Max minutes before rekey’ specifies the amount of time that is allowed to elapse before a rekey is initiated. If this is set to zero, PuTTY will not rekey due to elapsed time. The SSH-2 protocol specification recommends a timeout of at most 60 minutes.

You might have a need to disable time-based rekeys completely for the same reasons that keepalives aren't always helpful. If you anticipate suffering a network dropout of several hours in the middle of an SSH connection, but were not actually planning to send data down that connection during those hours, then an attempted rekey in the middle of the dropout will probably cause the connection to be abandoned, whereas if rekeys are disabled then the connection should in principle survive (in the absence of interfering firewalls). See section 4.14.1 for more discussion of these issues; for these purposes, rekeys have much the same properties as keepalives. (Except that rekeys have cryptographic value in themselves, so you should bear that in mind when deciding whether to turn them off.) Note, however, the the SSH server can still initiate rekeys.

  • ‘Minutes between GSSAPI checks’, if you're using GSSAPI key exchange, specifies how often the GSSAPI credential cache is checked to see whether new tickets are available for delegation, or current ones are near expiration. If forwarding of GSSAPI credentials is enabled, PuTTY will try to rekey as necessary to keep the delegated credentials from expiring. Frequent checks are recommended; rekeying only happens when needed.
  • ‘Max data before rekey’ specifies the amount of data (in bytes) that is permitted to flow in either direction before a rekey is initiated. If this is set to zero, PuTTY will not rekey due to transferred data. The SSH-2 protocol specification recommends a limit of at most 1 gigabyte.

    As well as specifying a value in bytes, the following shorthand can be used:

    • 1k’ specifies 1 kilobyte (1024 bytes).
    • 1M’ specifies 1 megabyte (1024 kilobytes).
    • 1G’ specifies 1 gigabyte (1024 megabytes).

Disabling data-based rekeys entirely is a bad idea. The integrity, and to a lesser extent, confidentiality of the SSH-2 protocol depend in part on rekeys occurring 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.

4.19 The Host Keys panel

The Host Keys panel allows you to configure options related to SSH-2 host key management.

Host keys are used to prove the server's identity, and assure you that the server is not being spoofed (either by a man-in-the-middle attack or by completely replacing it on the network). See section 2.2 for a basic introduction to host keys.

This entire panel is only relevant to SSH protocol version 2; none of these settings affect SSH-1 at all.

4.19.1 Host key type selection

PuTTY supports a variety of SSH-2 host key types, and allows you to choose which one you prefer to use to identify the server. Configuration is similar to cipher selection (see section 4.20).

PuTTY currently supports the following host key types:

  • Ed25519’: Edwards-curve DSA using a twisted Edwards curve with modulus 2^255-19.
  • Ed448’: another Edwards-curve DSA type, using a larger elliptic curve with a 448-bit instead of 255-bit modulus (so it has a higher security level than Ed25519).
  • ‘ECDSA’: elliptic curve DSA using one of the NIST-standardised elliptic curves.
  • ‘DSA’: straightforward DSA using modular exponentiation.
  • ‘RSA’: the ordinary RSA algorithm.

If PuTTY already has one or more host keys stored for the server, it will by default prefer to use one of those, even if the server has a key type that is higher in the preference order. You can add such a key to PuTTY's cache from within an existing session using the ‘Special Commands’ menu; see section 3.1.3.2.

Otherwise, PuTTY will choose a key type based purely on the preference order you specify in the configuration.

If the first key type PuTTY finds is below the ‘warn below here’ line, you will see a warning box when you make the connection, similar to that for cipher selection (see section 4.20).

4.19.2 Preferring known host keys

By default, PuTTY will adjust the preference order for host key algorithms so that any host keys it already knows are moved to the top of the list.

This prevents you from having to check and confirm a new host key for a server you already had one for (e.g. because the server has generated an alternative key of a type higher in PuTTY's preference order, or because you changed the preference order itself).

However, on the other hand, it can leak information to a listener in the network about whether you already know a host key for this server.

For this reason, this policy is configurable. By turning this checkbox off, you can reset PuTTY to always use the exact order of host key algorithms configured in the preference list described in section 4.19.1, so that a listener will find out nothing about what keys you had stored.

4.19.3 Manually configuring host keys

In some situations, if PuTTY's automated host key management is not doing what you need, you might need to manually configure PuTTY to accept a specific host key, or one of a specific set of host keys.

One reason why you might want to do this is because the host name PuTTY is connecting to is using round-robin DNS to return one of multiple actual servers, and they all have different host keys. In that situation, you might need to configure PuTTY to accept any of a list of host keys for the possible servers, while still rejecting any key not in that list.

Another reason is if PuTTY's automated host key management is completely unavailable, e.g. because PuTTY (or Plink or PSFTP, etc) is running in a Windows environment without access to the Registry. In that situation, you will probably want to use the -hostkey command-line option to configure the expected host key(s); see section 3.11.3.21.

For situations where PuTTY's automated host key management simply picks the wrong host name to store a key under, you may want to consider setting a ‘logical host name’ instead; see section 4.14.5.

To configure manual host keys via the GUI, enter some text describing the host key into the edit box in the ‘Manually configure host keys for this connection’ container, and press the ‘Add’ button. The text will appear in the ‘Host keys or fingerprints to accept’ list box. You can remove keys again with the ‘Remove’ button.

The text describing a host key can be in one of the following formats:

  • An SHA-256-based host key fingerprint of the form displayed in PuTTY's Event Log and host key dialog boxes, i.e. ‘SHA256:’ followed by 43 case-sensitive characters.
  • An MD5-based host key fingerprint, i.e. sixteen 2-digit hex numbers separated by colons, optionally preceded by the prefix ‘MD5:’. (The case of the characters does not matter.)
  • A base64-encoded blob describing an SSH-2 public key in OpenSSH's one-line public key format. How you acquire a public key in this format is server-dependent; on an OpenSSH server it can typically be found in a location like /etc/ssh/ssh_host_rsa_key.pub.

If this box contains at least one host key or fingerprint when PuTTY makes an SSH connection, then PuTTY's automated host key management is completely bypassed: the connection will be permitted if and only if the host key presented by the server is one of the keys listed in this box, and the host key store in the Registry will be neither read nor written, unless you explicitly do so.

If the box is empty (as it usually is), then PuTTY's automated host key management will work as normal.

4.20 The Cipher panel

PuTTY supports a variety of different encryption algorithms, 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:

  • ChaCha20-Poly1305, a combined cipher and MAC (SSH-2 only)
  • AES (Rijndael) - 256, 192, or 128-bit SDCTR or CBC (SSH-2 only)
  • Arcfour (RC4) - 256 or 128-bit stream cipher (SSH-2 only)
  • Blowfish - 256-bit SDCTR (SSH-2 only) or 128-bit CBC
  • Triple-DES - 168-bit SDCTR (SSH-2 only) or CBC
  • Single-DES - 56-bit CBC (see below for SSH-2)

If the algorithm PuTTY finds is below the ‘warn below here’ line, you will see a warning box when you make the connection:

The first cipher supported by the server
is single-DES, which is below the configured
warning threshold.
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 ‘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 ‘Enable legacy use of single-DES in SSH-2’ option; by default this is disabled and PuTTY will stick to recommended ciphers.

4.21 The Auth panel

The Auth panel allows you to configure authentication options for SSH sessions.

4.21.1 ‘Display pre-authentication banner’

SSH-2 servers can provide a message for clients to display to the prospective user before the user logs in; this is sometimes known as a pre-authentication ‘banner’. Typically this is used to provide information about the server and legal notices.

By default, PuTTY displays this message before prompting for a password or similar credentials (although, unfortunately, not before prompting for a login name, due to the nature of the protocol design). By unchecking this option, display of the banner can be suppressed entirely.

4.21.2 ‘Bypass authentication entirely’

In SSH-2, it is in principle possible to establish a connection without using SSH's mechanisms to identify or prove who you are to the server. An SSH server could prefer to handle authentication in the data channel, for instance, or simply require no user authentication whatsoever.

By default, PuTTY assumes the server requires authentication (we've never heard of one that doesn't), and thus must start this process with a username. If you find you are getting username prompts that you cannot answer, you could try enabling this option. However, most SSH servers will reject this.

This is not the option you want if you have a username and just want PuTTY to remember it; for that see section 4.15.1. It's also probably not what if you're trying to set up passwordless login to a mainstream SSH server; depending on the server, you probably wanted public-key authentication (chapter 8) or perhaps GSSAPI authentication (section 4.22). (These are still forms of authentication, even if you don't have to interact with them.)

This option only affects SSH-2 connections. SSH-1 connections always require an authentication step.

4.21.3 ‘Disconnect if authentication succeeds trivially’

This option causes PuTTY to abandon an SSH session and disconnect from the server, if the server accepted authentication without ever having asked for any kind of password or signature or token.

This might be used as a security measure. There are some forms of attack against an SSH client user which work by terminating the SSH authentication stage early, and then doing something in the main part of the SSH session which looks like part of the authentication, but isn't really.

For example, instead of demanding a signature from your public key, for which PuTTY would ask for your key's passphrase, a compromised or malicious server might allow you to log in with no signature or password at all, and then print a message that imitates PuTTY's request for your passphrase, in the hope that you would type it in. (In fact, the passphrase for your public key should not be sent to any server.)

PuTTY's main defence against attacks of this type is the ‘trust sigil’ system: messages in the PuTTY window that are truly originated by PuTTY itself are shown next to a small copy of the PuTTY icon, which the server cannot fake when it tries to imitate the same message using terminal output.

However, if you think you might be at risk of this kind of thing anyway (if you don't watch closely for the trust sigils, or if you think you're at extra risk of one of your servers being malicious), then you could enable this option as an extra defence. Then, if the server tries any of these attacks involving letting you through the authentication stage, PuTTY will disconnect from the server before it can send a follow-up fake prompt or other type of attack.

On the other hand, some servers legitimately let you through the SSH authentication phase trivially, either because they are genuinely public, or because the important authentication step happens during the terminal session. (An example might be an SSH server that connects you directly to the terminal login prompt of a legacy mainframe.) So enabling this option might cause some kinds of session to stop working. It's up to you.

4.21.4 ‘Attempt authentication using Pageant’

If this option is enabled, then PuTTY will look for Pageant (the SSH private-key storage agent) and attempt to authenticate with any suitable public keys Pageant currently holds.

This behaviour is almost always desirable, and is therefore enabled by default. In rare cases you might need to turn it off in order to force authentication by some non-public-key method such as passwords.

This option can also be controlled using the -noagent command-line option. See section 3.11.3.9.

See chapter 9 for more information about Pageant in general.

4.21.5 ‘Attempt TIS or CryptoCard authentication’

TIS and CryptoCard authentication are (despite their names) generic forms of simple challenge/response authentication available in SSH protocol version 1 only. You might use them if you were using S/Key one-time passwords, for example, or if you had a physical security token that generated responses to authentication challenges. They can even be used to prompt for simple passwords.

With this switch enabled, PuTTY will attempt these forms of authentication if the server is willing to try them. You will be presented with a challenge string (which may be different every time) and must supply the correct response in order to log in. If your server supports this, you should talk to your system administrator about precisely what form these challenges and responses take.

4.21.6 ‘Attempt keyboard-interactive authentication’

The SSH-2 equivalent of TIS authentication is called ‘keyboard-interactive’. It is a flexible authentication method using an arbitrary sequence of requests and responses; so it is not only useful for challenge/response mechanisms such as S/Key, but it can also be used for (for example) asking the user for a new password when the old one has expired.

PuTTY leaves this option enabled by default, but supplies a switch to turn it off in case you should have trouble with it.

4.21.7 ‘Allow agent forwarding’

This option allows the SSH server to open forwarded connections back to your local copy of Pageant. If you are not running Pageant, this option will do nothing.

See chapter 9 for general information on Pageant, and section 9.4 for information on agent forwarding. Note that there is a security risk involved with enabling this option; see section 9.6 for details.

4.21.8 ‘Allow attempted changes of username in SSH-2’

In the SSH-1 protocol, it is impossible to change username after failing to authenticate. So if you mis-type your username at the PuTTY ‘login as:’ prompt, you will not be able to change it except by restarting PuTTY.

The SSH-2 protocol does allow changes of username, in principle, but does not make it mandatory for SSH-2 servers to accept them. In particular, OpenSSH does not accept a change of username; once you have sent one username, it will reject attempts to try to authenticate as another user. (Depending on the version of OpenSSH, it may quietly return failure for all login attempts, or it may send an error message.)

For this reason, PuTTY will by default not prompt you for your username more than once, in case the server complains. If you know your server can cope with it, you can enable the ‘Allow attempted changes of username’ option to modify PuTTY's behaviour.

4.21.9 ‘Private key file for authentication’

This box is where you enter the name of your private key file if you are using public key authentication. See chapter 8 for information about public key authentication in SSH.

This key must be in PuTTY's native format (*.PPK). If you have a private key in another format that you want to use with PuTTY, see section 8.2.14.

You can use the authentication agent Pageant so that you do not need to explicitly configure a key here; see chapter 9.

If a private key file is specified here with Pageant running, PuTTY will first try asking Pageant to authenticate with that key, and ignore any other keys Pageant may have. If that fails, PuTTY will ask for a passphrase as normal. You can also specify a public key file in this case (in RFC 4716 or OpenSSH format), as that's sufficient to identify the key to Pageant, but of course if Pageant isn't present PuTTY can't fall back to using this file itself.

4.22 The GSSAPI panel

The ‘GSSAPI’ subpanel of the ‘Auth’ panel controls the use of GSSAPI authentication. This is a mechanism which delegates the authentication exchange to a library elsewhere on the client machine, which in principle can authenticate in many different ways but in practice is usually used with the Kerberos single sign-on protocol to implement passwordless login.

GSSAPI authentication is only available in the SSH-2 protocol.

PuTTY supports two forms of GSSAPI-based authentication. In one of them, the SSH key exchange happens in the normal way, and GSSAPI is only involved in authenticating the user. The checkbox labelled ‘Attempt GSSAPI authentication’ controls this form.

In the other method, GSSAPI-based authentication is combined with the SSH key exchange phase. If this succeeds, then the SSH authentication step has nothing left to do. See section 4.18.1.1 for more information about this method. The checkbox labelled ‘Attempt GSSAPI key exchange’ controls this form. (The same checkbox appears on the ‘Kex’ panel.)

If one or both of these controls is enabled, then GSSAPI authentication will be attempted in one form or the other, and (typically) if your client machine has valid Kerberos credentials loaded, then PuTTY should be able to authenticate automatically to servers that support Kerberos logins.

If both of those checkboxes are disabled, PuTTY will not try any form of GSSAPI at all, and the rest of this panel will be unused.

4.22.1 ‘Allow GSSAPI credential delegation’

GSSAPI credential delegation is a mechanism for passing on your Kerberos (or other) identity to the session on the SSH server. If you enable this option, then not only will PuTTY be able to log in automatically to a server that accepts your Kerberos credentials, but also you will be able to connect out from that server to other Kerberos-supporting services and use the same credentials just as automatically.

(This option is the Kerberos analogue of SSH agent forwarding; see section 9.4 for some information on that.)

Note that, like SSH agent forwarding, there is a security implication in the use of this option: the administrator of the server you connect to, or anyone else who has cracked the administrator account on that server, could fake your identity when connecting to further Kerberos-supporting services. However, Kerberos sites are typically run by a central authority, so the administrator of one server is likely to already have access to the other services too; so this would typically be less of a risk than SSH agent forwarding.

If your connection is not using GSSAPI key exchange, it is possible for the delegation to expire during your session. See section 4.18.1.1 for more information.

4.22.2 Preference order for GSSAPI libraries

GSSAPI is a mechanism which allows more than one authentication method to be accessed through the same interface. Therefore, more than one authentication library may exist on your system which can be accessed using GSSAPI.

PuTTY contains native support for a few well-known such libraries (including Windows' SSPI), and will look for all of them on your system and use whichever it finds. If more than one exists on your system and you need to use a specific one, you can adjust the order in which it will search using this preference list control.

One of the options in the preference list is to use a user-specified GSSAPI library. If the library you want to use is not mentioned by name in PuTTY's list of options, you can enter its full pathname in the ‘User-supplied GSSAPI library path’ field, and move the ‘User-supplied GSSAPI library’ option in the preference list to make sure it is selected before anything else.

On Windows, such libraries are files with a .dll extension, and must have been built in the same way as the PuTTY executable you're running; if you have a 32-bit DLL, you must run a 32-bit version of PuTTY, and the same with 64-bit (see question A.6.10). On Unix, shared libraries generally have a .so extension.

4.23 The TTY panel

The TTY panel lets you configure the remote pseudo-terminal.

4.23.1 ‘Don't allocate a pseudo-terminal’

When connecting to a Unix system, most interactive shell sessions are run in a pseudo-terminal, which allows the Unix system to pretend it's talking to a real physical terminal device but allows the SSH server to catch all the data coming from that fake device and send it back to the client.

Occasionally you might find you have a need to run a session not in a pseudo-terminal. In PuTTY, this is generally only useful for very specialist purposes; although in Plink (see chapter 7) it is the usual way of working.

4.23.2 Sending terminal modes

The SSH protocol allows the client to send ‘terminal modes’ for the remote pseudo-terminal. These usually control the server's expectation of the local terminal's behaviour.

If your server does not have sensible defaults for these modes, you may find that changing them here helps, although the server is at liberty to ignore your changes. If you don't understand any of this, it's safe to leave these settings alone.

(None of these settings will have any effect if no pseudo-terminal is requested or allocated.)

You can change what happens for a particular mode by selecting it in the list, choosing one of the options and specifying the exact value if necessary, and hitting ‘Set’. The effect of the options is as follows:

  • If the ‘Auto’ option is selected, the PuTTY tools will decide whether to specify that mode to the server, and if so, will send a sensible value.

    PuTTY proper will send modes that it has an opinion on (currently only the code for the Backspace key, ERASE, and whether the character set is UTF-8, IUTF8). Plink on Unix will propagate appropriate modes from the local terminal, if any.

  • If ‘Nothing’ is selected, no value for the mode will be specified to the server under any circumstances.
  • If a value is specified, it will be sent to the server under all circumstances. The precise syntax of the value box depends on the mode.

By default, all of the available modes are listed as ‘Auto’, which should do the right thing in most circumstances.

The precise effect of each setting, if any, is up to the server. Their names come from POSIX and other Unix systems, and they are most likely to have a useful effect on such systems. (These are the same settings that can usually be changed using the stty command once logged in to such servers.)

Some notable modes are described below; for fuller explanations, see your server documentation.

  • ERASE is the character that when typed by the user will delete one space to the left. When set to ‘Auto’ (the default setting), this follows the setting of the local Backspace key in PuTTY (see section 4.4.1).

    This and other special characters are specified using ^C notation for Ctrl-C, and so on. Use ^<27> or ^<0x1B> to specify a character numerically, and ^~ to get a literal ^. Other non-control characters are denoted by themselves. Leaving the box entirely blank indicates that no character should be assigned to the specified function, although this may not be supported by all servers.

  • QUIT is a special character that usually forcefully ends the current process on the server (SIGQUIT). On many servers its default setting is Ctrl-backslash (^\), which is easy to accidentally invoke on many keyboards. If this is getting in your way, you may want to change it to another character or turn it off entirely.
  • Boolean modes such as ECHO and ICANON can be specified in PuTTY in a variety of ways, such as true/false, yes/no, and 0/1. (Explicitly specifying a value of no is different from not sending the mode at all.)
  • The boolean mode IUTF8 signals to the server whether the terminal character set is UTF-8 or not, for purposes such as basic line editing; if this is set incorrectly, the backspace key may erase the wrong amount of text, for instance. However, simply setting this is not usually sufficient for the server to use UTF-8; POSIX servers will generally also require the locale to be set (by some server-dependent means), although many newer installations default to UTF-8. Also, since this mode was added to the SSH protocol much later than the others, many servers (particularly older servers) do not honour this mode sent over SSH; indeed, a few poorly-written servers object to its mere presence, so you may find you need to set it to not be sent at all. When set to ‘Auto’, this follows the local configured character set (see section 4.10.1).
  • Terminal speeds are configured elsewhere; see section 4.15.4.

4.24 The X11 panel

The X11 panel allows you to configure forwarding of X11 over an SSH connection.

If your server lets you run X Window System graphical applications, X11 forwarding allows you to securely give those applications access to a local X display on your PC.

To enable X11 forwarding, check the ‘Enable X11 forwarding’ box. If your X display is somewhere unusual, you will need to enter its location in the ‘X display location’ box; if this is left blank, PuTTY will try to find a sensible default in the environment, or use the primary local display (:0) if that fails.

See section 3.4 for more information about X11 forwarding.

4.24.1 Remote X11 authentication

If you are using X11 forwarding, the virtual X server created on the SSH server machine will be protected by authorisation data. This data is invented, and checked, by PuTTY.

The usual authorisation method used for this is called MIT-MAGIC-COOKIE-1. This is a simple password-style protocol: the X client sends some cookie data to the server, and the server checks that it matches the real cookie. The cookie data is sent over an unencrypted X11 connection; so if you allow a client on a third machine to access the virtual X server, then the cookie will be sent in the clear.

PuTTY offers the alternative protocol XDM-AUTHORIZATION-1. This is a cryptographically authenticated protocol: the data sent by the X client is different every time, and it depends on the IP address and port of the client's end of the connection and is also stamped with the current time. So an eavesdropper who captures an XDM-AUTHORIZATION-1 string cannot immediately re-use it for their own X connection.

PuTTY's support for XDM-AUTHORIZATION-1 is a somewhat experimental feature, and may encounter several problems:

  • Some X clients probably do not even support XDM-AUTHORIZATION-1, so they will not know what to do with the data PuTTY has provided.
  • This authentication mechanism will only work in SSH-2. In SSH-1, the SSH server does not tell the client the source address of a forwarded connection in a machine-readable format, so it's impossible to verify the XDM-AUTHORIZATION-1 data.
  • You may find this feature causes problems with some SSH servers, which will not clean up XDM-AUTHORIZATION-1 data after a session, so that if you then connect to the same server using a client which only does MIT-MAGIC-COOKIE-1 and are allocated the same remote display number, you might find that out-of-date authentication data is still present on your server and your X connections fail.

PuTTY's default is MIT-MAGIC-COOKIE-1. If you change it, you should be sure you know what you're doing.

4.24.2 X authority file for local display

If you are using X11 forwarding, the local X server to which your forwarded connections are eventually directed may itself require authorisation.

Some Windows X servers do not require this: they do authorisation by simpler means, such as accepting any connection from the local machine but not from anywhere else. However, if your X server does require authorisation, then PuTTY needs to know what authorisation is required.

One way in which this data might be made available is for the X server to store it somewhere in a file which has the same format as the Unix .Xauthority file. If this is how your Windows X server works, then you can tell PuTTY where to find this file by configuring this option. By default, PuTTY will not attempt to find any authorisation for your local display.

4.25 The Tunnels panel

The Tunnels panel allows you to configure tunnelling of arbitrary connection types through an SSH connection.

Port forwarding allows you to tunnel other types of network connection down an SSH session. See section 3.5 for a general discussion of port forwarding and how it works.

The port forwarding section in the Tunnels panel shows a list of all the port forwardings that PuTTY will try to set up when it connects to the server. By default no port forwardings are set up, so this list is empty.

To add a port forwarding:

  • Set one of the ‘Local’ or ‘Remote’ radio buttons, depending on whether you want to forward a local port to a remote destination (‘Local’) or forward a remote port to a local destination (‘Remote’). Alternatively, select ‘Dynamic’ if you want PuTTY to provide a local SOCKS 4/4A/5 proxy on a local port (note that this proxy only supports TCP connections; the SSH protocol does not support forwarding UDP).
  • Enter a source port number into the ‘Source port’ box. For local forwardings, PuTTY will listen on this port of your PC. For remote forwardings, your SSH server will listen on this port of the remote machine. Note that most servers will not allow you to listen on port numbers less than 1024.
  • If you have selected ‘Local’ or ‘Remote’ (this step is not needed with ‘Dynamic’), enter a hostname and port number separated by a colon, in the ‘Destination’ box. Connections received on the source port will be directed to this destination. For example, to connect to a POP-3 server, you might enter popserver.example.com:110. (If you need to enter a literal IPv6 address, enclose it in square brackets, for instance ‘[::1]:2200’.)
  • Click the ‘Add’ button. Your forwarding details should appear in the list box.

To remove a port forwarding, simply select its details in the list box, and click the ‘Remove’ button.

In the ‘Source port’ box, you can also optionally enter an IP address to listen on, by specifying (for instance) 127.0.0.5:79. See section 3.5 for more information on how this works and its restrictions.

In place of port numbers, you can enter service names, if they are known to the local system. For instance, in the ‘Destination’ box, you could enter popserver.example.com:pop3.

You can modify the currently active set of port forwardings in mid-session using ‘Change Settings’ (see section 3.1.3.4). If you delete a local or dynamic port forwarding in mid-session, PuTTY will stop listening for connections on that port, so it can be re-used by another program. If you delete a remote port forwarding, note that:

  • The SSH-1 protocol contains no mechanism for asking the server to stop listening on a remote port.
  • The SSH-2 protocol does contain such a mechanism, but not all SSH servers support it. (In particular, OpenSSH does not support it in any version earlier than 3.9.)

If you ask to delete a remote port forwarding and PuTTY cannot make the server actually stop listening on the port, it will instead just start refusing incoming connections on that port. Therefore, although the port cannot be reused by another program, you can at least be reasonably sure that server-side programs can no longer access the service at your end of the port forwarding.

If you delete a forwarding, any existing connections established using that forwarding remain open. Similarly, changes to global settings such as ‘Local ports accept connections from other hosts’ only take effect on new forwardings.

If the connection you are forwarding over SSH is itself a second SSH connection made by another copy of PuTTY, you might find the ‘logical host name’ configuration option useful to warn PuTTY of which host key it should be expecting. See section 4.14.5 for details of this.

4.25.1 Controlling the visibility of forwarded ports

The source port for a forwarded connection usually does not accept connections from any machine except the SSH client or server machine itself (for local and remote forwardings respectively). There are controls in the Tunnels panel to change this:

  • The ‘Local ports accept connections from other hosts’ option allows you to set up local-to-remote port forwardings in such a way that machines other than your client PC can connect to the forwarded port. (This also applies to dynamic SOCKS forwarding.)
  • The ‘Remote ports do the same’ option does the same thing for remote-to-local port forwardings (so that machines other than the SSH server machine can connect to the forwarded port.) Note that this feature is only available in the SSH-2 protocol, and not all SSH-2 servers support it (OpenSSH 3.0 does not, for example).

4.25.2 Selecting Internet protocol version for forwarded ports

This switch allows you to select a specific Internet protocol (IPv4 or IPv6) for the local end of a forwarded port. By default, it is set on ‘Auto’, which means that:

  • for a local-to-remote port forwarding, PuTTY will listen for incoming connections in both IPv4 and (if available) IPv6
  • for a remote-to-local port forwarding, PuTTY will choose a sensible protocol for the outgoing connection.

This overrides the general Internet protocol version preference on the Connection panel (see section 4.14.4).

Note that some operating systems may listen for incoming connections in IPv4 even if you specifically asked for IPv6, because their IPv4 and IPv6 protocol stacks are linked together. Apparently Linux does this, and Windows does not. So if you're running PuTTY on Windows and you tick ‘IPv6’ for a local or dynamic port forwarding, it will only be usable by connecting to it using IPv6; whereas if you do the same on Linux, you can also use it with IPv4. However, ticking ‘Auto’ should always give you a port which you can connect to using either protocol.

4.26 The Bugs and More Bugs panels

Not all SSH servers work properly. Various existing servers have bugs in them, which can make it impossible for a client to talk to them unless it knows about the bug and works around it.

Since most servers announce their software version number at the beginning of the SSH connection, PuTTY will attempt to detect which bugs it can expect to see in the server and automatically enable workarounds. However, sometimes it will make mistakes; if the server has been deliberately configured to conceal its version number, or if the server is a version which PuTTY's bug database does not know about, then PuTTY will not know what bugs to expect.

The Bugs and More Bugs panels (there are two because we have so many bug compatibility modes) allow you to manually configure the bugs PuTTY expects to see in the server. Each bug can be configured in three states:

  • ‘Off’: PuTTY will assume the server does not have the bug.
  • ‘On’: PuTTY will assume the server does have the bug.
  • ‘Auto’: PuTTY will use the server's version number announcement to try to guess whether or not the server has the bug.

4.26.1 ‘Chokes on SSH-2 ignore messages’

An ignore message (SSH_MSG_IGNORE) is a message in the SSH protocol which can be sent from the client to the server, or from the server to the client, at any time. Either side is required to ignore the message whenever it receives it. PuTTY uses ignore messages in SSH-2 to confuse the encrypted data stream and make it harder to cryptanalyse. It also uses ignore messages for connection keepalives (see section 4.14.1).

If it believes the server to have this bug, PuTTY will stop using ignore messages. If this bug is enabled when talking to a correct server, the session will succeed, but keepalives will not work and the session might be less cryptographically secure than it could be.

4.26.2 ‘Handles SSH-2 key re-exchange badly’

Some SSH servers cannot cope with repeat key exchange at all, and will ignore attempts by the client to start one. Since PuTTY pauses the session while performing a repeat key exchange, the effect of this would be to cause the session to hang after an hour (unless you have your rekey timeout set differently; see section 4.18.2 for more about rekeys). Other, very old, SSH servers handle repeat key exchange even more badly, and disconnect upon receiving a repeat key exchange request.

If this bug is detected, PuTTY will never initiate a repeat key exchange. If this bug is enabled when talking to a correct server, the session should still function, but may be less secure than you would expect.

This is an SSH-2-specific bug.

4.26.3 ‘Chokes on PuTTY's SSH-2 ‘winadj’ requests’

PuTTY sometimes sends a special request to SSH servers in the middle of channel data, with the name winadj@putty.projects.tartarus.org (see section G.1). 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 understand the message; it is expected to send back a SSH_MSG_CHANNEL_FAILURE message indicating that it didn't understand it. (All PuTTY needs for its timing calculations is some kind of response.)

It has been known for some SSH servers to get confused by this message in one way or another – 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 ‘winadj@putty.projects.tartarus.org’ request, and will make do without its timing data.

4.26.4 ‘Replies to requests on closed channels’

The SSH protocol as published in RFC 4254 has an ambiguity which arises if one side of a connection tries to close a channel, while the other side simultaneously sends a request within the channel and asks for a reply. RFC 4254 leaves it unclear whether the closing side should reply to the channel request after having announced its intention to close the channel.

Discussion on the ietf-ssh mailing list in April 2014 formed a clear consensus that the right answer is no. However, because of the ambiguity in the specification, some SSH servers have implemented the other policy; for example, OpenSSH used to until it was fixed.

Because PuTTY sends channel requests with the ‘want reply’ flag throughout channels' lifetime (see section 4.26.3), it's possible that when connecting to such a server it might receive a reply to a request after it thinks the channel has entirely closed, and terminate with an error along the lines of ‘Received SSH2_MSG_CHANNEL_FAILURE for nonexistent channel 256’.

4.26.5 ‘Ignores SSH-2 maximum packet size’

When an SSH-2 channel is set up, each end announces the maximum size of data packet that it is willing to receive for that channel. Some servers ignore PuTTY's announcement and send packets larger than PuTTY is willing to accept, causing it to report ‘Incoming packet was garbled on decryption’.

If this bug is detected, PuTTY never allows the channel's 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.

4.26.6 ‘Requires padding on SSH-2 RSA signatures’

Versions below 3.3 of OpenSSH require SSH-2 RSA signatures to be padded with zero bytes to the same length as the RSA key modulus. The SSH-2 specification says that an unpadded signature MUST be accepted, so this is a bug. A typical symptom of this problem is that PuTTY mysteriously fails RSA authentication once in every few hundred attempts, and falls back to passwords.

If this bug is detected, PuTTY will pad its signatures in the way OpenSSH expects. If this bug is enabled when talking to a correct server, it is likely that no damage will be done, since correct servers usually still accept padded signatures because they're used to talking to OpenSSH.

This is an SSH-2-specific bug.

4.26.7 ‘Only supports pre-RFC4419 SSH-2 DH GEX’

The SSH key exchange method that uses Diffie-Hellman group exchange was redesigned after its original release, to use a slightly more sophisticated setup message. Almost all SSH implementations switched over to the new version. (PuTTY was one of the last.) A few old servers still only support the old one.

If this bug is detected, and the client and server negotiate Diffie-Hellman group exchange, then PuTTY will send the old message now known as SSH2_MSG_KEX_DH_GEX_REQUEST_OLD in place of the new SSH2_MSG_KEX_DH_GEX_REQUEST.

This is an SSH-2-specific bug.

4.26.8 ‘Miscomputes SSH-2 HMAC keys’

Versions 2.3.0 and below of the SSH server software from ssh.com compute the keys for their HMAC message authentication codes incorrectly. A typical symptom of this problem is that PuTTY dies unexpectedly at the beginning of the session, saying ‘Incorrect MAC received on packet’.

If this bug is detected, PuTTY will compute its HMAC keys in the same way as the buggy server, so that communication will still be possible. If this bug is enabled when talking to a correct server, communication will fail.

This is an SSH-2-specific bug.

4.26.9 ‘Misuses the session ID in SSH-2 PK auth’

Versions below 2.3 of OpenSSH require SSH-2 public-key authentication to be done slightly differently: the data to be signed by the client contains the session ID formatted in a different way. If public-key authentication mysteriously does not work but the Event Log (see section 3.1.3.1) thinks it has successfully sent a signature, it might be worth enabling the workaround for this bug to see if it helps.

If this bug is detected, PuTTY will sign data in the way OpenSSH expects. If this bug is enabled when talking to a correct server, SSH-2 public-key authentication will fail.

This is an SSH-2-specific bug.

4.26.10 ‘Miscomputes SSH-2 encryption keys’

Versions below 2.0.11 of the SSH server software from ssh.com compute the keys for the session encryption incorrectly. This problem can cause various error messages, such as ‘Incoming packet was garbled on decryption’, or possibly even ‘Out of memory’.

If this bug is detected, PuTTY will compute its encryption keys in the same way as the buggy server, so that communication will still be possible. If this bug is enabled when talking to a correct server, communication will fail.

This is an SSH-2-specific bug.

4.26.11 ‘Chokes on SSH-1 ignore messages’

An ignore message (SSH_MSG_IGNORE) is a message in the SSH protocol which can be sent from the client to the server, or from the server to the client, at any time. Either side is required to ignore the message whenever it receives it. PuTTY uses ignore messages to hide the password packet in SSH-1, so that a listener cannot tell the length of the user's password; it also uses ignore messages for connection keepalives (see section 4.14.1).

If this bug is detected, PuTTY will stop using ignore messages. This means that keepalives will stop working, and PuTTY will have to fall back to a secondary defence against SSH-1 password-length eavesdropping. See section 4.26.12. If this bug is enabled when talking to a correct server, the session will succeed, but keepalives will not work and the session might be more vulnerable to eavesdroppers than it could be.

4.26.12 ‘Refuses all SSH-1 password camouflage’

When talking to an SSH-1 server which cannot deal with ignore messages (see section 4.26.11), PuTTY will attempt to disguise the length of the user's password by sending additional padding within the password packet. This is technically a violation of the SSH-1 specification, and so PuTTY will only do it when it cannot use standards-compliant ignore messages as camouflage. In this sense, for a server to refuse to accept a padded password packet is not really a bug, but it does make life inconvenient if the server can also not handle ignore messages.

If this ‘bug’ is detected, PuTTY will assume that neither ignore messages nor padding are acceptable, and that it thus has no choice but to send the user's password with no form of camouflage, so that an eavesdropping user will be easily able to find out the exact length of the password. If this bug is enabled when talking to a correct server, the session will succeed, but will be more vulnerable to eavesdroppers than it could be.

This is an SSH-1-specific bug. SSH-2 is secure against this type of attack.

4.26.13 ‘Chokes on SSH-1 RSA authentication’

Some SSH-1 servers cannot deal with RSA authentication messages at all. If Pageant is running and contains any SSH-1 keys, PuTTY will normally automatically try RSA authentication before falling back to passwords, so these servers will crash when they see the RSA attempt.

If this bug is detected, PuTTY will go straight to password authentication. If this bug is enabled when talking to a correct server, the session will succeed, but of course RSA authentication will be impossible.

This is an SSH-1-specific bug.

4.27 The ‘Bare ssh-connection’ protocol

In addition to SSH itself, PuTTY also supports a second protocol that is derived from SSH. It's listed in the PuTTY GUI under the name ‘Bare ssh-connection’.

This protocol consists of just the innermost of SSH-2's three layers: it leaves out the cryptography layer providing network security, and it leaves out the authentication layer where you provide a username and prove you're allowed to log in as that user.

It is therefore completely unsuited to any network connection. Don't try to use it over a network!

The purpose of this protocol is for various specialist circumstances in which the ‘connection’ is not over a real network, but is a pipe or IPC channel between different processes running on the same computer. In these contexts, the operating system will already have guaranteed that each of the two communicating processes is owned by the expected user (so that no authentication is necessary), and that the communications channel cannot be tapped by a hostile user on the same machine (so that no cryptography is necessary either). Examples of possible uses involve communicating with a strongly separated context such as the inside of a container, or a VM, or a different network namespace.

Explicit support for this protocol is new in PuTTY 0.75. As of 2021-04, the only known server for the bare ssh-connection protocol is the Unix program ‘psusan’ that is also part of the PuTTY tool suite.

(However, this protocol is also the same one used between instances of PuTTY to implement connection sharing: see section 4.17.5. In fact, in the Unix version of PuTTY, when a sharing upstream records ‘Sharing this connection at [pathname]’ in the Event Log, it's possible to connect another instance of PuTTY directly to that Unix socket, by entering its pathname in the host name box and selecting ‘Bare ssh-connection’ as the protocol!)

Many of the options under the SSH panel also affect this protocol, although options to do with cryptography and authentication do not, for obvious reasons.

I repeat, DON'T TRY TO USE THIS PROTOCOL FOR NETWORK CONNECTIONS! That's not what it's for, and it's not at all safe to do it.

4.28 The Serial panel

The Serial panel allows you to configure options that only apply when PuTTY is connecting to a local serial line.

4.28.1 Selecting a serial line to connect to

The ‘Serial line to connect to’ box allows you to choose which serial line you want PuTTY to talk to, if your computer has more than one serial port.

On Windows, the first serial line is called COM1, and if there is a second it is called COM2, and so on.

This configuration setting is also visible on the Session panel, where it replaces the ‘Host Name’ box (see section 4.1.1) if the connection type is set to ‘Serial’.

4.28.2 Selecting the speed of your serial line

The ‘Speed’ box allows you to choose the speed (or ‘baud rate’) at which to talk to the serial line. Typical values might be 9600, 19200, 38400 or 57600. Which one you need will depend on the device at the other end of the serial cable; consult the manual for that device if you are in doubt.

This configuration setting is also visible on the Session panel, where it replaces the ‘Port’ box (see section 4.1.1) if the connection type is set to ‘Serial’.

4.28.3 Selecting the number of data bits

The ‘Data bits’ box allows you to choose how many data bits are transmitted in each byte sent or received through the serial line. Typical values are 7 or 8.

4.28.4 Selecting the number of stop bits

The ‘Stop bits’ box allows you to choose how many stop bits are used in the serial line protocol. Typical values are 1, 1.5 or 2.

4.28.5 Selecting the serial parity checking scheme

The ‘Parity’ box allows you to choose what type of parity checking is used on the serial line. The settings are:

  • ‘None’: no parity bit is sent at all.
  • ‘Odd’: an extra parity bit is sent alongside each byte, and arranged so that the total number of 1 bits is odd.
  • ‘Even’: an extra parity bit is sent alongside each byte, and arranged so that the total number of 1 bits is even.
  • ‘Mark’: an extra parity bit is sent alongside each byte, and always set to 1.
  • ‘Space’: an extra parity bit is sent alongside each byte, and always set to 0.

4.28.6 Selecting the serial flow control scheme

The ‘Flow control’ box allows you to choose what type of flow control checking is used on the serial line. The settings are:

  • ‘None’: no flow control is done. Data may be lost if either side attempts to send faster than the serial line permits.
  • ‘XON/XOFF’: flow control is done by sending XON and XOFF characters within the data stream.
  • ‘RTS/CTS’: flow control is done using the RTS and CTS wires on the serial line.
  • ‘DSR/DTR’: flow control is done using the DSR and DTR wires on the serial line.

4.29 The Telnet panel

The Telnet panel allows you to configure options that only apply to Telnet sessions.

4.29.1 ‘Handling of OLD_ENVIRON ambiguity’

The original Telnet mechanism for passing environment variables was badly specified. At the time the standard (RFC 1408) was written, BSD telnet implementations were already supporting the feature, and the intention of the standard was to describe the behaviour the BSD implementations were already using.

Sadly there was a typing error in the standard when it was issued, and two vital function codes were specified the wrong way round. BSD implementations did not change, and the standard was not corrected. Therefore, it's possible you might find either BSD or RFC-compliant implementations out there. This switch allows you to choose which one PuTTY claims to be.

The problem was solved by issuing a second standard, defining a new Telnet mechanism called NEW_ENVIRON, which behaved exactly like the original OLD_ENVIRON but was not encumbered by existing implementations. Most Telnet servers now support this, and it's unambiguous. This feature should only be needed if you have trouble passing environment variables to quite an old server.

4.29.2 Passive and active Telnet negotiation modes

In a Telnet connection, there are two types of data passed between the client and the server: actual text, and negotiations about which Telnet extra features to use.

PuTTY can use two different strategies for negotiation:

  • In active mode, PuTTY starts to send negotiations as soon as the connection is opened.
  • In passive mode, PuTTY will wait to negotiate until it sees a negotiation from the server.

The obvious disadvantage of passive mode is that if the server is also operating in a passive mode, then negotiation will never begin at all. For this reason PuTTY defaults to active mode.

However, sometimes passive mode is required in order to successfully get through certain types of firewall and Telnet proxy server. If you have confusing trouble with a firewall, you could try enabling passive mode to see if it helps.

4.29.3 ‘Keyboard sends Telnet special commands’

If this box is checked, several key sequences will have their normal actions modified:

  • the Backspace key on the keyboard will send the Telnet special backspace code;
  • Control-C will send the Telnet special Interrupt Process code;
  • Control-Z will send the Telnet special Suspend Process code.

You probably shouldn't enable this unless you know what you're doing.

4.29.4 ‘Return key sends Telnet New Line instead of ^M’

Unlike most other remote login protocols, the Telnet protocol has a special ‘new line’ code that is not the same as the usual line endings of Control-M or Control-J. By default, PuTTY sends the Telnet New Line code when you press Return, instead of sending Control-M as it does in most other protocols.

Most Unix-style Telnet servers don't mind whether they receive Telnet New Line or Control-M; some servers do expect New Line, and some servers prefer to see ^M. If you are seeing surprising behaviour when you press Return in a Telnet session, you might try turning this option off to see if it helps.

4.30 The Rlogin panel

The Rlogin panel allows you to configure options that only apply to Rlogin sessions.

4.30.1 ‘Local username’

Rlogin allows an automated (password-free) form of login by means of a file called .rhosts on the server. You put a line in your .rhosts file saying something like jbloggs@pc1.example.com, and then when you make an Rlogin connection the client transmits the username of the user running the Rlogin client. The server checks the username and hostname against .rhosts, and if they match it does not ask for a password.

This only works because Unix systems contain a safeguard to stop a user from pretending to be another user in an Rlogin connection. Rlogin connections have to come from port numbers below 1024, and Unix systems prohibit this to unprivileged processes; so when the server sees a connection from a low-numbered port, it assumes the client end of the connection is held by a privileged (and therefore trusted) process, so it believes the claim of who the user is.

Windows does not have this restriction: any user can initiate an outgoing connection from a low-numbered port. Hence, the Rlogin .rhosts mechanism is completely useless for securely distinguishing several different users on a Windows machine. If you have a .rhosts entry pointing at a Windows PC, you should assume that anyone using that PC can spoof your username in an Rlogin connection and access your account on the server.

The ‘Local username’ control allows you to specify what user name PuTTY should claim you have, in case it doesn't match your Windows user name (or in case you didn't bother to set up a Windows user name).

4.31 The SUPDUP panel

The SUPDUP panel allows you to configure options that only apply to SUPDUP sessions. See section 3.10 for more about the SUPDUP protocol.

4.31.1 ‘Location string’

In SUPDUP, the client sends a piece of text of its choice to the server giving the user's location. This is typically displayed in lists of logged-in users.

By default, PuTTY just defaults this to "The Internet". If you want your location to show up as something more specific, you can configure it here.

4.31.2 ‘Extended ASCII Character set’

This declares what kind of character set extension your terminal supports. If the server supports it, it will send text using that character set. ‘None’ means the standard 95 printable ASCII characters. ‘ITS’ means ASCII extended with printable characters in the control character range. This character set is documented in the SUPDUP protocol definition. ‘WAITS’ is similar to ‘ITS’ but uses some alternative characters in the extended set: most prominently, it will display arrows instead of ^ and _, and } instead of ~. ‘ITS’ extended ASCII is used by ITS and Lisp machines, whilst ‘WAITS’ is only used by the WAITS operating system from the Stanford AI Laboratory.

4.31.3 ‘**MORE** processing’

When **MORE** processing is enabled, the server causes output to pause at the bottom of the screen, until a space is typed.

4.31.4 ‘Terminal scrolling’

This controls whether the terminal will perform scrolling then the cursor goes below the last line, or if the cursor will return to the first line.

4.32 Storing configuration in a file

PuTTY does not currently support storing its configuration in a file instead of the Registry. However, you can work around this with a couple of batch files.

You will need a file called (say) PUTTY.BAT which imports the contents of a file into the Registry, then runs PuTTY, exports the contents of the Registry back into the file, and deletes the Registry entries. This can all be done using the Regedit command line options, so it's all automatic. Here is what you need in PUTTY.BAT:

@ECHO OFF
regedit /s putty.reg
regedit /s puttyrnd.reg
start /w putty.exe
regedit /ea new.reg HKEY_CURRENT_USER\Software\SimonTatham\PuTTY
copy new.reg putty.reg
del new.reg
regedit /s puttydel.reg

This batch file needs two auxiliary files: PUTTYRND.REG which sets up an initial safe location for the PUTTY.RND random seed file, and PUTTYDEL.REG which destroys everything in the Registry once it's been successfully saved back to the file.

Here is PUTTYDEL.REG:

REGEDIT4

[-HKEY_CURRENT_USER\Software\SimonTatham\PuTTY]

Here is an example PUTTYRND.REG file:

REGEDIT4

[HKEY_CURRENT_USER\Software\SimonTatham\PuTTY]
"RandSeedFile"="a:\\putty.rnd"

You should replace a:\putty.rnd with the location where you want to store your random number data. If the aim is to carry around PuTTY and its settings on one USB stick, you probably want to store it on the USB stick.


If you want to provide feedback on this manual or on the PuTTY tools themselves, see the Feedback page.

[PuTTY release 0.76]
putty-0.76/doc/Chapter5.html0000644000175000017500000004441714072266315012702 00000000000000 Using PSCP to transfer files securely

Previous | Contents | Index | Next

Chapter 5: Using PSCP to transfer files securely

PSCP, the PuTTY Secure Copy client, is a tool for transferring files securely between computers using an SSH connection.

If you have an SSH-2 server, you might prefer PSFTP (see chapter 6) for interactive use. PSFTP does not in general work with SSH-1 servers, however.

5.1 Starting PSCP

PSCP is a command line application. This means that you cannot just double-click on its icon to run it and instead you have to bring up a console window. With Windows 95, 98, and ME, this is called an ‘MS-DOS Prompt’ and with Windows NT, 2000, and XP, it is called a ‘Command Prompt’. It should be available from the Programs section of your Start Menu.

To start PSCP it will need either to be on your PATH or in your current directory. To add the directory containing PSCP to your PATH environment variable, type into the console window:

set PATH=C:\path\to\putty\directory;%PATH%

This will only work for the lifetime of that particular console window. To set your PATH more permanently on Windows NT, 2000, and XP, use the Environment tab of the System Control Panel. On Windows 95, 98, and ME, you will need to edit your AUTOEXEC.BAT to include a set command like the one above.

5.2 PSCP Usage

Once you've got a console window to type into, you can just type 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:\>pscp
PuTTY Secure Copy client
Release 0.76
Usage: pscp [options] [user@]host:source target
       pscp [options] source [source...] [user@]host:target
       pscp [options] -ls [user@]host:filespec
Options:
  -V        print version information and exit
  -pgpfp    print PGP key fingerprints and exit
  -p        preserve file attributes
  -q        quiet, don't show statistics
  -r        copy directories recursively
  -v        show verbose messages
  -load sessname  Load settings from saved session
  -P port   connect to specified port
  -l user   connect with specified username
  -pw passw login with specified password
  -1 -2     force use of particular SSH protocol version
  -ssh -ssh-connection
            force use of particular SSH protocol variant
  -4 -6     force use of IPv4 or IPv6
  -C        enable compression
  -i key    private key file for user authentication
  -noagent  disable use of Pageant
  -agent    enable use of Pageant
  -no-trivial-auth
            disconnect if SSH authentication succeeds trivially
  -hostkey keyid
            manually specify a host key (may be repeated)
  -batch    disable all interactive prompts
  -no-sanitise-stderr  don't strip control chars from standard error
  -proxycmd command
            use 'command' as local proxy
  -unsafe   allow server-side wildcards (DANGEROUS)
  -sftp     force use of SFTP protocol
  -scp      force use of SCP protocol
  -sshlog file
  -sshrawlog file
            log protocol details to a file
  -logoverwrite
  -logappend
            control what happens when a log file already exists

(PSCP's interface is much like the Unix scp command, if you're familiar with that.)

5.2.1 The basics

To receive (a) file(s) from a remote server:

pscp [options] [user@]host:source target

So to copy the file /etc/hosts from the server example.com as user fred to the file c:\temp\example-hosts.txt, you would type:

pscp fred@example.com:/etc/hosts c:\temp\example-hosts.txt

To send (a) file(s) to a remote server:

pscp [options] source [source...] [user@]host:target

So to copy the local file c:\documents\foo.txt to the server example.com as user fred to the file /tmp/foo you would type:

pscp c:\documents\foo.txt fred@example.com:/tmp/foo

You can use wildcards to transfer multiple files in either direction, like this:

pscp c:\documents\*.doc fred@example.com:docfiles
pscp fred@example.com:source/*.c c:\source

However, in the second case (using a wildcard for multiple remote files) you may see a warning saying something like ‘warning: remote host tried to write to a file called ‘terminal.c’ when we requested a file called ‘*.c’. If this is a wildcard, consider upgrading to SSH-2 or using the ‘-unsafe’ option. Renaming of this file has been disallowed’.

This is due to a fundamental insecurity in the old-style SCP protocol: the client sends the wildcard string (*.c) to the server, and the server sends back a sequence of file names that match the wildcard pattern. However, there is nothing to stop the server sending back a different pattern and writing over one of your other files: if you request *.c, the server might send back the file name AUTOEXEC.BAT and install a virus for you. Since the wildcard matching rules are decided by the server, the client cannot reliably verify that the filenames sent back match the pattern.

PSCP will attempt to use the newer SFTP protocol (part of SSH-2) where possible, which does not suffer from this security flaw. If you are talking to an SSH-2 server which supports SFTP, you will never see this warning. (You can force use of the SFTP protocol, if available, with -sftp - see section 5.2.2.6.)

If you really need to use a server-side wildcard with an SSH-1 server, you can use the -unsafe command line option with PSCP:

pscp -unsafe fred@example.com:source/*.c c:\source

This will suppress the warning message and the file transfer will happen. However, you should be aware that by using this option you are giving the server the ability to write to any file in the target directory, so you should only use this option if you trust the server administrator not to be malicious (and not to let the server machine be cracked by malicious people). Alternatively, do any such download in a newly created empty directory. (Even in ‘unsafe’ mode, PSCP will still protect you against the server trying to get out of that directory using pathnames including ‘..’.)

5.2.1.1 user

The login name on the remote server. If this is omitted, and host is a PuTTY saved session, PSCP will use any username specified by that saved session. Otherwise, PSCP will attempt to use the local Windows username.

5.2.1.2 host

The name of the remote server, or the name of an existing PuTTY saved session. In the latter case, the session's settings for hostname, port number, cipher type and username will be used.

5.2.1.3 source

One or more source files. Wildcards are allowed. The syntax of wildcards depends on the system to which they apply, so if you are copying from a Windows system to a UNIX system, you should use Windows wildcard syntax (e.g. *.*), but if you are copying from a UNIX system to a Windows system, you would use the wildcard syntax allowed by your UNIX shell (e.g. *).

If the source is a remote server and you do not specify a full pathname (in UNIX, a pathname beginning with a / (slash) character), what you specify as a source will be interpreted relative to your home directory on the remote server.

5.2.1.4 target

The filename or directory to put the file(s). When copying from a remote server to a local host, you may wish simply to place the file(s) in the current directory. To do this, you should specify a target of .. For example:

pscp fred@example.com:/home/tom/.emacs .

...would copy /home/tom/.emacs on the remote server to the current directory.

As with the source parameter, if the target is on a remote server and is not a full path name, it is interpreted relative to your home directory on the remote server.

5.2.2 Options

PSCP accepts all the general command line options supported by the PuTTY tools, except the ones which make no sense in a file transfer utility. See section 3.11.3 for a description of these options. (The ones not supported by PSCP are clearly marked.)

PSCP also supports some of its own options. The following sections describe PSCP's specific command-line options.

5.2.2.1 -ls list remote files

If the -ls option is given, no files are transferred; instead, remote files are listed. Only a hostname specification and optional remote file specification need be given. For example:

pscp -ls fred@example.com:dir1

The SCP protocol does not contain within itself a means of listing files. If SCP is in use, this option therefore assumes that the server responds appropriately to the command ls -la; this may not work with all servers.

If SFTP is in use, this option should work with all servers.

5.2.2.2 -p preserve file attributes

By default, files copied with PSCP are timestamped with the date and time they were copied. The -p option preserves the original timestamp on copied files.

5.2.2.3 -q quiet, don't show statistics

By default, PSCP displays a meter displaying the progress of the current transfer:

mibs.tar          |   168 kB |  84.0 kB/s | ETA: 00:00:13 |  13%

The fields in this display are (from left to right), filename, size (in kilobytes) of file transferred so far, estimate of how fast the file is being transferred (in kilobytes per second), estimated time that the transfer will be complete, and percentage of the file so far transferred. The -q option to PSCP suppresses the printing of these statistics.

5.2.2.4 -r copies directories recursively

By default, PSCP will only copy files. Any directories you specify to copy will be skipped, as will their contents. The -r option tells PSCP to descend into any directories you specify, and to copy them and their contents. This allows you to use PSCP to transfer whole directory structures between machines.

5.2.2.5 -batch avoid interactive prompts

If you use the -batch option, PSCP will never give an interactive prompt while establishing the connection. If the server's host key is invalid, for example (see section 2.2), then the connection will simply be abandoned instead of asking you what to do next.

This may help PSCP's behaviour when it is used in automated scripts: using -batch, if something goes wrong at connection time, the batch job will fail rather than hang.

5.2.2.6 -sftp, -scp force use of particular file transfer protocol

As mentioned in section 5.2.1, there are two different file transfer protocols in use with SSH. Despite its name, PSCP (like many other ostensible scp clients) can use either of these protocols.

The older SCP protocol does not have a written specification and leaves a lot of detail to the server platform. Wildcards are expanded on the server. The simple design means that any wildcard specification supported by the server platform (such as brace expansion) can be used, but also leads to interoperability issues such as with filename quoting (for instance, where filenames contain spaces), and also the security issue described in section 5.2.1.

The newer SFTP protocol, which is usually associated with SSH-2 servers, is specified in a more platform independent way, and leaves issues such as wildcard syntax up to the client. (PuTTY's SFTP wildcard syntax is described in section 6.2.2.) This makes it more consistent across platforms, more suitable for scripting and automation, and avoids security issues with wildcard matching.

Normally PSCP will attempt to use the SFTP protocol, and only fall back to the SCP protocol if SFTP is not available on the server.

The -scp option forces PSCP to use the SCP protocol or quit.

The -sftp option forces PSCP to use the SFTP protocol or quit. When this option is specified, PSCP looks harder for an SFTP server, which may allow use of SFTP with SSH-1 depending on server setup.

5.2.2.7 -no-sanitise-stderr: control error message sanitisation

The -no-sanitise-stderr option will cause PSCP to pass through the server's standard-error stream literally, without stripping control characters from it first. This might be useful if the server were sending coloured error messages, but it also gives the server the ability to have unexpected effects on your terminal display. For more discussion, see section 7.2.3.5.

5.2.3 Return value

PSCP returns an ERRORLEVEL of zero (success) only if the files were correctly transferred. You can test for this in a batch file, using code such as this:

pscp file*.* user@hostname:
if errorlevel 1 echo There was an error

5.2.4 Using public key authentication with PSCP

Like PuTTY, PSCP can authenticate using a public key instead of a password. There are three ways you can do this.

Firstly, PSCP can use PuTTY saved sessions in place of hostnames (see section 5.2.1.2). So you would do this:

  • Run PuTTY, and create a PuTTY saved session (see section 4.1.2) which specifies your private key file (see section 4.21.9). You will probably also want to specify a username to log in as (see section 4.15.1).
  • In PSCP, you can now use the name of the session instead of a hostname: type pscp sessionname:file localfile, where sessionname is replaced by the name of your saved session.

Secondly, you can supply the name of a private key file on the command line, with the -i option. See section 3.11.3.18 for more information.

Thirdly, PSCP will attempt to authenticate using Pageant if Pageant is running (see chapter 9). So you would do this:

  • Ensure Pageant is running, and has your private key stored in it.
  • Specify a user and host name to PSCP as normal. PSCP will automatically detect Pageant and try to use the keys within it.

For more general information on public-key authentication, see chapter 8.


If you want to provide feedback on this manual or on the PuTTY tools themselves, see the Feedback page.

[PuTTY release 0.76]
putty-0.76/doc/Chapter6.html0000644000175000017500000007472514072266315012710 00000000000000 Using PSFTP to transfer files securely

Previous | Contents | Index | Next

Chapter 6: Using PSFTP to transfer files securely

PSFTP, the PuTTY SFTP client, is a tool for transferring files securely between computers using an SSH connection.

PSFTP differs from PSCP in the following ways:

  • PSCP should work on virtually every SSH server. PSFTP uses the new SFTP protocol, which is a feature of SSH-2 only. (PSCP will also use this protocol if it can, but there is an SSH-1 equivalent it can fall back to if it cannot.)
  • PSFTP allows you to run an interactive file transfer session, much like the Windows ftp program. You can list the contents of directories, browse around the file system, issue multiple get and put commands, and eventually log out. By contrast, PSCP is designed to do a single file transfer operation and immediately terminate.

6.1 Starting PSFTP

The usual way to start PSFTP is from a command prompt, much like PSCP. To do this, it will need either to be on your PATH or in your current directory. To add the directory containing PSFTP to your PATH environment variable, type into the console window:

set PATH=C:\path\to\putty\directory;%PATH%

Unlike PSCP, however, PSFTP has no complex command-line syntax; you just specify a host name and perhaps a user name:

psftp server.example.com

or perhaps

psftp fred@server.example.com

Alternatively, if you just type psftp on its own (or double-click the PSFTP icon in the Windows GUI), you will see the PSFTP prompt, and a message telling you PSFTP has not connected to any server:

C:\>psftp
psftp: no hostname specified; use "open host.name" to connect
psftp>

At this point you can type open server.example.com or open fred@server.example.com to start a session.

PSFTP accepts all the general command line options supported by the PuTTY tools, except the ones which make no sense in a file transfer utility. See section 3.11.3 for a description of these options. (The ones not supported by PSFTP are clearly marked.)

PSFTP also supports some of its own options. The following sections describe PSFTP's specific command-line options.

6.1.1 -b: specify a file containing batch commands

In normal operation, PSFTP is an interactive program which displays a command line and accepts commands from the keyboard.

If you need to do automated tasks with PSFTP, you would probably prefer to specify a set of commands in advance and have them executed automatically. The -b option allows you to do this. You use it with a file name containing batch commands. For example, you might create a file called myscript.scr containing lines like this:

cd /home/ftp/users/jeff
del jam-old.tar.gz
ren jam.tar.gz jam-old.tar.gz
put jam.tar.gz
chmod a+r jam.tar.gz

and then you could run the script by typing

psftp user@hostname -b myscript.scr

When you run a batch script in this way, PSFTP will abort the script if any command fails to complete successfully. To change this behaviour, you can add the -be option (section 6.1.3).

PSFTP will terminate after it finishes executing the batch script.

6.1.2 -bc: display batch commands as they are run

The -bc option alters what PSFTP displays while processing a batch script specified with -b. With the -bc option, PSFTP will display prompts and commands just as if the commands had been typed at the keyboard. So instead of seeing this:

C:\>psftp fred@hostname -b batchfile
Sent username "fred"
Remote working directory is /home/fred
Listing directory /home/fred/lib
drwxrwsr-x    4 fred     fred         1024 Sep  6 10:42 .
drwxr-sr-x   25 fred     fred         2048 Dec 14 09:36 ..
drwxrwsr-x    3 fred     fred         1024 Apr 17  2000 jed
lrwxrwxrwx    1 fred     fred           24 Apr 17  2000 timber
drwxrwsr-x    2 fred     fred         1024 Mar 13  2000 trn

you might see this:

C:\>psftp fred@hostname -bc -b batchfile
Sent username "fred"
Remote working directory is /home/fred
psftp> dir lib
Listing directory /home/fred/lib
drwxrwsr-x    4 fred     fred         1024 Sep  6 10:42 .
drwxr-sr-x   25 fred     fred         2048 Dec 14 09:36 ..
drwxrwsr-x    3 fred     fred         1024 Apr 17  2000 jed
lrwxrwxrwx    1 fred     fred           24 Apr 17  2000 timber
drwxrwsr-x    2 fred     fred         1024 Mar 13  2000 trn
psftp> quit

6.1.3 -be: continue batch processing on errors

When running a batch file, this additional option causes PSFTP to continue processing even if a command fails to complete successfully.

You might want this to happen if you wanted to delete a file and didn't care if it was already not present, for example.

6.1.4 -batch: avoid interactive prompts

If you use the -batch option, PSFTP will never give an interactive prompt while establishing the connection. If the server's host key is invalid, for example (see section 2.2), then the connection will simply be abandoned instead of asking you what to do next.

This may help PSFTP's behaviour when it is used in automated scripts: using -batch, if something goes wrong at connection time, the batch job will fail rather than hang.

6.1.4.1 -no-sanitise-stderr: control error message sanitisation

The -no-sanitise-stderr option will cause PSFTP to pass through the server's standard-error stream literally, without stripping control characters from it first. This might be useful if the server were sending coloured error messages, but it also gives the server the ability to have unexpected effects on your terminal display. For more discussion, see section 7.2.3.5.

6.2 Running PSFTP

Once you have started your PSFTP session, you will see a psftp> prompt. You can now type commands to perform file-transfer functions. This section lists all the available commands.

Any line starting with a # will be treated as a comment and ignored.

6.2.1 General quoting rules for PSFTP commands

Most PSFTP commands are considered by the PSFTP command interpreter as a sequence of words, separated by spaces. For example, the command ren oldfilename newfilename splits up into three words: ren (the command name), oldfilename (the name of the file to be renamed), and newfilename (the new name to give the file).

Sometimes you will need to specify file names that contain spaces. In order to do this, you can surround the file name with double quotes. This works equally well for local file names and remote file names:

psftp> get "spacey file name.txt" "save it under this name.txt"

The double quotes themselves will not appear as part of the file names; they are removed by PSFTP and their only effect is to stop the spaces inside them from acting as word separators.

If you need to use a double quote (on some types of remote system, such as Unix, you are allowed to use double quotes in file names), you can do this by doubling it. This works both inside and outside double quotes. For example, this command

psftp> ren ""this"" "a file with ""quotes"" in it"

will take a file whose current name is "this" (with a double quote character at the beginning and the end) and rename it to a file whose name is a file with "quotes" in it.

(The one exception to the PSFTP quoting rules is the ! command, which passes its command line straight to Windows without splitting it up into words at all. See section 6.2.19.)

6.2.2 Wildcards in PSFTP

Several commands in PSFTP support ‘wildcards’ to select multiple files.

For local file specifications (such as the first argument to put), wildcard rules for the local operating system are used. For instance, PSFTP running on Windows might require the use of *.* where PSFTP on Unix would need *.

For remote file specifications (such as the first argument to get), PSFTP uses a standard wildcard syntax (similar to POSIX wildcards):

  • * matches any sequence of characters (including a zero-length sequence).
  • ? matches exactly one character.
  • [abc] matches exactly one character which can be a, b, or c.

    [a-z] matches any character in the range a to z.

    [^abc] matches a single character that is not a, b, or c.

    Special cases: [-a] matches a literal hyphen (-) or a; [^-a] matches all other characters. [a^] matches a literal caret (^) or a.

  • \ (backslash) before any of the above characters (or itself) removes that character's special meaning.

A leading period (.) on a filename is not treated specially, unlike in some Unix contexts; get * will fetch all files, whether or not they start with a leading period.

6.2.3 The open command: start a session

If you started PSFTP by double-clicking in the GUI, or just by typing psftp at the command line, you will need to open a connection to an SFTP server before you can issue any other commands (except help and quit).

To create a connection, type open host.name, or if you need to specify a user name as well you can type open user@host.name. You can optionally specify a port as well: open user@host.name 22.

Once you have issued this command, you will not be able to issue it again, even if the command fails (for example, if you mistype the host name or the connection times out). So if the connection is not opened successfully, PSFTP will terminate immediately.

6.2.4 The quit command: end your session

When you have finished your session, type the command quit to close the connection, terminate PSFTP and return to the command line (or just close the PSFTP console window if you started it from the GUI).

You can also use the bye and exit commands, which have exactly the same effect.

6.2.5 The close command: close your connection

If you just want to close the network connection but keep PSFTP running, you can use the close command. You can then use the open command to open a new connection.

6.2.6 The help command: get quick online help

If you type help, PSFTP will give a short list of the available commands.

If you type help with a command name - for example, help get - then PSFTP will give a short piece of help on that particular command.

6.2.7 The cd and pwd commands: changing the remote working directory

PSFTP maintains a notion of your ‘working directory’ on the server. This is the default directory that other commands will operate on. For example, if you type get filename.dat then PSFTP will look for filename.dat in your remote working directory on the server.

To change your remote working directory, use the cd command. If you don't provide an argument, cd will return you to your home directory on the server (more precisely, the remote directory you were in at the start of the connection).

To display your current remote working directory, type pwd.

6.2.8 The lcd and lpwd commands: changing the local working directory

As well as having a working directory on the remote server, PSFTP also has a working directory on your local machine (just like any other Windows process). This is the default local directory that other commands will operate on. For example, if you type get filename.dat then PSFTP will save the resulting file as filename.dat in your local working directory.

To change your local working directory, use the lcd command. To display your current local working directory, type lpwd.

6.2.9 The get command: fetch a file from the server

To download a file from the server and store it on your local PC, you use the get command.

In its simplest form, you just use this with a file name:

get myfile.dat

If you want to store the file locally under a different name, specify the local file name after the remote one:

get myfile.dat newname.dat

This will fetch the file on the server called myfile.dat, but will save it to your local machine under the name newname.dat.

To fetch an entire directory recursively, you can use the -r option:

get -r mydir
get -r mydir newname

(If you want to fetch a file whose name starts with a hyphen, you may have to use the -- special argument, which stops get from interpreting anything as a switch after it. For example, ‘get -- -silly-name-’.)

6.2.10 The put command: send a file to the server

To upload a file to the server from your local PC, you use the put command.

In its simplest form, you just use this with a file name:

put myfile.dat

If you want to store the file remotely under a different name, specify the remote file name after the local one:

put myfile.dat newname.dat

This will send the local file called myfile.dat, but will store it on the server under the name newname.dat.

To send an entire directory recursively, you can use the -r option:

put -r mydir
put -r mydir newname

(If you want to send a file whose name starts with a hyphen, you may have to use the -- special argument, which stops put from interpreting anything as a switch after it. For example, ‘put -- -silly-name-’.)

6.2.11 The mget and mput commands: fetch or send multiple files

mget works almost exactly like get, except that it allows you to specify more than one file to fetch at once. You can do this in two ways:

  • by giving two or more explicit file names (‘mget file1.txt file2.txt’)
  • by using a wildcard (‘mget *.txt’).

Every argument to mget is treated as the name of a file to fetch (unlike get, which will interpret at most one argument like that, and a second argument will be treated as an alternative name under which to store the retrieved file), or a wildcard expression matching more than one file.

The -r and -- options from get are also available with mget.

mput is similar to put, with the same differences.

6.2.12 The reget and reput commands: resuming file transfers

If a file transfer fails half way through, and you end up with half the file stored on your disk, you can resume the file transfer using the reget and reput commands. These work exactly like the get and put commands, but they check for the presence of the half-written destination file and start transferring from where the last attempt left off.

The syntax of reget and reput is exactly the same as the syntax of get and put:

reget myfile.dat
reget myfile.dat newname.dat
reget -r mydir

These commands are intended mainly for resuming interrupted transfers. They assume that the remote file or directory structure has not changed in any way; if there have been changes, you may end up with corrupted files. In particular, the -r option will not pick up changes to files or directories already transferred in full.

6.2.13 The dir command: list remote files

To list the files in your remote working directory, just type dir.

You can also list the contents of a different directory by typing dir followed by the directory name:

dir /home/fred
dir sources

And you can list a subset of the contents of a directory by providing a wildcard:

dir /home/fred/*.txt
dir sources/*.c

The ls command works exactly the same way as dir.

6.2.14 The chmod command: change permissions on remote files

PSFTP allows you to modify the file permissions on files and directories on the server. You do this using the chmod command, which works very much like the Unix chmod command.

The basic syntax is chmod modes file, where modes represents a modification to the file permissions, and file is the filename to modify. You can specify multiple files or wildcards. For example:

chmod go-rwx,u+w privatefile
chmod a+r public*
chmod 640 groupfile1 groupfile2

The modes parameter can be a set of octal digits in the Unix style. (If you don't know what this means, you probably don't want to be using it!) Alternatively, it can be a list of permission modifications, separated by commas. Each modification consists of:

  • The people affected by the modification. This can be u (the owning user), g (members of the owning group), or o (everybody else - ‘others’), or some combination of those. It can also be a (‘all’) to affect everybody at once.
  • A + or - sign, indicating whether permissions are to be added or removed.
  • The actual permissions being added or removed. These can be r (permission to read the file), w (permission to write to the file), and x (permission to execute the file, or in the case of a directory, permission to access files within the directory).

So the above examples would do:

  • The first example: go-rwx removes read, write and execute permissions for members of the owning group and everybody else (so the only permissions left are the ones for the file owner). u+w adds write permission for the file owner.
  • The second example: a+r adds read permission for everybody to all files and directories starting with ‘public’.

In addition to all this, there are a few extra special cases for Unix systems. On non-Unix systems these are unlikely to be useful:

  • You can specify u+s and u-s to add or remove the Unix set-user-ID bit. This is typically only useful for special purposes; refer to your Unix documentation if you're not sure about it.
  • You can specify g+s and g-s to add or remove the Unix set-group-ID bit. On a file, this works similarly to the set-user-ID bit (see your Unix documentation again); on a directory it ensures that files created in the directory are accessible by members of the group that owns the directory.
  • You can specify +t and -t to add or remove the Unix ‘sticky bit’. When applied to a directory, this means that the owner of a file in that directory can delete the file (whereas normally only the owner of the directory would be allowed to).

6.2.15 The del command: delete remote files

To delete a file on the server, type del and then the filename or filenames:

del oldfile.dat
del file1.txt file2.txt
del *.o

Files will be deleted without further prompting, even if multiple files are specified.

del will only delete files. You cannot use it to delete directories; use rmdir for that.

The rm command works exactly the same way as del.

6.2.16 The mkdir command: create remote directories

To create a directory on the server, type mkdir and then the directory name:

mkdir newstuff

You can specify multiple directories to create at once:

mkdir dir1 dir2 dir3

6.2.17 The rmdir command: remove remote directories

To remove a directory on the server, type rmdir and then the directory name or names:

rmdir oldstuff
rmdir *.old ancient

Directories will be deleted without further prompting, even if multiple directories are specified.

Most SFTP servers will probably refuse to remove a directory if the directory has anything in it, so you will need to delete the contents first.

6.2.18 The mv command: move and rename remote files

To rename a single file on the server, type mv, then the current file name, and then the new file name:

mv oldfile newname

You can also move the file into a different directory and change the name:

mv oldfile dir/newname

To move one or more files into an existing subdirectory, specify the files (using wildcards if desired), and then the destination directory:

mv file dir
mv file1 dir1/file2 dir2
mv *.c *.h ..

The rename and ren commands work exactly the same way as mv.

6.2.19 The ! command: run a local Windows command

You can run local Windows commands using the ! command. This is the only PSFTP command that is not subject to the command quoting rules given in section 6.2.1. If any command line begins with the ! character, then the rest of the line will be passed straight to Windows without further translation.

For example, if you want to move an existing copy of a file out of the way before downloading an updated version, you might type:

psftp> !ren myfile.dat myfile.bak
psftp> get myfile.dat

using the Windows ren command to rename files on your local PC.

6.3 Using public key authentication with PSFTP

Like PuTTY, PSFTP can authenticate using a public key instead of a password. There are three ways you can do this.

Firstly, PSFTP can use PuTTY saved sessions in place of hostnames. So you might do this:

  • Run PuTTY, and create a PuTTY saved session (see section 4.1.2) which specifies your private key file (see section 4.21.9). You will probably also want to specify a username to log in as (see section 4.15.1).
  • In PSFTP, you can now use the name of the session instead of a hostname: type psftp sessionname, where sessionname is replaced by the name of your saved session.

Secondly, you can supply the name of a private key file on the command line, with the -i option. See section 3.11.3.18 for more information.

Thirdly, PSFTP will attempt to authenticate using Pageant if Pageant is running (see chapter 9). So you would do this:

  • Ensure Pageant is running, and has your private key stored in it.
  • Specify a user and host name to PSFTP as normal. PSFTP will automatically detect Pageant and try to use the keys within it.

For more general information on public-key authentication, see chapter 8.


If you want to provide feedback on this manual or on the PuTTY tools themselves, see the Feedback page.

[PuTTY release 0.76]
putty-0.76/doc/Chapter7.html0000644000175000017500000005334114072266315012700 00000000000000 Using the command-line connection tool Plink

Previous | Contents | Index | Next

Chapter 7: Using the command-line connection tool Plink

Plink is a command-line connection tool similar to UNIX ssh. It is mostly used for automated operations, such as making CVS access a repository on a remote server.

Plink is probably not what you want if you want to run an interactive session in a console window.

7.1 Starting Plink

Plink is a command line application. This means that you cannot just double-click on its icon to run it and instead you have to bring up a console window. In Windows 95, 98, and ME, this is called an ‘MS-DOS Prompt’, and in Windows NT, 2000, and XP, it is called a ‘Command Prompt’. It should be available from the Programs section of your Start Menu.

In order to use Plink, the file plink.exe will need either to be on your PATH or in your current directory. To add the directory containing Plink to your PATH environment variable, type into the console window:

set PATH=C:\path\to\putty\directory;%PATH%

This will only work for the lifetime of that particular console window. To set your PATH more permanently on Windows NT, 2000, and XP, use the Environment tab of the System Control Panel. On Windows 95, 98, and ME, you will need to edit your AUTOEXEC.BAT to include a set command like the one above.

7.2 Using Plink

This section describes the basics of how to use Plink for interactive logins and for automated processes.

Once you've got a console window to type into, you can just type 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:\>plink
Plink: command-line connection utility
Release 0.76
Usage: plink [options] [user@]host [command]
       ("host" can also be a PuTTY saved session name)
Options:
  -V        print version information and exit
  -pgpfp    print PGP key fingerprints and exit
  -v        show verbose messages
  -load sessname  Load settings from saved session
  -ssh -telnet -rlogin -raw -serial
            force use of a particular protocol
  -ssh-connection
            force use of the bare ssh-connection protocol
  -P port   connect to specified port
  -l user   connect with specified username
  -batch    disable all interactive prompts
  -proxycmd command
            use 'command' as local proxy
  -sercfg configuration-string (e.g. 19200,8,n,1,X)
            Specify the serial configuration (serial only)
The following options only apply to SSH connections:
  -pw passw login with specified password
  -D [listen-IP:]listen-port
            Dynamic SOCKS-based port forwarding
  -L [listen-IP:]listen-port:host:port
            Forward local port to remote address
  -R [listen-IP:]listen-port:host:port
            Forward remote port to local address
  -X -x     enable / disable X11 forwarding
  -A -a     enable / disable agent forwarding
  -t -T     enable / disable pty allocation
  -1 -2     force use of particular SSH protocol version
  -4 -6     force use of IPv4 or IPv6
  -C        enable compression
  -i key    private key file for user authentication
  -noagent  disable use of Pageant
  -agent    enable use of Pageant
  -no-trivial-auth
            disconnect if SSH authentication succeeds trivially
  -noshare  disable use of connection sharing
  -share    enable use of connection sharing
  -hostkey keyid
            manually specify a host key (may be repeated)
  -sanitise-stderr, -sanitise-stdout, -no-sanitise-stderr, -no-sanitise-stdout
            do/don't strip control chars from standard output/error
  -no-antispoof   omit anti-spoofing prompt after authentication
  -m file   read remote command(s) from file
  -s        remote command is an SSH subsystem (SSH-2 only)
  -N        don't start a shell/command (SSH-2 only)
  -nc host:port
            open tunnel in place of session (SSH-2 only)
  -sshlog file
  -sshrawlog file
            log protocol details to a file
  -logoverwrite
  -logappend
            control what happens when a log file already exists
  -shareexists
            test whether a connection-sharing upstream exists

Once this works, you are ready to use Plink.

7.2.1 Using Plink for interactive logins

To make a simple interactive connection to a remote server, just type plink and then the host name:

C:\>plink login.example.com

Debian GNU/Linux 2.2 flunky.example.com
flunky login:

You should then be able to log in as normal and run a session. The output sent by the server will be written straight to your command prompt window, which will most likely not interpret terminal control codes in the way the server expects it to. So if you run any full-screen applications, for example, you can expect to see strange characters appearing in your window. Interactive connections like this are not the main point of Plink.

In order to connect with a different protocol, you can give the command line options -ssh, -ssh-connection, -telnet, -rlogin, or -raw. To make an SSH connection, for example:

C:\>plink -ssh login.example.com
login as:

If you have already set up a PuTTY saved session, then instead of supplying a host name, you can give the saved session name. This allows you to use public-key authentication, specify a user name, and use most of the other features of PuTTY:

C:\>plink my-ssh-session
Sent username "fred"
Authenticating with public key "fred@winbox"
Last login: Thu Dec  6 19:25:33 2001 from :0.0
fred@flunky:~$

(You can also use the -load command-line option to load a saved session; see section 3.11.3.1. If you use -load, the saved session exists, and it specifies a hostname, you cannot also specify a host or user@host argument - it will be treated as part of the remote command.)

7.2.2 Using Plink for automated connections

More typically Plink is used with the SSH protocol, to enable you to talk directly to a program running on the server. To do this you have to ensure Plink is using the SSH protocol. You can do this in several ways:

  • Use the -ssh option as described in section 7.2.1.
  • Set up a PuTTY saved session that describes the server you are connecting to, and that also specifies the protocol as SSH.
  • Set the Windows environment variable PLINK_PROTOCOL to the word ssh.

Usually Plink is not invoked directly by a user, but run automatically by another process. Therefore you typically do not want Plink to prompt you for a user name or a password.

Next, you are likely to need to avoid the various interactive prompts Plink can produce. You might be prompted to verify the host key of the server you're connecting to, to enter a user name, or to enter a password.

To avoid being prompted for the server host key when using Plink for an automated connection, you can first make a manual connection (using either of PuTTY or Plink) to the same server, verify the host key (see section 2.2 for more information), and select ‘Accept’ to add the host key to the Registry. After that, Plink commands connecting to that server should not give a host key prompt unless the host key changes. Alternatively, you can specify the appropriate host key(s) on Plink's command line every time you use it; see section 3.11.3.21.

To avoid being prompted for a user name, you can:

  • Use the -l option to specify a user name on the command line. For example, plink login.example.com -l fred.
  • Set up a PuTTY saved session that describes the server you are connecting to, and that also specifies the username to log in as (see section 4.15.1).

To avoid being prompted for a password, you should almost certainly set up public-key authentication. (See chapter 8 for a general introduction to public-key authentication.) Again, you can do this in two ways:

  • Set up a PuTTY saved session that describes the server you are connecting to, and that also specifies a private key file (see section 4.21.9). For this to work without prompting, your private key will need to have no passphrase.
  • Store the private key in Pageant. See chapter 9 for further information.

Once you have done all this, you should be able to run a remote command on the SSH server machine and have it execute automatically with no prompting:

C:\>plink login.example.com -l fred echo hello, world
hello, world

C:\>

Or, if you have set up a saved session with all the connection details:

C:\>plink mysession echo hello, world
hello, world

C:\>

Then you can set up other programs to run this Plink command and talk to it as if it were a process on the server machine.

7.2.3 Plink command line options

Plink accepts all the general command line options supported by the PuTTY tools. See section 3.11.3 for a description of these options.

Plink also supports some of its own options. The following sections describe Plink's specific command-line options.

7.2.3.1 -batch: disable all interactive prompts

If you use the -batch option, Plink will never give an interactive prompt while establishing the connection. If the server's host key is invalid, for example (see section 2.2), then the connection will simply be abandoned instead of asking you what to do next.

This may help Plink's behaviour when it is used in automated scripts: using -batch, if something goes wrong at connection time, the batch job will fail rather than hang.

7.2.3.2 -s: remote command is SSH subsystem

If you specify the -s option, Plink passes the specified command as the name of an SSH ‘subsystem’ rather than an ordinary command line.

(This option is only meaningful with the SSH-2 protocol.)

7.2.3.3 -share: Test and try to share an existing connection.

This option tris to detect if an existing connection can be shared (See section 4.17.5 for more information about SSH connection sharing.) and reuses that connection.

A Plink invocation of the form:

plink -share <session>

will test whether there is currently a viable ‘upstream’ for the session in question, which can be specified using any syntax you'd normally use with Plink to make an actual connection (a host/port number, a bare saved session name, -load, etc). If no ‘upstream’ viable session is found and -share is specified, this connection will be become the ‘upstream’ connection for subsequent connection sharing tries.

(This option is only meaningful with the SSH-2 protocol.)

7.2.3.4 -shareexists: test for connection-sharing upstream

This option does not make a new connection; instead it allows testing for the presence of an existing connection that can be shared. (See section 4.17.5 for more information about SSH connection sharing.)

A Plink invocation of the form:

plink -shareexists <session>

will test whether there is currently a viable ‘upstream’ for the session in question, which can be specified using any syntax you'd normally use with Plink to make an actual connection (a host/port number, a bare saved session name, -load, etc). It returns a zero exit status if a usable ‘upstream’ exists, nonzero otherwise.

(This option is only meaningful with the SSH-2 protocol.)

7.2.3.5 -sanitise-stream: control output sanitisation

In some situations, Plink applies a sanitisation pass to the output received from the server, to strip out control characters such as backspace and the escape character.

The idea of this is to prevent remote processes from sending confusing escape sequences through the standard error channel when Plink is being used as a transport for something like git or CVS. If the server actually wants to send an error message, it will probably be plain text; if the server abuses that channel to try to write over unexpected parts of your terminal display, Plink will try to stop it.

By default, this only happens for output channels which are sent to a Windows console device, or a Unix terminal device. (Any output stream going somewhere else is likely to be needed by an 8-bit protocol and must not be tampered with at all.) It also stops happening if you tell Plink to allocate a remote pseudo-terminal (see section 3.11.3.12 and section 4.23.1), on the basis that in that situation you often want escape sequences from the server to go to your terminal.

But in case Plink guesses wrong about whether you want this sanitisation, you can override it in either direction, using one of these options:

-sanitise-stderr
Sanitise server data written to Plink's standard error channel, regardless of terminals and consoles and remote ptys.
-no-sanitise-stderr
Do not sanitise server data written to Plink's standard error channel.
-sanitise-stdout
Sanitise server data written to Plink's standard output channel.
-no-sanitise-stdout
Do not sanitise server data written to Plink's standard output channel.

7.2.3.6 -no-antispoof: turn off authentication spoofing protection prompt

In SSH, some possible server authentication methods require user input (for example, password authentication, or entering a private key passphrase), and others do not (e.g. a private key held in Pageant).

If you use Plink to run an interactive login session, and if Plink authenticates without needing any user interaction, and if the server is malicious or compromised, it could try to trick you into giving it authentication data that should not go to the server (such as your private key passphrase), by sending what looks like one of Plink's local prompts, as if Plink had not already authenticated.

To protect against this, Plink's default policy is to finish the authentication phase with a final trivial prompt looking like this:

Access granted. Press Return to begin session.

so that if you saw anything that looked like an authentication prompt after that line, you would know it was not from Plink.

That extra interactive step is inconvenient. So Plink will turn it off in as many situations as it can:

  • If Plink's standard input is not pointing at a console or terminal device – for example, if you're using Plink as a transport for some automated application like version control – then you can't type passphrases into the server anyway. In that situation, Plink won't try to protect you from the server trying to fool you into doing so.
  • If Plink is in batch mode (see section 7.2.2), then it never does any interactive authentication. So anything looking like an interactive authentication prompt is automatically suspect, and so Plink omits the anti-spoofing prompt.

But if you still find the protective prompt inconvenient, and you trust the server not to try a trick like this, you can turn it off using the ‘-no-antispoof’ option.

7.3 Using Plink in batch files and scripts

Once you have set up Plink to be able to log in to a remote server without any interactive prompting (see section 7.2.2), you can use it for lots of scripting and batch purposes. For example, to start a backup on a remote machine, you might use a command like:

plink root@myserver /etc/backups/do-backup.sh

Or perhaps you want to fetch all system log lines relating to a particular web area:

plink mysession grep /~fred/ /var/log/httpd/access.log > fredlog

Any non-interactive command you could usefully run on the server command line, you can run in a batch file using Plink in this way.

7.4 Using Plink with CVS

To use Plink with CVS, you need to set the environment variable CVS_RSH to point to Plink:

set CVS_RSH=\path\to\plink.exe

You also need to arrange to be able to connect to a remote host without any interactive prompts, as described in section 7.2.2.

You should then be able to run CVS as follows:

cvs -d :ext:user@sessionname:/path/to/repository co module

If you specified a username in your saved session, you don't even need to specify the ‘user’ part of this, and you can just say:

cvs -d :ext:sessionname:/path/to/repository co module

7.5 Using Plink with WinCVS

Plink can also be used with WinCVS. Firstly, arrange for Plink to be able to connect to a remote host non-interactively, as described in section 7.2.2.

Then, in WinCVS, bring up the ‘Preferences’ dialogue box from the Admin menu, and switch to the ‘Ports’ tab. Tick the box there labelled ‘Check for an alternate rsh name’ and in the text entry field to the right enter the full path to plink.exe. Select ‘OK’ on the ‘Preferences’ dialogue box.

Next, select ‘Command Line’ from the WinCVS ‘Admin’ menu, and type a CVS command as in section 7.4, for example:

cvs -d :ext:user@hostname:/path/to/repository co module

or (if you're using a saved session):

cvs -d :ext:user@sessionname:/path/to/repository co module

Select the folder you want to check out to with the ‘Change Folder’ button, and click ‘OK’ to check out your module. Once you've got modules checked out, WinCVS will happily invoke plink from the GUI for CVS operations.


If you want to provide feedback on this manual or on the PuTTY tools themselves, see the Feedback page.

[PuTTY release 0.76]
putty-0.76/doc/Chapter8.html0000644000175000017500000007400214072266315012676 00000000000000 Using public keys for SSH authentication

Previous | Contents | Index | Next

Chapter 8: Using public keys for SSH authentication

8.1 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 secure and more flexible, but more difficult to set up.

In conventional password authentication, you prove you are who you claim to be by proving that you know the correct password. The only way to prove you know the password is to tell the server what you think the password is. This means that if the server has been hacked, or spoofed (see section 2.2), an attacker can learn your password.

Public key authentication solves this problem. You generate a key pair, consisting of a public key (which everybody is allowed to know) and a private key (which you keep secret and do not give to anybody). The private key is able to generate signatures. A signature created using your private key cannot be forged by anybody who does not have that key; but anybody who has your public key can verify that a particular signature is genuine.

So you generate a key pair on your own computer, and you copy the public key to the server. Then, when the server asks you to prove who you are, PuTTY can generate a signature using your private key. The server can verify that signature (since it has your public key) and allow you to log in. Now if the server is hacked or spoofed, the attacker does not gain your private key or password; they only gain one signature. And signatures cannot be re-used, so they have gained nothing.

There is a problem with this: if your private key is stored unprotected on your own computer, then anybody who gains access to that will be able to generate signatures as if they were you. So they will be able to log in to your server under your account. For this reason, your private key is usually encrypted when it is stored on your local machine, using a passphrase of your choice. In order to generate a signature, PuTTY must decrypt the key, so you have to type your passphrase.

This can make public-key authentication less convenient than password authentication: every time you log in to the server, instead of typing a short password, you have to type a longer passphrase. One solution to this is to use an authentication agent, a separate program which holds decrypted private keys and generates signatures on request. PuTTY's authentication agent is called Pageant. When you begin a Windows session, you start Pageant and load your private key into it (typing your passphrase once). For the rest of your session, you can start PuTTY any number of times and Pageant will automatically generate signatures without you having to do anything. When you close your Windows session, Pageant shuts down, without ever having stored your decrypted private key on disk. Many people feel this is a good compromise between security and convenience. See chapter 9 for further details.

There is more than one public-key algorithm available. The most common are RSA and ECDSA, but others exist, notably DSA (otherwise known as DSS), the USA's federal Digital Signature Standard. The key types supported by PuTTY are described in section 8.2.2.

8.2 Using PuTTYgen, the PuTTY key generator

PuTTYgen is a key generator. It generates pairs of public and private keys to be used with PuTTY, PSCP, and Plink, as well as the PuTTY authentication agent, Pageant (see chapter 9). PuTTYgen generates RSA, DSA, ECDSA, and EdDSA keys.

When you run PuTTYgen you will see a window where you have two main choices: ‘Generate’, to generate a new public/private key pair, or ‘Load’ to load in an existing private key.

8.2.1 Generating a new key

This is a general outline of the procedure for generating a new key pair. The following sections describe the process in more detail.

  • First, you need to select which type of key you want to generate, and also select the strength of the key. This is described in more detail in section 8.2.2 and section 8.2.3.
  • Then press the ‘Generate’ button, to actually generate the key. Section 8.2.5 describes this step.
  • Once you have generated the key, select a comment field (section 8.2.7) and a passphrase (section 8.2.8).
  • Now you're ready to save the private key to disk; press the ‘Save private key’ button. (See section 8.2.9).

Your key pair is now ready for use. You may also want to copy the public key to your server, either by copying it out of the ‘Public key for pasting into OpenSSH authorized_keys file’ box (see section 8.2.11), or by using the ‘Save public key’ button (section 8.2.10). However, you don't need to do this immediately; if you want, you can load the private key back into PuTTYgen later (see section 8.2.13) and the public key will be available for copying and pasting again.

Section 8.3 describes the typical process of configuring PuTTY to attempt public-key authentication, and configuring your SSH server to accept it.

8.2.2 Selecting the type of key

Before generating a key pair using PuTTYgen, you need to select which type of key you need.

The current version of the SSH protocol, SSH-2, supports several different key types, although specific servers may not support all of them. PuTTYgen can generate:

  • An RSA key for use with the SSH-2 protocol.
  • A DSA key for use with the SSH-2 protocol.
  • An ECDSA (elliptic curve DSA) key for use with the SSH-2 protocol.
  • An EdDSA key (Edwards-curve DSA, another elliptic curve algorithm) for use with the SSH-2 protocol.

PuTTYgen can also generate an RSA key suitable for use with the old SSH-1 protocol (which only supports RSA); for this, you need to select the ‘SSH-1 (RSA)’ option. Since the SSH-1 protocol is no longer considered secure, it's rare to need this option.

8.2.3 Selecting the size (strength) of the key

The ‘Number of bits’ input box allows you to choose the strength of the key PuTTYgen will generate.

  • For RSA and DSA, 2048 bits should currently be sufficient for most purposes.
  • For ECDSA, only 256, 384, and 521 bits are supported. (ECDSA offers equivalent security to RSA with smaller key sizes.)
  • For EdDSA, the only valid sizes are 255 bits (these keys are also known as ‘Ed25519’ and are commonly used) and 448 bits (‘Ed448’, which is much less common at the time of writing). (256 is also accepted for backward compatibility, but the effect is the same as 255.)

8.2.4 Selecting the prime generation method

On the ‘Key’ menu, you can also optionally change the method for generating the prime numbers used in the generated key. This is used for RSA and DSA keys only. (The other key types don't require generating prime numbers at all.)

The prime-generation method does not affect compatibility: a key generated with any of these methods will still work with all the same SSH servers.

If you don't care about this, it's entirely sensible to leave it on the default setting.

The available methods are:

  • Use probable primes (fast)
  • Use proven primes (slower)
  • Use proven primes with even distribution (slowest)

The ‘probable primes’ method sounds unsafe, but it's the most commonly used prime-generation strategy. There is in theory a possibility that it might accidentally generate a number that isn't prime, but the software does enough checking to make that probability vanishingly small (less than 1 in 2^80, or 1 in 10^24). So, in practice, nobody worries about it very much.

The other methods cause PuTTYgen to use numbers that it is sure are prime, because it generates the output number together with a proof of its primality. This takes more effort, but it eliminates that theoretical risk in the probabilistic method.

You might choose to switch from probable to proven primes if you have a local security standard that demands it, or if you don't trust the probabilistic argument for the safety of the usual method.

For RSA keys, there's also an option on the ‘Key’ menu to use ‘strong’ primes as the prime factors of the public key. A ‘strong’ prime is a prime number chosen to have a particular structure that makes certain factoring algorithms more difficult to apply, so some security standards recommend their use. However, the most modern factoring algorithms are unaffected, so this option is probably not worth turning on unless you have a local standard that recommends it.

8.2.5 The ‘Generate’ button

Once you have chosen the type of key you want, and the strength of the key, press the ‘Generate’ button and PuTTYgen will begin the process of actually generating the key.

First, a progress bar will appear and PuTTYgen will ask you to move the mouse around to generate randomness. Wave the mouse in circles over the blank area in the PuTTYgen window, and the progress bar will gradually fill up as PuTTYgen collects enough randomness. You don't need to wave the mouse in particularly imaginative patterns (although it can't hurt); PuTTYgen will collect enough randomness just from the fine detail of exactly how far the mouse has moved each time Windows samples its position.

When the progress bar reaches the end, PuTTYgen will begin creating the key. The progress bar will reset to the start, and gradually move up again to track the progress of the key generation. It will not move evenly, and may occasionally slow down to a stop; this is unfortunately unavoidable, because key generation is a random process and it is impossible to reliably predict how long it will take.

When the key generation is complete, a new set of controls will appear in the window to indicate this.

8.2.6 The ‘Key fingerprint’ box

The ‘Key fingerprint’ box shows you a fingerprint value for the generated key. This is derived cryptographically from the public key value, so it doesn't need to be kept secret; it is supposed to be more manageable for human beings than the public key itself.

The fingerprint value is intended to be cryptographically secure, in the sense that it is computationally infeasible for someone to invent a second key with the same fingerprint, or to find a key with a particular fingerprint. So some utilities, such as the Pageant key list box (see section 9.2.1) and the Unix ssh-add utility, will list key fingerprints rather than the whole public key.

By default, PuTTYgen will display fingerprints in the ‘SHA256’ format. If you need to see the fingerprint in the older ‘MD5’ format (which looks like aa:bb:cc:...), you can choose ‘Show fingerprint as MD5’ from the ‘Key’ menu, but bear in mind that this is less cryptographically secure; it may be feasible for an attacker to create a key with the same fingerprint as yours.

8.2.7 Setting a comment for your key

If you have more than one key and use them for different purposes, you don't need to memorise the key fingerprints in order to tell them apart. PuTTYgen allows you to enter a comment for your key, which will be displayed whenever PuTTY or Pageant asks you for the passphrase.

The default comment format, if you don't specify one, contains the key type and the date of generation, such as rsa-key-20011212. Another commonly used approach is to use your name and the name of the computer the key will be used on, such as simon@simons-pc.

To alter the key comment, just type your comment text into the ‘Key comment’ box before saving the private key. If you want to change the comment later, you can load the private key back into PuTTYgen, change the comment, and save it again.

8.2.8 Setting a passphrase for your key

The ‘Key passphrase’ and ‘Confirm passphrase’ boxes allow you to choose a passphrase for your key. The passphrase will be used to encrypt the key on disk, so you will not be able to use the key without first entering the passphrase.

When you save the key, PuTTYgen will check that the ‘Key passphrase’ and ‘Confirm passphrase’ boxes both contain exactly the same passphrase, and will refuse to save the key otherwise.

If you leave the passphrase fields blank, the key will be saved unencrypted. You should not do this without good reason; if you do, your private key file on disk will be all an attacker needs to gain access to any machine configured to accept that key. If you want to be able to log in without having to type a passphrase every time, you should consider using Pageant (chapter 9) so that your decrypted key is only held in memory rather than on disk.

Under special circumstances you may genuinely need to use a key with no passphrase; for example, if you need to run an automated batch script that needs to make an SSH connection, you can't be there to type the passphrase. In this case we recommend you generate a special key for each specific batch script (or whatever) that needs one, and on the server side you should arrange that each key is restricted so that it can only be used for that specific purpose. The documentation for your SSH server should explain how to do this (it will probably vary between servers).

Choosing a good passphrase is difficult. Just as you shouldn't use a dictionary word as a password because it's easy for an attacker to run through a whole dictionary, you should not use a song lyric, quotation or other well-known sentence as a passphrase. DiceWare (www.diceware.com) recommends using at least five words each generated randomly by rolling five dice, which gives over 2^64 possible passphrases and is probably not a bad scheme. If you want your passphrase to make grammatical sense, this cuts down the possibilities a lot and you should use a longer one as a result.

Do not forget your passphrase. There is no way to recover it.

8.2.9 Saving your private key to a disk file

Once you have generated a key, set a comment field and set a passphrase, you are ready to save your private key to disk.

Press the ‘Save private key’ button. PuTTYgen will put up a dialog box asking you where to save the file. Select a directory, type in a file name, and press ‘Save’.

This file is in PuTTY's native format (*.PPK); it is the one you will need to tell PuTTY to use for authentication (see section 4.21.9) or tell Pageant to load (see section 9.2.2).

(You can optionally change some details of the PPK format for your saved key files; see section 8.2.12. But The defaults should be fine for most purposes.)

8.2.10 Saving your public key to a disk file

RFC 4716 specifies a standard format for storing SSH-2 public keys on disk. Some SSH servers (such as ssh.com's) require a public key in this format in order to accept authentication with the corresponding private key. (Others, such as OpenSSH, use a different format; see section 8.2.11.)

To save your public key in the SSH-2 standard format, press the ‘Save public key’ button in PuTTYgen. PuTTYgen will put up a dialog box asking you where to save the file. Select a directory, type in a file name, and press ‘Save’.

You will then probably want to copy the public key file to your SSH server machine. See section 8.3 for general instructions on configuring public-key authentication once you have generated a key.

If you use this option with an SSH-1 key, the file PuTTYgen saves will contain exactly the same text that appears in the ‘Public key for pasting’ box. This is the only existing standard for SSH-1 public keys.

8.2.11 ‘Public key for pasting into OpenSSH authorized_keys file’

The OpenSSH server, among others, requires your public key to be given to it in a one-line format before it will accept authentication with your private key. (SSH-1 servers also used this method.)

The ‘Public key for pasting into OpenSSH authorized_keys file’ gives the public-key data in the correct one-line format. Typically you will want to select the entire contents of the box using the mouse, press Ctrl+C to copy it to the clipboard, and then paste the data into a PuTTY session which is already connected to the server.

See section 8.3 for general instructions on configuring public-key authentication once you have generated a key.

8.2.12 Parameters for saving key files

Selecting ‘Parameters for saving key files...’ from the ‘Key’ menu lets you adjust some aspects of PPK-format private key files stored on disk. None of these options affect compatibility with SSH servers.

In most cases, it's entirely sensible to leave all of these at their default settings.

8.2.12.1 PPK file version

This defaults to version 3, which is fine for most uses.

You might need to select PPK version 2 if you need your private key file to be loadable in older versions of PuTTY (0.74 and older), or in other tools which do not yet support the version 3 format (which was introduced in 2021).

The version 2 format is less resistant to brute-force decryption, and doesn't support any of the following options to control that.

8.2.12.2 Options affecting passphrase hashing

All of the following options only affect keys saved with passphrases. They control how much work is required to decrypt the key (which happens every time you type its passphrase). This allows you to trade off the cost of legitimate use of the key against the resistance of the encrypted key to password-guessing attacks.

These options only affect PPK version 3.

Key derivation function
The variant of the Argon2 key derivation function to use. You might change this if you consider your exposure to side-channel attacks to be different to the norm.
Memory to use for passphrase hash
The amount of memory needed to decrypt the key, in Kbyte.
Time to use for passphrase hash
Controls how much time is required to attempt decrypting the key. You can either specify an approximate time in milliseconds (on this machine), or explicitly specify a number of hash passes (which is what the time is turned into during encryption).
Parallelism for passphrase hash
Number of parallelisable threads that can be used to decrypt the key. The default, 1, forces the process to run single-threaded, even on machines with multiple cores.

8.2.13 Reloading a private key

PuTTYgen allows you to load an existing private key file into memory. If you do this, you can then change the passphrase and comment before saving it again; you can also make extra copies of the public key.

To load an existing key, press the ‘Load’ button. PuTTYgen will put up a dialog box where you can browse around the file system and find your key file. Once you select the file, PuTTYgen will ask you for a passphrase (if necessary) and will then display the key details in the same way as if it had just generated the key.

If you use the Load command to load a foreign key format, it will work, but you will see a message box warning you that the key you have loaded is not a PuTTY native key. See section 8.2.14 for information about importing foreign key formats.

8.2.14 Dealing with private keys in other formats

SSH-2 private keys have no standard format. OpenSSH and ssh.com have different formats, and PuTTY's is different again. So a key generated with one client cannot immediately be used with another.

Using the ‘Import’ command from the ‘Conversions’ menu, PuTTYgen can load SSH-2 private keys in OpenSSH's format and ssh.com's format. Once you have loaded one of these key types, you can then save it back out as a PuTTY-format key (*.PPK) so that you can use it with the PuTTY suite. The passphrase will be unchanged by this process (unless you deliberately change it). You may want to change the key comment before you save the key, since some OpenSSH key formats contained no space for a comment, and ssh.com's default comment format is long and verbose.

PuTTYgen can also export private keys in OpenSSH format and in ssh.com format. To do so, select one of the ‘Export’ options from the ‘Conversions’ menu. Exporting a key works exactly like saving it (see section 8.2.9) - you need to have typed your passphrase in beforehand, and you will be warned if you are about to save a key without a passphrase.

For OpenSSH there are two options. Modern OpenSSH actually has two formats it uses for storing private keys. ‘Export OpenSSH key’ will automatically choose the oldest format supported for the key type, for maximum backward compatibility with older versions of OpenSSH; for newer key types like Ed25519, it will use the newer format as that is the only legal option. If you have some specific reason for wanting to use OpenSSH's newer format even for RSA, DSA, or ECDSA keys, you can choose ‘Export OpenSSH key (force new file format)’.

Most clients for the older SSH-1 protocol use a standard format for storing private keys on disk. PuTTY uses this format as well; so if you have generated an SSH-1 private key using OpenSSH or ssh.com's client, you can use it with PuTTY, and vice versa. Hence, the export options are not available if you have generated an SSH-1 key.

8.3 Getting ready for public key authentication

Connect to your SSH server using PuTTY with the SSH protocol. When the connection succeeds you will be prompted for your user name and password to login. Once logged in, you must configure the server to accept your public key for authentication:

  • If your server is OpenSSH, you should change into the .ssh directory under your home directory, and open the file authorized_keys with your favourite editor. (You may have to create this file, if this is the first key you have put in it.) Then switch to the PuTTYgen window, select all of the text in the ‘Public key for pasting into OpenSSH authorized_keys file’ box (see section 8.2.11), and copy it to the clipboard (Ctrl+C). Then, switch back to the PuTTY window and insert the data into the open file, making sure it ends up all on one line. Save the file.

    (In very old versions of OpenSSH, SSH-2 keys had to be put in a separate file called authorized_keys2. In all current versions, the same authorized_keys file is used for both SSH-1 and SSH-2 keys.)

  • If your server is ssh.com's product and is using SSH-2, you need to save a public key file from PuTTYgen (see section 8.2.10), and copy that into the .ssh2 directory on the server. Then you should go into that .ssh2 directory, and edit (or create) a file called authorization. In this file you should put a line like Key mykey.pub, with mykey.pub replaced by the name of your key file.
  • For other SSH server software, you should refer to the manual for that server.

You may also need to ensure that your home directory, your .ssh directory, and any other files involved (such as authorized_keys, authorized_keys2 or authorization) are not group-writable or world-writable; servers will typically ignore the keys unless this is done. You can typically do this by using a command such as

chmod go-w $HOME $HOME/.ssh $HOME/.ssh/authorized_keys

Your server should now be configured to accept authentication using your private key. Now you need to configure PuTTY to attempt authentication using your private key. You can do this in any of three ways:

  • Select the private key in PuTTY's configuration. See section 4.21.9 for details.
  • Specify the key file on the command line with the -i option. See section 3.11.3.18 for details.
  • Load the private key into Pageant (see chapter 9). In this case PuTTY will automatically try to use it for authentication if it can.

If you want to provide feedback on this manual or on the PuTTY tools themselves, see the Feedback page.

[PuTTY release 0.76]
putty-0.76/doc/Chapter9.html0000644000175000017500000004600114072266315012675 00000000000000 Using Pageant for authentication

Previous | Contents | Index | Next

Chapter 9: Using Pageant for authentication

Pageant is an SSH authentication agent. It holds your private keys in memory, already decoded, so that you can use them often without needing to type a passphrase.

9.1 Getting started with Pageant

Before you run Pageant, you need to have a private key in *.PPK format. See chapter 8 to find out how to generate and use one.

When you run Pageant, it will put an icon of a computer wearing a hat into the System tray. It will then sit and do nothing, until you load a private key into it. (You may need to use Windows' ‘Show hidden icons’ arrow to see the Pageant icon.)

If you click the Pageant icon with the right mouse button, you will see a menu. Select ‘View Keys’ from this menu. The Pageant main window will appear. (You can also bring this window up by double-clicking on the Pageant icon.)

The Pageant window contains a list box. This shows the private keys Pageant is holding. When you start Pageant, it has no keys, so the list box will be empty. After you add one or more keys, they will show up in the list box.

To add a key to Pageant, press the ‘Add Key’ button. Pageant will bring up a file dialog, labelled ‘Select Private Key File’. Find your private key file in this dialog, and press ‘Open’.

Pageant will now load the private key. If the key is protected by a passphrase, Pageant will ask you to type the passphrase. When the key has been loaded, it will appear in the list in the Pageant window.

Now start PuTTY and open an SSH session to a site that accepts your key. PuTTY will notice that Pageant is running, retrieve the key automatically from Pageant, and use it to authenticate. You can now open as many PuTTY sessions as you like without having to type your passphrase again.

(PuTTY can be configured not to try to use Pageant, but it will try by default. See section 4.21.4 and section 3.11.3.9 for more information.)

When you want to shut down Pageant, click the right button on the Pageant icon in the System tray, and select ‘Exit’ from the menu. Closing the Pageant main window does not shut down Pageant.

If you want Pageant to stay running but forget all the keys it has acquired, select ‘Remove All Keys’ from the System tray menu.

9.2 The Pageant main window

The Pageant main window appears when you left-click on the Pageant system tray icon, or alternatively right-click and select ‘View Keys’ from the menu. You can use it to keep track of what keys are currently loaded into Pageant, and to add new ones or remove the existing keys.

9.2.1 The key list box

The large list box in the Pageant main window lists the private keys that are currently loaded into Pageant. The list might look something like this:

ssh-ed25519  SHA256:TddlQk20DVs4LRcAsIfDN9pInKpY06D+h4kSHwWAj4w
ssh-rsa 2048 SHA256:8DFtyHm3kQihgy52nzX96qMcEVOq7/yJmmwQQhBWYFg

For each key, the list box will tell you:

  • The type of the key. Currently, this can be ssh-rsa (an RSA key for use with the SSH-2 protocol), ssh-dss (a DSA key for use with the SSH-2 protocol), ecdsa-sha2-* (an ECDSA key for use with the SSH-2 protocol), ssh-ed25519 (an Ed25519 key for use with the SSH-2 protocol), ssh-ed448 (an Ed448 key for use with the SSH-2 protocol), or ssh1 (an RSA key for use with the old SSH-1 protocol).
  • The size (in bits) of the key, for key types that come in different sizes.
  • The fingerprint for the public key. This should be the same fingerprint given by PuTTYgen, and (hopefully) also the same fingerprint shown by remote utilities such as ssh-keygen when applied to your authorized_keys file.

    By default this is shown in the ‘SHA256’ format. You can change to the older ‘MD5’ format (which looks like aa:bb:cc:...) with the ‘Fingerprint type’ drop-down, but bear in mind that this format is less secure and should be avoided for comparison purposes where possible.

  • The comment attached to the key.
  • The state of deferred decryption, if enabled for this key. See section 9.5.

9.2.2 The ‘Add Key’ button

To add a key to Pageant by reading it out of a local disk file, press the ‘Add Key’ button in the Pageant main window, or alternatively right-click on the Pageant icon in the system tray and select ‘Add Key’ from there.

Pageant will bring up a file dialog, labelled ‘Select Private Key File’. Find your private key file in this dialog, and press ‘Open’. If you want to add more than one key at once, you can select multiple files using Shift-click (to select several adjacent files) or Ctrl-click (to select non-adjacent files).

Pageant will now load the private key(s). If a key is protected by a passphrase, Pageant will ask you to type the passphrase.

(This is not the only way to add a private key to Pageant. You can also add one from a remote system by using agent forwarding; see section 9.4 for details.)

9.2.3 The ‘Remove Key’ button

If you need to remove a key from Pageant, select that key in the list box, and press the ‘Remove Key’ button. Pageant will remove the key from its memory.

You can apply this to keys you added using the ‘Add Key’ button, or to keys you added remotely using agent forwarding (see section 9.4); it makes no difference.

9.3 The Pageant command line

Pageant can be made to do things automatically when it starts up, by specifying instructions on its command line. If you're starting Pageant from the Windows GUI, you can arrange this by editing the properties of the Windows shortcut that it was started from.

If Pageant is already running, invoking it again with the options below causes actions to be performed with the existing instance, not a new one.

9.3.1 Making Pageant automatically load keys on startup

Pageant can automatically load one or more private keys when it starts up, if you provide them on the Pageant command line. Your command line might then look like:

C:\PuTTY\pageant.exe d:\main.ppk d:\secondary.ppk

If the keys are stored encrypted, Pageant will request the passphrases on startup.

If Pageant is already running, this syntax loads keys into the existing Pageant.

You can specify the ‘--encrypted’ option to defer decryption of these keys; see section 9.5.

9.3.2 Making Pageant run another program

You can arrange for Pageant to start another program once it has initialised itself and loaded any keys specified on its command line. This program (perhaps a PuTTY, or a WinCVS making use of Plink, or whatever) will then be able to use the keys Pageant has loaded.

You do this by specifying the -c option followed by the command, like this:

C:\PuTTY\pageant.exe d:\main.ppk -c C:\PuTTY\putty.exe

9.3.3 Starting with the key list visible

Start Pageant with the --keylist option to show the main window as soon as it starts up.

9.3.4 Restricting the Windows process ACL

Pageant supports the same -restrict-acl option as the other PuTTY utilities to lock down the Pageant process's access control; see section 3.11.3.27 for why you might want to do this.

By default, if Pageant is started with -restrict-acl, it won't pass this to any PuTTY sessions started from its System Tray submenu. Use -restrict-putty-acl to change this. (Again, see section 3.11.3.27 for details.)

9.4 Using agent forwarding

Agent forwarding is a mechanism that allows applications on your SSH server machine to talk to the agent on your client machine.

Note that at present, whether agent forwarding in SSH-2 is available depends on your server. Pageant's protocol is compatible with the OpenSSH server, but the ssh.com server uses a different agent protocol, which PuTTY does not yet support.

To enable agent forwarding, first start Pageant. Then set up a PuTTY SSH session in which ‘Allow agent forwarding’ is enabled (see section 4.21.7). Open the session as normal. (Alternatively, you can use the -A command line option; see section 3.11.3.10 for details.)

If this has worked, your applications on the server should now have access to a Unix domain socket which the SSH server will forward back to PuTTY, and PuTTY will forward on to the agent. To check that this has actually happened, you can try this command on Unix server machines:

unixbox:~$ echo $SSH_AUTH_SOCK
/tmp/ssh-XXNP18Jz/agent.28794
unixbox:~$

If the result line comes up blank, agent forwarding has not been enabled at all.

Now if you run ssh on the server and use it to connect through to another server that accepts one of the keys in Pageant, you should be able to log in without a password:

unixbox:~$ ssh -v otherunixbox
[...]
debug: next auth method to try is publickey
debug: userauth_pubkey_agent: trying agent key my-putty-key
debug: ssh-userauth2 successful: method publickey
[...]

If you enable agent forwarding on that SSH connection as well (see the manual for your server-side SSH client to find out how to do this), your authentication keys will still be available on the next machine you connect to - two SSH connections away from where they're actually stored.

In addition, if you have a private key on one of the SSH servers, you can send it all the way back to Pageant using the local ssh-add command:

unixbox:~$ ssh-add ~/.ssh/id_rsa
Need passphrase for /home/fred/.ssh/id_rsa
Enter passphrase for /home/fred/.ssh/id_rsa:
Identity added: /home/fred/.ssh/id_rsa (/home/simon/.ssh/id_rsa)
unixbox:~$

and then it's available to every machine that has agent forwarding available (not just the ones downstream of the place you added it).

9.5 Loading keys without decrypting them

You can add keys to Pageant without decrypting them. The key file will be held in Pageant's memory still encrypted, and when a client program first tries to use the key, Pageant will display a dialog box prompting for the passphrase so that the key can be decrypted.

This works the same way whether the key is used by an instance of PuTTY running locally, or a remote client connecting to Pageant through agent forwarding.

To add a key to Pageant in this encrypted form, press the ‘Add Key (encrypted)’ button in the Pageant main window, or alternatively right-click on the Pageant icon in the system tray and select ‘Add Key (encrypted)’ from there. Pageant will bring up a file dialog, in just the same way as it would for the plain ‘Add Key’ button. But it won't ask for a passphrase. Instead, the key will be listed in the main window with ‘(encrypted)’ after it.

To start Pageant up in the first place with encrypted keys loaded into it, you can use the ‘--encrypted’ option on the command line. For example:

C:\PuTTY\pageant.exe --encrypted d:\main.ppk

After a key has been decrypted for the first use, it remains decrypted, so that it can be used again. The main window will list the key with ‘(re-encryptable)’ after it. You can revert it to the previous state, where a passphrase is required, using the ‘Re-encrypt’ button in the Pageant main window.

You can also ‘re-encrypt’ all keys that were added encrypted by choosing ‘Re-encrypt All Keys’ from the System tray menu. (Note that this does not discard cleartext keys that were not previously added encrypted!)

CAUTION: When Pageant displays a prompt to decrypt an already-loaded key, it cannot give keyboard focus to the prompt dialog box. As far as I know this is a deliberate defensive measure by Windows, against malicious software. So make sure you click in the prompt window before typing your passphrase, or else the passphrase might be sent to somewhere you didn't want to trust with it!

9.6 Security considerations

Using Pageant for public-key authentication gives you the convenience of being able to open multiple SSH sessions without having to type a passphrase every time, but also gives you the security benefit of never storing a decrypted private key on disk. Many people feel this is a good compromise between security and convenience.

It is a compromise, however. Holding your decrypted private keys in Pageant is better than storing them in easy-to-find disk files, but still less secure than not storing them anywhere at all. This is for two reasons:

  • Windows unfortunately provides no way to protect pieces of memory from being written to the system swap file. So if Pageant is holding your private keys for a long period of time, it's possible that decrypted private key data may be written to the system swap file, and an attacker who gained access to your hard disk later on might be able to recover that data. (However, if you stored an unencrypted key in a disk file they would certainly be able to recover it.)
  • Although, like most modern operating systems, Windows prevents programs from accidentally accessing one another's memory space, it does allow programs to access one another's memory space deliberately, for special purposes such as debugging. This means that if you allow a virus, trojan, or other malicious program on to your Windows system while Pageant is running, it could access the memory of the Pageant process, extract your decrypted authentication keys, and send them back to its master.

Similarly, use of agent forwarding is a security improvement on other methods of one-touch authentication, but not perfect. Holding your keys in Pageant on your Windows box has a security advantage over holding them on the remote server machine itself (either in an agent or just unencrypted on disk), because if the server machine ever sees your unencrypted private key then the sysadmin or anyone who cracks the machine can steal the keys and pretend to be you for as long as they want.

However, the sysadmin of the server machine can always pretend to be you on that machine. So if you forward your agent to a server machine, then the sysadmin of that machine can access the forwarded agent connection and request signatures from any of your private keys, and can therefore log in to other machines as you. They can only do this to a limited extent - when the agent forwarding disappears they lose the ability - but using Pageant doesn't actually prevent the sysadmin (or hackers) on the server from doing this.

Therefore, if you don't trust the sysadmin of a server machine, you should never use agent forwarding to that machine. (Of course you also shouldn't store private keys on that machine, type passphrases into it, or log into other machines from it in any way at all; Pageant is hardly unique in this respect.)


If you want to provide feedback on this manual or on the PuTTY tools themselves, see the Feedback page.

[PuTTY release 0.76]
putty-0.76/doc/Chapter10.html0000644000175000017500000005547114072266315012760 00000000000000 Common error messages

Previous | Contents | Index | Next

Chapter 10: Common 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.

We do not attempt to list all error messages here: there are many which should never occur, and some which should be self-explanatory. If you get an error message which is not listed in this chapter and which you don't understand, report it to us as a bug (see appendix B) and we will add documentation for it.

10.1 ‘The server's host key is not cached in the registry’

This error message occurs when PuTTY connects to a new SSH server. Every server identifies itself by means of a host key; once PuTTY knows the host key for a server, it will be able to detect if a malicious attacker redirects your connection to another machine.

If you see this message, it means that PuTTY has not seen this host key before, and has no way of knowing whether it is correct or not. You should attempt to verify the host key by other means, such as asking the machine's administrator.

If you see this message and you know that your installation of PuTTY has connected to the same server before, it may have been recently upgraded to SSH protocol version 2. SSH protocols 1 and 2 use separate host keys, so when you first use SSH-2 with a server you have only used SSH-1 with before, you will see this message again. You should verify the correctness of the key as before.

See section 2.2 for more information on host keys.

10.2 ‘WARNING - POTENTIAL SECURITY BREACH!’

This message, followed by ‘The server's host key does not match the one PuTTY has cached in the registry’, means that PuTTY has connected to the SSH server before, knows what its host key should be, but has found a different one.

This may mean that a malicious attacker has replaced your server with a different one, or has redirected your network connection to their own machine. On the other hand, it may simply mean that the administrator of your server has accidentally changed the key while upgrading the SSH software; this shouldn't happen but it is unfortunately possible.

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 section 2.2 for more information on host keys.

10.3 ‘SSH protocol version 2 required by our configuration but remote only provides (old, insecure) SSH-1’

By default, PuTTY only supports connecting to SSH servers that implement SSH protocol version 2. If you see this message, the server you're trying to connect to only supports the older SSH-1 protocol.

If the server genuinely only supports SSH-1, then you need to either change the ‘SSH protocol version’ setting (see section 4.17.4), or use the -1 command-line option; in any case, you should not treat the resulting connection as secure.

You might start seeing this message with new versions of PuTTY (from 0.68 onwards) where you didn't before, because it used to be possible to configure PuTTY to automatically fall back from SSH-2 to SSH-1. This is no longer supported, to prevent the possibility of a downgrade attack.

10.4 ‘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 Blowfish, single-DES, and Arcfour encryption.

See section 4.20 for more information on this message.

(There are similar messages for other cryptographic primitives, such as host key algorithms.)

10.5 ‘Remote side sent disconnect message type 2 (protocol error): "Too many authentication failures for root"’

This message is produced by an OpenSSH (or Sun SSH) server if it receives more failed authentication attempts than it is willing to tolerate.

This can easily happen if you are using Pageant and have a large number of keys loaded into it, since these servers count each offer of a public key as an authentication attempt. This can be worked around by specifying the key that's required for the authentication in the PuTTY configuration (see section 4.21.9); PuTTY will ignore any other keys Pageant may have, but will ask Pageant to do the authentication, so that you don't have to type your passphrase.

On the server, this can be worked around by disabling public-key authentication or (for Sun SSH only) by increasing MaxAuthTries in sshd_config.

10.6 ‘Out of memory’

This occurs when PuTTY tries to allocate more memory than the system can give it. This may happen for genuine reasons: if the computer really has run out of memory, or if you have configured an extremely large number of lines of scrollback in your terminal. PuTTY is not able to recover from running out of memory; it will terminate immediately after giving this error.

However, this error can also occur when memory is not running out at all, because PuTTY receives data in the wrong format. In SSH-2 and also in SFTP, the server sends the length of each message before the message itself; so PuTTY will receive the length, try to allocate space for the message, and then receive the rest of the message. If the length PuTTY receives is garbage, it will try to allocate a ridiculous amount of memory, and will terminate with an ‘Out of memory’ error.

This can happen in SSH-2, if PuTTY and the server have not enabled encryption in the same way (see question A.7.3 in the FAQ).

This can also happen in PSCP or PSFTP, if your login scripts on the server generate output: the client program will be expecting an SFTP message starting with a length, and if it receives some text from your login scripts instead it will try to interpret them as a message length. See question A.7.4 for details of this.

10.7 ‘Internal error’, ‘Internal fault’, ‘Assertion failed’

Any error beginning with the word ‘Internal’ should never occur. If it does, there is a bug in PuTTY by definition; please see appendix B and report it to us.

Similarly, any error message starting with ‘Assertion failed’ is a bug in PuTTY. Please report it to us, and include the exact text from the error message box.

10.8 ‘Unable to use key file’, ‘Couldn't load private key’, ‘Couldn't load this key’

Various forms of this error are printed in the PuTTY window, or written to the PuTTY Event Log (see section 3.1.3.1) when trying public-key authentication, or given by Pageant when trying to load a private key.

If you see one of these messages, it often indicates that you've tried to load a key of an inappropriate type into PuTTY, Plink, PSCP, PSFTP, or Pageant.

You may have tried to load an SSH-2 key in a ‘foreign’ format (OpenSSH or ssh.com) directly into one of the PuTTY tools, in which case you need to import it into PuTTY's native format (*.PPK) using PuTTYgen – see section 8.2.14.

Alternatively, you may have specified a key that's inappropriate for the connection you're making. The SSH-2 and the old SSH-1 protocols require different private key formats, and a SSH-1 key can't be used for a SSH-2 connection (or vice versa).

10.9 ‘Server refused our key’, ‘Server refused our public key’, ‘Key refused’

Various forms of this error are printed in the PuTTY window, or written to the PuTTY Event Log (see section 3.1.3.1) when trying public-key authentication.

If you see one of these messages, it means that PuTTY has sent a public key to the server and offered to authenticate with it, and the server has refused to accept authentication. This usually means that the server is not configured to accept this key to authenticate this user.

This is almost certainly not a problem with PuTTY. If you see this type of message, the first thing you should do is check your server configuration carefully. Common errors include having the wrong permissions or ownership set on the public key or the user's home directory on the server. Also, read the PuTTY Event Log; the server may have sent diagnostic messages explaining exactly what problem it had with your setup.

Section 8.3 has some hints on server-side public key setup.

10.10 ‘Access denied’, ‘Authentication refused’

Various forms of this error are printed in the PuTTY window, or written to the PuTTY Event Log (see section 3.1.3.1) during authentication.

If you see one of these messages, it means that the server has refused all the forms of authentication PuTTY has tried and it has no further ideas.

It may be worth checking the Event Log for diagnostic messages from the server giving more detail.

This error can be caused by buggy SSH-1 servers that fail to cope with the various strategies we use for camouflaging passwords in transit. Upgrade your server, or use the workarounds described in section 4.26.11 and possibly section 4.26.12.

10.11 ‘No supported authentication methods available’

This error indicates that PuTTY has run out of ways to authenticate you to an SSH server. This may be because PuTTY has TIS or keyboard-interactive authentication disabled, in which case see section 4.21.5 and section 4.21.6.

10.12 ‘Incorrect MAC received on packet’ or ‘Incorrect CRC received on packet’

This error occurs when PuTTY decrypts an SSH packet and its checksum is not correct. This probably means something has gone wrong in the encryption or decryption process. It's difficult to tell from this error message whether the problem is in the client, in the server, or in between.

In particular, if the network is corrupting data at the TCP level, it may only be obvious with cryptographic protocols such as SSH, which explicitly check the integrity of the transferred data and complain loudly if the checks fail. Corruption of protocols without integrity protection (such as HTTP) will manifest in more subtle failures (such as misdisplayed text or images in a web browser) which may not be noticed.

Occasionally this has been caused by server bugs. An example is the bug described at section 4.26.8, although you're very unlikely to encounter that one these days.

In this context MAC stands for Message Authentication Code. It's a cryptographic term, and it has nothing at all to do with Ethernet MAC (Media Access Control) addresses, or with the Apple computer.

10.13 ‘Incoming packet was garbled on decryption’

This error occurs when PuTTY decrypts an SSH packet and the decrypted data makes no sense. This probably means something has gone wrong in the encryption or decryption process. It's difficult to tell from this error message whether the problem is in the client, in the server, or in between.

If you get this error, one thing you could try would be to fiddle with the setting of ‘Miscomputes SSH-2 encryption keys’ (see section 4.26.10) or ‘Ignores SSH-2 maximum packet size’ (see section 4.26.5) on the Bugs panel.

10.14 ‘PuTTY X11 proxy: various errors

This family of errors are reported when PuTTY is doing X forwarding. They are sent back to the X application running on the SSH server, which will usually report the error to the user.

When PuTTY enables X forwarding (see section 3.4) it creates a virtual X display running on the SSH server. This display requires authentication to connect to it (this is how PuTTY prevents other users on your server machine from connecting through the PuTTY proxy to your real X display). PuTTY also sends the server the details it needs to enable clients to connect, and the server should put this mechanism in place automatically, so your X applications should just work.

A common reason why people see one of these messages is because they used SSH to log in as one user (let's say ‘fred’), and then used the Unix su command to become another user (typically ‘root’). The original user, ‘fred’, has access to the X authentication data provided by the SSH server, and can run X applications which are forwarded over the SSH connection. However, the second user (‘root’) does not automatically have the authentication data passed on to it, so attempting to run an X application as that user often fails with this error.

If this happens, it is not a problem with PuTTY. You need to arrange for your X authentication data to be passed from the user you logged in as to the user you used su to become. How you do this depends on your particular system; in fact many modern versions of su do it automatically.

10.15 ‘Network error: Software caused connection abort’

This is a generic error produced by the Windows network code when it kills an established connection for some reason. For example, it might happen if you pull the network cable out of the back of an Ethernet-connected computer, or if Windows has any other similar reason to believe the entire network has become unreachable.

Windows also generates this error if it has given up on the machine at the other end of the connection ever responding to it. If the network between your client and server goes down and your client then tries to send some data, Windows will make several attempts to send the data and will then give up and kill the connection. In particular, this can occur even if you didn't type anything, if you are using SSH-2 and PuTTY attempts a key re-exchange. (See section 4.18.2 for more about key re-exchange.)

(It can also occur if you are using keepalives in your connection. Other people have reported that keepalives fix this error for them. See section 4.14.1 for a discussion of the pros and cons of keepalives.)

We are not aware of any reason why this error might occur that would represent a bug in PuTTY. The problem is between you, your Windows system, your network and the remote system.

10.16 ‘Network error: Connection reset by peer’

This error occurs when the machines at each end of a network connection lose track of the state of the connection between them. For example, you might see it if your SSH server crashes, and manages to reboot fully before you next attempt to send data to it.

However, the most common reason to see this message is if you are connecting through a firewall or a NAT router which has timed the connection out. See question A.7.8 in the FAQ for more details. You may be able to improve the situation by using keepalives; see section 4.14.1 for details on this.

Note that Windows can produce this error in some circumstances without seeing a connection reset from the server, for instance if the connection to the network is lost.

10.17 ‘Network error: Connection refused’

This error means that the network connection PuTTY tried to make to your server was rejected by the server. Usually this happens because the server does not provide the service which PuTTY is trying to access.

Check that you are connecting with the correct protocol (SSH, Telnet, etc), and check that the port number is correct. If that fails, consult the administrator of your server.

10.18 ‘Network error: Connection timed out’

This error means that the network connection PuTTY tried to make to your server received no response at all from the server. Usually this happens because the server machine is completely isolated from the network, or because it is turned off.

Check that you have correctly entered the host name or IP address of your server machine. If that fails, consult the administrator of your server.

Unix also generates this error when it tries to send data down a connection and contact with the server has been completely lost during a connection. (There is a delay of minutes before Unix gives up on receiving a reply from the server.) This can occur if you type things into PuTTY while the network is down, but it can also occur if PuTTY decides of its own accord to send data: due to a repeat key exchange in SSH-2 (see section 4.18.2) or due to keepalives (section 4.14.1).

10.19 ‘Network error: Cannot assign requested address’

This means that the operating system rejected the parameters of the network connection PuTTY tried to make, usually without actually trying to connect to anything, because they were simply invalid.

A common way to provoke this error is to accidentally try to connect to port 0, which is not a valid port number.


If you want to provide feedback on this manual or on the PuTTY tools themselves, see the Feedback page.

[PuTTY release 0.76]
putty-0.76/doc/AppendixA.html0000644000175000017500000026653114072266315013103 00000000000000 PuTTY FAQ

Previous | Contents | Index | Next

Appendix A: PuTTY FAQ

This FAQ is published on the PuTTY web site, and also provided as an appendix in the manual.

A.1 Introduction

A.1.1 What is PuTTY?

PuTTY is a client program for the SSH, Telnet, Rlogin, and SUPDUP network protocols.

These protocols are all used to run a remote session on a computer, over a network. PuTTY implements the client end of that session: the end at which the session is displayed, rather than the end at which it runs.

In really simple terms: you run PuTTY on a Windows machine, and tell it to connect to (for example) a Unix machine. PuTTY opens a window. Then, anything you type into that window is sent straight to the Unix machine, and everything the Unix machine sends back is displayed in the window. So you can work on the Unix machine as if you were sitting at its console, while actually sitting somewhere else.

A.2 Features supported in PuTTY

In general, if you want to know if PuTTY supports a particular feature, you should look for it on the PuTTY web site. In particular:

  • try the changes page, and see if you can find the feature on there. If a feature is listed there, it's been implemented. If it's listed as a change made since the latest version, it should be available in the development snapshots, in which case testing will be very welcome.
  • try the Wishlist page, and see if you can find the feature there. If it's on there, and not in the ‘Recently fixed’ section, it probably hasn't been implemented.

A.2.1 Does PuTTY support SSH-2?

Yes. SSH-2 support has been available in PuTTY since version 0.50 in 2000.

Public key authentication (both RSA and DSA) in SSH-2 was new in version 0.52 in 2002.

A.2.2 Does PuTTY support reading OpenSSH or ssh.com SSH-2 private key files?

PuTTY doesn't support this natively (see the wishlist entry for reasons why not), but as of 0.53 PuTTYgen can convert both OpenSSH and ssh.com private key files into PuTTY's format.

A.2.3 Does PuTTY support SSH-1?

Yes. SSH-1 support has always been available in PuTTY.

However, the SSH-1 protocol has many weaknesses and is no longer considered secure; you should use SSH-2 instead if at all possible.

As of 0.68, PuTTY will no longer fall back to SSH-1 if the server doesn't appear to support SSH-2; you must explicitly ask for SSH-1.

A.2.4 Does PuTTY support local echo?

Yes. Version 0.52 has proper support for local echo.

In version 0.51 and before, local echo could not be separated from local line editing (where you type a line of text locally, and it is not sent to the server until you press Return, so you have the chance to edit it and correct mistakes before the server sees it). New in version 0.52, local echo and local line editing are separate options, and by default PuTTY will try to determine automatically whether to enable them or not, based on which protocol you have selected and also based on hints from the server. If you have a problem with PuTTY's default choice, you can force each option to be enabled or disabled as you choose. The controls are in the Terminal panel, in the section marked ‘Line discipline options’.

A.2.5 Does PuTTY support storing settings, so I don't have to change them every time?

Yes, all of PuTTY's settings can be saved in named session profiles. You can also change the default settings that are used for new sessions. See section 4.1.2 in the documentation for how to do this.

A.2.6 Does PuTTY support storing its settings in a disk file?

Not at present, although section 4.32 in the documentation gives a method of achieving the same effect.

A.2.7 Does PuTTY support full-screen mode, like a DOS box?

Yes; this was added in version 0.52, in 2002.

A.2.8 Does PuTTY have the ability to remember my password so I don't have to type it every time?

No, it doesn't.

Remembering your password is a bad plan for obvious security reasons: anyone who gains access to your machine while you're away from your desk can find out the remembered password, and use it, abuse it or change it.

In addition, it's not even possible for PuTTY to automatically send your password in a Telnet session, because Telnet doesn't give the client software any indication of which part of the login process is the password prompt. PuTTY would have to guess, by looking for words like ‘password’ in the session data; and if your login program is written in something other than English, this won't work.

In SSH, remembering your password would be possible in theory, but there doesn't seem to be much point since SSH supports public key authentication, which is more flexible and more secure. See chapter 8 in the documentation for a full discussion of public key authentication.

A.2.9 Is there an option to turn off the annoying host key prompts?

No, there isn't. And there won't be. Even if you write it yourself and send us the patch, we won't accept it.

Those annoying host key prompts are the whole point of SSH. Without them, all the cryptographic technology SSH uses to secure your session is doing nothing more than making an attacker's job slightly harder; instead of sitting between you and the server with a packet sniffer, the attacker must actually subvert a router and start modifying the packets going back and forth. But that's not all that much harder than just sniffing; and without host key checking, it will go completely undetected by client or server.

Host key checking is your guarantee that the encryption you put on your data at the client end is the same encryption taken off the data at the server end; it's your guarantee that it hasn't been removed and replaced somewhere on the way. Host key checking makes the attacker's job astronomically hard, compared to packet sniffing, and even compared to subverting a router. Instead of applying a little intelligence and keeping an eye on Bugtraq, the attacker must now perform a brute-force attack against at least one military-strength cipher. That insignificant host key prompt really does make that much difference.

If you're having a specific problem with host key checking - perhaps you want an automated batch job to make use of PSCP or Plink, and the interactive host key prompt is hanging the batch process - then the right way to fix it is to add the correct host key to the Registry in advance, or if the Registry is not available, to use the -hostkey command-line option. That way, you retain the important feature of host key checking: the right key will be accepted and the wrong ones will not. Adding an option to turn host key checking off completely is the wrong solution and we will not do it.

If you have host keys available in the common known_hosts format, we have a script called kh2reg.py to convert them to a Windows .REG file, which can be installed ahead of time by double-clicking or using REGEDIT.

A.2.10 Will you write an SSH server for the PuTTY suite, to go with the client?

Not one that you'd want to use.

While much of the protocol and networking code can be made common between a client and server, to make a useful general-purpose server requires all sorts of fiddly new code like interacting with OS authentication databases and the like.

A special-purpose SSH server (called Uppity) can now be built from the PuTTY source code, and indeed it is not usable as a general-purpose server; it exists mainly as a test harness.

If someone else wants to use this as a basis for writing a general-purpose SSH server, they'd be perfectly welcome to of course; but we don't have time, and we don't have motivation. The code is available if anyone else wants to try it.

A.2.11 Can PSCP or PSFTP transfer files in ASCII mode?

Unfortunately not.

This was a limitation of the file transfer protocols as originally specified: the SCP and SFTP protocols had no notion of transferring a file in anything other than binary mode. (This is still true of SCP.)

The current draft protocol spec of SFTP proposes a means of implementing ASCII transfer. At some point PSCP/PSFTP may implement this proposal.

A.3 Ports to other operating systems

The eventual goal is for PuTTY to be a multi-platform program, able to run on at least Windows, Mac OS and Unix.

PuTTY has been gaining a generalised porting layer, drawing a clear line between platform-dependent and platform-independent code. The general intention was for this porting layer to evolve naturally as part of the process of doing the first port; a Unix port has now been released and the plan seems to be working so far.

A.3.1 What ports of PuTTY exist?

Currently, release versions of PuTTY tools only run on Windows systems and Unix.

As of 0.68, the supplied PuTTY executables run on versions of Windows from XP onwards, up to and including Windows 10; and we know of no reason why PuTTY should not continue to work on future versions of Windows. We provide 32-bit and 64-bit Windows executables for the common x86 processor family; see question A.6.10 for discussion of the compatibility issues around that. The 32-bit executables require a Pentium 4 or newer processor. We also provide executables for Windows on Arm processors.

(We used to also provide executables for Windows for the Alpha processor, but stopped after 0.58 due to lack of interest.)

In the development code, a partial port to Mac OS exists (see question A.3.6).

Currently PuTTY does not run on Windows CE (see question A.3.4).

We do not have release-quality ports for any other systems at the present time. If anyone told you we had an Android port, or an iOS port, or any other port of PuTTY, they were mistaken. We don't.

There are some third-party ports to various platforms, mentioned on the Links page of our website.

A.3.2 Is there a port to Unix?

There are Unix ports of most of the traditional PuTTY tools, and also one entirely new application.

If you look at the source release, you should find a unix subdirectory. There are a couple of ways of building it, including the usual configure/make; see the file README in the source distribution. This should build you:

  • Unix ports of PuTTY, Plink, PSCP, and PSFTP, which work pretty much the same as their Windows counterparts;
  • Command-line versions of PuTTYgen and Pageant, whose user interface is quite different to the Windows GUI versions;
  • pterm - an xterm-type program which supports the same terminal emulation as PuTTY.

If you don't have Gtk, you should still be able to build the command-line tools.

A.3.3 What's the point of the Unix port? Unix has OpenSSH.

All sorts of little things. pterm is directly useful to anyone who prefers PuTTY's terminal emulation to xterm's, which at least some people do. Unix Plink has apparently found a niche among people who find the complexity of OpenSSL makes OpenSSH hard to install (and who don't mind Plink not having as many features). Some users want to generate a large number of SSH keys on Unix and then copy them all into PuTTY, and the Unix PuTTYgen should allow them to automate that conversion process.

There were development advantages as well; porting PuTTY to Unix was a valuable path-finding effort for other future ports, and also allowed us to use the excellent Linux tool Valgrind to help with debugging, which has already improved PuTTY's stability on all platforms.

However, if you're a Unix user and you can see no reason to switch from OpenSSH to PuTTY/Plink, then you're probably right. We don't expect our Unix port to be the right thing for everybody.

A.3.4 Will there be a port to Windows CE or PocketPC?

We once did some work on such a port, but it only reached an early stage, and certainly not a useful one. It's no longer being actively worked on.

A.3.5 Is there a port to Windows 3.1?

PuTTY is a 32-bit application from the ground up, so it won't run on Windows 3.1 as a native 16-bit program; and it would be very hard to port it to do so, because of Windows 3.1's vile memory allocation mechanisms.

However, it is possible in theory to compile the existing PuTTY source in such a way that it will run under Win32s (an extension to Windows 3.1 to let you run 32-bit programs). In order to do this you'll need the right kind of C compiler - modern versions of Visual C at least have stopped being backwards compatible to Win32s. Also, the last time we tried this it didn't work very well.

A.3.6 Will there be a port to the Mac?

We hope so!

We attempted one around 2005, written as a native Cocoa application, but it turned out to be very slow to redraw its window for some reason we never got to the bottom of.

In 2015, after porting the GTK front end to work with GTK 3, we began another attempt based on making small changes to the GTK code and building it against the OS X Quartz version of GTK 3. This doesn't seem to have the window redrawing problem any more, so it's already got further than the last effort, but it is still substantially unfinished.

If any OS X and/or GTK programming experts are keen to have a finished version of this, we urge them to help out with some of the remaining problems! See the TODO list in unix/gtkapp.c in the source code.

A.3.7 Will there be a port to EPOC?

I hope so, but given that ports aren't really progressing very fast even on systems the developers do already know how to program for, it might be a long time before any of us get round to learning a new system and doing the port for that.

However, some of the work has been done by other people; see the Links page of our website for various third-party ports.

A.3.8 Will there be a port to the iPhone?

We have no plans to write such a port ourselves; none of us has an iPhone, and developing and publishing applications for it looks awkward and expensive.

However, there is a third-party SSH client for the iPhone and iPod Touch called pTerm, which is apparently based on PuTTY. (This is nothing to do with our similarly-named pterm, which is a standalone terminal emulator for Unix systems; see question A.3.2.)

A.4 Embedding PuTTY in other programs

A.4.1 Is the SSH or Telnet code available as a DLL?

No, it isn't. It would take a reasonable amount of rewriting for this to be possible, and since the PuTTY project itself doesn't believe in DLLs (they make installation more error-prone) none of us has taken the time to do it.

Most of the code cleanup work would be a good thing to happen in general, so if anyone feels like helping, we wouldn't say no.

See also the wishlist entry.

A.4.2 Is the SSH or Telnet code available as a Visual Basic component?

No, it isn't. None of the PuTTY team uses Visual Basic, and none of us has any particular need to make SSH connections from a Visual Basic application. In addition, all the preliminary work to turn it into a DLL would be necessary first; and furthermore, we don't even know how to write VB components.

If someone offers to do some of this work for us, we might consider it, but unless that happens I can't see VB integration being anywhere other than the very bottom of our priority list.

A.4.3 How can I use PuTTY to make an SSH connection from within another program?

Probably your best bet is to use Plink, the command-line connection tool. If you can start Plink as a second Windows process, and arrange for your primary process to be able to send data to the Plink process, and receive data from it, through pipes, then you should be able to make SSH connections from your program.

This is what CVS for Windows does, for example.

A.5 Details of PuTTY's operation

A.5.1 What terminal type does PuTTY use?

For most purposes, PuTTY can be considered to be an xterm terminal.

PuTTY also supports some terminal control sequences not supported by the real xterm: notably the Linux console sequences that reconfigure the colour palette, and the title bar control sequences used by DECterm (which are different from the xterm ones; PuTTY supports both).

By default, PuTTY announces its terminal type to the server as xterm. If you have a problem with this, you can reconfigure it to say something else; vt220 might help if you have trouble.

A.5.2 Where does PuTTY store its data?

On Windows, PuTTY stores most of its data (saved sessions, SSH host keys) in the Registry. The precise location is

HKEY_CURRENT_USER\Software\SimonTatham\PuTTY

and within that area, saved sessions are stored under Sessions while host keys are stored under SshHostKeys.

PuTTY also requires a random number seed file, to improve the unpredictability of randomly chosen data needed as part of the SSH cryptography. This is stored by default in a file called PUTTY.RND; this is stored by default in the ‘Application Data’ directory, or failing that, one of a number of fallback locations. If you want to change the location of the random number seed file, you can put your chosen pathname in the Registry, at

HKEY_CURRENT_USER\Software\SimonTatham\PuTTY\RandSeedFile

You can ask PuTTY to delete all this data; see question A.8.2.

On Unix, PuTTY stores all of this data in a directory ~/.putty by default.

A.5.3 Why do small PuTTY icons appear next to the login prompts?

As of PuTTY 0.71, some lines of text in the terminal window are marked with a small copy of the PuTTY icon (as far as pixels allow).

This is to show trustworthiness. When the PuTTY icon appears next to a line of text, it indicates that that line of text was generated by PuTTY itself, and not generated by the server and sent to PuTTY.

Text that comes from the server does not have this icon, and we've arranged that the server should not be able to fake it. (There's no control sequence the server can send which will make PuTTY draw its own icon, and if the server tries to move the cursor back up to a line that already has an icon and overwrite the text, the icon will disappear.)

This lets you tell the difference between (for example) a legitimate prompt in which PuTTY itself asks you for your private key passphrase, and a fake prompt in which the server tries to send the identical text to trick you into telling it your private key passphrase.

A.5.4 Why has Plink started saying ‘Press Return to begin session’?

As of PuTTY 0.71, if you use Plink for an interactive SSH session, then after the login phase has finished, it will present a final interactive prompt saying ‘Access granted. Press Return to begin session’.

This is another defence against servers trying to mimic the real authentication prompts after the session has started. When you pass through that prompt, you know that everything after it is generated by the server and not by Plink itself, so any request for your private key passphrase should be treated with suspicion.

In Plink, we can't use the defence described in section A.5.3: Plink is running in the terminal, so anything it can write into the terminal, the server could write in the same way after the session starts. And we can't just print a separator line without a pause, because then the server could simply move the cursor back up to it and overwrite it (probably with a brief flicker, but you might easily miss that). The only robust defence anyone has come up with involves this pause.

If you trust your server not to be abusive, you can turn this off. It will also not appear in various other circumstances where Plink can be confident it isn't necessary. See section 7.2.3.6 for details.

A.6 HOWTO questions

A.6.1 What login name / password should I use?

This is not a question you should be asking us.

PuTTY is a communications tool, for making connections to other computers. We maintain the tool; we don't administer any computers that you're likely to be able to use, in the same way that the people who make web browsers aren't responsible for most of the content you can view in them. We cannot help with questions of this sort.

If you know the name of the computer you want to connect to, but don't know what login name or password to use, you should talk to whoever administers that computer. If you don't know who that is, see the next question for some possible ways to find out.

A.6.2 What commands can I type into my PuTTY terminal window?

Again, this is not a question you should be asking us. You need to read the manuals, or ask the administrator, of the computer you have connected to.

PuTTY does not process the commands you type into it. It's only a communications tool. It makes a connection to another computer; it passes the commands you type to that other computer; and it passes the other computer's responses back to you. Therefore, the precise range of commands you can use will not depend on PuTTY, but on what kind of computer you have connected to and what software is running on it. The PuTTY team cannot help you with that.

(Think of PuTTY as being a bit like a telephone. If you phone somebody up and you don't know what language to speak to make them understand you, it isn't the telephone company's job to find that out for you. We just provide the means for you to get in touch; making yourself understood is somebody else's problem.)

If you are unsure of where to start looking for the administrator of your server, a good place to start might be to remember how you found out the host name in the PuTTY configuration. If you were given that host name by e-mail, for example, you could try asking the person who sent you that e-mail. If your company's IT department provided you with ready-made PuTTY saved sessions, then that IT department can probably also tell you something about what commands you can type during those sessions. But the PuTTY maintainer team does not administer any server you are likely to be connecting to, and cannot help you with questions of this type.

A.6.3 How can I make PuTTY start up maximised?

Create a Windows shortcut to start PuTTY from, and set it as ‘Run Maximized’.

A.6.4 How can I create a Windows shortcut to start a particular saved session directly?

To run a PuTTY session saved under the name ‘mysession’, create a Windows shortcut that invokes PuTTY with a command line like

\path\name\to\putty.exe -load "mysession"

(Note: prior to 0.53, the syntax was @session. This is now deprecated and may be removed at some point.)

A.6.5 How can I start an SSH session straight from the command line?

Use the command line putty -ssh host.name. Alternatively, create a saved session that specifies the SSH protocol, and start the saved session as shown in question A.6.4.

A.6.6 How do I copy and paste between PuTTY and other Windows applications?

Copy and paste works similarly to the X Window System. You use the left mouse button to select text in the PuTTY window. The act of selection automatically copies the text to the clipboard: there is no need to press Ctrl-Ins or Ctrl-C or anything else. In fact, pressing Ctrl-C will send a Ctrl-C character to the other end of your connection (just like it does the rest of the time), which may have unpleasant effects. The only thing you need to do, to copy text to the clipboard, is to select it.

To paste the clipboard contents into a PuTTY window, by default you click the right mouse button. If you have a three-button mouse and are used to X applications, you can configure pasting to be done by the middle button instead, but this is not the default because most Windows users don't have a middle button at all.

You can also paste by pressing Shift-Ins.

A.6.7 How do I use all PuTTY's features (public keys, proxying, cipher selection, etc.) in PSCP, PSFTP and Plink?

Most major features (e.g., public keys, port forwarding) are available through command line options. See the documentation.

Not all features are accessible from the command line yet, although we'd like to fix this. In the meantime, you can use most of PuTTY's features if you create a PuTTY saved session, and then use the name of the saved session on the command line in place of a hostname. This works for PSCP, PSFTP and Plink (but don't expect port forwarding in the file transfer applications!).

A.6.8 How do I use PSCP.EXE? When I double-click it gives me a command prompt window which then closes instantly.

PSCP is a command-line application, not a GUI application. If you run it without arguments, it will simply print a help message and terminate.

To use PSCP properly, run it from a Command Prompt window. See chapter 5 in the documentation for more details.

A.6.9 How do I use PSCP to copy a file whose name has spaces in?

If PSCP is using the traditional SCP protocol, this is confusing. If you're specifying a file at the local end, you just use one set of quotes as you would normally do:

pscp "local filename with spaces" user@host:
pscp user@host:myfile "local filename with spaces"

But if the filename you're specifying is on the remote side, you have to use backslashes and two sets of quotes:

pscp user@host:"\"remote filename with spaces\"" local_filename
pscp local_filename user@host:"\"remote filename with spaces\""

Worse still, in a remote-to-local copy you have to specify the local file name explicitly, otherwise PSCP will complain that they don't match (unless you specified the -unsafe option). The following command will give an error message:

c:\>pscp user@host:"\"oo er\"" .
warning: remote host tried to write to a file called 'oo er'
         when we requested a file called '"oo er"'.

Instead, you need to specify the local file name in full:

c:\>pscp user@host:"\"oo er\"" "oo er"

If PSCP is using the newer SFTP protocol, none of this is a problem, and all filenames with spaces in are specified using a single pair of quotes in the obvious way:

pscp "local file" user@host:
pscp user@host:"remote file" .

A.6.10 Should I run the 32-bit or the 64-bit version?

If you're not sure, the 32-bit version is generally the safe option. It will run perfectly well on all processors and on all versions of Windows that PuTTY supports. PuTTY doesn't require to run as a 64-bit application to work well, and having a 32-bit PuTTY on a 64-bit system isn't likely to cause you any trouble.

The 64-bit version (first released in 0.68) will only run if you have a 64-bit processor and a 64-bit edition of Windows (both of these things are likely to be true of any recent Windows PC). It will run somewhat faster (in particular, the cryptography will be faster, especially during link setup), but it will consume slightly more memory.

If you need to use an external DLL for GSSAPI authentication, that DLL may only be available in a 32-bit or 64-bit form, and that will dictate the version of PuTTY you need to use. (You will probably know if you're doing this; see section 4.22.2 in the documentation.)

A.7 Troubleshooting

A.7.1 Why do I see ‘Fatal: Protocol error: Expected control record’ in PSCP?

This happens because PSCP was expecting to see data from the server that was part of the PSCP protocol exchange, and instead it saw data that it couldn't make any sense of at all.

This almost always happens because the startup scripts in your account on the server machine are generating output. This is impossible for PSCP, or any other SCP client, to work around. You should never use startup files (.bashrc, .cshrc and so on) which generate output in non-interactive sessions.

This is not actually a PuTTY problem. If PSCP fails in this way, then all other SCP clients are likely to fail in exactly the same way. The problem is at the server end.

A.7.2 I clicked on a colour in the Colours panel, and the colour didn't change in my terminal.

That isn't how you're supposed to use the Colours panel.

During the course of a session, PuTTY potentially uses all the colours listed in the Colours panel. It's not a question of using only one of them and you choosing which one; PuTTY will use them all. The purpose of the Colours panel is to let you adjust the appearance of all the colours. So to change the colour of the cursor, for example, you would select ‘Cursor Colour’, press the ‘Modify’ button, and select a new colour from the dialog box that appeared. Similarly, if you want your session to appear in green, you should select ‘Default Foreground’ and press ‘Modify’. Clicking on ‘ANSI Green’ won't turn your session green; it will only allow you to adjust the shade of green used when PuTTY is instructed by the server to display green text.

A.7.3 After trying to establish an SSH-2 connection, PuTTY says ‘Out of memory’ and dies.

If this happens just while the connection is starting up, this often indicates that for some reason the client and server have failed to establish a session encryption key. Somehow, they have performed calculations that should have given each of them the same key, but have ended up with different keys; so data encrypted by one and decrypted by the other looks like random garbage.

This causes an ‘out of memory’ error because the first encrypted data PuTTY expects to see is the length of an SSH message. Normally this will be something well under 100 bytes. If the decryption has failed, PuTTY will see a completely random length in the region of two gigabytes, and will try to allocate enough memory to store this non-existent message. This will immediately lead to it thinking it doesn't have enough memory, and panicking.

If this happens to you, it is quite likely to still be a PuTTY bug and you should report it (although it might be a bug in your SSH server instead); but it doesn't necessarily mean you've actually run out of memory.

A.7.4 When attempting a file transfer, either PSCP or PSFTP says ‘Out of memory’ and dies.

This is almost always caused by your login scripts on the server generating output. PSCP or PSFTP will receive that output when they were expecting to see the start of a file transfer protocol, and they will attempt to interpret the output as file-transfer protocol. This will usually lead to an ‘out of memory’ error for much the same reasons as given in question A.7.3.

This is a setup problem in your account on your server, not a PSCP/PSFTP bug. Your login scripts should never generate output during non-interactive sessions; secure file transfer is not the only form of remote access that will break if they do.

On Unix, a simple fix is to ensure that all the parts of your login script that might generate output are in .profile (if you use a Bourne shell derivative) or .login (if you use a C shell). Putting them in more general files such as .bashrc or .cshrc is liable to lead to problems.

A.7.5 PSFTP transfers files much slower than PSCP.

The throughput of PSFTP 0.54 should be much better than 0.53b and prior; we've added code to the SFTP backend to queue several blocks of data rather than waiting for an acknowledgement for each. (The SCP backend did not suffer from this performance issue because SCP is a much simpler protocol.)

A.7.6 When I run full-colour applications, I see areas of black space where colour ought to be, or vice versa.

You almost certainly need to change the ‘Use background colour to erase screen’ setting in the Terminal panel. If there is too much black space (the commoner situation), you should enable it, while if there is too much colour, you should disable it. (See section 4.3.5.)

In old versions of PuTTY, this was disabled by default, and would not take effect until you reset the terminal (see question A.7.7). Since 0.54, it is enabled by default, and changes take effect immediately.

A.7.7 When I change some terminal settings, nothing happens.

Some of the terminal options (notably Auto Wrap and background-colour screen erase) actually represent the default setting, rather than the currently active setting. The server can send sequences that modify these options in mid-session, but when the terminal is reset (by server action, or by you choosing ‘Reset Terminal’ from the System menu) the defaults are restored.

In versions 0.53b and prior, if you change one of these options in the middle of a session, you will find that the change does not immediately take effect. It will only take effect once you reset the terminal.

In version 0.54, the behaviour has changed - changes to these settings take effect immediately.

A.7.8 My PuTTY sessions unexpectedly close after they are idle for a while.

Some types of firewall, and almost any router doing Network Address Translation (NAT, also known as IP masquerading), will forget about a connection through them if the connection does nothing for too long. This will cause the connection to be rudely cut off when contact is resumed.

You can try to combat this by telling PuTTY to send keepalives: packets of data which have no effect on the actual session, but which reassure the router or firewall that the network connection is still active and worth remembering about.

Keepalives don't solve everything, unfortunately; although they cause greater robustness against this sort of router, they can also cause a loss of robustness against network dropouts. See section 4.14.1 in the documentation for more discussion of this.

A.7.9 PuTTY's network connections time out too quickly when network connectivity is temporarily lost.

This is a Windows problem, not a PuTTY problem. The timeout value can't be set on per application or per session basis. To increase the TCP timeout globally, you need to tinker with the Registry.

On Windows 95, 98 or ME, the registry key you need to create or change is

HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\VxD\
  MSTCP\MaxDataRetries

(it must be of type DWORD in Win95, or String in Win98/ME). (See MS Knowledge Base article 158474 for more information.)

On Windows NT, 2000, or XP, the registry key to create or change is

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\
  Parameters\TcpMaxDataRetransmissions

and it must be of type DWORD. (See MS Knowledge Base articles 120642 and 314053 for more information.)

Set the key's value to something like 10. This will cause Windows to try harder to keep connections alive instead of abandoning them.

A.7.10 When I cat a binary file, I get ‘PuTTYPuTTYPuTTY’ on my command line.

Don't do that, then.

This is designed behaviour; when PuTTY receives the character Control-E from the remote server, it interprets it as a request to identify itself, and so it sends back the string ‘PuTTY’ as if that string had been entered at the keyboard. Control-E should only be sent by programs that are prepared to deal with the response. Writing a binary file to your terminal is likely to output many Control-E characters, and cause this behaviour. Don't do it. It's a bad plan.

To mitigate the effects, you could configure the answerback string to be empty (see section 4.3.7); but writing binary files to your terminal is likely to cause various other unpleasant behaviour, so this is only a small remedy.

A.7.11 When I cat a binary file, my window title changes to a nonsense string.

Don't do that, then.

It is designed behaviour that PuTTY should have the ability to adjust the window title on instructions from the server. Normally the control sequence that does this should only be sent deliberately, by programs that know what they are doing and intend to put meaningful text in the window title. Writing a binary file to your terminal runs the risk of sending the same control sequence by accident, and cause unexpected changes in the window title. Don't do it.

A.7.12 My keyboard stops working once PuTTY displays the password prompt.

No, it doesn't. PuTTY just doesn't display the password you type, so that someone looking at your screen can't see what it is.

Unlike the Windows login prompts, PuTTY doesn't display the password as a row of asterisks either. This is so that someone looking at your screen can't even tell how long your password is, which might be valuable information.

A.7.13 One or more function keys don't do what I expected in a server-side application.

If you've already tried all the relevant options in the PuTTY Keyboard panel, you may need to mail the PuTTY maintainers and ask.

It is not usually helpful just to tell us which application, which server operating system, and which key isn't working; in order to replicate the problem we would need to have a copy of every operating system, and every application, that anyone has ever complained about.

PuTTY responds to function key presses by sending a sequence of control characters to the server. If a function key isn't doing what you expect, it's likely that the character sequence your application is expecting to receive is not the same as the one PuTTY is sending. Therefore what we really need to know is what sequence the application is expecting.

The simplest way to investigate this is to find some other terminal environment, in which that function key does work; and then investigate what sequence the function key is sending in that situation. One reasonably easy way to do this on a Unix system is to type the command cat, and then press the function key. This is likely to produce output of the form ^[[11~. You can also do this in PuTTY, to find out what sequence the function key is producing in that. Then you can mail the PuTTY maintainers and tell us ‘I wanted the F1 key to send ^[[11~, but instead it's sending ^[OP, can this be done?’, or something similar.

You should still read the Feedback page on the PuTTY website (also provided as appendix B in the manual), and follow the guidelines contained in that.

A.7.14 Why do I see ‘Couldn't load private key from ...’? Why can PuTTYgen load my key but not PuTTY?

It's likely that you've generated an SSH protocol 2 key with PuTTYgen, but you're trying to use it in an SSH-1 connection. SSH-1 and SSH-2 keys have different formats, and (at least in 0.52) PuTTY's reporting of a key in the wrong format isn't optimal.

To connect using SSH-2 to a server that supports both versions, you need to change the configuration from the default (see question A.2.1).

A.7.15 When I'm connected to a Red Hat Linux 8.0 system, some characters don't display properly.

A common complaint is that hyphens in man pages show up as a-acute.

With release 8.0, Red Hat appear to have made UTF-8 the default character set. There appears to be no way for terminal emulators such as PuTTY to know this (as far as we know, the appropriate escape sequence to switch into UTF-8 mode isn't sent).

A fix is to configure sessions to RH8 systems to use UTF-8 translation - see section 4.10.1 in the documentation. (Note that if you use ‘Change Settings’, changes may not take place immediately - see question A.7.7.)

If you really want to change the character set used by the server, the right place is /etc/sysconfig/i18n, but this shouldn't be necessary.

A.7.16 Since I upgraded to PuTTY 0.54, the scrollback has stopped working when I run screen.

PuTTY's terminal emulator has always had the policy that when the ‘alternate screen’ is in use, nothing is added to the scrollback. This is because the usual sorts of programs which use the alternate screen are things like text editors, which tend to scroll back and forth in the same document a lot; so (a) they would fill up the scrollback with a large amount of unhelpfully disordered text, and (b) they contain their own method for the user to scroll back to the bit they were interested in. We have generally found this policy to do the Right Thing in almost all situations.

Unfortunately, screen is one exception: it uses the alternate screen, but it's still usually helpful to have PuTTY's scrollback continue working. The simplest solution is to go to the Features control panel and tick ‘Disable switching to alternate terminal screen’. (See section 4.6.4 for more details.) Alternatively, you can tell screen itself not to use the alternate screen: the screen FAQ suggests adding the line ‘termcapinfo xterm ti@:te@’ to your .screenrc file.

The reason why this only started to be a problem in 0.54 is because screen typically uses an unusual control sequence to switch to the alternate screen, and previous versions of PuTTY did not support this sequence.

A.7.17 Since I upgraded Windows XP to Service Pack 2, I can't use addresses like 127.0.0.2.

Some people who ask PuTTY to listen on localhost addresses other than 127.0.0.1 to forward services such as SMB and Windows Terminal Services have found that doing so no longer works since they upgraded to WinXP SP2.

This is apparently an issue with SP2 that is acknowledged by Microsoft in MS Knowledge Base article 884020. The article links to a fix you can download.

(However, we've been told that SP2 also fixes the bug that means you need to use non-127.0.0.1 addresses to forward Terminal Services in the first place.)

A.7.18 PSFTP commands seem to be missing a directory separator (slash).

Some people have reported the following incorrect behaviour with PSFTP:

psftp> pwd
Remote directory is /dir1/dir2
psftp> get filename.ext
/dir1/dir2filename.ext: no such file or directory

This is not a bug in PSFTP. There is a known bug in some versions of portable OpenSSH (bug 697) that causes these symptoms; it appears to have been introduced around 3.7.x. It manifests only on certain platforms (AIX is what has been reported to us).

There is a patch for OpenSSH attached to that bug; it's also fixed in recent versions of portable OpenSSH (from around 3.8).

A.7.19 Do you want to hear about ‘Software caused connection abort’?

In the documentation for PuTTY 0.53 and 0.53b, we mentioned that we'd like to hear about any occurrences of this error. Since the release of PuTTY 0.54, however, we've been convinced that this error doesn't indicate that PuTTY's doing anything wrong, and we don't need to hear about further occurrences. See section 10.15 for our current documentation of this error.

A.7.20 My SSH-2 session locks up for a few seconds every so often.

Recent versions of PuTTY automatically initiate repeat key exchange once per hour, to improve session security. If your client or server machine is slow, you may experience this as a delay of anything up to thirty seconds or so.

These delays are inconvenient, but they are there for your protection. If they really cause you a problem, you can choose to turn off periodic rekeying using the ‘Kex’ configuration panel (see section 4.18), but be aware that you will be sacrificing security for this. (Falling back to SSH-1 would also remove the delays, but would lose a lot more security still. We do not recommend it.)

A.7.21 PuTTY fails to start up. Windows claims that ‘the application configuration is incorrect’.

This is caused by a bug in certain versions of Windows XP which is triggered by PuTTY 0.58. This was fixed in 0.59. The ‘xp-wont-run’ entry in PuTTY's wishlist has more details.

A.7.22 When I put 32-bit PuTTY in C:\WINDOWS\SYSTEM32 on my 64-bit Windows system, ‘Duplicate Session’ doesn't work.

The short answer is not to put the PuTTY executables in that location.

On 64-bit systems, C:\WINDOWS\SYSTEM32 is intended to contain only 64-bit binaries; Windows' 32-bit binaries live in C:\WINDOWS\SYSWOW64. When a 32-bit PuTTY executable runs on a 64-bit system, it cannot by default see the ‘real’ C:\WINDOWS\SYSTEM32 at all, because the File System Redirector arranges that the running program sees the appropriate kind of binaries in SYSTEM32. Thus, operations in the PuTTY suite that involve it accessing its own executables, such as ‘New Session’ and ‘Duplicate Session’, will not work.

A.7.23 After I upgraded PuTTY to 0.68, I can no longer connect to my embedded device or appliance.

If your SSH server has started unexpectedly closing SSH connections after you enter your password, and it worked before 0.68, you may have a buggy server that objects to certain SSH protocol extensions.

The SSH protocol recently gained a new ‘terminal mode’, IUTF8, which PuTTY sends by default; see section 4.23.2. This is the first new terminal mode since the SSH-2 protocol was defined. While servers are supposed to ignore modes they don't know about, some buggy servers will unceremoniously close the connection if they see anything they don't recognise. SSH servers in embedded devices, network appliances, and the like seem to disproportionately have this bug.

If you think you have such a server, from 0.69 onwards you can disable sending of the IUTF8 mode: on the SSH / TTY panel, select IUTF8 on the list, select ‘Nothing’, and press ‘Set’. (It's not possible to disable sending this mode in 0.68.)

A.8 Security questions

A.8.1 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 you plan to type passwords into either. It might be watching your keystrokes, or it might tamper with the PuTTY binary you download. There is no program safe enough that you can run it on an actively malicious PC and get away with typing passwords into it.

If you do trust the PC, then it's probably OK to use PuTTY on it (but if you don't trust the network, then the PuTTY download might be tampered with, so it would be better to carry PuTTY with you on a USB stick).

A.8.2 What does PuTTY leave on a system? How can I clean up after it?

PuTTY will leave some Registry entries, and a random seed file, on the PC (see question A.5.2). Windows 7 and up also remember some information about recently launched sessions for the ‘jump list’ feature.

If you are using PuTTY on a public PC, or somebody else's PC, you might want to clean this information up when you leave. You can do that automatically, by running the command putty -cleanup. See section 3.11.2 in the documentation for more detail. (Note that this only removes settings for the currently logged-in user on multi-user systems.)

If PuTTY was installed from the installer package, it will also appear in ‘Add/Remove Programs’. Current versions of the installer do not offer to remove the above-mentioned items, so if you want them removed you should run putty -cleanup before uninstalling.

A.8.3 How come PuTTY now supports DSA, when the website used to say how insecure it was?

DSA has a major weakness if badly implemented: it relies on a random number generator to far too great an extent. If the random number generator produces a number an attacker can predict, the DSA private key is exposed - meaning that the attacker can log in as you on all systems that accept that key.

The PuTTY policy changed because the developers were informed of ways to implement DSA which do not suffer nearly as badly from this weakness, and indeed which don't need to rely on random numbers at all. For this reason we now believe PuTTY's DSA implementation is probably OK.

The recently added elliptic-curve signature methods are also DSA-style algorithms, so they have this same weakness in principle. Our ECDSA implementation uses the same defence as DSA, while our Ed25519 implementation uses the similar system (but different in details) that the Ed25519 spec mandates.

A.8.4 Couldn't Pageant use VirtualLock() to stop private keys being written to disk?

Unfortunately not. The VirtualLock() function in the Windows API doesn't do a proper job: it may prevent small pieces of a process's memory from being paged to disk while the process is running, but it doesn't stop the process's memory as a whole from being swapped completely out to disk when the process is long-term inactive. And Pageant spends most of its time inactive.

A.9 Administrative questions

A.9.1 Is putty.org your website?

No, it isn't. putty.org is run by an opportunist who uses it to advertise their own commercial SSH implementation to people looking for our free one. We don't own that site, we can't control it, and we don't advise anyone to use it in preference to our own site.

The real PuTTY web site, run by the PuTTY team, has always been at https://www.chiark.greenend.org.uk/~sgtatham/putty/.

A.9.2 Would you like me to register you a nicer domain name?

No, thank you. Even if you can find one (most of them seem to have been registered already, by people who didn't ask whether we actually wanted it before they applied), we're happy with the PuTTY web site being exactly where it is. It's not hard to find (just type ‘putty’ into google.com and we're the first link returned), and we don't believe the administrative hassle of moving the site would be worth the benefit.

In addition, if we did want a custom domain name, we would want to run it ourselves, so we knew for certain that it would continue to point where we wanted it, and wouldn't suddenly change or do strange things. Having it registered for us by a third party who we don't even know is not the best way to achieve this.

A.9.3 Would you like free web hosting for the PuTTY web site?

We already have some, thanks.

A.9.4 Would you link to my web site from the PuTTY web site?

Only if the content of your web page is of definite direct interest to PuTTY users. If your content is unrelated, or only tangentially related, to PuTTY, then the link would simply be advertising for you.

One very nice effect of the Google ranking mechanism is that by and large, the most popular web sites get the highest rankings. This means that when an ordinary person does a search, the top item in the search is very likely to be a high-quality site or the site they actually wanted, rather than the site which paid the most money for its ranking.

The PuTTY web site is held in high esteem by Google, for precisely this reason: lots of people have linked to it simply because they like PuTTY, without us ever having to ask anyone to link to us. We feel that it would be an abuse of this esteem to use it to boost the ranking of random advertisers' web sites. If you want your web site to have a high Google ranking, we'd prefer that you achieve this the way we did - by being good enough at what you do that people will link to you simply because they like you.

In particular, we aren't interested in trading links for money (see above), and we certainly aren't interested in trading links for other links (since we have no advertising on our web site, our Google ranking is not even directly worth anything to us). If we don't want to link to you for free, then we probably won't want to link to you at all.

If you have software based on PuTTY, or specifically designed to interoperate with PuTTY, or in some other way of genuine interest to PuTTY users, then we will probably be happy to add a link to you on our Links page. And if you're running a particularly valuable mirror of the PuTTY web site, we might be interested in linking to you from our Mirrors page.

A.9.5 Why don't you move PuTTY to SourceForge?

Partly, because we don't want to move the web site location (see question A.9.2).

Also, security reasons. PuTTY is a security product, and as such it is particularly important to guard the code and the web site against unauthorised modifications which might introduce subtle security flaws. Therefore, we prefer that the Git repository, web site and FTP site remain where they are, under the direct control of system administrators we know and trust personally, rather than being run by a large organisation full of people we've never met and which is known to have had breakins in the past.

No offence to SourceForge; I think they do a wonderful job. But they're not ideal for everyone, and in particular they're not ideal for us.

A.9.6 Why can't I subscribe to the putty-bugs mailing list?

Because you're not a member of the PuTTY core development team. The putty-bugs mailing list is not a general newsgroup-like discussion forum; it's a contact address for the core developers, and an internal mailing list for us to discuss things among ourselves. If we opened it up for everybody to subscribe to, it would turn into something more like a newsgroup and we would be completely overwhelmed by the volume of traffic. It's hard enough to keep up with the list as it is.

A.9.7 If putty-bugs isn't a general-subscription mailing list, what is?

There isn't one, that we know of.

If someone else wants to set up a mailing list or other forum for PuTTY users to help each other with common problems, that would be fine with us, though the PuTTY team would almost certainly not have the time to read it. It's probably better to use one of the established newsgroups for this purpose (see section B.1.2).

A.9.8 How can I donate to PuTTY development?

Please, please don't feel you have to. PuTTY is completely free software, and not shareware. We think it's very important that everybody who wants to use PuTTY should be able to, whether they have any money or not; so the last thing we would want is for a PuTTY user to feel guilty because they haven't paid us any money. If you want to keep your money, please do keep it. We wouldn't dream of asking for any.

Having said all that, if you still really want to give us money, we won't argue :-) The easiest way for us to accept donations is if you send money to <anakin@pobox.com> using PayPal (www.paypal.com). If you don't like PayPal, talk to us; we can probably arrange some alternative means.

Small donations (tens of dollars or tens of euros) will probably be spent on beer or curry, which helps motivate our volunteer team to continue doing this for the world. Larger donations will be spent on something that actually helps development, if we can find anything (perhaps new hardware, or a copy of Windows XP), but if we can't find anything then we'll just distribute the money among the developers. If you want to be sure your donation is going towards something worthwhile, ask us first. If you don't like these terms, feel perfectly free not to donate. We don't mind.

A.9.9 Can I have permission to put PuTTY on a cover disk / distribute it with other software / etc?

Yes. For most things, you need not bother asking us explicitly for permission; our licence already grants you permission.

See section B.8 for more details.

A.9.10 Can you sign an agreement indemnifying us against security problems in PuTTY?

No!

A vendor of physical security products (e.g. locks) might plausibly be willing to accept financial liability for a product that failed to perform as advertised and resulted in damage (e.g. valuables being stolen). The reason they can afford to do this is because they sell a lot of units, and only a small proportion of them will fail; so they can meet their financial liability out of the income from all the rest of their sales, and still have enough left over to make a profit. Financial liability is intrinsically linked to selling your product for money.

There are two reasons why PuTTY is not analogous to a physical lock in this context. One is that software products don't exhibit random variation: if PuTTY has a security hole (which does happen, although we do our utmost to prevent it and to respond quickly when it does), every copy of PuTTY will have the same hole, so it's likely to affect all the users at the same time. So even if our users were all paying us to use PuTTY, we wouldn't be able to simultaneously pay every affected user compensation in excess of the amount they had paid us in the first place. It just wouldn't work.

The second, much more important, reason is that PuTTY users don't pay us. The PuTTY team does not have an income; it's a volunteer effort composed of people spending their spare time to try to write useful software. We aren't even a company or any kind of legally recognised organisation. We're just a bunch of people who happen to do some stuff in our spare time.

Therefore, to ask us to assume financial liability is to ask us to assume a risk of having to pay it out of our own personal pockets: out of the same budget from which we buy food and clothes and pay our rent. That's more than we're willing to give. We're already giving a lot of our spare time to developing software for free; if we had to pay our own money to do it as well, we'd start to wonder why we were bothering.

Free software fundamentally does not work on the basis of financial guarantees. Your guarantee of the software functioning correctly is simply that you have the source code and can check it before you use it. If you want to be sure there aren't any security holes, do a security audit of the PuTTY code, or hire a security engineer if you don't have the necessary skills yourself: instead of trying to ensure you can get compensation in the event of a disaster, try to ensure there isn't a disaster in the first place.

If you really want financial security, see if you can find a security engineer who will take financial responsibility for the correctness of their review. (This might be less likely to suffer from the everything-failing-at-once problem mentioned above, because such an engineer would probably be reviewing a lot of different products which would tend to fail independently.) Failing that, see if you can persuade an insurance company to insure you against security incidents, and if the insurer demands it as a condition then get our code reviewed by a security engineer they're happy with.

A.9.11 Can you sign this form granting us permission to use/distribute PuTTY?

If your form contains any clause along the lines of ‘the undersigned represents and warrants’, we're not going to sign it. This is particularly true if it asks us to warrant that PuTTY is secure; see question A.9.10 for more discussion of this. But it doesn't really matter what we're supposed to be warranting: even if it's something we already believe is true, such as that we don't infringe any third-party copyright, we will not sign a document accepting any legal or financial liability. This is simply because the PuTTY development project has no income out of which to satisfy that liability, or pay legal costs, should it become necessary. We cannot afford to be sued. We are assuring you that we have done our best; if that isn't good enough for you, tough.

The existing PuTTY licence document already gives you permission to use or distribute PuTTY in pretty much any way which does not involve pretending you wrote it or suing us if it goes wrong. We think that really ought to be enough for anybody.

See also question A.9.13 for another reason why we don't want to do this sort of thing.

A.9.12 Can you write us a formal notice of permission to use PuTTY?

We could, in principle, but it isn't clear what use it would be. If you think there's a serious chance of one of the PuTTY copyright holders suing you (which we don't!), you would presumably want a signed notice from all of them; and we couldn't provide that even if we wanted to, because many of the copyright holders are people who contributed some code in the past and with whom we subsequently lost contact. Therefore the best we would be able to do even in theory would be to have the core development team sign the document, which wouldn't guarantee you that some other copyright holder might not sue.

See also question A.9.13 for another reason why we don't want to do this sort of thing.

A.9.13 Can you sign anything for us?

Not unless there's an incredibly good reason.

We are generally unwilling to set a precedent that involves us having to enter into individual agreements with PuTTY users. We estimate that we have literally millions of users, and we absolutely would not have time to go round signing specific agreements with every one of them. So if you want us to sign something specific for you, you might usefully stop to consider whether there's anything special that distinguishes you from 999,999 other users, and therefore any reason we should be willing to sign something for you without it setting such a precedent.

If your company policy requires you to have an individual agreement with the supplier of any software you use, then your company policy is simply not well suited to using popular free software, and we urge you to consider this as a flaw in your policy.

A.9.14 If you won't sign anything, can you give us some sort of assurance that you won't make PuTTY closed-source in future?

Yes and no.

If what you want is an assurance that some current version of PuTTY which you've already downloaded will remain free, then you already have that assurance: it's called the PuTTY Licence. It grants you permission to use, distribute and copy the software to which it applies; once we've granted that permission (which we have), we can't just revoke it.

On the other hand, if you want an assurance that future versions of PuTTY won't be closed-source, that's more difficult. We could in principle sign a document stating that we would never release a closed-source PuTTY, but that wouldn't assure you that we would keep releasing open-source PuTTYs: we would still have the option of ceasing to develop PuTTY at all, which would surely be even worse for you than making it closed-source! (And we almost certainly wouldn't want to sign a document guaranteeing that we would actually continue to do development work on PuTTY; we certainly wouldn't sign it for free. Documents like that are called contracts of employment, and are generally not signed except in return for a sizeable salary.)

If we were to stop developing PuTTY, or to decide to make all future releases closed-source, then you would still be free to copy the last open release in accordance with the current licence, and in particular you could start your own fork of the project from that release. If this happened, I confidently predict that somebody would do that, and that some kind of a free PuTTY would continue to be developed. There's already precedent for that sort of thing happening in free software. We can't guarantee that somebody other than you would do it, of course; you might have to do it yourself. But we can assure you that there would be nothing preventing anyone from continuing free development if we stopped.

(Finally, we can also confidently predict that if we made PuTTY closed-source and someone made an open-source fork, most people would switch to the latter. Therefore, it would be pretty stupid of us to try it.)

A.9.15 Can you provide us with export control information / FIPS certification for PuTTY?

Some people have asked us for an Export Control Classification Number (ECCN) for PuTTY. We don't know whether we have one, and as a team of free software developers based in the UK we don't have the time, money, or effort to deal with US bureaucracy to investigate any further. We believe that PuTTY falls under 5D002 on the US Commerce Control List, but that shouldn't be taken as definitive. If you need to know more you should seek professional legal advice. The same applies to any other country's legal requirements and restrictions.

Similarly, some people have asked us for FIPS certification of the PuTTY tools. Unless someone else is prepared to do the necessary work and pay any costs, we can't provide this.

A.9.16 As one of our existing software vendors, can you just fill in this questionnaire for us?

We periodically receive requests like this, from organisations which have apparently sent out a form letter to everyone listed in their big spreadsheet of ‘software vendors’ requiring them all to answer some long list of questions about supported OS versions, paid support arrangements, compliance with assorted local regulations we haven't heard of, contact phone numbers, and other such administrivia. Many of the questions are obviously meaningless when applied to PuTTY (we don't provide any paid support in the first place!), most of the rest could have been answered with only a very quick look at our website, and some we are actively unwilling to answer (we are private individuals, why would we want to give out our home phone numbers to large corporations?).

We don't make a habit of responding in full to these questionnaires, because we are not a software vendor.

A software vendor is a company to which you are paying lots of money in return for some software. They know who you are, and they know you're paying them money; so they have an incentive to fill in your forms and questionnaires, to research any local regulations you cite if they don't already know about them, and generally to provide every scrap of information you might possibly need in the most convenient manner for you, because they want to keep being paid.

But we are a team of free software developers, and that means your relationship with us is nothing like that at all. If you once downloaded our software from our website, that's great and we hope you found it useful, but it doesn't mean we have the least idea who you are, or any incentive to do lots of unpaid work to support our ‘relationship’ with you.

It's not that we are unwilling to provide information. We put as much of it as we can on our website for your convenience, and if you actually need to know some fact about PuTTY which you haven't been able to find on the website (and which is not obviously inapplicable to free software in the first place) then please do ask us, and we'll try to answer as best we can. But we put up the website and this FAQ precisely so that we don't have to keep answering the same questions over and over again, so we aren't prepared to fill in completely generic form-letter questionnaires for people who haven't done their best to find the answers here first.

If you work for an organisation which you think might be at risk of making this mistake, we urge you to reorganise your list of software suppliers so that it clearly distinguishes paid vendors who know about you from free software developers who don't have any idea who you are. Then, only send out these mass mailings to the former.

A.9.17 The sha1sums / sha256sums / etc files on your download page don't match the binaries.

People report this every so often, and usually the reason turns out to be that they've matched up the wrong checksums file with the wrong binaries.

The PuTTY download page contains more than one version of the software. There's a latest release version; there are the development snapshots; and when we're in the run-up to making a release, there are also pre-release builds of the upcoming new version. Each one has its own collection of binaries, and its own collection of checksums files to go with them.

So if you've downloaded the release version of the actual program, you need the release version of the checksums too, otherwise you will see a mismatch. Similarly, the development snapshot binaries go with the development snapshot checksums, and so on. (We've colour-coded the download page in an effort to reduce this confusion a bit.)

Another thing to watch out for: as of 0.71, executables like putty.exe come in two flavours for each platform: the standalone versions on the website, each of which contains embedded help, and the versions installed by the installer, which use a separate help file also in the installer. We provide checksums for both; the latter are indicated with ‘(installer version)’ after the filename.

If you have double-checked all that, and you still think there's a real mismatch, then please send us a report carefully quoting everything relevant:

  • the exact URL you got your binary from
  • the checksum of the binary after you downloaded
  • the exact URL you got your checksums file from
  • the checksum that file says the binary should have.

A.10 Miscellaneous questions

A.10.1 Is PuTTY a port of OpenSSH, or based on OpenSSH or OpenSSL?

No, it isn't. PuTTY is almost completely composed of code written from scratch for PuTTY. The only code we share with OpenSSH is the detector for SSH-1 CRC compensation attacks, written by CORE SDI S.A; we share no code at all with OpenSSL.

A.10.2 Where can I buy silly putty?

You're looking at the wrong web site; the only PuTTY we know about here is the name of a computer program.

If you want the kind of putty you can buy as an executive toy, the PuTTY team can personally recommend Thinking Putty, which you can buy from Crazy Aaron's Putty World, at www.puttyworld.com.

A.10.3 What does ‘PuTTY’ mean?

It's the name of a popular SSH and Telnet client. Any other meaning is in the eye of the beholder. It's been rumoured that ‘PuTTY’ is the antonym of ‘getty’, or that it's the stuff that makes your Windows useful, or that it's a kind of plutonium Teletype. We couldn't possibly comment on such allegations.

A.10.4 How do I pronounce ‘PuTTY’?

Exactly like the English word ‘putty’, which we pronounce /ˈpʌti/.


If you want to provide feedback on this manual or on the PuTTY tools themselves, see the Feedback page.

[PuTTY release 0.76]
putty-0.76/doc/AppendixB.html0000644000175000017500000006063614072266315013102 00000000000000 Feedback and bug reporting

Previous | Contents | Index | Next

Appendix B: Feedback and bug reporting

This is a guide to providing feedback to the PuTTY development team. It is provided as both a web page on the PuTTY site, and an appendix in the PuTTY manual.

Section B.1 gives some general guidelines for sending any kind of e-mail to the development team. Following sections give more specific guidelines for particular types of e-mail, such as bug reports and feature requests.

B.1 General guidelines

The PuTTY development team gets a lot of mail. If you can possibly solve your own problem by reading the manual, reading the FAQ, reading the web site, asking a fellow user, perhaps posting to a newsgroup (see section B.1.2), or some other means, then it would make our lives much easier.

We get so much e-mail that we literally do not have time to answer it all. We regret this, but there's nothing we can do about it. So if you can possibly avoid sending mail to the PuTTY team, we recommend you do so. In particular, support requests (section B.6) are probably better sent to newsgroups, or passed to a local expert if possible.

The PuTTY contact email address is a private mailing list containing four or five core developers. Don't be put off by it being a mailing list: if you need to send confidential data as part of a bug report, you can trust the people on the list to respect that confidence. Also, the archives aren't publicly available, so you shouldn't be letting yourself in for any spam by sending us mail.

Please use a meaningful subject line on your message. We get a lot of mail, and it's hard to find the message we're looking for if they all have subject lines like ‘PuTTY bug’.

B.1.1 Sending large attachments

Since the PuTTY contact address is a mailing list, e-mails larger than 40Kb will be held for inspection by the list administrator, and will not be allowed through unless they really appear to be worth their large size.

If you are considering sending any kind of large data file to the PuTTY team, it's almost always a bad idea, or at the very least it would be better to ask us first whether we actually need the file. Alternatively, you could put the file on a web site and just send us the URL; that way, we don't have to download it unless we decide we actually need it, and only one of us needs to download it instead of it being automatically copied to all the developers.

(If the file contains confidential information, then you could encrypt it with our Secure Contact Key; see section F.1 for details. Please only use this for information that needs to be confidential.)

Some people like to send mail in MS Word format. Please don't send us bug reports, or any other mail, as a Word document. Word documents are roughly fifty times larger than writing the same report in plain text. In addition, most of the PuTTY team read their e-mail on Unix machines, so copying the file to a Windows box to run Word is very inconvenient. Not only that, but several of us don't even have a copy of Word!

Some people like to send us screen shots when demonstrating a problem. Please don't do this without checking with us first - we almost never actually need the information in the screen shot. Sending a screen shot of an error box is almost certainly unnecessary when you could just tell us in plain text what the error was. (On some versions of Windows, pressing Ctrl-C when the error box is displayed will copy the text of the message to the clipboard.) Sending a full-screen shot is occasionally useful, but it's probably still wise to check whether we need it before sending it.

If you must mail a screen shot, don't send it as a .BMP file. BMPs have no compression and they are much larger than other image formats such as PNG, TIFF and GIF. Convert the file to a properly compressed image format before sending it.

Please don't mail us executables, at all. Our mail server blocks all incoming e-mail containing executables, as a defence against the vast numbers of e-mail viruses we receive every day. If you mail us an executable, it will just bounce.

If you have made a tiny modification to the PuTTY code, please send us a patch to the source code if possible, rather than sending us a huge .ZIP file containing the complete sources plus your modification. If you've only changed 10 lines, we'd prefer to receive a mail that's 30 lines long than one containing multiple megabytes of data we already have.

B.1.2 Other places to ask for help

There are two Usenet newsgroups that are particularly relevant to the PuTTY tools:

  • comp.security.ssh, for questions specific to using the SSH protocol;
  • comp.terminals, for issues relating to terminal emulation (for instance, keyboard problems).

Please use the newsgroup most appropriate to your query, and remember that these are general newsgroups, not specifically about PuTTY.

If you don't have direct access to Usenet, you can access these newsgroups through Google Groups (groups.google.com).

B.2 Reporting bugs

If you think you have found a bug in PuTTY, your first steps should be:

  • Check the Wishlist page on the PuTTY website, and see if we already know about the problem. If we do, it is almost certainly not necessary to mail us about it, unless you think you have extra information that might be helpful to us in fixing it. (Of course, if we actually need specific extra information about a particular bug, the Wishlist page will say so.)
  • Check the Change Log on the PuTTY website, and see if we have already fixed the bug in the development snapshots.
  • Check the FAQ on the PuTTY website (also provided as appendix A in the manual), and see if it answers your question. The FAQ lists the most common things which people think are bugs, but which aren't bugs.
  • Download the latest development snapshot and see if the problem still happens with that. This really is worth doing. As a general rule we aren't very interested in bugs that appear in the release version but not in the development version, because that usually means they are bugs we have already fixed. On the other hand, if you can find a bug in the development version that doesn't appear in the release, that's likely to be a new bug we've introduced since the release and we're definitely interested in it.

If none of those options solved your problem, and you still need to report a bug to us, it is useful if you include some general information:

  • Tell us what version of PuTTY you are running. To find this out, use the ‘About PuTTY’ option from the System menu. Please do not just tell us ‘I'm running the latest version’; e-mail can be delayed and it may not be obvious which version was the latest at the time you sent the message.
  • PuTTY is a multi-platform application; tell us what version of what OS you are running PuTTY on. (If you're running on Unix, or Windows for Arm, tell us, or we'll assume you're running on Windows for Intel as this is overwhelmingly the case.)
  • Tell us what protocol you are connecting with: SSH, Telnet, Rlogin, SUPDUP, or Raw mode, or a serial connection.
  • Tell us what kind of server you are connecting to; what OS, and if possible what SSH server (if you're using SSH). You can get some of this information from the PuTTY Event Log (see section 3.1.3.1 in the manual).
  • Send us the contents of the PuTTY Event Log, unless you have a specific reason not to (for example, if it contains confidential information that you think we should be able to solve your problem without needing to know).
  • Try to give us as much information as you can to help us see the problem for ourselves. If possible, give us a step-by-step sequence of precise instructions for reproducing the fault.
  • Don't just tell us that PuTTY ‘does the wrong thing’; tell us exactly and precisely what it did, and also tell us exactly and precisely what you think it should have done instead. Some people tell us PuTTY does the wrong thing, and it turns out that it was doing the right thing and their expectations were wrong. Help to avoid this problem by telling us exactly what you think it should have done, and exactly what it did do.
  • If you think you can, you're welcome to try to fix the problem yourself. A patch to the code which fixes a bug is an excellent addition to a bug report. However, a patch is never a substitute for a good bug report; if your patch is wrong or inappropriate, and you haven't supplied us with full information about the actual bug, then we won't be able to find a better solution.
  • https://www.chiark.greenend.org.uk/~sgtatham/bugs.html is an article on how to report bugs effectively in general. If your bug report is particularly unclear, we may ask you to go away, read this article, and then report the bug again.

It is reasonable to report bugs in PuTTY's documentation, if you think the documentation is unclear or unhelpful. But we do need to be given exact details of what you think the documentation has failed to tell you, or how you think it could be made clearer. If your problem is simply that you don't understand the documentation, we suggest posting to a newsgroup (see section B.1.2) and seeing if someone will explain what you need to know. Then, if you think the documentation could usefully have told you that, send us a bug report and explain how you think we should change it.

B.3 Reporting security vulnerabilities

If you've found a security vulnerability in PuTTY, you might well want to notify us using an encrypted communications channel, to avoid disclosing information about the vulnerability before a fixed release is available.

For this purpose, we provide a GPG key suitable for encryption: the Secure Contact Key. See section F.1 for details of this.

(Of course, vulnerabilities are also bugs, so please do include as much information as possible about them, the same way you would with any other bug report.)

B.4 Requesting extra features

If you want to request a new feature in PuTTY, the very first things you should do are:

  • Check the Wishlist page on the PuTTY website, and see if your feature is already on the list. If it is, it probably won't achieve very much to repeat the request. (But see section B.5 if you want to persuade us to give your particular feature higher priority.)
  • Check the Wishlist and Change Log on the PuTTY website, and see if we have already added your feature in the development snapshots. If it isn't clear, download the latest development snapshot and see if the feature is present. If it is, then it will also be in the next release and there is no need to mail us at all.

If you can't find your feature in either the development snapshots or the Wishlist, then you probably do need to submit a feature request. Since the PuTTY authors are very busy, it helps if you try to do some of the work for us:

  • Do as much of the design as you can. Think about ‘corner cases’; think about how your feature interacts with other existing features. Think about the user interface; if you can't come up with a simple and intuitive interface to your feature, you shouldn't be surprised if we can't either. Always imagine whether it's possible for there to be more than one, or less than one, of something you'd assumed there would be one of. (For example, if you were to want PuTTY to put an icon in the System tray rather than the Taskbar, you should think about what happens if there's more than one PuTTY active; how would the user tell which was which?)
  • If you can program, it may be worth offering to write the feature yourself and send us a patch. However, it is likely to be helpful if you confer with us first; there may be design issues you haven't thought of, or we may be about to make big changes to the code which your patch would clash with, or something. If you check with the maintainers first, there is a better chance of your code actually being usable. Also, read the design principles listed in appendix E: if you do not conform to them, we will probably not be able to accept your patch.

B.5 Requesting features that have already been requested

If a feature is already listed on the Wishlist, then it usually means we would like to add it to PuTTY at some point. However, this may not be in the near future. If there's a feature on the Wishlist which you would like to see in the near future, there are several things you can do to try to increase its priority level:

  • Mail us and vote for it. (Be sure to mention that you've seen it on the Wishlist, or we might think you haven't even read the Wishlist). This probably won't have very much effect; if a huge number of people vote for something then it may make a difference, but one or two extra votes for a particular feature are unlikely to change our priority list immediately. Offering a new and compelling justification might help. Also, don't expect a reply.
  • Offer us money if we do the work sooner rather than later. This sometimes works, but not always. The PuTTY team all have full-time jobs and we're doing all of this work in our free time; we may sometimes be willing to give up some more of our free time in exchange for some money, but if you try to bribe us for a big feature it's entirely possible that we simply won't have the time to spare - whether you pay us or not. (Also, we don't accept bribes to add bad features to the Wishlist, because our desire to provide high-quality software to the users comes first.)
  • Offer to help us write the code. This is probably the only way to get a feature implemented quickly, if it's a big one that we don't have time to do ourselves.

B.6 Support requests

If you're trying to make PuTTY do something for you and it isn't working, but you're not sure whether it's a bug or not, then please consider looking for help somewhere else. This is one of the most common types of mail the PuTTY team receives, and we simply don't have time to answer all the questions. Questions of this type include:

  • If you want to do something with PuTTY but have no idea where to start, and reading the manual hasn't helped, try posting to a newsgroup (see section B.1.2) and see if someone can explain it to you.
  • If you have tried to do something with PuTTY but it hasn't worked, and you aren't sure whether it's a bug in PuTTY or a bug in your SSH server or simply that you're not doing it right, then try posting to a newsgroup (see section B.1.2) and see if someone can solve your problem. Or try doing the same thing with a different SSH client and see if it works with that. Please do not report it as a PuTTY bug unless you are really sure it is a bug in PuTTY.
  • If someone else installed PuTTY for you, or you're using PuTTY on someone else's computer, try asking them for help first. They're more likely to understand how they installed it and what they expected you to use it for than we are.
  • If you have successfully made a connection to your server and now need to know what to type at the server's command prompt, or other details of how to use the server-end software, talk to your server's system administrator. This is not the PuTTY team's problem. PuTTY is only a communications tool, like a telephone; if you can't speak the same language as the person at the other end of the phone, it isn't the telephone company's job to teach it to you.

If you absolutely cannot get a support question answered any other way, you can try mailing it to us, but we can't guarantee to have time to answer it.

B.7 Web server administration

If the PuTTY web site is down (Connection Timed Out), please don't bother mailing us to tell us about it. Most of us read our e-mail on the same machines that host the web site, so if those machines are down then we will notice before we read our e-mail. So there's no point telling us our servers are down.

Of course, if the web site has some other error (Connection Refused, 404 Not Found, 403 Forbidden, or something else) then we might not have noticed and it might still be worth telling us about it.

If you want to report a problem with our web site, check that you're looking at our real web site and not a mirror. The real web site is at https://www.chiark.greenend.org.uk/~sgtatham/putty/; if that's not where you're reading this, then don't report the problem to us until you've checked that it's really a problem with the main site. If it's only a problem with the mirror, you should try to contact the administrator of that mirror site first, and only contact us if that doesn't solve the problem (in case we need to remove the mirror from our list).

B.8 Asking permission for things

PuTTY is distributed under the MIT Licence (see appendix D for details). This means you can do almost anything you like with our software, our source code, and our documentation. The only things you aren't allowed to do are to remove our copyright notices or the licence text itself, or to hold us legally responsible if something goes wrong.

So if you want permission to include PuTTY on a magazine cover disk, or as part of a collection of useful software on a CD or a web site, then permission is already granted. You don't have to mail us and ask. Just go ahead and do it. We don't mind.

(If you want to distribute PuTTY alongside your own application for use with that application, or if you want to distribute PuTTY within your own organisation, then we recommend, but do not insist, that you offer your own first-line technical support, to answer questions about the interaction of PuTTY with your environment. If your users mail us directly, we won't be able to tell them anything useful about your specific setup.)

If you want to use parts of the PuTTY source code in another program, then it might be worth mailing us to talk about technical details, but if all you want is to ask permission then you don't need to bother. You already have permission.

If you just want to link to our web site, just go ahead. (It's not clear that we could stop you doing this, even if we wanted to!)

B.9 Mirroring the PuTTY web site

If you want to set up a mirror of the PuTTY website, go ahead and set one up. Please don't bother asking us for permission before setting up a mirror. You already have permission.

If the mirror is in a country where we don't already have plenty of mirrors, we may be willing to add it to the list on our mirrors page. Read the guidelines on that page, make sure your mirror works, and email us the information listed at the bottom of the page.

Note that we do not promise to list your mirror: we get a lot of mirror notifications and yours may not happen to find its way to the top of the list.

Also note that we link to all our mirror sites using the rel="nofollow" attribute. Running a PuTTY mirror is not intended to be a cheap way to gain search rankings.

If you have technical questions about the process of mirroring, then you might want to mail us before setting up the mirror (see also the guidelines on the Mirrors page); but if you just want to ask for permission, you don't need to. You already have permission.

B.10 Praise and compliments

One of the most rewarding things about maintaining free software is getting e-mails that just say ‘thanks’. We are always happy to receive e-mails of this type.

Regrettably we don't have time to answer them all in person. If you mail us a compliment and don't receive a reply, please don't think we've ignored you. We did receive it and we were happy about it; we just didn't have time to tell you so personally.

To everyone who's ever sent us praise and compliments, in the past and the future: you're welcome!

B.11 E-mail address

The actual address to mail is <putty@projects.tartarus.org>.


If you want to provide feedback on this manual or on the PuTTY tools themselves, see the Feedback page.

[PuTTY release 0.76]
putty-0.76/doc/AppendixC.html0000644000175000017500000004663214072266315013103 00000000000000 PPK file format

Previous | Contents | Index | Next

Appendix C: PPK file format

This appendix documents the file format used by PuTTY to store private keys.

In this appendix, binary data structures are described using data type representations such as ‘uint32’, ‘string’ and ‘mpint’ as used in the SSH protocol standards themselves. These are defined authoritatively by RFC 4251 section 5, ‘Data Type Representations Used in the SSH Protocols’.

C.1 Overview

A PPK file stores a private key, and the corresponding public key. Both are contained in the same file.

The file format can be completely unencrypted, or it can encrypt the private key. The public key is stored in cleartext in both cases. (This enables PuTTY to send the public key to an SSH server to see whether it will accept it, and not bother prompting for the passphrase unless the server says yes.)

When the key file is encrypted, the encryption key is derived from a passphrase. An encrypted PPK file is also tamper-proofed using a MAC (authentication code), also derived from the same passphrase. The MAC protects the encrypted private key data, but it also covers the cleartext parts of the file. So you can't edit the public half of the key without invalidating the MAC and causing the key file as a whole to become useless.

This MAC protects the key file against active cryptographic attacks in which the public half of a key pair is modified in a controlled way that allows an attacker to deduce information about the private half from the resulting corrupted signatures. Any attempt to do that to a PPK file should be reliably caught by the MAC failing to validate.

(Such an attack would only be useful if the key file was stored in a location where the attacker could modify it without also having full access to the process that you type passphrases into. But that's not impossible; for example, if your home directory was on a network file server, then the file server's administrator could access the key file but not processes on the client machine.)

The MAC also covers the comment on the key. This stops an attacker from swapping keys with each other and editing the comments to disguise the fact. As a consequence, PuTTYgen cannot edit the comment on a key unless you decrypt the key with your passphrase first.

(The circumstances in which that attack would be useful are even more restricted. One example might be that the different keys trigger specific actions on the server you're connecting to and one of those actions is more useful to the attacker than the other. But once you have a MAC at all, it's no extra effort to make it cover as much as possible, and usually sensible.)

C.2 Outer layer

The outer layer of a PPK file is text-based. The PuTTY tools will always use LF line termination when writing PPK files, but will tolerate CR+LF and CR-only on input.

The first few lines identify it as a PPK, and give some initial data about what's stored in it and how. They look like this:

PuTTY-User-Key-File-version: algorithm-name
Encryption: encryption-type
Comment: key-comment-string

version is a decimal number giving the version number of the file format itself. The current file format version is 3.

algorithm-name is the SSH protocol identifier for the public key algorithm that this key is used for (such as ‘ssh-dss’ or ‘ecdsa-sha2-nistp384’).

encryption-type indicates whether this key is stored encrypted, and if so, by what method. Currently the only supported encryption types are ‘aes256-cbc’ and ‘none’.

key-comment-string is a free text field giving the comment. This can contain any byte values other than 13 and 10 (CR and LF).

The next part of the file gives the public key. This is stored unencrypted but base64-encoded (RFC 4648), and is preceded by a header line saying how many lines of base64 data are shown, looking like this:

Public-Lines: number-of-lines
that many lines of base64 data

The base64-encoded data in this blob is formatted in exactly the same way as an SSH public key sent over the wire in the SSH protocol itself. That is also the same format as the base64 data stored in OpenSSH's authorized_keys file, except that in a PPK file the base64 data is split across multiple lines. But if you remove the newlines from the middle of this section, the resulting base64 blob is in the right format to go in an authorized_keys line.

If the key is encrypted (i.e. encryption-type is not ‘none’), then the next thing that appears is a sequence of lines specifying how the keys for encrypting the file are to be derived from the passphrase:

Key-Derivation: argon2-flavour
Argon2-Memory: decimal-integer
Argon2-Passes: decimal-integer
Argon2-Parallelism: decimal-integer
Argon2-Salt: hex-string

argon2-flavour is one of the identifiers ‘Argon2d’, ‘Argon2i’ or ‘Argon2id’, all describing variants of the Argon2 password-hashing function.

The three integer values are used as parameters for Argon2, which allows you to configure the amount of memory used (in Kbyte), the number of passes of the algorithm to run (to tune its running time), and the degree of parallelism required by the hash function. The salt is decoded into a sequence of binary bytes and used as an additional input to Argon2. (It is chosen randomly when the key file is written, so that a guessing attack can't be mounted in parallel against multiple key files.)

The next part of the file gives the private key. This is base64-encoded in the same way:

Private-Lines: number-of-lines
that many lines of base64 data

The binary data represented in this base64 blob may be encrypted, depending on the encryption-type field in the key file header shown above:

  • If encryption-type is ‘none’, then this data is stored in plain text.
  • If encryption-type is ‘aes256-cbc’, then this data is encrypted using AES, with a 256-bit key length, in the CBC cipher mode. The key and initialisation vector are derived from the passphrase: see section C.4.

    In order to encrypt the private key data with AES, it must be a multiple of 16 bytes (the AES cipher block length). This is achieved by appending random padding to the data before encrypting it. When decoding it after decryption, the random data can be ignored: the internal structure of the data is enough to tell you when you've reached the end of the meaningful part.

Unlike public keys, the binary encoding of private keys is not specified at all in the SSH standards. See section C.3 for details of the private key format for each key type supported by PuTTY.

The final thing in the key file is the MAC:

Private-MAC: hex-mac-data

hex-mac-data is a hexadecimal-encoded value, 64 digits long (i.e. 32 bytes), generated using the HMAC-SHA-256 algorithm with the following binary data as input:

  • string: the algorithm-name header field.
  • string: the encryption-type header field.
  • string: the key-comment-string header field.
  • string: the binary public key data, as decoded from the base64 lines after the ‘Public-Lines’ header.
  • string: the plaintext of the binary private key data, as decoded from the base64 lines after the ‘Private-Lines’ header. If that data was stored encrypted, then the decrypted version of it is used in this MAC preimage, including the random padding mentioned above.

The MAC key is derived from the passphrase: see section C.4.

C.3 Private key encodings

This section describes the private key format for each key type supported by PuTTY.

Because the PPK format also contains the public key (and both public and private key are protected by the same MAC to ensure they can't be made inconsistent), there is no need for the private key section of the file to repeat data from the public section. So some of these formats are very short.

In all cases, a decoding application can begin reading from the start of the decrypted private key data, and know when it has read all that it needs. This allows random padding after the meaningful data to be safely ignored.

C.3.1 RSA

RSA keys are stored using an algorithm-name of ‘ssh-rsa’. (Keys stored like this are also used by the updated RSA signature schemes that use hashes other than SHA-1.)

The public key data has already provided the key modulus and the public encoding exponent. The private data stores:

  • mpint: the private decoding exponent of the key.
  • mpint: one prime factor p of the key.
  • mpint: the other prime factor q of the key. (RSA keys stored in this format are expected to have exactly two prime factors.)
  • mpint: the multiplicative inverse of q modulo p.

C.3.2 DSA

DSA keys are stored using an algorithm-name of ‘ssh-dss’.

The public key data has already provided the key parameters (the large prime p, the small prime q and the group generator g), and the public key y. The private key stores:

  • mpint: the private key x, which is the discrete logarithm of y in the group generated by g mod p.

C.3.3 NIST elliptic-curve keys

NIST elliptic-curve keys are stored using one of the following algorithm-name values, each corresponding to a different elliptic curve and key size:

  • ecdsa-sha2-nistp256
  • ecdsa-sha2-nistp384
  • ecdsa-sha2-nistp521

The public key data has already provided the public elliptic curve point. The private key stores:

  • mpint: the private exponent, which is the discrete log of the public point.

C.3.4 EdDSA elliptic-curve keys (Ed25519 and Ed448)

EdDSA elliptic-curve keys are stored using one of the following algorithm-name values, each corresponding to a different elliptic curve and key size:

  • ssh-ed25519
  • ssh-ed448

The public key data has already provided the public elliptic curve point. The private key stores:

  • mpint: the private exponent, which is the discrete log of the public point.

C.4 Key derivation

When a key file is encrypted, there are three pieces of key material that need to be computed from the passphrase:

  • the key for the symmetric cipher used to encrypt the private key
  • the initialisation vector for that cipher encryption
  • the key for the MAC.

If encryption-type is ‘aes256-cbc’, then the symmetric cipher key is 32 bytes long, and the initialisation vector is 16 bytes (one cipher block). The length of the MAC key is also chosen to be 32 bytes.

If encryption-type is ‘none’, then all three of these pieces of data have zero length. (The MAC is still generated and checked in the key file format, but it has a zero-length key.)

If the amount of key material required is not zero, then the passphrase is fed to the Argon2 key derivation function, in whichever mode is described in the ‘Key-Derivation’ header in the key file, with parameters derived from the various ‘Argon2-Parameter:’ headers.

(If the key is unencrypted, then all those headers are omitted, and Argon2 is not run at all.)

Argon2 takes two extra string inputs in addition to the passphrase and the salt: a secret key, and some ‘associated data’. In PPK's use of Argon2, these are both set to the empty string.

The ‘tag length’ parameter to Argon2 (i.e. the amount of data it is asked to output) is set to the sum of the lengths of all of the data items required, i.e. (cipher key length + IV length + MAC key length). The output data is interpreted as the concatenation of the cipher key, the IV and the MAC key, in that order.

So, for ‘aes256-cbc’, the tag length will be 32+16+32 = 80 bytes; of the 80 bytes of output data, the first 32 bytes are used as the 256-bit AES key, the next 16 as the CBC IV, and the final 32 bytes as the HMAC-SHA-256 key.

C.5 Older versions of the PPK format

C.5.1 Version 2

PPK version 2 was used by PuTTY 0.52 to 0.74 inclusive.

In PPK version 2, the MAC algorithm used was HMAC-SHA-1 (so the Private-MAC line contained only 40 hex digits).

The ‘Key-Derivation:’ header and all the ‘Argon2-Parameter:’ headers were absent. Instead of using Argon2, the key material for encrypting the private blob was derived from the passphrase in a totally different way, as follows.

The cipher key for ‘aes256-cbc’ was constructed by generating two SHA-1 hashes, concatenating them, and taking the first 32 bytes of the result. (So you'd get all 20 bytes of the first hash output, and the first 12 of the second). Each hash preimage was as follows:

  • uint32: a sequence number. This is 0 in the first hash, and 1 in the second. (The idea was to extend this mechanism to further hashes by continuing to increment the sequence number, if future changes required even longer keys.)
  • the passphrase, without any prefix length field.

In PPK v2, the CBC initialisation vector was all zeroes.

The MAC key was 20 bytes long, and was a single SHA-1 hash of the following data:

  • the fixed string ‘putty-private-key-file-mac-key’, without any prefix length field.
  • the passphrase, without any prefix length field. (If the key is stored unencrypted, the passphrase was taken to be the empty string for these purposes.)

C.5.2 Version 1

PPK version 1 was a badly designed format, only used during initial development, and not recommended for production use.

PPK version 1 was never used by a released version of PuTTY. It was only emitted by some early development snapshots between version 0.51 (which did not support SSH-2 public keys at all) and 0.52 (which already used version 2 of this file format). I hope there are no PPK v1 files in use anywhere. But just in case, the old badly designed format is documented here anyway.

In PPK version 1, the input to the MAC does not include any of the header fields or the public key. It is simply the private key data (still in plaintext and including random padding), all by itself (without a wrapping string).

PPK version 1 keys must therefore be rigorously validated after loading, to ensure that the public and private parts of the key were consistent with each other.

PPK version 1 only supported the RSA and DSA key types. For RSA, this validation can be done using only the provided data (since the private key blob contains enough information to reconstruct the public values anyway). But for DSA, that isn't quite enough.

Hence, PPK version 1 DSA keys extended the private data so that immediately after x was stored an extra value:

  • string: a SHA-1 hash of the public key data, whose preimage consists of
    • string: the large prime p
    • string: the small prime q
    • string: the group generator g

The idea was that checking this hash would verify that the key parameters had not been tampered with, and then the loading application could directly verify that g^x = y.

In an unencrypted version 1 key file, the MAC is replaced by a plain SHA-1 hash of the private key data. This is indicated by the ‘Private-MAC:’ header being replaced with ‘Private-Hash:’ instead.


If you want to provide feedback on this manual or on the PuTTY tools themselves, see the Feedback page.

[PuTTY release 0.76]
putty-0.76/doc/AppendixD.html0000644000175000017500000000463714072266315013103 00000000000000 PuTTY Licence

Previous | Contents | Index | Next

Appendix D: PuTTY Licence

PuTTY is copyright 1997-2021 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, Christopher Staite, Lorenz Diener, Christian Brabandt, Jeff Smith, Pavel Kryukov, Maxim Kuznetsov, Svyatoslav Kuzmich, Nico Williams, Viktor Dukhovni, Josh Dersch, Lars Brinkhoff, 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:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


If you want to provide feedback on this manual or on the PuTTY tools themselves, see the Feedback page.

[PuTTY release 0.76]
putty-0.76/doc/AppendixE.html0000644000175000017500000013222114072266315013073 00000000000000 PuTTY hacking guide

Previous | Contents | Index | Next

Appendix E: PuTTY hacking guide

This appendix lists a selection of the design principles applying to the PuTTY source code. If you are planning to send code contributions, you should read this first.

E.1 Cross-OS portability

Despite Windows being its main area of fame, PuTTY is no longer a Windows-only application suite. It has a working Unix port; a Mac port is in progress; more ports may or may not happen at a later date.

Therefore, embedding Windows-specific code in core modules such as ssh.c is not acceptable. We went to great lengths to remove all the Windows-specific stuff from our core modules, and to shift it out into Windows-specific modules. Adding large amounts of Windows-specific stuff in parts of the code that should be portable is almost guaranteed to make us reject a contribution.

The PuTTY source base is divided into platform-specific modules and platform-generic modules. The Unix-specific modules are all in the unix subdirectory; the Windows-specific modules are in the windows subdirectory.

All the modules in the main source directory - notably all of the code for the various back ends - are platform-generic. We want to keep them that way.

This also means you should stick to the C semantics guaranteed by the C standard: try not to make assumptions about the precise size of basic types such as int and long int; don't use pointer casts to do endianness-dependent operations, and so on.

(Even within a platform front end you should still be careful of some of these portability issues. The Windows front end compiles on both 32- and 64-bit x86 and also Arm.)

Our current choice of C standards version is mostly C99. With a couple of exceptions, you can assume that C99 features are available (in particular <stdint.h>, <stdbool.h> and inline), but you shouldn't use things that are new in C11 (such as <uchar.h> or _Generic).

The exceptions to that rule are due to the need for Visual Studio compatibility:

  • Don't use variable-length arrays. Visual Studio doesn't support them even now that it's adopted the rest of C99. We use -Wvla when building with gcc and clang, to make it easier to avoid accidentally breaking that rule.
  • For historical reasons, we still build with one older VS version which lacks <inttypes.h>. So that file is included centrally in defs.h, and has a set of workaround definitions for the PRIx64-type macros we use. If you need to use another one of those macros, you need to add a workaround definition in defs.h, and don't casually re-include <inttypes.h> anywhere else in the source file.

Here are a few portability assumptions that we do currently allow (because we'd already have to thoroughly vet the existing code if they ever needed to change, and it doesn't seem worth doing that unless we really have to):

  • You can assume int is at least 32 bits wide. (We've never tried to port PuTTY to a platform with 16-bit int, and it doesn't look likely to be necessary in future.)
  • Similarly, you can assume char is exactly 8 bits. (Exceptions to that are even less likely to be relevant to us than short int.)
  • You can assume that using memset to write zero bytes over a whole structure will have the effect of setting all its pointer fields to NULL. (The standard itself guarantees this for integer fields, but not for pointers.)
  • You can assume that time_t has POSIX semantics, i.e. that it represents an integer number of non-leap seconds since 1970-01-01 00:00:00 UTC. (Times in this format are used in X authorisation, but we could work around that by carefully distinguishing local time_t from time values used in the wire protocol; but these semantics of time_t are also baked into the shared library API used by the GSSAPI authentication code, which would be much harder to change.)
  • You can assume that the execution character encoding is a superset of the printable characters of ASCII. (In particular, it's fine to do arithmetic on a char value representing a Latin alphabetic character, without bothering to allow for EBCDIC or other non-consecutive encodings of the alphabet.)

On the other hand, here are some particular things not to assume:

  • Don't assume anything about the signedness of char. In particular, you must cast char values to unsigned char before passing them to any <ctype.h> function (because those expect a non-negative character value, or EOF). If you need a particular signedness, explicitly specify signed char or unsigned char, or use C99 int8_t or uint8_t.
  • From past experience with MacOS, we're still a bit nervous about '\n' and '\r' potentially having unusual meanings on a given platform. So it's fine to say \n in a string you're passing to printf, but in any context where those characters appear in a standardised wire protocol or a binary file format, they should be spelled '\012' and '\015' respectively.

E.2 Multiple backends treated equally

PuTTY is not an SSH client with some other stuff tacked on the side. PuTTY is a generic, multiple-backend, remote VT-terminal client which happens to support one backend which is larger, more popular and more useful than the rest. Any extra feature which can possibly be general across all backends should be so: localising features unnecessarily into the SSH back end is a design error. (For example, we had several code submissions for proxy support which worked by hacking ssh.c. Clearly this is completely wrong: the network.h abstraction is the place to put it, so that it will apply to all back ends equally, and indeed we eventually put it there after another contributor sent a better patch.)

The rest of PuTTY should try to avoid knowing anything about specific back ends if at all possible. To support a feature which is only available in one network protocol, for example, the back end interface should be extended in a general manner such that any back end which is able to provide that feature can do so. If it so happens that only one back end actually does, that's just the way it is, but it shouldn't be relied upon by any code.

E.3 Multiple sessions per process on some platforms

Some ports of PuTTY - notably the in-progress Mac port - are constrained by the operating system to run as a single process potentially managing multiple sessions.

Therefore, the platform-independent parts of PuTTY never use global variables to store per-session data. The global variables that do exist are tolerated because they are not specific to a particular login session. The random number state in sshrand.c, the timer list in timing.c and the queue of top-level callbacks in callback.c serve all sessions equally. But most data is specific to a particular network session, and is therefore stored in dynamically allocated data structures, and pointers to these structures are passed around between functions.

Platform-specific code can reverse this decision if it likes. The Windows code, for historical reasons, stores most of its data as global variables. That's OK, because on Windows we know there is only one session per PuTTY process, so it's safe to do that. But changes to the platform-independent code should avoid introducing global variables, unless they are genuinely cross-session.

E.4 C, not C++

PuTTY is written entirely in C, not in C++.

We have made some effort to make it easy to compile our code using a C++ compiler: notably, our snew, snewn and sresize macros explicitly cast the return values of malloc and realloc to the target type. (This has type checking advantages even in C: it means you never accidentally allocate the wrong size piece of memory for the pointer type you're assigning it to. C++ friendliness is really a side benefit.)

We want PuTTY to continue being pure C, at least in the platform-independent parts and the currently existing ports. Patches which switch the Makefiles to compile it as C++ and start using classes will not be accepted. Also, in particular, we disapprove of // comments, at least for the moment. (Perhaps once C99 becomes genuinely widespread we might be more lenient.)

The one exception: a port to a new platform may use languages other than C if they are necessary to code on that platform. If your favourite PDA has a GUI with a C++ API, then there's no way you can do a port of PuTTY without using C++, so go ahead and use it. But keep the C++ restricted to that platform's subdirectory; if your changes force the Unix or Windows ports to be compiled as C++, they will be unacceptable to us.

E.5 Security-conscious coding

PuTTY is a network application and a security application. Assume your code will end up being fed deliberately malicious data by attackers, and try to code in a way that makes it unlikely to be a security risk.

In particular, try not to use fixed-size buffers for variable-size data such as strings received from the network (or even the user). We provide functions such as dupcat and dupprintf, which dynamically allocate buffers of the right size for the string they construct. Use these wherever possible.

E.6 Independence of specific compiler

Windows PuTTY can currently be compiled with any of three Windows compilers: MS Visual C, the Cygwin / mingw32 GNU tools, and clang (in MS compatibility mode).

This is a really useful property of PuTTY, because it means people who want to contribute to the coding don't depend on having a specific compiler; so they don't have to fork out money for MSVC if they don't already have it, but on the other hand if they do have it they also don't have to spend effort installing gcc alongside it. They can use whichever compiler they happen to have available, or install whichever is cheapest and easiest if they don't have one.

Therefore, we don't want PuTTY to start depending on which compiler you're using. Using GNU extensions to the C language, for example, would ruin this useful property (not that anyone's ever tried it!); and more realistically, depending on an MS-specific library function supplied by the MSVC C library (_snprintf, for example) is a mistake, because that function won't be available under the other compilers. Any function supplied in an official Windows DLL as part of the Windows API is fine, and anything defined in the C library standard is also fine, because those should be available irrespective of compilation environment. But things in between, available as non-standard library and language extensions in only one compiler, are disallowed.

(_snprintf in particular should be unnecessary, since we provide dupprintf; see section E.5.)

Compiler independence should apply on all platforms, of course, not just on Windows.

E.7 Small code size

PuTTY is tiny, compared to many other Windows applications. And it's easy to install: it depends on no DLLs, no other applications, no service packs or system upgrades. It's just one executable. You install that executable wherever you want to, and run it.

We want to keep both these properties - the small size, and the ease of installation - if at all possible. So code contributions that depend critically on external DLLs, or that add a huge amount to the code size for a feature which is only useful to a small minority of users, are likely to be thrown out immediately.

We do vaguely intend to introduce a DLL plugin interface for PuTTY, whereby seriously large extra features can be implemented in plugin modules. The important thing, though, is that those DLLs will be optional; if PuTTY can't find them on startup, it should run perfectly happily and just won't provide those particular features. A full installation of PuTTY might one day contain ten or twenty little DLL plugins, which would cut down a little on the ease of installation - but if you really needed ease of installation you could still just install the one PuTTY binary, or just the DLLs you really needed, and it would still work fine.

Depending on external DLLs is something we'd like to avoid if at all possible (though for some purposes, such as complex SSH authentication mechanisms, it may be unavoidable). If it can't be avoided, the important thing is to follow the same principle of graceful degradation: if a DLL can't be found, then PuTTY should run happily and just not supply the feature that depended on it.

E.8 Single-threaded code

PuTTY and its supporting tools, or at least the vast majority of them, run in only one OS thread.

This means that if you're devising some piece of internal mechanism, there's no need to use locks to make sure it doesn't get called by two threads at once. The only way code can be called re-entrantly is by recursion.

That said, most of Windows PuTTY's network handling is triggered off Windows messages requested by WSAAsyncSelect(), so if you call MessageBox() deep within some network event handling code you should be aware that you might be re-entered if a network event comes in and is passed on to our window procedure by the MessageBox() message loop.

Also, the front ends (in particular Windows Plink) can use multiple threads if they like. However, Windows Plink keeps very tight control of its auxiliary threads, and uses them pretty much exclusively as a form of select(). Pretty much all the code outside windows/winplink.c is only ever called from the one primary thread; the others just loop round blocking on file handles and send messages to the main thread when some real work needs doing. This is not considered a portability hazard because that bit of windows/winplink.c will need rewriting on other platforms in any case.

One important consequence of this: PuTTY has only one thread in which to do everything. That ‘everything’ may include managing more than one login session (section E.3), managing multiple data channels within an SSH session, responding to GUI events even when nothing is happening on the network, and responding to network requests from the server (such as repeat key exchange) even when the program is dealing with complex user interaction such as the re-configuration dialog box. This means that almost none of the PuTTY code can safely block.

E.9 Keystrokes sent to the server wherever possible

In almost all cases, PuTTY sends keystrokes to the server. Even weird keystrokes that you think should be hot keys controlling PuTTY. Even Alt-F4 or Alt-Space, for example. If a keystroke has a well-defined escape sequence that it could usefully be sending to the server, then it should do so, or at the very least it should be configurably able to do so.

To unconditionally turn a key combination into a hot key to control PuTTY is almost always a design error. If a hot key is really truly required, then try to find a key combination for it which isn't already used in existing PuTTYs (either it sends nothing to the server, or it sends the same thing as some other combination). Even then, be prepared for the possibility that one day that key combination might end up being needed to send something to the server - so make sure that there's an alternative way to invoke whatever PuTTY feature it controls.

E.10 640×480 friendliness in configuration panels

There's a reason we have lots of tiny configuration panels instead of a few huge ones, and that reason is that not everyone has a 1600×1200 desktop. 640×480 is still a viable resolution for running Windows (and indeed it's still the default if you start up in safe mode), so it's still a resolution we care about.

Accordingly, the PuTTY configuration box, and the PuTTYgen control window, are deliberately kept just small enough to fit comfortably on a 640×480 display. If you're adding controls to either of these boxes and you find yourself wanting to increase the size of the whole box, don't. Split it into more panels instead.

E.11 Automatically generated Makefiles

PuTTY is intended to compile on multiple platforms, and with multiple compilers. It would be horrifying to try to maintain a single Makefile which handled all possible situations, and just as painful to try to directly maintain a set of matching Makefiles for each different compilation environment.

Therefore, we have moved the problem up by one level. In the PuTTY source archive is a file called Recipe, which lists which source files combine to produce which binaries; and there is also a script called mkfiles.pl, which reads Recipe and writes out the real Makefiles. (The script also reads all the source files and analyses their dependencies on header files, so we get an extra benefit from doing it this way, which is that we can supply correct dependency information even in environments where it's difficult to set up an automated make depend phase.)

You should never edit any of the PuTTY Makefiles directly. They are not stored in our source repository at all. They are automatically generated by mkfiles.pl from the file Recipe.

If you need to add a new object file to a particular binary, the right thing to do is to edit Recipe and re-run mkfiles.pl. This will cause the new object file to be added in every tool that requires it, on every platform where it matters, in every Makefile to which it is relevant, and to get all the dependency data right.

If you send us a patch that modifies one of the Makefiles, you just waste our time, because we will have to convert it into a change to Recipe. If you send us a patch that modifies all of the Makefiles, you will have wasted a lot of your time as well!

(There is a comment at the top of every Makefile in the PuTTY source archive saying this, but many people don't seem to read it, so it's worth repeating here.)

E.12 Coroutines in the SSH code

Large parts of the code in the various SSH modules (in fact most of the protocol layers) are structured using a set of macros that implement (something close to) Donald Knuth's ‘coroutines’ concept in C.

Essentially, the purpose of these macros are to arrange that a function can call crReturn() to return to its caller, and the next time it is called control will resume from just after that crReturn statement.

This means that any local (automatic) variables declared in such a function will be corrupted every time you call crReturn. If you need a variable to persist for longer than that, you must make it a field in some appropriate structure containing the persistent state of the coroutine – typically the main state structure for an SSH protocol layer.

See https://www.chiark.greenend.org.uk/~sgtatham/coroutines.html for a more in-depth discussion of what these macros are for and how they work.

Another caveat: most of these coroutines are not guaranteed to run to completion, because the SSH connection (or whatever) that they're part of might be interrupted at any time by an unexpected network event or user action. So whenever a coroutine-managed variable refers to a resource that needs releasing, you should also ensure that the cleanup function for its containing state structure can reliably release it even if the coroutine is aborted at an arbitrary point.

For example, if an SSH packet protocol layer has to have a field that sometimes points to a piece of allocated memory, then you should ensure that when you free that memory you reset the pointer field to NULL. Then, no matter when the protocol layer's cleanup function is called, it can reliably free the memory if there is any, and not crash if there isn't.

E.13 Explicit vtable structures to implement traits

A lot of PuTTY's code is written in a style that looks structurally rather like an object-oriented language, in spite of PuTTY being a pure C program.

For example, there's a single data type called ssh_hash, which is an abstraction of a secure hash function, and a bunch of functions called things like ssh_hash_foo that do things with those data types. But in fact, PuTTY supports many different hash functions, and each one has to provide its own implementation of those functions.

In C++ terms, this is rather like having a single abstract base class, and multiple concrete subclasses of it, each of which fills in all the pure virtual methods in a way that's compatible with the data fields of the subclass. The implementation is more or less the same, as well: in C, we do explicitly in the source code what the C++ compiler will be doing behind the scenes at compile time.

But perhaps a closer analogy in functional terms is the Rust concept of a ‘trait’, or the Java idea of an ‘interface’. C++ supports a multi-level hierarchy of inheritance, whereas PuTTY's system – like traits or interfaces – has only two levels, one describing a generic object of a type (e.g. a hash function) and another describing a specific implementation of that type (e.g. SHA-256).

The PuTTY code base has a standard idiom for doing this in C, as follows.

Firstly, we define two struct types for our trait. One of them describes a particular kind of implementation of that trait, and it's full of (mostly) function pointers. The other describes a specific instance of an implementation of that trait, and it will contain a pointer to a const instance of the first type. For example:

typedef struct MyAbstraction MyAbstraction;
typedef struct MyAbstractionVtable MyAbstractionVtable;

struct MyAbstractionVtable {
    MyAbstraction *(*new)(const MyAbstractionVtable *vt);
    void (*free)(MyAbstraction *);
    void (*modify)(MyAbstraction *, unsigned some_parameter);
    unsigned (*query)(MyAbstraction *, unsigned some_parameter);
};

struct MyAbstraction {
    const MyAbstractionVtable *vt;
};

Here, we imagine that MyAbstraction might be some kind of object that contains mutable state. The associated vtable structure shows what operations you can perform on a MyAbstraction: you can create one (dynamically allocated), free one you already have, or call the example methods ‘modify’ (to change the state of the object in some way) and ‘query’ (to return some value derived from the object's current state).

(In most cases, the vtable structure has a name ending in ‘vtable’. But for historical reasons a lot of the crypto primitives that use this scheme – ciphers, hash functions, public key methods and so on – instead have names ending in ‘alg’, on the basis that the primitives they implement are often referred to as ‘encryption algorithms’, ‘hash algorithms’ and so forth.)

Now, to define a concrete instance of this trait, you'd define a struct that contains a MyAbstraction field, plus any other data it might need:

struct MyImplementation {
    unsigned internal_data[16];
    SomeOtherType *dynamic_subthing;

    MyAbstraction myabs;
};

Next, you'd implement all the necessary methods for that implementation of the trait, in this kind of style:

static MyAbstraction *myimpl_new(const MyAbstractionVtable *vt)
{
    MyImplementation *impl = snew(MyImplementation);
    memset(impl, 0, sizeof(*impl));
    impl->dynamic_subthing = allocate_some_other_type();
    impl->myabs.vt = vt;
    return &impl->myabs;
}

static void myimpl_free(MyAbstraction *myabs)
{
    MyImplementation *impl = container_of(myabs, MyImplementation, myabs);
    free_other_type(impl->dynamic_subthing);
    sfree(impl);
}

static void myimpl_modify(MyAbstraction *myabs, unsigned param)
{
    MyImplementation *impl = container_of(myabs, MyImplementation, myabs);
    impl->internal_data[param] += do_something_with(impl->dynamic_subthing);
}

static unsigned myimpl_query(MyAbstraction *myabs, unsigned param)
{
    MyImplementation *impl = container_of(myabs, MyImplementation, myabs);
    return impl->internal_data[param];
}

Having defined those methods, now we can define a const instance of the vtable structure containing pointers to them:

const MyAbstractionVtable MyImplementation_vt = {
    .new = myimpl_new,
    .free = myimpl_free,
    .modify = myimpl_modify,
    .query = myimpl_query,
};

In principle, this is all you need. Client code can construct a new instance of a particular implementation of MyAbstraction by digging out the new method from the vtable and calling it (with the vtable itself as a parameter), which returns a MyAbstraction * pointer that identifies a newly created instance, in which the vt field will contain a pointer to the same vtable structure you passed in. And once you have an instance object, say MyAbstraction *myabs, you can dig out one of the other method pointers from the vtable it points to, and call that, passing the object itself as a parameter.

But in fact, we don't do that, because it looks pretty ugly at all the call sites. Instead, what we generally do in this code base is to write a set of static inline wrapper functions in the same header file that defined the MyAbstraction structure types, like this:

static MyAbstraction *myabs_new(const MyAbstractionVtable *vt)
{ return vt->new(vt); }
static void myabs_free(MyAbstraction *myabs)
{ myabs->vt->free(myabs); }
static void myimpl_modify(MyAbstraction *myabs, unsigned param)
{ myabs->vt->modify(myabs, param); }
static unsigned myimpl_query(MyAbstraction *myabs, unsigned param)
{ return myabs->vt->query(myabs, param); }

And now call sites can use those reasonably clean-looking wrapper functions, and shouldn't ever have to directly refer to the vt field inside any myabs object they're holding. For example, you might write something like this:

MyAbstraction *myabs = myabs_new(&MyImplementation_vtable);
myabs_update(myabs, 10);
unsigned output = myabs_query(myabs, 2);
myabs_free(myabs);

and then all this code can use a different implementation of the same abstraction by just changing which vtable pointer it passed in in the first line.

Some things to note about this system:

  • The implementation instance type (here ‘MyImplementation’ contains the abstraction type (‘MyAbstraction’) as one of its fields. But that field is not necessarily at the start of the structure. So you can't just cast pointers back and forth between the two types. Instead:
    • You ‘up-cast’ from implementation to abstraction by taking the address of the MyAbstraction field. You can see the example new method above doing this, returning &impl->myabs. All new methods do this on return.
    • Going in the other direction, each method that was passed a generic MyAbstraction *myabs parameter has to recover a pointer to the specific implementation type MyImplementation *impl. The idiom for doing that is to use the ‘container_of’ macro, also seen in the Linux kernel code. Generally, container_of(p, Type, field) says: ‘I'm confident that the pointer value ‘p’ is pointing to the field called ‘field’ within a larger struct of type Type. Please return me the pointer to the containing structure.’ So in this case, we take the ‘myabs’ pointer passed to the function, and ‘down-cast’ it into a pointer to the larger and more specific structure type MyImplementation, by adjusting the pointer value based on the offset within that structure of the field called ‘myabs’.

    This system is flexible enough to permit ‘multiple inheritance’, or rather, multiple implementation: having one object type implement more than one trait. For example, the Proxy type implements both the Socket trait and the Plug trait that connects to it, because it has to act as an adapter between another instance of each of those types.

    It's also perfectly possible to have the same object implement the same trait in two different ways. At the time of writing this I can't think of any case where we actually do this, but a theoretical example might be if you needed to support a trait like Comparable in two ways that sorted by different criteria. There would be no difficulty doing this in the PuTTY system: simply have your implementation struct contain two (or more) fields of the same abstraction type. The fields will have different names, which makes it easy to explicitly specify which one you're returning a pointer to during up-casting, or which one you're down-casting from using container_of. And then both sets of implementation methods can recover a pointer to the same containing structure.

  • Unlike in C++, all objects in PuTTY that use this system are dynamically allocated. The ‘constructor’ functions (whether they're virtualised across the whole abstraction or specific to each implementation) always allocate memory and return a pointer to it. The ‘free’ method (our analogue of a destructor) always expects the input pointer to be dynamically allocated, and frees it. As a result, client code doesn't need to know how large the implementing object type is, because it will never need to allocate it (on the stack or anywhere else).
  • Unlike in C++, the abstraction's ‘vtable’ structure does not only hold methods that you can call on an instance object. It can also hold several other kinds of thing:
    • Methods that you can call without an instance object, given only the vtable structure identifying a particular implementation of the trait. You might think of these as ‘static methods’, as in C++, except that they're virtual – the same code can call the static method of a different ‘class’ given a different vtable pointer. So they're more like ‘virtual static methods’, which is a concept C++ doesn't have. An example is the pubkey_bits method in ssh_keyalg.
    • The most important case of a ‘virtual static method’ is the new method that allocates and returns a new object. You can think of it as a ‘virtual constructor’ – another concept C++ doesn't have. (However, not all types need one of these: see below.)
    • The vtable can also contain constant data relevant to the class as a whole – ‘virtual constant data’. For example, a cryptographic hash function will contain an integer field giving the length of the output hash, and most crypto primitives will contain a string field giving the identifier used in the SSH protocol that describes that primitive.

    The effect of all of this is that you can make other pieces of code able to use any instance of one of these types, by passing it an actual vtable as a parameter. For example, the hash_simple function takes an ssh_hashalg vtable pointer specifying any hash algorithm you like, and internally, it creates an object of that type, uses it, and frees it. In C++, you'd probably do this using a template, which would mean you had multiple specialisations of hash_simple – and then it would be much more difficult to decide at run time which one you needed to use. Here, hash_simple is still just one function, and you can decide as late as you like which vtable to pass to it.

  • The abstract instance structure can also contain publicly visible data fields (this time, usually treated as mutable) which are common to all implementations of the trait. For example, BinaryPacketProtocol has lots of these.
  • Not all abstractions of this kind want virtual constructors. It depends on how different the implementations are.

    With a crypto primitive like a hash algorithm, the constructor call looks the same for every implementing type, so it makes sense to have a standardised virtual constructor in the vtable and a ssh_hash_new wrapper function which can make an instance of whatever vtable you pass it. And then you make all the vtable objects themselves globally visible throughout the source code, so that any module can call (for example) ssh_hash_new(&ssh_sha256).

    But with other kinds of object, the constructor for each implementing type has to take a different set of parameters. For example, implementations of Socket are not generally interchangeable at construction time, because constructing different kinds of socket require totally different kinds of address parameter. In that situation, it makes more sense to keep the vtable structure itself private to the implementing source file, and instead, publish an ordinary constructing function that allocates and returns an instance of that particular subtype, taking whatever parameters are appropriate to that subtype.

  • If you do have virtual constructors, you can choose whether they take a vtable pointer as a parameter (as shown above), or an existing instance object. In the latter case, they can refer to the object itself as well as the vtable. For example, you could have a trait come with a virtual constructor called ‘clone’, meaning ‘Make a copy of this object, no matter which implementation it is.’
  • Sometimes, a single vtable structure type can be shared between two completely different object types, and contain all the methods for both. For example, ssh_compression_alg contains methods to create, use and free ssh_compressor and ssh_decompressor objects, which are not interchangeable – but putting their methods in the same vtable means that it's easy to create a matching pair of objects that are compatible with each other.
  • Passing the vtable itself as an argument to the new method is not compulsory: if a given new implementation is only used by a single vtable, then that function can simply hard-code the vtable pointer that it writes into the object it constructs. But passing the vtable is more flexible, because it allows a single constructor function to be shared between multiple slightly different object types. For example, SHA-384 and SHA-512 share the same new method and the same implementation data type, because they're very nearly the same hash algorithm – but a couple of the other methods in their vtables are different, because the ‘reset’ function has to set up the initial algorithm state differently, and the ‘digest’ method has to write out a different amount of data.

    One practical advantage of having the myabs_foo family of inline wrapper functions in the header file is that if you change your mind later about whether the vtable needs to be passed to new, you only have to update the myabs_new wrapper, and then the existing call sites won't need changing.

  • Another piece of ‘stunt object orientation’ made possible by this scheme is that you can write two vtables that both use the same structure layout for the implementation object, and have an object transform from one to the other part way through its lifetime, by overwriting its own vtable pointer field. For example, the sesschan type that handles the server side of an SSH terminal session will sometimes transform in mid-lifetime into an SCP or SFTP file-transfer channel in this way, at the point where the client sends an ‘exec’ or ‘subsystem’ request that indicates that that's what it wants to do with the channel.

    This concept would be difficult to arrange in C++. In Rust, it wouldn't even make sense, because in Rust, objects implementing a trait don't even contain a vtable pointer at all – instead, the ‘trait object’ type (identifying a specific instance of some implementation of a given trait) consists of a pair of pointers, one to the object itself and one to the vtable. In that model, the only way you could make an existing object turn into a different trait would be to know where all the pointers to it were stored elsewhere in the program, and persuade all their owners to rewrite them.

  • Another stunt you can do is to have a vtable that doesn't have a corresponding implementation structure at all, because the only methods implemented in it are the constructors, and they always end up returning an implementation of some other vtable. For example, some of PuTTY's crypto primitives have a hardware-accelerated version and a pure software version, and decide at run time which one to use (based on whether the CPU they're running on supports the necessary acceleration instructions). So, for example, there are vtables for ssh_sha256_sw and ssh_sha256_hw, each of which has its own data layout and its own implementations of all the methods; and then there's a top-level vtable ssh_sha256, which only provides the ‘new’ method, and implements it by calling the ‘new’ method on one or other of the subtypes depending on what it finds out about the machine it's running on. That top-level selector vtable is nearly always the one used by client code. (Except for the test suite, which has to instantiate both of the subtypes in order to make sure they both pass the tests.)

    As a result, the top-level selector vtable ssh_sha256 doesn't need to implement any method that takes an ssh_cipher * parameter, because no ssh_cipher object is ever constructed whose vt field points to &ssh_sha256: they all point to one of the other two full implementation vtables.

E.14 Single compilation of each source file

The PuTTY build system for any given platform works on the following very simple model:

  • Each source file is compiled precisely once, to produce a single object file.
  • Each binary is created by linking together some combination of those object files.

Therefore, if you need to introduce functionality to a particular module which is only available in some of the tool binaries (for example, a cryptographic proxy authentication mechanism which needs to be left out of PuTTYtel to maintain its usability in crypto-hostile jurisdictions), the wrong way to do it is by adding #ifdefs in (say) proxy.c. This would require separate compilation of proxy.c for PuTTY and PuTTYtel, which means that the entire Makefile-generation architecture (see section E.11) would have to be significantly redesigned. Unless you are prepared to do that redesign yourself, and guarantee that it will still port to any future platforms we might decide to run on, you should not attempt this!

The right way to introduce a feature like this is to put the new code in a separate source file, and (if necessary) introduce a second new source file defining the same set of functions, but defining them as stubs which don't provide the feature. Then the module whose behaviour needs to vary (proxy.c in this example) can call the functions defined in these two modules, and it will either provide the new feature or not provide it according to which of your new modules it is linked with.

Of course, object files are never shared between platforms; so it is allowable to use #ifdef to select between platforms. This happens in puttyps.h (choosing which of the platform-specific include files to use), and also in misc.c (the Windows-specific ‘Minefield’ memory diagnostic system). It should be used sparingly, though, if at all.

E.15 Do as we say, not as we do

The current PuTTY code probably does not conform strictly to all of the principles listed above. There may be the occasional SSH-specific piece of code in what should be a backend-independent module, or the occasional dependence on a non-standard X library function under Unix.

This should not be taken as a licence to go ahead and violate the rules. Where we violate them ourselves, we're not happy about it, and we would welcome patches that fix any existing problems. Please try to help us make our code better, not worse!


If you want to provide feedback on this manual or on the PuTTY tools themselves, see the Feedback page.

[PuTTY release 0.76]
putty-0.76/doc/AppendixF.html0000644000175000017500000003202314072266315013073 00000000000000 PuTTY download keys and signatures

Previous | Contents | Index | Next

Appendix F: PuTTY download keys and signatures

We create GPG signatures for all the PuTTY files distributed from our web site, so that users can be confident that the files have not been tampered with. Here we identify our public keys, and explain our signature policy so you can have an accurate idea of what each signature guarantees. This description is provided as both a web page on the PuTTY site, and an appendix in the PuTTY manual.

As of release 0.58, all of the PuTTY executables contain fingerprint material (usually accessed via the -pgpfp command-line option), such that if you have an executable you trust, you can use it to establish a trust path, for instance to a newer version downloaded from the Internet.

As of release 0.67, the Windows executables and installer also contain built-in signatures that are automatically verified by Windows' own mechanism (‘Authenticode’). The keys used for that are different, and are not covered here.

(Note that none of the keys, signatures, etc mentioned here have anything to do with keys used with SSH - they are purely for verifying the origin of files distributed by the PuTTY team.)

F.1 Public keys

We maintain multiple keys, stored with different levels of security due to being used in different ways. See section F.2 below for details.

The keys we provide are:

Snapshot Key
Used to sign routine development builds of PuTTY: nightly snapshots, pre-releases, and sometimes also custom diagnostic builds we send to particular users.
Release Key
Used to sign manually released versions of PuTTY.
Secure Contact Key
An encryption-capable key suitable for people to send confidential messages to the PuTTY team, e.g. reports of vulnerabilities.
Master Key
Used to tie all the above keys into the GPG web of trust. The Master Key signs all the other keys, and other GPG users have signed it in turn.

The current issue of those keys are available for download from the PuTTY website, and are also available on PGP keyservers using the key IDs listed below.

Master Key (2018)
RSA, 4096-bit. Key ID: 76BC7FE4EBFD2D9E. Fingerprint: 24E1 B1C5 75EA 3C9F F752  A922 76BC 7FE4 EBFD 2D9E
Release Key (2018)
RSA, 3072-bit. Key ID: 6289A25F4AE8DA82. Fingerprint: E273 94AC A3F9 D904 9522  E054 6289 A25F 4AE8 DA82
Snapshot Key (2018)
RSA, 3072-bit. Key ID: 38BA7229B7588FD1. Fingerprint: C92B 52E9 9AB6 1DDA 33DB  2B7A 38BA 7229 B758 8FD1
Secure Contact Key (2018)
RSA, 3072-bit. Key ID: 657D487977F95C98. Fingerprint: A680 0082 2998 6E46 22CA  0E43 657D 4879 77F9 5C98

F.2 Security details

The various keys have various different security levels. This section explains what those security levels are, and how far you can expect to trust each key.

F.2.1 The Development Snapshots key

The Development Snapshots private key is stored without a passphrase. This is necessary, because the snapshots are generated every night without human intervention, so nobody would be able to type a passphrase.

The snapshots are built and signed on a team member's home computers, before being uploaded to the web server from which you download them.

Therefore, a signature from the Development Snapshots key DOES protect you against:

  • People tampering with the PuTTY binaries between the PuTTY web site and you.
  • The maintainers of our web server attempting to abuse their root privilege to tamper with the binaries.

But it DOES NOT protect you against:

  • People tampering with the binaries before they are uploaded to our download servers.
  • People tampering with the build machines so that the next set of binaries they build will be malicious in some way.
  • People stealing the unencrypted private key from the build machine it lives on.

Of course, we take all reasonable precautions to guard the build machines. But when you see a signature, you should always be certain of precisely what it guarantees and precisely what it does not.

F.2.2 The Releases key

The Releases key is more secure: because it is only used at release time, to sign each release by hand, we can store it encrypted.

The Releases private key is kept encrypted on the developers' own local machines. So an attacker wanting to steal it would have to also steal the passphrase.

F.2.3 The Secure Contact Key

The Secure Contact Key is stored with a similar level of security to the Release Key: it is stored with a passphrase, and no automated script has access to it.

F.2.4 The Master Keys

The Master Key signs almost nothing. Its purpose is to bind the other keys together and certify that they are all owned by the same people and part of the same integrated setup. The only signatures produced by the Master Key, ever, should be the signatures on the other keys.

The Master Key is especially long, and its private key and passphrase are stored with special care.

We have collected some third-party signatures on the Master Key, in order to increase the chances that you can find a suitable trust path to them.

We have uploaded our various keys to public keyservers, so that even if you don't know any of the people who have signed our keys, you can still be reasonably confident that an attacker would find it hard to substitute fake keys on all the public keyservers at once.

F.3 Key rollover

Our current keys were generated in August 2018.

Each new Master Key is signed with the old one, to show that it really is owned by the same people and not substituted by an attacker.

Each new Master Key also signs the previous Release Keys, in case you're trying to verify the signatures on a release prior to the rollover and can find a chain of trust to those keys from any of the people who have signed our new Master Key.

Each release is signed with the Release Key that was current at the time of release. We don't go back and re-sign old releases with newly generated keys.

The details of all previous keys are given here.

Key generated in 2016 (when we first introduced the Secure Contact Key)

Secure Contact Key (2016)
RSA, 2048-bit. Main key ID: 2048R/8A0AF00B (long version: 2048R/C4FCAAD08A0AF00B). Encryption subkey ID: 2048R/50C2CF5C (long version: 2048R/9EB39CC150C2CF5C). Fingerprint: 8A26 250E 763F E359 75F3  118F C4FC AAD0 8A0A F00B

Keys generated in the 2015 rollover

Master Key (2015)
RSA, 4096-bit. Key ID: 4096R/04676F7C (long version: 4096R/AB585DC604676F7C). Fingerprint: 440D E3B5 B7A1 CA85 B3CC  1718 AB58 5DC6 0467 6F7C
Release Key (2015)
RSA, 2048-bit. Key ID: 2048R/B43434E4 (long version: 2048R/9DFE2648B43434E4). Fingerprint: 0054 DDAA 8ADA 15D2 768A  6DE7 9DFE 2648 B434 34E4
Snapshot Key (2015)
RSA, 2048-bit. Key ID: 2048R/D15F7E8A (long version: 2048R/EEF20295D15F7E8A). Fingerprint: 0A3B 0048 FE49 9B67 A234  FEB6 EEF2 0295 D15F 7E8A

Original keys generated in 2000 (two sets, RSA and DSA)

Master Key (original RSA)
RSA, 1024-bit. Key ID: 1024R/1E34AC41 (long version: 1024R/9D5877BF1E34AC41). Fingerprint: 8F 15 97 DA 25 30 AB 0D  88 D1 92 54 11 CF 0C 4C
Master Key (original DSA)
DSA, 1024-bit. Key ID: 1024D/6A93B34E (long version: 1024D/4F5E6DF56A93B34E). Fingerprint: 313C 3E76 4B74 C2C5 F2AE  83A8 4F5E 6DF5 6A93 B34E
Release Key (original RSA)
RSA, 1024-bit. Key ID: 1024R/B41CAE29 (long version: 1024R/EF39CCC0B41CAE29). Fingerprint: AE 65 D3 F7 85 D3 18 E0  3B 0C 9B 02 FF 3A 81 FE
Release Key (original DSA)
DSA, 1024-bit. Key ID: 1024D/08B0A90B (long version: 1024D/FECD6F3F08B0A90B). Fingerprint: 00B1 1009 38E6 9800 6518  F0AB FECD 6F3F 08B0 A90B
Snapshot Key (original RSA)
RSA, 1024-bit. Key ID: 1024R/32B903A9 (long version: 1024R/FAAED21532B903A9). Fingerprint: 86 8B 1F 79 9C F4 7F BD  8B 1B D7 8E C6 4E 4C 03
Snapshot Key (original DSA)
DSA, 1024-bit. Key ID: 1024D/7D3E4A00 (long version: 1024D/165E56F77D3E4A00). Fingerprint: 63DD 8EF8 32F5 D777 9FF0  2947 165E 56F7 7D3E 4A00

If you want to provide feedback on this manual or on the PuTTY tools themselves, see the Feedback page.

[PuTTY release 0.76]
putty-0.76/doc/AppendixG.html0000644000175000017500000001551614072266315013104 00000000000000 SSH-2 names specified for PuTTY

Previous | Contents | Index | Next

Appendix G: SSH-2 names specified for PuTTY

There are various parts of the SSH-2 protocol where things are specified using a textual name. Names ending in @putty.projects.tartarus.org are reserved for allocation by the PuTTY team. Allocated names are documented here.

G.1 Connection protocol channel request names

These names can be sent in a SSH_MSG_CHANNEL_REQUEST message.

simple@putty.projects.tartarus.org
This is sent by a client to announce that it will not have more than one channel open at a time in the current connection (that one being the one the request is sent on). The intention is that the server, knowing this, can set the window on that one channel to something very large, and leave flow control to TCP. There is no message-specific data.
winadj@putty.projects.tartarus.org
PuTTY sends this request along with some SSH_MSG_CHANNEL_WINDOW_ADJUST messages as part of its window-size tuning. It can be sent on any type of channel. There is no message-specific data. Servers MUST treat it as an unrecognised request and respond with SSH_MSG_CHANNEL_FAILURE.

(Some SSH servers get confused by this message, so there is a bug-compatibility mode for disabling it. See section 4.26.3.)

G.2 Key exchange method names

rsa-sha1-draft-00@putty.projects.tartarus.org
rsa-sha256-draft-00@putty.projects.tartarus.org
rsa1024-sha1-draft-01@putty.projects.tartarus.org
rsa1024-sha256-draft-01@putty.projects.tartarus.org
rsa2048-sha256-draft-01@putty.projects.tartarus.org
rsa1024-sha1-draft-02@putty.projects.tartarus.org
rsa2048-sha512-draft-02@putty.projects.tartarus.org
rsa1024-sha1-draft-03@putty.projects.tartarus.org
rsa2048-sha256-draft-03@putty.projects.tartarus.org
rsa1024-sha1-draft-04@putty.projects.tartarus.org
rsa2048-sha256-draft-04@putty.projects.tartarus.org
These appeared in various drafts of what eventually became RFC 4432. They have been superseded by rsa1024-sha1 and rsa2048-sha256.

G.3 Encryption algorithm names

arcfour128-draft-00@putty.projects.tartarus.org
arcfour256-draft-00@putty.projects.tartarus.org
These were used in drafts of what eventually became RFC 4345. They have been superseded by arcfour128 and arcfour256.

G.4 Agent extension request names

The SSH agent protocol, which is only specified in an Internet-Draft at the time of writing (draft-miller-ssh-agent), defines an extension mechanism. These names can be sent in an SSH_AGENTC_EXTENSION message.

add-ppk@putty.projects.tartarus.org
The payload is a single SSH-2 string containing a keypair in the PPK format defined in appendix C. Compared to the standard SSH_AGENTC_ADD_IDENTITY, this extension allows adding keys in encrypted form, with the agent requesting a decryption passphrase from the user on demand, and able to revert the key to encrypted form.
reencrypt@putty.projects.tartarus.org
The payload is a single SSH-2 string specifying a public key blob, as in SSH_AGENTC_REMOVE_IDENTITY. Requests that the agent forget any cleartext form of a specific key.

Returns SSH_AGENT_SUCCESS if the agent ended up holding the key only in encrypted form (even if it was already encrypted); returns SSH_AGENT_EXTENSION_FAILURE if not (if it wasn't held by the agent at all, or only in cleartext form).

reencrypt-all@putty.projects.tartarus.org
No payload. Requests that the agent forget the cleartext form of any keys for which it holds an encrypted form.

If the agent holds any keys with an encrypted form (or no keys at all), returns SSH_AGENT_SUCCESS to indicate that no such keys are now held in cleartext form, followed by a uint32 specifying how many keys remain in cleartext form (because the agent didn't hold an encrypted form for them). If the agent holds nothing but keys in cleartext form, returns SSH_AGENT_EXTENSION_FAILURE.

list-extended@putty.projects.tartarus.org
No payload. Returns SSH_AGENT_SUCCESS followed by a list of identities similar to SSH_AGENT_IDENTITIES_ANSWER, except that each key has an extra SSH-2 string at the end. Currently that string contains a single uint32 flags word, with the following bits defined:
Bit 0
If set, key is held with an encrypted form (so that the reencrypt extension can do something useful with it).
Bit 1
If set, key's cleartext form is not currently held (so the user will have to supply a passphrase before the key can be used).

If you want to provide feedback on this manual or on the PuTTY tools themselves, see the Feedback page.

[PuTTY release 0.76]
putty-0.76/doc/IndexPage.html0000644000175000017500000031213414072266315013065 00000000000000

Previous | Contents | Index | Next

Index

-4: Section 3.11.3.17
-6: Section 3.11.3.17
Abort Output, Telnet special command: Section 3.1.3.2
Abort Process, Telnet special command: Section 3.1.3.2
accented characters: Section 3.3, Section 4.4.7
accessibility: Section 4.9.4
access to files, changing: Section 6.2.14
account name: Section 2.3, Section 3.11.3.4, Section 5.2.1.1
account name, for auto-login: Section 4.15.1
account name, for proxy: Section 4.16.4
account name, local, in Rlogin: Section 4.30.1
account name, local, in Windows: Section 4.30.1
account names, different: Section 2.3, Section 4.21.8
ACL, process (Windows): Section 3.11.3.27, Section 9.3.4
-A command-line option: Section 3.11.3.10
-a command-line option: Section 3.11.3.10
active Telnet negotiation: Section 4.29.2
address, IP: Section 3.5, Section 4.1.1, Section 4.14.4
ad-hoc proxy: Section 4.16.1, Section 4.16.5, Section 4.29.2
adjusting a selection: Section 3.1.1, Section 4.11.1
Advanced Encryption Standard: Section 4.20
AES: Section 4.20
-agent: Section 3.11.3.9
agent, authentication: Section 8.1, Chapter 9
agent forwarding: Section 3.11.3.10, Section 4.21.7, Section 9.4
algorithm, encryption: Section 4.20
algorithm, key exchange: Section 4.18.1
algorithm, public-key: Section 8.1
allocation, of pseudo-terminal: Section 3.11.3.12, Section 4.23.1
alternate screen: Section 4.6.4, Section 4.7.4, Question A.7.16
ALT-F4: Section 4.9.3
‘AltGr’ key: Section 4.4.7, Section 4.4.8
‘Alt’ key: Section 4.9.5
ALT-Space: Section 4.9.4
always on top: Section 4.9.6
ANSI colours: Section 4.13.1, Section 4.13.7
ANSI graphics: Section 3.3, Section 4.10.4
ANSI printing: Section 4.3.10
answerback string: Section 4.3.7
Application Cursor Keys: Section 4.4.4, Section 4.6.1
Application key: Section 4.4.7
Application Keypad: Section 4.4.5, Section 4.6.1
Arabic: Section 4.6.11
Arabic text shaping: Section 4.6.10
Arcfour: Section 4.20, Section 10.4
Are You There, Telnet special command: Section 3.1.3.2
Argon2 passphrase hashing function: Section 8.2.12.2
arguments, command-line: Section 3.11, Section 9.3
ASCII: Section 4.10.5, Question A.2.11
assertion failed: Section 10.7
asymmetric key algorithm: Section 8.1
authentication: Section 4.21
authentication agent: Section 8.1, Chapter 9
authentication agent forwarding: Section 3.11.3.10, Section 4.21.7, Section 9.4
authentication, challenge/response: Section 4.21.5, Section 4.21.6
authentication, keyboard-interactive: Section 4.21.6
authentication, public key: Section 3.11.3.18, Section 4.21.9, Section 4.26.9, Section 5.2.4, Section 6.3, Section 7.2.2, Section 8.1
authentication, to proxy: Section 4.16.4
authentication, X11: Section 4.24.1
Authenticode: Appendix F
authorized_keys file: Section 8.2.11, Section 8.3
AUTOEXEC.BAT: Section 5.1, Section 7.1
automated operations: Chapter 7
auto wrap mode: Section 4.3.1, Question A.7.7
background colour: Section 4.3.5, Question A.7.6
background colour, bright: Section 4.3.6
background colour, default: Section 4.13.6, Section 4.13.7
backspace, destructive: Section 4.6.8
backspace key: Section 4.4.1
bandwidth: Section 4.14.2, Section 4.15.4, Section 4.17.3
banner: Section 4.21.1
bare ssh-connection protocol: Section 4.27
‘basic’ authentication (HTTP): Section 4.16.4
batch files: Section 4.32, Section 5.2.3, Section 7.3
-batch Plink command-line option: Section 7.2.3.1
-batch PSCP command-line option: Section 5.2.2.5
-batch PSFTP command-line option: Section 6.1.4
batch scripts in PSFTP: Section 6.1.1
baud rate, of terminal: Section 4.15.4
-bc PSFTP command-line option: Section 6.1.2
beep, terminal: Section 4.5
beep, with PC speaker: Section 4.5.1
bell, disabling: Section 4.5.1, Section 4.5.3
bell overload mode: Section 4.5.3
bell, terminal: Section 4.5
bell, visual: Section 4.5.1
-be PSFTP command-line option: Section 6.1.3
bidirectional text: Section 4.6.11
bind address: Section 3.5, Section 3.11.3.5, Section 4.25
BitchX: Section 4.6.9
8-bit colour: Section 4.13.5
32-bit Windows: Question A.6.10
64-bit Windows: Question A.6.10, Question A.7.22
black, bold: Section 4.13.4
blinking cursor: Section 4.8.1
blinking text: Section 4.3.6
Blowfish: Section 4.20, Section 10.4
bold black: Section 4.13.4
bold text: Section 4.13.4, Section 4.13.6, Section 4.13.7
box-drawing characters: Section 3.3, Section 4.10.4
-b PSFTP command-line option: Section 6.1.1
Break, serial special command: Section 3.1.3.2
Break, SSH special command: Section 3.1.3.2
Break, Telnet special command: Section 3.1.3.2
bright black: Section 4.13.4
BSD: Section 4.29.1
bug reporting: Appendix B
bugs, in SSH servers: Section 4.26
bulletin board system: Section 1.1
cache, of SSH host keys: Section 2.2, Section 3.1.3.2, Section 3.11.3.20, Section 3.11.3.21, Section 4.14.5, Section 4.19, Section 4.19.3
Caps Lock: Section 4.10.3
Carriage Return: Section 4.3.3, Section 4.3.4
cascading credentials: Section 4.18.1.1
cat: Question A.7.13
-C command-line option: Section 3.11.3.15
ChaCha20-Poly1305: Section 4.20
challenge/response authentication: Section 4.21.5, Section 4.21.6
‘Change Settings’: Section 3.1.3.4
changing permissions on files: Section 6.2.14
changing user names: Section 2.3, Section 4.21.8
CHAP: Section 4.16.4
character classes: Section 4.12.1
characters, accented: Section 3.3, Section 4.4.7
character set: Section 3.3, Section 4.6.9, Section 4.10
characters, line-drawing: Section 3.3, Section 4.10.4
character width: Section 4.10.2
Chinese: Section 4.10.2
choosing a protocol: Section 1.2, Section 3.11.3.2
cipher algorithm: Section 4.20
Cisco: Section 3.11.3.6
CJK: Section 4.10.2
CJK ambiguous characters: Section 4.10.2
clean up after PuTTY: Question A.8.2
-cleanup command-line option: Section 3.11.2
clear screen: Section 4.3.5
‘Clear Scrollback’: Section 3.1.3.6
client: Section 1.1
clipboard: Section 3.1.1, Section 3.1.3.1, Section 3.1.3.5
CLIPBOARD selection: Section 4.11.4
clipboards, multiple: Section 4.11.4
Close button: Section 2.5, Section 4.9.2
closing window: Section 4.1.3, Section 4.9.2, Section 4.9.3
code page: Section 4.10.1
colour: Section 4.12.2, Section 4.13, Question A.7.2
colour, background, default: Section 4.13.6, Section 4.13.7
colour, 8-bit: Section 4.13.5
colour, foreground, default: Section 4.13.6, Section 4.13.7
256-colour mode: Section 4.13.2
colour, of cursor: Section 4.13.6, Section 4.13.7
colours, ANSI: Section 4.13.1, Section 4.13.7
colours, system: Section 4.13.6
columns, in terminal window: Section 4.7.1
COM1: Section 4.28.1
command-line arguments: Section 3.11, Section 9.3
command-line interface: Section 1.1
command line, loading saved sessions from: Section 3.11.3.1
-1 command-line option: Section 3.11.3.16
-2 command-line option: Section 3.11.3.16
Command Prompt: Section 1.1, Section 1.1, Section 3.11, Section 5.1, Section 7.1
command, proxy: Section 3.11.3.26, Section 4.16.1, Section 4.16.5
commands on the server: Section 2.4, Section 3.11.3.6, Section 4.17.1, Section 4.17.2, Question A.6.2
commands, reading from a file: Section 3.11.3.6
comment: Section 6.2
Compose key: Section 4.4.7
compression: Section 3.11.3.15, Section 4.17.3
confidentiality: Section 4.18.2
configuration: Section 3.11.3.23
configuration options: Chapter 4
connection, network: Section 3.5, Section 4.14, Section 4.25
connections, half-open: Section 4.14.3
connections, idle: Section 4.14.1, Question A.7.8
connections, interactive: Section 4.14.2, Section 4.23.1, Chapter 7
connectivity, breaks in: Section 4.14.1, Question A.7.9
CONNECT proxy (HTTP): Section 4.16.1
console window: Section 1.1, Section 1.1, Section 3.11, Section 5.1, Section 7.1
context menu: Section 3.1.1, Section 3.1.3, Section 4.11.1
Control-?: Section 4.4.1
Control-H: Section 4.4.1
control sequences: Section 3.2, Section 4.3.1, Section 4.3.2, Section 4.3.5, Section 4.3.6, Section 4.6.2, Section 4.9.1, Section 4.10.4, Section 4.11.2, Section 4.12.1, Section 4.13.1, Section 4.13.4, Section 4.15.3, Section 4.15.3, Section 7.2.1, Question A.5.1
cookie, magic: Section 4.24.1
coordinates, cursor: Section 4.3.2
Copy All to Clipboard: Section 3.1.3.5
copy and paste: Section 3.1.1, Section 4.6.2, Section 4.10.5, Section 4.11, Section 4.11.2, Question A.6.6
copyright: Appendix D
corruption, of display: Section 4.6.10, Section 4.6.11
CP437: Section 4.10.1
CP866: Section 4.10.1
-c Pageant command-line option: Section 9.3.2
CRC: Section 10.12
CR (Carriage Return): Section 4.3.3, Section 4.3.4
creating directories: Section 6.2.16
creating key pairs: Section 8.2
credential delegation, GSSAPI: Section 4.22.1
credentials, cascading: Section 4.18.1.1
CryptoCard authentication: Section 4.21.5
Ctrl-?: Section 4.4.1
Ctrl-Break: Section 3.1.3.2
Ctrl-C: Section 3.1.1, Section 3.1.1, Section 4.11.4
Ctrl-H: Section 4.4.1
Ctrl-Ins: Section 3.1.1, Section 4.11.4.2
Ctrl-PgDn: Section 3.1.2
Ctrl-PgUp: Section 3.1.2
Ctrl-Shift-C: Section 4.11.4.2
Ctrl-Shift-PgDn: Section 3.1.2
Ctrl-Shift-PgUp: Section 3.1.2
Ctrl-Shift-V: Section 4.11.4.2
Ctrl-V: Section 4.11.4
Ctrl, with right mouse button: Section 3.1.1
current working directory: Section 6.2.7, Section 6.2.8
cursor: Section 4.8.1
cursor, blinking: Section 4.8.1
cursor colour: Section 4.13.6, Section 4.13.7
cursor coordinates: Section 4.3.2
cursor keys, ‘Application’ mode: Section 4.4.4, Section 4.6.1
cut and paste: Section 3.1.1, Section 4.6.2, Section 4.10.5, Section 4.11, Section 4.11.2, Question A.6.6
CVS: Section 7.4
CVS_RSH environment variable: Section 7.4
Cyrillic: Section 4.10.3
-D command-line option: Section 3.11.3.5
debugging Internet protocols: Section 3.7
DEC Compose key: Section 4.4.7
DEC Origin Mode: Section 4.3.2
DECterm: Question A.5.1
Default Beep sound, Windows: Section 4.5
Default Settings: Section 3.11.1, Section 4.1.2
delays, in SSH-2 sessions: Question A.7.20
delegation, of GSSAPI credentials: Section 4.22.1
deleting directories: Section 6.2.17
deleting files: Section 6.2.15
DES: Section 4.20, Section 10.4
destructive backspace: Section 4.6.8
development snapshots: Section B.2
diagnostic, proxy: Section 4.16.6
dialog box: Section 2.1
DiceWare: Section 8.2.8
differences between SSH, Telnet, Rlogin, and SUPDUP: Section 1.2
different user names: Section 2.3, Section 4.21.8
Diffie-Hellman group exchange: Section 4.18.1
Diffie-Hellman key exchange: Section 4.18.1
digital signature: Section 4.26.6, Section 8.1
Digital Signature Standard: Section 4.19.1, Section 4.19.1, Section 8.1, Section 8.2.2, Question A.8.3
directories, creating: Section 6.2.16
directories, removing: Section 6.2.17
display corruption: Section 4.6.10, Section 4.6.11
DISPLAY environment variable: Section 3.4
DLL: Section 4.22.2, Question A.6.10
DNS: Section 4.16.3
DNS name: Section 2.1, Section 4.1.1, Section 4.9.1, Section 4.14.4, Section 5.2.1.2
DNS resolution: Section 4.16.3
DNS, with proxy: Section 4.16.2, Section 4.16.3
Domain Name System: Section 4.16.3
double-click: Section 3.1.1, Section 4.12.1
double-width character: Section 4.10.2
downloading files: Section 5.2.1, Section 6.2.9
Dragon NaturallySpeaking: Section 4.9.4
DSA: Section 4.19.1, Section 4.19.1, Section 8.1, Section 8.2.2, Question A.8.3
DSA authentication: Section 3.11.3.18, Section 4.21.9, Section 4.26.9, Section 5.2.4, Section 6.3, Section 7.2.2, Section 8.1
‘Duplicate Session’: Section 3.1.3.3, Question A.7.22
dynamic port forwarding: Section 3.5, Section 3.11.3.5, Section 4.25
East Asian Ambiguous characters: Section 4.10.2
ECDSA: Section 8.1, Section 8.2.2
echo, local: Section 4.3.8, Section 4.3.9, Question A.2.4
echo, remote: Section 4.3.8
Ed25519: Section 4.19.1, Section 8.2.3
Ed448: Section 4.19.1, Section 8.2.3
EdDSA: Section 4.19.1, Section 4.19.1, Section 8.2.2
Edwards-curve DSA: Section 4.19.1, Section 4.19.1, Section 8.2.2
elliptic curve: Section 4.18.1, Section 4.19.1, Section 8.2.2
emulation, terminal: Section 3.1.3.6, Section 4.3, Section 4.3.7, Section 4.4, Section 4.6, Section 4.15.3
encryption: Section 4.26.10, Section 8.1, Section 8.2.8
encryption algorithm: Section 4.20
End key: Section 4.4.2
End Of File, Telnet special command: Section 3.1.3.2
End Of Record, Telnet special command: Section 3.1.3.2
enquiry character: Section 4.3.7
environment variables: Section 4.15.5, Section 4.29.1
Erase Character, Telnet special command: Section 3.1.3.2, Section 4.29.3
Erase Line, Telnet special command: Section 3.1.3.2
erase screen: Section 4.3.5
ERASE, special character: Section 4.23.2
ERRORLEVEL: Section 5.2.3
error messages: Chapter 10
escape sequences: Section 3.2, Section 4.3.1, Section 4.3.2, Section 4.3.6, Section 4.6.2, Section 4.9.1, Section 4.10.4, Section 4.11.2, Section 4.12.1, Section 4.13.1, Section 4.13.4, Section 4.15.3, Section 7.2.1
Event Log: Section 3.1.3.1, Section 4.2
execute permission: Section 6.2.14
exit value: Section 5.2.3
expiry, of passwords: Section 4.21.6
exporting private keys: Section 8.2.14
extending a selection: Section 3.1.1, Section 4.11.1
FAQ: Appendix A
features, supported: Section A.2
feedback: Appendix B
feep: Section 4.5
filenames containing spaces: Section 6.2.1, Question A.6.9
files, changing permissions on: Section 6.2.14
files, deleting: Section 6.2.15
files, listing: Section 5.2.2.1, Section 6.2.13
files, receiving: Section 5.2.1, Section 6.2.9
files, renaming and moving: Section 6.2.18
files, resuming transfer of: Section 6.2.12
files, sending: Section 5.2.1, Section 6.2.10
files, transferring: Chapter 5, Chapter 6
finger: Section 3.5
fingerprint, MD5, of SSH host key: Section 2.2, Section 4.19.3
fingerprint, of PGP key: Section 3.11.3.22
fingerprint, of SSH authentication key: Section 8.2.6, Section 9.2.1
fingerprint, of SSH host key: Section 2.2
fingerprint, SHA-256, of SSH host key: Section 2.2, Section 4.19.3
firewalls: Section 4.14.1, Section 4.18.2, Section 4.29.2, Section 10.16, Question A.7.8
flashing cursor: Section 4.8.1
flashing text: Section 4.3.6
flow-control window: Section 4.26.5
font: Section 4.8.2, Section 4.10.4, Section 4.12.2, Section 4.13.4
font size: Section 4.7.2, Section 4.8.2
foreground colour, default: Section 4.13.6, Section 4.13.7
forwarding, of X11: Section 3.4, Section 3.11.3.11, Section 4.24
forwarding ports in SSH: Section 3.5, Section 3.11.3.5, Section 4.16, Section 4.17.2, Section 4.25
forwarding ports in SSH, changing mid-session: Section 4.25
forwarding, SSH agent: Section 3.11.3.10, Section 4.21.7, Section 9.4
Frequently Asked Questions: Appendix A
ftp: Chapter 6
full-screen mode: Section 3.1.3.7, Section 4.7.3, Section 4.9.7
function keys: Section 4.4.3, Question A.7.13
generating key pairs: Section 8.2
glob (wildcard): Section 5.2.1, Section 5.2.1.3, Section 5.2.2.6, Section 6.2.2, Section 6.2.11
Go Ahead, Telnet special command: Section 3.1.3.2
GPG signatures, of PuTTY binaries: Appendix F
graphical applications: Section 3.4, Section 4.24
group exchange, Diffie-Hellman: Section 4.18.1
GSSAPI: Section 4.15.2, Section 4.22
GSSAPI credential delegation: Section 4.22.1
Gtk: Question A.3.2
half-open connections: Section 4.14.3
Hebrew: Section 4.6.11
history: Section 3.1.2
HMAC: Section 4.26.8
home directory: Section 5.2.1.3
Home key: Section 4.4.2
-hostkey: Section 3.11.3.21
host key fingerprint (SSH): Section 2.2
host key management: Section 2.2, Section 3.1.3.2, Section 3.11.3.20, Section 3.11.3.21, Section 4.14.5, Section 4.19, Section 4.19.3
host keys, manually configuring: Section 3.11.3.21, Section 4.19.3
host keys, trusting: Section 2.2
host keys, upgrading: Section 3.1.3.2
host key type: Section 4.19.1
host key, verifying: Section 2.2, Question A.2.9
host name: Section 2.1, Section 4.1.1, Section 4.9.1, Section 4.14.4, Section 5.2.1.2
host name, logical: Section 3.11.3.20, Section 4.14.5
host name resolution: Section 4.16.3
host name resolution, with proxy: Section 4.16.2, Section 4.16.3
HTTP: Section 3.7
HTTP ‘basic’ authentication: Section 4.16.4
HTTP proxy: Section 4.16.1
-i command-line option: Section 3.11.3.18
icon, PuTTY's: Section 2.3, Section A.5.3
icon title: Section 4.9.1
idle connections: Section 4.14.1, Question A.7.8
‘ignore’ messages, in SSH: Section 3.1.3.2, Section 4.26.1, Section 4.26.11
IGNORE message, SSH special command: Section 3.1.3.2
importing private keys: Section 8.2.14
inactive window: Section 4.1.3
indenting: Section 3.1.1
integrity: Section 4.18.2
interactive connections: Section 4.14.2, Section 4.23.1, Chapter 7
intermittent connectivity: Section 4.14.1, Question A.7.9
internal error: Section 10.7
internal fault: Section 10.7
Internet protocols, debugging: Section 3.7
Internet Protocol version: Section 3.11.3.17, Section 4.14.4, Section 4.25.2
Interrupt Process, Telnet special command: Section 3.1.3.2, Section 4.29.3
IP address: Section 3.5, Section 4.1.1, Section 4.14.4
IP address, loopback: Section 3.5, Section 4.16.2
IP masquerading: Section 10.16, Question A.7.8
IPv4: Section 3.11.3.17, Section 4.14.4, Section 4.25.2
IPv6: Section 3.11.3.17, Section 4.14.4, Section 4.25.2
IPv6 address: Section 4.25
ISO-8859: Section 4.10.1
ISO-10646 (Unicode): Section 4.6.10, Section 4.10.1, Section 4.10.4, Section 4.10.5
IUTF8 terminal mode: Section 4.23.2
Japanese: Section 4.10.2
keepalives, application: Section 4.14.1, Section 4.18.2, Section 4.26.1, Section 4.26.11
keepalives, TCP: Section 4.14.3
Kerberos: Section 4.22
kex: Section 4.18
keyboard: Section 4.4, Section 4.15.3, Question A.7.12, Question A.7.13
keyboard-interactive authentication: Section 4.21.6
key exchange: Section 4.18
key exchange algorithm: Section 4.18.1
key exchange, Diffie-Hellman: Section 4.18.1
key exchange, forcing repeat: Section 3.1.3.2
key exchange, repeat: Section 3.1.3.2, Section 4.18.2, Section 4.26.2, Question A.7.20
--keylist: Section 9.3.3
keypad, ‘Application’ mode: Section 4.4.5, Section 4.6.1
keypad, NetHack mode: Section 4.4.6
keypad, numeric: Section 4.4.3
key pair: Section 8.1
key pairs, generating: Section 8.2
known_hosts: Question A.2.9
Korean: Section 4.10.2
last selected text: Section 4.11.4.2
-L command-line option: Section 3.11.3.5
-l command-line option: Section 3.11.3.4
left mouse button: Section 3.1.1, Section 4.11.1
LF (Line Feed): Section 4.3.3, Section 4.3.4
licence: Appendix D
line-drawing characters: Section 3.3, Section 4.10.4
line editing, local: Section 4.3.9
Line Feed: Section 4.3.3, Section 4.3.4
lines, selecting: Section 3.1.1
line wrapping, automatic: Section 4.3.1, Question A.7.7
links (web browser): Section 4.6.2
Linux: Section 4.4, Section 4.4.1, Section 4.15.3, Section 4.23.1, Section 4.25.2, Section 6.2.14, Section 10.18, Question A.7.13
Linux, Red Hat: Question A.7.15
Linux version of PuTTY tools: PuTTY User Manual, Question A.3.2
Linux virtual console: Section 4.4.3
listen address: Section 3.5, Section 3.11.3.5, Section 4.25
listing files: Section 5.2.2.1, Section 6.2.13
-load command-line option: Section 3.11.3.1
loading private keys: Section 8.2.14
loading saved sessions from command line: Section 3.11.3.1
loading settings: Section 4.1.2
loading settings from a file: Section 4.32
local echo: Section 4.3.8, Section 4.3.9, Question A.2.4
localhost: Section 3.5, Section 4.16.2, Section 4.25.1, Question A.7.17
local line editing: Section 4.3.9
local proxy: Section 3.11.3.26, Section 4.16.1, Section 4.16.5
local-to-remote port forwarding: Section 3.5, Section 3.11.3.5, Section 4.25
local user name, in Rlogin: Section 4.30.1
local user name, in Windows: Section 4.30.1
local Windows command: Section 6.2.19
locking up, SSH-2 sessions: Question A.7.20
-logappend: Section 3.11.3.25
Log, Event: Section 3.1.3.1, Section 4.2
log file: Section 3.2, Section 3.11.3.24, Section 4.2
log file, flushing: Section 4.2.3
log file, header: Section 4.2.4
logging in: Section 2.3
logging out: Section 2.5
logging, proxy: Section 4.16.6
-loghost: Section 3.11.3.20
logical host name: Section 3.11.3.20, Section 4.14.5
logical palettes: Section 4.13.5
login name: Section 2.3, Section 3.11.3.4, Section 5.2.1.1
login name, for auto-login: Section 4.15.1
login name, for proxy: Section 4.16.4
login name, local, in Rlogin: Section 4.30.1
login name, local, in Windows: Section 4.30.1
login names, different: Section 2.3, Section 4.21.8
login, passwordless: Section 1.2, Section 4.22, Section 4.30.1, Section 8.2.8, Chapter 9
login scripts: Section 10.6, Question A.7.1, Question A.7.4
logo, PuTTY's: Section 2.3, Section A.5.3
-logoverwrite: Section 3.11.3.25
loopback IP address: Section 3.5, Section 4.16.2
low-numbered port: Section 3.5, Section 4.25, Section 4.30.1
-ls PSCP command-line option: Section 5.2.2.1
MAC (message authentication code): Section 4.20, Section 4.26.8, Section 10.12, Section 10.12
Mac OS: Question A.3.6
magic cookie: Section 4.24.1
mailing list: Section B.1
man pages for PuTTY tools: PuTTY User Manual
manually configuring host keys: Section 3.11.3.21, Section 4.19.3
maximise window: Section 4.7.2, Question A.6.3
maximum packet size: Section 4.26.5
mc: Section 4.6.2
-m command-line option: Section 3.11.3.6, Section 3.11.3.6
MD5 fingerprint, of SSH host key: Section 2.2, Section 4.19.3
menu, context: Section 3.1.1, Section 3.1.3, Section 4.11.1
menu, system: Section 3.1.3, Section 3.1.3.7, Section 4.9.4, Section 4.9.5, Section 4.9.7
message authentication code (MAC): Section 4.20, Section 4.26.8, Section 10.12, Section 10.12
middle mouse button: Section 3.1.1, Section 4.11.1, Section 4.11.4
Midnight Commander: Section 4.6.2
minimise window: Section 4.9.1
mistyping a password: Section 2.3
MIT-MAGIC-COOKIE-1: Section 4.24.1
modes of files, changing: Section 6.2.14
mouse: Section 3.1.1
mouse pointer: Section 3.1.1, Section 4.8.3
mouse reporting: Section 3.1.1, Section 4.6.2, Section 4.11.2
mouse, three-button: Section 3.1.1, Section 4.11.1
moving files: Section 6.2.18
MS-DOS Prompt: Section 1.1, Section 1.1, Section 3.11, Section 5.1, Section 7.1
MUDs: Section 1.1, Section 2.1, Section 4.3.9
multi-user systems: Section 3.11.2, Question A.8.2
Nagle's algorithm: Section 4.14.2
name resolution: Section 4.16.3
name resolution, with proxy: Section 4.16.2, Section 4.16.3
NAT routers: Section 10.16, Question A.7.8
NaturallySpeaking: Section 4.9.4
-nc: Section 3.11.3.14
-N command-line option: Section 3.11.3.13
negotiation, of Telnet options: Section 4.29.2
NetHack keypad mode: Section 4.4.6
Network Address Translation: Section 10.16, Question A.7.8
network connection: Section 3.5, Section 4.14, Section 4.25
network protocols: Section 1.1
NEW_ENVIRON: Section 4.29.1
new line: Section 4.3.3, Section 4.3.4, Section 4.29.4
new line, in Telnet: Section 4.29.4
‘New Session’: Section 3.1.3.3, Question A.7.22
new version, verifying: Section 3.11.3.22, Appendix F
NNTP: Section 3.7
-noagent: Section 3.11.3.9
-no-antispoof: Section 7.2.3.6
non-destructive backspace: Section 4.6.8
No Operation, Telnet special command: Section 3.1.3.2
No-op, in SSH: Section 3.1.3.2
-no-sanitise-stderr: Section 5.2.2.7, Section 6.1.4.1, Section 7.2.3.5
-no-sanitise-stdout: Section 7.2.3.5
notification area, Windows (aka system tray): Section 3.11.3.27, Section 9.1
-no-trivial-auth: Section 3.11.3.19
numeric keypad: Section 4.4.3
numeric keypad, ‘Application’ mode: Section 4.4.5, Section 4.6.1
Num Lock: Section 4.4.5, Section 4.4.6
OLD_ENVIRON: Section 4.29.1
one-time passwords: Section 4.21.5
OpenSSH: Section 3.5, Section 4.21.8, Section 4.25, Section 4.25.1, Section 4.26.6, Section 4.26.9, Section 8.2.11, Section 8.3, Section 9.4, Section 10.5, Question A.7.18, Question A.10.1
OpenSSH private key file format: Section 8.2.14
option negotiation, Telnet: Section 4.29.2
options, command-line: Section 3.11, Section 9.3
out of memory: Section 10.6, Question A.7.3, Question A.7.4
overriding host keys: Section 3.11.3.21, Section 4.19.3
packet log, SSH: Section 4.2, Section 4.2.5
Pageant: Section 3.11.3.9, Section 3.11.3.10, Section 4.21.7, Section 4.21.9, Section 4.26.13, Section 8.1, Chapter 9
palettes, logical: Section 4.13.5
passive Telnet negotiation: Section 4.29.2
passphrase: Section 8.1, Section 8.2.8, Chapter 9
passphrase hashing, for private key files: Section 8.2.12.2
passthrough printing: Section 4.3.10
password: Section 2.3, Section 3.11.3.8
password camouflage: Section 4.26.11, Section 4.26.12
password expiry: Section 4.21.6
password, for proxy: Section 4.16.4
password hashing, for private key files: Section 8.2.12.2
passwordless login: Section 1.2, Section 4.22, Section 4.30.1, Section 8.2.8, Chapter 9
password, mistyping: Section 2.3
password, one-time: Section 4.21.5
password, plain text: Section 4.16.4, Section 4.16.4
password prompt: Question A.7.12
password, storing: Question A.2.8
paste, copy and: Section 3.1.1, Section 4.6.2, Section 4.10.5, Section 4.11, Section 4.11.2, Question A.6.6
patch: Section B.2
PATH environment variable: Section 5.1, Section 6.1, Section 7.1
-P command-line option: Section 3.11.3.7
PC speaker: Section 4.5.1
Pentium 4: Question A.3.1
permissions on files, changing: Section 6.2.14
-pgpfp command-line option: Section 3.11.3.22, Appendix F
PGP key fingerprint: Section 3.11.3.22
PGP signatures, of PuTTY binaries: Appendix F
plain text password: Section 4.16.4, Section 4.16.4
Plink: Chapter 7, Chapter 7
PLINK_PROTOCOL environment variable: Section 7.2.2
POP-3: Section 3.5
port forwarding in SSH: Section 3.5, Section 3.11.3.5, Section 4.16, Section 4.17.2, Section 4.25
port forwarding in SSH, changing mid-session: Section 4.25
port number: Section 3.5, Section 3.11.3.7, Section 4.1.1, Section 4.25
port, privileged: Section 3.5, Section 4.25, Section 4.30.1
POSIX: Section 4.23.2, Section 6.2.2
PPK file: Section 3.11.3.18, Section 4.21.9, Section 8.2.9, Section 8.2.14, Section 9.1
-p PSCP command-line option: Section 5.2.2.2
preserve file attributes: Section 5.2.2.2
PRIMARY selection: Section 4.11.4
prime generation method: Section 8.2.4
primes, probable: Section 8.2.4
primes, proven: Section 8.2.4
printing, remote-controlled: Section 4.3.10
private key: Section 3.11.3.18, Section 4.21.9, Section 8.1, Chapter 9
private key file, OpenSSH: Section 8.2.14
private key file, PuTTY: Section 3.11.3.18, Section 4.21.9, Section 8.2.9, Section 8.2.14, Section 9.1
private key file, ssh.com: Section 8.2.14
private keys, generating: Section 8.2
privileged port: Section 3.5, Section 4.25, Section 4.30.1
probable primes: Section 8.2.4
process ACL (Windows): Section 3.11.3.27, Section 9.3.4
prompt: Section 2.4
protocol: Section 2.1
protocols, debugging: Section 3.7
protocols, differences between: Section 1.2
protocol selection: Section 3.11.3.2
protocol version, SSH: Section 3.11.3.16, Section 4.17.4
proven primes: Section 8.2.4
proxy authentication: Section 4.16.4
-proxycmd: Section 3.11.3.26
proxy command: Section 3.11.3.26, Section 4.16.1, Section 4.16.5
proxy DNS: Section 4.16.2, Section 4.16.3
proxy, HTTP: Section 4.16.1
proxy logging: Section 4.16.6
proxy password: Section 4.16.4
proxy server: Section 4.14.4, Section 4.16
proxy, SOCKS: Section 4.16.1
proxy, Telnet: Section 4.16.1, Section 4.16.5, Section 4.29.2
proxy user name: Section 4.16.4
PSCP: Chapter 5, Chapter 5
pseudo-terminal allocation: Section 3.11.3.12, Section 4.23.1
PSFTP: Chapter 6, Chapter 6
psusan’ program: Section 4.27
pterm: PuTTY User Manual, Question A.3.2
pty allocation: Section 3.11.3.12, Section 4.23.1
public key: Section 8.1
public-key algorithm: Section 8.1
public key authentication: Section 3.11.3.18, Section 4.21.9, Section 4.26.9, Section 5.2.4, Section 6.3, Section 7.2.2, Section 8.1
public key file, SSH-2: Section 8.2.10
public key fingerprint (SSH): Section 8.2.6, Section 9.2.1
public keys, generating: Section 8.2
punctuation: Section 4.12.1
PuTTY Event Log: Section 3.1.3.1, Section 4.2
PuTTYgen: Section 8.2
PuTTY icon: Section 2.3, Section A.5.3
putty.rnd (random seed file): Section 3.11.2, Question A.5.2
putty @sessionname: Section 3.11.3.1
PuTTYtel: Section 4.16.4
PuTTY terminal window: Section 2.3, Section 3.1.1, Section 3.1.3, Section 4.1.3, Section 4.7, Section 4.8, Section 4.9, Section 4.13.7
-pw command-line option: Section 3.11.3.8
-q PSCP command-line option: Section 5.2.2.3
QUIT, special character: Section 4.23.2
quoting, in PSFTP: Section 6.2.1
random seed file: Section 3.11.2
-raw command-line option: Section 3.11.1, Section 3.11.3.2
raw protocol: Section 2.1
‘Raw’ protocol: Section 3.7
raw TCP connections: Section 3.7
RC4: Section 4.20, Section 10.4
-R command-line option: Section 3.11.3.5
reading commands from a file: Section 3.11.3.6
read permission: Section 6.2.14
receiving files: Section 5.2.1, Section 6.2.9
rectangular selection: Section 3.1.1, Section 4.11.3
recursive: Section 5.2.2.4, Section 6.2.9, Section 6.2.10
Red Hat Linux: Question A.7.15
Re-encrypt: Section 9.5
re-encryptable: Section 9.5
registry entries, removing: Section 3.11.2
Registry (Windows): Section 2.2, Section 4.1.2, Section 4.32, Question A.5.2
remote commands: Section 2.4, Section 3.11.3.6, Section 4.17.1, Section 4.17.2, Question A.6.2
remote-controlled printing: Section 4.3.10
remote echo: Section 4.3.8
remote network connection: Section 3.11.3.14
remote shell: Section 4.17.2
remote shell, suppressing: Section 3.11.3.13
remote-to-local port forwarding: Section 3.5, Section 3.11.3.5, Section 4.25
removing directories: Section 6.2.17
removing files: Section 6.2.15
removing registry entries: Section 3.11.2
renaming files: Section 6.2.18
repeat key exchange: Section 3.1.3.2, Section 4.18.2, Section 4.26.2, Question A.7.20
Repeat key exchange, SSH special command: Section 3.1.3.2
‘Reset Terminal’: Section 3.1.3.6
resizing, terminal: Section 4.6.3, Section 4.7.1, Section 4.7.2
‘Restart Session’: Section 3.1.3.3
-restrict-acl: Section 3.11.3.27, Section 9.3.4
-restrict-putty-acl: Section 3.11.3.27
resuming file transfers: Section 6.2.12
return value: Section 5.2.3
RFC: Section 4.29.1
RGB values: Section 4.13.7
.rhosts file: Section 4.30.1
‘rhosts’ file: Section 4.30.1
Rich Text Format: Section 4.12.2
right mouse button: Section 3.1.1, Section 4.11.1
right mouse button menu: Section 3.1.1, Section 3.1.3, Section 4.11.1
right mouse button, with Ctrl: Section 3.1.1
right-to-left text: Section 4.6.11
Rijndael: Section 4.20
Rlogin: Section 2.1, Section 3.9, Section 4.30
-rlogin command-line option: Section 3.11.1, Section 3.11.3.2
Rlogin, differences from other protocols: Section 1.2
routers: Section 4.14.1
routers, NAT: Section 10.16, Question A.7.8
rows, in terminal window: Section 4.7.1
-r PSCP command-line option: Section 5.2.2.4
RSA: Section 4.19.1, Section 4.26.6, Section 4.26.13, Section 8.1, Section 8.2.2
RSA authentication: Section 3.11.3.18, Section 4.21.9, Section 4.26.9, Section 5.2.4, Section 6.3, Section 7.2.2, Section 8.1
RSA key exchange: Section 4.18.1
RTF: Section 4.12.2
Russian: Section 4.10.3
rxvt: Section 4.4.2
-sanitise-stderr: Section 5.2.2.7, Section 6.1.4.1, Section 7.2.3.5
-sanitise-stdout: Section 7.2.3.5
saved sessions, loading from command line: Section 3.11.3.1
‘Saved Sessions’ submenu: Section 3.1.3.3
saving private keys: Section 8.2.14
saving settings: Section 4.1.2
saving settings in a file: Section 4.32
SCO: Section 4.4.3
SCP protocol: Section 5.2.1, Section 5.2.2.6
-scp PSCP command-line option: Section 5.2.2.6
screen, clearing: Section 4.3.5
scripts: Section 7.3
scrollback: Section 3.1.2, Section 4.7.3
scrollback clearing: Section 4.6.7
scrollback, clearing: Section 3.1.3.6
scrollbar: Section 3.1.2, Section 4.7.3
scrolling region: Section 4.3.2
SECONDARY selection: Section 4.11.4.2
secret, shared: Section 4.18
secure shell: Section 1.2
security hazard: Section 4.6.6, Section 4.16.4, Section 5.2.1, Section 9.6
security token: Section 4.21.5
selecting a protocol: Section 1.2, Section 3.11.3.2
selecting text: Section 3.1.1, Section 4.11.1
selecting whole lines: Section 3.1.1
selecting whole words: Section 3.1.1, Section 4.12.1
selection, adjusting: Section 3.1.1, Section 4.11.1
selection, CLIPBOARD: Section 4.11.4
selection, PRIMARY: Section 4.11.4
selection, rectangular: Section 3.1.1, Section 4.11.3
selection, SECONDARY: Section 4.11.4.2
selections, multiple: Section 4.11.4
sending files: Section 5.2.1, Section 6.2.10
-sercfg command-line option: Section 3.11.3.23
Serial: Section 4.28
-serial command-line option: Section 3.11.1, Section 3.11.3.2
serial line: Section 4.1.1, Section 4.28
serial port: Section 4.28
server: Section 1.1
server, commands on: Section 2.4, Section 3.11.3.6, Section 4.17.1, Section 4.17.2, Question A.6.2
server, HTTP: Section 4.16.1
server name: Section 2.1, Section 4.1.1, Section 4.9.1, Section 4.14.4, Section 5.2.1.2
server name resolution: Section 4.16.3
server name resolution, with proxy: Section 4.16.2, Section 4.16.3
server, proxy: Section 4.14.4, Section 4.16
server, SOCKS: Section 4.16.1
service names: Section 4.25
session ID: Section 4.26.9
session log: Section 3.2
-sessionlog: Section 3.11.3.24
@sessionname command-line argument: Section 3.11.3.1
sessions, loading and storing: Section 4.1.2
session, starting: Section 2.1
set-group-ID bit: Section 6.2.14
settings, changing: Section 3.1.3.4
settings, default: Section 3.11.1, Section 4.1.2
settings, loading and storing: Section 4.1.2
set-user-ID bit: Section 6.2.14
SFTP: Section 5.2.1, Section 5.2.2.6, Chapter 6
-sftp PSCP command-line option: Section 5.2.2.6
SHA-256 fingerprint, of SSH host key: Section 2.2, Section 4.19.3
shaping, of Arabic text: Section 4.6.10
shared secret: Section 4.18
-shareexists Plink command-line option: Section 7.2.3.4
-share-plink: Section 7.2.3.3
shell account: Section 1.1
shell, remote: Section 4.17.2
shell, remote, suppressing: Section 3.11.3.13
Shift-Backspace: Section 4.4.1
Shift-Ins: Section 3.1.1, Section 4.11.4.2
Shift-PgDn: Section 3.1.2
Shift-PgUp: Section 3.1.2
shortcut, Windows: Section 3.11, Section 3.11.3.1, Section 9.3, Question A.6.4
Signal, SSH special command: Section 3.1.3.2
signature: Section 4.26.6, Section 8.1
signatures, of PuTTY binaries: Appendix F
single-DES: Section 4.20, Section 10.4
single sign-on: Section 4.15.2, Section 4.22
single-width character: Section 4.10.2
size, of font: Section 4.7.2, Section 4.8.2
size, of window: Section 4.7.1
S/Key: Section 4.21.5, Section 4.21.6
SMB: Question A.7.17
SMTP: Section 3.7
SOCKS port forwarding: Section 3.5, Section 3.11.3.5, Section 4.25
SOCKS proxy: Section 4.16.1
SO_KEEPALIVE: Section 4.14.3
sound file: Section 4.5.1
spaces in filenames: Section 6.2.1, Question A.6.9
special character: Section 4.23.2
special commands: Section 3.1.3.2
special commands, in SSH: Section 3.1.3.2
special commands, in Telnet: Section 3.1.3.2, Section 4.29.3
speed, terminal: Section 4.15.4
-s Plink command-line option: Section 7.2.3.2
spoofing: Section 2.2, Section 4.30.1, Section 8.1
SSH: Section 2.1, Section 2.2, Section 4.1.1, Section 4.17
SSH-1: Section 3.11.3.16, Section 4.17.4
SSH-2: Section 3.11.3.16, Section 4.15.5, Section 4.17.2, Section 4.17.4, Section 10.1, Section 10.3
ssh-add: Section 9.4
SSH agent forwarding: Section 3.11.3.10, Section 4.21.7, Section 9.4
ssh.com: Section 4.26.10, Section 8.2.10, Section 8.3, Section 9.4
-ssh command-line option: Section 3.11.1, Section 3.11.3.2
ssh.com private key file format: Section 8.2.14
-ssh-connection command-line option: Section 3.11.1, Section 3.11.3.2
ssh-connection protocol, bare: Section 4.27
SSH, differences from other protocols: Section 1.2
.ssh directory: Section 8.3
.ssh2 directory: Section 8.3
SSH file transfer protocol: Section 5.2.1, Section 5.2.2.6, Chapter 6
SSH host key fingerprint: Section 2.2
SSH ‘ignore’ messages: Section 3.1.3.2, Section 4.26.1, Section 4.26.11
SSH key exchange, forcing repeat: Section 3.1.3.2
ssh-keygen: Section 9.2.1
-sshlog: Section 3.11.3.24
SSH packet log: Section 4.2, Section 4.2.5
SSH port forwarding: Section 3.5, Section 3.11.3.5, Section 4.16, Section 4.17.2, Section 4.25
SSH port forwarding, changing mid-session: Section 4.25
SSH protocol version: Section 3.11.3.16, Section 4.17.4
SSH-2 public key file format: Section 8.2.10
SSH public key fingerprint: Section 8.2.6, Section 9.2.1
-sshrawlog: Section 3.11.3.24
SSH server bugs: Section 4.26
SSH special commands: Section 3.1.3.2
SSH subsystem: Section 7.2.3.2
SSH tunnelling: Section 3.5, Section 3.11.3.5, Section 4.16, Section 4.17.2, Section 4.25
SSH tunnelling, changing mid-session: Section 4.25
SSH X11 forwarding: Section 3.4, Section 3.11.3.11, Section 4.24
SSPI: Section 4.22.2
stair-stepping: Section 4.3.3
standard error, proxy: Section 4.16.6
starting a session: Section 2.1
Start Menu: Section 5.1
startup scripts: Section 10.6, Question A.7.1, Question A.7.4
statistics: Section 5.2.2.3
sticky bit: Section 6.2.14
storing passwords: Question A.2.8
storing settings: Section 4.1.2
storing settings in a file: Section 4.32
‘strong’ primes: Section 8.2.4
stty: Section 4.4.1, Section 4.23.2
subsystem, SSH: Section 7.2.3.2
Sun SSH: Section 10.5
SUPDUP: Section 2.1, Section 3.10, Section 4.31
-supdup command-line option: Section 3.11.1, Section 3.11.3.2
SUPDUP, differences from other protocols: Section 1.2
supported features: Section A.2
support requests: Section B.6
Suspend Process, Telnet special command: Section 3.1.3.2, Section 4.29.3
swap file: Section 9.6
switches, command-line: Section 3.11, Section 9.3
symmetric-key algorithm: Section 4.20
Synch, Telnet special command: Section 3.1.3.2
system colours: Section 4.13.6
SYSTEM32 directory, on Windows: Question A.7.22
system menu: Section 3.1.3, Section 3.1.3.7, Section 4.9.4, Section 4.9.5, Section 4.9.7
system tray, Windows: Section 3.11.3.27, Section 9.1
talker systems: Section 1.1, Section 4.3.9
taskbar: Section 4.5.2
taskbar notification area, Windows (aka system tray): Section 3.11.3.27, Section 9.1
-T command-line option: Section 3.11.3.12
-t command-line option: Section 3.11.3.12
TCP connections, raw: Section 3.7
TCP keepalives: Section 4.14.3
TCP_NODELAY: Section 4.14.2
TCP proxy: Section 4.16.1, Section 4.16.5, Section 4.29.2
Telnet: Section 2.1, Section 3.8, Section 4.29
-telnet command-line option: Section 3.11.1, Section 3.11.3.2
Telnet, differences from other protocols: Section 1.2
Telnet New Line: Section 4.29.4
Telnet option negotiation: Section 4.29.2
Telnet proxy: Section 4.16.1, Section 4.16.5, Section 4.29.2
Telnet special commands: Section 3.1.3.2, Section 4.29.3
Telnet URLs: Section 3.11.1
termcap: Section 4.4, Section 4.15.3
TERM environment variable: Section 4.13.2
terminal bell: Section 4.5
terminal bell, disabling: Section 4.5.1, Section 4.5.3
terminal bell overload mode: Section 4.5.3
terminal control sequences: Section 3.2, Section 4.3.1, Section 4.3.2, Section 4.3.6, Section 4.6.2, Section 4.9.1, Section 4.10.4, Section 4.11.2, Section 4.12.1, Section 4.13.1, Section 4.13.4, Section 4.15.3, Section 7.2.1
terminal emulation: Section 3.1.3.6, Section 4.3, Section 4.3.7, Section 4.4, Section 4.6, Section 4.15.3
terminal modes: Section 4.23.2
terminal, resetting: Section 3.1.3.6
terminal resizing: Section 4.6.3, Section 4.7.1, Section 4.7.2
terminal speed: Section 4.15.4
terminal type: Question A.5.1
terminal window: Section 2.3, Section 3.1.1, Section 3.1.3, Section 4.1.3, Section 4.7, Section 4.8, Section 4.9, Section 4.13.7
terminal window, inactive: Section 4.1.3
terminfo: Section 4.4, Section 4.15.3
three-button mouse: Section 3.1.1, Section 4.11.1
timeout, of connections: Section 4.14.1, Question A.7.8
timestamp: Section 5.2.2.2
TIS authentication: Section 4.21.5
token, security: Section 4.21.5
transferring files: Chapter 5, Chapter 6
triple-click: Section 3.1.1
triple-DES: Section 4.20
trn: Section 4.6.2
trusting host keys: Section 2.2
tunnelling using SSH: Section 3.5, Section 3.11.3.5, Section 4.16, Section 4.17.2, Section 4.25
tunnelling using SSH, changing mid-session: Section 4.25
typeface: Section 4.8.2, Section 4.10.4, Section 4.12.2, Section 4.13.4
UDP: Section 4.25
Unicode: Section 4.6.10, Section 4.10.1, Section 4.10.4, Section 4.10.5
uninstalling: Question A.8.2
Unix: Section 4.4, Section 4.4.1, Section 4.15.3, Section 4.23.1, Section 6.2.14, Section 10.18, Question A.7.13
Unix version of PuTTY tools: PuTTY User Manual, Question A.3.2
-unsafe PSCP command-line option: Section 5.2.1
upgraded version, verifying: Section 3.11.3.22, Appendix F
uploading files: Section 5.2.1, Section 6.2.10
Uppity: Question A.2.10
URLs, Telnet: Section 3.11.1
US-ASCII: Section 4.10.5, Question A.2.11
user name: Section 2.3, Section 3.11.3.4, Section 5.2.1.1
user name, for auto-login: Section 4.15.1
user name, for proxy: Section 4.16.4
user name, local, in Rlogin: Section 4.30.1
user name, local, in Windows: Section 4.30.1
user names, different: Section 2.3, Section 4.21.8
UTF-8: Section 4.10.1, Section 4.10.2, Section 4.23.2, Question A.7.15
variables, environment: Section 4.15.5, Section 4.29.1
-v command-line option: Section 3.11.3.3
VERASE, special character: Section 4.23.2
verbose mode: Section 3.11.3.3
verifying new versions of PuTTY: Section 3.11.3.22, Appendix F
verifying the host key: Section 2.2, Question A.2.9
version, of Internet Protocol: Section 3.11.3.17, Section 4.14.4, Section 4.25.2
version, of PuTTY: Section B.2
version, of SSH protocol: Section 3.11.3.16, Section 4.17.4
visual bell: Section 4.5.1
VQUIT, special character: Section 4.23.2
VT100+: Section 4.4.3
VT400: Section 4.4.3
vt220: Section 4.15.3
WAV file: Section 4.5.1
web browser: Section 3.5, Section 3.11.1
web server: Section 1.1
web site: Section B.7
white space: Section 4.12.1
wildcards: Section 5.2.1, Section 5.2.1.3, Section 5.2.2.6, Section 6.2.2, Section 6.2.11
WinCVS: Section 7.5
window border: Section 4.8.4
window caption: Section 4.5.2, Section 4.9.1
window, closing: Section 4.1.3, Section 4.9.2, Section 4.9.3
window, inactive: Section 4.1.3
window, maximising: Section 4.7.2, Question A.6.3
window menu: Section 3.1.3, Section 3.1.3.7, Section 4.9.4, Section 4.9.5, Section 4.9.7
window, minimising: Section 4.9.1
window resizing: Section 4.6.3, Section 4.7.1, Section 4.7.2
Windows 3.1: Question A.3.5
Windows, 32-bit: Question A.6.10
Windows, 64-bit: Question A.6.10, Question A.7.22
Windows clipboard: Section 3.1.1
Windows command: Section 6.2.19
Windows Default Beep sound: Section 4.5
Windows file sharing: Question A.7.17
window size: Section 4.7.1
Windows process ACL: Section 3.11.3.27, Section 9.3.4
Windows Registry: Section 2.2, Section 4.1.2, Section 4.32, Question A.5.2
Windows shortcut: Section 3.11, Section 3.11.3.1, Section 9.3, Question A.6.4
Windows Terminal Services: Question A.7.17
Windows XP: Question A.7.17, Question A.7.21
window, terminal: Section 2.3, Section 3.1.1, Section 3.1.3, Section 4.1.3, Section 4.7, Section 4.8, Section 4.9, Section 4.13.7
window title: Section 4.6.5, Section 4.6.6, Section 4.9.1, Question A.7.11
Win32s: Question A.3.5
Win125x: Section 4.10.1
words, selecting: Section 3.1.1, Section 4.12.1
working directory: Section 6.2.7, Section 6.2.8
wrapping, automatic: Section 4.3.1, Question A.7.7
wrapping, terminal: Section 4.10.2
write permission: Section 6.2.14
X11 authentication: Section 4.24.1
x86 (32-bit processor architecture): Question A.6.10
-X command-line option: Section 3.11.3.11
-x command-line option: Section 3.11.3.11
‘X display location’: Section 3.4
XDM-AUTHORIZATION-1: Section 4.24.1
X11 forwarding: Section 3.4, Section 3.11.3.11, Section 4.24
X server: Section 3.4
xterm: Section 3.1.1, Section 4.4.2, Section 4.4.3, Section 4.11.1, Section 4.15.3
xterm mouse reporting: Section 3.1.1, Section 4.6.2, Section 4.11.2


If you want to provide feedback on this manual or on the PuTTY tools themselves, see the Feedback page.

[PuTTY release 0.76]
putty-0.76/doc/putty.chm0000644000175000017500000117364614072266315012227 00000000000000ITSF`4Vx ý|ª{О  É"æìý|ª{О  É"æì`xTPÌPþ¦÷ITSPT ÿÿÿÿ j’].!Ðù É"æìTÿÿÿÿÿÿÿÿÿÿÿÿPMGL<ÿÿÿÿ//#IDXHDR„÷W /#ITBITS /#STRINGS…—W–./#SYSTEM¡4/#TOCIDXÉÁIÅ0/#TOPICSˆyÀ0/#URLSTRË÷Q¹€/#URLTBLËÇ)°( /#WINDOWSÉ¿}L/chm.css¸ÿ~/config-address-family.html‘âiŒA/config-altf4.htmlŽÆ †y/config-altonly.htmlŽÖ†A/config-altspace.htmlŽÍ‰/config-alwaysontop.htmlŽÜG…-/config-ansicolour.htmlâ\‡//config-answerback.htmlŠñ@Œk/config-appcursor.html‹à^‰L/config-appearance.htmlø$ˆ,/config-appkeypad.html‹ê*Œ/config-autowrap.htmlŠ«0ŒG/config-backspace.html‹¶~/config-behaviour.htmlŽ£Š?/config-bell.htmlŒ˜‰j/config-bellovl.htmlŒ¼ ’E/config-bellstyle.htmlŒ¢ŽT/config-belltaskbar.htmlŒ°\‹0/config-blink.htmlŠç"Š/config-boldcolour.htmlü?ŒG/config-charclasses.html½5’!/config-charset.htmlŽô/“_/config-cjk-ambig-wide.htmlˆ‹/config-clipboards.htmlý|/config-closeonexit.html‰µ‹>/config-colourcfg.html‘™S$/config-colours.html×vŠf/config-command.html“ć$/config-compose.htmlŒ‚‰Y/config-connection.html‘¨w‰/config-crlf.htmlŠÇ‹/config-ctrlalt.htmlŒ‹sŒ+/config-cursor.htmlŽ€P‡E/config-cyr.html“‡P/config-data.html’ˆ{Š/config-decom.htmlŠ·w/config-environ.html’¿^Œ/config-erase.htmlŠÚ/config-erasetoscrollback.htmlï‰/config-features-altscreen.htmlŒùE‰H!/config-features-application.htmlŒÞoˆ/config-features-bidi.html½'‹V/config-features-charset.html©2ˆ!/config-features-clearscroll.html›-†? /config-features-dbackspace.html¡l‡F/config-features-mouse.htmlŒçŒ /config-features-qtitle.html‰A‘l/config-features-resize.htmlŒó †;/config-features-retitle.htmlƒ †4/config-features-shaping.html²1Šv/config-features.htmlŒÎQ/config-file.html›Bx/config-font.htmlŽˆ‰A/config-fullscreen.htmlŽát‡Z/config-funkeys.html‹Ï-‘1/config-homeend.html‹Æ‰/config-hostname.html‰‚1–/config-keepalive.html‘² ›1/config-keyboard.html‹§w/config-lfcr.htmlŠÒˆ /config-linedraw.htmlš`“/config-linedrawpaste.html­aŒ{/config-localecho.htmlŠþ+Š/config-localedit.html‹ˆ+/config-logfileexists.html‰íˆ;/config-logfilename.html‰Ýn-/config-logflush.html‰õV‰L/config-logging.html‰ÀM!/config-logheader.html‰ÿ"†3/config-loghost.html‘ï*™Q/config-logpalette.html‘‰‡W/config-logssh-omitdata.htmlŠ˜†_/config-logssh-omitpw.htmlŠŽk‰3/config-logssh.htmlŠ…U‰/config-mouse.htmlÕdŽL/config-mouseptr.htmlŽ‘V‡Q/config-mouseshift.htmlä0Ž/config-nethack.html‹÷)Šq/config-nodelay.html‘Í<ˆ)/config-oldenviron.htmlš˜"d/config-paste-ctrl-char.html¬Šn/config-printing.html‹—EB/config-proxy-auth.html“FŽ/config-proxy-command.html“W’C/config-proxy-dns.html’ÿ; /config-proxy-exclude.html’í’"/config-proxy-logging.html“°‹/config-proxy-type.html’ÙA“X/config-proxy.html’ËmT/config-psusan.html™½3—X/config-ptelnet.htmlš¦Œ./config-rectselect.htmlòFŠM/config-rlogin-localuser.htmlšÊQ‘z/config-rlogin.htmlšÄi…h/config-rtfcopy.htmlÏVˆ /config-saving.html‰˜6œY/config-scrollback.htmlâ}Œ/config-selection-autocopy.htmlŽŠ"/config-selection-clipactions.html˜“w/config-selection-copy.html¶t†A/config-selection.htmlÊ\‹/config-serial-databits.html™ñ…i/config-serial-flow.htmlš†:‰'/config-serial-line.html™ß1ˆn/config-serial-parity.html™üI‰q/config-serial-speed.html™èˆs/config-serial-stopbits.html™ö{…N/config-serial.html™Õ Š&/config-session.htmlˆúf‡K/config-ssh-agentfwd.html–“XˆD'/config-ssh-auth-gssapi-delegation.html–É?-&/config-ssh-auth-gssapi-libraries.html–Øl</config-ssh-auth-gssapi.html–¶“&/config-ssh-auth.html•º/Œ~/config-ssh-banner.html•Ç-‰/config-ssh-bug-chanreq.html˜ÐŽ/config-ssh-bug-derivekey2.html™‰</config-ssh-bug-hmac2.html˜ü‰8/config-ssh-bug-ignore1.html™™KŒ[/config-ssh-bug-ignore2.html˜«rŠY/config-ssh-bug-maxpkt2.html˜Þ‰`/config-ssh-bug-oldgex2.html˜òW‰?/config-ssh-bug-pksessid2.html™…NŠA/config-ssh-bug-plainpw1.html™¦&{/config-ssh-bug-rekey.html˜¶K‹/config-ssh-bug-rsa1.html™´!‰/config-ssh-bug-sig.html˜çrŠeqÁY Á " y ç T ¹ + –pèc³÷_ãW¾0¥Ž@‰PMGL</config-ssh-bug-winadj.html˜ÁYŽ7/config-ssh-bugs.html˜‘gš /config-ssh-changeuser.html–œŒ7/config-ssh-comp.html“ׇ/config-ssh-encryption.html•¤–)/config-ssh-gssapi-kex.html”ª2“2/config-ssh-hostkey-order.html”ê’/config-ssh-hostkey.html”ßDŠW$/config-ssh-kex-manual-hostkeys.html•ˆ ›}/config-ssh-kex-order.html”—$“/config-ssh-kex-rekey.html”½d¡`/config-ssh-kex.html”‰Ž/config-ssh-ki.html–Š=‰/config-ssh-noauth.html•Ð5ŽP/config-ssh-noshell.html“ËB‹D/config-ssh-notrivialauth.html•ß•j'/config-ssh-portfwd-address-family.html˜ƒŽc"/config-ssh-portfwd-localhost.html—÷‹q/config-ssh-portfwd.html—Í©z&/config-ssh-prefer-known-hostkeys.html”ü2‹W/config-ssh-privkey.html–¨SF/config-ssh-prot.html“ÞŒ"/config-ssh-pty.html–îD‰;/config-ssh-sharing.html“ê9žf/config-ssh-tis.html•þg‹V/config-ssh-tryagent.html•ôo‰x/config-ssh-tty.html–è(†/config-ssh-x11.html—¡‹M/config-ssh-x11auth.html—¬^•K/config-ssh-xauthority.html—Â)Šp/config-ssh.html“»)ˆu/config-supdup.htmlšÜKˆR/config-syscolour.html‘]ˆv/config-tcp-keepalives.html‘Õe/config-telnet.htmlšaˆA/config-telnetkey.htmlš²4ˆ9/config-telnetnl.htmlšºm‰|/config-terminal.htmlŠž}Œ3/config-termspeed.html’³XŒ/config-termtype.html’¥ŽT/config-title.htmlŽ­V_/config-translation.htmlŽéNŠa/config-truecolour.htmlö †6/config-ttymodes.html–÷©/config-username-from-env.html’™R‹2/config-username.html’“ †E/config-utf8linedraw.htmlº\/config-warnonclose.html޾5‡V/config-winborder.htmlŽ™'‰p/config-window.htmlÈ}ˆ/config-winsize.htmlÐ~†5/config-winsizelock.html×3‹J/config-xtermcolour.htmlê ‹~ /config.html‡êK /contents.hhc¹†Ž /errors-access-denied.html¦Ü‹ /errors-cannotassignaddress.html§ÇJ‡!/errors-cant-load-key.html¦Àb=/errors-cipher-warning.html¦’F‰/errors-connaborted.html§˜>./errors-connrefused.html§³'‡^/errors-connreset.html§§l‹;/errors-conntimedout.html§»ŒE/errors-crc.html¦î.ŽZ/errors-garbled.html¦ý‰f/errors-hostkey-absent.html¥í_Œn/errors-hostkey-wrong.html¥úM‹c/errors-internal.html¦¸ˆ`/errors-memory.html¦¨&\/errors-no-auth.html¦ç‡/errors-refused.html¦Îa/errors-ssh-protocol.html¦†0Œ/errors-toomanyauth.html¦›`ŒF/errors-x11-proxy.html§†n‘P /errors.html¥ÑOœ/faq-32bit-64bit.html«ÖeŒt/faq-admin.html®½"”v/faq-alternate-localhost.html­Àg‹ /faq-bce.html¬»Š/faq-checksums.html°­p’i/faq-cleanup.html®œ#ŒC/faq-colours.html¬ŒŒ7/faq-commands.html«„B‘7/faq-connaborted.html­Ö'ˆ/faq-cutpaste.html«©\Œ/faq-details.htmlª²i‡</faq-disksettings.html¨ó…l /faq-dll.htmlª™~ˆ,/faq-domain.html®Ú"Š{/faq-donations.html¯˜+ /faq-dsa.html®¨fŒ-/faq-embedding.htmlª“†p/faq-epoc.htmlª‚d‡j/faq-export-cert.html°‡Šq/faq-fullscreen.html¨ø}… /faq-hostkeys.html©‹–r/faq-howto.htmlªí%G/faq-idleout.html¬Ï@Œ/faq-indemnity.html¯®@/faq-intro.html¨ªq„5 /faq-ipc.htmlªª|‡m/faq-iphone.htmlªŠNˆ@/faq-iutf8.html­ý5/faq-keyboard.html­†{“J/faq-link.html®ê ’u/faq-localecho.html¨á7Š\/faq-login.htmlªúl‰V/faq-mac-port.html©ø)Š;/faq-mailinglist1.html¯‡OˆC/faq-mailinglist2.html¯ˆ /faq-meaning.html°Õe‡ /faq-misc.html°ÀY‡/faq-missing-slash.html­ËgŠ@/faq-openssh.html°Çx†b/faq-options.html«µa‰:/faq-outofmem.html¬˜Ht/faq-outofmem2.html¬¦<^/faq-password-fails.html¬þvˆ/faq-password-remember.html¨þ /faq-permission-assurance.html¯ñD–;/faq-permission-form.html¯Ì?ŽP/faq-permission-future.html¯ÛŠz/faq-permission-general.html¯æ ‹;/faq-permission.html¯§I†w/faq-plink-pause.htmlªÝ\I/faq-ports-general.html©Áw/faq-ports.html©´@7/faq-pronounce.html°Üq…/faq-pscp-ascii.html©¬g‡Y/faq-pscp-protocol.html¬>ŠS/faq-pscp-spaces.html«Çe/faq-pscp.html«¿‡e/faq-psftp-slow.html¬´†f/faq-publicpc.html®’U‰N/faq-putty-org.html®Òˆ /faq-puttyputty.html¬ê,‹>/faq-rekey.html­ÞCŠI/faq-resetterm.html¬ÅŠ//faq-rh8-utf8.html­£:‹l/faq-savedsettings.html¨ì†~/faq-screen.html­¯&‘A/faq-secure.html®ŠHˆ ‚ú ^ Å J Ç X á g í o ëaÍ?•ê\Ò;«ãPžŽPMGL@/faq-server.html©¢Šd/faq-settings.htmlªÃv /faq-sillyputty.html°ÎZ‡ /faq-sourceforge.html®ýŠM/faq-ssh1.html¨ÚC†t/faq-ssh2-keyfmt.html¨Ó!‡"/faq-ssh2.html¨ÍX…I/faq-ssh2key-ssh1conn.html­šEˆu/faq-startmax.html«•y…&/faq-startsess.html«›‡w/faq-startssh.html«£†F/faq-support.html¨¸w”a/faq-system32.html­ðfŒ-/faq-term.htmlªº%‰Q/faq-timeout.html¬Û@Žl/faq-trouble.html«ãYe/faq-trust-sigils.htmlªÑŒY/faq-unix-why.html©Ü8Œd/faq-unix.html©Ñ‹# /faq-vb.htmlª¢*ˆR/faq-vendor.html°’p›/faq-virtuallock.html®µˆ/faq-webhosting.html®å„p/faq-what.html¨¯&‰Q/faq-win31.html©ît‰5/faq-wince.html©é…X/faq-wintitle.html¬õj‰ /faq-xpwontrun.html­é ‡Z /faq.html§ÎkÜ/feedback-address.html²ÌS…/feedback-bugs.html±§P«\/feedback-compliments.html²ÄˆL/feedback-feature-priority.html±ó‘/feedback-features.html±Ü —/feedback-general.html°ò>/feedback-largefiles.html±‚Y›/feedback-mirrors.html²µzŽ /feedback-other-fora.html±l‰d/feedback-permission.html²¦u/feedback-support.html²„1“}/feedback-vulns.html±Ó,ˆa/feedback-webadmin.html²˜.W/feedback.html°á?/gs-hostkey.html»H¤j/gs-insecure.html¦•1/gs-login.htmlà2“"/gs-logout.htmlúT‰9/gs-session.htmlóT‡/gs.htmlžD‡S /index.hhk¿аe /index.htmlð /intro.htmlð† /licence.html´“/pageant-cmdline-command.html¤ðpˆ/pageant-cmdline-keylist.html¤ù…@/pageant-cmdline-loadkey.html¤ç>‰2"/pageant-cmdline-restrict-acl.html¤þA‰/pageant-cmdline.html¤Ü!‹!/pageant-deferred-decryption.html¥ •h/pageant-forward.html¥‡T˜J/pageant-mainwin-addkey.html¤É‹_/pageant-mainwin-keylist.html¤·!‘_/pageant-mainwin-remkey.html¤Ô_‡B/pageant-mainwin.html¤®<ˆe/pageant-security.html¥¶›I/pageant-start.html¤˜–+ /pageant.html¤ˆu/pgpkeys-contact.html¸ƒZ…;/pgpkeys-master.html¸‰ŠI/pgpkeys-pubkey.html·Ðy–/pgpkeys-release.html·ý†;/pgpkeys-rollover.html¸“^ªF/pgpkeys-security.html·ç‡o/pgpkeys-snapshot.html·î~Ž! /pgpkeys.html·¾i’/plink-batch.html¡¥(Š/plink-cvs.html¡¯C‰u/plink-option-antispoof.html¡q”7/plink-option-batch.html Ö4ˆR/plink-option-s.html ß†T/plink-option-sanitise.html ü^”/plink-option-share.html åZ‹e/plink-option-shareexists.html ñ?‹/plink-options.html ÉTŒ`/plink-starting.htmlŸècŒH/plink-usage-batch.html ª|žX/plink-usage-interactive.html ˜,’P/plink-usage.htmlŸõ+£/plink-wincvs.html¡¹8r /plink.htmlŸÜ)Œ:/ppk-keys.html³Õ” /ppk-old.html³é#…/ppk-outer.html²õ^±A/ppk-overview.html²à•Z/ppk-privkey-dsa.html³¼6‡]/ppk-privkey-ecdsa.html³ÄˆY/ppk-privkey-eddsa.html³Ìlˆ1/ppk-privkey-rsa.html³²b‰T/ppk-privkeys.html³§‹C /ppk-v1.html³þK”G /ppk-v2.html³î6 /ppk.html²ÑWŽ-/pscp-option-sanitise.htmlœÒ ˆI/pscp-pubkey.htmlœá]/pscp-retval.htmlœÚR†A/pscp-starting.html›Œ0/pscp-usage-basics-host.html›ð6…f/pscp-usage-basics-source.html›ö‰i/pscp-usage-basics-target.htmlœ€ˆh/pscp-usage-basics-user.html›ê.†/pscp-usage-basics.html›ÈU¡Y /pscp-usage-options-backend.htmlœ¿;’N/pscp-usage-options-batch.htmlœ¶fˆU/pscp-usage-options-ls.htmlœ–t‰/pscp-usage-options-p.htmlœ †/pscp-usage-options-q.htmlœ¦"‰/pscp-usage-options-r.htmlœ¯2‡4/pscp-usage-options.htmlœˆmŽ/pscp-usage.html›©CŸ /pscp.html›’:ŠY/psftp-cmd-cd.htmlž¶.Š /psftp-cmd-chmod.htmlŸƒnA/psftp-cmd-close.htmlž©]† /psftp-cmd-del.htmlŸ¡/ˆ,/psftp-cmd-dir.htmlžû)ˆE/psftp-cmd-get.htmlžÊŒ8/psftp-cmd-help.htmlž¯f†H/psftp-cmd-lcd.htmlžÀN‰2/psftp-cmd-mgetput.htmlžâPŒ/psftp-cmd-mkdir.htmlŸ©[†W/psftp-cmd-mv.htmlŸ¸<‰\/psftp-cmd-open.htmlž˜Š>/psftp-cmd-pling.htmlŸÂŠ/psftp-cmd-put.htmlžÖ8Œ/psftp-cmd-quit.htmlž¢Q‡ /psftp-cmd-regetput.htmlžîSŒV/psftp-cmd-rmdir.htmlŸ°2ˆ /psftp-commands.htmlÝQ—`/psftp-option-b.html¨N/psftp-option-bc.htmlµgŽM/psftp-option-be.htmlÄ4†r/psftp-option-sanitise.htmlÕˆL/psftp-pubkey.htmlŸÌ/psftp-quoting.htmlõ1‘h/psftp-starting.html“}”Q/psftp-usage-options-batch.htmlË&‰_/psftp-wildcards.htmlž‡zjÞQÌ C ¾ 5 ˆ Þ j æ r íRÇ@ÀpóyÝgîx|“PMGL:ÿÿÿÿ /psftp.htmlœñp¢ /pubkey-gettingready.html£ëzœ"/pubkey-intro.html¡ØY /pubkey-puttygen.html¡øn”e /pubkey.html¡Ç*‘//puttygen-comment.html¢æ0Š/puttygen-conversions.html£ÕA–9/puttygen-fingerprint.html¢Ø>r/puttygen-generate.html¢Ê;Ž/puttygen-generating.html¢S’"/puttygen-keytype.html¢Ÿu‹L/puttygen-load.html£Ê8‹ /puttygen-passphrase.html¢ñ/–r/puttygen-pastekey.html£ VŠr/puttygen-primes.html¢µ(•/puttygen-save-params.html£«Hˆ\&/puttygen-save-passphrase-hashing.html£¼Ž4/puttygen-save-ppk-version.html£´$‡`/puttygen-savepriv.html£ˆ!‹/puttygen-savepub.html£“1%/puttygen-strength.html¢«A‰g/reset-terminal.htmlƒ®h‹g/sshnames-agent.html¸æG˜I/sshnames-channel.html¸ÇŒ\/sshnames-encrypt.html¸ßz†M/sshnames-kex.html¸ÓzŒ/sshnames.html¸¾$ˆz/supdup-ascii.htmlšë~Š[/supdup-location.htmlšå†a/supdup-more.htmlšöY…+/supdup-scroll.htmlšü…>/udp-640x480.htmlµÖ‰k/udp-compile-once.html·¡u”9/udp-globals.html´ïY/udp-keystrokes.htmlµÊ‹k/udp-makefiles-auto.htmlµßk”]/udp-multi-backend.html´áCO/udp-multi-compiler.htmlµ“s‘V/udp-perfection.html·¶.ˆ;/udp-portability.html´²q®R/udp-pure-c.html´ükŽL/udp-security.htmlµ‹7ˆ</udp-single-threaded.htmlµ¶O“F/udp-small.htmlµ¥I‘/udp-ssh-coroutines.htmlµôH“E/udp-traits.html¶ˆ ™h /udp.html´£#N/using-changesettings.htmlƒžŠX/using-cleanup.html…šuˆ?/using-cmdline-agent.html†§‹/using-cmdline-agentauth.html†ž ‰/using-cmdline-compress.html†å`‡,/using-cmdline-hostkey.html‡™ ‰/using-cmdline-identity.html†þ'‹ /using-cmdline-ipversion.html†õ:ˆm/using-cmdline-l.html…è1‡4/using-cmdline-load.html…Æ4Œ!/using-cmdline-logfileexists.html‡Â`‡{/using-cmdline-loghost.html‡mˆ/using-cmdline-m.html†€L‹A/using-cmdline-ncmode.html†Ï–C#/using-cmdline-no-trivial-auth.html‡‰1‡</using-cmdline-noshell.html†ÄKŠR/using-cmdline-p.html†Œ ‰,/using-cmdline-pgpfp.html‡¢(†z/using-cmdline-portfwd.html…ïeg/using-cmdline-protocol.html…ÒEŽ/using-cmdline-proxycmd.html‡Ê[‰4/using-cmdline-pty.html†»2‰/using-cmdline-pw.html†•9ˆT /using-cmdline-restrict-acl.html‡Ô– /using-cmdline-sercfg.html‡©"s/using-cmdline-session.html…‹Z/using-cmdline-sshlog.html‡·‹K/using-cmdline-sshprot.html†í ˆ./using-cmdline-v.html…áD†m/using-cmdline-x11.html†²#‰/using-cmdline.html„ç¤W/using-copyall.htmlƒ¨v…r/using-eventlog.html‚æUŠ /using-fullscreen.htmlƒºO‰ /using-general-opts.html…£4£/using-logging.htmlƒÃZ‹/using-newsession.htmlƒ’U‹I/using-port-forwarding.htmlƒî´y/using-rawprot.html„µ‘$/using-rlogin.html„ω=/using-scrollback.html‚ÌLŒ9/using-selection.html‚¡uªW/using-serial.html„£ ’/using-session.html‚•ŒV/using-specials.html‚ð_¡v/using-supdup.html„ØYŽ*/using-sysmenu.html‚ÙP/using-telnet.html„Æ?ˆ]/using-translation.htmlƒÎxŠl/using-x-forwarding.htmlƒÙd”/ /using.html‚„ ‘/which-one.htmlŽ]g/you-what.htmlö˜A::DataSpace/NameList¡4<(::DataSpace/Storage/MSCompressed/Content¡p’¦X,::DataSpace/Storage/MSCompressed/ControlData’Èv)::DataSpace/Storage/MSCompressed/SpanInfo’Èn/::DataSpace/Storage/MSCompressed/Transform/List’ÈH&i::DataSpace/Storage/MSCompressed/Transform/{7FC28940-9D31-11D0-9B27-00A0C91E9C7C}/InstanceData/ResetTable’É„HB d ë j å P ÇwÎ-}úuâ`åW¨zjPMGI¹//config-ssh-bug-winadj.html/faq-server.html /psftp.html Halibut, NOVERSION PuTTY User Manual index.html contents.hhc index.hhkmain$ D3"ˆwfU · þ³×ÿ T#SMxV4ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ Uncompressed MSCompressedVCD6#oet™á>W—V‰ B5BÁy»g¥hõnŸï¹r„’;N0«êÿÓ˜!*™€ƒ{,‹1Ï>‘³¼%~âx^<ïçMæ…Áóî(fîõ$êEomĪ“Fý *JI‚RµU’Ž@«T•$@[¿ê­Êu Ä_Úª@*I$!׳»`ØhFʈ ÞËœ÷¿çßey¹ÝžËÝ ©APÔL@Ä46jn¨¦‘ÿ#=ïù7ÐÏ$ÂM‰$ß’g‚1wí;&oëîÎwoÞû¶¹‰F€ö¦$ã#gæ~f'7 >~k>uŒ®QH¥Iãchèchd¨‡£!î|=öI!“E$¢T@rƵmßÒ÷ÂÐþCC) (Ü|?xNï%©q€ Šb¡q0ÿ€êFüxª@í D^ú¦ôpä¼ÜÄ5ÝÊC¡oÛÎûÎß@Ü2„…p–ÞøXºQŸ‹FêJ ÀÈ $^4`¤ƒñ@ÙÔ¤Œ$v¥J†Ã@Ê£ú² AŸ$§K$ð´´dÖèðVT}´ÄUÆðYºÛ6ñ] Ñ@@Dƒa¹_›(<ˆìÐì2èIbèŒ@ dœœÀõQ€‡ŽNê%6¨§Ž:^$öøâ"œt€‚©AÆ„rB„g7&¾‘êñûÎmíæ_0Âzœ£rh tíz´nÁ’Û¥‡n0(8Ã7¿ z‰Ä–•á ©9U*&€D ¡À'/*”à:Åê($š¢ŠýzÄD¡'pJWL™#äà«À?CSC†ÂCÁ8ûÄSºðC¹§¥°Œ@¶$„K³2¡Û>Ï^ûÿ*@Ó$"" “ ­”Ã!€’'†¹Î¨ÀÖqáü³d ¡l ˜KÈ-nú+/¬ø²C*Ÿqdäå]¡FºÛŸ ;sWZºVªÈ­mQU ^OÒ˜4"Á*¼vôV(Á’ÄïÈ&„HH×ÉV«@•0ã«hø¦@PÒˆMû¡¤é œà/³1s¡–8Šâ*ÅqEchížë”jƒ¬‚=%e¢Õ6 FBÆ¢ÄÖ­`!èü\ëB߆-ܪ¼ fÌ$ØƒÉ ɔњ¸@œŠ-ŸÚ´ ÕIê·®³N–ìЛ(ÆbÃße1afV €tT~Bjíj]t ¨çôU®äüp+uÁTwìØ)Æàt÷Ac½btq 9[Þ¾ïLŸ·^ö£RKTã)@ïòÆ"™ìƒ©×\…øR_eH„"ƒ¥UMõŒ–°ži##*2XŠ`d $)ˆ-t…Y$´ žd›sOßYÊñœ`5¦Â3!l“]ûô~H¦%9†O° ¦ZÁCùD )Jgˆ¯£àk€GÚ…í6KåÅI‰r{Ü@†(®eä+}¹ýd0¹¢KJ¯|)FǧÃîÇHil±…©Õá¼&'{äHŒ¨«”SÖ1)îêÇéf%LY)¦Èì/ïOÉ—×YSÁè ›]h´BÍ ˆHn¿ J‰!äXeílJõÊ*–^‚ý[A_ó€ãaӈǑº¿ÌEÿ±ÖÛèõ:ev2>ïÙùHó¾L$’n1%“Z˜÷é>5/Q8ô‹ó³Lû5íÇæwp8޾#fQV ¿>ýpì®ÇÇìËË«eÑ÷«ÚÏ^ð++H»"„,‚©ûI^ž¡/Àë7ÕGüxíŸ÷YQ× )_$~¼}×\Ãa_ô1–ß¿Lýhß®äî6÷Ê$/R?×±k¸ë‘‰¼/ (dµLHÖÛ¯à‘LÔ™_lx¥œ<…ýŠY¿Aïdä’ aº/ÚÖúè“Óû’²ÉÅûVÕJY9ý½Vs~‘oßÏ+¢$óxØw<áBEøa¯H‘ÌNàb§*(ßÉ3²Ñ5ô+x¤~Ã÷LC/êö–%Bò’S¿ GŽý‡¿"geÛµ?áŸ8X5¬/6ÅÛM™¼©Óä Õó!nÀ#¿œÛÆu›šQº‡[ðHZ0ùÂ× šj-óäÀ¸Ú,fÞf”fàVW2L}!<·¡{²´ÿeR¦.ÜÑŸ¥â#n„à7ɰÕâ½MLŒJ³“Kv±')Çë|±uàÓbB_¨¨¶©gx®\œ¥#ž;Ãv±Ù«7Ú¯kœÑžšý\ìª2}ß÷®òè¾"öÇGTDeB^ï ª×\N·—¢²`ޱ+˜Š¸Û·²g™­L>ÖöÅd"Æó®ÄɧäÉŒž§ˆxß“–±·E©Þ8_.„Y´gýû(e§Œ„¹œ&$ðÂpWqqúë&Ì[|9ƒnO‰Reù„ULR¶J¾ñŸÈÏóãÁÀƒ” ¯D)E?²»-dï(be‰bM/  7þ%’xÇŽÉ `ÇÇDœåC/Ï7dK¥±v”O77u‹Ò-†ƒÂe³1hZåìPX—ܥьÁÂáŸ6އžQgs-gÓMÃ’ÃçÊC€”€Ypþr2Hý–ÍÈÑú.§QÝnœ rl× ñè#àÆÃí‡ÙTÚœ)!öìÙÀb˜·~|Ú´;¢C4oTVo-`Ü9ô&Ú¿Y:S»ùä“U§œ„’Ô „-iË_‹itâæ1(B¼÷ èJÛû\L–ƒ¯ó ÝÛ„ Á6YB”†”9{„Ü6òïCÊfÚø:[í€L-Ÿq›øÊ&>ˆº‘%…ީ⋆È߉…i©¨6·4>zÒHÁMG°M.½a«™ñŒyä)õᤤ”B¹Š×ÒÏ|{øáa÷zx%!ÂÄìÒÕü¤ïƒïË/›•±¿µfùñ݃U´¤;6I Âûü-W1BY@[uXQ*ÂÚj?`ZÙqh´žÕ0Â{s¡Ìý”€Þ¡ÁÝ|É‘2lH:-öä?{; $¥‚ëJ|$“Á›íìgwqó2‡ üGH*žð ™ûc€È?L¢hoU³èª"Â%°¬zëJ~’¥—ÓÞ›ah}æPÝÇÖSOÏaj u>€À`ÎßÏïA%• ¨ ‘Éé÷¸&¬(áílœî)šÄ[•¡ªótWT‘#Î?¬SSD>>ÿK¦ÝB>Õ„F ° u²?¿©+GÇwÓË`Š"ÃݧR&?ý?u)?ÒS½ŒU¨(¡D€¡eu.B§¢uöŸ<üÇŸ­ÚS !Ýú F‹H0«PM<ÅJ„ÓÓþ‡ƒÁÉØ‘m2ùõU+ÍP*¾Dó÷/¯/—¶$ÐyéT\Õ›þ—o㸆íÖIÝwœj#G—EùƒíºX1œê*Œñ¦”Ëûü½ëL—¿Õpƒ)ðyÕûðèò”¦ йŽ0 õ¥Ö¼$ä(XÛ)m w˜z¿&ësª"œ{:È轆.ð *K£L0eü§ÀfOßùÕJBš=HK æª0õ`~÷îÄ ì„֦’ ¯fe¬]Î"Áý¦~KÝÂÝy§Ý#Qˆ¸ÜeJÉn«dÁ J!Hì÷R%½K0?üV2€©úx+±ˆ_ Ï™?*ïKw!–)¡††¨yºg·5[¢UyÔ> ¯=«:ëHö²Wý{`7»Àc{¾- û§~o[áØõËÛËYíåŽíÁ”é½V‘|±@IQœ%‹dbî§j¬@ªJ{~ä=áK˜ûÂc{:…v^Ò@ öHA… &û¾Öî CÖ¶·S>·-E7üö j°Èæ¸xZÌ0ZRž2{{ÕeN0g|¥–}\¡ƒymq™íÁìŒ=„µvÁ˲—»a6ÝeqíiíÁÔL.Ã2'¤Ÿ.«D)9Zºˆ±bSñ›t¬îÓQPBðe+¬vuHÀèH÷ï/BOП™ŸD‹ÂVá!ѹÛý¶ðTO¾HÃ[.“É»}3aJÇ«ùy–3[Ñ¡ÔÊêüžPÉí贶ŘR7Wµw^`ÞÏ-€[Ì)^Ô Ÿ‚ÑPŒ ÷zW‹˜„—àw®oeû:4¹¦÷nQFhÆÀ‡xêi—v–¦u!´Xp‹yEhn0…ÅÑV™)ôõ uk+¨•´Á™7""½ªèÄNs¼\!××|‘¸r]¦‰ÍömJ©Fävº^¬f©î}ÁÝÁ[;À¹&R‘U’çéº~„]ƒ ÙuYÍSñçéâ5—¤ügÍ ›ËLsp½pFÛó(ÁI@«2esÍÍÓÀý%¿æ0a–ajr$hfÛšXÆœ¥™Á¿šN"´Skăçø1ÿ=#S:jÐÐL8 ؆AZx,Ô ã‰ §ŒgÚÓ4¦cÞBxžÄyÎ¹ß ¤ïnÕ(¡QM}Ê4‚©áslõP=à?6ÃÏ“rž¡ÀzBìM“j1xp‘2žÆç¥h ìæûT3aišS2†aÇRÙâ(èè|a¿§]¦$2µð;“²ßìÉäIú²¿áüí'2;ö0ZÑ}Vá·޵m‰@ì 2<|¶AôRp]~‚值Coîb /vÿC<÷g ­€åNçz3¯™bÕôp{=¦ã÷;œl¦Ñ‹Fu×E!Û§<¬³ö[ª 0Ÿ"{Žñ‹p˜Õ~¶ªHsjnb D4Iøçûßl·Íc¸v9os䤿./Ê`á–Š©#ÓÜà€ïƒ™eµÄáW¸(ýÈe¹í‘Ël¦F©þ½ŠûêÎÏØnžýXŸËVø¡ymÞuˆ“¨V}Ï–VÁ±ØBùG2U/‰¥ÙM1!W¤÷Å÷Á©€ ïߢ,»µäÖt&‘×õÔzÖK}•dóÉt›¸zÅ÷ï4 ÕMËè±ã;úRºÕzTë;âd‰/ñp—~j–u^ ¤f!Z?:¾€÷ÐôÞyà1}Ùž¥Ã+‰¦ ¨ òÜè©y—yÚÀn±Ê&—`xË„®ÝÇ,õö¾GHÈrq(U‚¿2† ù÷-e ºÐÛ.à:­øÌ‹Â¾Xû@»ÿ1ôišÞœhÜ Ëømž˜:{q&æ77¯r»9DÁ˜p®ñÊSÏ“ã).2˜ñùóŒ•÷B:Œ "ùšäñÍLÈT¬ÞÔ<$įtÊ%‡ùiÃøÙcC?Ý#Üà@ãØ þwRYrÔ1ðË­QlBÏ ·ÓA$Ò%mÁ)y¼ÛØMÀí'ƒÆß¤è 2äÜQlûlÉy—Ÿ6ë¯æ×4ÐZüÞvG¼ŒˆÜ)-±ÿW¹m$œf Êþ$%žBø´ÄÞYÆ„´yîA©TY KÑEî›gºL9ÇJ]ô¼ËWW_b+hiLãW‡A+¿Œ¡·  ·ÄìêùG„MÍóãþ‡2„^é0}ç®^ÃÃ…ÉPÉêÞÍáï`‹ “Õzâ˜Â´÷­øJø+µôÊÆ ª6:‡úËêYd«3>l´žÝ ÏömÌß@d Íœ»D”oFÌx÷¨Ô0x”º:€W}Áß{Ô8h­e/Kú˜^2À=Tz$|’µÖâ<ægÝLü•ã»wB¡ú qÕªAÇÆ õ`u2ý%ê‘4­½R˜+  E‘Õ<‰²kPþAR¢±™8ú';lK,еCÈ«Ÿ»§<<ëVb’LŸÀ¿c –k¢¢{2°oÍÄ<~ÅcS°^íð*/ëË™6³•­\®6³Z5þxÛ¸•¿ˆF«í€ü ÌøÜÕuø<(Šh¨¡¿Œ•ö†ä»îf]İpÙ øY7HÅt}ñ Ni–ïfúW ?ÝCu2±ÐYOæCµ¡#8<ÏÿO9,ú˜ÚðÍ{‘|ÞOJ¢Îã¥õ¬„â˵¨òð"'ÅwQâ=‡¯NöjùÔêÑ-hôùI:E½÷ž\¯ø|uwN·) ÏÊ8Ê$ëÚ1áÍ5÷ZŸoÌjAþ²º¡žJ1“ Û¯oå£ñøAééŽl]ø/Öë¯Rô8dÔéÑÐ`•ß§±ýÖ…ˆ•\÷k7‘WoCKAÛÖë÷¤3Ô!nÒ3]’©Rð©ÓHñI£Ô³X»ÈÙ²®f|`žœž´‰ ¼¯»ŽrƒŒUˆ…9ôfãÅ%”g¬ðc©cž}­Tè< êŽN¶!:¶v6¨¾ÇÔj[òø‰=;ÞÕaL8•óEæc%Ö+ôÅu}ÿÞW×G ­_̨>·¡õªÖ áÛZ¯8®Ë~]™®~)¾*Pš´„Õ£ÊÉ hŒçlëSìw³N”Ù³ìÆqùi;ˆSö®ù b}2xA#<µ¥Œ­Í¥ç¾ÉOÞu±3¨FeA‚pš4+¶K´`ÓvùøíßÂàï×oq⹎øé«Ô›bY5¨«ò„<{Íž„~V?ŸW-áU+IX€Ô9«’N“9KBã}Ä8\ s$é“û¨ úÎÔ—ÑoÿØÜýæ54ýåkÐ^í+xZßÃÓ„ÿÇ⩳²Ò^É×BçI·ŠBO³SF‘Ì|’{ÛcU¸£¸-ÂÀ ¡a¡ әƺ¬¿×Œ>0”¹\­×˜icü̈Í9†šøåovU¹‡€ÔkèݪÛàí0*0³~A~‰S0׳¨=‘­ÀÀŽ·´~¾3Qd,½{'#¤~²[rdçàL8»gœEÄÊZ™øy¬•Ìi,mfÍŒUDŠÐùž³:¥÷ZT':Å ‰WFï¼æ„Á¢ÒV³½+'ŸýÐGô€v&NàxOˆ/…jYöó[ öýò¹ï 3¹>Ê, /ð5×Àõá”V:þŽ{T×µ¿H`G†±c«Ñ0ïÙ Èï?†´®+s¤€÷zNòò;¬”êÿ"üt„ŒåÏO&½q.AÚL)ÑÖØ4‘ßµ7²!ü™$iççz=bó_ÐIþõÙAù:höt+†¤d3‚ØKZÓYBCÎŽÌê>+¿:ëp~Ǽ5ßqPX%š°£R·á4‚·Pñ>çë:¶ª5·ƒzˆ"8€Ê@·šýìšÑý¾ÍÌÿVõù>¶ë’Â. 8œ™éè8ñæíƒ=åulŠã&g4˜>ð€™l3Bûø¦ŽÈ9èâåÄgËéá@@Kÿþâ."ÔÉ+coD  s‹HˬÇNésId ²uà˜êÒpÄ?!d „A„ƒ°‘Øßj´"AÙÆÕg¶B»K9&¢\i£Yñ,¨6VyÏHLEC«€€DøØµ6[hõó›]¦fÿ>ö•úÅF?Àøâ~ùì÷'¶Z704@{;ê%h% ‡Ü9áoÍ9S¿£8¾sïõñYV¤ ¬ç‹ )ÜžXEŸAL*7£(k e¼Ñ,`áÆ×Ú`ø»_Hëùa3=@kúHTÓòBjš4í/d¿^(›5–0·ÁÌ å8®Ünî>fþGñ§IkunÒ{ÆmÎ N:Ï\Ö²ÞÆ´d*ÝéWƒô4ß›V[a,3kæý _sõE`s*öJ§OÅÏžéŽø%Ùëv0[|ÿW"'8žôÇF¾Qü·b1MÊ5*½h"/P®Š>qö÷¶*X¹ 6B6Îm®’æ$e”³Ó³Fùa ÝDÍW}:ÈvH37Àè u­¦Ò&Œ,ØjÆÌƒ]ôMæYTùã¨Á²¼õ3l%S²ùØ1ŸZŸÖbÜè'$ .¯C´ÞÛê@ŽxVÄe«ñ=öÎ¥º•[ÿl.ĸñÚCÊ<±Ããè o{ã½ÌÜœ¼ß“  p!aQC0‡OœæqͳpVЇìEn–Pñk]Çḯ°n„xºÚd›Çm݃8E4¼™ Án®¹öEŽçì>þ+òª™È‡Ó=¦ê%á·Óí¡ú ;q=T ?%`·«Ä1¸$²Qp^¤Çδ5F2X}Z¢„øV³ˆZôuŽa€l¯•Ôób£‚‘xȽbæQõ¸s„ˆ@·zjØÃ4 Qab—–"þH_•X8*Ýq×Mˆ ø«Íå…sºäÿr’kyÝæzŒÚ¹Wsí·;Uã¹±n—rqX­¾J$YÀZ½¨;OBëióëû›Xèc³Ún‘©Û€¶ézäò"IMøX"q)ÍŽ2/'"Ó‘î\x}¿0’ç"1²pF•3AkÒ,r6[$쳇8óëT¿r…¦>•cZÍÓâà´µM1ÇG>}ùÌjZuÄÍ-U=óôƒÕ8µáz-cÖHçróïÆmàg ý‡$li¾ºÈ»eÙÿâ7ée÷æ§€ÃBx7¤âNRÊ3q¥L Ï%mh[âä¿]RhºtôÝÝþç½_+DÔÝËo`"14Ò>DV§ßJö„¤_¸ûÿ ?î°…uwý9Ê+ ȡް€1@RHÀPd@:$É@2t äÑQœŽ  *†PCÍÅyâßÙh "z„t¡â$G0!QˆŠ't(¡"…vDZº6=]Û¥,휥Á Ûiÿhúz. 9U$—ÆArD5Á’`‡’Fê~m3-ÚŽcÉs*Ê„Ž÷è.Òrx†oDøÅBd”@iDü*3~)WsO<)@!IÀð!Ô‘:En1(LCqƒƒq¦ß ¡AG´Ô!¢(B`:²Òlš´ÇÉ\Àpu@ƒé  {!¾W ô0Nµa*’ Ht(ƒÉÆCSÒ2¦Óã.„URÿ«ÅúrM!¸”~ u!ëñ¿¥ã«À¾^Ü42Éëº$.V(#¤6¥ãZ¬ÇdžÏG…$¿()ã¿&øsw¾òÖhî¼pçæ oû›¸˜èaÿøJb>bf>gvrãÒqãæãQ·èš…Äš>V†>†††:8JrÎÇiï'á¹DpG)!e y/HäHå}e(›ýÄéõ #(ÉŠ1R<Á„Æþ©{P©òµãeH›zÃéóÒ“ts(×…Bm^¾ÔSZšõ?tÝ¢°? §[©€˜è9€1@H½hÄHždϵ¦ŽeÜô#óèÂãUeI$\ðWôe»g4vâ–QJÉea ’ñϵ4­ª –Há;µ+ÇúE{R0Ûó_äÅ$nY•’sGíëÖ¶3¨ŒW>…c"@8!c@®B(ÁÐA§]iÈhéHgWCŽÄ4 *t :)«èþM#hê@ޏ*: C?èdWAÖÌñ œ“t™1ÑÕ¶[?[,GÄS`ÌðÆIºmÉk’Öª -©XÕ¸g+&}„ä¸%îO'h:´Mc±+ëðãIÎ3G© e‚9.çßVÔ*¼X(Òv+Ë6åó•¦MQÜ “VIŠóSÇZR•>üˆQ‚g>A¬ZDPœm¼ðQïlO<[/%(U™j9\r ÒNq¦%A”E¨àÍúÆKAŒØ©˜kÔ‰…k6¿æ>zÅuWàL¢Àž&ž»øºæ6äànJÈÁ {ÙË{Ö;Ä’Ž}O¯uú™V(±Õ¥â)ÉJîÕN£Úä«VßVÅB3› aoÞucu‰oÈ”¶7¦þÈffЯ‘¼Œâ>$Ý{é³E>M'^2è’\DŠêгtã0É} ‚óIL˜ Ô«U§X7ºV7pRQB+ÉÊ T‘îÈAAj€èØÆzkÍ¢L¸xŽ:}µ¥SŠ˜ßõ§Ô¹êä^¤¸Ê\$ÕÛIÏYù°“Óûà‘¼ F¼êBåz^[ð‰ Eö°{û%¸Qè0ÂC¨žñz¸èÝçß6'q¼1ù‡7÷¿t%HèWHò\²t;§·_ðP¢<8 F¥„šç ÀÍH¦¹EõB•,’,ˆ‚ˆ•q¨×À%¾ÀmA$%Qª&pw A Æï0>-¡O&ï¯÷ê×À-Ãw¿ ØŽl>`‹îE Å®7e U‡/~Ìq UŸ¶,å<Š"ÜÐNéCê.›ÁF Ø@BÍKù8%ãAß‹ÌVù¶€QEᎨ¨:/¬q𠝶¹muÏ.òéV„ºªGsÚÐ8ƒQù p,g· ŠÓS„¼¥Ý›o­žèxÐ&‹r°ÉcÚØÅK¶ Ûí ‡¬âÍøÝ¢çÒÉ"¹ÐyS²u¡óìq˜VÂagîÎ|õ¸vdâBÞZÁ.o Ózø«*…|Qö*v"ŽiÕg},Gµ¸Wº× ɵÉÜ®ï ÃpÊ=Ïg¶äø¯ôÖ6à×:CŒnÀõ{—'R‹“ƒJªõìš+oUõ©üvœW"H¬"ÜÑp€j-þ<Ãß‘³²?×ÚÒôW›èf&-iEøÓ 츤ÝK¾Á.™qŒF7cõ<쀣‰3`L ˜W-âî@v,LEûà*ŠÀÿq¿Ù¼îPÞ˜SC¥ÉxŒËÀ³ÕÁpõΚ€¤µ¯ø (ÁCÉ»¬­4Ê@Ì\ eÁÐ>-Žqm¦ PzßQª(º‰ð9F ž=&ÂZnC®3Ãw4"ú½ˆ–ÂG¾/ÞÞŒ íàfg3‚yÒ•Þí¤ž*é`J‘˜•XrBõTÁ§Â‘&$°h({î¾ ¦ ei«Ç±æèøAúöÄ´ŸëC©ZÔ5s{vNÍ=“в&‡ýÆ|™Öþ[Ô !næÎ¯ªÁ=õ£9_ÊCÄlÙ{Õ_‡´ ®]^Z™¸¥"cŸ›Ã+ õÏ)$%Õƒ”XêÛ1nö¹ãýbâw:Bg~0OÍ:¨ø`¡e™¹VƒT¹Þ©£‰ÏömC+ƺe®Q7ЕnèÛa1Ú%ɤù]%çå½–¸Õ3©É&µW,€†„þù„ÏB€·ob[òÄx(Ùò"˜ÒÈ.ñ+ÍHÖ¬Dž…-•- 9H)/ðulm_MøÖ~ýð­p¾ù¥´+®±/e°ý—QÚÇlgt,ß9&…f°ÁÒM |ß®Ç2ʼCuˆµì2cv]:r÷ìE†c ƒŸ½xQCÓáÕÌ”mc>^fó^ÝîÅɪŸBñ’É€/U´Go œ]ÆT äí«TÁ4¯a¦"Fš%š·5·pTÙFd¬ò6wÔVßÌ¢’•”&|Œ„´ÕµÛë9¹‰l°ÃÅ­„3ÑS]MN;~{pÛàp~ãÝ63Ï„\7ŒrýEúùøòÛÛŠå›T›±³†Ð®(yN”;‹Bµˆ=,6LRÁn¦x_ ebâlrFý=vôÆ¿k¼TUÀo?)V[½.ÓÝ‘àmoÜvà Ô´jª&ÔàŒ¹eÚVzSGº›‹ûÊ€õÑÓwéù,{\–¶£èºqô6hçðÖ‡rgY®t ê´€¼©Øª­2þR eC\¨°ã„Ø ³€oÄ0½9Ænaþçäà4"f-ޝÙ8ª6¯Áа;/^6[mG¿ì‡yUÞ5Ia0ÄÜà2ÄÔ›ª¿<˯թŠôýÕÅÏÐW¨ÙvÛ±hÓ_ûbÔÚC‰vj ×H©þú£Ë_ä\¡×L¿GðáLŽPmÙÆm½ŒSÓ›A3˜RZWŠDg;õ$HTëÆ/¸ðŽQm/° c÷ Æ8ÌWÕ{5|Ö+%1Yª¬[I:weíâ^¹ÚOÂÖㆱÂO«Sÿé$jºFÛ;½%c¡•s^”]`„µ+²Ë7Eó£ÏuÖ-È]еÿLûضØÅÎÓÚ“j¹[öà¼À%#ˆ‰Ov:ÊÞƒ6œ.8c¡Ç‹ ¢¹¦³oOnwx;#÷d˜~Ø#Qû{Wr¤y`[d_HE#¡;µ!}›24¢B>I®—ô±-0–N%×[ËÓÍFS!t¦˜y˜ˆB“5hr]Ø-ѵ5 W©DxQ4ŸA™Û; VÛdøŽÈÕº÷™U#É"`rkK53Z“FíÃç3 {8Å¿>Ø5!Võ¶:tÃE‡©ÞoAv€tœ¢Í5MF[ä9 ŽƒuaÏ™ÖÜÎ}ë}w ­ˆbɆ‡Î™½Ñø/ %BˆÌU®ßÁÉØï Á5ãÐ÷!¤€ïä:r_nX’—E½ØOÝÚ—Øö#vJ:– v ˜ùÕC›µð…C0æeáïØk†i¨ª¨$çüLŠ}q~õxãºk±ªŽéÖ©9’®Žñ5ùÐä èÀ]¦ ¨œ§Eq„Xe+ê‰]de‹©û6±ŽT#3±a¥øãúg¶îfS‚;C_\´¾KÙ9ªNQìÒÙëYV‰#½ÌÍ-Õ:7üÀS0Öøc¸„ TûÍvÇ“UW@a‘ÄYL¦VOšß{:«ô{îYkƒZü¯˜A4‘=Ј‰ÿC°ïïh9ÍG žZ~ñÚ ÄÙäé8pGÊ¢(b½pG¿)xÛ` gn¼MP†¯û6r†GTÿ3¨6cÝðq QW³”×á[>` -ˆ9)ˆþzzï’ˆváù#ì‚]{­Ú¸¿ ðƒ\MS}4⥡4Å<7á(Èäzœxüà/}ˆAŸ€¢KRíÚaXöFZI@bÊGh‹Q^*RtÏíÛW—Åœ%gð½É çI¢ñ7æðò=œ•W[ó£ÈhŽ‹¯ÊMð(Ûœ3ùÈZ¥Àß` Ã2ȶ^ÞÒ\÷‹k"¿Åv2n3Mè_´/J·½iü 2oóK¼G :“û6qÚ—€LjãV7í"Û¦>HÍ‘ÏfA*)P›= 6Â^?m87z5¤³  NÙ²„ý0FžˆÚ#\__¸<åséuÀ÷s9Ê:ªBÈß.ÿÕ]~á¤Ûú=/À{ÊÏ€Ñ0 "6»Î¡& Ð^ÊJ¸ŒâÿsüË,AÖVæcBG¨úëë ›Už¬ÓñEC—&‚ͧü–CüËë»`}ׄïçåÈqò8"‘]¿Cú³jâ9²Ð­¿påÒK aä°á¡f¢ù|ôœ¾ªµ§†U)ÓÊai ¹ð V® Ú0A%¨WN͈†t8œ¹V. ¯øÇÀ R2¶HL vK6x£6Ò¿m‘Náç&™Å åA6žyÃ’;’bBY÷™bÞ/·¾äÕ|AåÕÉÁÌ,ZQfX„—SÒ:#™Í“Ä9Ï—”ÝgIÓ•©ênÛŽú¢Þ›3±‘Ÿ#®}{IXW „Ëóû™»ÌJÛOä ѦT,+5b„ª3>F6!º ˆ‡Gçc5”*¶SrRÂIÈë“#b1‡#oŸ/º¬§{=O¦ÕωÅ<’"b!4e×Å1ã ʼnPh\tx Ê‘óÛ^Ó.^••Š€¥h«zù Öv¦« ŒßJ5Áóù °œM§J Eußœº­ïÝšf Øâ%èý9W[™!l> ‚ñ‚h°µQÇï@jW®V±Ò°ÓS–w{@²I¬é(9T†UGBZ]4‘pŒôø×°.šPJ’Áj…!ÆKF|yq¿¿EÙœŠ‡âcKΖöÔÎVÙÁw\H |zû=QþðGÀÙö÷Ù Áe4Ñ’‚ U–¡>uÑ¢fQ¡­GÌðÛ¾qZZ‰Ö6’’}y¹±¾%a$D”i†ÎMüAÆá^Ú<‚t­Õ=ߑޥ "%Ϥ젡Á#3tH¹H9Ý¡Bw†çäðZzÚ}óDS Ÿ•­ÃˆÿÛQö¥TÒˆ“‹klxKîVC¼þÃ*Œ®ûjƒF#Â\H¯tΑææ¯z±[ðzªBǯͦšPncªU#bC‘è";´eæ÷ ð`×0]÷ÏRÎtl>ä~ðs°+HIìÌZW N=`ྞ<#ã%)òv-´ûúÄÊrÆÖoÝ›·N¦iý­Ùäî”aƒû]œâ7c”qˆƒq»y›œpöõ›Ô;«ŒÃ3<¬º êÇJ˜ì©¥ø”`܆¨7W?ú½nTNA†ñË ¯æT€P^ÖMúƒƒlR\qZÈ@pÀo& ˆ&A94ŠF£õ²“Ù¶l˜\úСMð»!®¹Ä9æàðõ_[<Ùt¸H;g¾òî¡Ð¨þ‡û[õ%ôßÃú_‚K¾¤²;¡»’Øqo°bî#´_†ºPê,Þ9IñŸ~m¢rÚ'“’*íü͸}ègÉ~k3óÒa„²ììßA¯gL±ª-W%××èýÇ¿ºu¨jÚ]o â` y@/EЗ¿W}hƒʇ½H êNb“™$ÓÓW*,“îu*¢·?Q 3´<áÊÚ?äX`k@è4`Þ†‘åˆùSOXËÑ/Hú)s¦GÊ¡ÿÕ[*´EÚ'Ét,ÿ˧nJuÎ 7™Ïé1ôüi\=*–q¡"ix{÷=ñò})Ï”HÛ¤s2Gµj>W‰~SæFçjð—´1'ƒ‰E8ؤ Þ“EvãpoãɃhø¸æîˆ`›ŒØá”59o ®ÞŸU466®´ ý4DX1ãÈy š?ÐвäÈŸhzuˆ!—\C¬€fµ¹úèé·3scúh¶¦z%i„N\ゲ^ëZȱ{çã™ÝHo<yLçäÝÇ’pÚvùh«äH¼A‚—(ÏjW#âl§“\œË„tMTû²-¡ü˜š;A;n]qÿ \¨÷ýδCæÏz|Ð3IŸÓD'fÒ¢V‚³Hy|0ªÌðt¥¶†ä¯æèâEýB¿Æã£¢³Ïrßw¡Óýʘ;À C>w?÷iŒú´›¿Á‡ ø¸ùP6Ò ±ÄØ]…a,Á›ì7¬´‰¢¥Í4žÞ<Ä$ó*©ø‹Ò˜ N+ÚfîÒ›$äÎ+˜Ï޵øæ>Wž6 ‚²°¦¶*¶– K7d·® Â|2LÏÊ …ÒôMÓùÖväQ¾ZÚ‘>Êí‘⿨R¦–[¯Jz_©Yçù÷¯ÒЄP |bª•®´Ëú³:dfö°vRtܱ±«q9øøÔáÏ}rG›ËSX|kÇÅgЉzm¼<3±ji,zf¶UM4¢±Í²5¨Sž¶æqc¬ ªÒ ²²òä ÎDþ÷<9>­ÍÓ1àkãØo†¼*«!ÂÖ-éáNœ% fmiâ;FÏ'ÒˆEàeÛùB¾It#/O•Ýl¬¨”±¢°Ì’2#Ä»§5çVwø–÷"ÿt5e²O—|ûŽÍWdÆ ŪbÊ ˜è뤹yë÷|\M¯©y]sô¥òI´¹–35 à ÌÓw¶ˆ h±uüPRAk£Ú(æ‚“-—w«ö®ŠaðØƒ h-ƒêRp…ôYîÝgE)ÃÙÞÙjÕwm$Vi—À¹ÄXf&‘´å—ùåÕð¤q¦ÀIï lô’Ö|·_#Ù–æEÅ&‡êû}òïPèŸÏ%¦4l]Îxcuy3â™KÔ颔Ji¬*G©…rä|ŠøY>¦À¿Ó1dS4´#xÅõ0æ~F¾[)Y,›‡Êiu#äïÆv§Ë!nÑè9Dõý½Ü bæíí¶ö¥Æ!ªµ;ê×·î;ÎFæAú¡\“5’óŠl8µÐô½EnEüâ¹€xÂ,®´‰V­ÒòZ…b¤iýb6,¸ücô1U¢%ÈôF^Ýk¶‘ˆy÷u5çOÔ«ÿ¯ØâÛ¸Ù*³¾·¦AN{ð¤›gJ‹’|Z3âMÍL³~Tö*ÊQV +u×^Û½0]ô8æ~# –«&«¨ˆÇ_ƒqªÌUKöA`€7HaU¡[».n˜»v&ô­Nê.RR…ÒáåÄ)U¾à5€ìƒï¹…#iéè5èÖX4ú':ú9<ç}?rO1:þ@Îeþ8¦ÎŒŠ¹ &€ô×*¶Ûf¡>¨f&Þ=‚ÊSºÊ—ùŸy6‰zV¨JæÓB&üwX75zE%Íõ£¨®Ï."Â%ÏžgdÛ¾Ýp½ä˜pÊd»àmÆcËXÇ–QíâЬ Œé;çi=ù×Îé¬ô[€’-]ú3dYäïH·Uκƒ`®#±ä¿†Ît˜;„3Ã?ºÐó}zÆÁ¾3ܘCŸ´<š~ˆ=Ï$½Œ3”á¬ÓIÑe»8–Ñr`ÁÌy§QÁ³†Ò%wY7™Ùlvzj&#Çòl{ðÌgçn²ùQQUæ ÿî½61gæüÏGôo¹ÈB½,rBy¸+߈õGŽHõšÊ—£¢æÕÊû„íìèŸ(Ì…Dp2¿ìÇ»¬u-‚ΑÇrè7‡g es{É@ïPTóõüg¢ZÌ<Ò#DLrªÖ××vjªñÔ«þšfšÎöF9ÏźXcØ!]Š õ,–ü@²Ž5Ý/Ë‘>’9²òXï|bêžt»3Ë|[«¬Îèësa&õwV¢åÀ øTÄ‘KÓ? Cýɿダ´u‘²ô¢¤ú§?hCÞnÀ‘µVžMO0ÇÎ3ϯ\}ê)óY_#z¯š|DI¯uã§ ‡¢]Qžkß ”>{l?ôÊ€`^Zã fôlÎóÓ™Œán£VfÑeÛÓ£aˆ†Q³"üëwÙKþJõ6®£|¹³—9·mà|òŠ?ïÐüô©"¼¶ð'µÜ?!²\ÇU­®¶E#§¤<Ücå·kXÄ?:!^ÅÈz÷(Z"}˜DkM][РГîJÌÐG(,X³õdÉßH¦-è·k)˜‘Å"C[~Mþf°¤&îZ h³½Ê¥þê-BÖÌÜY#KjŸ}»°ƒ»ÁÞçÕ1V{¡›w®Òƒï$ö]¼&ÍËažw­,ÚÉÄý;.N"Ô¿ÑdÄ¢5“]ìvWl9åÀ†Mó%æªx»­Dk46æWd§BÀˆ˜PëûE÷FzÃa@ò1ówË%V€¼‡^÷ü_Ý•õó—s.Q²."þ3ª ‘º -ŒqÙ®~˜â+/èei¼÷¶y¿äþáØ®Ý>àyé'0&^Ä1q5ñç€*Y…ø@оê;ÑàEË<&û­2+¢»,rV6õ=ž°Ýxå ˜æ?# §èeËñZôË} jkx1ý¦Sÿu^*‡ŒàÚvÁ:sû¤h‹Ð§ö?¯µÄ ñ|ùÒ•”‘‘•ó>麓‡³jÜ5„Ši‹ü»÷Ά…Ñ?XÁ«WóyÇçßI\2«9öýÞ*÷|‚]3Ð-Òã_ÌÜÚï yU¹™æÍ_âôÚ§XY.Sƒiµœuâñâ]ª*wæ~US¢/:õ(F>f¦ ÊšÑÚeþ?¬'‹Q¯¼¿ÿØõäéì®7Ìý„ü‚tÌ£4 ¬’ r¸ýõ*솶گ3 §6m‡÷/1ÙvZà^H7a “|’QúæÀyVüèÇE¹œÙÑÿÆÇØ„Ó *ïß;ú k=#!"ù&QÅOxl,8ÙÀø¾¯"»Heis`:«ºÿ1ÞüùÐÇZ!ÃF›“×!ÎÛ8¯–Vú#~74ô‘ŠÝžoz<Ð# ÜÁõ˧ƒýùœÐs-—ðÞ•ô“ân-‚]œ q³ÇÓA “‡—÷£ÏoÕàû$>l“ˆ™sÍCYR›º†›ª¯SúS­Õy5>µÙŠþ$c\Ì›À!T Oˆm»Ý’S ÝEYü~ηz#˜¾RŸI¦1Ì&=~ây_Àhç*[Wq í´R ž*ùRKÚõÍÖúõѹàeLRÓúâhÞ·jRæ†=5VI‡ÅĵxûÜÁ/ð†ðú Ñk°ƒN×Ô¦v Ã\á Ö…Ї ±“}¢޹ ˆÕn _+¢Ág4AÈàõÒ•*ñ“ñ1xM¬µò[9¡’XW Uc[¸ “gþÓA‹è›{)3µ1ÈWÁ­¸x¡rè¹< ÒI{_¨íÏ€×þ¹Tüùam“ð¯Öd[²å™J…BnP {c)kàÇp©ËÒ°£»mÂÂâÔØ‚u5;;„ýù¦nÄE¼”—V±eÒ³ÒnÉ=÷½pSVNÖ)äO¬{±¦¯NÝ…J=ªuqe”sZs"î”Vc±^-À¢Az—€ý»Û¥F™‚ |}®ü›ÐeV!à䕵6œ“ªpŸææLÆ€ÿT3D63_etÙ^7ÂŒ ÞR#ê7”ec5aEQCñI¸6IÀ¹·,%éeÇ|³8®;ÿþ0©d"D`E•W!UÉH_fÍ—µmÙlË9µ¶cBcÊ„"Ù8Ä QFq':Šƒ‡tï$nø"4‰ADÄHB¢2‡Ù ôÌËì.Muz€¡ÎÒXŒÒàÏ®ÖçZ«^¹@Ý"âÓås‹jâ‹DRYh ‡·4˜dcLQp‹Ã ÏÆÿ´…4€Q€Å¤B›°@ÙbSÎI=êêRT•KÃÕžÔ–BØij²ŠSÇ©¶Pgi0˜ô?ìQ€× Ái+faMóS¾Ê5*g‘8’Œz(káA85J ¨j7CUqŒ„-Ý>Ûð|ÀªåŠ “EyW/RGÖ§P§L²Å‘`”FˆHÐN4à~ÞJš ® §O¥øÅl‘ $€XÁ@ó'ïEtšŒå)ޏjEY®>±jܪ(H€Óâ(ôä·q2¹%ÁŒõÔ*Ú0+¨t5’’ 4DÚâÈVÁ­Í6_gU¡Ï÷¡v*µi Ü~I úµåd)4ÇKw÷ž²+x­MRu/BÞx3€‡~!Áô 9Ëëtœ S¦…5ÂÖSÐ ÉcǵTù¶3ÐðÒ¢÷™7Þëöë(` …̱´ hgé‚áwq€Š58ÒOF&qó‘khø†®jþÎ>–ª(GÁrK(Ç­‰‘ŸPyÖS§6²ÿMá/®Ñ¡[uõf ÀY_ÿêE`ˆ‘£Çýuœ=»%vt_æ)‘hûC xx‚¿.¯’“§¤£´0Ärw*rÙœªç.3ÿ³×AÝ™èÓ$Òڛψ¡£¸×4HÊC¥së¦ëù~-ZGc3c = 5ÀqØ2Œ¾5¶<ìÎÑKu(¢×ß²Ì%Ž °ƒ³Ù\v¼$ÛÂ@kP¿' ‘.»³‚EyjìðùÝæª½\7âß“ã øNHé—ÅÊVªÃ±…V0Úf…äÅ>~è-¸» è™ 4vò`2ƒÚ-§Ê-ë{ó/Ž‚ŽEÓà’+ݦºNóXø•϶Ö°«#\Ô|vÀ˜Vªïœ1-â§y+™åyy\UNèTºC«ë`W”"¨Ú­Ú+ÈŠªèS$½Æ—«^À¾ ,JÀ³0—”-ölãqBò5l˜™kÃV!³lƒsëmöÞ7í½¨¼ˆÚÂúÙtcœìrÆõ‰¤cõ¶`=z^qÎW‹C„Çad±gre_S9¸ÖˆXN{sõ¦.ŒÐ€R’H‚æ©×€‘ëaoKW¢*XGÆ'3Ðð¬MöZÞ8ߊàÙÏ,B(Ÿùa¨šÌÈâFóµßs›€q?ë¿Á0 è^0°|¶~€mR ZÁIÏ ¯ö ­:œÊu-?svºK§Õpi´ýç^<+[¹ÇBliK1ˆ™ë€é:ã †0@´Ù®$=2]Lº^–N’t†87^Qw_EN¯N)[&Kfðô†¶¦¸x¸÷+)µ84|ßxÂ+äÒ1ô'}î%&-¹bO“œ~âiÄqn~=ýê–x*aº,X+Âk¬  EˆýhTZ+ÜäÓmÙåù*ÔÎÿƒNøçw7Ú ÉíÀHïU´~íîh'òbëIÏø˜ W­5˜íSÕ®}ÿ«,J·è«¨ɦ4*¥[לkÅXp“êà¿?ênYÆUç¶·žl.þ¹_²™6YH$>êø§ïnÑ~ÿ$O‡é@¹Ë¬CšGUýºøLŽ‚Ñ;™^•ÂíFYrJO4Ûj ÅÁÚ|‰TZÖ¢P®Ñ>\âö÷m³`eÂÉwìíµÄ‰vógõ¥Ö¯$î»O÷ycîÔ³TÚ_mY/ÏÝ…Ý)º ¹[yi9>IØ»ziWWt‡©ü)øÿ´-š'Ç+Ÿo¹š)W½ÉÌðK‘gÚõñ:£u¨X€«¾ìÆ>¬•Ò;›ìó1»ùþ!'‡¤B’ta˜tM6]éÕë‰o§ÿ/o¨W·”!°ˆ;Œ©È»A>ÝEáŸÐ륡ˆ­¾*¹µÝW)´§0Þƒƒ0ˆÅÛž¤„;wu¶mÌJvj%Àb²;L¡Ø!ÎðЧ`Ks„dy¥ž‡kdÚ<Ü~”øÞýäò£®ë7#¾õl²øZ÷Sw -ñÇ%Ùãc 4 ¶—‘`µÎðõÞçñû’>Í$Vç70«ò=ëWà<]7úŸ–i–h‰-~lîS—ÓŸ±Ú¥EMeaQœÛD‘£¹/’”4v3"TZMã¯dliŽhe Úæêsy Iź·ýU wŒ{ìß©TÀœiØ [3@p ŠÐUb6Ú9€P¼GybïȰ«†–ÄƧ[·ŒêÀú§UÎñö­t¬çX˜ªæ«¥~¿7A€¨ÒÚñ Ùà%€ßP#w#Aõ-ºØ®µÙ»ˆßF%âFA&Z¼éyÀßX­œ–e›éÔâçÛæ,Wùkšõ-Zy‘»òmÇVþ?îøÝú4êÉrרö¡BuëOŸ¿©Už\YÏÌ5µ3=§3Ó¡ÃÿY›[Ÿ33êë³åÌÒ9Žä¤ËÌ­V5VXšŒYhfµu›4™ã圃µ-%ªÔÛ­LÆ­%Ø~Kæ4€Ä@Ö2}WÔÃ\cƆÍdÀga¯8Mù-ÙÃÒZ¥ c ˆ:!·oì|iŠ'$œ J]÷6½ÞÇ»²h1p·Dª5Ê,Ê'->ê2¸è6ºS?Æ7ÎÝÑn·Å-¬ìþ¶yÔÚlT+ý>ªËØE8F×®D+SŃ ôÐÛ‰EBXmšC°ŒpOK p¡Ôßå¦ÃAãþò߆Òoa¿Ñ’"Lr¥ÈÊ„ϲíÿ†EMQ®¾´C*uÒ-ô]aÍINÒä§’]4”Q7rÂÕ!‹ W²Ë€¡´g Z¬g[,ii›0/©bVã|×ãºý8}àTqê’]m”¦™µ|\4­M{³¶,¤dÂÝAVœ˜ÓR’}Vò`¾¦\Š“‡ÈÓ¥uÇѼ1[‹ºG»x—)^ô÷,Æô8éïS„3 Ø®Á;%XÎæsØ¡~ùÚw/õ …‡U?TàüÂÍl¢î²íØì SŸÎËÍŽEö¼„¸ž¡öÑkò& Wù–5ØïçÂ`7\P©KÑ”ó ¦"ŒŸÁ „ Nv Ϋ&{ x•šü]dg¢kë/Éô“h™1{ÉÇQ´xÁuÝ>Þ>ÖÅ=èìBa l9G0b˜Ê×ôúã_À³ob‘Ûßœá €7¦d}­}Ùí:ŒÌ÷Gnï RÒ'\ÑŸY"˜PÞ&zPDЯڀ%5âàåºãü¼E+dZ‰éå¢ºê £#|Š)^%ߨ?É `F5wÆÁÄ2‰«ÒŒRŠK#7i©X¡ >Öd!Ò§b€¼v¾O¼ïž 0ž¦ØQ.£3× Š¢q_Ê»ËþÉpäKûˆX7ƒè”°Ó3²éKn ¥¹|÷R' ˜Ò€è]'رqâEA¦vˆA]E!Æë¡LAC q<ûa/ntq8Öý”½AÌÚ–µþŽ{@ºp<ˆSèVš=ñû%=Pñ6'‚GTóæ1°¶W¯ã°?×V•P’›z17—Ä)~ÅÃn¾µ'¹ƒ ZÄ1Œ1š° nEÜVÃeäΚ|¿Š ¸T¿U§¤ˆÒâDÓAÀ&'¾è*_ºèÈr„õùÐPƒp±—ÏdZ²Ò tŒZîtŠwcIp#t,PXâšû¯Ô>†Wùi¨)lkÐÔÏí_ÕÏï×^a_Ó`Þt¢ÉòœÍ…*Ô¿Š#}ÀÖ¹0¨îÀ÷'—ÆXMT‹æÝÒÕó=»ðbÈ9læ$ÚW8x«…SôKÚÁÐgž›»U¤ Þ.ðš÷nÔjlG; Bhnœ0 ¿`Ý«vÛŒhprpa7•Q«íµ©Ž\ÏÆ@Çè¿¶xîšî6Þ²,yUäªí(Ò næÔ1€|мÝöÖaâÅ&9¸é9ÅvÙÜ0¾Å:€FгÏ?dØ¥B1ñA–‰¼—}3f²\ÅYJU3–(«w¯Ôñ?ÿ=ºÂZ/Ž)$hv 0¤Œc’ ûGÐ#^à{¹; È^äŠáÅZÉáÊDdŽ®èu€U+Ý ¸j[ø¡E?},¹PKvët}–e(uíœ*Ü •m”ª‡6ß-Ÿ!eEaâ«þŸ¡64Wf­4’.<Á ‹Q° ¼Kȱ›ÆDt¥J5ú<ŽïÔÎғá˜o·6äö·&ý=s·8”’(»±Ê…=À‘ö¶Éï°Â€ð_&ã9ü]Ÿúlšb?ÆS¡«}é†O³:Šg¬Ä„ªß&ë|½¹e:Óýl_ÿ‚fSñPñí‰0ú, »dÈØ°¡Ív" WmI¶ØÁ\€cI¢&mAú'+âš¼°;Žâ–…¸7;NÂñ Ädžéý…Ù eÇí[ ÜnQE IaíEÔ‡Ç.ü0~HÑPKýÌÿ×OLÒöÁ÷ª '®®ÏÌÏ2 úy­»˜—¸Üáv^9ê6íù?‚ý±tÞwj•aî,Ã8䨿€ ,¼ç¹Ý$‹E¹c&¼À„­±‹v+*5'Ç»5`>~¤ $q,y†Úöš{& .ÜPêç.ɵR¨¡EQ„p½Üc‘ú`,_Ú+‘î²´FðßkÝßÊ]_Ň”à"£ü.—<=©8ŠÞHÃYÛÑl#hø†Ðz$6þßÏlIlå—‘aª"4¹Eû(¿ïhèǬëøXa‰[\Ò„ÂéÈ[œ.ö Bz¨þ„šDRŽkX¯Ÿ0Å8cшe|aRɬýÒªä.Uí<Ížbè×5º®OKŸŽÔ¬%÷Í wxÌÔÐì=é vA5ʰw–ÇaònOTA”F|ﱞéOÕçkÕlŪÊKˆó88Ä,ˆq4éZtkLšÁú"‹oÅyº*—žŽÀ?ðGoðYoÆ› ([C(?¾DZƒ÷Æá¢öüø~„†Ù£ÿ5±ý-ÓºYV› ñ‰^€w>Æ<‹a†çU|ÍøWKÓÐc_ÿîñ:Ѩ䌄VÎJ2&k/Vœ{ZW r¹Ñôtµ¦ôÉmkYrLþÊê­Ämûõ­»YùÖ£wèí‹[m¾MW䤈U´1äuŠÀO©84Žâ#†Û\¤,èO¢1 Û{Ù(ЦäUHF&V ”Ïõ`nèJAl}‘V¸ÿßê°ŸÍ£uw°DêêžKî6²¯<›¾µL´uD‡‰;hÜÝ¥ ضÉ_ ´¼msjVw9Á…Âk•­YØšíp …‚‰1oïB"àÒÅX#fÄŠ=¿²×¨ñr7enôq‘zëÞ`³óØ¢óo›¸*W¤FhÖ!¿C“ ’«ù·PÂXc»rïC¿4db»‰fPŠ›ê1Aé*.Ž‘Þ÷uüÏHvg#ûLˆÂše­Ýý5U³|ß=‹êú÷>G\ÌÅ’`‚è÷Þ¶÷ÑÎù>DE°‚]_Œ?r3>Ö¿ô".d<ù~Á!¾I…x†œóä^Beü©_aU7ZÚop?¸z3²–+¼ZZ=Þu Ëæé¾Žt2ÙÜÁÚzÕGÿ@¼va³8*mV3`oÝ&›$%®‡í¾lšë û8èÑ?è©65ÉÖŸNÿ¹Óx)~J³ªsPèL¡Ð!Ò³C ]Ò\+Âúæ)öQ%àŸ¦wssªUñ ¹6-ÚK%À\Ra‰·WÞî ‘tûçÆ¬6‡BB+g‚a­ë(Ô\õØj9Ò"uj<Ðâ¢äN†B‹ [B·:j†g ¡„†M­[„sA\ã|s¼kĵ%ø´âä!ÎPû·7í?%;ÁÜí›ÌNQCwA’Õ˜9dÂBë%DÎYå{]ç¡£?Ð…´à &"´µ ÆåWbÃͱPÜvÏ×CN;§!Èeº˜ˆÐßÄ}» ©9¶*ô<ê;„œ–ÐP„GÈÄ!ßQ9íp?œû]uáùGV"T„VÔT„ò¶AŽöwi—ÈEš{ƒ³œ$i‘ù²J‚á\—\-+úi‚Ë¿À’9.âoãÁ6+aÝ:šäÇï˯ºo™‘àkù$ÖwÒÎû/ þ¥22åæè_H‰³$º¡%!à/kg&2ˆøò¬’'5„¿Òø]_Õ­ge9Yi)·ð¦E3cÊoÖÙ•T’Ò"ÌL©‰Kœ‡ßSß2mè¿r¦¯®™êvyÞ§É1ì%b#'Ažµ×ÊWpÄ 0^ËÖhUQö¾Cå„>ZÞ‰>ö/¶÷ð…Ü¡‰€aA1W¼.çoîɯÒÊkžO8cM&yæ‘×ïè¼ pK«?Y®ð¦Îlz»>}ìb†ìu*PõìþäRè0öÊ\ôøý C\¹ÿQE—±ßîóJU9Åf|*0@ÙüJã1STIFƒt'S~ǵ㱠0Ã^ŽÈ<{áu÷ùtŠFxøÇ#_àÎz7¹§9N%áïÅ7ÂÁLc¯±Ü'æé2+Ç<Öä„zQï³bŠhàëü*Õe„›d¥-þÐ×U$[y¤lÇòžõ§Í™”Ð=§[gÑL`pʈ ÃDI…F&•cÇ_•ñjnÛè`u*Ƭ–7á ä7jb¯Èë“ÇK …d á9œÙ¯°Œ¬è ?2ËòÇà3ÊùcÉGÁŽßá~Ètâ Òš?G'‡ŒÛhØm5ñä@µüR¦X’»9VÉ)_Jó ­5²rÈçËPË΃,7ÚðVßæ{¶u%Ðè#}ßB^m­ã·gí< ¾íôQ‰vo^QŽozüøH;ßi˜ü"í±ìÙŒ~ùÎ:h††ffhh††ffhh††ffhh†Oi¸ñ8]4bw}?}%yOŠ…júÈÇÄ¿zSïìÆÈ§c ¹ÚŽ-`(ìàžÍñ[¸#p—ÅHãGÅ(”æÃƒj牚•·üOÍ/{Çf¸dñyŽ»÷³#¼ô <ãEoÇü$Ñ…Úc{º)ŸYŽ"ð”’ø4¦,¸ýþ]wÉû{ÏŠIi6Þ™(H3q”¢²ÞÎlö‹j”$0KˆäÉ7é6pEá*æ_—q´Ó¥Ûž3y§nfû£2þï™wÀ6@ªÇ#òsÁ4ôâ$ÿ¢zmæ¸GK-»±”áIÑ5±ªcÏË?÷þÛÚäöäÑS—£:ðÈÄ,î ¥JW+vß+!9Gñƒ³ï× þ™¼Lþm¡œ¯Q˜á²ãÊ ?VG¿×q¾øWš)Ù‡*Oä÷›~…Z'æçmú™Ä㽓û…¼çÄ¡Bó’/¿ÄB"èrØ`" oq 4D—jÌè &™žv7ª‚‚R« º»j¢º«? ‡¼ˆéÓÃÓC¾MÞ‡£FàÐŽ?mzõ«zÙõi›56ý0Q¤…N}ôò–fî“)qCܨ3(9ìz“h^¸ ÅÜÚnmäÖÚaÐöUïâøH)—=¢ÐKõïøÎÏñ 3Æ{ŒÓ•Ûƒ=#œŒï¾f•?içQ.š§h/MWÞ•ò|gï½ÆƒâðqÌàEk¡:ïä3T½"•Ÿv(Gܰò›%¼I68gÞU3D¨2&¾Ð‡9 žîæ$ß[‘ùs¡< 6o²áϰRÔÛû 9ãÅôìþÕ-RPgýPmwÏÎxÐãår ŸyÔ·#`ÎdµvÂÆM,/ÕVŠó:ÏPcæ‡é$EJåR”ŒCà[ˆéqÝR8|ß> Ô«ø&­}ªö)¼•° æÿ]}vL¦…sb»CŠ£ð~G'a)àãщaq,_¡h¶l“› ;µþît™ƒçí¾V0p·6Tž»—ÔÄØNAä‡ÏqHl›gŒÀvíeåí¤ãû¡™,b¼Ïž›âx$È?fhåÝ^Ó"]¿&AAñ²†ˆŸ¸î.ô +'TÝœÉÞ#æûLÞx}÷Æ¥?ùx´Žî+ ÷´gfª¢WgfÅÔðŠ©«”×ú!° :"sCÐ$ý«zcù[¯Ì¦NVÁô‹oÎܾoF/¼´>T UEHµ¸+ ¸Tª"³HÔau‡2sóÉl1ã½-  ûœ´Aw¾[±’R¾•¹×ƒpÝpO\¹4ïåé]À²Ý>¿ˆS*–i³*}aÖ“cæ©Hýœä¤Ø7—Ë>èç¼IúO¼{³æÕ¶/zíì  rKÜÄpjr:F´t¸ü|7¯úâßûlÞ!ùT£|Mëq:ó&Múª ¯ÓÛ´z»ê2w£Øµ^Vw®ï[ŠÎF@£¨qS`i¢ufžBFñŸ‡R~Qƒ>C=³2;KÓÏžþNj£Ä†YjBætgÎϧ¡¡÷0S€&@f}C‹VžK¯+LËù·ŽLOf~ö> z²Ü‡õŸöÄ}(Pú“çïjÕ„gW–33MíLOéÌtèðÿÖæÖgÌÌúúl¹³ôŽ#9i23«ÕU–fcšm&DMæÂ89ç`-ÝOéQ+ö0›(VnÊy´~Űp”9 ÍYšOXö±´¤À¢:¨>2ƒÃw@®­W  ¦Ôžwê8‰ƒ•9[×Ê…vA2+Ι8±³Y2‚„184FÏLf$³?§ç I+ª³´CAF›g#,#ÖÃ<– Ò¬bcfÔìÚ aÌ²Ž¹¬»m Õßs*àP¥¤•º+Z3|¸·s@b k™G¬BqkŒè™Ìø¬–ÇÍ;*º%ÑY(DtVAϱ4ÕjK“;4IþKJàdPÿgNâXŒ1öf'?J9n‘½å¢ÙP×éÔp€,1Ýs.‡¶RZ4 Œ™1ç´†4d‚)<0¿¡8f±o§þòºåU¤¹¿ÖåZÚ׋ÆÕty‚ÅWtÀ([ˆÐÁùØŽ˜óÄÞlæR³‚.N©mÜ+ëð§MÐ[¬Y¢\`oÐýž¬*…™{‚µ×[U×ÃXá)iS˵¯HŒ£»UX“Âeú‚}«"©“ÙÍxãܲ!søNŸ‹cåÚËæ B0[h¶ ý ]¦Ç@Qذ%⚤ˆÖœÆf*̱ˆgµ0UJЊ„óyf'ûÓ&-âÍ @óݽ‚±af™¬„2§,>„M˜”{†­àØ5¢]d;6­Ê–fU¥fUÿ1¦U°Dv ¼H¾¼Iâ{±nº×¥Åtƒ×Ÿélö”ßO¾^‚°dC-øð ” $Ï>ÊÃÂ*µ Aš4&†QøŠ+3o D¢6¶j_¦‹ •Ð÷oV“ÐsåÈúÂæ;+Rçÿ¹Ìš4‹•ågÞršR \ä©Èï±âbœÚ”M›É‷Nbª—X¦ƒl;aÖÃd oöÖ¶«È®ïe^WÝ~¨æJBñê6Mˆ»tpeªýXÁÖç)2Ï‚ƒFÐè£ÅíP"Zt#:–¸kì2j‘ô+b°V1[¯^(¤K¢Eb ¹ÉíDÄ^蘀´ƒëþ¶)C @|ßï”Ú6,z@ÄE]§,¬°µ=Æ»î‡`B³±Þ—]|O• Ô6Éì¢CÔDÚU@’èÊ[™3+ $yοè=Ö|«iŸg‹çÜëIÎ àDÑÄ ØX¬xw¤“fG(y¤Ë,B«ªÐ®b#±2êŹ傧dH†cqJ4ÁÓç»Ü¾4{Ô®°IÌx¸ƒY ¸ªø[ ÈÜzkÐô§éúËûïùꦈw9gˆ¯ñ¢;¡BZ™öÞjÊ‹²^ÿ#pr¹°m?õ‘ðov h8Œi¨õ>ø­"C¤Äz6¶SCï·/¾…=BÒ]ººóá„Ïó8Ž-·Ê k9„ÑóÓ\“ké{r˜ó ¶2 š-q~)`ç ³tE§¯<Ö §§²CáÁà|ÿE¤é@€FàÇ ÒêÀß÷Ž~->]‘sRK£±ÿÌFÈŒQ^E›^)f³~Ó`+YÆ8 ÏÙM¹¸Éj˜´øÝ_½™¸¦ C…øOòùÙ)j»ƒL½a".;dÔÎÀûá‹ÎásªsøÜ;qƒirÊ5>6:ªé6ßîL¡¼\.?­äƒ4Æð#qÃWŸŸ¤«Ú|NZ~vîñè$ÑÁBuH¶•Ê!aÑšS²Œâhø0I`ÛÕl($#l4ZGBOàA¡)´ylÓƒ¯@'Ï÷†7píý?èç éWÆæëîö2"mÿÐjqûûÉ'Ùt3Ï?µZ>†Úˆ´£qÁM#¾CÛÞ^Ó:üµ/¼¤‰øCêƒg=˜aPÉOH›*¬ÎšçS µ¦ëá£N"ÞDCКÜëRC©6›Ò{(5û lâÐÐ93xÆœd hŠ‹Ë©úµd:›Þ¼(u ÈõäB‹1—¶Ø¡£qðÅU7¥Å.šyDZÚÏy›Ãsóx÷ië`MõÕÙ]QžŠ)N5;y>Êda¨~Ö:§ Åm°øAáT'·â0!ƒ+ºjŒ™’†ÂÅœTöÓQÅrÙ>’FùIØHha£ºrAQÝöÉÒ¸ÝûPRá[}áB›Ð&¡Ä/‚^Ü­×Åìè Ú†"ôòµâ˜ã»©YÏíJEՠƦÔ1ý¶ Ø”iõâÊ÷ŸîÊ$»ô–0êö…ó.~‰ >㸋Îs{Ãïm YÉVSgÜVí<“ –,7ÂQ7Š'açÖä|NÇ–ÚÂl¨§µáVÉ@B}ƒj4$÷=Ûƒyú1qÔUîc‹®hêœÉ\ù7³ZÓ0YÅ›CŠÀ~¹ZœjºÔL°qªõ•+4äõî› cTuø†uó±hŒ¸%c+Úu: óY€ àa5e!æˆ6àWÕ,=j7ê§#Hbå$¢u¬¥Š¡ Á œvÿÑã0x,÷h@B›/‡v¿’6èøÓ³¸jMyý>ªŸ§Œ2ˆ~K~À¬%Ã!О1aHW¼ ;ò¢½’<³gá>ºF_` U¶8M{'ÊŒ‘ú£¿DßyPOДéyñµå]"H4üÙ]¶â4…Í„Ï 5¾{ËqcŠrªCÔ쾋Ây²¿}†õ›p_“ÆŽ N¶L?¶à(™\HŽñ8êjÝúÿÎ"© ±Ž Ùs¹J¼œTàÖÔZ¶®Ï¡»¤ ÔY¼önÒ½¹ÛGÃýªè&Œ•Ðû?Í“ãÿñBõð 8h‡ö5Ú–‡=|AÃfëO!5èaÒ–4ÉÉæ4ÊÃÐ3µ¡ÀÖ¸amâè äPR¶‘,râ-Ô ©™£Ÿ€›½Oð0SQl®Ù‡_*ØçK!† J¦¡Y&Üðn2*''Ž"°â{®F±þµ©EqEŠÁ2yÃy4ë8Š1÷l…¿#Udšl¶o2C›!ô’+}Öipx·^؈KS9ëéEÄ®’õOUHa²–!ÉÉÈ!‚ŒÔÀ‹æ ÝÇ‘E$“Z§á¤C Ä°aÞˆºm¶Š\Ï7Xæ±ççÿ\Ðŵľ|#|uZæÃ ÑÊïVë51´4vî*¸Kb§.Oy |àíŠTtñm½ÝúðDz“‘(òóÆ)üØ?á\1Ò]¡fƒ8fWä…K‚Sðk“"ßJÌÔÿ;¹|X¶ÅI†Z‰)ÔÕqE\Ð%ødò–l/>/†ò$ ŸÔ\% ¯{Ò–¼¦—š|'DHÏrFN]£ §FîÏMƇI±Vø`è¶údiÜË~ø¼ü—ª†-LÙ²-93º#ŒŽpAí<ïÅ·¿¤&1]ÄW €nr9‚çtv¼ÚûSÇ%Mœ] qj ’½p@"E›o´·úÁVtûeÏýT$¦!f]‰õ± wßà¤bëE¸@Cm¾}Ò5&*›¬IG¶ç,A¾ßù{KßçV+K÷î“®Ì ¹²¸²’•¦ä”‰o‰öÝb˜ÄÏý™ÏÅ\EÉæZJä;¡Š1÷¼áO¢ÁƵP±-6²1Ç$gì_ú[ù¯ŒHØÆ#Æ®]»í¹ˆ–Ý@¾wÊÌôŽÌ9/ ÓÔŸcÝ3·>‘þ¨²öu[hå­‡ 6n¦]ò«ªœDbó¾œÿ:Ý%™ÊaÝ¥lQe*€ñLDÿXöéÝT c/ì9Ív÷ §=á$>Jä‚A™<À× öå!“loÜZ •0ˆÁ¤k½ÁC.,.€ jðGŸ°A¢e‰C”\Û€…¬¹[¹gÝ÷_ÏTpßç™"7à‘(†3ZÝ8¶1X(·ÃT¥4„7¯íbÉÊh°0ƒ€}¦T2 ]'9×¾Ì/ó®Ó‰Ëj-Éuxn úÄR ä+þ‹Í, #éÊ$Ò_…ÅüÊI¤’*œb¼)¡f˜2{-£õ¨r¾ÅÈPkƇ¡æê·Á¼á Ûý?ï({hÉ.dîÇ Ëf0C /Á &rÉYŠ@eÂp”:Õ«ŽvÑÍN„–„QuGºõIW^zzã³;po‹Ì½ÞY´È`Οí;‰,zù`¸m)‚^ E￞o@æ`Þ²!f¨-¶6M[¢+J‘LåúqÞ‡·^ß^ÎF‚ÙçŸ(ëm›É•¤=·ú `ŸÄ럀p“¹º¸ "‘0Ô¥E¬¾3jöqnˆvê¢s…S“ÏNjoÂü:ú‚£á¼.òSK©Z&Í{"^ù$f2†ê¼†Íõr{0m;NIòñŸ¯‘-æÕ¶]“Ë¿ ‡°\™)‰©{Ǩïø.tòi¶j¼ÁîÜXy¼/rœøAûámɤܠŸÂ΢Še_õÓ6{!ÔhGªšQ`i0Vºè(ù.Êe÷ÈÍ?D=ØÙqq˳#èlÅËÉtÑ·ñ‚à‡f!À+Dc$:SÇÌê Fœs„ÚFT—ByKq,fé…|òݯh–˜]:¸J™Ã·´ZÃüHudïŽ,ÄÃÁÂ,z¿Q‰Z‘9´º½Ï[¬·ÌÕzûFÍ–¢Zwõ…4´34ÔöO¯KÐÓ_kµûâµ—Ùi 5Ó™&îÝ‹‹tù³ZõE5Z*¾ª`Ž.Ò"ðuBÈ2äÚD`æS*ðxâ¼¥B®’œÅÐÖ±³ÊÔQš©˜ãdĶ#Œš÷ñ™(·ì±iÈæÿ Ô¾žâhØÞåLq (‹Fë™LxÖÎxúâ8zù:5:ŠÊ«½íK¹×EÀŠf@m±ÖUšï óôù^6¸E·sýÃ7ŽO¼2‘7ŒsÏãÿð'—/î^‹M&#‚²™œ(“úÚ5›éPyë;¬•zƼi麫ÌÎ#óª1ŒËÆ 4¯7Q¦Í/ÕoŽ'ßÎ|eT' ÖÇÉAˆÔ Z<â‰7³BkfÒí{96ñ"ŠÜVG?û™±¾ÅþKÈ6öDéëJŒÁ£®ïpµ2N },7tÜÔ‘×u÷‰ÌNHÔð÷éð©»`A…ˆ0¢ÍDÕ~:× éàÒH€niÖ/Ë;ƽ8>ëµ}Šîõ^ã³òy¯ Ÿ+…·±tÑx̱d–”^$o¿Ü!`o[7^Ñ”^5އQ‰­ Æ24¶óå ɶŽdƒOßͼí]mÜÚ7âã!7bϵß÷ô¼ó&oPÉ´ÔÿÏk3ÆaM>6šá‰—}b¦¿ D$Y(V¸ÒÕ‘rëÚËBç=:º§kZ Šx¸7.0vb½µ¼Ôz&‡guwç裭ÿ¬åñ„ Wñ‚Bdœ;ÓäñEZð£nnÐPÏ5”yÊvëN‡6Ü>Y`pø'§{y!ÖRQj„’Í?(NÿÀHÞ[ldްXÿö÷›…¹uš-Pî÷˱à†û0…ø{s€ˆu¦ì_ Ö‘4l­Yß<$I,Çþ:¾¼_²ÖÉHH¹+¬—嬕¹þ@³Âÿ~€Û[åÜ”S¤«þXöC'‹ô~$òÓLv»´³h¼išRíµ},Ñp„®õÀ9à!R¤!=rÏ"‚¼ÔP޾Áš¼Ð÷—ßiòa&TH z‹ôÂDr§²ol}ëW'ªí§}y0þùðåÁ¢öü=;òAj©k”¶+û=_þø¿[=z1¢üÈOY¥Áªm“Ùû!pûkÕ]6⎘WöFôiwÖÓÉ?L×p A'_ïÌiH'5‘ä÷bÞ•oô^äŠf|{qin›…1Ø>p ^š{õS+é¬EÖ»æìYxŒ8ÛßA‡ZðÃU³¦‰1~Ø™°[Èòë©~ßGhUÎ}…ç*ë uGªiýÎþØ$ó§ÂÊ»úUÇ-}E.I‡ç÷ˆ7ãmZ)ƒvð ÕZ+ã‰Á‚?'×9‘t[¹¾µôØ?;n"bÿÀ¾‹;PïKä¨^{ø:ž÷"ó±jÅýÆËþ¦ rxÑf½@5/¯Xæ ÏoP÷ Õ5VèhVúw/·È3žåIy`óöØK—j>Ö豚;<§N¯ 9Å·jb{îOC5Qòʨê±z_¢ÿýw ’b‚ý­ýøá¢¢¾ö‘›­Cm—€Ne_ÓÍz±9ŠÒˆÔ’3¸Ä¨Í‰H~q†ÛlVnò9ÝïýÊY/ï¨8 UÅ«¾Wœby_Wä3'ñØ!-µãˆ³S†¾·ˆÚÿ]aiŠ>%rO}M pA<ÏØ‡1ÁÁð¯ûÙ-CØæQ &Æ’t}¬«¨*gÝΩƒ !ã%ld8Á€y"oÊ‘v¶â&í,¨`Ûët¨ê ¡„Ü1§‰ˆ¯™.jS"÷sVÛµW¢ÊÝrPMhNÒ9ßþÃk‘{`œRK.úûHC“(Ó09ãpºxŽ6Ù*ùƒå¨oY.Ãß È[¤°\Xöàš÷«‰+‘¸S˘‹Ò¿oë ý *e*)Æ€”ïüÍÊQ"8Ù&wæÂ^·3ý%”àa´¹ÄĺFfg•­¡îç1ÿo©õÛŠš\åâ89xƒd ÔˆÏyŽp†ü8aé™"0< Ù ¹sñXÇi,›#åç¬I·Dš=íÔïRÏ ¶<_ :,{„áR-¼ìù"ä4^·­—vüÂûžÏâ4á¹yÁ丨žñÔ—›ùå…Cž}Æ‚KaÚzbw4 ‚*t«“ªÖ¼ÓãB^TîxE¥Ü‚PGi¼û.ì8<`*-‹ÖŸRÈìr,|ò}ä}$ûþÎ'øžª±G–·D¤xkDÕÊŒð=ÂL¸ï!>ÀJ¥ã¯wËå}i9î” ê”±$ß²dÅåf•;ZîΡ@¬K‡_5+¾Ó k× ×.ª0÷ÃÚ¥aþÏfñ·bð4K¼B…d™÷ÇQçöÆ‘v“Ó1„Caã¢G&£¸¶÷5D—XExKé¼_»e±)Ž1Ó[V›-ߢ'~®uwiÏs3,3< 1>< (Ùx³WŸu BNpÌÀ¹#¯Ðq8|à¤o’7_f$\ßwµ-ý¶5ñËù  ­FïDÎñ2Fè‚+ñv»ç5¿9T£ÎlZÍPœ7úó ç('˜Z¢,É_Y®Ò´½˜´×˜VWr¿Æ»Ã†ê‘$H Ëøö<Âä©ÒëO Ï2¬lB'”ã²Æp–0ƒ¸K½ß;I$lí úßMØ¡V5×&$<žþx½uª{ Ôµgm¨‰øšÍÖËr.6ÚëåçѸŽØ£Ù~ÖñTW|L}Ú8°ÏôÐ[סeèJ³6bÓÍÂÛ·¿dÀ,î\4ýZÀJƒ¯ûÿïzÒ¹a¢ m‘ôÖýzKï:Ú1~;ŽªÑ¸ôó»â­ë¸iï–íÛUî…Ò³¸•^ 'œÈÛw5¿Àä•!ÿó>Hø5¹çåcByï üm?Ûí€ BD73eð˜›fšUi6´€ãàr)¡þ}‘X½½ÕYQ¹ù99·#G#ÙqŸ’3ÕÈ«5š³YÈ[Ê}D Њ‰ã-€hRø7VËH`Íþ—6\Û×÷ÍåŠÉ*êCh'Øm:+´…¯•¦PÒþ± µøÙ½ñqQ !Â.Ôíħ’+²9JÀ”íù¬Iø¼^ %AÙÏiœë£þ½su¯Ä¿(æŒáÀŒb®TʾK‰q¯SRÄ-ÕsyÚ"7ìƒ.ˇ ç¹-X¨º¥òHqV«Óªj»Æ†ˆq!%L¿]œ™úUš lœk ÷y)Ö¨Ë,úWL–Tð0¨&) #´•u®UmÝýÒ·Å0Ó‡`³×m%qéûûým½È Ds—Þ RË'±^×@þ¯à…*TA¶îI¶¥táqìP#keCÔè pºdkHÑ”d뫆v¿Œ¼W øk®žKjodrmUúÁ%ø†06Vro?'W; ñ¤Â2ÌH餟ӒÁÅBáZ{¦Ò‘Ì ùœ^®Ì_Ï€)bƒºø„ß™2ÚÜ­d,û¶– ®W¤*ï±V£À]×'&ÀªG¤É1;ÚÇU‰^ŽÃÖ|…6r>Š:ù©Þ2QU‚€')Ú®þ0‡áxyH^ &—ö#ÃqR•º]øò1f"‰ÃÌà-t Áõ€PËï%sI®—J'¶ÈxzBð@Å\ÊKoÐ&ñá$ÔeR®af‡Î ÎÓŸA«ä•Y¿{B*[©D®HKõ(ÆpÄ×ÅM€ ®áôî[p Ù <†ewa˜5ï£íiG\ÒÀÐK½$œaŒ¢ö"l’" ×¥  yÓŽx`Ú4"c+Á@Ä×·ÍFO¦îœ5‡¹€¿ZVq—무AMf˜ êq‘_Âo‰€OÆ µnÿ™º½œá8TR`N¶&ŽÄHvß@ïybmšÂ‘þë|LL’fB貫ÜfK/j›)~Þãê=y“!Œqô|Ì“^CAõ äÃX†j‹`Ø=êÅ z÷W=ú ¯Ð’Ãö™$‹3) –kÌxÍœƒXŒÂÅvzs¼ì@FÌa`¶-(ófH†ðµÀ0¦ ¬E¼}¯«c‡yP•×7Ó.ñ"1JœŠôÒµÕ;.÷­8WÞk¯ö··ÅõHò÷f㹓X%{ÅzfãEG^> C£( Rl‹‘>--B¶øüŽÙèÁÕÓhq0ÃìwÞ?ô:í¸9®½Þ[вŒŠ=ו¬rêt]-ÕJ(ç¯@÷ÁãÄlâó¦ñl45'ÑÇAùìVù£($nýœÉ k—„ë`?»ñA©l†pÉÚæëØ9o)ëá:¾ù´#® [ƒ+sAÕãð)6g¬a|/¢ô¿6Azã$w’É £OàãX8µˆüU–õ´É(œ„k?Èz¿¼¤(0¡Þ†ŸM·ðˆ{ím’|7»‰ LL™ár=O-ÅÜ@×E[4#Óĉ,gfžû¥ž7hq²Åb”ï&Õ‘æäƒ‘ßž–^ÍðÜfœQ¥Ø–‰¾­àÐQ—,þ ˜²‘UþNUPA뚆{ ‘þô†)´‹0ò[0›–榜2¡ =X»šQç´ï2¿†£ r0wä–5‰ýRê[KŠÐå2ƒ^B&Å¿$32£S8†B“1‡œAƒ²n¶UOf0E©T`û&z Yƒ,XɉÁ)S_3ÒL¿n– …åƒ0³¥ ÏðÖ¦/Õ–·@·WÊÇ)5­&œs¦È¤4¸æLªäÁ‰Mà É=Ó«­ª†ˆõ6 ÜPrxL‡[I;hÃ!BÎËØîXÞ…oÃþ½ºœÀ3%¹iüžÄ$žõI™0ýÍbÚ\®öìí}úš [ ±û^&*e7¶#(¨™ÈEaE¤v:r(òëKKïIç6-rQ´M HdªKfpô\Tl'çj R ꛡ}Œ š  `—Hò)ŸÁדxN nÛéÒèÑÇo…YÝ–¶CÃ9Ï #ÌÁxÑ^XÍ%¹î?g¥Bi:!ÑË ð0Ø„ÉIláÊ QÓ¼=íµ^ßcIª‹ãÂŒz›šÝ[!@¬†ÍÍ^2âLjG°Fì„ùD„âhêm›%7¡Yí\«`Õó ÃèÕ´ñÁ7úâô¿»ÎѶ0Cìr¡C›rÉzn: SEd‚¨çÔ¡YJQ`Sª§vÇí¯â(ä†â×Yœ=QÚx/Ÿ\izÉštæµÝ&p,.¯0wÅí¡ýKs"ެît†IäÄ‚j؉SZJV/’W`t¥3‘´ ñ¼Æ… h츱ìZ.ß#$Sú6‚¯'ŠƒjàkŽ­˜o@ä0çˆ&tiéG'†cªÙV%SlÂÆõ;ñë_|‡÷¸& 9®Ê€Δ“Å‹¢Ü7¤%Þeµ†mHRÂõXƒz5âY<-‚qfë®3W¾6¶d¶ïjÎIm÷ú zˆ¸æ*Vt®NÇØ#qÓ:yй±§@Ú‡ÐÉ1\`]´ì.Ÿ+É:3 ß,åÛ¦g8éf’qwõLæõµ¿sï÷²v2’±7Æ7Ól•äÆüÊuÁÕ1ªtïÉ£;3ü—å ìb4nÛådÕ/:Ó|±Î( Q]x’'ÏLxöÞºã G„ÛBí@ Ï[çEÎØ|¼>©´:³ZàÎXÏ5Û˜¨·I;Ç’cƒÇÙ9 ý^oŽ‚‰½^‘±âÀA½7¾gžïÄø:SA%ÅcõørºwPBEfŠûå² dÕ‡ZÅç׬‚ÌlÈauný¦êBÛ”.ñíû¼¶’+Hƒ¥Äe6Õ°·\Èï^³pö &Z˜÷U0ça7rF3¸xÕâ„C–ö]‰&Õ Nž°GŒ6í[PÑñ‹—e®ñéŒ+Ò=özSeôŒ˜d;Ó>¼h[.b¡èÙ°éå™ ®ôÁËC˘‰a„×m÷üzšC²k8úÏ\–ªTîÏz¥ãâ6x4VÐe²¼\cç²1c„ ‰2®qYö…h×kq3ݸŽsQ »$à” Åé{Be±na÷éOÝ•]~÷¾ºw}ʦš^^Z›ÖÞXŠÂÕÎ…ð 7ôV‰ ¤½coûϯè4·;ƒÆT³Vàèê‘£ÑMÀæ< âÓzÀxî®Çl9­rL¥meÐ[>§û" › 1jqË ÀÇjîóíN'7üµwÈò`æ÷þ ܸÈ€GIßDÌU†Ï"2_«x&Öl±Ž‚%ñ%DCBš¨oß›6ãí9]ò±Ï'ò"5CA®XV»‰r.v…J£Oañ•pŠßÃ2(Òr}›d¼‘Â8mÊËð¹c$Cà€~RÇ?ݹeï·ãªd›~ã5tö*”CH€‰(oU·T íšû—0¨ê!²züuÉèýÿ2ËÔ@6j_d21EÚ·ç1jlSè ² JŸX%]L¨„š¦”KõìoüÜM¿¤¼§ •/mܹ¼zß ¼”ZñÑ”þ¤„~|_ÒüiŠb}øˆ]ù§Ö©QYZükøè,ö§X c‚œGVïË!i#7^_,„MMyZÄ>æ\u­*|MÍÁtüÖ/jµýBÏ£ãÔm­v$e!l¾ŸM–Ù¾£8hîl[…¬‡Ç:øÃ¼ÔÌçõqê Ð=,ÔÊýš{:<õ¹7%rö=œŠÍ¸‡»= 3X!Þ…$0<Þla”€Òq¦ “jŠZN?¹k²Ödù"-Í^QGK?¯-S ·uEÁ¬mÚõû%G=Õ¸įð}§ûõàf‹ÁÜsüzÔÇXrÆšëÄ¡c¯¿]ˆ±doÁub|„ˆ˜ðò@FœÞÒ.|¿&f»Á12ÃùÜ‹inãUâTÐ܆šÞk²eò`-E¬ Q÷–1`Ü8chGÅ}3Ù¶ëÞÑq¤m>׿&2ïæÅXl<‹7cä Œ¬üR?H'ˆcûº[³EÙ-×ß§:£_Âeu.×ó«à>Ÿu›ÂWaáMÝs7‘äœã5Ÿåº3±«`쌲…÷–¹õ¦ðjeAÇpÑ £Œõqä9з°¸/fx8‹ =î Ixå2ŸØÞªX!ŠbZYÙ~ê)T'ŽÝtP –´LŠzD”,¼øâóÛ~¼#P]³eý¾­,ðËäŒT““muJ®¾¬Feœ´Tù¼ÀÊ- ÒðRQ;Òy\ù´/Oê.縧spó ȱ©•NÝÿ¼Ý‹>ÇÚî›Ñ@D~ðB+ 9*úéšÇÜt+Ĩ"x\bƒÓ=d*Éí` ètiØ[1Ò“âˆNÞd¬õB[ú»â Ø÷áKÔ¯.´Ï\—¿À3ãeg5P§Þ‚%!ëxJ¿ï:¸qÝ6YKݳW8¥#þOýKp~: мìÌÊÀÁ¸M¿ÌPnô9b(Æ‘ÙÆ©k&}íУÍR• !ÊJ88Œ¢Âôü.AÈÆ‘ë¢Ûd3ÛÿÎVæ¥ÛEy2»v%7¹Ø.!¤GÔ k‡Ø 5%”¶^-¯Ò>v8Ýd×_†jñò˦ |R€r³ ¢ùt͸5ÕõeŒ' ×+©'7T'²Zk¢6nÅÝe¦‡â¼Ä›j½µeÓøxºöENüÑEš”N-ø&¼”5ޤƒ©VØ'ñáÚm- U˜r½Ÿ8ŠÈ™%d؇Ö9E÷Kál˜Ø¡òÙCX–Fx$cr± rÒó € ‚ü|ž5ÐÂØ±!ÍCºuP)· ˜O‹‰[Ï;Úï+.oöíFæù{‡§P>/òÆrÙV_}„oÇ>c*”-¶Ùqp¢“Q\ò²<{Œáš Ï,íáK0k{PcŠƒ¥ëfGœEr÷€î% þ¡t°‘ws(ÅMÏß¡F:g1,¥”Ê‹+½ BÔ“‘›×éÊ„2:rdyÙ¡3óín¿Sv ÅØ.7Ûã›äW±X]:9H‡h§Ñ²D·†Šæø²!íi›ì»îX¾Þœï„‚|sÿÕ“¶%?àkYHDûyŠ1»v 'ØLŠüýLS¤÷Ø ÔL>‘)HÒpŸ.K/RT/ø)×ÎD# Îy¤ìú•LÐò„e}<:-çýø|OS¢»öIñ½.â§‹Æ4gÉ_^‡y c\ùÍ»,ÖŸeð¬Ü¯§Ý }Óq"#ò®ó gGÎb–Ø¢Ãó!~þƒÜx!Œ¯“,‚´‹Á5s+°%Òö7ÜZÊCú„ÃXm+}ü}y¾`H©X<úÍ%æ™nù"¢/΂±`Hm±ª"ºˆNÎ"‡Þòó¯`úe’ÙYoUŽÃ‘ˆìBîdÛœñÑçòuÊ qC÷¾i[Nª]ä‚õÈ_îï„eŽD8« 3*“Ìd^nµ0Ó Ñ„PÚDß¼ˆÜ_ô;¹ˆ)¤ìêÑ¡ÿx…ëdtš8¸„0QÔ‰rnÇ”Sï…`ý‰®ÀJ×Eh‡Çž¾ÎïV‡“S;åU´">‘åûbtr•îѱ>-aÅN3d8Œ?¹»—/Åì÷S­ðy2‚¼ŽÚ±Ó3¯†ïóŽDpâ¶ÔT»¯ºáõ‚HÔ˜ÁçÎoõ´ЄMGiKvwJÞ³yÆ-+!TwšæÄÁ!Pžã|cŸ‰ K¢z/Mfª·kË:íu´õU‹D>1£×]ÆØÀAc<ÖÖtU%hiè›êô[ð+ŽÇ' ›Š#ÁBÖaø."v$¶Zº¬iK°5|SY™iÃÆLJ­ðð}vËv‚w‘Í^dóãn¨‰4í õÃ±ÔæÀú¸ItS˜/€¾äôT]B¹—dØ"ËlÑ ÎàGsúó‚ËÅå€êe‚gçmµÃ„ôA8D3Û&Žûç¡Õ÷ëm&9Ò?*á×iãjgL--h}ç³±êöÑö‚°azÊ z›I˺±ó|ïk_N¾aÚ½Ãìü»Zaïd}²ÂK/ íÁÒ0©$wY  UU–zP¼™xr•³AAÉ·Ý+£ÿé¦L{û•½KÝNÿZú{WKê|=¯ÅzpÂ’ ®py¬ëz0—©÷ õ2j‘ô‰EKX°8^¢Ë· i²ñuýj§kõöZq¾ EQ8T6L„!ëT ô£û–hýšLl ¤LuÎò\s>Y%f AxÿáQºex¯]ö‹ˆ d“´áeb_ÄP™ïJBˆæ£õ1ŸvЈӯæÿ±íW›põXŽÃW3¡!¡o2¢ƒYÎoÁ5j×Ð6äùm’F7‰¶ RäÇó$û†| @¼ÐNüAìçÿVn¢ãÉEÉ0ä%ëÆ¦u«b ¦«:9ÛMã/¸'zúèI »µßmÙ¦;­råóiüغ=ƵãñöGÁoŸª…ÉäBÙrj“­c‡ÐW»Tj!wu­GbPPá9ˆ¾ßË1íÕ=3²˜Ú ¸À¦’~Ü(¥‰s@xÇô½O«ø.÷5VèA ,3 :âô3¦A’ þJùìfTã1<µ7®?!Ñ·öÁ…³k…ßáÕ‹8\%?x•‘÷çXm„á“‚翼qG(mØRºíÖùÎ-m‰í‹d~çÂB^ùÙäI/ŸTú½¸©C¨ûäR*{¾0 ÅK92dâE@…Ü’–Œ!«ÈFUþY̰  É!NWèò“.œÆœ`^¨5ñÕÇeôw1RÅ™¦‡ ÀxG)2A»†l—cTÄ{¶Ã®ªù"[¹<Ø{,^€ N¹;ÐéÂQóì¥äË ‚Vm®NŽ«ÊÚP÷Ĉ£’¡aƒ¨ž{$Œ7þ Êw …n¡ò˜Ú=@©Ù"ëKÞoœðŒÏßÒªìήò\‘†øžáòêÈ›¶XŸôv«ŸîÓ/u)ãýÞ£~ 7bM8/&òV2ñ†z8a÷ÜÜ‚d?5H:Jö¶“†…!zÒ·F¿l{Ð,@³TIu‘ö3͈sI6÷û§:±[hçÜ+ÀÈ6Úí}e`I°ô.$Ú­øI ™äm5uBÐN«ëvp4üûA€©“ÁP[:Ûºõ©Ûìru0 O‘ÄAUPq§ÔÄÖƒmrË«¶HÎl/ùf¥!‚ms'váTÊ(v²€Þ­Cyæ®kJA4ƒÙ')ŒV~¬ŠÇ]#k—À êóàáòÂ{‰ö˜y1c™êw!…!Ü¢QÌfçž)á/j¾FÓÔŽ|«Áé 0âÕÆ;ܾû,NÒ /‚ÌGAhøÓëè8RûID!Wf·Eüî¼p¹ ®^´Ið&]QŠæî º S Z£oÍ-‰7?ˆ|=[¶Ðg´µ}˜´·}×>£Bqžñ—¢Ëga,i³P|[µ¶msç--º«_D‹”ÿ˜Û¼b2ãlOÒÏV1 Ÿáó¯CB£eñÖ¤Ÿ¹f˜ã¹±r;=Ç'úBVŽ“Äªl/6ñLÛ‰S©õãûWqêú9/w:{=õnj«}øÿ²wý•ýÓéíC^Ü­ŒøNŠL]z=uÛ"ª‹ŸNæ#%*‡á uú}'&ï¬S(ÖkÖëT(Ó¬Që×vÔ®sPŲ›¾ùžÛ-Üá’`¡P•2Ä? p¶?I$þSôš6>þ¹´¼â³ÿ#=7œã‘”¨Î@ýG ÄN=/è)èÞ Êßæ Þ¦O`‚ØVlwZ $s+Ö”žæàÉÖäI‰‹à*î› ‹Æ+^¦vcLyO1ÃöâR0x¾.äß_›hº0õ Èæ_ʃ5Ì µlÒ¯• ØK ghû"‡¯B9ØñU ¥cVVø¸æ³fŽMyÿ¬¯ÁÓþÆo{A Z«àó#láSü¡?§jÊ]ÃX?Ž*­V øEÅž¯4­L„SÍŸ¯¾;ð´•œ-‹ ´òÐÝû ükõígpv6\QÏZ²ÄW°ZÂkYÓ0ôkÔÏ ªÀri9d‹¼¿óh%õl‚õ¸®¥b¥’¡ P÷óÉÒå¤6¥}ûMK±þ³ÃoR¯K¢·cPÌT¥¯?¶ë(;uÿµ…!d@"Ÿ7^©&H!…Z–’Ï|+abXˆf¬-KOO-Ò‰\OÅM@ìðéQi¬”YÓ-JÀh)ÄœJUú)*U©äjÉþìÙë£L#¸*6J {ÂÜ5–GN‘<¥³$ÿp/uQÀŸ¨›íõû%D=ѺJå…@ŠáÄ0(1D&0*²»@AŠ Ð´0ûK^äC*¼g†RÌ…Ö¡>Ö~@‹wý‡¨æJhÅs"À¶p„å6¸LA&[@©¥ 1%?¨Ó¾*:=`²Ž„„2§ª7†B1½Žt#bJŸHòHXŠk¢„e!"þÂðÚ.*m’iWõú(z —Là)˜ XU¾]¯ÐQ;a¼//õ逘ü?k¤ë µU“1Åùföo'´ÁÊÝúKÉ{¶PûÒÀÍ[¡÷Û¸ÊM©HéGJíÇö%ÁŸ§È(ÖÝ•ujšŸÿ>:‹=)ÈØ '‘•ûòHÚÇHY”ª"8-‰…g^#ÎÚ¯»kïë"(‹ª¬ŠUU¥o¶¥/ÁT Èê««²¯[ÿqËRÖ¯˜€Â…½–¯‹âf­¡uiTç6[îÕˆŠ+Ôl¼«GA_¯W:ñi9Õ -ÖÁC£‹‘/ ý‹¬ )åµCDCKBSƒ‹&…rY, ‚¼ÖyVÀ¢:/YÉ-6oDn²H pd.TˆdNÀXV9D!@8Ô ':˜ÉnDkADØ}¢@•r ¨„˜20C”Sв¹ØùË2w¥Èµ0`,rzìó+i(ÔÈêœ&-GÉÊp!š@¶¸ÕV˜¥ÀÈNSâP˜ ZX':žš;ÖQ2xe‚ÄÕDº,¬Æ‰­ÆU?´ØÃÞ§Fµ±€:r»Ü=…û92G 0@¨„£:¼\ß´Þ胵êJn–SU°Z$tMeRß¹0íßz犦ö?[u³Æz¿¾fà°q›ÃÖŽ¸­ôŠð–¸ÌΑÎn^!×;¨k.k"ÝÚn©:à2ÔcH˕ȩœšU-oÈvQVv$\ÑÐpxÚ­•5è¡@µ+SWÿ*€%äS¤SKúm)½@&`ÊÉ€5ä©Õ¸ZOzÕ'Ì…„èÌr²;áÝù)(”`2²¶žÚhíᅮܮÁ^[9Dw->Bî‘ëˆé £÷ljëä„p. ÕÙ+ÿeë¹Ê¢¢¶p1ÄÔv:&—¢Ôw‹ª±tIÕOW€súìwùœ'nSGPÛ÷TÇî6ž½áÊ¥ÏléÐ]W¡šÍ¿•gÎ Up7úE÷Ú–m©jòô[5ˆÒ¦Û÷, GOb\Úïe˜ª\6¬ч;KTgzï ±‘ÇÖÝPn c8üŠ•±Yûc§»Qœü”´“W°UâW²€c» â[ü2³rûXäÕ'V}îW°+o86|ÊË#oD­Möâh2=kVØžÚý`}ýÁ~˜0§²_, ÖŒZ”BPÿ/p—%'oÒ]êÆ æ–×ë]µýK—Z‡×©CukòwÝV±¡5$Ÿó‡l¥Ô ‡¶ EáÞß L´°báô3@«@™Ý€Y-É­ô5”ÝÄŸ5RÝ:@½.¨†öìÍD× ÿº$Ý&œ$…úÁ@!.ôîÅQA´t Ü/‰Ý\ˆbÏÎq m=e%Ý-’³½gräF¢bV†Cþíô/×UÛN›ÒRÛ¿…Å[<ªõ‰j»h¿#g‘ÆM™ÕÞ”½³ZòPk/úwÓßv¾˜% |$¬†ªÜˆ¼o´ÂQ) ¼WLû%™’¿2ý!·\ µ)Y|´o¿Ö¦& sž5ýÛ€sÃOçÿG•O‹z{hÔ¯·ëßNÿâ ‚v *³¥ [ÁþBœeT¥=^ýëKýõéRë#-5_o¥u5¦»¸$áÈ|ü™·æCL:rÅ)„„̉÷Ž»ÞÓå èËã_ì"ßÈÛò1âÅòú ß™½\@®àÐh t ôùé*Òb>Ó„aBr±{™‚¸½I9(6%°Qò³zLAÕO&»0ZüÒåÌ1âNw#%o/°WmJáˆú}HQˆÒÎî@…XB%5ÒM8.q\­w`WI/®18¢8 Ú¶¶4Ý¿ÜÏWä^ôîßüo¾.p+Ÿד«åÅ TañšƒížüÿMÀÊíq!r…ŒÅ››RhSuB*ø€‡½ Ù­}Õ©lõù£å‚–Ë ïò<„UKÝ¿»ãü©ÑH‹àãY‡ÁâÐu˜)nv",1, b(jú\¯Í³áÚLNâ„ó&Kœ«ŽæCøî4;Æ—0ºdž{–9íM‘ŸOX µâ“ãÃZóŠý?âµÿëܺýÏÝ“·À•Tò¿¹ƒwç‚ϯñKïRŒœð ï'>.¹ÂD¡âQ¸;!vÓV#žL &‰êðæ™¿à<¸•ŠðØÝ]òí~Wû+ð›Bw;6EØ0±›È.¿æ¡U™'’ÉÕ7DÂ)Dí#µÌAaê4a/Ú_Økʆò€kR‡Á‰#6ÀÓƒ»…ž¾Ð ‘°šº€ÛIV ÑÀ9ÅvyÈp!±Ì:Is ¤]±‘è\#W¼Ý 0Ъ気˜$üÑA@‹+f/ìøY†lø«Ü­;Ë$XêéÁ¢E!ïÇÙ|´üªÚ² ¥¾½h¬˜¬±mSÚKÙwŠ/Ò.ÖVïŸKÞ§VCÜ‹5…Í\êj=~–ùYögÒF§:ó–!B…ì ±4zðf’Lcaô©IÜ2#$*P¸LeÚö¬–£+Ây}¤Nqe—Ÿ¥Üêœà/Dð"X‡¤í%G¡±û%«»®­Á î‘[8„qð£ãš3ã]•òD— Cùæò*b}áÐ7¹ïí¯c,Õ×vVuÆö¾¦æhøzW ˜xl;C©LØ«™w?ÿ,k¬Ô”qî2Ä9è”ß#SÈ2â®7ufVÈòóáO¹[çŠ$9£}wˆ‡ôç(G$2š}[ü5*ÏKu »·ê¹L#Nê̵‚XõÂBXt¤gdÁCºôÜ@:°F½*Çî‘.Ø· †°¹R_µÎdérÍ€H—@nC—%ý²ÎÀÝa0Úê!´¡ÑÏ0·Ü뉿Gêül)ÊG.Žü‹NKóoþNƒ«nÁIfO1FLGÏÍþt:þ$£) [çÄëó‘€çgÕ´ ‹éª6ñÊ_Óž¬’¹/i—Q?šæVQËÖ{ê† ïG†:¯û}Å¢ÏBp`|ÇÅ jœ†ÂÎJRÌ«Ãk$Ë1?Á:é (ß…ùv®´]’=3ÿùõ¯‡ï5g«¼é\·®ò(Êí¶èŽ o¨QÞ1ùâå°bÐÇ+”V/"ª®rŒúB=IÕ.FÏÁà"FÄ·û9ÿã©Øšiµb†Æ(³)O>j ´­Jä?½I\d!?à)Ïvƒó'gB;_Õþ÷ÿèôËIÕ\Y5f%ùÞÄûVŸÿÆ6Ѳ`ãˆdÌhúÏž§Kñœf¯ìu6îÙÃÃÀÍp#$ÁS€—…šs9Ù0EƒËg<È•W2Å1Ǩ/“ ´ûí3RéoŠy ùÊŠßö© |–30²_CêŽý ÙdÛ鲄±R‘-W”"ùÞ¹Ž~ÍÊ_:LƒaFÁ•6(Ϙ?šRC„Õ¬î"o<4lʾl/6Ç]€‰jé»ÅcI1_ ¸h1€LÛOóÈON!•ÐÛxP¥ÂL|÷³x !û7URïÆ_’Þrüî Ás»{=‚8תùeaÿ@~áÞé°|äg¨$œü.«)¹ƒƒIêæ¦jUló@š1S;PÅUÃFTÇî·ò»ÜwŽ~j~³«¼“Œ‚ŠÆ‡FÒ}ÍësªÀ¼¬¿Öh¦ÿDwC¾B6xwsN™‚!Þ`gYÏ’¸w$Õ…ëÓŒbv´(¸¸)ZBý—\«ø’-Gq¢Wh‡1íKÚ ÷µ–oìJèåÈq“ñYº,y6®ËS¤‡ŠQÔI„¹,Ž7Ø"úrqužH¸Z(Òo qÀé›cpEÊ·ï28¦P™mxYs)ÑrQñ/ömkIƒ‹Ú®ÑSLÉs¨ähä~äôeóæ; †9¬µó-?X§åü¥¯"' !i€ß%‹†Æµ×—Óa«ëº3®ÝõÛXu4ÂÑÇrjyY¶ÜE{<ßYÀF³±¥Ì3óòWÓ˜~C¢•c¿ÿ…:wW€ÏB(Þ™}çz´&¿F5õÎÏ›ö¯gÉVØbÎjOýI ά @*l½Æ„¤ÑƱg ¡áéõ툩$¯¤Ðay=g<Éóäd?¸þPGZÆô™8ÿi(çÀÙÎ?ŸqÙÌ¡÷‘å%ÆwÎHÏ£àyXåËùÁÈô¡›'%Ÿöò¨ËkÖù'úr×嘛ƒÁ”‚~Z!EkÑ Sç<œ–6òª¾ÉÕà yY íyeQ%­#ä;•£°ßWóº’§iâtùæˆ+»YǨÏD¾: ¯ñøßm£Ã'¼~ˆ{eã$Â&~SîÚ³$“R÷~Û÷ ¯T&aq©dÞHï,D0xßÛ-t÷c]|Í—Ö§<ÄxžŒ!=n’{ðç"!ä«UÙþÖíºK˜YçˆéÅ©ñ~ö÷E×m(T&˜²gUþ ÕÙÖ%¯xƒ9"õN=v/ö0*ÖHI¯i=G÷ëH°Ac¬{¯šèC¤BPÛÖjË.1nG¡IGˆZ÷ÇUž7Å@ÉqÛRÊ䉿ˆz®5ß_|/ >2Ù·8Ñg¦[ˆ<öçøàJîý² ÿ„À"ò,H6ñ\ÉŽyí€~rAŽ– S8¦Ò–DO°Î·óN]ºóX>1>£h# a¶š¶q‰ 5pU½Hɳ>ÓŽ'l²9$¯øaZ –{ïë™_­ÿ[©gôHŒ4X£Bþ…rÒNJ¼°Ô„ì$î.X'>óÈǹ9š1ù' çj”±Õ œC¦‡ZV€c¯§ôEy®£oKRvTú…|ÅË¢È_ÝCÙm.ŸºpáÅ"Ðaûð;Á¢ÍCs{€ ¶Tݪ£†á9¿ºô«;àªù'4xjˆ=%^Ž˜OÛÿÑ3Lï>g磿2{…o?QÞíF"Õ¶ˆÓÞÜ£ú´hÙP)¼ƒoR ÏáÃ;ö žeîjn~1BÛÐ_Wx¯#yª)n€(6ÖÍá—òNÌU¯¯‘Y½«¾¾Y0K!9ª|à¹ü¼ºQà £±p^~b»}am¤¸eôfs!åïÑuŽ8C|Š&}p Z¾¹šDIÆè øPÄHUÚ*kþDveùˆ ˉþùËK…VHWaÍk ƒv^ZY•€ú‡‹ðƒÂÿ>M:¹_ކâ\‰?ðþc¢Þ)½i3쨩GbÌH¨sYÝAß„ z¤Dä{¯áN-À| W‡˜f¢×I£ƒšm7eBg}Å,¨Øê@‹óÍD¯3B!¼CëÈÜëNÃNÍĽv²Å  Z>å—h,géœ'ò¡'Sn —á—füÂNf-ÎA¼½*¸%¨lºya#Î:o¡±è6íA¼e²*§Ä?Ó1GÒ‘L3êÖ¯OHælâ-Ó㔠ʧ$‹ül4MÑZD3BŒ+†¦Ä}¦dõ ʹô}ù¡Î4´iÿ:èEÃ`9zy÷çν0˜¦¢K·vôAâW%Ä·k½'J§¦1+˜€=Cß~(Q!Õ}”¿u–OÚëÛ®a€õżúè­Ük‰†Š‚ë}Õ‘¯¨ÒWÓK%þ8^}pcñ—ŠÇ„º ÇZµAç§š¤´@Âç·ÇÀÐ3R÷S‚mxšHËA‚`s?»~FÉ/"š*” ‘ûÖ šf³í†S ©É@­lGdê“ õŸABeñßißOÂVù¢ùìk 7îˆwO/ß`9*rtágu?iÓc!D Véée.s©) Â}(ô]¬¥8oTàxÆ Õ¸$ýC ¡FݤxуÂJ¦õýêoù;¥³Üý¿„œQFHØ»£|èûV°›=Ûœð}Ù —溳XUyaÀýÅ9¼9nàï×½â`÷Œÿ¶ƒäuq…õl}—Õ[²åZ¬¿0˜Ï@'ù_èùÛpÌsp&Ç´,¶\p´»j¿'ÍoƒÝ vo‹ˆµP)ÙT`¢W—hÐ!¿ït>Ø=l)úý¡(óÃ6ã9L…÷`†½Øô3ÞÓK¿‰õ¢ƒ¾™ÜLѬQߨe÷cÌßî\È‘ /AL^™:V:ˆeä¹ÍYÁY¢0£v, Ô•Ò&G7åŸR¬B8XBÇÉY‚qZV/PQè;ÇóäË5 —5|,²Ðé8qÕF:DÑlUf4Øîʳ-uãМÎDÏcFi/Þj“ÆsÚ‘-µU]U/5¯wÂkì›Ö˜)ïiöÙÜÑÈÔúf½óåðÎÓ¦4XKà]ÚÐ:¶.ˆx¬ùÖ’fJ`oVEÕÆ}BÕ\M` 'Žy ô‚Wà|>êë¥ÜvûmÔ¢ó²?Aï`(D¥-°Ò¸Žak(â`¿üŽûdÁîR97p¹ùÄû²8¬ˆ1½Þ'Æh+JÓV&=fxó½pÜT}„êÜ^; ¾ “*Y¼Å„Êaš¦=WÀEðíeckU ôß IÜ¿jÜ?)6&_td}ªÜÈӯΉŽI¡†- –# ›K˜M)Ô~êžjÞ|ÉëÐÉY×¶Y$»Û6ÞÞÒYm ¾.GÿÑÛ_Ò˜¡¤ÄàþXýxø ;rÓ}¡ù!äešu’Î;Þâ– &*ÚöÒï'­ªß ¶ŠŒÙØ»rý3Ýg]3_ÿ¼G]&4ÏËtnJ»Ì'¬ã;@Z—?€ƒ3Bþ”̹iÊQ¾9Ód}z6V½c*hLˆL#™qªgiZQü˜¯,ÿ#f0†\õ,ÅÂÄH¡^D~LAVW.\R€@GíÅ??)bQw9¼±ZÏD{Dÿ~¼aiPßTæ˜#œú$ùÔ¾ˆçx†DB•É„•º ÆÃiÏvTÇÆðæ…˜q†ÌDwì ·6ž¾xìÒ×ÑR5É$à õƒöá‡Ý–Úi²î:êÙíÝ-æGî£+£,«±)T•c bçߤŠ¡Ç²œ±ó7u™ÕÞc5~ý†Wˆj™Îteæš]cQ]K®}axÞSDeÊ“rf6©°”Ì0§‘Á¡¯Ë;œ9;¢ÎæƒÁΗݢÞ):a'‘x>M:ÇtÄZ£áŠ;5þyj¿¥ëH÷rÕŒ€ï‡¥Ï-š{¥çÏÉFð ¦Ç²g®1#ºL%”þRÎ͉ãÏWæT‚<ŠUNzP‚¡ã©*´gu›WIŽãîsoŸÅ€>:×QÇ?––cë]ÑtO…"ó¶î ¾ûÞ¶~ôà@õÄàãfúW!¸­®š‡vC5UÀw±u¾úimÿyÌ~!p©aÁ³´÷åéÁXo×°iüé­ÆRš øœ4~ÝÅ8—ý_åuÜ(—`~¶ƒom ÆÀ¢æpˆ^Kùys.ÿQ áïÕÊ?Ÿö‚œõO¡;Ýuí§HúšÈðÓwLŒúHMsÄm¾°/ÌÎÍΗ¹ãí$÷‰†ú¼ò1Û£‘ê L? à9Û[wûíLßj>‘®]èPxîC&µÂ)g •¶3“åÈ `C ¿æ6î®#…²¬¾ÌÈL˜wf•ȯÖâ‰D£IoÞ“^nŸ<òª§A>«ÁH3ÞvòV×—ãNœÃT¤ˆ0[Mfƒ³3‹~,ÇfkNvã¡ÿV%»“ü×&\5!yŸËŸV„ÄeœÚñÐ3ýgÓ2"?y<ïßœ^¥]"/ª„€“t2TE3ouxìÃ=¸*X™cãú‹@0¡‹XˆÏÙ¤ŠÃªîöêìMqsÝìw‹„cýÊûïIþ2€¢¨6(ÁêKìÌ>àá[âÏåø~Í›ÄÅñ˜‰óݽFMÑÍm@AŠ¢Žu YªVÝ:@¬X±ª@ÅÏÙVU'’ÙVU'C–Ûªê$ý˪Ô!H æ*ænÃCV3UnU›sþÞþÖ­ÿËj’kŽ?4†ËP ÷í©¶. Ie0*ġ;íŒY Gk•íäZö¶¨U¥g‹e·îÇ~kõ‚”@÷‹z=Qž¨ÚÕ~‘Ô=ÜMÑïÒ×4UA.HU»h)Ä`9%Xj0½:Y˜åKe*-†i%B€åŠI ƒPP…V—jìabUUò­ãÔ©‹SA@Ÿ\E—8<Úa‚‹–eAÌ%Ý´'c@];Îà.Ñ3ý¬ÿï§…JÏþ¬ÃPÓµj‰ª¤Zb¼/zunÛœ6½Š_UÖ€}¤T‘°W•^·Ä'øI U)µ+ôñ­|·ÛÁµˆápsÉÀ"òä ïµûïžþE;8¿ÿŸýM‡Ó§k5’4ûJo5‘YHR …“á`p9Û%Ÿfz¯ÆmrÁÆá#T?öp>7ÆeKg>Œßg°b‡Z*#˜‘ïÃ]ËíZ ¶ôËÐþ~æ™2²€û_%Tiu¡oëóÀ8¹g!UoÕòómPq¹ÔŽDº¡ßÇFá®V¤ƒ¢¬óVs!míÔ§µçSõ¤‹wY«uN7€ñ ûÒ6¿Ü­( WBd©‚€¸L„ÓèÁÜ{Õh½kÒÎ.«¥2íz´~ø»ËœZ ó­ ½óÿ@)¿3¨ l»nÕêÀP ñ Ó+³x‘Žщ-mÅ·ÙâŸÁu Žm—WÑÁéf»@(Rç"6LZ4DÇd:ϧIò-°µ,ÂU} *§ù[ª¤¾{ùÂÞß µé«õó` Z2í–‰F€Ëšßð%Ÿsßñ>â¶ÒžúAa >˜†¸ªB§O†û(D¹‘ w†St²:Á~þé½X¿žm§Ÿ€r½wéüΕ@ø¨''ç!Ì8â¤,òr‹iLÁV!Ï wZ×Zbã*U „l9èù ×2§À¶*š .Xª7I$ Àé1Šxo¹iæž[úNƒ½œNhÓÛgÔéJ1Ø;­<_\/Æ Av†ù ázÌnëÞ °„ËEC‚·ä_ÇEÔëó‡ñÜb‹YS[(!zËÚ?Ũþœäû§^Æ÷ sƒáæx}Ëà+ã>5ìªöéH"ú6Êû¹Ð^…AÅÎ?°­°Œi…&|ÿ'ÀEb§µpÑpH•Ó=‹y¥Ü¶¦»vÜ› ´¿Àh¤ `õj0zý(˜Ñ.]çY¡“|g`&á̵é`‘°9ûýßÕÕ!Ž0k(Ä\à<°H®½±mFka!°­-îr¾NÅIƒB|´¡¡‚kpäʦh§Ô*n½áÄ|Ŕە]Ú„j¥˜åÔƒ„8îï­®»O§H)*¸W†EVÂf!ìà@èqn0ŽÌñ (ßî%:¼ (TvÓ;V²Ñòuš“¢ëðmÖ@wiU¼²öf £*?JÙ/¶tsˆáúzòún‡`1÷žÎÜ~Tws²@ßEƒÃHEuKp²`×ÉMÝ–€+1Їf×̱lÒˆ#Æ–1«*º0ÿ«xÔõÁÐØÈïƒ_´ªYxÔ4Lçga_Îutf ÃÄôù‚ý;e?TÛõßímñN9$gIRƘßf¥Ž&ÐŽ×ió1W-±ÄÅI–DÚo.Œ…ÜCÙCÕc’ Ìç9,;ÍKfߤ‚ÈËAÅ¡¼¨Q¸ú߯—!8Šöß¼6ñ·U.Øz-Üóv¿aÞo¤V1FûŽ7zø`óSb¨´ß‘[„ë×;EñÕD‡íf]¦&/¢[ô«†x6[MÜôƒ×ý#Ái Ri1ál䋦7·KŽ]ÝÀä Ë@/F• ×Ç~‚N’Dn"EnÏõ"€Ñ.Á²~ÝÿPÞ¥ÐÉ>C^¼­˜½Ó/Æmò{ìÜÌK‰}tÆDËÞ¤GTþÓ=›í§œï„ôsm¾œ¢Ù¯E‘•êò‹2H u3p}²˜8H²=Uâ€Êb¿#ríL)Oÿ8|ØÈ=ÑN ÍKÉçVíú”'0e›ÈõuáŸõ$Ý.úŒõ.+¬Ïr#vÑÀ5«|2°8Aé _ÅFr|?²HLñ³ˆ,XìkÄ7àÒÛí/±³§ãÞŠ.B]Œ;È­¶P¿ÂFÀ9Dɼ“¥ùÓ¤´Õ—^SI_Ñ0*»K¨¢‹ÄEq´µì"nñM}?ánÞ+ÝÌÿ6ï^û*„Å}ß>BKNáÁºàH£F .ÔµØî«&´^:ûoƒ£Ûî?‚ ùÕŒFñ‹«4¯`¿#³}2U¯ÄÈ/yâVyí ÚcÖÇ4¼öË­ëøµWî<Ý€›Ý¤kÓÃãeŠKŒ ðõ)±2Q)ãî™$ê%;Cvµxm¼& ƒ4@£\PÛ=#ëÉN2ʼSò€Q õ>Œi;îae\ùŠ©þ„¢e”€ŽçáyÞ1ì[˜*bì&ëÚ ˜—Ãç$dIÊæ‡¨>±ˆŽqßí¾)N¹’üÈZ5Cp¦]ц^§J¾1\£Ù—Um ¹Ê©W¿ˆïÏg@ÌÁ©u>Ecž%ÕW!FÕ˳¡¨‡ë=tÔ¤äéÈe‘îI°oJ0B½úÆ0% ;5H I"ײs¶1…ô¥Þ©ô É“æèkßvi”³P4þç"iV'ÉÕ0¥_ Úpó»¹óÒöÍжJ:»Á´ËwA5È"_~\Ñê!ØaÞ(àÊé6¯Jiy"&ÍnkëÚϰµ«0÷ä\è¶·{#”ìMÙU«è×ðúúHJh‹è€aÅÚXpõ Õ„‘¦qJ_ë•ã~–#­=ç&»+ã0-ž"^NïG_ryác®ÜÙJ”§úÌ%]ÅOf•ŽYõƒ,ƒt˜¹“…“\ùµõÏO4&Z8ÕZ1ã®ERÁ3.’U7Ë4KÛëoWÖÉ×ú’Qw#ãü6Q¹ölõSü†ÚÌ ¹tÛ¥þ:Òh4³ßÅ=¤GïiÖ¢Æ÷öÃ,¿v±ù]Ûxj,ÌõÓ;jKˆ«09„ŨsÛË>;ܪjÝë-¢ zgåLY;w@•” )ܤ#öf(«½fµµÞW€l ³þÁÜÎwáZ6ufRŸ”õêa(âK YàÒmà…š±›ó¸T[ÙÄ ¦§dÏŽ 1Ç#уÅë¥ia©Y l”*ž•ùÁž©™Q.°D‡0lãÝÎv;ÄÔÖHŽ—wÒFßh?t !À?8;éø©óѸ)~"ŸÃ^_¾‰cÎL‚ål83Þ¹¡-f“Ô ßÕ\Iµ[Új¨žë÷òQ+Ohâ~D¬„"Ú©ÍãköˆS$¾ZV킵ãêþ»#±R¹HÐk«Ò¬í.u52 ‚± /ÆÞaÊTÛ'ÐËÐ&’A Ø¥bºÙ®@kæ2<ÞÎ]~”ƒŠ±ÿæ5ìY؈y¹TY^Î $ã’ŸR¨þÙ•E)˜p^Û­:Øxjx S"Ƕ1$Äs;?0: *)ν“?æd²ù_¬a­ÝUéÇð"uìDaåb½þ¡F¿3\2~d¯ÔIJÓHÕA ê)“]®2@ê yDZÃjŒ•­@¢§ÆHãAi;¤DÒNß|%Â%ƒQ–æân@k[Exk^6¡r³Žûè̘Ã[ïÚ9ÿé}q³Q˜‰$§^‚%ÇÐ ¶‰ú‹†±E«… 5¼n¿ ­uK&FXÇ9ìÜ Hι­–Že±`¢,qX¢2X¿¼É:i¸&Àõ(—H¿¯’EÑò*µBà{‘gCRC $ÞÜU=ãg*œ‚‚ŠÛz ÕnÖÛ˜ó/¢¡ƒ‚Ü'? )Ñ÷ 4T~ƒþB‘ ÔÒúèð¬¯m=m®­îÑ=œîP»ó f ¸]A”x1¯à(2’$a®g{4g-ÿžÒ‰âôKÉ3GBD]y}ïè¬o¯¢‹‰C3¥x·<Ç=óèüJúλcä;óýÏ:êª$༨‹(§Pê%/Ú<ë½z§â˜®«&_Ã?ª"ÑÝÇjKÉ«®æ±›eƒûcމ`¢8&êF¾H«” ƒàg$–éP ‘Ò¨ìâOŠë¹€w=Q±ä‘ZxÛGbö)¾]ˆ-s?7à Yw¤dˆ±ézAgøŒMpq¨þCóèK¬êëã÷»ñÞê_K/j—{˜gt=’`ùÁCe4· ¼9¶k¹•arÀ=™Q Õ 'ÁË8çAô1#¥8L¢¼Mòï*5ªã&ò-¸íøô“#YþC9Ú}bÓY¬og•ü!_@ŽÒÉ6dd“ƒwÙ#ü;æ1œýFt²M¯ÑÅ`ý's,ê?öà–ÿýñ˺çy²VœWÒM6 ,Í[¦gû—´˜Ë¥Kö¿»wŒ@ÝÛÈF')sNRDhA:ˆZ%ZWP_œ¸#9rr`Þlð€HKÿÅD3¥JC““#UºÆ1m­nWŒ1¸AZ?d&fo$quî2DJíµØ¤ìz1X:æ'a•@Rð ‘:y3ùÁ“íRa–\2`ŸæÄkrÇâØØáW§Û¶ ôÅ‚ùññxŸ2eôÚŽ™K\†Dõ;I#݇‹pEoqÆý Û9Pl¨f^–ÌJSÃ×ÒP%n?© ê!+·g:Fä™ê8¬Y-SÏyCe¹¦ÑÆv‚‚i¼ü™¨õ-â†yUú «{ñó¦]à1~ìQçc±1õvAžË¬Žs±ìÖú¤u´‰Ý•gDœ^ÐúKØ÷fð½Cßž¸¼¬òHþM¶¹5XÅjÖ¦UTG øQ³Ó¹àó,ÝDS(µ€Êëú–òºßÙµqrš0ÞÿâóþLLÜmòĵ½ð¢pŽÜBü‡ßÁ@5KÓ8$–Wö2®ƒQ  o;cª¢+®W°Ãf•Ë >Û8ž k;SËÔó<«Ä½>rre­y«Z²Þ¹Òñ dŽ:冸ô}ÝÞ/¥Ù*­òžÒÛóÖ št Æa2·y´+†¥Ãî ×øú1é2Ý­eä:®æ±©¸_þˆåW‹rËI?|sD~<w쾩½Ai¬…@r›Vwî¸|k–WI‘AQçeÜa¶’|V”6#_Ë5ƒš"~ô˜¨|NÐQâ„~ïûÓ¯ŽÿdtèA§?bÑ24;¹ŒÈ9ù½Êÿà Ç1¦BªEæWâæ&Â}>ôˆêg¢{OçÓÜy~x-¿ÐM(ë!ÎBÓ“Àö„&yâæ®Ûês.äI+|™Ó™—L%擦ƒráM—#¿ý¤å`yJŒïƒkmӪγÕÁüìï¼Ø /‚YùAvD˜ÏìÊ&jË a¢ìŒm"à’à[åeú… tÐÌNGYÁ Î]ŽDSÎ/k°¸˜6¡þ€Ø Ûу€ƒºœ`œ•"YÝ‘¡‘ÎØA€âQÿÉzJáœvç¬wKž³‰îÐyx紃͈éª=×,Á6rÙ-ÍZûáæœ0–:X9ã5Ê‚4u2÷ „~%Àýwœ³çèõv`×w¤-Я–)ÏW„¦ýöâѦJ].ÍMË/™Ç¹Ï-šKÎOÅÝ0N͵‰,¦fê3 ³¯¡Á]m¶oŘ¿w#Ä[ïckk¾ñ¡æ¥fòªC<¤›/ãàË=­ù+xC ¼{å¼KTnŒ)§hßûçXú9ð¦7´.î ÊÄ)›Úã™un#t^ûÜpä¿ÏVÜ}?÷Ôé‹V€ÙxXß YÀ¨¤4îªÒAï öYA{¨8uýÎôAä‘à_~+¿»åOÉ5w+ð£wÛÎÆ“™{ó¼‰Iß r·áKÒâ¶JËkÅ„ãÛþDî,^É(v¥‘®ßãÎÀsà…˳ßG–íy#€wíeÆ„¤9jÏèÑó×¢ƒ*+â 4›DæïsŠÑiu®ß)Ÿð(¸€©LÎÈÄç¬eDÀåÓ9%t”5Ř½Hsg£áRÖ6¿gAË"+«Û{Xmô4гI»AÃDˆWNl:¾-Ž•óqš_•Òyþ9“¦Í*ƒ òGOë@:ÊŸžE\­©£‚>f;_þº†~sOÆ¿3$o¼· Йtn¥›Øyøßnï¼ðóU¢QùôKo õø2A›7’Y°ÅOÔÎ7#³1¾;‰·%gR¤%’†ß{Ë@AüK·)¶z"Ëc•ºŒÌÛô¬Üã-‹ÀÝJ‚ )܉4{úüzëâyLøØky'ZQô0¤ëú19 ¹ù”æëÓLêAîÌ´£w3«û9Z®mÂöΔ,Í–?;èÁÝý™ië`µ 6¶¦B„ܸBSEo¤äƬƒƒ !„˜G×Ì4`°Ö}iï«^àém¿"僇=lRØÆÿpf£7þ¥ð‡f€¤g<婊F4i¾þ\È\ÏŠYi>š÷uŸn{$Ä_§÷k®¤sûK÷+|§;ZmTø¿ßm1QÆbA¦YM™ ‘ö^ä…¶ÍÏ;€÷ ¾ƒIA†X•ÂéU¨¹‡9f³œ ÐQ:’¤õݘÎavöJùÞõÿ˜@•@Ïùߌ_XÙ•®„æóPøâ/lu2ìØÝ¨PN”—Ü‚÷¡]å*ß?fV†…-â>HƒSH¶u'œ»üêˆ?I W‚õ ž Œ"+¥Y–ˆy5ŒÊ™×!{7i`R§«E“-õHåA°9ߣ~/Ž )(oÞ󊫯&°Ù#Ï“¢f5ÿB²èÙ-ϼ9ˆYÞx:W+_sYú“>1ð,'ŽoHÌèÿÑ/ÓæLÖ•dY_š»Vèˆmãß+ó³ i½†–ÎjKö ×|EVVR·‰—ÐQ[­{“¹­jK„­–&þ®ïü3[Y¦£ÔÖž!þúeà{ÒEZMmª †›œÙ/™ÿÏ¢·à@Q9MšÀ[‘Ìu ï,ÿ÷rãÜ™~&ÍÖYý‡Å{gEqÏ6»avÿ±ñ*?î$éÍ”øo‡ŸàÔ…wÀD§üÍÙ“ëwÐe'¿Ê{)¶Iægä‰ÇåNûG‰ŸÓÜà¢ã·8xdU¤Óp7Ä"³_µóŨž§õ˜ï§Ý“ô h²þ§gå*5#Hˆ/Ž–å†ÚM ÞçMQz“+s˜IÒú}4ÅZyï eÁÝm:€Ì©±ù˜3A°Héðòw˜Mh5wu¦Cƒ…òˆ —_ÆÛ›hNGÀÞYsor&§Ød¥f³B˜þ!Þ^YTgÕ+zïæEæ ¸xÁÑrõä~Ù_Ž#Ÿp.Ðmœó“ 3u8ÓMÜò¾ÛqÀJ_.3è…ÓàŽ5årVa¦ÑFªceW䌟µY(xÿÂVc’Ì ïc‰\ëƒ=ÈÛŽþj¨ã·4åogB¢åUo3Ï»O»Øõ§ÿÈH`´ƒvd¼®ãy¸ržñ\mYÃþH«%1²uÛÓd‹/+4Õë†õ©æóžRäÁú­î²%ŒþåLâ Èér·$ùˆ³Jr$s'Ïs2‡ìÙÝ¢Ü+’š:cᘒ08‘‚ßä „Ÿc5 ¦2žÌf›x€cf3 ¿CmQžé©Õ{ ¡=eýó½'vôw™fcÓX ãÙ"ô  û³ÛÀ~<ÏFԼи:bf´¾~`§tðÛ£Oë¥û/?Â~•æ£Oý‹IÖ Ü¢çÍz>Rpß©qân|80oz´D~1ÚY£ñ‡I÷¶ï®3†¯žú_GÀÙŽI ›øõ§iLyi‚¬R‰çä9\ÇfcO*ÖÍ?Mô“ZŸt¸4üœßoÿ/†×<¼K}½F,ñ ¶—³PÉi»ü´¥…»€îIR„Bt­šøtýãøôkéZãõôë-Zàw PÁ±( ‰=~‰‹v¾hŽØA"iµg­z…âHR¬°dµÅ®9üZ†ÚØlQéžú6˜_›ÓCÐÙFƒ~¢m¡§ŸŸÑØìÔ{iO'höç®^EX£‹aé¼öù 0OÏÓUý$¯†Á|¢ ³Q“.˜«ŽR¼,î‚¡•@0¹o}@ã•yS5úDH`A1WŒF|úy33·ƒh,¢Ÿg¢µ°Q¡¢ìÙîêÆ` }HãHEŒ X¹DpB(«¢÷å¥Kuh)ÕµKG`ÑÏ(®Ã4¼½u+Å‘"CêY4Ð|2j~hqCÞ·¤Úé4*•¥­Û³pŽÖ5É%ìÿQÙJm«nÏŒËØü( ú5á‹@^jQ‹¬= žØøM@í9oŽ´ñ-¿”óoÕ®E¯]QÑ?Ô~öfƒš­Uý¡’Õî?½Z he\H£0á1°x%x9(/¯&0`) hŽªJÐY@·SvƒG :Ú¼˜X¡2Oïaß¼J¤¨B¡•}/ZaƸR ÜN ŽØ!8KI u•¬ËK³Ö³rŒ´…Ã$,°óÍUc§.J¾muhP66 Ö€K‰UoË’AðE¿ÀsX£`1I`¥To€±„uZ ¡HŽ/Îkö8 ÆX^°E»]f£¥âÝçÅV 3×Ïo- a\½­Bª^. [ îkT )ø(„aކ W1[ÜèÀù.y'¹±rZà±lÎ~ôÇ€ó7©ÏôuôÜèËès‡*¡%$è*¸ŒË#ÉbRÿE0ÂMUËUqf¬o’qå©D hI$k¾Ò±E!Š$Å Ì+§¶°Ìßz¥’HÚ[!LÞ>¤?¡0ñ(ß|»Šw+¶­•-„: ½ºƒ'™!â½ÐŽÙ{ïFœžÀé%kX:õ†¹¶ug‚¥±EŸ{ƒãªsÊ´•zݽ“­1’+,Í…ºP«pŽ57p·@JÁ±PñìbD;öeÖ%¶I% p‚ÁSÝß™ûÂ6¶¿“[oüÜ8ŠACá°—ü{Ùš|iÒØ‚Ë‘³MPšfyåÇùî½››Û UNÉòÓ6Åh€g¦¬±½&?ûŒ²þ´¥$è8íIï.Ý6È<|ß+¤¬ç^¨ç:§ùƒCȇhK¯Lj}´B&Û¤n'îø5Ì8å}~Õ:K–ªq K´¥È -x ¿I\ì"rû’g¬³UK­û¦%C€Klej}`(g¿Og|špG“¸ƒU,Rõ•Ÿ!ß ËÈžI ×J¯åµ+!S¥&ƒÊâdF„˜ˆæj Ò¬Tu¿»z,BóŒB!bÄZ‚†¢„ýþGR¡œÏ„¥â7)ŸñžlƧ ÜÙ刳qLW+}ECR&%?A B `X&`ëU@l…tºÓÿ &X5É·}ó †16ÖxÚ˜ü߃ÿ7Sº¨GèC}£ý‘BØ×¨`Aâ‚ù1Qȧ¹PØO¼Vˆ¦wÊ“GÙ¹Ð.„psºéÎéÞtâ“ïÍ—q¹ ùm±èø ë …iw4ù¸ê(­ÇÝŠ`!7@†ë¾h:]v÷o½œn‡<_©oýâFðƒLò´½qï\ÉY V[$A*™Ré-·½nÚF+¢VÍ#úõñbB£IÏÄzÅh†}:âÁÛ±q!®Â»°èôzÔ†:>]p-›Š0¨ˆ:7 w{!`Ì}‹ú¬IT±“fÜ©ij^ù«êõ…l6‰â™+ꦷӯ"²ì«'åؘ+ ô™Z-°øØ#HP•fÜ«ÿj£OàPGÞjØ”ù8Hý!ïí裴»ôw诽²)Âw‰ GЊ\(?šÏ¶szÛ³þ5°ßp¢#]èòuòÉÍw Og_p+U"._ʨXófIÖ{è8欯 ¯Ç´ˆÙA{W… #T¹­{,¯;%  þžz€uhÿÓ"" #á›·"W’“ÌÔ”:Õ:xäbô0_»g{jQîž%÷' ãR ))Mo•Í‘ø²Û-ÂÈØdaâø!Œúle®M“»~ô4Ãtiƒq‚y W6jR@" çNJ"–õ‚ZƒÚÖ§ñ·k¼ÒfZk­¢Ü¹Ø´±76Ò+¯µïu¿­pŒ×Æ@$ ÷¹÷vQº=,ç7Zœ¾0‹Š7˜È)üÛŒsnÄ¡úÍ/ñ< ж„dWÝ(8dâÖ:7ó ˆ·ÃuHgI¤\ýÊ0yì2ö£Ñ(ãMà‹{÷µ\uKÝ@þsýéÀß°oÓz‘bì"Ãõ~‹óy*u#nqzøqË&8E…G–瘠]—‹ŒTaüškk÷χg‘% ¯{ß ”H<àRÃãËŸ«<–y¤õŒÇÜϨAmQü‡C’ïÂ#Ô(ÀÍ#ý¢G¦Ñ" ÝB™,ºb|p7³½Å²ØÈ ýÅ“®»<£ïº1UÛÔA¢Á…ªê ª[X#wXÛD¶a],;µŒ8¡³éøó“q$¤HONð¬W;Šûþ–¶ÌzqÍæÌYX~ôãU†zaÏOݰæV†.f¨9vöq¤Ëv‡çµ'Ô'¢ˆ´Ùþÿ„OÑ~ µ3–÷Ÿ'þl‹A‰!•$¡v%¸™F Áòõ¿ C|9…ñ[úÑÜf|;f¾ ärb—âEî2“ ÕfÏ×ËzZÑ4]©nìN–ëèÀ¤1ߊ%\7¢.G?Žù„­]"Ó ŒzâT÷Xäï„p†cmŠzMF˜%,-¢ÇA‘,EìýöžÔÈ¿à5s´ª¬v”.9±Æ=UG5×&ŽK°ìåÐѽbÜÕ<÷m÷u œ‡’7± ä=›ñ}E³}WØpê=Z9ïiÍ^à>y}=Ÿžä|=²Ùëà þéƒOëȾ2GyŒý°ÿÓäùyÓiB­2i!çÄ´ÞüÑXÆF¿7B®=¾èè<'ìóŽö"Ôló$/¡Ï=„ÈóØêƒõ@™¢É[7l¯%¾Ú®‚%Ý&ꊾìm ”swÆOà²O…²V¤;£/’E®„éÄö+)4¥»Ê­9D¶ìŒq HÆ(~U~PñD²o¡¢aÓ²C<í./äLE×1€:!ŠÀé¢Õ˜l„pc(Ç©Y Sn”YQ€Ê–Ò25#±*$OA¤†ŒN…®(b…=d§X£Ÿ9e¿£Ô¹=+Jl“O†W†|Ñ<³8cf«\I6Àp EˆhcSµT¦ Se®Ö};s@~L&àßøq´Ï3¡¸ï)qFçf®#[¨½ctEÁ§©š:àÅlöÜÚÚAZ\ÿN6)…¸.H„ÇŒêöyKCaÖyƒªû}”ô÷œTõ-å}¨äƒžÄ¢#ž½€ŒúÅ åà<"ï“\ü_·ôjš¹®;“ªå´ïÒ’î8Œò±Ó6¯]íÕ9ŸÞ¬çeK÷I*qvêq¹©Ú Q:µŽ6øªŒ)Ñ1cžÈ¿MÔæËð·Jè4L——Û‰Là zÄçerpÕTt#±ìÞwU°N5Yúæd:Ø1¶HË$Ú$–ÐÉs WcíÙ“=Õþï¡Õºú͙ŸviÔÁy"+¿{–Ï5ŸZýˆšdoëUX´'"Ñ<'‰2³[£˜Þ\íx€œå.ȉ‰µ!!hݮ葧Úp̶Æ©ƒ®–K?qI²øk8šdNhÜË/ JªE«e¡Éf{ÓŽHGý ˜ù@þÍoä{1øŽUág< «fJÓÍHû/‡"BÁâæé­Â™X\X(ö”2  -Xª¡ÏÍ R`„›Íö¨Š“ûpÉ£u¾èë5Ï–ÅÔ=³šêh­}œM%.s6‰¾ü¼±ŸŠyõ›®ÅU«¾8ô&Yg-»hV8´6Š~c”4Íüóoä¼KKùrQ?wÐÑ}kåáL{Q›fÝœ»Å~k|(.õ¿5 R0Y±”øA:qÕ"¨¶-Ö Ê(µÆL,–Íò›ÞrÛ¯Pð®šîK̶øX`ÓÛ¨Êòs>Ò‘ñ²CÚôX4ž#.;K¾%„éGScIVˆw:TãÊõ²û¿Îyø‡Äx åCÒGœráD†HŠ,/ððü¤6rG/ÆhÇŒUº3q=BwÏÛP.Bmq{6Ý-?µ^²a6ö½é x“%…^­ D3ùTÀVƒoÆ<Èp"7D¶^lüZû‡® !Zú.ñae!QV¤ç®™¤ ôÌ,7³0w"’7Môï3¸~—4ÆMòóë•¥nµ=‚ûRÚk/öáJÙh­˜T–ãýjbÖ;Q€— •Ñuz+I.&‚¾_×Tk éHX)⩪ü¾o³­–gn˜emnÐ9‘¾Þ°ñu›ï’Ós…~û{`IT܈'}éÌï6WM‰bÑ­·É¨úÊÍ“›ð90+ôD{/²õš•ùz¹ã ‹Ü6ày%à†±¯²?äÆ7d S9‚¦¤ÏÓM&~õÖ¡¯’ÞÞÕF<£B•&å«¡á(¤S˪,UÁìœûWy[c Ê¿¢øÞ QŸ&eãç¼ß7Ç_¡y YˆÖß7~þ Žƒ½ÜrþØ2LÈkwVnó¥L0Çàå¶L„ØäüÚ® պʼõІq‚Ž;UB ädj·S­Á|Žx´ãëvúɸðÛ¹È;Z‹ãÓÓ:­ŠYj“é讹”Y®àn™ ÜB¢ß}" ˆ­×þ`òª‡I;×T’9©×V˜Áö†…6LÔâQç½3{Ú'Ël¢\÷pNE®08A»»'‹aðô†+n¯ò¦/2…˜cª?)…ÿpálšì¼­Üç3d[ÂU ¿†íqmŠ$‰¶[޲¶O´û”É_ˆ7ÿ.?B?u»y‰E?O2žÌ&ø<êåÒ)å9“÷n¶úl>÷'4Ïàxf ÐW_ Œ I¶À:p×Ìt×*F`-ˆÒ£Eñ"W6érŸÜw“÷²F¸ÑKõ°Ÿzö;1Å.°@¤1‹:0™pbX§gËê¡Ë‘èã?I®…‘¼¢1ȃÆ}KT«¿«hžTà2¤PcÊK¡€ÅjÑhä·‡©—Æ2HUF £’cï¡IØÜv ½Š Óç0j“‹¸o èÆ§/$=U˜tçQ%:Õã,µ‡ó¥5â!Ü)àÐ-c A0D¬œ=,^ØÃÅu[•¹E§NØÛ[¥çß»8ŠIöM]˜kûba5ܸ=çWîr&XsŸtrPë⃊ÆqfnHoLŽÿ\BóÇGü”6ŠxÌf:BÖr&¦÷S¦ZXÆ#·*¨ÌGN÷PCäûòäo­Ÿ³xžÁÓ†o&1µ•Bãå ý6Š)Ekˆ¨¯3ÁAãn«z†."ʼMDŬm3züµ«Ë‡ME«è³ú/ÛBS¯+°3TˆþücÊQ`¾Hef76L³+XwEõ7üð:‡¤¿j™hË­¡šš*Y„µ"Ã×´=óV˘Πè\ÄûŠˆöco^)~pwzŸJÛ<ƒ»†Vâ8‰Ñ=ï*;>÷ë¨óÄf”xÖzFª!´_K´Qvk?‡"ö~)=çÕXüMíJ:â;uw3™>ýqD°¦b¡P(< ´©OOn‡LHÓ¸ù¤!Ýc^??’ÍO Æ÷ ƒè;í9¡'pþ7mQ˜,ùÁÒeLúZ ÞŽ _Wì„­{1ùhn[2(,6—k3ÄFöÇŸ}—fÎwfßO@ô®aùY%XijÍsdyÅ>…å^ÕIî*ƒX@¨çÉ.‘7uúñ‚½AÁTß/Ispÿù^ oºŸó&û~ÕÑÜòJãv< û`M þ»QZÖúb f¯€|U¾ŽìÙðœºNïÄ”®=”“ßco¬œ(M^&,Á#[¬ý7·ŸøÚ² ŠkmzÇø¸ùFFàþ5L–¼æÙív9Íò½ ùƒÑÛïpBjæØ³éôô•-³ó&bà—é „9ãRï\^ÿò¿{^½iº ;¸“ÎÊEü :ÎtªRŸ&»v±ÍÔµ8d™ŽÑåd2„v˜ø–@ÜLÌ‚º4!°ô3”ó"ìTÅÖ¡Z¨Ë à9“€…”'¢>rÙ@ÿ.EÐy¯Bã#&uãàNKQ™4—ܘ@™ª„®åX¹Ã ¼ŽqÚŠøÖ>¢M}j¥vÅLžà^‘aòÙœbX1­§—10-û÷ÉçâMæƒ:Kåú4²LÒ.h«€hüò0h°2áO±Ñ7bb&Ûi\¦ËÂi+ìóáÁ©j9PNÒÅ5Íáýôa¼b¡ˆ!DFµoä–|ŸŽÄ|#kg4€gŒÉF_dh&_)ù²èUÏ_îEˆ»¶¸×·çÊ÷ásê°ä%÷×Ú.sK†‘ H"µõ\$8”Œ.僪÷9泂Q‰ä¨8õŒt]ï–©j… œ­T% ”÷1ß,jF/•ŠñS´bÆÍ¤W{Mº ÞíÔö*ëÌ”¯D¼+o89Ãï`lSÎ8›ˆÉvÇãÄšeˆ2²¥}§sïBH—$tv™® W²l¾Μgç3=#ųÿDÕ(Mã—eÊwf縑Ÿj¤»Ÿé è}BÝ#±(Ãã1^Y”Èm7©nÖbǼùhÙÈd[ûàLxL×üÃíb³aîS½ŒÎ{/Š3"˜ŒÇÔE"?Á†ËÙ©ô2ë O¿„&ß"¦÷Ú)ªw@À ¶ÚLßÕúL…„ÑIè‡ ¢þò2Aýƒ)½› „·Á˜ñÌSäjÇsHþRçMd Æ6#dfæ9“^Àƒ»¨Â6ù7ŠØ“ÔÛÏ"ÖݤôÓ/t¹É9'›¾ ´\’È¢eÑ;hº^)U÷áä‚?únepÀë±ÍßÔ ò!å|*eDÝ®Lë] ûMýßÕéá~éÿi#çq²?ȶ±zÖ5 ( &‘äjíÍf¿þ”¢îqÔ49y4Ó†ÞÌ,ckäã)ÎÚüƒ{Ò‘t#œb›C³koû¥fÀ!ѳÛªPÄò›™¾_´#mÄš.<æaÍ…Ã6~dìæ¶Sè›ÒùNgC"h¸ ¦;µqðš¬JÞ’zNyÏ4EÃ)K·âŒ•Û8^Ûtjc€r¯a¶™D=·ðáÖ£BÁÆ÷¿/ ƒrÁ7J LÉ‘X WÎÒ$—Ñžéî¿•÷lË«$²ÇkÎÍdOWYöÞ†bSkXtšš•ðKüÎæQw†âØ¡Aâ>……X´4äÍÊFÛdbè×w>SkºB|fqƒ_ïÁóàüŸâ:"À-(‚Nši-PKˆ=؆³eÙnð»\™×%Ì'ê +ÒI\'4¿4SNÉã®{Ûä:ÇìU'L+þ;©_6èà×aX`eÝfÁDÈ YM”FîœÆ8¯M¶ÓP[ˆ=7èr\{‡_³ÍZ|/§ÇE›ùIgle¿þãqåètÖ\ØéŸ3Ċƃ^Äu-‘iÀó¤ËÇ8I`ª5Úí)G•Ý—˜†Ï”Px²— Ùú1žE¬Q˪çìú¯ëäQ$Øë$¥f ´}â¸EæúÿE”ò;ŠJ¬ÂÚæØî§üð#h.%{ђݺ* ¦àX8q¦“ËùÿôUem uhŠë;Ó'ÜSüZ<ç¬éÜò [„^Lñ-M>؉SÿÂϺ“Jy¬}Ô‰ÄÖvÏ÷~°¢Q¾J¼¨º£;ìR³üMÕÎÊ»»U2QŸb'"ô¹Ñè®­ ÿƧê,6ÑUÙºSI÷-Ÿ]Vøu¶ÊÓã™øÁĶš&¡Î‰ª,ÊQ¦J›£Ùþ2i=LI’‡EÚšìðS·_'ãÄXж4í˜ì>l|펳¼1+zwÜfgÛk¢ñÑ…÷"/Épó<šàÑ`Ôj“ĦµÒ„[Ÿ|9b¨{? ÙùŸêŒ>¨?gÚQç•Ò-»Ÿ?õ`áæ‚÷KMÕu!ÞhºÁ&󹓈b´O {‘5éÂl§Ç9¾%Ž»óëÆ·<qP«/›«£ÃN ö’2èõór"/8A‡Ö¶µ?8ô—ãÎõcëV}üɲª^ËÛ­Ô Â8ÉÞ¼c¤b›&,µ“Þ"hå-YÙA/Z°tóÔÙÚõ›/zõVUµ4?؇~ÙÐ mÈCŸ|}ñc£[ý€?öXݶŒè²:Èn6šü§%âãÅ­ED°|B@kSðí€ X‡!MFtZ¦ŒüLÙ³Š Ê ½Ò6À;†/l8}i"Ëÿ ÊwîžÒf;Cm@ ~jz 3473ex™‘7 Ð$c6òŸ¢Q³pi+¬(žMæ ÁwŸd”£-;Û‚S}sÿ0ÉT2C5\Yûb5‰—Ä<3I•®ßÂÑ–§©–¦æ°3ËÏ>û¥ HÙJEÁ?n Q¨¬3Å 7.„d:×l&8@ô?Zªñ~¿ϸXU—ZÛ„QÏ‚Ò5I%9ƒs~вÕÝÊ'T ì³]äì§ò[v6Â…øÜçøn°V[Itƒ8¦ÐCÞ»l&ÿa¶óº+Úâ™[`ù]ß´÷j 5MÆRˆ>æÛ]^½X8'pBõ)Žc¼î!âüÈÕ /${gÛ¥bÒ5aŸü¸RIH… €âÝ[>ÙÖ4¹ŽáÅÙÛ&vmR JòiýV5[üb¿‡~ßÛïhõ}-X³ÿzêÙÛ¶ðf)½¦œ­ÔüîÖw‡ÜúmêÜ”ÖÔ}óرUlR”Jßijõz—éÓìEÕ —¢Ù JER&›RçÚKÒbùÀ×Ð’#úƒ —^I⤼¶·ˆÝ¨_Ý_;¹`gä_–«ÏÌ&ëGGø¹P¬@EEÞ%LŒ¾ó.U›05"ßG~‘Bß]•¨DÝ!¬½öNÊλ÷HÓ‚æ„H(l2ËÊZ8˜%Ú#.« §‡ÍÓ²ô²Pøa‡3iË,yâ¯Úû5X‡×™0ÊHïq1Ž 1걕ôR6xÁÛ¼+YeE€€°«”ë‹:£s¾U¹sÿ©â|gÓ=ý‚IÆuOw~)Y˪ÝÌ€KÂ5í`ÝEZëR¹ã,"lhº€<,™fNÚ>»û<¾ÏÍý½+ú‚/mÓM”¡fa’F¸‹zô‰N¦×¼Šû†õ®Z6l@ ð+ñ aD[An8D’‚h¡ùz\ mÕ7 WÇMgÍváªÕwuN°kÞE¼©^÷¬ð‘’Zô¢~¿£Ë–ÈrÒæx?ˆR¹1-¤nçß»HÕxELã†lív¯í”X" ;Á '.Û½¨’;.ÅG9´ªºä ±ÿݾ_K"&²`ñ jÊh™z&½`¨x8ûLpce¤ŠÄ&˜¯ŸÂ{ײ‡LJHŠŠ½0Åç,nÏp|ÿïyö`g7xp“vÏäzj( !mR ¨V#P5@.Â#ïc ¬G̲o7F/yõØ]“Bºá;­Øâ5¦+nåΌ§FÌýå1‡=N–a–ô¼?ÀrZ‰C:ŸRç%Ƹí1 „_ò¼Û_ÁåãpîÀªJ ?æn´©%Kwœ§ƒ½UÙ±sÛkÞÏoëkxŽ–›Œ;#Á”mNaø}G¹µ·ß?øú¡M ªÚà× ÷ÞÆÄ^«ä½ » ; 9ÀWŠ!™Oùþõ¦1GNq0ÕÖ~WÂež‚ìܘ™Í†`“ ¼ÂŸnœ¢+âwW´Û˜he¿ ù33øùL§ Ûz¤-³}àbß½¢…m–¡6øæ˜@/v°Ñô©µÃF~† Á¬PR@…càeSO ú%tZ[LÏið3±, H õ*P T$¿Ä®¢|õcEí¨Án:Ñ!Ùz ÅȪÁ\~=w¯4Lœ¯þd˜£èý:OCÍE .Á—÷î´ž%˥à ȡ$ÝÖzïñ€îMÚe§XK}&M}ÊI"‚wsð.{w+P­‰(,E†í]y].À|a©Ö¢¾'D äÁ)®¬C¬ 0È^âmÀÖ3Í; ’+O%‰¯Læw¾ÐM„´b€1})ƒ”¯ºør©Ð¡Þ î|ØŒ¿ãüΣÖÂYh‹þ°Á>~0ü€yHüäU47dK¼ xâcP°îÈ ~x¯ÊÖïpÈùcßB‡ÝáOŠøèD©Ó½‘—G,~Ýx1õŸ¹ã3õõp oÔFÞü•N›äsz‚ ñrò>Z¤†<¦{Së>ŸCġǞ½èàèéÉr§|nµœž\A¯18Ëe¨Åq!Ü•üÊEª†‰u¾ããh(:yƒ²›x¬ZòHˆ'ൄÐâ~ïÜd%ôËe2˜‚±2v$Ùn g9ØÈè5I†Çä©× èßòCÌÐÆjç¥å1Ø|N|]éQ ‚&c¹æo¨9pw#Ñd‚ #)ØgqžþÀøM¬ê©ä%ØX¬aã"R-ï½?Ø*.)wÎ7T›² àæÒÑUÛµa#ð?¶PTX'Á&Ü÷4Ït~:¿º W¹äM™g¾9ÈGò]ÐôZ„¶2xUІD„s6_ÜOÊ Ì0t•XxÛÃ(Ô6U®¢kõ.°òà·ë™d‚$3/uåËtøL¥òÕ$ï `m„2+È•sç~€žxŽ«ûØÙ%½B,Xg4:꜄ ÑašÚ<Òü Ý›=Á†½¿E޽ÿ¤íQì©ÿܽÏçô±…N»<÷LnÓšWžß\RX®æèšç„Þùß¹äY~T+é¸C2Ûl¦­¦{#›ç[ ¯S9º'yÅ=]»Ñ¹YÙ~zN‡ÎI1»wÉó‡”XoUx’"Oܧ †P#:“þŸã,æü!Z.^ %ÞÁ³ó: B«›‹«Mû‘î!U4ž†Ï1/3s[¨àhß?fýwéW÷§ü*Qç×›Å̹÷Ó=è2+Uú­3‹R1&z3®¿À žÛìÎjLZ9ñH}y ƒX¨é·.^q¯Ðý#|ÁHaüPKé-AÜ;ó¿N¸®ÿŸ ©BL§`ÜÄwúD|<`^!݆Y œÐ…ƒá›¤¦0Ue©j›ßh%DŒës:õ1 XpŽ––¿fï¢vöa°ÿÐÊàÖ;­…<8ŽŒ#( ‘ü*;çýD­úøÑ'—7ý, âcawBý/S†À’XGüéO ”¯NØz+rêÊ Y¢Â8?Ÿñºš=$·‹^³ðѨÉÕŽîümšÍNÒH|åh.­ê«,SO~˜æÿûrö¿¹™õ já?5ý™z¨¯DøƒV¥Í ( *벬u¯ÿôB÷ú?s ûíX¬B7J\>ò¡Ï¯teõ&[+Dè ’*×ýʹ—Ùïú}‹UJl§§·ø˜Q&ÿ¹Í ít>ѵ\·êèÊ Kr¼ê©Êáã>ËüD±\ãºûg…EÏç66«‡?²‰’Ÿ‘ÿùëºþg@)R»{t¸ù°÷r""¤yYýʵ×ÒQÀ©õädß¡hNBÃ9›IzNÑAº€™nÓ€jÞ廣‰Ä3_!ß[E2hTæ‡I¦qd¢· t×w²x¾NÒ× RD Ôˆ%ög™G€| êÓÏj¡]%¦`êÏg4Qd˜A5êõsȘÌ@Èmƒä‚Ñ´ù"Ã9àÙóXCs0íÝ>â!;”ò\ÚÌ’ðhÖœ\ w”÷¯ð¿“ɵíLypâüJÂ'"¯w{íÏ?µ¹Í6BGÞ­ç êÓŒ_‘ñ_ÃОvʪ/áÔ`øy§ðŠ/ÀóŒíh'èƒË|“‰}¬™­8Œ#…ºŒ@nú]eìh¡üÈÃ1Ãú6 °xn´4ž;ÔÀþW—2;4J3Œ¶%¦ ê ©»“}¿ç}Q.–§ñ.Œô¼#éJ³²“h˧£ ÷È÷3l,3hbÄýžð”ÀÄ÷…Êðôœ¡ÓVä‡ÁmºŒ&““ê¼–Ú%€!ËB[£¸¹S?¨=ËT30ey27k/=$&à}”ƒH@ð÷Q1D;»8Ó¨ã¦Á±»‘sÏ,4ކóBgœk12T°#Þ=p`Ù:ŠÇØ ܉KÕÄŽøXQ"hv"ú¬Á¸(ÑŠÌÏÿÙ µÈ¡tˆõcë:Î11Ã+ÑÐÒcÃÇÖHl, ¸D΃%ï¾i9o ‹a§2w§$+P ¯v3Töû¶iÏ;8äªÄ…ƒmºqƒ:·@«ÐÀÍó•Ex‹cp½ç1Z±˜á2tîŽòOñ—Õ@Û4‚ÌÏ[ê¦É27›?·ØÀì±/nV”¿*¾,Ç_@©’ôN®ták=|½©Ò,}¯hcfÙ½ÖŠjÝêO^ÞÞ˜Žó¬:°sÿ ŘŊÞjXdçY8×y8ý aa€Õ'z1q‡±v¬qä’u <=废VŸjê\žÊu‡Ò.4^¾«èqÝD ítcšÜ„­u©âª(ß^œ¾› Îµ©ëÞ€Ôâ’ä»Ôzä!è$}d8é×37ˆ¹0ävDƆ0ç»ñ“‡–)5íHÁä'Îë{3å#(éAY:e‰7`mVÊKÖ| ôèyT/§zF©©°Oõ‚£è£-°ì’q™Æ,ÁWnqüKhǃz€WËd·.&¤iž=°g-ií‡eÑšå·ƒª¹n^½Úi=-/Ñê°G£³ܹ!n(¡*¸*¹ƒ­#­J(NÙÅQGkÕݤ#)ÐØö…“^×püoÓ4âdzgÿ(#:ÕE( -Ǻ^I*ºõ£&4zVdЪ­–¨GV½+bÕ>Ã1Z™ÔPæ-ëg Ù²U}c ûigš7¶T3?-ʲ"€%µVBV“é§G#xÐæÈlš8:‚5ŽÚå{“÷¢]dòžfÒ»­§Àßm5 ex_»*{uùuôÚQkˆ£þaÒ¬sj½.5{£BÜ]oÒ1²ªEœ šqUj¨ð²u«— ¬`”dr<ÏÕgS¥2üá) [çª às}Ô–RX…'ª—ðƒÚ¼Âw —ÎQ²Æv}q9¤Äèöô²z ,I+1 ú1üCc»M6¸1÷ Ú¼Dä,8¥¦××`]·€U!»Œü6§1×%”qi <2êÚ ÜÊ«æÂQ xùna³PÛÁ gg‡Î§*‘áQ׋ž£Ú×¢xC좥va¥ D6±Ô›·„|Öû‰ÒÎ4]ƒŽ­a)mÓÍ ¶Þ:þ¾Óíüçà`¶ÎÐé§ü³­ÔîQúKÑÆé£svj+ã^—_µë"‘X– 4,”:ÎÛÔ°ÛÞ:c¯ë§ŸâãGîÉûõA˜Û¹Muå™TD9Êù¨@=(³TN–ÞtÔãÎãÓY øIRÈW ^R³È‹N‚·ž:G…[ËÔë˜k‘küV 3fq•yrI³9œ)jŽxèUÝI‹É¾w‰[g1 ³¤]»²%‘ —’zª%óO—ƒøUãÐ4X€õ$Ú8ˆhÂcàÈæòJÙƒîßýpÿ€§ó‡|ʱŠã-ö©Ñ[®Îû¤ÐŽÍl¤"(81¤Ì­Éüí(•­Qš^ÄŒíy"Zi˜¡%Ù¬Ôv^êµìÒ7Å!²¨‘ × åÕ«\\C æ·u¬ x& Ñ~é›fÚœ¹é›%ï Ì8MÛ ë¤~‡öÅ{—h[3}ý 4@ îû‰Dñ®o Ã{6¹ˆòÒ-œ:ºø™Z›MZ¨c±)¦‘×Fð̈÷Öä~ïQz€Fë!‘ï­ÒÉÐÉ5|-Ó2e1‡“~Úxµ° 1Øž«»Õ+Ãã¯é® %:—»™fÝ6¦zëAs,r}.#£p‘ö¨:îiç Y<óyÀ-RÉñô‰Ÿ…žëd^j¥ßø0'êˆ »à[nbÇ •uÈ}éôk':nÊBÓ>h™ £¾á~³bú|iç0ê»r üÂûù¢Q  ?ç}t9k$Û’ Á3âì}ïP,=7•v•± þúÈÑh%¨2EQÊÛ^ò"ô›Ë&7ßÏM¢2pú ¢0’r!`¯øY2úø `ËÌó£ì÷à-ø%'y £{nœ{~k)ÁŠÉÝ*¸¯~ºN²ØK“eÏ£e¨¼eä^ôw§MÅKìuðñZÞ·—ÍžX2ÌuïSºÙy¶t^ˆ†üþ*–”믎(·f«—WìwÒõ–Ç=[Ó©y5ÄRK” —U[j•Ÿe‚· YNõ‰ê¯— '3z«£ªžþMö{£»[ƒøJf‘ÃCog¥öo–u‰t0„¸söµš*F²8¡þ¦Œ6XÄჼũîv—5-y™•xl¯jÝDßR5ì¨Ì°šTÅ锿€3ú™a\Q6?Ž8Í™\67B _Úî f_9,S¤CJö2ëz#Ùe_ “‘œ*HFa RS¯Y~™â5f·(p)BOÏe(s¬c)×J¿;r©Î8S(i{7¤¿?ô?æ÷@×€™œy /˜… DHþ\î\V—gq|v>G#cì [‡õSª{ÁýÀ†¥ŠWJÈ·­ 1nÏ·eÛ¯ä \f•¡Eh§a²µÔK¢¡Nqm{‚Uú4ö‘ÈXaEZcG´æÏ`‡ÙêÊZø¶RÒ£Rxé9:ík¿ò>Ëæá.QˆÕ,?G Ö\‘S4D¤÷±üÙÖnö÷)ͳÿÀCW:̤]ò„åwTˆ[Y¯(¸ˆõÇÃÁçåâ:"滥é×HŒƒì>DÏðZžóH2† De'5ÙîNæ¤õmxJ~†ùöhлôYPI]C×ùjQ6Tì¤ú,å¡j ç‰7ZØAMtü›k#4 øƒÔA©y(͸:ýÈmˆÃ}ÈýlUкt>ÇYñeSº=î—O”b)ùðôqDôÇÄ‚¬…OR÷…[^×^6÷´ÜA÷íAœÉtÄ-r€ŽÛò©šD€á óZ`¾âõPÒñeoœ²™œ/Îânõaèç×¢T\#IÈÙP8TôjðÔ8Ó£"ZkšRÕ¸yf32ÁK %:2†y§¥¡¡bà_Úª~†âHüŽz[j=æŠêÔ ­ävÕš¯Ú˜>"w } ,Ì›µ×Ðô[@ÁOÉ ³›_%-bù€u$†€4˜‚Î4cùîðl™½À™';–ôÈ` ôÇÿÚµ€Ú”3ÝàÂp„Œ ׋\pbPþ˜Ó¸Ã³ß:†ˆ=¿— ,üo0'ÿë ÝÇ‚¥d…ýW»MÎx?»¦GA êÀÆL™#>sÎ0dŽÈô('Q9™c¸¥>“]–½Šà$ƒI¹_³Š«Ò•ÎóÏ$XØ,Ñ â<¤ÇtЧÝD R6ÎôöÏ–ì/ÌóN[ž_­dH0èÖ>¥Ú¯u·eY”·Ía )Gúø²Ô °òIÔT§ˆÔT 1LÓ Fä#E?²‘±ÙpK[Á*»ÆOíˆ>~ìÐpèÎ ¶äî&’…šçfê tÏÚ¸JJ3xÎ7Ý<>lØN-õi:yÐÿM„Êž§Ü"ššragš¸¹[nؽ?T½×+ý(‰ P0•1úXƒ•@+EZ‡ƒPŒú-êlâ%( J¬PPTȯ`Ô`”áÓ*à¨@Â5Ç•£W2Ç•ôA%h% WÜÆ«z€L(¬8Pqà P(B¦p eJE´`EŠ*d08SU% ë§ášà€±† h¹ôæq*„hM9,úÄ2$‰å]ú§ŒÄß~Í­U¡„$m¹«_!ÃôÿÞxC«úëoÁŸýÖSÏÞ±…5Kí5çl¦æt·¿;çÖlS榷¦í›Ãެb–¢SúKS¯×¿L˜f(r¨¦xN¸ÍQ)(’2ì”»i‘ôXÃSVɦŽ\J\ê'%±§®}à¡+6Ð~!é¡Jì\®‚g¬P 2^³Ÿ—@Au½?ÓÞÔÞs#ÓBìži: ­ `š¸cý¸aAú¨ú[{¤A× ±`[t¾ •DÍžöh§<:ÿç6or—Ú©“‰‰/-X9%0«9Ò ª›‚êY¬•úMæN…Ä‚ú°½¹àÎ;OOAcš:ioPÆ?÷üog½xmq!û‘8uÑö϶XUl.H©:︈– œT_ò<»eÙ‰µ"6ït÷zGJuì¶KáFÝV‡ŒÂyÑéxv) &îóºvé»p¯k¡8¢nÀS:ì--܆¹“Žu7l¥Õ¹÷Ô:œ£zÙ³$\> Ž‡Ð¸ai](8Ú•õlôRÿ+›&רMüy7ÝÅ(.D{LÇÜ|W% ‰mªmTUú•N@I#5D¹ç8M²øõ–”AŒ:*ð:²ÌžÑ©¡÷³}6¨®xÁ p‹êx½LÔ÷·ÍdÐX g°É}Þã‚5éø8Ð<„T—m-Í=T *ç:ñIËL›Ó [¸õìÑíH±²š_¼Šùõ7—NNÂ(ìñ_7’r¥üö¶’7&÷”Ö›0„/ìZyWÝU\=ªÊѼ†¤º„ Œ¹âs3óððÕMòæûðLSwLÎI0j^/0~ù|醖}ö!^ÇJô\“¸0z©°ê‚¢dÆÁ‚¡R:1ú@çÖÝ£$ #%èñ¯åßsw]œÅ»'h‚y»)ÈʓߘYÐE/ÏñÂux. §ý<8çÍHNHßð¬´W¸þU›ƒ9t=pòÏC\òÌÜÅáTÀIP Å4jTŒçŒ°¸,>Ü:A6"œÜ×^`]'pÍp1En̉ ¤Š„ÃøutôÈPh¢Š0Æ Ÿ ý÷·š.§/‹(»AQ"– < õÈ$T‹Gƒ°r6Ç–þK'm¦ªÙ-âvЋ8KPO1aÖ«ñÿS ±Ä1ÄL˜ÏØ q(BZ]¡¤_QµM…K6¹_²Í]š?ƒ‚¿uáÞÀʸà'‹¬q5@BÕïÖçˆþÀFí,’È¥URã"Z I–hÌcåäw›œœ¿æÊ—ÃŒ9ù-’tçÔšn¼mûÔÒÂ÷[øõµ9u}94£wà{c·cC峡 çvYïóH]¶çn97¶{–Æ0­a¦„{ †‡=ŒC2å¤5Ùè/è’nökPà‚ÝFDê¹{+ÖrVÎBí•y¼}êšçK7´¸u»_n€´Â¿§I/C+—Âg3[Ÿtví¼±úóÈðþs³ZY‰³G¹¹Û$1Îé¤1ß|S(5'{êú˜ŽbeNåÆ^X+õ£ X„i%Õ“ îK—÷N-¹-“ðàPåÏaBCçI +¸às`£ÏëýL¯—É)g‚„ÙQ:1»^UíðOMÆ›ÁöÔႃ[WXGö É–k>A-e¤+žfgT{صï@^öÂÈzp¤²€ïIîUÛõ3©ºØå¿¹;ûGñD7`ª4[ý´®J…wa?TÈíB!ew­hd‹žd_lviÛ×ÂÖÙ7;ÂÚ,¿Àî™PŒÍ8«ÁÙ«‡ËY_û/¡ …ÇxˆDÕ¯Àf}-W;:­öæ+Çz2“â»ûê5f»'¬Wƒ\Ž5"êY´Ï[Ò$Ú¤ží ‡ÍgNµáÓEK|Î)îbÕØ\BwWü%¦Ú#×Ñ8 I zBBx\¿jÒ~ 2AoȾàÖ7ã|?âÛTè½±áljÙŸX“ªè'‘¸ÌȨ4KÍ¿© … OŸ›ÑÎ!éÛ5¼½H"XxNá“lêâ²pQع‹Ñµy1Õb¤ëð3Í#±³F6ZbÆ È]Ò÷E4Ýš…Z{þ¨¸Ögý­Ø‡™è¢Â²vÎøƒ4ÙÊ™;SÂ/¼[K=!—¬A€ß ]2’™¾%±¢¶@9(Œ)ø†²½ûBUJîw¤ R qKàf$·ÆŸœHuó]ÀÓ0ËP ¤CW1À†1°´q7¨ Z89of9WùëD®m°& Iµ÷ªÈ0¢I¿ØÈ¢:±ÆõÁªÝSØ“ :WãqÄ ãU8/Îrn.áî©.ǤÁôIV¸y­´+I·ØS^Š˜"Zõ:Qàquügéiv•¸Ùáòô¼†Ü@,±ùý‚•–Ü·„¦¤T=@òNYAh&ªeQQÀ„Œ½ p!S¯¦î ²l³'¡ Ì@MöƦ„;GÜ À”{I*Áœûù{Оڱ³…8qìXpò  œMA»f×qhûx¾¥N6ËM#A»òìd.Âáõƒ!ˆíX]KßãÃ…3¸8РªîeQ;­`%î—P!Q^ClI$Ö*¨0¡.¤9”Q\«€‡ˆ¥ˆŒ˜Ë÷ï<ªH9öøñU2”é=v)úþ‚;2@‹ï´Î´Õ‘x굄ÇÔã…•w¨îäÎÒš¤1 ‚VXD&$PUbX¸‚ÙŠ?<Çck§“¾ðe•ÍK>Ô8½Dz'‚-—ê‹ÌÄÁƒÅÀ~ °Þ2Ý3d#Û=[³ùH›ëOäh:†ɳ• ‘8{K§¨#>CÅh@‚”KšÀ¶Ý*% þ P ƒü%‚O~Þõ²!9òIèíþrÊßâ•Snè•õÚMN+JéPšŠëA*ÖÀ˘™ÐØ…á–táIŸÊã—63ÙÃH,yràôÏ2ð?7]íîû¨Ô…]ÝÁ法™–ÚÛ>šPÐŽÛeVi¤#­b<Œ `e4 ϧG•4ÅF¹a^›Üq Š5ÈZ8¼úcaóþO´ñö¹¨È_ëxØß .žî\9Îõ!P+ÔÑ9LJ žîÝàAõZ±»u‡Í9Íqr¤j§/YJÆ[v¸w <ú¿8²ìßÏCyqÒ×aó Œ‰7÷Q?R›™Ñ7gþéÚ<ãn[hY&Ï(ò Ê Ø1(8þ†”äj@ïc†Œð!˜ä…—ƒ §ct¢Ãƒ :6H±->üÕÐï^f*þë øAêï±Î5f\àî-öÚ3âz±˜~´ú;4¸þ#cÌÒ‹DcIÆêÛñD|Å‹Ça3;TÆø:ߨ·oô“@9ìžó$ %ú}.àÙe»€®” pUB²¥NúæmÉölйí~àð9H¿éÞÒºðiÍŽÕØƒþŸz~§š¹ý5öŽûŒ:ÿGé'.´™)Èb‡;B_àTR¾P½â‹ÈVزòžÐe…e„äÌæ[!Y»ú¨MSõ[Ô' ý¹'býq×™ oß`2a,'¯8Ú{?à~M6ë3|À01^П٘Æù&'œO8ùÏ~®í¸Ìýd$Öšj¬-ÏT¨ú|ccŒž‹ºJ<„Hõ+}ÏuFeù×]ÒDÕL—L!BQ ‘Fê“C˜U…(+C±+?9¿›çúôUë2ðLâsÌÐê‰"k‹3ƒÍ¤H0¢cA²ã”ˆÅ“²œ§jUU k‡ìÏ¡ fö®ÃMŽ‹¾ºêއ.mßwxss¨f }CR¹cïʸKv†7üqn÷â§l)o3³JÝ[§ýoX¡¹JÜ®è…3ä6ÀéšÂ5 ´þ}^±â‰H~Ð×nÌg41ÚÊ׊HÕör9y¸yJd‚2Fæä•з&°öþn‰uÈê )uãb1¿Ý67û›§óé0­ú¯ ^b‰»³‹·'ˆ¤SåLˆ T¾é^3XÁSËïµF?ÊÚœªðksÒsÄ › ]ÿ»£H_{¨3~èP 8øäg¢ïˆB{h_ô\Œòа#̰Ðw—î%C!»R}+¹3}¸žòVý@™ÇÄ>÷Q´ˆ#»…5X—s›ÚrY°{äŽÒx¿'úa%KÆP#ÀÅÕXÒP—OR’Q¡êh–ëxDÂÖ"êl YC› ƒÄ…ýGwê{þ ÝDðz4^UHŠ84|§Ùvg»à¡e´þ¾„{–Ç‚ ¡Ž<*Ó _ ¹{Öb*åî÷0ÐhÎ;é9§ f éx¦1]_v¾qnÈÈX"øÙÒ í*ÅPÉ Z¼!TÆõ3@»ù­§|9ìâ“¿6Ì !U/;½Ë°ûnÃ|Ìä…ŠŒNß3¥"*ùogmmÓéö‘¿Ü—W;üb°Ùïƒê^–ÒÀí5,›ç±½o£ª½¢×ò±*W¹5Ÿdº[hú~Ì(•ІÔìóÏø£fC…¸¯ÇQœZ5Rmœ96‚x6×nz$Þ$”É-ê/¿šÕŒáRéÌâÛа\\Kd-A³%´Óïwžmôo}?¯d¡:úDxcÌŸ|¾6òf“]”ˆ¤³µšÒí' ÇÄ^ò™$VAìwkÎü°ùŽ>$;d,×ø¶ºç~„6¯«r’•ű3fl„äÎxG iËh Ÿi$¿Ý2¤ã¶ÈCl{¿q~DÈõPªS ž;x®4mSÌÇ‘¸£…Ô–y¤Ä“Yd«Lî±éZ™XB«°;{Gò9:Ä&áDmZ’tæÝMNFîÔ®GñOÞpòHM'å¡îB.åVÌŸ»&á×Û6'Ó•kQW¤O‘%¸¯uèf\:Ë„/‚˜¥Ìu«ЦøïµmY` SP",3ü¡ï½r‰Üs#[‘ͱ].Åf˜n´Ø!d¼•ƒPÿë< 1…8Ù 5¹ÖyD¶Gòyù”•5è%ã‘z°}|TKf¾R!’¢¦'xF:¶Õ1l±ž×L^k‘ß¿‹®£ÅÔdîï=üòT¿ZðëfUAËdoÏ–>Œ¾Dí5jÉ2Rå]‹¢…úÍN†Ög;GËü·ç øÜˆFˆ%‡#óÚ­ê_úI$ ZvW &}%²÷dóÉ(¼¢uì3ƒ©g ž5C˵ÎyÌ‚–L`þ™=áì…óM†Æ5u¾ef‰™i¡*Í«k8‰¥ ?f¿Cxîtsþj³îɨ².Ï’HÀörYð+ˆ¬¦·b\7UEîbD`½Ôâm€sBÔäúÇs_æ…)îñ•+L°Õ˜7†SàÞüãÏíáLiÿåïD=~þñjêèj¸ 5Lá•0þ$¸ìËD¥¤¨C>¦¬ø­\*n²aDºG0W=òO‡Ê4 ²‹õ±‹þî«Q|õDeaÈP¥'òy„ÔG¹ºJì BHÚŽ{QöÆhìÁ*M»|mÍöÈhúï&¢^¶¥nJE8fu~žpöxëS@–|M"Òw_Bœ{åA½Z&„u¢ë ¼#€…3i?ëÙâ•nå@^]ý§Ö]èü‘Áui”˜"È·È=’‹¹«ÌýªÈ˜;Ÿ¢ õyâÝ1Z¹Ä‘ã­%·CD1Îlô)jâ> -9"Œ?B®%¹Ù²¦ñ=J,a6M:ÈΓÑf…Ç÷}¦íÂZørc¤¿#Ú"i[#mœ“ñª`ì{/Ãè1K>_W ” PE.Õ…¦õèÔ”ƒ¹`43Žn^yà 2Ù@€ÏiÝ™;*H+Ï,Zþ$ï‚Oï£CLâ´ž®2ŸÈÌ»&n¢Á·hž& P\ w"ÿëýUr½AâQªR¢Ž¯Ã1ÒÌc¿æKºú/Â[Ì!£UIÙ…¨íå7±òÛÛµ2 œ/ÖŒò„†Ø¡9wñæ.îY9׋>Û¹;Ê$ W»J „ä ŽÝŒQñ7W§qb12ÂnH†›èï 9yó=” lt…'‘©YwS½ :ð§-9Çqc—ÈËþŽ0u¼¹7?2PŠ7¦kC†I~¤\HGÀÎGYc„=û”Š –ˆÅ»càNYucõ¹‹ÑôÙ8«4f¼D ”ÍNo ˆvG•ÊÝ …,·[ëøxábÙäjJ+Sz­ñ®¦¨ÏmŸ)\±^²t.TÞ\5¶W |È,-¹68Ž³Ò¯æbzåéÇóÔ§#¯Ü4>-Œ§™I=td3(XæBïìYvøÍï,´1OÑt ÜSðI †Æ!y›¹,¡|“bá’0W6u¨p‚¹xŒ|ùsg§¾gèèÒø‰¨’@Ì ±€”,ƒZhÛÕ=~%ŸEë©v¢—•`ëuæþ¨šŠ Ep3]”ü&—ólø‹ÖcK‹*×mÊL¨Rî¾Ãâ~P_fka ÕdI0Õ¿?ÄîuÎd+c•J0Ä•ga])‘Ì0f×#Ï­o²­]©x¯Lw_Ä)Úÿíí‹°}dM­ {¯*J—VÉ»zq8³U±d Š:oÅZêËaÃdôÞv™EÑáÞ;3³çP3¤¬µËؾ}à©Ghº«lgzYW+Ã'7‡VúÚèhŽ­YÚžd$óÜÞ,©Tóp?³Úº[+–¶WK÷)»ÇÇÎ €Œp¦$½T‘·8nTp¯­…ä×çQ_º¼›¿¹ ¬­‘SpçúíKñ”à;7o‚áq¹üâÒ‘·’û¹Ï·ò ÛÕ¿Öö-1üœ]÷¢|o |‡‹J“‡B]DZ¡&Ë#L^V´P1=ÀLôèiMæF »Š”ÙžÅp¼Ï@k–óCÙ…Y/֬̔!K@dÁ…¶Þw±2GzîÃã¬{ÛUþ×É0oÏlÖÂsï¼ßäKy®"X¸|2ÍõFÔb>7®¤\<VFç—¨s⹞JîœÙÕkÒÿ]W-8­°ü»{‰rw§%i/3ÒÎ3~˜žoétö’:T48©LTþ‘ˆº–¨JΊq.Htq÷ÕiØ„ÄXäœ3zŽI Öd5b¿©Ÿ'h‹ø;šÙÃd1Ó?#üü)ÝG¨:Û°Z„ }Yðz(Y&†h@Pÿü~ÇÊ} `&òúF´V•ǵ‡ÍIƒ6+®rò—©oˆ ਠä¡DSÎŒGö^ÃGë»Ìõ2ÍÜ>é7»˜Óˆºó•Uxe·Ž7ÑuçÓ35»éœÈ1ˆ¡ÑÄý,§f–üá‹jÈþFÜáøIýÃÆç^7oª`±Z7_(܆ÛѼ{7‘uŽ¢Æ`ë2׌ƒ!3>œ';¤,Hy‹0â‚é5|ÆøŠü?àõãµö£®AêJ82.³Ì[uh½ŽDØ@c¤”½/—ŽôÅ;±jÙÄÚIÂ}#î3ó%£¥®W­–‚ÊåDÔù“cЄ1S¦Xž£ŠaTœÏÛ™§hÙ>rSœ=Iø×žC¨R]—KÛT×»¦9UyyÑSË—Ô'Soe†ñù=Á¸fF› I1 ½LN´}8kðóx¥ý[ÌÄÈ\·àœ«TçÁô‰nÌÑÉ@ ü«ÌÇU÷RÂJ2·ê‘)Jî8ôÎ}«G||îDûÇËCp6C¤¥Ä^•®/oŒW$®áø„œ¢·ùàÁöòU×Û2h(nýY妿"nÕÐ]×È/ñº{930 eà€±}dN —9QJ¨jwäqÏ ±‘Á¾¢•#8Ë÷Ø[2Áé¢T-óüGÎÞ!kû¶u vDwyÿO敤]gþüæŒSK?©wÉÿ¹ÙϬ×î®’Z¬‡Ä+Zá2ó¶ºe—‰HÝ»Ýæ;cHpY Ú[Täs> …ÝóøÏKíæ}¨M½CºiOzR‘'É1x®Ç×â*ÏHí_½À,ý>èÏ–¿è;´T •8)Qêx§ýoh¥üwúP²–½›ºÚDkD·µ9£‰‰î{fæE·ä¾{tù¸ˆŸ­§Ëªþ{Ò}²ì]ðéXÎK3‰ˆB$@öŠe¡ÈDÌO9g·N¯Ë&´Û—9p¹Ê2§Ó ¢öGl¡0PeïO#4¢|‘å«§´ ÿ©"]~ÍÿÈÔê>7ñ»˜ÓLÌ“·ß\Ù5¾Wl0ãü”g ê*]m8@ †WhŸ§p˜”Ó›9"}õ±l…‡qÆUG$H#²ó™gêaYMGݳÕ–‡Lµ‘b¯ŠJëϧ¨8Òæ¼¬Ç~߯:z“²­izXsH×ó%ÑŸ½£%ÊI¡²zóÑùÒK»áKÿù_vRÆHà=¡µŒÂ kc¯û.åza%¥-P´JDÐ.S2ú7âÊÇ †?ï|>Âô`õ¹Ue§ñ…Šç%\ç?)ø ¨ÝAû®ÇÂ[ºÎ5Ëþ”¥£yŒ ÅÐpŸó´Ùmûs8+v•857¿®®=£ÆÂµxžf¢kMlÙrÈ+€bÐ+©“¸Ù­Ë«›ý+x%€ª½-WAÖX_W6Ö}Ï>´]‡?û†`…ßxÙî) /ì)4\ÉïŸß(ßA\.žÙb³õS;újXï]]½O.°@ #XƒV³ØÉƒ½æþÒTï×Zzx”eGvKù‘а!Ã5€JEŽŠ¿Ñ³.Žm+0äÇÞw\>ú|–ÄhêrõkäÎyjYÍjñÜ»"Qk÷¨xiÉ–³è¾þª4gÀ ,ËßV×>V¢«\ÑmáâRºêk­ xq$9$a剪ÚÍDÿÙÜaï¦e`‰ÊÑòsé-¤è%hyòà ”ßHôB 9r{â_Œj[† ùheÇL cdü!àtÞÛYm¿W›’ò.ïî„dŸêêJìH[ÐÎÐo‚s}óùÎ ÎYªÙ>–OVf Ig›·è­ˆ¾ØíÇÇ]ñQt `vö^G¾¬Ø¾3T63_etÙQ7K$xxIý°"Ò"X:§ðÙÂOÂΙå 9úÙuÛlmlN¿3úïsDT#CPPWU¼{”k|¹uÕUIsùUÇÛâðªUfIÒîí-]õþ·7Ó˜s7ZÄÞö| (¸¢·7€6(ö¸ 6 ’âê7€©q}Ÿ¨D:1˜ØÄ/p³ö¡+£(w*×½ÿüûœ\í.¹µ¤õV®RlT¥zBsC  IA"€ÓA#ð=ÁSüÅÒ¢te´›œíÙ¤Â|#¥Åb¦¥‰B&Õô „b¨$øpZLˆiPÀ@*$‘§SÃ)%,Ò‘*€ª ?: x,ŠK2$&…>¨AIH´0‰ª± H*Fe±J e8"€Š1 ÿÔ+óÕèJJ, Ç "Ö“‚ E(=jÜqµ9¯aQ˜ Ä$¹ÈšqĪ!«CPq„Ár¥¥â0Tód_jî)£9rî¾&"( ȱ’¯cÌ %N*J¦ªÁò£@XŠW ËÔ¥QÊàˆš’ÿ2–¹%—Ua®Eë@¥3*P_ѹà‚èàªA`ÝugsaUå 5(JVcZ(({À4o¬+ KœÝ[ÓKZZÎ&¬eðhÊפ&nî”Ûuû¨N“=À¤š$Wôø}[e½-›Ìý‹ÍIN© SÃÃ]#¤âˆR EUº]DaQÞÚkZÀi² ª•ŠJ8¦T^T%«® o-›9;eœJÅ1š) $¯ÔÔàg3mE†¦~ŠÁ†ªR/„Žˆ!ƒ Ú°#S R‚CêU*Û,nJëjHW–þÔ »`ÛýÎA0”†²ìXÐ)(ŒÉô¢ÜvÞ`½–ÇO‘æwT|O”e9‘j(ôi¶'¤NמJî˜éý4qç~q»ø £[½°q7¨meݵҀ3@Z°³IkÙ£‘f¯j]ÿÿWÿï?{ïGïPõýh\*ÓoèõÂ'£×šš'÷æg·¦{tñ¿És6SsÏ[_g×®,§;µ©iŠª„Š•©–&jãÚzB¾jRÑ–ÄBÕטwàDO P‰ëâó0rE~ÓBZú]ûLŒ–jµ¯Aô KX^Å7£ñ ·ÙœÁ,/}‰nê~Š‚‚å®XŠÜÀwÛõn¾ln¾&®OYܰK…ÙAb)NŠ ô×vóÑ,R”}t»š<+þ½ÿ Q€8pߣð®-f L2ò ñ¥p¬`œ]âÝ…R)¼”¾OBîŒLm/Z§ë7Of¹;P·~¯¥ž¢7Iá´{(ΟÆðBâvæÛrIÁ$ÈŠ»Ã;Á*v!wi·»³p¥öáÁeŠdœ‚\¹;[ ž®öÊ)m°=¼õʪ­À92–$Dæ"T¥pKÛ=ùJt{;V|k\ÌqÚ;}G¦˜°·3VpkÐÜ + %ö¿ié~Ó nIrDk Ü–'ÞßUI¬â8Í.¢p!‹¾ªJm)@uu”ù+=°|pABË…°¥P …-Çþ°Œâü»²ØÁ5y|'‹¿«Ìбuz”j•pKRqðž-ÞQÉ NÑ­@ILQ¦ ß\ùV~©q%@œvõwq\©2ZtâJQœçÀý[ƒ³jmSÌß6 /åG–8­·qrú`Èíþ•€ôÕ`Âb&Pø1ÙVð›¡ìG¨(í¹ÔðdØÔÞEN ®Fk²¨âÎÃø™Å·ú‹pÓèíÇÜ&Ú ÀÈtÑk')»0Âå•Y‹EU ÞËñÒ(ó‘U×KKkéá.囋µÛ~Yñæ°rŠæ ú†M‡iϤz½Ó,Jµªu¡«Åb9x¨Xˆ%zxröØoóŲúëZl¨]øL]›C TwS·^ˆ¦›Ó¦áãÙ›%mï=Tû [Ƕƒç媕àC7¸7èVJ¡ë¢x²ê 3"µ·Ó#?4‘¶È!gžÊœ<XÙ+Ô¼nÁ׎õÌóÔa7a,ü{Óš’|B¬ËW´ûìÂKMö-i÷á%:_¥æÿ#€ã‘šhŸ<Ž1o <ú·þ†1ª|ÑC_lŒ¨µoMÐ9¿ËŸoþ{õûÍ™;ÖÄ&¬ùÅa§ù7Ʊ ÃiÓº©¨×ío„¹ˆÜÙNNL(˜Héˆ'„°Ki'aÏE ‰ÄÕxêƒÒ¶À7lE÷–(Y9#v@¿š–Nçc®Ø5É/L¢£³^õRnò&í+ûS¯í_ÄÒ…Å"Ʊ[\o–ì °¤$û76g{¢)\HÒyáá¸;¥ Â#PEUîÕ\Âø#Â0´Æ!+¯PÓýìx%pó^¨_¿£†‘x7A>„ r†£›ðœÌ¬{ ¿I,B„/‹‚ý‚|# À8§áÐt…€¼„ÎçÓŸ£_g¶ /“á<¥²`*ÛåÚ¥b|¯ÂÐýÎ׊Átxg˜ß&ØèŠ„Ë<ÇYRÂ…}gFjµP÷eÉnÌcB;§K ¥Ìɳ÷/ˆ øfˆÂX|´¼çþ€ Ú:à,Ÿ³vxiôl؆YÜ튞­Dn~CxÏ*LêÕ~¸“›J¸€@ÍÜnÓ¿&²ó9¸©°…JŽ˜‚:ºÍOqXìÝöo(Fúè³ú_š‹¨³úÇÀ>+üN, ›îfƒø¾dÓÙú¸„Ü_¿nÐÙ<äù'^ÿõñïHÔGر#B¼9æ,@ß§}{<çáÂq÷ IZÐ)Øï°ïù±`ñÙx»)§µ9; “¿TYKßdu¼é¥:ƒUý 7×R-õcû-Œ¸1Q;À1¤Ø•ƒ3BÙ{ÏlrÛëÖW€ßÛÔã³+ðªÛé¡é>ÝLƈafKK^ÃpÓü—›rõô}vú‘sn#.·÷!?Ùચ—e`ê!Ó²QùÞÁÂáô‡=³À©írcB™Îi~}èuw\ÿÖ•G ö·ÂÕ¯L8 ,ǧ=k1IqX†Çî9CÞÉÄ4E',xp‹Ÿc’ÚMiUûŽ?öV¤Œ–ÑQð<)^zVðB Nâ8êØŠµ€RlÛ0ç_­omrE¾¥½Ã¹$s>Í…ûCcËÛK÷>¹ö|‚â¨û[Ü!†ÎýØYÊH²ì;KYÒX'd‚âÊ¡"‚A±îÖÎM0{ïÜÚ;ÏNðì…ï|•úìbò%·ðŸlýêµ…3îtÌKgNÛ³Dl;¯gµã)Cc‚­yNvб²‡2\cGNšMY²Æœ°aâcìuóGw:YïØœfì¨öN†:ð^HHYÌä漣5Þóº¬ÇÒ/Ó™m¨´µoQ`A³qÇŸdX{s\ M‰ð¼{z¼h0"\±6÷eÈ‚%kâQT=å¥ l¬=<'(­rã¯h¿s[yfÙ|Ö (ÑÕ8¤‹nΥϜ9˜Y’뵺EŒc{U<²íø®Ž¬Êf/Š‚Zˆú@‚!k/%µÈKì8xæp0®Z+³³ÚßtÌÊPA¸ÄòÜí‡Õom±AZ@´™ÄäÛ!ö½>Ç É+Q'iêæ_ )9Ñõ¿‡¬ ´i±tÛ‘„b ÂSÑì×I`ƒãÈn:û%™ÿóÇ ãÔ'‰È*„ˆ"x ܸRмWÆŸ>-#ÀmÃFæçþY“Ãr{×ÖpÆ8¼D1±Q8ì½C Á"¢¨*ÑaäÚ\´\K¤Ðr?„¶Ôþ›“Ñiy~Óq”ãæ ‚åÜ1{þ¼,%a€Ö³'†Uã½—T¾• Y0*Š`–q?­kÝèìøh”ó‡ºKoO!(:…:ôNÄëM`À¼òZn‰G†–4³~cc²+¯A☕üì°=^>*ÉÌRàsäd2LƒíjlÑŽaPÔ1³©‡ÒÞ³1¨[ê«(LjF (2€YcõÄsäe«‡ÏQ[;y¯é2mcáÏ~ºÐì—âãdø˜¡äbü–r{ˆQÑllé¨ÅÕÄPˆú2矤2/úÔì0b©óLÓ@Â<ŒŽolùÓUƒWÅÞ –¢Ûš¢pœ§ '“Mîåu̳èja¦)‰RçP¤œ®4l'mz_;Y~ëæNéõ—´'é;9groÇ¿‚c_ÞÃ3w²8‡3v£–3Ü• êîîïNìÔßyL Ý,B}¤G.‰•µó¶x—΢æb'c')¿Ç9yqoo´jé€Ïᥠì ¨Ù,R1á­QsÁ(xÖsh¾ìôÞ`yõÂÕ E¯7ùÊ;ŒWÀÆycθ‘²fÎNíÁù/¿AÈÏŸ‹dyô ›¸ìweÑìM㜮Î9XT¼ìcâdäpƒO.–ÄŽ*hRW3vcÃ@©Ù1O&]oX˜ºÔ –¼°Q;…!·² Á}üj.mÛÛâ¼Pæ_ÎÚ>}z­¬é´9CU_žC+5ùôaÛ1zyÌŒ£t0çŸKKV3üœ‘^ƒ[㢇à§’6åuP †¶cNíÛäfvƒ¶rú:¡ÚOíDÅoâHôëãy]h½?>e°¯ ÖX󩱦‚æ`¥¹%PÎZÊiýZÆùá,¯_q Å’äeüDSµêAs*"¨Fa²•¯,ùfŠEiãÖƒÂ]_Pá˜ü8ˆ+«kfGإݞ†» ’1–—$ΚT\ ûìüÈX6z Lí ÙŠSB)™öǪK8ø+Îá…ØY°Ž.k:”eçaeÚ@Œ¢RFû½}æ‘Ó½”5â&aÉèÖ?AæVÁªÔyÍﻓ®ë/áðÉ_ç5˜€%éù ‡÷MPWÔ#Ayˆ$>zd8üWLªk?EçÑòÝ­¼±Tó€’­H™d>u„ Hèл]½èÒ]Í9ËŽ/j«Òšá%ÑUš>àÑ=du4,Aù#‡ë‚òHA¹zÀµ³Ûœ !ÑG Ž.wzDMó(½Wœ¬ÙöéLè߯sËsöÆ{7sŒ(“\ÕÎC#XZ—qxÊ®Ü÷öÈ¥Ù÷]ø¬½oj×/š1BÌ8„èXë»™öp3çu«i>ç¬ÈúBÅ%>|…µzÀ&hb|ˆSvSÏ9‚Í[6IŽÔ™ªÔͽ$€Ç¹·¡s¨f&vŽÁ>Lz%†*ãÒ`äè7S¨‹ô ð ƒfë·¬€´†ñ‘×pjqh9ÏÄrÃrDNd$0Rh¹P-‚¿^L|A…qâôò ´V_QrÊ¡·ïð_x}¸sD·­utfkünòtë -ƱX§G~å Uijꈹ¦RðX]å'6r%Îzdˆ?ŸDI‹ö ’›Û”~/·ƒúA™ÞWÌó?1ó¸sü `KÔÕ"ÇJp®Bš Ͻ’/f1'ñ‡oÒ$7G¹:„ù> ^®—…ÿ·ïö°ùêòàA† ~¸Çùs1#” ”ø5çð¯sY9K@Éá4_óÚH°¼væ\œ0Ò<+óò³ ‹þÄ 6½È€´§Oò6çèHÜ\ÃîdCpœT;–Uëã6ê)|û‹{·›ÕVòÖ)ˆhÇ_ ‘\¼¸‰<²ÇDÖÞs›¯Êœt ¹[?úãkþsÏš²öë”誮¬Pµ7=?êȾƴ¬F:ݬºŒé#œ°Ê›.û×¶{7…Ú²Ù,šU—Û¨Es­Pÿ?çÖfû¢” l›L* ¿DìÔ®XÛÎ]îaáÞ”3m+šéÔëŠÎðú½Û8w}/=C²·Py“”»]ñ ¿6™ZÂ@Oóõú+ØìŠX yzµ)]mÐR< ÅØÆ0!_#êRC)O#£Ø3D¶\ZˆÕ)ºÀ-e©A¦Ö\­àþH¸,ᳪÁÌ¤Éø>FçXØ#Ú~L=€î©Hå‡aÚ\íˆÙ¤â½q÷xãy$/ô/hϤΔA䃱"¥–¹CSÊ{ƒÁ‰4â°“}Òçñ°Y>-MŒgt‚'¯ø˜óOzÈÌXacœpo.XxTxÍo4Žc©uüA”Ñëãú2µŠTOYÄ]³Sdˆ±¬LI^@üʤ Þ:Þüm¾¡/èót Z8ÞsìÝŒ«Þ~Ý×2w-«éæó±9‰†!;]Ïi²ûá8JÿBY'Çø£¹opI>9 yU§ Q…‡ì¾v(qº* öxPv{Ugu«m×ÓÅ얇έ㧻”¬¾þ¤laoăãæö¡kâçÿÂ&`c6Èü®!ÅV¯¬ \†3^?ø;óÜ)T÷ Å‚z7ת»þyºŠ»£Q#ž­Áñ5Üu­¶ 9ÖAÌ7ç|9Áçÿ‡§Kä ±¤ÁËMXzŽÏ>m;§Nð¨Ø6/ØÊjÖ¥iøÔ§ú D¿ë‡Îk-{~žàd—˯ìÏ5´xB-Ðó–Rö³?Ç:j' þÏqŒÇÏõp ð4ß…éÒZ|Ù-ŸýÜV„% ¶ßÚle¦ê¨okðg÷lLãÁÊ>6PÌ·!iæß²5FÖàÓoÚP毽_¦õ’=Ý!]ÉïnÐ~ž§oJ{*f^ºö!ÀÄÚo.í 7)ÏcIJ‡ó±Pð¤;´›âH1¾ÔMóëø¾¦õ8Ñú¼Tž4 -±—¨9??EÞa~ið!~$CBÎjZÒ%¿zßï²Ú¾?…Ííä ûeò zž‡cж/€¹Ýq–§l.EÁþdʰn5IJ&Ä o²–ŠzY—›ÉçK¹òêné$ûk_Ë•–,u[…j!Mbld·„‡‡Xx„^ФF‡iJ'ïÌjfQs¡èµ¨ØÅs %”öonœ gT«„<ùA=ðLT?…Ö×s˜³Vß$4SaÂÁÃ77ߨ‚3Ýyv•Ÿmó“’;›oô\B‚_Ü@+58«b糦£ X$‚=I" #½€"º BOTL€jû±tÅ‘I“€Š5z~ûäCÂcï¾f=°Š¸VcÓù8Ä_jIÜ#¢¶¤'Ëï¯p£I’¼`!’©ß—Jc8 ÍÛUT39ÉíÒuUŽxFÚìŒj°Óóÿ]%^“Ô$˜¾Yz°‹’Bk¡ÆÙqT DT{pÁXTÇKåVÆdBÇdèÎ Íhðhóù¥‰ÉöÁ1%°¹ Š½9;UÂþÆÄ~w²d ·> ,ïi«27ÌQî€ï“ɯ‘ÃÆÅPø)*M‰Ò·šö²D;iiõ†Hÿ£­º´»¤_\ÍûŸ}Í»$–¶D Ô?M/¦(æ ËI4è4’³‡â¡œIí'ä—=}/¼o°D¹¬Ëý,BåñêQÇïñw¨EÓ–£S`h³2ŽoJÙfÆ&'ã­Ÿ·«þˆŸÞ½õб©YKù¿J¾·ÂwnÆ_¾âás}çéÕúÝÜ;Î<á*Ù¡ ‘ Å£§RQ“YÈOÙ»]5ÎiŒ±‘·å¯E;_ÉP2ajìÐ/1þÔ[ã†ë=ëÈÆÄÎ…KÞqià9€ð–«ÉSíb§ q€ûæ!¶O¬Æîq0„õ O‹çeÖÔÌ=‡ö)Hɘ9ŠèCÎ ?^©·ÕguYÔêÌI4ÜGàËn†265ëO}¾\$Lj˜"¯È›.@!+y–é~édùß ÐGcŸoz6 ÆÒˆ$ô%³XÏ}žM]ç“ ‹æ×·oÏV·ÿ²}ÂÁ­µ˜¥-9Yعxè?£¼Ñ”oÉÂmnG;ißzF¸Ÿs–xp´€ÆP—¬¹V[)ØŽÚ`½ÌóˆT7$í™çÞ¯å:õçÏí:]Ë+¡RÎpkrzs”¾…èÖ,ÌPèœt9ä»ã,äèŒÓyˆÅåÕ›ˆù詌oz·i0ÐåhÃ8»ñš)Æ[]M®89f3¯5 *D“·^KM™µeì>;lô3v^J°¡Cß©€'¬ ·Ž··î;eÕºXÿ,Å?ÜÃR%:‰¡óÍÄñ‡)]ôÁæ·¢ô‚¶ü.–}RÙÊÃ:œÁ›@§©qDª²‚soú_Ùy™aá嬘=p,\øe—;útõ²Ÿ}Sª+)ûµÌÑ£¹7~üaY›¾í„¼§ËÞR ïç¸è·[ÿ–W¹ûlh+…sZbb½UœSyï ¼cûö>oúOM¹¾1™Î}õn¼Îç½  ï¥S‚|vÏ%>-¼@t„:a=… ºâÆ~IJø;4£™ZR{šõÿ¶¿À(Ñé2G)\ù£Ná×Ó¼È,:§¥O…5Žó¹Œé€üh]meÕ¹µâƒx”›…Á35¤ºðìJ¾¯ëÃ{°0-É"Ëæ¯I%Ö¥1‚ÀMn Dn¾&î.Óò–(‡8⥣zÍÔ%rÍju•(£¹nz€º«ååﮉy%V—®îñamMU  Œ(¾ßzãÔ´øþ—Ýö§ÝípI' \Åíiò€bÒ. püîÊEPʽ Ã}èqY@š7¦\`Лгµò¾qÁë\H¹H··á“| Û¬*“'²ÆG_X8g µéÂÝOÒàIDx¦dI¸Y&~ʧá\oÉ /xÆ«^X65¿~‘ÚNúC ‚¾‰Þõ¾¯`}®P- FÊQ$vÃԳ؂ÛÃÕíÑv ¢*7÷Ó&Ýy¯½ý~Ô‚²ûú*ú3¼.µ`ÝÂKÖˆ¿…æ ¾¡Þ¾Â$Ùƒ\±úy¸£˜=çºÒQϳwR=Û®6ÛÐÚkw~fBˆ>½fHAúFÀ"|÷[TÃ2·å ßèBJ ÛLSuÒ½¢ÍãõKz©ðE¢c®q*¶ð¤½_ý ¦¡Ú_œŒÖŸ÷j8~Äíñµ®p9#¾¤wŸ‡` å;)€ÂRçIýÖ CCëC&nÿ˜Ìîass± n–÷E~R_Î÷’ç€s;ä0\¿#*vfYTÞjH¶ÙÞœâBÿh‹q’Ù$"«ùÖkß*ñz_N¬;û]ƒ6 M¯5âÁ…µNÿ‹Ã¸WV"èôCskt³°g†tÖ+&LÓ R…Q˜0yŸµš¸g´§ÎÓâvÂÄk“õzŒõ¿&»W'´ÎŒ…Ä‘&¥|ôï®Õß«–°õ³mç«»èù×FG~4E1P jÇä”Ö¸¹:iÕc@ÁEÑ«uuŠoƾVÄZƒ+þÃ|à˜kŸÔ{ 7g½²á­= 9†ñšJ –á@ÏI¥Z“¨¡G°D4áK\îW‚ƒ§Ô]ò¿Œ‡X“…Œ(¼D]}ÒÜwš Ù÷¥á~;Ôµus.L ÁïöüˆTTª[ƒ`g€•~<Bëß#R‡[šã»­¿8_+ì#gÈ37šcwÍ_ÛxèÉI©J}>¬·—5÷º`çGf+X1Fòù¤wŽåcBð" Q8²Ï‹DMìHOe‘_ø‚;C˜I àb€ÁDÖœñø¬Ð¶™} Œp™Ç^M.Ãd{Ì]#ù‹û†ÝˆeØ8s¹:yÅâfÌÇc8†›1e¢&L°©ÒER,³9›4ÁïðN¦* ù~W!ÝHÛ¾³ò:?ËÈìŸïc—o:ý™dð› ë°<¤âš=b4› ¹Û;ràÍäºTîTÂÉ”C &0(%ÛÕÒY¼Í*ÇëÐkž½¤‡ðêA›2<µ‚ÙºŽ¶£ ënùþã ¯7¦×i͇ê òÐ8y$<.5Ä^‡Œ’&«{‘ÁŒØNôÓº¢¢ ¡Ä™oã¸Eâ1cŒ[ ÎàôŸqƒpXM©\Æ4Ù)ŒEfC½ú㸇ÒÍÙÔ]æ%†Å÷ ÎàïÓLäåŸB|ÝrªºZ­qb†¬Ø-VÀé÷ºç½·Å=ò¹'CΈy?ԣƫh±¼9CæõT““Yât˜UùP½Ws®=a ºTפìÓ2\ý\{ñÑu…Éöè‹pl^½u"¨ œ¦

›c-§b1 ‹QúE‚t`°4éÚè9彯®9‚Õ–&½ÓÃúÅõôgÕò ˜A-³wo½µ¢:ó@÷ÝnþP £öõuª eò, âù[ünëgÔ¸¢³\·ƒ@”%ïΤ#αͷp¿~Ö¯ÊmÒkæk9ûløÃûr¾`{ÊÙç^ƒL…ä;Gò9{¤ï)¿ˆÇéÉ&Û$™\ï¯WÙ‘È Ì|{%‹ca¾Ò‡‘&–sNur„ÓQpi ÿ¹ˆ°¤ÿA‡Þòvs/ý¾ë,£± Œ‡=Ì¯Ë ¯=/èU«DI±T@!°Ûp^ŒõvöyÍN*ãØ™Ì'‘Ýêü^½â¶Û9;ջô4eN;-|ðÌSJ^ý/Žu§_”|ü>ó2•?ÔŸÅ"û';ÛÀ¸´›äè-¥‘.['ë¹£ù=¼yU°µwE vŸ)mމg¿–ÉõTR½BÃ_™|A“/QöÎÕÚÖŠi­C³ú…|cm^€2 ¦º(`³#˜70tŒTž†`X—§K|68¹x†‡¯©5òN{õ¨îŸ9§ž‚2¬a%2 Í– oåªpa{n»–;ÇÅ–rÞIÒ8ÊCÉÍp–zFÿ]y µvºÙ±Jh€Ó¸HéÉ7•Ú¿DtòÜ Ÿ1P•W õ°áÅ °g<¦Ç*Ξ=ìüß+8T´«B*½(r1=9Äÿ¦êjÔ!Åû¡«ƒ7}g6’zÜ ³ÝÊh1V™±ÏRž'Ùàe[¬º†–â9,dA†®š2H7ô2(mÅ&BmË8GŸ[EÄ›ªq*ÞAÞ]ŒCs*›5 Ã1ò3+zÎ;âºö¹3h€í>xT›"g'wØ/û¨©¼’ëíxÊØ%á½L€Ñ_¶œ_Ñ›t8zá}…/™ªæ¤³°Ì†Ü…y‹]QÊÙ4 £_Ì ëÜûYŠ^ëù3æ|âʸXmõyÂÛ`°ÍXøùE9®¬ Àz'-oàŠ/B™Û=g2Šfý«…ç{Ög0 ó¸5$,e!XqŒ… ““FZÕ‹ %¤Ò™%‚÷ _a\véÆs^ß3²vgùBŽhw‰,Â^í2=ÍJ9N¦ 0 ÖÇ4ϱ¦،†óN‰Z¾Ÿ"ìhöhc®”I'»µOÆFt¤`ÞØ}HQ•ea,™m8_^²ÇìZŠö݃°¸;Ê«†£h ÐW»ÎÊ#Á'ð‰Þ\Ø|ÖI3'zF™ &ùwF̪—FÜs/|ÆR£×6Ûù)ŽÁklÐãŸ>ºMdYÚè&£à„\Ü’µ1V”]E™@MJŠ¥K74ÝN뇴y¨óK£½Å]ÖN;SØSúˆ%ÉSq®a`Ùñ “ëÄ-%áÖŽOÞáe€‡<),ÈÝuH€ßàƒò¥1á˜gzç&å{pÏ;e—üJkrÆ ##ìŸäpøK Ðô›úŒðÞ^gsiøÛK_ý_Äm™ú†Óo°áK;Ùp#(³*'i†ïY¿âñ¥ªCµ£T`~#ý£ %Ùa„}NXùÎtÚ‚Å÷ /žHq®ñŸî~¸íC5ï¬Vð,-´I$ ‚ÜtıiP÷cÍïæ(K/±¡2±úwÖÎØJ§r‡Íg6>hìîNd—ìv1[ñ?t{m6‘&YÀhÄ»\k\i%ç2t寽Ód¤þìø{¹F¤TÔ‰tj‡\ö"Ü30nm|•&\ª\( tI¸þþû µ1~çY]Ó+¸ål¼­ê‘¦ô²dDÒúŠå ³Y"^B&ù,Ò%†ïMZ"§ ehçÛ7=öŒ÷hEÞe]7g¬]ýÜï¡1 ÓÝŸ;{dߌÁ{Ê Í€9?ÓÁÃâÅ™qHºl‰²UÉ’¯‚x¯Ð¸î‚I ¸ ïrœu äëû4`Û1³”¼0L¢Õak8Y¶ÝêË¿U^ ; “Æ—"†ï•‘*¬ŠibšìA7­ÝHçølÎ £´Ô&´´T‹/!»¦ËoÔ¶îOe9?bäæD~yu¼;„¼»Ã~ÚpãT}8R9h»-.pôMÉ'õ5‘Me“Dª#þ[—ï‹Üø–AíÆ{š¶• äeŸVo1R*|â4#ÊžEÛ„°pþ í˜ôžâ-äl\:šù¾ aÄ4=5è"Ç<“Ó…J°1™®:<«¥I†QÀŒ¬*.W-fÕ(¬NzÈÝÂm jaœ¬…áKFù¼ñ䂦–:†bV”  =¿ÈLËù9 ¸-ñ>eä”Ú˯ìýÔEQSfÔá¡„oZ>¤ÈÊÑXxÓ¦CçúaWàœÇ{B«mÿƒü¢Z;Ц…ŠøpsùH‰ÆY–mÄf Bæ1c ìá“ØÁHÊFºóÜÊ›M7ì5×R«.!_jßôÓZŠæ "¤ÿ!ûÏ_ã7»¿7Ïù7ØüŽ2Ò¿4ß;'$Žx:2&æ_Ž80¾ñbåÎ÷ŒxTg@Á“ºÅ‹îX3ºÛ¦09Üñda2oÿ8w‚"Ê¢P?j$¥)5Fò!ÔŸ/ 3¼Ô/žXGRÀ1'!íKøÈõÀc÷a}<Ý~O«¿@ =üˆËÀϱõ^´•0:r/¼8ïWy…—y/KZf{mtñ>±>€KºaËù‰-T Ú¹‡Xžúij·Ûè¹óœ¾î‰S¨þœ…NÙÏKuÀ@æÓ늑Ž9[‹ø ލ¶`y•àæÖ$lDÃ¥ &´bò †I£³#ßNAÛ%tªsÆ Îb°”VeÆß5ÛN>žµãËÿµ®åUiöó®ð6h»±Ä•-?§˜ ô£^Lÿ8.`ËøC&!ô;CL;颽×Óh ¤[­‘Ë—Ë`f™Î½^{ãRŒMÃ+@ë´D « ”rÈ1Í*—×ÚÊ++·2[éNppòƒ˜xBæ¸ù¥EúórN>mUY"– xCHœw±úCŸbK{hýâן ?GÞ½ÔúÄKp(… à±¹>Jì‘FãwÑ1lÀùnõŒ¦È/ªÁ€  ] Ϻ¹÷(0ŸÌÇ·”f,jÃX0Nˆ¶Œs™w›gU[úvRÚq’öë4Ô?G&{X>ý¼âÑëƒ éLTJÎ ç<ýéñçVPâj«„o<ëþ3 f¦¸u)Ë;•ˆEw÷Â~×=G·ïêª}ö¨î7è˾Õ,=ôÐ Ç-Àè(«gk»iLÈê‚F\üu³´\Y.üÖÔ0ågXL#©±W¦q”ªÛ84ú¢iœª³ü2r¥é˜“C5ÝG«ï©»4Èy3·)HÄs ABÕá—’ó˜ó­þà?“€¡ŠþC%4½#Øg‘EP ùÇëÏÎÝtŽ£}Ú ¿P sÚ•ãLŸÆl-ð¨ˆÂذGq>Kô ç§q.‰_-"<‚Pd'žûÉG)"/üÊÆ4€žô9ç¯È—ªXåÓHÕOmvªEȬ¸ ì@Fkµ{§šÛ„ƒ„ôÎL¼…e¼éx“ÙŒ4N.{tAÖim]¶–³…:a«&š²+Ék9FvÐ4cAL¼4ºH$+vîÐ#@8‹ô¹9ÌæN&—û\„œºL95±ð>aññ õ½mLnã¼^ÈÀ´}æxb+²mœ”»ù¯ÕzuKÀˆ¼‚–#`¨3ï¢LÈýi]WNRDü»Û[ãA-æÎ#,ž,Í ÍH”KY®kcy!£12µ4£U;*®ò¡&¼d/&}H ³%ê€ûò>»‡hÂlý(z @éK~ôÄF²²!ÎT‘Þú†¥ÿøzcí…´;ä)4þ¼Û_©½äA ¡òðFü³9?ëÆóv†nÊLYÞÇ ‘ˆ‹…ÄŸu®Zʈ^8^ÓHà|!MZkW>BÅq”b˜ø]¾›<äúb&„#.<9 GP£• 'Ü=fòóq†bí8¿ò†8×ýxnαn™¨ ŽbQ!xl`ìV¡ P*<†Ê›Ü\] ›Á”Dy-ïFŒc€O¦¢@ÿðÎâ&EÛùpòÇ"}st¿€yz J@º]{­{çVÝc§•Èó•Eß®üs(åœWwµ,ÛËbÞ\b9V–I3´,ðȤ(§@1b"—Öè…6ÇÁcá²µd³\Ž¡ôáįüœ´HÞϨWà|ó{¦ˆ¬©úJ¡ÌâÛ÷‰µ¶Ç\'1YÂQN3ÿµSrãxþkd”˜ö¿\èxîL\ŠC3ö¢Ûr9œ{|/‘øÚ¾¡—ÝöVn¥ ‹í”؆Ûï¿cËåÞjV•™¨ƦÇÄ“¨ä ínà–…‹kQ"§•-x °µ(¡ÿ²¢ŒOŽð½¶Œj’ö+f‡9чódŒŠdY¿Èo^23Jäcו Gg~QGW媈ñ4´€¦·ñ¯GVr €¢ÅÌŸa‰æA +r–ϹeÈ mIœŒÒÍR®Ó£?¨˜ó<ëJAr¦¡{’¼V…)vøZg¯²:ÏÙEÚWƒušÍ˜»‹Gš£;A‰ËÊä ” ·(ëT*¹±&g5úØO[lx‹"?{Që==5šºÀØ·i$Ù‹m˜ŽZúp¥dÄ õÑVÏњоÊÞ¶Þ¶A޵K…ð»·>¡&*|ð‹WƒBóEL©yg÷?O~(á–î)}hDCÑÜ»pü{š‰#Õ‡½Çx1ßñ׉±ÀóËœ,ë}å|m`ƒº¸ Ì9ƒVC„Nàß#޼:¬†BŠÉV0Р¤Þ,æÁ®ÿ-B_ë ¥m]‡è蘦+±á–J[,ºŽgÚše¹ ¥K&¾z¾ÎÞs-0¯f °„ùx›‰o"ÇX>ínI±ødÍ'+_PÏQ N¦TP„6áMwÑf‹Ê³­3X²¤` ›oQhœÎÔ)îbtœÀ”ò¦ÈÅLJkÚMÛìÍ(×|t@ê²ð>PÓP±ìmÐô”£ðF°äWãTIUg¦Êb BXÒÏÑŸ+Ûî¦máÿœ¶ØœÍsÉéBþ«"ôžtŒ 'Gý7m;Ù’~¶ƒñ‚cG=J£™`¬ê„:¡ãȽ¦žLTbùÃ\º$-O' aY}¡·¹Öº® øçØz™Ãy¸->ÀÖððîãV»Í»«–#lA¹Ö$Ç sL™dÏnöfsa“[ù¶jÏÐúCL¤ê[~³ÒðT Onìr…\ß±§ï…ŸÆ«Þ.ÛiÁ5548È „nÈ4då{_$Ðîðà 8/z—<Û ê‡3æŒó¸ºmåC½zLTévqŒ(ÃøW¤#JäÓç¡¡¦µTÜ(0!HéQ$Ò™~Z+΢cí|W¦¤Ý§³ iož®ýIJ Ê''|{V„Í»T›øzÛ¹ÇJñ9{,9¼p-Bå ‹¶V²Ö`ÔšÕC1„„C üáÜmáçLýµIõ0Ìvæµ±Zy 9ÿê C2)’iŠuÔŒ: [ÏÔ] œ(ÎõûƒQ¿Ð»¸šÛM:EH9RÓ·Ü|B‚§íªÑù:\Ò´bèxæFŠcýHû’ûâ˜9m²iÊÈŸ{r= yÕJ¬-'qPµ8ïð¢O28hiÆJ¡È„h„šÞ­0?æûý§øòÚ”5¨KN6ÖÒ¼]íÓbÈ¡ª<‘¼hW’Þml”§†Ôô??-ÐxwDò øãP¥e®§aãu;orõDl?ÌQ0.Íï ï1Š…>;$›Œ=[ý¦":bä¤XuÉ þÒ,åØg;ÁϨ52,:°óéKí}æ÷8ÒšhK¬yq2Lé•~#‹Íª[¢žEO‰ÞŽ¿­ÍËËÅ8ƒ "áí<¿¸v"9Hj•½!->¦u)!9öÊOñMt,½§'"þÅ÷IÇdŸ6på†û‚1#…èL2ìH ?¶ÇVG•’ý!„£˜¸’bo]Ò|ÖïFÝÚÝv·«”Ìœý÷ÿÀ ØÈÎj[›,‘XÍÅÒg‰Ëë?qË÷$buçQòv¹)ï#6@øê8Ž$OUD%b#ä’¨*6•IZoʇB’h«*•PB[¼|Q!%lI@ô*Þbùæ†ÿ†¬fÊ»ªÿÞßÿÖ¾µ®,¯û޲$l;Å­\àrxñf!!$#f7Ì<àH# 8¢GÒï;C &(Dƒ‰?ýas!#Ù¯” æI–„ðû6„F€@I)(0‰ (%¢äp$'=£¤Æ…B±àÔ`M–ˆ`Š)ŠMPJ*ñöó››Û•°ý?Eœ.DI2€!˜êøyCª£æ?žÖXC„{$I¨O0ˆNâ¿‹HP9˜D"JŽG„ 0 »?ƒ¹–'Ô «c“©gU³Bi7'Ò&8a©HXÓW‡–ó4¥Dwh`LpœêIÆŽÇÓ6(|iç'@ m HW•—c/2r¢Ösv­D8 ,¡Õë×££ Ò)UE$ú&¦É¢:?bVsR™Ö–sÚ·qÎÑëËÛÊ5|¬©”••œ®ýIB®>>s[”ÃcìÔãR©N… ±$%œ§š”„fy2#`Š´±ù½” "S¾ÁGÈÔø? ³ü­H`1“Š4†¨ãl L”Ùñ Q&,÷lhS³6’rôò0×cÒ©nF¤XÑËDIźã&ð¹)„'—ñ2qLžˆA®³PNÆgA% qPJòQFÙD2–•#-Rº›KR 1QKܪ„3Ê CÅA‘ª¢T‚ÊBuéB*¨˜–p!”j¢½ãˆ"\‹Jd™X¶ô}¯’mÈ„¹Ù2$Ô:62_jåpyºÄýj_äM®#CÓ$#í'Œc¤%æ±I%T`†J%jòpZ¢È*¬±å>gæä?Q\Üj×g¹i3·’(ù êkz€*Q( ˆFv˜ìeªÍ¨ÖQÇËv jn‘RÀe”¦¿„>5fˆÅSõl’«–mÌ8#öF“;¨itB²Gä²f¤c][ŒÞ ã]ˆÕ”îÜu Ë´.jh¡í]šD‘‘Iš¯ºkq|»ˆ+ñ†·Á‰®$)ø²þ­ ªx‰ÓíJŠƒºµ€K†Âã8$ ¥Æ±ÉfM Jªo” Xâeÿ=„ÖL,®¢ ”@ËDŒÃЪîsÈ0’%C´Œ/Ò/E¬ -ù©•%uØ’Jv²uuÙªj¼Úb•}sXê ”ÿ²?/Ç~±yÃÀ§æ¹´ êÂŽ$èQ·¢TM£h`{0y‚Yž",6æ3¥¤„Ò }©6ðÓùRó‰~Ö¸](Ä» Œ‰t_ê›24f³ »!<ÙuÛA)s³Ø­ üa &­E†Û=©ï¦Å3s€ÓRZ™›1$+&û0)Ú¦@]ãäÝ÷ h/(¹TT¼KZE@¨Û.QS“ æ&bÓ)ÀÏ-n½z~ÇuÉžÝvìzŒ\ŒvƒJlôšG…ŸUÛMkZ´€¨¤Z)RQH`ø4ðiA'ÏN)§ '™Fht1ýí:ÿ-–żr¢çKÙd$¸Ô¯0-÷ÛíÒÊkêß ¨!j o‘«ò›ë®Š½¹+)ÛøÎÝ'wT á$©ë©vê‹ Óâû]Ã=ÑælsâÍQRh9g¯×d[lôOÂZ™ %9ÿÛ;;‡È¿«ëA¨¯ç$oPÀiEVš -âs…ƒ­’Võ«–¥¬{TÆX®mó)gÊðà„¤Á1WÉRcFðAAm ÉN]²äR(n m³¾ÈÍéF9ÔîÆ¤œaõŠÙ7tÅùW™høöî²7Œ¥¼ïï4Ï‚FÍDVƒýl‘çµÐų€zQ’èÉ`Y‚롸Ѽšúvd/ˆÕ±F+è´[EUã!HÚÂdØíׄíiAb×”Ä[s¸²µMNäŽ(…úú‚ŦÁZ'î}Á=X hi#˜V>½‚âuSè$Z´Î—ÁÂcRøš–p±÷)F¸%)œ M›ajUËyµn=Ñ™‡LÅܺȫú‚àŽêu¨/áØao¾È|° z¶KÖ ÕûÙò½álTá^KÀ°t–¾ðað>b»ÚÅ- ÉCÀî^ Õ1ÍÑ ÐÌt|_!îD´§ÿ׿¢Fº¦Õ·óè¾ÆŒ¡-•ZDßnJ¹B¿C‘?ŽØ‘ãǃÆ=Ê1$]f0P7ý®&ÛàÎ/™[r÷·z×DY²¤º?àößa8‹ßD,/­å)­yù²ªàÐXÝ‘ïÈÌ6š2>8QG`j6Ðzª’QR4Í1R –øu5½›eeí#rØÄ4ƒLŸ¶¤ÿã¾Ç -´mf Ô/žme®·d\rM«;íO_¢Õ("1Ø+FÄ¥šu˜²JÆó>XoÝÖæ&œ­˜Þ$*2Í„È §¥†ù³ M"†Æw»¬®¸3¸¹û™U׫´ð4”P–ã™[°îjzF̺»vŠ–…U+ g6rnX4öÍ)Q¹C!œàq:!•Ê­@²Ð šŽ>o)–anYÖ ‚T%&1B8ê©nb …,UîÒËÊfêÔ[ؘáj®%²È?”çë½`ŽÒtŸ{ ¹cn~ïJ(·dåÇ4{}‘ûíîè…Ô´ñM =+ħßKì{ú‰™m©æUû?óÀÏÞÙ%DéCœ˜Ïá+Qޏ)õtG!å× Å†zoþŸ€›m†‹ü@qá“F`ÜWuèëýæ¼Gé$.‹_âAiDËeñë i¥¾ö & 6íóq卸uâ |æSTì:ïû ãzqðBðkQ—vð0ÖT7R3ñ½Òù€™Ù…àƒ8ûò×>Å{{‰; PÈ]ÊN®sZÓ%Ô¯Üûgp›‡ƒ×0Kø”¶f%²PUŽP7æ/Ì+TïËL Ä»*‰w¼¼~nŽîJD"ŠôùÄ; ×BO󨮲Hù˜Ú‰dêyæÕ³J„B—Nw'­w€ž°®¿* Й䟎™õõö†Ï×…ÈÝÍzfÙw“à÷†nØÖ\ߦ³ñ9Ù4äØ|¢ðœ «Ö³”“øËc%aíøCÝfo jÑ |2lTNm§øØÕ’°!¥‘ªžq°œsÓ+ŠSŒDA=ŠL×Í^Qpg£O‚ÉkiOÙ.#ôÓ[|(¶IÓà1Ä>%†Sß¿k‰†cÅû¼Íµw?õ˜a訑Q ãÞ'd§lX€OŠiì YQ‡ªù¿ ÖU­-ºé -ÿ6 ~!ÕÅ^Ѩ9PÖÑÁW/ûªc *‹õyu~ ;çÛÂj ­{º…_ü~l<47µ¼ ‰5Éžºs Úc[|âÕ¿é/§õ‡P_ê|#°7+¡]ß)º¹Öš¥>1»ê³–»ä–ïJZA £§~…;ýÓÚgí1Gß.àÙ:¢¸úÚÝØ›ŽC¿¡¬þGÔÀs{³é<‡¥¸uäÙ÷á_i¶°&÷âL­0íGø%ÉgÔoáù=.P¾f½ëÅÔ\HŸjý‰B‰Œ×J!± æœtìÊ`tß-ÈȈ[¦WAs> ôù:¹±‚8M˜o aü*&Oƒ_@ (ô¶§Í/Èõ.¯É(G>µýüÌò¬(›GÑÈ»ò"‚ 3ÞG¤N´Àµ†­Aã œÕ=½P–[+?&¡˜ÀJJ'|W”·mÛ;wm™±¿méöe©ûn©÷›Á¤æÖÞÿ RÐQô#…ÓP¸rðÔ/Zíº>šâí¼ñ¹”ÖÙÆ‚@‰xâivcu™Øtfi%†˜Ï`3°ƒTKõj™,ŒSPÉ:K“A‚ü!ÚH‚Ý Ôõê-ÛbÁy]‰Õ/,ñ–†dÊOK Á[ɉ½Æ£ ¸é\€àJGCmÄ,¤Dn {>ýM÷×E\C–Ö‘Žª ¡ÝânR‰mñHX³ô«¬Ä»§¥Öå´î Õ(¸¢Ý;Ëòr`ZrÉËk>¡¨HÝ'C51?•K“Ôö¾×m žð­amÜwÏçŠcž…‘Üzáú¾?»©¹ë(Ãrjåµ6Úã(Ú“eòöÄüÜ—\TŒR“É9ûú}[§’¸÷MÏJŸoß½B%÷ è‰…‰ÏdòîDñIÇ%o€0¼¤$²uæV’ÜML‰;w­¸EvlG€´arQ²‚è?°7æ?ËÜÜ#ÄJ°q†žÄAPƒxv„çš¹a>oîžå†Ì^½Ó»ŽcÂ2i­çNù¼³ð_x¸·È €´ö" ²q‡°€ry}SŽð~®sDÀƒ?Þ8ž?(nË»è)­`åJ›»^Täqô.· Ø·¨Ö ~–a:—+†P9ÕC „\>—Õä„<kõ5y(´;ñwßÛä¡r¼~œAW0¹˜eÖ³ˆ‘k|€a7›ã¹ÉñÑШ°ER8’•È¢l³5GöMÒ¢Ú™FþÁ´Š{ÂéØ ^±O~H¢û7!g°6Ä;¾îÅŒKã%¢øT· 'mô¯5^Eú°+?¯÷’m„%–dmq¸/ñ:=.¿7wÎC8 RMi[iûŠHòHÝâ°xNs¦‡öȧÓèÚð O#VUÇGG`)Í8,ñ‹Ñl˜—ö…0?& °0‘»[<Ô«gdc1T¶dw´T^Kþb艄§EBÍÐyLÙüQ6\ÒŒå)Œ´Å;‘k+:bAC#œr“ó5‘”øÖ)Ýœ¿ö]±o÷þ¢tß#·-üÞÐÖ7¨rßæåw}õ·x•ÊÇ0ŒÌòs¥ì~ü»¦ü0—`/òB¾…»º½XïºĈB~¨zÁüf(½ß9¢³Bu£rMãV>ž¤¢aì…¬}„àŸ¸ÅÚ~ –ÇAó)téÃñÇGÞÊÂZ A—€=ÙŠYŒ kùýV—æÚ•¤fSf0¯G4æº!¡›[¸Äõ­EWfxú¸!¶9¨ÊËÇk! KÈ/š?xÐXÆHü Íoµe9 "®à?‚þ³xø|†¦6A»c'÷㢻f1½›ÛìXcN6žšo6éb³[_0W¿_ g_üZ3>Ö­prdq×GoÌÕ†ZXšÙS™š²™9 ?ßJFå¥aj6Ù¯áH#!Š‘7܇LÃû:&Jï ˜?‡½!5ÊEn¯J–Êù¸Æ nê¤rwKTÛknáj˜êd^\¢™X–…X¿øYó5´u ý7æøÉìÌ&’x.á?~‰‹çQ$¯ŠjÀàöTÈ+ýä´‹²â½Æ£2¼L¬T@Áe Ö»j‡€±IXgÁ?ºÌß[sÅò=­€€è¡­‰ ÕeÙ;nè ëöBï¦ë¯TèøN>‚EÀ úZI ²]ʤO₱lƒÇp5¹P@JW.‚³pjÁ.mn¬güXlŠýÇòÁ¿ßuë–Æ“%#’ÛE™"„«€ÊåáŽóásA%ÌG‡q<¹ìë刟¼N‹†Ñîî£pg[wóK†Ñ‡ êsSšºF›Gmg—•¨–ê ®à¾.•7ãÏï ý½¼=]8!‘ôj~)±Ž÷î¡>q×ó ½0SUc¯ý^oéØWÛ•{½ý©‰÷o¥n禉=¼~¯iÄÉk4‰ÀKæúóJáxóh0"z\s»ñÇpcÜM‰º z7aû¢BÌi¦“HöÌ¡fñ×k´‡T¦¡øá¥¾ ŸhVj Û3ùçï>k£K©Ört:n¿è8¼©¹pž4°…Sï+lõü/‰1ü2bž¦Ü/ƒE TQK…€óO=˜ÌóO{Y³bòI§_«€xRN¯§q .HR)¨Ï´3L¥Ž ÜšÍ§c½<ï$æk¡²zsÁ›Cu&¢èŸc7ëcÒ1wÄ"ÍÅÞ(ô[TxDÙó{I™uC# )¯yg0ãYô,-Ù¨ð(‹.èóÇâMD±Å2«²÷ßõ‰±×Ëc– /3‘€ÆÝθEMg¶Ck›´±êõ€È g‰+-ßËI~bÓÄ·Ðî0`B $,€OÌá'z2Ä… qþ>Œ§A/ áA9 û-¹Ñ^o’— :³´¹ï^¶ýxKÒ†R+xp‘3¾Ëßx(NG·÷©+¬ï5‘ÙžYcØo( ªJ/Vz²Vv/¦óæ9¿j±âÅ»ÈÎæ¾ŒÆø:áæÕY.áÙes;ZPLzhO®ïœá¬ðÌ h–Ÿ%~ó4¦ó™´†w’¼ClËF Õ‰WüD}5DMcœ•²ô6¿w‡þEûêvC/ÐÌú½è++­ø†¢¯t»ÂÅâ‰n𪬴x6¼ôÓx(nk°eˆ‘îi ,¤‹zKù8Gcoÿû5áà=GmMáǧwH‹âÖ»7÷wà/$#[492ËÍ¢ý­sþÒX ƒ+Xñ[öçç)‚#4ª¹QTÐ žjõ‚HDýç¯3Óóƒ¦Æ¥\¨dtâõÉ™(®ú-;ºÑ4CbÍGÿÚç®ÔОixMòÚb”áÁ¨ÍcÛÓ}‘¿ß ö"oA\âÏŠ§½”æÍ_^,–BGV»\V›~õ°è¹Æ›æÞÊ4iÜX+[b´ºNðÅxÎäU.;l›Cüÿønq&ŸËœX Ì/Å´f#$Þ‰;ú+äHIŸú°~ ;Aƒzåº!qJHÇÆš'‹î d{Š^k¢9©sü÷Fc/‡Ë£û kËÖŒÅÀèhò#±ÂèpÈ×ëFUTä¤BÒáçmÂ,Y(?|’Ð}½Ï»˜0–_ ›Þ„„ ;Ýp:>T|˜Š>¼@6‚'Þô†õ`jò×+o–?ƒ$LéÏá=’|ÅoË€á~wcNç5à©@ÖÚg´8™Šå0)Öfž¾ÍôFÅÑ{ÕÇÓøDžÎî\¼)àù ¹kóx¡«ˆÍâS†N4˜ñNñ:l9"ºù„èî¦ÖøC%왟xyײ ±Ñ1­tòQ¬Òw®lÑrhWÙë…{¶eÞ8 ^ºÄüm¯ ?!9©Ã™9¥.OC‹l…?Öy" <-À®V ä ½æÊ;Š!¡Dù5˜ #?V}1F쉀6šßømÎ}óKÓX§ÉËÏŸH  x§¡1øoù"“­ª¹…™ƒåÎL˜áy Ÿ`M—f›Õu,£w9/:âk~ù"É žmpì@øÒܽí³W̬R7¸†T¢K;Á(zý'_Úì¤c"ê/å§ä€Z¥¨§²‡‚Þ&Â|)Ò¨ùXU’eœüt4ðgbfE›³[ðÌæ•vÄÚ4éWüǨÊUß¶«ô±óóд·äD¹NÖ®Û2¾þGC#²ELa]\¢ð|eÒ·³'Ƴí7DÀf¥™“ÑÄY?)Ci3Ÿ® {tyn©pѧh9¶ˆ¸ÌÊun€šœõ09Ô °Ü6'óÒ™`˜š˜œ˜”ƒÛ˜ÒšZæÔsš˜Àû˜”¶ËI)yÉÌœßFÜ1Üu"À)ØðcGÜQ_qÞÀîÙj# ʘÎÁôëߛژu‹YÙ=ÞóÃÝ; Û°àùNqw&쀾°bq'?ÛŠÅö+­iP c¯õ>Qgˆ©t~¯ÔÜïY[OaÐPçó›SŒÑ¶Ež_\°&;KëíëÑIŠ=>КÍé=Áä$#ÎàÌ,GÑG.ÅíÃuw+ÜŸjyÐÅrœuþé¹@[iÌXÄÑúÖcöì¥6¿ÑQg͸A¡“dÑ£õ}áìGº·w“ÝË=šßäK°ËFë²lõÃÔšh¿¿L¶¡0ff¬óh}û8{Ù´+^Ÿ(ÿ·¾¸1×ýQÜcÅû¾Û SÆo¦}l³aoÖ ß«”7…—Äñ#é6­_Ø’Z†Æj„Ö_…ö‘–ì;QQDOŒW1yþ†Ã0™‡¦=âO³ßC‰ö1…`Æ(ë+J5†Áu¼´±hñq˜”Hܶ^¤÷¦#¯TOÃMÉ?…?ÐȆ»ë§ùjWìÎ.\ž QßWøÔ–× ¾73J‡‹ûìØ×1Ú WsòJúNíÔœ‘ûÓ>î5Ø Ü66—?ÃõO%Óã /9˜t¬ÇmW7b³ã&Áÿ› æøëf9¼zo»Ý åzÞCˆí´±"¶À(æÜg…’Z‚J¨43ŠR–røƒq¨þþ—Ijí3ua.@òÊßS¸⊜…}v W¯ BôGÍ:–„V¯ƒüuëü„>¥¸IçpÓÆ_ í Å8Å¡ùkUB-o<7þ­lÉD.Å¢Û*(9ÿŒ}²Ci%Óã”ì/Õz ýµî•¦üSO0-ð†Ð:3ÿÏ­í 7 yãvæ”çpÞs‚ÞCh‘šÚ¥xÝœàœ­ÊŸþÄ”¶Ó>¦%ÌP¢Ö4yÙêÅ];Û¤s)Ÿ¯±Í£ƒtz1œkï|>FÏLTÇ™fËš›Þ½O• Þûz‰KÙÓ@i_!}eÃEûQ—Ù¹“óp'?‹ü)´@œÝq3˜Ûmø“`§$[UÉH²4I1‰@³ƒ›C’°#­CÚÇI´‰(ÿa(Œ€„(1X@JÀABœ1†P %ï)—r í'ƒJÉøû4ÉHÎOÿØÜÈH¶k?Sÿò_²4éÉqXR JIb ,”RC¤báÖ(#L„PæBq ˆqXÚ©L°P1vq’DëP“Ò>qL‘¶Òœ$wûh(O£³ÇaÀ -¨kEiTGó‹8HB ¤]• ¤@(%£êbDh­¡TId!_ÿÅ>¼•ID4¡JèñçL!éâHž®¢™°ði»ù‰°®À½O…ýP©WÖ HŠ$yXâ´ "­Ïº~À22ÎO€@á±(-˜ÊK±—™Œ9Që9¦‚•0¡Õë×££ Ñ.ztSdQŸ1+9)LëË9mÛ¸g‚èuåíå‚?>ÖÔGJÊÇJÎ×þ‡$!W¹-ŒJ‰a1öêñ©T§B„X’’ÎGSMJ³< ˆ0EÖXÁÑ^¸•´2´ltb›¢§ƒB]ÀµÖ_Ú|½¹*.u£; 9yñu,Ô ^!Œ¨“É D’*Ù£¨F@±R´Ã™@;P}›,E 13ÚM—'ÐI šlíõÎ7fþ1Ç¬é¥ .±.Wƒ|×;¢JL²7ÕF‹¯•ɵè2??–‰YYÉ©ù¸ÉIœµPTÓ_ˆÒyZ¢”ƒ×ýÙSe7+Ë4kÎÕ{?DDÛÞ»~C›j9`ËO#qßGR¨Ôß$BqøDÅ¿AŸ$h úHœ¸ßµq¶/Ycì²±U; Ž†¦æôC0»/$ùÕD÷éÇÂø%,³ØaZòÒ«ØÜeÕ´JùÊýP°FÈÿò!Cs0Û­ë‘Zª«ocì+nKü~Ó¼¤=«Ó8UüÂí÷ÚJ=Ét;ù#‘»Èu‹ŸÞwƒÓ¼>  )a0û91„"œµäHxž–*µ2½›ÌgÞ§© DÒ[(IeÃká¤ÎDù“),-κyv 3u-)–Ühò(&€(e-)d ¹mÝÚ!FD¦ÂF`´­þÊÂßh ™ÈŽtŽ›Ài79uÀß´ë¹›Z.O÷ÆšJ 0Äëî¦Åx ’0-’DS}YÇ^³ Ü9çëEÜNÂ@Õ" цhD´0FBE® , `2c÷-F›­¦®Òk”–„a¯÷ªKíщ´€=p¹|P€òd2¸ãHiÓ2‚çhÁúwµ P9%Daö’U±í™¬’Y”ÖüXÅ㡤¡CóNQ I,¡塳ŒÝ7wªÓé|uZ¹ÈƒM„.«¢Þe‹œ‰ˆ@Bž-|²Ôo^IJøî×5"þ嫉“»âõ;Å×ÔIƒEJ(@•ìVf]Œâˆ+Ƽîk•âúä÷2Ã)JeÆhÔiÀ¡„Ó53†XP’"À¿ qd˜L‰6÷BÓÒ?š×(©ÜŸGÊ×ò@òªÒqœ"„—ÂQ‰Vd¬SþRq ;Žt»±\ïvC´2ˆ­nÐi"Ø<öÊio%ª©,êç‘_릸»+uœ8Ê‚Uø”às§Šnÿ¦!ÖbHc©–zJÃò³õÉí27§Þòm?Nòÿ›;“…†i³pOã¾YˆZfUT²j-ÿ³'0T`„„>Oƒ¾ a»UAÂñ„ð‹à@• At¥ÚžÓJ.F‹ÌÝ«A¶ão“Ÿ Œi¥@%ÞâŒÈE󨫱›¨0Ÿe,ª®L&Hâ°Wlë2Üs5\ûߎ¹}Òз;âÜ5Ý÷_çm+º÷h$ÖQgé_«¯·;¹¬¶ “x‘TÁ-!ÖŽ·ûý{¶Ö§4‰xŽ¥5C˜ð!¤å·" hæ¦eŸùx¢jC1®,Ý~É”¦0.Ú-H~/$ÀWë¸ušcví Z2¾&e¦¥Ãg‘ræhdìÖ†´¢QƒÖUbÙò‘•  ¾ž_¦—žú1qæב–r¶@Œ±µ·FÕ¸6ñ¢>Nñ'|Ç¢ô¶ ,ê³FŽv qÎ>]W﮺¦øj¬Ä¬}°Êè5%&‚öÀÉô+´†­‚X¶ÏF“ÙRÔ±Ïâ2²j5à^w®Õª¶Þ‹Ö*ËFÒ$;eú¾Á³§¦ÁéA°Wxk|d|!èÙt"þ=º~±mûATbBÓ¼:D…+µhüÓ©¦ KD­(IçW¾Ž„:HˆÂÔxÓý‘JEVÓ(‘£Rr>öÑ‘•rY³·ã³“P¿M®О·K&@Ì¢I2Þ=?«ÍÊÉw\è¿…» ³Él°!˜šŽu”ÝT]ìH’@t„·åòÛ=·`9Ð÷N£õ_Ook¼¡¬oO›ù8Þƒ_à†|`³ÿ˜oytÏ8[+úõÀÿ ‡ž›ŸÍâÅ” 4#’ÌnR³ kÔaAë¤6ÌWZøƒ§ˆÅ‹Ô%Ãõ3Š1ßûɶ{y¥Ë PV<æÙIÙ)L‚¬\Q®j6$‰{ve9¡™€–(5¥ûxúþ¯Ü¡ŽÚà¶ò´ÙzäÍ}®‚–WU´ƒÆ-ëÁP&C|yâÏÍÒÆï·¡R–^ßôÙ!Åþ~´²(vÝœR¤%‡ø´¼ß%‡BíÆ¼Í¿ŠŽ/ÉCtùÇû5K&ͦ Á%W½Ž-„é$¢ãrX­ýÛB“³¶ç͋ٔo‚ÜëûP¯¦Ùš^¸£Ë‰fœo5Ȩ8?ÑæÐËÛ_ÜúÿþøÚª6*¾ŽDwåâðš‘?؜͋S6=åX¯ì!5¬G~eŒÒbZ¡ze¼b9$ï".Ñ P²+{³4ܦ.~›VN|ÏBO/ˆÜRo¸±XlÜ-Ójv\à5i}^WüªzÞQ¯O Üêñ;›‹a‚2”“¤3ò¨ ®Újò>8bdô¦\*¼a¸Î5<Œö î ½¿»&ÐL¨u²£fšû(ò%vQ”êÚoÖÛ*\Àú=‘°n³[ñ÷`Þ(·0ºoÚ¨5ëêxôO/W¿/ RñwªÀƒÐ˜}ÁØÇöè…oŽÑGß¨Ñ UýØPÝI)²=cü{G}an{ƒ¿îâƒ"L @ø bú{KMU½2‚òSšèé#ðë"˜Ù?Úè‘lÙŒ|Œñíw•”貿ˆÚõ@¬æëê‹|îD†æîußKº7ÄòÜU¿-Y°" “ü3øV§Ìº¿‚Ú!þìB<îŒjž5rCL„ô$? "Ò¹z×@ÛA)D1wÆþ¿þ2W€*ÞâJ•Šô¹–ð‘m¦_ç`w=½ÈaxA1gãًΗ÷„(Ï…f@G© N«ƒ+ªOV­tZ„(Þ~Œœ- ‚S׳7~–ÜMÂ1 #Eõì¸ÕSÂõïp_“&M/aܶDÈ#ô\_ˆ)]2x[ƒiÕ š=‡xæîŸ˜§{E«!’‘Ðù8Ö.£dwBAgø“Cæ>‚‚}(DOPÁ€·„õ'¡˜…é·a(Ö–O ƒT’# H T‘Šw+¡§úŽbÞ/¹~ð)އ< •ai^Cq×SÇž ST U«.CöUÇCVã;K¹BÄs¼J)K1Íú Øø|ÆBØ·¨?.ð„@Â*´”éñb>”Å *I/,*æ„àg:’„Õmm‚Ô«SáðΊ%˜–°#Z~ËÇ`Á6øoç¸@åÚÅx}±çõT' „ÒÍ™l†^‡Ð¾³vé#âJOfÉTpä´Ë“£H*HâÖEöƒÐ²úDÕ"l^P)aåÛÚÂk£^u ×ãX¥"AÈÆ–÷ âÐËÍ&§Üü…ªÛ Ä@x&[ømƒµ­ŒéžXΩAÇÇç4ΦÌ%´ÿ¸í×ý´°öôUí ßQä÷Ý›ÊL¦îvhËÉú¬<¼ééÕmí;?Ô'L„ÞL¾oN÷×;4ä» ã2zóm–K@COÿ™“ûÄz 2/WµŠíí˜IšøšÙ±‹¸““?aÜûú<7ä r70rädÆ‘„—8 ±0DÛÊ;¹µä×§¡ÊšT:žgä³R¼œVÿ51³›ÊÆüóÄýÔÄŸ·ÝçW¾}u\’Žä¶:–ìðì'¹_! \÷…]¹{¼NÁacN´Ëºz{n»Ò$Äoõ¨Ö)¨¼WìdGÜÏ–p Œë¹ù:³+úïðõ$DÆÜêL9É@³!Ž\JIZÿ¹H2®–Ubý€t§XEÌówxq»,‰ØàÜ™£>ý “8ñ •ýª‹{µÆã‘7Èà4)–˜„Î}9_æ~+MÆ(ëÔ[[Â25í–†ˆtªA¹Ì>LbzÞ¯ ƒ#K~®µ#×~F{׌o ²¨ôÜ3.ˆØ(ŽD°ê(Ë‹*b¸FâD,,™Ì±n&>.­Ë>ëê~^aÔñž‚<×£±°‡Ô"æ+*2ìOGÿzCÛÁ÷£ÞðŠº$ŬH^(1îì½M0$VfU6ÀT{_Œë®Ùp}_R™×£ë’2lÊíTçʑۂ·t6jSS¹2 Ly5¢Ê Zò-'.ÆÖ‘¦!ê¢X[)LJ*­Ê½Ù`U\L3Cm —Ë<úkî»1¤ÿÝØÊ=é?ðßòýc®ÿ,»4,0ó°Ã'J¿Ê_r°uç÷á²èÈFõ¹/©ûã«p%~GÄ„â3ú`›â¨ZI¸ÿ6ÁÙJ’oTt±ìºÑ‡*9^¶›r¢M¥SßPÖ¹£‹ôWšý¨qyyk‰¶ ޤ5!0<Äl§v ·|ï }´"‡ûDbÿ;eÜbC¸x?„g>V¯urtL7V[»h·ÖõýT2ï)(3þÝæp±l[=7à.¤QÃ+Tá z7 (Ý¢ ïÐ,<îaòWè:bGœð©mâ =Òǽ÷Kž|ãÇ$˱m£:ðõL=`†b²ÇÜül#àu-§¶¹kœ ›êú×ö ²1׫àÁ3ØÕ¿Åã¿TŠØi*òŠW1bJ•XYq›¬`x©|÷Ùñ}çʼnN<ÜBÃ}Àª²ö­}I]Üð‘d`gŽ\Þ>§´ëНrÁ­cûˆSuæªWΫê>4ÖŽùrñbNpØXØ:ª¯ÐšZð¸#»µß-¹1ÓX׈À £°¡»²HKÓUS˜ñz÷ᔞÅyÿdûÅòEöB‹£ªXÄe‘˜­¦Å'•ò˜¿øóL¿(Ôã‘¡bû{¸øçw>Ap_^%*9ud§áv¿SZ0m¾ ŒPš+~Ø"fP.pïwÏõ–•ž¤‡âÀÚSÐÄІ„(á©6HzßÖ[%öS7RS™ÛcÏ7u5-Í댒.Å¿Õl^Äd8‘³#7Åѯý”ˆ©ê.Ö"ÿ׺@ðhì7,_O56Õìi,±ýÃEQÿP– Õ§,ç6 Ù+ÖlUæÔ ¦· Æ ÀyÀ i|y\ï­ëª¡ÚõŸ•"¤/¸1¬ˆwQ|¶¬eÝ–1m.ª’´RÞ"¬‚±c¤ 0â1KÜ™VÁýˆ^8”jVÙU1QºÀ«ç-° øVZr^2Jð3° Ýô6üGΘ@ÝxÃ*º WŸEf¤vf#«ì>úBºTÙ‰Æõ±~ý™»Æ­®´þæ Šm¸þ²(]„€þÕèìÍ©£1õÂÜǦ_ØÒ!V3—a+ÖˆhÑ®j¶lÿ3#hD#ð^±š× 7uÔèdÖS(úù‚CVA£†CÞêÖ Û733)YhcC!°½‚§Ãjö®s¼õ-È+­;`CâxÊãi+æ–%±[ê,._¯ ñÃt<Ê{ÀÒ,ýæÉõÆ¡,Ÿ¹-l¨ äŽ"•-Êd!7®2ß/Q÷÷íj“ {sŒh&Bü—õ:Ü#AEƒjDÊ)¨¿™rJÁe¢œÑrᙲqÇ™E5…Ò©®ÂæiáéT^Ì~®hN2K ‚NµL„ãn ;”Õa’I¶”ß‹·lÕn ä~·Þ;{~—óÝä Î7H.¨q-/UpV€üöÈþ릔ܲzÿ j ÑÉj‚9œo“jïáì½ <ó¤Z Ç|ªí™ò¥Ú >‹ôŸõY—Àã (¤¼¢NLw¶‹dº¿´KÁ-Æ!E\±Î0Ö#Ÿ“P3¾W_¯O·-ÿŒmM ¡ä¹½7rxºêÄý=öÍ!·Zí©Õfè¡Y6ÛÎ[ú܃?ÞçÎøfÂ,¢vÿšˆ\Jw9–íA(ŽEËP—mØÈß‚šœðÚž _·>Ÿ™”“ ™‹ÝI°˜ñ¡'Ž­¡¼°wªÑØÂ«ãùÒ†ewXhf%ó&«a9 þGéU w´‘3‘ÀêÕõý˜Y£_Ù%I:—ý‡p|¾‰1ñ^›(k(­]¾¿^Š fnd$ ø‰¹^Œf5åÊ'/&_`¹Ñ×`¾ í*=ük™žàükXCÅ3¢J“òùãm{‘¥Ò×Û׆{¿i"¾·Ù^3…çÃù­ Úæ¢|[ùøLdæÒë¥S×[»ÎÁ8aÙLÍ,Ü +ÚþºõDho]”Àò‘Ý[sö‡ÛÁv´¶ñ»Áž‰3eДmx÷&ÙïàX@×{ÔQ¯» 顉®(ÿ¿JÀÌbñnAg¡÷¯Ø• Ýj®Ls×ËéLM¦näê%~þ)ÐÅ`Ã~‡\Q÷”°ÆâJ îÈïÈõS„ÔúÙÿüöyD,îô¡óJ?HŽ‚’ ݳþ=Úi+ñ¯Ó3Œð’ÏâÉNªôVÃ4YH¨Õ7ugFSÜq«P& _Qàôæµ%ž$ëÈÜþÚñ†Krº#06ð-Q]C ?ªÆˆ¨+ˆBæƒxÒV͘œóx¿¼H? YÐØ¸]f|½Ù2Í »ÿ›„ÞÞ1õk[à #—î^í+wç½3|‹?üÞ»ímFÿ,÷ÄaŒÇg¶ÚÒOFÂp„°O ëAÙ ¤B‡ú*ëü,ìôF ×É˃Û:èøè_#¥B?~úd”×Y-xÞ’p6:M­Nž…åuÎb÷¹kúæÝy·hÃýɧÎz“VmzoœJŒ±œ®±kÞ·qz,ž­4¼êa<ì›zꜯìón×Ûï×ü5[áAÒ ‘ m^ÙÀÂCøë¹éµ?šÜú¿ø 6X»é½å½¾ü½Š¼·‹2«ß<–VÝÖò ÄI^©ôQ¿µ?j"­%5¼êqÕÝqo¼ÒÞ{úÁ¢‡:ÙDF>N[ª½ûª˜ð>Š;,N[ÒTâuZšûà!ïC_ ÎYz"‹[”_„ø×%ã¤ÐZ.(8ðmÑEæµÜWŽº¹¬± fÉ>I.¾ôý0Ár:Œ¬¥5 ?ØEgïy„¯—£ë•$äéžX‡%açbñd õéýÎauyX“ŸU±.ªçmœÏ&½DÉÊвhèDy÷~X»¯è/å}Z nîô¼Ô>œºÿo‘ qŒuDǸèùSQôCÊŠÇ^8ÍQdé7#õ>ô>6œI¢ùŸ9Vt>[wæb$JEÚ…xá&ØGlüyfÂÈâÉôÚù°»ëáÇܸ*¢\ù8È5eg3'><òÊÏ£ Wå:^5Ç@˜@ì®Â'>³!ƒú›`ßÏ †q,á`£‹f/8O<}„úP±[äV—÷z¨áÿ¥Û¡ªùN«¡B*Ì5g%îÏpgQü34üþÃ/–â£_v~ôO–'þ^´õŽí”9ÏŠí‡ ¶Éc`¯Ô •°Qi;®B}¦@5lÏâÜLŒ„ë›øMÄæ‰%ólíCd˜ñ3æf5“ýÒ4 4²/÷lÉKð€éèk‚¬ x»ZnªqîF™C{òSæi¿ËkØ®ò‹À2ÄݶÜl ¹F`ƒbØ)r9Î=Öð¾p#­7ª­—†+ƒà΢ҧ5”gC·±„-^Eô‡°•±¾äÚ¹ì°Ï ›¤Aµ(ã±_Þ ‹9F"2:„=÷ç™+f‡6íY5Mzõê#”êR@¨Mê ¾ÊÆ+6$ˆcM§÷Ú2[®{M}&“ã3OÔu©…ÚVš‘nA¨»©=SseŸ«cÎÁ:ªóD{´X'©÷ÈŸÈשðV8 ‰(ßñ7"ëæ1•ºö­<" ž#¼>lšV ºCEû`f‰½ [%¡{žßŽÞ¥ÞMZ¦w]”†÷8åö•Ö_o˜¼ã‘+XÂèNš<ɦêà˜6ç{ÒaÌ*»žø_N"t¾*ïn7-G3;-[ã÷zX¹ªæ!…r·Âå@˜«wlp…ŽÙ£9¸ O`i‘álh@kþíë4Wþ£ºO$‚¹õjpê^“U«£þŠö7c'2¢³TiþãÎVIåÅ¢4á- -ÿtE¾`Ô#ª¦ø§Ä…ÝêĘAQèÀprÐl| s“Åÿ³Õ_¤ [£«R%€|u5ñä„Hº5aÔﻇy{ø»¡Ð":ý”•þ‡~ôFþ˜ÝþÁ«‘ ïCŒ÷Žîy­7qæé–G=¶tqe×ÓÎÒßT/œÅÂê`FµX’Î ~ ¶5Êý ¹‹ §É[ÕÛ‘b$ûô*ËI_lUª“`Ÿºqžl8þy¢¯ÕX¬¯û(ܼ¯Õ<µ(ñ¡‘µhãwékbµÈ.:ZfyÏTœ¥™öÅA!}ÙTöxGÀÝãóz†¨ˆ/yË9ªÀqPÓw¨Œ¯žBWö?6oî *ÐÖu¯Ññº«ãîÏn}ïÖí{Û¬ãc¡®"Ðx¦ÝÛ‰§°ÎÛ ¥­C}ÀÃÐs–á*mªEL|,èO”/‘\~‹{,ÝÃ9!î¹Fžý[Ф$Ͻuk3ˆúñ̳ÏN‡ý4ý¸páÌ„ §ÿnÝýev_q=èÎü ò,¹`ꘅBÝà^ÀhÇ*wkE—Ðö´WãUi;ÒF¥Ùéš^|žÎë°O¡x:-°Ž®´>å×°ÛPªvD¼'"þ?ƒÌ‡ƒ ^oK¸-¬ßäÿ§wç4ó,_û(Þ¾¼¤ö¾ªK%Ó­yƒÚTÍÀ½"sO¹TcQÖæ· mÄß ¾|\Òôò#½ÖI&’]û`353oexšQ=Tˈ²cjŸ61 S´"RØŠ2»Ëv×”öjYÖçPï¿&ùdŠ f© =nju[ÝîBâ˜Y,–Ï'ž’šùÄÜÌœioêùâ ¶qcHªPÝU d@@R’*Õ d «­ÊuúY¿êªCBÈ~«_ª@§_T‰ Wñó{x  Í”YÞ»ïû<ÿýålËmÉòŸ“Wc2-’Ì©4 m n ÈMâ$¡7Íö°Mèq'›QßÁˆ®ÀÃ%V¨øÓ­ÚÌl¥ßծ̓ó›àüµk›ÿÖ΂L4E¯¬/£+3ª+Õ›O|ŸB¢@WVúWd$U$jš‰ÙUY×ë_§œ.ç¢f% ýtUÐP˜ÊcijŠqFª‹øVö@4ynv0‡ òžEä“(IB©´` 2G÷"—ghÝÍLÇUYˆ7×Ë&ÍÍÔëL”54wHs  Ñ œyºù_iÈÈЙ¹<¥x†—r"eW…Œ¶jÆe8 Ê0DLqaL‹K)Å÷¡‘›}g*èÉû3©z¯4¯²Z åheL Xĉ;×âDö)bX«fŸu^yÅR %¥ÂÒœF@Í"ÍÓh'ËŸPµC5éNƒC)#6¶¿êˆ7Q·6! ¬9T£ ,X4EØ¢Òµ€ qä7#9Ó$уVÃj`-39T€KY#ÖPh`@žK{ ˜„(YF(.Áæx+¬ƒFZ¤ó(7Y9ÀÀ?¸Šê²4ò§%Ù@¯fŽ&ò)+qÅ¡Lu%)xÇ*.ûöOœRcCU%>&!1•§žk¯ž£îšã­ÃH—ßTä”âb²YUKi*%K…S‚`˜ òƒUÏN9:þ#õ¬þÊPxþ\ÛvUM¸=(qHÔLjn¨½jpµÍ ú–´têBšÛå:´­…rfÍúpäFB!ø%ÕïˆKˆ"€t÷¬þ4|é³¶¦«4„êy•+ÜSºº .’{áðþ™b=0c´5kLFkHM—úÍvT†úMd‚iÍç•Jd;«ý½µ³ I¿˜¯ƒÞ&øî tÃı,ÙÀ©E]{35®Š‚¦˜9×Ζ@ðµéte+­o­{‹­–º(è>,æ`DQþ:ý ~Qt!¬@Uq/¼…ÎÃÆRˆ.\$¼d Uˆ¥ŠI}œ4®rá5h*’.ûÄ\'Æcn29-¼+©yêt'‘—Ԯ㠻\Ü@Zá©@@ ,~N¨´N™N) $¾ 6î”$Ÿn‘¾–òB°Bçy2g7ãÛ Rè+‚ÊÖŠTÙ^Š2K-ËDéR¹÷p&Ráüƒ|%-‹#èË‹™;¬‹ˆP"Ø•sÇG}æ¢N™G:íE‹ÐSíĶaê¡ÂûmÁÙ BénÖoy[^Z×pÏ#ˆ3º†RðÖ¸Í|1Øy_^Ø×ïz¡Î|¢Š|ʺ^Ê  y×0lXÄõ/¶õ¡šúÅD{ÿ¸âîmíºÿEö*ÏÁSƒ!s=!R«¢÷fzÑ/úl_^ßžÄq(@º‘·}-tçÏ<š„Ι‘4¾¾~×Q1t¿O©ax‹[š®„߬ •:[5ª .ÝBnÇã•J¢~g”™ÆõHƒlú@_OÄI’ã¶Áhî/1+n¢,í€ç…‹3>ÀppQ-¶2' aû/ìN?ûÁ.CL"šþ´ƒÈ©Ø$â3öˆòú‰V9§Ü­Ï «Q©*ÖAA‘¬ð¸8ÜŽS ­¶”ü‡nÛèn?˜tº·Î5 Ҩ܌i›iß4òÒ&_¹„¯ï^A¡’¿FæEà÷6’2ÞmHažI¦¸ ñoñ* ®lû¶ùZ”€:fúvë€w°Ç™¾…„OMX¯€œIïYÎK)/QÁN7wÓÛ¥|Íè¥Ë¹QÀU0„ÎÅý¼%¸ѪwA>(-ª‘­!=¥cihˆÇzÏv»ñq éÆÏ“J铨'›® {Äúƒ‹òº®üÎ ‚ dþLÛ¾0™EAgŠßjI‚¶c&S·Ì;Ø›õ1Ý Â]±›i¹&Øe›j—ü¡̪Ï\„ôCÌ®rµd‘Í_@Ìk8;ã¥|]»ü~`¤3#ÛÔ†r6C#u­½üÕ[Òá}Kœë e8Õ;0ƒÊ›†ó˜åß)AäC°l»Ú»µH ß¶Œ¹>-vÇ.9G5sÓlØbO0*Bæ¬a· ¿TÞÿþ»Æñ qn™ÆÓ—!bõ';¦Òu°'&„ÿÁÿq‘F JÍßûˆ+ì¯3•õÙzs€™4h¨øø—‰:Ä×}CU„Ï­çÖž:6W²0ðª€d¥: Vsœl"¢J,B®÷Ñ:9`)qðÅ'Eßóà€]$T %¨êu{@©G¨¸C*2Ú5¤hH³VpDm¨ì“›ô#;ǧ‚ã•–¾·©¿ÙMÍ¥îéˆkÔù¸_ùŽn¾ÛÌ:Ç`?5Æ«Þtzm’âÂHn@ÄrBÎK$÷ƒ‹ú¦j;`SÑÇëz§“™¯ ~7¶œ#Ë4¨g v«¯rûÉýšwIjÉ`ÿ0äK|ÝŽô¶LTîk¥³=ãÌr¬âù;æ’sÈØ@$8ÑEf„Ôa¬‰À0(Ó ½!¾‘TîYs6÷ÐDòû¥nÀúlqà+ „(z[ :)\«µdÕ„Ãá°!L¿±þX#£„Û»[jâS‰ýŽúV-&c¢<‚í­¬k«+@Q“%£¶c$Ä•7ç]è5E8£¸çÇÈÀõ—G$eàÜ; ¦ÿ½ÂþëöRžsÝ‚ª`~Iv‹Ã¹npS-xôþÂìRÏöYª³Ä¸vã»!¨Â]ŠBiÃë aj»t T¼_žÞo†ÅÁVÐ7ÛׄU¸…q\UH]‰aIÛfìQ |‹ »\æ8‡À€ÉƒbÖVÅ{ppTýõ0 8bÈyM{e†(²Ó7t°½”Båñ TŠð–Ä‘ŠƒÖê“l{‡ðŠüð! $ápzÛY'NÎßa;Õ‘3£Þ~h‹àØ;Œ’QxóU;>Y¶°W¾œ,Ó˜éãBÀ?Uð)±ß†Ì¡îUGµIUÃK¥ñᢌ1‹55§æª¡ˆ‘úÎ]Äj”Háð'œ‡;¬±Þ\ð'èáHê;ˆbKR(¸ä (Àœ}„uÓ¦{#€ßœäP0Œ€zË6ˆ·¶–G×[­o`êÄJ¹V§èz&_^•†Y”à7ïwO q¹÷b¹Ãšø>€— Èà#gKgàKO?yç˜Q‹Éžû٧׬)çOÐ¿Ń¢ŠäDõÄk-O1FÆ™ÖÎïà n˸.ZS kKR”ˆ¸˜´€Ëó%êÖ}çzíú%)’R‚k|wA"RÀvÕÒpÊ‚Ñ!•HMBA 'þBÿ&më ¯fôо5‡ý«¿:Ø9TáUÙý–s®>b€;x¨>„Ùx ØabSLƒF”¾"§qam6è÷˜È lGýLi«Áÿqnô+p»JÞ&àÃË¿D–³´ïX ‘7ª¼Êé^qP%°­ŠÄw[CíÕ×qnF|Çbx/Òóáä ’üu¥T¾ô¬ÝoC¹G&²Ä! €g,þb» ªÃ×C›:[®é|ƒöuQ$q¦ßp÷øÍšÎ—Ù/+‚Žb”ÏàïÕ²c’Ñü_ÂÐ#V®º‡™p)w É[(r¦v¸xð‡1¢³’Ãp1d¦³%Áߢ¨ŽÖQƒ£¾@[-1í?G”¾“ü2Šæh¸W­@*Ò‹ãJ=ècBÚ]æñ­t]Ž*úS•~]bDÇ6º×o\ ŠÂ†"ÈnD ‘éô³ ‚=3ÎÌÑx!ÛÌ^Rƨû„›ÿžq5v? Ü)D`JÎ$ËÞŠ CŠÙ¤*F.BݽËîLØkˆÍ<ÖÉâ•ãFŒºo{ÜÞÆ¤7(gp#J&$KÕ°<Ñå𛱚³1ÿ ;€sÙõƒia  DFfý²˜Ç/­'n8ÿÆÞZÐ)òÛÄÀm%³n¡pã#îÄ0™/ 5Éø X ‰›íZò«0€ŒT¡åœ8h8F¥égy.‰O» -§™…ïTÜR“ÑCt”FôgNºUäÈ@ñ|AŽ-øG¦t%ŠAÒwÀ%¢×»èNq»”³$šrƒ%ñ§dS`Ǭ¡;ÄÖ ¾1$  zÚ Eó-t¦5Š |}û$Üp’ cúÇ ¾w«œê¡a®h‹•ÙEÃÎÄÚñI„M®@XÇ#°øeÖ4§À';?#1 Æu'²k‚Wýb6[§ , ‚ÐTéãR¶vˆÈŒãa§ïÖ—9ÍknÆn±µYf-«Fëˆ V¤Ùb’rÎqëÉ!–,¨ø*Ö_ŒH-yQ98²+¬Äå0̃zï~ªãfeMŸkqt;—â¤1Q‰k˜™…þá8Z…¥o,íBsS¨ ±$¢^÷š'vìžì™’Ä=×½ÓÙçŒñ¹DÿnšÄ†Ô¼4åS`ÁÕ ó‘mmj¸Mö;¬3¬ l)¬Ö§ôÕÐ7Ù^íÄ‚R^q¬c3'P䤬ˆâû¸hÑ [1ge|6€#ÌãžEŘ?j[?ÅFQmßœè›ríG|¬|³ÍŽô)3<Ý4­'§¤Æf¹:ã…“1¢[ KùÛ‹VFm¤ÎE"”¦hHiém}+KC!¥¦á;Øà=üèÎïþÖ+˜òÒ£¸é¨9gǹ•":PMÏ=·ƒ^ìï3Ð>V™‘QŒ{ArùL±¦¬¨{ã SŠü°Eš×‡ËKˆxp,—=#2)MN” ÑZ¢m¥©„Z®X‚=Y.²ÕOšÆpÑZ´‹(g™9Eû2oµ1sò±a­[NEýI£ÌAÛÓÔ9W5>v\MØ/iô€§‚6C£õA•æÌ+Úó9ÐÛV6í|:Y5Ú­Š.géÙ^é¢ú‚êœXW7Îw´9(ûggï;=¡µAód¥6A£ÌAYÑÎ<ßq»¾¼D#”àm‰½ªphÑæ øZôÔð–´Hu“ÏÁèõr°¤ã‰í¸Tr"+ íÆ¸è 2áñ -íˆ0x\鉚*ç>Á6±I!o¡k€5þ s²kÑœÀ+Í´à³åŽÉ•ísø5õ‘>A{š4Ú&«Vp I—W˜WÄó¥qp„/ãiYê\oÉÊjgäë|&.Hêï€ÞÜQÊ>ª:„rG¢hˆ/|EÝÏ’dO€Ü7p·íb›).E=ŸË×ç$ë…ñh‚ ý|– $b5‘ë.÷ŠÝ>$§Åpþ®M>m©¯ÀœãM7ÆüCŠÉ±ÊäðLø’Z7ØíGF”´÷[oöFLÖ7”‡ââ0 ”­Þ}Л´»·ÏÞTŸèœ/ÂdD‹$ÈÆ§ø‹)UÝ^Ncú™ÓøD4þªæGÇ·üѵ(ýŽu? Js?(r~ešù.P…œägb(c'yP¡“þ Ú}™lÇF'ÚcOÄHB!QÆ\e¤â™~:"‘ œî¹lzSu ”**…JãfîÀ×V›{°¯ã<27Œ—‚H9ë‹ $¯R«²ƒ¬Ob lßË¥¥@ä³ìiºÒ~ìùÜæÁXæðg~áÑûÌ}FåGËôèÂ\iŠ‘®Ÿ» 4ŸCÔ¹^›/zà§;Á|]Œ"H2%õˆ‚LZV:þ$Ã,åëÀ™ iÙY ¢ŒUýo\ÜJ†]úJ÷˜!÷•ÔƒbÅlÏeRÏyBV¾³Ûé3È“íd@Ï/Ø…üÍû£Ð&r3*½z齬ïyÁ_3&WórºÙ…OÊ %¤L˜ :£ÑÓáú€¹§>Fxœõ}wÜxjø¥Ñº—ܺXüÿ\ÚTΰޟëÜZdš°ÏY'Áp»^Æð*?‘»Ž|<¨™öø#ÑðÝheN€ï³£ ¬"™OÏ‘æ„ëdþ‚>€ŠÈl£ƒï Ùi©üs¤* îXb–åc*W¢s«Qd·q äwÖeBïŒMöÚTî¾±;Û‹dÙŽ1CRPŸ‹—3pŽ!—¹k&?ù|^MñÊaŸåâx¼S‘ ëŽ<|šñ"ÝZ%6„®Ö)àmA¶žÖha«~!òc¬¡;lÿÜEú†²…J™ø? Ù±ÀŔؕ;„?f0œyt½Iï$>œzˆO)ºøÚ7BÜmU¯ÆÅ£!jéÊ…>`‰v9“ˆMæéÑ8ºcܼ͡t2DË—0ZÞÇšêd1Äu{㾟—yÒ¯o"§á4@a9Ž_÷@ɵ¼ìÒ*Ç6qhÁlxƒ)2+¼¿JÝýÑÒîndA“Ì9Ôø¿ê-` ݲ-KQÁà™4HƒZO¬peÔÆ1Wž›Ù NðzWÆ£øÂ+úKš¯pT½@û^Ɇc»Úp£cþù¥Q€«^­f•h™³Œø¾>á«ÅWŒV %›½`ž#8˜f –ø·ii¾qíý£}Åòg4‡3¿ÉƒŸ5 %Xž}n¾[Ž„ÿr~ËèÜv“?©¦Ðè_@ž4˜J”†´«Z ¡ 0`’ãgQ,e‚9š€3À(/€±V`Xˆ”†‰Y4ä«òu}X“ ¨\ B¨§íÍ ãB§ö• Ф\ƒ€¸`B£" ‰,…#¤´)‘ PÏ ˜ÃÀ²BP.ú^òŸGÝ”Bä ü¥Q“qƒ×°íD’X÷$(Ní„©!iMÃRt9âÁ©³7Pí÷Féä«!l#Æ¥ùþ›<8‹ÂgXÄ€´oÊ),Kn–ê°â40” uTLßj8Ç™nD«F‚kG2ò‘ šãUJÒGF¯yR}9ú4²ùH‰¾Kôªªr¼N¢³Òâóš²*öèâo´ÚœÈð ½$8ç·m«Br«¡),[HÉgš$­ÛÙz’ÀŸ4éï¿ ÒiÊY4Ã·î¯ØN®,QÝ” !‘&âi…¸£¢ ûþé:2¯]o£Ây‚ˆk¨¬Ãg x¹ñò J»žR‚;¢uf×n¯„+KÃy|Å=`É¥p櫦㼠ßͬ­– áÒz´³íM*h8 NÒ @l,+¼*¹)ÚX…m2“Å8«Yñ´æåi¨“¤òu^ž1Óâ‰ÊO¨Ç'CÖãOL&à f–µÔ£š$&Ë5æ¦*ýÄ‹Jš‰úMd‚ÕTå3”ý~ÔÏþU™­ô›Úµy°~³œv ó¿ÚyɦH••eôeæuezS‰¯SèèÊJÿêŒDŠ„M³1[*+z½ëôÓå\”¬ä¡ß†»V†p šâcœ‘Fï¢EÔaaoXÓZ䏨Á.Ë{‘M¢¾pšDvèäò M»ùé¸Ò« ^¾i¾¦6gj¬Y¹£š£u@ˆ ä•(œ2ÖÓÕüÊCRPOwòÝ‹E<ÃK9œl¶o?«ê4òS.Le»ü,Wû3©z°4·H›„–½Ã|8XVaî‹ØG¦ø 4­ }bu™ÊöSV¡P£bàÀ‘˯ږš°Þµw€_ 4ûB§Ï•Ûîx´ZÍ¢ãôñʲz4`fèCž7ô_lãMz–5Âͦb ââÆFæ=ǽVÁ¢Pg±…1¨EhP‰âh]$`m­ V‡JÇ,@.ÎôX÷¾f‹]ÏÌÑXyâÒ÷¤ªçV´¿Ÿ,ù¬[Všw¬ÁÏç†,Hˆ'¯~ü]Ë}f=t¤ÑcY]jitèv/+á[1J¾&ÔX½µKÑØëVqt9/Dç,æHÊÛpvU,Ô1<¹<$Õ@©çØT13&?‰¡EÖ…@ÎîmÜëQµkZ³ÕÏB9ÉŽˆK8ÔöÂw: `4{U’èÀÁÁ¦jpÔ/8¯Zz9’"k¸ /P=R–bãLoÍRÐ'u~·Gfÿ|2øÒoYYÖÐXÿEŠ”ý_<ÀiѶµ©‰òä󺧱”z¿s+kMt3¯ÞmìVaîe«›–$3!›37Wý/4‡úiûfì5¯‚EÙ)Ucr‰Wª1¼QúÕ}q®šz–|72/äcÖ¯F˜=•RóÄÈ‚­oÕiÇ/s“Yèñ”Ó 1}¬ý“¬éñ.úgÕ9%Ð?~b™Öq‚v€xÖþ~9ÊÑ@ÓŠDÂt1›\g‚ˆ}†JÂÙª)Âx*_.é±×µ‘wR„2+õ¤5áöƒ¼:´ô,OF®«,J¦š®>Xμ˜gÜPƒ$±¢ÝT¯& `",ðõó†€I\¿Çn»»”,õvó ÓLH8@.µõ¨AZ§Ëp«tS´7XVë]ˆÆeøñv†´ÐI£ÿÌåû¶ÔãL±“r¨º• Ð!EÖ0œy(ÖȲºA‰]€—Zpêº!LùOµJõç›l4NMººnz³89·ÝóSõëL–QOésÒZµ)ûú9|Ý Y7¯¦ëùõÆD±ü¬ÞMx±’?¤ %¬Ô¸µÞuÌ!u1r gÍXÕºä?Ãõ !a8¡¨žXG£gr¸M½”ul9•Ò[© +ˆ€žš)°ÐÍ «0i¡…ƒõ¥û“(Íy- äZÊçÚeå§/¥‰ó©WÓë4K™ {†¹^Yi ècÖjÜ|ˆÕ¯òPU/h7r¯e¢¶K^a 楀8 õ m ÏŸÒîOzÕñ7„ Šj³¶ Ê—°b*sn„ƒG%Ü]凄ðtÞp’r‘™ÁíeÚ¾8ɽ®ÓR1,ë7·­G¬ƒµY“u”‹þ0CÈ·,ÐN8"&@ɰ|g4—9º³¨‘=ƒºb¡,tÈQÛH :Zp£»¿Š $‡XzðvBû¶KgºFã\ænýaÑiîÑ :±)6´h›îF!Á)B’N7ó!qW+ý7‚ç™-îýËÎkD‰ÎuÑ.ëĢ<\B–´»{—F´ÂàpŸ/®š["ãÏQ 8š¡«Î¶™ŒÊû[cå¯t¸lò®¼olö؃3 {ÑÙX¡p!ß|'ìÜÔwFÀ×Çb¤‡t ±(dTfB@[FckO£ÚŠ:–Ü2…ÝvãmH2]U8yŠ}Ê2ìe 0ÿc߉ýسƒgP`˜„’@· v«rL{çˆFâuzîLÏLø8 ÂWk§q¢ l§wxCSb—sçú¿xO²°àò¡7Õ4”´à [>QÁCe¹cOt`ïG¡”ñž»ŸjÝ•Ž:Lo¸¥¥‚ÞdIl1ìèáQZJÜV£×>»Ú 7¨L‚ ðPate£0`søFXhc¶aÊ ºëä¾EDŒ¤³ƒ1I6—ÊÇFvÝ‘R´Q´N¬Š•ë D$ªÉG±Ôæ²àKØrŒa޲UÀI–.SÝöNW«*D®Úĵ™ ‚÷®½8ï þ-&eöÁ34<&«¶uâíûΆ®j^‘mô$lÔŤE‡\ß´k.Éjo’îeÜü$3—ã’ÑT ,Öºy·¿£¦,,›âØÒ/°Z¬1mÝU, .Á¬–nšƒ%ŒŽwšö“Ò¢Áb½à,óžµ\l=&Kcó*M07Žˆjò¹—^è^ÈeC11Û_°ó7ØÚBƒ&e©‡ú4j‘âè‘z&oJÜÎÏ\:ö´Pó½}+ÒCi=èÃ?+ü)΂H˜ûP¹K’2MßV>»#*ˆõ]ÏéVûáœCCÔ¨ÿ¥n›öø ·ÿ¹jhPÚ~¡±í½æÞXÊzÕêÍ×+%ˆs œ?uPZ½¢Â)ÖÅPb( –ŒIuXm6Ç´êS¢]¡‘…OLʳįÊåPb;€«¢Žœ—i…í(Wp#ò¤h\äQ‡w1= u†w]ò ÷*Ì/1²”Op}uÌàù#Ú÷X ã`þ •§96û9x»˜åUCsBÿgKúSI¸IØ($‘‚Œ«@ÁzYʬò·UYR)uz¸n%Ó]ßl᩼ÊÙ›à (+h#’¼žÊôæf/·Þœ'õë:ÖihÝ±Ž¥ü„zWŽuÜ6ìžêøÑö-1ÇX!'F‡ÂŸ¤hàÕ÷pÒñ±pÖnyòZ # `‘ÉË<+è›ú©ˆæ³'úœ]¨³‘ȵ¤Ï2»_`zi;¡†H[6›ÍÚSl—=ú0Êö⣔XVÞ«ßÛRXª 1 ðÿe ÊT3Eà‡"–Ž"®be3CúeHOöê4?ìÆÆGZ¢Us/šN‡§Ðmî°-©ázŒ?T>+ …qO0Î)Kât1»ß“±U1{+´¶÷€Ð¬è"*ä:ý—Fx¼ böwViëöÌ„þd|äˆñ`gGer]Š›Í‹P;Ã#Õ½pk¶º‹åe>æ.ô44R•+aØšñtÀø‘Cu!Þ {¢¡Hzš ”}XDžúU@7bGåÑE<ÛÁØ[&›pÄݨ2})Ë·EÒtûúSpï…ÁIÇtðâ?@Å)€Ë2l)u¿68«.-jG`#¸ÝåZɶ#‡¼2õ ΔÿrhbgÁˆþJÐñ¸Ø:\Y­ñpƒ´Ø ;¸ÃÓõ_eÙŽ<߆Ôù/¤í$Èv„"ÁÄ•ånÃn$¾ˆ¤Rre#Uëšb•¥þþÛßhÁNœÏfƒ³r0)X‹¾§,ïN^‚È•®‰1Pý3TÉ~®uµ%…LØ#êž!Q~†ù xÑQjsE·}'Z°ÌïHKʪ!`‰è´©%#ô}3ç}g,¢Ýy˜œ Òuj¢»¦‚ Î]BTqµ¬ÜWQT•6éfCjLöãꜵ­8=7nô/Ê5L\„$£õÑú’g?.`д­ŒÔè—Ñàö[§åO ñÆ|ÛŸðÍ0~·¶ æ­.ÂÖ§°Iv{'f¢ÍªH$»â!-”l]‡2ìÆ)`A3¢³RU¦ÂmÉ"’R,`H¥+|o£eÍYk£O¹ijQ¦Ç \„ÊÚ¦ {5‚ ß‹f Ón% éŒeeH”ÇE!R‹ƒ_Õ£shöZÞLjßD\æ7fÓ~Ô?°.z—hÑ…&9|»…›üI ¢fØ–‹{I{;6æQQ&rtQp‰ÆEn1P‹¨CãÈ8ÅbRÏ×.Ê‹ü?ޞDŽרñ!?qx!£hRº|±vŸôh:@NÖ›ßhODzwMyFžŒ1múvÃYv…ò 7çÌ1nokXf'™’ôN¤“æûzB©ž¡åm¿]9¯a %láFzWó7ǓƥLöaÔ âô—# ÆÊBï‘|˜tê•õx5Åx<¼šæå­Éˆâ˜0¸X•LðçâÄ<1‘r3rúø\î-ÉcÈ—„lÝ×Ë“IЏÍE§XE7oz“ß\ëÆÉæ\Uñ<0OnzHD|…MòÎë'ÂÖ­fç_{õÔíÕâù¸ú†5C ÓXtý®CKxgú;BõЀ…‰Q"ýgÞ©ò¿: @nÑZ s_ƒÐ¶Fjn›ä×,ÚðLbwØß«=Ÿsdï8Ì»ãõ_bS”S¤~"í&òBwů˜Nu5àe$ù6¾‚“Áüº ‚Q!äØ}£­k.y¼ªB!xy>‚¢‡À– ¥Ê9 èÌp0hûyd¼¯üe_Ók•øÔ@õ|ä×Å®ó&ûIzïŽA0Ë:o5°LêÎm´me<_J·vr÷4K_ Ì šoáñä‘[vqôp]'ã¶rçžìvpfÁq÷'!ÿ‹}€Ü|×Όݤæ†YÁ+ įDGÐÉú5»˜äÊÞmùµ«L¹mk#V?Ͳ}…–…0REoÔÂØÜ"Ã6ßÏù@—€¢7D"+D ‡­©íb g@öO@ë-ìÜ/kQ›OvÝ?ÿæ {×ÎYú-ñ‘¬ëÈÏÖá³¾z0$ëVouɬ«OìMa¯ŒDM½f‚Jg06Z•Èå.îÒÓÔæŸ§Y’‰¤ƒ¹×X¿ˆFhT#W¢(Úÿ¥ÙOÔ«$bW6¼Â唪AæG¥]×›Böœ÷:áq©UwÆ…˜³¢×qj<~b½ûøhl¢‡NªÓ@{aáÊ…»MÛ+"~…MŠü‚Ût»`²ï"¾KH¯0™k)Æ<ª½kâ }ƒzŒß(Í÷^,=:™+Š.]³wXi°gˆòŸÍIõ˜¶ÙgegGì‘4êDY¿r6:2Æ0‹ÊÕ•ÚP8t6šD9[çg*özEËúT8».ÅáÐíXnÖŠØŒçûõ‡gãöF,êY3¢±gàÏÂmÞ©uhžÙ8(¿w3ž¹}f¾ÙƒÞ¸a_ÑÚý;µà]ÝNuáÒ<þ¶£>¤×üŸc}e¦T}KD¹šPlUï>]yñ¥Ña—ìB¸W”˜±»#ÐTä^Á@Ê/ÿYŽÉyäá&í×8O”ŸwX ÌtŽwÞÒ¯ŒD¾f“ELÇØw-ö^|Õr…|TÕLïzÌâPî /Q´Í”p:ó¼›^¬«{¥ÜÀ›>â‚÷k:êìpûM‰Áë°'øØ´EÐ%ãeZoäêcYµË¯+˜ÎÀ\ŸD Ã{8 4"—rtÃX î¯CIWf£p¶IŒK™ ÙäS#‡,Wú:#[‰òÓräø Ù:{z<$ëœQÆ7¡<.>ÒPb¸dˆ6AÛÉÞÛë1ºx2ísßÿ¯¡×sÐO6òdaÐI;°­±`ˆ²‚Í2þ7’êC¥mtkk¥Vª¨Œ›|›œM»?è½/<ëš>f'^`>5#¦6'<âÇL€Ò3ü,' ÅÇ\­o¸ü-Q·ñNûB7ÛþÞ﬈ûÄ„˜ŽÈSïW¨ZAVn ³ÛUuö¯X .ÞfૺCn¢t$ûÏwò¡ì'ŽÞtšã¥¬çS|ï¾F" 7÷´g8‹Ìïþ5´Ì¦uGX[ýL‚R‘Ô‚èmÕVu,®±H)×JíÑrß U^lñg®-ð ñ± Òñ­{ØeW•0]IüzòÅ B³ØF=}nu7Δ͟‡çæKâD{y;×#î ïSX;$³³œ8&ÞU>äÇciŽG»È ‚;x§;¸~ÌIÔ8£üíUÏ.˜›-!Îøéš—8¯ÿX*ö9}X6PÿÕdÞâu¶BÚw5|Ù’_/5-Ý•8Ž«ãò\Ó£@³¶SKs*¦Î7~_ñÍ·gÎÞ§Ð1R*ÒÑ,äy+qŸÎÙ,ï¿·¡kIìŽéꦎ+Â6Ims™Ç¶Òë9ÐÀ‚÷ÓkòTš_„é§¶ -”` 6téͲ± Á™s¾á<$]Àr˜JÉ•X¹0ÝF…Œ7¸L¿þôz$mÚW ñ×Â"P|uÐ’3f%Ê$rF¾Ê+5ä?gͦû`ùÁ™3ÿfúÚ²K˜`F¤…ÄqwʱN/< ¯QÜ}Š‚1È/"e£Rç¿)áãYÑÙšTv;ز¨<»î;YhÍã-i;°&(Ö²í»ŽAþs•(qŒª€l#à¼ú0íû SSÙ£Óì™,­õÛ)ãçî#Mû¦Q›SlŽ)Ði« ÉÉrYð·— ýå}i`óG%¶»}X:¤îÝ£]Wílã'É0xÇRkZ NžÅshqÇ•Ýâ“MäÎq¸Ë g™Ìã8²iì|eÚ%Îv¬äxD<]öÓÝ…¨š˜>ȪY}ëœQ'ÆŸN ?/IÐV?™$‚Ý–D™3į;Ès{þÈmVÁ!S“Fï T‘]ç[‰n_'t³™„µ¼M­< -­5Ôêçœò#g{ÔùÇ£ywÊ>?6i[« 4+vÊÛ]vŽÔÃÈMôÊA[РO|kõ€³bß—¾òÌl’Ý^Ý“FåŠ[ÇŽÕ‡ ¸ÁÌÇN’‡œF®—ÖžW’hÍ¥ÐdîõXR’!É| >f${ è‹>c²È$\§mÞJ×µfjØü– 0.¾…‹ÉˆzO1zwc%a¤®hµö&ºgÒ@á™(Ëðžgº,„”z„½Š'î=¦ŠÍí8Òì£P—·13­éÕ™~z¥”ykXMW øM§¶¢ød¾_ÌØÝPâÝ>ºúqLõyyÌq;"êÉž6{úúÙÇ fk«D̹×Óõ‘mDó×S&Ìœ¿&Ù4jqÑbŒX€‹xwILßáä\sȃ3§¾7_Õ­­þÊ\Í›© 䯸%çdH°Œø¥²`â½ÅrXþò>þ4¸'ôg¸‘ǹÄɾ%•bSË$ŠFð––PUE8Lz0ì|ŠÕU\LEã·¤t×}‘“Û´ö@ôÕšÇó+ÊïB÷ÓnèJŒ?TÒYáõÚn·A¤ä:³ðré ¦Éà4®åö;¶ƒ×…ð˜`ìwýOxì†ëÿLì/&¿¯?{ ÙûHI ü3F:°Ô6âŠ9én óBúM¦wp†í¤±ˆ©ø3?)+rgZªëÄ>¿»a¸‚ø†ÿ…2{Ê ëzìðýçG»LíÕfÑ;à«~ÅÁ{çý‡Q¸Eà;m¹‹XˆÜU¼'5?ùb¿”?„ã–#þkJ­Ä 1ì!qGif”BöF›©¬“²_ \(;ÙßµÎû "·æohü@CC63_dpÙB`èÛ®øh(½4GÓ@i~¨5Ü›±Ú*x>7ÌmÈMúÄËDªÊÿÎŽ`¦¦ld ­ã´‰"׋Kïœ×æ6?(ÿãßß8R·Nqë“[µí$²XÁd²³Û"± 2mÛÁ–Xnr)ÃÛ€å-·°- m¹òá[0¡—ðò ·~)½KßmÀðÍ™ @œ—_çïun^Ýì\“ÙGŽ“ðnŒ,`4Á‚ˆÖštÀ¦0.SÊÿǶHRDg¤Á«D0( AIPbA/bµpp¤Ã  Z*)NjèЃS¬¬‚ Xä…XPàóq­DX\£xÚ,-–Ž­½ý½EØÄ£ØD(JXV‡QÂôP¶U¥ªBTGBa9–%$e B%¡™aRö%ÓŠªMB¤KÉýGZíüÌO-X±bïØqØ?%ÐÆB²ú[RÅJ¯„¦¨+?”£å*ÈÞ"YÎNú¨¨o‚Ÿñ §¯Pù5䟣IÑ+]ÊŸØX÷K8 ›w¿éü\ýEI~³lwi@»“ÏÙuFЦ¢Ñé…)ߥxO *ý Éjs •táÏÁù’[uŽ”I¤]—aŠl´zŸ–\}¦åk‘÷ÁK ùXËš9¹ÿ///W„n/J.Pk…A—ˆÇÈ\ºú‹¢T.]Y"äUb±%‚rM‹rA‹Ã*x#ÉJP'ÁPÀk~šÎ.Û‚ôÍÐü•„¶ýŠÎ+'sLoMºº°ã$<‰¸Bç¡hÅÜÃG*¶¡ì8 Á{c)fÐ 5†žaõ§ïÛŠ|xE¿ô²âY+%‰£þöi Ùˆ1âk×uØ=àlq…bÁU’“–Äb”iu"Ùˆ©ú>þIg™x†{ÃÊֿؼ™P%«v¿(.£hQ¬N‹ñÒ€Ïø B)B–@8pð 0')½ “ÑIÄ?û´èÓ¬q&7bXØH#‚Ôqî”EÑÜPEáÈK¾5ÞÇìº8¸˜å’䣺Œ«´Ä ­„±Æ=Ý—Oôö[REœÙ `UîÅÝP"¼2hÀ¢—ë‰Ó ¥á‡½˜,œt­ÛSˆ.â­^\*õÎ] 4èX`[²_èŽBÈ JXàf!sWº¹ˆª`A,Ùƒ¤#ªoµ*°d(«þUŸò}ÔìÎŒ[Åcòzíiý„BкP^ÿåyš×Hù¤õV/b)á+}˜Ÿ®D‘¶ú§ðåY€€¼àÞÈ÷¢þX1Ît é*W._råÍÅ@Ðß*û¡–†qçð,¹qÜ’Ü.oÉhHµœÌu‚'F˜š%0o©zÓìË7"3yñKRbp/Î,¸s>tPÒù¨k)…xJjý+Sj-»Ï´;0M^šsOÈýœ5Ô•È]²vûÜ.Üa KOY(4j‰ ’ Ç‚b÷­[‰\g«ý¶ ·¤š™_Lásò™Ø°ö9wt¤kÄÏëÌÍ´oig`qâgªn³iŸ¶{õK‰åV©VöaªdØîÞó÷¸rd©­óÑzÿÿ™(¾Æý4*K¶ÅŽßsðcc5_r±Ÿ™[æ.\nó×PMÅ?ÂŒŠ6Nð]3&"å´ãÅÍÙÊ6w€=€©Ü-Gý™ö@³ßÆ'ÆVÌ–Ú„|¿nvÏV¶ùàoÐX… ò¥dtýÐ/¾ä>ÝÊ"šÐYøà¥î£K%q~¼‰ÀÝ·³oÆþ÷v»€µ;± ´MÉÙK%$ÆŒäûÊjŸnî³÷ÿ›}_‰çoè?ýMH÷zðÉ[$ÁËÒzK3ÓÒ’½Ûïþ¹ôÛ¤yoiÔ?’“V“kY妗¥^¯|™bÊUJ¿-yõšº–¨ 0-ÅÇ&I¿¼{o×*ÁœÜ8”Á”üç:éuÎ¥]‚-Iàþ‰ÎC,“ùÞÛ€`_ÀºKœoÞî(åX¬ÞäßF•Åhí ]æºer| L¶™Xt“8¸ãÉoýœ9Vă‘ºtO»™¼& xÁ–`Á\ŽÅ?w fÌ¡*ÔZáÞîuÓdÕ]®ÞÃm IçùÜ ÜÑbë´Kü> ˜óÜÁ¯:ÏÕ¼óF‹“uwV¸³@°Û$Œ÷ouåQϪ{?KUõr‹Õ¦å„cÔx7ÜËA]Wþ›œüQy{¹“&ƒZ½åÔ'ˆ]å¨5v9õ¨í“Þͨ ÌÝ}|:úÿ&xçóнøîBÜùï-Á K˜XA™G É£¯y¼*2¥YP6î}ER¿¤§WŽ¿Èê© L½°ŸÀožÄ©òY”xÀ€°ðÐcìÿ“ŠÕÿ¿ÖmP»D"³ÄwQs» bŸ ïBå±:ÎgË7Ë{ë—HƒøûÐnN"ÃÉñÁ$xäÈxö¤P¿Ï®[¤ìðtÍÞ¿BS|&ïå®'Æo—ô´Ô…/r§_݇VêÔDÓ\˜œª¤ÌĆœ÷^ƒ‹(XXæe%Ñá:‰Œ¤&  %'fºª•Åâ™Ã¦ÃM¾¹ß<99H/=[dV} »ÄÙÓZs¹)‘™-"~Ù¡ë±7±Mù’Ý:7‹ümòRv¿žÐN«_ҿΦ®‘°¹Fu½Òö+Ð5ws@[½ý)¦ÄÀè2–rÒ#oã×"Ʀ ÷^7!lqþêÕÖu’ìBšì7G½úž±&¨šéz]Žè-ŸG’³Ë =»} »íÕÿ¾Ð•R¶‚¾fZê\½ $¸ÏëT¬ØG:>Ä8½ÒàõüïC©ÍÍ©;‡U|Z©ï†a™ÑÕÛŠÙtɬ_™š/át¼[E-Ÿ~<©·Üá†öän“IŒiÆwÅÂæ„qH·‹ ZåIn“ùœÔ}Ãæ‘ g¬(RÖ›z„§…ñY$Êd‹ü$bÑÂrÕLºÞ÷ó–ôÔ&ÕûïÞ³÷ß›µJ€ë.8ħémÈ´ wpúÀçŽ ²aMö&Ÿ¡y!?”rD-ðP8–e^_ˆ®¼ÈU¦¡`ˆ¤%XçrvN3»Fúq¯c´Àœ~ëÓu}eQK¡¸ó®{8Ç'S'uÔyH×`cØ‘QüX’O.<£ûá-ü æ ,2 *ÅȲ [O«ú£ç/pG f<ØÎÉ1Ø °9¢ýn9ÂòˆøJÕY=ÞÚÿp2p9eˆ•+Ù3Êá Ì¡qƒ;¥W”#]ÄZç“Ï­yv§aSNàç°~Ñó”åˆW>ö°ö÷Ý®Ñ% qP¿q­-µýµí{T ¨G˜´o9sñ§ (" »§v¬åt5-_1þpY![º*Ê‚è%ºî°r8Lëû•Ýy‹“;¿]@†þ•CïœÕࢠ=ˆæwÐÀ³8@•Ü~ Äz 0ÓLJ†ƒßLùAE¥ù¨}/ØžoÒìõm/ÒG_DbYtÁ!bü™fœ05· ?óv=µðº¼À x¡}x2²ÏfEì‹,ïo/›Š¤" Œû4Û¤â¹âÙßKªkV˜ÇZ«h“h4ùá ßî1QO Ë[Ä¿”Ÿ6Æ|¶¹æoe ¹k¯åÎH¶X¼X‡6håsÚRO ƒl$Žzæ@¸Ì_cÓá˜dŠyŸ$m ê©Å/ã`AE‚!¤cCî%j5a$Q^=ôtÕÃ4ŠX 1“£Zî›QÉX#&:ÓŒ©BóÀ{C²I\Ïõt›^þ#4·ib6 ³Móð~Ðy+>”4¿ÞÙ!Æžz÷`o\ËÃ@5TÓ'Hú¸Ñ†xÁ^ã_ é öÁð›fiõĨ3rõÑ@¢Yý€ßzýi„þŸ#ñhÿ¥#ÿý:€×ƒl”Š@ã³?EßDYÁKËòbª› âÆuÍÏò’# Jƒp¼áPÃHw4¸Y)þoÀ8£œv߸ˆf¨øüæ{4*PÙ^Ÿ$¹x Š›Lc³û1\šR’;Âß«‡‚Ž¿éG6ãÁa¶™f_èæÉˆS#ƒb7-1s4yýïí…D~€æ ÓÐ{œŠ\x¢´+¿k4¼â%®H„_÷ ã{[WüRk„›,ØG2Úàix<¡Û}Y"³åœå´õ’Q¿„I”»ŠRÅ}7o…ÏM£j¹ËI=j‹Ç–‰—UwRœ¡8†¡cYÞàöÅ’ž29Ã>ãë^÷+möG–2L‡ê¦Ë–LÝûÏ̉"Ð*^0Uì=~¿,.Ô•¶í¿KTm¦H.,uûª¶ðx±ê]Oš{¸{i± }­¶P@sE’ðÈóÁ3Œº-ØNYð?¨€Eî0Ÿ»xš¨†ˆœ\%}e.Ý–u’ ‹´C–3Ù§Õª]ãT&Èc·la§0oêk3°ÙsýÞnÞ…HvŸðÌ«Æ_بU#ÕîZ–;öæY° ÿÑ?Íôèâºz˜ó¶1½2áaöƇgofAT¿“£€Í$Yz½×¹€3bл’r%'¶y+pJVÍm‘¼â¸”•õeŸ+ò” OaÌèHž@-@¡Q®cŠfîâC;@ç¯"Wpõ{‡þÄàa¹fKñBûÄ-óãÒ.+ç}DˆT÷_”D:uC¼/_‘·M½Ò†¢’aî³æƒ Bfž÷. ëå>¿Pý>/kíUŰE«¾ƒ5ìéžë(s·hÒ+~ôéá¿G„AY½6 ýf[­Øö-éq*•†Ý ¡âèÞÚJ]¹µÚä¼9Ê‘·dσø8•Rö†™Ò½‡œy¹‰e ê¦GN·ó6~Îpò9ªç™€Câ—.LvÅÿª|ò8Z0z k]^>FaŠ»T’ b;^špµB&‡®Ø Öwo0ˆã2h"ö«²R.ºI9ø)Üë±¥ éy‡H’l9rH4º»gj™%ÝúñYߘÞD}üî×£2mY#u*FbL´Š…µRü½"w ħý0j´ójN‚m²¥ôJ;oZm[Xê"NÎŒøâøLrÝÆuÑÖ „ÞjøÈGøøƒ±eD#¤ñ'' ú-¯uMGP=%¯»úhæ5˜>"¯M„ÇŒ­‚ùÌœ7½/‡pÝãQ™š¼hG|zvígÓâLÝán‹"”„Qn›7Sg¬… x¹YÇjÅ,“zñ Lœ˜9Ézƒ±®yÐú…±qÊS´1ebÖ²¼tç,µD4N:Qp†ÓÎiƒ†nˆ¥¿´çôÎѶ½³}íÑT…È@OE ¡/EâQª,‰OEªžùXq"¡H+® ß)ì{øŠGßâ°ê à4-Òu-ñòölÌ“¨TŽTFT÷MïáŸ,E ƒOtKú2£°HwüŒ¾,1צ?ddÈ`„óˆÎ—¤&ªMÂ.Gt£A>«¦~j–an!ÈÃg/dsÕ.Ýÿ½“ܧ‰È?ñýUžK‡Vì?Ò,P¢(úéRÐÿ¯scj«(ă•yGêÀÆÖëíûhrŸm(<Éa=ˆÚ/ªdˆ¨çËØœ‘Öwõæ=˜@üN°…/€Ë*/€ƒÐˆò2NZ^Àe¿ÀÇ<<‰º9N¿‹°¼Æ™N~°wBúý1QÍ €5ÿÜ ¢žvÿÁ¦t—(áû¥Ú… yë#J#6‚^Q·9smy:g÷¹vËa}ýÑIDùÔI”fq ˜¯|@Ü‹,×7lÉ;4ýçÕVîg!ôä¦XöÖ´óÓ€?*dzŸß/ŠY™ôÕ7Åââζ¸g>§(M½V"U3}JέÒ8Ûúc¤ðlÝf³Q!éjæSx4ÿ.~ù¹WŠÑ>nY:6ÊâP“H)\ ªÕá1n‰ãË®~hjc¶Æ±\µì­€+qô鱤TøÛ׺WùCILÊ…Ï ³GoäXpü‚ àIÚ²ªk¨¶8-œD£õe¾É´7¶Lµïöä9‰!×öµ…\ LCƒºSu/©Üö°-¢“Räõ8Pœ|ª¦Ãž¬FH¹wb‡lµ±ØD‚²Bš¼ìªÄ°ô8HÈ)jï\žÀà ²E¬jÆdn­Sî\ ·Ñ)ß0Ý’Ké¸þ_5%êåÑ™›¿k1/Òü=É È|hü˜©- E¢7[;‘9^œy…znð;¸¡bÏ™¿Î7f×k^þRýl .©FáøṲ́–üòmù+ßFª`ÃÊ^#²™çò2eŸ“™¬¶¿'Ð2eíG;<-ÿí¤Å=ßa\9òýþÐ÷¾åk Õ:©×1 ñè¼ô]jÌ ^шödLlǨ¥¡.,ØX² u›yþæ~rì (´z4wõlÁ¦V—eù¢•¸c† ¸­+sÜÃVJ|e@4äM?§u»;|Eûk2 AJs޹#ìå?}c¹†@-„3ÁJbüi/•~—ÕËPæ>(:qëEi}[r+µ;fõ¹×:A‘€“Õh?2£ñø[²dãCf‡Í\'”Ë9.‹¤þÛFÜ“&z^¿€:ȳ‘6()ª‰øU ã´¼x>IwÙë/IãX­Å½HâüpdY,Êt +<•6<‹½QÜNaêÊü !Îoª”/Ö’oW¾³èírŠÄÝ„Y?Ú¨h>¤Ì¿èæç®7m‚ª™RÙ»:­6++¸h¿×ÅÓ¯ ºOF€À%gVŽÁ»ý(ðfã-s>ù˜‹Ô•Ë&²ßE®Ã«726Ù‡MGTâ“ýœ)5'h(¼5Š!ú ˆ©Î¯ktØ‘ðYõÏüùÐÔÇë€M>85â\)̈­ô¾LMÿúþó+[æu´Y *™ªªÄˆ`½sV—oËÄZTfÊØªÛ²Rg…·+É9±ÐY;œÝ!zÖLïuÇ æì²öО¾’’wÚöje;²°\+õcøYsÇ#)?Jü22[ÓëÉ8¹ú‡Éá‰áõ„Ö2 9Ï?’gŽÏ;×󺻅­·Ls2aš]ÄŒa•ºnþáÈ[³i7.‹‡Ü)ç 4Ôj—Ny—5Ž&è ʨÇ9ËâãY³‹êÚGÆN9œ_%g ;€îèeA‡g„W;8+½z9ÇÜÖùÎsš¢Ù“×{·õxÝÙj_™Õ÷°sÏ"Z¸´GÌϯ$)h©¹ªC¯ñ…ΰ‰êc·œD ýP¨Õh{ºÚfô¿ Í͉ÊQ«aÿcä°ÜIlzRkÊæbñE¢¾!áÒ ­þ§v3”Ô‘®ùg´˜ðîíéø©À¿oØ«0Ž% ì]/øi¢äôhÓ-¤×)MG„œÒ ”Y µGÿm²f±!ìV©I‘- ¼[å™ÁéêËÙ­’ ŽeX…Mnùc˜ (=¨1[mÕ/s¡ím·û¹‹®‚²éíç½éáJ\ÂúÞq*Š ¥‡³¹0šmeP&ëÆ…ê&Ø>Fx~¥ävMgôµåËO´Ÿ­µ© w”ù!¦=óŸK™´m΃÷7H”̆ê%sþ´¨ð"P/ìkcFWzJÕÞ傯 6ÀÓ [N‘IuZ?š¶D S€»Ãáú¡eç‘kÄMTÌB¤:æ¥Ö¬¾Y…H™ï¿¥ë'_ÌÚäf×þGØv¯éŸ&ÓJ縰êHÕ±zÈÎÔ$™‚ŒÊá¥åƒ¯ÂÇÛ—ýÓ‡~<ʺŸ¸=×_Ù·}¸wá•ÖFw•±/š9ÏÅ‚QjAêÖÍl^Œ/Ùž,ˆMHD)î¤J_tÞ[î¬ é•4iôÎççn]w'Ñÿ;¯tGƒµe`[[kï%>£‰) )lmc(+3sw¹³~µ'twýèÿõ™Z±»%ôÙ—_§¤vîôQ„¡Ý÷ÌÚìKþö"‘ýÓîFož5ÙrÁ%BŽ)¡SnI—%dn·¤,ÉÜn¹ ܲ"§€“Îa\°e§4/-^V¦ÕZvŸjwaš¼4çž‘û?V6Ü7 Kܹ1K&}¡tØR.Œ" ÇI`,ÐŽ DV (TBrÒ%X”‘®g^3…!A÷$Àë§Z)|a–4ã´td5‰šÂž uŽžI-Éj–õÐ2·ÙC¶poBçØ«Ð§lØé’ˆ6)Mýà;* Â…dá³èlѯn@Ç$È ž¦-JP ÕâdpÚ¶—&×Üø-EXíayMŠ}®PVj¡¶Ç?¿´¶ˆ —iBPø,Å,©Üö°-¢“ÑÏûl×éòQtÍ3,.RÛì!Qm,6¶ lœñ¾½*,lz{‘òTkýæ»D¸ˆMÍíʉ\½'}g½1Nq†‰”ü4úWÕà”|–S«P±]½<ü®À¼ï›ƒ°Ѓ¥îT˜ëFFéJ³äHìi霓•Иzoß½DEñø7Ÿ„ª Åe³hæÿShøÏÂ"l¥ ¯M”ÛÉý%jyÙ¥ ë¡î>“{á2/ª'ƒøu˜\|'¤qž}q¶ ·y97¡`eŽºZØF…n¼äd•^¥h®†|Iû.R‰ÛÖy »–ÜÍøÿþ×§Àz;MÛ’œ¼TNbÌH¶?¨þùïæ9{õ¿Þ÷žxÿ†ôÓß„?p¥œ¼M´,­·93--ß»ÿîšK±M–—ó–Iý)9e5º–ZnzYéõÌ—$¦R¥ÿÛ‘W© œgÔA˜Æbc“¤ßÀŠÞ(ÝÊ¡/^%0åÿyM:"¡ÞÝ$ÿÄç!–I ˆ|Äí¬P‚Rüî]êìÔóæGyÇr`-/7ròþ^w”?ñ 0Ém2ô€8ô¬—ÀZŸTÓL~êçÈñ%ŠÔ$yëßñ¡Bà[jIÀÑ+Ù/€* ³V(7µk¸t«õ׫Sp—C”?ªO:æ¹ü¯_ÿŠ«[ÒˆýíXÝÎö ß$éÂØÀr¢¸T cÍ·¤ªá»iÑ®Ij°Xô•@û5¡6€œá…¬±KZÀ]—ukÆÒ´˜á— ‘–fí ”í7H‘z«B˜³á_ñº&_KHª° Â<°Pq „èàäoÔÒÐõ93Üws'?!‚fê ¼IžsŒTŸ ¬ãr/®(P?z¬ÛY(ET ªU0‹h±šÜ© æ4ë˦•žzjÚ|6\¹èÍÞ Õ˼²êN걚8$ Ü¡›ÄMV¨´WR¬©âÖJCÐ4R¿vVÉz评çn°S§Í¾ÂÑ+þ…9¯;Ý~‘µÀ¸¡­aZ'%@ç6;¶jð¶èY÷zÕÉ«¤¢‡*è€g@cP¢‹°aE"Ùí%¨sgƒ“9Ü 6| H»§/òa"·£™ÒÒÞ ¶ck糉K³±Zyɉ~%ÜžjÃîr¶Ä¬ ­œ£ÊõdõØ è¼–Ã`F³ÀC{bÃ&Ù·v+âmÄ¡IòƒŒ²»ù FsâTž¨DéÙ»´ÓÏ"B×®³3L&}”]ÓxÝëó,`Èû©[ÖzþøÜÖÇ*¦G¡ç"ý{_ÅïUc¿Ì2kM¦c©; CGäXpyÝÙ÷êHÀŒU5€t8Û,ËW ÍîõÅRà]ˆÙ…ix·ãçX€¸Ó²”·¨Ž1\¨Rý|HÁ‰ÜùI³©½÷k•.½ ˜ì'÷Ÿéà0¡¢YnéíqSbÒ;Ñ´NaPMä–\HZºa,•I¬î³f«\.EI˾A=Aº|‡en,MèËcŠÁˆ•À:P^—ÝÀˆ„¿$T¡³8’éÆÔ«“Õ±st6Û¶I¨JWûЕVotwü_¥{اY2°Š÷Cn¼©¬oßè±Èfrõóùl”ø”™JáÁb!ðuèU^Ñ—¯)åòëÏSx ¥¬;¨s­9ŠUFŸVËU¶Ÿ€#ÙÝÕNøú¶Äå`;šœlÀD©c/@²”$±ˆkÅλ’@~šnqÎ~èN×%EÏ8¸Ü…&Ãt1xÚ§%ëE˜èãUlõœ¨oÆ6 ±F€y$سBu…K_^¬náþÎõ3óÒ{zõ°fVööVÝ8‰ýš—C$Ã8ƒ‘ê;µzxNa9bìEPRתҒ†WД’¬B'zM#6 ÓP/é\:°³a‚ Û6¦[3Z DÎŽªyؼԴ)&Îy­Î0$’QS—–O]«G:hÎNÆœfËI¬êÕ²[—˜šåf͹×]òþâtÚÑM"LÕX² ö™ã‘„—Ôlc J„ÖÝÁë!6«4Š:4öÜ’PéK7ŽüÅL¯¶lÂ\(å8T{«vàYÍìãU=€OdxêÆó9b·}fYIüÅS•Qo¯ªÙÛ•”lwçûB–Üí$^ï–ì™ëÍɃ ÄôBŒ3+¯¸ehŽ»+Ó(6¨aööxp¯¯SºÕÑ t•-ë8N‚IrF E9ëÆúyvÕÛÔ:?Ó=ýüxÐÆ—ÞÜ1a®½Üs›ªÓ½@5Ñc»fÓ¨+]^.Òû¶Ñçq††øÈ0£fͱ¶¼¦aåJµ&?ôü>±­ER8µ6Z±Áç(÷¤|rR5BlÁíx––îéÞTo…CÞÉŠÝÎpttà¬av\G‰Ë-ɆÕZä±$‹;¾)ðHV„¶C kÈLíߟ"{JòøžÛóÇ3†OŠ÷AOEM-´RçBap¯uR™ídC/ ¼QîÇvím8Ý ûÝš˜Ì|%u»¸xt™uåÔKŒÃ‡ÒÞºËX…0Y’»–9Í>a@}#¸Áé§ÜǪ]ÔJ$­nr·â¾Ù[ùó†š\kõ•ÓŸ†­ELMËÎ`SçýŸ~ÝÛè€ä.[ö¡FɰOPÀ>f:RÈÑMúñyˆ¡½©…`½F ·£®kµ f¼Pÿ:êa,þMÀÜi)€()¢•9úþs°¸¿‹®Ž€lÓÂà·X’}"ÃKox¶]ª‘„bÍeÞÙJÁdTøqÒ-@jË]¸"8þ«Q7²cõÜÜP'l Î/;ò{b ”‹üo]ë|ÌéÂkýÓi(¢_–ºdò”Yá&arB7jRJF~÷ºÁ¡ò޲èéâǰœX£r¼U¬¥ªóšp|Žȉ‹æƒ†þ»])"£Š&gårˆ‘wEiRÚp0°P-d;¼>2³/eiºz3rh»(ô‡ˆ¤oT¶üTÏ8Ú±v•¢>‚µ7p$¡.eß$Hj~ÈS8<“äQ‘§áé/SIíäÛÂòB­u…ÙHUÞiù uðejŠhqøŠ“V‹à[Kº‰*m ƒLAJDÒ0ñ°žw¼} ”ŠRâè½6øµ`žØ…&î/Ùb1¦ö;;¨hF¡mèæŽ–çb3·_ÌüÇ ÏÇ›Õë9ªÇ^û·Îj£ô}Ùiˆ¥bÎÖ1(+e õ­ïcuf\ìH!ÖLóbgýħëÝ=›N€®£]¥ëQëK¹[ãUuÒ”"SÍ”V†ÅöB¢[!ê…êjÐÍ"„9B:ê ¸•õ†€µ¶.\;2e^òΑŠ/a(±Ë"Z²¶l/ìÙ§i©K±Abuý¥77 Ì3“á™ÒÙ8„IÞ=ËИ;æÒ‘˜Q* ÿû{è®”¿iq%ÏÞvåðã‚|En<ó’Žä4″ß/€Ý2NI5į6Ñ`ÞÖ7é ©øÅl÷^—ø€Î÷ }B£Lj]¾ÏãŒêg¸CÄ ‹eLWÃ?ÕÛ2Ny"‰`Ê»(0ÑUU'àˆækö­{ÍÛÑ,91…r:¡ÁoMÌ=3®¹@°½®X,$“­#P&…Ϭûm¨þõ~t,ïz o¦XËvÕ Ûo‚3„ !C»%Y“âï뙇Ћ…ÁxŠƒ]¥ðÕ]ÿ|°HrG¶•Ô6y½‘ˆÅѵ3œÍº„IñËÛ_b£¤=ýŽ 3Gmw¿HCïÜÀ_#ÉGò®¾‡¬½à.¸Š/©Ä¾V< F^4;œY[3ØlÏ6_"¡tI À~…›äÖV!Q”C¢t¯Vs"|“¶ƒTæ<ã*Æ:)&¢±Y½!+X»ÐØ ü•1úS¢IEËž¤ éåCßuv¹ó†!lQóyL€”¿ƒJnèq$‘÷ô*{gH‡†$¬à™YÕ°Št, tÚjóML¦¦›TfLóß»×_!,’½tíLfÓÎú¡D,"Q¶Ÿˆ‰û$GÌér%’çœi×utÄåâòrÝ€Ëæ¼˜$"ZÎq壚¹ÿ#éhé¿ÃvháúãZ«÷Oö¼”Gw{ÒûÒۜԽ<=R“òÖ?-Ý CZi-(ð ¢6JòËž‡+ÖÞvÀ^^EÑ.8--¦ÜNì òÌV`x{îÜŸc+´SלV|×¹@à³4÷“ ðÆßM÷'U‰Ã‡ÍÚX=š2nzÞÔ nÒÃyðY¸@8ò+·Q”in‰„6f;yyy‰†‹F¼œ§çVÇH”4¹wôè8Nd%ý1xÿ¼v÷­Ùªär<Î"ÉXzÿ}îr•­©N²þ§‘\Ó»“tÔ@[«Çl~›©ÿgÔ¾25Ÿ<}íEQݤî‰P™±©“ù‡Zòd¤${»@¬|iC˜X~øXœÞ=$â@¨AÁ·÷Ý­í¥ÎØ®ð„óäøA»®3>Ëœ‚µñ–î.¢Œ'úT´Å$i[ŠU>:Í{pÇs¬W޼cˆœ=#G„FZ ã¼,ÅÊA?&xe´T_À3·ýØ‘7Ü> ;Ü£€ß6åcÆG—oƒìÉ_;5ËHÏULqp¸d-æ˜Ã…¨žI¸eÍà’—îV/\q²Xc¦Á3‰Ä ‰²®¯¤´Gu¡eW\œÿW” SwàÌ’þþò­œ¨åzf$ < b¤m"R4k+ }«ÜhiVs_w³ÛÎËíX6¸‚2ˆ%_¿âþì²ÎR{ߦÄáŽr×#¬[D'*3ªÁaû{bFv ­òëÃÛsÿ÷Ö4:ð3øº”F‡º·ÎÎhÈÛL3 e>¦'ùŸN‹ÞÕÇÝD³E€&z‚±ÁXDvC§ÊEàl-X±î· ¹­½‚Hkì¯HÁpÃ-)ŒilÀBržÍBóÏ® †H]“M£îî¬Á3ì‘: pDäÌYÌɹ½Qv³ –£ˆÆ#Ñx.^reqV+¬ä#È…ó?Nv—¬ŸR¾ÝžxíÐÞû€ÚУè&oùf'—Red-Oøèß3GFïda©VàùSoüu¤qÕxÑ«ehÇÄÁ"³E0<Ý3ñ0ÉGEGX 6 Ru’üKé¯(¯ê×»oÁ_oÌ*“·ŸÜÍ–ÍC÷T#$ÑésþT¹øÎà!˜ÊêÖ¡Þ4ÿêÐjŒÃ ÙEøE5HïÈp|—áXI£ ïŒ"žÏ{ñ%=Í‹.+HOyš4^b^Cç·ýV®«)gзzá è‡?ÄéŒòvˆ¤ÉJ?§{©N—šž²(¾B‘›×y²q”uŠ5‡¢Òp {ÿ5X¸§‘Q4‹Èòª¥Ldà'¸n_z§z|ð€ª@$Êrá›þ’u$/Ÿ…[¨ëòÐÚ¿›/!4±Qä<èþß_}8ÃÖ$_ÒgB  ½¨ßÐnsPžó㬠Cx-ýîÝYÇÓxú|¸ ±ážœ€ý½`½¹,¨¡wã}šKíû·!מ²ŽŒjµ«WÊcȱF$¬ö¦É F/$Úr8Þ˜—'z—¤=>y±‡øÁr<Ú!DòuѽüEÁ=‹ÿ×_îò âdûXGyk,Gu˜1vHÍDDŸFS  ©ŽsD.l¬„dy±7ÓH´ñw6â”}#Ž@®·ÕUˆT| Oë$lWbΗ³:³ô?…ñæ”tždzr$éÉéD ôÞc)Côœçß´´Ž!}(»á—]G˜¡úOÙñû¡CĘï"e½É©GÀ±ê²kí‰þÕWô_Êq±ÒÛA¬øüÛ9…Gþ P<™u8°z—UåͯÒÔÀ’qþ£t:§:´3îZ4S¯$ÌÙ‚øD†Ó®lŸ)&4­ÞX5õluwÀ‡¹ªàÉÈ×UM!ŒZ‘çy!;‡*ý´©8`WodH±I*󸃜õ¥,ôŸ~œ£( FU ’ñ¾€»|xº»ùCBÏ­4Ðd{ŒB\î¬Xʳµ¼&«IÿhùþD×Q|?­hÌÑŽàSô™‘›©ï‹¢Ä²$c® rܧ¸9ü¿”s¨4.NKt^ ט[¸ˆï#çgmqK.!Fת“w¡ñƒ‘%ÔúùÖ¢ÄßòCmÕZ½tFGÊ9ŠÝâé¬t1‰l/ÐÂùJLGÕAç~£óÒˆjÎ;ÍH©ð;ͳֱÀo‹²ÍTμˆÙI GÒÒ`ec%oá®™è;j£Ód”Tíú×1!ëõøÖ ,šÈR±Ü¾A{eMr‚nűo”5}1““hõÈüAd¶,!ü©’lÀ¦•=!U€Ï‰'É9ÌÅÒÙ‰JÍÑ-cÖ1c*/·„Y½_gqW÷súð²Ò%™¾ìáàù£û[ÒŸ…¨Hk,&"7•83>J÷DáwGƇàøûîŸ=øìí;Dû™Cêß9èÄŠøÖP­­[Cnøé9ž>¼RïíˆpN,äÇ\Ï_s%² Ê"­74Ö°žÝ•÷?šž$„€m)6¬*Q`A¡5ï°\¶h!Ó\ êQX„ƒÆå(^¢Ç{tàÅ”ƒf1dÓsSœ!‹3Vd¹xµë£†ˆˆùÝÃ'Rù3ªÛȾÑM¨9óÈÏjhMÏ´Aëµ1ˆ™{µ,Ò“1³x!ŸÍÛë@z8œ˜Ù­– 3–£Ë„BGlégWG§qÄ‘~?fÚ㤒¸~”ÓòÑ$üŠ„",5û¼¡t‡Å„—*yõGQ©DðæˆÜ ôÀÑj¥¢3ZBò¤BUÝ>´7¿Á3ÇWQ.®Œ°Z‚`§zy’lL/Ïå6%$N+IØÃ½µ,ªêƈZ¾gf‹¥`nzx˜q²@¦Ó­Šê‰Uò]Û¢X2¶?78 #3•ýØuÞA³Nö‰öÚ]ŽQáÒæH>HïfŸžBâ¹|-]:U¥Šª]÷ß&”7f£çôæ¡5‹Ù.?o<¿€Š›ÿcΜןÃ}æŒTŸGšà˜«2ì0üù…=Cßw=°3âÿ5MÑÒ|qZDŽ;¨Cä±Î–øi‚Ìà7¨ÇŒÞ@Eã¼s‡´Ý¹¡Ü¦Oœ|vl‘ڲͭá×øéØÊ £³ M±H°C„Â÷¬˜ÅcKÀIo´—Iš·éÒ2nêÒÆRøRYu š`Èå­7l%Pàš:“HEç)^ø´õ‘§7U¨Ÿâ{ÈÉ_¹1‹„½£8¤†»!‹œÚ:Ã¥½ô3·U<ài³*\茽Cl›HØvÊ×ðjdÆççÆÉ©çåjaÎ-[ˆL_4Ü¢KèÉrø>Œwag+¤'¡ Ìäzv£¼8 þ¶òïÇ^a2p>ÄÞ–5³î@Áæ ³—Yœ@y»ùdŠS"!Í]/ö¼Ø`¾Ô B:Râ<Ì à–·‚]H/TÄ3åz1AD+ IM½ŒŸWŠu?½oAÒéå_üªÕCùéÔ¦hUyªŠì+DŒ‰TòsÞ9J`¡Í–{ë̽tz7άÁZ‹ÎBQä4¢Ê?iŸ²6fé¼Ë1ˆ9j–ã<ãs}5Ï4}Ô68¬Ÿ;¡×RÈw27» <‘Éõ¿§ÌL3èÉ‚Ù1džÁaÌ<7=¿bûQV>(Q#.TâBiö›ñ@jUÉ#¥\-ºEÇ÷ q.GB_Õ~JÅK'vpóÌ·kù¥‘ªÆóJ7B¸Úi[¢ÿz0üJã¡3æ‹H¾¼m•¡˜‡™hâà§ü[@Œñ˜UÒéòòâ&îj»ÕÑuôŽÏ/„Ô÷ t©EvÔ­žlFšòþB½_<Æ/SêU Z4Ðù ÷NìAÇe–à`y¤#pÓ1zH^ÿÜÑÔ£®—ã,q™óÃ6b2Dîi‹øÙšð×Õ  >H†ÕÄtWßÙí ±Ò®µÕáºü¢€^;ywo9WR»`¥gÈ™Dºîƒ™®å‘¾!C‡ý:_å¥û¿ÿùŒÓX¥ÕÀª¾Ø¾ØÄ»Tð¬1Dý‹•‡yKËjÌG[eFØ×ÇH›Ä×ïÝøï?bÆ5œ+zëÅd%ãnŒ(ù!MÞ©fðCD7#dp™™fzuá0±’Õ!”´-o^Äò zí[ý¹QŽùadcŒªÊÿ¾Ž `†Šjd ¾ÏsŸ¹±/\¬Å¾Ëã7ó8øÀ³Þ(âê_oT3¯›Dˆ[Œ#rD¨:ÔŒ~ýT *Õ&’@ª[uuB#PÚªª%þÐV¿:UCiªR%JBëµ{n ÑÔUï½ý|óÞ̼fΙs/“ä%rÉ«ÚȪ.9¦¬í­¡ˆK*Ü€ ¡ÝtAtÛÐü÷'‘ù) ƒˆ. DÅ!ØÓ’H ¿Å2} ·Œ¥» 9B… I THT†¥`ŒÂT ¢Oö`&@Œ”F¡"¨Õ  c!¬Gh¨"¦ÅTV@JPGB¿”ˆéà@BB¤’?H ~„Fõ%¬ %=„A„Ãé@RŠ0‘•Aت(ËjÙ*¶ –ËDÈÉ* *¶úÛ×¾$ÿ²MŠÄÿ}ËUNO=¹2W¨Ú2U[ªR- Ô"æIRˆ‹0ªÂ ŠQ:Th’,ƒ€H ® `½µ¼Ùb䬮 ÊŒù2¡]q _Tx>8VPSºAA{éb—ûË ,Ô–±êöžÎ ôô8ŠŽA"¬ï ÉJVƒßT’=¶‘[Ù…§j0À5ÑXµSùè.¥üBþýöR…!4Ut¾Á—/‚¤¬&Ä8-Šße~º :$-ùU‘®_õ׌-{°’ 7²(†!Å}Zöp(A«R’ꔄf[ý1üÕ »ö(Š$oZ:Ø\Ó z ^Zôj S‰ÈÎQ3MQC"õìËìŸí*úw #´7º„ŽRX.eb|™æÕ&ÒA –l¥ù¥<…I?@²êÊ]†Z"!}ÿ:b·“%ˆìd…½.rö*:}_²0\´–ªÙlÕÄ|š¹¼àÑöOIJÓâ‡V5[@ª¤‚‘¿"«ôÿ­3/¹i ·ôP[Ô¶-@óÖègNO$…Z‚{[;1-- ´|Úe­´×¶%q’ªYL&j¥å*•…TÂT –¡Ý áð,3dQŠ"´$W•äxL¦Ä¯SÒ O¨Z¤u;ìò@] Ø X¿¤ó‹|!ì8_’_€¼AŸ?9=;W=L{¼e%ºCþ\˜c¯û-Öêô€bVm H1TRò—¥l¥„, §º¶’ƒ‹.™Å!¿8Ф€²§·Ž&Ip¢€$Ã{ºEqm«LT''f¾ I\âlîCÊ{Þ›ÎÈDL¶‡œ= ÛCÛ2N.ZõÂf积.Ã|Õ$qTþ Ê ±‰~†m’X|‹bÎ ª€—ÅS˜„„ñ*Â_//j¾Ü,9«•¹1¼\>‰í*ÚM–vâÉÝr$[›í…Ìë7æÄÍ)¤Å-“ŸCÃ9kHîçyfJQÐä Äuëz­¡0ÿ¦І^ÏÇfÜö;û³n…§=fÅde1œTGõ(‹‚ų̂0º+xQU!Ä[Ub×ÿÿèÏ—Re+ìx¦n{à«H¤ÈÑ] ÄÜÃe«\…?‹ñ¯¥kMÑ'lÈ/~Öèì¤Fí²V‘oO KéVŠ²Ý£ÍnfYî{Œu_&:?LakËÍ*ìäŒdLÀ¹s!SÃm1¢&‘:G®¦?[©ŸÃP—™^ÑE躦”Š}¥ àBCçq†$g3L¢=ÇöŠõsñ×èPcz…J1ܳ‰Mi—X¾3 ™ðwÖ•j-·wEALròHœ@ôõ6ªq– ¿‰ÎØ›…Ë_ï —Ýb0(w Ó i‚Wü<ÖÚû ¬IàÎWNŽKºäLZvJv$©WÒÐëUÍiöÅÙ+ð±èÍNL¦t5Ý×3÷1f?×Ô̺Ӽú7æ×‰ôë"à}-ܺûŒû920é5ýƒ¸)áîW¦Kµ 7°ôw0ì/mB6Âòëxv¯jr6û¦‚_aêúƒSV³*Éû‘\|¦oIÓm £…Ê{ßû‡žö–Íz«m츲w7ÜÛEÕR„?²?Ã#±þM’­ ,NðŽ{OâØF ÏÐVŸæSÞষ-6ÅU¹®§òYÞÊT±WùiÖb‹^¨Žîo…N^ ON ¶7›%ŸÖö›§9Áì&ÅG(G¾"hÇ“d9Î,†éø,†0Q<;—'”TõT³‘ÌSh;Ù±ƒ¸IåÅYn0ñØÚÔÄʹˆ~@aûª¸”4’—âÄÐMã^ÊZçx†ä·3m¯Dß ©‡Qù—BÄ$£)8aÙÆÚçÈMF [wšéJV+ÑûIr.UŇ®=pk&„ºaa鸛 &A¦TáX ËÒÃ.òN$ª;áÉÚ©m¡£;Ÿ8¼•Üå©0}¹ EÁ±ÕæÐÃv8oìëÌb+9~7V.c‚…]UD7lÅÃDÝ1eˆ0²ÀhÌ0º«×³›{®¬:ç…ÔȽ}Ñ Ô-ÿÆEþ·ÜnX¢Ä]åö@—ùjdô\/Á?£ð¶Ò )†<èÚZô–Éq¤á£²•CŽ‹*G¢´òðü/Ý6_v­#×ý† õI×ͬp]â¬âªÉ:1õ¯òÈc ^®´¼”öÕ În\rû)šŽ”1WÎ GÙP*†O¬ÙÒvÜI)œ(0ÎÙn£²Ž„Ú½jÆŸál—éäH-nXòÓg ?°ó‘JG×ú¢“ rÿ~WuÌkÁÑ Û¹ÒU‚ò'‹×¡ÁºU‘8gåÖÃl\ÞA7 $j(,FJíï`O™L^ÌÒúÍÌ®ÚÙ×– Nk·„:¯h©Áâ¿®Åf)0ÏÕÌZùoòL!@¤S0ñoù÷9æ{)_ÊêMº–žŒ‚þ~ÖÌRО{/£3W‡gÿ2„¨æRöе<\]ºÐ›ª÷”ÍÅL¹-u3z_€ŠÜêݼב†bÜ Ld;cãóD •Êê––kï7öeýi§¦ 2ú´ƒC¬¹}÷sžÍí²Ú wË1g7óRìøú¶FëRKð´)†wš’¹¾•ÿ°3vL”Ÿm`º„âÅ­†Ó¼àtý‘>ŒÉnÞ€(×Y•ÎlWC9H)g~ô"KÌÚ s¦«kÙÀ#xõ v¹ÀV„Ú\ºÜâ–ûˆñV™ÛÞÁoJg|gÞcê ?0žÕî™31ÆFÌ{Ï/Ï.ù>^Œ7w.9äd6Áéögð-bv»Y±ßÞ)#†EâÓÞž‚‘<ÖæéÖἩÀI¾$X³^‹Siû}ÉÔdÝ ‚hùõ¥O{TÌ¡ÏI»T8]ò㦢^x£ÈÕÒmÍà?fšŠ«l9{æùjUĪAI/…™«z×íÚž§½Zy²/Š*ÊjÉzÿq£Gš¯6õ6雿êsekÿ•tr¿êå/³MúÞ}^; ÏY²=H`åà_åyÙñ-ßv-¾“+ÌøÙà‡v5toÊ”âSø‘où#ÙO‚ÛÞù¹SÌhqîíÆoï¬A¿ˆ/A%¥k(DRªÒÔ³$@ Ð9˜t¹ØÚŒíÎuÓX›éë¬àÙâ™±w÷ÁY€'ªt&bñå¾ixJE½aµ—VÂÔWM º¡K1Q*׊Ã+&–Èh¬sbT– ÓƒÄjeUup¤WgBEZÙvÔåÄ*d…K|ÑÑ Nઠ\ª&?µÕ'c„Ï <šôÊgUÚ™/L©GrÍÂAðÿGÙ×À¦0Õ0‹¥ÞbxrbØ-]¶–=LÏfGˆ©ã×¼{\~“xœï;]O<~´06ËÁVÖ>Høº4²›¹\X"¶CÑ´–w|Ú·8kQªI-LmŒ%Ø.^96‡DÚh“¡P<0ËÄ{žDÁ ¶¼ù$!A(X†Èbñ¨d½Fð¹­)_@þÙžWœ½Ð<b¥vDF(¸%¼õ”9àçI¡*}ïYo¨ÌnH%ìB ¨Ëáñ,HÛ DΆ‘žºžÍ }¾ë² î`x<4"›˜Š–sò ·ŠŸ¤Á>CÚhYíßÞ¦lˆ3fºç7_<€âîM>gVܯ«üïFMáIæÙã•5ƒ>¤ 6X¤D_ b†+XÛ ,º™înòë*fþìL«JwöÜ_.Ý»’ÁM; Î õwÁ±Æ·„›®àEܦŸˆlh€t<• B@·Ý÷[Àòˆ™‚гì¢e µ„â³ÁzZŠ?Z%—Uƒ«3~ç)}B\¤çæÜ„< qăÂ}Ù3 Ÿ”Ž /ïab' 3'Ìn€å¿NAÕ‡x (wØÎ•ÿ (Dw òŠÏüÓf]™R©óÆM3­Ü?¢탹õ‹Óuëǽ§e#‚¶~ŒKñ’A²4 è£(w»±Ã.—v?ú‰Köôç–V|ºP»Ë榡›?2 ×n™8;íŒqóÕà÷ówáO«‘\l¥³Žû°(XAp,«hÙQ{˜Tœ,ì@2Õqšyc³±bÃÕ¤c$xòwÆã)j¬æš+e+gÊîš åT[Ÿøêd( ßaÿ|3—òįCݯ/6îÁµÁæ‡7tó†‰ÿÛóøYÙDýás×µé\‹3Uæ{³­‰`g¹Up¬ð:¿iTÞã³E‡ôc,~™î³™¶/¥qîÖ† ÆezÓJ)…n²<øÌô°vy<¯sÎùÃC¸ †“{Ì.#¦E Û?n×ðwñ¢ZÙ’_$Yl ‡ÀdƵ?Ÿµ2—Óú™ëú’`ت¯L ùÁˆ;°¿tÍgœ0ÄZÍwTVK‹÷F²KóÕ{“…OhB·ÆÿÊŸõ´n¥´ã(æI ð¬,ÜoXLh]ô’—RsdN«2ÕÄÇ»dÂâ uˆð̱3ÿ°ÔÎÉ3/aÂîr"ºrÞe<”³í)…<>€às²ïO/±Ì¬¦}R"ˆ'8Q6~ÔZÆ%ÈÿVYÜ›?rGŽõ! qº!õ|–  ‡*k– ã~…1tvm鞢M+¸ˆ`Á0ä|zÒ1 YSüÆÁ[Û|‹ÄþÛí^3²?Æxês¹OåÕP®kçÇG·E¦¥åŸôªÉY*ÝÝ i\¬f€<†ä.°cRañR.J4¶}ôÆéû»D¿„µúÒníg¾ÂUv>˜§kØ?z͢γ$àc‡Rœ—Ìö¦]}Ç+ylœx¯‰U5††‹Ø,DÐÝ|à[Xl £Êø½;*¬rÔ¢’Ç=xB6@sˆvtÑùTQŒ¡¬a÷¯\…(ˆbð÷ϯ:Äø‘jù„45y3šHQ“NUA\$ /ÆêÜ[¿ðëà §ï·§ŸþL7HÝ:º7L:)$¼„Ï׺Ìwx)'µ rµþf,°0މvoLgÑAµÎ¢z6ÙtåʦôfªÖ/÷ÉÉô›–«/·®€HƒØãâ ÝãØÑLV×§"û× Ì®´¾èúõ+:¢^77ÙϺDêp¡T‹”EÒ€*¢½#¯•QRÑå£&jfÚaÇç2|ŽñûSßì@@ÚuÂö(õ[…à]ޏæÓ <´ˆgÈØà¡8ϵÜ'ƒÏŠ5n…Jþ¨O'NêÀ·I¢:YëÉ2›’å¯ûNwµ“8ÑOzÓï© cÝäŠ>O Eàœ ÿ9k6Cô/3•¦4¦ù˜±\¾dÒ¬7¬83ÐW§¼v›ç¯ÛS:1Kû†¹»Áô¨©#‰´‰åRðò :ÿZ¨P;‘‡`ë†$/ÑþY Hë4éÕ™"ôãvˆÞÄùå²þ†PO8ê\òt10ݹ÷ ›÷¯È³Ði\„é_Ç“50é}pÏé(ÐëëåÂDgñ5"éuý RSØ „±äU7cZxÌ·ÇæwÈ™7îö”O¼˜[j$P6ð™í½ûîï(|0ãÀ<ŸÏÀ>Í$æd/2|9ƒ„xP6À–m«R§“(ôEÕ¬×Özp~¯3[I‹ˆ‡=UÙdq‚-¹“Ÿ!ög ã%ä=ªö|_Äy \lI `V뇃4LÇž¬ë8J<÷+»jŠˆ¿â™Äû².°Ï¼³=— ‘\Ú-Ìý?¤G—òt†Ã¤át÷§Â‰øðu(P‰IÙµºP׬2쌚"8ŠE¢ÐFsï)‰HÅn0ð×àÚX¡‚ÜpS‘æmæ Ã&Ø›êA'õØ}tcÆÃ9ýÄDkÎa#Þ›°?Ó\9q(2›‡øêF ­ÏSR\»kÚèû>‹+|Ô“rZY’<£(©Š_UÒ«‰uÚ²ú@Ô7õâeþôH4‡3)jŠÓYÃÂûÚÔÄ´Ú`í# ‘[sàk280 :`¬‰Çr${Fò‘abÈxE5Û®¹Õ'™^…B½'nm!Ûg„Ü~ä%ôÑ—¤ó–m.Úå$J^#¾žÖJû;`®³lŠ)}v£gY[ts ‡Ë>sc¿–ÝcÍ84 X%k¬R,/±–`ùRkÄ%Þs®\&t²¶ù+Ø–Ü2zÎßꉸªÎ»`ÌF—ˆ‡Ô׿amD¼Aï°¹Çû¼!+M²hé…¦r|¢£¦¯™ ÐÇݵ^ÉBÈw®Ø‹*EqÑ`RÅuEr¤x{×Ï›áᎵO{»¡`¸äó Ù¿¦hdŒôƒ7Ó"vv§{ec@¢ ÅX-{Ñ«PCg¶Ã°xåz…ECææ¹CÞJøp½6ÖGÄUË_€œ‘ÊÇÓàØÑDi.y–`7VÏÀûŸÆàuÛª\[ªÆŽ€&+„³d’åÔYõÿg?2`Ë1!§ŠrÍ^ÈÿÃä)Äü¿ò-ÎÆÍèCGȇî¿gscQß2,jÂæ‘çV…éHN-ŠAœ4ꣶ§Txv²\a¤~çܱ[¿ï½,vH7ŠBA0jÎMEè0)°­BR…ÂEjÁiâím?¤™Ñ©0‰ăÞÃǾ0ìôÙ܆ÂÑÿ£=w ººn_›E«¼&iŠGVíh=àü–„°ž¿Œ´ Ý©a’ᯠ·ÙGGü»ØƒUñ„}â–XŠÂis Æiࢅѥ¸Ô’¹ ¤‚„m*Kd.åá>Ŷ§Ç¾nDŸ¹x:ÆÍ‰–œ8Ë„¥Nˆ<»Dä/ã‘¶Æ2x#ÅÃÙ¾7œ{ü£WY=Š?}E¯?ÚÉÐÔŽ¼Ó¥Þ‡»ÌMZÄD.äAÒ¼B=ÙÀE.U„Aˆ+þ¿¬½­Ýœ'î·åÚºgÐÜwmžÓS€YûæÝyîî”wÏœµÛ‹yÇ7\îftX~1aÌêH€B&a³×œKü1)ø¦T>Û¿fú-·.~7¹þqQYÙÅ5åL}»éËP…K–¹ÏÆqÕgb™qÇ×ý=ñ<pý\Š£¥„ÕÞÓR}HñïÏ&—Çí’6òÜH½ÝYÞZ¾²d†Ì+)®B'p—Nsòxkýõ7~2ê·'ÍxëÖuÓú ßÕòZâžV¡^¿'½´ dy1‚²ÍÂepær]ÎâÕ»|×ÞÝ–¤¹PÞ9-Êb¥¤†ŸVùe”~»Ý^‘–Wþ5¾®NA½…Ãÿc!‹:¨¢=ƒA=¥.?|“»8¸|¡ ìU.fߥýeˆ$Šú~Ycãìw àr†5WTº‡CpðUKÖZ{à)Ì$ô‘’•bIæƒ<'B:q ˜vy»ØÉ8(,%F¥Ä¥Î›^9b¤*îî—Û ¡±˜¹dy7E½±0ø’¡:câ)ÄŽÿ71‡YÆT¾‘ˆ _i¤¤˜WF E ¢èMñ‘êÛ\âÒÁÈ w'Så ÓG‡Ñ†Ñ°dôU¬ª¼0"wU3AÌãbm/ö¡šµ#R­÷Ü*ѳ¼Èkü·’KSj4auÅWÇ—+2=?k¯¨è'«µô<ÔkÅ…tü¥oÊÐdEÞkQn–ÅcñgåsD %²¼%Hx¤b±Âñ_‹áë7›P¾‡d •®kG2Þ{>~óešèþ¥ûÿIé7 ?tÿؘ­aqF_þû/ÉáXÄc¼û-Éuv¬'¼<|bÚ¶¹³gJ'S wÉŠˆ§cñW5̗¼$¥¸Æ¿_uÇ@¶ÓŒß‰¿*œÀÐÐ}°ß;`']Îîn0Gðâ~ÀOfÈ*žg¿ÞqEcµ‘0#óWK ‰;tî=1 ;£ `©ÅôþÁœëCòÂÌnY`}î „h.Ç2ªŠ1FkŒúQWÑòŽ­ ba‡³[Z¬ 40,b¶s‰‹)Ðr%\^°7»ePžû>Ïû—a÷9?<ôÆ¢_ìŸ{Ìó™s¨ÓÐOâA nÛ¸œu/˜u¨(Ê|B ¿ûC]*åûßÞåˆÁ–§A×ÜG/«(áéŽE¸¸¬ép^³B%½Ä^o‚zò™›ÓýõÁb(‹‘XS̪¾TÁŸó”_s§t9öê(Åø¡Å 0;Ï{¤ãçñíõÁhù·wÉl ÿ½1¢ò]—&D·\·ŽvT÷ºå›Aéܧ›0Ò(uµŽc±8ˆgecÞµ aæ:¼ßÖ3•°>”€,L‹§öçÁhHÀÀH‚”*A*ä%(ƒI!,(KÄ$X@TV ÄEö!&ÂHel¶½¥ FB°¤“IPRH8¥‚J9@a8¶êÊ2Z6­ ²)L'AX ÀIõª»{ð0ô©I!AaY¢¤5Z‰[HƒBV~’Õ—Z ”e¨|D ˆ¤!#ÕMIxEŠymŠXDH*i·  ¶5·Ç="[ÇVúZŠ:6—†V‚cì¡Ô¿‡ Øñµ=£°š„ÔP\ŒW¨<4B¥Â†Œ’¦rç_ ¯R­ ÝÞNd¯ËWËr:7_M9ÎÆéQÀÐAʶx]D^ø´æ,”|•ò}ÿ tUãD0 •‰Ê¡`-ˆ:[ÚÅ·¥¨Õ¾Çk¾À!ß@@ÃñfÃq[sXÖð¾¸YkŒ2—dÚIŠ‚Lï¦óRQ(Pk˜Ì_SY©”ÄT¿+)BŠÉŒ-µ·*¯;úkT ©¨ƒí¸y¹c6!JI´ósÞ!â{ØA’G­×9éF[:®S] 9H´8‹)¢&Ø0K, ¬} <çW1 ÎÙ„¤4j´\k{¿E” ¾\Zt®oA&•ù€É·¹™jþæFGAÔû·ú;’oÈIµ9LK"ØÏZ»ý²”Kô„ýLÈ1f›ÃK@+4Kàusm¶^š…6RÙx¥» / °Âx(†¦)Jd.“·vukTK¿ä’m(Œ&¯B˜Ø‰{÷k)‹ÍD]2qiý^÷¾ÎQ°2dW%[Îô¾šy2Qoßú¯º#!'O6 ž9„·"8ó+¼¥×"RM¤‰ué‚9,&jøw(§¿u}—£…¦2\Ppµ¥Jù‹ӯŒËÆãÕiûJ;ŠÒiRÚ_-ªUñ0m³6¯òzirréûRÂ>Z‰Bz¸U™v„Õb¯Óìëõe6f_¾nV ¹–ÆQ¬ “ qÞw†áŒ"?ðÁ2Ñ…qŸŸ6¤(<Õ_‡²…F‹¥ YÚú Y„[‚c†”ÏPœþàÝö¡LÐÛ³Dt®ƒÈ-ö¡¸®vºI+¹¯°I ›|Ȳã¹ðªÿ’FçPjÅ7—I$‘\UœùƦ,9ØJæ>³½ (eÖùÃ1ê¿×=A«¶)âyI}"™º¶QöåÝ&¹”cGïênG”ùV0%1¸Pw-µzVËt…ìØ uZhbÜêë…*l²Ð”¾ŒêœÄi+&g<7.Ü&¥»Õ-a‹¦¯ÝÊâMv„Ž7:¥B0Ót³"Ùû¶¶"TªGzq±`_禄€©.€y/L õÀQW(·ƒ˜ÞZöÂkMvµÁÆV#Ibl„ï×÷)ûÎɽøæ€_ Ö‚DÖ"²ï$^ÁŸn¤¨9o´šÏöډǼïÅÜÓaãÕÄ𨡅Ö³-åeš[¡ó ’ÿ±:¦ø2Œ©¨¯À!´V~ 1Ò'PQ¶,Šž_»ÞV‰°€š)ÊbB[[ Y„lÒs+C!€†aŠ&£aR·2éêA‰°ñ‹ÙIêç_eeÏ}*sÊ¿KÝ<‡Û9IÔQŠ»5B¯ ¯ƒ'ÆÎ§þßßø‹Èr¡IåmÎxÓ>Hvr†4õê†#Ÿ1d(ÕõšºŒÃ¨ó1ËtP¸³Í#Þ0l2ÄÀå=ååqèMY×ÔB°-›­¨w3ˆ¥}Ï‘m61¿5½ÃâÜ)6ó¥Þˆƒw§´/÷ôž ?4ø!(s²‰à^¥Íß HW7ûO™Lñv‰’w<ól0%)¾¢˜g«“Žn’5¾äñE·Z¦ ¦¹êؘ›BÒíF^¯t*5ð=n ¸%/襎_Ü>Ä”Ûi¨îmÆP¼lžèé€éHä¹ÚƒFæù¿~ÄŽ@l;Úò^8a°p˜oˆm|ùîóƒËþ3}çèŽÓUºó¾9‹foîÀY+X‡çK}ß´cÕ³Ål¥K½Í{ÝÞ‹~8¢8×¶tæq&rÚ„JÓžd½×Ò¸B ûK>_°ifef§øs$ gá&^7é=Åîí.¬*=õª8é&Sà'¬jÝþÑÆ–`w¶ UÃvóqà}¶îk”tiÙ°­yqÞlÇuŽ;ô#¿ã«Œà jGo/DÕ#h{8~LðTgyú4‚ËßÀ/- ± ö[4sÑN M……³ÑÈÎr Þ¨‹?vQdˆ2M«‚®ÀÖîgs²%ôpZ¶@UwË­[âð°ÉIu2GÇ¥cEŒ_ª~ÎLg«¾²àÊ•–.]/¤cH˜Ráe 3ݧœËdÄRbâÞ{Ù'^mx[º @¿éÛ;XõLž¿|G¨°} õ–§Óg0ö¤ÿÎßGËn†5|êú®Q‡ Åò.×`þÎÉ7'S—L¡ê7A¤ÕŒ_ C*oD_X…Bƒ¨’…yvü(µ%NÌ~éa ´ ŠØE¦=¢ìC#ŽõßC¸#ãfäCÓƒ´ó*Õ0¨YÑuÀ›pÃVÍk‡*àr4Ä,¬£¬ZFÜS!½Ls•ÞíDìâ®»fó}±º&*x¤^D›"‰\·Â\p)÷–n53Pb¶{•Êaö‘×,?tx—q#e'ÚÉó>>¨ßäu¦˜Rx0y úÉ=h%K_™PžÈ(ù€¯–± )â³?*œlÙeo¸r˜ºÎíÛÈ¡¾É0¤/7ž±¿ëùuBd“êwwÊr°EuwGjÙø³¹+zhú‰2äQèYCõçd+ ÖàdÎǵ_°UxkðøÎ3gcýû¬Kš5»8yPt;xxïÔÁe¥ìcy~)—žšç ì©,ÿønßüÑÉÌ•›+áý¹.ƒ“¨†}ÅY¤ÿPø‰¨â½7âì2sjÓA{+ÇÓS%ÿÏ ‡\q¶uâ{ÉÜ,D1w}]=Œ(}„×øÃufþ¾Ž,dYì3샳7T< ²Å»r)aϼB®d²,uÒ!JBL–j¸dµ ÿÔb)©)L¹X=—ÎáRǰL²«.±øÖ êŸ‡É0ñòG 0ÀåbXõ!Ra½‚Ú–Ÿ0ž}^ÿþ8 –ž0~ðLÑ éQ²,‰’˜.(ª9Q‹¥½›;“74q[ùص¯Ý9 Kâh½¥„ñžûPê7Á^vZ Š6ï ˜HFuzdtg½ø0YŽ¢V®RWF|SX ïYšìôo¶Õ  JÝ F(*è¦X[²'J´Q<å·Þ;Eï©·ŠkŽà¸ŒjŸ;!NÚ˜T#a»'à‰S&"žeΪ³â<²„l¥=#e:‘¡ÖËqTg•vU &íÈdž9»xÑlÖYòe™è+¦³ »t»i¸k.;eã××È\E@Yc¡Ã“Mk½¯~p» CEÙq´gŠvödé0Ö ¶Íj— ¼8N—”ܘæ«ÜòzQÛv„õËŸ;C”Êú³Á(˜ó¨þ*;È»ÌaŸ÷–[{V–5Iäâí¸´ÑXê<¢Ø„8w§D ä´VA8Ÿ&qÚŸEu›\åÿ§ø‹ Ž.ögæ÷‰|ÝÜØëÔR®Dú´HŽUþ^òE’Ø€)}˜] 7~µ5ÂGz=RNÒ«ØPê/æ&ÿºÌ.ÃqB&ÖÞÊr¤zoWH+ºéZ"üÏ<”Qú;ööõ—äÓÑ·‹!=¯f;`—âÃüådO?·ý÷º9Y±Ù&Pé=­5ý% ”“5Oè_pêéìNéÀ(ôÐ_Û¨Á¥¬E?®}k“)¶˜Ln¨ÕãîžQŽ.þ\,ŠŸË®Hàl¯~& oq«¿äÙ`åѰjâwF#¹ï™EƒO¡ 'Ë!Ìa.3F;q(Qì(Ûj°e|lÅ1À8=!3‹^ÏÀcG;^ùCœ…AÝ‘}Y²@¾ Ç¡µQÃEéš=ì‘Ü\Œìôáoá´ÄU9ÕÆüØçRì€bב’>î;bôy{ÇÞÎ&îÜWðßÎ3$!zNaZö½“ÓðŒŠ%A\N‡k¥Ëä·íÝdÛ¯ååÞ»õÈfÎ0bÈøý¡Ìéy!Iµ/1²cÎÜŠ‡þCtÈV)£ßí¼8!<'§,g _#ÝÅÇ)é}Ýl¯(™KŸàI ‰=äµ—54)ÇÛ’Èá<ÎŒÞÓù¬¦äzÿŒ+6=3´Êÿ%çÖSB “Ö­’u.’  “ èH³zn{ðâÁþLþy‘)|!âhèmØ1윕ƒñ¨O‚ŽÊ¬ÿôbÝî ti»¥W% Y"_ÞMk¹LËÒž1^ÇùB¨Õ 1îPذmÐÝÝÿÏUY­â1Õ1«Éä…©z̉ʺGÒ}<"ASdË96?° z_È3Äï{FrLFIÊÁµžˆŸ×žç0m >šc힨q0R|º7Îé< 9*Œ½Ò#Ý>I "°á^]+Ën3õK ?Úüì*¨©Dse¸ÒÙŸáíÑÙ˜ùèvï—D1-Ìc&Àˆ*)„Þb•'¾™ªÈ#Üq¤¬þ2ë“+ârºØÄþSÁ5†äÓƒ½,ÍqâÉ?7rrØc9³Î{ç(‘‰GÃNÄÖ:×L3=€Ò0+XE¾í&×Üû^Ì„TÜ1-}¢›Pv=bðöØ©±äsûœ8(Ôy!÷û‡Þñˆ?±àlã)TÝí‚õ·Ù÷<ÕÛ5Jdnd“|…WBŽñ;õ ãó‡eCˆý€»핹-•ÀLÄëu“~iSØÑ¼nˆÐï¡g}WÊHJ1É{‰­çc?as€I˜d7È­ƒL9Ö½RÁf)¹‘CîM.ÿÏÙ¼ŠwWo–ÉQºùø*á¿;é»ïÙ‘ô(Å<Ä ð’ Æ|#…cSþ½«€ª•¾ÎÐQöñ}KÝŸ ü‹ß¯oÕÞ¾øCDâ0·ÎÔ°¶ØÅ%V˜YÐp\‡¸ œðÄóÜŸW•9OjS°8Ø,º© Çï [,R×bÎ âÝ9Dg§±ìRò­ït_»¸Uä%ÄEŠS²”uZNBüÀÞëgÓ®g>k”bî\Èúd+õ&稡kàãi‹%”"mù÷Í !ŸQC`ƒîÔOËU—¯•GKê džŠáDŽÖeå噥½ÕIÖñ¯ÌBmö—n³>;#Ѷ¿Î֢ÄLç¶eÍY­lTÃèpto™ü{ ËO„róƒšâ²Q& #ÅæÀ?vŽ1Çf!Ñ~V´ñZpˆÙS̱ã«ÖwfGŠËgÈÂükL‚ÈJwEßþ+ÇÐg+ÉQI§ Q8I„a£" ­Áûlö¤ežj p<‹?+¡‹x|‰ÕÁÍâÿØÙo—_d‡HPI~y cã™ôÝãE&8ð893i—}£T"¯,=Ö8y…±®ûе—ù®z¹Åuà)êW¾w]#lœ¯E‡ 4µ«‰§s÷ 'OsFsZßøh\Þ€A¥øØ¸O¾zytÝeœOÚ…ÛX»1à-8 Õo*SÏ­ÂÑFH,æw j4òŽ"tü‰ú>éabåxÈT×}±ìЫbñ|¢5¾Æ| ¸È/ÿ›õ¶Îâ5¢ïäM×ö¹sr©Æ"ï§sú&Nœì«“æëjI°3ýt¥Š¹ˆd ï4±"ñ,²ÂÑý ¾î䋸I ³¡ò0צÂŽìăÀSü—óÏÅ”g‹5@„hî­kháí8Ò´!_Õý{‡UŽ_{¬jZŽRUöQÖäÝ\Ês ö8yaqîÜaV§%O‹]qn ÒeÃïÿ…Lôù—8ÔÀ’.ê«iw/„S‰Ùù¥­²ŽÑÕFÚYêÆÂ1ä/9œ].E=æäÆDbúËVÑïd£ ˆ†Äw)VïÌi¦øo*ˆ]“G®{ó™Uöæb}³¥¬§¼-yùœãòᑞM9’ò'»ŠÌñÊÙw”—(ÉXô­.Õ"O“Ât­ý2K¤ÝcX¦¶Ë°¾[ÒÖRß?hgŠ$ ‚¯®ÉP´…ÏÜZø³|÷'-ìéÜ‚àñEa‰»¶®}c¥ºÉä}…̺„Ž\kq/l¨ÿ˜pØ÷÷I¦wƒØ×¯'{ ¤9[<û‡U͉óuó܆DXûÛæ€ò6ieuFÐÞzE±«FWßøÖÀÕ»Œ©k1G@ŠwùÿÛÈ"‹hÕ9Hœ"ÜÌXÇ’ò@(I s=×x¢È¶eŠÇwIlïÏ·93 Û„"úÁ¸p4˜u $÷Ž0—ß6F9cÉá,5ô+ËK¦=·ó$÷ŽºÒG C׎ }©V ¸'SžÐ, f·N9\³\kž(Öøò뱨{L§@wç({¦Ä£xüpú»ïâܘÀ·4X€â”L9'ÜvØsÜ}3^»bŽò"ê¶ñwجƒYBòbLK¼°ÐçA ™V@œÒèxxñpW¦îi>Í4Ü™•+g¤ÒU¬Ôp}¦;Ö]ËæŸ__Ìl¿kv ©,Ú•çSux 7ˆlÁzÎÉI”¶ý¿9y~(g±rM#¤fgö‰›žO-º‘ÎÂxG_ã/õ´—=ô£±Fèfªz”ÿü\˜Ø4Ÿ-¿uB’+;Øp±†*Ì&ÚÍ3m JÐðdÎQäÝ«ÀûÙàô:ÂåíÒ·Ø¡æ<ŽàN¤7J»nýÁlâ¢t ëÊǧt³µëйî‰ÞŽ&ÙðÖ[—˜)F&hi¦ƒ«½/½ÂêëŸt‘.B.Ú]bŽüšq÷™CF3ç2„v¨³‡`”[Bkø H ï®unB^ÅšVˆZwÈÛ·¶¿ÕÏ(gï­Ï7¥rò§åºÂæÿÚ„@ÑFÝËÉ :×§î½I«(›’4¶ÄÞÖéO–›cÛuŒlÉÁc•Ì;qküLN”ãø€>_áÑ*“ÛÚåke?”0—ý§)¤èÏCT[Žp Ç±â<í5ï@™ÎüíËm!"ûbWö Óö%î<Ã#IqÐû_p~ãj›Ÿ”ÙfÍ‚]=sqx¦Z];dœBh Òÿµ2TE3oeð˜æz€%-%X’øà<àcqœl0$Æ—½Ûî²»MþµeÙKâÿžã¿!"˜™‚ïöC$@sèFÞ,ññi€gvöîÒÍoäò Ï\æšIP©6mdFR…Ú:ŸÚH¶Pé8qdÛÒ¥#·‘dK–rÝH²>ÙR§JÖ[IútD7-—Â^354U^e|s­Þ­Ö¹Ìu™=sä’Ö“¹“Øzn“ GHÁÇãï¿VúRÒFÀ¦z‚Œt”JÀ$ˆ€”˜©’€@’ ¦ÄÀ°i( †BØÞ–äÝaÐŒ=›ÐŒlãÛô׈ØEaʞĩ˜†obuЈüÇÛ;´;¬79³1ìÿ× tØ$ ìPÐÆfþkã!€Ia„3ÃJ½˜@Ã5ø§§¶'&ÑÑŸúîkÀ6€$L#ô¢ÄhP 1SÒ6AŒƒT8g¦y=ŽÊé*(URR±‚@ÀBˆ)!©Ü;’  €ê ³ŠwE(Õ²mâ)µÞSà¹IzJÇÉj¡J¨ÁB~lv« =9@ÃÅ(¤ÊDD|˜‰B’FåÞ'?®úΠóÛPP’„@ÒªÀÝÀR$)Ìi©ˆ¦b§a°H"°b€%,S…£(Œ"@ƒDªŒgÌT˲zDž«8ÍéµOJëU­ê¦P^㸂êOŒh’×â9 Y"5j†0¾!€TN¹ó+kB€í9-©pv•;š«e=Óæÿ¶©‘ÒNkbj.•ׯÜÕi¢õÑиÜþT¥‚ˆÞÄ NÙi°µ˜“ê, w”…À •Ý‹–:’Ñ¡+Óú¹‚n{Ö””(ðZ†@QRR¼ :”Ó†ug©zƒ›¡:8ÄçJABp“Ð{¦Vzai|ƒ^y)b›b¡Ž¤JÈD¥nÖ¨úz‰Ê®nKØå¯¡‰Î˜b{ËåëÈAA¬kÀ"QÜ®±ñ¨rVû—¹ ¸™·wNt,CÂ>Èd,„N†pmR'¬Y{l-Sî‹Þ4σºpÚÒ5¬ºjB0¤câÎ"–ŸvEJÅKø…ç7"³„7RÌg,-ZͤÖîQèO(2–¶ÄËaÖ¤T¥\Ž«˜É@-DÆL¦ £{¥¯v,^¥‚a¡Å’ÜËX¿ò'ópÿÑF®§wJ¤ËF[Xf­#`ß IÒÅ«ùS¯Tc)¸3ëö´ÍøFØ ËQX¯Xo$KªF1¨»®³¹õa‹ÛZúØc+Ÿ¦£éÞ£¤få…ê®Ô Ý DZçgbÀ-u Jˆ%+äýWÞà^8P¦#sO!Tm?q1§?¸©(MóZ‚¬Äg·O+‹zôÞܳ¾u¸ìëŽj½;•аâ[É/&e °1r̾JºP?¡Ëò~CpL(üA,ñ,!’éŸ:PJË7T·B±= „˜T&†@O8fk4±ú´ëÖo8 Ó+o³N–%¦îÙº®‘¸A™î»½÷ù%PIV|¶ä[·5@X£­T:i)1{t° ¾E-cç(Sö?5k8ÃúÇ•bóÔÍÑá±<›þpÕPÏØÜæ7w:ŸkfÒSÌ4‚>ˆ†v_iuß0Ö[)ßE]F;M×B\Ù_CÄ7NœdMÀøðfµæOy5Âû^¡Sÿ· ¦JG¾e”ý‚Œè5ÁD•Ƙ}hȰђ9 ®¾ÒÞøk{”=Waü_$k={"×H>âÜíMU‰@ÕŸò%×¹¬@Šä§·'¦KÝq –&„çx®ˆÛ-Vx“oE8³»+P—\mÑøÉéq™ŸÙÄcïÞþ¯ýÚ¢õëèª=èJäcÅ™¡vêšà e—Ú~èÙx*Êêñ;uÚkËpï"aÑwD¬I}õU_XõG?É)~h=esû6c›æ³8¦tªo© ýz?7AÙóoƒ±MOÖŽiàò‘MEÕìÿ晟۵¼Ï[™z`~¢— ÏÐè‚Oä`ÖÔvêj~kkµGÞ÷ì¨äÜÖÔÓÛÓ˜"LÄ0ùš¤©R©Kâ:šƒÜ£CV4UèËÀ˜¢Å,yWxÅ Q©Å„¸Ep//6=aÊYL«ËãB~€²ñ0˜æ4GVù`G6š[›#)J ú» ¾:[EM|3ö»_zZïnëØ˜²SHe¨4f¹ÅÎ1ýx¿&£ùýŸbR¡Ö Ç9†©þÄ,ëËQß;K²JšÔF"Ûw®6º‘þ§h—Ó¸S6QäÛh,è>á´ö,ÌR‡¸¼]ªƒÅÅÀ•´wMnö’®¸+[Nvµ¢{¸ûìêºP7 ‹(ª‘ZI±ú³{v½­8¸%Î'¦îö7šxa¨g£LðÈ<7H“1…/‡æ¹k†¡ÿ Œ%A0œ$5p¬zþB"öþ»ë?v·÷KÝ’ÈÊ]z¥ÍJ`#j¹qó î$ÜBF|(+r2k¾¿Öà/¿žuÈÿç½É¼ Q”ÚÏüó±mY„:Üçx#B)‰òoÊ(CÍB@¬¿RW/¥”I½Œ~d£¶ërÚ1p7Ïcˆ5[U^€ cËiQ4ðUâSÓÝÀÉÿSèJÀû¸(»û/pŒtCaÖA­fnÁâC–"e  H婱‹Þ£_òO}±¸ÒßeuXyzj%xŠ!ø'ÿ²p IJ²Ïk7ÂçrؿܺZð¶¥·•˜,ŽK*ÔP¬™D½_½¸à’‚za9Núú´ü4ˆð»ã!/t»rÆiöàÄcm¦IüÆváÛ=mØÝöA¼MÃñðÏ}âu\™»>ãì•Þ¼¡n”[1Oט? ÈCî¼Ç4Ή Uò’YiÍEÜ ½r†AÛ$w=«b„ÄÇÈñŒ^È´´é_³]àŒ!¢Oš#¨Á¦fp6¡ økÜ:R ëÄ‘ï .þKqZG^ø{éãàØÈMƒwx˜¡¯SÞ iëÁ —Ÿ]»JÌOø âKÔó˾Ì^½Ó­û*ÍAœ‚/ø'üâÃ&` ì4b)unbW0mãnÜñºooª<õùxÄÂx¦Ä¡Bx » èÀ‹YÞ$Žé ^èú2ÍÕö„Ä‘j’’„R»w‘•ŸWàðíb”YÑkcuãåÿ‹_³²œÏêûà Yø›t÷ÜP  i§F½üâ§¼ÎKõ„ª›nÆ„ 2uùóÅ[™ž$Ì9Ç–))€ƒÿµÞKÃ%޳ñî)¹;ª÷PtB'É»>c-õum¤kÚ¶sû—ƒ»ÆªŸÖm{ŠÊßðâØ*ouÙØcMÑgK¹™ÈXkHZ(ZÅÓj‡Ó__Á75òH inÔB·Ÿ'Jê¶–A¾ñ!¤HÒ€bú›ðð¾5n¤. ²´„Åú}`ÐôÐg€Õ´FM6>/•›é3€lúÓq`}‘­'šò8(ÏÃ]öžJp¹,Ož/ñz¡W‰ä1ÛµÑ4ùîî³÷ñ ¡½K *Àódý§RçV…ËO‰å¼-IŽáe!ì 1²¿º;á+¦î×Iµ„²”·oJÅÎô3˽¿¤÷aðVC¡ˆÉ ‚™}Õª5vòiŒln¼çÝEÄel€W n.wx¦i܃»YˆÔ¤B‚ñ—É)íûîZ*êR†ì…ߺì)úø@šâß´ á5m¾ËVð€k}—§Ùiç±nóͽ £iž²‘;ÑéTOÚ8–osݨÌ?*b…¦á‘ËŸ#cU?"*Í€ØûÜôÛÜ™¶cèu€ØÓ¯†‚q[6ùü ÞÅ ÓÕ´jêÉÃuEš)ëÐçzöŽ%o€»›ãUAþÚåWC=ygwu*;žË*•b¶üɰ.°‰~F4 «ž,båãò&|ÎèÁ~M»+ª›rÄZ“ÀXC[àüÀèãLÔDÆÌ™ïê ¹ôyÞ³øãjY”½ÀÉ]QôKÝÈ«\‘?O55Ó”Uå\g {•´a>îÈ@Û†ÎÎaÆ0v» E¸må|ˆCX:ôºÃÚf³Á-.N9$‹œTÑ‹¬XY”¸‰»‰{DJÐz¥[Ku,v Ç)Z“9-`ª]—ûÑœlz„È[ü­ä‚Ìx†Üû5³ÅI zÐen+_ÒqrM[ÎLB}Û¾L‡d<âP`hˆ4æF Ïï$†É¼&™\5£lRø)Ø CªS:çá)kÙR™ß.ÖÀ‚¬ˆ-HÎWº ˆ™êñÁïĽÏÜü0ëb&ãâ¸_aJÐ1¡ºf$ƒ<ÅÁN+ÍÛYXòÉñºçšËöø'ˆRˆ˜’ swÓ)Ãã>:…qÒY D¼×ßÃð8¨µª§ZÅãÒœyKbXà:É}90¾mÓ zô@6¶ã—ÌÜi·R†]LûzrƒºÿJ³Á™I#ÀBÀ-–Aé–•ý¼ áÆå«{ð ƒ/õ.÷°š•y¢6ké}'‚ß9±Žïnh‡ðïU‘üH@-šTéŽë£û½‡ð®‚ PÊ÷.H™Õ7s†- Ø@‰•ÓëÜ\=*ê`ØÙnÃꩃßÍêú›^õp3gŽ€ÕéÒ€ŽDš£KÁ³Ã¥u^ës®NV·s-ÂËy‚â©°L.¡°Å §Û®PÝïa5õ×Ô¶]T‡¤ƒÊœ6ÿöaúÁ{±ZÐ’c.@WZÛêÝ?.vվԥصÃY’v»m­‡A »ÆWž²èóÐc96vQNÕã£!³ÉIBcø*½1>ôOʉ!%TñõžZÍ/t¥ÉãF+´<’˜Á²RÇ M_Óå”Â/2¶_BÁ Õ vnÚ>è_€ó#3¹¥)Úwøœå6¬cÄÃ_Š6 „`ñéE±üIÓ#®ƒ=:ÓÇ£Žp¹bº _RãÎFÊôBê ÿ$¥ä°œŽ:3åz¶ìäðs& ~°öì$àWi´q£sS«(/û`+اàÕ‰¤-ýwñ\YÐTÄ|ïñgvêuùG˜Än…/Š‚!l|…8[%¥ÔXÊ´âsŸ•Et8|ÏðSáÒ¸¶ûÐ’¦H¬oÊäÉënTcKÙ˳Ì%_@Ï©¼*ßó·ò0Üe"¢7i›—X]Ò}êdÆ,I3™’¢Ìü¢£‚48ž´ÚîO96ã‡)U»Cõ§•±&å´t\ëøéÀ„bZFîÙåÒ:Øm‘‰ê×&›ð6°‰PQb›×qT$Ú U'‚sÙ»8F“AGŽ´{ØÖÇ>ß_¼Êl™Ó12pˆ-ikutpn9Ï|ÍÔ5OŸî/!éÈi …ím‹ÅòÉcÖjÑ_‘wY{ï^á¿ @çx<[†‚1à7žÑ¤p ÉÒd¿HåÕ@R‚ˆºXEt~;ÒqG´‡¦Ù.d®r%=qùå›m˜ã›ç5L¢V Ã:‚S‚T&æ%k––Át;.`¨©3›ÏBòĤí2‡à«‡¾kñªc±gØGIü|Ë|#Ê2°&ÄhÛùb ü °{Ç7T|ÓëWâbЀþŒXûŒ¨tDwÈ9º=W¨ó˜‹3w.É`ûRÝ#{½ä6¼á_—ŽÞ< ßúADà\(TýjlƒõÖ¬öÔNòëÎC$æKж[&é5¤:æ`bIñ”µ#a²¥ãÙî’iÄ‹uµáL™=¤«ªjW^OÔd­à½ ÌjÒ?üÇöúCך%õ󪜕´'ùØ ˆ`·=rU‹—“Bò:&9 u¥Ap®—«59ǬšMÑða^Í6y¦×ÝÉž5_ɤÄo„.Þ®T.8bU‚æá; …KBhÍ”a‰/Í á$öš(’B€ð¹)ùmÒÆ[DUyuwq “{퓢馫ÿqø P*åÒïTVßôÕyü]ªõ±œ$ð/Ry7³{ImÈA>ôI„Bªi ØÉ+C£ãmÏü;fYÌß YdíêyD_Åæ‡""ÀYì.ìi÷• †s=Bæ`eÓê.§íÊ}!« ]Çr„ºÕÄžsûj  SÐ#C0± ü‰é1@Æ6ß뤌ç¾›±˜Ä›M‘7Âa‚ø._{Эú÷ qn"Ä? jréñîôŽ¥á ”UÀÉëN&Qƒ®aýv™M=²Á²ëSwšf_~kuRà³Øk‚áò*'A6«ì¡y?Ê^'>Oϧ{{ÍN$ª?Èvºæh ºƒøS/_œ¥ç‡éÝ^•Å¥G,: îá­ÜŽ9J’™ŸÃ[B]¬ÿ‡ÍØùÝA{â½"ýÑ%{Ï·»C‚ÕÝ~ývÓ4ŒXs“s˜8“ðø˜wœÄ3$qu)¤E ÷ ÙBz BÁÓWèSúþœžS×Ë¡'³eFÓ~ŠVá= ´fìÅ~ Ðá›ãQ ùF8às($A5\øClƒþÓü³Çä;C!ɘ“’: YºƒR°²Ä4׎ÅžÇqÂ…ði8÷#½½á/,å[dgSå:ž ]ÈãR>swÍŽ“ñ!ò‘þrX„„2‡L/7Á4E9ëgó8|;‹&åLÊŸ8C¦-8•6 5Ú|i?œ`©76æ+£+*wžeé“P¾(òO»¬¿º‡Õožmíx!Úˆ¯^KeøÎ~pÆ¥¹Ÿ=¾R]ïû“l}¦‚=½dT6,®£ìsš¾©(ÖUæs¨ÙŠ ÖéЭHIlJ¿N•í~¯­uBž™íf¾ó×k©×sCm³·Ç»­Ï¼ÔZv“*µ§›‡ƒ¨»ó‡œ°mC7kF6°-0WgzÙ¹†FnZ{!9j%ü/nu¸®AxHæŒá'ùâ#íÃ%r­¶)ÊXÙ¥Z×ÒåiبàXgŒ#ãuº˜¿µxÆ · " 5éÕ¡Ÿ#;ûÀ¾æß”çÏû&”æE:ª1zS8ªIñ.súíPŠ ªX,0Tþé¹IhMnAØMy!£9”Æ9ºfKȘ$Nâ[ŸÛ=ûb­>mâqnôðt²TõOŽªjFyòœñÃòÐʹ1‘Ã2Hl4–³žÏ¿Å ÷¼_àsÀ'_E"ÍÈÝ?H/ºÓjIþ¨1ªÁ ’Ka&ŒóçŽ]éŠÑdEÃÑÈjR7àÎxëÑ_´Ç"+!£M_ІýK‡QˆZ{z©à\*P«éUUÚ%è²ÈäXH§ÝñÀn}0îäXQo²mí™>ÚZñÔ[`«¬Í­IŽz¼V©¿YLT‡(Ik.O«6.ªó¥¨±W]qäwøs oÕh^ÙÞ*™ñ0>D&ï» S¨l—Н‘¾ßÙ3’ò uk…÷ÁpíÂ%6’·Se˜ÿR—å~ÌÞv5Âðù\ÒˆŸHî{¸o|¾‚Å ×-)ÈÑ"ÚeÐú«úlÅHÌ`;ƒ‰-°Qý².‰åeíJs—IGvnK[…‘ `Üß6ÍnËyö¯Ýnû3mœ‘Ƕ(U«vm¤ô*vÕÒ ÏÛ‘*`ÂÑ}g ×Á ¬7›ü)gñ}^0”y}÷‡B¤‹CÑœfb `È?3ÝrqŽì£úN´|à£pqw{ Þ€÷桉_y¬cpr¦Ž­{;pd±±}ñõiºšxÜwŸ§—¸äak/ßÏ~ïùš ™À¶Ä_›mKµó™ÅŽeã ç߃ª»Èõj¬¬K—í8àUùØßR?Çä£à»¾zMIëDÙ¤ŽCLÀ,à¥føÆÅý¨™úø’†½[š’/~ÎÇÛbèåõÏooÛyñZ—]º(ƒHôÉ/v‚³Ø>pæ´‚ØvxHXI°,ãH×.©¾꾘—@yžI2†ßx/” žùr!&;æÂ€¦ߊI¿ññ¹Jßê|Ç÷D´ñt¶áÅ ³Û}ˆÞ´(i-ˆÝ½”ö˜r˜ö`íÊ;Q 5¤²iÑ,÷2t©0F#e™-e©¼ŒSÈ‚`úªòûåa1çQØ+J_N%aEüÇ$LɲI aÞÙ ³ÜˆšîÄèSDq´Ï6®ˆ=àèþ¤^—±´Ø7äâN'FÖËGy-¤<Š“5¸åò(‡5E® [“9Y=éNâ“a%¿ž²6ÈÍÅ<ç_M,/e<“å¹(dÕÖ^ÍM5¯xâ@Dûñ+`åÉh}ÊÉeµ-˜‹ËL*Æ5…›0.`ä>fäuHÏæ€91-ÝgY%RŠg9ò<²_7ˆ¹/+O@ÓTÿÕ×é³Ð#á~y|‰M‰ð°ÌÌT ï[ËŠ‹- îeÓWBÿ:ebŠ‘àP?Ø \¿“ûiOïê›Þ‡ó$oÀ ÇÍ-æy›[Byë³8ùDö1-X Ì> !|ãc©CjÛ¶¨È ~bã´N\è˼[U1bZ–´ës4÷ŠYž_³D‘!~òd™ëÁ£øõ/Rb»Ý6óñ›¹B™Zü$Düàx¢6¿(-Êà—âeNø,‹£1¢¸¿k<&ÂÀÞì ±Ì>Ÿ-ç!bEi÷¬PÓƒŠsÊ!½'ãá?‚вñø¿`‘ûÎÁ;}÷;ëÛ†O8“}Æ:•ex3P™­¿ƒã³ø¿ÌƳ&—XY åß<ù]ýA©¹g°÷ öFã™»ÚT¡]Õ¼õõñ~†–¡$ßœ¢^&Ùm7!5‚("׳I(É(À4ú €¥³piÌòƒÓqÒŒÅ@¢t§Û›¤6=õǨB€DoË‹SI4ü@T Œ& ¡£Š@ 0J°APJh×"˜ $LÁ‹’J \ È„H•ùð‘#Œ¤"*1J€ˆ(YðÃ@pQPT‰åš\×áø_F ˜ð °}{Ü®(cÄ€bC‘4³©©æ¥ "¨C36æ^ßKÒ$í—ͱMóR  —ÚÓ¯›PGGWêp`Xˆ6AYw¦=q8ùPä£›Š‘ÙŒñ6óy»?ú‡RlBôÒCAó¹½ð LššNÝÍÏmMöhû¾Õœ›ššzÛs„iF_“45*•©SNgU’Õ$â²¢©(ÌdfÁ»*"Ȉ J-&ŒªÔh)Û„`0H‰á’`l¼ ¦9 ‘•Ø!, l&4·6GS”ôwc0uÏémëPÇU’íX$l7m ·CóŸ3v_¶éŒ#-Em Â”ƒB1L—&0EÊa©žbÄ9 ³Áh¼qLhŸÉxþ1Ÿ†R¢Õª’$"êLÌV8‡ 6€sW@eZzÎHgûjgžc鿊Ngs±€Ûÿ;´w:¾Y6að^ü ê)œu)[¦ˆ¾8CqGFæÅœ]Ý „ŠÖ²,ÖLS7¼oXZºÌb{Yqó.U‹ÂÄö(“bš±@©õ L)0%Hw-×x©ÔSøW7ƨD¤'gÉqY®/ƒ>+èî†y©¥±‚*Ò$«Ì*®¢UÐ Q^YwÀxŸ©×Î~T \èE•ÝÕ¤`’Ò*U2þµëðb¯¶~€–»€”$¤;yjG¦n±wè? T!U§ 0  [¦ÃÍÒ†t s4 IÄ0KÓËB¹ôdR‚X%¿ ‰cùöjSR–ÚF8…BMŒRÓoÃEeÆ¡FuºÇaä „îa îá3U,¼:µuZ|:#ûþö äW/B)’,¥kl@0ä‹ÈÂB0=ØKloÞ€]=µ q½3mfÖwëÁ] oOCXµÿ¢‰D†‘,ã.ä³’D¤©´(D^×ß-Èñ‰s…Åôš\D4ÿ^T€2ÛÂoÍš¢ ÊHB„¿åƒØ¤“Q¢d,ºFsY1|! PÔ!¯(›M‘a@IsÂ(9R©ÿŸM+z†ˆ…ªéyö]c1ÖëeÅ»:mE _ùþbˆf1%‰ö³×ºd,cC!9¦8m×^!U)Ÿâÿ”NÈÜL±0F&¶êý@Œ€ƒÍ(Cy,÷²Ürìo_öR•b²ë¿ˆpÁzngcšêèŒêÅýÙdKݲNÛ[¯~ÓFáó¾Rv´Å âÏ6iâÇQˆi‹òwhØUj¯€|¶!b›°mÆA®Ùßuµžš«³£“;u 8 êe‘ !Gµ®Øn¦g{ë ˆàˆúÚ‹>¸Šâªyú˜vXïGõä'³—WÂy2é I£ŒjƒcÎ@$©ë;N5Š ‰“x zë×#‘•õ¾IÚºG‰Þwpª+„ z¦×*­ú0.9ñ×¶m¼Ém†/7º-WwweLe<¾ý¦'N1Zùm óu ’*íhÛðg‰‘däÃòm¼f;äϺåìîÆÌüÀ ·ÒÓß÷ÞžgÞß…AumV#~_›eеnj¡ÌÜk n¬"Pè`ápÔ7[X¨ø8€q#܉¢å(S¥"Í~uÕ¸æÙÕŒ—tå‘~™%͇Žs^ A>0©~ºxgµ'$! ª»Ûk"ª¦ˆâé¥ð;bÕý¡âGÖ-£¶NLo½d¥¤¬D,.ÊÅRrbbgT>+ÍstÏcš8?]Ä€tË-(žð÷”«›Û><¯ñÓ´õ‚¼-ÛßÅÌ/§ðPAõìm´…OÊïôš()u5ÐíZ½ˆ»ê8o”ŒÑ½SH<®¶"æ•ÐÙ—)–¢ll±¢µí1Îþø¹Ç“ã%Œ€àf9Ä(ÍXŽ{„9[Bk®Á‹Ág#Y ›Ušmþ~’ÝïÇ×ǧPœEÆ'¿¬œ.¸!ÐËÏk…ìè_½!À)–WV_37Á}ã’PÆ8¸<64´„…¿åw/²«ŽÛÇMH†Ø°b [ø ‰ëŒÃ¦·ŠKa g|’Šac±Ä„‰ÝŸé¶D…>G-÷å¥9çç{BUF¶F‚Úõç;¾º›Š}0i->–‰êwýºÞŸ«l*™›{í£Z_ysÂ0IuˆY #¬7n¼,üy½ŠŠð)^)25d‰½ˆXBQñݱ&J¼ÈFð?qÇgö?Ú’Àç‹(±!oÊ5R¯˜˜ Þ‚ïÂyµ®DÌOò"ÒA’Z¾X„Zh '䲦÷ˆ>*†tzŒ ÈYIç %ÿ,£ÿ Ϲ"N’7‡©~g¬×ÊjËO¯/í‹4:O¾bDozüm=^_V÷= »=Ñ·'RÀÄxµ•Þ JÖÒ¨W?pÕ|Ñ_îrµzü©ØAÖº/ÞR›­ÿþúœµÅ[謂Bnë““g4 ³5b'ø1-vã-ÕSWc`¸QqõÆÝýÆÀžn¤“#ÅH½p© C;O'·¥H yu¬ÖNzÕ¯Úáн3·L@7 ïúøÃ§5'§¶Š07Á-¸›nÞÝ®ÿLÖ& lwΟ»ÎÑ}¿y¶ Û2s>œn®NLüþÓþ~¨Wn÷çåµÄ£ÐS¢¯9üMðèü\ø'Ч K¿3x<%Òà6fcqC‹SˆLj[¨}ƒ);)Rs Z0Qš|0 ¼¹ÏÈlARW£šbׇCð><ßZ?m,ÑZMët0¹&`2šþJC2~>A,aíŽcá‡bÉÏ#P‚ÉRÕ¬$³WwKî]›ú´´ÿPEÙñÄFrÍDÿ¥ŒTutõ<Ï׌~ý&-¢¶JçÍb1…ކÔgBäx+teâ]&{yæ)a­ˆÂYžr°œË¼ì;¡-ÕpjuÌ0åHG¶‰å=áAƒå80¦éñ¡¿cÌ0Š vž «1ê#]·/¹hJ°°sm” M-¦(6\í¿_ØÈíµÃwæKëj½87£?PÆZ{QŽt¸Ô¢o)”Ô‹æHã7Òo ëS¶ðe€Ãü’7AzÙÅñóÐë{üSo§û;÷ÆÓÛþ£ÍíÛ»q%¢ Xftw#CŠ$Ï7w·Žaðš¨VBÕªåùÈIÝ«Ù8É#f13àr$ìà zý¸ø¢®KÄÖÅêfâ(’gzÑÖ{üìÕäd·Ó¢Ì¢€+– ÒÖyË § sï *#6kzÑÚX¤*¶1ôŒÐ;÷¼ð3޹¡"‡å‰'²"¨ì¨ÏRîP^B=ÅLJ̉C."ŒocIš‚öm*ص çÆÜ0Ð0sÜ¡°Â»kO»Íü`Öõæ=yoÆi°§TmÜo˜c–ôð 49Ö˅П¸”c„Ç—¢_VtRûŸ*àA[Kw4EZ(b;"® ¹-Œ7¯ÆÎæeÆF:†Rr,´ôÔÕçjw1ÿ¢¸wkPæÍž/% fÕœzĤ–.±Â<àOf`ÝiÝuøLi‚ìØšito}¸®1Õ Ý(•qÚn ®Ã-À;ð?:š¾ûÎu—ßF{TÁ¿m¥z¹Íl[®^éÿ]4ÀÆÝmEò¿Û®h_óìVK2§áÙçêþî¿{¾ñÀ%³„<òÔUì¡Þ‘Ë®‘4:{Aöc¨Br &xj»Kä{Ô¥©À8&_xWo»iͤ~qj;"°¶0sÌ.'b,üŸ˜ PC†ÈòaûÓßsóÏ,͘ àof®àÁUU ö£4˃µj<ˆ<-âÉ.~ÁÿjM…‚sÉ w ðùÜéc8á@¿AîØ¬þ¡ážÉFæÆ?ûæåÂVÒÖ*¾¤—æbr0*–¾Þñ"”\”|)¤†gZCÄŒi_ä…íAPä[Dª\n?rN‹Èù0ÿrc3¥+¸VI5(+uy­þ¦ùÄŒ¢O_bö®¬%)àŒÔ|w›^Þ±DeQ¾¤½¦ªæ7è×Öá ßRy®ÈEüBÅËü©m2¸úÜk´,Šˆªc¼Ç2}´5]Ñ:oyAŠr9±˜eIý¬û8V_Úù–V)›ÉÍÿq¤çŒSÂi¿Ü†Zïï"ŽIÛs„Aê¶H4 ö톴¼»0d.YnÓí¿p$MëÈ…uü‹ï“™‚¢ŠÙÑ€r³z³÷Zg ’“+"ëÒ/W¬#¶àÑqIþopï Sà³µ™ÏKnên7³I‰)š‘N·^þr“’e8ŠÝ8 ȶ!Sú‡µöm–#·¼¼“¾¨=¤•®Ûxð41&¬[‘“€¿X‹ŸÉ^“|/:%mÚ¼o_¥(n¤é×…qLˆ¦ál¨M6Xfàh¥Y¤. ½A¶ñeP5gL¥ýøâ ´„ Y”ä $Iü÷}îc¾ÿ:B¡QÀ¤TÐõH£œèÏžÇSéùKeŸ{=ß‘6¿¾U_=ψù%¯xå­¥s¹a´ÏýÀ~óÖüÚ)¸X,Ϋ.Ö8×´Kœ/xã>м :SÈ¢"Z_XGkcvt¶.÷…õx< Ÿó¸jŸu²º-)¨èbü‘hµ%µÆ Ù0`~©‰3y#•ê{d;>éVG8Å+Hù1ÅÄŒÞ;$i Ä\£7–Þ£¾6¯µe >l^¿]jv¶Ž\5"vÔd |&¸Ü3­ÌxROÃŽ­b€.޽y±œwÃo¨ë8$ù¬d;áL% ×` ”Üa‹gÎ 0 i %6Íé°™è<ûÔ¸÷~ (€_¶åëúÕ}ÜîGVƒÒ»¶Õ!Ãz€>öÝt¼,ð¼ãi;’jÑÛ=‡K•¼§Ç1³f|™¦æ¢*2öÔ)÷X¦û}{žÖP}1ä¯cy’C‘Æ”÷t§G´î LÄè@”{–£*HÏrÅvqbqà?Z;¬ŸT,ti®YØÎ4ý¥ƒ,PßŘ—*b”rçƒAË&$€Z×ôú™ŽñN¢eÅRýj]mrCCˆ”9o3B»·oÒ1toLÓXS`çµi½Ðï0ðcÐ2xëL®}cÂ>àß‚úG%'U#?O¿^¯UÎZu?•}ý UwÒQ¤OÐKx’ÈÉe—;í&…Že%‘ÍykÉÜÙÈb»¯ %ýÇ EGÑçØÐÏpw îZgO‡&ÿ‚0¯ßvm—áæ´/ì’„DžFloŒY ÁO«£¾ÉË;œ0êY”zÇNu¨¦F-»>×ÖITÜõL骽’w´l •±WÝ“2§aŒíÉHUÑï¦ó‹¢`ì¨[—§soŽ|%Õžƒ{«´´‘}œfö8¸×æÛ‡Ü`þh}xö-AzÖÞz÷”L˜Íj³ô(²¢lpéóüýKâ$™]¶¹–­ã¯•Úªê—!Ç 8þô*ä4¿­Ö²í9jê"Õã`ð»ã?xŒÅµ¢A!ŠªÒjfLGMvÚÌòù_ ŠÒ0üg}‡tPðÍßßÖ#fs›ùí9æ¿ZJÞR"i¨Hsá”q{ä|efÃgåpù;o5éŸ ó`AŽ —BeŽ÷ÏgŠãqPgŸàù¹§½KŸ‚UºJ ‚ë‰÷v<šµ‘QõK[™èxùk†Âqw?Å_¾¤KÊ5ƒ¤wÜTÆÃîÞ±6acŽýž hî¥9wi·ÊÉš—çbƉI­&•_Œ^ÔY¥šÓ·T!Æ|OÇ„zC3¼Ü¯œ£ {{Š®:ø±{b G˜$;^]Öe¦a´[:0 ¤rܼk^¬/„ èE‹AoLÓÜš%_ê“›÷¶!Fßp= !ÓSð¹æÀ—%’5x:Û"ø/‰›¬|ѪRU™Ã‡­ÜB?ƒYÕ{RL-ãp>ÝE‰hʹ;{>„çkf¥Cr¨oR>àA}‘¯þ.¤Èc ;óqÕ5Í‹VÏæQ¥b¨¸¹3yA¼ôt+E.¤Ç–t‰Á ÔÞ]„\ìÀ=‰ÎstÙˆÆW8Ò_¦IeоÚ"Íð«ó9àÓ/É‘O>ÕCQ‚µB}Â÷8#9­[•ÄߪĨöÓelâ/0íxš¸‡g¬[1ËkþíÁóÉß¿sa†Ò¦ÚçÜÀøsŠnÚÓór‚UýÇÖ Í­¸ª¼ôÓìÔZM¡d”…€ÌÇgùHáZA–Ɖ êw:Ê¨Š’àÕÑ!½%î c08„=½9ãÌPE²²¯[ˆ—æ}3­yFÿC;öì•â쥇òÐ#yòÖ°d"¬þa¬´ÍO¾RêªÐ©ÝQ²ðêéO*tyã“€ê,d ³P¬8Ê?Øz16dn‡O£Ð%&®3È!èý4ž„~Xã¥As^ÄkþÐÉ×W{DnƒZ¡C #šyŒÕÏr0ñb’£² ÃŽ™àdÕ×¹+ÿ_ <žÄòæÝ©#ªrž¶NU+q¤dؼĜVÌa“ó“7ðß™/÷Ux‹UÙy–à—»q‡Ò?÷¬„÷pX«mÕ¾?+9Z#¹š—Ñò.ãèÏèØC2A—¸Ì~õЩ›ó%m®k‘U8ÀŒ]çJ<¢#ðM\öÜÌøÏÛ-ŽWÒ“Èßms À¤é.S¸»G³¯»v';aѸtá4¥С0!¤¦1)‘Ôedz˜†-ïMÕ÷΂ç¬fxjÊOÇYöyÁÓ<mí¡jàdûz¼ÑÇ`ôéï<)¥ín¬j_¡Àú#3÷ k6ðhD0hÇ0PžØ…Â~×[ÜÃå+òE2, è0°#næµ®A{´Û7„3Æè؃™4\Ò€ÕîœÑd€# YyK̘ôõÚr…~øûÞ‹ûQEößÅ!ó²½xa†$ýüC«–úl{VŽ'LC†Á°)pΠ†hÊ`ú¹lí ²(—å¿?Á0·Ï§¢ rÑõ#˜cc€ …=«µW¯~˜r|üŸÊ‡ÜέbÇoEDÄý!>Wˆ Ï ¥‘|°ˆº·±I¯K óãÈ>$|NµÙþS~Z¢C¾ŠIÎ|‚‡ÂA9°ˆû•÷iCÍ1€ž!#|P Ïå½ëðÿãÿD[ý½¹ç0ù@C>Æ®!Í/áCø‚·ÁqÔ¾dW &ºþÔùÔ¡_{Ž|Lséï Ï•½Å T¶çJ´Áõ(æ8ž³î “L¸…W |‘fÎtçKÒ£tM·:G\¿NoPàï–f23R] &·ò»gç\ç%‡ SÑ!xr9ðqØZÅç¾ô_bs•HÞƒ(?aƒ[¦Ÿà± ‡X°ÈEap…˜u?¾Pw«oß&Ð×b™ä¹n¹ñ'ûyå[-çò@~•Jôø^U¦QX\‘(„ ³Å¶ ä ¿-X¡s•+¤°s¶é©"9c>racÛµèÙ›Ÿ½9„“í„Ù!'üƒW™®Ðuu7µr| Ê,³Ë‘=½t»Î±IAÿˆ+ÎöÒµËHͳ°ñ*ï9²g®`ø±<­Yæ®êh{ }§i£ˆÙpc&6çyZn˜¼áu\êå‘S@=˜ˆú1$¥Úè:}á$^YÛ9˜zwи¹eÌ[­šM¨{™=ŠòYS+ }?*f4kÐÁš–ÏÚ „’°oŽ/äÀ¹ÜW°FV8™yëê¦[ÒM1ÒÚ’7oZÚÖL%Á}ÛW¾Íyžw6•íäѹK¡Lå’6ÛÅ{|“ÓråÁx³ûp}HÜ&;æ˜k†º2®x…äóm¥(6ôQóêáÛhït†E~@þ­.NŒF?û§ß÷€0`2=sw7ò•X5$è´(ZŒèP³²'×G7l3ùèAyo]¤Æ¶þ6—.Ïkù)÷;ÚHd(;þDFˆ¨.³ ì骞í½U çõÛäÙ¼íÙ§n³ž;øœ_®•†™ûå´ø$¹âÏá0j0­ÔƒFñ¢›Å‹áác$r9¡*´SüÉɤ/W%äþ Þ¦aXf{Ë0öÉPçtôCÉË9 lîWO†Ûæ:OzË®€ØIMû©ýÃ[ûhÔºXšuï›<ЃÛ)m¤éS´Ýý»ÜFÉCŒ“Ëôþ¯8£ˆ*þ-* ¥ª 9¨g¾ÿ¿g¤§ü˜ÂJðÖ¡³ªþ¢]`åƒ|ÏhžZª?ŠêH¦3êÈH÷¾ø-ãÐàNÌý‡ ´¾o”í¿«0ËQÎ#7RRë >*mVxÌæÓ?‹§Í뢎/ËwrkžÚ‘´m}8ؘôÙB·Þ]š—9Ãè5]b³ÍèkTgÿªMe›.ð¬6ÚJ²hUî¥â‚,Ç<ðõüñžŠ_²pxÏ68§–rVtÜt%cVäc¹nmZBÃðƒÕÏxÖ¾m…¥¤²¿ûîd%nç}tøÅG@h–𭣸¤}òE·7¹€Õ ãSI‡‡°‰,”ªUY!7ƒR¤i9 Z`øm>ñµÖŒåÃ9ÚÐKP- 3΄~¼m×^}Cè ¯¥[ì¯ì;RÉÌ}¼‰fô0[UŸŠ·Û¹™ryõŽR¾ú“ÍþõSµiíh!ßWéRIÉÍŠ½—œ¿­¬[£±?<ÂÜÒ7=Q>§Q‚¼­gäRLþög!•r‰Ô©ÐÅFV ç}¥Ú Æù2âãBˆÈ³ )$2Lß Šøæf]zUÛ–Ö·ox¥šâù„¹ —ùÎC[÷3R‡ÂýOu 8‹+çrÂ7éRiÒƒ"Mié(#„@ ]½2µàÈ`“µ‰ÒqN4³”ϰ±'7 RÃë—ö'h R‚„ ±½QÔ©Ç8‚ÆÄ•h»‰kSHKhY>MŽüj{17ÏuRñ;¦4ê4fóÝoê°Ý3 çÚ×ýKî(IPoú)ìsÿ‡ëèB¡‰(º6SÞl”êbÏ9½qìKW¹di¨£•3 ŽÆ‡Ã)®åE(†8ÜX)Oë[áL¨åb0p|Í’K™l¥:ɺxîã™ÃL "£@yLuùÃ8úv9« :'¾ãÐÆåçsÑ䋪ÓóFöHêwYŽÚ ˆ'söÍ7,Ê [f¨uWõš=kq”Òõs>ÛTú#þ)Õ£ådú3ƒN§TùX Sœ#ÒÙÃó|Txì>9}#;üÅiEŠ8­ˆiªu·T™Õ]Í&ÊA%s@|Ó=(K·´­=7;EÝ|å} ½X©Ó w•ƒ?B]Ú­bß'çE²öæÍHŸ>è’P‡%Újšt½Á *ýéÍH'/–W!¤­¬?Ç7d–)<±µâ2/Hä³hÔRd_5gĞɻåwÔ}–(²)ÐûÊ×yYfoLÖ¤hÀ•¹™ü'kþÍå¡ó; èÆ‚“ ° •÷[s—Úʲ¶éu¼øÕ%W”+j©-0S”æŠþÓЯ·³¸àšdm4¾”¡çͼbáGxµå6N,ž°„›õêûÓ Ki‚—O9ö=ô¬$Ûàó ³|ö„ѨìýPSšLû^ªô’t¡@°@Æ»"W«©¹•ìëéIÏýË«£33³Á,î ¸y Qžã€n¹•pA¨1)û5c%)&[Žñ ;]ªNÏN¹ì©ð;l÷¡Q‡<ëÆÏ‘÷ëª{O·kQÝènJ½=*¥·z:öY#óvŸäB=ø7Íoj˜04—i¼«œºqsh 2DEC_eèØcyÃJòžà™¡Ë)ƒÛŠžøcª¶Ô×M¶“ä6oÇnäæ$%¿¯úïsD#3``»Ul–\Ë¥fý-?{Ì,bI{=øÅí7Šœ½wŸònw’RsÛ7$ nˆñŒ$#APáª#F’*P1 nÚÐʯ’>)øVåU‹ ª@ÚÐËÒ«ùTé¾þ™+¢‚3þ³ß÷ç<›7—»æ2%eOdË--m‹‹cúHq $ (i¦²òôÙÑ>þ÷è»Ø'FQL‘>ŠQòÇ£?“z’ng{Þ¯8~Õp j@%Å(EÚD M’ú´b/'€H…$œít߈}ƒûÄ´[½·kŒcE¿s^'¥°‰ÿ½4xN@½’žB:˜šöpëzÒù$¿ïŒm¡@{ã)8o¡tõi’ûEa8.f%ª…y\i°u9éA+ÕëIAŒ}»§Š›w±Š@ù-iÊ;-ag&Ã@yíMKívófB]b¨©ÀH•,H‚ Cœ>Y˜òD ,DèbòÙ€T.- @U¢Å€äA™Búa†¨B{B°Œ?*èFeQ3Û@T jÅ,€à€1E,½ôiÈ4ÉHqØbQ ˜P©BE™Ô˜äŽƒ˜‘ÊZø§«ƒ¿$Ϻˆó¶Mj]`eda*³5+ `PN ˸.æÊÀ5z"€«ØŠX…Õ‰E ÅA @BJ¡¼˜&Ó°?¦êýÖcYk2†•‘À6ŠÕ$MJDqŒãÕÌu¤%E•zI Z&‰µ€ŸU€bÓÿ§¥Š2[vÇ‘œ¼C˜HÕBŸ—v×þ?¡~æ9¿Õd8•ªN†â«=è+^ 4’ÏŒ¡ 䬣¬ÂH…-¿Ø‚ sbp‹º½±rW€´¨àoí P×Ã3Î,›UH¨ÐX$a¶e`Ð×jÒ xQoîtsjŒË;ör‘–Û-I25±š·Ô$ìwR;\¨“»WCdÙ]ºeŠ}ñ½‚.l––Šéu„;ol5U[¯|ƒ'$r)˜ÎAàtRл]ä¡»"5¢**–•ßbÑœjݧ®¡ÞJW]®u)RN×Mä0´CDž­ÖñúÔIzYŒÑ½T…Š*Þ§Lwºß‡nWNDÕƒ÷Ÿù ¦ÀÚIrq¢|·DŠ+<ÓB¤“'¤’Ý_©Žˆ“6QÈXi¿I  4˜bª¸ôJîäàe//á^#– ¯LÜ¥be!¦Ô÷ùwžó»²Zö`rhŒ\ ×\(¥»•-L]<|/ï!²|*v䢯s‘Ú£"n;æ{íúß©í—ÈÙÀI‹¹m2R=‘6çÞÞ:¿fÛlÓÐgë¼&9U©iØàÖéÆº>Êñ°”h,šÖÖPŒ•’­ÂPÚt³i™´[nî¬EñÂê’w«²p»³¨n¡Ÿõîd\ÜIËé†÷æm§¤Ûc$A 醰òíLÄ“j/Ĩ»VPUÏý±sÈtuõ)cgT¼·Â×ì£}qØ”9Áã¶g\Ï¡HªV¬=UØËÊk‰5X+söÜßâ†ÑkDCÕ¿EÙAm€ˆ­ïýËìÿCsÄÞý5ðN|r-,ôBº%œN|ï8íhëçd‡> Ö‹”îCªÚÛynNÒ†i]Öì1Z„¸ªJØt͆¤'Q¼”á“gãíkUlsÙ[¼w0s«Ø¦ 1ŠÈ»†_ßËb4f>#Éî]ðŠ®h‘öø":—†rˆ»ú9ŽSÀ·f¹µo,¥@ËûâïÇ—z_>בHá ®'A¬ÁE»*! Ø-Ù¡{‹Á…ì "T(¼ >ð;QR)‘ö›4hC6.Qvê¦íï&\í©[ìX˜¸]L= xÐÐlØ«‹ Mv/?™]ÖÕþŠOÊU‚‰&q6_û‰Ùi)Ö,>ôÈŒ(ˆÁ¢°Ül¹™›æØ 6 £;³ìpֿƯ®JS·¸ ÀX ‰÷—K¬pl×dÐdò€4©é;h£¥Ì 6jdœa¶÷cïÀ¥÷ s·ÒŒ^b³““…%¦BjfÉâ.‚n3ÆÍ,çÕJ1*¤ Ñ÷û8©î¹|pŸá 5”ÈŸ¿ÀA'~‰¦œÿÕùΓ ~ÉÔç›zÇkP[ò0r”6ŒÎJHK•e.’lvme—M7A_‚)ŸaÉÝš-2l1hA%AÄx‚ÇwtVØö+ AöÆ‚¿5aZ(œÆí:(Ä÷­ÀïxÖ¾j-kuŽº¤A!u­©Ô· Ìxn5(ù H`Î É ÊFÍòŠ~&âiv,8641dÔe„óOÛÂ`M²ÉËeN6ó¸‘5/e>ŽÃ¼ÆðÉD,îX¥¡Nȴ̱ÃÖ;¿Ì ª¨2<¼f¦ôt SWO“/ŸÀªÊ¸ –´ï‹ÖßÙ×ö¯ l•÷~f<}Û'!F 3!Žˆ¢4C¼š|úÀÈÛÄ/?¦\Jlø³oèŠuúKìCïŽbàmV*È7IÊE×!ÆVÑ®t¯ ˆÚù-®9¨n¼H‰‹ ¢ºaçX_©n: ØýwÐ@žžcŠR¯¬hý‰6m6Ål¥á\¿x¼ 'Ý<ãô«I¿ {e¶`z‰ þì<‰E0 ÞÀ5­v¿Fh¡ƒ±ªøAv°k§JùL£ê[kžñÛÒ=~õv:<()jkýÂÞ,öÕ%ï^`p©º+n0M?ƒ_6Þ‡ƒ¬ß½w¶jìPCeØí,®ø²C má(ht½·±­ã¸g}Ô.©ÐB®ç›LM7$”ù©Ê×qw¦›®q`J¥eÉÁwO¹X\O窴"P­Ç”Ø9fúr0nh®’²MÒhV¡Æh ûå93æÁÇ娥)¡'6d q0ݵQÒq=ŸQÆ_ðëa¨ÈQB&h`j³…¹¬ìÕY ü³ˬR¤Ý@üriÿðnz—9 w]«³Ž}l®çûB²–¶¤P± bruXè@ ;r­ Ô¥«É•ÉŠÜqïxüh ‡`=(È}€Â¥-bqœ~ÑÓœ­Ö†…Î-Á§^Tps+u;å!`R¨Áé,ãúbЦgù(?7Þ|=Ùå0 o7GĔЯã¢Þ%c@hÿª·)½„·~ì<ƨ¯ øñÏøÔt:±FÑBl::Õ*<Úbâši­z!b9Gž+©ÒaMjIá^É•k½üX…ºéü´Œ­dHŸQ¶p9´·‘4?H_p%9ƒƒ’ÄeÌŽ´§II:vºþT¦è¬M„Ú " žD …(k½/7”åaQX¸ƒU ÅXGˆ(!”nÞmër‹A©þ=¢ÿæÒgÅÒzuY5 Ó¨wóµcUWfrËÆO0j}PˆI— xep¢Vq;2ÆÜźV_@fXI6Gm²áÐÓ…õæz5IsMøÎ†ò…­;8tâTo;]$¡K‹Ø‹Ö`ÚU¬ÉÊí¾»pY²ƒëö×BŒÿ fx$¥Éq£dw&JlWF i0 ÞvBº´í’<£*ØT8·4‹á'¾ì‹’N0NŸÄ\]zê]GÑa¢í+³-zKGXö÷D)¤¬£|?òCÈ'>³²ð×Å2;%Ö!œE‡²a”W?’_qsݰä¿hQ'YK¥³µyãKkwmYJcôÔáLßí×ù.Æv¿A±%Q?c=æsL8´œ~GF„JzƒçêwxÇq€[ú09¦}°’‡‹ì0(Wq%¥xiù™S!‘[‡g/£Àx-¶=Âvñ2Á‚ûbâðÀ¯j®õ§XâÁÍìC ð:ºPÝtÁ”gJS~çØfa:©h,yDç©úâ]¤«?Pò~7@TOÜÓòξÞ~%”â(Ý)Iá‚H_ÐR»¼ƒí‰Ï¼å·?úæÕ÷ðßÛú7í¼ûîà̼ðº¿b÷»ûì}ÛÔ“Tî3:Ðàð¢šïþ8üÿÛ®»< êþ·œï'Ü~ç…iЇ*ÕM©ÿÓcÊž”/uÏLæß@xñ~~ªý­JÅš&*0 3¬ëôiw¾9—;†ê¼}J¼Žº*£ÜN)Î9t—8E3‘ÉX$Ê&¹‘~¡».DÖB¾qiÑƢɺM /Í;uŸµmÚiñã·I›"Î8C+îì‹ÉòᎠ:¶ç-[X—¹/²ÄÀ¤—£GCAùå}Ü)t ÖIJW§L4Ñ4Úz’Ért++ëe©eÇëœ?°…}÷Ùž¶m—ŽÇÒN¥·—9[¦ÝkË8‹?ôe ÕXÁ´Ø®h¹A;ZbN/윽ƒù²-õXTn&Ü1f…—–æ*`äoÒttކŒùSõ©±à¸>ÉWŒd¡zçÞ¯ÿĵäÛËU¨Ìæ»ùNõ2JçV+]ÜM-‰áˆ˜²Çшeî¯ËHLâa ò‡à—o§fÞ‰ï¸Ï–ô"0BÁÊX…Hñe錿œ¡/F‘Œø®Ëˆ›¸üñ¨ùã°ü…T2}’År•wÅ»TBä匼² ö¥:É£ln+²¥óó¸‹m¡LïeÙ^¿§Ãaâ—iÙ€ŽÊmæd[ÍœPI­sµpˆL5?K•„¼¬m?ê ¼»`Ë'¡#2·Õ+µáø‡Z=@’÷¢(^%_€'ç3cÄAA—•⦬S¶s[ág@YÆœêj˦±|x»^|­ÈÝç6*/“ÿ¶²ßSW6TÃ¥§v<·àæ¦sƒÂ¢y±£ngd®b“Ñ(H×"¨7ÁÉ91d··Óã»äÞ’ÖÂY ‡d<ÞN˜6åzv·Å:¦1hãm_íJÂ%Ä{RlË7]òù“²ÌyWÏßí|’ÆËuï£oê¢Ôø!Òv·-(êЦ¡•Fú¹_Æ´oÂfr©sz¡Ÿ"ÄUÚ»› ÷óó!m¥^ΧV\LUÓœÑ Ýš…®Ú,‰hȇéSAû|NÑbôa€9¿Hò¬‚H¸íNð\ã¡J\œ,Q÷ëœ[Ê”Œ8†%3Ì…V*37yÙÉ¿pÅÛu!ÈZ(}‘Ÿò½ûµpÉ+VŸ)D*Æw­Q³ÇÆåêó+&¥VÒw$/[q~Z[Ò{ÛìŸÖñ¾Ø)ar\Iõäa‡¢‰ÄwþV¢Ö¬ãkÞæÓ‡&ª¶œñÍ^óèŸv󆲦­ÇŸ>Uߌ¸ëÏ&v:Ú‡°úèx»9|#hïð·>º4QDdB÷øP‡“ËíšÁSAM„Ì£I?‚Èpdçð6ò“gËP <ô¨$Pð>l±/†¨KÙY¡«28=€üÙ1»‰[$0bkÌ2zqŸÄWºãZm5y°U§cÍõȆfîhÃç±I›}sz¡]5@'›‰2Ú>ihcÍ¡½¿=zÚR~ý=‘óþØÒù+ØVqËH ‰14ÿ bã&}óÈxFž¬Ìë÷ Ô2¼u´I¹ÃÐLo<´2™n°;1ÙÔ¾Ôî4 M9iº‡Ùõz³òñ[eX„·_ëö—*¨,'OÚ®\ a{Eådº„u&‰fS Á­x?P]7(C@­daÜj-‘ôëöPèðÜÏtL!µxå–;^“N(Iï"tÓyÓ«É+L%WÃó a޽¡Ÿ¨þ‹„K¤^[©¤¥T`:âOÆ»R…§š¥Ë’’Õ¦r7ÀhLŽ]ƒq½'ó˜BÒè{ïìÌ?íŸ.¿?ïØ·ƒ`‹‘úï%Qã²e×ÕǬ$Qx¯±dgÇ>̃Ƽ+'ONü ©è‹¦¬#jÙýAU™s˜Ð%º]àÂŒ¼:Xë#%ˆ°”pŒ×ßV–J¥ö_‚§QÈgz> Éå¡Ç6’ˆ;²öå,{xMbF¯?Ĥ­)n'‹º~ð Æ…–§X[H1Õµ •ð¿˜~ìR¼¨rJ%tËgz©©T; ¾¡cÝé°YÍÝ [Y˜ËñáÏŽd´™¡˜¥"p„•)ÁÓWÁ̦(&èûÊLöÐNJÎ`bEÎãÏE|ZY?©¤°pX_ÚK©ó¨]ˆl?\UyïgYOQ\ãäÛDŽ€X$˜ÈAýKr¿*bOj5¬wso6ˆ5“ß±Q9a—5Ä„Y˜seœ'1gÌÄà¯TRz#Y‚h´Ì·•CÅ'jÔ¾|Á?¶Ëi¨0G"V%ùÿ<= ;NâØÊ±C0™÷ØP'2¹XÐÓ:ųdÖ¨^õ®Ý›þ݉Ãò.™mµµ‡‰4Õ¨ÅJJEÆÌ(q­É¢­!/3 §-=~¹êLÆ1Ëex29l3x+ïE¾›Éœ®-éxAóññn k`‰›©¤’ŸEMÙ7—Z®¨³ƒ†ë¤ÒüzMÚôZÁPkWÄÅÌ’“Ï> µ žQgè-š¶\+¯¡ ­I§1¶œL÷ôà§—Ò°%”e…d˜¶é½q“»Â¹$[Ô‹Ñž$Ѥ4$³v5~JY²(HýR¼ÿÐB¥D彟bJÙê‰U ¼ôï˜uÕ,†.ج¤a(•Ñ]ו4ƒ þ[Í_0¹Ì?Z…Ì€q +ֿǨYr¯V‚q-´VÖ ÖªÔsE±GÔË­QÓšùY5s’Vbóâ$rIµ4“‘†èrñB<Êûš·±ˆÌçì¥eª1ýŒ5ÿz¾~4aàvÿÕF™ûê€2H®¸Oß³þž!6ØÙKcÅŒ™ÝœÛžÙ¤$~ôªj‰üP ŒL}B‘!!éþhêÂàcÝ á¿û¼Ì=s¾fïó2e£I`RÓÕj,MPÒ¶jî£gý7‘“<ßî´ç˜7k©|©é‚ÛÇ/ÙRÁc R“dvT\<è(ï3„dÐ!:­!GÔý‡Lè:ñdáY¸ÜRql¸¦Œù8)-¿nz†,*YsÊ ûkÃàL ¯`^ðû<åvÔôËÎçÑ­69‹d??Ù´2Oü/ûuyíø“gG”•ôô’|S!×úÅŽ5ÚiæÚ#Yè ‘µ/CÌÓZ—Îúw5YT/Mj ßrzÏ›‰û¢-Y\ö†ÚtAmεjGí“mYú•¥¯Z-ߊóÐÐ{ªÓ\§Èx#áo§Ê{Ó}QsQE+®;6Âeþ~ìT:[çnÛ±ñàõŒ”ÚZÀË(†ø˜vÎ |Ç>c‡lG‰…1êVæe3ó{%ýçaÍØ[ŠÁݤºÎËe¨)_›¥²OòØlh±í¶ïz­'ó—D5ºø¸K¬O½E.t³Å³Ãó×þÅ}%ŸIïÔÆ£#<”¿Ô5ˆJ|j"1“߉xïr?D¸•~ 1ôˆjħS¦9ò#Xµºù òŸÒ“~°m ¿ÕIºÃpÀác¸vö(Qξ˜ÇÒf±ÀÓ‹ÖÈÏ-ïûû*óB)Jé°V°RšQøèøu¬=!UÎf™D–;–ש7²±tˆóíùgUwýaÈ$-ÝŠm<È›PKèÉÀ3œrÄ7¢«\¨I5*UI}OVˆªú£o¿-uâ™Ü^¯=æÝ>€!‘§Ò¨…ò£ÕÍGƒ¯%½ $¸æ[˜@ƒfý_‡ê»åÛè;ÒqêV¡¡SzÍ‘µ¡6÷Š[¼S›ŠüÂkÖWlb®ƒÂ3Y+,…ékïüüEn죳:þtf 1Ô2²¤í ‡Ë˜<þÅI þñ0ù~²´ìeÜIŸ3R¾ eGžkè˜{"dZÝæ&®ï¾Â›{ÙAÇ™4 {ÄÃ?˜vb°ÍT-¶·€ÁÄù§_9<Â4«nÕ9¶™—AUAN*ÒZ^Ïñb˜…üO¸L‹1¾0½#§|¤RN¢Ü\•säsЪì’à…ûe¸é»‰Yò– GÑÐ0,Æ/úcÕ¹[LR~¸õ§h³+#žQ3ª~òÒù–YzRJ²‘]â6âÀ Žœ*šmúT5~¦ÈŠ›ôœ"‡²®â>Eø‡í‰Ó $¡=Ìø-ÍA’JÂqp*¤?ÿk"ûì'Ç<¯[(_x<ËÏ8ZšPà¸4ù”ádý1xìj“¡êœä0öxzf¤Öß/ƒjXAÖ-T5 ¹#kVÓÒ‡€ã?¸Þü9«"r‹WsȰ*­ŸÖXÿ+VÝS¬FIé…‰ä‹V÷˜aQTÚBÑZ;*Á2T fÞŸw˜ÀÒ¡ÒZ«ÁãgBìôT7ÇÓa hþ¸Í>Ù4È„å{Xæº4Œ0$†"‚î˜2±Òã?áâÔÌ K[Ø‹ˆE¹Uú-óPi ã¥F,Jâ¼_›4ŒÛv̯`÷Ä —™ý#ƒ„Ç=ÝŸûQ"*W£UgÎöy?jÉ×m{çöñî’yˆœäm£ªm3¨P#â3“áó{ª°®ÍxpéfÅça•§„™Ï¡’–z@eºZ/&­Æ»Sε>˜ýJÃþ7óãGµHI¯[^äÃþéÐCÇͧ/‹Ú¶Lë§Õ7•¨Öösþ[3C‘ªõÒ‚\Òj?Љ‘`;·'ßZ¹£]P)§¿ÏqZ²²"ÅCEp„ô·ò“6W7%œMÒþv±oò£Ëh?Iœµ"Ž3æÖ+|¸œ˜†Ã§-â°VôTm¯6DåcÝAƒ8:‚â,PuÁp`«­n±¦ ù³Êð,ý…wK3ÆQ„Eê¢úêáŠÈwBd°«íEv#³!ŸµÎæâ¾ÖûÏûÖê´klŽÛí9øïþÈ™Æäé§ÜlèŽÝ1(@ù#ò:Æ^3EG¯“'PŒ¤§° 4T¤±bûõwÍì²³ÕÁ‰Ø ÄÉO!'A’N(g®ÕU¦Ø¼4'c (X,Â8œ*) ¨ófÁ]PšÄ(P<(À¨éB¨YÀ‚3g^Íe-Â%ë(œ€Kr…¾<ø$1u:$Ò#¡ÿX@e!$­Q¡BÆèÕ(©*˜( ?Ö¼"VZê„Ѫ1®T‰øP1†ß”•zÖœ,%Ä ÀtQX&‘œ¿ÀHp¤.š„ÚíU¨uU¤ÙõˆU¦uð/ >ûüDîôuÛ¢Qˆ±p$+úPoûDºÚ @~pÞ }íþä/‘¡jª“ˆí$,X .…_†@fŽ‘ë»4É}@!QÊb˜~î´ ò;6(lŒž¤ï×° xVôR=…ÅPñƒ¨L@ýÅNg©j |1@g˜^Õs5ªZ¬ÔúXq@ U{8_Ÿ³¼,Vº$×¾ÉIº ólVÛ$! l­‰4‰@Ä•,²aå‘hŽÃAÈpˆ$«‰¾4Ÿè+Q"T”stH\BªÖÌ­òˆTa1jIêÝ"ýëÖÑR /™eªÍ/¼£¸QM¢Æ ¬ë*×bXUIˆ"G`«îTAÅmªYÓsÆóßxrä³ìÝXª ‚((v•Ût€ÑBc •·Î–¬T5"~¬žp6¶¸åWÒÜå f³ X<!~`_mÂÎr¿1{f÷$¸k×DµÑ+*,‡%(û—€N&H¯ ÍmFÆòÞ©eÁ…l’^ÑKˆ„i–“µ ¿Û°,XOàØËïëÜŸ»­A­M’Û¾\ûn ‰šúlK}oì{øê‰Gƒå¡Ì¼óqÐ=Í+ï௕F/¾g—JCý¿h/e“•:—/×µëŒêf1*å«U¤V³[IHRJùÐýma–'C 1 ¦ùÖð˜ê&¬t3ræú0FnGÐôb&¸6xÝ(?˺wst„ê°(ä™ú÷´îŸsp0(ïù1†=pB#f[íæé/G‡Í;` 0ÓOÀ¹B?ûNÈö Ä7‚³¸¹°ÖÃÔŒwb;´“ÔþøTq+ÀÝ]{6»P»oãVÕȇ¸nIŸ¯;­çè$,8Ìñbh(nÃWpqÜi%’'gkfî#²MW‰û¾)UÒîÊÒûžd§Aä)«§Ý9½ŽæâæIqÍhÅ÷ÆÉýy–³¸¯íh̶Úo»ç1½©¾G³}Êw)±öÜž~£Ï²~fM¬í7ïS7è®ùá–ð1…cÌ^[pËæêÄ="ï¬U1j0ŠÑ”UôíL§§´“øGW­"ˆ·š¯ r¹ÿS·‡ožlö.gu˘#ÆJAƒÚ¨ID#ÍjáZÈm™ŽÐªŠKfæwµ9þlçŸ,Ãâò ÔøþÖCÛ¡wåÏ+œ*f–͈ì a¦ºFC¼AòMw@Ï÷¿DÁö+éÿ"M[ >É./3eW©ÈD÷ YŒ *âoC+ܹå1ì„äœ`*Í ±ª”™1<Ó8¼^ÊvM™²ñxm¿"˜”µ[¬>Hý“Fv:] ¡ñÍ` ÜaéwÂîx¯;A¾´í¢|ç+Ò° J5«~£PùH~0¦+ÚE¬lå—ܯ™¤&­EýC©,¶ò ´÷ab¥Á’®ìêËÓT/t:TЙ¸ž?\·…é|˜bÓ6µXw¾È=F0D­‹(M=Õ§;°öPÞ &ÑEáW…§/GÑZZÚlú:GSÉØ§ènÚö¹NÊÑØQ?v~?MÌíîù?:&Ì…ô² ™è-†Üî4?å³£õ{Ï‹— #_V+J2Üzá‹þÿ¾bÇ‚, M VžÆ÷ÌC¤g Y–EଵÂcƒçU×N1†>û—i%Û`åÜ-ªšç{úIWôqQiˆV˵'ð×|Ÿè#UåB¾ÿ¯¿¦–jGð¼ïµjå— ÆǬkU6sM8a¹Ô¶”\,#€–߆a΃•ÁÏ®o ÿjæT-VyÆóš'ýuÂÑIpŸ¹iˆó„;A…¥'r[þIJ5·k]‘wrzª_Ã@yXð•ïÖ”ë}"Ê*š ÙIØÿø±¢ý ¦n¤P5§üZN=(oùÏYñ ÿç;óä]¦‰iˆ!ü§*å—%šÐmW*i„TtTi§*• »²ªðïÁÕÃå#s¼€ë'«¶o»-¬º^Ì΢þEL1ÿßGØ=¥ª½l%Ÿ»¢Èî3"#æŠÞ_*§?8Ø­—à"V©ûÞÕ¼HV-w‹Iö*û\lÝò=•QF×mæ®DE·Þe;ŠÝÕÂAP·OþZ€Â«‰–íØ¬¼Š:Ò/§ç䋸íÜ>,ì¦sÛ^‘Ê«øÅϥǹ9±×‡ÀG¤r;#ލ4ï­”BDîD–å)c…„t°VŸv@æ¼Â÷šõ<¯Ý“s¢ï6ÈBð$¤¡j $n ˜ï ISR+O湊ç1SG;·ºFÅŒ 6…È?awN˜ZBc-<ꇀìeÅò¦³Z{r¼‘3‡¾Ùº6¥‡4Eµ‚ˆ@äYÑt=ïì §Ì“zkñ»–JÝ+*uwd'9Üé9SrCVÏ£‹;ÄÚ%0m«õ/ÈX™œé­Yä™”4±OQ½h’Õ¿®¾•BÂeï ÷Nï= &Á_-§0ùeä*¹ x›¤¥X¿iš]ÇÇÂVßc©‘zùØøU2à;$hå‘êÙ;Jà8‹æóÏ$*šXð&J;v´<í¤‚!˜UåÊÖ%­Šl¹mÅãU¬È‚5N‰Dø|{ù¬êb5'~èD f.Â!’~y×Ïbvm»³–ŽŒa¬ðÄpG± ±Fci >Ýûs!ª–Ýx©Å¤Ñ›²-·pÏ.¾9eT_´*°sW3€sAœKq»â§ÞE<óŸªAÃÉqD 0|UßÓ®àÅø…ú¾á,Ožšm¯¥§º´]À Pey]e?:õäj5i¢NZÚu×®@-3õŸåTžÝëüì;Öj3Ž^•!NHkë MàÊ‹¸YG0]=F£c—«_¸íï”mÝŸ-í<-=VDô[ÿ~@âY¢]‹¡;,W5-F~¤sÎÖÍŸÖj[T]̃ú£N«z\ø  ¾¼û–àq¾Ò­Ävã…Æd;ir?ê,ádyg‰8»á æ§kY'÷ ©–;Ó×ÁÚÙJâ¾Â'Ó¹ƒr×Ú M~š/"þÄ„y§VÙiÞvÛžNþ?¯ÁÁ+u-¦C9¦Bïw 1x¿DPEÍ$?¸§oïíéüÝ'›D_ù¾GGÕàežâí*t€ –‰#/yñZŠ‹†–4âÿ®ãþ·õ\Ã¦ÏÆ×xUn°>R7+½a§zÜ€{ºoy{Aé*WX„)UÇØô*—JVüf”¼è"xP‘”´1V=¶U­‹‘.aŸ#ôeltlºõnôKò½€ëþ*P뻺,$Òߢ{n"/;êÝï\þ3l°6üFªÊΆ¿:WŽ‚>¿¤ú£0FçVJ5 AW™Ç¥z(]gg¿îì3±ÿ 쎖Øê¯*«. •îT±tÙ °ú—Y¶ãt6«§K^ä‚)©4-kÛN²tÚ/d­5݈oÐÈ}í?ÿàR¼¨;<[†•§ÓâèîÑ]±:®(òÐϦ[™5‡E­j?~Jà1Ž¥Œªžî»sVOæcôÓ ÜjÛg·RÑÎR[, pà”x×eˆlRœY‡ }LÝ’bTUuÛ{Zd8‡ˆ5Œ:é›àøö ¶ÞO§Œ÷`Þ1 Þü·‚Ù ìxäÁè 5‹ÐYÁºíÎ{Ïjy„Wåhƒõ—=%Ûµª~„Fà&¼—Èî+¿¼a¾5_4>aàdœ<’/>öÇ#‹Ü²Ÿz ¢Ò‘ϳ|KŒ¸­¼ýùE&ÂÈê¼ç¤ìh21rf9L‰3|Y7rVpûÁtˆ âIÓ&Úg1_x8}Ǽ/zõ'B?v²¸W7¢u;AÖ¢…×úÝÍÒÙu!Ð&g Ð"ÖÁ×ûËóbK¾Ž]x.Ö³:¹ºÅÚ |î'¾ª=óÚð3Ö«ÏBå½à-„S`’rFÉLzðsí+0Ú²oYñK†¤./J÷é ¤/¤ ö…-§%ùÍw—ÁßždÈuP:7ë=z¹Å9ëÝ€„Í÷K˜®•£Sè†ä¡ç’P‡‹|JøÀæ=7z¿f'Á…bXçÕ£­]u&ú—,c'$7Þ/kX llò—.GÄ]…ò–T®Ëî½T”ö¡Ž­ …“òž½¸DͦÑJN ß«et)û2?_Õéõ/j“®bM~x¨{'ýŒIQ(ëÙlt“üÈÔ*úŽ/‘-çKþ°HX¬ka™VAŒ*Hì˜Ç…Ô~¨sP¥«Xþ¨Ð¥2/îV_ÊÄ.ÉY|'Öâ‚ èË˼»F‘è.´þÇxƒjP€úŸÀjÌîþŒüÛŸƒ/ BRL^°Þg4-ú»@)9B)õ¯«“ÊdšÚ+J¹.Ðbç’´ræ#æRPÀà¢3Ø‹£Z„xü\¤] W¦ÚÕjk­^àB Ü@ŠàÓéœ/—­†å©âoÿGp T|¹iëî'.üQÄ<ܺ!ìF‰gØqë7GËRdµöd¯ÄØ"m.¹Ú'vìvq2°Ì` Ý’•µö$Òl>ì\°tÇôÍ:†´çKÿÑ’×=-ú|Ž­µ¨îI<Ò gezÑêàïˆG¡-‘òË9ÙËæH1˜µç´éf­äÊáE˜ ƒN÷¡AÞÊ w å Û·¯àƒt¦¿»fÒÁî­aI±C@-”M};µ ãИ~úÅ<ý'çR_€°žfQˬ¦ÊÜ;”ÖG#µ…h,Z ›† %’¥h5lÿØöæ8Uc5Lxµ _úy—Z"êµQL9ˆÑ¹¬ôŠbâ±Î¡[iH=€ÐÚ¤ÒÛ~ÉŽw“zî<º„§»ÜÛÈO:´ØŸŸ?ŒŽ%ªEûM–¯çHs¿'ae£/¦.Xç*§žÉ%"ž¼Ìd÷c0¦ÜqEÈ’½èó(¤{naê{3rÒÑ‹Bg’'Ór†Ÿn$Ãwnwh øõRÂ2lòhÅ5zC“;!Z0‘¥¤­…òƒ7 f"AÈw±—ÃFσΓ&oã3ƒO³«Ä^Z§‚@Âû–bñ¿ÓäüpÒjÒi¤šCM \'òùÖxˆ£^ù|Êê}ibl–gŒ…5¨ÌŠQ1›Çuìgœ;(?× <…ö~‰ùžÙmʪ,Î]ì8Ñt=ÇžnüÏæ {ʰlõÓHƒ\•À*VL7ãÆYF~1·"íÁáÚ/øp«}Ý­òbÿ ¯6-[~W ¨‹RJ$þ•P)Ž}×â‹ó  7—©ÝuYjŸyÊÐÙÎÒÓùÈÐætSrœÌ)eï{Ž¿ÇmzHæ áî]å/Z{ƒé7a>¡žÈgŸ:ä< 9ŽÉẓè|¿ðȦ/²<”Ô«I¼äÁœˆX>^5èë^S>å+îhtPj|Z{`Lu‰gkÛHμ>Ú)^ÕÝVRÇŸ0–†UøBëSOX üIDóTÍ‚+9ãéa ¢3€Zh&6wâ½ó¶“h­«•ïIPKz‰[¥0K½òQ§>’ŽbGv"à†>\ûè¼AØk2+ì—3ÇuæÍ¼xKÄ~Òî*¦åa;ÿDë#!• âvß~޶÷ÙµGocqrêY"@T)£ml0ÇæÇ;Þ®Åé|¸¡Œ¦¦7lAy¡æëÞöl/Ô»–uÿÁ}è4»~ô7m‘a?xöÃP<ƒ „R_óò ªZh›EÑÿaúºS; œä6ßR“¼NëBë·H¹³ÒÌO®ûÂ\Mþ}˜úèÊÕMk×ayÐ&IºLäú»øfº×ÇC7'î9Á?Ì ÆÅI。çÐø¤A>hòгêWò<·–Š Ó€-‡ÑâñÛ>Þsz1Z‹Ãv/€W;œ{¶¨U×9!83þõ–4»AÍÐ…H®[R?U¤ˆÄ‚Éi¶.ŒO#åf~PAëßb0#åüS\Š™Lhö9ž©^µI£Zdš¿×d‡%å‰gMHÚ´¾9lË»@$Ô°lüý_©Šð Iº'šÇ¥jRç€Çc¥»wÓ¡4¢ëË"‡ oØÊ"V=k'Ô¬a•ýQ•_Ý*kS^à ‹S·¡ç(@•‘ |†K-„¹Í•¾:ÉÇ®M>žäÙ¯qÒ„øe?™¿)aã‰J²?ͤZÖhºXy¸‹¤ŽŠÝ‚ÁB,U“ß߿ѡê Aä,•Ú'è ¸}A÷ðè.*rªiâ‚ :?ú†„ñä,‹£FÅùq@°-6ø4Èß‘E> Ç–ÿ҄̉xt€¶¦y¨—IM,$òÝwV.o>ñÃĪ™éÿéjf1îi³³« &ŹËÊN 6},¸"¯ùâõª2½ ªÕ¡­¸Fcä!¨[Ì#–(ݽŽ38½HèG°»@.èÉÉYh–תâÜhNÏ&öºþ¥Ç”›*l–+ÈN`=s¬¼¸ã÷(† ÅÅ«œ7*ßTŒ:k6åÉ wÕÓÎÁTYÈQ/ëe k1¥Šš/w õƒ(‰B[‰¥7‘Xí'ÁÌ“BxÊ¡‡*àéåÜ™áá@»s1Át+Àϼs¿ ÿ¢Í>'ÖÞ˜¿JªíŒ³žMå/ÑK}2¤ñZ´­q£ÂzM¢fÓ±Wò³(׳vw…±?iÎY~ÞblÅeøÙÏ'ZŒ[~ã1É4¡¸K¼ùK㞉eó’CÏ|×ëŸfÒÂ#ܸ;áA²dóìÏè”V*û¢Ð{HRñãâIJ‡Ž¤’R1t¢c-Íì&Qº…¯Çw\|“7Õf±T—®³Òa*ˆ¥Û»Ü—ï½Cù\2j™áŸš·2#“8ØiÒDé%g" F¹Ô"Kh@<í*™$DNZ‰›Á˜y»¯ rzij? w}x5ë.íH+o"‰ö^·Of™Å‡™%šxÿ:¸4â¼½+;ðžþUÿ$‡$¨›ýL Ê¡™õç£ „×½ «½@AÚ –%þ½k›Û°dèÝPá7dYórN×J•ñ OSàÊáúà)k¼ 9}µgQ¬ö <ª¾2¨VP …LóñPbŽ{Ò#e%ÐaAݪ&©ÎûMµjÖÓlãd_T )Ðòȵ+2$¯¨¨ŒÑd–“òHæÑ—å¬s>Y>Ôú º¥6µº³cßâ$ËÜ(2t!n ××…GƒWLãE½0á÷±3„Z2S̘_ºš OÍÉ4bý|íÔ*Ržü8È^cmP{ujk(>DõE‚2»T 椈Ûñµ_‘p’ÉÌɳ1[ÖÎõO¡*í»À)‹ˆ9™…ËWh’½LM´Ü¥²©©h¾˜ºcõè ¤2e”ÖHhÇzºão~gwÇÖEnhŠªÖŸÀ¢+Ov°“ y§UW í›ÆP2œªÐ1r¿† Š+/Gð,-{æÏ‹9"¬^›Ù£ÖÆÍÑ™ÉaÚE; ž¯@BàB$dK»g½ÊœSPÈPçØ¥"‚µqË7¼¦‘ŸéÑQ –|Æ7¿Î(¿ûÕ]4J©jÖZM„ÿï¿ÏPNÕSokYÐ2ëù_Œçë÷éIÿ2Ìm"AÏ]¨ |nqs¤»h6K•‹{qºÑ¯á„̰u™1´Î~Òm+Hv¸T¡ ”XØ—-(š _ã >ÝÆÛƒ^˜èä[›¦†Y4 PwÚíóã2kL±ƒ¤7ÉŒ~™þ šJÇ43Ó˜Õ–Hí8£üâ"ðÚr…ÐÉ×òhFþ§N.¨&ÁÞOåÐ5ƒ"ç¼1ÝRò³Òƒu[Lö°-€®¹$únœÑ¦µ)ƒþ¡+ ,ZÇǯ-Û–@íro¡,˜Eàe†H¦õæÖ\–0}“MàOÐÞ&%M†Ù;Ò,ÞbTë; 4W Næ/ÈÆN"óÿ¬º-e$µ›òAÎÏhQ=õ(.Ô™1 ”ò4ò_g51 B³ sôýXó»CsE2œÒX0WC+Çì…0ýfÓ±z µ_iK•НÇ!¿A£uÈEÔtùBܱîëJ4]ë”gÕu~žOoœð§æ3œYEÙÜW’))Tö Å%6|›sG“1’ýûé”þÁYd™a…è‰ u¼ÃdÇIWö2êl=<ÆBYc1 ⢠J¿t+i£x“lùÆŠ˜ k•Ý/XïñW5Ò:1ÚF øKû ÕqÉ‚Ø/B¬Vda²xÚÒ°‹:OÜyöëÜ.+†i¥'Ò}C…ìuèËRÏÃÀ£˜I®8:‰™˜TKWIX ¢Lßôè€?II­Q}Ù aÂutù#TÕwóN’I° »õîîMÖŽá0XuWµ®EÔd›)ß®· )㳂b¨ø÷‚B·;™ªˆF¼·sKÔúý¦ŸHû‹²]ÏÁ²kz®óé!Xû&±SØ1vºdª›@6Hšä§“^¥§øfAÔrN Ôqg~Zú9˜ õ5Cúþ ‡#Ó‰ øºn¿cÜŽ>|~0vº÷ÜNì"c¿o«=ÏÿLïMþñï>=Ã5E<ŽøRêRMJå|ÂEKâaÓï¶ÈäaˆòÀ'W6Ú ÎèUEÃÞQ™ŒQÜ—¡Äbi1‹ÿy •àÝÈ#CŽ3Á`²hþj¶ÖA©ô>y‹SÑ=Лè w óYNáyÒK»~ø¼ý0a¤k†ð¯ç³ú/(Þv"]€™ÈÓ˜…E°Ç^ä\ïç þ–Me•ÙUÝØC•½´ù©ovݬŽ|¸œÇiC‡yˆO³´¯UîýäÃz¾½û™¼ˆ4Q®­JÇØÔ±ÿp¾ê^w¦¬â[U#9 CÁ¢Â˜7É é—.Ä1N4ñ~¶;WMô-ºï ]3„ª[9–R_¥òhÙ²ü\L…í-Mn*Ãë*äJq(žÍ2Õåá0Œ–Õ›Za åÇ!8îÙ“/ÝG"7—®4QO<úCó¸mh,M•øl»tWDœËÐ^fã·Ê·gáåq‡Ü¸ÿ"éÂÁì0”\ãeày5!v ŠQ…¶`|5Ì7›~d\²)ÿÚ¹±úlŠÊ}#ÄoÔWJkWÍ¿­¤ä­¿ ߀9Ôñ¿:…ãUú¡Ó4QÎøZæaÕÈ¿ÝöN ’™—þÑ0$åMé[Í®îBÛ†k(5 ܪ½6{ï™ö‚ÿêÑá"7¿—òŸST88ýœÛÓ‚šnž{l°@Ö¿‹î5·: $Š›Í{Å—„’ž>锡þü :ãÜ›ž¯T<ÍÜ‘·/3Åx¦ÌP„nÌï»~éMu²™LøøÜ…EÄþ©‡´¿ž6¯_ ‡Ìx¶r%°(c8£ÌÝÉá¡L5¾ùY3Ò–6n8žÏxŸ÷>õNjœzâš)2°ú×6a² ~½Üç^øä×™0Mõ¿žwëÔ¥~°™ukl@uò—È„Q(“­ºÔåk Ù"b“Ã2„g•×DÜZKe¼ëÚ÷/b鹸e÷A ‹òÄ®n¾êè›Í ¿qéÑ8äz·A¨\õue–z›qH&÷*maÔš9z—!—zqùå‹éK7’ ‹3ùçiÁlýöå±ßÞÓ:.Óz-ĬÜÕ.A_ؽ>_5} Enë‹“C5ÈW™‡$ Yý7KPÃËãªRl }ó÷@Í<9ß`Jì:OA«™®"sïPÔ,öžË«—²±H¬¿^?„¿¶oã]<«„Ö2TF3nUà±ê VIÖØX~9`b/bA‹Á²Šú&[$ý{Üf7×~• ÏUßÌñÈ€ÍVÞògáÕ,?Ë g®óä{òø›Ý>ÄÜçÛƒ¼kŸ7@ƒÂ…o2 à)òâR"@¶HªVÈYÛªù$!_¶UåClÛ«ö*{ËgTØÏ]÷ž`øfdÌŒàÏé}ß¼ï<ófîÌeζ¬ÚKŽÇh*#mÐÅ¡À’§ªkºf¬ÉŸi¨ÿJl[„ Á¶PQ@T³Aœ²0÷ ‹‹"H!Bˆ¤ÓQºy3ü¼(ž  P¨ ‚aaJf‰À  ˆA((! ƒHœ¬„ƒAT ƒNÙhHJê8BH|•'Ê`",ÑÒ‡…Ð $Ò‘  )TøivIÂå0„©h¿ 8q¤\K2!ƒ.O?Iü6è{ˆ8THI„&"Ôñ’¤?aR= æVÑḥ œ55€)D)n:$ßZ,‡tezþšïD…(FŠ@dìmtŽVÃEÎB¼/EjĆ-¡Ê)£X`™ji°^p-ˆ{âº$–Â$ó‰VU@\'§µõôq³çYê圢¬–2¨`kVö Æa}&a20C1»ø|‡k(èÔB~¼@Z£¯ÖBN±¹Õ(I*LºœÑ|tµá’‡2€ATfz^® B˜¢q™—[RŽô·öÈq‡è5Tm³UÆ{ °VÇà@³ê®éoáBõš‹ — ö”k9êŸS¶vŠ­z¶—Ûš;ŠU h†‹(Ö\ ùPu£ ÒvæÊJµÞðl’¿p1y@n‚l©F Né SŒÄH3|ìX–ë £dV^K/Sjµ,§u¤c[ “°ù|•EÑ7U{󲡈 ”)¸…•ÚÕ°gVLÜ¢Ö<ôRý+NDãYÿj9þ3ï¹kÞŸ~ØÖS/ëy¡z½nOÜÕ…OC«99ëìç§6'ͻįèõ³µ“³o›W_Õª,§;µÉ JÅ"•®UAD§)›7<¾ó5…×J„èM,']¤yÂNÜÏT°òâ~3ô¢vµÖpB÷µZÕ ð…KXV¥7ãñ ±p" Øé—{v”û-Å˱Ÿ³˜·Å¸l¢Z’¸]„7Õ$¯˜­~ ¦×2œo ݸP éDIEýúýÁÏ‚ÙêrNª9 Ùô(+Zú0¶"Za“°ÙµôCihü€T•»”R¶lÔÉXUøê»Ñ'„ÿñ3÷r‰[3]ãÁ‰'”u±ZR€pWŒé/H¦ÀRDÚêç{egÇf[‡yBžÝ0È ‡E^ÿÑEÞç;œpŸ=Aúú/S¥åÚ¾¿³ö±2ÜT ^”S(mï<ÌæÿpŸ*ט1×6B9¨´­ÿü7¯FP€Ý!„–r©8ÕªNs>ÎæêYEx5€ÄæÑ $Z‘  ³¾FÜû•‚<2ÝîýªX()“¢ïJà RKwqàð±ÆìcP«3XG ñ¯€èÚá2˜Û¸U““OU@`QPÝ]U»ZI ¦Æ„ç…umÙºÒê«Z B‰6€u)ú”¢×ËŒAµÛIª j¶-1aÆ–µô¥Ž¢b)_Ñ,´ŽóÛ ^jt°‹ 7¼! ±PJ m ×úpoJ 'ê–Š”`‚hÕÐ-ÍõŠß\^êáVóG7ÁĶ«<Bè‚I=Ò:YTã ÛdS(&-®,ÀvXä,Â)À^Ëì4 f±3¦ wÔý@ ˆÃìKƒŠE’ÖØ©U+2ôÕHh×tW¶JpÏØ}ˆ(ûÞО…«Ýũހ…°¾½ 4K·®õ]?•Ž)½]+×Aöÿ—„l8-˜+¼Q›·š¤ ý†À‰„’£oÏmßÏŒPqdÇrÌsÚver3D¿ôÿ±L÷£¾ßzâ/Gñº}iëCu%·‘ »l’-±c46Ýt9o¼.M×EUÐf”!›a¦“·ï¶¿ÅkN$³qCÙ¹vçz’Á•‚Y \8”Øb[‚ô|”á^†>¾mmúxðsNB[  ÒpIè«©5®z‡FXo„0£ÁLbÓV„™XÑ5ÛeÛŠŸÖå¦8iýÁ-І|K²­ð|uc¶0]zéPö‹Üâ•bʌ٬н÷ßX° |çp_ º/yÏÞàú†-®ˆð¢ß¯¹D ))'Ž(ÜHïm¤¤Eñ½?Q_äåâSWú¦é®ö†¾zù…Éæ!g‡Ôœ² ø¿×]:‹«úØ÷ÕÍÑJª3½¸,ÂgÈe3êþaÜãW\2P}›tfÿJ¯¥'Œó´E´Óö«J¡ÉOm#ŒÊÂ+ÙÖê­Ñs–ƒî«ŸYwf,+0îÂ[lSMs ›òÀéy率„Rž£ü¶Së7„ðdR§±$t/xU [)'§àš…íci2zy xí™7>òèPE­÷÷r ·ÈÛÔ\›Q'€=¶LèfsáÕ³zUë [¿Ýصú¦e/#\5+Š’x¿Ñø :Á»–¿’Ø.¥¤9ÆÔ®ycý´iÔñ½‡'%¥¬<Œ°¢»H.+g;mk¹õ@`ǤùäÎbKËèt'¦D“¢ÖЙmëNÉ{îŒ%´@ù5»@î–-© ³×ü$Lµîî¯Sn¡Â C J忍Œ=(@H)Ÿ0Áø]¿Ñ¿`õ`mlömŽTD~¦îÊ•³ šß/xàeûÜÄú8â°“â´êtµ3Ú½;S¿k»6ƒûB†ïݯ]|ѦµÖàÁü_¼ÿôù_áµzV6¼.ઠыÆPõÓe¡þqZ¿bRÆé4eÀ‡JûÓNs—¦ÜB`üöóï8>š¹pJ0X-p4v*ÑPtY S¥Ê#!A¡"Qº†Gà Û4lõe€ÐÎWw8a?¦Q=ÖoSB!•Cd›µ†ô)tÜý.ÿP­Êð¢“6˜t¸’Ný9ïÎl×p-Ʀ” 9r‡–jU=¾X°Sݜ֮H.súb¡A.«ÞÓ=‹N¾u‚û‡¿¥(1ä:\;ŧÝT|Xps0n»—èO[«å¿±T¾ÁRÜ1Š-Åñ(º‰dçdQ˜{«®(ñÔ´Uƒ‡Ùqá©\kb*]xÚȬL _)œ¼)©w ÊMŠ ?¥Ô CòŠüÚÂ[­«‡ë{Á$y4‘4##Ø·%TW ˆ0Ü©¾·5ÛñC12;?ÍÓx¸œFŽR£ttZË‹&|O»ÎŽ|aã”»(¤y)­L!"œ*½>€÷x<á’_Ãxh;M½³æÄV.`~'€h [ëáŽEy7ɘ$ë¡ "´·ø²|;î숃Z‰ÐvH§ž”ó×J~xë«=6%úKFöËÌÞ‡“Í’Ž;H4‘fÆ(øS°Woe³@+;³ ¥0@—AwòØÚï3˜ßÚø´GÑËǹ3×Êýià£t?nZ±òýÎ÷X¶#FнÌѶ‡Ë61ä?7‡2Q×LçɈ P÷9ßYzõ!SþMr\«*R¦“ÍíoM>RpA9-C°ž¯\@“F%K»jKØ€Õ÷Âí– ½™úšimjD“ÁtúXÛÿfºÈž& tIRf´¦»9Õ¨„…é}èËÇÇ7ìˆOEO¤E ¼6(c’m¯0üÞz¿1à®ÃÜï—X6Ýʺ ¸“„r[­ t yHq°ªˆ]›C+KÊ´ª=ÐÉ×¶ïL¢”Ôøoƒ…Àš;mhh,m½1—£ÆÓ.¤r Nœ;ç¢ÏD̸¶³’ž".fN¨ï¥5е®y|¢’ olîø_~ÑÈßÜKö)î ºX»£·wÏç=–gð›§>²‡œ.£·IR¨Hb%ŽYÓa2Ï£~ˆyЉs9¼àIü€ÅãÕe‚id+Ôêx¦7v•ÉgžÂÖÄ^—-ÿ‘B>ÂÀ%»»üzåÈ.ù2PôÏõËú¯ÇFXÅú€èfŽ(V³ÌsY"¡7è6<àà¿[’mÝDºfË4DDFíÀМ¥HIœÈw–¥[åÖªyL€[ñSM¿ò9­_HÐ>¿!0p½ŸäTlø•ñv©kÔÑ*˜R}î?*—Ù¯{¦gšÜv}üH áð˜å®8Õgf-Û¥édC¼°³¾|ÛôÀáîÿ({,ômAÑHùjzDÌ¢Ê^­Ì [g*‡}~]©eûd‚ùÅì„­Ú@ÝÆ GC½ç< ¨YÖÆ9g•\&aî•qJ~Gë¤UI¼…úßœH[]âœíHm;ÊìŽkbΤ*$šÔWŒ:0×·ŠD®g^I‡Žƒç½T)"mHÝÂ#—G%ªÕºGB¿J€èâ`溯?1Û O£Jµ9CS;l KAU¼%f2s7±ÂŠ.ú·Zmcª*>3 —ÜTÉË=·2ýï³â‹ÁÕìøw'«öó˜;‹$œ´µ,_¥îÓ9µÌLBÓœPÎòŽl” Qdm¹¢ò›­_ü<å9:Z+j'v–éÒ/¡×ͺú&Ú_vqoöŽ#ƒ´ð€khÒìc¡9R²mÚ}qIæÝSíGÞ …ùd¹Qï°5Ö”ó<‹ö8x ä^¾6Jº9eLcUÆ#!íZ¿ãŽdÐ&Û#l­/›&ÑDMTsè?®lˆ\L2ƒSž64ÑçáØ4èö8…"²f/a»¼_Ø˜ÏÆ|á:uT؉áéAB‰ãc‚1iœP½&)ÓËú#h[Âä}§!_MHXÆuzY%6ö‹sBcü|SE‚ÑNÙæ^ö/´¦‘Ö¾9O½ð+æ³ê˜å"´X qZ%©žø¼p+€ ž©šØþà'eÏœC¥þš2+ )ï‰]¹¶ç÷ã½o×Zÿ±ýÇ×Á1꺤ÛIÍû‚^ Ð, k̨ù- aPeá+ÝójV0ÔCCxÄ[Æ5ÖÇÈ ÷š´2YçÌ ‘E#Ù–ŠÀx¡åY%þ—BÅÆ_N²…ßÜRY*JXÞ•l$%|2Ïq‰ã£`8SÖ¸ÙKoJyFµÕ‹1Ђe`.,lY`¯xCAW­ï»á=ãÕoæNqø‚FFâõ`˜ýŽ,áÜ‘µLˆÔ]/&ÙªÇäTî;‘J,.m;þ~Iý2bXT¸çÂÂ*Gá>(¯eGH'b'-OåÊ›ÊÐÉ{ǵœëÒ@cº|ɬ}[1çEŽ.nÈîs£™×ÇmNÖ%zXßD˜äøƒyÙcføû†%áÂÛèzXísQ!ФZ·9G³!á“åà‡Ýõ‘³AšdÜòˆr/¡Û^ŽÙ—õ,Udù†Œ˜bêtN¨9N‡`…GÀjríØïïvXMŒôz¥¨SôÐk™…{ûI䟉Өñ1 FE§°'僕{‚ֱݨå,]a-ðÍ.³ž[W¡-¦Ì?"Öjì [h:øAørõë§³nâ8ý/ñ’ŒÆ¯R’E%›ŽÔ0/¬Ì>+€5Šî¨ÂN]”Ý£¨n£½€¾æ-oãã·ï¿6î›èlf°˜ÒZ 8[Ë Š×U¦¥_H+°5Á:õÀHé &Ü®u™ÆÈšÓÞ,Ô[u´XFúìIýÒTÖÕDü|ÙhÍæ?©+ìDð#lð.b…èûz¬»^ HÚ âjmƒÜÏeðŽxóU¥b”ëy”«L~T¢.OÓ]Û_ :]õN(îƒzÏí8èBˆC 9â†5 É ·i¸ë…€/Ã.PúY XáM ô_2Üe‡î¬Ëª·¡æ¨™KÆŠöQÎpyÊ{Ú`¦=aÏŽ¿}ϤcÇíkêŽÁf»ÆÑw:нa˜bEA]ƒm+ˆêÊàYHyäYª†ó)Ù9½ôõïOVÙùºˆƒk¬ý`Üš²õÛ³ÿâ_\ ßf7O̰ŠnYO ¦g\%ctÀ½ ý¦€TŽ-µö ¼EýxmÌŽ¯zÆ”m}9L½jS ¹+!gZ+Ëé5#6I©:wˆ\VÓ#­43Vø?@{ÔÈ]Øx÷óüä XSf´gjwg®‹ûòÞm O™›k$Џâ§hâGê>Oo‹+ Xš% "^ãÖQÿ’1¼WògV_ƒsEõÑÔé‰uÌmÈ>»méG§{7u_ u¨[DE“Ó0åý,Xó Wäûâ-_âÖG~;Dp‡›#sJ[®²ã£Zf39øW£MØÎ¥‹~±üŽs7Ðd×—oF<½ƒÆoMÓ1ÝC½.akk«q’xI¦„ꊣ^µ¼OîÃY&±E€]¯Wd¢Ð+ÇĬ‡’ˆ(#?g¤´ºÅC 9Qj%'©k¨àæÏ>²ú—KÀ >R§NPy³&©|G–Ãq<î Ôþãùõ™@âìÞ³ƒ¯¾<ºb `‹Ô9}(½íAñ-½°» ,Üêå_â§‘÷ÝßÄA B[wÞ÷mñíb”Ï›84á0oòf¶Ô »·½6]â!ߨ匧ökÿ AyùјIM¶Ñb}¢ÏÅð´ä‡• ýÀìW¢â8dz$9Š ÙU}.¤Á$¬¼ª "’t ƒ.âÊ­j˜ðzxÃ3Sü×)صu\£üÉ^Wù¦Q’ºP>i¨q¥Òør9ØìT\‹2"O÷ÃÃà!ÕTsòÆ®"¨„G6nÖ5;3ÞW#8ÔI· „‘ÐgHˆþ \Ú¼a”ϱàå­Zµ؇&sÒ"1óº†Eë·†÷ô¬qÏQ£-çvÍoƆ(ÆYf…òÊ/í²*IüÈnGߨKªþ™Æ#$’Š¢^‚ k¶XQÊ·>TPÁioØõ†×m]/~ŠâúºëwÒ <a@¯ñ"àBؘ𕴗‚%5÷·Ÿ©éÇ»þ”´‰?Ôö‡á /ºzÓŒP²4 …H+OpÄÜ”­÷.ØÔiÍºÆ Û»Ë š@bìxËÔìA¶nƾ¯êØ-;~  gä<§¹KZ)£B¼/š #pY‘…ê±ÔpZ @Æ_}"ýšïx‰ôï Ú ¿ÊÚ¶Qh‹å‘#Èu{mc}¼½iŸü0Déã—ÖaË‹z6¼çºV63 ÿts|a šŒ(T@iëC'‚{2UÉ!›ôFðA%âüƒ²ÿbºÑœÔ¬aúFð+/Q‹KŽÝøh‹.†îf¯Â·š4˜ŒýÔè@J€ r<Ò@æÒ¯[ü‹“r®Q'ǘ—S¤ª(~«QþC!5 ]£×ëÓö8°Iä7£í%/uóÚŒïñ ×€¨ ê¬×a£`ïª÷îÊžt²Ð¢ÇA†ñ¡$ü$ÇPcZ¸[ÛÇD1á—Ø®7îZ—¨—‚Åæû&ÓÃ… Œñ÷P'•ßÛeæ£.L6:Øm‡/þ* Í”=C†Ž>‰­Ô 4é' òK½p¥"ØçÝ`%©e•ÕÒvF€&‹Ý?’ú|ËñO/0û«ˆ~[ÖNK4uQ]tÉñ “® 6Ëq-A ™¼/ù6‡¾‹‘d¸nºÕ°§…ÊÓGàTTY†#w»ø3Óœ!{^ås¶‹Lö„º‹è÷DöE΋A8Y~_&6WTpI¼ã 6ëÕ¼¬±TÐÈP¬÷¶)+qlž Š* ÌÚ pâ€GZßä˜Û8—+¢ %j`HÙ#mÚÌÒØÒÏÈÈm '3ÛÅ´òµítåaXý}ß×NÈM­äÂýù¶7}Ö%„U¬} ù¿ÖÍOOßnqë_9Z2Ô†|àêlÅ"ÁŠü‹{`óð>j×|%7ÜvíEy-5>öð´ÇÊ%xúB(xf7:µœ!7õ-å˜Ì7—qèGofDJ%jÀ»‰þÄPÞq•4y®Ü ¤lÅRT“œË„lüÆ[ÒÃ(¼`TÈ*H˜â 9Âb‡™E†ôÁHdò}M+€Ü›4Môî ;Ίf8¡þžvkd'¹]ÿÎ5cSl0‚}GS(üµ éš×؈‚Ñ#ñÈG}£ôè*=Úw(Ëö¶Å˜Ú‡1jaE|#ñƒ#’àjÒ‘­/ÝÂçn¯›ÀÇõXä%óSù;¦òFSnŇͿób¦uÆáêë$÷·Ê/TB‘ëqùº[ÏÈt¿m3EUÝBF™¯f_ßñdö̆f~ãOuðžë9ä…¹+|3”‚Øp„á÷ŒÆ >Ô0ý ûj+ôGaÆè©P¶¢’Kòž5zôv"eÍä8;À7¥¨q7a+rœÜaè^H¿/@þ‡0&¢–T³tÿüCü ŠdŒX…×½gP;Úñ0;ºp34lìãò‹ ¨hT÷€ÁøžQ™¹‡æ%Ü"8³0ÂÄÑD/geÎÊiÞ¯ŠðU»F¼%©ÃËtCH ö®ˆZCç"!y„Î/ÀH™«ýo»•ºÙ›×ëVÅÜþ<`c‚f©ã6Ü-Ë.ϪՈmàäŽ$5ã0Ê»Zo‚‡öN3°×çyRØa„µ¶+Á6Em=È30fËòõgÅMœ¹Þç—·ùl¦f3`ØLû¾VEŸDO°áW+£@~¦„¥b”ßãÏDÌËþ d#O.ù|êå9‡Ù.µ´WÒ-:xðÉü!e•[’‚ÿ8öÛþÿåØ%ô]Ü¢•ô-ÅíK_µñzr&µ A¶t˜V:yº$x ÖÊ qd€N H¸2‘¡Õ^år§Á¼Ð\Ã.à ôcfÂ@=Ï,I“"þŠ’‰ˆÎ_NÛΖoõÚ0aNS=Žé®ÈF,_hð•ÑðFd«½D‹!–Ô*½|6ç¦<8§ÎÜëççßîÙÛ{Lv§î¬í§’¨/Ç3¶¯»LXÔ¢>ªB½ó3áL—I>ÿ4‡—‡Tã¯ÈaÒQöµ–"8é³_‚Ñn½¼ç’²Úê,)’¬3ÞÑòJ«o„óýü{‚(ý?:\ð6`††‰!=ì)ÄšÒIuZ?`âO2pÔ>ÉûŸ_ûNWC¶ 30‰-M¶NWúÙóî#ZGc'0œe­3æp›åúË6ïÄ©D’ýPÂ+DŸtmcµ„-…ןÜd¿Î‚Ú§<ò Ðwpjþã÷pÒßêd$†J±¶Ÿ¯ù#ïÎìÜc!g³C„}ÐLpAñL&ƒ4¹mÕî(10ÛWÚÊÝyd&a·?nŽÝ%–Ћ¸ä´€Ã¿gI¯/ Ëÿéµá’#”L…QIöLÐ>ëI°ži|ñ–Pÿ±d3^—rb†©u̓ÓóHV~×"{”,<«˜¯Â¨(«æ_¯Çòç9j÷ØkwˆÒïD¸VS°Ú‡x–Kš$†zï{Â! (p @+3 ¡T +‹ÀX%E2FEŒ$(¤a€lf£TŒ)ÛrÏ( ,@Â¥ ë)– JX(HQ¡ŒN‹a* EN¡*ý} aa'‹°==ª¹¤B: ¡pRÉo¿< )*_ô´‚Ñ´5¢RN´ƒ9HK t¬Ð¯‚8V›ý蔿dÿ“R)ŒÖEȘP BTÂë$eD‘€(8 “©ü¡f¥0«¢¨–?Oé$G”BiR¤(Z”½)ÊVapzv—­;ÿ©”Akòà pô§$*úR$7@ "œx‚RÖÿèFç´Õ@r+ãé¬vPÓ¶çuØ P · ˜ñJ*¬Y–°(òDV²B¼€qÕzÌÓ!Ù¨R€~.†Ó³ jÕ^¶bQ!¶V)¸…•ÚÕ°ŒnoÞ?SV@ZÕ^ÔŸ‡BªŠ‡ÈH_ÉøQË›G3/¹kÞŸ~ØÖS/ëy¡z½nOÜÕ…OC«99ëìç§6'ͻįèõ³µ“³o›W_Õª,§;µÉ JÅ"•®UAD§)›7<¾ó5…©HD b!éjÍ;p"'¨¸aÅ… äZÒ‹¤Õh!ËáZB µ…ÕðVK7Xñ¥±ã" ëp/qöÒ((í÷'Š–[c)gb@$•]_lÀŒY)[‰ÏÆY†Ÿ»VK¤ Q…AÂjHNú´Z ÃˆBI¬‡ QE’Ê"´ú¨\øjÛÑ'„ÿ8ŠÞ€.ñêQ¦jª³Â€@ýñ£"®V‹ 0NÅQ¿v‰]X H#B|/ììØë¶O.³;ÙᣨzMŸö@æ~ƺ×ßÚ×n[õ;%V=¸áYOp±JLGQÅt@ ±Üù‚ÅLO‹­À…#Ž& •cMÿÝ–~Û³¦<®Ïο‰ÁÁ$÷mj»£¶DJ\ZgCHEÕ—[¢Z!)`IS åD>p[B—Æ]†"ëï¦Zú…Ó†‹jHˆY ˜èci¹´1|UʈRrcašHyþ‚ÿ¿€5›Ô°ÊŽBcšÁÜj&-qVÈZ:G·/¼lTÙi)Ðù®VäP´tamu,LK2̬–‹¶+v :­´6¹`ß@×Â_X“Z)*$1¤¢)Õ€4Ò*f ¥ÞSÛ¬y§OQöjet%|áŠÝd´Xj§®§Ì@ÇÕ#ì(éF[ë#z ] ¦ñƵ"¯wÞŸHv­,•£h«A,À1²‹TEÈWó”áHtT¥dO‰µ7p³[Ûzr%!ÔGK€‡¥fÛ¢Hô;ë+½8 ¢MrµY1“)@ŸÔK [cÕ¯YC ƒšúQ„iló•à’‚_Ý,„®ˆH‹6xêrÝ‚©v0ªáºÄ˜¶Œ ®fÜWŠ&kFÖ¨2J_Œ‹ ¾Ü°f|ÝÅ™þ™U “‹®\­hz²jû¤MÀÀ¿PeÏô4(¯Mú FKW%q{¡…ivPY¯1‚3“K!'W¬U LcSÛŸuXFY¼(u‡k–/´õlZQ–ò_tŒqSR>Pd¢Ï6û]Ò£5{®\Rà¸íþP°®.âUŽýŒb¨EFXÊ&—&a9Ú$ÒX[ukB‡ÏšÇ8>‰0x¶Pc¢¬àY)­E|¹tϺdi1Ç6@ºm™Âÿ7ˆÖÚ]û…Â@*©îÈ@ -ñïŠn)5´ì{l£Õ[1u ‘…üÓ14jà+lê­¥ÓÑ-¹êœˆë•³ÊeêX²«cשñÃŽœHšÛO&ç|Ùh0×­…¦bÁ…^ð°Rý.¾Ñ>eÊy::Ð…¢töY½¡]-Öß•¦·\|s°zÑRŸ_øNþ?È´,otçAçf§„ýº§^ÙúП~ ‡qå'¬¡È:ÕÅâdôDgâ!`‡ j Ò|Þ¥s0-…É ÍO xtyƾg¯ÌŒF“uß ÝA„Îz–Úžrv÷afàµñBmªî(oþfï(†žÏWünà¨{«›ß‹"ÎÔû^ÝÛ—g¦…* ©°œ1©k`¬œeDD"éMÓq¥ °Å€¾Zߘ†A…mwuÍÊV˜ï7ücÙÉ|¡¼½’L\K÷êvk­Žr½Ž·~^Ô®·‘Â!ßwï:%u¢ôÛ)Îì½n\¼Ù¡Ðk·¢Œ>R¸àzw"¯»Z’V¥€œ].ÊÀ5ó[¨Z^–:JçOz\¡ÔœH( …N½‡{ë…ÓUµ;ãºjºÞ¯¿´*P×ä§ÛxƒœL!Í ÎbÃW!hAB“1‰ í‘­ŒÈœ¡Òþ“I'ê«åaœûÔÙººñewó9ÁeòUº‚c[ð»¯RX)WR«h§)ù„ÓÚߌè-² Ç{“W…¿ÑX\šKxúÄ-ŽË "qàøYí–{…±óÈIa01k{i€+sú;I ?BuU¼y°[~ y'­Z¦·Z_lp÷ÊÕŒ)N[Ø÷@Ò y–JªÅè,¶w‚Üæ6ø˜v£6H’›Âï}Ê¢8çvîz·Êªös½6Ï„èP¿ëÞát5 ÇJW Ÿ7²=­ÿ޸ꈵàŽþ&ñ[!¾Ùˆ6í½ºj˜µT  ½Tk-'ZøÞDî “—„ƒCõh !By³ÏÓíV‹ÓÏ„ó-´D·ã°~)aÄ!ÚþÄBŒ CëÁýí.Ô¿å«£ˆ÷5©ÖøM"Ú®+­^¥©€`…—÷úp¶ÞÀ4)‰ŒoæÎÙ‡ éÍpϪ+&˜ŠÅï« Ñõ™¢ÀdÊ™ÐÞo©b'‡×åðk0 œW²SÉî ýnY0';2ÑÀlð—õ˜¿¼MŠFonS\ûñOFÉÅ&k„ Ò´ÍŠóL‡S‘—øsjЊQ%Òfì»È…Ù_2ó"˺eÝÒ~«·s³ïÙŒ!Ú†^Eͪ&d`úÃèkï+qav~0¶|{¿×¯…q‚7jŇbÌÊVmh_|ûHÂ4–õëa;ˆÄpO]è»[Í,ñ›?uÈ1ð? (Öéu¾ïÈŸ¢(rØgCK SÔoXæÑÁ àfùð¹~x®³H`p!»y¿·ŠÂpÉT¸:ÖÞ¹½É`½—ØßTL¤¢ítb–_˜ìè›Ì0¢c^?ö¥öŠö| ‘½Ãê‚žŽ‚pô]T¦rû›é$^œ]#{¬D‹ H”¡ i§Üd+»1üµvƒßõ³Ämýöõøsõ˜¾tÝoƒ,ó¨&þ7Œç;nbÈGDøO¸¾u p–,Ê<…Îé)—(K(&iNæžÛdÿž­¿[}}ƱŸž=‡wgú;÷0`I`ÏËÙsûýŸßïQ¿Ò8Ù3e ‚·iâ{Ic¤¸¾±1â̬ԗÑßê‰é²D«‰÷R-Ѝ(iYðοrµàÖK3‹Ý‘xôì‚Jc¢)Y#W <’Y0ð6õÅñ&„®"¿Z>þ‡âcÄõŸ§)L†ð¤²FÖqÃÝT½®šÊc/9EE}bCm(Ü”A¡œ¦d.ùH˜§Zçü·l‹båøÏp»­…òb’ß±'…¬—u/ÎH·¤cjNnÎÀ²@™äø¡(ヵAµåøžü(k´¼ÑfVq0¬-8ðí}VßA•ä(ÊFjß‚L…S;~[#ž(°Á”>{'­£ë5žØ5ð:ƒ¯5ܰB2{ë#ö½´Þï% ²[R$rÛ+8ìäªcëËR,Û tRU­~’)? Ða$¾T“¤'CÞ0vò(Ûö5 ð#èª7øJ̦RÞ„ìÁøî…N]_ïR°Ý“Aþþ!äì\`Ö?Šbp.ö?EKa#)}“ÿ/Ž{┾AŠN…IÕYìÕ<þ©þ¸?’š$XŸ»¥Ùüº¡XeÊÑ1ç—å ¢ D×(ë2ÚŒÛOíĪ1þd%Àödd¹`ßÓ˜ûã÷‚#Db À¼iΣ˜áuïD⬠„ŠÎ{Xßóp'x ‡£Ò ²e­ÄÜ©’%C€è’¤ì)-L†gEÒÀ©‚ûÖø;˜æë'"°¢ýóµWÝÓŽ" $«8ˆÚ[ömHü …i«ÄÑ•ïò áYø´ D!-ˆÇÁƉzt HÉg˜YÊôo<Ö E–,:ȲÌÅ@ˆ”Pä–ˆ‰áôR)çké¼6ß`Zó¡{µÞ=|Þ/˜¾šžuñºÕ†ÍsåçïÝì©ÐW×}8æ· ™Ó"] YæmOÐj¢öënárªoòi¼ÙÆSýcF¾‘Ê!«l<ÅúÙz’kw ×åüT;È•@Lö„r{hü:}`B*AIÓEô÷t·E㛡+ÐBÜûÚ@^¦$ô­K×7PÜ1oò©ÂÇRH®¹iSl¸ú&ÿu¯ßl J%×ô)ܰ­Þ“w÷;¿­ÂvõkŒþB݃\¨dkÇX^Ë/¼dÁÙð`ËÈ @qÂ0‡öÅnáÈFxCí¦S¶¨]rm¢Ï¦žê/÷Æ¿³<—9³STe¼–ÇýÚð«ºë­ÓAù*Ø}î³1°@kïÝDgqÙ w^{a&G|M|ݹnN!å|9©7ˆIÐñ ¿Àé-–™ÿi>ø¿ÑSÒ¯ûùœÉÒ3‘ÊÑAq%̶L€ÒL­áœ”Gþ|øªCI°+•Öpµªî¿NÚãVЭO•…ÇäX»€Ü…ÀÓ4䜒P¯¾>Í«‘˜î!x÷{tÉÁ HK¢Ë"h µ+`£jfÉ%E—©ž)eMNŒÂöºw°Ì3ܾÑT"f¯N¿§XèK¼à3„_\ÜÅôZ·­:$¨ƒDXÑ»xÆ"!û2ù Þ ãÙC®‚œÐ õ“¶Máù^°!x±ë:|ÕQÓdjlRÜØ¡ï´XÀ£ûU»â‘–9‹¦æÝ&ë|mj†Ælc!ñY'žL] ª«1œÿ’å"'a£ëfÑÕñ=}§8UXxMqµ$ÛåÝ* æ…ÄtzÙŸeÎ+Wj•ÙD1àUûqt9HÂ52”ª5õªÀ¹n^(W“¼Þ×|8)wX U C(ÿ¶o’²Ø6r€öêØMmƒä`hÞô-X óEó®Ô^©m]¶Es¡°ØZeHò£  }b:×Zóø@Bx¹¬û¨)‚¿š„…iÉ滛@í¹TszÍÈ©eªG™(IV’–Cž=l²©`ÏWA•Äš{ÐcÇD;ŸCýV9D^1âÆ×¾¹m´œsýe°Ø©1´µa|^­e6˜Ó?а^xÐÑ4pq‹7p⬔ÆÙýÑ ;­¬F(¶AÎ+òN‡rÓ„W/‡/eU>£÷HíÃMRíYÿNÑ—–ÒþTä©Öpî­´UŽÈ¡’ÿààCƒYBduF[7SÜl§RªÏìluŸM'Ù²Avg¼ÜÅæ¡Ð–pD¤%ÔôlV%Œ¼áÊAJÎåh‚*öëj™z‰h²ý±,¸¾Ä¤¢ÉÞJ: ¨]ªd×)Hg/ÊWƒlh"×6b ¢¬cž#m{ØÉà™Í¾â D-~Í v ÏÚ §q´66h2á`V‚P‹ZØhÂOY‚oÆKÀÄ,òø_ss+ñ½c°—I.8ù= ,û˜!‘c›]~Qº• 1 y섯ÂTc²E?ŽRƒ Oê‡\Œ¸—o0|4£è‘5oþÌÃ'–¼]üÌ›îü¡çZºn¿çú;±m†\ã#þ.sr9=B[€˜%z €ð Ì ùýç¾].EÈìØwØp }>\ØòáwšQ)èokS¿ûH»46ûÓ7´yÊV W¢) l1¸L1óXNq}ÝA&ÚÈRµ’= ‰F1¿¡ðEq‹äøÆ¦è0Ö æilÜEG»ìU[©#ê ‚‚Ä$æ;µU¹Š=ÄK°äE(„‹oQ´G~­$}ºöÕã â¦Ð|#åÛ: .[ñ•DW4?9-*¡KÛ³h°ÎwAo4~EÂùÍÝþÖÜAwýËFœ?ꞥÁø>^¥œ‘à“ß3ˆ• lƒéÜ'^ã­-aÏ Ê¥dåÂss ¼riŒ´;#ǦaGN¶rC¥QÅéç1Ù—ä†a¾íîÚ¦~”§T,Fƒ$µš¦c9-„áý§ø9Y ~N˪¦k!J EKž¨¬e.°™s6sÈ×8,ÙÀy÷<0˜dÍöPlŒ×h£~A9„eÆÒ!ä¤@3ÂÎw»ÏÍ Â7¨íS:|0ogb¡mƒL`BÌ9ÙfOè"Z?’è—¤¹«4^\9õ ÏÞ&¼oË×, o8QF~ë¸iËáa?Ï~OÎË6ÁóÞ=-òNv âsU ÑK™;#¥ìÖ Q:wê"‚[›øOA©{Z¼Îìf7N†7wó¿ôd=j*å™$m2/éc’Csà ò©2÷9 ŠW"™®`Y~<$#…C<% V_æB÷ Í‚˜l± 6`¹¢º´¹ï¨Ü„^Õï0º:s¯Ê×ILñèoå}óðÀ@ l©0n¯ÐÙ…#ër­§\ü`_Èp¬J×Czû0• !6õZˆœ‚\®¾Á:ó5§œpoSæ§ŒÖÉ}oKsHYл߷mÚ7”Î,Õ›ºXå£77ÔO•?C?˜8‰¦fþt¹à#cýö'ã™R}¬gÈ7CضƒÏ_5ª™–÷g欬*É ý¡÷­Åá)½4àyAc-å‘Ö©¤O´m‚/É&™r)i?g êðªiyÕ³ÂIÝÉF`·#ò}lø í»kŸëͼ3fßĸñiÆÍø7–e¨6^›áü®å磳â%;”S¬¥Ÿé­)ùKÏËxU¼.“)±9¾º d '›ýñôíš–û!Ë8†Ün<ÅJœH¿ßL˜<è`xÍ˱›Ä 4?çĦÑQj˜JQùc ih…v†O³d¡’Ò™n¾+bµ¹i€Â(øR'€ Â;J¾ /Ê'8ÇLøì‚-¨f)ÏnT&ô[à&YmÝc[ÚÈ ]ñõߟ IrºÉC6 ¨Â}ñhƒ4ÑåÔNެ„ Š…,——fÕøg®Ó˜ûîÞ°-¼8[ñÙ½a[Ý7™§dl/<5nï’9²6DÌ4Û9F/¡=Öl樱B®šoÄK`\ô¢Ð&¸Î1î…LAsªØÊSÅוG-¡P¿´ZSi€Ø)OµR'n³ðßÒb“’˜ G #òÕ˜~v/!½,ÝLÓÐŒÌqFðO&É09Ø9€ £áÉýé»Î¦Á•ÿÃQö)Ójbi™M{žÌf-P5÷øÖÈ4ÅJ(wÒwhy_<3’‚ñTtDE­ãIÌDW\Ê}è²ùv±øhO…ø1è€ÓðmkÞw±s†Ç6õ—\½©³m®Á9üÏwíìßûÝF½ÈèBt£x cæž#“ M”ñØ­¼õéTæ!ÞíDâñX¬ÏŸÿ°z"‡÷•Vâ7re½qoçk”%¢ñ˜“Î87p°¶òJ¾Yiߤ•ò*ïç†ÅGŽ ±MÀ‹þüoÑÑS¾ÙNz'TÝöžxiÔç¯lÜoÒä}v£¼!ßc7ŠgÄü¾WæÍóßüz®9àn ™2#kÝxîñdý™Œ€„-$¿"¨A‘h¥°4ëÃX³ê êèYTï•Îä^ž½ø›WІXf"ü~Ô€`õh¸ü˜ƒë ä"1^Ô—ÕtƇÕ$#þûZ¡º^Ê ^“‘—v” ¿Èh,ò`ÿÇÇBW$,oZ1\“Ê–2«ê+óñ;ûº–]åÑäEFî’Ö¾ùkIîñs³Ž'‰œÃAËÿ¸¯—Ôç¡ äžÑ‘"͇/Éôsð–R¥Cå™è¡GÌá‹%* Tí‰ù ¬›¦>|—T"-ð@2í P3¿GIò*÷ùz»I®õ0¾4ÇðQ_ÒòúcôVYI$X›÷zËÁ{@nk%¹¹ÃH>,Ø3Ä&|4ôàêÈHF9o>Ãnñ†ÀG€¹:;:s¤ÀôGÛ¢qµV¾5/J¸ò„ô+ïJË­×9‰K‚Éí™L”ãþˆIØûŽåÚ•Wä„.­zš!8¶ ŒÑâo™Œ±Žßñ³†:A&ØQfz¼€/ùÇ‹>LypÂׯZT>‹k7 Wl¦©PÔÊ<²Ôe‹R9Â’{Wö•§ÇÖÒ›ŽV¹HXÍ(9AcNfn¥´{š¼OÜ )aʋѬ©bÛ¡û?nÿ³¼æ=Ü—â O%¥]nctw‡z86ãVbÑq>5ÓÐ:íyë– l¶$õzVÊëSæŠØé¼„Ðý†h¿ò;»ýçtÝÜ÷vJ—>>ù¶Žêç±þÕßÏêUFÞ4Ñp=ðÿâs¾ œCŠWÕä_ÊŸ ÒqãŽýšH RÓü#|ÕŸ à›ÚAæŠ;¡ëžkLCºó>¿ §]ÏZÄqŸÙÏJwQ¯ã×8„ø^6Ù—÷Ýcd™s»Pú°þë~žìÒ’’Zíâ/jÇ9‹,º>ùuE=™¢ãg}&¦ä@½2­Ç/iÙÜòòåa¬¿Gõ—ÝÔgŽ'“Ï~ýE¶KiŸ8·i ½;¾uþÏT)±wÓsG§wñN.܇f‚¬gÑ/ÝN£A$oÓ¹Lû@–ëx†®w~xˆÙ©þ-ˆQWæ±{T IjÜs3¾lžY’NÛÉb˜õº}ú|²¹Ò»˜ÑFm¼ø?ë“âšãnšèfÓÙ&8M°,>5ÙÖž'ñ93Ïÿû—+à‘ÙYß冷 ØÑé¶ûÂe>“̃þÊ‹¸è!…H®8c½›¤¶ Ýlæ•—ÎÅýwNÿ `†Šld u³×¦»x©™gf3y~–ŸoþŸy݃.Þß(wîQjëO¼ b¨#6’y**U7% F@hŪ•@[Ъ^…ùPI[ò*V…*Ч<ʤ * »{¿Þ€ðÑÕœÿ[ï¾û{YûˬËÌ\.å’ɪZ±¸ A×à R.Òÿ½Ì‚J× À-ð¨Çä0N æm É‘jÜâ¶-8'„«¥8BUQŒ°ŽîCnNv¢ùh(ÕÕÒM? °òb¦ J«U(ÿ$@ÈÎÀ)µª@Œ>ˆ\%^-½j'JÁÔ1Ú~s~·qAAzÇù9„­ïÔ~ÜÄ®îê-è¶w®ñQð@JÑ €"t¦# t’hâ…ˆÃᥴÀd(†\œ¨A ,^)Лƒ N‡Hó:ÆÂQêy4««Ö6o7…!«ý£R’Já‚î–„…’ÃìèÜJµ¥hH8%Ô$DÊf>îûNˆ ©=Ÿµ³’ø2|<(v¥L ôH¸7BÆÞÛoªøá é0ç…(9B”U‚­\4šfr QB~'ˆ‡ßÈ5ðíïÆ­@ÑñŽfú‡«*(xî!š¸@œ‘Û*×ì7³÷õÓJjšÅ!8yGë‹FÁ^j;ðìaÔvþ°R÷[J°iB –Çß BãA$&1$±i†€—m’t4­`~Ü;A¦ù9UÊñ¨×d‡F­I’ñì‡ BÎít Á-«Û·þÍeÎdg¾4íw¨Éˆ£a˜‚×÷P¥D½OÀaƒ^†jc¤ÇO2J‚˜/br×GUkØÚcb+G€%ÈE]ŠˆÕ¯ )*^Ën}.éîŸÒY A÷ÔYÝÖ&Ý€ŽZSâQWP?‡U8/¶•PÙ2æ®´_jtï€þ/VáøQj¥ô¯U›XÄ1~ÇTÂÿçé5žXÅýQúo*˜‚”&•EÝÝL0kPêt¸¹XHPÞ¯uõæKa»4>­²¾~™RMRñùg4Yds½¾tǰ&µBòú¦5‚T~måZ±ƒãËÓÔk¡ 7^ö³Þ›+OU{”ã¹€sÿƒ¥n#ÈI¤ «nð2hX¦«®îdz&kº—ù+ùC½ÒŽÉn_‚Ëð/ôöï4¾@ËL¡ƒ Àü{Ó6SÍ—ÿWÛÓ¤-J`:–6?m>œÊnu2ŠV ‘ŒPÁfFž ¹²ÙdZ£Í“j—ÊùVAÒ…@áiÓÛ3,×ëE¾£• x›G†›HŠÌÔæÎèÿ·ýü,]¿BzÁÒ‡émílªóª²£z~¿*”±ª¡ÑI[¼tMÊ|Œ™fõÇ{ æìñ#ŒÊf­,š:’g΢þ±šÏ6>úg‡9;Û±ƒuràݨökP¿_¶l;ØPúÙ˜¤úG1à$Ú]RTË\äÀ§Õ/°·Uü§Í¢2# ªkÜÂtbÏ™[vQj³±ËLè¶· ~ü 'ýÀ­¡IwÞ½Àná”wàSÍjÍͺúù麹J àóõÙZ¹Éº¯ö«UI>v(s³‘hš"®EšizNº2ùyµ §:šÖIOëLÐåTØRr Ø8ÍK uÅ•õìsø'l°šp?`ÅùU¶~ä¥O£Â=7?GEEwYÇ1 Z—ã¿h›„í9b¡Žò/g’¨³ÈPÈ«¯RËÕ¡P}ëç„“¥Å¦J–j{ÎBX ùmN›6s#ÙºÀÙÃ(¨@®²ÈáÉ›!RrpÓ¥F³ Ø8ÜÀñÐ0ØMø@±‚à()üƒæ” €@oøWÛjœ )<ÍÖœQP>þ„;^8„ÆËÐ[ HÍêù…Ý@84ƒïæ8ƒœ0Ü_8‚øèغ[¹Ô¬MǹÞã‰ï ƒXÕâÄúÐ(E1Ͻ@øS³Ï´ë_‚Þ9=DÊø)>' µÆB ª $ƒîbõd$ü,Qࢳ¼’¼xfò@ ÊO%¶úV{•¶™ýÉô=€¿k €:Æ©2°Æz›H +hù] E¼´G7P¸~÷¨Â?ÕàYV=HË `°Ô‡€BsªTã õœºæ·o<*Þ­ï¥òãs=iߟhð£@¢ Žzjâ:Èï½Øaž_…^l¦/ÙHð6áO© ^0Ó {=âÅ_8P©ˆ[v¬ú¶3KbJ<ø&?¿«v8nÓ²t¿Ò9ï¦Ê7•XÍÙ „ž¡ÙKÜjÜLùh pÌůru[4¾œQ5M1îs#!öìw5ÎméšhlföV¬àÂ¥û9¢XÛ‡ÃUJ2¶¢­ Í'ú¨»Ú°„ Y`u6ﳆ”TÃ.†›ˆŽÑRƇ­¶ËÏF QO¡ÍÈíäAEêb÷ïÐvW ·MC4€T£¹³¸%»½×­eB³…Òø¸Bâðû-GÖç…ÍRJ÷ ¢qî{ª±bR¨Õno3T÷¬ž¦ò*ú80©ÿAEACãö£žâ³úÛM*ÿÞH…38åÛmçèþí ¬÷ãߢP‡xû:70ü·( ¼åoQ÷§*œ- ¦÷ç[ýí -Êíh%7ésàÎÿm@·ho3x¾ûžÑÜ(o=©@c¨n7©–ÕUÝxÞ÷Š’…ž¼³àpXx:ú UÐ}ç¾EpÝQk½Cß¡:R7A<Üî©;mÍ;Açû³þÁ­ô¢t÷n€û÷ Ê šÏ¨wn=”X?ž¼Þ¢l¯oO½gõ: xøoÁû§ø¿ÿwWïCÓZÏ?çEIß¿ýW¿?RßxpÕd¼ƒN¾Y­ õŠÌŽëF¿ƒ ­nœY:ß«+rC}EÍ ž$.¯Õ}£ÂL¡ Žž¡UÞÔºúæ|ªþ¿Ï悦ï¶)_´Í…÷ïFA ¥É@_F ú}¨P«)ûþí60±âù×úL¨ —(5e¼·8Ef¢þ7ûí¢C‚u(†èzôcÖ#À¨1µ¿(0ïZ-‰±ã°ÈšB½øòv8?î7ÁSAŽ +†§£kÃb<5~î:¦Çé8>ÄoFkSO4ÒRX’±&UºR Ìçsú{o`ÿ}ða!ÿ/¥¥¤"U*û»Nu¦Ñ !ë 6¿Ú»DǨÇôïÃ9€O,¡„.ùÂüº+ àáål-pæ×i>Sãè?<úš¾G@D-•Ãöû‘(Šÿï695xál座è…æ°B:›±Í‰ji˶uÅ„k!gàrÈ?uÓÞ¡dËžló/›møIÕÄBùÔIz)#ðd¥ãá†Ù^/ÖÚfªôæ‘'U?HÄ5ŽW8¦ ±Ò@{?EÂM}qO¦þ‡À«?ªB:d+Q›º¯cÔÖöëÕzS‹s[­«ˆý;ð9qÛ³AÚ :£”¼Í¬ˆüSÓÁ;”""„§Î( §lÃl‰:¹ù p î‘~+Éâªc»ÐpÞô;wUõA/CÛ…oqKz99½Û:ø“d1G5¸» õ„|G_‰«÷š(à_j‡EÍßBÆk i݆#üÉ ø‰Šæ »æîˆ&è~õ_sov~w ÆÀ3u…®Ïnççí¯I_ï6çPæ6}H@% ÙwäŒNÏ‘õ ÿ5õ[úÚ/úèwùZsSäñXG›L-n³y9qT…¢ýlõ WÆ«`xÏU«ž Ù!Bw㯴ƒI°ü Ü ÷‘ïPÜ~l˜*0Dî΃­óJÜö½?ç ìÖ¯ZŒ®em$1À­$1¯¢Ôÿ?½Šî#˜§Kõü°÷d>£ÿÒP(­CQbR¬–Ì|ÅZùO·MÙʈ ‚Ì„9š§€sëc[‡ÄB4C:ø\-׿á¥b*-'1ŒØÃÂáé@ƒŠZØS(¢`) ð;<=¨úoEié|Ê3“HÅ?aÍ2=ÊÖe Å,”¸+Ïîh¼v`†|UVU´—Œ%»M"ì¡ê•b€êñ¶Z¬R•‡%XIËGq§ •Ϊà"(YWŒ=']ùÃ'F åÂ"óxžükS½ñ $ö°ßÅû¶z«[tk{Õ‡$•$ø"&ñNÍžý»Óù-ÎGë`,« QmÑJÊ._®‘)!·ìPCîV€Lñų¦2Àêb œÖdk‰j}n!Ñ´–]Úì-–/jeI1öéÅàáH #Gñ\ðA:/ç‡aïñ;¯eIHÁ…oYiii@rÈXʨÚGy¬˜u•¦v+xÿÁ.&¦Â#Ú†b ´WD#‹‘W "|õÝVn—å…ãæÒxÖúu{ÀÅâgYS85e—`é{1ª›÷M5™vi©U§ñJSËŠgÅßŠí£ RQ&ZA ½] EÂQœÇqKÏ0®7·Ò¯cJ7G¼³SÁØ–m§ÐA‡25`¿(aÒw!ŠÄ:7Â}AV`ùþ9ØØyQ&iT´‹^(pì[n°Ô=ˆLL%Iƒo€Ç/8ޝQ*bl0{L'`8õRÙwбa¯4“Ð'¦Ó0ª¥¡a¤*0–Åkobæ@ƒC/1\±ÝgKÙô>k´/ ¤äjûaö7eës`Ã]S§«é¯1ßÖF¶š¥ˆ¾#­9ë]À .(pêJ¡2PB²Ïéƒ5~¿WR‘C/¯¨LÖu áþª(8"Õ§¤ÿÄ„œÎ –B'øÃ‹ è¦ëdT-f’®UîI@°y_ZýìQt‘*1(! Йcà\¯ã+Þ®ªþm¥/=¶Bþ¨k4ÕYÿ`ŸaÇ+mV:\\-ìšz '¡^'_.vü»Õ_N+/ŠæúñDbÙâ«¢ßZR¹az$±¡{êÆˆË©¶+F\§icÓL¦úQpQäÇçÐ…_ £’j~„N¬z ++Ÿ\¨-8ëÐ]ÜÅEÙ¬ø_–“¸çU4;8.îe6ìpŽcox$õ^IîÈ,žL¶‘ ß4M*OU‹æ+¦›§*í–þ¾Xî(8o”D÷/0@ JÃIsÖFMnÆçÝ0Ç+™HmL¬×0\9ÙÁ¸€IoÝ?D`äRLp„4‚hÖ=0ðÓËÃóò¸ü˜™\ƒ†¦ ­!ÖÛs¬©ùï0³z׋¼ŽÚëûU›¸ž Wzjª‹Žž°Œ(ÂúUwÛ¿õ}³Ôue½óUfk>N‡MÊÄÍÏÖ:]ÿh­ VÍê1¬…ü«~•¿lñô콺r«•j¤—›³!u½ŸâÔ0Ÿï<=oqK«h6?¡ Mzx¦#<âj²_Íë6\ ¸`ôªÂÀH~1ÎÆ[É(fY¬vŠeÒÊiºÝEQkoŠr•ÙBµXŸÈ&/=ªºfI[®4 U .ÜQ­» ¸å–l|’uÓÅgÁj¡ñ3@…Q?zÁµáZ}ç–"èYnR\¯‰±JÙììè;};ÞÂo©„†MÌrF–•¶.‡8L~MV'YåL/Ñ87†p$ìˇÔè>…±I3&Í]ßH^ƒ!þ¢ÔÒ M¸ƒ*ïƒ#1óØÊýÆ@D§jL9õ¥äÍ V±XÄÙqûIœÚlÚ¦µ³õùŸ­íÖ8*ûI 0ÅCúÌ Õ»õE< ýQ>¼!ÃÅÖ¼k4’ó`Òéµ™ŠIGÆÛvbï·¡‰wÍÈI íöIãÜîüäêmnÝõH~òÐÛf¤p—’µxØl““ãš­VúrúPÆ‹´„†”Æ{„‡ñ£Ó÷£m6JŒê”îô™@ÀÒÜ«;Üû½ãþ×(þçúys5V•´}eàa]{=qnR•Ï#`‹‡%–°—m ê¬á"4÷sä‡f l˜’eñ}m÷ÖýÜZZ™X³²ñ¦@Å’ð yi\?Ô|cÌ7',‰a/w(ô3•¸ÿÒaÐ×?]vÁ­ñúdzJ¨Eþþ?赚aƒó åƒW -–y ¶hUn "öw/ì·Î@ɯ­÷¬=šDv4½iõ"€8BØ ¸3L>®±Âî eÒ¼¦J)"ß‹,éÞb}}[ ê)áûöE¤ŽFÏÛ¡¹f$uø@eW*ˆxÙÒÿ’/NÙþÓ½¬ìÞÍö•C÷Cõðt&Ñû³Ö…Ê=¬7áþþ!9ý¶•P^<¥Ý ×þ7^T9ÊXЏÅ«MÏìô…ûtj9 &‚mk§@m¤ˆ‘ò ó"—UºÀ¸¹ænº:d ¯Éõßo¬Ø GqÜÀ*ëY³ð+Dé n=$8bnM÷ZHšÀÖ¶vurOÙº‡Í%µç*sºŒá°~7Rn²ðÂ1ì·»/<Û.)/ÜÏ>âÞØ˜7:Ì ¬. ²R3;‡‚ˆ0m°5!:•¤!ºÞÇ8M93|©™Ôo0š1ɰÞ=°CJOk-c¸–€LØcÉäîp~G¾<­œ aÌ呱ޤû{Íúáq×d»gƒ%Útn{@Èè\·ær¯C,À<¸¢½²îD#û—1¡:®¶ÐY’Ø ¡¡T|oXü8þT¶ÎM¨ë”aÃ8"¶„_C§G¬©Ç²É§怒ae©!)3©ú0Åòz,wp¦÷“ч9˜šM|ÊU&¿ñÂ:#‘®{>Ò4{v™•ñ¯wY§²¿8·8ÿ,œï‘ãmº½naª„âã“„çÍð_ÜÞ}’ZoÂ©Ž“óíÏgr;GÚd«è…ÒGÞ Ø×4‘$ ?œ# $EŽ&?rjwüKô>ÉâRlgì;Âe‚®ÓD+cìýõ¬X]Ö?ˆ'Q˜­^¹bÇPójLÒÞÀêÎjÄ#íg=b©Ò…Wý¹Ûðm|{DÑa ¶S¹Ò=LRÒþÙЈ›rëzðûC08F-ÝÚS\®qÎÉ/íj3»‡ÝOºõô"CÏ<ùFîÿ(Vü> 0Ç _΢‚¿ÊØû¡#è&¾¦}ײßÜê&¿˜|Ä\-wÜ|ïÏdq$wÄþQ6ôa\?º–ŒºòÅ0™7£},±”Q4âz"Œ¯D³b¼iF㘌î”oºÉž%«ÌÅ ¶n DsOo#pŽY- M3&€¥2c½ˆ´ëåÈñu˜ôl³„oZ†è=½¼óŠ"Šší!*ù3I©‡mè_Ã-'ö‚£žAˆ-¬ãúÃèîÄÙ¶Ht•Pfôã±Q×küTö!–êòpñˆ—ñ…¯By*~ã »#µÚŸB¿ ÿ!RNH=‚ÕyߢQ1Êfsü‚äGü´Á²óíÞáqûK¼TlÔըJq‡IàÛDY4Àlùûɤ0e dœAb{»æz¹\ Ôñ‚ŸYΜ ØnÚf³ ¤”ðᔈ „Û#9Z+òw beQ6w¤+ÆV'¬•»pf»…Æÿ`iÑÄ”|¡'þ M] ŠXb!z»ìÂ×Ç=×È«€¤dOÅ]|«TÒfÚí°œwØ!ç¨ ý¼ƒOÈÍsödÁµ=­ º©âÌîYµÁmô™×¤¶ÜÍ{kB°äh2¯±Ëšì_U™÷‚Ë ˆÿë¡eÀ—)×üâúÇGÏ\ÑáîHÏ‚ y›øN÷i‹ÕãQÚLqqúåGYw sÇÖ`à{íã!q¾À*CÆ,Ñ?Œªù,,B‰õ«Hà¿áò&< ´œ²ô MÁŠ#˜]´-= U†rᘅ¥V[©Gx%ƒìÆyEfÁŒÍŠ%¹ÛÅÊ®3Ë>þO /‰WŒ#ì¤3bF8yÔn Â12G_‚”†­sd‘‡*t"`,T¦s>ŒÍÒF }E•}¿ J¸A® G"YE®¾“Ï òéYfÓδJJa¸éÁ¶o·ÄVûð¬Ò§åKèÛÆWù4aÒôdÔ¶Q–8+¾¿øDöÚ¾ÆÅ\¤ó¨Ål·nÇ2Ùj)I)Pz(¦äûDô5i§t^¹3&ïÏøŠçRšM'ì|[Kgy´â âDg`d†ô¥bøW¯.åm”J3Ìßñäþó4Ý~¾ðx¥¼‘Úxë>qË¿yħÀçC:Z÷çáWÞ¤^à´³È óSa8ºåtyöJQÇ1æà Bêo: úJ»à N›Ì÷Òq5SåŠæC CrióIøL[U?•`œV™y! ‘’ƒÞ–±£’!¥]Q|®EüÆÌ·sHºƒÐåÿS ¦,?1÷K[4 Ž´zp,W^íƒb/²e÷鋪èò…´e&?æûØ^:Ÿ G‚TÄ Ý¬´”°¶Ã´yÍ•ÑÕŸVOÍe±7Py´%Ò?Or¦£¡}›@ÅÆc×"÷N=L÷=n  `²q+å¯õHW‡&s£iï&SS_ÂØhq²,¶`ÖõJtÀ2ê âD3 !Vý¢æ†éÂÝQ<…Þ_=å^7sã.sì8§ÙvÏ@GÊK¾8gÝš} bò'–;ö-Ôü(2 &]€ –2FõáËzÈ™/YqåXšê#ƒ÷¥Z}’òC1È(æ«“Ò—!{y\Ò FÚ'uÅÊÄ”õÛÑ㈫ÐQ`„Å?÷¹Câo•YZ¥±¬¨ ßj<ô§/¼…ÐFnW½4]¡oÏWÚcK…3&šï"î†4Ž+ Š1Ȥ8f¾ÆXãÉgá?ÎÊeSw—˜HYauØQ¬ ÚxAçdÈ þ´ødÝ•-»ƒð“FªWï<áh³%–œ§EÔ¹ Rå¨~Ž©ïÕ\ÙZ$3¡ƒ|È Ï¹XåÏh¥Ë†±ú‘æ|€.ænÙ©ì›>H3Ev²yÍAF‚ð ;ÿ«:®O$±àÖ"9~!Œ)-ºbbë$SøäŸ…æ•}QIÆ3â#Y˜¼î(­„¨…Ö Ž{;Ë¥#2PÑ$¤WžfÒššÎ`¶«ö!ÖÔ„C‡Vý1ûIÍGìRLuî ”Æ=ÞõÕË\Le`9Þhr/{ÑK£$¿ñH‡t´¹½@¦‰ºâIXÇdµXm‚AF®Ñ’¿÷™£à­(¦o¦TJS¡úö—± ½þâ`FYzÛÍü—m¥³R®„{ñ%UöàÀ.Æà@b³Áœ½(Kh9=Ø&#÷-P•µ´ÕþV=øR¯ ªþö’l¿BˆyóZÅÏS )eã4ùÂÐøÍgfF‚9£_ÈsŒ¡ÇIì#·k¹~Û1>kJ\ø0G3²P Ø4ñ‚ã_=„ÏäÍ”ñ&;Å6J%s޳óÅ6ÔôýÎlAßG¥zŠÈz¤bgûŠSç h÷Ìñ¯á%Y±}>1‘V%)Ôµ©'3AçôËÖ´M3ÈJÉ—À9nô³w*<¼ºÕ3C-M‘|Jj—¦*޾÷ùC‰DvÝhÑB„@¢“Wf‹Ý"6'ó(yêÁWLŒF“ #²%Ï#ý°¾<Úéí¼LVqá:Þ‹·=³ØxÅSÖ‘‰í‰6¡xR o*ø€ÑÁŽì í³Ðggü·.‚neÑ„1ˆY®^¼#–—:3«Ë¹ r…˜N¯M6R¥ë[k;`‡B5ÖCȃ·¯,Î&ƒ3(ÒæC UV€Ÿ * C‚ù¥Ê uØ6¦í­ƒlîòh.޳¼“­=XãðPľ\ÒxÁ;ÛΉ&Ö<îdÁÞ=«$fíÈqÏ [Å®‚´Eã¤:n—¬]tsÄ^5u—¦g@¯.ìKå@Ð%•9÷Ù¤O4v¬<Óº¤¼Ò üPOäûùŽ©*!S ~‰·  ý† ÷›Ðjò(&,>³Æ9tÛáêât[Y‰»!˼¿„³VKáF<¶¨¾÷rèð+L"ý¶¶\Nב= dä%c•J¬ìÆŸ„RçÉêäµ+Áì"Ô_ˆÅ^…Tâqae ùì’R^ÇüŸý/³¡‘¢‹ûŒ8çì€`ŒöÃL­À£rhõüE c¬“>ÖyÑ¥+œèŽö"ILðžÞYãM›Iì»ÇL„»iŸ\{ÞTf‰Ž(Òwž>úõp-ÍÓ“6(j}’G§5¿¢þ¨ûžË4„²©ýrûá~¸}j•åg7£ªÏJÔ‡ã³Ð­c¨žÕ´ tÖgÞL U)þ[däwL,»sê§M÷nSH,¨lªˆuàO ƒXÓs£Õžž!(§Öô1«ô2cÇöŠzÏ¿7˜Ö¼Fnª©G1ËvNuUuý‘[©>6”ôƒXŸƒ°ÂiÞF¾?ã®ÛÈž-¦•4§LóöyHÊp62áŽî¢@I–Ÿ"¡'ÉB ùIØŸehSÈMâ0&¿Ž~¥¸SÞ Ì@ƒ!v2ɳNdDÐV·ùz¤¹”®׉<ñß©Ð&‰Ã<Ÿ$›T%¾cqDO³FÔ ‚ð? þ&4C_¢œ=>à9%—b±xÍŒÅâ"þüÅØ·k x£¬Ì¿9)n¿tO I.$¼ù±–ßFçÅ—iLу/xÂ÷b‹J`ËOîÉë÷[@W®Ã<Ó°ºm«ÃCÓ7¿<šs{ÜBry^.‘}i£ÖRÚF"A ´ µ÷8¥|<Êfü”Bž=*dÊI–yæÊ Œ…£‡£¾Ô1§Çü¨ŒûAäñÛ€KA›–ŽdæÇmöízZI>OC¯Éê¤Q ëZ¦ÏJæùðWç¥KÍ's°Ç(¦ÝÖúxN0ùKµrî$¼õJ Í@>5™­Šj˜¬¼Áø- ƒßÁªŸœa6ÁD ìæÒCVˆ|ßU~ŒÀ³ÁŽ=Æ„ÏK#¿—$›`%°,Ä^V“fËáwÿã)»üZ^É̃a¦r;E‚o¼•-Rœ14>íšÃ¡ÊPÿ÷ÜŸ‰epLË5]9 µ:njZì>¸p¡…ð›ÏR¸¹äó}¿"ÌË "Åý‰Âkñ‹áx¡wü¢Ùžöpü­޳§Ê/¸/ÐÙ—P÷/@þëø<…V-*É@ó›iɯAU¯ãCwt™°åîëï,õzz1з9g³o¯u;o²þò’õB ãr £tEzáyƒT="3C2€ž-ÿ2ßÑ%*ç €ß/a]“ ËæœCÆ-¾ œå²¼aá+ýxœzad#ˆ[VÛ^ó5mf—æà!—ìb`¾w†ùëvF8a'ÄkÿÕ––¾"SoÑÎrðCØ>S‚š ¾P’@©*m²@°iü¦³vÏú—OyÇÓ&Þ‰ ¡Òò²xÛR·5*%>ð6S5¦Wâyâ?¡4¾æ6ÎnElIˆ¨›jŠU9øüKFÖÍ~ê=es$™-›q[Á ÔmÂô¾õ?Þx$çVØù ¦ÈAz?.urð qÑörµkÁ_†ÝYýFÌA*¿2I{÷öÞ¦ð•|^žÞiÏË9ã–­îFð_œ­šµ¹4Ð@9FO•7ˆý™è¡rëLô7 õ ‚Y-TÎÅì¼ÞdüŠóò¦ÿ9kÖ±@ýNNù½˜÷y¡Ò ]züvñD} …ûn! „Là»âòð!2%ÝÑ¿No`Ù*AˆVm1øVù†>vÍD€È&Í¿«FÑœ6jF“^#àÄÉsv¸„œn¸Ä”Ïdüþ+Í#F°SG¦ËCæÓ]Êæ:¶”©¿ 0Ñu¬5¯ÚC~‘’Êj ;èG¢W4RR3ø?Ú%`ÅFh¹ó-¹ö}ÍzN;Né¶Z™¤!À:³­£É;¯å¨]Ënd,² {®tMá›°8º²`ÙU;zòe‰Èó†÷²K·:çùÆùRÊy/ój£´Z¿œC»±ñòSÆ—ãH^²ªö3úŠ;ž ŸdÌD´Lr"š±”Ž?S®âò)!Ò7´ Q>~ÆŽ„¡~@£±Ë ¡~”´æBå¡/xÿÊ­Æèס0à8S—Wº':H—q_s0¿fÇ!À™Lˆ•ä%ÚáÕÏkõÖ°DÄ<Ý©ûžfZdC¾ æf:‚*X,5Cä^%<žÔB –ï¦ê_Ë|N#ñ²<é©YÄñmU°í:m%-Ñ ,Ôu‡“[]OA¬ÌÒ­+/ø6ñº 2¤ÿçü:4§u~ôÎÒ}#3÷<é’ÉåF(ˆ òcxæiØŸ~1õM«Ø©ûÇï‘+â#¨Qÿ’ÍU¶@ÑÓ˨aC‘fd¤¼·v[¹r%ô»{ïãÛu;ÀŒyŒg­hýyy^+>T›Þ<¾ýRз»úÉì%ò×Ì$Œ€ƒ¡õá,¦á6Döt“¹÷ÝJ,5óã4Ý•EúiŽâ;ÿý]-cÏŽÜD˨nË. 8)G„YI¸Ü?¬øaÒÆ‘¥G1êå97”>õ0×餶Z-3Ì%Â#èåºLrU¤õ¢²œ*†ˆ?Ýò\ÿ b¤múˆ—ùáòáå¥ñgf~èÎÌK!eP>»Á`´ÏIÙIo­ÞŸó‡]?×È´`e±¢¥ÃÒB´¤q_J*õÖiúÊ'ôùâ°»Y¢vdËÓ‚7àü¢úbLÔ{åRÿ =‹ ”HûMÍòÀŒáM½d&´®›’©.½¾•ºq ²º73•‡¾‹WY'1à}¾"#Ô„‰I²oK±F7†i ×ȿۣ#qþÈO9QíYÏ&æ4M®Xý1X;u£×&õVl¾Ö—þ÷ƒþ3ãÅú2®¥ü½ÿ˜¤¿,¯LÜß¼@iýyój/ùñÏÞÔùESû+ªæ1 ዊ1êÓ¥©gªüQp~/Ò±ÌÐÀº«2ï1& ‚ÓIG%ï~†àá6辋?Yšº*вk!ÿ§¢’÷L¯}òw“‘P§Œf0Ÿä±{™ÕuԞ؊$¥‘7E‹t2¶ÌàbèeD¯fàe§Mjq.Š¡¬ÞXùÿZŠ=Í•XÙ_3¯×š çCÓQ¶®9ýÓ9á²xQc`sÔHDžSÃ3µ-ȯqŲÁŽ’ý$  êC†< VÙ@2-È p'TÐRažPÀÕ"Ë1΀€U%ÍèäW˜*¦V"1øÂá×ý>•Q÷.ü®¼RƒÔ±`(÷6{£îyó&Üš­’•Æ*R'ÿÜrÏ<}¬¹ã"&8G°—¨ŽM4Å6êUú¯×-ö7„׋'ƒõƒ"P;ëõ?[lÀ دž(k¨êÚ¸RR×z ÖlcË6ªÕnÝLÇ(þQ€©ü?þ“~àÖP¤»ïÞà·pÊ;ð)f5ææ]ýütÝÜ%ðùzl­ÜäÝWûÕª$Ÿ;”¹YH´M‘×" M4='Ý™| ¦ÚÎ`4“tººý¾>'¶}¸Ör Ø8Þ_uP–]­Ô"d“B£¬Ü-=!Y“TŽÌØÁ‡Õ¥£Â=7?GEErY$C·@5D(â µ‰Þ,ªî7LWò‚U•ÐR¼ý*ß[5qž¿Ì«¥8BUQŒ°­ô‘eµB‚l›’ddL¾z½\Ó ÈÿƒUèZ5âURò˜EC,# c.p„^Q‚ˆÂŽ böûŽl˜ôá^_éì§ûL"ÜœÁmPqPŽÞj†]sPÆw¬ØO­Šú×àŒåz^¯bqÕ‡ˆW ¬²àµ: ß[ˆ'Ô0WäU¼Ê ¨ˆÓÛ­:fü¥ç‹Ææ:­Ù¸Wé÷ ˜!„’%qæø% ÷½S˜‹÷ÃT‚#—è%YR§’”ǪSœ-ãŽsÖ®Ï༹i–Ømg1ßo/þ%ˆÿÀįŒkè+@pá¥ü£øiw)eEÉ­#u½×c³Äd3žónÍFÑñ”¿ ¢¸ã“DŒЉ’N³,y»& )øò™¡ªy†2™ZÚÄižŽAx_`¡TÛ¨QÚXDFÄ*dTLT‚t¿Bè? ú|/­ ^³Hq…WûòþOýÁ`›°RœAŒ:( CBå­©çÂÁh—*%â TGþ§ŸÅ¾ƒ‡B_.Dçþ’l1 (É’Îwò ãÒŽÍýê6^1ö0vó1Š×BcSâÖr8ÕD­<#©k”,]µ pm‚ùyTŸW«zæãÐÊð€½ÃýîNêƒ23i4‘Ìû`r4ë7%ª·;ÓÊ8 Fyù/ÁËp!+BÁçxCp œAâ ‚ÝÔ¾ JÙ[±º³B|0›v<Û^·‡ÞÙáÎÎvlB̲mÙXÈíì ƒhñ/¿LaNUóÕ “ Z{ˆMWØ„á3Ø}8¾U!)H¼BU¢„ v„Èdç‰×6—Kzê«ô‘‹"Tá½Q…³Í8 BUšûƒ‡ƒuKL-k¢î4?»­\SÀÓTæ^Pºo¦q¼¤k²’Ažb«í°íW¤Ý£ÏØflnlEÙ—"W£w!–ª°ÔÖÎبÈ9BÍw,·[Ü3<dèÝ›9œ ¡®<´Ý³†€šcAÀ–ß9/U±×4Pü»>ˆ»¸»ÃÉP8Ž€’ãU§~1¶½ŸˆÕ¾”æ òJñ8·ažè¿9"Ò’Š#w¸”žÎÔ‡9š ý3[çL©¼\¾ÂÅT¬yÅJ\t=N\‘BÒ¬ëÙ–;æj€[ÏÏ^×á¹õÆ9UÐ{7€ç¨²šù·ÛȪj÷º‡©ÿ®m=«¶ËUèÕíÿêŠâÏü˜„BfŠÊb_NüUíÌ9¹0P@gü˜èM™¹–„UM*™ÕÍaI:ˆV˜¦pÿý¨®|ùF™pî€*ü˜Þ_CPý¤ÿÃ~rý?§\í˜Í¥ÒlÛô¼vzífiM¯Ÿ§ö–¤Çv‘^|e^+À'ò'í~˜¥¬ÄÑeMÑÄu¾ÌÀ1ÞÝ¥ÿ‘éa—Á®óHtOx ;qøŠmí…%mªÚ¢ÃòRg÷v0LÚF D¹êæMcM|Žäd"8Ðq7êKŒ©KÌß5R‹ÃºúÀûï0>“ÔÙ1ÇæqâÎaw Í¢<Ãýåœk»ÄÀ¦CiŸüP0…iB‡_‡HÓªP¼EÙa’W±1_œp^V=§îïó­QÇÖq€Ú¾®RŽ0öµ.(lò+¶¤”´K-$¸4[y;ŽŸ È…4`“!ÐDbE‰þÜ1jN|…- ÝåBÇ+UDaw [Åí g ðT€m.ãbvé+’Ÿ‰àžLi”é+Íf|RI{î3ï·dPÒüyŒØcÞ_üXQ—-”×°5˜Pè“ýÈTö`Õ·~cTü$Íԫ¾¬±¡£–U¦±´ «Xo–w¯‰r·éÕ]Ì}jcB“ð ·»LN.Çiýjþž 4;û>ûè­¦/ªí­ª´SZmÂv°05Ї¼j’Ã{è]‚%šJǪºšê$ÏyxrOM`ˆ˜?ééh ?Äpø)ÕðùI¼ÊÿÀiR×B¯ì?Ñ›ôÖj,òõ—ÃÝû NvL¥õy„SO¿èþW»ÛO}èåÇû¯éŽã‡æÉsÜüÝDn2%ø´Ü¾®©0,}žúÆpØŒ²¬]éÏÃM÷ú«z|Kͽ!’þÁ齄.âï¦î¾ÉCo[á&Þo™vG¶IYɹg„—›ˆƒ euI (Öõ#Ží8÷,hÂn¶ , SèÊ7˽FPAÅéÐY›ÎT¬>+(YJÛa-æ)fcV—›ïÀ€&!ä«['BÈ{% ¿òâ-Ùê¯)‹óézÑ'°ÓañºKéjÍyulCH­"Ñut-A"g£÷’½VijfÚØéž§ éš'í,{€(ë‹DÊp›¸ŒY%ü4½!„”\EneÔ¸šž(Îöe\Ü m\¨ñÀp5´knÄ¥ÖZ6n ;~È* ` Ƭuc¦<â&䪔$`ìL-0=È!i}uÝNÔÃfåX] ƒ‡^€±Ÿ»»q4&x ™À¶2,+ðŠ!÷©iàâ](_hÉÔöï_¦ø'²>€Lu—X! €# Y-Yü¥‹J…]ø9ø‚ÚæX ùhïýmͬ-^“¿¾y熒7àL:¯Eò•$» ç?櫎Z›ðýÉN쪚_슖YWoí•K•Žëª:‘ÑÅŽjÅ\‘µ7{ˆ±0Á| •¯Û¢ &c'c8`­F‹2&E¡[‹—õŸ`Æ· l + î¬a|²¥ðß&qŽKÄ’â}ÂZg;…Âwv`\ê-NAuXWFØìø-¤ƒcµN@xkàŽ½t…ˆ yµç¨ÄHd\K¨iv0OVÁ¸ýA®ÆŒÝKÆß»S»Y$?| :üÞ[ÂQ`1)Ì>˜Œ$ÀœÉÞ]É`¬˜e]K¯o>t8ò`otÚ7¡ÝÌšŠµØÐüo@[wö‹»{C\DÕØ×2žÉæš%.Ër^Ÿ1O–ùéP.‚Ìîî‹<”ÿwN+ÊD¿àÛdN×Ây´Ü!tcñ &j•©5þaÕ»ž÷33(1Ë‚¯ä¶ôØZïªvoU´ZUC•¦ì{Â)”·Íá e ’$>€a§xÎßÁê{föõ€Û»2²FüùJ’/¨Š ŽO(ÏoÀ«{Ï”÷t˜ò,ÔÐxœ\F¶κ²4ë‰V1ý²h±¿V¼W¢>³²¤íCP&p 2÷%e™7¡2Ö%i]¦hÖHÜÍeͺ±þeÏû ØÔØâ-‡~3tm=Á„’u$d©©S&Akýq ¼ªR4_Åq£† 94Œ§š]&0sЄË«©Y¢Á’,md¨zkQ^]iq Î3h 3ø45…±qQqY«×G}•—s9¾ÿ°$õÛ/îA÷5¥¡mºlon+. 9$œ‰< Ô…ÉL|D—*TÛ2VíÇØBËY7Ÿ >8]–vÓFBçž9ÎzØ”½†8ù2þËÌvÍE-Âʺ˜ÀÛe5™~!°Žàu.–Ë*|{Mî=æñçö>ØaJ×$ŒŽË”VâèÞã|m.‡5 Î]L°£~LÜ/x‡TvçØ1:ã ÙA_ýò¨ÁÅËmÅl ‡7¹v[Ÿ¯hŠÁÙ;ª¢¯qÃ&~±KÉÈ8bR6·”’zÌ/1ña£;Õ YE1ž/UM“Ecu$Àé’G{V{Mâå´pèð¯9;êUQ|™ÇÞœ3n|f!6FŒ"'æ†8̱„vMg²uæ,ß|3â ’ ’ñ5Q‚Y ­œ-´ÈC‘Ì'sç¤Ü‘U©o1F%B{"®*Ÿ£“™½ìÊ“—Öà•ðk”×­|Èø(²VŠ c4ÜGÂBO•Fõä_WäãJzÇO12ãõwb—pLvÒ%™{Zü ˆ }²·õ¿ÂÎ"È©ÄÕÅl1Ê-G K/]K™¿.XXì(¶„äù:Ñ㽚“D.ÞEʈ”ÂÝ ¢îˆ“†â¥¼˜V’|‰`È ‹Š$Ô¯þ¨ËL.s2òÐ]ÊJ'Ÿ|€iJÅÆ½U÷&›È½qÂQ•¢©®H†•ßb/G‘k_»“®¥òÚlàµÎ>‹³ÑÂår8>zN‹«Â=}±¬Ð¬á/‘nl—{´n£+YB ëÑBßí{¤< UqÂ8òÜÈë7(VGz¡“ÚBXšÛçªëtÖ0ñ÷dê®Ûeâ Ûg|µÜ°ír+L’ØE|CdÑlú7® '%ú+†}·+¹&•žOwØ}.Õ•*–~i[_k: ¡Ô’)`tÓ£ÓdÝéñòó÷û ¿ÑVdõ¬eH+9™Óa:Áó¹HüXïã× 9™;ûù4¯rβüH¸‰5l‰‘Cvµl«Z#1Zßë°~¹vŒšaj6ºÞdÖ˜C,B_]ÃVÄ+Xdežÿ³]{¶ØÖÓâSªA i MEÔ)‹AÔÂÈ÷h® ðºplæþ"„Ö¦ L¡™cÜ^5„TØ1XE$À™*É ²Þ­XhÜÿÃM,Ù똘'Èdéjße"ÓÝ×Žê¿ø¾mj¹ˆ¨ TZ^U½(·²=PyŸ´fŒ?*t0íhÝùha jNeS¿;|–*Ðéõ[d¾ê6d“¢6­xz“õÀØ I­8³`x§—DÐ1Sò ðë$Sæ^·áÝ_!‰W“{ä‡%ƒX:YåqMkÙöÆõ”\“ó»1Dâ=E¥‘6¤3%”HÙj Âó ¥Ç5ä™êöe\bP:~ÙN‹Ä£ómùºG<µò²N`¸¯.ö)ç7F]ŒÀ`!ÊÜLBÏ©ÊÖ¨·¤œì„ÇÈxg“˜€ñ³VÓU:H­“/O0¬±-K¸GRñ_7ƒZ qçm–‡ñGÎy¯z×x{ t±\¿¶¹T€ÉÆüÊX-^<ÒV õ€•1±‹ƒ@¦†R­žãY?y´}˜¹ÞK¸Ä·ya¢Æ÷b[‘n#Jœ–eÀmÝÅðÊ\Ay¬¹BY’¼yÅàìŒ2Ð+¨˸¯¹æÄycŽªÂ🚸vkúWNç×u¬X%k91ƒaxGX,Z³íî–LÅ]{;dst`dDsjéÅå)‹÷´zã è‰o»°{OÒã|œþCÐaö‚à‡ËE}›#ëTšçKì&¼9Md$àu hXTc¦ë¢2+ªŸMû¬ÂxÏÀ¼å4/³6l!3}€U”F;¬H)^IUû5ÈãGFLWNI»m¹ãå$ Ó!LgÞs©w|7dõR¼×¶†.øŽÝ¼bÈ æwoA8.gÍ^zxkè|& ¤²`ß klõ1 )05˜‰Ðþb`B´„WUÓ.Æïe ¨ ^¡ÖX½ðšXø€!Šu»ïŸkTÙ°,‹Ú%ÑV—ó¥«åë¸ÄþGœ…¯ÞÅÄϹ”«ú½øYp¡XµëÈyX×fDv¹è†iôï3‡9|»ö±²ßS0?«Ìûs‡àT„Fa:X„Éæá?ZRùcßÀäЖ‘Ó{²(TÛB0by™j¬O•ò»×ÔùcmáŒÄö§€Ú.6¾q!]{= –‹í„’K`âBË-MÕ9A¬ »D°IV¶:ÞŒÏ3¹ô/›zí2%·{·àÁþHR™æœ@ŽÐ„Oá~&†Þ Ϊf­VEó+“U=XAh}§Z½‚pBÅÝ!¹µ¡¶Ó„8:SÇ®{"G\  bD¼ïw•¦nbZOW5úkRí+"Éõ×qö.-»®îÛ uXÀá”f\ù^Ë(:L,sï–Á“Ì>Ð0)\!9¶§]ær‚›èc£†èðI¹fÊç;n#3À¾ÎîQ«høò5šMtÎ7ÆÚ`{Ìzfþiáªrb­ s€™ƒíŽÞHü+-#Pž~«RxX¬IK7më½8 2Amw¿Jð-X8q.Á@‚â‚Ññø¿MâjјAñ²È£?OÃåOdFEÂs¤KÀ–¥ò³`Àj¬œ¬ýáµ`nçôòŠÀ¢\¿&·0Æ ìH ‹Åš\ÅI¿Á4d‡E#‰Òl Uv0a?b‹+ó1­·éøýÐH˜CJ©ìB¶e‹ÇèøÆ Mñ…oÁŠä$õ€ B[ÿ±™íL|)>Èñ^¹T853(‡-ןÅtïA@F=ì ÎÉ—ø9³):H®=g‡ä^že!|â$MHñ“ˆS‹¯r¬ž¼ž ƒ.ØÉCð6Ný2ÉQ”‘×ÁbNóäþ—©ÐlÑ6Jb.……]±– fíÈž ËE»P#Zvô°»u­s£Ü³]Ü×þÓa»;9âÍ—ÄÉqbVøâž4ÂI‹"—ƒ'Ûy¢º„ ùKÖ=0a¦vdH0bÆØôe~»ã8.Te’çTè¸0mB<¾$Æ›a%_Ýòs.]›ÉÎ/s¥ž`~$ˆÍ£ W*WØìÎe”Ün—(*)ÙFëuÎø B¿ã…ˆ:ÁüvxŒ×pùrU’BßÝaÎ)ÏsP³)žŽõ¦o~s!3²÷ö@|Z#ñS¨€Ø¢íQ5Õ¾ußÑ ©.,#²¼¸èØlÂ7þRQIø$¼ljñÝN±? 2ÚuŽÐœMÅ•Mè”óXnØ-\Er)Ñ6«ó{‚ñ…i”`ŽMZá®\»öb´èö!þyŠs» WTÈ7,I¼KØ.ÜL͘…ë|ê8¬\°)( oE<#ÃG.R:Òè5kí6ÁYB±àepëF3j'¬Z™(áð8qˆ¯‚â‹€s—>¼®w½);ÔRxåC, %h¿6˜¨Hy-$ZuÒ¬2$7öÕLP¢Õ€´ÝÝ£pRv.ò »•”&圪'È $g»¬îö²ÌCH.+U€!Â2[­©JÖVSFª/;µ× ;Š^/¯szd½E?lˆÌFX™×‡C}Mƒ‚â&Â)Là;=Ž>îPRqôç¨1B1•n.”'ÂËWµ¯?cÊ ìÔøœ|æëBŸpß)‚}pB“eó¥Ñ_eg—Ô’PE¡ÄY/¡ÑX²„ñg^p¦Ë«ØÇ9íbꜗñIÖuëÈ„Ïöÿ;ð·„ ëÉÅÖxïZA2!³‰uПi%³&— A{7r’¥ß6ÕÑ5¥ÈÑÉgø"G ñ]þÌ·k=òƒúžå u0Ë…ˆˆÿ`á—/™¶ tÌ_!i(L(ß]ò Ìyì…Ç.0†BˆŠ7îKƒ)%.º\kK»€%ò'TÉæè½¶{*¿0px1ŽøjŸi3²Zýv[ÊÕZo¼Ç_¥ŒÙ²ÀÆúo±¢-áJN°†G;ÉÑ2/` m¨¬1á‘.ž|5§MÞ„"ô¬f'ƒI¦è ¬m§fø.+dB±YÙg"Î3Î(iLã>„M‰1¹ ·2ÙÂý\Щu;ê¤`\ŸO„CwA>Z>åѧ[‡8çz—7KfTà3b]÷Š[däbó1Ì‘†ùw;šF&ììŽèš‹Â…”![*%ßÓZ¬ú'Èu–/rôíú˜±X¥·{[< Ï‘‡g‘È~‚xÐCUS©™}Zâ¦ú>lMh]¸‡ãCs*íõž¯ ;QÍ‹ ÷›¯zÂæ)0‡r(³V1ŒÍÞüL î[:ÿLàÈOûÃv¢€‘`4Ì£Öò×JúõD ÉHž@¦~“Ÿþ«±8ŽÀ ¨v˜BìŒm¬'9jÒ’Õp%r£Oðõk鳋2K©!E¹»¥Wöp›–ñ++”_[ö“.¸1±nMi9EaÒdÚe%¿g9s¾•b±¿ÄÑ~T#€Ê 6ŠY¦ò«ìýÂâËç ÿ•Úi—T³ù_cíðÈûÙy-¡Æ ”œã–¤Ô<š•V<%>qyç¸cuFêì­æ‚1ŠÊUî+Í¥k ¦ýþÚD¯DçìÂk.MÂÝÃÎG#„7‹7rБi¥7sKÖ-[/ùÙ„cìЛàÖ­ ¿G y£‘’ˆü=Ü©-Í£a{"æ/õÄ–*’”0ቒg9Àœ0Æž•E£ÚÔc°¿¤æ’M‚¹’ ô™™…ª ÚjISñëdÒáèƒØ£Düñ2:Šÿ¾ˆ7¶@ýÀñ›¦ˆr”#;#B'Ôf‡+‚‰€ÿáé9rå÷ë8=ÙåÅñndœnñV%þCøá=¬‰NnÂcY‹YɯÒrî"q毲a%Ê:flœ;®ËŸ6¯ c„yå«–‰rï0eæâ+xÿÊ›=UùVWŒÎ玘Kk×d´/6nèԿ׆ÿ®òVsv볂Ñ}oyõ3’=_YÃ1 L»äYt± £³Îf@4¾PE®¨»í´>1bªx–ã í 5 $êü–ÜXEy+ÈÝ9p óMò«Øû'±¿äñ¦ºÅÔ‰‹³|?½5$ä1<‚lCGÕà•KWî=œbjI—äèÄ-Y€o<ß¿Ð+§âÕQØŽ·N¶ a*ymmf]3µ%è æÔ”1´%¿,|‚§²“V—‘¾ó+ªÙ6Š£àv§ù®’àSÆOÖ£R_ÎÜ“‘ÿÚ‹¡z÷ª¸\ò h8‘dÆX2%"`†;þÇû•¸‚::]"ü/Ø È–Fþf+Ñ-P˪Q@v’ÅZ‹+±¾X‹ˆ?éùs!†cºxZRÏãï⥺¦L”ýÜUÙ-íŽY?Gcȹ´a N" '΋–iRë÷Ò™ã Ò3À yÛLb½ì’†}Qpl¡|˜æ+‹©²ÕW›ÍáfÔ1BpÝŽ‘,bÔ¯gAæWiAÑäÂõš¿X궆!‰ùÖ≓ê‰ä$qiÁЦ|†{ç3}‰ ’{½4hð¥…)-xš uþ0ºóé—:r4 /̳lJ@”x¿_|º/e×øþ Õ2×Ú¿ .Î{ØÀF=—A‰Ü’Lfgn†+~Tq öGÄaCŸY‹ Ó9XÈñ³>·Œ3R $äCãü]…tþ„\ã;óXiÁ¼¶Ð\w»A?ùïÿRÓÚɤZùí²Â )Œä¼¢RûKѼ§ÂÈ+ˆã‹Ç³Se.s51kP› ÉÖŒè¸A ejí±’*Tª0Â4s«Hã…ï`è ›QŸf kAó2ó<¢«€¼ˆ?hšrÚ†1>cNÔÁwááÚn©Sl;"‰áÝ„ð¼B*yžm÷/7“ž€ŽÒ üð¿­÷zˆ!¬5ËÛp›× )Ì¿ÔÔ†öžÌĉ¿Ü DŸùe§W·y䓿ª3w•P[û`J’• ì[¡zgÂoç¬é±=ðŒ4ôØ)ÖÑá®6›í6hó] ²b©óƺ0ÿGXœ4§“³×Ü@ê÷¦»Ùh[lŠ+Ècý´9Žeƒ^¨%bD¬€íQíú÷pÛ’eÀ!Æe§\W ŸZ ýØ@Ê%1ö'kš ç+¶"O`¼`9m‹¡t¦¦ ­$¸{kÈöªÉì³¥ÔÜdÐsÚ-§åÙ°1š—uˆòεMà}XüÏ­ßùaèŽ:›“YH×gÿÐÅ•KèÆJ; 9ì Kߪ„<~ÄÈÇ¿+–fÛ€D,ÓGѨûÆ8&|yÈñÄ$’Ðe‡á{¼ôSü»Ân~€T…–ïˆmàDvNÈßÚRhu¤DHé©cDz±ÐU_3PG†´2ù!%«y› t ¹SFÍ¥_< ,?¡ÈZÒŸÁ=)¾y=O’tÅt¦^}÷³Äˆ,–KÞ•£ò@B¼øµÑ).›ý úNX_}§"CNdçR±øGÍiÛ¡I‚«Œdâ¼çjbu³G™™ÍUzAíý£ =ð ùš‡OÖ(XHßî:ƒ 7Ï ¨‚zˆÌƒµ4ü©{ì¥Óß+«O µsY0 ?->±I0-ý (0Šÿö[0¨bNtS$œ|>ù?1]÷}3Þ¶Uÿ2>äÞì ‚nnèñô€’ /ТþK~éí³3ï¿°è™LºþÞõ)Mîµ¶ÍØ6Æ[Š`’˜ý, د.ÅbO½ÅƋز ²Cïå™Üpô9¢ê2É6þýçƒÿ †ú¿LË!޷ИQ>æ ?ùê0sÒæ¤^᪠ð}‰·Û¡: ·$S@êߣ‘™¬(Ž"až ¶* pAUpVïC½;_L!O ,ÏÅÎ vnØ]Àñü½„+ۻƇjÆ,ùÀ'OíúÉÞ'è'À SMe™’PŸ;2]úÌy…âÁÕåK”ûb;Bh ‰Qd|­jí·ü9ç 2áO#§,ZR ÀßO]+ܳlÏg§›v–gÃݳNP]{ög»“¶ž ›Ú-Áö¤cçIû ÊܺÖrƒ%¦îN_v­Ü…m…÷®y+Dû%Ì„?[¬N‰KÒºÕú³ìKT6ýËž²inÙ›¶ý ͦYOÛóž­··Î³[y»Ú;mv:4ÅþÙ™ùÙ1å[?¯CÏ»Âç6îݶ;‹Þ.fÊ)è)õ¸ÇKlcø¹°b¼–ÿöHï çÑmïÆV©€çüaÓ¾Fÿ ÒtgŠçSν­q†\+Çb\‚#»4O†¯|o„6O²¢¦œðDÕÅgï÷¸õÿk_»àâ븳w ùÇKï—A̲}WÞ?¬°w”A*Ÿ·œß7Åt6J p“y!çÈY !ºÿú¾"&€d‘‡²Êðwéܤðz”xuÔÊ3P¤Aʨ¡¸ ªÈ(¡¬Q˜ÀÇ¿8ô4íö(õÚvuÆŒ¤0×bŽ” ö‰©É¥Ê6k–Âw€ÈRŒh<#‡÷9Ú»£ëDB2G“{b‰èH:Ì m³w†o¢ØqÜþ5†þPŽùÊú¡Â&!˜)‰eð2ÀÛïJñdñBL™}¨oÎq ‘o£X.Ú,­§¼ ³»ºl‘Cõƒ×#)1r4Ha·~Giä³?DžÚ&éI™BÕ=Ì`¯Àô*Ù@A·oŠô¸,å¾EF¸¸ºÖ\1;Võ_•w%H19žô\aD{‹€õ9Û<ôå‚ú? ÎlœqÕÂ÷Z¦Ø¦ô0/ÿô„»H€%£«`ašÚ³\&¸Ì€* ýœ¡1žïÏé¤ÃÛ³²oa•œ‰ ¡Üs³tªF ¾ÿ_óßÑsû Åí‰ׄ<¡ÂŠI3®¢¿t257Yõá<3.ÜÐÉËטEöç‹~a™•p‹þ€3V5#nUð³·yÌš­IB:„6!ó6ú€|f¥ ÚÐ%×’¼YTûm!õš7wÞ÷šþý™2š€308npÀx¦?Ÿ/ñf¦;€ãÝEwÐs3þIÛÂK#íbÕ$¶ÄzÆ&[‹¿#µ*­ãécÑß­ãz’X[ñÇ£‚ÏÐPp±…#Z<#HÞ¦çÀ ‰y€!p‰fÀ86›$…M‚`Бšºfµºm!à2§·a&WÝÖb•#íÿºEáÝp¡¹ŸšNú ˆ»—™-p4+šu”j·ÛñÎjÎt¨Äšvóë‹ð¤.Ϧþ{üM–J×€§·¦]M5% +Èi»\>crÉmæÿÙ°õœ¹^Js>N›$íÖ\.ëõÇÙó³ ‡sé­zÄE. $)%’ÂO+ðIöÂ$x ‚ .•_ë2›`jœ5B0"týà„#e½ø½v ^[be¶KKtmH‹kÁ~°Í@CÆÊJÂð@p0½QÓY˜—µøŸsÐgi¨&žVûèÈçÁ‘99fºÑžn¤/sËW¡4mWS_&nv·o|« ŠþûPgPÆë|C÷:ÃÖ>`ñà\ù¥줄ðD³ €ðþçΊOZ°†R ÖÀqÛ-£"©'¸ƒ PjƒGš„@a =ÈM¯Eƒˆ ¶7ñy³‚“\‚ÿ0Úí»5ù’DÎgMo>ÆÜÜ¡â5Έ2)Çm# 5£lÍd&×(~y¤Ã»¸ó²qk(71’+N‰QæzžHK"I»!ö׬X\ÒBÚŠ|»Í§ÀQ ¾øqÁè!Û ö3ù"¤Ð*Á;ß½9|ˆ£ß£|Œüúh­@á˜âú~JÞ|áÑñr|Øx?—Ø$‰HË/.¤„w‚ßÄ t|ÅcyÌ3%8ßb@Ó¡†CòwA„p-daäùaHï|ÞìYø- ïÏGùÞ $NÃz{÷­ãÇ„ÙõËÞ-/•ö=mJ)ÒúBäUGóq ì’»ˆÊöa—¯çÃ|{|JYAßXÜÔ©Zï$¹™g ãÂW®éoºî,ö:ß:/õ†aÉ-­."­ö(j¢è ïŠÌÓÝ¡øŸóÛ“ËØ8AØÄ€!‚Ùôj·PqѰա릕†:=оrš3ñõD g.0|Æ Æi´óp·ðˆ`tAÝÂîò:ø”¼›˜MÅé»3L).…õm ŒâÔßrŒM93p>#…]Þ’ü¹…Ú¾êí?lg†ñÇ$ô!^þrœ¤°Ãà2aÿÜÌÚP1|PNâ9Ùn»ñöX‡ø²=SÇ?þ;žcÜxô`Û#‰¶ãbïIox@XsDÑÏ‚ˆ©*û~¸Šúw¢óPÑK°ö2œ#Ä4#ÀUŠ^t_qä8zL×DɦVÉ ÑÐõ®×ë=v“Zr¹.ñE(1–Û£V!ÚkFÊhËKÆÃÂ]#1OÈ]Ç…ãd¤Ù¹a¥'+¬ÿ]]ˆ% XÇÅ’¦—O^œ»°ÌrQà&¸çkô:ûÍêt8 ×]-T¦ uÌq7¢0Æ!Ù=Þ}ïM\=+(`8Ék¡_]ªÆD‹uÙYJÚÞ5‹{äÄöñë­ &1YÎbÑvÔÅNB§Â‚Âüöl¦ïu‚ó”´&« Õᤴ՟d²Ñ¾ÿÅX›KB©™ËzÆD‡z†ýD¸þþ‚z§ujšg¨þÚ“fôÓ¿¨ùÏø¯õ¢ñ4{¼¼ÈÈ“ªàߔ¾®«ä(«ÚªÖñ•Š09õÌ_ç×à&P·Wz l‚²¾ººàÍjÀëKUX§êPZ¨¬ê¾<±ž ¤ ¢ÊfцòÅ``uÂÆŸ’²Íƒ5)WLœßEc´ú&ûPªE_Û~sÝ4È_wÝPÈüÁpxö.俟cåŸJÔ§¤1aðq¾ÂHü !XV›Ô“(,­ðWz ïaf$Ͳ ‹‰ý?è‹ÏÉ|L”Ÿ_ Õœ6›ñ¨·ž¿7¹Qšß`(ûß"=OhÇ~R¶ÿ²ßô¿fü¾©®¥ø4Z=k=; «q´0lŸ1á¤NTîû´šÈajT>éÆ[Þ3ïü©U\8¨ûÞÜFþ·çíÖYÞlÑ-Çf«[y=w;;ü:ÐŽ{‰s,õ’7®›:f¿…2V…Xho§¸?R×Ïί0ú¿RÕíåØ'd1}Á‡º§t¶ZU­[#G.ø]÷5uåòweþïy§ØFù?VK(8EÓÂÈgê‚öïÖüù+ß1jˆ¼Ëνó›WeÎÎö½üu×L±PÏÕDÒØfbƒ¹Z,”ÐÚ8Z¶¯OàŸŸ§ªi#ã¤0pÕãi·ŒÚæ1Ïi³¡oìîÇæ£«›ÓÿßsC!fÜð†X̰ ô0RÉ^I‹]í-|ò&Ú¶S,ŽõûyèÞ*– b¼/*âÚ;ú'm`Áz¿7«›KýDŒŒ×?%3ôÅø`Mw¾*yˆ'‡©hX'ãêéa=}Ñj:“³míX Û'EJÙùRÞ~§‰›EW~;†ãƒÁ/;¯_É£î3ê§_ÝvÍw0¿q"@ð±ðôÏOjRgçר«¡-Ï /q´àbpÑvhpú¯ÿ8#HðÂÈkêWB½Š¤ÛØ÷3ÿ™?Œsôù,ãJ?ìavb~òq高‡Tƒ}Σ²<Ãæú”kÁGgèðÙÑtk4ÆžŸúõL¯Z §Õj•fÚ³¶n²æ£«›ÅßöÜJü‚šf_ÉHåÛlD^F÷#9ÔK£ÇÉÜûb³Þªf»¹BéjVFº)XF4ýJ+«vcUyb—𮋻XŠø™æ!Z)Ô`ßËzÈ"àô®gh©#M”¥09ÈR3SV·0&LŽ´/Në~€Óº¨åCŸÇ£xîŸxâZÎÆçe–œQsKÉPX“_F4èng*X÷ÝØ%ÌŠné%¯Zb“*`œ¼6L¾ØÞ%oJ¤ üì=£5-xf­X7°¶©qºŸ~å[Yr·Áîüºúq›å7YL†oØšÖ5ö½ ð„³ms„éå“Òp®‚ƒ¶¡Ëг‰·QÝ·µI >íQV`ÚuL<·“ñ–1L»ÜŒqç¹@åÎÇ÷‘Â(ÃwÉþ­ŸÚXÐØ¼Ù=Snfõs×}swGEˆ €‚Œe‚~Å-˲²ä8äA" ¼ vZïÚ–½CEoJ©û§Úêo6ÉâØn0%g: ýB˜ÆÒÐB£›2în¾XË%7žå°ya”yjršårh†'$t¹-ÛfŒßå,ô8±†hLlT]´…Ë+ö½²‹_÷Ž‹qp-Ã,™ÿ:ç·™‡ßl¼uãO—{Íôܯ¿æEÌ"ÁÊÌ©ÂK»a’/3½Hô4R ^Øñf±öϼ“-äË~˜~¿t*ubLMŽÝ±Lî$fÈÆ²›º©kF£AÏ4Í/Ê’‚g5#ð\>h¼%•ãEoyæ®ú~ó¼ðüËÀʱ‹¨¥°gøØY¬Jþ^µÄK# 4q½ Rå—Ó_öL ¨Þ*á†Õ®=[¥áGÙEÐ h)©S)Â îæ–òÅM,sF™@¨“\ØIÀ˜WP}/êз Å¿ÅÙö‚Òä7SžCç Ÿ¼’Gûô+ýì§?úFX;ãlycÇüÒÃ@õ‰»àó½gifuÈ ¦ÞôĶÕ$›}ƒÑù gìîk jIâ—ã&ñÐñùŠ#Ëü½y€q¾QœE]ûy¤¸«€È[ÿ—b ½eÞÿ«âHV³ü 1sW)QÏÑ-ö¸” ›3˜#¯†]YµöéåòĘhúJ÷2­a6Jú}h=¥’^ •í¥)4À±&€,´é QsÙóHîBIòôˆ8×#ºI4Ï×A{«[ þ—²wÿž ˆqΜ$=üã7qW4T¬FxÉ [ºì6¼¬M¼ÁQ»˜©øG#¨!f4àÉq¹hâ¼J ÞȆ^2\Ç¡Ò/»§݆C¥]jD……¤• Œ3¬Ï([<:ú¨ñnôšÜgT®N…t de“ü|ÎÍrªÊt2áÁ-:˜©©rð}˜ŽàUø@ô,ú´ôГðz.$±GÒ±p}-Z(E)ƒx»ãýYLñ³YiùN.ô-SƒˆÐ_›Œö""í7º8wî‚í % Ñ\ЩùS8ç»JáÁ7ÐçìuTD’!ž¯‘'”†Âêço©úªÖ¾ˆ#ùÁfÎvm{Sî·á>-Kúአ‹ax7Ðz‡Á»8%ÂC›ŸS­}J„‡ûÑ'¾Y&Ï£¤ÿezÝ|H&& ÕV"Ü÷Üê²ù~€òVùˆ3!ý!9·¶âd«£ÉÛfËÏísð˜rPá~wàÛ2jºðZ1Ülƒ¸û½L‚û¼ áF’O±£¶Ž:ùþê ¥#08£-Ù’Û{ÌpÜ#v4wldwS¶4"‰däMÛ%7Jš,løªñV¥*‚¾ƒR—_ô…Ýèëû¹q#ï—§Ô»«bwÀR:=Wv„F[ã¿¶¢’óó€§%¶EbìEÙ–åó«–;E´ŠMäúðñ~•÷–O a¾:ís95ê2z~æ~sàäÀ~!ÒÚëݽƒ¬ûøù(‰°²æFÃðÀû÷w¶€÷ýjÿÉÎ ] ,ºÁ´J+Ýà¾haF¤.ž“`l@ëý׿¬=þM"\dûûú»Mä­i$6ZXñ6Ô´ØŠª W&‡¯”]‰È’Ùl,ê‚[ÆGš`•eÌÂ@û/^I_c¾•äŽËÀÏ’Vt)¶¾ï¹C75•EÖå&$×ÙÇçƒDýÔ?Ú¯ƒuîWXÓ—pb¼Ì(d¹R‰×à`L¹_ϥ动ÖÒ•åøõÄ;ºaÑ!ÉÌ}öZ×m¸TyÁ¬÷YllÑGXÅs36iAE–¤Dæ9/ªúBŒÄ<ç“É¿íE¢²[¡‚ô}ãµ(}¬Œ.Fë°£)³Î|­ê‹•ðîyÆz2Ž#En dŒ ÷,Iêë‹#’”är,?B&'¾‡¨ãDx΃ª¸Ã)šýê‘ê誺wù­9þRKd›œU-Òš²<ýªÞ #q(æœ<-s®Óc¦¹/ CL¤¯ü$áy¬–&4é¾3p”&‹‰ÎŠ5Ïð߯×OÉaOsTLsÙŠ{H"®aÐ^Mr) íÉ•h&n4ÂÄí^É8JªÅ­ãÑ0¶‘*œò—5 <³¿sÛ@s› Ë/îðöœìþ(whÊEÚUô))ÿØ$„Jx‹ ےΰúŸ\VTé“X¤s >yÏu9^Âè¯äåè%"ïÂVFBÞY35EÔžž°HÏZž#² ì7Kth¥âÂE˜JŠ•¶§ã+Á XÓác‚‹-ßkCÊa™Õ_(óà„òô:IMÀíRýO"ÂgŠøf¯ûã/½ÁÃy)/Ðã|Löýÿ¡nµº³|3—œïb_”ªu—ºÆ±¡úͱZYaÔX©-‹ sUá|4—“ÌêÈ?MΫ¨)¹Ž {Ьyh7ñæÉE¼$øÍ/g¹z>5þð¦md§JøI›F²ìÄ[(õž¸F ˜FÓIazul¡öÀF4þñïõŽ ¨9Õ©¤ô×s·?ÍÁö(M¢Ô9ÎvÜØ;)€‹# ƒiÖ6îéðd$‡/«ùÆ«~CÖ(rãõx MLìDôG™JÝ90Êa–`Pi*™5È&Ô£äúíÛaçh¢ ›AÖY§ÈøIÃLÖ=‡W-E:†oYe>ÚäÎÆNœN0(Ô÷|““F-2WLÖ\lEíÞëjï¸Zo.ñ×'O˜¢Ñµ^ÑUg¡ñ³)ó2L0äß^CÜ×-Lçö³2ì¸Tš‡îGî™(,õmµb®aŽ4ˆž‹uÖ[ûЛƒ=Š8Ò×€n!’Áv¶£^-‘ÛY‘s>¦©!‹—ÒÇ&gŠ„ÏJ#^z"¨Â…²7ªé šÞU÷e󟌪ZCvÒÈ;Tð“ñ_S©®QΙ¹üm»Xf"Ú!…¤\rTºzW__žÒf%‰ °¶7ìG‡°(èÿ%9Ì…¢§?>-·t…6«œ‚X»îé߯=Î-–4ŒÛâLíC=>\…y^}ÙÑ’ÄiI,¸ÃjJÌs•ƒëmV„Ýÿìä7÷ºß#ƒ×qÅΘd¦:Ô—;ç­Wšا¦²Ô>·™ÇÓŒÌñÝ‹/'/AÇÊ*IfŒþâœþ"©²Z ßm­¾Ø~xQ=Y+“Wž4_ȨTš¿z)=`¯ô‹ˆôÍÊu¯™ìü¿ÊÝD´¥€¨ã‰Œ¹²éPbÇóœ+­«Êtk«’B°‘²Óë¦F?¦Ž,LVìk»0½ô?“D¶íÝŽ@YÐ4†r´™UÔdÑÄ1vÏ xcí:¢¶¾…)͘¤“žû/ba vGØýªR‡ö¬t‡ì’äÏ)§ä£Ôu&Šõ‹‡ï¤ë<Øú/p:ÐÜ4M™bsUÍå­ó›QXÿÝüô·½©ÜßNû{~cÊ úÙúm‘yue84içY_›Wå"½/äþ4†ô—ùÀÆH[ç`»EªÚì2(ô)øäÔHóÇb”fÙiéR¬T`²2õûª±ÃÏ÷™âh¬4‹r Ù¥5Ó¯PUâJrÉÅhÙïØŽ`!d¥¦Ifz¿F%Ûc×3`dòiÆÌ4Aʹ½gõ]H¨•lœÀÒš˜’Ž?àι—­¿Ñ¢-Ô®,’2’‹ŽÁö³¦@óºÐ—}EþéyQ\”Ä¢YõºŽj€I0ŒE ô"5uÍÖ§ÔѪ±Nc† í4 <1=„Ä8Í&<Õ40{ÆI-¹ÑN¯I;–áŽÀ+ºoB @ 땎öa»ùI2­oQÓ«®ò¦jÊm]èo Tâ!íW!¹$ÒÀnŸêåðáVîÄOœ. 91¢mD·YKÖ(tªÉhMØzu Èz\Rã,s@<ŸRÄš O"cÄNAc=›ýøë£yΦ³”Rž¤ì‚FØÝ¸(.Ñ7S+ôwÁy©áyh(ÇB£¬#ÉâÞaÄ&Ìó,Dïšdœ ‡¥8< ¡„Éó‘³Gý~ð/”•‰U…ÜhPùµ$§ðF¸+ÊDƒ»“Rš­aŒ60ˆÁº‘Ï9Œ”l'U*Ëæb„C€hÜ¢¹59 ç@ô€¹ i $‰ ¹EH ##‹Å EˆÿbØdaIQÈñI‚\—㙩p¶© 7Ž5‚Bâ~³4äJ?–9ÂKƒj×:»xJnÕ@¢¢Ã\Ø Kk49¡­Ž[1Zº)ˆ€&V‘×Nt;žUË,“GŠod¯#Ëy¾e4>Äéš[Ô*1œ¹Z !Žðc.“Ãï‰G@„œÿh±|ö‰;O@Hþ@Íò› ‰4ØF:q³í¥K¤e¾w6í2C3Úv¡$;+‰ ¢m‰±‡ç-=¥Eœvžã–ƒ:~ªŒ'lœk!Câw¾Lå>H§¯Žò`Ó^<öÉ>‘“€Io6œëw‘PI´ã¸Õ_‘ «Ä#•A¿ñ€CHGˆ„ €XÇu÷†¦Š0çU-Aj“ܲàcb’ë.z®´Øíw²à@‹ÿ\÷½¢(<ѾÀš¯1ÙZ®<6du,evÜfä‘,$ÅfºÓ 50·)âo.rȃV$öpÃ.8Ä®×m¡¼zLšC”+„5Òa«Ž"]qˆoD!h‘ ”:vÑ dë<%Ìp„ ¢$ýpô껒*q6ÁþM«†Ú€Je·c*ež1fÐÆG%ƒ£|Foìú#Ð¥™OAÜó€‘Vldh¯¯,Ä©èÛä…G9½ê ú„KÇÒ• nJ­mdÙÙRnÉÌD#yšÞÅ‘#Þƒt%‰ìt#­m­(œà<…EwPQÐä·Þ)´ÌVDQF·ú¬å_’NI³³0¢óT™Ú£C–`ŠÉiôFzïIŸøËæð ̨6:¶RÅwvGBñ %ñ º Œ_ç Ù.{U[e_’S3¯^q2Æ%‡à@·KZÎ2NR1dá,SHD¹+¤¼2È_ÎUÉà(0‘ÄNËmÆíb5ïÙPº¨Î]²½È© <&K“¦|˜*Øö%è¥-Û‰ÚÓ¦1¡ì¹Ìcñ—[cÀÚP”²ãú"£%±í!Fw(åˆÙ‡¸Èê  L°lmÒž:û„yž»ù•¤!i!2ë>—q73è+Œcì®hN¾NNEhV¡B°F[/= Œ:‡R»Žm/™ŠB/v!·5w•#!‹Ë¯brõWbšŒ¡9}ás͘y®\B¶ö” àÙDÊÚ`E²?õ‚‰¥‰Ï@ϳU.{„I_P ·_ªæÙ T‚Ñ$Ç„!GI$†)¨ò»—:Búç@T­›µ¦òL$¾’¹µÍ¢µØ%“Ýß)@¦"üQ‘ÙW•ŠÖaë‚¥ëq î²@º™­˜\¥›Bâ $+hJØ×P*º:ÆRO”tEF«Nšæy趨% ©q³q¥&&ÏU…¥ ˜Ä9=Î ÌA]“¡÷( Æqÿ‘›ŸçIŸØ2]ÎúôÖRÁé)È¢Fµ<\Oߣu|$¥NÒv±ŒrËoÜðrF©ÖV8®êF0{»)&R¡Q´éËÔ¹…èþ&yøÛ%qÀTÃ(#Ûçb éf‡4ŒÜš±£u~2ú©ð² ~æ<p¹¢£$™¿äœ‰]:|i=­»>ÖJÌ7"Ê;•„†ÔR£õAvŠ{¬tô†²­6EX™3gM¶áäд0ÞGñ?¡Šˆ3pÌ#aU¬cáœÁýš šÿJ,¨Töý†ÜÊ)½MA|ôñmŽÑ?žaùËÚÑ'BI¤îl’!1U Kó–.¹‡?b6ÌU¥“ºIߨq ËEÿ«j#»-‘/ÜOEóŠ`ûß=“ù%:Ñ8˜!-Ò1ÄoµìBÏtú/TÁþ[$„1‡?"¶2êÇÜX’™•$Z¤tݶ¢È8svËOè2‰#ŽŒ¬äPKÖ‚@¶±¦•³ÙWfKrÆ4©á½ré·kø××\Ï}½*{dzçþ›¨÷£¾Œ2îðɱño±LçÀ»3 *¡÷‚ÛÚÖNQX8ªdn¥q?qÒtÝ¢¥‚[øÌã\ 'çÍ·‰ Q ÈðæÐEF޹]v³†¶ñò´`¦-ÆõîÕŒ]7°ÍöӣÌç!7©+ÖоÖ“g3×aêŠ ÞöòçÆÊˆm覷FOË-{_iòïŸå„ïVMh·R*µÀRáö¹¬™±Û‰÷¼ÙRËL•xãk÷FéÅN m[#ã…å sÀÏç]Ž7ä}è5|<2eÌÚ“d¸.mÇAÆkàx‹ÃœÒy\[¿Fžm˜#Q‹X:”ªÉ„¯Ü©ÅÈ\2Uzc6Kv{£öÇÈØ½È‘†ä1s¿†ñ]T)“Õ õÁðzškcx˜)*tè§–›ÉO%MƒAN_–ņ`Áx;Gh1}êpžPœ•5 ©`yðÐ’0æÍú Ãóš‚$Rú‘0YÆö¢g$íÞ/¾MWÖzÜ+®àÛÇ¿gsÑ!‡ún&¬~fë³zO¾‰„+Ь˜7¢Nñº®Tv‚Ñ›¬e<žö`‚ƈ6±m´ÅP)+þúe6œ´¦Oæ5ï¼¼°ý}©%l‡#¿ zy]dcf^û¢E¯û#iÃ2Yþg¼ß${ÞºÂæ–¥y=+vðdMú£ŽŒT“)lk²— ‹g’d]‚šˆ×ÏóQŠ«ÊŒëCåœ2‹u»ŒJ`ÝÚù3»k—#Êì£U¢Ã9 èl™)­ø<*Z*…­àGˆÚñ!SôëkǼ9ÎN:¥™æ¦S7ßP4€ŽN¹– »èƒØi>c‘zÄÜ!±Œ2.º8wBíêj?Ê5é²6LBU™Áó¶á@M‚ž›¥eôЄ×gÓõgø›FíWåYf«á 9°2é¶Ô.#ôûŒ+shç¬k*ƒYêÔÕ]Æ™ &åþ{YýÙê†Úwƒpuf2•³šliÜô¢°³ÅZ*2Pžîê‡À'I¢zçß}ìSš*ÝwBöNœI“ÈS ý…mhŸÄ]­›¨¿mçÒ‡Ùê³#›¯¡Š†þÉ–¡UžÌÑè šÒî¿j ¶¥wš{ÖwA˜]÷£ùkæãd„ÑSk%ÍG뺼LýRÜŽc¿±W»È’ÇT‘4ë‚´ñ˜U£g”,ÉT9Q`b—”ׂž4yõm|)JgÍGNˆ¶)Ñ{ERga«ºaúmkmíUýsÜPÚv‰yÒCD¬Ö½0¥ üŒÍR­Ž­L„B–»ég‹ž/ó¥°ô¦¸â_ÅÆ3DD3U|6ˆ=Er![6 +V‘°M ðÜ´"µ( ,7Izö´ioíÞÑ%ñûÿôk CD63`Ì4Aܸ00—¸|%¾`§» Á°Ã-4¼¥Û8‘P̵ÃHQ¿zm$ã÷’K‰¤¥jwµR©ËcÈR¥’µ‘#KOåm¬X|êÚ#Y²p™DodÜê–ö™1"€s«û›%`Ô°ånÞïn´üA(Y8mFé´”SåèC£¾ÌÝÌÜ„¶A¢¦6rž“°ÿzûêÊgí±µ­ðžƒîÿll»ìýÀáÆçcoõ÷…k¹‘HøDÜ*vcfÛPÛ6ZùÚж…/¬ÚˆoÏVÿ8¾u<ÒZ©Ýz8B!ˆTTÔ%f;4ФrVA«/jÔ!•F¨=¾ÒcN¾ê}²¶p¾‹%t ¼?KªˆRHXkm‹åD¬‰Ÿ'8Ø%o‚®T/€’ÆI ‰B `ß[ª@|Ãa© á‰Â9Ú•€Vzkˆd ÈØ ÑL—+IÚˆ*XØ¿–£€i@fŸ-¢„l' j©Q¢„†…ÔKD°-øˆV¡`ñò¬'ÂÀzjä ÄµnÚþ½Ý•ŸÛè« ¬…Ä5Õba!ã&°‰Üð(® 4é °+€è•ˆ”ìÁlŠ ôXÜ97v±{Ý ۟ö$6î[n£€#ŽÒbl˜$Œ¾yê;›âç{æ~ëLpFSà1W{[Q“ †¸ˆxm^RÀüRKLêø|"* C‚ßÔÁDºlè9%ª‚axhHÁÐw ¬º,˜‡„"IœÄ¢$åâ(„æ‰ ÙlêÆ9] -›½„ŸÍù~¤?üØ/†×døþòæ)êu&쇆Z~â” ˆÎœ|T"ëâËf„*¶ÊLHÏØ±¸A4I PÒ))N =°3r0eö¨Nö”Ê­b¥« •†žŠÔ#JÄßoN»K˜ Èu¯áýn÷1A ÎŽC~ ÈBÎIåqpÇDϾš¼Pp¡J)mš˜yJu⼋uØDˆ¿XÉçQ¡æ`;·|Oˆ2·µ¿âÕ6‘g™;”OgÒû‹O•§ž¸Ìîh&æ Yì÷`m¡˜;ò@ –zMÔ·ÜUn$ÃÄr^ɈD5°÷Û}n;‚ Ìñ=Óü¶Ÿ†™.WR¼K©uÓyÂÚü¬ìÀ#ÂÒï"¥¹¾¬s¼-£ÕEo›Ø ¿^®å&ϽÃ×YF8¥HlBÈòYÞXd‚'§GÚ¨…u~ùPŠx{(Ddzœíý¾¢‚ž Â&Ä¡MÇ£m×ÕŸ•òï»ðó£O²#Îò^Äq)|Q«ŸýÃfغ¡’½pwˆ’Žû?±ÎNU'~žé² íÉõ4 Cmú÷AÇËý"ÍŸ=râ)ÌM˜*Í$Ñü{lïÆžPËMeÙ»Õgöòl©Ô™å^6ipŽC¬Æ£[.lü­#‚ Q¦ô1ºMKy\¥ãmðÿƒL=þ–Judeè+Og1òfê„gÓ gÃÍÒöz%Ýg‘ áÐÆ] Ñì’”gZFšüí‘Q°‘r¤h•39÷§1V€¡ùõ8–Öˆd¯{wãx€î·ws!Nïeý!ÕVf/Ÿ®§š¥&B»øÄîäk4ɦ}‡ÞMññD#†¶bFEx7s ˆ;»Ýà›÷H£S£Ùˆˆ²< ú»™í©¾lI}Öm½ÍÁ"z% N%5ùºðµˆ‡Èà¹7ä“ucƒø*( YlÆOu6—")<¼GüL_²5'ý¼C)®Waaº¢Xð˜Ã’i‹îÚø\ÏL£»£Žñ/C²:€-x‘‚,”`¦ùõF¦©Ô6P /P7Àn“í=]^B×g/ dLó‚es®–îNå.ÙÁòn u.…G®{¿'ÎGBØ$«)ÂAý°‘ŸF¯Ô‰ÿ.Wéw­§?[à: B½ÍI`¥ô+]øÜ ræ–àºå·P¾ÊÜèæÑ“Ëó'¹|2V««¢©ÙÉÃÁõhP½Š4X{@öºC;û ¸ðàØ´C§â6gÉ!íÑ%²+8¯ø Óx7Dà>è«63÷wL aD,æÆ-—iÊÏÑÛþ‘@nþŸƒÆ|,êEŒ8L¯NÜV( ó>¡Ü“ÈW‘¢o¾Œí•Ö·úð6Ö,#Ð4ò«×lƒò™¦×@án<þ“dÞ(„Ë|øGÿÚûD] “°QÝçŠ$êÄç2°aÈVÌA}Eà(ÓÆï91§ ÒŒY?ìa©ÒÙ>Â_Þø#Œyùê[ÿT™"ÃFÎPxן7”ÒôˆyøàÈûLo_p*U Qg½ÇñÑHÖm³QT][{TŸ+sÞ,“oɆ¹·/éå4±±úË M&©¹‚õ‘mDy1êMEAµÜvnâÙoV”7õIû,+^”ÜhžHGÁtys]ѵ˴ ¡—D‘¶MŸ8§R¿‡æÃA®ç©Oýó¤¾Âüpñ©X^oþ¥j¬ Oç{J>N4À¼s’Å9òDúÒIoqɯ’ÌQÛëfÈ$ž¡½Á)}:Ö¾gÞJš”…õ澇:šÜ‚ 4¿Ïú~(Ãk`Ž>ïáæQ(4}à šD¡NEÖ¥lð}>+'HÂs°©%G©tÈN‚Eê°AÂê'x©{7­–³ÎàÎåq€tW=@ƒ¶˜þÈH¥ dö1ýÓÊg ’AKÝ5n¼HD§f¹w6ÜZÛZYTxsRW{66lŽY›Å†mßDeøÏk;ÖO{º@åtX©šûoþêœR¿Ce$BAPŸô´s/žðÔSÁNÔ0X4ƒ7€ ÖZ4P`ÑÓvÝ.d]k" G}­¡b>4+–´:‰J‚÷⺈ª)‚åˆìáuV\VVÓ"^¿¬A•i+!FÊ^½-‹åz:ôÿðÚí|móHî€ÃˆèŽTDr”ëd·_)¿ªÁkŠ­<uoÛ³0F?ÇÈŒ9•RÎÊ)ìË'Ó7‹we à˜$7§•7¤¼•&‰µE¿>•œå´’Ÿgm¦Yv2­ –Ì éö?Ø1jô$¶ÈKD`UQ—,“¤Mµ™ºìζÆ$ÔÏ×oz°Ê騣Ìo-¡0“ï¯åÒ{Σӑ‹¹Ï³–ê.÷ 1 ö݃H>èê·~ÿdœNYóð_ ~M‡¶²#CDž%Jf{¤˜œð`לGþ–i–ƒÌ`]˜mÝÇ&0£ Ké›5¹ädÉ¢¿™òH[íjX &ëT#r6=ÔÇÍ=ñi#Mù'oŽê%–=ép8©²YàÀVÈ1xgŽœH;ÏH>º»·`°ƒª’•äíÉøÞú»ºW“·rKEлÑ=Nû8̲f9A&4ç&»@Œº©ÛIßçw„ž¬!9L3"ŠLU<–o…ÿàÙÄeBòa—úD¢ô™ÊâÊ⿯Aà*$ádi´O"F0ëx¦°Lñ£È¤c“®2ÃÍ/°™âNv.¿m×vx—-ëçöÖ -òy-7‡.¡ÙLMì ð z†o›u¶_-ä)(š¡Öx”˜¼püVl±/†ÿfÞ­ªX„¯X[õXò60ž¿›âìiÞŽdíãÃ^’냜ge¹S3(Å·|?h™Éè¸ÞøÝàiH3z»ÞÞ»i&ú€fx& Y0ìvöÊÜ™ñä°Pf`ûêZ²5‰]ÍüÃH¯8¤†gбò3—œiÞKþ];ê4>×ÿ¢¡ÎˆFQšÖzÕW+ rÐðN1œ©Ÿyе 'Id˜<☱êEhç ä3Íľ?¹lUg1ükÒ'þ>;Õ²vgœ°i ©ß=œ³(V8€T}šø4‡r è[qUGéÊÚæ 8cQ‡œ©´¦Ï(³~`•È*ù>³…%ïöHºt¤ VÁ‚’ ïd§:3[ƒ]¸îêaC)Îö,,½¬Øøˆ¨·ÔH0I‚·M¬‰D>$¼xUÒÿštDxCƒwŸ›ã#CûÕšÊVà}[Ê"}œ<=€Ûu’÷Bºý¼×Û;!çRßqätà~è´üa]\ÒÎâš ¿Óó£o¨ì-…°¿:½Éô„Õ.`&{QÐ]8eå“qy¿ ]óã7 j\ >g ‹ßÓ ‹ÝX{#¨£BjÏÞMIKhÒÑSo¹l¸ünUމ·ÐÕã»ë²d JóÒš$þö¿nòšD²š£éÖàÔÎ<Î~zÎÄÚå:¡ñ"¯ó?£$˜$ 5Ä›ÿtÕ²ÚS(–àì3ãû)1Ë…ÚThŒTu#;gH¡%zrÄ#„x{Bvò«æ¤Šo+sÑD™Gçó¹sÁ‹®ˆ­”Ÿ#åa<l‹PàµÒ&aÌØ(k¼šçŽC #>›ìáÞG@ˆÚçÕ3ÙðcU~Qýòv©¯ªcÙ²ÎòY…Srd‘Ež4¬üÐã¡Ýý¯žEå@ûgù–0„è»SÝç†S?E·ÿÔÍô£ZW‡\2³Giƒ¸kjsªÐO=¿ôø ¥{\ZÇØ±d­æÆžŽ&&©˜Ä#ž”Ãã¸øF½Æ»4h£(ŒŒoð}t¬àÑä„Û2>òñ ßS³ çæ«tkÚªHhBÉåÂÄ·}ëö|wàQĨXÎ/ÿW¨Óþ+=/ÎþÝ^“Iu‚JØ©XýÙò6ùßÙJ$&zž/=xpÙyyzD3wå( Áj{íG$’Ka†÷³¼¥Ðå=[2Ü)’ÕõZj±¸>Y1x¹åTa¿˜ý;_ÔÜ[N2gÚ<ž«¤M^Æ9ÑTGõúy®Yû`§Ý/RæúšñˆÐs7&b½0xaÖ¾øX>¶&x$ÔYrÅgpÃûüíÖ£ÇMy ÊMcûæ¨d \ pú= I¼Ø§z&Nò%¯‘$ÙB·Â§îfÕÎŽ~Mw‹œaÊ;®ö!°>u{ü¯écÑ~åZøº÷I÷ZhîæVÊEýlgó1/ئ䢒tBX©9»‚Â{Ð7Þ6·±íýí*7Ø-VœrS×}BµŠÎþna” ÃÌgn<5ÞÜPúà Šñ¨N›ÿÔžq×[,Eçí4úùž3f+?$'œ„Ç3‹jwgê²S‡H FSž-="ýøÆ4‘ÁV>5¬ƒ³ûEÞ«•ÓÛ´nXÓ÷¬>wn±N+ÃÀöY(´N½± ”i¿â§AJ7 :Ú§Œò䘄¨>8ýÖL\®/»0‡ê|[†O(p ü^óbÒsï´9 ©itÒsõiâsJ"Ð\ø^õÔVÓô#íèˆ0ª+•TWNòÜ¡W+<Mz13ã«("µW¢÷:rÙÚžÕüíWˆØ;ÊÔ£‹ý¾S"eý’ö)è˜vSçìå†*4Ÿ³c—¯ShtÃ87³~ûoþSb_¯÷‰±¥8ÀÕšTLåN ©5®ú'¨vêÏy¨ÏÝmª_™D‰:¶:ë{es‚ÿt¯Àÿ7¨ÑøïÉòD‚PAÔýþç¾¢ö¥ƒ¿HÂÂýžpNÕZ>¢˜ñÑ*®5~æÉsÞvc-Õ::t_ŸCOª´:š9²àKÿ}Çgç$]ŠöIYØÁªêÚvÕšáP׬SuSE@µž-“ÜLÀ¥ðÍs£çÝò<éìS³XTû·Yô¡z¤ÎtJUÍ)LxÝáì Ý.wg|P‚Œ (ô [P.2(óÿøÿÎÝbÙ¶Ô¶¶¶?fY4°ž#=k˜¿‡jÇ=B߯öOÈUE +ò•Pw“żà¢ïk ê ëê­‡ðµƒÿžlîìlý»áçÀoÆEp¹HkD‘*ø!ñ  _ ˆ=Ü ¾ºË|âS¢@D· ûõ”@i½(;¸ÎêìK‚4¸@øèAVø @ÑDŠ0UÁ «,QœŸÃžÅÍ÷+Wø¾ÑìJ Y^u,9yk{ÝÁ}Ö{í@€èDi ºÀz…ªdÜ­’lØnS—Dú%zX~·Û( àˆ{+\þg \é{ëú\ÚM\ œë @¬("D[iÿÕ¬íÿî^©´rÄ)P%üTôì)dÓÙw-7jâ|¾„À>!É¿€Ð"ùb(-ZÈAk¸hbhy¤5$L®{M9%éü®ÈŽC6®Pì·=V]ïAàrËn+£FtÂfSC‹xu’$+—á½v"µ7‚6k ððríÞ‘Ø0ï@øPìÛ<{a?Ñ û‚ñH¬A)­˜çŠ$…‚É…°Ñ ˜Ž¬H&yŸì¹n‹ çøMU$´í­¾OÚ[òˆÚ [tãÊrXÝP²ì²*’Xå=W{|IÛpÁß°a]þ•Åʦp×ùãx\ÎíØÊ{_ð*9Þy÷ëí¹ùÛ ’ ÿ¤ˆ¹+»‚G©!O ‡‹’ ×å&|ma/i?_x@ý€gÝß{ís³œ¸<ªÏ¸¯‚|x"²Y½˜\Dí4l:2-'—’¬çíÖåìœî‚Ä$ë X×ñ0I®’ ¨\&&oe_P  ‹]bœl˜à‰C¦î¥”3ÆB܈^&¨Ë`’ao©$~~„ª`zä¥ù¡S‹_’ëð´dH€‹SÈše–EÈÊŸcgö¶ý ;ˆó*5¤¸ HÏë§KQˆnë }wßmÇ;¤Ëÿ¨Ñ/7|šâã'ň ègÈ›üÀßðœpG,ÝÊ.•QîÔ'õù^¦×錄wO^þ˜WÉ -ž>oì¸ÉRžÙ,éa ‰^¦`•×¹2IC_Þ­N0‡H¿Ú|ÌÁBƒæ#Q,:4“&_5H Ghü¿ôSý¶ß¸Ç=jÙgtaÜx•l’C^„€e»±­`Äþ&±/—Ñ@¯H8@K”ÅN€z´*Ðõ—§BXw‚bò$ú¼êÍ‘]Á¬1ODI”ÜÅ(æÉFÉL¢¾ë…aÒˆ1éÏNQ똥gÁb¾?½’ålH>ièòB‡¢™­3l:aââš ‚új>è1}\ö½±)$âÐV³3Ré+–Ëñ|^ŒØŠ8#zäbð=÷yuiýø³ÃÙ;¾tNÎ÷1ï<øyŒ•“¬!¡P2tZFâ¦ß Ûî䘠-矛Óî)…&×| ªš‡‚äô£ö‘óâ©É·h—¢ÑÌ×4s¤1 µÙÙp7™ R‰ówüºè Ыõáà­j™$™o¢¿]’ÝŠ†.,kÏÃÄXƒD‘ý_È뻼?I&\ÿ"iæ T€<=¼˜Ì¹Ï9\CåEk µû¯ïÝyþûQØö#%Zä"³US½d£u`îAöC%é²#Bà$z5&¦Ïö‰»jˆT“€*Lk|’¤ÚÔhÛ'õ©pç-žU@”JcíhМßÿú~‹Î<)‘Ñê&•Ž‘Õ Ø0Óî襘ŠpÈzô,›éÏV†ÃsÏÍS¶Àt ,Q“ÜÿúDœ³éæå †Å×ÿSR—}ÐVôí>}4Íú„˜WÐ3ÍÖ™¯ 44—‚÷ÌàWw]ø/•úœ†³MŸpåFÄÕUp…R¹A#æ—qþq…ÅY”kSé”Õ!ˆýœÜ≞L5²¯L¡Dì2íFV¿–%Þò&d0l<ÕG&§ëh…éT²Ç¢rÑ«æ‹ÿ|ÍùÌ!åàǽŒ½W7°>!½,›……„5iª83‘èÞ=‘$õ’yÓüêuÜϔ̭Š5ú¶|¨ýehØ FÃ1ÊPáK!ŒÆŸçec, &?æóKxÎäõ…Iµ¢eßMÎØ—¾2Â}v$·Ú)X°fÂ즌R¤æÝÎÌrlõµa¬ }d—»å*´éšBŠî6«*¸@ÖúÍ€Bgmš¹ÐO­§ØL« }øÝ›ÇÑyO餻¦Éh÷:n‹÷gþÌÝpl۱ባڪ‰ÈîCÓP¡Zî8YrÖ jc¬A¡ÉxþšÞ„MªÅâ]x“yŶÿ÷½¿«ŒÝ±_Gï¯ÇµÔ”œ|wRÞëgE¿ïÖMø9GºÝ¾ñ'§^{5rl‰}ïB …b¤åÔ$£iÂ%/ Ì+ßxnÒõ&®žJòž¹+´„Ðè,6³ñÜ®žŸÚÒã¹öFMba å]“t¥ŠUëè†5Bqµ•—^zDTkò×§›´2]¬¤×Ç€YaÃÎ`§'z:#¦Gfôß/^¤¯ç¯÷«]ÍB¯0¥z«Ø¥YbÃçâ=tcô)šÆ}KüaÅ_‹¨M÷ÞXcF´ˆ ˜%úxá—¶ÎÍÌ©,Í‹Ž»Eš†D8e‘Ê'EËß^0¢Ò›b˜ë§‰ç—¼$‘æî¹¤6#Õ|}½RÍ ôIæ(f«¦Ü§ÚÌcm³äÓ‚ÍL}'¨é{ê¶žò‰áÈ)H>ÓãnH 3ì×½÷×ðÝ÷9ÝJ«_'‹T$çfßÂVN“?Iß.üoµì.©wZ3ŸÅåu7s঵==ÿQªÃ†Òí¼[Ó¼î_UxCÔ8::¶ˆ‚Aöóƒ{;V}¦ä¯øæ´¸‹:‡lˆS Í0Üó‹U‘0a>Ødç‚}õïÑmQë`Æ áÿÌ?sÔ¶jJªK½ì €z0G(äÇŒaæïÚYN¶Ùçw×°`º2ž 5_©z!ñ*sVF%júÉ žº<«*ªÙ;rÚ ˆè3ºdš7Ûå – jÎì|´@?@áGª¨£w¥®p@Rñ6Û|£ è1Ùó½£n×™s Dp TQ "Ìð½àÛíéP·‡W7òÛCgÔ®^ F` ŠCÏfÍ}:¢}¤§ŽùŠ™€&ÙP‰î‡0Ûß’5àhw¡Q¼NªÌþéí`Éé¯ÔgS)/ôS¡ÏŠðhô~š…-5oê¯-ˆLÞÌ›­ÐøzêÓ.´§œ-›4Êiï»è·D¨¯~VÓú­±mð^võ7G»_;¾ÅÊîÂÖÐü$~ûÊÎÀµÆ¶µ.Vuv»µ;'î/µ•6¶Íšïðcã±´²mýò‹­×–å[»äÙÛžx½øeôæj· ØÞÚZ±9s´§ì÷âzü§‹ ö÷îq¶eayeÞjvv<Å£÷1j!kêü¯¾ ›ªûDHeNWRèÍ~¹Ÿ]ñ¦Þ?ùÚ„N¥šÅȬ©qÑT,+â^¶EcGnHÛwçô¿YØ…“M÷ä6ˆFÒé„:9éFµn¦[r:ñ›Íü P=@‚OdUTä(NÖ%O8–cÚH×exƒTÕËŽ×:Œ|«¨£ásåÌSK¯¹ðS•ñq(L‚HwHE­‚$W½øD—'Íùil@Ú [’‹T´ñйTtó1±·=ªŠîmePMWîoU(Íý0)´­VÞ½îÒô¡0ËÛŹ?È¿sã“¢l·uh ‹>”s\ªÈÖVm)Öy7ûÑ5øça§T ´(ÔHI: ©¤^âÞ 1É·ÔPÕ›‘wž½&›;`‰?ù^IèÊj‘å €K‰ …Ÿ]:ý3…6彪›©H‰Ô{$Š"GæƒÚ’¿ªÿiwü»ä ‚“{|¤Þ¼„r!Î(\0ýS·?L}f,!ͪZah!É0ÊÏy}B|G£ËÙ úý8²M§à*º¼!ñɤÕqÐz¨ÁÅE»TÄÂÉ›Úä”üxLª Kûëô¯áÖ3•ÿCG•Ël¢¼µHa!7•¹W¿p6—üÿ=ž|Që" ÄŒÜ^³f{ M#MB,«zFÁ4oaôµclÎе‹n²±ïõÛ_QMÚ¢K¬6º̇âzÏ ­Ð_~—ãé ?¼½vENó—d4õXnîK›=¤k1ub5 :¸º?éFŸo«ð¥ì µÜë̫ٞ/¯w}õÜ_L¶¾ Û>E>Cy$Ft“Uä½7Ùº4€Þ&5ã[p 8¯Œ®óà´Ùt“U–ÊMË1¬†*8ÚI«§ñLPb%Z(À%³Ño— ©ÚUà ëLJFSëvŽ™ƒ}Ù4¨ªú2›ÿI‹JD€„µ%9B(@f Ý Bp·”Û‰°? ÏÝͪªý†ËaXjý ÑQúÿ|RCÚs¦ËQ¨…m[nøzþ*€Ø@t)’œ\p¢–Õ:É N›;èe‘K2â›®þhµZZÕ•ÔãDCåñ@¬ª™"¬_*üHÁ³_©OEÈã^ßÏ•À§ýÕMä6±ø¨êf5P%5º âÿZšDË:_<€K26ý…ÚÿE*£@é0Týpšß™\]ÞŠBÀz«n KÕA@“_£¶5ªC/NŒ©têʧdÑl‚ð/Hœ©ÙÂãó% ç–Z¶fêŒdÅËœ}T‹”ÏPÙÚþ´¡g÷\þŸpœÉ&¦…ªlµz…R™RÉ,$m?’læ'FåCIJu¡’/¾ +kv•švQpfM©±>ÀºUõÕ„m[ )¾FT,ØÍnûðê’fÔµ©£mà²:ºÍ¤‰·þšfäºîÐs·B›‹²×ºÞÙ¸m=ÿ¼$u‡eO¹ñÈ…VBÈg?¤ÍIqgñFÈFâ;_®cÞ"xЫš…êmD›»[ƳÂlÁ:æ¾̵VRýl7jWÝÔƒm§RDzíÈ,Ù<«0¼û«vÃ+‹;‘¹å«IeºpG <¸7¡T³ʪ‡¤©Ççe9û§/ãLf­£¹:@'YJødê†OIÓ;M ’|„¨3×üòëø²øÍrZ6›ù2!ñHæu虽Á(¥Œ+‘$Ó$eê­¶»¹ì#7Bq2ššŒ™i àˆ±@½ä¸ÙFlJKc’Ív³su­EÝü×ÍKbè)²ýüŽa «›Œ¾а)ü“Ë6è ƒÿ:ü¿žÖ i0cÇÃÏŸ{`qÛÜhOpü†û¤}MOsJ.ohoÒ}ûI‰ ûÅ1ÑÀî96ÛW4DÄÎ N>ÈÞCí‡B) %„BZ‰À“ÐÜL}ãwdao˜êc-iH ¸9;Âf¼øŠ*”JiFÚ%]þF4§ÚÑ®?Àò³ã“Í1Ѐ3S43^Uð°w„CJfL¸¤a†™m É&(iiz6#¶[M´Ûíîú ߘé1š‘8"µƒñþ ?žñëWŠx¤Ääxÿ)ËäÊ¢1’eÊß2ü¡rÊ Ѐ¬åþ&’mõr Ëv5ÎZ’Y«;Ò²Ûr5l[nY.l¶¬¨Ik„=FˆhàÝ̹½2¿Ëà,önçÞíÜîÝÜcËT,R-¹í™l÷ÝvsÉÜÂ"Šøh_‹üÄä· ð%BøOR=>÷SƸÄïY<à—M/ r½ÔÎïÅA7]\7àñ²›wxþZ–P®"6Ö·¸½O¤ÿçÉ«…‘AG¥,Tø¿N;ßíá…LáßǸ˜žóc°³øxTqCDRBš1„¬êg$‡'M·šÇÈáºDa–8ÂÈÓT&¥ìóô—†€V jO¢¡/”[¡²݈Êh'2¢bYC@aE82Fkd:…J´>U@zmŽMê9uü`+ÁOM‹™zðQcûn½¹„ß§3äGŒVtSBèVΕÛùXëÔ¢ ”X 9Â\Þççñ„óM‰CçU—ŒÀ»¶š?9#][OžÙÂÕšMO]Ki<@‰Ež|”ïÿ&t/‘0‘±äžÚˆ)Ôñžþ¢C_‚“ו±Æª)Q3}0j™óbKBJ Øš *""Vd 춳щÖùð^ñ)¬H™;Àý¯fª_ä“,ÙUÔùBIŸ†ƒªØcà,S\„ åX@/ú{Zù06ù°|‡®øÂ¸6ˆÆ#GîDfùŸiÜ6.™:±vÊ<‰ãf#<Å¡£ïDäÿ…o;hb\Ëîq%̪´(ÞhÄNµ%Dò'"Xp(ôcZŸ´~*ÛÈ^Ù †´ø7&Ï‚‚¬T#%ˆN\a˜b¢S@)¿"͈ÈàW7]É@YyÝä¤ïU6®ôñ8O;Í5HËvLš ³Ã[Ì•O‰*JX|ih-O`z0Y@3ç|ê³¾ÀLA—@UÆ@“Âð1VV–²š*ª2aÓa@äE™þaN²²Ð(‚1ùÈváÅ6vcÓ³¤¹V8̃*‡]þÇY"Ô-ˆnËðšÒªE»ì‡ÿ¬´–„‰µ"Ï!jDZ€bî»ÌEƒ‘ôh4}Â$íë8P3Î?‹FÖ“ûo'PFDRyã Öü 7ž]ãI=u&q,É`œ€5*]…¾Ù‡›Qä2Ô6úò˽ØÈ­FÿšßëjÃB5—3%%©JyzϮ؀…Áš„1ìùQ™$„X¸OSÜ>€Vµ%«Ö‘¹"ÛN>GGž·Ó—Í03´ÆýîcîòÎk@Ô¬c ñP³ò§²ó ç÷ž|Z<Ểs$¬–9„¥É „ÓŠ4¢¤èFPjï‹Å Ò… šÔ QŠž£ï ë$¥ÅÁˆXè>÷ˆ·Ýøáó€Ñ:4“hw/ûXL*€óZóÇÇPñ´E4 +Ì>üáÍ~ì©È»l6h™´B} EX–Ówá<&§,R¢¶…[ÜLB¥#üÖù  !v†õ¯Ï¼0£`n ›·}RÑ3äDwúfÄd¨¹`ÅL›Änáãå`ÁÃù‘Þ;ãXgXÚeã#a)•A‹]G{¯;öâ 5^¢ù£ # ­êÝŸ"ƒW»Ñà ªÀÍ+›c…³«xqA‘˜«›²‡†JE ŒÒ2õDmeŸ3ú/HZÐÝŠs¡  ? 4R’ €• }%€Ïô¤ë ²™©2-9þTwN,ÿ60_›À³›Džë¦Oˆ§réŶW¤s`QÑ= ?—ùºùˆŒéhúèžá½Sh?1àØ¥NëÛ¿ÙGŠéäò‡1í݆vîœN\¯=9 rÖ3´%pÒ OðrN4æ&öv.,»åèX)BUî›í› Xåù›ôNÔnfÒ`½/ŠêÝ)•u´WÁ Ÿ²…ÝÉʈ"ê-$ ŒLŸÍÏoR»)¤6«™Pö› )ëáû-fñXoÙ½•æ!©U*­ .ûïH)‹uêa&²nä5çW¶U™[Ù> “é‹?öïƒôŽY7¸ÅÌq¾,ŠL0«QCؼ7Ðþ°wÀŸþ¢»«¤ù©eúÈÜ[ü©ƒÝ2÷3#•1{*-UÌÚëÌ1lTz”´)lÝdÇ 7‚*`{?XeÕ3 BGÕŠÒÒFÑP(¡h+³PPÆÕg Õ0žþ†œq:P>Ô.2u!Ó•½+“ˆIïæèVëh«J¦Ý­Ž’xŒ :ê'WHþm©a>̽ŒÝÆEi>€.Ö®jl&D "™“†ÕͰR‰Õê¸î¹ÐVã°&_›‰Ö½*ëíy;(<Üy|¿üÐ:¼SŒ…E(¯ =Á¡†Ó枯UÙý˹to¯ÆMåžȸÜúKmj3ƒRFò°´~»ÿÊHšñ,ºŒb¤Ò[œ…ÛwT®eJ- ¥i“Žçmœ_ÌýeËo¼9hÙ:ã¨Í¾dõ¨4Î#jâVœœ`˜³¾ãðÉÙ¶7¯žÈRƒÚýíF0JVþÈa–ýn×µYb2ó4\›v‘ˆeS’²Fš4!ÀqPœ×!:¸+þêû š¶–!¸ ÈD’R%Vvù£dÉÄ6 ˜Eo› îã?Î÷´y6Êd×}¤:Úba¶§ýAõXqwÍÞB¯•õ^Àê•`fëÿ€GuÔB‹PÌú;¿Fn2¿Ï—þûÑp’™×m7Úµê£$7Á•¬louäÇzt|1iÌ ˆ¿ý*äŒy;Ð×Û–YÖÑÜkÍÌh¥ši÷ ‡Mw$¯³àÈ,:(B 7X-›òO&q°nZU‘§ŸNxtí–¼“ ”éÄæÒpÈÑ 4ÞFÆÛñ-JD¯í²Á%`ëfª¯<“KUÍÛ‰9”}mÿ³>èAÐjïÿY8ˆÊaIu=Ç]Üë°:s`ÄÚαåâ/†UrÐæ¸Á ÒQ—ÛXrƧyñhÞu­óÕóá­ÇG·2:Oîz—4ÿT"Ô\Aô–î×ï$–] 9ûÃ.Ex‡H ›Mr^§Ã Ñ( ´-'+qÿbªbÒ¶Þ²VëöÑ~X(ãÉ.º±?‰ –ŽÆù¸/™Sß@fK_*¼Ðyü°eÁך6pŒU¤C9ÆQx4çÿ“3Ï9{e÷/}H,+o?|gmœÅtË{Õ‚pZ RºC|’J-–)ѲýÁί´„d°³Ý+9.®îÀé:ño†æ¸ß¾Ú&ëGÔ{’ÁǘÂ< ­.+Åw¬oÛá—YÆE¤Gš‚Qø>åò« ÜPLWx8J÷f^ø£/_¬‰ŸtüóýWíþˆÞDëφ÷“”/]‰¯÷} ¶Ÿõ%¿Ñ1rÙ~é_+ÊfýG•ßyÙo•8™|$Ðã/Nn`du¿%ŒÑõ•c¸®‚71çDø)úŽMÙÈoŒXÊó1Ó¯$Êò¼û®áóL¿Ö†¶ÿ.\ÌðKãxØ/ Ú8çá|^ÕõB#…ÞY¤…ÙJNffÞáø/,ÞGØÑ JÌ(¿ÑÓ@Tˆªu½„ºû®~=Gg½ß \wcOZ×+'@&oïàº/«ÊøŽð'Õû¿‡·Ä–4°:;ȲSÁî²_z;u¯º#?‚?°ks 6Ç„Va%‘~ ‡—VŸZ\Ý‘²YÖÿóþœô| in‘ ÐzÝn渟C]Ä<à7‹ö¡~Yƒ!šÞn[{ùsù€WÄE%JKÉP%ÐÔv¤ˆ4ž 4”ÄÁ7_‹CÉö—úœ°&oº­%69¬äå+”ðc ËŒ;8O¨tŸå1.f ëdaA¤Â¹ ó}y)mL&ÿü娤W€×»Öc"¿Øáé“»ï¾Ý[–¢új.â®Ü ý–ݦ ŒÍz):]€r¸ÈbCuôý¡°êê´Q9äI~O²í·¼ÞCíµï”[cúQc¼ß`Dk/¼Ý®³B·RF &CCÁZ\wuësÞ9«ª›r'ýÊ®éˆØ¥3æþá¾éII½1nÄ_ZszàIé(Jlm+Á3Žœ•S,ÌʺZÛÞHÏ„òÛSUd¢·°ïëZ.aÐçZzø‚}S=c“ï‘U*þ€`2êBÀúÕƒáöØ3KÞ @³7!ÿ.£S˜,üåäÏbo}-wã;Íæ<ÓYúï”/$UË6<+ÌŠs?½â¯sÓc©´lÚ%¾"tËYÿZûbbùóºKM ÏžlÐ_M÷ú*ð©æ”-nF«ÓµÈd=”ÅGO²A³£”ò÷Ÿcº·ú´öÁveù¥'Êã SyT[ˆ©\²öÊÛÊÕÙ”ÙSΈeFEj\y*Z n:%r›»ãÈõºOì-W.z®5ŒÇx]áVäüTÞÅ|añ^02è¨D%ˆ‹)L¨ðuÏ»àxïöð¦ðïc\ÌOùÀ1ØYü¼ª8¾Î0Â%•Ïe³KãƒÜ/uôsèv‹+¼_öÜ™&ímP°Æ¥K#0Ì€1‰`…E& ×q /‰n…† †ÑÅJ¸Vd0ö!“GTãñ˜šj!:S(Š8‚£ùƒS…g„<ú0)C\L‚†QÂz2>H S6Ê"y7M«¡¼0Ò3[Ÿ?± ãþ²ÊUÅ•ëüó¢2°exS9åêFcÐK)…ŠÖÆWs’\Õ Šö±±x·¸ØÜ[T,f›L)¤G«¹0PH#.4½+ôæ¹ ³0.œ/0¡šô¬OJm>-]h¦ä²PQÚMí g¤6¬22¨Rv‚#й$xo|?­3ó¸š(”¶ió”5’ªÒv¢Þk2Æêh%™E#YuôÈíûÙž«.«»™¸<5Éê6¤86`O:±KÅ.%Ò+/»ó+«{u££æ$¨8ÚÏ€`&aÌ÷‘¬wS™’²©k7œ`b†=f±ÿ„.Ǫ́ü  3PçN’Fž5²Î‰O!àÊJA:$åL€¨CÖ2ƒ˜€5KBš./ì{Tè>Å]6\«:= 翌rì¾úEþ™sÛÀøË„Ù¡â³ñÏ3ö$ gÚD— J¥¡5õ, iJ(„t©õ“ÁšxÊ®–½¨t>4{ÛôØŒîaÈ)\ú3¾?D~Ž[Wj}ìÊBÎÃÐ̱¾þI=ÿ€·.ÒŒû™…_!B€/¬H†•dRŽ€^Fˆy%ãhËY“˜DI‡‚ÈžõGÌ&_ö'XZ”\Pžôrb‘kfNû(XDu]!‚¢P¨ä£Ð¶¢ÿº3¼Ý·¨JÏ\YQˆŒe´PÏ÷"ÐÕù•Ôi!ª„>C监Ábåƒsç´sÉa§‰‚} Ž¢æ±â޲3¿Y{>D?¢M´o9#ž¥“Bw|\Gÿt†Æ“N4”Uw:übt4Q•¦gͳ ñÞs°BèùÁ†=1ÅʱW‚œ½sÝ „„œP9ì`i6m{¯Š8a*kY{G r‚´m´·ƒ9æD™?äéñ6MiÝ ¿@h[­Ñ¢†%ÂÆ™"Ñ<Ê ìmŒÄëõêé',)ÆåãøF§Ä,Ë‘rX`¨oJÆ Z{X3j|-ÔlPVùkòËc #Su5#!Ô´w 01H,ù­&Y,¬¡LëF>dË‚ÐCªÁ…çm¤ýM]É–÷ø¶M/nh`¦ÑØQç0\ëЮ×öC•X¥¦ •JÊr–˜k¨!ÙÆlÊw›¨VSÆI‘ñ˜Do¶|èJY‡R­ ®» Np¼Ûïqxs©»?õrÉ`ªÖf Èø€Í#Ý‚8-¨»ñm)ZU²4uÉl¹åDVA<7ò¦²Ö'¸w´Å.Ö—˜yæ¬þÑ'áfÝ>HÀ~n{šbTl=ò.=]yY¸1Õl_ÐVËȦp‰“et-ô‰<1í†À+ò/øo=ÒnO$ëwnMì¾ÇaÍÞômd«—ƺ —qÜ•¯©ãîÛ Dt<G웸‚¸ ±ïÇzKI†|¬ÆÕŸ¥»Q›AhT.Tå‹‹Àº6 ?ûÀLH×6GLŽÎÈ¢SžU]̨ª–Ä+wŠŸÎoÑŠÊÒÄmÒéâæ M·ÔKÉ^R*ª/0ñäh¢P‰…º¬„ÊëZŸôJÔ#êº1<è/éq îQI= t0M™:ñ™+Db+m¨:qªÜäɱgŒuNJöºÅ0&>€2A"Œ'¨ñÎòŨˆVÃXÃ6u¬0»Ì¹“"ÜP\QÆ)m³´«·€s݆ë(á%1;*n–qþøR3ÌWÎ^x ]âø:³¿¾ù;'ÝÈYôíÈ :à\íEJ’” ]ଭ+æ}¹¹äj× +1;®žË7¡´þz ê~²t<¯®c?ÜçÑÔʇ˜¿uÄSšÁL(hìÑá×ñ>l­&½5üO¥tì *µZ“¶mýÑžT½Ò†£· õÅØnn!é)ûe­/$¥»ÉQaH$Ó­Y]IÓƒ³ýò+Èé¹WÝP|ÓØlÈ¥8s¨ºEN´š‚Â¥QÒ#ƒŒ¼p«ô¬÷A…&¥;¬]Ò‘)ËÇÚdžñÂ-ÐÌ™ÏGõ¾<@.yc)ªh•š¥QÒp½!53›²ú®´¶7^G1,ýúìÙ÷®D¬Þ¹%ý…|¾ºåa0‹¹’â2QÙ±‡f¨‹½9öh;˜o£ HªÞŠ2Nß.Eë-¿šðU:OÅÙëfÉ£Ü}7`q·4®nâ[7l¼H/Y'Gù—ϽAïÅ9ûµQKF$š„0ÄLjeÃа¹Pk×—¯7š=Åüщ±y`’¤g+fºøò˜£<· ”¬'k0„(™o\Z¿>ã|²ÁJY ´’ßqò“¯ßG‹.¼ƒ p{õ•Õ&«$;òžxíeM½S—´oYt>Á+ ¹úu¼ïßê ¨ÂÉ•&žJשíºfÊí ö‰;g®Lx¦•e­5\äÿx8A“ÚøÜ •Iã} ìÝzn¹m €@oF?îεcíp.¸uMc.*4É“x³i“ßl ÿi]mBÿ[?ç£YH·smiÛ¶ˆO€(¸^3`dCq|Ù®=jÏ÷Æx¬—¾  ‚ë¦Ýt£_"(‰âå)—ɾÕkºø‚±ï¼ïÛÇíÃÏ\tY?‹DÙY¿<Õ²0,TŸ?•W¿ŸŽ)Ë4’r/m¤?Þ ‘ù?" Q·uó¸¼&ét—4‰wÓ®ñ¦'-•±r=cvØ=´Â í’V· ~žôº³/°xZ­‘ƒ'>ïÕƒ¶›{ŽAFýê&ŸbárÏ €CP+J$ŠŠãïÜj\§µ!èäî8¾‘¥ž(v€™A]ÿU‹CÞ£Ï>ÉÆnæ¦c%}ÞÛÂü¶Ýf3õ"I\³Ã½:¾E —ïC ÈY? J° ɯFè¹w]¡Ð‡q#Ä'(j<ÉñýaÏî¥Ò—në%e¥4_dçžÓu'Ü}H)ÕÀhÞPÃ^`Ð y›'oQ}^d‰±Hüû00j­µ„»óþ5açœ_*#=©÷l‹zœ¯êÊ=š²Xƒnud$‡F–ë!Ö§ÜuŸ`Sžô6zÜ.ïsÛà¸ë3¾d“²ÊN¨*«+œ´\½>ËfQÿóã¼C­¢%_ñ##ÝñªK­Áø£ï37>(çgßSºy@>ô/Läe½n‹ˆÊ|¯ÀÅ  ¡†]ræåÀ l+\FO·f^R´ÊÖ#—Lù8Ý[ÿz±PÊìÿoÍ(‹«—©o†zûÝ¡ðw“ì’°K_,”Á6?Ûƒæ hàÒ ö+€Ñõäÿ’1ê{u§Åýv¡µMQk†Û^%ùöªl˜dþ»A9ÁÇÄâ ç@3SD3Tà±è2Äd„ŸE'‘Ž éKHkНså6sÕq3îî*[7áïßÌüÉ xi†sü“ޝ|*\\U^Dá5¢&± n»Z>„rD@Y 4¢YÚ Œ(ä’j®“°öJ-fÝw©X“ûÛ@ÀΊéyäA“V*¨ÂPŠã¯šä°5L@#º+GÂ|IX,Øýg)Š‚ég‰ÜÞ…5î× SxYZ&c QôXáÏæôÐu,ŒêªÑ—Ë?è±°[„eü×YŠ ,+r1£'âĪWƒÜñmaM0³*«\ ŒÇY—µlˆt×$úYgÒc˜g§ ¿¢Æ©BðaEo¨¡8”Ò²`Ù8øI¸daldqqÀdªò÷õïÆÉå&a޹Xpj⪓];M¾ÍkNfáDT|¨¿»\yÃqñü1\,Œœw‹8&( (Ô¶°Ú7*ûQH™ššVú© ³•´‡a4yj`±ÙüûHÚæã*!ǰôÃûsTÐ(¤)ØšÔÝ œä Ôœ‘™0&Ï­jl§ ®–D«sœ¦¼×…Ž0?b‰ŠÀ‚‡ópÅ\'0åü:E4°Gæ­Â[è æ›Óí1µ¼(Ge GŸ¬JíYüæ¤Ê%dêÃcZá‹éॡÞõ)‰Úžr¤3Ö_p1«+4~äʳݡ ¢„–œ’¶C¸ÖmŸ^/Ÿ0MÂF¼L3™X†ØI® 9 þΖ¸ÙÚÓϲ#çoç^9PQk¾ä/×é“çÝF.6Ÿý3ο‹;æß6wáÉôÊÊG‘Q`1‹«“{oFG](¡˜ÇFIQmPÿ®}+ÓüýÒaÆü­vÚôgÕA"³ït³R%- œDq C©e‹›½ÒçU ÊyRÿõà§°‰L‚Þ*û'ÕϘY7³ú3´Ns‚5áfgbBÓb£NàZœ™¢ÓÂÅ…zÇ9ðÁÕ¬íRÍÁV0Ú(¨]y•8"ë' †Âì/¶DSÚ ¼(WÐl%.ZÈNñ{°ë´ÒMr£ÜvÚgRGz½È}¶§×Å»"1 ÜÈÿH±¶ª +&·¦ô>9}¾HTA·#*_ëF¶vN-L«]–—)ûƒì`Å+Rq<ª£Zs½MÔôƒ3Jå‰Úy}ÂÓÝV æØæ̪†â’'¥Ò¯™x®±~Ì;à ”¬íù;RD"aû©i3•€®©_Âè¢k›(È•FãÒ^<ÃÐi¢/"d ·BÏfð2ŠàyÃ&=œ­õªèv`Õ…ÖÌ¢¬Í°  t¦,ZF2HÆs¤ŒáÿQìd÷¯b©í¦›cÑö;£ôi8HÎ ŒûOVbðO6²qÏNìß+‡¯œ×ˆõBR…G{Èj¬4žo,&)Õ üÕ™+3ˆÐM‹5÷œê}=r»)¹n1仺I8éYZ3”*ãÌ9þ¼ÎÕ¨±$úöéªûm/s=š|¼}”AÂå<utíI‡\'ìòY??a¡ :3ÞzH\ù- pˆ[O™UÑ®SCÂæå$Z]f@^¤ï³ ú_ }C}ÚÿŸL7§Õ?A+`Á¢ÛÎ{{0WƒAJ)c‰ˆUé `,~kgqçm¾ÿx…WödvÜFeLïàãfß²ýý B„£þ ¥±tÁnL¡¢›ÃE4rÕª¦šej…¸ž ý?*Bô¿«v*¸.œ5`yº¹7™ËYìåi¾R;Ô\3x¯o®Ï×:5lÙnÎɉFFùÂuKâ!¿ý–iÞÏ< ¤ý«Çbß6@ã{­\Ìí:QŠü„|ŒîCGµØFx¦hIˆƒ»+Ûã5\–âwÓ°°0>ùd¥la;e·Ôg÷”u&¸8*³Úm&lÍïwéÁ´[8ˆJ8jJf˜ª±m UÀ¶´I A=‡ T•I<àKö4¨ñ2j߮ɽiMΡTG¾ï‚¢zc2ÐÿÜuþ‡Õjp¦0-é;ý{¿ÂÕ·äï÷]—ƒ_uZ£zJ…1üGmº!--¼ø>ølÉïÚ™¤™=› X4Œû¨g ¶§æg¢EÇ\mk)ñTðíÁFLâ”![ƒ×˜A×mYpzzWßkn¸5VÿybÉÓÿSt¼Ì÷¦;ÏV^Á²0©§†à@¬OXñ—õñùFؼh´¯<_%pºC¿aµ?‚ÓÔÕ4z=ê4Ç5ë/³ª]ã/9Vs‚Œ+¼ «[…,½~¦bè<ÐM@~­:¾ÛÍÅ9Ýéép›¶y@,î¨K@MHƒQÎÛUáú ¾ºÛ !$G÷¤NyšP0Äm®¡‹ÀéYÙÞUË‚wv÷T#ÌÐàƒ¾\3ÖN’jlÆ ÅhÒ½¦ñRÂEÚ8‰ˆvçÆlùeñ¾Èó³Ï«™‚*à‹‰åQ¾ÑÑžu¢ªŠmõ‡ÛK"tË-cÿæmÝ~}¼©u»»|£6õ•‡zU3IžínCrlG‚™Ư/”´úd¦}áäš}ô£TèÚ}¾ë·ú6¾g^©6Ë*–RÛKèÞݧ¥¿„pîÝP$ð÷Y?œ]0eÖ…Ž\<–“(û5§€¦AÃ#N>ðêo®À™÷õ鱂¥cXÑÿ¸Û ¾“·QN¼Z”ás½âǹ¹±õ7ýÕšÍûm€v¬a8¤A?,жïG“ö›¡Ð¤¿ÇÍtDêμ‚\Ÿ{‡ÓRÿRîQ‘F¬ôÅ­ZŽZyµ´qÃô)O¨zùüª¶ë OŠH½Ù•Û´èÅ—?Kè Au*ê¸À>ŽýMÙ¹wVd,¾qhg¾.¦6O¾% Pl”l¬+þï}·'' d|'–ÖÜc­0¬ƒ…³`ÿ™5šüRÞ¿:Nn뽓×Èìý)LI~‹÷ߋ߿NNå£,¦÷ÄY¸·ßpü-®<ø.QˆCNqÖ1ÿºCP=8x`œIad,¸ÑR¤uMIׄ‚" EpY–¡Á§1Á$d7Èešˆ’uÆÇóF r¤ãzd€8…&B´UwX–JoÝ gáÍ*Ú6º¨Ûv0w¡Žà˜°sQHȈ6q4Múì¤E÷UŠ.šP5”rÈ‚¤Q‰å˜¤w07 2ü ,Ó°R(l$w½2(Dv‚¸xr¤œ“‚f$˜D±Âx¶’²¸(n˜9»ÒMsÓ5óoÎɨÕÛ ¶Rý«Â—Š,\å(¨Çß’k#–àç"*²m.жHG“ÿR݇‘ñã)<5o¦ ž[|K:!*ra#‡»M\¤>Áç\Y‰¸Û :æÛDFÁ¸3+ÒS7A:ÅNv)uÕÀ*dŒÖ~Ÿià6¥9ZSé˼<¸xP¡Ó`[D¶Q‰ˆ¢z¶c yškÞM&ÁœAå|²* £Ê"Š -Uødÿ:T2"hÄÍ©“¶¼Câ,æ äd b•6*µ9Fóé6a$¼È6Gîi$®ÎÊ®<8pPSó6#ÚÕ(pœN.€$X™l¹I·„ Á9XÎã9*=aârªå…#¥¿yá§€”QçåZÞÏÀwFÓ=Ù²{&<à ¦ÕA÷RÂŒØç¤e“ËÉåáЙ ,+“€ŒâÑtÒ¾Óm6Æ?Ñ|[ßûŸÌ¥€`Fвmnüd9¤®+N*EÆgÀÉ6œ‰*,âëp‘óP„öyí°ŒVÝêˆÄUÅ‘~¾£±ú8üÏ ™ Ã¢ŒÿH-”"5…]‘:†E+à@VèQ£a`ùQ²ÓÌ«:câL+P˜\”««moU¬L;)^{Vý­Ñ oUÔ U°4^˜Ë\¦iªº>Ø2¤÷Ì2[5å´p0‰-j’kf<—HùÆØœÒ˜¨7Ïz¤‰LjÉ-€VÝ…‰1ëÚáÁ3aÝ T˜¸ŸÇÊ®©NpŒ?«±±A‡lïÅã?íØ˜:§ß¦˜ŠÀb7xO«ª($Œ¼¿ÝK^ÄYa! P: +På’|â¼!. 1bB F8dÍ?±™„jÚ{¶Òª vb×ía²kä0¹út4`¤Ð瀕G ü:³JÖšµûh QߊæèÊÚ)ôÐéËÍä·L? jÅ+ÆwËUÁìÙ¬çïQóGè×Y"½BÑb–.mùš .#㞈dd|ŠF«e4da“e1mFëWËÛ¤Ö]ðö^àÏLÂÍ>0Ï)&{6ÍÅ_¢´ ‰k™dªJ^«¸$Nˆ(¢–^™šÉFð̺Ʋàeù[á›EXºEVÝ{B©(÷¼ùqŒÐ»Íä J>õ<|?ØÈI/ù%ØXqò˜xYxø>9 ·†pÀ*7kÖt¥ ÂŒ§¢Ñ.ƒü]³„ÂÅÆ¯“ÁÈÃÉ”pµ+&½µÍääìâݫܑ†\]íŽù,zT¯–Iðš¼Æy|ÄS¿ü`ʲ~G\´‹Æ±²Í5ýq4ãcXÀ ííŸnZÙ•â¸Y´ëÒÐE=2JRo¥ûòrɵDˆ½ÿÃ縬¨Ux âÛ{ú¶:.‹“wôì9ò¹ÅNàÒˆgTùîsòG0‡ Ûî"áêÕcF<>ë$˜Ý~ËaG\.”¯†²§l(•ډѱrWÝâ ‹)*Rئ´êв“iXèéæL¶z#Wf1 ýmÔùÈ`Oåý®u~‡Pëîš• R]V:M‘»m!:/#²Ò¶0ª2ÿô1rñ ¨ëÐè¾°h¸11‰x‘Þ'ô{‡8¸¸JÜ˰½êé+ yà;MˆrgÆÿÝQ>ö@(V•订#á(‹›WÃéÛãz&v~h”Àh%mîÂ>!_ìòWk|Q¤“é­eg\,L0OM ;°ÎIÖÔJ´SgY…tÚÓ¥²Þy‘Œœ¨É¡)Ûµ®ÕùЗ­™xhún¾^e]rêà•ö>±n`î¦6Þ&/Ò%ÌlåR0}×ñ¯£¸òsÚË€foùùÖ›šü<¯{¶ƒyãätŠ¿ Uú­òDsˆÏ‚†ØC i0lǪÑAe+¶ê•~rU.&{ª‘4jeT€“À>¦Û.‰¦ú‡­ßO$^Z‘seuŠJp)F¦yŸ‚UÅ“#ÛlïN~ê˜ýCu_õT>ÍáZ:Òú]³Ž D0Ï9¬˜žCC4óL*IÆáJ“Šáœ³ogiεÖÚ4¯ûð˜(QgPÓ-5°ìloﻲÂf*ÑYy<™§D·2™ÿ5ÉoñŽ‘¥*Ð#ˆOæË÷-™—KYâ˜êð -£_V¢ÈÐñ$,ñ°8%ñhÚêuÃè«qƒ©³É-ªj‡[æ`¿ã€JZÞ‚NÒC©)vˆ{•¶ãÖt%ÉkLÕ1JQ0—™®Ö2ÂúiòŒù' =3¯7É} ¶H—¤+r‘fɇ_g4ÖoS¿§ú¨v({W±L'½GglÔ=JÃkÙÜò,ïʦ³2«Û<3[ù(Ÿ™?ކ—OË€´ÿ•ìõ¸Ÿ6$¬3Y©.ÇN6…/ˆ{”B…y0…5-JJBtF™=öhé1¤FèB}˜È”.Í£íï!$Ý'—Ew~K!¦ó_k£Û:ôUÅÑ‚ëQ­¿S„M‹†íÎRÆåf‰tZ\¤Þ·,¿s5 ì—£>ýÊ#݉A8½$¤Ñ$Â%Ú—Âþ)×Õì:oUÕ¨ô4£6çňÌm9MCÉ¥WT¯°Ÿ¯Ta´@üI6Œ:g¿íö?ßuLvDs´û\“uÊᢾÂìemCç,²K”¼Ð”ÍÓ*òv¾ìÀ¡àì‡?òwÈÕwk#W»ÆÉâÓ@ó\ƒtÒsú[Ê¢³"ç_~| !}­}x‹û®(û¥‚:'zÅ^lýï¿~ót u»›†2|¿Sæ!ï>±èçzò÷Gó|Hx¦#[Éöý®–ÖÞë’óõ÷~Þ“Ê10G®üy¥µ2öo7ÐýÆŠïS¡ŠÅ*œPáêOÈë~ÂèÖ!¶E²ÕϨdÝ­ìû¡úo®ðn˜ö³ˆ\û˜Aúåì¯Áµa6fo¬·q9<#\çbÝœÃ2Y¯·vtK^õâÜ–Ón€ûCêû瑞7ˆmÏíÁ0rúÙÈ —£u·‚î \å-Ñ`xj\ÔsudîѺžÎØêƒV=¼ÈµDLèî~mõ5µ Ψ肽¼Ö6€[º`CË÷ºyð¾íë|_; ÎôÊ.dñè9Ô_Ín×9 LªÓHóV;.™nÒœûsñeo°ÅöØþþö ~§< ¬‘ n›Ó¡´¿\œ~³Ò%á.˜ƒ«^>#´žáBo™¨qdµ”*SQÓÝ(üÀ@•îhéHÑ‘¿+C©j¨Ö#ËÌοsù+[ÚïúŸýwG 2i_¾`Qî½/Xvöz/ùo·ÉW$À›jDS©÷êU¿É29ßþt/CB´‹ésÑG ºÝW-5&­ðþ·~¦ÕMEôêË7ê; XýCx_EÃé×Îâ¸02Û®)HÅü¯A÷ãlïs°Qõm”Šç+’à€"æÂâˆZ1(ëü!‡-¯”%±^Dg'g-³ÚÝ®G-¢Ý)Ï^`Õm²·’~Zßþ?³{h}°ë¶Éý‚!I&úÙm-"㮂ýpr§O*^¼¼MºÃ¤ 6eVS‹¼ÇÔ¤$¬ãï¼g=Žvµñ½*ö^Yˆ°ÿ½¨-Nµ˜¼ µL@4Pi_Õª°j“¥DK¶RV­n»ÛªµÊÔS‹­Õmï²n³¥ÕHŠ¢BŠPL!„¥[­ÔZP„"„…€ˆ ÕÌ@YòÀòôÖ§Æäê{†ÚÌGù[`ÍÁß ·Ã’0‡­´¬ÉŽ“G*È]ÕD®L.NY8UªÛÇ•,UÇÅÌJ®Š¢&–/œ©w÷Òª{¯éä¾z ÕqéüR°Ñšc€?€ÍŒÀ]—šÿ›Ý´µ›e7OkîιlY0²‘`’Au_ÍvQkz_(Æ EPóã~Õ;Î~¤ef{uyyztgvðJ“ômj ”#¤‘ÝŒîd´¥{³qju{Kt2µÑÒÒ¸ñ“ùýêá΢òêõí¿¦;Kyx’t|¤¨zt’MémÝ‹º Ay9õþ+Ûñ–õHL’í4áùG2G—aÏh¯Î=)ÏWÕ”—Ÿ‰^êY*I®¬j‰o/ ¯ÞžcêXõöxP³¨?¿³;z¤}u¾ªx&z{Ž“özUwuefHré•f"çWŽÄ¯‰?§œë9ý>¥œûuÜ"ê±·W×÷á쪒}¿s—'”Šw•KtOŸ]Yž:R{¦ª}2³½½î2ïÏNHuÞçTæ7púäœ{8±‘òÿéI>«êÖ÷ßÿˆT²Ò~¥}u‘H%ëùãàT¢ÏÏŽŽ2©žYz{UKÉØ‘s³Ÿ©3]ü·[ùÉJg|H}ùýÄwîÌ©Ž¨tï3ÉS§f™8-Ñ#I/뙸ǧ¯êìoGõDwóÙ‡µã³#uÄ~ZS©݈cžz*‘X_°Ž¯LiO*žš®>KèÊ‘úBÓÖ»YI¥—ѹBnsÿÐŒ‰_î3•ýûïæŠ'i‰ï±ª/ÔÅ&¹w)Pe§÷©±lœö¾nUº£#L¯i¯«£I5G"DǯŸ4 ÎÔŒ¯%ùŽŸr©F›·£\ÐïQÄô~ìö+‘¦rZ{?U!ޝï:ó«jÄçù„Tö·„$SCÉE5Üçøj‡Ì/ïÉO_RßR|Øù÷ëúfU¿µ+åèß¿þ~$Ô~‰1”¥ÑmÏWxzŸ»7õ´YrñP¡ûHí§ÄµÌ×–·´WúÎûlµü¿ïQ•áÝÝÕï¤ãþ}9IFeTøqMàö¹¯ËYsöÛ¤W•Ó5~¦ïð¿4o¾ãóîi j¾÷Þ6Cû-"æMÁjVúâì¥ý óDÍäѯ¿.ÄcŽ³ÇŽinïºgü,43·G“;(趑ʦêúðõö‡©ÌÔ7µ×GJ‡]zƒ)ä{c×ãöµ¼¾h=Š%ò>8·¾¶#é?9ŸG4éÈWU“éRP¯úÔ½^ó2ÑÔ½>Ò{¥vtye±Ó¿£ö*3IÚëWV›äÿóH÷ã÷ÇÔ½?jgÁ}t÷L}T|tÉdç0’>óh‡µ‘,¨Ýt$§M×iwæ·—í÷ ÏÔ´ W)Ž4Û}GR#™í½ó#Å»PC3ˆ»«ë£WªçGŸä£ãóÓ–ÆßøCÎöšÛ•¡%[i¹›/ŸÏ&Š÷wNíOÛ=£:N_ji*74‘|ýå¢.›#$ 3‘ñ?šyýápɽª,}$º§è:žQZüçXþä_|™‡=ðAÒ¿xš«šûõi‚jø¿·P|í!“3ì•Æ“ñÿre†Ò}•æÕ MM%KÍÈuTÍRÚ]?{íH–¡cTב÷ؽ‰çOhW=®c§ œêsôÚÃÍ cÓÇûŒÉOšÉÐïQ’þOq„Û+Mò^N<ÀÐ&_æ÷r¨tø”‘øõÑëÑHäißé*Ýoeß­Ó£BÝŸfŸóeî¿ssøœìލ|Ç=ßöÉ›õÚ‰OÏMV¬_Æ'ù¿ç7PÃPG/þQòðí‘Tÿǧ†º­ç¾ä–5KÍš"ìðø—5ÛkXqãçgŸ[µ.åj¸ß_ß™§#*'%%R—/9õõŸöD£p}ëžÉ5=‰’Ô3=þ2í«MxŽUúxŒªýóƒî˺xP¨KWà$~1´Ï'ÕÙú7Öâë’Ty-Öáï¥ßò»#O4èüœí­¬ä}ºÏ_<~òU¨;ý}Fùe {wM~\¬ºªÏÈië{«}'ª?ÅÈ«ä&w_Ëþ¤Ih¸'cŸSßÔ3ßšjíd„Y’ïƒøצ 8­³8’!¾;õñíéßùÃV²'ØDžæ6gëfW£»ÃÒ+³ÃËë·ô³ÞwN÷•i =có¥úgŸ~éÕþ3u^íÕ>óöޝs7%¦´Fú¸”¶ªõÍG’\Òÿº…îüùèÍ«å Z‹“üî?þ¬Ÿ'5‹¤:9ý}SëÖ–ÿŸ‡«ÊWNu©¹¤óýR?lƒpuu}§bùõ}Ù¢Õt_,USuÚIyN7 Hõ’™í‰N:¾½j¿y^Oë;RjÏȾ¼6Ο»¯ôuÙ[€»¤Š~äþ¼å¦2ù{Ó ¦îÒGG@ýI— # ü§ïõ:?TÒ2?)Ï)ùußÀ×øS5Ý9Z£¨*ß®/ÉŽ¿ø¦ÿÍñ®ªPðûšKà‹Q½¨| j*'Š/sH5ÔúPK±n\–,.ãœík:×ï]ÿÜ”èþ,G^÷_\|G§ëµä3ÿÞ³ZºPþK¾m5“RX¨œóLâø¨êâßuLjîÿ4ª8;<jù"€^i“ó®wöÕôþ srúïùœ¾×kî:‡hc}|4ÇÔ“ÜQKu3_O<ÑœÊét KÈî4émÍz@û°¦é‹t¢<üÜÍÛg…u¯¹éˆÍ™šÇ[Ùúª—¦ŸŸÏ0¾#ó’öQ™³¥^u«¹ÁÒõEmßû»{9ë_°òû¢ß¥GÒpÿÏÞ:¤ùð”Ÿïèúíì'ªóãA=åÑ.á<¯ÿfõ‘cìå,ýGR CåëY€åÖüv;Ÿ£–Úcªñú̺Súí±Ì|y‘6œÍo-QxS~è×û–z_i7Ëf­›ˆ*¥åcþ·´ã)DC8æ ‚õþ¬¥¥ 4¹4´|G*ǧiܳ¾Q{KÝ-/õCw–õUÙSïiº‡V‚Ôɺ»ùsÍÇ£>ž›ùw õî‘û²´ži®£ÏŽ/CÊòe-XýÐß}’r¯Ž´ž˜†5MWóWkm5c ñIá’ýÿ½DCüŸô1^£šy2¼Rü±Qó›HSW©ý”á_ ÿÈëN½¯¨ÖÖ1Ÿ§ «8OË}{øv[¤£ÞeßëÈ` þPR¾ó•©9–î­={Žþì`ֵ暛µü× ¿òѵéõêíý31BTjÕÕœ¯¹<ýüy^Ç ]3^ƒòå^ºÏï]³|¿oìó²gCö·`¨*îmÞ±ÒI ìþ5/üz&:}{ž«ða™é³u䬃³Ìëé==4á#téóGî}Ö¦Áðž:š›ãNƒË+}¥öxØúÿ­¡q†üóšýKRûÚža޶ÿÞ4ù’²lüä£vúVÕ¥?ÊL­ÝãÄž.½¦Æ|{®æŸwŠÌ¦¨u>NÈëùË1~kH&¹qûccK!ÍÒ|ùsþûQ™T„ý¯Ë¨Ìè:ýðƒ¾ÞoùÆÖÜݧ%ÙãE?ÙÕ«ÿæÖØz-ŸYŸ÷.ý{UÌþû9i9ùyTßÇ/ýWBzÔÞJòkþ\IéÐ5• ‹œÿ:µÿå5cÕIkÍÇS_Ú½ˆgþ:÷wDÙœ¾'|€ª×³EýïÒ$ûïÁ¦Ìqoß_gïó¿Òšê|íûÙÉŸ·–ìd~»²þ/»YEOŸ¾_{éר'óÝ“^¿£kÐ^¡±˜/ÕZÑ|ú×Hóæ·sãWû¹ì¾=–Ôgåí#\ž¯×Í„êYê¿•º_×÷E O³¬|<îìuÞ™ÒþŒÓòûý—;ûù>ɪrì~ß#Ÿ¿û Ý‘ÙK²}¾Ïå"𵝸øŽ½>éžs>Ohn@Ž'OúšèÞþ Ëà47ÿçH]Ÿ˜Vts›ÐnžÂöjLzî„þ=‚¿ôB+Š¿\sãŽÏççËò¾ï|@OŽCÿ17t¿GgrJ£pÔîåM•gFhž¹{ÊïÍì-çœç?1ùËšÕ¬òF}hßà¡täÚc0Žw¿y{“H¥öæñ¡½2w3PèösO`_ø©ß#÷UwòÕmÕFûû•y~Ï Üf†ÿ–Ò[œNP¨ÛûíðÇ.£[l‹x¥Š¢ÿ¸óèÉ1”$™¢ÉvR1¾bc—öNÀªüC3^ál]EneŠËQHV¾¢±’ÅJ+R¬J±+Ŭ±RÅJ+R¬J±+Åÿÿ`záª],ôý¯Käyš‹¡/þ#I' ›§ÛâU{±yù‹_29ôô|²I’Ƨ\"WhÒLª +ádГְâ DQRDå}Ê” ú—bV” P)Eem{Ø&òûÇ9Ìa6ì+X“®´•*~ŠdXR°Wr¶öcKi –^‹wE1BL‡«c+Ævì@³‚eÊh+Áe —2ËðÅc³b6˽ba ÓÀlLîÆ;©1†‰8;qv4b›ˆ´Ãh˜Y$È™¡d(!íEÚ aðÏ3]¼ò\Ë[Ë•2kéZŒiM|“Õ«-óʾ0ÚX 㯬0Kû}¦ÂT±LÔÛ¶  h;Ñv†7lœ°7ÃÜ CI·ngœ È"g´,˜·Âœâ, †3è`¡;ì pX´L ò>©p2æ&xÏP=CHÁg Ÿ-Ài!0x;ñv>p*àwÐî±"~†øƒ ¹rZÔ‚€(…µX¬  bØDÁÿ¶sö¡oȱ_š …Ø_µRœæ°<&‹ŠÈ%û,2*Ž'F·À¸a¯@ :Mí* —ø&¼Áä¯È–µ *äŠBh†¾†°pŒûåáŒy7;Qï #^!e–Q‹ªb¢( ã¯ÜÎsg©8TÌïÀÞøäÈ;A«ƒÞ ½Æi`‚‚öìaPß ¾›&zíWšÍ’Ö#2á§F½â/Å>é§"1Až†x]¢yrSÔ0]àxÇ£b’¿"S"É‚¤ |5@VQƒD 0V…»PÜxAÀ à‡¾}j@©A ¡,J^@òB/&Ü…ë)ô—(Ǽ•=Øä22Ÿ-> FýLÈ÷å¢ßø­áN }ªè~áð S¢Æ},L¢GÈ‚þ 0Û+§ZTÅ ‚j†À0‰ýªªÆÍŠë¦æXû8Œò7±½E3ѪXUƒH WN\àAÀÜ <xÃU XV`À†F£án¯ñÎná*WÅt Ñ5XÉ^÷ê• æ±¼FÄA–^‹ƒ1qWš(2þ”¿§/É`ë_V/žc²*îâ0÷ŠŽ Å-\Å¢XÌxq™ˆ‚ÎÈHcX o_‘©6nã*øDBl­Ž+l±aŠx<ñx‡A( Gž®Ú¯DË%;O™0Y #^!/ࣨr¥WQ&$ò0äaÌÄP <‘Ê¢(_y9hDQgd–Éå>£¯X«mnQ*^Åå c6˜Aa˜00(!óEæg‚ö'Î>}½dŒh-l{£¾~EÍÐ\Ø iL¤æÁÍɃ`'ó çûÎçJÑJ‘™[«>NFµ¢»½âX¸ÖÖª (Áó…ç<|¢øÐ MÚ³` 5œõ"g~ºô{%Ól™è±|dÑ¢Û$+ÑÁ€€)Š¢üèÖ+i;ÿRi÷¿…/Å«(Ì,¢x¦¤û:Œ4Ožà‹€¸t”ÎàUÁô…én[Æš²Ùþétã…þe^¨(l€:õ[zb꤮2Eþ•àåèâK1oƒÛÆ7ÅT¨H pTÀÜ€qC°°zbõ€X_°uø†¹Jëaò5²8¢åZ\÷ŠÝ2¹WÅD12ÝteºQh,X®€…±¯ÔÅ·¢ "¡O¨úÝl·]™ç„š‘˜&ïú]ûÊß|…¡ŠE“ a(=Yl¯€…¯àÕ·´ÈU<(Λ¯L·"Zm2ˆ"Œû }½WE_¡{Ãì PXÁõ„ëëÁŠâc¾‚/d^M¼ìؼ ˆ={†9ú’貇d(aöÅì a7øZáü =Q3¦*´Ï@v¯¿×®Ð½¶ñW°b{=½Úèz"À*\ ܸ=á¦î Ü{÷¤"—òîÕ¥½EwÖFFñÚw÷w{e°v»Å«È¼8 à n…zô=QÜ÷ønMEE}0•ÁŠí 'Â( &û¸Æ¿åw´}Ñ"ð¯áMCð‰à8ÌáPEƆ²aô­/ü(N–çïF?ïsŽ¿|ܶ(}¡ ñ{ ¢"ÍÄò&§•Ò²ïE9Q¥¨TáN aª•áôä{­¬î?Ö%¤„‘{±;þ–Ìe㤱–ßË•& Á({‰‹Û[ìŠc1¶a²dÛü6÷ìPc¢¸ á23^‘xfÌ 'Ì0X¸ÆÂ¸*pvâì°b¬ˆÚÁ´Ú @ ²3Ì …Ò¤PXÃÌ3|à´·&ÊÍ@4@ vBíÚ‰›ñx^Nßîk5­R·É iñMÙömß‹ÕÖ-rã¶ßv§Bf Åf°Þ˶–¦ÅTñPÜÛe²y·f^ˈpÂé½DlsZNÅDÖì}wfÔá,g3üÎ â„Øbgh‰4ÆYf×7˜§çÙƒô=ô 3QÂì³O7qrÎÛþó±`L½Ònø‹„øm¿ÝšÐÀ †âà{ÁØÒ¶ˆã"nœbä~3üMñ¹CrüA-jZˆÖÂp*P“°‚-®-´²¢ŠPÖDAˆ+æQ9À”ÿ]æF7Ë –ÞŨU-QÆø¡ü2¨¢Ê8¾ BåÍC‚öž‡fR¿l8§Âùp0ü^Mp¢hc…žæ ¬¢XІé½4ks[·Š[1âBîD=™ º–,T@Âh½Œ“üÅ£qTz1œ†1Ï´CM‡ºuíNÝŒ¬²õãNê’6p*‚\(äWšTµÛ¬gàQÇf¸Ô–Âø½fen‹Vñ+î\hæÂ@(ïÞòXäïPÞAÀ„ûÏÀ”ÓÈ;ìšØ¾|x¡i‰¦4ÌÓà”„.ti§áŠPŠºл`Ü (HÔ`P5EÔ ‡]¸Åîñà™-XF¾ëÄoß¡¿WÓü–Y*©6d/˜(…©e¿dó^yjû¹Šz…ž d^ØkÅà _qüWø¾Àô… X¿Eø•e|–¦Æ¸á/5ÜÔ0€Ä@5?ÕŸúVÐ1—G4;…ÕÇÂX½ªIøâßü­VƒÕV5 ÕpnÅÁPCà‹ÀkÀ)¯ /÷þ@3îÌ‚‚ _hPÉÆYWVǽÅ78¸ƒ«˜Ak ­„'îÉWEÏÈ\… 8ŒïU–äµH*nŠ­Ökข†‡ ž(î7t+Eó ûì*]aö^ÀÄP=¢Gðª™Äˆx@ño"| ñ5†9À’Ї(añÅâaV0Ø€aCO˜—4+ÑûŸ§b»,ŒÞK°çnQ*:„ã÷T,÷±a\Š<x<á¦ä Èyò¤"È ƒÉ’yò0¯b”((+¡lвØèù½8ÿ)ú–¯¹.º6}¯’„¡Š½ †Œíy»ìTßmw`†ÂUÃÀ@VÞx0¬‡áÄ7EÞ |b@‚è ¡7U ±¿·úÝw½àÍ®’©qû×ÇÂȽø`øßâë¼®aN2ìØeWy4Ë›ŒØaz¯¹Z¾WÅYÑÙ²¬˜=1{ßD—±«ì"_ÿ×/h…þaæ@rí/ÚYz/ƒªEo¨]{¢gl›mG°×=ô€ (@ËZGün{ÝÔÒ·èqwL=ä«ÈÀ‡ + {B÷‡µ»+v{ˆ÷À¾‚A°CïˆÞ·¬TµÆ¾Â÷—¦Âá$ÃïŠß>àƒÁ‡]þpõ‚¿QXÚMÝ´–ÛÃ>Fïe‹Ã[DÆÂ^ø.…ã.Š|ï5_KØ"«x,|ø@+‚-×U¾±î/_ýeãé[=âÄ áFíû_<žãa{¯ç„ò¯"I ‰C$?PIle1~蘀à‚©¨U¿A¬‚¸ 8 p¬+à ZÁ°WÇtqàâ_A€P°8€qƒ°ˆ,pã`ÈÁ‘09€rƒ²•,\s`ægA Ð -Áæp-èµ@žƒ>€À tŠè;‰[Á-ß–oË·eÛrm™¶´[Ò-c–¯ËÖåj2µyÚ,m޶A[ž-ΖfK²eÙòlY¶”[B-–‹ËÄåa’°)ØlúµùZz-¼–]K®¥×’k)µ„Zº-Ù–jK´eYr¬Öüjvµ·ZY-«–UKª%ÕRj µtZ.-“–FK¢¥P¨éÓäiæ´oZ4-˜–KË¥åÒ²i¹´LüšÕ¿R¤ Òôhr´5Z-‹–EK¢%Ð2hé´dZ*-–Ë%@ÒŸÉÏÔgâ³íYô,y–J=Jæ ˆjí’VÑ—J­êh»Z°ÔKXéÖÄ¥/k¥­¹5K¾Ä ˜jmWZíÂdÀRk…Ù§/+–Zóox9PZ«&—úBMÔÚšºƒ 3—Zò†àíkhöR/sªZKä—Ü¥?Skâ. aj%,`õÖD¦/h¬Z{«˜}qhԡƹ‹ÅL­†˜» )djrØ€îkr,SúÍL­”É@¸SÎÔ—4v­9ùL»À¸ÔšSÑ… »šZáSÀpÖJ¢¦]´àjÖjá2™ÀM­tw5@±©—A Z»H›ï‚@SkÀn{—iM­ Ö¸¾Æ7µ¢ÁWš×àÔ3‚×ÖÀ §_à `Z jœ}a,ˆjmOrß…dASkZ™²/•N­Êzf_3 Za+àwZêÔE®ÁßÖb§_ú hZ[¶¾ÔµÆÌ;":lok;žZ)xš[òÔ—4‚Û­ :O_ pZ‹§žûb\Ôš öÀ ‚O­x…è¾Í|jRÖ°û5Dö©¶ÞÖøå§/r ZëËŸ}é/èjíØûB`àÕ/µè1¸Zà‚Õ˘ÂÖ!¬¾¤üµFgaüaÕZS‡ð ‡AVk|" /C ±ZÑJ`x-~BÕë ü /5V_† PÇáeÂÀYA ~#+ aAñ@.™¬E~ñ ƆYŒI@¼,+/fÇ*çeüM€+P¦2‰Ý³æS­‘ ÓÈŒœ“V€—ÐÚY\Æ (AŸ±~Z ðµ@ ­¹ (V ÀŽ&—zi‚Xàä@p4­#ŰFÅ µ© €rRS+."R $œÕ•‹Íµ¹“ l ¦¹V.W Vrëkr;ÀV Å´\è²Ë b T"ÚØ øÀ[[È^È· bAÙ‰[GŽ—í®[1£ðr @îV^D ÌÒNoÌE\­@îô[È+ÀËUÁÙ@` ®.` b ÖpöbX€+ÐA‰È–" q‘Ì@e=®/i™,¹sAZ0+1•”iQÖ Á1W.5 hZis{ù-Pˆ9ϵÚ‚]Ü ¨¹È-ÒËUÛ:]^À ÚŠPuÍEr®@µÜ\ˆØD·6»¹"À+_¶QÓ‘³ß¹+–1 ÌêŒw³Åþ‚]Àùй0á Sƒ)^¶ð`€+­È<ýÅ2AÏ@…:¯¹0ðè èiË!½‚Hðö;iZÕ ì÷¶/Ú+Ó_»}؈˜îŸË…_±1{›ñ‘ Ôbsg¾Ú"ÆÀ¯Øø¤Z62ì+­ØÅVë}mš +¶‚ò-í&ýŠeP¢b3ˆ¿Ñ‚¡_±Ûp[[ÿé/mÊsÃö&@Û2Àа1#tl"4]D †íœ‚¡‹ÁаÜi·¶8­D°!šÐ]ƆM …Ü‹æÀhØeˆ[)‡† "ÝlN>4Q'Fa³%¢:ý£­ÝѰ”)p›¶Ò(/iuaã±¢-:iØ ›RµGhØNÄäÌ»:£avB¡T¶À7ÂŽ¥Ø´èhÛ›w°qèÑsë˜†Í oÖ°6(C»}ìȆ-‰‘oÔ±C¶IS§d4lN˜{›•Ò°x|+™Ò°:‘hJ 핆èDáÔ6ÁX$';èa[±¥ :íx†‰—:ÏÚŽiØN„ì4s;¦asBƒdlƒÎ4SQj¶¸j'Q°ÉhÓ³œá¸ 8x”3§a81ú6†8NÃäÄ!Uß< Ç‰Ž’Øúði9šwäÃöâOqÀC6'‚ö <¨a8Qà“Mð¢†Ä Ão6‹‡>Ø 8j3œðð†íD¤Iሇ5lN(üjÄꜸ¦¾œjÚœhP l§¦àDPma@5N6ªÓ&bTíorüÓæÄTUð±aš¶©UYÈÆjÚ›°€Û+ªi òÀjŽjÚ›È`ÅvsVÓn¢è6³¸ší& Á´¡ØÕª›6²Ó–ÄW¸ÖÏ1MÛ‰`V¹†Ö´7!›hcM·‰Q¸¶·ÊšÝ&ƒÑ¬*6ÜàM[hpk¤Ãš6*Ó¼-Ü8M›kWgæFkÚMd^•1Ö´6á Næt[Ólâ$nÍn¸¦¼‰ÆWÚî”kEM`ÓV]Ü×ßPM‰uq„LckÚš¸8^Ók"ÛÕzMVGãÚÜkUMb´iÑ×N5u8ÓÆ¢_Ve×1M›€qQ°ã›6rO°W&ìÈÆŽlÚM$d5‘Ø´4aHÎVvaÓiâLnfvbÓÒDA¹ÚŠM·‰GYÚ`lGš;*o[Fù˜ÚË[ØlÚ™x.Wk;²i40+Ííɦ  smƒN6~&GæmqT6ÍLg´ÉhÙå™p ¦ ̹já›63´8!ŽÙ´3±lnÆwgÓf"!¨[ßžM4çm~6f&Gçm€„6ÊL<Ôi Q´3x¬Ó&i9ÌááM‰¥¹—áñM[§è.‡6m&¢¶ZmÚ™PBgM5eõL\Ñ­/'6¡L´£«ke¢h+PØ`›Q& Ò ‰1¶w29JrÄlºÉ±a6¡&Ò·J‘ ¶ pœ®¨l·Mi‰ºPÍr›B&GêB7Ôm*Ll Mȼ[ÐÆÛ„˜ØªÛJo½Ä®¡Ÿ71&q& áu‹DcÜ„˜8¬«zqÅDÑ:!¸7&& ¹ ŹWbÒ1›“ºBÉ ¹ bbQ¡¹27&„ 1¹«Té°MȉÏ]†ìÆÝ„+Q(¡»J7&4 M¨Ô¼ñ º 0±8T!Ó\7L8ò&DìÑÀ«7¡Lhô 8í&ÀıWÖº›&ׄ¸èÝú—:øòxº¿Øñ›Ãvý°7¡KŒ±³¼ /‘諈ìæMø‡ìBeäÆwÜXtõmnvLJDáUÓîŽânN¾;á#ĸÙï•!n|ÇíDyV¥;nHÐÅUñ ï¸X‹›f½ã„ÄÁ¸7Àñ~-Ûíê}Ç £qnî;ê#±Ü´èwkޏqèïZ:Ævó¸$ŽÇÝÀ®‚Ä€\ ì?ÿbÌÔ;%ù„ÿ@zl-ÁÆóe-JCñ¼D‹ÞÎÅ€å–l€pÈ"~3ôÇkñÈo6²¤ÕÁWÊŒçXapÆóÒ*IÀñ¼©Še“Š!¬ž’b<è¢O1žvF* P €ÕƒÊ2_Ï ‡ëâ ð T¢ÇB'i„c?Î*@¼gçYcŒçÈ@Üf¶‰nÒÌ'ïT33É8ü't/&¸e´a¹E ß([ÌŸñ4ʳ”LþKc~D8ØRPc0Î÷º€ÀDËç¤Ì8À]»0ÁÙËx3Ü‹$øÍØ‘Ô(2ü*÷&@Jÿd±9(Â2óu†©¾¡›Êlr õ啽†'2åAA»%ØfðfsÂfT°.^!FYœovÈH}²!€€n'(<`ŒÝÎúh¶˜Z™6›` ·$R 8Z ÿ•g` ¹Ê"B1—Ï¥€…'þ+XÇ’áíçë¦'¹gBe€¸Ð· a¹Ë€,'®]CB+?ti„§BýXÌ0±»X0%溭mN=·¸g@9¶ŒøFäðÓ `m -„/²C@÷±12Dý¯íÊáÀÑ@„ÏlÊfDÊ@uÊAj,C_l–ž`€º2X€°r'87 Ø* `ø ¤«1Rcî%9ðÌü±=B[¾`¯Ä`eM;‹÷u¡F“<è õl¢ÌŠ€YuNÆúP­O§8ó>g&[R-oC–/C×%$3Xç¨|{1k/¦/8‚NÀü~2)wgv?p× èfÇú@ä`}¿¨;öÜ)`¶Á´ë7ßÂ$“ì놀ËPÑpcèl©P¹[eÆûÅŒÅñ!•ái ¾\ŽÐf`gÓœ1õø}ßé¡lÖ éSLz”aî"H9.eù_³Þ¬<ìX§0s@—ŸûsÓÓ^ÿe‹Ù0o[lt§˜$Z2,]n)k¤ —– (y«ÔÐö°NÏ„3,bä´YzHøt³¶3”Ù±ð>èõ<Ú ¼H`³z2Çœ†‹PÊw7ƒæÂ°±Ve¥`̨Â_a!לB@fjËv»Xvé¬ Ç3.¹XÛʺ˜lY6zà ¤‹Œ~ÚWðÍ%€hÈ`£'€ê‡”™e2å"ÍfÉ Ѭ×IiNCʤ*á±O_[ÔÙf§Y´ØêìXkëÔ'x ò…l¡;g²@¨†jE™ù6ƒè”Iq k.ž¢•¾Ø/¦˜–Í›†cF\!íAîNqbŽ-³H¾S†N!øv4Eêq†Ð)¨qFglZÌÚzaÅŒKâÆ«›![QÚº˜p XYÉc|s ÙËâ® PAèÍ€€€X'<6ÈÁ#°‘»d5H” nÉÔ -s)Ò S¦¾œ/k™ª`€ºü; v¶£€ç@¢YÇ„;nÜI`v¿6ìÑr@ ««´fßÍÚ¤Ýkw"Ô mÎé:îí:ý„R;¬!Χá?_€Å„¹]ϱd H¶f83çú73õKÑO9aÐ7é@’ £]†2‹Tèµ1Äföh§]ÌfV”i/fÒ‚g%g¸ÑrgÐ]@ô—.X òj¬ }ECó ó4^BA”ßÁ°ò%€•…v\9SeŽuu6eÌšw€¤l 4½s×ÀlN'ráÌMlä²yÇà€0Þ'D 02æ$PcÐ5š¾ 5Í8Ù^Ìi}®bF=ö,$4Jí±Ù7Ãà±<¤ „;.ÝQ`ö—½özw—Æ==×ÂÞ›tÖîU:ÄL µ 3 {ÁNo‚ M=²÷A›T”¿äƒÚØYª~uò˜@Y@;ð÷ï ¸ï bBƒõÙ&¯Úòï|2`TÇOæPÖì-Ùq—cFyÊ{Ò­NyúM rñù Ö€&vÔyY°è¥ºmÅåŽ\h¶èqÝÇ>à ¦í³‡öz¤9L´iNƒfâÚ˜eçCð´~|جÖÕ`MÖµ[ÙÍ‚„0mó&qÌÓt%/ÚüÓPÙ¥¼–½ ß•¡ÜBÈsÄ2$\2›•Ãÿ»ßÉ€ÐúÉÊre³AIDŒØ3 ¤ïG‹›áª÷ˆg‰e×:”¥sè²3Àû¯+C’ γ·¶3Ç„(FÂêM®pŽ5–®qÞ‹è)¸¾Á46<¾| ¤Ï 5ΫÌ݉»Æcyu<‰\ç±&Ó©‚Þ/.×9laiid‚œí8˜ßùW`p£'€”Þ6ß;åäíÿ\ Í¿æ;@謿:¦ˆ»<›Ÿ:¬Cšè0}%€ÍH8g`[ „”^±þÜúuÖ3MmXù@¹ˆ¤22ª™ :ÃÉ¥õó¸é»´Ñ,Ö#€³µ20ÈtcÔì86ÝbÛɸõ¬¶Èæ@¢l GîÌÁÿN4Õ}x% §¦Yk œ- 7hî:r¤ Aô#¸^GÇadN^n@°«æV—.ëç*3 žti†cGV5’­ã†•mjÊ•Û-†˜¶é¿ôÑ’*tÏŒ·¾6B÷ŒOCäUнw'¤G+ó:mŠÉ Í”áêdzæ=†hCå+æ¹÷ñçæ¿ažKðÀ§ÿ¿@€2$€¤ÑwÞºxêž/@˜œ`\1Zº 'àÍôÎ^»P\,3J2Ô¸ˆ³˜2lœ¶™ù8CìâÅ¢íÁìÿŒáÅEËažE“}Å€ù~N Üfç|,ø¥?íÃÜ`³ ¬í¢d0?Ð sÀ‚õ’b­Ñ„•'5è;2Ä]Š2‡q†ýS~qA÷›N»e²ÝFbM‚ŒçãüeˆØ<ÚmlÃH¤ï§.%'Ï'€72 l Ø&ê"ø×:–{æÀaØ:ì(ë ¡“$ó0Ñì†éÓVœí‹´ PfÇÍ™[:° ?N'Í`ËÐs²g• ë6q½º˜i‹E›åÙuÌÈ#Y–sP}GcÊ€uÂCr:Ã&„FÞ }­Í×÷üSRcœÎp¶XXo¦3].1õ±w³à Ü+ [ÖÞÌÊ€3 Ù€i€î;ÊoBœsHé±/þG‹Ç@eu¬²¯~¶ýq>'ô4XhÂÉð иØ?c&†Pâ|Ç‚õŒú …Ꭰ½ýà ۀ€§$ÿ`ŒÈ Ù“Úù°3œ.|£½Êðµß]ÅŒžñ›©–!µ¯¦>é.ÿAUiŽÙÁ„:<Û 86»daÖÊ eèÚ³FáX!”ãÚ€p8%ßlдñJ#i¬È|ä  4@ ²3GOøhþ3À.c2†a§rŠÆÁtïÛ +£ßtA;|Tor=Ì~u‡6ø®ÒØ@ÝýÍ ¶Ð«Çõ0[´“=Þ'€OAu‚úË"€Júï g¨³<-@rÔbÛ§nvm†Ð‰ÐßÐ?Ü¿ÙKcªÀ€d%ït Ld lêåë.î½@'ÐÎv»ÙXma—ËKgŽN"‹°O.`àgæ[Ë þÛAÞNðóÛ@8cUØò¡!#“9ëúN'Ë€„é§h³‚½&Ù¤ò“wNQc—«SÄ@€'4\n}Ï¡vÕ)Éà WÐÍ-Ư…}Ì3ÄênüI+X©´ß+9ì:¯öÔSï“NGö/è<šûõ.>þÖcýòÍ뀯"'x¼Ù…dKaå[;ñÂJø‘Ko[ÊW ICî,õ1C¿É×Y¨W1jbÇ!ÙØ*,ʱvÒ0"Þ*,áW*Õß"¶ Š>½l¾ÕDyרlÒ®ßmDl¡ºXáÛJT¼ç«%Üks—’vèž5^í·¬qrö¿UçS¦®Kö”ô•˜šÖUÂ÷“4½Ýu¬L›®áÄÄd,Jĺè=˰rÒì\C®°™»I’eÖù|1ÅN¬ç$(“Äz«˜%óN&RNÊëJJÕ­äœiëÓQ¹ê]ôiŸ¤DÔPÎS´›ž”®tro¦Rlפï‚ZóºDw£’tж$WJ´ Lïü$¢åé$‰ÝÿOMê»·m„ceÛUm‰É’öaßU;¡S˘ý#–4û–_Õ²+=–†è_¼e¥tì|°~JX‚‚U Ó>,X¦ÃDûOaIêöëg‚ªÊü¦[¥‡²R%É4¯IË®©l¯S%6)iùmª¹®?­›ˆ°kõ9ØÜ4œ.˜¤Çªµuglh¼lj§f{»ÑùU]Iúå±Ùý“å^’²x˜]Æø²Ž[¼ÔFËBÈó4t ÎÑûö&)ù§[öö[.Ë‚#·îa=P¾¼«\c%‘ÐÃÒ°ÕãÛ5¾>U‰ñ—´æK´ï 6™yIÊéXå•’IF˼tÉý\©kÈË®PdÈ^…óp™ .pùT]¶ûnˆ6ù}¨^‡Ž˜Ô+rɶâ"ú(wÇ+^Á¦»fï¼”°³ºé! ˜ M©¶nIÆ£"Ë·Ú ì:K?’T˜Â2Ϭ.œ|ÎyÂÖ§vâ\‚v«r(§WÂd{²½ðÏ“…å2oŒ“&õF——U‹™_Ia^ Xm¬¹òå‘*À¾¦T³{L(ÖlúèS‰W‘µÐÔ›˜Èl‹ü)áÑÁIû ‘ε)±ø*&ã’™å@´•¯ÜiÌéºa_a*o¿33«×~­TJÃ3KÝT­fir™ž7vIé] =4]÷ö°\êYFÿþ¡@«æÜ¦Õ¼™dE¿…ù.±jº"˜ZçJei;Öò=qÇ<6†¢Ê£‡ L²ç¦«· ®©|+›?t’Ëi÷"Ûj=7u3¹Ó\9›]“ÛþÙ¾n7áõ>´^Ë òÄ7ïB!µˆŠIгé»!¹–´©?5Ón¿cXg_lÔm<»ûàrÅeØ«s Ý®œ„Ó<u]<¹s®æ5bN]5ˆÖÞÎô2yCˆ*)1³™ì@õù=ôZ±ãN•}‰<¼8ë;yèLÊô‰í•“æt™û<ÑæVÙVbÃG'àƒm£>=vHèÛ^‰ü-A5æýê+(൚¡ŽWTR6SÄ÷΂öñZ~E|Ö-äà0å”õ¼}Å‹×wFªÁýëm"©öXAÕzo‡[Õöwªfzå·åþžœ®”¤«uCÉÛ]ðræ®ÂÎpƒ‡?zv1)v-Àízõ…ò®€„gî¢ Jmþ¢#K]7â^"Ü9îT;¨ñ{‘‘ƒ¾5Ö»¯¹yW!Å‹¨ÍŠ—‘“gT¹Îú¨Ò¡aíÚ`ÞzÖˆf–C¦MÖö]zM ”¡&ÛÓœ ù¹«Õ¼³ÙÇLYþÕ%ûVÏ´¢éz ¯6àßžh]ròJ»Î¾e…ôøˆP»¬®>{ïnÏOh×¼œŸËÏ-Á´õÁ6¼¡Ï6DˆÒx/ë%ÌúaJÜ?Þý† –fù#t]ÔzËFª{ãè:õëxž®f‰õ\Q-Ftĸ&7¥Â;Æ5}ŒãyŠ\y»¸äLrÅÖ²è}=ü W¾4=÷åy¢6Ý”˜ÉÖß6(U¤ø{ÏøÞ•£zÙ?+“t?|^ÿG$ÒÞ/ë§BcäÎRÅŽøœfÕOä³FU~é§àèë¼Ã¹ÔöÁå¹­-hìýqÑU6ïÈ‚"tê\ yß§O@ž{0ä-Upyê;åmWZÙÂç)Ðô8Ÿ½­Ã|§šÁ’è/v"s¶ò´ÖHP7ð­kû<ý% ¿+`m>u¼>˜ß¤ 8ÐS—UÍG2›d†Ï]¡âv„»îr‡)}¢/i݇µüƒŸKÎëo\x× l½)ÇÉòú¦MCðŽrÛ-„êK·õ—oKÔ³2/O$h•‘*ºíÕù*[Õ©¯-0¢¢r8®"z¤¼ÚØ`îJ »]z[%µvÊIs…J|£¸P•=©•ÌøäûÚ”ƒ+¿˜Ÿ¼¡þs~.sËÉ× IÿµÐ½®¹9ÓUYßšôH ~)_æ‰öà³" Ûž»µwBÁD©ýõø½õM••tÈwiÝã”k¨,©eg'}Eêý¦²Á_Q:¨6Uú¶ øóú¢ L|½}¿sÊIáÙ´y±ÇD(²2ö® Ÿá1yÇÄ—½4ý™›q£afÅm“ç¯WÞ¦n­OPäOÝ«²7[<~ªy¡É¦’Š’QWU5gê+$²zÕtUÁÚ•áÅ7Ãõ e;“V{ðëù&,w‹)·Á—ÕõçÛq¢ßUÀM•ø®.9¹ÁUlÇuGãí“\%ûîáUL{6pÝá´ëœ,ç®àn¸_dÈ?å11 V¶¾ÍÎ*n¦±¿7u@Wx~ÞM°TÝ\ôÎXõµ&è¼â î{°âý°gÒ%{<¾ ØâN‹¾C@žÀ·Y=Og»".ÁÏ$Ìå¶° ®EqôY°Í]²ò@¸Õu!ù]ËeIVž£Àtö+§=?ƒºÖôѶŸ‹êú•ú¯òŸ½Ám1®›².{µ²Ÿë‘ýsçqÀ&œO¦œÊ¦Çµ¯ö§*å?ß2ö,ê|¶tQé£[Øù†\[Ð<ÚáYÝîmMÞpØ„v#T“Wª#Wêoµ®š$.YvÙ²×ÚœjרlK§—„<†òò_$òu›¾Z†Û2Ö43“á&ðϨ!bòЦºsåê‡6Óp–Õ tÒ#È)Ñ{Þéêëðı•°Mi×´—~K:\>¨¼N{4ìL^-nG\lн™:âwå=>[û™é{θs%ÒdðHÛ÷}̓qcL³â×wùª•ú:ËEœwÍÿußS{Ù§Oö óÔKQ¶€ó+üš“”Û”lÈQ|­>:ßšô·.Å( QN©|KÝâÀ—½‹JªùU_Ö®Åë!˜žÜ=išZÜeã[™Ì8R,>¿‡ãÊîîýùŒ·7\þ!§uühÿ«zž^qçí{¼+ÞgÎ P&éV¢‰IX¹ØìŒ ¸i=abŒõ‘ß[nºE5ZÏM¼öêiÛu™Îìì"vXT4msWy4±"ò+¡8ÔàãV5“E ½/å}ÁzJÊÚ@óvž»œGrjÄYþ Ü,å’mÐÑÁüpå¢oš§óYÆ”Åâø‘…Ç[Ó\eÇÕÜÈ›|T£¿W£C'Tn5iUÇš·ì8åcYÏ8< ø»-.¯¸&¢rrú»„¸.Cçé|Ø~¹—;‘\f[6wf©òÆë-$|©ä¥ÖÊe²­ÄHõªåÂÛõÅûü߻㾟ÌAÉÇ’‹æƒÓOÎWR¦«Uën•y"Âý*%Peæˆ6IYl¿0\Ê<äÐvç.;Dó¿>ô¿ÓÏÙ6F©œ•FM}л^%Vse=° 2ð3ëšÔü[A jý””é.;üçB{˜2öJK[Œ¨Ã}J´iýNK&q+â“¶ö¶>ŒÕþi§\ú0³hµxôÈ•âýèï4»7äJÕ £è|¶ýžÍ¹ÜôXú¸¿½>›g”ÆäÅ‚%DÒ*>ªâhÊ%¥ú®ëûyçm"6Tìš4®¹UÌËU€ŸÖç|(W#w.ZjÕB?b»Eeó9{–çQg+¢È׬R ©äG¼ÂŠÖû‡èûÏ ­¢ßçmM”kD}øÈÕpy]Ͳe»bº Ͻ.è@p »–=Gý¥æsóyäè÷ѸR¯‹z>­²R*ô¼=+¤¢OØèŽ|{ªD¢ý¼+ï‡í×Ë]Ïýx_£¸B¯ô$mÛ˜ÑV~Îr±dýwê#ú]×.UÑ}k® W—#ÍOÛ×nëpœÒÖ¤j¾žˆqôŸõ‡thy:3ŠJ—¹¸ìA$`zéoµˆ ÿP‘º)˜î’ý[Ü…C§ÿïZ&Ùo_ÜEúLÆ…*†a Ø+=?¨DØN¶J9P¤þ¢ù¢ÓØ5zÕ¤Ž=ü- –Ú`K,ÿ¬)΋‘ .²z§U¤E—·`ã&×̋ىj Û¢/×g÷¦Îà.äR ͪ Úw$Ð{Tw9Р+.gÒ*)Üß:ÐǵpMÍùx|µfgu[šz¼>Ó_~Ö¶î4Íl˧ÊÖuÂÜz·Î·¦‹uš…-÷+`Ü—æÏ#>k'YÚWË=kè鬋ïº3—\ ,w^=®\sÐ\d¨o¥ß=s#&!ǧÒéÇ‹n*’-ó’µÚðq™‡w¨Y¹þVª+]„Ë–›é»+J.úÐY{ê:·¯Àe{¥Jµ¥JP××݈"žÞ-×í»‡¡Ÿ¦Ü ×{wgž&e¦¸µ±PsWíëÚë5–¡\öŽLªnÙ‰.Cjƒ{Œ$¸·£lÚÆóïÕ j.çª3!ë o…‰ï*&ä¿o£ç”é]yׇq„\¾¡srvYÚNô¹….zù‡†Ø=¢IËzC®¼hWœoÍly¼‰†ž4QªàM%‘ì•>ËB*´«GæÒGqÑíó‡ó½®Ñ¤Ü¯¬ ½xNkúª²kWîä³E×€§8äzöw‘ÊsÊnI.¾hþs<÷N¯Ûzl»®àüOvTìÙãÔJÉxšÜ7ùãš=¢’ˆ{×þsÆ+V&÷S`=Ïí×S§®iRå®ÜYÄj®ˆó0ß+ëZ’‰Æ•ù ìWï"{{ š´K÷ŠŒXç•“]É;Ñ¥€·ËËWw²‹žx[’õFÞB¬åÑse¥¼w ]ªDÍÞ+q ‡d`{Å*âMK0z•óßy¬ÑØl¼ývµ¶?ÊmÊÿ0uèúTó¹z^–÷t¦·âdå{­Ç_“lñ»ç€åLR«¿„35X¥Ëó ™Õ¿pÓÄgœqµx«ö|è¢pöÖ!œõïA«‹Õ瞇°*؈¦…7àXõ;2ÇMë•“8³žoòÃÚ×ël3ø¯B Ží¦G>Å|èÄyUʦÜJûä‚ï‰}õ~15Ûê®àæŒO‰ûù«°Õ˜îÙUH¢cp披"ª(¿£+ñ¹eX2Ô"¡ÂÕâáË0š‰w*)ÞÛøuÎeJ%T2°Ë~„ä 4WxW[I„W/4Mµ²ðl9õTþÔÓzÖGG†×WÖ ÊÉpËŽë÷J`3â)sñ(åz»(#•]ÄײÜÜl\įI²P’’[9U¼uÔzáëW½c<ë<¶ò­ M|ãË%ªgÕFÄ9 .¸PY9L¬öâB† ʯÊðô”Ï¢Xõ‡ ¹Ž¡‰u>0g›ÇwŽ\w¯1é´[ã¥YÈVOárµ'üXdJï½ßïB‚k òv(0ÒÖMÿ#ÙõÃ/ÏÄN+þê­ï3¼ï;=€$;y¦üKý+ë3*ÞUõýí¨žèî!›•{wGW‡wv¤· ™¨Ý¿F ‡wä§¯Ž¿ï¾a»3¿½©&ª$ä{%¼¨d=ãGòçç·‡“¬=ŽÇ¿z_üþõðþQ0ö›Tú©Ž²ÕõíõZ*£{K­:ìUååùÓ#–ÿJâGšÙ¤üi¾“$¾xuJtýzrTIÞŽ«³£»EÕò#ÉDó“tؽ:U¾]È¥û^ªï:ûd/¯Nr‰{NP—Jd/>‰¿ëårÎQ¼búLôè•ü9ÓŽõ ·£Ú룱·ëˆÛãoêû°Éï^°#}íªt>>è½\öòôJ “Ÿï8tÌ«ŽïïÏÇ™^k’]h¼÷Z ²Cÿ˜‘•ê£#±×ʇGФ‰®ÞYß}?‡Ü½;ºŒ}߇½½ºû¥îO™ïÁ 524P |±’—'iR¨¨—Oá Œs:ßß Ë3`FÄ`ýÁÁAºrÎÑxG& &L\‡Å]Šž^X¹’uŽ«.,½,eK‹áTJJ³BW²)6挚Œ¥ÍÞ›¤u³œêÈ[:Õ·¬äÂrÍ]KüÙ kžÎn¡…ë‘ɹ]Ù²œ{ík[Æ\¶rÊSvûåêöѳôXzJíÔ¹õ¥þr}´Í­÷÷’îÕ%Šôz(g¯ZD<¹Æ¿n•1IÒéÄéB7ã÷DSè–ÍÑr8¥°#Ø•¹Ï±Š¤‘º„¿3©l/ó÷S±USÓ(odÕkDÕ®?gÒ¡Š=ëŒÊ”E8ëÖí„È^‘¥²TŒ’úõøïÃv–µF.E.Ì©É]MÕÍ„Ú[Ý.QŠºQÑ\³çcPFöR÷Aì%úc-#þÁnN5„ B2ÂÒHñC+ÈiTìJÙÚo™ªÚWçÙ)*Bö÷ñóåà=2eª u\4ª“ÈT“Örx*”v¿± ôVsï©’»0 Óê•/˜oØMÑj›.9š>·Ú°oíž;[§ç-‹TÌ´|eGŠªÝ¬^¬É®äÝ'“^M*W’§!Û icÎDØ×ñ™–›¼Y®{t¯~_7bäÊýJ“m-£Ô´ªü³§{GùQCäZ'½’iïó—­{…©7 CfQ?zsøŠl;L~nfŸÌÑÅÑîvhuЖíÉý€v¸û‚3ÞžwÒuf­[¿2Ž›_¥å_ÎBºªíÑC­%ùŠìã³zJg¹œ½ÀÛ ðòI€Ìo+yܳ½Æ„ø8Ç>þ?‡#À€ûóþãæÊ=Û ëÿwÀÿÌ s«ñaô.€´çÍá?ÿz!-˜£<€çâ-zkUs9&›‹õQÍ-¾‚^æ§ ¸{€´ß¸RA*›â\—ê£rÀ€¿ë¸R€4§ /þûåÖLJ0—>¾þ àÛÞ úþཎ #LÞ þýõ¿M>ÿÀ ÿ›å#`#C@ÏÁŽN ˆÁlmû•ˆõ D Fþ†˜Ó28@ó…[„¸Ä6d¨5õ&Ïßo‚ÒðTO}½(Øÿ»®çå9wmãSο㠧+é¸Wõ°œO¹®-V”°ïð:§yï/ÿõŽßð_±.s}¾ž@ Dû€~>À_<HéÂRSÚxÂõaÙš>ËÇ;†•g€õ-Ê4žûž€ë]6…´@ýù­S:!€?u¤2”Ÿ¶)åüÇ }ø¤i}c@ãøþmÚ2€›p"B7aEŸ¾ ê)þ÷7óÀÐŒtú7'÷÷ÀÞˆø«Ï ˆ±ŒÀ %ßÕ}ÍäBF Ë×ã8˾tR¹FÚú{zÕBÖå£Ýp÷ €¦¼…n@º¾çêü[¿{emeÀÿ~Ó¥< ñ\NìÔt ?âßÿ/€ìåÀÀì»q³œK‡é„ôu¹h·Ü¯ëÆáxõžÊ@]ó6ï•>§yúŸøÏíË €}+\!Œ¸€>i-Ó@€qã¤ÿ ÀG8 PÀ"3@»acÍtÀ´ozèï!é'òdÂýRÂø‰±¹R1(¡ñ€»R¿ ô¢êÚ0Bq‰"þì+¥he»>0æ¿yQ|íÇô ü0»ñįy_λÜ3Îp¤Ë\úý·ëÀå\¬˜ºÚwà•À¿@€äÃí ´‰·Yãkk®çÛ‘ù+€ñŸ9/ÿë;@n`° ŸsÞý¦w\Ï€Zz O×àOßDyÎôÅù@"B7a ¡õn)˜ïÿßß ÌBER@!ðöè¿7ïßà‚š [› aK …ó·³Ú”|NBâP@}nÀ:p¥ý¥o#e»•gÀÿ3T×|„Àé{ñJ6µ¿½ë›th§´`Ë?–Ò“–x¿ `ï†À r;þ÷¬5G9ÿýýV¶ðõ€Ô»Ò m¼ôK¥¥ï¤'¬J+ݱ³àÊìÓpá<>ê}>ézOiÀ–O`,åa¸Ÿ`€ŸŸÐÆÀ}ç­¬Ô¾@iüð5ñTŽî @"3ßá‰ÖA°bmÉ:ÿû”‘#Œ6þ€?wD÷÷Áð£ÁȵфõF ¨f¾#7 õ’~"!‹z7?Ww~r¬£¤¹”ziN<ÄÀ3þܙԫOûŸúÿ¿€ˆ1mÀŸˆó席6þõ”ð؇×w~r¬£¤ç,KËpÒ Þ¦ÖÒØ ÔfïP¸wû3ëYÐéëÕ¶çü~__À·{üù{ÇZùàÿ`"B7aç¡áÆá*üƒßß Ì@5S§0~úBBPïo¢ï¡¤­nË!(¡z€¸ßµyÎÅ] àB$‰ýïÀ:q¦û–iùØ Êì3¼¸}öã2¾<®…aÎcÀ Zü#@+lV† {àïNŒ•¥å€/ïÈ÷€ý¦µZeè?ÿöö1•ŽêÀ°»ÓË8ó}c¥_9%¤ÇvL^eÜLËÂ__'ýK?+<ÚÝ®°uÀjz?ÿXÿPË3Nów[ÓãÈ€¿øò¼Ê`gßý¿V97½-c7@#2?á.µÀªMWH°ÿÿö(#1GüÆFïÊíïÃðøèaaN±ÀŒ Olé ¿ûބķõÂÀ7J—l×¥#(ÿÎLËc8ù\¸Ý´Q^»Ú6J M ³Ò˜ŽWÀ`­) œå¸i€À€çÿh¿ý÷Ÿý€ò•€?®ãÆyÀ oñ…þ,) gx €à»%µÒÛ}éÖ!ž2x]\cÜùÐô°ôm¥PÊcn;ó}ßÿ@Îpàžu_Û\öë¯Ót-Ü{ýíI©8c7R ã–cy˜@²u¼‡õ¯€ïtýÀ."3 oð‹æÊŠD(°lÿÿ0ì$5RO0ùéD>Mýý¬¶br;5¢ "`[Cû…ßv@Íø·‚T.ÿÁ ïkíéð-WÊë¼¥•ïÖÏÏôë ¥ ß ¸Ï ó ¯p» uBøxÝþzÿ°/€í9à@¼û8?F ÐÜ€¥ý@üvÒ8Î9ù²˜Àåë¥ ß i çaYž´ç·zëÈgFœR:¬@ïsgÿ¯=ÀõöÚÒžöÂ7çà@ç·°¥é!¾ o~h¿¤Ëi­÷Gmã¼õ¯ô€€ÒnÞǾÓ@þþ þ¥Í·-p032@‰bº=óBé‚-&ÚýÍÂü@T$þ‹€û÷øý=íFNÂÊ@ØF†Ia›Þíù|'C ˆ¸­ü…f‘Ë‹_;:”w^ùÇ•ñœ®øN~jÑîú@ÿ{í.çl±+[÷i€í]ç:—Hâ´<½€€ýZ\ÎÚç½Ûî@ƯÀkZïhàl:WÏŠøO@ö®S¥%Χà_n@ ùáT´ˆQ^—OÓkŸ®ñœOùDz<ï¥t.O¯×W`J[±}€Ÿ;Øôey/qÖOË耀ݿ¤§Åºû½î@ç¯ZýŸµF+€<ú‘ÓK€3553 U}7é[è„™žINü¢”žz“Dãfž -;’ìÙñM{®í>8ÿ&nÿ`þ€LÀ0ß?Lp0áÏá<Î<èp½³,âšìñ[dѧJÆdU«M6%Ú«Û~MYeµ]?Ó¬Vm¶ŸÓÕ«vùM–f”¬ûÝYK6%”±8c$agæž‹àD%D3CPÜ ¤mó]IzÕ8¬~B€:äcÉŸÒ¡0…B P(B¡ÿ…šDLcŒ(&¦ÊNGQ¸Ê(¡P…B P(B¡ …Ðû€î¡pDSÕ@ E(¡P…B P(¡£ÿ û(@·‡'D˜Mˆ¶ÔeÝ=²Éœ.G’Iª7 dš®qtÛ ÊÛN¯9uéi»f@…â•Îj qD¢¢‡ÖÞd\äq{¨ì‚*5 1“Y’¡³s¦Ä$Nä‰Z²7®Ea— [©“Ò¯C$dRïy-+,;‰›&ªÈõ‰:OX»;§¨h¢lžWùíMÈweáÕ3$ÑaNÒƒ*AÇwI¦µZ*kتÜ™'Pq¡TNnônjª©&e¢pÃI¨ÑV\ iÐÃ¥KÔ!â|nZ:n4…\¡Ýè¦@£<ÕîWžŽ#-UURƳÎJ4IªÛ} ‘*š_ú\+õÔyÿš¸TqÕÕªXTw“4ƒèpqªÆª|¹„6€$Ñ{^¨‘‘æ(4üéæä¸b¹N=Tž¡rÍÐæE.Û¼¯IÛUÿwÉ‹Ñ=Kì£ ëCƒ…QË3…С›Ð¦Á¿Lµ ’56ªkó£ëw D * ô]öñÉèÌ[·aXEîÁñÁjV8ºu}bÉ«I5:btt¶©º¯Êç®»Ú'ðöU;^lóhcj¼ à*™PÄ}êéI¨¨YPECÅÌ9m ¡²µq×]é­ ȦÛSÅ×ÚqMkP\ aðºM‘ßk\гV¸†n¬qÉïGBk‰rÂø‰r¯öÉ^Nþ]ªIõ\ëSO°wÑL•›8 ‹Cª‘õÄ¢ ÔH¤<œòJàÔFWÀ$Ùh:B6FDø=x#r;伂=V±jDÊÚ"CÍ3Àží¸_ ­çC GN åÝvUµ½Rªó¼6snø=ö/i<•Ã.êÊà¡E ,ðO¾¬¹?{jGØÉ÷\‰ø·’$ËF•-ëä•ʉ'ÊÓ©Š™+x¿5V*Sj‘'[–sËö‹^# þÝÅHŸ S·uêªÛ¸Ñ±Rºöï4 -ÅKÿª¢×‹4D¨˜À!ÑxP×\m‰èÁ×òZÎoIÊgúÀÏ÷ð- PÊ…h&—#zÎ[âOéÛÿŸ: TWôØM‹ß#wF8—3ú#Ö X0‘mqÔêP!Ír--§­©Äv˱EôíõK—áÇB­§ƒÖ$D|º¿½ÁËc¸L+¯Ñ³3‹•"C–å(£Fìœpë›Ô0Wtav ¼t{}lG½Â¤úÚý¿ÞëÔ{z½Ùη¦]Ù]»)áȈÍ&<7½Rý½Srˆ„°œ®?é ÈŸ>f²§Ó ËÜ êJ(Ú\ÒæôRÔ4&ðé^gÀO_©á‰ÕŸd+V§Hªœ˜[[m“ƨ3UIÐ?.§.\‚jüK2²ž‹/›.½;Y(ɤc4—-©8¡¡²„žª"|W~ä™[äå¤y8M©qÈptSc%=Ö-Œ y©—V(¨«¦wI2ËÓ«ß°H›…ù?¢sþ„×3Œƒ..¥…ì[—€˱VÞæ2Üö 5ôøíµÓdÓË5ß\Qt<˘áëˆDN%.´Ù;ßkѶšùW+I?agú®‘ÝôÆD2àÙüL÷ ùïÀ¨k€”äš¶;=–aUg.<üh.ò‚Ë=[;óÌq]ïaæë¥ ÁëöÝÿæÆñAÓo+ÙVa¦>ChÝE}¿d¼„qÁ=N4 {·=W´^xs³ÑMo¹^HQ€¼>tbjp‘×ĶêB9DvsvGÚX/#®l{»›tË%kQÁ®çKáÒÛ›VAß$‘e’éñ›­Ÿòë:WX±e¿Õç|ŸRkÿ‰®·ÞÑÁ¼´Ãæ"¤hÝ!’SpBpŽ–:qk'j©.>ƒ*„û£N+UÇéù©Ž®Ù¸NhÂ= y®’µ.TÃd}bpdÁRz‘B'º& 9¢@eÏi˜ÍEüÃPó„HÅ$:Ö?h—øSZaÈÇîÀÚŠHŠt×Êh¶&®£FíøR‹³ß.’;½Jm—Ç}a-x•”Ådr1’›tñ/€E(»GN·ÞJ Ø©d=1ÙŠz殽ƒ£ÞÔD9:øÕNÃ:0)ý³çÇ›-4(}¥òkÚHWõäÒ¤%cˆEyÜÿúÐäTp9~Z§Àµž‹Ò¤–'²ê)òB_Xô L4HÁÖœDë74´ZzÛÐ3ЦNè²Á<ØÙ´ lÓÙ²WLfŒÑ7†ýKƒ‹$iXÐÌ'VwÕTísÓGŠ-nÍu$oÿ ‹’vq™P'5ô× ¯#Ì^Ÿz‹)ÃU°ºŒHyÿA·~4¾‡¿H)ç7#Ã4ü„Gžcoûßßú=ˆ½†×v¼ÿ¢I»½õöûFÓÖ×UGí¤«+Ãw ŸhO@ kÎ1y}s2Ñ'߽šùVѱ ÍØ”}ÀòP¶%ÈÎn!)p6K—7ÃU•áû%e¥Èä0Ö²×{*ç±z§ç¿HÖô Ú¤©E¾ãw ~p:¡’S¨Mò] ¹¢ßÕ?¿ßN¿÷(4¸S<ŇdÓäàaŸ“'÷q÷y¸À»m¢Û_¯HÕ²ŸXæräÉ9«Í9l؃ӎÏìZr=ó‘Âáˆ.®´³£q힄ݮÇïzö¾ó-¡R{À$Æ{`»›~=»NÂ{àF~Gnö\èܶꥩ§ ÂB+Oïþz9T¢ümä)$°r§ÖzœXx—•_ïè´yºê…:é—iJÅÕÖÁÇÞ9..룧÷.ÐżÑ``áÞ2Ž–® .Ì0wö{ƒz ¥f‹ÚØÍXnD½©ðgzÍ÷ªÛ6±p“aÀ¢ì²þjä'öò¤¸È7Ã-Óœ¸S8ʾ ËŸûÙÄŽ$ê2v¾Öós}Ç×ñNa¶ù´Lü ðøèW7ÒQ¼¢Ð ©llÙõ5šjâò苯ÒFnñï±`µšð&k²Ý¬hÚtP{PÙHieŸ¥Äóí(*{ HM&$RÒ˜’káo1ã¯GF˜SegŸþ@Õr(~+#¾Ø :•6ÊHsHjwSv}ÚÍ]sѧîÞ|rÎRäL’ŽÙ¾ÒÜâY­L<« Àö· jÕp>=n}Y³ÀÊ™1­mÁ>›—‘¡bÜlzå½Äk†6‘qŽ&l ¦5Ñ©51¾ƒ>Ú³ÃjD(3Ø­Rÿkp´Ê"µF>¨D7}+<eˆÞ½ú2Ë´pZÚ;×î²,?ZÑyo7—ª”¸«”qZïõFÁÌpµô‹ÍÇ‘›øk#èÁ)cºqÍ1%Y`›°ALÜSÿþJ8JÜJëEîä)t_Fy”þ +nö¬Ð3Þ5™_ìàg‹þõ]ƒd¯›×vMGãs8/ȸ&aÇÕ°"ÞÎ#ÎÎñ¨7¤¼§ Kµd¸û†LìS[Ò7ßR¡YF`_ñÆn™-o6E±ç*Ç ‘ÅÎr]ü`OÅØ–ø+Ⱦ@ØÚ^kÊ®Mê‡;œ20XÈs7ËîÆÓ¹wëÅM» |ô‚†#NØ*ÞP0'îoíúy=À”Ÿ×&ãØ4즋Æcoc2ÌŠÒD3´wêh¸Nc*­ãÂÈmøuïö4wIw‡žSÌ;ª¢ Ê÷¸/:rß×SGÆŒCû#´´<òhPt+ Wšµó›u»SyIõ«ó§>G=dsÐ9ÿDqÙrÓe“Ì>Ùw‚ -Ü-ª¢ïšáYè÷´5¡¸¸ºú fêíŠ ™`ñˆJ¢ -I†ý Gϸœ–´µi Yµ‚ü8¾ ÒÆp>«Röá‰Nã’ª_ Œš&ÞíyPÑR¡ÍIŠÊ ø,.‚w¬›'n9ö>U}7.É?ÇÞ1X«R.þ‹…¾…îHÙ<Üàl†Âó¹ñqc½%Vá'—ýò>Nj_Òg‘´¬8uv!ƒ)“Îd›/ðxfëfÈqU¡Crü¡8”ÐÇ{Í^3g±×ô4.m³~‹Ô³aû‹Å¯½ù¨_Ü#º¼ä÷Ø| †Cgð–¾ñà3èÊ¿°ëfgfdîZÒ4èó¿ú~F8Ñ«Lüÿ ’^hÂçŠêAÜߎL}¬¿3ïŨ¸Õó5ÑyÇòi¿QÎ6S.‡*VIì¼ek*ãcd—ÿ)wžõ‡ÓûÿÎ^í§yUnZ½uud*»’j¡!OfÿÀ¬Òµ•}ÇoÑ¡7¾ç®#Öüÿ5á\ÏNÓ‡õY,y‡¦ÆEvÝ3?b’¦¹‘× KðØ.n|_5W b/à¿Y çÁö«ã]Ò~« ¢ß°K‡ö4ûÄþ@ÏËÙÆ+ïÔ¬WžåÆïŸÂ7úU¹yÎÊè84õ¯?q»¤øÕ^L›€Ò+î¬u`I»§¢ý‰á¡1• Á6¿ž­_/þèp vèó:½ÙÛ^ßð9‘{)³cg >‚ßäþæëR3¡/BjÃcðÏ'’»ï²zÕÒå7ÉÆ„í7– ¡y4“B¶ì¶)`ÞiίˆÒxºUxšm+ª3j¦åNÇ œjì®—Pâbo-õHÂiÓÅi+î é×µÂ1âdï°÷Ø. ¥PÖ¶ï}ü ‰· ›”Èqõž¢ÍþšGltø×£Â„üت›*¢åzÚt4›ÔO± {»êƒÂq½nÝc"26në—»4A0¢'pÉħº½è"¬¸G¦Ý Œ@ÙzúW2Û þcŸMÇFÑÖìóSd¦ô]ÙúW#šÖg”ÓÌIÑ4Ü!t ÀÎhŒŽò íÖüæ:ÛjJÍ/³ùÜó]®®Ï1ÊÎOZÉ#Ä7R_ú]ï5IĬ•±:õÖ»ôâ­Ý…¥ž¦ueí™ÖWøtšD‘Hó¤eHfí¼÷œÙn3%ñn¿ÞEþ#.rANøeï›eü;O¨Gl°‡—½yhIýôT':vá×<ÉÁUŸIÆ:±ŒÔV°¢M3ûûö6ÉÜW}ôדÄ&ãÿFjÁ|ˆÑ‹ïEÒK~PÒ¥&w¯Èó¶æ)á«c—d> ©6Òðä󵁾Î-†G¦§gÄ äÁ.ìùz«ÕkA!_ßg7eœ>p€}rä °m»§ÛþâÖôÀ;ì¦9ˆx»3ÛÓÓ ^ˆÃlZ<õ˜Æ_ï6¾þ­Åsá=°Eø¿kn)¼LüÒ›= ôwš^>éÍýç€ï8¸¥ù­&R¤|+¬ïÖè‘n?W·yõíßäÝœ”žŸúó©"W*ìœmMG~<嚆Ãm=úþÓÜr]1 yO/]Çù³¢¢¸kЇÛ<“¾5­ º–=œ°ÞF}KÉ7wßå.Ç÷…DЇÏñèÉê5»aC‹_g}ê§Ùó÷p™GD\Hÿ÷Æ^â¹Ér5Çäð ø¶¥ø1ñHbv+<_L%·ºìëûµjXÀ7×÷³ØFnՒǶ{ áî:îÿQ×z˜ü*›Ä½’-¶ âËž¨¹¸³€ZõHhÌsHr£ÿö]o;jN¼^ÕÞ¼¹fo$Ïýþa„tTÃÔäúÑÏF‹þÇ·][þ…d¿Ér¤„wœË²&Ü-‰ÀGzÍE°cþ (rÇÞžn¼‡]EÜ£ã|íò®¤a1#ù’0TÑË_½ÑÌÁ]6š¹…¯ä„<ËD,ɪp[?uui ý6ºÍ­Wb€OÓCï>Ô÷йÕЄ8ˆ„߇'ðžü™/!²T5}™bÍ+¯6û´êÕý_º²ûÑR{4”•-ïxDÃ÷\#„q@Ó<äSaržì½³Fí§/~ug3ä~1{NÌÞÄVûì˺& † FƒÝað{Æk^YŠÌÛìÁ=¹€7:¹ºÁ.ºy{¹zoa±´m9ÈñZÃ=µ»¢q_øÍﯸh²~›£yù9ü¢»µ›d,Óêe㪆cÌýß3&RÓHâü˜æ´o‚Ji›´GØóR5SÒmÑè)6èi@ÏDMkÁFÎO¢¦g ý±ý8ìÙ56„k>âG@KïX\n(ö]ZÊË–ÄyOø?#úsz*áÆŽ|ýïÎY±¾éÜôÙÙ>œ#ùÔ”F«H4 ^i$\?iRóæA·än="ø âèÌ–Ò‡“ ¯ü­¸yª†Gc»U~œ < iâZ4`³t÷‹ýð 7Ö ©{K Ö¨ÙRÊk_÷ŒjY¥ÇN³¾VÓ‘¦ˆ¤}½˜ßÓÿ/û‡À”Ÿ@$Þ媃´ö v'Kµ¹T±‚yWî«„ùܳCï;ÜBEDÂùùT ðRúÛú_ï+) ¤Ñ¿WœN·?#è¢ÿÓ/æšAtÓ¥àÐ-Ï5+Àé>I.eÿf¿á¦³,–-²é¾K.|ÛKÞ¶©±|ƒ{­…ûµw-†á¯ùiÿl¼)Þ:ߚϮæî ÿÏ%cù…çmÂm¤ñÂÖíÕ™ÿsÑšV"K•RžàÀ•o­;°Ÿ+õö"5a³ï¥¢g¤øaéÏÆE™øÿô¾J ÞPÚA†Oup3fÑ­”º;fhõ,vé/[¨Ü±iÓ8DJœÑ '¤ m¨Âô "O@¡pyH¦µ!å$¢ŸTb3@7(4Œ‚ ¥@žVóŸAÈ8jˆ6phöDѨFÑxdäÆÁQÖèÔ€$p’Ànc\˜™Ï%Bj, @·6^H݇I†…Nz OnñE;*@Hb8¿'KÖO“¨pyEg¸@¨\ënDrß著I )XU`˜È|Ówy>JÊÛ ×¥ ô@8j {jàÏ.¸Eé*S (ahªñ\Ô&/¾ã±öôd•ž =E=KD‰C?ÎD’äBò@@GHOCW‡U­Ïè­ïn‰BèÄæjzü30ø»üùf‰uÃcwB2þ¸/©5nîQ O{Ž5k1-HéLq#KPmÕ{Šz¦Ôæ¡‚ªÈ˘9ÓÓ㕃v§÷¸ü " „‰îö½ Ÿ³.¸`0|¦ÒÁw ó|QÓè(GBnž^ñ| AXŠÂæ%."„^òpíM]ù³‚ÕÀJ:àµ*˜ì—“ª:f®ÎÒÓdâB‚ÖöpÙP!ß`nh¥–ÎÕs6¼ôá°ru¸NszÑSt^àB‡s¡b62ÕÅÆ ¹/õƒ€6Ø»«~Öà“p:®y D5 Øœiõ­ˆw~.•?¹°n \‰ÇéoïUH,M’âÆ|*ŸjÚm£Êë2×ùjŸ8‘;kæÛÿ$RTî™ÏŸNµ/ÆÁÆ¢ÇúlÐ@Ø«RDxB©A4š$ÓÀsª¢gIŒ XÄ=Q\Ú}]ÏzŽù@mZ‡« @Õ€¸N†lëgAˆèœÍ¶Gzs½9ƒ ¢€øÞøåû2Þ¶‰Éß^¬k#ó ãȧ]ËS@a »é*æì`Nä›FκNFD<Š}SŠ: &zÄ—Dš„§ã†Âº«´ÛÑ—?ßb¼jqš¸{°‘´ºü©—Á|-:*[ ‚ºÛas¾X¤01g°PÍí _·‹ ÷ßj`=g¨±,>©.µ‡y—”¾Ð®A…È %±]d¤"nÑÌ~~°Ïê„ð± ½GAîÝ:ÑKàEÔÉf´‚Ïç r¯X~Vù95ˆÜE +=£ñDÁŠ ç"=Üe?üI…;êü‚iÄ3…mëJð·Z§Ø ¬_»›0o·òŽå«ÿ‡¬—Ÿ¼‚]“5–­h­Þ‡úý¨yûŒnü"—…]-ÖDl}ƒÙQ»/†AÁ@¤ZÈ7Ñ®±ò8…$]âð" áL'E¤Ébu¦Õ*½f™–šø[%©0ʃ|ŒïƒpB2yò!P<4Úî ‹âŠ8a@Üã™úËaçmgg"k¸úày¹X7÷îóÜ_,X/מý Æ÷òs|.±•oêØW«¾zdÕ*ÍÖŽ÷Øœ¹„½RZàav‚«©sIâÚij¤$Ljµqo;Yëå-ç Ö5¨¾‹ÁÙ-Ü*üøÀ:ßlŽô°fv¬Í ž¢ÍoÒ©•x^×,$ô-f8ŽóÅ×ø@Í÷»nýϺkêߺ‹x¶ž=ÇðHÇGÙۤ­4öíàxÐ@äq$NäB™;0 u·¨¥ßZU´e/_šÃË*j­¤&’œwO(oÍ€‰¨—d9V°ïnß%<Ê6 * %@Qo3¶)A ;¥p€sO_y}\áÐÈ‚ÇöW·Ë¤¾[´Ó³®,ü$ÒŽ!OD›ç ¿£ÒPÚzÒ?M§içEOò³Ýµ¾Y¾Ò+Rf:tõò*(pq)ì×FhõäÏ¢Ø-îUʲžÌwzóSX¼LüþDX9£Ö °QËÂ…ƒ|}W`\0FÜœª4Tõ¦¿QH¹6áOص8¾qÒQå7™Òx›#w}MRMȳ%€¤E¤€Û®çè>kP Vá7‚p4'®çÉÇŒïcx“GÃHpLªdHÔâ(-/'îºYR³H¸974¢à¸‘ŒÑþ\®ƒN]%ƒŒ¡ôcoßKDÔôÉKvøªEþ(éÒáaêo¿"Y¶ê•$€æèñs†a‚8øÅåwÔ4:ÞÛ'}Jñ¬±2c:øšýÊÞwr‹eë'/¦Q0 Éä†9~=´eîNŸ%LÒ ?„lÝ6òv¦r2ÐûîvÊ~Ug­ gŽ\.§Ò[4îïï† ƬVÏ!/jŠ  ¶ªÉä­Ñ„¬ ’ªAlF¿åÙð±Ã¹¢JŽƒhu/_I׺çÚ!;ÉÃÝ*êcUnSFøå€„sÒyÜïÅUëã|p¡î[ße{ç80±ºi’¬zŒwMpÜé«”²#a'äYëâ|D@ÛCBå  &xûxð]9áöDÎØ²¢F~fð"’Y8ªM"ÁHþä"ÒˆÃ+I¬¤M#"È¢wº§\›½ “ÿgpXË›á ²X¡¤ÚÑÿ÷¬V%{»(MÇo%ÕžÓ!/½ κ]|Þ› Œ98W"åñ$‹b5!hƒg°‡Ë“9r!‘@0Ù2£\ͳ¬™ZþîI‹xùá»æÑÓÉß•Ž" ($n{6ÇŽÐ4}fRü´%–>êÏÝ#U¬c"™>[-¡/X¦äch0þuOÑ»w ÂÍOIj#:à+Òïý“»nóË·?×\3#ÆéuH˜\õ‰ŸQÝÚqmœfç y‹dTƒ°‘ëi€âC—$ez›·<&jJw×ï;÷ûx–êsµ"î³Q2X–ÅiVd1— X¹}&ÊaQ3ÿeP/-Ý¡¯uŒ~|¤I5tâKfù‚3 “p¶áÀS$ŽÿÝ(­ 3EFiœÒ® ·EÉ•h—=”™VGÍ‹x@Gô¼k/¿ü[¸\Ž(ë×ßüÏ ‡C…óeÈt§mˆ£ÿî<;¬Ùæ±èYÊþw Fé!·Ù©£0c]‡õ¨ KÀèiÆ„ŒHeÒGw³j6¡ ,éñuõ1hsÀqŽ‚?Y {ž–³”Ú]Wì¸ñÅ*”Ü€²3†Eîg ?ü)Ã#ê54$çûxûQ†â±˜ˆ•u‘ö$“Ьºlef²Ì4yu@’ÿ‡±0–yÊòŽr/®qê‡í¬s.Ô’ùhµ…'RÓ`îúÒñÙ&Æšygýõúnþ>²ûèßa¦%‘¨bÌ2¤Uˆ$`À¯Ú7Àb³‚풊ˈq%óG ™#Õ³’ó„È2—Wa#˜{¦Iõ†—׺ì1æçEÐytsÀXk¢¯ƒãÓ(ÓÉÝ1âpvú8Q.}”‘Rãuƒ,VO—ίAN‰;³½6ßàq(ô R л˜[‚¦ÐÆ–ŠX›nšâ¼a„E€=ô³ƒí˜Ü“B’ÊÜñ`B¤;+.O$š“qÔgùý»h=Kå/0jvH\q`*rá¬äf8JÞ¢ÄÔDãÝŸ»AE9-öEªäc•òaÅÿŠPHB¡ …(¡P…B P(B¡ …(¡P…B P(B¡ …(¡P…B P(B¡ …(¡P…B P(B¡ …(¡P…B P(B¡ …(¡P…B P(B¡ …/ø{7FC28940-9D31-11D0—!LZXCD(—!X“€´$ðELcÀ}˜Ÿðº¼×þô˜p5®X{v˜B·”Ñ$îÌp. $@ puttycfg.ico: puttycfg-16.png puttycfg-32.png puttycfg-48.png \ puttycfg-16-mono.png puttycfg-32-mono.png puttycfg-48-mono.png ./icon.pl -4 $(filter-out %-mono.png, $^) -1 $(filter %-mono.png, $^) > $@ puttygen.ico: puttygen-16.png puttygen-32.png puttygen-48.png \ puttygen-16-mono.png puttygen-32-mono.png puttygen-48-mono.png ./icon.pl -4 $(filter-out %-mono.png, $^) -1 $(filter %-mono.png, $^) > $@ pageant.ico: pageant-16.png pageant-32.png pageant-48.png \ pageant-16-mono.png pageant-32-mono.png pageant-48-mono.png ./icon.pl -4 $(filter-out %-mono.png, $^) -1 $(filter %-mono.png, $^) > $@ pageants.ico: pageant-16.png pageant-16-mono.png ./icon.pl -4 $(filter-out %-mono.png, $^) -1 $(filter %-mono.png, $^) > $@ pscp.ico: pscp-16.png pscp-32.png pscp-48.png \ pscp-16-mono.png pscp-32-mono.png pscp-48-mono.png ./icon.pl -4 $(filter-out %-mono.png, $^) -1 $(filter %-mono.png, $^) > $@ # Because the installer icon makes heavy use of brown when drawing # the cardboard box, it's worth having 8-bit versions of it in # addition to the 4- and 1-bit ones. puttyins.ico: puttyins-16.png puttyins-32.png puttyins-48.png \ puttyins-16-mono.png puttyins-32-mono.png \ puttyins-48-mono.png \ puttyins-16-true.png puttyins-32-true.png \ puttyins-48-true.png ./icon.pl -8 $(filter %-true.png, $^) \ -4 $(filter-out %-true.png, $(filter-out %-mono.png, $^)) \ -1 $(filter %-mono.png, $^) > $@ # Icon for the website. (This isn't linked into "make all".) website.ico: putty-16.png ./icon.pl -4 $^ >$@ xpmputty.c: putty-16.png putty-32.png putty-48.png ./cicon.pl main_icon $^ > $@ xpmpucfg.c: puttycfg-16.png puttycfg-32.png puttycfg-48.png ./cicon.pl cfg_icon $^ > $@ xpmpterm.c: pterm-16.png pterm-32.png pterm-48.png ./cicon.pl main_icon $^ > $@ xpmptcfg.c: ptermcfg-16.png ptermcfg-32.png ptermcfg-48.png ./cicon.pl cfg_icon $^ > $@ PuTTY.icns: putty-16-mono.pam putty-16.pam \ putty-32-mono.pam putty-32.pam \ putty-48-mono.pam putty-48.pam \ putty-128.pam ./macicon.py mono:putty-16-mono.pam colour:putty-16.pam \ mono:putty-32-mono.pam colour:putty-32.pam \ mono:putty-48-mono.pam colour:putty-48.pam \ colour:putty-128.pam \ output:$@ Pterm.icns: pterm-16-mono.pam pterm-16.pam \ pterm-32-mono.pam pterm-32.pam \ pterm-48-mono.pam pterm-48.pam \ pterm-128.pam ./macicon.py mono:pterm-16-mono.pam colour:pterm-16.pam \ mono:pterm-32-mono.pam colour:pterm-32.pam \ mono:pterm-48-mono.pam colour:pterm-48.pam \ colour:pterm-128.pam \ output:$@ clean: rm -f *.pam *.png *.ico *.icns *.c putty-0.76/icons/cicon.pl0000755000175000017500000000154014072266310012323 00000000000000#!/usr/bin/perl # Given a list of input PNGs, create a C source file containing a # const array of XPMs, named by a given C identifier. $id = shift @ARGV; $k = 0; @xpms = (); foreach $f (@ARGV) { # XPM format is generated directly by ImageMagick, so that's easy # enough. We just have to adjust the declaration line so that it # has the right name, linkage and storage class. @lines = (); open XPM, "convert $f xpm:- |"; push @lines, $_ while ; close XPM; die "XPM from $f in unexpected format\n" unless $lines[1] =~ /^static.*\{$/; $lines[1] = "static const char *const ${id}_$k"."[] = {\n"; $k++; push @xpms, @lines, "\n"; } # Now output. foreach $line (@xpms) { print $line; } print "const char *const *const ${id}[] = {\n"; for ($i = 0; $i < $k; $i++) { print " ${id}_$i,\n"; } print "};\n"; print "const int n_${id} = $k;\n"; putty-0.76/icons/icon.pl0000755000175000017500000002224314072266310012163 00000000000000#!/usr/bin/perl # Take a collection of input image files and convert them into a # multi-resolution Windows .ICO icon file. # # The input images can be treated as having four different colour # depths: # # - 24-bit true colour # - 8-bit with custom palette # - 4-bit using the Windows 16-colour palette (see comment below # for details) # - 1-bit using black and white only. # # The images can be supplied in any input format acceptable to # ImageMagick, but their actual colour usage must already be # appropriate for the specified mode; this script will not do any # substantive conversion. So if an image intended to be used in 4- # or 1-bit mode contains any colour not in the appropriate fixed # palette, that's a fatal error; if an image to be used in 8-bit # mode contains more than 256 distinct colours, that's also a fatal # error. # # Command-line syntax is: # # icon.pl -depth imagefile [imagefile...] [-depth imagefile [imagefile...]] # # where `-depth' is one of `-24', `-8', `-4' or `-1', and tells the # script how to treat all the image files given after that option # until the next depth option. For example, you might execute # # icon.pl -24 48x48x24.png 32x32x24.png -8 32x32x8.png -1 monochrome.png # # to build an icon file containing two differently sized 24-bit # images, one 8-bit image and one black and white image. # # Windows .ICO files support a 1-bit alpha channel on all these # image types. That is, any pixel can be either opaque or fully # transparent, but not partially transparent. The alpha channel is # separate from the main image data, meaning that `transparent' is # not required to take up a palette entry. (So an 8-bit image can # have 256 distinct _opaque_ colours, plus transparent pixels as # well.) If the input images have alpha channels, they will be used # to determine which pixels of the icon are transparent, by simple # quantisation half way up (e.g. in a PNG image with an 8-bit alpha # channel, alpha values of 00-7F will be mapped to transparent # pixels, and 80-FF will become opaque). # The Windows 16-colour palette consists of: # - the eight corners of the colour cube (000000, 0000FF, 00FF00, # 00FFFF, FF0000, FF00FF, FFFF00, FFFFFF) # - dim versions of the seven non-black corners, at 128/255 of the # brightness (000080, 008000, 008080, 800000, 800080, 808000, # 808080) # - light grey at 192/255 of full brightness (C0C0C0). %win16pal = ( "\x00\x00\x00\x00" => 0, "\x00\x00\x80\x00" => 1, "\x00\x80\x00\x00" => 2, "\x00\x80\x80\x00" => 3, "\x80\x00\x00\x00" => 4, "\x80\x00\x80\x00" => 5, "\x80\x80\x00\x00" => 6, "\xC0\xC0\xC0\x00" => 7, "\x80\x80\x80\x00" => 8, "\x00\x00\xFF\x00" => 9, "\x00\xFF\x00\x00" => 10, "\x00\xFF\xFF\x00" => 11, "\xFF\x00\x00\x00" => 12, "\xFF\x00\xFF\x00" => 13, "\xFF\xFF\x00\x00" => 14, "\xFF\xFF\xFF\x00" => 15, ); @win16pal = sort { $win16pal{$a} <=> $win16pal{$b} } keys %win16pal; # The black and white palette consists of black (000000) and white # (FFFFFF), obviously. %win2pal = ( "\x00\x00\x00\x00" => 0, "\xFF\xFF\xFF\x00" => 1, ); @win2pal = sort { $win16pal{$a} <=> $win2pal{$b} } keys %win2pal; @hdr = (); @dat = (); $depth = undef; foreach $_ (@ARGV) { if (/^-(24|8|4|1)$/) { $depth = $1; } elsif (defined $depth) { &readicon($_, $depth); } else { $usage = 1; } } if ($usage || length @hdr == 0) { print "usage: icon.pl ( -24 | -8 | -4 | -1 ) image [image...]\n"; print " [ ( -24 | -8 | -4 | -1 ) image [image...] ...]\n"; exit 0; } # Now write out the output icon file. print pack "vvv", 0, 1, scalar @hdr; # file-level header $filepos = 6 + 16 * scalar @hdr; for ($i = 0; $i < scalar @hdr; $i++) { print $hdr[$i]; print pack "V", $filepos; $filepos += length($dat[$i]); } for ($i = 0; $i < scalar @hdr; $i++) { print $dat[$i]; } sub readicon { my $filename = shift @_; my $depth = shift @_; my $pix; my $i; my %pal; # Determine the icon's width and height. my $w = `identify -format %w $filename`; my $h = `identify -format %h $filename`; # Read the file in as RGBA data. We flip vertically at this # point, to avoid having to do it ourselves (.BMP and hence # .ICO are bottom-up). my $data = []; open IDATA, "convert -flip -depth 8 $filename rgba:- |"; push @$data, $rgb while (read IDATA,$rgb,4,0) == 4; close IDATA; # Check we have the right amount of data. $xl = $w * $h; $al = scalar @$data; die "wrong amount of image data ($al, expected $xl) from $filename\n" unless $al == $xl; # Build the alpha channel now, so we can exclude transparent # pixels from the palette analysis. We replace transparent # pixels with undef in the data array. # # We quantise the alpha channel half way up, so that alpha of # 0x80 or more is taken to be fully opaque and 0x7F or less is # fully transparent. Nasty, but the best we can do without # dithering (and don't even suggest we do that!). my $x; my $y; my $alpha = ""; for ($y = 0; $y < $h; $y++) { my $currbyte = 0, $currbits = 0; for ($x = 0; $x < (($w+31)|31)-31; $x++) { $pix = ($x < $w ? $data->[$y*$w+$x] : "\x00\x00\x00\xFF"); my @rgba = unpack "CCCC", $pix; $currbyte <<= 1; $currbits++; if ($rgba[3] < 0x80) { if ($x < $w) { $data->[$y*$w+$x] = undef; } $currbyte |= 1; # MS has the alpha channel inverted :-) } else { # Might as well flip RGBA into BGR0 while we're here. if ($x < $w) { $data->[$y*$w+$x] = pack "CCCC", $rgba[2], $rgba[1], $rgba[0], 0; } } if ($currbits >= 8) { $alpha .= pack "C", $currbyte; $currbits -= 8; } } } # For an 8-bit image, check we have at most 256 distinct # colours, and build the palette. %pal = (); if ($depth == 8) { my $palindex = 0; foreach $pix (@$data) { next unless defined $pix; $pal{$pix} = $palindex++ unless defined $pal{$pix}; } die "too many colours in 8-bit image $filename\n" unless $palindex <= 256; } elsif ($depth == 4) { %pal = %win16pal; } elsif ($depth == 1) { %pal = %win2pal; } my $raster = ""; if ($depth < 24) { # For a non-24-bit image, flatten the image into one palette # index per pixel. $pad = 32 / $depth; # number of pixels to pad scanline to 4-byte align $pmask = $pad-1; for ($y = 0; $y < $h; $y++) { my $currbyte = 0, $currbits = 0; for ($x = 0; $x < (($w+$pmask)|$pmask)-$pmask; $x++) { $currbyte <<= $depth; $currbits += $depth; if ($x < $w && defined ($pix = $data->[$y*$w+$x])) { if (!defined $pal{$pix}) { $pixhex = sprintf "%02x%02x%02x", unpack "CCC", $pix; die "illegal colour value $pixhex at pixel ($x,$y) in $filename\n"; } $currbyte |= $pal{$pix}; } if ($currbits >= 8) { $raster .= pack "C", $currbyte; $currbits -= 8; } } } } else { # For a 24-bit image, reverse the order of the R,G,B values # and stick a padding zero on the end. # # (In this loop we don't need to bother padding the # scanline out to a multiple of four bytes, because every # pixel takes four whole bytes anyway.) for ($i = 0; $i < scalar @$data; $i++) { if (defined $data->[$i]) { $raster .= $data->[$i]; } else { $raster .= "\x00\x00\x00\x00"; } } $depth = 32; # and adjust this } # Prepare the icon data. First the header... my $data = pack "VVVvvVVVVVV", 40, # size of bitmap info header $w, # icon width $h*2, # icon height (x2 to indicate the subsequent alpha channel) 1, # 1 plane (common to all MS image formats) $depth, # bits per pixel 0, # no compression length $raster, # image size 0, 0, 0, 0; # resolution, colours used, colours important (ignored) # ... then the palette ... if ($depth <= 8) { my $ncols = (1 << $depth); my $palette = "\x00\x00\x00\x00" x $ncols; foreach $i (keys %pal) { substr($palette, $pal{$i}*4, 4) = $i; } $data .= $palette; } # ... the raster data we already had ready ... $data .= $raster; # ... and the alpha channel we already had as well. $data .= $alpha; # Prepare the header which will represent this image in the # icon file. my $header = pack "CCCCvvV", $w, $h, # width and height (this time the real height) 1 << $depth, # number of colours, if less than 256 0, # reserved 1, # planes $depth, # bits per pixel length $data; # size of real icon data push @hdr, $header; push @dat, $data; } putty-0.76/icons/macicon.py0000755000175000017500000001377014072266310012666 00000000000000#!/usr/bin/env python3 # Generate Mac OS X .icns files, or at least the simple subformats # that don't involve JPEG encoding and the like. # # Sources: https://en.wikipedia.org/wiki/Apple_Icon_Image_format and # some details implicitly documented by the source code of 'libicns'. import sys import struct import subprocess assert sys.version_info[:2] >= (3,0), "This is Python 3 code" # The file format has a typical IFF-style (type, length, data) chunk # structure, with one outer chunk containing subchunks for various # different icon sizes and formats. def make_chunk(chunkid, data): assert len(chunkid) == 4 return chunkid + struct.pack(">I", len(data) + 8) + data # Monochrome icons: a single chunk containing a 1 bpp image followed # by a 1 bpp transparency mask. Both uncompressed, unless you count # packing the bits into bytes. def make_mono_icon(size, rgba): assert len(rgba) == size * size # We assume our input image was monochrome, so that the R,G,B # channels are all the same; we want the image and then the mask, # so we take the R channel followed by the alpha channel. However, # we have to flip the former, because in the output format the # image has 0=white and 1=black, while the mask has 0=transparent # and 1=opaque. pixels = [rgba[index][chan] ^ flip for (chan, flip) in [(0,0xFF),(3,0)] for index in range(len(rgba))] # Encode in 1-bit big-endian format. data = b'' for i in range(0, len(pixels), 8): byte = 0 for j in range(8): if pixels[i+j] >= 0x80: byte |= 0x80 >> j data += bytes(byte) # This size-32 chunk id is an anomaly in what would otherwise be a # consistent system of using {s,l,h,t} for {16,32,48,128}-pixel # icon sizes. chunkid = { 16: b"ics#", 32: b"ICN#", 48: b"ich#" }[size] return make_chunk(chunkid, data) # Mask for full-colour icons: a chunk containing an 8 bpp alpha # bitmap, uncompressed. The RGB data appears in a separate chunk. def make_colour_mask(size, rgba): assert len(rgba) == size * size data = bytes(map(lambda pix: pix[3], rgba)) chunkid = { 16: b"s8mk", 32: b"l8mk", 48: b"h8mk", 128: b"t8mk" }[size] return make_chunk(chunkid, data) # Helper routine for deciding when to start and stop run-length # encoding. def runof3(string, position): return (position < len(string) and string[position:position+3] == string[position] * 3) # RGB data for full-colour icons: a chunk containing 8 bpp red, green # and blue images, each run-length encoded (see comment inside the # function), and then concatenated. def make_colour_icon(size, rgba): assert len(rgba) == size * size data = b"" # Mysterious extra zero header word appearing only in the size-128 # icon chunk. libicns doesn't know what it's for, and neither do # I. if size == 128: data += b"\0\0\0\0" # Handle R,G,B channels in sequence. (Ignore the alpha channel; it # goes into the separate mask chunk constructed above.) for chan in range(3): pixels = bytes([rgba[index][chan] for index in range(len(rgba))]) # Run-length encode each channel using the following format: # * byte 0x80-0xFF followed by one literal byte means repeat # that byte 3-130 times # * byte 0x00-0x7F followed by n+1 literal bytes means emit # those bytes once each. pos = 0 while pos < len(pixels): start = pos if runof3(pixels, start): pos += 3 pixval = pixels[start] while (pos - start < 130 and pos < len(pixels) and pixels[pos] == pixval): pos += 1 data += bytes(0x80 + pos-start - 3) + pixval else: while (pos - start < 128 and pos < len(pixels) and not runof3(pixels, pos)): pos += 1 data += bytes(0x00 + pos-start - 1) + pixels[start:pos] chunkid = { 16: b"is32", 32: b"il32", 48: b"ih32", 128: b"it32" }[size] return make_chunk(chunkid, data) # Load an image file from disk and turn it into a simple list of # 4-tuples giving 8-bit R,G,B,A values for each pixel. # # To avoid adding any build dependency on ImageMagick or Python # imaging libraries, none of which comes as standard on OS X, I insist # here that the file is in RGBA .pam format (as mkicon.py will have # generated it). def load_rgba(filename): with open(filename, "rb") as f: assert f.readline() == b"P7\n" for line in iter(f.readline, ''): words = line.decode("ASCII").rstrip("\n").split() if words[0] == "WIDTH": width = int(words[1]) elif words[0] == "HEIGHT": height = int(words[1]) elif words[0] == "DEPTH": assert int(words[1]) == 4 elif words[0] == "TUPLTYPE": assert words[1] == "RGB_ALPHA" elif words[0] == "ENDHDR": break assert width == height data = f.read() assert len(data) == width*height*4 rgba = [list(data[i:i+4]) for i in range(0, len(data), 4)] return width, rgba data = b"" # Trivial argument format: each argument is a filename prefixed with # "mono:", "colour:" or "output:". The first two indicate image files # to use as part of the icon, and the last gives the output file name. # Icon subformat chunks are written out in the order of the arguments. for arg in sys.argv[1:]: kind, filename = arg.split(":", 2) if kind == "output": outfile = filename else: size, rgba = load_rgba(filename) if kind == "mono": data += make_mono_icon(size, rgba) elif kind == "colour": data += make_colour_icon(size, rgba) + make_colour_mask(size, rgba) else: assert False, "bad argument '%s'" % arg data = make_chunk(b"icns", data) with open(outfile, "wb") as f: f.write(data) putty-0.76/icons/mkicon.py0000755000175000017500000011125114072266310012526 00000000000000#!/usr/bin/env python3 from __future__ import division import sys import decimal import math assert sys.version_info[:2] >= (3,0), "This is Python 3 code" # Python code which draws the PuTTY icon components at a range of # sizes. # TODO # ---- # # - use of alpha blending # + try for variable-transparency borders # # - can we integrate the Mac icons into all this? Do we want to? # Python 3 prefers round-to-even. Emulate Python 2's behaviour instead. def round(number): return float( decimal.Decimal(number).to_integral(rounding=decimal.ROUND_HALF_UP)) def pixel(x, y, colour, canvas): canvas[(int(x),int(y))] = colour def overlay(src, x, y, dst): x = int(x) y = int(y) for (sx, sy), colour in src.items(): dst[sx+x, sy+y] = blend(colour, dst.get((sx+x, sy+y), cT)) def finalise(canvas): for k in canvas.keys(): canvas[k] = finalisepix(canvas[k]) def bbox(canvas): minx, miny, maxx, maxy = None, None, None, None for (x, y) in canvas.keys(): if minx == None: minx, miny, maxx, maxy = x, y, x+1, y+1 else: minx = min(minx, x) miny = min(miny, y) maxx = max(maxx, x+1) maxy = max(maxy, y+1) return (minx, miny, maxx, maxy) def topy(canvas): miny = {} for (x, y) in canvas.keys(): miny[x] = min(miny.get(x, y), y) return miny def render(canvas, minx, miny, maxx, maxy): w = maxx - minx h = maxy - miny ret = [] for y in range(h): ret.append([outpix(cT)] * w) for (x, y), colour in canvas.items(): if x >= minx and x < maxx and y >= miny and y < maxy: ret[y-miny][x-minx] = outpix(colour) return ret # Code to actually draw pieces of icon. These don't generally worry # about positioning within a canvas; they just draw at a standard # location, return some useful coordinates, and leave composition # to other pieces of code. sqrthash = {} def memoisedsqrt(x): if x not in sqrthash: sqrthash[x] = math.sqrt(x) return sqrthash[x] BR, TR, BL, TL = list(range(4)) # enumeration of quadrants for border() def border(canvas, thickness, squarecorners, out={}): # I haven't yet worked out exactly how to do borders in a # properly alpha-blended fashion. # # When you have two shades of dark available (half-dark H and # full-dark F), the right sequence of circular border sections # around a pixel x starts off with these two layouts: # # H F # HxH FxF # H F # # Where it goes after that I'm not entirely sure, but I'm # absolutely sure those are the right places to start. However, # every automated algorithm I've tried has always started off # with the two layouts # # H HHH # HxH HxH # H HHH # # which looks much worse. This is true whether you do # pixel-centre sampling (define an inner circle and an outer # circle with radii differing by 1, set any pixel whose centre # is inside the inner circle to F, any pixel whose centre is # outside the outer one to nothing, interpolate between the two # and round sensibly), _or_ whether you plot a notional circle # of a given radius and measure the actual _proportion_ of each # pixel square taken up by it. # # It's not clear what I should be doing to prevent this. One # option is to attempt error-diffusion: Ian Jackson proved on # paper that if you round each pixel's ideal value to the # nearest of the available output values, then measure the # error at each pixel, propagate that error outwards into the # original values of the surrounding pixels, and re-round # everything, you do get the correct second stage. However, I # haven't tried it at a proper range of radii. # # Another option is that the automated mechanisms described # above would be entirely adequate if it weren't for the fact # that the human visual centres are adapted to detect # horizontal and vertical lines in particular, so the only # place you have to behave a bit differently is at the ends of # the top and bottom row of pixels in the circle, and the top # and bottom of the extreme columns. # # For the moment, what I have below is a very simple mechanism # which always uses only one alpha level for any given border # thickness, and which seems to work well enough for Windows # 16-colour icons. Everything else will have to wait. thickness = memoisedsqrt(thickness) if thickness < 0.9: darkness = 0.5 else: darkness = 1 if thickness < 1: thickness = 1 thickness = round(thickness - 0.5) + 0.3 out["borderthickness"] = thickness dmax = int(round(thickness)) if dmax < thickness: dmax = dmax + 1 cquadrant = [[0] * (dmax+1) for x in range(dmax+1)] squadrant = [[0] * (dmax+1) for x in range(dmax+1)] for x in range(dmax+1): for y in range(dmax+1): if max(x, y) < thickness: squadrant[x][y] = darkness if memoisedsqrt(x*x+y*y) < thickness: cquadrant[x][y] = darkness bvalues = {} for (x, y), colour in canvas.items(): for dx in range(-dmax, dmax+1): for dy in range(-dmax, dmax+1): quadrant = 2 * (dx < 0) + (dy < 0) if (x, y, quadrant) in squarecorners: bval = squadrant[abs(dx)][abs(dy)] else: bval = cquadrant[abs(dx)][abs(dy)] if bvalues.get((x+dx,y+dy),0) < bval: bvalues[(x+dx,y+dy)] = bval for (x, y), value in bvalues.items(): if (x,y) not in canvas: canvas[(x,y)] = dark(value) def sysbox(size, out={}): canvas = {} # The system box of the computer. height = int(round(3.6*size)) width = int(round(16.51*size)) depth = int(round(2*size)) highlight = int(round(1*size)) bothighlight = int(round(1*size)) out["sysboxheight"] = height floppystart = int(round(19*size)) # measured in half-pixels floppyend = int(round(29*size)) # measured in half-pixels floppybottom = height - bothighlight floppyrheight = 0.7 * size floppyheight = int(round(floppyrheight)) if floppyheight < 1: floppyheight = 1 floppytop = floppybottom - floppyheight # The front panel is rectangular. for x in range(width): for y in range(height): grey = 3 if x < highlight or y < highlight: grey = grey + 1 if x >= width-highlight or y >= height-bothighlight: grey = grey - 1 if y < highlight and x >= width-highlight: v = (highlight-1-y) - (x-(width-highlight)) if v < 0: grey = grey - 1 elif v > 0: grey = grey + 1 if y >= floppytop and y < floppybottom and \ 2*x+2 > floppystart and 2*x < floppyend: if 2*x >= floppystart and 2*x+2 <= floppyend and \ floppyrheight >= 0.7: grey = 0 else: grey = 2 pixel(x, y, greypix(grey/4.0), canvas) # The side panel is a parallelogram. for x in range(depth): for y in range(height): pixel(x+width, y-(x+1), greypix(0.5), canvas) # The top panel is another parallelogram. for x in range(width-1): for y in range(depth): grey = 3 if x >= width-1 - highlight: grey = grey + 1 pixel(x+(y+1), -(y+1), greypix(grey/4.0), canvas) # And draw a border. border(canvas, size, [], out) return canvas def monitor(size): canvas = {} # The computer's monitor. height = int(round(9.55*size)) width = int(round(11.49*size)) surround = int(round(1*size)) botsurround = int(round(2*size)) sheight = height - surround - botsurround swidth = width - 2*surround depth = int(round(2*size)) highlight = int(round(math.sqrt(size))) shadow = int(round(0.55*size)) # The front panel is rectangular. for x in range(width): for y in range(height): if x >= surround and y >= surround and \ x < surround+swidth and y < surround+sheight: # Screen. sx = (float(x-surround) - swidth//3) / swidth sy = (float(y-surround) - sheight//3) / sheight shighlight = 1.0 - (sx*sx+sy*sy)*0.27 pix = bluepix(shighlight) if x < surround+shadow or y < surround+shadow: pix = blend(cD, pix) # sharp-edged shadow on top and left else: # Complicated double bevel on the screen surround. # First, the outer bevel. We compute the distance # from this pixel to each edge of the front # rectangle. list = [ (x, +1), (y, +1), (width-1-x, -1), (height-1-y, -1) ] # Now sort the list to find the distance to the # _nearest_ edge, or the two joint nearest. list.sort() # If there's one nearest edge, that determines our # bevel colour. If there are two joint nearest, our # bevel colour is their shared one if they agree, # and neutral otherwise. outerbevel = 0 if list[0][0] < list[1][0] or list[0][1] == list[1][1]: if list[0][0] < highlight: outerbevel = list[0][1] # Now, the inner bevel. We compute the distance # from this pixel to each edge of the screen # itself. list = [ (surround-1-x, -1), (surround-1-y, -1), (x-(surround+swidth), +1), (y-(surround+sheight), +1) ] # Now we sort to find the _maximum_ distance, which # conveniently ignores any less than zero. list.sort() # And now the strategy is pretty much the same as # above, only we're working from the opposite end # of the list. innerbevel = 0 if list[-1][0] > list[-2][0] or list[-1][1] == list[-2][1]: if list[-1][0] >= 0 and list[-1][0] < highlight: innerbevel = list[-1][1] # Now we know the adjustment we want to make to the # pixel's overall grey shade due to the outer # bevel, and due to the inner one. We break a tie # in favour of a light outer bevel, but otherwise # add. grey = 3 if outerbevel > 0 or outerbevel == innerbevel: innerbevel = 0 grey = grey + outerbevel + innerbevel pix = greypix(grey / 4.0) pixel(x, y, pix, canvas) # The side panel is a parallelogram. for x in range(depth): for y in range(height): pixel(x+width, y-x, greypix(0.5), canvas) # The top panel is another parallelogram. for x in range(width): for y in range(depth-1): pixel(x+(y+1), -(y+1), greypix(0.75), canvas) # And draw a border. border(canvas, size, [(0,int(height-1),BL)]) return canvas def computer(size): # Monitor plus sysbox. out = {} m = monitor(size) s = sysbox(size, out) x = int(round((2+size/(size+1))*size)) y = int(out["sysboxheight"] + out["borderthickness"]) mb = bbox(m) sb = bbox(s) xoff = sb[0] - mb[0] + x yoff = sb[3] - mb[3] - y overlay(m, xoff, yoff, s) return s def lightning(size): canvas = {} # The lightning bolt motif. # We always want this to be an even number of pixels in height, # and an odd number in width. width = round(7*size) * 2 - 1 height = round(8*size) * 2 # The outer edge of each side of the bolt goes to this point. outery = round(8.4*size) outerx = round(11*size) # And the inner edge goes to this point. innery = height - 1 - outery innerx = round(7*size) for y in range(int(height)): list = [] if y <= outery: list.append(width-1-int(outerx * float(y) / outery + 0.3)) if y <= innery: list.append(width-1-int(innerx * float(y) / innery + 0.3)) y0 = height-1-y if y0 <= outery: list.append(int(outerx * float(y0) / outery + 0.3)) if y0 <= innery: list.append(int(innerx * float(y0) / innery + 0.3)) list.sort() for x in range(int(list[0]), int(list[-1]+1)): pixel(x, y, cY, canvas) # And draw a border. border(canvas, size, [(int(width-1),0,TR), (0,int(height-1),BL)]) return canvas def document(size): canvas = {} # The document used in the PSCP/PSFTP icon. width = round(13*size) height = round(16*size) lineht = round(1*size) if lineht < 1: lineht = 1 linespc = round(0.7*size) if linespc < 1: linespc = 1 nlines = int((height-linespc)/(lineht+linespc)) height = nlines*(lineht+linespc)+linespc # round this so it fits better # Start by drawing a big white rectangle. for y in range(int(height)): for x in range(int(width)): pixel(x, y, cW, canvas) # Now draw lines of text. for line in range(nlines): # Decide where this line of text begins. if line == 0: start = round(4*size) elif line < 5*nlines//7: start = round((line - (nlines//7)) * size) else: start = round(1*size) if start < round(1*size): start = round(1*size) # Decide where it ends. endpoints = [10, 8, 11, 6, 5, 7, 5] ey = line * 6.0 / (nlines-1) eyf = math.floor(ey) eyc = math.ceil(ey) exf = endpoints[int(eyf)] exc = endpoints[int(eyc)] if eyf == eyc: end = exf else: end = exf * (eyc-ey) + exc * (ey-eyf) end = round(end * size) liney = height - (lineht+linespc) * (line+1) for x in range(int(start), int(end)): for y in range(int(lineht)): pixel(x, y+liney, cK, canvas) # And draw a border. border(canvas, size, \ [(0,0,TL),(int(width-1),0,TR),(0,int(height-1),BL), \ (int(width-1),int(height-1),BR)]) return canvas def hat(size): canvas = {} # The secret-agent hat in the Pageant icon. topa = [6]*9+[5,3,1,0,0,1,2,2,1,1,1,9,9,10,10,11,11,12,12] topa = [round(x*size) for x in topa] botl = round(topa[0]+2.4*math.sqrt(size)) botr = round(topa[-1]+2.4*math.sqrt(size)) width = round(len(topa)*size) # Line equations for the top and bottom of the hat brim, in the # form y=mx+c. c, of course, needs scaling by size, but m is # independent of size. brimm = 1.0 / 3.75 brimtopc = round(4*size/3) brimbotc = round(10*size/3) for x in range(int(width)): xs = float(x) * (len(topa)-1) / (width-1) xf = math.floor(xs) xc = math.ceil(xs) topf = topa[int(xf)] topc = topa[int(xc)] if xf == xc: top = topf else: top = topf * (xc-xs) + topc * (xs-xf) top = math.floor(top) bot = round(botl + (botr-botl) * x/(width-1)) for y in range(int(top), int(bot)): pixel(x, y, cK, canvas) # Now draw the brim. for x in range(int(width)): brimtop = brimtopc + brimm * x brimbot = brimbotc + brimm * x for y in range(int(math.floor(brimtop)), int(math.ceil(brimbot))): tophere = max(min(brimtop - y, 1), 0) bothere = max(min(brimbot - y, 1), 0) grey = bothere - tophere # Only draw brim pixels over pixels which are (a) part # of the main hat, and (b) not right on its edge. if (x,y) in canvas and \ (x,y-1) in canvas and \ (x,y+1) in canvas and \ (x-1,y) in canvas and \ (x+1,y) in canvas: pixel(x, y, greypix(grey), canvas) return canvas def key(size): canvas = {} # The key in the PuTTYgen icon. keyheadw = round(9.5*size) keyheadh = round(12*size) keyholed = round(4*size) keyholeoff = round(2*size) # Ensure keyheadh and keyshafth have the same parity. keyshafth = round((2*size - (int(keyheadh)&1)) / 2) * 2 + (int(keyheadh)&1) keyshaftw = round(18.5*size) keyhead = [round(x*size) for x in [12,11,8,10,9,8,11,12]] squarepix = [] # Ellipse for the key head, minus an off-centre circular hole. for y in range(int(keyheadh)): dy = (y-(keyheadh-1)/2.0) / (keyheadh/2.0) dyh = (y-(keyheadh-1)/2.0) / (keyholed/2.0) for x in range(int(keyheadw)): dx = (x-(keyheadw-1)/2.0) / (keyheadw/2.0) dxh = (x-(keyheadw-1)/2.0-keyholeoff) / (keyholed/2.0) if dy*dy+dx*dx <= 1 and dyh*dyh+dxh*dxh > 1: pixel(x + keyshaftw, y, cy, canvas) # Rectangle for the key shaft, extended at the bottom for the # key head detail. for x in range(int(keyshaftw)): top = round((keyheadh - keyshafth) / 2) bot = round((keyheadh + keyshafth) / 2) xs = float(x) * (len(keyhead)-1) / round((len(keyhead)-1)*size) xf = math.floor(xs) xc = math.ceil(xs) in_head = 0 if xc < len(keyhead): in_head = 1 yf = keyhead[int(xf)] yc = keyhead[int(xc)] if xf == xc: bot = yf else: bot = yf * (xc-xs) + yc * (xs-xf) for y in range(int(top),int(bot)): pixel(x, y, cy, canvas) if in_head: last = (x, y) if x == 0: squarepix.append((x, int(top), TL)) if x == 0: squarepix.append(last + (BL,)) if last != None and not in_head: squarepix.append(last + (BR,)) last = None # And draw a border. border(canvas, size, squarepix) return canvas def linedist(x1,y1, x2,y2, x,y): # Compute the distance from the point x,y to the line segment # joining x1,y1 to x2,y2. Returns the distance vector, measured # with x,y at the origin. vectors = [] # Special case: if x1,y1 and x2,y2 are the same point, we # don't attempt to extrapolate it into a line at all. if x1 != x2 or y1 != y2: # First, find the nearest point to x,y on the infinite # projection of the line segment. So we construct a vector # n perpendicular to that segment... nx = y2-y1 ny = x1-x2 # ... compute the dot product of (x1,y1)-(x,y) with that # vector... nd = (x1-x)*nx + (y1-y)*ny # ... multiply by the vector we first thought of... ndx = nd * nx ndy = nd * ny # ... and divide twice by the length of n. ndx = ndx / (nx*nx+ny*ny) ndy = ndy / (nx*nx+ny*ny) # That gives us a displacement vector from x,y to the # nearest point. See if it's within the range of the line # segment. cx = x + ndx cy = y + ndy if cx >= min(x1,x2) and cx <= max(x1,x2) and \ cy >= min(y1,y2) and cy <= max(y1,y2): vectors.append((ndx,ndy)) # Now we have up to three candidate result vectors: (ndx,ndy) # as computed just above, and the two vectors to the ends of # the line segment, (x1-x,y1-y) and (x2-x,y2-y). Pick the # shortest. vectors = vectors + [(x1-x,y1-y), (x2-x,y2-y)] bestlen, best = None, None for v in vectors: vlen = v[0]*v[0]+v[1]*v[1] if bestlen == None or bestlen > vlen: bestlen = vlen best = v return best def spanner(size): canvas = {} # The spanner in the config box icon. headcentre = 0.5 + round(4*size) headradius = headcentre + 0.1 headhighlight = round(1.5*size) holecentre = 0.5 + round(3*size) holeradius = round(2*size) holehighlight = round(1.5*size) shaftend = 0.5 + round(25*size) shaftwidth = round(2*size) shafthighlight = round(1.5*size) cmax = shaftend + shaftwidth # Define three line segments, such that the shortest distance # vectors from any point to each of these segments determines # everything we need to know about where it is on the spanner # shape. segments = [ ((0,0), (holecentre, holecentre)), ((headcentre, headcentre), (headcentre, headcentre)), ((headcentre+headradius/math.sqrt(2), headcentre+headradius/math.sqrt(2)), (cmax, cmax)) ] for y in range(int(cmax)): for x in range(int(cmax)): vectors = [linedist(a,b,c,d,x,y) for ((a,b),(c,d)) in segments] dists = [memoisedsqrt(vx*vx+vy*vy) for (vx,vy) in vectors] # If the distance to the hole line is less than # holeradius, we're not part of the spanner. if dists[0] < holeradius: continue # If the distance to the head `line' is less than # headradius, we are part of the spanner; likewise if # the distance to the shaft line is less than # shaftwidth _and_ the resulting shaft point isn't # beyond the shaft end. if dists[1] > headradius and \ (dists[2] > shaftwidth or x+vectors[2][0] >= shaftend): continue # We're part of the spanner. Now compute the highlight # on this pixel. We do this by computing a `slope # vector', which points from this pixel in the # direction of its nearest edge. We store an array of # slope vectors, in polar coordinates. angles = [math.atan2(vy,vx) for (vx,vy) in vectors] slopes = [] if dists[0] < holeradius + holehighlight: slopes.append(((dists[0]-holeradius)/holehighlight,angles[0])) if dists[1]/headradius < dists[2]/shaftwidth: if dists[1] > headradius - headhighlight and dists[1] < headradius: slopes.append(((headradius-dists[1])/headhighlight,math.pi+angles[1])) else: if dists[2] > shaftwidth - shafthighlight and dists[2] < shaftwidth: slopes.append(((shaftwidth-dists[2])/shafthighlight,math.pi+angles[2])) # Now we find the smallest distance in that array, if # any, and that gives us a notional position on a # sphere which we can use to compute the final # highlight level. bestdist = None bestangle = 0 for dist, angle in slopes: if bestdist == None or bestdist > dist: bestdist = dist bestangle = angle if bestdist == None: bestdist = 1.0 sx = (1.0-bestdist) * math.cos(bestangle) sy = (1.0-bestdist) * math.sin(bestangle) sz = math.sqrt(1.0 - sx*sx - sy*sy) shade = sx-sy+sz / math.sqrt(3) # can range from -1 to +1 shade = 1.0 - (1-shade)/3 pixel(x, y, yellowpix(shade), canvas) # And draw a border. border(canvas, size, []) return canvas def box(size, back): canvas = {} # The back side of the cardboard box in the installer icon. boxwidth = round(15 * size) boxheight = round(12 * size) boxdepth = round(4 * size) boxfrontflapheight = round(5 * size) boxrightflapheight = round(3 * size) # Three shades of basically acceptable brown, all achieved by # halftoning between two of the Windows-16 colours. I'm quite # pleased that was feasible at all! dark = halftone(cr, cK) med = halftone(cr, cy) light = halftone(cr, cY) # We define our halftoning parity in such a way that the black # pixels along the RHS of the visible part of the box back # match up with the one-pixel black outline around the # right-hand side of the box. In other words, we want the pixel # at (-1, boxwidth-1) to be black, and hence the one at (0, # boxwidth) too. parityadjust = int(boxwidth) % 2 # The entire back of the box. if back: for x in range(int(boxwidth + boxdepth)): ytop = max(-x-1, -boxdepth-1) ybot = min(boxheight, boxheight+boxwidth-1-x) for y in range(int(ytop), int(ybot)): pixel(x, y, dark[(x+y+parityadjust) % 2], canvas) # Even when drawing the back of the box, we still draw the # whole shape, because that means we get the right overall size # (the flaps make the box front larger than the box back) and # it'll all be overwritten anyway. # The front face of the box. for x in range(int(boxwidth)): for y in range(int(boxheight)): pixel(x, y, med[(x+y+parityadjust) % 2], canvas) # The right face of the box. for x in range(int(boxwidth), int(boxwidth+boxdepth)): ybot = boxheight + boxwidth-x ytop = ybot - boxheight for y in range(int(ytop), int(ybot)): pixel(x, y, dark[(x+y+parityadjust) % 2], canvas) # The front flap of the box. for y in range(int(boxfrontflapheight)): xadj = int(round(-0.5*y)) for x in range(int(xadj), int(xadj+boxwidth)): pixel(x, y, light[(x+y+parityadjust) % 2], canvas) # The right flap of the box. for x in range(int(boxwidth), int(boxwidth + boxdepth + boxrightflapheight + 1)): ytop = max(boxwidth - 1 - x, x - boxwidth - 2*boxdepth - 1) ybot = min(x - boxwidth - 1, boxwidth + 2*boxrightflapheight - 1 - x) for y in range(int(ytop), int(ybot+1)): pixel(x, y, med[(x+y+parityadjust) % 2], canvas) # And draw a border. border(canvas, size, [(0, int(boxheight)-1, BL)]) return canvas def boxback(size): return box(size, 1) def boxfront(size): return box(size, 0) # Functions to draw entire icons by composing the above components. def xybolt(c1, c2, size, boltoffx=0, boltoffy=0, aux={}): # Two unspecified objects and a lightning bolt. canvas = {} w = h = round(32 * size) bolt = lightning(size) # Position c2 against the top right of the icon. bb = bbox(c2) assert bb[2]-bb[0] <= w and bb[3]-bb[1] <= h overlay(c2, w-bb[2], 0-bb[1], canvas) aux["c2pos"] = (w-bb[2], 0-bb[1]) # Position c1 against the bottom left of the icon. bb = bbox(c1) assert bb[2]-bb[0] <= w and bb[3]-bb[1] <= h overlay(c1, 0-bb[0], h-bb[3], canvas) aux["c1pos"] = (0-bb[0], h-bb[3]) # Place the lightning bolt artistically off-centre. (The # rationale for this positioning is that it's centred on the # midpoint between the centres of the two monitors in the PuTTY # icon proper, but it's not really feasible to _base_ the # calculation here on that.) bb = bbox(bolt) assert bb[2]-bb[0] <= w and bb[3]-bb[1] <= h overlay(bolt, (w-bb[0]-bb[2])/2 + round(boltoffx*size), \ (h-bb[1]-bb[3])/2 + round((boltoffy-2)*size), canvas) return canvas def putty_icon(size): return xybolt(computer(size), computer(size), size) def puttycfg_icon(size): w = h = round(32 * size) s = spanner(size) canvas = putty_icon(size) # Centre the spanner. bb = bbox(s) overlay(s, (w-bb[0]-bb[2])/2, (h-bb[1]-bb[3])/2, canvas) return canvas def puttygen_icon(size): return xybolt(computer(size), key(size), size, boltoffx=2) def pscp_icon(size): return xybolt(document(size), computer(size), size) def puttyins_icon(size): aret = {} # The box back goes behind the lightning bolt. canvas = xybolt(boxback(size), computer(size), size, boltoffx=-2, boltoffy=+1, aux=aret) # But the box front goes over the top, so that the lightning # bolt appears to come _out_ of the box. Here it's useful to # know the exact coordinates where xybolt placed the box back, # so we can overlay the box front exactly on top of it. c1x, c1y = aret["c1pos"] overlay(boxfront(size), c1x, c1y, canvas) return canvas def pterm_icon(size): # Just a really big computer. canvas = {} w = h = round(32 * size) c = computer(size * 1.4) # Centre c in the return canvas. bb = bbox(c) assert bb[2]-bb[0] <= w and bb[3]-bb[1] <= h overlay(c, (w-bb[0]-bb[2])/2, (h-bb[1]-bb[3])/2, canvas) return canvas def ptermcfg_icon(size): w = h = round(32 * size) s = spanner(size) canvas = pterm_icon(size) # Centre the spanner. bb = bbox(s) overlay(s, (w-bb[0]-bb[2])/2, (h-bb[1]-bb[3])/2, canvas) return canvas def pageant_icon(size): # A biggish computer, in a hat. canvas = {} w = h = round(32 * size) c = computer(size * 1.2) ht = hat(size) cbb = bbox(c) hbb = bbox(ht) # Determine the relative y-coordinates of the computer and hat. # We just centre the one on the other. xrel = (cbb[0]+cbb[2]-hbb[0]-hbb[2])//2 # Determine the relative y-coordinates of the computer and hat. # We do this by sitting the hat as low down on the computer as # possible without any computer showing over the top. To do # this we first have to find the minimum x coordinate at each # y-coordinate of both components. cty = topy(c) hty = topy(ht) yrelmin = None for cx in cty.keys(): hx = cx - xrel assert hx in hty yrel = cty[cx] - hty[hx] if yrelmin == None: yrelmin = yrel else: yrelmin = min(yrelmin, yrel) # Overlay the hat on the computer. overlay(ht, xrel, yrelmin, c) # And centre the result in the main icon canvas. bb = bbox(c) assert bb[2]-bb[0] <= w and bb[3]-bb[1] <= h overlay(c, (w-bb[0]-bb[2])/2, (h-bb[1]-bb[3])/2, canvas) return canvas # Test and output functions. import os import sys def testrun(func, fname): canvases = [] for size in [0.5, 0.6, 1.0, 1.2, 1.5, 4.0]: canvases.append(func(size)) wid = 0 ht = 0 for canvas in canvases: minx, miny, maxx, maxy = bbox(canvas) wid = max(wid, maxx-minx+4) ht = ht + maxy-miny+4 block = [] for canvas in canvases: minx, miny, maxx, maxy = bbox(canvas) block.extend(render(canvas, minx-2, miny-2, minx-2+wid, maxy+2)) with open(fname, "wb") as f: f.write((("P7\nWIDTH %d\nHEIGHT %d\nDEPTH 3\nMAXVAL 255\n" + "TUPLTYPE RGB\nENDHDR\n") % (wid, ht)).encode('ASCII')) assert len(block) == ht for line in block: assert len(line) == wid for r, g, b, a in line: # Composite on to orange. r = int(round((r * a + 255 * (255-a)) / 255.0)) g = int(round((g * a + 128 * (255-a)) / 255.0)) b = int(round((b * a + 0 * (255-a)) / 255.0)) f.write(bytes(bytearray([r, g, b]))) def drawicon(func, width, fname, orangebackground = 0): canvas = func(width / 32.0) finalise(canvas) minx, miny, maxx, maxy = bbox(canvas) assert minx >= 0 and miny >= 0 and maxx <= width and maxy <= width block = render(canvas, 0, 0, width, width) with open(fname, "wb") as f: f.write((("P7\nWIDTH %d\nHEIGHT %d\nDEPTH 4\nMAXVAL 255\n" + "TUPLTYPE RGB_ALPHA\nENDHDR\n") % (width, width)).encode('ASCII')) assert len(block) == width for line in block: assert len(line) == width for r, g, b, a in line: if orangebackground: # Composite on to orange. r = int(round((r * a + 255 * (255-a)) / 255.0)) g = int(round((g * a + 128 * (255-a)) / 255.0)) b = int(round((b * a + 0 * (255-a)) / 255.0)) a = 255 f.write(bytes(bytearray([r, g, b, a]))) args = sys.argv[1:] orangebackground = test = 0 colours = 1 # 0=mono, 1=16col, 2=truecol doingargs = 1 realargs = [] for arg in args: if doingargs and arg[0] == "-": if arg == "-t": test = 1 elif arg == "-it": orangebackground = 1 elif arg == "-2": colours = 0 elif arg == "-T": colours = 2 elif arg == "--": doingargs = 0 else: sys.stderr.write("unrecognised option '%s'\n" % arg) sys.exit(1) else: realargs.append(arg) if colours == 0: # Monochrome. cK=cr=cg=cb=cm=cc=cP=cw=cR=cG=cB=cM=cC=cD = 0 cY=cy=cW = 1 cT = -1 def greypix(value): return [cK,cW][int(round(value))] def yellowpix(value): return [cK,cW][int(round(value))] def bluepix(value): return cK def dark(value): return [cT,cK][int(round(value))] def blend(col1, col2): if col1 == cT: return col2 else: return col1 pixvals = [ (0x00, 0x00, 0x00, 0xFF), # cK (0xFF, 0xFF, 0xFF, 0xFF), # cW (0x00, 0x00, 0x00, 0x00), # cT ] def outpix(colour): return pixvals[colour] def finalisepix(colour): return colour def halftone(col1, col2): return (col1, col2) elif colours == 1: # Windows 16-colour palette. cK,cr,cg,cy,cb,cm,cc,cP,cw,cR,cG,cY,cB,cM,cC,cW = list(range(16)) cT = -1 cD = -2 # special translucent half-darkening value used internally def greypix(value): return [cK,cw,cw,cP,cW][int(round(4*value))] def yellowpix(value): return [cK,cy,cY][int(round(2*value))] def bluepix(value): return [cK,cb,cB][int(round(2*value))] def dark(value): return [cT,cD,cK][int(round(2*value))] def blend(col1, col2): if col1 == cT: return col2 elif col1 == cD: return [cK,cK,cK,cK,cK,cK,cK,cw,cK,cr,cg,cy,cb,cm,cc,cw,cD,cD][col2] else: return col1 pixvals = [ (0x00, 0x00, 0x00, 0xFF), # cK (0x80, 0x00, 0x00, 0xFF), # cr (0x00, 0x80, 0x00, 0xFF), # cg (0x80, 0x80, 0x00, 0xFF), # cy (0x00, 0x00, 0x80, 0xFF), # cb (0x80, 0x00, 0x80, 0xFF), # cm (0x00, 0x80, 0x80, 0xFF), # cc (0xC0, 0xC0, 0xC0, 0xFF), # cP (0x80, 0x80, 0x80, 0xFF), # cw (0xFF, 0x00, 0x00, 0xFF), # cR (0x00, 0xFF, 0x00, 0xFF), # cG (0xFF, 0xFF, 0x00, 0xFF), # cY (0x00, 0x00, 0xFF, 0xFF), # cB (0xFF, 0x00, 0xFF, 0xFF), # cM (0x00, 0xFF, 0xFF, 0xFF), # cC (0xFF, 0xFF, 0xFF, 0xFF), # cW (0x00, 0x00, 0x00, 0x80), # cD (0x00, 0x00, 0x00, 0x00), # cT ] def outpix(colour): return pixvals[colour] def finalisepix(colour): # cD is used internally, but can't be output. Convert to cK. if colour == cD: return cK return colour def halftone(col1, col2): return (col1, col2) else: # True colour. cK = (0x00, 0x00, 0x00, 0xFF) cr = (0x80, 0x00, 0x00, 0xFF) cg = (0x00, 0x80, 0x00, 0xFF) cy = (0x80, 0x80, 0x00, 0xFF) cb = (0x00, 0x00, 0x80, 0xFF) cm = (0x80, 0x00, 0x80, 0xFF) cc = (0x00, 0x80, 0x80, 0xFF) cP = (0xC0, 0xC0, 0xC0, 0xFF) cw = (0x80, 0x80, 0x80, 0xFF) cR = (0xFF, 0x00, 0x00, 0xFF) cG = (0x00, 0xFF, 0x00, 0xFF) cY = (0xFF, 0xFF, 0x00, 0xFF) cB = (0x00, 0x00, 0xFF, 0xFF) cM = (0xFF, 0x00, 0xFF, 0xFF) cC = (0x00, 0xFF, 0xFF, 0xFF) cW = (0xFF, 0xFF, 0xFF, 0xFF) cD = (0x00, 0x00, 0x00, 0x80) cT = (0x00, 0x00, 0x00, 0x00) def greypix(value): value = max(min(value, 1), 0) return (int(round(0xFF*value)),) * 3 + (0xFF,) def yellowpix(value): value = max(min(value, 1), 0) return (int(round(0xFF*value)),) * 2 + (0, 0xFF) def bluepix(value): value = max(min(value, 1), 0) return (0, 0, int(round(0xFF*value)), 0xFF) def dark(value): value = max(min(value, 1), 0) return (0, 0, 0, int(round(0xFF*value))) def blend(col1, col2): r1,g1,b1,a1 = col1 r2,g2,b2,a2 = col2 r = int(round((r1*a1 + r2*(0xFF-a1)) / 255.0)) g = int(round((g1*a1 + g2*(0xFF-a1)) / 255.0)) b = int(round((b1*a1 + b2*(0xFF-a1)) / 255.0)) a = int(round((255*a1 + a2*(0xFF-a1)) / 255.0)) return r, g, b, a def outpix(colour): return colour if colours == 2: # True colour with no alpha blending: we still have to # finalise half-dark pixels to black. def finalisepix(colour): if colour[3] > 0: return colour[:3] + (0xFF,) return colour else: def finalisepix(colour): return colour def halftone(col1, col2): r1,g1,b1,a1 = col1 r2,g2,b2,a2 = col2 colret = (int(r1+r2)//2, int(g1+g2)//2, int(b1+b2)//2, int(a1+a2)//2) return (colret, colret) if test: testrun(eval(realargs[0]), realargs[1]) else: drawicon(eval(realargs[0]), int(realargs[1]), realargs[2], orangebackground) putty-0.76/test/0000755000175000017500000000000014072266316010622 500000000000000putty-0.76/test/agenttest.py0000755000175000017500000001731714072266312013122 00000000000000#!/usr/bin/python3 import sys import os import socket import base64 import itertools import collections from ssh import * import agenttestdata assert sys.version_info[:2] >= (3,0), "This is Python 3 code" test_session_id = b'Test16ByteSessId' assert len(test_session_id) == 16 test_message_to_sign = b'test message to sign' TestSig2 = collections.namedtuple("TestSig2", "flags sig") class Key2(collections.namedtuple("Key2", "comment public sigs openssh")): def public_only(self): return Key2(self.comment, self.public, None, None) def Add(self): alg = ssh_decode_string(self.public) msg = (ssh_byte(SSH2_AGENTC_ADD_IDENTITY) + ssh_string(alg) + self.openssh + ssh_string(self.comment)) return agent_query(msg) verb = "sign" def Use(self, flags): msg = (ssh_byte(SSH2_AGENTC_SIGN_REQUEST) + ssh_string(self.public) + ssh_string(test_message_to_sign)) if flags is not None: msg += ssh_uint32(flags) rsp = agent_query(msg) t, rsp = ssh_decode_byte(rsp, True) assert t == SSH2_AGENT_SIGN_RESPONSE sig, rsp = ssh_decode_string(rsp, True) assert len(rsp) == 0 return sig def Del(self): msg = (ssh_byte(SSH2_AGENTC_REMOVE_IDENTITY) + ssh_string(self.public)) return agent_query(msg) @staticmethod def DelAll(): msg = (ssh_byte(SSH2_AGENTC_REMOVE_ALL_IDENTITIES)) return agent_query(msg) @staticmethod def List(): msg = (ssh_byte(SSH2_AGENTC_REQUEST_IDENTITIES)) rsp = agent_query(msg) t, rsp = ssh_decode_byte(rsp, True) assert t == SSH2_AGENT_IDENTITIES_ANSWER nk, rsp = ssh_decode_uint32(rsp, True) keylist = [] for _ in range(nk): p, rsp = ssh_decode_string(rsp, True) c, rsp = ssh_decode_string(rsp, True) keylist.append(Key2(c, p, None, None)) assert len(rsp) == 0 return keylist @classmethod def make_examples(cls): cls.examples = agenttestdata.key2examples(cls, TestSig2) def iter_testsigs(self): for testsig in self.sigs: if testsig.flags == 0: yield testsig._replace(flags=None) yield testsig def iter_tests(self): for testsig in self.iter_testsigs(): yield ([testsig.flags], " (flags={})".format(testsig.flags), testsig.sig) class Key1(collections.namedtuple( "Key1", "comment public challenge response private")): def public_only(self): return Key1(self.comment, self.public, None, None, None) def Add(self): msg = (ssh_byte(SSH1_AGENTC_ADD_RSA_IDENTITY) + self.private + ssh_string(self.comment)) return agent_query(msg) verb = "decrypt" def Use(self, challenge): msg = (ssh_byte(SSH1_AGENTC_RSA_CHALLENGE) + self.public + ssh1_mpint(challenge) + test_session_id + ssh_uint32(1)) rsp = agent_query(msg) t, rsp = ssh_decode_byte(rsp, True) assert t == SSH1_AGENT_RSA_RESPONSE assert len(rsp) == 16 return rsp def Del(self): msg = (ssh_byte(SSH1_AGENTC_REMOVE_RSA_IDENTITY) + self.public) return agent_query(msg) @staticmethod def DelAll(): msg = (ssh_byte(SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES)) return agent_query(msg) @staticmethod def List(): msg = (ssh_byte(SSH1_AGENTC_REQUEST_RSA_IDENTITIES)) rsp = agent_query(msg) t, rsp = ssh_decode_byte(rsp, True) assert t == SSH1_AGENT_RSA_IDENTITIES_ANSWER nk, rsp = ssh_decode_uint32(rsp, True) keylist = [] for _ in range(nk): b, rsp = ssh_decode_uint32(rsp, True) e, rsp = ssh1_get_mpint(rsp, True) m, rsp = ssh1_get_mpint(rsp, True) c, rsp = ssh_decode_string(rsp, True) keylist.append(Key1(c, ssh_uint32(b)+e+m, None, None, None)) assert len(rsp) == 0 return keylist @classmethod def make_examples(cls): cls.examples = agenttestdata.key1examples(cls) def iter_tests(self): yield [self.challenge], "", self.response def agent_query(msg): msg = ssh_string(msg) s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) s.connect(os.environ["SSH_AUTH_SOCK"]) s.send(msg) length = ssh_decode_uint32(s.recv(4)) assert length < AGENT_MAX_MSGLEN return s.recv(length) def enumerate_bits(iterable): return ((1<>1) diff = new ^ old assert diff != 0 and (diff & (diff-1)) == 0 yield old, new, diff old = new assert old == 0 class TestRunner: def __init__(self): self.ok = True @staticmethod def fmt_response(response): return "'{}'".format( base64.encodebytes(response).decode("ASCII").replace("\n","")) @staticmethod def fmt_keylist(keys): return "{{{}}}".format( ",".join(key.comment.decode("ASCII") for key in sorted(keys))) def expect_success(self, text, response): if response == ssh_byte(SSH_AGENT_SUCCESS): print(text, "=> success") elif response == ssh_byte(SSH_AGENT_FAILURE): print("FAIL!", text, "=> failure") self.ok = False else: print("FAIL!", text, "=>", self.fmt_response(response)) self.ok = False def check_keylist(self, K, expected_keys): keys = K.List() print("list keys =>", self.fmt_keylist(keys)) if set(keys) != set(expected_keys): print("FAIL! Should have been", self.fmt_keylist(expected_keys)) self.ok = False def gray_code_test(self, K): bks = list(enumerate_bits(K.examples)) self.check_keylist(K, {}) for old, new, diff in gray_code(len(K.examples)): bit, key = next((bit, key) for bit, key in bks if diff & bit) if new & bit: self.expect_success("insert " + key.comment.decode("ASCII"), key.Add()) else: self.expect_success("delete " + key.comment.decode("ASCII"), key.Del()) self.check_keylist(K, [key.public_only() for bit, key in bks if new & bit]) def sign_test(self, K): for key in K.examples: for params, message, expected_answer in key.iter_tests(): key.Add() actual_answer = key.Use(*params) key.Del() record = "{} with {}{}".format( K.verb, key.comment.decode("ASCII"), message) if actual_answer == expected_answer: print(record, "=> success") else: print("FAIL!", record, "=> {} but expected {}".format( self.fmt_response(actual_answer), self.fmt_response(expected_answer))) self.ok = False def run(self): self.expect_success("init: delete all ssh2 keys", Key2.DelAll()) for K in [Key2, Key1]: self.gray_code_test(K) self.sign_test(K) # TODO: negative tests of all kinds. def main(): Key2.make_examples() Key1.make_examples() tr = TestRunner() tr.run() if tr.ok: print("Test run passed") else: sys.exit("Test run failed!") if __name__ == "__main__": main() putty-0.76/test/agenttestdata.py0000644000175000017500000003714514072266312013752 00000000000000# DO NOT EDIT DIRECTLY! Autogenerated by agenttestgen.py # # To regenerate, run # python3 agenttestgen.py > agenttestdata.py # # agenttestgen.py depends on the testcrypt system, so you must also # have built testcrypt in the parent directory, or else set # PUTTY_TESTCRYPT to point at a working implementation of it. def key2examples(Key2, TestSig2): return [Key2(comment=b'RSA-1024', public=b'\x00\x00\x00\x07ssh-rsa\x00\x00\x00\x01%\x00\x00\x00\x81\x00\x87B\x16\x99\x8c96\x92\xe7\x00-\xc5\xf0%\x13:\xe5a?_\x14\xb5\x15{%\xed\xd4\rB\x98\x02~\xb0\xfdWc\xa6\x8fSz\xa7\xfd\x94\xe1\xcegx\xe3\x14\xba\x87A4\xef\xb0\x056\x9c\x80r\x18\xd7Q\xb69\xed\x9a5\xba\x8b\xf8\xee\x84F\xceD\xfa\xccn\xd6\x9ba8\x8f\xb5\x9dz\x0b\xf1\xa3\xe9vH\x1dr\r[x\xbb\xd9\xd6\xf3\xcb~W\x8fYu\xd5|G)\xa9\xa8_\x91A\x1f\xef\x80\x83\xb3jp)\xef\xe8\x05', sigs=[TestSig2(flags=0, sig=b'\x00\x00\x00\x07ssh-rsa\x00\x00\x00\x801\xaa\xb4t\xe10\x83Q\xe4\x18\x84\x1e\xdeN$\xde;\t\xf9\xae"H\r>\xa9\x91B"\xfd\x01\\19\xee*\xb9\xc1\x8a\x1b*:\xc3\t\x91\x85\xae\x7f\xf8\x84\x08\xbd\x89P\xa9\xdb\t\x8fc\x95\xa4\xcb\xda\x19<\x14\xc4\x1a|\n\xef)\xf4\xc8\xfb\xc1\x04"\xf6\x8a9\xac\xec\xa6x>\xd3-\xb1\xf7\x1e\x04\x8a\xd4k\xcb\x12\xf9\xc1\xaa\x1aV\xc3\xb8\xdd\xf8\x01\x10Z\xdd\x8c\xcd\x12w\x83pJOr\xb8\xed\x84\xa5\xf5&\r:\xd7H'), TestSig2(flags=2, sig=b"\x00\x00\x00\x0crsa-sha2-256\x00\x00\x00\x80J)H\xfe\x18t\xc8\xa9$\x07*\xc9\x16\xb3\\\x8cK\x7f\xdd\xd8\xb0g\xed\x10o\xac\xe8\xd3\xefQj\xe2\x9fi\x13\x9b\x93\x07`}\x12\x9f\xc1Y\x19{\xb8\xc0\x8c\xe6\x03\xfd\x8d\xc1\xfat\xf0T\x055\x02r:AOM\x18x\xb6:\xb7g\xe5k\x12$\xabX)\xf8\xe9\x12\xa2\x04\xff\xfa^\xc7 G\x9c7\x92\x03>^\x00,\x1e\x063\x16\x9b\xd4.'\x01\xa4Lv\xd2\xae\xf0\xc0\xed\x8d\xf6'aj\x1aq`\xc3\x85\x08\xc2\x8a"), TestSig2(flags=4, sig=b'\x00\x00\x00\x0crsa-sha2-512\x00\x00\x00\x80;k\xd5\xf4\xe8\xec\xeb\x8b\xe13L}\x96\x918?\xd9\x90\t\x9dN\xec+|<\xc1\xc6\x10\xf6]y\x8c\xf9a\xd5\x07c:\x90\x7fzBQ\xe49\x87s\x1d\x81Kz\xe9\xee\n\xf3|\xee6\x84A\xd0\xec\xc0\xf9h\xc5$\x13\xd8r\xa2j;\xb6$?*\xd59\xcb\xdf\x85\x19U\x1f\x10\xb4\xd8C\xbb"`\x8a)\x0fy`|\xd7\xa3\xec`tw\x8a>(\x0fj\x08\xbc\x92\xd8K\xb0qP\xbe\xd9\xaf\x91\x16\xd7\x9a\x9a\r\x9d\xe5')], openssh=b'\x00\x00\x00\x81\x00\x87B\x16\x99\x8c96\x92\xe7\x00-\xc5\xf0%\x13:\xe5a?_\x14\xb5\x15{%\xed\xd4\rB\x98\x02~\xb0\xfdWc\xa6\x8fSz\xa7\xfd\x94\xe1\xcegx\xe3\x14\xba\x87A4\xef\xb0\x056\x9c\x80r\x18\xd7Q\xb69\xed\x9a5\xba\x8b\xf8\xee\x84F\xceD\xfa\xccn\xd6\x9ba8\x8f\xb5\x9dz\x0b\xf1\xa3\xe9vH\x1dr\r[x\xbb\xd9\xd6\xf3\xcb~W\x8fYu\xd5|G)\xa9\xa8_\x91A\x1f\xef\x80\x83\xb3jp)\xef\xe8\x05\x00\x00\x00\x01%\x00\x00\x00\x80 \xe6\x8f\xe0)\x06\xffo\xd7S\x12\r\x8dp\xcdS\x83\xe78\xed\x9d@\xcd\xdf\xafG\xb0\x1e\xe6\xafZ\x8d\x84\xffZr/n\xf8\xa1Ku\x08\x89\xf3\xef\xa7\xc8\x88\x80f\x16\xc7\xaf\xec\x8b\xa5\x80\x03\x91_\xfd\x06\t_\xc5\xbf\xc1\xcb\xe3\r12k\xaeY\xe4RA\xab\xf1\xba\x8a\x99\xcf\xaf\x06\xf1\x82\xc6\x9d\xd5g[\xb2\xfb\xd8\xbc\x9b]\xb9l\t\xb2\x0b\xc9\x98JK\xfe\x8a\xd6\xfc[\xd19\xe7N|\xa6m\x9ei\xce&\xc6\xfcM\x00\x00\x00A\x00\x95\xc13\x16\xd2O*\xbc8\xc6{D\xb2\xe5\x85\x9ao\xf9\xfc.\xea\xe7\x9d\xca4}\x9c7\xca\xe1\x1e\xdb`o\x88w\x0c\xa3\xba\xde\nF\xb7\xf2x}\xd9\x00\x00\x00A\x00\xa3h\x11\x1a\x99\xc6\xa4\x0eAj\x93\xff\xa0&D(\x05#MoE\xdb\x8d\xf4\x99\xc8\xd2\xffv\xf1\x90C~\xe4\xed\xce\x1f\x85\x9f\x92\xce\xacMR\xd7\x0c\xb9z\x87\xea\xe97/\x97\xbd\x19q:TLB\xb7$\r'), Key2(comment=b'DSA-1024', public=b'\x00\x00\x00\x07ssh-dss\x00\x00\x00\x81\x00\xbc\x0c\xb0L\xfc<\x03TyZQ\xe1\xef\xd4\xd5\xe4\xa2\xb3\xaf\x14t\x0f\\,!E\xdbf\x01\x9e\x95\xddr\xeb\xab\xae;\xc1\xe3\x0c\xe9\xd9\x15\xc2\xa9\xc3g\x04\xa5\xf1\x965\xf1\x81\x9dS\x9c\x83en\x93\x11\xe0p\n)\xdaZ\x17y\xff\xf2\xbf\x9b[;"E1\xf0\xde\xbd\xe1;\x9a6Xnc\x8f\xd3\x1dg\xd1\x80\xa9\x8em\x86t\xc8\xa9!\xcd\xb3\xe4mx\xd5\x93%R\xbb9u\xd2\x99p\xe2\xbe\xf3\xfb%\xebd\xc4\x86\xe3\x00\x00\x00\x15\x00\xec^\x98\x84x\xc1\xa8`\xcfB\xc7\x1e\xf0\x8d\xd3\x89\xa3\xa8\xec\xc7\x00\x00\x00\x80Q\xecf\xfd\xcc\x9a+\xcclxu\x1b\x0b\xd7\xfd\xedDP\xd1\x82~H\x0eqn\x1e\xed\xd8\xad\xe9\xe3\xf8!\x1d\\\xb4\xde\xc1e\xd7\xc0(\x1dpQ\xee\xad\xeez\xe1\xb3\xa4\x12d\x92\x1a\x89\x82u\x99\xa3$\x85\x9c\xb840\xe7\xd6&O\x85~\xd6\xac\x1eq\xa1\x06\xa2\xd1ro\xd0}>\xc0O\xaf\x8a\xf8@B\r7\xf6\x89\xda\xd0\xb9\x0e\xb6\xae\xcdh\x1a\x86%\xdcN\x8cE\xd8\xcd(\x19\xa6y\x9a\xc0\xc2\xd6;\xc3\xc9{\x104\x00\x00\x00\x80`\xc1\xe8\x18\xe4\xd0\x16\xf9[l\xce\\L*\x19\x14"20K\xc6\x18*\xe5\x91\x80\xbf+\xec\xfe\xd3D\xf7\xa6\xf9Y0x#sl\x88BU\x7f\x1c\r\xb3\x08EL\x86\xa2\x8c\x81\x15pD\xac\x9c\xa3\x8e\x02\x89\xb9\xb6\x9f\xaa\xd0\xc8\x89o\x81Qm\x18f0\\\x92\x1f\xbf\xa1\x8d8\x8a\xb1\xec\xb8G\xbd9b\x8d\x7f\x9bk\xb9x\xe5\xde\xce/\'f\xc4\x8bmX\xd9 \xb4\xd5\xe8\xd1\xe1\xc8\xeb\xe7\xbc2LG\x90]\\%\x9f', sigs=[TestSig2(flags=0, sig=b"\x00\x00\x00\x07ssh-dss\x00\x00\x00(\xeb\xf2\xb0 2(\x93a\xfc\x0f\xad\x1al\xd5\xd0n\xd5\x10\x9d\\G\x18]?\xf4h5D\x12WL\xe6#\xa0\x89'\x17\xd3;\xb7")], openssh=b'\x00\x00\x00\x81\x00\xbc\x0c\xb0L\xfc<\x03TyZQ\xe1\xef\xd4\xd5\xe4\xa2\xb3\xaf\x14t\x0f\\,!E\xdbf\x01\x9e\x95\xddr\xeb\xab\xae;\xc1\xe3\x0c\xe9\xd9\x15\xc2\xa9\xc3g\x04\xa5\xf1\x965\xf1\x81\x9dS\x9c\x83en\x93\x11\xe0p\n)\xdaZ\x17y\xff\xf2\xbf\x9b[;"E1\xf0\xde\xbd\xe1;\x9a6Xnc\x8f\xd3\x1dg\xd1\x80\xa9\x8em\x86t\xc8\xa9!\xcd\xb3\xe4mx\xd5\x93%R\xbb9u\xd2\x99p\xe2\xbe\xf3\xfb%\xebd\xc4\x86\xe3\x00\x00\x00\x15\x00\xec^\x98\x84x\xc1\xa8`\xcfB\xc7\x1e\xf0\x8d\xd3\x89\xa3\xa8\xec\xc7\x00\x00\x00\x80Q\xecf\xfd\xcc\x9a+\xcclxu\x1b\x0b\xd7\xfd\xedDP\xd1\x82~H\x0eqn\x1e\xed\xd8\xad\xe9\xe3\xf8!\x1d\\\xb4\xde\xc1e\xd7\xc0(\x1dpQ\xee\xad\xeez\xe1\xb3\xa4\x12d\x92\x1a\x89\x82u\x99\xa3$\x85\x9c\xb840\xe7\xd6&O\x85~\xd6\xac\x1eq\xa1\x06\xa2\xd1ro\xd0}>\xc0O\xaf\x8a\xf8@B\r7\xf6\x89\xda\xd0\xb9\x0e\xb6\xae\xcdh\x1a\x86%\xdcN\x8cE\xd8\xcd(\x19\xa6y\x9a\xc0\xc2\xd6;\xc3\xc9{\x104\x00\x00\x00\x80`\xc1\xe8\x18\xe4\xd0\x16\xf9[l\xce\\L*\x19\x14"20K\xc6\x18*\xe5\x91\x80\xbf+\xec\xfe\xd3D\xf7\xa6\xf9Y0x#sl\x88BU\x7f\x1c\r\xb3\x08EL\x86\xa2\x8c\x81\x15pD\xac\x9c\xa3\x8e\x02\x89\xb9\xb6\x9f\xaa\xd0\xc8\x89o\x81Qm\x18f0\\\x92\x1f\xbf\xa1\x8d8\x8a\xb1\xec\xb8G\xbd9b\x8d\x7f\x9bk\xb9x\xe5\xde\xce/\'f\xc4\x8bmX\xd9 \xb4\xd5\xe8\xd1\xe1\xc8\xeb\xe7\xbc2LG\x90]\\%\x9f\x00\x00\x00\x15\x00\x85mio\xa4\xa2\xcc\xadnC\x94\x84I*;\xf40\xc9\xd7\xa9'), Key2(comment=b'ECDSA-p256', public=b'\x00\x00\x00\x13ecdsa-sha2-nistp256\x00\x00\x00\x08nistp256\x00\x00\x00A\x04D\x01\'\xac\xa9\xeaJ#\x1e\x80\x1e\xd2R"xq\xb2h\x128c\x11\xf5\xe8\x19,;\x1d\xcf\xf4h\x8c\xaeQ\xee\x15\xc2\xdb\xc77\x80\xc4\xc2\x15\x1d0s\xe1\xbfa\xd9}pz\xc6af4d\xbd\xc6\xc6\x1as', sigs=[TestSig2(flags=0, sig=b'\x00\x00\x00\x13ecdsa-sha2-nistp256\x00\x00\x00J\x00\x00\x00!\x00\xe0\x0b\xa1\x01\xc1\xc6A\x0c\x0c\x02\x9f\xc2B\x18G=\xfa+\xde\x8a/)x\xea\xc0R\xc0\x92\xe9\t?\xc7\x00\x00\x00!\x00\xa3\xcd^!\xa5\x0f"\xcd\x9c\x82\xe0\x01\x9c\xf1\xcaa\x83\x83\x848\xe4\xfb\xd9p]\xe1\xcc<\xdb\xde\x99\xe8')], openssh=b'\x00\x00\x00\x08nistp256\x00\x00\x00A\x04D\x01\'\xac\xa9\xeaJ#\x1e\x80\x1e\xd2R"xq\xb2h\x128c\x11\xf5\xe8\x19,;\x1d\xcf\xf4h\x8c\xaeQ\xee\x15\xc2\xdb\xc77\x80\xc4\xc2\x15\x1d0s\xe1\xbfa\xd9}pz\xc6af4d\xbd\xc6\xc6\x1as\x00\x00\x00!\x00\xc3\x8b\xc3A\xde\xfd\xd4\xcb\xf7\x9c\xa0\xc7L\xd1\xb0\xfe\x8e\xf2\xf6o\xe4"\x88K\x15p0\xbc\x0b\x19\xa7\xad'), Key2(comment=b'Ed25519', public=b'\x00\x00\x00\x0bssh-ed25519\x00\x00\x00 \xd6\x9aC\xb8\xa4\x1b\x95\x8a\x97\x9a\x9d\x95\xaa5\xd5\x9b\xc3B\xd2\xd1*\x85\xde.E"\x1c\xe9\xef\xc0\x06\xdb', sigs=[TestSig2(flags=0, sig=b'\x00\x00\x00\x0bssh-ed25519\x00\x00\x00@\xda\xcbw6\xff\xb9\xc1)\x1e\xfa\xef/s!u\xe1\xc0%\xd9-;\x97<\xbc2o\x1a\xef\x17\xd3\x8dvU\xbf\x8d,\xed\xe8S\xe8\xc27\xdb\x1d\xe7\xda\\\xce\xdc\x00\xad\x97BpC\x9a\xec\xd3\r1\x9f\xc0n\x0b')], openssh=b'\x00\x00\x00 \xd6\x9aC\xb8\xa4\x1b\x95\x8a\x97\x9a\x9d\x95\xaa5\xd5\x9b\xc3B\xd2\xd1*\x85\xde.E"\x1c\xe9\xef\xc0\x06\xdb\x00\x00\x00@\rV\xff\xf36D\xe3\xb2\xcc\xda\xa1\x11\x9d\xa2\xa7\xc0\'}C\xcb"\xf0x\rlL\xfc\xcc\x99\x10\x91\xc8\xd6\x9aC\xb8\xa4\x1b\x95\x8a\x97\x9a\x9d\x95\xaa5\xd5\x9b\xc3B\xd2\xd1*\x85\xde.E"\x1c\xe9\xef\xc0\x06\xdb')] def key1examples(Key1): return [Key1(comment=b'RSA-1024a', public=b"\x00\x00\x04\x00\x00\x06%\x04\x00\x98\x8d\xac\xa1\xff\xd1\x05\xd4@\x93\x11\xfc\xd8\xb5\x8c\x18\xa8/\x9ePh\x06y,\xc1\xdd\xc2q\x90\xe0g\xebIgl\x12\xacs.\xc1\xd7\xd0,\x8d\xd4\xa4\xd1\x88F\x1dW\xa6\xb9\x808+0u`B\xa8\xd2z\x0c>}\xaeA\xa7\x945\x91\x0c\xd5@5s\xa8R\xc31\xc5\x8e'\xec6\x00\x98\xdd\x0b\x93\xa8\x8e\xe6\xa9\x19\xa2\xbaf\xd6\xa8@\x1b\x82\xf4\xf5j\xc4\x06\xdd\x08\x7f\xcce\xcdc%\xc4W\xd7k\xd2\xe3\xcf\xa2\xbaI=", challenge=105855771610781217219148893240371739231586890974005582821084910669621353003219053895226458126049169949952763904000891276244889049724351186264170655451406153989624471223276671179692313150356649135789040179224142632652879598695018927369451625737003799565133274183885945180682771324880529165922297762364545903323, response=b'U\xb2\xdf(\xb8\xc7\xf5\r\x16\xa0%O9l\xdb\xf0', private=b'\x00\x00\x04\x00\x04\x00\x98\x8d\xac\xa1\xff\xd1\x05\xd4@\x93\x11\xfc\xd8\xb5\x8c\x18\xa8/\x9ePh\x06y,\xc1\xdd\xc2q\x90\xe0g\xebIgl\x12\xacs.\xc1\xd7\xd0,\x8d\xd4\xa4\xd1\x88F\x1dW\xa6\xb9\x808+0u`B\xa8\xd2z\x0c>}\xaeA\xa7\x945\x91\x0c\xd5@5s\xa8R\xc31\xc5\x8e\'\xec6\x00\x98\xdd\x0b\x93\xa8\x8e\xe6\xa9\x19\xa2\xbaf\xd6\xa8@\x1b\x82\xf4\xf5j\xc4\x06\xdd\x08\x7f\xcce\xcdc%\xc4W\xd7k\xd2\xe3\xcf\xa2\xbaI=\x00\x06%\x04\x00\x94n+m0@\xfe\xc0\xad\x88--];\x04\xd9\xb8e\xae\xca\xc6\x14"\xdfp\x84\xbd09\xef\x19\x00\x9arv\xfdi\x84\xd3\x8c+\xed$nR[,\xbb\xf11N]\x07\x83\xacE\xb2\x9b\xb7\x9a\xcd\xc5\xde\x86\xe7\xf9O\xf9\xa0\xce\xca\x085\xe7\xb7En\x94\x1e;T)SH\xff\x85\xe5\x8d\x1f\xa6,\xc7\xffV\x80\xf2E=\x08\x8e\xe6\x9e\xb2}py\xad\xa55Z\xf4\xed\xc8^+tnIN)vE\xbd\x82\xb665\xdd\x01\xfb\x04d\xa4\xb80\x98l\xd0!l\x99[\x12\x1c\x85\xda\xa5\xd0#\xcc\x7f\x80\xcdE~\x8bF \xf1;\x9d\xfb\xc0\xa3\x93\xa2\xb89^\x91D\xb7\xd3\x18\xbdx\x93U\xc3{\xa4\t,F\xb5\xdd\xadk\xc9@\xd0$\xc6\x03\x02\x00\x9a\xa4\\\xb4\xe5\x05\xe0&\xb0\x06\xc3\x9dQ`\xe4w\xe3-6N "\xa2\x9a%\xf16T\x92%\x16\xf9\xfc\xb5\xc6\xc4\xbb\xa8\xf7?\xa7"\xa1\x9do\x10A3\x14\x12\x18Uj\x19k\x19\x93\x99\xc9\xab\xfa\xa7\x15\xcb\x02\x00\xfc\x8a\xdb\xcc2\x9d[\x1a\xd0\x12\x1c\xad7\xbdk\xaa\xc6Ql\xeb7;\x87f\x8fv\xafM\x8b\xa8\xaa\n40\x90)\xb8t\tBaU\xba<\xcb\xa1\x12\xad\xaad\xb3\x0e\xf4\xfc\x07\x13;\x1c\x17]P[|\x17'), Key1(comment=b'RSA-1024b', public=b'\x00\x00\x04\x00\x00\x06%\x04\x00\x97\xe4sJ\xf8i\x83\x9f\xe8k%\xc6\xb7\xcbm!\xd7\xdd\xd5!N\xad<8\x0e\x1f\xa15yV\xbcr\xec\x8c\xca\x94\xed\x0c\xdbDC\x9e\xe1\xf5\xe4_\xb6>\x19\xe0\xdf\xb1te\xc7n\x86\xf7\xd15\x9e\xfc\x81\x90V\x92\xae\x1cb\xcc\xde\x05k\x8eNIa\x87\x1a\x8aG\xdd\xc9\xc9K\xfe\xb3W\xb0%\xe2\x10bs\x18\xe2\x07I\xf8\x88P\x04i!\xa9\xdd5\x12\x12\xdbp\x06\x03\xbb\x0e(\x82\x0e\xe7\xe7r\x17\xdbN\x91\x141Q', challenge=106369452810277819728395930691403679528939443121481728310811020449278450935158409081846069415863931371431145424909167586350456375465223628112328681772147389155179853401808803792809242283837570604791348990555643874877574733757512944970974196016255159184273573080435875970788050018918135917906633435695051286334, response=b'U\xb2\xdf(\xb8\xc7\xf5\r\x16\xa0%O9l\xdb\xf0', private=b'\x00\x00\x04\x00\x04\x00\x97\xe4sJ\xf8i\x83\x9f\xe8k%\xc6\xb7\xcbm!\xd7\xdd\xd5!N\xad<8\x0e\x1f\xa15yV\xbcr\xec\x8c\xca\x94\xed\x0c\xdbDC\x9e\xe1\xf5\xe4_\xb6>\x19\xe0\xdf\xb1te\xc7n\x86\xf7\xd15\x9e\xfc\x81\x90V\x92\xae\x1cb\xcc\xde\x05k\x8eNIa\x87\x1a\x8aG\xdd\xc9\xc9K\xfe\xb3W\xb0%\xe2\x10bs\x18\xe2\x07I\xf8\x88P\x04i!\xa9\xdd5\x12\x12\xdbp\x06\x03\xbb\x0e(\x82\x0e\xe7\xe7r\x17\xdbN\x91\x141Q\x00\x06%\x03\xfe1C,O\xaa\x83\x15\xee\xac>m\x1d\xda\xbe\x84BS\xd9>4P\xde=\x0bB\xd9\xd3kI\xf2\x9d\xfb\xc2W,\xf2\x07\xb1$\x84\xd7\xa9&\xb0\x9d\x18\x1fn\x16;\x18\x1d\xe0\x8f\xb6M\\4\xb2\x8d\xee_\xbbP\xe7 j!\xc7W\xcb\xc9\x19\nM\x90\xfe4\xe5U\xef[\xdc&A\xde\xd9\x84\x02\xdek\xec\xaf\xb4\xd0I\xaaR\xa6\xc1\x8b\xbc\x13\xf1?,\xc6{\n\x02p\xa7\'\xa1\xb9\xf8\x1f\xeb\x99\xe2\xcf\xc4%"+Mu\x9d\x02\x00\xc4\x89W\x05\x8f\xff\xabX6\x8f\x9fQ\x19\xb8\xc2\xc2Y|\xa90g\xa9\xa7\xa9\x17 \xeb\xbbSMd\xf4YW\xa7\x93\xfcn\xc3AI\x04tK\x1a\xd74`\xec]+\xd8\x91`W@\xc7\xa6G\x82\x99\xac\x8c\x9b\x02\x00\xacs\x1e6\xa5\x10\xb2\xd1\xc9|\x87\x15\xb6\xd9*\x05O\x9e\x95\xec\x1f\xac\xbc/2\xc1\xdb\xa7\x97w@\xfe?d\xb2|\xd4\x96\x02\xc8y\xdf; \x89\x0b'), Key1(comment=b'RSA-768d', public=b'\x00\x00\x03\x00\x00\x06%\x03\x00\x9e4w\xb6C\x1c\xb1\xcdV\x96\t\x14\x04T\xb5\xca\x0ct?a8\xfd-\xb1l\x83/\xc3\x95\x97\x8b \xcdZW\x15\x87G\xa8\x1d\xea(\x1d\x03V\xe8\xe8/M.\xe6\xd6\x8d,\xf3>$"R\xcciYwp*\xc7z\x0c\xc3/k\x87\xc1{4\x1bw\xf1\x00\xda~\x84\x0e\xb0)\'\x84\x9e<\xd1\x19\x18\x81\xcb\xffo', challenge=600986133602165113984107876330614577281000470334319933978738264340033662158902949574073710382789301043986608302703563989903269508211841666685953915670687064469873711668788199922957307325206107166514327102609966835942325119838536897, response=b'U\xb2\xdf(\xb8\xc7\xf5\r\x16\xa0%O9l\xdb\xf0', private=b'\x00\x00\x03\x00\x03\x00\x9e4w\xb6C\x1c\xb1\xcdV\x96\t\x14\x04T\xb5\xca\x0ct?a8\xfd-\xb1l\x83/\xc3\x95\x97\x8b \xcdZW\x15\x87G\xa8\x1d\xea(\x1d\x03V\xe8\xe8/M.\xe6\xd6\x8d,\xf3>$"R\xcciYwp*\xc7z\x0c\xc3/k\x87\xc1{4\x1bw\xf1\x00\xda~\x84\x0e\xb0)\'\x84\x9e<\xd1\x19\x18\x81\xcb\xffo\x00\x06%\x02\xfe"4\xdb\x9d\x07\x97\x80c\xbf\xb1\xbc\xc6\x0e\xc65$\xc4l)a!\x14%\x8e%L\xcc\x0e\x9c\xe2~\xf2U\xea\x04\xfd\xbcb\x856\xe6\x856\xb4\x9d+px\x97\x0c\x17\xde\x93\xd8zOAO\xea\xab\xa2p\x14\x87\xf5\x1c\x00\xb2\xa3A\x08\x14\xe9v\xb8\xd2\xbf\x03C\xf2\xa3\xfeV/BZ\x11\x82#\xff)\xf8\x93\x0f\xf0m\x01\x80\x85\xde\xf4\x1b\xd2Pu\xf3\xd8\xf8Z\xabZ\x92q\xef\xab\x06\x85\xcd\xc3\x85\xd6?j\xd4\xaa\x96l\xeeRZ\xab8\x05\x84xT\xdd\x15j\xea9O_\xf4`R\x01\x80\xc4\xeb\x12\x92\xdc\x05\\b\x8c\xec\x11\x10\xc5\xaa\xdf\x97\x1eD\x92\x06_\xb3\xc28C\xceH\xa7\x8a\xf2F\xe6+\x01\xbf\xad\x81\x99&2\xcc\xff\xa2\xaad\x04\x08\x8d\x01\x80\xcd\xab\xe5\xdeE^a-\t$\xa4a\xd4h8\xe4>\xe1d\xcc0n\xe3\xee\xc5\xe7\xd4\xa59\x8f\x9f\xb2\x1d\n\x00h\x14\xad\xcdq\x89UTPu\x9e>\xeb')] putty-0.76/test/agenttestgen.py0000755000175000017500000000623214072266312013606 00000000000000#!/usr/bin/env python3 import sys assert sys.version_info[:2] >= (3,0), "This is Python 3 code" def generate(): import hashlib print("""\ # DO NOT EDIT DIRECTLY! Autogenerated by agenttestgen.py # # To regenerate, run # python3 agenttestgen.py > agenttestdata.py # # agenttestgen.py depends on the testcrypt system, so you must also # have built testcrypt in the parent directory, or else set # PUTTY_TESTCRYPT to point at a working implementation of it. """) from testcrypt import (rsa_generate, dsa_generate, ecdsa_generate, eddsa_generate, random_clear, random_queue, ssh_key_public_blob, ssh_key_openssh_blob, ssh_key_sign, rsa1_generate, rsa_ssh1_encrypt, rsa_ssh1_public_blob, rsa_ssh1_private_blob_agent, mp_from_bytes_be) from agenttest import (Key2, TestSig2, test_message_to_sign, Key1, test_session_id) import ssh keygen2 = [ ('RSA-1024', lambda: rsa_generate(1024, False), (ssh.SSH_AGENT_RSA_SHA2_256, ssh.SSH_AGENT_RSA_SHA2_512)), ('DSA-1024', lambda: dsa_generate(1024)), ('ECDSA-p256', lambda: ecdsa_generate(256)), ('Ed25519', lambda: eddsa_generate(256)), ] keys2 = [] for record in keygen2: if len(record) == 2: record += ((),) comment, genfn, flaglist = record flaglist = (0,) + flaglist random_clear() random_queue(b''.join(hashlib.sha512('{}{:d}'.format(comment, j) .encode('ASCII')).digest() for j in range(1000))) key = genfn() sigs = [TestSig2(flags, ssh_key_sign(key, test_message_to_sign, flags)) for flags in flaglist] keys2.append(Key2(comment.encode("ASCII"), ssh_key_public_blob(key), sigs, ssh_key_openssh_blob(key))) print("def key2examples(Key2, TestSig2):\n return {!r}".format(keys2)) keygen1 = [ ('RSA-1024a', 1024), ('RSA-1024b', 1024), ('RSA-768c', 768), ('RSA-768d', 768), ] keys1 = [] for comment, bits in keygen1: random_clear() random_queue(b''.join(hashlib.sha512('{}{:d}'.format(comment, j) .encode('ASCII')).digest() for j in range(1000))) key = rsa1_generate(bits) preimage = b'Test128BitRSA1ChallengeCleartext' assert len(preimage) == 32 challenge_bytes = rsa_ssh1_encrypt(preimage, key) assert len(challenge_bytes) > 0 challenge = int(mp_from_bytes_be(challenge_bytes)) response = hashlib.md5(preimage + test_session_id).digest() keys1.append(Key1(comment.encode("ASCII"), rsa_ssh1_public_blob(key, 'exponent_first'), challenge, response, rsa_ssh1_private_blob_agent(key))) print("def key1examples(Key1):\n return {!r}".format(keys1)) if __name__ == "__main__": generate() putty-0.76/test/colours.txt0000644000175000017500000001123714072266312012771 00000000000000Test of most colour rendering. Omits the SCO fg and bg sequences, since they are destructive. Normal text and bold; reverse video and bold ANSI plus bold: 0 bold 1 bold 2 bold 3 bold 4 bold 5 bold 6 bold 7 bold xterm bright: fg0 bg0 fg1 bg1 fg2 bg2 fg3 bg3 fg4 bg4 fg5 bg5 fg6 bg6 fg7 bg7 xterm 256: greys                      reds   greens blues  yellow magent cyans  0001020304050607 08090a0b0c0d0e0f 1011121314151617 18191a1b1c1d1e1f 2021222324252627 28292a2b2c2d2e2f 3031323334353637 38393a3b3c3d3e3f 4041424344454647 48494a4b4c4d4e4f 5051525354555657 58595a5b5c5d5e5f 6061626364656667 68696a6b6c6d6e6f 7071727374757677 78797a7b7c7d7e7f 8081828384858687 88898a8b8c8d8e8f 9091929394959697 98999a9b9c9d9e9f a0a1a2a3a4a5a6a7 a8a9aaabacadaeaf b0b1b2b3b4b5b6b7 b8b9babbbcbdbebf c0c1c2c3c4c5c6c7 c8c9cacbcccdcecf d0d1d2d3d4d5d6d7 d8d9dadbdcdddedf e0e1e2e3e4e5e6e7 e8e9eaebecedeeef f0f1f2f3f4f5f6f7 f8f9fafbfcfdfeff 24-bit colour: SlateGrey OliveDrab goldenrod SaddleBrown DarkViolet (bg) putty-0.76/test/cryptsuite.py0000755000175000017500000050122514072266312013333 00000000000000#!/usr/bin/env python3 import sys import unittest import struct import itertools import functools import contextlib import hashlib import binascii import base64 import json try: from math import gcd except ImportError: from fractions import gcd from eccref import * from testcrypt import * from ssh import * assert sys.version_info[:2] >= (3,0), "This is Python 3 code" try: base64decode = base64.decodebytes except AttributeError: base64decode = base64.decodestring def unhex(s): return binascii.unhexlify(s.replace(" ", "").replace("\n", "")) def rsa_bare(e, n): rsa = rsa_new() get_rsa_ssh1_pub(ssh_uint32(nbits(n)) + ssh1_mpint(e) + ssh1_mpint(n), rsa, 'exponent_first') return rsa def find_non_square_mod(p): # Find a non-square mod p, using the Jacobi symbol # calculation function from eccref.py. return next(z for z in itertools.count(2) if jacobi(z, p) == -1) def fibonacci_scattered(n=10): # Generate a list of Fibonacci numbers with power-of-2 indices # (F_1, F_2, F_4, ...), to be used as test inputs of varying # sizes. Also put F_0 = 0 into the list as a bonus. yield 0 a, b, c = 0, 1, 1 while True: yield b n -= 1 if n <= 0: break a, b, c = (a**2+b**2, b*(a+c), b**2+c**2) def fibonacci(n=10): # Generate the full Fibonacci sequence starting from F_0 = 0. a, b = 0, 1 while True: yield a n -= 1 if n <= 0: break a, b = b, a+b def mp_mask(mp): # Return the value that mp would represent if all its bits # were set. Useful for masking a true mathematical output # value (e.g. from an operation that can over/underflow, like # mp_sub or mp_anything_into) to check it's right within the # ability of that particular mp_int to represent. return ((1 << mp_max_bits(mp))-1) def adjtuples(iterable, n): # Return all the contiguous n-tuples of an iterable, including # overlapping ones. E.g. if called on [0,1,2,3,4] with n=3 it # would return (0,1,2), (1,2,3), (2,3,4) and then stop. it = iter(iterable) toret = [next(it) for _ in range(n-1)] for element in it: toret.append(element) yield tuple(toret) toret[:1] = [] def last(iterable): # Return the last element of an iterable, or None if it is empty. it = iter(iterable) toret = None for toret in it: pass return toret def le_integer(x, nbits): assert nbits % 8 == 0 return bytes([0xFF & (x >> (8*n)) for n in range(nbits//8)]) @contextlib.contextmanager def queued_random_data(nbytes, seed): hashsize = 512 // 8 data = b''.join( hashlib.sha512("preimage:{:d}:{}".format(i, seed).encode('ascii')) .digest() for i in range((nbytes + hashsize - 1) // hashsize)) data = data[:nbytes] random_queue(data) yield None random_clear() @contextlib.contextmanager def queued_specific_random_data(data): random_queue(data) yield None random_clear() @contextlib.contextmanager def random_prng(seed): random_make_prng('sha256', seed) yield None random_clear() def hash_str(alg, message): h = ssh_hash_new(alg) ssh_hash_update(h, message) return ssh_hash_final(h) def hash_str_iter(alg, message_iter): h = ssh_hash_new(alg) for string in message_iter: ssh_hash_update(h, string) return ssh_hash_final(h) def mac_str(alg, key, message, cipher=None): m = ssh2_mac_new(alg, cipher) ssh2_mac_setkey(m, key) ssh2_mac_start(m) ssh2_mac_update(m, "dummy") # Make sure ssh_mac_start erases previous state ssh2_mac_start(m) ssh2_mac_update(m, message) return ssh2_mac_genresult(m) def lcm(a, b): return a * b // gcd(a, b) class MyTestBase(unittest.TestCase): "Intermediate class that adds useful helper methods." def assertEqualBin(self, x, y): # Like assertEqual, but produces more legible error reports # for random-looking binary data. self.assertEqual(binascii.hexlify(x), binascii.hexlify(y)) class mpint(MyTestBase): def testCreation(self): self.assertEqual(int(mp_new(128)), 0) self.assertEqual(int(mp_from_bytes_be(b'ABCDEFGHIJKLMNOP')), 0x4142434445464748494a4b4c4d4e4f50) self.assertEqual(int(mp_from_bytes_le(b'ABCDEFGHIJKLMNOP')), 0x504f4e4d4c4b4a494847464544434241) self.assertEqual(int(mp_from_integer(12345)), 12345) decstr = '91596559417721901505460351493238411077414937428167' self.assertEqual(int(mp_from_decimal_pl(decstr)), int(decstr, 10)) self.assertEqual(int(mp_from_decimal(decstr)), int(decstr, 10)) self.assertEqual(int(mp_from_decimal("")), 0) # For hex, test both upper and lower case digits hexstr = 'ea7cb89f409ae845215822e37D32D0C63EC43E1381C2FF8094' self.assertEqual(int(mp_from_hex_pl(hexstr)), int(hexstr, 16)) self.assertEqual(int(mp_from_hex(hexstr)), int(hexstr, 16)) self.assertEqual(int(mp_from_hex("")), 0) p2 = mp_power_2(123) self.assertEqual(int(p2), 1 << 123) p2c = mp_copy(p2) self.assertEqual(int(p2c), 1 << 123) # Check mp_copy really makes a copy, not an alias (ok, that's # testing the testcrypt system more than it's testing the # underlying C functions) mp_set_bit(p2c, 120, 1) self.assertEqual(int(p2c), (1 << 123) + (1 << 120)) self.assertEqual(int(p2), 1 << 123) def testBytesAndBits(self): x = mp_new(128) self.assertEqual(mp_get_byte(x, 2), 0) mp_set_bit(x, 2*8+3, 1) self.assertEqual(mp_get_byte(x, 2), 1<<3) self.assertEqual(mp_get_bit(x, 2*8+3), 1) mp_set_bit(x, 2*8+3, 0) self.assertEqual(mp_get_byte(x, 2), 0) self.assertEqual(mp_get_bit(x, 2*8+3), 0) # Currently I expect 128 to be a multiple of any # BIGNUM_INT_BITS value we might be running with, so these # should be exact equality self.assertEqual(mp_max_bytes(x), 128/8) self.assertEqual(mp_max_bits(x), 128) nb = lambda hexstr: mp_get_nbits(mp_from_hex(hexstr)) self.assertEqual(nb('00000000000000000000000000000000'), 0) self.assertEqual(nb('00000000000000000000000000000001'), 1) self.assertEqual(nb('00000000000000000000000000000002'), 2) self.assertEqual(nb('00000000000000000000000000000003'), 2) self.assertEqual(nb('00000000000000000000000000000004'), 3) self.assertEqual(nb('000003ffffffffffffffffffffffffff'), 106) self.assertEqual(nb('000003ffffffffff0000000000000000'), 106) self.assertEqual(nb('80000000000000000000000000000000'), 128) self.assertEqual(nb('ffffffffffffffffffffffffffffffff'), 128) def testDecAndHex(self): def checkHex(hexstr): n = mp_from_hex(hexstr) i = int(hexstr, 16) self.assertEqual(mp_get_hex(n), "{:x}".format(i).encode('ascii')) self.assertEqual(mp_get_hex_uppercase(n), "{:X}".format(i).encode('ascii')) checkHex("0") checkHex("f") checkHex("00000000000000000000000000000000000000000000000000") checkHex("d5aa1acd5a9a1f6b126ed416015390b8dc5fceee4c86afc8c2") checkHex("ffffffffffffffffffffffffffffffffffffffffffffffffff") def checkDec(hexstr): n = mp_from_hex(hexstr) i = int(hexstr, 16) self.assertEqual(mp_get_decimal(n), "{:d}".format(i).encode('ascii')) checkDec("0") checkDec("f") checkDec("00000000000000000000000000000000000000000000000000") checkDec("d5aa1acd5a9a1f6b126ed416015390b8dc5fceee4c86afc8c2") checkDec("ffffffffffffffffffffffffffffffffffffffffffffffffff") checkDec("f" * 512) def testComparison(self): inputs = [ "0", "1", "2", "10", "314159265358979", "FFFFFFFFFFFFFFFF", # Test over-long versions of some of the same numbers we # had short forms of above "0000000000000000000000000000000000000000000000000000000000000000" "0000000000000000000000000000000000000000000000000000000000000000", "0000000000000000000000000000000000000000000000000000000000000000" "0000000000000000000000000000000000000000000000000000000000000001", "0000000000000000000000000000000000000000000000000000000000000000" "0000000000000000000000000000000000000000000000000000000000000002", "0000000000000000000000000000000000000000000000000000000000000000" "000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFF", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", ] values = [(mp_from_hex(s), int(s, 16)) for s in inputs] for am, ai in values: for bm, bi in values: self.assertEqual(mp_cmp_eq(am, bm) == 1, ai == bi) self.assertEqual(mp_cmp_hs(am, bm) == 1, ai >= bi) if (bi >> 64) == 0: self.assertEqual(mp_eq_integer(am, bi) == 1, ai == bi) self.assertEqual(mp_hs_integer(am, bi) == 1, ai >= bi) # mp_{min,max}{,_into} is a reasonable thing to test # here as well self.assertEqual(int(mp_min(am, bm)), min(ai, bi)) self.assertEqual(int(mp_max(am, bm)), max(ai, bi)) am_small = mp_copy(am if aibi else bm) mp_max_into(am_big, am, bm) self.assertEqual(int(am_big), max(ai, bi)) # Test mp_{eq,hs}_integer in the case where the integer is as # large as possible and the bignum contains very few words. In # modes where BIGNUM_INT_BITS < 64, this used to go wrong. mp10 = mp_new(4) mp_copy_integer_into(mp10, 10) highbit = 1 << 63 self.assertEqual(mp_hs_integer(mp10, highbit | 9), 0) self.assertEqual(mp_hs_integer(mp10, highbit | 10), 0) self.assertEqual(mp_hs_integer(mp10, highbit | 11), 0) self.assertEqual(mp_eq_integer(mp10, highbit | 9), 0) self.assertEqual(mp_eq_integer(mp10, highbit | 10), 0) self.assertEqual(mp_eq_integer(mp10, highbit | 11), 0) def testConditionals(self): testnumbers = [(mp_copy(n),n) for n in fibonacci_scattered()] for am, ai in testnumbers: for bm, bi in testnumbers: cm = mp_copy(am) mp_select_into(cm, am, bm, 0) self.assertEqual(int(cm), ai & mp_mask(am)) mp_select_into(cm, am, bm, 1) self.assertEqual(int(cm), bi & mp_mask(am)) mp_cond_add_into(cm, am, bm, 0) self.assertEqual(int(cm), ai & mp_mask(am)) mp_cond_add_into(cm, am, bm, 1) self.assertEqual(int(cm), (ai+bi) & mp_mask(am)) mp_cond_sub_into(cm, am, bm, 0) self.assertEqual(int(cm), ai & mp_mask(am)) mp_cond_sub_into(cm, am, bm, 1) self.assertEqual(int(cm), (ai-bi) & mp_mask(am)) maxbits = max(mp_max_bits(am), mp_max_bits(bm)) cm = mp_new(maxbits) dm = mp_new(maxbits) mp_copy_into(cm, am) mp_copy_into(dm, bm) self.assertEqual(int(cm), ai) self.assertEqual(int(dm), bi) mp_cond_swap(cm, dm, 0) self.assertEqual(int(cm), ai) self.assertEqual(int(dm), bi) mp_cond_swap(cm, dm, 1) self.assertEqual(int(cm), bi) self.assertEqual(int(dm), ai) if bi != 0: mp_cond_clear(cm, 0) self.assertEqual(int(cm), bi) mp_cond_clear(cm, 1) self.assertEqual(int(cm), 0) def testBasicArithmetic(self): testnumbers = list(fibonacci_scattered(5)) testnumbers.extend([1 << (1 << i) for i in range(3,10)]) testnumbers.extend([(1 << (1 << i)) - 1 for i in range(3,10)]) testnumbers = [(mp_copy(n),n) for n in testnumbers] for am, ai in testnumbers: for bm, bi in testnumbers: self.assertEqual(int(mp_add(am, bm)), ai + bi) self.assertEqual(int(mp_mul(am, bm)), ai * bi) # Cope with underflow in subtraction diff = mp_sub(am, bm) self.assertEqual(int(diff), (ai - bi) & mp_mask(diff)) for bits in range(64, 512, 64): cm = mp_new(bits) mp_add_into(cm, am, bm) self.assertEqual(int(cm), (ai + bi) & mp_mask(cm)) mp_mul_into(cm, am, bm) self.assertEqual(int(cm), (ai * bi) & mp_mask(cm)) mp_sub_into(cm, am, bm) self.assertEqual(int(cm), (ai - bi) & mp_mask(cm)) # A test cherry-picked from the old bignum test script, # involving two numbers whose product has a single 1 bit miles # in the air and then all 0s until a bunch of cruft at the # bottom, the aim being to test that carry propagation works # all the way up. ai, bi = 0xb4ff6ed2c633847562087ed9354c5c17be212ac83b59c10c316250f50b7889e5b058bf6bfafd12825225ba225ede0cba583ffbd0882de88c9e62677385a6dbdedaf81959a273eb7909ebde21ae5d12e2a584501a6756fe50ccb93b93f0d6ee721b6052a0d88431e62f410d608532868cdf3a6de26886559e94cc2677eea9bd797918b70e2717e95b45918bd1f86530cb9989e68b632c496becff848aa1956cd57ed46676a65ce6dd9783f230c8796909eef5583fcfe4acbf9c8b4ea33a08ec3fd417cf7175f434025d032567a00fc329aee154ca20f799b961fbab8f841cb7351f561a44aea45746ceaf56874dad99b63a7d7af2769d2f185e2d1c656cc6630b5aba98399fa57, 0xb50a77c03ac195225021dc18d930a352f27c0404742f961ca828c972737bad3ada74b1144657ab1d15fe1b8aefde8784ad61783f3c8d4584aa5f22a4eeca619f90563ae351b5da46770df182cf348d8e23b25fda07670c6609118e916a57ce4043608752c91515708327e36f5bb5ebd92cd4cfb39424167a679870202b23593aa524bac541a3ad322c38102a01e9659b06a4335c78d50739a51027954ac2bf03e500f975c2fa4d0ab5dd84cc9334f219d2ae933946583e384ed5dbf6498f214480ca66987b867df0f69d92e4e14071e4b8545212dd5e29ff0248ed751e168d78934da7930bcbe10e9a212128a68de5d749c61f5e424cf8cf6aa329674de0cf49c6f9b4c8b8cc3 am = mp_copy(ai) bm = mp_copy(bi) self.assertEqual(int(mp_mul(am, bm)), ai * bi) # A regression test for a bug that came up during development # of mpint.c, relating to an intermediate value overflowing # its container. ai, bi = (2**8512 * 2 // 3), (2**4224 * 11 // 15) am = mp_copy(ai) bm = mp_copy(bi) self.assertEqual(int(mp_mul(am, bm)), ai * bi) def testAddInteger(self): initial = mp_copy(4444444444444444444444444) x = mp_new(mp_max_bits(initial) + 64) # mp_{add,sub,copy}_integer_into should be able to cope with # any uintmax_t. Test a number that requires more than 32 bits. mp_add_integer_into(x, initial, 123123123123123) self.assertEqual(int(x), 4444444444567567567567567) mp_sub_integer_into(x, initial, 123123123123123) self.assertEqual(int(x), 4444444444321321321321321) mp_copy_integer_into(x, 123123123123123) self.assertEqual(int(x), 123123123123123) # mp_mul_integer_into only takes a uint16_t integer input mp_mul_integer_into(x, initial, 10001) self.assertEqual(int(x), 44448888888888888888888884444) def testDivision(self): divisors = [1, 2, 3, 2**16+1, 2**32-1, 2**32+1, 2**128-159, 141421356237309504880168872420969807856967187537694807] quotients = [0, 1, 2, 2**64-1, 2**64, 2**64+1, 17320508075688772935] for d in divisors: for q in quotients: remainders = {0, 1, d-1, 2*d//3} for r in sorted(remainders): if r >= d: continue # silly cases with tiny divisors n = q*d + r mq = mp_new(max(nbits(q), 1)) mr = mp_new(max(nbits(r), 1)) mp_divmod_into(n, d, mq, mr) self.assertEqual(int(mq), q) self.assertEqual(int(mr), r) self.assertEqual(int(mp_div(n, d)), q) self.assertEqual(int(mp_mod(n, d)), r) # Make sure divmod_into can handle not getting one # of its output pointers (or even both). mp_clear(mq) mp_divmod_into(n, d, mq, None) self.assertEqual(int(mq), q) mp_clear(mr) mp_divmod_into(n, d, None, mr) self.assertEqual(int(mr), r) mp_divmod_into(n, d, None, None) # No tests we can do after that last one - we just # insist that it isn't allowed to have crashed! def testNthRoot(self): roots = [1, 13, 1234567654321, 57721566490153286060651209008240243104215933593992] tests = [] tests.append((0, 2, 0, 0)) tests.append((0, 3, 0, 0)) for r in roots: for n in 2, 3, 5: tests.append((r**n, n, r, 0)) tests.append((r**n+1, n, r, 1)) tests.append((r**n-1, n, r-1, r**n - (r-1)**n - 1)) for x, n, eroot, eremainder in tests: with self.subTest(x=x): mx = mp_copy(x) remainder = mp_copy(mx) root = mp_nthroot(x, n, remainder) self.assertEqual(int(root), eroot) self.assertEqual(int(remainder), eremainder) self.assertEqual(int(mp_nthroot(2*10**100, 2, None)), 141421356237309504880168872420969807856967187537694) self.assertEqual(int(mp_nthroot(3*10**150, 3, None)), 144224957030740838232163831078010958839186925349935) def testBitwise(self): p = 0x3243f6a8885a308d313198a2e03707344a4093822299f31d0082efa98ec4e e = 0x2b7e151628aed2a6abf7158809cf4f3c762e7160f38b4da56a784d9045190 x = mp_new(nbits(p)) mp_and_into(x, p, e) self.assertEqual(int(x), p & e) mp_or_into(x, p, e) self.assertEqual(int(x), p | e) mp_xor_into(x, p, e) self.assertEqual(int(x), p ^ e) mp_bic_into(x, p, e) self.assertEqual(int(x), p & ~e) def testInversion(self): # Test mp_invert_mod_2to. testnumbers = [(mp_copy(n),n) for n in fibonacci_scattered() if n & 1] for power2 in [1, 2, 3, 5, 13, 32, 64, 127, 128, 129]: for am, ai in testnumbers: bm = mp_invert_mod_2to(am, power2) bi = int(bm) self.assertEqual(((ai * bi) & ((1 << power2) - 1)), 1) # mp_reduce_mod_2to is a much simpler function, but # this is as good a place as any to test it. rm = mp_copy(am) mp_reduce_mod_2to(rm, power2) self.assertEqual(int(rm), ai & ((1 << power2) - 1)) # Test mp_invert proper. moduli = [2, 3, 2**16+1, 2**32-1, 2**32+1, 2**128-159, 141421356237309504880168872420969807856967187537694807, 2**128-1] for m in moduli: # Prepare a MontyContext for the monty_invert test below # (unless m is even, in which case we can't) mc = monty_new(m) if m & 1 else None to_invert = {1, 2, 3, 7, 19, m-1, 5*m//17, (m-1)//2, (m+1)//2} for x in sorted(to_invert): if gcd(x, m) != 1: continue # filter out non-invertible cases inv = int(mp_invert(x, m)) assert x * inv % m == 1 # Test monty_invert too, while we're here if mc is not None: self.assertEqual( int(monty_invert(mc, monty_import(mc, x))), int(monty_import(mc, inv))) def testGCD(self): powerpairs = [(0,0), (1,0), (1,1), (2,1), (2,2), (75,3), (17,23)] for a2, b2 in powerpairs: for a3, b3 in powerpairs: for a5, b5 in powerpairs: a = 2**a2 * 3**a3 * 5**a5 * 17 * 19 * 23 b = 2**b2 * 3**b3 * 5**b5 * 65423 d = 2**min(a2, b2) * 3**min(a3, b3) * 5**min(a5, b5) ma = mp_copy(a) mb = mp_copy(b) self.assertEqual(int(mp_gcd(ma, mb)), d) md = mp_new(nbits(d)) mA = mp_new(nbits(b)) mB = mp_new(nbits(a)) mp_gcd_into(ma, mb, md, mA, mB) self.assertEqual(int(md), d) A = int(mA) B = int(mB) self.assertEqual(a*A - b*B, d) self.assertTrue(0 <= A < b//d) self.assertTrue(0 <= B < a//d) self.assertEqual(mp_coprime(ma, mb), 1 if d==1 else 0) # Make sure gcd_into can handle not getting some # of its output pointers. mp_clear(md) mp_gcd_into(ma, mb, md, None, None) self.assertEqual(int(md), d) mp_clear(mA) mp_gcd_into(ma, mb, None, mA, None) self.assertEqual(int(mA), A) mp_clear(mB) mp_gcd_into(ma, mb, None, None, mB) self.assertEqual(int(mB), B) mp_gcd_into(ma, mb, None, None, None) # No tests we can do after that last one - we just # insist that it isn't allowed to have crashed! def testMonty(self): moduli = [5, 19, 2**16+1, 2**31-1, 2**128-159, 2**255-19, 293828847201107461142630006802421204703, 113064788724832491560079164581712332614996441637880086878209969852674997069759] for m in moduli: mc = monty_new(m) # Import some numbers inputs = [(monty_import(mc, n), n) for n in sorted({0, 1, 2, 3, 2*m//3, m-1})] # Check modulus and identity self.assertEqual(int(monty_modulus(mc)), m) self.assertEqual(int(monty_identity(mc)), int(inputs[1][0])) # Check that all those numbers export OK for mn, n in inputs: self.assertEqual(int(monty_export(mc, mn)), n) for ma, a in inputs: for mb, b in inputs: xprod = int(monty_export(mc, monty_mul(mc, ma, mb))) self.assertEqual(xprod, a*b % m) xsum = int(monty_export(mc, monty_add(mc, ma, mb))) self.assertEqual(xsum, (a+b) % m) xdiff = int(monty_export(mc, monty_sub(mc, ma, mb))) self.assertEqual(xdiff, (a-b) % m) # Test the ordinary mp_mod{add,sub,mul} at the # same time, even though those don't do any # montying at all xprod = int(mp_modmul(a, b, m)) self.assertEqual(xprod, a*b % m) xsum = int(mp_modadd(a, b, m)) self.assertEqual(xsum, (a+b) % m) xdiff = int(mp_modsub(a, b, m)) self.assertEqual(xdiff, (a-b) % m) for ma, a in inputs: # Compute a^0, a^1, a^1, a^2, a^3, a^5, ... indices = list(fibonacci()) powers = [int(monty_export(mc, monty_pow(mc, ma, power))) for power in indices] # Check the first two make sense self.assertEqual(powers[0], 1) self.assertEqual(powers[1], a) # Check the others using the Fibonacci identity: # F_n + F_{n+1} = F_{n+2}, so a^{F_n} a^{F_{n+1}} = a^{F_{n+2}} for p0, p1, p2 in adjtuples(powers, 3): self.assertEqual(p2, p0 * p1 % m) # Test the ordinary mp_modpow here as well, while # we've got the machinery available for index, power in zip(indices, powers): self.assertEqual(int(mp_modpow(a, index, m)), power) # A regression test for a bug I encountered during initial # development of mpint.c, in which an incomplete reduction # happened somewhere in an intermediate value. b, e, m = 0x2B5B93812F253FF91F56B3B4DAD01CA2884B6A80719B0DA4E2159A230C6009EDA97C5C8FD4636B324F9594706EE3AD444831571BA5E17B1B2DFA92DEA8B7E, 0x25, 0xC8FCFD0FD7371F4FE8D0150EFC124E220581569587CCD8E50423FA8D41E0B2A0127E100E92501E5EE3228D12EA422A568C17E0AD2E5C5FCC2AE9159D2B7FB8CB assert(int(mp_modpow(b, e, m)) == pow(b, e, m)) # Make sure mp_modpow can handle a base larger than the # modulus, by pre-reducing it assert(int(mp_modpow(1<<877, 907, 999979)) == pow(2, 877*907, 999979)) def testModsqrt(self): moduli = [ 5, 19, 2**16+1, 2**31-1, 2**128-159, 2**255-19, 293828847201107461142630006802421204703, 113064788724832491560079164581712332614996441637880086878209969852674997069759, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6FFFFFFFF00000001] for p in moduli: # Count the factors of 2 in the group. (That is, we want # p-1 to be an odd multiple of 2^{factors_of_2}.) factors_of_2 = nbits((p-1) & (1-p)) - 1 assert (p & ((2 << factors_of_2)-1)) == ((1 << factors_of_2)+1) z = find_non_square_mod(p) sc = modsqrt_new(p, z) def ptest(x): root, success = mp_modsqrt(sc, x) r = int(root) self.assertTrue(success) self.assertEqual((r * r - x) % p, 0) def ntest(x): root, success = mp_modsqrt(sc, x) self.assertFalse(success) # Make up some more or less random values mod p to square v1 = pow(3, nbits(p), p) v2 = pow(5, v1, p) test_roots = [0, 1, 2, 3, 4, 3*p//4, v1, v2, v1+1, 12873*v1, v1*v2] known_squares = {r*r % p for r in test_roots} for s in known_squares: ptest(s) if s != 0: ntest(z*s % p) # Make sure we've tested a value that is in each of the # subgroups of order (p-1)/2^k but not in the next one # (with the exception of k=0, which just means 'have we # tested a non-square?', which we have in the above loop). # # We do this by starting with a known non-square; then # squaring it (factors_of_2) times will return values # nested deeper and deeper in those subgroups. vbase = z for k in range(factors_of_2): # Adjust vbase by an arbitrary odd power of # z, so that it won't look too much like the previous # value. vbase = vbase * pow(z, (vbase + v1 + v2) | 1, p) % p # Move vbase into the next smaller group by squaring # it. vbase = pow(vbase, 2, p) ptest(vbase) def testShifts(self): x = ((1<<900) // 9949) | 1 for i in range(2049): mp = mp_copy(x) mp_lshift_fixed_into(mp, mp, i) self.assertEqual(int(mp), (x << i) & mp_mask(mp)) mp_copy_into(mp, x) mp_lshift_safe_into(mp, mp, i) self.assertEqual(int(mp), (x << i) & mp_mask(mp)) mp_copy_into(mp, x) mp_rshift_fixed_into(mp, mp, i) self.assertEqual(int(mp), x >> i) mp_copy_into(mp, x) mp_rshift_safe_into(mp, mp, i) self.assertEqual(int(mp), x >> i) self.assertEqual(int(mp_rshift_fixed(x, i)), x >> i) self.assertEqual(int(mp_rshift_safe(x, i)), x >> i) def testRandom(self): # Test random_bits to ensure it correctly masks the return # value, and uses exactly as many random bytes as we expect it # to. for bits in range(512): bytes_needed = (bits + 7) // 8 with queued_random_data(bytes_needed, "random_bits test"): mp = mp_random_bits(bits) self.assertTrue(int(mp) < (1 << bits)) self.assertEqual(random_queue_len(), 0) # Test mp_random_in_range to ensure it returns things in the # right range. for rangesize in [2, 3, 19, 35]: for lo in [0, 1, 0x10001, 1<<512]: hi = lo + rangesize bytes_needed = mp_max_bytes(hi) + 16 for trial in range(rangesize*3): with queued_random_data( bytes_needed, "random_in_range {:d}".format(trial)): v = int(mp_random_in_range(lo, hi)) self.assertTrue(lo <= v < hi) class ecc(MyTestBase): def testWeierstrassSimple(self): # Simple tests using a Weierstrass curve I made up myself, # which (unlike the ones used for serious crypto) is small # enough that you can fit all the coordinates for a curve on # to your retina in one go. p = 3141592661 a, b = -3 % p, 12345 rc = WeierstrassCurve(p, a, b) wc = ecc_weierstrass_curve(p, a, b, None) def check_point(wp, rp): self.assertTrue(ecc_weierstrass_point_valid(wp)) is_id = ecc_weierstrass_is_identity(wp) x, y = ecc_weierstrass_get_affine(wp) if rp.infinite: self.assertEqual(is_id, 1) else: self.assertEqual(is_id, 0) self.assertEqual(int(x), int(rp.x)) self.assertEqual(int(y), int(rp.y)) def make_point(x, y): wp = ecc_weierstrass_point_new(wc, x, y) rp = rc.point(x, y) check_point(wp, rp) return wp, rp # Some sample points, including the identity and also a pair # of mutual inverses. wI, rI = ecc_weierstrass_point_new_identity(wc), rc.point() wP, rP = make_point(102, 387427089) wQ, rQ = make_point(1000, 546126574) wmP, rmP = make_point(102, p - 387427089) # Check the simple arithmetic functions. check_point(ecc_weierstrass_add(wP, wQ), rP + rQ) check_point(ecc_weierstrass_add(wQ, wP), rP + rQ) check_point(ecc_weierstrass_double(wP), rP + rP) check_point(ecc_weierstrass_double(wQ), rQ + rQ) # Check all the special cases with add_general: # Adding two finite unequal non-mutually-inverse points check_point(ecc_weierstrass_add_general(wP, wQ), rP + rQ) # Doubling a finite point check_point(ecc_weierstrass_add_general(wP, wP), rP + rP) check_point(ecc_weierstrass_add_general(wQ, wQ), rQ + rQ) # Adding the identity to a point (both ways round) check_point(ecc_weierstrass_add_general(wI, wP), rP) check_point(ecc_weierstrass_add_general(wI, wQ), rQ) check_point(ecc_weierstrass_add_general(wP, wI), rP) check_point(ecc_weierstrass_add_general(wQ, wI), rQ) # Doubling the identity check_point(ecc_weierstrass_add_general(wI, wI), rI) # Adding a point to its own inverse, giving the identity. check_point(ecc_weierstrass_add_general(wmP, wP), rI) check_point(ecc_weierstrass_add_general(wP, wmP), rI) # Verify that point_valid fails if we pass it nonsense. bogus = ecc_weierstrass_point_new(wc, int(rP.x), int(rP.y * 3)) self.assertFalse(ecc_weierstrass_point_valid(bogus)) # Re-instantiate the curve with the ability to take square # roots, and check that we can reconstruct P and Q from their # x coordinate and y parity only. wc = ecc_weierstrass_curve(p, a, b, find_non_square_mod(p)) x, yp = int(rP.x), (int(rP.y) & 1) check_point(ecc_weierstrass_point_new_from_x(wc, x, yp), rP) check_point(ecc_weierstrass_point_new_from_x(wc, x, yp ^ 1), rmP) x, yp = int(rQ.x), (int(rQ.y) & 1) check_point(ecc_weierstrass_point_new_from_x(wc, x, yp), rQ) def testMontgomerySimple(self): p, a, b = 3141592661, 0xabc, 0xde rc = MontgomeryCurve(p, a, b) mc = ecc_montgomery_curve(p, a, b) rP = rc.cpoint(0x1001) rQ = rc.cpoint(0x20001) rdiff = rP - rQ rsum = rP + rQ def make_mpoint(rp): return ecc_montgomery_point_new(mc, int(rp.x)) mP = make_mpoint(rP) mQ = make_mpoint(rQ) mdiff = make_mpoint(rdiff) msum = make_mpoint(rsum) def check_point(mp, rp): x = ecc_montgomery_get_affine(mp) self.assertEqual(int(x), int(rp.x)) check_point(ecc_montgomery_diff_add(mP, mQ, mdiff), rsum) check_point(ecc_montgomery_diff_add(mQ, mP, mdiff), rsum) check_point(ecc_montgomery_diff_add(mP, mQ, msum), rdiff) check_point(ecc_montgomery_diff_add(mQ, mP, msum), rdiff) check_point(ecc_montgomery_double(mP), rP + rP) check_point(ecc_montgomery_double(mQ), rQ + rQ) zero = ecc_montgomery_point_new(mc, 0) self.assertEqual(ecc_montgomery_is_identity(zero), False) identity = ecc_montgomery_double(zero) ecc_montgomery_get_affine(identity) self.assertEqual(ecc_montgomery_is_identity(identity), True) def testEdwardsSimple(self): p, d, a = 3141592661, 2688750488, 367934288 rc = TwistedEdwardsCurve(p, d, a) ec = ecc_edwards_curve(p, d, a, None) def check_point(ep, rp): x, y = ecc_edwards_get_affine(ep) self.assertEqual(int(x), int(rp.x)) self.assertEqual(int(y), int(rp.y)) def make_point(x, y): ep = ecc_edwards_point_new(ec, x, y) rp = rc.point(x, y) check_point(ep, rp) return ep, rp # Some sample points, including the identity and also a pair # of mutual inverses. eI, rI = make_point(0, 1) eP, rP = make_point(196270812, 1576162644) eQ, rQ = make_point(1777630975, 2717453445) emP, rmP = make_point(p - 196270812, 1576162644) # Check that the ordinary add function handles all the special # cases. # Adding two finite unequal non-mutually-inverse points check_point(ecc_edwards_add(eP, eQ), rP + rQ) check_point(ecc_edwards_add(eQ, eP), rP + rQ) # Doubling a finite point check_point(ecc_edwards_add(eP, eP), rP + rP) check_point(ecc_edwards_add(eQ, eQ), rQ + rQ) # Adding the identity to a point (both ways round) check_point(ecc_edwards_add(eI, eP), rP) check_point(ecc_edwards_add(eI, eQ), rQ) check_point(ecc_edwards_add(eP, eI), rP) check_point(ecc_edwards_add(eQ, eI), rQ) # Doubling the identity check_point(ecc_edwards_add(eI, eI), rI) # Adding a point to its own inverse, giving the identity. check_point(ecc_edwards_add(emP, eP), rI) check_point(ecc_edwards_add(eP, emP), rI) # Re-instantiate the curve with the ability to take square # roots, and check that we can reconstruct P and Q from their # y coordinate and x parity only. ec = ecc_edwards_curve(p, d, a, find_non_square_mod(p)) y, xp = int(rP.y), (int(rP.x) & 1) check_point(ecc_edwards_point_new_from_y(ec, y, xp), rP) check_point(ecc_edwards_point_new_from_y(ec, y, xp ^ 1), rmP) y, xp = int(rQ.y), (int(rQ.x) & 1) check_point(ecc_edwards_point_new_from_y(ec, y, xp), rQ) # For testing point multiplication, let's switch to the full-sized # standard curves, because I want to have tested those a bit too. def testWeierstrassMultiply(self): wc = ecc_weierstrass_curve(p256.p, int(p256.a), int(p256.b), None) wG = ecc_weierstrass_point_new(wc, int(p256.G.x), int(p256.G.y)) self.assertTrue(ecc_weierstrass_point_valid(wG)) ints = set(i % p256.p for i in fibonacci_scattered(10)) ints.remove(0) # the zero multiple isn't expected to work for i in sorted(ints): wGi = ecc_weierstrass_multiply(wG, i) x, y = ecc_weierstrass_get_affine(wGi) rGi = p256.G * i self.assertEqual(int(x), int(rGi.x)) self.assertEqual(int(y), int(rGi.y)) def testMontgomeryMultiply(self): mc = ecc_montgomery_curve( curve25519.p, int(curve25519.a), int(curve25519.b)) mG = ecc_montgomery_point_new(mc, int(curve25519.G.x)) ints = set(i % p256.p for i in fibonacci_scattered(10)) ints.remove(0) # the zero multiple isn't expected to work for i in sorted(ints): mGi = ecc_montgomery_multiply(mG, i) x = ecc_montgomery_get_affine(mGi) rGi = curve25519.G * i self.assertEqual(int(x), int(rGi.x)) def testEdwardsMultiply(self): ec = ecc_edwards_curve(ed25519.p, int(ed25519.d), int(ed25519.a), None) eG = ecc_edwards_point_new(ec, int(ed25519.G.x), int(ed25519.G.y)) ints = set(i % ed25519.p for i in fibonacci_scattered(10)) ints.remove(0) # the zero multiple isn't expected to work for i in sorted(ints): eGi = ecc_edwards_multiply(eG, i) x, y = ecc_edwards_get_affine(eGi) rGi = ed25519.G * i self.assertEqual(int(x), int(rGi.x)) self.assertEqual(int(y), int(rGi.y)) class keygen(MyTestBase): def testPrimeCandidateSource(self): def inspect(pcs): # Returns (pcs->limit, pcs->factor, pcs->addend) as Python integers return tuple(map(int, pcs_inspect(pcs))) # Test accumulating modular congruence requirements, by # inspecting the internal values computed during # require_residue. We ensure that the addend satisfies all our # congruences and the factor is the lcm of all the moduli # (hence, the arithmetic progression defined by those # parameters is precisely the set of integers satisfying the # requirements); we also ensure that the limiting values # (addend itself at the low end, and addend + (limit-1) * # factor at the high end) are the maximal subsequence of that # progression that are within the originally specified range. def check(pcs, lo, hi, mod_res_pairs): limit, factor, addend = inspect(pcs) for mod, res in mod_res_pairs: self.assertEqual(addend % mod, res % mod) self.assertEqual(factor, functools.reduce( lcm, [mod for mod, res in mod_res_pairs])) self.assertFalse(lo <= addend + (-1) * factor < hi) self.assertTrue (lo <= addend < hi) self.assertTrue (lo <= addend + (limit-1) * factor < hi) self.assertFalse(lo <= addend + limit * factor < hi) pcs = pcs_new(64) check(pcs, 2**63, 2**64, [(2, 1)]) pcs_require_residue(pcs, 3, 2) check(pcs, 2**63, 2**64, [(2, 1), (3, 2)]) pcs_require_residue_1(pcs, 7) check(pcs, 2**63, 2**64, [(2, 1), (3, 2), (7, 1)]) pcs_require_residue(pcs, 16, 7) check(pcs, 2**63, 2**64, [(2, 1), (3, 2), (7, 1), (16, 7)]) pcs_require_residue(pcs, 49, 8) check(pcs, 2**63, 2**64, [(2, 1), (3, 2), (7, 1), (16, 7), (49, 8)]) # Now test-generate some actual values, and ensure they # satisfy all the congruences, and also avoid one residue mod # 5 that we told them to. Also, give a nontrivial range. pcs = pcs_new_with_firstbits(64, 0xAB, 8) pcs_require_residue(pcs, 0x100, 0xCD) pcs_require_residue_1(pcs, 65537) pcs_avoid_residue_small(pcs, 5, 3) pcs_ready(pcs) with random_prng("test seed"): for i in range(100): n = int(pcs_generate(pcs)) self.assertTrue((0xAB<<56) < n < (0xAC<<56)) self.assertEqual(n % 0x100, 0xCD) self.assertEqual(n % 65537, 1) self.assertNotEqual(n % 5, 3) # I'm not actually testing here that the outputs of # pcs_generate are non-multiples of _all_ primes up to # 2^16. But checking this many for 100 turns is enough # to be pretty sure. (If you take the product of # (1-1/p) over all p in the list below, you find that # a given random number has about a 13% chance of # avoiding being a multiple of any of them. So 100 # trials without a mistake gives you 0.13^100 < 10^-88 # as the probability of it happening by chance. More # likely the code is actually working :-) for p in [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61]: self.assertNotEqual(n % p, 0) def testPocklePositive(self): def add_small(po, *ps): for p in ps: self.assertEqual(pockle_add_small_prime(po, p), 'POCKLE_OK') def add(po, *args): self.assertEqual(pockle_add_prime(po, *args), 'POCKLE_OK') # Transcription of the proof that 2^130-5 is prime from # Theorem 3.1 from http://cr.yp.to/mac/poly1305-20050329.pdf po = pockle_new() p1 = (2**130 - 6) // 1517314646 p2 = (p1 - 1) // 222890620702 add_small(po, 37003, 221101) add(po, p2, [37003, 221101], 2) add(po, p1, [p2], 2) add(po, 2**130 - 5, [p1], 2) # My own proof that 2^255-19 is prime po = pockle_new() p1 = 8574133 p2 = 1919519569386763 p3 = 75445702479781427272750846543864801 p4 = (2**255 - 20) // (65147*12) p = 2**255 - 19 add_small(po, p1) add(po, p2, [p1], 2) add(po, p3, [p2], 2) add(po, p4, [p3], 2) add(po, p, [p4], 2) # And the prime used in Ed448, while I'm here po = pockle_new() p1 = 379979 p2 = 1764234391 p3 = 97859369123353 p4 = 34741861125639557 p5 = 36131535570665139281 p6 = 167773885276849215533569 p7 = 596242599987116128415063 p = 2**448 - 2**224 - 1 add_small(po, p1, p2) add(po, p3, [p1], 2) add(po, p4, [p2], 2) add(po, p5, [p4], 2) add(po, p6, [p3], 3) add(po, p7, [p5], 3) add(po, p, [p6, p7], 2) p = 4095744004479977 factors = [2, 79999] # just enough factors to exceed cbrt(p) po = pockle_new() for q in factors: add_small(po, q) add(po, p, factors, 3) # The order of the generator in Ed25519 po = pockle_new() p1a, p1b = 132667, 137849 p2 = 3044861653679985063343 p3 = 198211423230930754013084525763697 p = 2**252 + 0x14def9dea2f79cd65812631a5cf5d3ed add_small(po, p1a, p1b) add(po, p2, [p1a, p1b], 2) add(po, p3, [p2], 2) add(po, p, [p3], 2) # And the one in Ed448 po = pockle_new() p1 = 766223 p2 = 3009341 p3 = 7156907 p4 = 671065561 p5 = 342682509629 p6 = 6730519843040614479184435237013 p = 2**446 - 0x8335dc163bb124b65129c96fde933d8d723a70aadc873d6d54a7bb0d add_small(po, p1, p2, p3, p4) add(po, p5, [p1], 2) add(po, p6, [p3,p4], 2) add(po, p, [p2,p5,p6], 2) def testPockleNegative(self): def add_small(po, p): self.assertEqual(pockle_add_small_prime(po, p), 'POCKLE_OK') po = pockle_new() self.assertEqual(pockle_add_small_prime(po, 0), 'POCKLE_PRIME_SMALLER_THAN_2') self.assertEqual(pockle_add_small_prime(po, 1), 'POCKLE_PRIME_SMALLER_THAN_2') self.assertEqual(pockle_add_small_prime(po, 2**61 - 1), 'POCKLE_SMALL_PRIME_NOT_SMALL') self.assertEqual(pockle_add_small_prime(po, 4), 'POCKLE_SMALL_PRIME_NOT_PRIME') po = pockle_new() self.assertEqual(pockle_add_prime(po, 1919519569386763, [8574133], 2), 'POCKLE_FACTOR_NOT_KNOWN_PRIME') po = pockle_new() add_small(po, 8574133) self.assertEqual(pockle_add_prime(po, 1919519569386765, [8574133], 2), 'POCKLE_FACTOR_NOT_A_FACTOR') p = 4095744004479977 factors = [2, 79997] # not quite enough factors to reach cbrt(p) po = pockle_new() for q in factors: add_small(po, q) self.assertEqual(pockle_add_prime(po, p, factors, 3), 'POCKLE_PRODUCT_OF_FACTORS_TOO_SMALL') p = 1999527 * 3999053 factors = [999763] po = pockle_new() for q in factors: add_small(po, q) self.assertEqual(pockle_add_prime(po, p, factors, 3), 'POCKLE_DISCRIMINANT_IS_SQUARE') p = 9999929 * 9999931 factors = [257, 2593] po = pockle_new() for q in factors: add_small(po, q) self.assertEqual(pockle_add_prime(po, p, factors, 3), 'POCKLE_FERMAT_TEST_FAILED') p = 1713000920401 # a Carmichael number po = pockle_new() add_small(po, 561787) self.assertEqual(pockle_add_prime(po, p, [561787], 2), 'POCKLE_WITNESS_POWER_IS_1') p = 4294971121 factors = [3, 5, 11, 17] po = pockle_new() for q in factors: add_small(po, q) self.assertEqual(pockle_add_prime(po, p, factors, 17), 'POCKLE_WITNESS_POWER_NOT_COPRIME') po = pockle_new() add_small(po, 2) self.assertEqual(pockle_add_prime(po, 1, [2], 1), 'POCKLE_PRIME_SMALLER_THAN_2') class crypt(MyTestBase): def testSSH1Fingerprint(self): # Example key and reference fingerprint value generated by # OpenSSH 6.7 ssh-keygen rsa = rsa_bare(65537, 984185866443261798625575612408956568591522723900235822424492423996716524817102482330189709310179009158443944785704183009867662230534501187034891091310377917105259938712348098594526746211645472854839799025154390701673823298369051411) fp = rsa_ssh1_fingerprint(rsa) self.assertEqual( fp, b"768 96:12:c8:bc:e6:03:75:86:e8:c7:b9:af:d8:0c:15:75") def testSSH2Fingerprints(self): # A sensible key blob that we can make sense of. sensible_blob = base64.decodebytes( b'AAAAC3NzaC1lZDI1NTE5AAAAICWiV0VAD4lQ7taUN7vZ5Rkc' b'SLJBW5ubn6ZINwCOzpn3') self.assertEqual(ssh2_fingerprint_blob(sensible_blob, "sha256"), b'ssh-ed25519 255 SHA256:' b'E4VmaHW0sUF7SUgSEOmMJ8WBtt0e/j3zbsKvyqfFnu4') self.assertEqual(ssh2_fingerprint_blob(sensible_blob, "md5"), b'ssh-ed25519 255 ' b'35:73:80:df:a3:2c:1a:f2:2c:a6:5c:84:ce:48:6a:7e') # A key blob with an unknown algorithm name, so that we can't # extract the bit count. silly_blob = ssh_string(b'foo') + ssh_string(b'key data') self.assertEqual(ssh2_fingerprint_blob(silly_blob, "sha256"), b'foo SHA256:' b'mvfJTB4PaRI7hxYaYwn0sH8G6zW1HbLkbWnZE2YIKc4') self.assertEqual(ssh2_fingerprint_blob(silly_blob, "md5"), b'foo ' b'5f:5f:97:94:97:be:01:5c:f6:3f:e3:6e:55:46:ea:52') # A key blob without even a valid algorithm-name string at the start. very_silly_blob = b'foo' self.assertEqual(ssh2_fingerprint_blob(very_silly_blob, "sha256"), b'SHA256:' b'LCa0a2j/xo/5m0U8HTBBNBNCLXBkg7+g+YpeiGJm564') self.assertEqual(ssh2_fingerprint_blob(very_silly_blob, "md5"), b'ac:bd:18:db:4c:c2:f8:5c:ed:ef:65:4f:cc:c4:a4:d8') def testAES(self): # My own test cases, generated by a mostly independent # reference implementation of AES in Python. ('Mostly' # independent in that it was written by me.) def vector(cipher, key, iv, plaintext, ciphertext): for suffix in "hw", "sw": c = ssh_cipher_new("{}_{}".format(cipher, suffix)) if c is None: return # skip test if HW AES not available ssh_cipher_setkey(c, key) ssh_cipher_setiv(c, iv) self.assertEqualBin( ssh_cipher_encrypt(c, plaintext), ciphertext) ssh_cipher_setiv(c, iv) self.assertEqualBin( ssh_cipher_decrypt(c, ciphertext), plaintext) # Tests of CBC mode. key = unhex( '98483c6eb40b6c31a448c22a66ded3b5e5e8d5119cac8327b655c8b5c4836489') iv = unhex('38f87b0b9b736160bfc0cbd8447af6ee') plaintext = unhex(''' ee16271827b12d828f61d56fddccc38ccaa69601da2b36d3af1a34c51947b71a 362f05e07bf5e7766c24599799b252ad2d5954353c0c6ca668c46779c2659c94 8df04e4179666e335470ff042e213c8bcff57f54842237fbf9f3c7e6111620ac 1c007180edd25f0e337c2a49d890a7173f6b52d61e3d2a21ddc8e41513a0e825 afd5932172270940b01014b5b7fb8495946151520a126518946b44ea32f9b2a9 ''') vector('aes128_cbc', key[:16], iv, plaintext, unhex(''' 547ee90514cb6406d5bb00855c8092892c58299646edda0b4e7c044247795c8d 3c3eb3d91332e401215d4d528b94a691969d27b7890d1ae42fe3421b91c989d5 113fefa908921a573526259c6b4f8e4d90ea888e1d8b7747457ba3a43b5b79b9 34873ebf21102d14b51836709ee85ed590b7ca618a1e884f5c57c8ea73fe3d0d 6bf8c082dd602732bde28131159ed0b6e9cf67c353ffdd010a5a634815aaa963''')) vector('aes192_cbc', key[:24], iv, plaintext, unhex(''' e3dee5122edd3fec5fab95e7db8c784c0cb617103e2a406fba4ae3b4508dd608 4ff5723a670316cc91ed86e413c11b35557c56a6f5a7a2c660fc6ee603d73814 73a287645be0f297cdda97aef6c51faeb2392fec9d33adb65138d60f954babd9 8ee0daab0d1decaa8d1e07007c4a3c7b726948025f9fb72dd7de41f74f2f36b4 23ac6a5b4b6b39682ec74f57d9d300e547f3c3e467b77f5e4009923b2f94c903''')) vector('aes256_cbc', key[:32], iv, plaintext, unhex(''' 088c6d4d41997bea79c408925255266f6c32c03ea465a5f607c2f076ec98e725 7e0beed79609b3577c16ebdf17d7a63f8865278e72e859e2367de81b3b1fe9ab 8f045e1d008388a3cfc4ff87daffedbb47807260489ad48566dbe73256ce9dd4 ae1689770a883b29695928f5983f33e8d7aec4668f64722e943b0b671c365709 dfa86c648d5fb00544ff11bd29121baf822d867e32da942ba3a0d26299bcee13''')) # Tests of SDCTR mode, one with a random IV and one with an IV # about to wrap round. More vigorous tests of IV carry and # wraparound behaviour are in the testAESSDCTR method. sdctrIVs = [ unhex('38f87b0b9b736160bfc0cbd8447af6ee'), unhex('fffffffffffffffffffffffffffffffe'), ] vector('aes128_ctr', key[:16], sdctrIVs[0], plaintext[:64], unhex(''' d0061d7b6e8c4ef4fe5614b95683383f46cdd2766e66b6fb0b0f0b3a24520b2d 15d869b06cbf685ede064bcf8fb5fb6726cfd68de7016696a126e9e84420af38''')) vector('aes128_ctr', key[:16], sdctrIVs[1], plaintext[:64], unhex(''' 49ac67164fd9ce8701caddbbc9a2b06ac6524d4aa0fdac95253971974b8f3bc2 bb8d7c970f6bcd79b25218cc95582edf7711aae2384f6cf91d8d07c9d9b370bc''')) vector('aes192_ctr', key[:24], sdctrIVs[0], plaintext[:64], unhex(''' 0baa86acbe8580845f0671b7ebad4856ca11b74e5108f515e34e54fa90f87a9a c6eee26686253c19156f9be64957f0dbc4f8ecd7cabb1f4e0afefe33888faeec''')) vector('aes192_ctr', key[:24], sdctrIVs[1], plaintext[:64], unhex(''' 2da1791250100dc0d1461afe1bbfad8fa0320253ba5d7905d837386ba0a3a41f 01965c770fcfe01cf307b5316afb3981e0e4aa59a6e755f0a5784d9accdc52be''')) vector('aes256_ctr', key[:32], sdctrIVs[0], plaintext[:64], unhex(''' 49c7b284222d408544c770137b6ef17ef770c47e24f61fa66e7e46cae4888882 f980a0f2446956bf47d2aed55ebd2e0694bfc46527ed1fd33efe708fec2f8b1f''')) vector('aes256_ctr', key[:32], sdctrIVs[1], plaintext[:64], unhex(''' f1d013c3913ccb4fc0091e25d165804480fb0a1d5c741bf012bba144afda6db2 c512f3942018574bd7a8fdd88285a73d25ef81e621aebffb6e9b8ecc8e2549d4''')) def testAESSDCTR(self): # A thorough test of the IV-incrementing component of SDCTR # mode. We set up an AES-SDCTR cipher object with the given # input IV; we encrypt two all-zero blocks, expecting the # return values to be the AES-ECB encryptions of the input IV # and the incremented version. Then we decrypt each of them by # feeding them to an AES-CBC cipher object with its IV set to # zero. def increment(keylen, suffix, iv): key = b'\xab' * (keylen//8) sdctr = ssh_cipher_new("aes{}_ctr_{}".format(keylen, suffix)) if sdctr is None: return # skip test if HW AES not available ssh_cipher_setkey(sdctr, key) cbc = ssh_cipher_new("aes{}_cbc_{}".format(keylen, suffix)) ssh_cipher_setkey(cbc, key) ssh_cipher_setiv(sdctr, iv) ec0 = ssh_cipher_encrypt(sdctr, b'\x00' * 16) ec1 = ssh_cipher_encrypt(sdctr, b'\x00' * 16) ssh_cipher_setiv(cbc, b'\x00' * 16) dc0 = ssh_cipher_decrypt(cbc, ec0) ssh_cipher_setiv(cbc, b'\x00' * 16) dc1 = ssh_cipher_decrypt(cbc, ec1) self.assertEqualBin(iv, dc0) return dc1 def test(keylen, suffix, ivInteger): mask = (1 << 128) - 1 ivInteger &= mask ivBinary = unhex("{:032x}".format(ivInteger)) ivIntegerInc = (ivInteger + 1) & mask ivBinaryInc = unhex("{:032x}".format((ivIntegerInc))) actualResult = increment(keylen, suffix, ivBinary) if actualResult is not None: self.assertEqualBin(actualResult, ivBinaryInc) # Check every input IV you can make by gluing together 32-bit # pieces of the form 0, 1 or -1. This should test all the # places where carry propagation within the 128-bit integer # can go wrong. # # We also test this at all three AES key lengths, in case the # core cipher routines are written separately for each one. for suffix in "hw", "sw": for keylen in [128, 192, 256]: hexTestValues = ["00000000", "00000001", "ffffffff"] for ivHexBytes in itertools.product(*([hexTestValues] * 4)): ivInteger = int("".join(ivHexBytes), 16) test(keylen, suffix, ivInteger) def testAESParallelism(self): # Since at least one of our implementations of AES works in # parallel, here's a test that CBC decryption works the same # way no matter how the input data is divided up. # A pile of conveniently available random-looking test data. test_ciphertext = ssh2_mpint(last(fibonacci_scattered(14))) test_ciphertext += b"x" * (15 & -len(test_ciphertext)) # pad to a block # Test key and IV. test_key = b"foobarbazquxquuxFooBarBazQuxQuux" test_iv = b"FOOBARBAZQUXQUUX" for keylen in [128, 192, 256]: decryptions = [] for suffix in "hw", "sw": c = ssh_cipher_new("aes{:d}_cbc_{}".format(keylen, suffix)) if c is None: continue ssh_cipher_setkey(c, test_key[:keylen//8]) for chunklen in range(16, 16*12, 16): ssh_cipher_setiv(c, test_iv) decryption = b"" for pos in range(0, len(test_ciphertext), chunklen): chunk = test_ciphertext[pos:pos+chunklen] decryption += ssh_cipher_decrypt(c, chunk) decryptions.append(decryption) for d in decryptions: self.assertEqualBin(d, decryptions[0]) def testCRC32(self): # Check the effect of every possible single-byte input to # crc32_update. In the traditional implementation with a # 256-word lookup table, this exercises every table entry; in # _any_ implementation which iterates over the input one byte # at a time, it should be a similarly exhaustive test. (But if # a more optimised implementation absorbed _more_ than 8 bits # at a time, then perhaps this test wouldn't be enough...) # It would be nice if there was a functools.iterate() which # would apply a function n times. Failing that, making shift1 # accept and ignore a second argument allows me to iterate it # 8 times using functools.reduce. shift1 = lambda x, dummy=None: (x >> 1) ^ (0xEDB88320 * (x & 1)) shift8 = lambda x: functools.reduce(shift1, [None]*8, x) # A small selection of choices for the other input to # crc32_update, just to check linearity. test_prior_values = [0, 0xFFFFFFFF, 0x45CC1F6A, 0xA0C4ADCF, 0xD482CDF1] for prior in test_prior_values: prior_shifted = shift8(prior) for i in range(256): exp = shift8(i) ^ prior_shifted self.assertEqual(crc32_update(prior, struct.pack("B", i)), exp) # Check linearity of the _reference_ implementation, while # we're at it! self.assertEqual(shift8(i ^ prior), exp) def testCRCDA(self): def pattern(badblk, otherblks, pat): # Arrange copies of the bad block in a pattern # corresponding to the given bit string. retstr = b"" while pat != 0: retstr += (badblk if pat & 1 else next(otherblks)) pat >>= 1 return retstr def testCases(pat): badblock = b'muhahaha' # the block we'll maliciously repeat # Various choices of the other blocks, including all the # same, all different, and all different but only in the # byte at one end. for otherblocks in [ itertools.repeat(b'GoodData'), (struct.pack('>Q', i) for i in itertools.count()), (struct.pack('= maxlen buf = b''.join(hash_str(hashname, text[:i]) for i in range(maxlen)) self.assertEqualBin(hash_str(hashname, buf), unhex(expected)) test('md5', 128, '8169d766cc3b8df182b3ce756ae19a15') test('sha1', 128, '3691759577deb3b70f427763a9c15acb9dfc0259') test('sha256', 128, 'ec539c4d678412c86c13ee4eb9452232' '35d4eed3368d876fdf10c9df27396640') test('sha512', 256, 'cb725b4b4ec0ac1174d69427b4d97848b7db4fc01181f99a8049a4d721862578' 'f91e026778bb2d389a9dd88153405189e6ba438b213c5387284103d2267fd055' ) def testDSA(self): p = 0xe93618c54716992ffd54e79df6e1b0edd517f7bbe4a49d64631eb3efe8105f676e8146248cfb4f05720862533210f0c2ab0f9dd61dbc0e5195200c4ebd95364b q = 0xf3533bcece2e164ca7c5ce64bc1e395e9a15bbdd g = 0x5ac9d0401c27d7abfbc5c17cdc1dc43323cd0ef18b79e1909bdace6d17af675a10d37dde8bd8b70e72a8666592216ccb00614629c27e870e4fbf393b812a9f05 y = 0xac3ddeb22d65a5a2ded4a28418b2a748d8e5e544ba5e818c137d7b042ef356b0ef6d66cfca0b3ab5affa2969522e7b07bee60562fa4869829a5afce0ad0c4cd0 x = 0x664f8250b7f1a5093047fe0c7fe4b58e46b73295 pubblob = ssh_string(b"ssh-dss") + b"".join(map(ssh2_mpint, [p,q,g,y])) privblob = ssh2_mpint(x) pubkey = ssh_key_new_pub('dsa', pubblob) privkey = ssh_key_new_priv('dsa', pubblob, privblob) sig = ssh_key_sign(privkey, b"hello, world", 0) self.assertTrue(ssh_key_verify(pubkey, sig, b"hello, world")) self.assertFalse(ssh_key_verify(pubkey, sig, b"hello, again")) badsig0 = unhex('{:040x}{:040x}'.format(1, 0)) badsigq = unhex('{:040x}{:040x}'.format(1, q)) self.assertFalse(ssh_key_verify(pubkey, badsig0, "hello, world")) self.assertFalse(ssh_key_verify(pubkey, badsigq, "hello, world")) self.assertFalse(ssh_key_verify(pubkey, badsig0, "hello, again")) self.assertFalse(ssh_key_verify(pubkey, badsigq, "hello, again")) def testBLAKE2b(self): # The standard test vectors for BLAKE2b (in the separate class # below) don't satisfy me because they only test one hash # size. These additional tests exercise BLAKE2b's configurable # output length. The expected results are derived from the # BLAKE2 reference implementation. def b2_with_len(data, length): h = blake2b_new_general(length) h.update(data) return h.digest()[:length] self.assertEqualBin(b2_with_len(b'hello', 1), unhex("29")) self.assertEqualBin(b2_with_len(b'hello', 2), unhex("accd")) self.assertEqualBin(b2_with_len(b'hello', 3), unhex("980032")) self.assertEqualBin(b2_with_len(b'hello', 5), unhex("9baecc38f2")) self.assertEqualBin(b2_with_len(b'hello', 8), unhex( "a7b6eda801e5347d")) self.assertEqualBin(b2_with_len(b'hello', 13), unhex( "6eedb122c6707328a66aa34a07")) self.assertEqualBin(b2_with_len(b'hello', 21), unhex( "c7f0f74a227116547b3d2788e927ee2a76c87d8797")) self.assertEqualBin(b2_with_len(b'hello', 34), unhex( "2f5fcdf2b870fa254051dd448193a1fb6e92be122efca539ba2aeac0bc6c77d0" "dadc")) self.assertEqualBin(b2_with_len(b'hello', 55), unhex( "daafcf2bd6fccf976cbc234b71cd9f4f7d56fe0eb33a40018707089a215c44a8" "4b272d0329ae6d85a0f8acc7e964dc2facb715ba472bb6")) def testArgon2LongHash(self): # Unit-test the Argon2 long hash function H', which starts off # the same as BLAKE2b, but comes with its own method of # extending the output length past 64 bytes. # # I generated these test values using a test program linked # against the reference implementation's libargon2.a and # calling its blake2b_long function. preimage = b'hello, world' self.assertEqualBin(argon2_long_hash(1, preimage), unhex("8b")) self.assertEqualBin(argon2_long_hash(2, preimage), unhex("1ff9")) self.assertEqualBin(argon2_long_hash(63, preimage), unhex( "e2c997721f1d64aa8c25e588fb8ab19646ce6d5c2a431fa560fcb813e55dd481" "322d2630d95ca6b1b63317b13d6b111e5816170c80c3ca7d5b4bf894096de4")) self.assertEqualBin(argon2_long_hash(64, preimage), unhex( "0c7ba7ee6d510b4bb5c9b69ac91e25e0b11aa30dd6234b8e61b0fe1537c037b8" "8ed5aa59a277e8cc07095c81aff26d08967e4dfdabd32db8b6af6ceb78cf8c47")) self.assertEqualBin(argon2_long_hash(65, preimage), unhex( "680941abbd8fc80f28c38d623e90903f08709bf76575e2775d4ce01c31b192c8" "73038d9a31af8991c8b1ad4f2b1991f4d15f73ab0f4f3add415c297a12eb9ddb" "76")) self.assertEqualBin(argon2_long_hash(95, preimage), unhex( "4be28c51850fed70d9403e1406b6ba68a83d98cf222a4ee162beef60fd3384df" "eba3fce9d95f646982eb384ac943ce5263cb03428fd8d261cc41ffdb7ba328fe" "098526f2b49593f9e7f38188598ce4693b59f4dd32db30c1be9a9d35784fa0")) self.assertEqualBin(argon2_long_hash(96, preimage), unhex( "20295ea01e822cca113f668f33e5e481ed5879bfd7de6359ea42d497da97be52" "2cdd518d34ae32c44cabd45249b4e697626b0b14b6a33a2bd138be0a4bceeaf4" "9528f93acef01b093ee84d8d871d1ee6cf7c10e83ad0619631aed19345166f03")) self.assertEqualBin(argon2_long_hash(97, preimage), unhex( "d24b31f3ac0baad168d524efc4bafee55fef743fd60b14e28b860d7523e319c7" "520e2d5457cc3d06dc1044530afdf6990fa12e38d5802eb642f8e77fcfee2c0b" "1f84a28877f2f2f049ed9299e1e0230f98af3a161185970aad21f0ea0f5184cf" "90")) self.assertEqualBin(argon2_long_hash(127, preimage), unhex( "5d1e8380450dbc985418ed1f3700b925ae0719e4486e29131c81bca7083ac6b8" "f535c3398488e34d3dc1390de44097f1eee498f10ebe85b579e99a7672023b01" "ca5c20e63c595b640e00d80f113a52e3773719889b266ab4c65269c11fb212e4" "75f2b769bb26321bb60ecc0d490821e5056d7dfc9def3cd065d3ba90360764")) self.assertEqualBin(argon2_long_hash(128, preimage), unhex( "be15b316f3483c4d0d00f71a65b974894a2025f441b79b9fe461bc740cb0b039" "c4fe914f61c05a612d63ebc50a662b2d59b1996091e5e3474340544ea46a46cb" "25c41ff700fafcd96c4f12ddc698cd2426558f960696837ea8170fd2fe284b54" "8f585f97919ef14f2b3cbb351eb98872add7ba6d08c1401232df6cc878fbeb22")) self.assertEqualBin(argon2_long_hash(129, preimage), unhex( "83da464c278dcb12c29b6685fee6d32f0b461337c155369ad0d56b58b0aa5f80" "9aa7b56bd41b664c8d768957f8f0e40999fb0178eb53cf83f31d725bf92881bc" "900774bce4cdf56b6386ad3de6891d11a0ccd4564a3431fc4c24105a02d0a6a2" "434712b9a7471f3223c72a6e64912200d0a3d149a19d06fe9dc8ec09d7ed5a48" "bb")) self.assertEqualBin(argon2_long_hash(511, preimage), unhex( "30c0c0d0467e7665368db0b40a2324a61fb569d35172de2df53a9739a8d18e60" "b4f25d521c8855604be3e24ea56302566074323d94c0bd3a33d08f185d8ba5ac" "a2bc3fb2e4c4e5ffec5778daea67c6b5913c9cac16f2e5c7b7818e757fa747b3" "69e586d616010a752762f69c604238ed8738430366fbdb7493454fa02391a76b" "30f241695b9fa8d3a3116227c6bb6f72d325cf104ab153d15f928b22767d467d" "4bf7e16176aaa7315954b7872061933c12d548f1f93a8abb9d73791661bee521" "b2ae51be373a229dfef32787234c1be5846d133563002b9a029178716ad41e70" "1539d3fad300c77607c5217701e3e485d72c980f3f71d525c8148375a2f8d22c" "a211ba165330a90b7e0e6baa6073833925c23bdd388ee904f38463c7e6b85475" "09b810aae5c9ffc5dd902c2ffe049c338e3ae2c6416d3b874d6a9d384089564c" "0d8e4dce9b6e47e1d5ec9087bf526cc9fa35aab1893a0588d31b77fea37e0799" "468deacde47629d2960a3519b3bcd4e22364a9cccd3b128cba21cac27f140d53" "f79c11e4157e4cb48272eecdf62f52084a27e5b0933bbe66ded17e2df6f8d398" "f6c479c3c716457820ad177b8bd9334cb594e03d09fcc4f82d4385e141eacd7d" "9ad1e1c4cb42788af70bac0509f0a891e662960955490abf2763373803e8c89c" "df632579cb9c647634b30df214a3d67b92fd55d283c42c63b470a48a78cd5b")) self.assertEqualBin(argon2_long_hash(512, preimage), unhex( "79a6974e29a9a6c069e0156774d35c5014a409f5ffc60013725367a7208d4929" "7d228637751768a31a59e27aa89372f1bcc095a6fa331198a5bd5ad053ba2ebb" "cbcc501ea55cf142e8d95209228c9ab60cd104d5077472f2a9ecaa071aed6ee9" "5de29e188b7399d5b6b7ed897b2bc4dd1ea745eb9974e39ca6fb983380cc537a" "c04dfe6caefe85faf206b1613092ebadf791eaa8a5b814c9a79a73a5733b0505" "a47163c10a0f7309df6663896df6079a7c88c6879bb591a40abd398c6deda792" "1cc3986435b1c840a768b2fa507446f2f77a406b1b2f739f7795db24789c8927" "24b4c84b7005445123154f8cd2ba63a7ede672af5d197f846700732025c9931d" "1c67c5493417ca394a8f68ba532645815cf7b5102af134ecb4fd9e326f53779a" "3039dbef6a0880db9e38b6b61d2f9ead969e4224c2d9c69b5897e5eeb7032e83" "334e192ff50017056ccb84d4cc8eee3ab248d2614643d0174fe18c72186dd967" "92d8545645ddf4a9b2c7a91c9a71857a399449d7154077a8e9580f1a2d20227d" "671b455ccb897cba0491e50892120d7877f7776d653cfdb176fa3f64a9e6f848" "cd681c487b488775aaf698294eec813b2cca90d68d63b5d886d61c1a8e922aaa" "330fd658ede56e34bcd288048e845eba7b8e2e7cc22ba6c91b523e48017aa878" "8ce4f91d0e6d6c6706762fb0cc7f465cee3916684fb21e337cfe1b583e0b1e92")) self.assertEqualBin(argon2_long_hash(513, preimage), unhex( "32243cfbd7eca582d60b3b8ea3ba3d93783537689c7cbcd1d1cbde46200b8c86" "617fc00e8a9ae991a1e2f91c67e07d5f0a777d982c1461d0c5474e4e164b053c" "2808559e2b8a5ac4a46a5fcbc825b1d5302c7b0611940194eb494d45ce7113a2" "3424b51c199c6a5100ab159ff323eda5feffee4da4155a028a81da9d44e4286b" "ac3dab4ffce43a80b6ce97a47ea0ac51ee16e8b4d3b68942afdc20e1c21747c4" "94859c3d3883e7dc19ea416a393a3507683d9d03e6a3a91f8f1cb8a7d5d9892e" "80c8fb0222527a73a1f59b9dd41770982f2af177a6e96093064534803edd0713" "71ede53024cedc291d768325bb4e4def9af1b5569c349b64816496c37a8787b5" "4fbe248372ebadb5ce20e03eaa935dc55ff4b8cbe5d6d844c7b71d4656fef22c" "5a49f13d75a7a8368a2dbc1e78d732b879bfc5c9467eda2bf4918f0c59037ae3" "dee7880a171409dd1a4e143c814e60301ac77237f261fa7519a04e68000530f9" "708ed9fda5609d655560a9491f80f5875ad5725e3120686b73319c6a727932e3" "20a2174422523498c38fea47aeb20d135ff9fd93c6fa6db0005e0001685d7577" "33a82a4dc9dd6556b938f7b8dafd0d670846780b9931b815063708189b17877b" "825533bcc250fb576a28be4caa107e6a3a6f7b0c60fb51b0def27008b7e272ac" "95d610bfa912339799a2e537ce543d7862dddbe31bb224fda4ae283571847a28" "54")) self.assertEqualBin(argon2_long_hash(1024, preimage), unhex( "951252f6fa152124f381266a358d9b78b88e469d08d5fc78e4ea32253c7fc26c" "3ff1c93529ab4ee6fcf00acf29bbaba934a4014ce2625e0806601c55e6ce70d7" "121fd82f0904f335c5c7ba07dc6e6adf7582c92f7f255072203ea85844b4fe54" "817476a20bb742710ffc42750361be94332d0fc721b192309acfa70da43db6ae" "1d0f0bbe8a3250966a4532b36728162073c9eb3e119ea4c1c187c775dbb25a5d" "d883e3f65706a5fca897cdc4a8aa7b68ba3f57940c72f3a3396c417e758ba071" "95be4afba325237c0e2738a74d96fd1350fb623cb2ad40ea8b1e070cf398b98c" "2865ea40225b81f031f2b405409ca01dc5d9903d3d8e1d6381fbe7ccfc8f3dab" "eadafd7c976c0ba84a936f78ff7df0f112c089ba88f82bed7f9a6e31a91e5fee" "f675755454b948de22695660b243b9eca3bcc89608f83d2baa1d73dd6b8bd4f9" "b995ed9cb0f1edc6e98a49ed841b506c1bf59b43f4b3457a376bbff116c1a4f6" "07cc62381fc5c19953c68f300c1b51198d40784d812d25810ba404862f04b680" "6039a074f612ad8b84e0941ba23c915c3e7162c225fbecffdb7dc1ab559b2b54" "32fe8a498c32e918d8e7e33254ff75077f648827705e987f4d90fba971e78e1a" "6896b4d775c7359dc950f1e964fa04621aacf3c0988969490f4c72c54caf79e8" "481053cc0a27ffcd3580aabf9ef1268d498d8a18bd70e9b8402e011753bb7dc7" "e856c00d988fca924ee7cf61979c38cda8a872e4cc4fbdc90c23a0ded71eb944" "bb816ab22d9a4380e3e9d1cec818165c2fba6c5d51dcbf452c0cb1779a384937" "64d695370e13a301eca7be68d4112d2177381514efbb36fe08fc5bc2970301b8" "06f8e5a57a780e894d5276e2025bb775b6d1861e33c54ab6e3eb72947fbe6f91" "8174ce24eb4682efbb3c4f01233dc7ce9ef44792e9e876bb03e6751b3d559047" "d045127d976aa042fc55c690c9048e200065e7b7de19d9353aa9ac9b3e7611f0" "d1c42d069a300455ca1f7420a352bace89215e705106927510c11b3b1c1486d9" "f3ab006d2de2ee2c94574f760ce8c246bca229f98c66f06042b14f1fff9a16c0" "1550237e16d108ce5597299b1eb406a9ee505a29a6e0fa526b3e6beafd336aea" "138b2f31971586f67c5ffffbd6826d1c75666038c43d0bdff4edfc294e064a49" "2eed43e2dc78d00abc4e85edcd9563b8251b66f57b0f4b6d17f5a3f35c87c488" "dbeeb84fd720286197c2dec8290eccf3a313747de285b9cd3548e90cf81b3838" "3ffcc8c2a7f582feb369d05cb96b9b224d05902b3e39e5b96536032e9dddeb9b" "9d4f40a9c8f544ca37cf8d39d7c8c6a33880e9184ed017bd642db9590759bd10" "7362048ede5c0257feecc4984584592c566f37fba8469c064015339fb4f03023" "56ece37fd3655aae2bfc989b9b4c1384efc3503c8866db901802cb36eda9fb00")) def testArgon2(self): # A few tests of my own of Argon2, derived from the reference # implementation. pwd = b"password" salt = b"salt of at least 16 bytes" secret = b"secret" assoc = b"associated data" # Smallest memory (8Kbyte) and parallelism (1) parameters the # reference implementation will accept, but lots of passes self.assertEqualBin( argon2('i', 8, 16, 1, 24, pwd, salt, secret, assoc), unhex( "314da280240a3ca1eedd1f1db417a76eb0741e7df64b8cdf")) self.assertEqualBin( argon2('d', 8, 16, 1, 24, pwd, salt, secret, assoc), unhex( "9cc961cf43e0f86c2d4e202b816dc5bc5b2177e68faa0b08")) self.assertEqualBin( argon2('id', 8, 16, 1, 24, pwd, salt, secret, assoc), unhex( "6cd6c490c582fa597721d772d4e3de166987792491b48c51")) # Test a memory cost value that isn't a power of 2. This # checks a wraparound case during the conversion of J1 to a # block index, and is a regression test for a bug that nearly # got past me during original development. self.assertEqualBin( argon2('i', 104, 16, 2, 24, pwd, salt, secret, assoc), unhex( "a561963623f1073c9aa8caecdb600c73ffc6de677ba8d97c")) self.assertEqualBin( argon2('d', 104, 16, 2, 24, pwd, salt, secret, assoc), unhex( "a9014db7f1d468fb25b88fa7fc0deac0f2e7f27e25d2cf6e")) self.assertEqualBin( argon2('id', 104, 16, 2, 24, pwd, salt, secret, assoc), unhex( "64f3212b1e7725ffcf9ae2d1753d63e763bcd6970061a435")) # Larger parameters that should exercise the pseudorandom # block indexing reasonably thoroughly. Also generate plenty # of output data. self.assertEqualBin( argon2('i', 1024, 5, 16, 77, pwd, salt, secret, assoc), unhex( "b008a685ff57730fad0e6f3ef3b9189282c0d9b05303675f43b5f3054724" "733fcbe8e2639cc2c930535b31b723339041bcd703bf2483455acf86c0e6" "9ed88c545ad40f1f2068855e4d61e99407")) self.assertEqualBin( argon2('d', 1024, 5, 16, 111, pwd, salt, secret, assoc), unhex( "399ffbcd720c47745b9deb391ed0de7d5e0ffe53aef9f8ef7a7918cfa212" "53df8cc577affbd5e0c0f8bf6d93c11b2f63973f8fc8f89dccd832fc587e" "5d61717be6e88ca33eef5d1e168c028bae632a2a723c6c83f8e755f39171" "5eda1c77c8e2fe06fbdd4e56d35262587e7df73cd7")) self.assertEqualBin( argon2('id', 1024, 5, 16, 123, pwd, salt, secret, assoc), unhex( "6636807289cb9b9c032f48dcc31ffed1de4ca6c1b97e1ce768d690486341" "2ac84b39d568a81dd01d9ee3ceec6cc23441d95e6abeb4a2024f1f540d56" "9b799277c4037ddc7195ba783c9158a901adc7d4a5df8357b34a3869e5d6" "aeae2a21201eef5e347de22c922192e8f46274b0c9d33e965155a91e7686" "9d530e")) def testRSAVerify(self): def blobs(n, e, d, p, q, iqmp): pubblob = ssh_string(b"ssh-rsa") + ssh2_mpint(e) + ssh2_mpint(n) privblob = (ssh2_mpint(d) + ssh2_mpint(p) + ssh2_mpint(q) + ssh2_mpint(iqmp)) return pubblob, privblob def failure_test(*args): pubblob, privblob = blobs(*args) key = ssh_key_new_priv('rsa', pubblob, privblob) self.assertEqual(key, None) def success_test(*args): pubblob, privblob = blobs(*args) key = ssh_key_new_priv('rsa', pubblob, privblob) self.assertNotEqual(key, None) # Parameters for a (trivially small) test key. n = 0xb5d545a2f6423eabd55ffede53e21628d5d4491541482e10676d9d6f2783b9a5 e = 0x25 d = 0x6733db6a546ac99fcc21ba2b28b0c077156e8a705976205a955c6d9cef98f419 p = 0xe30ebd7348bf10dca72b36f2724dafa7 q = 0xcd02c87a7f7c08c4e9dc80c9b9bad5d3 iqmp = 0x60a129b30db9227910efe1608976c513 # Check the test key makes sense unmodified. success_test(n, e, d, p, q, iqmp) # Try modifying the values one by one to ensure they are # rejected, except iqmp, which sshrsa.c regenerates anyway so # it won't matter at all. failure_test(n+1, e, d, p, q, iqmp) failure_test(n, e+1, d, p, q, iqmp) failure_test(n, e, d+1, p, q, iqmp) failure_test(n, e, d, p+1, q, iqmp) failure_test(n, e, d, p, q+1, iqmp) success_test(n, e, d, p, q, iqmp+1) # The key should also be accepted with p,q reversed. (Again, # iqmp gets regenerated, so it won't matter if that's wrong.) success_test(n, e, d, q, p, iqmp) # Replace each of p and q with 0, and with 1. These should # still fail validation (obviously), but the point is that the # validator should also avoid trying to divide by zero in the # process. failure_test(n, e, d, 0, q, iqmp) failure_test(n, e, d, p, 0, iqmp) failure_test(n, e, d, 1, q, iqmp) failure_test(n, e, d, p, 1, iqmp) def testKeyMethods(self): # Exercise all the methods of the ssh_key trait on all key # types, and ensure that they're consistent with each other. # No particular test is done on the rightness of the # signatures by any objective standard, only that the output # from our signing method can be verified by the corresponding # verification method. # # However, we do include the expected signature text in each # case, which checks determinism in the sense of being # independent of any random numbers, and also in the sense of # tomorrow's change to the code not having accidentally # changed the behaviour. test_message = b"Message to be signed by crypt.testKeyMethods\n" test_keys = [ ('ed25519', 'AAAAC3NzaC1lZDI1NTE5AAAAIM7jupzef6CD0ps2JYxJp9IlwY49oorOseV5z5JFDFKn', 'AAAAIAf4/WRtypofgdNF2vbZOUFE1h4hvjw4tkGJZyOzI7c3', 255, b'0xf4d6e7f6f4479c23f0764ef43cea1711dbfe02aa2b5a32ff925c7c1fbf0f0db,0x27520c4592cf79e5b1ce8aa23d8ec125d2a7498c25369bd283a07fde9cbae3ce', [(0, 'AAAAC3NzaC1lZDI1NTE5AAAAQN73EqfyA4WneqDhgZ98TlRj9V5Wg8zCrMxTLJN1UtyfAnPUJDtfG/U0vOsP8PrnQxd41DDDnxrAXuqJz8rOagc=')]), ('ed448', 'AAAACXNzaC1lZDQ0OAAAADnRI0CQDym5IqUidLNDcSdHe54bYEwqjpjBlab8uKGoe6FRqqejha7+5U/VAHy7BmE23+ju26O9XgA=', 'AAAAObP9klqyiJSJsdFJf+xwZQdkbZGUqXE07K6e5plfRTGjYYkyWJFUNFH4jzIn9xH1TX9z9EGycPaXAA==', 448, b'0x4bf4a2b6586c60d8cdb52c2b45b897f6d2224bc37987489c0d70febb449e8c82964ed5785827be808e44d31dd31e6ff7c99f43e49f419928,0x5ebda3dbeee8df366106bb7c00d54fe5feae85a3a7aa51a17ba8a1b8fca695c1988e2a4c601b9e7b47277143b37422a522b9290f904023d1', [(0, 'AAAACXNzaC1lZDQ0OAAAAHLkSVioGMvLesZp3Tn+Z/sSK0Hl7RHsHP4q9flLzTpZG5h6JDH3VmZBEjTJ6iOLaa0v4FoNt0ng4wAB53WrlQC4h3iAusoGXnPMAKJLmqzplKOCi8HKXk8Xl8fsXbaoyhatv1OZpwJcffmh1x+x+LSgNQA=')]), ('p256', 'AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBHkYQ0sQoq5LbJI1VMWhw3bV43TSYi3WVpqIgKcBKK91TcFFlAMZgceOHQ0xAFYcSczIttLvFu+xkcLXrRd4N7Q=', 'AAAAIQCV/1VqiCsHZm/n+bq7lHEHlyy7KFgZBEbzqYaWtbx48Q==', 256, b'nistp256,0x7918434b10a2ae4b6c923554c5a1c376d5e374d2622dd6569a8880a70128af75,0x4dc14594031981c78e1d0d3100561c49ccc8b6d2ef16efb191c2d7ad177837b4', [(0, 'AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAABIAAAAIAryzHDGi/TcCnbdxZkIYR5EGR6SNYXr/HlQRF8le+/IAAAAIERfzn6eHuBbqWIop2qL8S7DWRB3lenN1iyL10xYQPKw')]), ('p384', 'AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBMYK8PUtfAlJwKaBTIGEuCzH0vqOMa4UbcjrBbTbkGVSUnfo+nuC80NCdj9JJMs1jvfF8GzKLc5z8H3nZyM741/BUFjV7rEHsQFDek4KyWvKkEgKiTlZid19VukNo1q2Hg==', 'AAAAMGsfTmdB4zHdbiQ2euTSdzM6UKEOnrVjMAWwHEYvmG5qUOcBnn62fJDRJy67L+QGdg==', 384, b'nistp384,0xc60af0f52d7c0949c0a6814c8184b82cc7d2fa8e31ae146dc8eb05b4db9065525277e8fa7b82f34342763f4924cb358e,0xf7c5f06cca2dce73f07de767233be35fc15058d5eeb107b101437a4e0ac96bca90480a89395989dd7d56e90da35ab61e', [(0, 'AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAABpAAAAMDmHrtXCADzLvkkWG/duBAHlf6B1mVvdt6F0uzXfsf8Yub8WXNUNVnYq6ovrWPzLggAAADEA9izzwoUuFcXYRJeKcRLZEGMmSDDPzUZb7oZR0UgD1jsMQXs8UfpO31Qur/FDSCRK')]), ('p521', 'AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAFrGthlKM152vu2Ghk+R7iO9/M6e+hTehNZ6+FBwof4HPkPB2/HHXj5+w5ynWyUrWiX5TI2riuJEIrJErcRH5LglADnJDX2w4yrKZ+wDHSz9lwh9p2F+B5R952es6gX3RJRkGA+qhKpKup8gKx78RMbleX8wgRtIu+4YMUnKb1edREiRg==', 'AAAAQgFh7VNJFUljWhhyAEiL0z+UPs/QggcMTd3Vv2aKDeBdCRl5di8r+BMm39L7bRzxRMEtW5NSKlDtE8MFEGdIE9khsw==', 521, b'nistp521,0x16b1ad86528cd79dafbb61a193e47b88ef7f33a7be8537a1359ebe141c287f81cf90f076fc71d78f9fb0e729d6c94ad6897e53236ae2b89108ac912b7111f92e094,0xe72435f6c38cab299fb00c74b3f65c21f69d85f81e51f79d9eb3a817dd125190603eaa12a92aea7c80ac7bf1131b95e5fcc2046d22efb860c52729bd5e75112246', [(0, 'AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAACMAAAAQgCLgvftvwM3CUaigrW0yzmCHoYjC6GLtO+6S91itqpgMEtWPNlaTZH6QQqkgscijWdXx98dDkQao/gcAKVmOZKPXgAAAEIB1PIrsDF1y6poJ/czqujB7NSUWt31v+c2t6UA8m2gTA1ARuVJ9XBGLMdceOTB00Hi9psC2RYFLpaWREOGCeDa6ow=')]), ('dsa', 'AAAAB3NzaC1kc3MAAABhAJyWZzjVddGdyc5JPu/WPrC07vKRAmlqO6TUi49ah96iRcM7/D1aRMVAdYBepQ2mf1fsQTmvoC9KgQa79nN3kHhz0voQBKOuKI1ZAodfVOgpP4xmcXgjaA73Vjz22n4newAAABUA6l7/vIveaiA33YYv+SKcKLQaA8cAAABgbErc8QLw/WDz7mhVRZrU+9x3Tfs68j3eW+B/d7Rz1ZCqMYDk7r/F8dlBdQlYhpQvhuSBgzoFa0+qPvSSxPmutgb94wNqhHlVIUb9ZOJNloNr2lXiPP//Wu51TxXAEvAAAAAAYQCcQ9mufXtZa5RyfwT4NuLivdsidP4HRoLXdlnppfFAbNdbhxE0Us8WZt+a/443bwKnYxgif8dgxv5UROnWTngWu0jbJHpaDcTc9lRyTeSUiZZK312s/Sl7qDk3/Du7RUI=', 'AAAAFGx3ft7G8AQzFsjhle7PWardUXh3', 768, b'0x9c966738d575d19dc9ce493eefd63eb0b4eef29102696a3ba4d48b8f5a87dea245c33bfc3d5a44c54075805ea50da67f57ec4139afa02f4a8106bbf67377907873d2fa1004a3ae288d5902875f54e8293f8c66717823680ef7563cf6da7e277b,0xea5effbc8bde6a2037dd862ff9229c28b41a03c7,0x6c4adcf102f0fd60f3ee6855459ad4fbdc774dfb3af23dde5be07f77b473d590aa3180e4eebfc5f1d94175095886942f86e481833a056b4faa3ef492c4f9aeb606fde3036a8479552146fd64e24d96836bda55e23cffff5aee754f15c012f000,0x9c43d9ae7d7b596b94727f04f836e2e2bddb2274fe074682d77659e9a5f1406cd75b87113452cf1666df9aff8e376f02a76318227fc760c6fe5444e9d64e7816bb48db247a5a0dc4dcf654724de49489964adf5dacfd297ba83937fc3bbb4542', [(0, 'AAAAB3NzaC1kc3MAAAAo0T2t6dr8Qr5DK2B0ETwUa3BhxMLPjLY0ZtlOACmP/kUt3JgByLv+3g==')]), ('rsa', 'AAAAB3NzaC1yc2EAAAABJQAAAGEA2ChX9+mQD/NULFkBrxLDI8d1PHgrInC2u11U4Grqu4oVzKvnFROo6DZeCu6sKhFJE5CnIL7evAthQ9hkXVHDhQ7xGVauzqyHGdIU4/pHRScAYWBv/PZOlNMrSoP/PP91', 'AAAAYCMNdgyGvWpez2EjMLSbQj0nQ3GW8jzvru3zdYwtA3hblNUU9QpWNxDmOMOApkwCzUgsdIPsBxctIeWT2h+v8sVOH+d66LCaNmNR0lp+dQ+iXM67hcGNuxJwRdMupD9ZbQAAADEA7XMrMAb4WuHaFafoTfGrf6Jhdy9Ozjqi1fStuld7Nj9JkoZluiL2dCwIrxqOjwU5AAAAMQDpC1gYiGVSPeDRILr2oxREtXWOsW+/ZZTfZNX7lvoufnp+qvwZPqvZnXQFHyZ8qB0AAAAwQE0wx8TPgcvRVEVv8Wt+o1NFlkJZayWD5hqpe/8AqUMZbqfg/aiso5mvecDLFgfV', 768, b'0x25,0xd82857f7e9900ff3542c5901af12c323c7753c782b2270b6bb5d54e06aeabb8a15ccabe71513a8e8365e0aeeac2a11491390a720bedebc0b6143d8645d51c3850ef11956aeceac8719d214e3fa4745270061606ffcf64e94d32b4a83ff3cff75', [(0, 'AAAAB3NzaC1yc2EAAABgrLSC4635RCsH1b3en58NqLsrH7PKRZyb3YmRasOyr8xIZMSlKZyxNg+kkn9OgBzbH9vChafzarfHyVwtJE2IMt3uwxTIWjwgwH19tc16k8YmNfDzujmB6OFOArmzKJgJ'), (2, 'AAAADHJzYS1zaGEyLTI1NgAAAGAJszr04BZlVBEdRLGOv1rTJwPiid/0I6/MycSH+noahvUH2wjrRhqDuv51F4nKYF5J9vBsEotTSrSF/cnLsliCdvVkEfmvhdcn/jx2LWF2OfjqETiYSc69Dde9UFmAPds='), (4, 'AAAADHJzYS1zaGEyLTUxMgAAAGBxfZ2m+WjvZ5YV5RFm0+w84CgHQ95EPndoAha0PCMc93AUHBmoHnezsJvEGuLovUm35w/0POmUNHI7HzM9PECwXrV0rO6N/HL/oFxJuDYmeqCpjMVmN8QXka+yxs2GEtA=')]), ] for alg, pubb64, privb64, bits, cachestr, siglist in test_keys: # Decode the blobs in the above test data. pubblob = base64decode(pubb64.encode('ASCII')) privblob = base64decode(privb64.encode('ASCII')) # Check the method that examines a public blob directly # and returns an integer showing the key size. self.assertEqual(ssh_key_public_bits(alg, pubblob), bits) # Make a public-only and a full ssh_key object. pubkey = ssh_key_new_pub(alg, pubblob) privkey = ssh_key_new_priv(alg, pubblob, privblob) # Test that they re-export the public and private key # blobs unchanged. self.assertEqual(ssh_key_public_blob(pubkey), pubblob) self.assertEqual(ssh_key_public_blob(privkey), pubblob) self.assertEqual(ssh_key_private_blob(privkey), privblob) # Round-trip through the OpenSSH wire encoding used by the # agent protocol (and the newer OpenSSH key file format), # and check the result still exports all the same blobs. osshblob = ssh_key_openssh_blob(privkey) privkey2 = ssh_key_new_priv_openssh(alg, osshblob) self.assertEqual(ssh_key_public_blob(privkey2), pubblob) self.assertEqual(ssh_key_private_blob(privkey2), privblob) self.assertEqual(ssh_key_openssh_blob(privkey2), osshblob) # Test that the string description used in the host key # cache is as expected. for key in [pubkey, privkey, privkey2]: self.assertEqual(ssh_key_cache_str(key), cachestr) # Now test signatures, separately for each provided flags # value. for flags, sigb64 in siglist: # Decode the signature blob from the test data. sigblob = base64decode(sigb64.encode('ASCII')) # Sign our test message, and check it produces exactly # the expected signature blob. # # We do this with both the original private key and # the one we round-tripped through OpenSSH wire # format, just in case that round trip made some kind # of a mess that didn't show up in the re-extraction # of the blobs. for key in [privkey, privkey2]: self.assertEqual(ssh_key_sign( key, test_message, flags), sigblob) if flags != 0: # Currently we only support _generating_ # signatures with flags != 0, not verifying them. continue # Check the signature verifies successfully, with all # three of the key objects we have. for key in [pubkey, privkey, privkey2]: self.assertTrue(ssh_key_verify(key, sigblob, test_message)) # A crude check that at least _something_ doesn't # verify successfully: flip a bit of the signature # and expect it to fail. # # We do this twice, at the 1/3 and 2/3 points along # the signature's length, so that in the case of # signatures in two parts (DSA-like) we try perturbing # both parts. Other than that, we don't do much to # make this a rigorous cryptographic test. for n, d in [(1,3),(2,3)]: sigbytes = list(sigblob) bit = 8 * len(sigbytes) * n // d sigbytes[bit // 8] ^= 1 << (bit % 8) badsig = bytes(sigbytes) for key in [pubkey, privkey, privkey2]: self.assertFalse(ssh_key_verify( key, badsig, test_message)) def testPPKLoadSave(self): # Stability test of PPK load/save functions. input_clear_key = b"""\ PuTTY-User-Key-File-3: ssh-ed25519 Encryption: none Comment: ed25519-key-20200105 Public-Lines: 2 AAAAC3NzaC1lZDI1NTE5AAAAIHJCszOHaI9X/yGLtjn22f0hO6VPMQDVtctkym6F JH1W Private-Lines: 1 AAAAIGvvIpl8jyqn8Xufkw6v3FnEGtXF3KWw55AP3/AGEBpY Private-MAC: 816c84093fc4877e8411b8e5139c5ce35d8387a2630ff087214911d67417a54d """ input_encrypted_key = b"""\ PuTTY-User-Key-File-3: ssh-ed25519 Encryption: aes256-cbc Comment: ed25519-key-20200105 Public-Lines: 2 AAAAC3NzaC1lZDI1NTE5AAAAIHJCszOHaI9X/yGLtjn22f0hO6VPMQDVtctkym6F JH1W Key-Derivation: Argon2id Argon2-Memory: 8192 Argon2-Passes: 13 Argon2-Parallelism: 1 Argon2-Salt: 37c3911bfefc8c1d11ec579627d2b3d9 Private-Lines: 1 amviz4sVUBN64jLO3gt4HGXJosUArghc4Soi7aVVLb2Tir5Baj0OQClorycuaPRd Private-MAC: 6f5e588e475e55434106ec2c3569695b03f423228b44993a9e97d52ffe7be5a8 """ algorithm = b'ssh-ed25519' comment = b'ed25519-key-20200105' pp = b'test-passphrase' public_blob = unhex( '0000000b7373682d65643235353139000000207242b33387688f57ff218bb639' 'f6d9fd213ba54f3100d5b5cb64ca6e85247d56') self.assertEqual(ppk_encrypted_s(input_clear_key), (False, comment)) self.assertEqual(ppk_encrypted_s(input_encrypted_key), (True, comment)) self.assertEqual(ppk_encrypted_s("not a key file"), (False, None)) self.assertEqual(ppk_loadpub_s(input_clear_key), (True, algorithm, public_blob, comment, None)) self.assertEqual(ppk_loadpub_s(input_encrypted_key), (True, algorithm, public_blob, comment, None)) self.assertEqual(ppk_loadpub_s("not a key file"), (False, None, b'', None, b'not a PuTTY SSH-2 private key')) k1, c, e = ppk_load_s(input_clear_key, None) self.assertEqual((c, e), (comment, None)) k2, c, e = ppk_load_s(input_encrypted_key, pp) self.assertEqual((c, e), (comment, None)) privblob = ssh_key_private_blob(k1) self.assertEqual(ssh_key_private_blob(k2), privblob) salt = unhex('37c3911bfefc8c1d11ec579627d2b3d9') with queued_specific_random_data(salt): self.assertEqual(ppk_save_sb(k1, comment, None, 3, 'id', 8192, 13, 1), input_clear_key) with queued_specific_random_data(salt): self.assertEqual(ppk_save_sb(k2, comment, None, 3, 'id', 8192, 13, 1), input_clear_key) with queued_specific_random_data(salt): self.assertEqual(ppk_save_sb(k1, comment, pp, 3, 'id', 8192, 13, 1), input_encrypted_key) with queued_specific_random_data(salt): self.assertEqual(ppk_save_sb(k2, comment, pp, 3, 'id', 8192, 13, 1), input_encrypted_key) # And check we can still handle v2 key files. v2_clear_key = b"""\ PuTTY-User-Key-File-2: ssh-ed25519 Encryption: none Comment: ed25519-key-20200105 Public-Lines: 2 AAAAC3NzaC1lZDI1NTE5AAAAIHJCszOHaI9X/yGLtjn22f0hO6VPMQDVtctkym6F JH1W Private-Lines: 1 AAAAIGvvIpl8jyqn8Xufkw6v3FnEGtXF3KWw55AP3/AGEBpY Private-MAC: 2a629acfcfbe28488a1ba9b6948c36406bc28422 """ v2_encrypted_key = b"""\ PuTTY-User-Key-File-2: ssh-ed25519 Encryption: aes256-cbc Comment: ed25519-key-20200105 Public-Lines: 2 AAAAC3NzaC1lZDI1NTE5AAAAIHJCszOHaI9X/yGLtjn22f0hO6VPMQDVtctkym6F JH1W Private-Lines: 1 4/jKlTgC652oa9HLVGrMjHZw7tj0sKRuZaJPOuLhGTvb25Jzpcqpbi+Uf+y+uo+Z Private-MAC: 5b1f6f4cc43eb0060d2c3e181bc0129343adba2b """ self.assertEqual(ppk_encrypted_s(v2_clear_key), (False, comment)) self.assertEqual(ppk_encrypted_s(v2_encrypted_key), (True, comment)) self.assertEqual(ppk_encrypted_s("not a key file"), (False, None)) self.assertEqual(ppk_loadpub_s(v2_clear_key), (True, algorithm, public_blob, comment, None)) self.assertEqual(ppk_loadpub_s(v2_encrypted_key), (True, algorithm, public_blob, comment, None)) self.assertEqual(ppk_loadpub_s("not a key file"), (False, None, b'', None, b'not a PuTTY SSH-2 private key')) k1, c, e = ppk_load_s(v2_clear_key, None) self.assertEqual((c, e), (comment, None)) k2, c, e = ppk_load_s(v2_encrypted_key, pp) self.assertEqual((c, e), (comment, None)) self.assertEqual(ssh_key_private_blob(k1), privblob) self.assertEqual(ssh_key_private_blob(k2), privblob) self.assertEqual(ppk_save_sb(k2, comment, None, 2, 'id', 8192, 13, 1), v2_clear_key) self.assertEqual(ppk_save_sb(k1, comment, pp, 2, 'id', 8192, 13, 1), v2_encrypted_key) def testRSA1LoadSave(self): # Stability test of SSH-1 RSA key-file load/save functions. input_clear_key = unhex( "5353482050524956415445204B45592046494C4520464F524D415420312E310A" "000000000000000002000200BB115A85B741E84E3D940E690DF96A0CBFDC07CA" "70E51DA8234D211DE77341CEF40C214CAA5DCF68BE2127447FD6C84CCB17D057" "A74F2365B9D84A78906AEB51000625000000107273612D6B65792D3230323030" "313036208E208E0200929EE615C6FC4E4B29585E52570F984F2E97B3144AA5BD" "4C6EB2130999BB339305A21FFFA79442462A8397AF8CAC395A3A3827DE10457A" "1F1B277ABFB8C069C100FF55B1CAD69B3BD9E42456CF28B1A4B98130AFCE08B2" "8BCFFF5FFFED76C5D51E9F0100C5DE76889C62B1090A770AE68F087A19AB5126" "E60DF87710093A2AD57B3380FB0100F2068AC47ECB33BF8F13DF402BABF35EE7" "26BD32F7564E51502DF5C8F4888B2300000000") input_encrypted_key = unhex( "5353482050524956415445204b45592046494c4520464f524d415420312e310a" "000300000000000002000200bb115a85b741e84e3d940e690df96a0cbfdc07ca" "70e51da8234d211de77341cef40c214caa5dcf68be2127447fd6c84ccb17d057" "a74f2365b9d84a78906aeb51000625000000107273612d6b65792d3230323030" "3130363377f926e811a5f044c52714801ecdcf9dd572ee0a193c4f67e87ab2ce" "4569d0c5776fd6028909ed8b6d663bef15d207d3ef6307e7e21dbec56e8d8b4e" "894ded34df891bb29bae6b2b74805ac80f7304926abf01ae314dd69c64240761" "34f15d50c99f7573252993530ec9c4d5016dd1f5191730cda31a5d95d362628b" "2a26f4bb21840d01c8360e4a6ce216c4686d25b8699d45cf361663bb185e2c5e" "652012a1e0f9d6d19afbb28506f7775bfd8129") comment = b'rsa-key-20200106' pp = b'test-passphrase' public_blob = unhex( "000002000006250200bb115a85b741e84e3d940e690df96a0cbfdc07ca70e51d" "a8234d211de77341cef40c214caa5dcf68be2127447fd6c84ccb17d057a74f23" "65b9d84a78906aeb51") self.assertEqual(rsa1_encrypted_s(input_clear_key), (False, comment)) self.assertEqual(rsa1_encrypted_s(input_encrypted_key), (True, comment)) self.assertEqual(rsa1_encrypted_s("not a key file"), (False, None)) self.assertEqual(rsa1_loadpub_s(input_clear_key), (1, public_blob, comment, None)) self.assertEqual(rsa1_loadpub_s(input_encrypted_key), (1, public_blob, comment, None)) k1 = rsa_new() status, c, e = rsa1_load_s(input_clear_key, k1, None) self.assertEqual((status, c, e), (1, comment, None)) k2 = rsa_new() status, c, e = rsa1_load_s(input_clear_key, k2, None) self.assertEqual((status, c, e), (1, comment, None)) with queued_specific_random_data(unhex("208e")): self.assertEqual(rsa1_save_sb(k1, comment, None), input_clear_key) with queued_specific_random_data(unhex("208e")): self.assertEqual(rsa1_save_sb(k2, comment, None), input_clear_key) with queued_specific_random_data(unhex("99f3")): self.assertEqual(rsa1_save_sb(k1, comment, pp), input_encrypted_key) with queued_specific_random_data(unhex("99f3")): self.assertEqual(rsa1_save_sb(k2, comment, pp), input_encrypted_key) class standard_test_vectors(MyTestBase): def testAES(self): def vector(cipher, key, plaintext, ciphertext): for suffix in "hw", "sw": c = ssh_cipher_new("{}_{}".format(cipher, suffix)) if c is None: return # skip test if HW AES not available ssh_cipher_setkey(c, key) # The AES test vectors are implicitly in ECB mode, # because they're testing the cipher primitive rather # than any mode layered on top of it. We fake this by # using PuTTY's CBC setting, and clearing the IV to # all zeroes before each operation. ssh_cipher_setiv(c, b'\x00' * 16) self.assertEqualBin( ssh_cipher_encrypt(c, plaintext), ciphertext) ssh_cipher_setiv(c, b'\x00' * 16) self.assertEqualBin( ssh_cipher_decrypt(c, ciphertext), plaintext) # The test vector from FIPS 197 appendix B. (This is also the # same key whose key setup phase is shown in detail in # appendix A.) vector('aes128_cbc', unhex('2b7e151628aed2a6abf7158809cf4f3c'), unhex('3243f6a8885a308d313198a2e0370734'), unhex('3925841d02dc09fbdc118597196a0b32')) # The test vectors from FIPS 197 appendix C: the key bytes go # 00 01 02 03 ... for as long as needed, and the plaintext # bytes go 00 11 22 33 ... FF. fullkey = struct.pack("B"*32, *range(32)) plaintext = struct.pack("B"*16, *[0x11*i for i in range(16)]) vector('aes128_cbc', fullkey[:16], plaintext, unhex('69c4e0d86a7b0430d8cdb78070b4c55a')) vector('aes192_cbc', fullkey[:24], plaintext, unhex('dda97ca4864cdfe06eaf70a0ec0d7191')) vector('aes256_cbc', fullkey[:32], plaintext, unhex('8ea2b7ca516745bfeafc49904b496089')) def testDES(self): c = ssh_cipher_new("des_cbc") def vector(key, plaintext, ciphertext): key = unhex(key) plaintext = unhex(plaintext) ciphertext = unhex(ciphertext) # Similarly to above, we fake DES ECB by using DES CBC and # resetting the IV to zero all the time ssh_cipher_setkey(c, key) ssh_cipher_setiv(c, b'\x00' * 8) self.assertEqualBin(ssh_cipher_encrypt(c, plaintext), ciphertext) ssh_cipher_setiv(c, b'\x00' * 8) self.assertEqualBin(ssh_cipher_decrypt(c, ciphertext), plaintext) # Source: FIPS SP PUB 500-20 # 'Initial permutation and expansion tests': key fixed at 8 # copies of the byte 01, but ciphertext and plaintext in turn # run through all possible values with exactly 1 bit set. # Expected plaintexts and ciphertexts (respectively) listed in # the arrays below. ipe_key = '01' * 8 ipe_plaintexts = [ '166B40B44ABA4BD6', '06E7EA22CE92708F', 'D2FD8867D50D2DFE', 'CC083F1E6D9E85F6', '5B711BC4CEEBF2EE', '0953E2258E8E90A1', 'E07C30D7E4E26E12', '2FBC291A570DB5C4', 'DD7C0BBD61FAFD54', '48221B9937748A23', 'E643D78090CA4207', '8405D1ABE24FB942', 'CE332329248F3228', '1D1CA853AE7C0C5F', '5D86CB23639DBEA9', '1029D55E880EC2D0', '8DD45A2DDF90796C', 'CAFFC6AC4542DE31', 'EA51D3975595B86B', '8B54536F2F3E64A8', '866ECEDD8072BB0E', '79E90DBC98F92CCA', 'AB6A20C0620D1C6F', '25EB5FC3F8CF0621', '4D49DB1532919C9F', '814EEB3B91D90726', '5E0905517BB59BCF', 'CA3A2B036DBC8502', 'FA0752B07D9C4AB8', 'B160E4680F6C696F', 'DF98C8276F54B04B', 'E943D7568AEC0C5C', 'AEB5F5EDE22D1A36', 'E428581186EC8F46', 'E1652C6B138C64A5', 'D106FF0BED5255D7', '9D64555A9A10B852', 'F02B263B328E2B60', '64FEED9C724C2FAF', '750D079407521363', 'FBE00A8A1EF8AD72', 'A484C3AD38DC9C19', '12A9F5817FF2D65D', 'E7FCE22557D23C97', '329A8ED523D71AEC', 'E19E275D846A1298', '889DE068A16F0BE6', '2B9F982F20037FA9', 'F356834379D165CD', 'ECBFE3BD3F591A5E', 'E6D5F82752AD63D1', 'ADD0CC8D6E5DEBA1', 'F15D0F286B65BD28', 'B8061B7ECD9A21E5', '424250B37C3DD951', 'D9031B0271BD5A0A', '0D9F279BA5D87260', '6CC5DEFAAF04512F', '55579380D77138EF', '20B9E767B2FB1456', '4BD388FF6CD81D4F', '2E8653104F3834EA', 'DD7F121CA5015619', '95F8A5E5DD31D900', ] ipe_ciphertexts = [ '166B40B44ABA4BD6', '06E7EA22CE92708F', 'D2FD8867D50D2DFE', 'CC083F1E6D9E85F6', '5B711BC4CEEBF2EE', '0953E2258E8E90A1', 'E07C30D7E4E26E12', '2FBC291A570DB5C4', 'DD7C0BBD61FAFD54', '48221B9937748A23', 'E643D78090CA4207', '8405D1ABE24FB942', 'CE332329248F3228', '1D1CA853AE7C0C5F', '5D86CB23639DBEA9', '1029D55E880EC2D0', '8DD45A2DDF90796C', 'CAFFC6AC4542DE31', 'EA51D3975595B86B', '8B54536F2F3E64A8', '866ECEDD8072BB0E', '79E90DBC98F92CCA', 'AB6A20C0620D1C6F', '25EB5FC3F8CF0621', '4D49DB1532919C9F', '814EEB3B91D90726', '5E0905517BB59BCF', 'CA3A2B036DBC8502', 'FA0752B07D9C4AB8', 'B160E4680F6C696F', 'DF98C8276F54B04B', 'E943D7568AEC0C5C', 'AEB5F5EDE22D1A36', 'E428581186EC8F46', 'E1652C6B138C64A5', 'D106FF0BED5255D7', '9D64555A9A10B852', 'F02B263B328E2B60', '64FEED9C724C2FAF', '750D079407521363', 'FBE00A8A1EF8AD72', 'A484C3AD38DC9C19', '12A9F5817FF2D65D', 'E7FCE22557D23C97', '329A8ED523D71AEC', 'E19E275D846A1298', '889DE068A16F0BE6', '2B9F982F20037FA9', 'F356834379D165CD', 'ECBFE3BD3F591A5E', 'E6D5F82752AD63D1', 'ADD0CC8D6E5DEBA1', 'F15D0F286B65BD28', 'B8061B7ECD9A21E5', '424250B37C3DD951', 'D9031B0271BD5A0A', '0D9F279BA5D87260', '6CC5DEFAAF04512F', '55579380D77138EF', '20B9E767B2FB1456', '4BD388FF6CD81D4F', '2E8653104F3834EA', 'DD7F121CA5015619', '95F8A5E5DD31D900', ] ipe_single_bits = ["{:016x}".format(1 << bit) for bit in range(64)] for plaintext, ciphertext in zip(ipe_plaintexts, ipe_single_bits): vector(ipe_key, plaintext, ciphertext) for plaintext, ciphertext in zip(ipe_single_bits, ipe_ciphertexts): vector(ipe_key, plaintext, ciphertext) # 'Key permutation tests': plaintext fixed at all zeroes, key # is a succession of tweaks of the previous key made by # replacing each 01 byte in turn with one containing a # different single set bit (e.g. 01 20 01 01 01 01 01 01). # Expected ciphertexts listed. kp_ciphertexts = [ '95A8D72813DAA94D', '0EEC1487DD8C26D5', '7AD16FFB79C45926', 'D3746294CA6A6CF3', '809F5F873C1FD761', 'C02FAFFEC989D1FC', '4615AA1D33E72F10', '2055123350C00858', 'DF3B99D6577397C8', '31FE17369B5288C9', 'DFDD3CC64DAE1642', '178C83CE2B399D94', '50F636324A9B7F80', 'A8468EE3BC18F06D', 'A2DC9E92FD3CDE92', 'CAC09F797D031287', '90BA680B22AEB525', 'CE7A24F350E280B6', '882BFF0AA01A0B87', '25610288924511C2', 'C71516C29C75D170', '5199C29A52C9F059', 'C22F0A294A71F29F', 'EE371483714C02EA', 'A81FBD448F9E522F', '4F644C92E192DFED', '1AFA9A66A6DF92AE', 'B3C1CC715CB879D8', '19D032E64AB0BD8B', '3CFAA7A7DC8720DC', 'B7265F7F447AC6F3', '9DB73B3C0D163F54', '8181B65BABF4A975', '93C9B64042EAA240', '5570530829705592', '8638809E878787A0', '41B9A79AF79AC208', '7A9BE42F2009A892', '29038D56BA6D2745', '5495C6ABF1E5DF51', 'AE13DBD561488933', '024D1FFA8904E389', 'D1399712F99BF02E', '14C1D7C1CFFEC79E', '1DE5279DAE3BED6F', 'E941A33F85501303', 'DA99DBBC9A03F379', 'B7FC92F91D8E92E9', 'AE8E5CAA3CA04E85', '9CC62DF43B6EED74', 'D863DBB5C59A91A0', 'A1AB2190545B91D7', '0875041E64C570F7', '5A594528BEBEF1CC', 'FCDB3291DE21F0C0', '869EFD7F9F265A09', ] kp_key_repl_bytes = ["{:02x}".format(0x80>>i) for i in range(7)] kp_keys = ['01'*j + b + '01'*(7-j) for j in range(8) for b in kp_key_repl_bytes] kp_plaintext = '0' * 16 for key, ciphertext in zip(kp_keys, kp_ciphertexts): vector(key, kp_plaintext, ciphertext) # 'Data permutation test': plaintext fixed at all zeroes, # pairs of key and expected ciphertext listed below. dp_keys_and_ciphertexts = [ '1046913489980131:88D55E54F54C97B4', '1007103489988020:0C0CC00C83EA48FD', '10071034C8980120:83BC8EF3A6570183', '1046103489988020:DF725DCAD94EA2E9', '1086911519190101:E652B53B550BE8B0', '1086911519580101:AF527120C485CBB0', '5107B01519580101:0F04CE393DB926D5', '1007B01519190101:C9F00FFC74079067', '3107915498080101:7CFD82A593252B4E', '3107919498080101:CB49A2F9E91363E3', '10079115B9080140:00B588BE70D23F56', '3107911598080140:406A9A6AB43399AE', '1007D01589980101:6CB773611DCA9ADA', '9107911589980101:67FD21C17DBB5D70', '9107D01589190101:9592CB4110430787', '1007D01598980120:A6B7FF68A318DDD3', '1007940498190101:4D102196C914CA16', '0107910491190401:2DFA9F4573594965', '0107910491190101:B46604816C0E0774', '0107940491190401:6E7E6221A4F34E87', '19079210981A0101:AA85E74643233199', '1007911998190801:2E5A19DB4D1962D6', '10079119981A0801:23A866A809D30894', '1007921098190101:D812D961F017D320', '100791159819010B:055605816E58608F', '1004801598190101:ABD88E8B1B7716F1', '1004801598190102:537AC95BE69DA1E1', '1004801598190108:AED0F6AE3C25CDD8', '1002911498100104:B3E35A5EE53E7B8D', '1002911598190104:61C79C71921A2EF8', '1002911598100201:E2F5728F0995013C', '1002911698100101:1AEAC39A61F0A464', ] dp_plaintext = '0' * 16 for key_and_ciphertext in dp_keys_and_ciphertexts: key, ciphertext = key_and_ciphertext.split(":") vector(key, dp_plaintext, ciphertext) # Tests intended to select every entry in every S-box. Full # arbitrary triples (key, plaintext, ciphertext). sb_complete_tests = [ '7CA110454A1A6E57:01A1D6D039776742:690F5B0D9A26939B', '0131D9619DC1376E:5CD54CA83DEF57DA:7A389D10354BD271', '07A1133E4A0B2686:0248D43806F67172:868EBB51CAB4599A', '3849674C2602319E:51454B582DDF440A:7178876E01F19B2A', '04B915BA43FEB5B6:42FD443059577FA2:AF37FB421F8C4095', '0113B970FD34F2CE:059B5E0851CF143A:86A560F10EC6D85B', '0170F175468FB5E6:0756D8E0774761D2:0CD3DA020021DC09', '43297FAD38E373FE:762514B829BF486A:EA676B2CB7DB2B7A', '07A7137045DA2A16:3BDD119049372802:DFD64A815CAF1A0F', '04689104C2FD3B2F:26955F6835AF609A:5C513C9C4886C088', '37D06BB516CB7546:164D5E404F275232:0A2AEEAE3FF4AB77', '1F08260D1AC2465E:6B056E18759F5CCA:EF1BF03E5DFA575A', '584023641ABA6176:004BD6EF09176062:88BF0DB6D70DEE56', '025816164629B007:480D39006EE762F2:A1F9915541020B56', '49793EBC79B3258F:437540C8698F3CFA:6FBF1CAFCFFD0556', '4FB05E1515AB73A7:072D43A077075292:2F22E49BAB7CA1AC', '49E95D6D4CA229BF:02FE55778117F12A:5A6B612CC26CCE4A', '018310DC409B26D6:1D9D5C5018F728C2:5F4C038ED12B2E41', '1C587F1C13924FEF:305532286D6F295A:63FAC0D034D9F793', ] for test in sb_complete_tests: key, plaintext, ciphertext = test.split(":") vector(key, plaintext, ciphertext) def testMD5(self): MD5 = lambda s: hash_str('md5', s) # The test vectors from RFC 1321 section A.5. self.assertEqualBin(MD5(""), unhex('d41d8cd98f00b204e9800998ecf8427e')) self.assertEqualBin(MD5("a"), unhex('0cc175b9c0f1b6a831c399e269772661')) self.assertEqualBin(MD5("abc"), unhex('900150983cd24fb0d6963f7d28e17f72')) self.assertEqualBin(MD5("message digest"), unhex('f96b697d7cb7938d525a2f31aaf161d0')) self.assertEqualBin(MD5("abcdefghijklmnopqrstuvwxyz"), unhex('c3fcd3d76192e4007dfb496cca67e13b')) self.assertEqualBin(MD5("ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz0123456789"), unhex('d174ab98d277d9f5a5611c2c9f419d9f')) self.assertEqualBin(MD5("1234567890123456789012345678901234567890" "1234567890123456789012345678901234567890"), unhex('57edf4a22be3c955ac49da2e2107b67a')) def testHmacMD5(self): # The test vectors from the RFC 2104 Appendix. self.assertEqualBin(mac_str('hmac_md5', unhex('0b'*16), "Hi There"), unhex('9294727a3638bb1c13f48ef8158bfc9d')) self.assertEqualBin(mac_str('hmac_md5', "Jefe", "what do ya want for nothing?"), unhex('750c783e6ab0b503eaa86e310a5db738')) self.assertEqualBin(mac_str('hmac_md5', unhex('aa'*16), unhex('dd'*50)), unhex('56be34521d144c88dbb8c733f0e8b3f6')) def testSHA1(self): for hashname in ['sha1_sw', 'sha1_hw']: if ssh_hash_new(hashname) is None: continue # skip testing of unavailable HW implementation # Test cases from RFC 6234 section 8.5, omitting the ones # whose input is not a multiple of 8 bits self.assertEqualBin(hash_str(hashname, "abc"), unhex( "a9993e364706816aba3e25717850c26c9cd0d89d")) self.assertEqualBin(hash_str(hashname, "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"), unhex("84983e441c3bd26ebaae4aa1f95129e5e54670f1")) self.assertEqualBin(hash_str_iter(hashname, ("a" * 1000 for _ in range(1000))), unhex( "34aa973cd4c4daa4f61eeb2bdbad27316534016f")) self.assertEqualBin(hash_str(hashname, "01234567012345670123456701234567" * 20), unhex( "dea356a2cddd90c7a7ecedc5ebb563934f460452")) self.assertEqualBin(hash_str(hashname, b"\x5e"), unhex( "5e6f80a34a9798cafc6a5db96cc57ba4c4db59c2")) self.assertEqualBin(hash_str(hashname, unhex("9a7dfdf1ecead06ed646aa55fe757146")), unhex( "82abff6605dbe1c17def12a394fa22a82b544a35")) self.assertEqualBin(hash_str(hashname, unhex( "f78f92141bcd170ae89b4fba15a1d59f" "3fd84d223c9251bdacbbae61d05ed115" "a06a7ce117b7beead24421ded9c32592" "bd57edeae39c39fa1fe8946a84d0cf1f" "7beead1713e2e0959897347f67c80b04" "00c209815d6b10a683836fd5562a56ca" "b1a28e81b6576654631cf16566b86e3b" "33a108b05307c00aff14a768ed735060" "6a0f85e6a91d396f5b5cbe577f9b3880" "7c7d523d6d792f6ebc24a4ecf2b3a427" "cdbbfb")), unhex( "cb0082c8f197d260991ba6a460e76e202bad27b3")) def testSHA256(self): for hashname in ['sha256_sw', 'sha256_hw']: if ssh_hash_new(hashname) is None: continue # skip testing of unavailable HW implementation # Test cases from RFC 6234 section 8.5, omitting the ones # whose input is not a multiple of 8 bits self.assertEqualBin(hash_str(hashname, "abc"), unhex("ba7816bf8f01cfea414140de5dae2223" "b00361a396177a9cb410ff61f20015ad")) self.assertEqualBin(hash_str(hashname, "abcdbcdecdefdefgefghfghighijhijk""ijkljklmklmnlmnomnopnopq"), unhex("248d6a61d20638b8e5c026930c3e6039" "a33ce45964ff2167f6ecedd419db06c1")) self.assertEqualBin( hash_str_iter(hashname, ("a" * 1000 for _ in range(1000))), unhex("cdc76e5c9914fb9281a1c7e284d73e67" "f1809a48a497200e046d39ccc7112cd0")) self.assertEqualBin( hash_str(hashname, "01234567012345670123456701234567" * 20), unhex("594847328451bdfa85056225462cc1d8" "67d877fb388df0ce35f25ab5562bfbb5")) self.assertEqualBin(hash_str(hashname, b"\x19"), unhex("68aa2e2ee5dff96e3355e6c7ee373e3d" "6a4e17f75f9518d843709c0c9bc3e3d4")) self.assertEqualBin( hash_str(hashname, unhex("e3d72570dcdd787ce3887ab2cd684652")), unhex("175ee69b02ba9b58e2b0a5fd13819cea" "573f3940a94f825128cf4209beabb4e8")) self.assertEqualBin(hash_str(hashname, unhex( "8326754e2277372f4fc12b20527afef0" "4d8a056971b11ad57123a7c137760000" "d7bef6f3c1f7a9083aa39d810db31077" "7dab8b1e7f02b84a26c773325f8b2374" "de7a4b5a58cb5c5cf35bcee6fb946e5b" "d694fa593a8beb3f9d6592ecedaa66ca" "82a29d0c51bcf9336230e5d784e4c0a4" "3f8d79a30a165cbabe452b774b9c7109" "a97d138f129228966f6c0adc106aad5a" "9fdd30825769b2c671af6759df28eb39" "3d54d6")), unhex( "97dbca7df46d62c8a422c941dd7e835b" "8ad3361763f7e9b2d95f4f0da6e1ccbc")) def testSHA384(self): for hashname in ['sha384_sw', 'sha384_hw']: if ssh_hash_new(hashname) is None: continue # skip testing of unavailable HW implementation # Test cases from RFC 6234 section 8.5, omitting the ones # whose input is not a multiple of 8 bits self.assertEqualBin(hash_str('sha384', "abc"), unhex( 'cb00753f45a35e8bb5a03d699ac65007272c32ab0eded163' '1a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7')) self.assertEqualBin(hash_str('sha384', "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmn" "hijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"), unhex('09330c33f71147e83d192fc782cd1b4753111b173b3b05d2' '2fa08086e3b0f712fcc7c71a557e2db966c3e9fa91746039')) self.assertEqualBin(hash_str_iter('sha384', ("a" * 1000 for _ in range(1000))), unhex( '9d0e1809716474cb086e834e310a4a1ced149e9c00f24852' '7972cec5704c2a5b07b8b3dc38ecc4ebae97ddd87f3d8985')) self.assertEqualBin(hash_str('sha384', "01234567012345670123456701234567" * 20), unhex( '2fc64a4f500ddb6828f6a3430b8dd72a368eb7f3a8322a70' 'bc84275b9c0b3ab00d27a5cc3c2d224aa6b61a0d79fb4596')) self.assertEqualBin(hash_str('sha384', b"\xB9"), unhex( 'bc8089a19007c0b14195f4ecc74094fec64f01f90929282c' '2fb392881578208ad466828b1c6c283d2722cf0ad1ab6938')) self.assertEqualBin(hash_str('sha384', unhex("a41c497779c0375ff10a7f4e08591739")), unhex( 'c9a68443a005812256b8ec76b00516f0dbb74fab26d66591' '3f194b6ffb0e91ea9967566b58109cbc675cc208e4c823f7')) self.assertEqualBin(hash_str('sha384', unhex( "399669e28f6b9c6dbcbb6912ec10ffcf74790349b7dc8fbe4a8e7b3b5621" "db0f3e7dc87f823264bbe40d1811c9ea2061e1c84ad10a23fac1727e7202" "fc3f5042e6bf58cba8a2746e1f64f9b9ea352c711507053cf4e5339d5286" "5f25cc22b5e87784a12fc961d66cb6e89573199a2ce6565cbdf13dca4038" "32cfcb0e8b7211e83af32a11ac17929ff1c073a51cc027aaedeff85aad7c" "2b7c5a803e2404d96d2a77357bda1a6daeed17151cb9bc5125a422e941de" "0ca0fc5011c23ecffefdd09676711cf3db0a3440720e1615c1f22fbc3c72" "1de521e1b99ba1bd5577408642147ed096")), unhex( '4f440db1e6edd2899fa335f09515aa025ee177a79f4b4aaf' '38e42b5c4de660f5de8fb2a5b2fbd2a3cbffd20cff1288c0')) def testSHA512(self): for hashname in ['sha512_sw', 'sha512_hw']: if ssh_hash_new(hashname) is None: continue # skip testing of unavailable HW implementation # Test cases from RFC 6234 section 8.5, omitting the ones # whose input is not a multiple of 8 bits self.assertEqualBin(hash_str('sha512', "abc"), unhex( 'ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55' 'd39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94f' 'a54ca49f')) self.assertEqualBin(hash_str('sha512', "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmn" "hijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu"), unhex('8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299' 'aeadb6889018501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26' '545e96e55b874be909')) self.assertEqualBin(hash_str_iter('sha512', ("a" * 1000 for _ in range(1000))), unhex( 'e718483d0ce769644e2e42c7bc15b4638e1f98b13b2044285632a803afa9' '73ebde0ff244877ea60a4cb0432ce577c31beb009c5c2c49aa2e4eadb217' 'ad8cc09b')) self.assertEqualBin(hash_str('sha512', "01234567012345670123456701234567" * 20), unhex( '89d05ba632c699c31231ded4ffc127d5a894dad412c0e024db872d1abd2b' 'a8141a0f85072a9be1e2aa04cf33c765cb510813a39cd5a84c4acaa64d3f' '3fb7bae9')) self.assertEqualBin(hash_str('sha512', b"\xD0"), unhex( '9992202938e882e73e20f6b69e68a0a7149090423d93c81bab3f21678d4a' 'ceeee50e4e8cafada4c85a54ea8306826c4ad6e74cece9631bfa8a549b4a' 'b3fbba15')) self.assertEqualBin(hash_str('sha512', unhex("8d4e3c0e3889191491816e9d98bff0a0")), unhex( 'cb0b67a4b8712cd73c9aabc0b199e9269b20844afb75acbdd1c153c98289' '24c3ddedaafe669c5fdd0bc66f630f6773988213eb1b16f517ad0de4b2f0' 'c95c90f8')) self.assertEqualBin(hash_str('sha512', unhex( "a55f20c411aad132807a502d65824e31a2305432aa3d06d3e282a8d84e0d" "e1de6974bf495469fc7f338f8054d58c26c49360c3e87af56523acf6d89d" "03e56ff2f868002bc3e431edc44df2f0223d4bb3b243586e1a7d92493669" "4fcbbaf88d9519e4eb50a644f8e4f95eb0ea95bc4465c8821aacd2fe15ab" "4981164bbb6dc32f969087a145b0d9cc9c67c22b763299419cc4128be9a0" "77b3ace634064e6d99283513dc06e7515d0d73132e9a0dc6d3b1f8b246f1" "a98a3fc72941b1e3bb2098e8bf16f268d64f0b0f4707fe1ea1a1791ba2f3" "c0c758e5f551863a96c949ad47d7fb40d2")), unhex( 'c665befb36da189d78822d10528cbf3b12b3eef726039909c1a16a270d48' '719377966b957a878e720584779a62825c18da26415e49a7176a894e7510' 'fd1451f5')) def testSHA3(self): # Source: all the SHA-3 test strings from # https://csrc.nist.gov/projects/cryptographic-standards-and-guidelines/example-values#aHashing # which are a multiple of 8 bits long. self.assertEqualBin(hash_str('sha3_224', ''), unhex("6b4e03423667dbb73b6e15454f0eb1abd4597f9a1b078e3f5b5a6bc7")) self.assertEqualBin(hash_str('sha3_224', unhex('a3')*200), unhex("9376816aba503f72f96ce7eb65ac095deee3be4bf9bbc2a1cb7e11e0")) self.assertEqualBin(hash_str('sha3_256', ''), unhex("a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a")) self.assertEqualBin(hash_str('sha3_256', unhex('a3')*200), unhex("79f38adec5c20307a98ef76e8324afbfd46cfd81b22e3973c65fa1bd9de31787")) self.assertEqualBin(hash_str('sha3_384', ''), unhex("0c63a75b845e4f7d01107d852e4c2485c51a50aaaa94fc61995e71bbee983a2ac3713831264adb47fb6bd1e058d5f004")) self.assertEqualBin(hash_str('sha3_384', unhex('a3')*200), unhex("1881de2ca7e41ef95dc4732b8f5f002b189cc1e42b74168ed1732649ce1dbcdd76197a31fd55ee989f2d7050dd473e8f")) self.assertEqualBin(hash_str('sha3_512', ''), unhex("a69f73cca23a9ac5c8b567dc185a756e97c982164fe25859e0d1dcc1475c80a615b2123af1f5f94c11e3e9402c3ac558f500199d95b6d3e301758586281dcd26")) self.assertEqualBin(hash_str('sha3_512', unhex('a3')*200), unhex("e76dfad22084a8b1467fcf2ffa58361bec7628edf5f3fdc0e4805dc48caeeca81b7c13c30adf52a3659584739a2df46be589c51ca1a4a8416df6545a1ce8ba00")) self.assertEqualBin(hash_str('shake256_114bytes', ''), unhex("46b9dd2b0ba88d13233b3feb743eeb243fcd52ea62b81b82b50c27646ed5762fd75dc4ddd8c0f200cb05019d67b592f6fc821c49479ab48640292eacb3b7c4be141e96616fb13957692cc7edd0b45ae3dc07223c8e92937bef84bc0eab862853349ec75546f58fb7c2775c38462c5010d846")) self.assertEqualBin(hash_str('shake256_114bytes', unhex('a3')*200), unhex("cd8a920ed141aa0407a22d59288652e9d9f1a7ee0c1e7c1ca699424da84a904d2d700caae7396ece96604440577da4f3aa22aeb8857f961c4cd8e06f0ae6610b1048a7f64e1074cd629e85ad7566048efc4fb500b486a3309a8f26724c0ed628001a1099422468de726f1061d99eb9e93604")) def testBLAKE2b(self): # Test case from RFC 7693 appendix A. self.assertEqualBin(hash_str('blake2b', b'abc'), unhex( "ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d1" "7d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923")) # A small number of test cases from the larger test vector # set, testing multiple blocks and the empty input. self.assertEqualBin(hash_str('blake2b', b''), unhex( "786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419" "d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce")) self.assertEqualBin(hash_str('blake2b', unhex('00')), unhex( "2fa3f686df876995167e7c2e5d74c4c7b6e48f8068fe0e44208344d480f7904c" "36963e44115fe3eb2a3ac8694c28bcb4f5a0f3276f2e79487d8219057a506e4b")) self.assertEqualBin(hash_str('blake2b', bytes(range(255))), unhex( "5b21c5fd8868367612474fa2e70e9cfa2201ffeee8fafab5797ad58fefa17c9b" "5b107da4a3db6320baaf2c8617d5a51df914ae88da3867c2d41f0cc14fa67928")) # You can get this test program to run the full version of the # test vectors by modifying the source temporarily to set this # variable to a pathname where you downloaded the JSON file # blake2-kat.json. blake2_test_vectors_path = None if blake2_test_vectors_path is not None: with open(blake2_test_vectors_path) as fh: vectors = json.load(fh) for vector in vectors: if vector['hash'] != 'blake2b': continue if len(vector['key']) != 0: continue h = blake2b_new_general(len(vector['out']) // 2) ssh_hash_update(h, unhex(vector['in'])) digest = ssh_hash_digest(h) self.assertEqualBin(digest, unhex(vector['out'])) def testArgon2(self): # draft-irtf-cfrg-argon2-12 section 5 self.assertEqualBin( argon2('d', 32, 3, 4, 32, b'\x01' * 32, b'\x02' * 16, b'\x03' * 8, b'\x04' * 12), unhex("512b391b6f1162975371d30919734294" "f868e3be3984f3c1a13a4db9fabe4acb")) self.assertEqualBin( argon2('i', 32, 3, 4, 32, b'\x01' * 32, b'\x02' * 16, b'\x03' * 8, b'\x04' * 12), unhex("c814d9d1dc7f37aa13f0d77f2494bda1" "c8de6b016dd388d29952a4c4672b6ce8")) self.assertEqualBin( argon2('id', 32, 3, 4, 32, b'\x01' * 32, b'\x02' * 16, b'\x03' * 8, b'\x04' * 12), unhex("0d640df58d78766c08c037a34a8b53c9" "d01ef0452d75b65eb52520e96b01e659")) def testHmacSHA(self): # Test cases from RFC 6234 section 8.5. def vector(key, message, s1=None, s256=None): if s1 is not None: self.assertEqualBin( mac_str('hmac_sha1', key, message), unhex(s1)) if s256 is not None: self.assertEqualBin( mac_str('hmac_sha256', key, message), unhex(s256)) vector( unhex("0b"*20), "Hi There", "b617318655057264e28bc0b6fb378c8ef146be00", "b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7") vector( "Jefe", "what do ya want for nothing?", "effcdf6ae5eb2fa2d27416d5f184df9c259a7c79", "5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843") vector( unhex("aa"*20), unhex('dd'*50), "125d7342b9ac11cd91a39af48aa17b4f63f175d3", "773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565FE") vector( unhex("0102030405060708090a0b0c0d0e0f10111213141516171819"), unhex("cd"*50), "4c9007f4026250c6bc8414f9bf50c86c2d7235da", "82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b") vector( unhex("aa"*80), "Test Using Larger Than Block-Size Key - Hash Key First", s1="aa4ae5e15272d00e95705637ce8a3b55ed402112") vector( unhex("aa"*131), "Test Using Larger Than Block-Size Key - Hash Key First", s256="60e431591ee0b67f0d8a26aacbf5b77f" "8e0bc6213728c5140546040f0ee37f54") vector( unhex("aa"*80), "Test Using Larger Than Block-Size Key and " "Larger Than One Block-Size Data", s1="e8e99d0f45237d786d6bbaa7965c7808bbff1a91") vector( unhex("aa"*131), "This is a test using a larger than block-size key and a " "larger than block-size data. The key needs to be hashed " "before being used by the HMAC algorithm.", s256="9B09FFA71B942FCB27635FBCD5B0E944BFDC63644F0713938A7F51535C3A35E2") def testEd25519(self): def vector(privkey, pubkey, message, signature): x, y = ecc_edwards_get_affine(eddsa_public( mp_from_bytes_le(privkey), 'ed25519')) self.assertEqual(int(y) | ((int(x) & 1) << 255), int(mp_from_bytes_le(pubkey))) pubblob = ssh_string(b"ssh-ed25519") + ssh_string(pubkey) privblob = ssh_string(privkey) sigblob = ssh_string(b"ssh-ed25519") + ssh_string(signature) pubkey = ssh_key_new_pub('ed25519', pubblob) self.assertTrue(ssh_key_verify(pubkey, sigblob, message)) privkey = ssh_key_new_priv('ed25519', pubblob, privblob) # By testing that the signature is exactly the one expected in # the test vector and not some equivalent one generated with a # different nonce, we're verifying in particular that we do # our deterministic nonce generation in the manner specified # by Ed25519. Getting that wrong would lead to no obvious # failure, but would surely turn out to be a bad idea sooner # or later... self.assertEqualBin(ssh_key_sign(privkey, message, 0), sigblob) # A cherry-picked example from DJB's test vector data at # https://ed25519.cr.yp.to/python/sign.input, which is too # large to copy into here in full. privkey = unhex( 'c89955e0f7741d905df0730b3dc2b0ce1a13134e44fef3d40d60c020ef19df77') pubkey = unhex( 'fdb30673402faf1c8033714f3517e47cc0f91fe70cf3836d6c23636e3fd2287c') message = unhex( '507c94c8820d2a5793cbf3442b3d71936f35fe3afef316') signature = unhex( '7ef66e5e86f2360848e0014e94880ae2920ad8a3185a46b35d1e07dea8fa8ae4' 'f6b843ba174d99fa7986654a0891c12a794455669375bf92af4cc2770b579e0c') vector(privkey, pubkey, message, signature) # You can get this test program to run the full version of # DJB's test vectors by modifying the source temporarily to # set this variable to a pathname where you downloaded the # file. ed25519_test_vector_path = None if ed25519_test_vector_path is not None: with open(ed25519_test_vector_path) as f: for line in iter(f.readline, ""): words = line.split(":") # DJB's test vector input format concatenates a # spare copy of the public key to the end of the # private key, and a spare copy of the message to # the end of the signature. Strip those off. privkey = unhex(words[0])[:32] pubkey = unhex(words[1]) message = unhex(words[2]) signature = unhex(words[3])[:64] vector(privkey, pubkey, message, signature) def testEd448(self): def vector(privkey, pubkey, message, signature): x, y = ecc_edwards_get_affine(eddsa_public( mp_from_bytes_le(privkey), 'ed448')) self.assertEqual(int(y) | ((int(x) & 1) << 455), int(mp_from_bytes_le(pubkey))) pubblob = ssh_string(b"ssh-ed448") + ssh_string(pubkey) privblob = ssh_string(privkey) sigblob = ssh_string(b"ssh-ed448") + ssh_string(signature) pubkey = ssh_key_new_pub('ed448', pubblob) self.assertTrue(ssh_key_verify(pubkey, sigblob, message)) privkey = ssh_key_new_priv('ed448', pubblob, privblob) # Deterministic signature check as in Ed25519 self.assertEqualBin(ssh_key_sign(privkey, message, 0), sigblob) # Source: RFC 8032 section 7.4 privkey = unhex('6c82a562cb808d10d632be89c8513ebf6c929f34ddfa8c9f63c9960ef6e348a3528c8a3fcc2f044e39a3fc5b94492f8f032e7549a20098f95b') pubkey = unhex('5fd7449b59b461fd2ce787ec616ad46a1da1342485a70e1f8a0ea75d80e96778edf124769b46c7061bd6783df1e50f6cd1fa1abeafe8256180') message = b'' signature = unhex('533a37f6bbe457251f023c0d88f976ae2dfb504a843e34d2074fd823d41a591f2b233f034f628281f2fd7a22ddd47d7828c59bd0a21bfd3980ff0d2028d4b18a9df63e006c5d1c2d345b925d8dc00b4104852db99ac5c7cdda8530a113a0f4dbb61149f05a7363268c71d95808ff2e652600') vector(privkey, pubkey, message, signature) privkey = unhex('c4eab05d357007c632f3dbb48489924d552b08fe0c353a0d4a1f00acda2c463afbea67c5e8d2877c5e3bc397a659949ef8021e954e0a12274e') pubkey = unhex('43ba28f430cdff456ae531545f7ecd0ac834a55d9358c0372bfa0c6c6798c0866aea01eb00742802b8438ea4cb82169c235160627b4c3a9480') message = unhex('03') signature = unhex('26b8f91727bd62897af15e41eb43c377efb9c610d48f2335cb0bd0087810f4352541b143c4b981b7e18f62de8ccdf633fc1bf037ab7cd779805e0dbcc0aae1cbcee1afb2e027df36bc04dcecbf154336c19f0af7e0a6472905e799f1953d2a0ff3348ab21aa4adafd1d234441cf807c03a00') vector(privkey, pubkey, message, signature) privkey = unhex('cd23d24f714274e744343237b93290f511f6425f98e64459ff203e8985083ffdf60500553abc0e05cd02184bdb89c4ccd67e187951267eb328') pubkey = unhex('dcea9e78f35a1bf3499a831b10b86c90aac01cd84b67a0109b55a36e9328b1e365fce161d71ce7131a543ea4cb5f7e9f1d8b00696447001400') message = unhex('0c3e544074ec63b0265e0c') signature = unhex('1f0a8888ce25e8d458a21130879b840a9089d999aaba039eaf3e3afa090a09d389dba82c4ff2ae8ac5cdfb7c55e94d5d961a29fe0109941e00b8dbdeea6d3b051068df7254c0cdc129cbe62db2dc957dbb47b51fd3f213fb8698f064774250a5028961c9bf8ffd973fe5d5c206492b140e00') vector(privkey, pubkey, message, signature) privkey = unhex('258cdd4ada32ed9c9ff54e63756ae582fb8fab2ac721f2c8e676a72768513d939f63dddb55609133f29adf86ec9929dccb52c1c5fd2ff7e21b') pubkey = unhex('3ba16da0c6f2cc1f30187740756f5e798d6bc5fc015d7c63cc9510ee3fd44adc24d8e968b6e46e6f94d19b945361726bd75e149ef09817f580') message = unhex('64a65f3cdedcdd66811e2915') signature = unhex('7eeeab7c4e50fb799b418ee5e3197ff6bf15d43a14c34389b59dd1a7b1b85b4ae90438aca634bea45e3a2695f1270f07fdcdf7c62b8efeaf00b45c2c96ba457eb1a8bf075a3db28e5c24f6b923ed4ad747c3c9e03c7079efb87cb110d3a99861e72003cbae6d6b8b827e4e6c143064ff3c00') vector(privkey, pubkey, message, signature) privkey = unhex('d65df341ad13e008567688baedda8e9dcdc17dc024974ea5b4227b6530e339bff21f99e68ca6968f3cca6dfe0fb9f4fab4fa135d5542ea3f01') pubkey = unhex('df9705f58edbab802c7f8363cfe5560ab1c6132c20a9f1dd163483a26f8ac53a39d6808bf4a1dfbd261b099bb03b3fb50906cb28bd8a081f00') message = unhex('bd0f6a3747cd561bdddf4640a332461a4a30a12a434cd0bf40d766d9c6d458e5512204a30c17d1f50b5079631f64eb3112182da3005835461113718d1a5ef944') signature = unhex('554bc2480860b49eab8532d2a533b7d578ef473eeb58c98bb2d0e1ce488a98b18dfde9b9b90775e67f47d4a1c3482058efc9f40d2ca033a0801b63d45b3b722ef552bad3b4ccb667da350192b61c508cf7b6b5adadc2c8d9a446ef003fb05cba5f30e88e36ec2703b349ca229c2670833900') vector(privkey, pubkey, message, signature) privkey = unhex('2ec5fe3c17045abdb136a5e6a913e32ab75ae68b53d2fc149b77e504132d37569b7e766ba74a19bd6162343a21c8590aa9cebca9014c636df5') pubkey = unhex('79756f014dcfe2079f5dd9e718be4171e2ef2486a08f25186f6bff43a9936b9bfe12402b08ae65798a3d81e22e9ec80e7690862ef3d4ed3a00') message = unhex('15777532b0bdd0d1389f636c5f6b9ba734c90af572877e2d272dd078aa1e567cfa80e12928bb542330e8409f3174504107ecd5efac61ae7504dabe2a602ede89e5cca6257a7c77e27a702b3ae39fc769fc54f2395ae6a1178cab4738e543072fc1c177fe71e92e25bf03e4ecb72f47b64d0465aaea4c7fad372536c8ba516a6039c3c2a39f0e4d832be432dfa9a706a6e5c7e19f397964ca4258002f7c0541b590316dbc5622b6b2a6fe7a4abffd96105eca76ea7b98816af0748c10df048ce012d901015a51f189f3888145c03650aa23ce894c3bd889e030d565071c59f409a9981b51878fd6fc110624dcbcde0bf7a69ccce38fabdf86f3bef6044819de11') signature = unhex('c650ddbb0601c19ca11439e1640dd931f43c518ea5bea70d3dcde5f4191fe53f00cf966546b72bcc7d58be2b9badef28743954e3a44a23f880e8d4f1cfce2d7a61452d26da05896f0a50da66a239a8a188b6d825b3305ad77b73fbac0836ecc60987fd08527c1a8e80d5823e65cafe2a3d00') vector(privkey, pubkey, message, signature) privkey = unhex('872d093780f5d3730df7c212664b37b8a0f24f56810daa8382cd4fa3f77634ec44dc54f1c2ed9bea86fafb7632d8be199ea165f5ad55dd9ce8') pubkey = unhex('a81b2e8a70a5ac94ffdbcc9badfc3feb0801f258578bb114ad44ece1ec0e799da08effb81c5d685c0c56f64eecaef8cdf11cc38737838cf400') message = unhex('6ddf802e1aae4986935f7f981ba3f0351d6273c0a0c22c9c0e8339168e675412a3debfaf435ed651558007db4384b650fcc07e3b586a27a4f7a00ac8a6fec2cd86ae4bf1570c41e6a40c931db27b2faa15a8cedd52cff7362c4e6e23daec0fbc3a79b6806e316efcc7b68119bf46bc76a26067a53f296dafdbdc11c77f7777e972660cf4b6a9b369a6665f02e0cc9b6edfad136b4fabe723d2813db3136cfde9b6d044322fee2947952e031b73ab5c603349b307bdc27bc6cb8b8bbd7bd323219b8033a581b59eadebb09b3c4f3d2277d4f0343624acc817804728b25ab797172b4c5c21a22f9c7839d64300232eb66e53f31c723fa37fe387c7d3e50bdf9813a30e5bb12cf4cd930c40cfb4e1fc622592a49588794494d56d24ea4b40c89fc0596cc9ebb961c8cb10adde976a5d602b1c3f85b9b9a001ed3c6a4d3b1437f52096cd1956d042a597d561a596ecd3d1735a8d570ea0ec27225a2c4aaff26306d1526c1af3ca6d9cf5a2c98f47e1c46db9a33234cfd4d81f2c98538a09ebe76998d0d8fd25997c7d255c6d66ece6fa56f11144950f027795e653008f4bd7ca2dee85d8e90f3dc315130ce2a00375a318c7c3d97be2c8ce5b6db41a6254ff264fa6155baee3b0773c0f497c573f19bb4f4240281f0b1f4f7be857a4e59d416c06b4c50fa09e1810ddc6b1467baeac5a3668d11b6ecaa901440016f389f80acc4db977025e7f5924388c7e340a732e554440e76570f8dd71b7d640b3450d1fd5f0410a18f9a3494f707c717b79b4bf75c98400b096b21653b5d217cf3565c9597456f70703497a078763829bc01bb1cbc8fa04eadc9a6e3f6699587a9e75c94e5bab0036e0b2e711392cff0047d0d6b05bd2a588bc109718954259f1d86678a579a3120f19cfb2963f177aeb70f2d4844826262e51b80271272068ef5b3856fa8535aa2a88b2d41f2a0e2fda7624c2850272ac4a2f561f8f2f7a318bfd5caf9696149e4ac824ad3460538fdc25421beec2cc6818162d06bbed0c40a387192349db67a118bada6cd5ab0140ee273204f628aad1c135f770279a651e24d8c14d75a6059d76b96a6fd857def5e0b354b27ab937a5815d16b5fae407ff18222c6d1ed263be68c95f32d908bd895cd76207ae726487567f9a67dad79abec316f683b17f2d02bf07e0ac8b5bc6162cf94697b3c27cd1fea49b27f23ba2901871962506520c392da8b6ad0d99f7013fbc06c2c17a569500c8a7696481c1cd33e9b14e40b82e79a5f5db82571ba97bae3ad3e0479515bb0e2b0f3bfcd1fd33034efc6245eddd7ee2086ddae2600d8ca73e214e8c2b0bdb2b047c6a464a562ed77b73d2d841c4b34973551257713b753632efba348169abc90a68f42611a40126d7cb21b58695568186f7e569d2ff0f9e745d0487dd2eb997cafc5abf9dd102e62ff66cba87') signature = unhex('e301345a41a39a4d72fff8df69c98075a0cc082b802fc9b2b6bc503f926b65bddf7f4c8f1cb49f6396afc8a70abe6d8aef0db478d4c6b2970076c6a0484fe76d76b3a97625d79f1ce240e7c576750d295528286f719b413de9ada3e8eb78ed573603ce30d8bb761785dc30dbc320869e1a00') vector(privkey, pubkey, message, signature) def testMontgomeryKex(self): # Unidirectional tests, consisting of an input random number # string and peer public value, giving the expected output # shared key. Source: RFC 7748 section 5.2. rfc7748s5_2 = [ ('curve25519', 'a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4', 'e6db6867583030db3594c1a424b15f7c726624ec26b3353b10a903a6d0ab1c4c', 0xc3da55379de9c6908e94ea4df28d084f32eccf03491c71f754b4075577a28552), ('curve25519', '4b66e9d4d1b4673c5ad22691957d6af5c11b6421e0ea01d42ca4169e7918ba0d', 'e5210f12786811d3f4b7959d0538ae2c31dbe7106fc03c3efc4cd549c715a493', 0x95cbde9476e8907d7aade45cb4b873f88b595a68799fa152e6f8f7647aac7957), ('curve448', '3d262fddf9ec8e88495266fea19a34d28882acef045104d0d1aae121700a779c984c24f8cdd78fbff44943eba368f54b29259a4f1c600ad3', '06fce640fa3487bfda5f6cf2d5263f8aad88334cbd07437f020f08f9814dc031ddbdc38c19c6da2583fa5429db94ada18aa7a7fb4ef8a086', 0xce3e4ff95a60dc6697da1db1d85e6afbdf79b50a2412d7546d5f239fe14fbaadeb445fc66a01b0779d98223961111e21766282f73dd96b6f), ('curve448', '203d494428b8399352665ddca42f9de8fef600908e0d461cb021f8c538345dd77c3e4806e25f46d3315c44e0a5b4371282dd2c8d5be3095f', '0fbcc2f993cd56d3305b0b7d9e55d4c1a8fb5dbb52f8e9a1e9b6201b165d015894e56c4d3570bee52fe205e28a78b91cdfbde71ce8d157db', 0x884a02576239ff7a2f2f63b2db6a9ff37047ac13568e1e30fe63c4a7ad1b3ee3a5700df34321d62077e63633c575c1c954514e99da7c179d), ] for method, priv, pub, expected in rfc7748s5_2: with queued_specific_random_data(unhex(priv)): ecdh = ssh_ecdhkex_newkey(method) key = ssh_ecdhkex_getkey(ecdh, unhex(pub)) self.assertEqual(int(key), expected) # Bidirectional tests, consisting of the input random number # strings for both parties, and the expected public values and # shared key. Source: RFC 7748 section 6. rfc7748s6 = [ ('curve25519', # section 6.1 '77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a', '8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a', '5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb', 'de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f', 0x4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742), ('curve448', # section 6.2 '9a8f4925d1519f5775cf46b04b5800d4ee9ee8bae8bc5565d498c28dd9c9baf574a9419744897391006382a6f127ab1d9ac2d8c0a598726b', '9b08f7cc31b7e3e67d22d5aea121074a273bd2b83de09c63faa73d2c22c5d9bbc836647241d953d40c5b12da88120d53177f80e532c41fa0', '1c306a7ac2a0e2e0990b294470cba339e6453772b075811d8fad0d1d6927c120bb5ee8972b0d3e21374c9c921b09d1b0366f10b65173992d', '3eb7a829b0cd20f5bcfc0b599b6feccf6da4627107bdb0d4f345b43027d8b972fc3e34fb4232a13ca706dcb57aec3dae07bdc1c67bf33609', 0x07fff4181ac6cc95ec1c16a94a0f74d12da232ce40a77552281d282bb60c0b56fd2464c335543936521c24403085d59a449a5037514a879d), ] for method, apriv, apub, bpriv, bpub, expected in rfc7748s6: with queued_specific_random_data(unhex(apriv)): alice = ssh_ecdhkex_newkey(method) with queued_specific_random_data(unhex(bpriv)): bob = ssh_ecdhkex_newkey(method) self.assertEqualBin(ssh_ecdhkex_getpublic(alice), unhex(apub)) self.assertEqualBin(ssh_ecdhkex_getpublic(bob), unhex(bpub)) akey = ssh_ecdhkex_getkey(alice, unhex(bpub)) bkey = ssh_ecdhkex_getkey(bob, unhex(apub)) self.assertEqual(int(akey), expected) self.assertEqual(int(bkey), expected) def testCRC32(self): self.assertEqual(crc32_rfc1662("123456789"), 0xCBF43926) self.assertEqual(crc32_ssh1("123456789"), 0x2DFD2D88) # Source: # http://reveng.sourceforge.net/crc-catalogue/17plus.htm#crc.cat.crc-32-iso-hdlc # which collected these from various sources. reveng_tests = [ '000000001CDF4421', 'F20183779DAB24', '0FAA005587B2C9B6', '00FF55111262A032', '332255AABBCCDDEEFF3D86AEB0', '926B559BA2DE9C', 'FFFFFFFFFFFFFFFF', 'C008300028CFE9521D3B08EA449900E808EA449900E8300102007E649416', '6173640ACEDE2D15', ] for vec in map(unhex, reveng_tests): # Each of these test vectors can be read two ways. One # interpretation is that the last four bytes are the # little-endian encoding of the CRC of the rest. (Because # that's how the CRC is attached to a string at the # sending end.) # # The other interpretation is that if you CRC the whole # string, _including_ the final four bytes, you expect to # get the same value for any correct string (because the # little-endian encoding matches the way the rest of the # string was interpreted as a polynomial in the first # place). That's how a receiver is intended to check # things. # # The expected output value is listed in RFC 1662, and in # the reveng.sourceforge.net catalogue, as 0xDEBB20E3. But # that's because their checking procedure omits the final # complement step that the construction procedure # includes. Our crc32_rfc1662 function does do the final # complement, so we expect the bitwise NOT of that value, # namely 0x2144DF1C. expected = struct.unpack("= (3,0), "This is Python 3 code" def bitor(x, y): return x | y def split_words(val, width=32): mask = ((1<> width), mask & val def combine_words(hi, lo, width=32): mask = ((1<> (shift % width)) | (val << (-shift % width))) def rol(val, shift, width=32): return ror(val, -shift, width) def bitselect(bits, val): # bits[i] gives the input bit index of the output bit at index i return functools.reduce( bitor, ((1 & (val >> inbit)) << outbit for outbit, inbit in enumerate(bits))) def SB(hexstring): return [int(c,16) for c in hexstring] def debug(string): sys.stdout.write(string + "\n") class DESBase(object): def __init__(self): # Automatically construct FP by inverting IP self.FP = [None] * 64 for i, j in enumerate(self.IP): self.FP[j] = i def f(self, word, key_material): debug("computing f({:08x}, {}):".format( word, " ".join(map("{:02x}".format,key_material)))) sbox_inputs = [0x3F & (ror(word, offset) ^ key_element) for offset, key_element in zip(self.sbox_index_offsets, key_material)] sbox_outputs = [sbox[sbox_input] for sbox, sbox_input in zip(self.sboxes, sbox_inputs)] debug(" S-boxes: {} -> {}".format( " ".join(map("{:02x}".format,sbox_inputs)), " ".join(map("{:x}".format,sbox_outputs)))) word = functools.reduce( bitor, (v << (4*i) for i,v in enumerate(sbox_outputs))) debug(" S output = {:08x}".format(word)) word = bitselect(self.P, word) debug(" P output = {:08x}".format(word)) return word def cipher(self, integer, key_schedule): L, R = split_words(bitselect(self.IP, integer)) debug("cipher start {:016x} -> {:08x} {:08x}".format(integer, L, R)) for roundIndex, key_material in enumerate(key_schedule): L, R = R, L ^ self.f(R, key_material) debug("after round {:2d}: {:08x} {:08x}".format(roundIndex, L, R)) output = bitselect(self.FP, combine_words(R, L)) debug("cipher end {:08x} {:08x} -> {:016x}".format(R, L, output)) return output def encipher(self, integer): return self.cipher(integer, self.key_schedule) def decipher(self, integer): return self.cipher(integer, list(reversed(self.key_schedule))) def setkey(self, key): self.key_schedule = [] CD = bitselect(self.PC1, key) debug("initial CD = {:014x}".format(CD)) for roundIndex, shift in enumerate(self.key_setup_shifts): C, D = split_words(CD, 28) C = rol(C, shift, 28) D = rol(D, shift, 28) CD = combine_words(C, D, 28) self.key_schedule.append( [bitselect(bits, CD) for bits in self.PC2]) debug("CD[{:d}] = {:014x} -> {}):".format( roundIndex, CD, " ".join( map("{:02x}".format,self.key_schedule[-1])))) # The PC1 permutation is fixed and arbitrary PC1 = [ 0x3c, 0x34, 0x2c, 0x24, 0x3b, 0x33, 0x2b, 0x23, 0x1b, 0x13, 0x0b, 0x03, 0x3a, 0x32, 0x2a, 0x22, 0x1a, 0x12, 0x0a, 0x02, 0x39, 0x31, 0x29, 0x21, 0x19, 0x11, 0x09, 0x01, 0x1c, 0x14, 0x0c, 0x04, 0x3d, 0x35, 0x2d, 0x25, 0x1d, 0x15, 0x0d, 0x05, 0x3e, 0x36, 0x2e, 0x26, 0x1e, 0x16, 0x0e, 0x06, 0x3f, 0x37, 0x2f, 0x27, 0x1f, 0x17, 0x0f, 0x07, ] PC2 = [ [0x18, 0x1b, 0x14, 0x06, 0x0e, 0x0a], [0x03, 0x16, 0x00, 0x11, 0x07, 0x0c], [0x08, 0x17, 0x0b, 0x05, 0x10, 0x1a], [0x01, 0x09, 0x13, 0x19, 0x04, 0x0f], [0x36, 0x2b, 0x24, 0x1d, 0x31, 0x28], [0x30, 0x1e, 0x34, 0x2c, 0x25, 0x21], [0x2e, 0x23, 0x32, 0x29, 0x1c, 0x35], [0x33, 0x37, 0x20, 0x2d, 0x27, 0x2a], ] key_setup_shifts = [1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1] # IP is better understood as a permutation and flipping of the # bits _in the index of each actual bit_ than as a long list of # individual indices IP = [bitselect([5,3,4,0,1,2], index ^ 0x27) for index in range(64)] class DES(DESBase): sboxes = [ SB('d12f8d486af3b714ac9536eb500ec97272b14e1794cae82d0f6ca9d0f335568b'), SB('4db02be7f40981da3ec3957c52af6816164bbdd8c1347ae7a9f5608f0e52932c'), SB('ca1fa4f2972c698506d13d4ee07b53b894e3f25c2985cf3a7b0e41a716d0b86d'), SB('2ecb421c74a7bd6185503ffad309e8964b281cb7a1de728df69fc0596a3405e3'), SB('7dd8eb35066f90a31427825cb1ca4ef9a36f9006cab17dd8f91435eb5c27824e'), SB('ad0790e96334f65a12d8c57ebc4b2f81d16a4d9086f93807b41f2ec35ba5e27c'), SB('f31d84e76fb2384e9c7021dac6095ba50de87ab1a34fd4125b86c76c90352ef9'), SB('e04fd7142ef2bd813aa66ccb599503784f1ce882d46921b7f5cb937e3aa0560d'), ] P = [ 0x07, 0x1c, 0x15, 0x0a, 0x1a, 0x02, 0x13, 0x0d, 0x17, 0x1d, 0x05, 0x00, 0x12, 0x08, 0x18, 0x1e, 0x16, 0x01, 0x0e, 0x1b, 0x06, 0x09, 0x11, 0x1f, 0x0f, 0x04, 0x14, 0x03, 0x0b, 0x0c, 0x19, 0x10, ] sbox_index_offsets = [4*i-1 for i in range(8)] class SGTDES(DESBase): sboxes = [ SB('e41f8e2839f5d7429ac653bd600bac7171d42b47c2a9b81e0f3a9ce0f556638d'), SB('4db02be7f40981da3ec3957c52af6816164bbdd8c1347ae7a9f5608f0e52932c'), SB('c52f58f16b1c964a09e23e8dd0b7a37468d3f1ac164acf35b70d825b29e0749e'), SB('4ead241a72c7db6183305ffcb509e8962d481ad7c1be748bf69fa0396c5203e5'), SB('edd1b76c0aaf5036482e12c974938bf536af500a9374edd1f5486cb7c92e128b'), SB('9e07a0da5334f56921e8c67dbc4b1f82e2594ea085fa3807b42f1dc36b96d17c'), SB('f31d84e76fb2384e9c7021dac6095ba50de87ab1a34fd4125b86c76c90352ef9'), SB('d08feb281df17e4235599cc7a66a03b48f2cd441e896127bfac763bd3550a90e'), ] P = [ 0x1d, 0x14, 0x0b, 0x1a, 0x01, 0x10, 0x0e, 0x17, 0x1c, 0x05, 0x02, 0x13, 0x09, 0x18, 0x1f, 0x16, 0x00, 0x0d, 0x1b, 0x06, 0x08, 0x11, 0x1e, 0x0f, 0x04, 0x15, 0x03, 0x0a, 0x0c, 0x19, 0x12, 0x07 ] sbox_index_offsets = [4*i-2 for i in range(8)] IP = [DES.IP[i ^ ((i^(i+1)) & 0x1F)] for i in range(64)] def main(): hexstr = lambda s: int(s, 16) parser = argparse.ArgumentParser(description='') group = parser.add_mutually_exclusive_group() group.add_argument("--des", action="store_const", dest="cipher", const=DES, help="Use the official DES definition.") group.add_argument("--sgtdes", action="store_const", dest="cipher", const=SGTDES, help="Use the equivalent SGT-DES.") group = parser.add_mutually_exclusive_group(required=True) group.add_argument("--encipher", "-e", action="store_const", dest="method", const="encipher", help="Encipher.") group.add_argument("--decipher", "-d", action="store_const", dest="method", const="decipher", help="Decipher.") parser.add_argument("key", type=hexstr, help="Cipher key (hex, 8 bytes, " "low bit of each byte unused).") parser.add_argument("input", type=hexstr, help="Cipher input (hex, 8 bytes).") parser.set_defaults(const=SGTDES) # main purpose is to debug sshdes.c args = parser.parse_args() des = args.cipher() des.setkey(args.key) method = getattr(des, args.method) output = method(args.input) sys.stdout.write("{} with key {:016x}: {:016x} -> {:016x}\n".format( args.method, args.key, args.input, output)) if __name__ == '__main__': main() putty-0.76/test/display.txt0000644000175000017500000000142414072266312012745 00000000000000Test of all features involved in do_text() ========================================== Reverse video + red on yellow:  bing!  Yellow on red should look the same:  bong!  Basic attrs, combining chars, both widths: Bold blink under strike [Λ̊][ãƒ][text] Wide char should be off by 1 narrow char: Bold blink under strike [Λ̊][ãƒ][text] Double width, double height. Should be red top, magenta bottom, blue DW only: #3Bold blink under strike [Λ̊][ãƒ][text] #4Bold blink under strike [Λ̊][ãƒ][text] #6Bold blink under strike [Λ̊][ãƒ][text] putty-0.76/test/eccref.py0000644000175000017500000003330414072266312012342 00000000000000import sys import numbers import itertools assert sys.version_info[:2] >= (3,0), "This is Python 3 code" from numbertheory import * class AffinePoint(object): """Base class for points on an elliptic curve.""" def __init__(self, curve, *args): self.curve = curve if len(args) == 0: self.infinite = True self.x = self.y = None else: assert len(args) == 2 self.infinite = False self.x = ModP(self.curve.p, args[0]) self.y = ModP(self.curve.p, args[1]) self.check_equation() def __neg__(self): if self.infinite: return self return type(self)(self.curve, self.x, -self.y) def __mul__(self, rhs): if not isinstance(rhs, numbers.Integral): raise ValueError("Elliptic curve points can only be multiplied by integers") P = self if rhs < 0: rhs = -rhs P = -P toret = self.curve.point() n = 1 nP = P while rhs != 0: if rhs & n: rhs -= n toret += nP n += n nP += nP return toret def __rmul__(self, rhs): return self * rhs def __sub__(self, rhs): return self + (-rhs) def __rsub__(self, rhs): return (-self) + rhs def __str__(self): if self.infinite: return "inf" else: return "({},{})".format(self.x, self.y) def __repr__(self): if self.infinite: args = "" else: args = ", {}, {}".format(self.x, self.y) return "{}.Point({}{})".format(type(self.curve).__name__, self.curve, args) def __eq__(self, rhs): if self.infinite or rhs.infinite: return self.infinite and rhs.infinite return (self.x, self.y) == (rhs.x, rhs.y) def __ne__(self, rhs): return not (self == rhs) def __lt__(self, rhs): raise ValueError("Elliptic curve points have no ordering") def __le__(self, rhs): raise ValueError("Elliptic curve points have no ordering") def __gt__(self, rhs): raise ValueError("Elliptic curve points have no ordering") def __ge__(self, rhs): raise ValueError("Elliptic curve points have no ordering") def __hash__(self): if self.infinite: return hash((True,)) else: return hash((False, self.x, self.y)) class CurveBase(object): def point(self, *args): return self.Point(self, *args) class WeierstrassCurve(CurveBase): class Point(AffinePoint): def check_equation(self): assert (self.y*self.y == self.x*self.x*self.x + self.curve.a*self.x + self.curve.b) def __add__(self, rhs): if self.infinite: return rhs if rhs.infinite: return self if self.x == rhs.x and self.y != rhs.y: return self.curve.point() x1, x2, y1, y2 = self.x, rhs.x, self.y, rhs.y xdiff = x2-x1 if xdiff != 0: slope = (y2-y1) / xdiff else: assert y1 == y2 slope = (3*x1*x1 + self.curve.a) / (2*y1) xp = slope*slope - x1 - x2 yp = -(y1 + slope * (xp-x1)) return self.curve.point(xp, yp) def __init__(self, p, a, b): self.p = p self.a = ModP(p, a) self.b = ModP(p, b) def cpoint(self, x, yparity=0): if not hasattr(self, 'sqrtmodp'): self.sqrtmodp = RootModP(2, self.p) rhs = x**3 + self.a.n * x + self.b.n y = self.sqrtmodp.root(rhs) if (y - yparity) % 2: y = -y return self.point(x, y) def __repr__(self): return "{}(0x{:x}, {}, {})".format( type(self).__name__, self.p, self.a, self.b) class MontgomeryCurve(CurveBase): class Point(AffinePoint): def check_equation(self): assert (self.curve.b*self.y*self.y == self.x*self.x*self.x + self.curve.a*self.x*self.x + self.x) def __add__(self, rhs): if self.infinite: return rhs if rhs.infinite: return self if self.x == rhs.x and self.y != rhs.y: return self.curve.point() x1, x2, y1, y2 = self.x, rhs.x, self.y, rhs.y xdiff = x2-x1 if xdiff != 0: slope = (y2-y1) / xdiff elif y1 != 0: assert y1 == y2 slope = (3*x1*x1 + 2*self.curve.a*x1 + 1) / (2*self.curve.b*y1) else: # If y1 was 0 as well, then we must have found an # order-2 point that doubles to the identity. return self.curve.point() xp = self.curve.b*slope*slope - self.curve.a - x1 - x2 yp = -(y1 + slope * (xp-x1)) return self.curve.point(xp, yp) def __init__(self, p, a, b): self.p = p self.a = ModP(p, a) self.b = ModP(p, b) def cpoint(self, x, yparity=0): if not hasattr(self, 'sqrtmodp'): self.sqrtmodp = RootModP(2, self.p) rhs = (x**3 + self.a.n * x**2 + x) / self.b y = self.sqrtmodp.root(int(rhs)) if (y - yparity) % 2: y = -y return self.point(x, y) def __repr__(self): return "{}(0x{:x}, {}, {})".format( type(self).__name__, self.p, self.a, self.b) class TwistedEdwardsCurve(CurveBase): class Point(AffinePoint): def check_equation(self): x2, y2 = self.x*self.x, self.y*self.y assert (self.curve.a*x2 + y2 == 1 + self.curve.d*x2*y2) def __neg__(self): return type(self)(self.curve, -self.x, self.y) def __add__(self, rhs): x1, x2, y1, y2 = self.x, rhs.x, self.y, rhs.y x1y2, y1x2, y1y2, x1x2 = x1*y2, y1*x2, y1*y2, x1*x2 dxxyy = self.curve.d*x1x2*y1y2 return self.curve.point((x1y2+y1x2)/(1+dxxyy), (y1y2-self.curve.a*x1x2)/(1-dxxyy)) def __init__(self, p, d, a): self.p = p self.d = ModP(p, d) self.a = ModP(p, a) def point(self, *args): # This curve form represents the identity using finite # numbers, so it doesn't need the special infinity flag. # Detect a no-argument call to point() and substitute the pair # of integers that gives the identity. if len(args) == 0: args = [0, 1] return super(TwistedEdwardsCurve, self).point(*args) def cpoint(self, y, xparity=0): if not hasattr(self, 'sqrtmodp'): self.sqrtmodp = RootModP(self.p) y = ModP(self.p, y) y2 = y**2 radicand = (y2 - 1) / (self.d * y2 - self.a) x = self.sqrtmodp.root(radicand.n) if (x - xparity) % 2: x = -x return self.point(x, y) def __repr__(self): return "{}(0x{:x}, {}, {})".format( type(self).__name__, self.p, self.d, self.a) def find_montgomery_power2_order_x_values(p, a): # Find points on a Montgomery elliptic curve that have order a # power of 2. # # Motivation: both Curve25519 and Curve448 are abelian groups # whose overall order is a large prime times a small factor of 2. # The approved base point of each curve generates a cyclic # subgroup whose order is the large prime. Outside that cyclic # subgroup there are many other points that have large prime # order, plus just a handful that have tiny order. If one of the # latter is presented to you as a Diffie-Hellman public value, # nothing useful is going to happen, and RFC 7748 says we should # outlaw those values. And any actual attempt to outlaw them is # going to need to know what they are, either to check for each # one directly, or to use them as test cases for some other # approach. # # In a group of order p 2^k, an obvious way to search for points # with order dividing 2^k is to generate random group elements and # raise them to the power p. That guarantees that you end up with # _something_ with order dividing 2^k (even if it's boringly the # identity). And you also know from theory how many such points # you expect to exist, so you can count the distinct ones you've # found, and stop once you've got the right number. # # But that isn't actually good enough to find all the public # values that are problematic! The reason why not is that in # Montgomery key exchange we don't actually use a full elliptic # curve point: we only use its x-coordinate. And the formulae for # doubling and differential addition on x-coordinates can accept # some values that don't correspond to group elements _at all_ # without detecting any error - and some of those nonsense x # coordinates can also behave like low-order points. # # (For example, the x-coordinate -1 in Curve25519 is such a value. # The reference ECC code in this module will raise an exception if # you call curve25519.cpoint(-1): it corresponds to no valid point # at all. But if you feed it into the doubling formula _anyway_, # it doubles to the valid curve point with x-coord 0, which in # turn doubles to the curve identity. Bang.) # # So we use an alternative approach which discards the group # theory of the actual elliptic curve, and focuses purely on the # doubling formula as an algebraic transformation on Z_p. Our # question is: what values of x have the property that if you # iterate the doubling map you eventually end up dividing by zero? # To answer that, we must solve cubics and quartics mod p, via the # code in numbertheory.py for doing so. E = EquationSolverModP(p) def viableSolutions(it): for x in it: try: yield int(x) except ValueError: pass # some field-extension element that isn't a real value def valuesDoublingTo(y): # The doubling formula for a Montgomery curve point given only # by x coordinate is (x+1)^2(x-1)^2 / (4(x^3+ax^2+x)). # # If we want to find a point that doubles to some particular # value, we can set that formula equal to y and expand to get the # quartic equation x^4 + (-4y)x^3 + (-4ay-2)x^2 + (-4y)x + 1 = 0. return viableSolutions(E.solve_monic_quartic(-4*y, -4*a*y-2, -4*y, 1)) queue = [] qset = set() pos = 0 def insert(x): if x not in qset: queue.append(x) qset.add(x) # Our ultimate aim is to find points that end up going to the # curve identity / point at infinity after some number of # doublings. So our starting point is: what values of x make the # denominator of the doubling formula zero? for x in viableSolutions(E.solve_monic_cubic(a, 1, 0)): insert(x) while pos < len(queue): y = queue[pos] pos += 1 for x in valuesDoublingTo(y): insert(x) return queue p256 = WeierstrassCurve(0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff, -3, 0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b) p256.G = p256.point(0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296,0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5) p256.G_order = 0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551 p384 = WeierstrassCurve(0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000ffffffff, -3, 0xb3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef) p384.G = p384.point(0xaa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7, 0x3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f) p384.G_order = 0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52973 p521 = WeierstrassCurve(0x01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, -3, 0x0051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00) p521.G = p521.point(0x00c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66,0x011839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650) p521.G_order = 0x01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386409 curve25519 = MontgomeryCurve(2**255-19, 0x76d06, 1) curve25519.G = curve25519.cpoint(9) curve448 = MontgomeryCurve(2**448-2**224-1, 0x262a6, 1) curve448.G = curve448.cpoint(5) ed25519 = TwistedEdwardsCurve(2**255-19, 0x52036cee2b6ffe738cc740797779e89800700a4d4141d8ab75eb4dca135978a3, -1) ed25519.G = ed25519.point(0x216936d3cd6e53fec0a4e231fdd6dc5c692cc7609525a7b2c9562d608f25d51a,0x6666666666666666666666666666666666666666666666666666666666666658) ed25519.G_order = 0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed ed448 = TwistedEdwardsCurve(2**448-2**224-1, -39081, +1) ed448.G = ed448.point(0x4f1970c66bed0ded221d15a622bf36da9e146570470f1767ea6de324a3d3a46412ae1af72ab66511433b80e18b00938e2626a82bc70cc05e,0x693f46716eb6bc248876203756c9c7624bea73736ca3984087789c1e05a0c2d73ad3ff1ce67c39c4fdbd132c4ed7c8ad9808795bf230fa14) ed448.G_order = 0x3fffffffffffffffffffffffffffffffffffffffffffffffffffffff7cca23e9c44edb49aed63690216cc2728dc58f552378c292ab5844f3 putty-0.76/test/lattrs.txt0000644000175000017500000000016014072266312012605 00000000000000Test of line attributes: #3Double-height top #4Double-height bottom #5Normal text (#5) #6Double-width only putty-0.76/test/mpu-check.pl0000755000175000017500000000066014072266313012755 00000000000000#!/usr/bin/perl # Trivial command-line client for the function # Math::Prime::Util::verify_prime, which checks a certificate of # primality in MPU format. use strict; use warnings; use Math::Prime::Util; Math::Prime::Util::prime_set_config(verbose => 1); my $cert = ""; $cert .= $_ while <<>>; my $success = Math::Prime::Util::verify_prime($cert); die "verification failed\n" unless $success; warn "verification succeeded\n"; putty-0.76/test/numbertheory.py0000644000175000017500000006006214072266313013640 00000000000000import sys import numbers import itertools import unittest assert sys.version_info[:2] >= (3,0), "This is Python 3 code" def invert(a, b): "Multiplicative inverse of a mod b. a,b must be coprime." A = (a, 1, 0) B = (b, 0, 1) while B[0]: q = A[0] // B[0] A, B = B, tuple(Ai - q*Bi for Ai, Bi in zip(A, B)) assert abs(A[0]) == 1 return A[1]*A[0] % b def jacobi(n,m): """Compute the Jacobi symbol. The special case of this when m is prime is the Legendre symbol, which is 0 if n is congruent to 0 mod m; 1 if n is congruent to a non-zero square number mod m; -1 if n is not congruent to any square mod m. """ assert m & 1 acc = 1 while True: n %= m if n == 0: return 0 while not (n & 1): n >>= 1 if (m & 7) not in {1,7}: acc *= -1 if n == 1: return acc if (n & 3) == 3 and (m & 3) == 3: acc *= -1 n, m = m, n class CyclicGroupRootFinder(object): """Class for finding rth roots in a cyclic group. r must be prime.""" # Basic strategy: # # We write |G| = r^k u, with u coprime to r. This gives us a # nested sequence of subgroups G = G_0 > G_1 > ... > G_k, each # with index r in its predecessor. G_0 is the whole group, and the # innermost G_k has order u. # # Within G_k, you can take an rth root by raising an element to # the power of (r^{-1} mod u). If k=0 (so G = G_0 = G_k) then # that's all that's needed: every element has a unique rth root. # But if k>0, then things go differently. # # Define the 'rank' of an element g as the highest i such that # g \in G_i. Elements of rank 0 are the non-rth-powers: they don't # even _have_ an rth root. Elements of rank k are the easy ones to # take rth roots of, as above. # # In between, you can follow an inductive process, as long as you # know one element z of rank 0. Suppose we're trying to take the # rth root of some g with rank i. Repeatedly multiply g by z^{r^i} # until its rank increases; then take the root of that # (recursively), and divide off z^{r^{i-1}} once you're done. def __init__(self, r, order): self.order = order # order of G self.r = r self.k = next(k for k in itertools.count() if self.order % (r**(k+1)) != 0) self.u = self.order // (r**self.k) self.z = next(z for z in self.iter_elements() if self.index(z) == 0) self.zinv = self.inverse(self.z) self.root_power = invert(self.r, self.u) if self.u > 1 else 0 self.roots_of_unity = {self.identity()} if self.k > 0: exponent = self.order // self.r for z in self.iter_elements(): root_of_unity = self.pow(z, exponent) if root_of_unity not in self.roots_of_unity: self.roots_of_unity.add(root_of_unity) if len(self.roots_of_unity) == r: break def index(self, g): h = self.pow(g, self.u) for i in range(self.k+1): if h == self.identity(): return self.k - i h = self.pow(h, self.r) assert False, ("Not a cyclic group! Raising {} to u r^k should give e." .format(g)) def all_roots(self, g): try: r = self.root(g) except ValueError: return [] return {r * rou for rou in self.roots_of_unity} def root(self, g): i = self.index(g) if i == 0 and self.k > 0: raise ValueError("{} has no {}th root".format(g, self.r)) out = self.root_recurse(g, i) assert self.pow(out, self.r) == g return out def root_recurse(self, g, i): if i == self.k: return self.pow(g, self.root_power) z_in = self.pow(self.z, self.r**i) z_out = self.pow(self.zinv, self.r**(i-1)) adjust = self.identity() while True: g = self.mul(g, z_in) adjust = self.mul(adjust, z_out) i2 = self.index(g) if i2 > i: return self.mul(self.root_recurse(g, i2), adjust) class AdditiveGroupRootFinder(CyclicGroupRootFinder): """Trivial test subclass for CyclicGroupRootFinder. Represents a cyclic group of any order additively, as the integers mod n under addition. This makes root-finding trivial without having to use the complicated algorithm above, and therefore it's a good way to test the complicated algorithm under conditions where the right answers are obvious.""" def __init__(self, r, order): super().__init__(r, order) def mul(self, x, y): return (x + y) % self.order def pow(self, x, n): return (x * n) % self.order def inverse(self, x): return (-x) % self.order def identity(self): return 0 def iter_elements(self): return range(self.order) class TestCyclicGroupRootFinder(unittest.TestCase): def testRootFinding(self): for order in 10, 11, 12, 18: grf = AdditiveGroupRootFinder(3, order) for i in range(order): try: r = grf.root(i) except ValueError: r = None if order % 3 == 0 and i % 3 != 0: self.assertEqual(r, None) else: self.assertEqual(r*3 % order, i) class RootModP(CyclicGroupRootFinder): """The live class that can take rth roots mod a prime.""" def __init__(self, r, p): self.modulus = p super().__init__(r, p-1) def mul(self, x, y): return (x * y) % self.modulus def pow(self, x, n): return pow(x, n, self.modulus) def inverse(self, x): return invert(x, self.modulus) def identity(self): return 1 def iter_elements(self): return range(1, self.modulus) def root(self, g): return 0 if g == 0 else super().root(g) class ModP(object): """Class that represents integers mod p as a field. All the usual arithmetic operations are supported directly, including division, so you can write formulas in a natural way without having to keep saying '% p' everywhere or call a cumbersome modular_inverse() function. """ def __init__(self, p, n=0): self.p = p if isinstance(n, type(self)): self.check(n) n = n.n self.n = n % p def check(self, other): assert isinstance(other, type(self)) assert isinstance(self, type(other)) assert self.p == other.p def coerce_to(self, other): if not isinstance(other, type(self)): other = type(self)(self.p, other) else: self.check(other) return other def __int__(self): return self.n def __add__(self, rhs): rhs = self.coerce_to(rhs) return type(self)(self.p, (self.n + rhs.n) % self.p) def __neg__(self): return type(self)(self.p, -self.n % self.p) def __radd__(self, rhs): rhs = self.coerce_to(rhs) return type(self)(self.p, (self.n + rhs.n) % self.p) def __sub__(self, rhs): rhs = self.coerce_to(rhs) return type(self)(self.p, (self.n - rhs.n) % self.p) def __rsub__(self, rhs): rhs = self.coerce_to(rhs) return type(self)(self.p, (rhs.n - self.n) % self.p) def __mul__(self, rhs): rhs = self.coerce_to(rhs) return type(self)(self.p, (self.n * rhs.n) % self.p) def __rmul__(self, rhs): rhs = self.coerce_to(rhs) return type(self)(self.p, (self.n * rhs.n) % self.p) def __div__(self, rhs): rhs = self.coerce_to(rhs) return type(self)(self.p, (self.n * invert(rhs.n, self.p)) % self.p) def __rdiv__(self, rhs): rhs = self.coerce_to(rhs) return type(self)(self.p, (rhs.n * invert(self.n, self.p)) % self.p) def __truediv__(self, rhs): return self.__div__(rhs) def __rtruediv__(self, rhs): return self.__rdiv__(rhs) def __pow__(self, exponent): assert exponent >= 0 n, b_to_n = 1, self total = type(self)(self.p, 1) while True: if exponent & n: exponent -= n total *= b_to_n n *= 2 if n > exponent: break b_to_n *= b_to_n return total def __cmp__(self, rhs): rhs = self.coerce_to(rhs) return cmp(self.n, rhs.n) def __eq__(self, rhs): rhs = self.coerce_to(rhs) return self.n == rhs.n def __ne__(self, rhs): rhs = self.coerce_to(rhs) return self.n != rhs.n def __lt__(self, rhs): raise ValueError("Elements of a modular ring have no ordering") def __le__(self, rhs): raise ValueError("Elements of a modular ring have no ordering") def __gt__(self, rhs): raise ValueError("Elements of a modular ring have no ordering") def __ge__(self, rhs): raise ValueError("Elements of a modular ring have no ordering") def __str__(self): return "0x{:x}".format(self.n) def __repr__(self): return "{}(0x{:x},0x{:x})".format(type(self).__name__, self.p, self.n) def __hash__(self): return hash((type(self).__name__, self.p, self.n)) class QuadraticFieldExtensionModP(object): """Class representing Z_p[sqrt(d)] for a given non-square d. """ def __init__(self, p, d, n=0, m=0): self.p = p self.d = d if isinstance(n, ModP): assert self.p == n.p n = n.n if isinstance(m, ModP): assert self.p == m.p m = m.n if isinstance(n, type(self)): self.check(n) m += n.m n = n.n self.n = n % p self.m = m % p @classmethod def constructor(cls, p, d): return lambda *args: cls(p, d, *args) def check(self, other): assert isinstance(other, type(self)) assert isinstance(self, type(other)) assert self.p == other.p assert self.d == other.d def coerce_to(self, other): if not isinstance(other, type(self)): other = type(self)(self.p, self.d, other) else: self.check(other) return other def __int__(self): if self.m != 0: raise ValueError("Can't coerce a non-element of Z_{} to integer" .format(self.p)) return int(self.n) def __add__(self, rhs): rhs = self.coerce_to(rhs) return type(self)(self.p, self.d, (self.n + rhs.n) % self.p, (self.m + rhs.m) % self.p) def __neg__(self): return type(self)(self.p, self.d, -self.n % self.p, -self.m % self.p) def __radd__(self, rhs): rhs = self.coerce_to(rhs) return type(self)(self.p, self.d, (self.n + rhs.n) % self.p, (self.m + rhs.m) % self.p) def __sub__(self, rhs): rhs = self.coerce_to(rhs) return type(self)(self.p, self.d, (self.n - rhs.n) % self.p, (self.m - rhs.m) % self.p) def __rsub__(self, rhs): rhs = self.coerce_to(rhs) return type(self)(self.p, self.d, (rhs.n - self.n) % self.p, (rhs.m - self.m) % self.p) def __mul__(self, rhs): rhs = self.coerce_to(rhs) n, m, N, M = self.n, self.m, rhs.n, rhs.m return type(self)(self.p, self.d, (n*N + self.d*m*M) % self.p, (n*M + m*N) % self.p) def __rmul__(self, rhs): return self.__mul__(rhs) def __div__(self, rhs): rhs = self.coerce_to(rhs) n, m, N, M = self.n, self.m, rhs.n, rhs.m # (n+m sqrt d)/(N+M sqrt d) = (n+m sqrt d)(N-M sqrt d)/(N^2-dM^2) denom = (N*N - self.d*M*M) % self.p if denom == 0: raise ValueError("division by zero") recipdenom = invert(denom, self.p) return type(self)(self.p, self.d, (n*N - self.d*m*M) * recipdenom % self.p, (m*N - n*M) * recipdenom % self.p) def __rdiv__(self, rhs): rhs = self.coerce_to(rhs) return rhs.__div__(self) def __truediv__(self, rhs): return self.__div__(rhs) def __rtruediv__(self, rhs): return self.__rdiv__(rhs) def __pow__(self, exponent): assert exponent >= 0 n, b_to_n = 1, self total = type(self)(self.p, self.d, 1) while True: if exponent & n: exponent -= n total *= b_to_n n *= 2 if n > exponent: break b_to_n *= b_to_n return total def __cmp__(self, rhs): rhs = self.coerce_to(rhs) return cmp((self.n, self.m), (rhs.n, rhs.m)) def __eq__(self, rhs): rhs = self.coerce_to(rhs) return self.n == rhs.n and self.m == rhs.m def __ne__(self, rhs): rhs = self.coerce_to(rhs) return self.n != rhs.n or self.m != rhs.m def __lt__(self, rhs): raise ValueError("Elements of a modular ring have no ordering") def __le__(self, rhs): raise ValueError("Elements of a modular ring have no ordering") def __gt__(self, rhs): raise ValueError("Elements of a modular ring have no ordering") def __ge__(self, rhs): raise ValueError("Elements of a modular ring have no ordering") def __str__(self): if self.m == 0: return "0x{:x}".format(self.n) else: return "0x{:x}+0x{:x}*sqrt({:d})".format(self.n, self.m, self.d) def __repr__(self): return "{}(0x{:x},0x{:x},0x{:x},0x{:x})".format( type(self).__name__, self.p, self.d, self.n, self.m) def __hash__(self): return hash((type(self).__name__, self.p, self.d, self.n, self.m)) class RootInQuadraticExtension(CyclicGroupRootFinder): """Take rth roots in the quadratic extension of Z_p.""" def __init__(self, r, p, d): self.modulus = p self.constructor = QuadraticFieldExtensionModP.constructor(p, d) super().__init__(r, p*p-1) def mul(self, x, y): return x * y def pow(self, x, n): return x ** n def inverse(self, x): return 1/x def identity(self): return self.constructor(1, 0) def iter_elements(self): p = self.modulus for n_plus_m in range(1, 2*p-1): n_min = max(0, n_plus_m-(p-1)) n_max = min(p-1, n_plus_m) for n in range(n_min, n_max + 1): m = n_plus_m - n assert(0 <= n < p) assert(0 <= m < p) assert(n != 0 or m != 0) yield self.constructor(n, m) def root(self, g): return 0 if g == 0 else super().root(g) class EquationSolverModP(object): """Class that can solve quadratics, cubics and quartics over Z_p. p must be a nontrivial prime (bigger than 3). """ # This is a port to Z_p of reasonably standard algorithms for # solving quadratics, cubics and quartics over the reals. # # When you solve a cubic in R, you sometimes have to deal with # intermediate results that are complex numbers. In particular, # you have to solve a quadratic whose coefficients are in R but # its roots may be complex, and then having solved that quadratic, # you need to iterate over all three cube roots of the solution in # order to recover all the roots of your cubic. (Even if the cubic # ends up having three real roots, you can't calculate them # without going through those complex intermediate values.) # # So over Z_p, the same thing applies: we're going to need to be # able to solve any quadratic with coefficients in Z_p, even if # its discriminant turns out not to be a quadratic residue mod p, # and then we'll need to find _three_ cube roots of the result, # even if p == 2 (mod 3) so that numbers only have one cube root # each. # # Both of these problems can be solved at once if we work in the # finite field GF(p^2), i.e. make a quadratic field extension of # Z_p by adjoining a square root of some non-square d. The # multiplicative group of GF(p^2) is cyclic and has order p^2-1 = # (p-1)(p+1), with the mult group of Z_p forming the unique # subgroup of order (p-1) within it. So we've multiplied the group # order by p+1, which is even (since by assumption p > 3), and # therefore a square root is now guaranteed to exist for every # number in the Z_p subgroup. Moreover, no matter whether p itself # was congruent to 1 or 2 mod 3, p^2 is always congruent to 1, # which means that the mult group of GF(p^2) has order divisible # by 3. So there are guaranteed to be three distinct cube roots of # unity, and hence, three cube roots of any number that's a cube # at all. # # Quartics don't introduce any additional problems. To solve a # quartic, you factorise it into two quadratic factors, by solving # a cubic to find one of the coefficients. So if you can already # solve cubics, then you're more or less done. The only wrinkle is # that the two quadratic factors will have coefficients in GF(p^2) # but not necessarily in Z_p. But that doesn't stop us at least # _trying_ to solve them by taking square roots in GF(p^2) - and # if the discriminant of one of those quadratics has is not a # square even in GF(p^2), then its solutions will only exist if # you escalate further to GF(p^4), in which case the answer is # simply that there aren't any solutions in Z_p to that quadratic. def __init__(self, p): self.p = p self.nonsquare_mod_p = d = RootModP(2, p).z self.constructor = QuadraticFieldExtensionModP.constructor(p, d) self.sqrt = RootInQuadraticExtension(2, p, d) self.cbrt = RootInQuadraticExtension(3, p, d) def solve_quadratic(self, a, b, c): "Solve ax^2 + bx + c = 0." a, b, c = map(self.constructor, (a, b, c)) assert a != 0 return self.solve_monic_quadratic(b/a, c/a) def solve_monic_quadratic(self, b, c): "Solve x^2 + bx + c = 0." b, c = map(self.constructor, (b, c)) s = b/2 return [y - s for y in self.solve_depressed_quadratic(c - s*s)] def solve_depressed_quadratic(self, c): "Solve x^2 + c = 0." return self.sqrt.all_roots(-c) def solve_cubic(self, a, b, c, d): "Solve ax^3 + bx^2 + cx + d = 0." a, b, c, d = map(self.constructor, (a, b, c, d)) assert a != 0 return self.solve_monic_cubic(b/a, c/a, d/a) def solve_monic_cubic(self, b, c, d): "Solve x^3 + bx^2 + cx + d = 0." b, c, d = map(self.constructor, (b, c, d)) s = b/3 return [y - s for y in self.solve_depressed_cubic( c - 3*s*s, 2*s*s*s - c*s + d)] def solve_depressed_cubic(self, c, d): "Solve x^3 + cx + d = 0." c, d = map(self.constructor, (c, d)) solutions = set() # To solve x^3 + cx + d = 0, set p = -c/3, then # substitute x = z + p/z to get z^6 + d z^3 + p^3 = 0. # Solve that quadratic for z^3, then take cube roots. p = -c/3 for z3 in self.solve_monic_quadratic(d, p**3): # As I understand the theory, we _should_ only need to # take cube roots of one root of that quadratic: the other # one should give the same set of answers after you map # each one through z |-> z+p/z. But speed isn't at a # premium here, so I'll do this the way that must work. for z in self.cbrt.all_roots(z3): solutions.add(z + p/z) return solutions def solve_quartic(self, a, b, c, d, e): "Solve ax^4 + bx^3 + cx^2 + dx + e = 0." a, b, c, d, e = map(self.constructor, (a, b, c, d, e)) assert a != 0 return self.solve_monic_quartic(b/a, c/a, d/a, e/a) def solve_monic_quartic(self, b, c, d, e): "Solve x^4 + bx^3 + cx^2 + dx + e = 0." b, c, d, e = map(self.constructor, (b, c, d, e)) s = b/4 return [y - s for y in self.solve_depressed_quartic( c - 6*s*s, d - 2*c*s + 8*s*s*s, e - d*s + c*s*s - 3*s*s*s*s)] def solve_depressed_quartic(self, c, d, e): "Solve x^4 + cx^2 + dx + e = 0." c, d, e = map(self.constructor, (c, d, e)) solutions = set() # To solve an equation of this form, we search for a value y # such that subtracting the original polynomial from (x^2+y)^2 # yields a quadratic of the special form (ux+v)^2. # # Then our equation is rewritten as (x^2+y)^2 - (ux+v)^2 = 0 # i.e. ((x^2+y) + (ux+v)) ((x^2+y) - (ux+v)) = 0 # i.e. the product of two quadratics, each of which we then solve. # # To find y, we write down the discriminant of the quadratic # (x^2+y)^2 - (x^4 + cx^2 + dx + e) and set it to 0, which # gives a cubic in y. Maxima gives the coefficients as # (-8)y^3 + (4c)y^2 + (8e)y + (d^2-4ce). # # As above, we _should_ only need one value of y. But I go # through them all just in case, because I don't care about # speed, and because checking the assertions inside this loop # for every value is extra reassurance that I've done all of # this right. for y in self.solve_cubic(-8, 4*c, 8*e, d*d-4*c*e): # Subtract the original equation from (x^2+y)^2 to get the # coefficients of our quadratic residual. A, B, C = 2*y-c, -d, y*y-e # Expect that to have zero discriminant, i.e. a repeated root. assert B*B - 4*A*C == 0 # If (Ax^2+Bx+C) == (ux+v)^2 then we have u^2=A, 2uv=B, v^2=C. # So we can either recover u as sqrt(A) or v as sqrt(C), and # whichever we did, find the other from B by division. But # either of the end coefficients might be zero, so we have # to be prepared to try either option. try: if A != 0: u = self.sqrt.root(A) v = B/(2*u) elif C != 0: v = self.sqrt.root(C) u = B/(2*v) else: # One last possibility is that all three coefficients # of our residual quadratic are 0, in which case, # obviously, u=v=0 as well. u = v = 0 except ValueError: # If Ax^2+Bx+C looked like a perfect square going by # its discriminant, but actually taking the square # root of A or C threw an exception, that means that # it's the square of a polynomial whose coefficients # live in a yet-higher field extension of Z_p. In that # case we're not going to end up with roots of the # original quartic in Z_p if we start from here! continue # So now our quartic is factorised into the form # (x^2 - ux - v + y) (x^2 + ux + v + y). for x in self.solve_monic_quadratic(-u, y-v): solutions.add(x) for x in self.solve_monic_quadratic(u, y+v): solutions.add(x) return solutions class EquationSolverTest(unittest.TestCase): def testQuadratic(self): E = EquationSolverModP(11) solns = E.solve_quadratic(3, 2, 6) self.assertEqual(sorted(map(str, solns)), ["0x1", "0x2"]) def testCubic(self): E = EquationSolverModP(11) solns = E.solve_cubic(7, 2, 0, 2) self.assertEqual(sorted(map(str, solns)), ["0x1", "0x2", "0x3"]) def testQuartic(self): E = EquationSolverModP(11) solns = E.solve_quartic(9, 9, 7, 1, 7) self.assertEqual(sorted(map(str, solns)), ["0x1", "0x2", "0x3", "0x4"]) if __name__ == "__main__": import sys if sys.argv[1:] == ["--test"]: sys.argv[1:2] = [] unittest.main() putty-0.76/test/primegen.py0000755000175000017500000000525514072266313012731 00000000000000#!/usr/bin/env python3 from testcrypt import * import base64 import argparse import itertools assert sys.version_info[:2] >= (3,0), "This is Python 3 code" def main(): opener = lambda mode: lambda fname: lambda: argparse.FileType(mode)(fname) parser = argparse.ArgumentParser(description='') IntArg = lambda x: int(x, 0) parser.add_argument("bits", type=IntArg, nargs="?", default=1024) parser.add_argument("-s", "--seed") parser.add_argument("-f", "--firstbits", type=IntArg, default=1) parser.add_argument("--fast", action='store_const', dest='policy', const='provable_fast') parser.add_argument("--complex", action='store_const', dest='policy', const='provable_maurer_complex') parser.add_argument("-q", "--quiet", action='store_true') parser.add_argument("-b", "--binary", action='store_const', dest='fmt', const='{:b}') parser.add_argument("-x", "--hex", action='store_const', dest='fmt', const='{:x}') parser.add_argument("-o", "--output", type=opener("w"), default=opener("w")("-"), help="file to write the prime to") parser.add_argument("--mpu", type=opener("w"), help="MPU certificate output file") parser.add_argument("--safe", action='store_true') parser.set_defaults(fmt='{:d}', policy='provable_maurer_simple') args = parser.parse_args() seed = args.seed if seed is None: with open("/dev/urandom", "rb") as f: seed = base64.b64encode(f.read(32)).decode("ASCII") if not args.quiet: print("seed =", seed) random_make_prng('sha256', seed) assert args.firstbits > 0 nfirst = next(i for i in itertools.count() if (args.firstbits >> i) == 0) pgc = primegen_new_context(args.policy) if args.safe: while True: pcs_q = pcs_new_with_firstbits(args.bits - 1, args.firstbits, nfirst) pcs_try_sophie_germain(pcs_q) q = primegen_generate(pgc, pcs_q) pcs = pcs_new(args.bits) pcs_require_residue_1_mod_prime(pcs, q) pcs_set_oneshot(pcs) p = primegen_generate(pgc, pcs) if p is not None: break else: pcs = pcs_new_with_firstbits(args.bits, args.firstbits, nfirst) p = primegen_generate(pgc, pcs) with args.output() as f: print(args.fmt.format(int(p)), file=f) if args.mpu is not None: s = primegen_mpu_certificate(pgc, p) with args.mpu() as f: f.write(s.decode("ASCII")) if __name__ == '__main__': main() putty-0.76/test/scocols.txt0000644000175000017500000000072414072266313012750 00000000000000Test of (destructive) SCO colour rendering. SCO fg: [=0F0[=7F [=1F1[=7F [=2F2[=7F [=3F3[=7F [=4F4[=7F [=5F5[=7F [=6F6[=7F [=7F7[=7F [=8F8[=7F [=9F9[=7F [=10F10[=7F [=11F11[=7F [=12F12[=7F [=13F13[=7F [=14F14[=7F [=15F15[=7F SCO bg: [=0G0[=0G [=1G1[=0G [=2G2[=0G [=3G3[=0G [=4G4[=0G [=5G5[=0G [=6G6[=0G [=7G7[=0G [=8G8[=0G [=9G9[=0G [=10G10[=0G [=11G11[=0G [=12G12[=0G [=13G13[=0G [=14G14[=0G [=15G15[=0G putty-0.76/test/ssh.py0000644000175000017500000000512414072266313011710 00000000000000import sys import struct import itertools assert sys.version_info[:2] >= (3,0), "This is Python 3 code" def nbits(n): # Mimic mp_get_nbits for ordinary Python integers. assert 0 <= n smax = next(s for s in itertools.count() if (n >> (1 << s)) == 0) toret = 0 for shift in reversed([1 << s for s in range(smax)]): if n >> shift != 0: n >>= shift toret += shift assert n <= 1 if n == 1: toret += 1 return toret def ssh_byte(n): return struct.pack("B", n) def ssh_uint32(n): return struct.pack(">L", n) def ssh_string(s): return ssh_uint32(len(s)) + s def ssh1_mpint(x): bits = nbits(x) bytevals = [0xFF & (x >> (8*n)) for n in range((bits-1)//8, -1, -1)] return struct.pack(">H" + "B" * len(bytevals), bits, *bytevals) def ssh2_mpint(x): bytevals = [0xFF & (x >> (8*n)) for n in range(nbits(x)//8, -1, -1)] return struct.pack(">L" + "B" * len(bytevals), len(bytevals), *bytevals) def decoder(fn): def decode(s, return_rest = False): item, length_consumed = fn(s) if return_rest: return item, s[length_consumed:] else: return item return decode @decoder def ssh_decode_byte(s): return struct.unpack_from("B", s, 0)[0], 1 @decoder def ssh_decode_uint32(s): return struct.unpack_from(">L", s, 0)[0], 4 @decoder def ssh_decode_string(s): length = ssh_decode_uint32(s) assert length + 4 <= len(s) return s[4:length+4], length+4 @decoder def ssh1_get_mpint(s): # returns it unconsumed, still in wire encoding nbits = struct.unpack_from(">H", s, 0)[0] nbytes = (nbits + 7) // 8 assert nbytes + 2 <= len(s) return s[:nbytes+2], nbytes+2 @decoder def ssh1_decode_mpint(s): nbits = struct.unpack_from(">H", s, 0)[0] nbytes = (nbits + 7) // 8 assert nbytes + 2 <= len(s) data = s[2:nbytes+2] v = 0 for b in struct.unpack("B" * len(data), data): v = (v << 8) | b return v, nbytes+2 AGENT_MAX_MSGLEN = 262144 SSH1_AGENTC_REQUEST_RSA_IDENTITIES = 1 SSH1_AGENT_RSA_IDENTITIES_ANSWER = 2 SSH1_AGENTC_RSA_CHALLENGE = 3 SSH1_AGENT_RSA_RESPONSE = 4 SSH1_AGENTC_ADD_RSA_IDENTITY = 7 SSH1_AGENTC_REMOVE_RSA_IDENTITY = 8 SSH1_AGENTC_REMOVE_ALL_RSA_IDENTITIES = 9 SSH_AGENT_FAILURE = 5 SSH_AGENT_SUCCESS = 6 SSH2_AGENTC_REQUEST_IDENTITIES = 11 SSH2_AGENT_IDENTITIES_ANSWER = 12 SSH2_AGENTC_SIGN_REQUEST = 13 SSH2_AGENT_SIGN_RESPONSE = 14 SSH2_AGENTC_ADD_IDENTITY = 17 SSH2_AGENTC_REMOVE_IDENTITY = 18 SSH2_AGENTC_REMOVE_ALL_IDENTITIES = 19 SSH2_AGENTC_EXTENSION = 27 SSH_AGENT_RSA_SHA2_256 = 2 SSH_AGENT_RSA_SHA2_512 = 4 putty-0.76/test/testcrypt.py0000644000175000017500000003057614072266313013165 00000000000000import sys import os import numbers import subprocess import re import struct from binascii import hexlify assert sys.version_info[:2] >= (3,0), "This is Python 3 code" # Expect to be run from the 'test' subdirectory, one level down from # the main source putty_srcdir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) def coerce_to_bytes(arg): return arg.encode("UTF-8") if isinstance(arg, str) else arg class ChildProcessFailure(Exception): pass class ChildProcess(object): def __init__(self): self.sp = None self.debug = None self.exitstatus = None self.exception = None dbg = os.environ.get("PUTTY_TESTCRYPT_DEBUG") if dbg is not None: if dbg == "stderr": self.debug = sys.stderr else: sys.stderr.write("Unknown value '{}' for PUTTY_TESTCRYPT_DEBUG" " (try 'stderr'\n") def start(self): assert self.sp is None override_command = os.environ.get("PUTTY_TESTCRYPT") if override_command is None: cmd = [os.path.join(putty_srcdir, "testcrypt")] shell = False else: cmd = override_command shell = True self.sp = subprocess.Popen( cmd, shell=shell, stdin=subprocess.PIPE, stdout=subprocess.PIPE) def write_line(self, line): if self.exception is not None: # Re-raise our fatal-error exception, if it previously # occurred in a context where it couldn't be propagated (a # __del__ method). raise self.exception if self.debug is not None: self.debug.write("send: {}\n".format(line)) self.sp.stdin.write(line + b"\n") self.sp.stdin.flush() def read_line(self): line = self.sp.stdout.readline() if len(line) == 0: self.exception = ChildProcessFailure("received EOF from testcrypt") raise self.exception line = line.rstrip(b"\r\n") if self.debug is not None: self.debug.write("recv: {}\n".format(line)) return line def already_terminated(self): return self.sp is None and self.exitstatus is not None def funcall(self, cmd, args): if self.sp is None: assert self.exitstatus is None self.start() self.write_line(coerce_to_bytes(cmd) + b" " + b" ".join( coerce_to_bytes(arg) for arg in args)) argcount = int(self.read_line()) return [self.read_line() for arg in range(argcount)] def wait_for_exit(self): if self.sp is not None: self.sp.stdin.close() self.exitstatus = self.sp.wait() self.sp = None def check_return_status(self): self.wait_for_exit() if self.exitstatus is not None and self.exitstatus != 0: raise ChildProcessFailure("testcrypt returned exit status {}" .format(self.exitstatus)) childprocess = ChildProcess() method_prefixes = { 'val_wpoint': 'ecc_weierstrass_', 'val_mpoint': 'ecc_montgomery_', 'val_epoint': 'ecc_edwards_', 'val_hash': 'ssh_hash_', 'val_mac': 'ssh2_mac_', 'val_key': 'ssh_key_', 'val_cipher': 'ssh_cipher_', 'val_dh': 'dh_', 'val_ecdh': 'ssh_ecdhkex_', 'val_rsakex': 'ssh_rsakex_', 'val_prng': 'prng_', 'val_pcs': 'pcs_', 'val_pockle': 'pockle_', } method_lists = {t: [] for t in method_prefixes} class Value(object): def __init__(self, typename, ident): self._typename = typename self._ident = ident for methodname, function in method_lists.get(self._typename, []): setattr(self, methodname, (lambda f: lambda *args: f(self, *args))(function)) def _consumed(self): self._ident = None def __repr__(self): return "Value({!r}, {!r})".format(self._typename, self._ident) def __del__(self): if self._ident is not None and not childprocess.already_terminated(): try: childprocess.funcall("free", [self._ident]) except ChildProcessFailure: # If we see this exception now, we can't do anything # about it, because exceptions don't propagate out of # __del__ methods. Squelch it to prevent the annoying # runtime warning from Python, and the # 'self.exception' mechanism in the ChildProcess class # will raise it again at the next opportunity. # # (This covers both the case where testcrypt crashes # _during_ one of these free operations, and the # silencing of cascade failures when we try to send a # "free" command to testcrypt after it had already # crashed for some other reason.) pass def __long__(self): if self._typename != "val_mpint": raise TypeError("testcrypt values of types other than mpint" " cannot be converted to integer") hexval = childprocess.funcall("mp_dump", [self._ident])[0] return 0 if len(hexval) == 0 else int(hexval, 16) def __int__(self): return int(self.__long__()) def marshal_string(val): val = coerce_to_bytes(val) assert isinstance(val, bytes), "Bad type for val_string input" return "".join( chr(b) if (0x20 <= b < 0x7F and b != 0x25) else "%{:02x}".format(b) for b in val) def make_argword(arg, argtype, fnname, argindex, to_preserve): typename, consumed = argtype if typename.startswith("opt_"): if arg is None: return "NULL" typename = typename[4:] if typename == "val_string": retwords = childprocess.funcall("newstring", [marshal_string(arg)]) arg = make_retvals([typename], retwords, unpack_strings=False)[0] to_preserve.append(arg) if typename == "val_mpint" and isinstance(arg, numbers.Integral): retwords = childprocess.funcall("mp_literal", ["0x{:x}".format(arg)]) arg = make_retvals([typename], retwords)[0] to_preserve.append(arg) if isinstance(arg, Value): if arg._typename != typename: raise TypeError( "{}() argument {:d} should be {} ({} given)".format( fnname, argindex, typename, arg._typename)) ident = arg._ident if consumed: arg._consumed() return ident if typename == "uint" and isinstance(arg, numbers.Integral): return "0x{:x}".format(arg) if typename == "boolean": return "true" if arg else "false" if typename in { "hashalg", "macalg", "keyalg", "cipheralg", "dh_group", "ecdh_alg", "rsaorder", "primegenpolicy", "argon2flavour", "fptype"}: arg = coerce_to_bytes(arg) if isinstance(arg, bytes) and b" " not in arg: return arg if typename == "mpint_list": sublist = [make_argword(len(arg), ("uint", False), fnname, argindex, to_preserve)] for val in arg: sublist.append(make_argword(val, ("val_mpint", False), fnname, argindex, to_preserve)) return b" ".join(coerce_to_bytes(sub) for sub in sublist) raise TypeError( "Can't convert {}() argument {:d} to {} (value was {!r})".format( fnname, argindex, typename, arg)) def unpack_string(identifier): retwords = childprocess.funcall("getstring", [identifier]) childprocess.funcall("free", [identifier]) return re.sub(b"%[0-9A-F][0-9A-F]", lambda m: bytes([int(m.group(0)[1:], 16)]), retwords[0]) def unpack_mp(identifier): retwords = childprocess.funcall("mp_dump", [identifier]) childprocess.funcall("free", [identifier]) return int(retwords[0], 16) def make_retval(rettype, word, unpack_strings): if rettype.startswith("opt_"): if word == b"NULL": return None rettype = rettype[4:] if rettype == "val_string" and unpack_strings: return unpack_string(word) if rettype == "val_keycomponents": kc = {} retwords = childprocess.funcall("key_components_count", [word]) for i in range(int(retwords[0], 0)): args = [word, "{:d}".format(i)] retwords = childprocess.funcall("key_components_nth_name", args) kc_key = unpack_string(retwords[0]) retwords = childprocess.funcall("key_components_nth_str", args) if retwords[0] != b"NULL": kc_value = unpack_string(retwords[0]).decode("ASCII") else: retwords = childprocess.funcall("key_components_nth_mp", args) kc_value = unpack_mp(retwords[0]) kc[kc_key.decode("ASCII")] = kc_value childprocess.funcall("free", [word]) return kc if rettype.startswith("val_"): return Value(rettype, word) elif rettype == "int" or rettype == "uint": return int(word, 0) elif rettype == "boolean": assert word == b"true" or word == b"false" return word == b"true" elif rettype == "pocklestatus": return word.decode("ASCII") raise TypeError("Can't deal with return value {!r} of type {!r}" .format(word, rettype)) def make_retvals(rettypes, retwords, unpack_strings=True): assert len(rettypes) == len(retwords) # FIXME: better exception return [make_retval(rettype, word, unpack_strings) for rettype, word in zip(rettypes, retwords)] class Function(object): def __init__(self, fnname, rettypes, argtypes): self.fnname = fnname self.rettypes = rettypes self.argtypes = argtypes def __repr__(self): return "".format(self.fnname) def __call__(self, *args): if len(args) != len(self.argtypes): raise TypeError( "{}() takes exactly {} arguments ({} given)".format( self.fnname, len(self.argtypes), len(args))) to_preserve = [] retwords = childprocess.funcall( self.fnname, [make_argword(args[i], self.argtypes[i], self.fnname, i, to_preserve) for i in range(len(args))]) retvals = make_retvals(self.rettypes, retwords) if len(retvals) == 0: return None if len(retvals) == 1: return retvals[0] return tuple(retvals) def _setup(scope): header_file = os.path.join(putty_srcdir, "testcrypt.h") linere = re.compile(r'^FUNC\d+\((.*)\)$') valprefix = "val_" outprefix = "out_" optprefix = "opt_" consprefix = "consumed_" def trim_argtype(arg): if arg.startswith(optprefix): return optprefix + trim_argtype(arg[len(optprefix):]) if (arg.startswith(valprefix) and "_" in arg[len(valprefix):]): # Strip suffixes like val_string_asciz arg = arg[:arg.index("_", len(valprefix))] return arg with open(header_file) as f: for line in iter(f.readline, ""): line = line.rstrip("\r\n").replace(" ", "") m = linere.match(line) if m is not None: words = m.group(1).split(",") function = words[1] rettypes = [] argtypes = [] argsconsumed = [] if words[0] != "void": rettypes.append(trim_argtype(words[0])) for arg in words[2:]: if arg.startswith(outprefix): rettypes.append(trim_argtype(arg[len(outprefix):])) else: consumed = False if arg.startswith(consprefix): arg = arg[len(consprefix):] consumed = True arg = trim_argtype(arg) argtypes.append((arg, consumed)) func = Function(function, rettypes, argtypes) scope[function] = func if len(argtypes) > 0: t = argtypes[0][0] if (t in method_prefixes and function.startswith(method_prefixes[t])): methodname = function[len(method_prefixes[t]):] method_lists[t].append((methodname, func)) _setup(globals()) del _setup putty-0.76/test/utf8.txt0000644000175000017500000000333514072266313012172 00000000000000Test of UTF-8 output in a terminal emulator ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾ Some basic Unicode: ∮ Eâ‹…da = Q, n → ∞, ∑ f(i) = ∠g(i), ∀x∈â„: ⌈x⌉ = −⌊−x⌋, α ∧ ¬β = ¬(¬α ∨ β), â„• ⊆ â„•â‚€ ⊂ ℤ ⊂ ℚ ⊂ ℠⊂ â„‚, ⊥ < a ≠ b ≡ c ≤ d ≪ ⊤ ⇒ (A ⇔ B), Combining characters: STARGΛ̊TE SG-1, a = v̇ = r̈, a⃑ ⊥ b⃑ [----------------------------|------------------------] ๠à¹à¸œà¹ˆà¸™à¸”ินฮั่นเสื่อมโทรมà¹à¸ªà¸™à¸ªà¸±à¸‡à¹€à¸§à¸Š พระปà¸à¹€à¸à¸¨à¸à¸­à¸‡à¸šà¸¹à¹Šà¸à¸¹à¹‰à¸‚ึ้นใหม่ สิบสองà¸à¸©à¸±à¸•ริย์à¸à¹ˆà¸­à¸™à¸«à¸™à¹‰à¸²à¹à¸¥à¸–ัดไป สององค์ไซร้โง่เขลาเบาปัà¸à¸à¸² Wide characters with difficult wrapping: Here we go then: コンニãƒãƒ コンニãƒãƒ コンニãƒãƒ コンニãƒãƒ コンニãƒãƒ コンニãƒãƒ コンニãƒãƒ コンニãƒãƒ コンニãƒãƒ コンニãƒãƒ コンニãƒãƒ コンニãƒãƒ コンニãƒãƒ コンニãƒãƒ コンニãƒãƒ Arabic and bidirectional text: (من مجمع الزوائد ومنبع الÙوائد للهيثمي ØŒ ج 1 ØŒ ص 74-84) عن جرير رضي الله عنه قال قال رسول الله صلى الله عليه وسلم: بني الاسلام على خمس شهادة ان لا اله الا الله واقام Mixed LTR and RTL text: جرير رضي back to LTR. East Asian Ambiguous characters: ¼½¾¼½¾¼½¾¼½¾¼½¾¼½¾¼½¾¼½¾¼½¾¼½¾ putty-0.76/test/vt100.txt0000644000175000017500000000157314072266313012160 00000000000000VT100 line drawing characters, actually using the VT100 escapes (B)0ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo lqqqqqqqqqqpoopqrssrqqqqqqqqqqwqqqqqqqqqqpoopqrssrqqqqqqqqqqk x x x x ooh, swirly! x top right corner x x x x tqqqqqqqqqqpoopqrssrqqqqqqqqqqnqqqqqqqqqqpoopqrssrqqqqqqqqqqu x x x x stuff down here x is quite inane x x x x mqqqqqqqqqqpoopqrssrqqqqqqqqqqvqqqqqqqqqqpoopqrssrqqqqqqqqqqj (This won't do the right thing in PuTTY's default UTF-8 translation; you'll just see lqqqk letters. It should work with a character set like ISO8859-1.) putty-0.76/test/windowchange.py0000755000175000017500000001641514072266313013600 00000000000000#!/usr/bin/env python3 # Interactive test program for assorted ancillary changes to the # terminal window - title, position, size, z-order, colour palette # etc. import argparse import sys import termios import os from time import sleep def send(s): sys.stdout.write(s) sys.stdout.flush() def query(s, lastchar=None): send(s) old_attrs = termios.tcgetattr(0) new_attrs = old_attrs.copy() new_attrs[3] &= ~(termios.ECHO | termios.ICANON) new_attrs[6][termios.VMIN] = 0 new_attrs[6][termios.VTIME] = 1 try: termios.tcsetattr(0, termios.TCSANOW, new_attrs) s = b"" while True: c = os.read(0, 1) if len(c) == 0: break s += c if lastchar is not None and c[0] == lastchar: break return s finally: termios.tcsetattr(0, termios.TCSANOW, old_attrs) def pause(prompt): input(prompt + (" " if len(prompt) > 0 else "") + "--More--") def main(): testnames = [ "title", "icon", "minimise", "maximise", "minquery", "setpalette", "querypalette", "winpos", "setwinsize", "querywinsize", "zorder", "mousereport", ] parser = argparse.ArgumentParser( description='Test PuTTY\'s ancillary terminal updates') parser.add_argument("test", choices=testnames, nargs="*", help='Sub-test to perform (default: all of them)') args = parser.parse_args() if len(args.test) == 0: dotest = lambda s: True else: testset = set(args.test) dotest = lambda s: s in testset if dotest("title"): send("\033]2;Title test 1\a") pause("Title test 1.") send("\033]2;Title test 2\a") pause("Title test 2.") if dotest("icon"): send("\033]1;Icon-title test 1\a") pause("Icon-title test 1.") send("\033]1;Icon-title test 2\a") pause("Icon-title test 2.") if dotest("minimise"): pause("About to minimise and restore.") send("\033[2t") sleep(2) send("\033[1t") if dotest("maximise"): pause("About to maximise.") send("\033[9;1t") pause("About to unmaximise.") send("\033[9;0t") if dotest("minquery"): pause("Query minimised status.") s = query("\033[11t") if s == b"\033[1t": print("Reply: not minimised") elif s == b"\033[2t": print("Reply: minimised") else: print("Reply unknown:", repr(s)) if dotest("setpalette"): print("Palette testing:") teststr = "" for i in range(8): teststr += "\033[0;{:d}m{:X}".format(i+30, i) for i in range(8): teststr += "\033[1;{:d}m{:X}".format(i+30, i+8) teststr += "\033[0;39mG\033[1mH\033[0;7mI\033[1mJ" teststr += "\033[m" teststr += " " * 9 + "K" + "\b" * 10 for c in "0123456789ABCDEFGHIJKL": pause("Base: " + teststr) send("\033]P" + c + "ff0000") pause(c + " red: " + teststr) send("\033]P" + c + "00ff00") pause(c + " green: " + teststr) send("\033]P" + c + "0000ff") pause(c + " blue: " + teststr) send("\033]R") if dotest("querypalette"): send("\033]R") nfail = 262 for i in range(nfail): s = query("\033]4;{:d};?\a".format(i), 7).decode("ASCII") prefix, suffix = "\033]4;{:d};rgb:".format(i), "\a" if s.startswith(prefix) and s.endswith(suffix): rgb = [int(word, 16) for word in s[len(prefix):-len(suffix)].split("/")] if 0 <= i < 8: j = i expected_rgb = [0xbbbb * ((j>>n) & 1) for n in range(3)] elif 0 <= i-8 < 8: j = i-8 expected_rgb = [0x5555 + 0xaaaa * ((j>>n) & 1) for n in range(3)] elif 0 <= i-16 < 216: j = i-16 expected_rgb = [(0 if v==0 else 0x3737+0x2828*v) for v in (j // 36, j // 6 % 6, j % 6)] elif 0 <= i-232 < 24: j = i-232 expected_rgb = [j * 0x0a0a + 0x0808] * 3 else: expected_rgb = [ [0xbbbb, 0xbbbb, 0xbbbb], [0xffff, 0xffff, 0xffff], [0x0000, 0x0000, 0x0000], [0x5555, 0x5555, 0x5555], [0x0000, 0x0000, 0x0000], [0x0000, 0xffff, 0x0000], ][i-256] if expected_rgb == rgb: nfail -= 1 else: print(i, "unexpected: {:04x} {:04x} {:04x}".format(*rgb)) else: print(i, "bad format:", repr(s)) print("Query palette: {:d} colours wrong".format(nfail)) if dotest("winpos"): print("Query window position: ", end="") s = query("\033[13t").decode("ASCII") prefix, suffix = "\033[3;", "t" if s.startswith(prefix) and s.endswith(suffix): x, y = [int(word) for word in s[len(prefix):-len(suffix)].split(";")] print("x={:d} y={:d}".format(x, y)) else: print("bad format:", repr(s)) x, y = 50, 50 pause("About to move window.") send("\033[3;{:d};{:d}t".format(x+50, y+50)) pause("About to move it back again.") send("\033[3;{:d};{:d}t".format(x, y)) if dotest("setwinsize"): pause("About to DECCOLM to 132 cols.") send("\033[?3h") pause("About to DECCOLM to 80 cols.") send("\033[?3l") pause("About to DECCOLM to 132 cols again.") send("\033[?3h") pause("About to reset the terminal.") send("\033c") pause("About to DECSLPP to 30 rows.") send("\033[30t") pause("About to DECSLPP to 24 rows.") send("\033[24t") pause("About to DECSNLS to 30 rows.") send("\033[*30|") pause("About to DECSNLS to 24 rows.") send("\033[*24|") pause("About to DECSCPP to 90 cols.") send("\033[$90|") pause("About to DECSCPP to 80 cols.") send("\033[$80|") pause("About to xterm to 90x30.") send("\033[8;30;90t") pause("About to xterm back to 80x24.") send("\033[8;24;80t") if dotest("querywinsize"): print("Query window size: ", end="") s = query("\033[14t").decode("ASCII") prefix, suffix = "\033[4;", "t" if s.startswith(prefix) and s.endswith(suffix): h, w = [int(word) for word in s[len(prefix):-len(suffix)].split(";")] print("w={:d} h={:d}".format(w, h)) else: print("bad format:", repr(s)) if dotest("zorder"): pause("About to lower to bottom and then raise.") send("\033[6t") sleep(2) send("\033[5t") if dotest("mousereport"): send("\033[?1000h") pause("Mouse reporting on: expect clicks to generate input") send("\033[?1000l") pause("Mouse reporting off: expect clicks to select") if __name__ == '__main__': main() putty-0.76/test/sclog/0000755000175000017500000000000014072266316011731 500000000000000putty-0.76/test/sclog/.gitignore0000644000175000017500000000013514072266313013635 00000000000000/.ninja_* /CMakeFiles /CMakeCache.txt /cmake_install.cmake /*.ninja /*.ldscript /libsclog.so putty-0.76/test/sclog/CMakeLists.txt0000644000175000017500000000076214072266313014413 00000000000000# CMake script for the 'sclog' DynamoRIO instrumentation system that # goes with the PuTTY test binary 'testsc'. For build instructions see # the comment at the top of testsc.c. cmake_minimum_required(VERSION 3.5) find_package(DynamoRIO) if (NOT DynamoRIO_FOUND) message(FATAL_ERROR "DynamoRIO not found") endif() add_library(sclog SHARED sclog.c) configure_DynamoRIO_client(sclog) foreach(extension drmgr drsyms drreg drutil drwrap) use_DynamoRIO_extension(sclog ${extension}) endforeach() putty-0.76/test/sclog/sclog.c0000644000175000017500000004776214072266313013141 00000000000000/* * sclog: the DynamoRIO instrumentation system that goes with the * PuTTY test binary 'testsc'. * * For general discussion and build instructions, see the comment at * the top of testsc.c. */ #include #include #include "dr_api.h" #include "drmgr.h" #include "drsyms.h" #include "drreg.h" #include "drutil.h" #include "drwrap.h" /* * The file we're currently logging to, if any. */ static file_t outfile = INVALID_FILE; /* * A counter which we can increment and decrement around any library * function we don't want to log the details of what happens inside. * Mainly this is for memory allocation functions, which will diverge * control depending on the progress of their search for something * they can allocate. */ size_t logging_paused = 0; /* * This log message appears at the start of whatever DynamoRIO * considers a 'basic block', i.e. a sequence of instructions with no * branches. Logging these is cheaper than logging every single * instruction, and should still be adequate to detect any divergence * of control flow. */ static void log_pc(const char *loc) { if (outfile == INVALID_FILE || logging_paused) return; dr_fprintf(outfile, "%s: start basic block\n", loc); } /* * Hardware division instructions are unlikely to run in time * independent of the data, so we log both their parameters. */ static void log_div(uint n, uint d, const char *loc) { if (outfile == INVALID_FILE || logging_paused) return; dr_fprintf(outfile, "%s: divide %"PRIuMAX" / %"PRIuMAX"\n", loc, (uintmax_t)n, (uintmax_t)d); } /* * Register-controlled shift instructions are not reliably one cycle * long on all platforms, so we log the shift couhnt. */ static void log_var_shift(uint sh, const char *loc) { if (outfile == INVALID_FILE || logging_paused) return; dr_fprintf(outfile, "%s: var shift by %"PRIuMAX"\n", loc, (uintmax_t)sh); } /* * We need to log memory accesses, so as to detect data-dependent * changes in the access pattern (e.g. incautious use of a lookup * table). But one thing we _can't_ control for perfectly is that in * two successive runs of the same crypto primitive, malloc may be * called, and may return different addresses - which of course is not * dependent on the data (unless the size of the allocated block * does). * * So we track all the memory allocations that happen during logging, * and any addresses accessed within those blocks are logged as * something along the lines of 'n bytes from the start of the mth * allocation'. * * Allocations that happened before a given log file was opened are * not tracked. The program under test will ensure that any of those * used by the primitive are at the same address in all runs anyway. */ struct allocation { /* * We store the list of allocations in a linked list, so we can * look them up by address, and delete them as they're freed. * * A balanced binary search tree would be faster, but this is * easier to get right first time! */ struct allocation *prev, *next; uintptr_t start, size, index; }; static struct allocation alloc_ends[1] = { alloc_ends, alloc_ends, 0, 0, 0 }; static uintptr_t next_alloc_index = 0; static void free_allocation(struct allocation *alloc) { alloc->next->prev = alloc->prev; alloc->prev->next = alloc->next; dr_global_free(alloc, sizeof(struct allocation)); } /* * Wrap the log_set_file() function in testsc.c, and respond to it by * opening or closing log files. */ static void wrap_logsetfile(void *wrapctx, void **user_data) { if (outfile) { dr_close_file(outfile); outfile = INVALID_FILE; } const char *outfilename = drwrap_get_arg(wrapctx, 0); if (outfilename) { outfile = dr_open_file(outfilename, DR_FILE_WRITE_OVERWRITE); DR_ASSERT(outfile != INVALID_FILE); } /* * Reset the allocation list to empty, whenever we open or close a * log file. */ while (alloc_ends->next != alloc_ends) free_allocation(alloc_ends->next); next_alloc_index = 0; } /* * Wrap the dry_run() function in testsc.c, to tell it we're here. */ static void wrap_dryrun(void *wrapctx, void *user_data) { drwrap_set_retval(wrapctx, (void *)0); } /* * Look up the memory allocation record corresponding to an address. */ static struct allocation *find_allocation(const void *ptr) { uintptr_t address = (uintptr_t)ptr; for (struct allocation *alloc = alloc_ends->next; alloc != alloc_ends; alloc = alloc->next) { if (alloc && address - alloc->start < alloc->size) return alloc; } return NULL; } /* * Log a memory access. */ static void log_mem(app_pc addr, uint size, uint write, const char *loc) { if (outfile == INVALID_FILE || logging_paused) return; struct allocation *alloc = find_allocation((const void *)addr); if (!alloc) { dr_fprintf(outfile, "%s: %s %"PRIuMAX" @ %"PRIxMAX"\n", loc, write ? "store" : "load", (uintmax_t)size, (uintmax_t)addr); } else { dr_fprintf(outfile, "%s: %s %"PRIuMAX" @ allocations[%"PRIuPTR"]" " + %"PRIxMAX"\n", loc, write ? "store" : "load", (uintmax_t)size, alloc->index, (uintmax_t)(addr - alloc->start)); } } /* * Record the allocation of some memory. (Common code between malloc * and realloc.) */ static void allocated(void *ptr, size_t size) { if (outfile == INVALID_FILE) return; /* no need to track allocations outside a logging interval */ struct allocation *alloc = dr_global_alloc(sizeof(struct allocation)); alloc->start = (uintptr_t)ptr; alloc->size = size; alloc->index = next_alloc_index++; alloc->prev = alloc_ends->prev; alloc->next = alloc_ends; alloc->prev->next = alloc->next->prev = alloc; } /* * Record that memory has been freed. Note that we may free something * that was allocated when we weren't logging, so we must cope with * find_allocation returning NULL. */ static void freed(void *ptr) { struct allocation *alloc = find_allocation(ptr); if (alloc) free_allocation(alloc); } /* * The actual wrapper functions for malloc, realloc and free. */ static void wrap_malloc_pre(void *wrapctx, void **user_data) { logging_paused++; *user_data = drwrap_get_arg(wrapctx, 0); dr_fprintf(outfile, "malloc %"PRIuMAX"\n", (uintmax_t)*user_data); } static void wrap_free_pre(void *wrapctx, void **user_data) { logging_paused++; void *ptr = drwrap_get_arg(wrapctx, 0); freed(ptr); } static void wrap_realloc_pre(void *wrapctx, void **user_data) { logging_paused++; void *ptr = drwrap_get_arg(wrapctx, 0); freed(ptr); *user_data = drwrap_get_arg(wrapctx, 1); dr_fprintf(outfile, "realloc %"PRIuMAX"\n", (uintmax_t)*user_data); } static void wrap_alloc_post(void *wrapctx, void *user_data) { void *ptr = drwrap_get_retval(wrapctx); if (!ptr) return; size_t size = (size_t)user_data; allocated(ptr, size); logging_paused--; } /* * We wrap the C library function memset, because I've noticed that at * least one optimised implementation of it diverges control flow * internally based on what appears to be the _alignment_ of the input * pointer - and that alignment check can vary depending on the * addresses of allocated blocks. So I can't guarantee no divergence * of control flow inside memset if malloc doesn't return the same * values, and instead I just have to trust that memset isn't reading * the contents of the block and basing control flow decisions on that. */ static void wrap_memset_pre(void *wrapctx, void **user_data) { uint was_already_paused = logging_paused++; if (outfile == INVALID_FILE || was_already_paused) return; const void *addr = drwrap_get_arg(wrapctx, 0); size_t size = (size_t)drwrap_get_arg(wrapctx, 2); struct allocation *alloc = find_allocation(addr); if (!alloc) { dr_fprintf(outfile, "memset %"PRIuMAX" @ %"PRIxMAX"\n", (uintmax_t)size, (uintmax_t)addr); } else { dr_fprintf(outfile, "memset %"PRIuMAX" @ allocations[%"PRIuPTR"]" " + %"PRIxMAX"\n", (uintmax_t)size, alloc->index, (uintmax_t)(addr - alloc->start)); } } /* * Common post-wrapper function for memset and free, whose entire * function is to unpause the logging. */ static void unpause_post(void *wrapctx, void *user_data) { logging_paused--; } /* * Make a string representation of the address of an instruction, * including a function name and/or a file+line combination if * possible. These will be logged alongside every act of interest * where we can make one. */ static void instr_format_location(instr_t *instr, char **outloc) { app_pc addr = (app_pc)instr_get_app_pc(instr); char location[2048], symbol[512], fileline[1024]; bool got_sym = false, got_line = false; if (*outloc) return; symbol[0] = '\0'; fileline[0] = '\0'; module_data_t *data = dr_lookup_module(addr); if (data) { drsym_info_t sym; char file[MAXIMUM_PATH]; sym.struct_size = sizeof(sym); sym.name = symbol; sym.name_size = sizeof(symbol); sym.file = file; sym.file_size = sizeof(file); drsym_error_t status = drsym_lookup_address( data->full_path, addr - data->start, &sym, DRSYM_DEFAULT_FLAGS); got_line = (status == DRSYM_SUCCESS); got_sym = got_line || status == DRSYM_ERROR_LINE_NOT_AVAILABLE; if (got_line) snprintf(fileline, sizeof(fileline), " = %s:%"PRIu64, file, (uint64_t)sym.line); } snprintf(location, sizeof(location), "%"PRIx64"%s%s%s", (uint64_t)addr, got_sym ? " = " : "", got_sym ? symbol : "", fileline); size_t len = strlen(location) + 1; char *loc = dr_global_alloc(len); memcpy(loc, location, len); *outloc = loc; } /* * Function that tests a single operand of an instruction to see if * it's a memory reference, and if so, adds a call to log_mem. */ static void try_mem_opnd( void *drcontext, instrlist_t *bb, instr_t *instr, char **loc, opnd_t opnd, bool write) { if (!opnd_is_memory_reference(opnd)) return; instr_format_location(instr, loc); reg_id_t r0, r1; drreg_status_t st; st = drreg_reserve_register(drcontext, bb, instr, NULL, &r0); DR_ASSERT(st == DRREG_SUCCESS); st = drreg_reserve_register(drcontext, bb, instr, NULL, &r1); DR_ASSERT(st == DRREG_SUCCESS); bool ok = drutil_insert_get_mem_addr(drcontext, bb, instr, opnd, r0, r1); DR_ASSERT(ok); uint size = drutil_opnd_mem_size_in_bytes(opnd, instr); dr_insert_clean_call( drcontext, bb, instr, (void *)log_mem, false, 4, opnd_create_reg(r0), OPND_CREATE_INT32(size), OPND_CREATE_INT32(write), OPND_CREATE_INTPTR(*loc)); st = drreg_unreserve_register(drcontext, bb, instr, r1); DR_ASSERT(st == DRREG_SUCCESS); st = drreg_unreserve_register(drcontext, bb, instr, r0); DR_ASSERT(st == DRREG_SUCCESS); } /* * The main function called to instrument each machine instruction. */ static dr_emit_flags_t instrument_instr( void *drcontext, void *tag, instrlist_t *bb, instr_t *instr, bool for_trace, bool translating, void *user_data) { char *loc = NULL; /* * If this instruction is the first in its basic block, call * log_pc to record that we're executing this block at all. */ if (drmgr_is_first_instr(drcontext, instr)) { instr_format_location(instr, &loc); dr_insert_clean_call( drcontext, bb, instr, (void *)log_pc, false, 1, OPND_CREATE_INTPTR(loc)); } /* * If the instruction reads or writes memory, log its access. */ if (instr_reads_memory(instr) || instr_writes_memory(instr)) { for (int i = 0, limit = instr_num_srcs(instr); i < limit; i++) try_mem_opnd(drcontext, bb, instr, &loc, instr_get_src(instr, i), instr_writes_memory(instr)); for (int i = 0, limit = instr_num_dsts(instr); i < limit; i++) try_mem_opnd(drcontext, bb, instr, &loc, instr_get_dst(instr, i), instr_writes_memory(instr)); } /* * Now do opcode-specific checks. */ int opcode = instr_get_opcode(instr); switch (opcode) { #if defined(X86) case OP_div: case OP_idiv: /* * x86 hardware divisions. The operand order for DR's * representation of these seem to be: 0 = denominator, 1 = * numerator MSW, 2 = numerator LSW. */ instr_format_location(instr, &loc); dr_insert_clean_call( drcontext, bb, instr, (void *)log_div, false, 3, instr_get_src(instr, 2), instr_get_src(instr, 0), OPND_CREATE_INTPTR(loc)); break; #endif #if defined(AARCH64) case OP_sdiv: case OP_udiv: /* * AArch64 hardware divisions. 0 = numerator, 1 = denominator. */ instr_format_location(instr, &loc); dr_insert_clean_call( drcontext, bb, instr, (void *)log_div, false, 3, instr_get_src(instr, 0), instr_get_src(instr, 1), OPND_CREATE_INTPTR(loc)); break; #endif #if defined(X86) case OP_shl: case OP_shr: case OP_sar: case OP_shlx: case OP_shrx: case OP_sarx: case OP_rol: case OP_ror: case OP_rcl: case OP_rcr: { /* * Shift instructions. If they're register-controlled, log the * shift count. */ opnd_t shiftcount = instr_get_src(instr, 0); if (!opnd_is_immed(shiftcount)) { reg_id_t r0; drreg_status_t st; st = drreg_reserve_register(drcontext, bb, instr, NULL, &r0); DR_ASSERT(st == DRREG_SUCCESS); opnd_t op_r0 = opnd_create_reg(r0); instr_t *movzx = INSTR_CREATE_movzx(drcontext, op_r0, shiftcount); instr_set_translation(movzx, instr_get_app_pc(instr)); instrlist_preinsert(bb, instr, movzx); instr_format_location(instr, &loc); dr_insert_clean_call( drcontext, bb, instr, (void *)log_var_shift, false, 2, op_r0, OPND_CREATE_INTPTR(loc)); st = drreg_unreserve_register(drcontext, bb, instr, r0); DR_ASSERT(st == DRREG_SUCCESS); } break; } #endif #if defined(AARCH64) case OP_lslv: case OP_asrv: case OP_lsrv: case OP_rorv: { /* * AArch64 variable shift instructions. */ opnd_t shiftcount = instr_get_src(instr, 1); DR_ASSERT(opnd_is_reg(shiftcount)); reg_id_t shiftreg = opnd_get_reg(shiftcount); if (shiftreg >= DR_REG_W0 && shiftreg <= DR_REG_WSP) shiftreg = reg_32_to_64(shiftreg); instr_format_location(instr, &loc); dr_insert_clean_call( drcontext, bb, instr, (void *)log_var_shift, false, 2, opnd_create_reg(shiftreg), OPND_CREATE_INTPTR(loc)); break; } #endif } return DR_EMIT_DEFAULT; } static void exit_event(void) { if (outfile != INVALID_FILE) { dr_fprintf(outfile, "exit while recording enabled\n"); dr_close_file(outfile); outfile = INVALID_FILE; } drsym_exit(); drreg_exit(); drwrap_exit(); drutil_exit(); drmgr_exit(); } /* * We ask DR to expand any x86 string instructions like REP MOVSB, so * that we can log all the individual memory accesses without getting * confused. */ static dr_emit_flags_t expand_rep_movsb( void *drcontext, void *tag, instrlist_t *bb, bool for_trace, bool translating) { bool ok = drutil_expand_rep_string(drcontext, bb); DR_ASSERT(ok); return DR_EMIT_DEFAULT; } typedef void (*prewrapper_t)(void *wrapctx, void **user_data); typedef void (*postwrapper_t)(void *wrapctx, void *user_data); /* * Helper function for bulk use of drwrap. */ static void try_wrap_fn(const module_data_t *module, const char *name, prewrapper_t pre, postwrapper_t post, bool *done) { if (*done) return; size_t offset; drsym_error_t status = drsym_lookup_symbol( module->full_path, name, &offset, DRSYM_DEFAULT_FLAGS); if (status == DRSYM_SUCCESS) { app_pc notify_fn = module->start + offset; bool ok = drwrap_wrap(notify_fn, pre, post); DR_ASSERT(ok); *done = true; } } /* * When each module (e.g. shared library) is loaded, try to wrap all * the functions we care about. For each one, we keep a static bool * that will stop us trying again once we've found it the first time. */ static void load_module( void *drcontext, const module_data_t *module, bool loaded) { bool libc = !strncmp(dr_module_preferred_name(module), "libc", 4); #define TRY_WRAP(fn, pre, post) do \ { \ static bool done_this_one = false; \ try_wrap_fn(module, fn, pre, post, &done_this_one); \ } while (0) if (loaded) { TRY_WRAP("log_to_file_real", wrap_logsetfile, NULL); TRY_WRAP("dry_run_real", NULL, wrap_dryrun); if (libc) { TRY_WRAP("malloc", wrap_malloc_pre, wrap_alloc_post); TRY_WRAP("realloc", wrap_realloc_pre, wrap_alloc_post); TRY_WRAP("free", wrap_free_pre, unpause_post); TRY_WRAP("memset", wrap_memset_pre, unpause_post); /* * More strangely named versions of standard C library * functions, which I've observed in practice to be where the * calls end up. I think these are probably selected by * STT_IFUNC in libc.so, so that the normally named version of * the function is never reached at all. * * This list is not expected to be complete. If you re-run * this test on a different platform and find control flow * diverging inside some libc function that looks as if it's * another name for malloc or memset or whatever, then you may * need to add more aliases here to stop the test failing. */ TRY_WRAP("__GI___libc_malloc", wrap_malloc_pre, wrap_alloc_post); TRY_WRAP("__libc_malloc", wrap_malloc_pre, wrap_alloc_post); TRY_WRAP("__GI___libc_realloc", wrap_realloc_pre, wrap_alloc_post); TRY_WRAP("__GI___libc_free", wrap_free_pre, unpause_post); TRY_WRAP("__memset_sse2_unaligned", wrap_memset_pre, unpause_post); TRY_WRAP("__memset_sse2", wrap_memset_pre, unpause_post); TRY_WRAP("cfree", wrap_free_pre, unpause_post); } } } /* * Main entry point that sets up all the facilities we need. */ DR_EXPORT void dr_client_main(client_id_t id, int argc, const char **argv) { dr_set_client_name( "Time-sensitive activity logger for PuTTY crypto testing", "https://www.chiark.greenend.org.uk/~sgtatham/putty/"); outfile = INVALID_FILE; bool ok = drmgr_init(); DR_ASSERT(ok); /* * Run our main instrumentation pass with lower priority than * drwrap, so that we don't start logging the inside of a function * whose drwrap pre-wrapper would have wanted to disable logging. */ drmgr_priority_t pri = {sizeof(pri), "sclog", NULL, NULL, DRMGR_PRIORITY_INSERT_DRWRAP+1}; ok = drmgr_register_bb_instrumentation_event( NULL, instrument_instr, &pri); DR_ASSERT(ok); ok = drutil_init(); DR_ASSERT(ok); ok = drwrap_init(); DR_ASSERT(ok); drsym_error_t symstatus = drsym_init(0); DR_ASSERT(symstatus == DRSYM_SUCCESS); dr_register_exit_event(exit_event); drreg_options_t ops = { sizeof(ops), 3, false }; drreg_status_t regstatus = drreg_init(&ops); DR_ASSERT(regstatus == DRREG_SUCCESS); drmgr_register_module_load_event(load_module); ok = drmgr_register_bb_app2app_event(expand_rep_movsb, NULL); DR_ASSERT(ok); } putty-0.76/unix/0000755000175000017500000000000014072266316010626 500000000000000putty-0.76/unix/configure0000755000175000017500000000010114072266313012442 00000000000000#!/bin/sh $(echo "$0" | sed '$s!configure$!../configure!') "$@" putty-0.76/unix/gtkapp.c0000644000175000017500000002604314072266313012202 00000000000000/* * gtkapp.c: a top-level front end to GUI PuTTY and pterm, using * GtkApplication. Suitable for OS X. Currently unfinished. * * (You could run it on ordinary Linux GTK too, in principle, but I * don't think it would be particularly useful to do so, even once * it's fully working.) */ /* To build on OS X, you will need a build environment with GTK 3 and gtk-mac-bundler, and also Halibut on the path (to build the man pages, without which the standard Makefile will complain). Then, from a clean checkout, do this: ./mkfiles.pl -U --with-quartz make -C icons icns make -C doc make and you should get unix/PuTTY.app and unix/PTerm.app as output. */ /* TODO list for a sensible GTK3 PuTTY/pterm on OS X: Still to do on the application menu bar: items that have to vary with context or user action (saved sessions and mid-session special commands), and disabling/enabling the main actions in parallel with their counterparts in the Ctrl-rightclick context menu. Mouse wheel events and trackpad scrolling gestures don't work quite right in the terminal drawing area. This seems to be a combination of two things, neither of which I completely understand yet. Firstly, on OS X GTK my trackpad seems to generate GDK scroll events for which gdk_event_get_scroll_deltas returns integers rather than integer multiples of 1/30, so we end up scrolling by very large amounts; secondly, the window doesn't seem to receive a GTK "draw" event until after the entire scroll gesture is complete, which means we don't get constant visual feedback on how much we're scrolling by. There doesn't seem to be a resize handle on terminal windows. Then again, they do seem to _be_ resizable; the handle just isn't shown. Perhaps that's a feature (certainly in a scrollbarless configuration the handle gets in the way of the bottom right character cell in the terminal itself), but it would be nice to at least understand _why_ it happens and perhaps include an option to put it back again. A slight oddity with menus that pop up directly under the mouse pointer: mousing over the menu items doesn't highlight them initially, but if I mouse off the menu and back on (without un-popping-it-up) then suddenly that does work. I don't know if this is something I can fix, though; it might very well be a quirk of the underlying GTK. Does OS X have a standard system of online help that I could tie into? Need to work out what if anything we can do with Pageant on OS X. Perhaps it's too much bother and we should just talk to the system-provided SSH agent? Or perhaps not. Nice-to-have: a custom right-click menu from the application's dock tile, listing the saved sessions for quick launch. As far as I know there's nothing built in to GtkApplication that can produce this, but it's possible we might be able to drop a piece of native Cocoa code in under ifdef, substituting an application delegate of our own which forwards all methods we're not interested in to the GTK-provided one? At the point where this becomes polished enough to publish pre-built, I suppose I'll have to look into OS X code signing. https://wiki.gnome.org/Projects/GTK%2B/OSX/Bundling has some links. */ #include #include #include #include #define MAY_REFER_TO_GTK_IN_HEADERS #include "putty.h" #include "gtkmisc.h" char *x_get_default(const char *key) { return NULL; } const bool buildinfo_gtk_relevant = true; #if !GTK_CHECK_VERSION(3,0,0) /* This front end only works in GTK 3. If that's not what we've got, * it's easier to just turn this program into a trivial stub by ifdef * in the source than it is to remove it in the makefile edifice. */ int main(int argc, char **argv) { fprintf(stderr, "GtkApplication frontend doesn't work pre-GTK3\n"); return 1; } GtkWidget *make_gtk_toplevel_window(GtkFrontend *frontend) { return NULL; } void launch_duplicate_session(Conf *conf) {} void launch_new_session(void) {} void launch_saved_session(const char *str) {} void session_window_closed(void) {} void window_setup_error(const char *errmsg) {} #else /* GTK_CHECK_VERSION(3,0,0) */ static void startup(GApplication *app, gpointer user_data) { GMenu *menubar, *menu, *section; menubar = g_menu_new(); menu = g_menu_new(); g_menu_append_submenu(menubar, "File", G_MENU_MODEL(menu)); section = g_menu_new(); g_menu_append_section(menu, NULL, G_MENU_MODEL(section)); g_menu_append(section, "New Window", "app.newwin"); menu = g_menu_new(); g_menu_append_submenu(menubar, "Edit", G_MENU_MODEL(menu)); section = g_menu_new(); g_menu_append_section(menu, NULL, G_MENU_MODEL(section)); g_menu_append(section, "Copy", "win.copy"); g_menu_append(section, "Paste", "win.paste"); g_menu_append(section, "Copy All", "win.copyall"); menu = g_menu_new(); g_menu_append_submenu(menubar, "Window", G_MENU_MODEL(menu)); section = g_menu_new(); g_menu_append_section(menu, NULL, G_MENU_MODEL(section)); g_menu_append(section, "Restart Session", "win.restart"); g_menu_append(section, "Duplicate Session", "win.duplicate"); section = g_menu_new(); g_menu_append_section(menu, NULL, G_MENU_MODEL(section)); g_menu_append(section, "Change Settings", "win.changesettings"); if (use_event_log) { section = g_menu_new(); g_menu_append_section(menu, NULL, G_MENU_MODEL(section)); g_menu_append(section, "Event Log", "win.eventlog"); } section = g_menu_new(); g_menu_append_section(menu, NULL, G_MENU_MODEL(section)); g_menu_append(section, "Clear Scrollback", "win.clearscrollback"); g_menu_append(section, "Reset Terminal", "win.resetterm"); #if GTK_CHECK_VERSION(3,12,0) #define SET_ACCEL(app, command, accel) do \ { \ static const char *const accels[] = { accel, NULL }; \ gtk_application_set_accels_for_action( \ GTK_APPLICATION(app), command, accels); \ } while (0) #else /* The Gtk function used above was new in 3.12; the one below * was deprecated from 3.14. */ #define SET_ACCEL(app, command, accel) \ gtk_application_add_accelerator(GTK_APPLICATION(app), accel, \ command, NULL) #endif SET_ACCEL(app, "app.newwin", "n"); SET_ACCEL(app, "win.copy", "c"); SET_ACCEL(app, "win.paste", "v"); #undef SET_ACCEL gtk_application_set_menubar(GTK_APPLICATION(app), G_MENU_MODEL(menubar)); } #define WIN_ACTION_LIST(X) \ X("copy", MA_COPY) \ X("paste", MA_PASTE) \ X("copyall", MA_COPY_ALL) \ X("duplicate", MA_DUPLICATE_SESSION) \ X("restart", MA_RESTART_SESSION) \ X("changesettings", MA_CHANGE_SETTINGS) \ X("clearscrollback", MA_CLEAR_SCROLLBACK) \ X("resetterm", MA_RESET_TERMINAL) \ X("eventlog", MA_EVENT_LOG) \ /* end of list */ #define WIN_ACTION_CALLBACK(name, id) \ static void win_action_cb_ ## id(GSimpleAction *a, GVariant *p, gpointer d) \ { app_menu_action(d, id); } WIN_ACTION_LIST(WIN_ACTION_CALLBACK) #undef WIN_ACTION_CALLBACK static const GActionEntry win_actions[] = { #define WIN_ACTION_ENTRY(name, id) { name, win_action_cb_ ## id }, WIN_ACTION_LIST(WIN_ACTION_ENTRY) #undef WIN_ACTION_ENTRY }; static GtkApplication *app; GtkWidget *make_gtk_toplevel_window(GtkFrontend *frontend) { GtkWidget *win = gtk_application_window_new(app); g_action_map_add_action_entries(G_ACTION_MAP(win), win_actions, G_N_ELEMENTS(win_actions), frontend); return win; } void launch_duplicate_session(Conf *conf) { assert(!dup_check_launchable || conf_launchable(conf)); g_application_hold(G_APPLICATION(app)); new_session_window(conf_copy(conf), NULL); } void session_window_closed(void) { g_application_release(G_APPLICATION(app)); } static void post_initial_config_box(void *vctx, int result) { Conf *conf = (Conf *)vctx; if (result > 0) { new_session_window(conf, NULL); } else if (result == 0) { conf_free(conf); g_application_release(G_APPLICATION(app)); } } void launch_saved_session(const char *str) { Conf *conf = conf_new(); do_defaults(str, conf); g_application_hold(G_APPLICATION(app)); if (!conf_launchable(conf)) { initial_config_box(conf, post_initial_config_box, conf); } else { new_session_window(conf, NULL); } } void launch_new_session(void) { /* Same as launch_saved_session except that we pass NULL to * do_defaults. */ launch_saved_session(NULL); } void new_app_win(GtkApplication *app) { launch_new_session(); } static void window_setup_error_callback(void *vctx, int result) { g_application_release(G_APPLICATION(app)); } void window_setup_error(const char *errmsg) { create_message_box(NULL, "Error creating session window", errmsg, string_width("Some sort of fiddly error message that " "might be technical"), true, &buttons_ok, window_setup_error_callback, NULL); } static void activate(GApplication *app, gpointer user_data) { new_app_win(GTK_APPLICATION(app)); } static void newwin_cb(GSimpleAction *action, GVariant *parameter, gpointer user_data) { new_app_win(GTK_APPLICATION(user_data)); } static void quit_cb(GSimpleAction *action, GVariant *parameter, gpointer user_data) { g_application_quit(G_APPLICATION(user_data)); } static void about_cb(GSimpleAction *action, GVariant *parameter, gpointer user_data) { about_box(NULL); } static const GActionEntry app_actions[] = { { "newwin", newwin_cb }, { "about", about_cb }, { "quit", quit_cb }, }; int main(int argc, char **argv) { int status; /* Call the function in ux{putty,pterm}.c to do app-type * specific setup */ setup(false); /* false means we are not a one-session process */ if (argc > 1) { pty_osx_envrestore_prefix = argv[--argc]; } { const char *home = getenv("HOME"); if (home) { if (chdir(home)) {} } } gtkcomm_setup(); app = gtk_application_new("org.tartarus.projects.putty.macputty", G_APPLICATION_FLAGS_NONE); g_signal_connect(app, "activate", G_CALLBACK(activate), NULL); g_signal_connect(app, "startup", G_CALLBACK(startup), NULL); g_action_map_add_action_entries(G_ACTION_MAP(app), app_actions, G_N_ELEMENTS(app_actions), app); status = g_application_run(G_APPLICATION(app), argc, argv); g_object_unref(app); return status; } #endif /* GTK_CHECK_VERSION(3,0,0) */ putty-0.76/unix/gtkask.c0000644000175000017500000004706114072266313012203 00000000000000/* * GTK implementation of a GUI password/passphrase prompt. */ #include #include #include #include #include #include #if !GTK_CHECK_VERSION(3,0,0) #include #endif #include "defs.h" #include "gtkfont.h" #include "gtkcompat.h" #include "gtkmisc.h" #include "putty.h" #include "ssh.h" #include "misc.h" #define N_DRAWING_AREAS 3 struct drawing_area_ctx { GtkWidget *area; #ifndef DRAW_DEFAULT_CAIRO GdkColor *cols; #endif int width, height; enum { NOT_CURRENT, CURRENT, GREYED_OUT } state; }; struct askpass_ctx { GtkWidget *dialog, *promptlabel; struct drawing_area_ctx drawingareas[N_DRAWING_AREAS]; int active_area; #if GTK_CHECK_VERSION(2,0,0) GtkIMContext *imc; #endif #ifndef DRAW_DEFAULT_CAIRO GdkColormap *colmap; GdkColor cols[3]; #endif char *error_message; /* if we finish without a passphrase */ char *passphrase; /* if we finish with one */ int passlen, passsize; #if GTK_CHECK_VERSION(3,20,0) GdkSeat *seat; /* for gdk_seat_grab */ #elif GTK_CHECK_VERSION(3,0,0) GdkDevice *keyboard; /* for gdk_device_grab */ #endif int nattempts; }; static prng *keypress_prng = NULL; static void feed_keypress_prng(void *data, int size) { put_data(keypress_prng, data, size); } void random_add_noise(NoiseSourceId source, const void *noise, int length) { if (keypress_prng) prng_add_entropy(keypress_prng, source, make_ptrlen(noise, length)); } static void setup_keypress_prng(void) { keypress_prng = prng_new(&ssh_sha256); prng_seed_begin(keypress_prng); noise_get_heavy(feed_keypress_prng); prng_seed_finish(keypress_prng); } static void cleanup_keypress_prng(void) { prng_free(keypress_prng); } static uint64_t keypress_prng_value(void) { /* * Don't actually put the passphrase keystrokes themselves into * the PRNG; that doesn't seem like the course of wisdom when * that's precisely what the information displayed on the screen * is trying _not_ to be correlated to. */ noise_ultralight(NOISE_SOURCE_KEY, 0); uint8_t data[8]; prng_read(keypress_prng, data, 8); return GET_64BIT_MSB_FIRST(data); } static int choose_new_area(int prev_area) { int reduced = keypress_prng_value() % (N_DRAWING_AREAS - 1); return (prev_area + 1 + reduced) % N_DRAWING_AREAS; } static void visually_acknowledge_keypress(struct askpass_ctx *ctx) { int new_active = choose_new_area(ctx->active_area); ctx->drawingareas[ctx->active_area].state = NOT_CURRENT; gtk_widget_queue_draw(ctx->drawingareas[ctx->active_area].area); ctx->drawingareas[new_active].state = CURRENT; gtk_widget_queue_draw(ctx->drawingareas[new_active].area); ctx->active_area = new_active; } static int last_char_len(struct askpass_ctx *ctx) { /* * GTK always encodes in UTF-8, so we can do this in a fixed way. */ int i; assert(ctx->passlen > 0); i = ctx->passlen - 1; while ((unsigned)((unsigned char)ctx->passphrase[i] - 0x80) < 0x40) { if (i == 0) break; i--; } return ctx->passlen - i; } static void add_text_to_passphrase(struct askpass_ctx *ctx, gchar *str) { int len = strlen(str); if (ctx->passlen + len >= ctx->passsize) { /* Take some care with buffer expansion, because there are * pieces of passphrase in the old buffer so we should ensure * realloc doesn't leave a copy lying around in the address * space. */ int oldsize = ctx->passsize; char *newbuf; ctx->passsize = (ctx->passlen + len) * 5 / 4 + 1024; newbuf = snewn(ctx->passsize, char); memcpy(newbuf, ctx->passphrase, oldsize); smemclr(ctx->passphrase, oldsize); sfree(ctx->passphrase); ctx->passphrase = newbuf; } strcpy(ctx->passphrase + ctx->passlen, str); ctx->passlen += len; visually_acknowledge_keypress(ctx); } static void cancel_askpass(struct askpass_ctx *ctx, const char *msg) { smemclr(ctx->passphrase, ctx->passsize); ctx->passphrase = NULL; ctx->error_message = dupstr(msg); gtk_main_quit(); } static gboolean askpass_dialog_closed(GtkWidget *widget, GdkEvent *event, gpointer data) { struct askpass_ctx *ctx = (struct askpass_ctx *)data; cancel_askpass(ctx, "passphrase input cancelled"); /* Don't destroy dialog yet, so gtk_askpass_cleanup() can do its work */ return true; } static gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) { struct askpass_ctx *ctx = (struct askpass_ctx *)data; if (event->keyval == GDK_KEY_Return && event->type == GDK_KEY_PRESS) { gtk_main_quit(); } else if (event->keyval == GDK_KEY_Escape && event->type == GDK_KEY_PRESS) { cancel_askpass(ctx, "passphrase input cancelled"); } else { #if GTK_CHECK_VERSION(2,0,0) if (gtk_im_context_filter_keypress(ctx->imc, event)) return true; #endif if (event->type == GDK_KEY_PRESS) { if (!strcmp(event->string, "\x15")) { /* Ctrl-U. Wipe out the whole line */ ctx->passlen = 0; visually_acknowledge_keypress(ctx); } else if (!strcmp(event->string, "\x17")) { /* Ctrl-W. Delete back to the last space->nonspace * boundary. We interpret 'space' in a really simple * way (mimicking terminal drivers), and don't attempt * to second-guess exciting Unicode space * characters. */ while (ctx->passlen > 0) { char deleted, prior; ctx->passlen -= last_char_len(ctx); deleted = ctx->passphrase[ctx->passlen]; prior = (ctx->passlen == 0 ? ' ' : ctx->passphrase[ctx->passlen-1]); if (!g_ascii_isspace(deleted) && g_ascii_isspace(prior)) break; } visually_acknowledge_keypress(ctx); } else if (event->keyval == GDK_KEY_BackSpace) { /* Backspace. Delete one character. */ if (ctx->passlen > 0) ctx->passlen -= last_char_len(ctx); visually_acknowledge_keypress(ctx); #if !GTK_CHECK_VERSION(2,0,0) } else if (event->string[0]) { add_text_to_passphrase(ctx, event->string); #endif } } } return true; } #if GTK_CHECK_VERSION(2,0,0) static void input_method_commit_event(GtkIMContext *imc, gchar *str, gpointer data) { struct askpass_ctx *ctx = (struct askpass_ctx *)data; add_text_to_passphrase(ctx, str); } #endif static gint configure_area(GtkWidget *widget, GdkEventConfigure *event, gpointer data) { struct drawing_area_ctx *ctx = (struct drawing_area_ctx *)data; ctx->width = event->width; ctx->height = event->height; gtk_widget_queue_draw(widget); return true; } #ifdef DRAW_DEFAULT_CAIRO static void askpass_redraw_cairo(cairo_t *cr, struct drawing_area_ctx *ctx) { double rgbval = (ctx->state == CURRENT ? 0 : ctx->state == NOT_CURRENT ? 1 : 0.5); cairo_set_source_rgb(cr, rgbval, rgbval, rgbval); cairo_paint(cr); } #else static void askpass_redraw_gdk(GdkWindow *win, struct drawing_area_ctx *ctx) { GdkGC *gc = gdk_gc_new(win); gdk_gc_set_foreground(gc, &ctx->cols[ctx->state]); gdk_draw_rectangle(win, gc, true, 0, 0, ctx->width, ctx->height); gdk_gc_unref(gc); } #endif #if GTK_CHECK_VERSION(3,0,0) static gint draw_area(GtkWidget *widget, cairo_t *cr, gpointer data) { struct drawing_area_ctx *ctx = (struct drawing_area_ctx *)data; askpass_redraw_cairo(cr, ctx); return true; } #else static gint expose_area(GtkWidget *widget, GdkEventExpose *event, gpointer data) { struct drawing_area_ctx *ctx = (struct drawing_area_ctx *)data; #ifdef DRAW_DEFAULT_CAIRO cairo_t *cr = gdk_cairo_create(gtk_widget_get_window(ctx->area)); askpass_redraw_cairo(cr, ctx); cairo_destroy(cr); #else askpass_redraw_gdk(gtk_widget_get_window(ctx->area), ctx); #endif return true; } #endif static gboolean try_grab_keyboard(gpointer vctx) { struct askpass_ctx *ctx = (struct askpass_ctx *)vctx; int i, ret; #if GTK_CHECK_VERSION(3,20,0) /* * Grabbing the keyboard in GTK 3.20 requires the new notion of * GdkSeat. */ GdkSeat *seat; GdkWindow *gdkw = gtk_widget_get_window(ctx->dialog); if (!GDK_IS_WINDOW(gdkw) || !gdk_window_is_visible(gdkw)) goto fail; seat = gdk_display_get_default_seat (gtk_widget_get_display(ctx->dialog)); if (!seat) goto fail; ctx->seat = seat; ret = gdk_seat_grab(seat, gdkw, GDK_SEAT_CAPABILITY_KEYBOARD, true, NULL, NULL, NULL, NULL); /* * For some reason GDK 3.22 hides the GDK window as a side effect * of a failed grab. I've no idea why. But if we're going to retry * the grab, then we need to unhide it again or else we'll just * get GDK_GRAB_NOT_VIEWABLE on every subsequent attempt. */ if (ret != GDK_GRAB_SUCCESS) gdk_window_show(gdkw); #elif GTK_CHECK_VERSION(3,0,0) /* * And it has to be done differently again prior to GTK 3.20. */ GdkDeviceManager *dm; GdkDevice *pointer, *keyboard; dm = gdk_display_get_device_manager (gtk_widget_get_display(ctx->dialog)); if (!dm) goto fail; pointer = gdk_device_manager_get_client_pointer(dm); if (!pointer) goto fail; keyboard = gdk_device_get_associated_device(pointer); if (!keyboard) goto fail; if (gdk_device_get_source(keyboard) != GDK_SOURCE_KEYBOARD) goto fail; ctx->keyboard = keyboard; ret = gdk_device_grab(ctx->keyboard, gtk_widget_get_window(ctx->dialog), GDK_OWNERSHIP_NONE, true, GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK, NULL, GDK_CURRENT_TIME); #else /* * It's much simpler in GTK 1 and 2! */ ret = gdk_keyboard_grab(gtk_widget_get_window(ctx->dialog), false, GDK_CURRENT_TIME); #endif if (ret != GDK_GRAB_SUCCESS) goto fail; /* * Now that we've got the keyboard grab, connect up our keyboard * handlers. */ #if GTK_CHECK_VERSION(2,0,0) g_signal_connect(G_OBJECT(ctx->imc), "commit", G_CALLBACK(input_method_commit_event), ctx); #endif g_signal_connect(G_OBJECT(ctx->dialog), "key_press_event", G_CALLBACK(key_event), ctx); g_signal_connect(G_OBJECT(ctx->dialog), "key_release_event", G_CALLBACK(key_event), ctx); #if GTK_CHECK_VERSION(2,0,0) gtk_im_context_set_client_window(ctx->imc, gtk_widget_get_window(ctx->dialog)); #endif /* * And repaint the key-acknowledgment drawing areas as not greyed * out. */ ctx->active_area = keypress_prng_value() % N_DRAWING_AREAS; for (i = 0; i < N_DRAWING_AREAS; i++) { ctx->drawingareas[i].state = (i == ctx->active_area ? CURRENT : NOT_CURRENT); gtk_widget_queue_draw(ctx->drawingareas[i].area); } return false; fail: /* * If we didn't get the grab, reschedule ourself on a timer to try * again later. * * We have to do this rather than just trying once, because there * is at least one important situation in which the grab may fail * the first time: any user who is launching an add-key operation * off some kind of window manager hotkey will almost by * definition be running this script with a keyboard grab already * active, namely the one-key grab that the WM (or whatever) uses * to detect presses of the hotkey. So at the very least we have * to give the user time to release that key. */ if (++ctx->nattempts >= 4) { cancel_askpass(ctx, "unable to grab keyboard after 5 seconds"); } else { g_timeout_add(1000/8, try_grab_keyboard, ctx); } return false; } void realize(GtkWidget *widget, gpointer vctx) { struct askpass_ctx *ctx = (struct askpass_ctx *)vctx; gtk_grab_add(ctx->dialog); /* * Schedule the first attempt at the keyboard grab. */ ctx->nattempts = 0; #if GTK_CHECK_VERSION(3,20,0) ctx->seat = NULL; #elif GTK_CHECK_VERSION(3,0,0) ctx->keyboard = NULL; #endif g_idle_add(try_grab_keyboard, ctx); } static const char *gtk_askpass_setup(struct askpass_ctx *ctx, const char *window_title, const char *prompt_text) { int i; GtkBox *action_area; ctx->passlen = 0; ctx->passsize = 2048; ctx->passphrase = snewn(ctx->passsize, char); /* * Create widgets. */ ctx->dialog = our_dialog_new(); gtk_window_set_title(GTK_WINDOW(ctx->dialog), window_title); gtk_window_set_position(GTK_WINDOW(ctx->dialog), GTK_WIN_POS_CENTER); g_signal_connect(G_OBJECT(ctx->dialog), "delete-event", G_CALLBACK(askpass_dialog_closed), ctx); ctx->promptlabel = gtk_label_new(prompt_text); align_label_left(GTK_LABEL(ctx->promptlabel)); gtk_widget_show(ctx->promptlabel); gtk_label_set_line_wrap(GTK_LABEL(ctx->promptlabel), true); #if GTK_CHECK_VERSION(3,0,0) gtk_label_set_width_chars(GTK_LABEL(ctx->promptlabel), 48); #endif int margin = string_width("MM"); #if GTK_CHECK_VERSION(3,12,0) gtk_widget_set_margin_start(ctx->promptlabel, margin); gtk_widget_set_margin_end(ctx->promptlabel, margin); #else gtk_misc_set_padding(GTK_MISC(ctx->promptlabel), margin, 0); #endif our_dialog_add_to_content_area(GTK_WINDOW(ctx->dialog), ctx->promptlabel, true, true, 0); #if GTK_CHECK_VERSION(2,0,0) ctx->imc = gtk_im_multicontext_new(); #endif #ifndef DRAW_DEFAULT_CAIRO { gboolean success[2]; ctx->colmap = gdk_colormap_get_system(); ctx->cols[0].red = ctx->cols[0].green = ctx->cols[0].blue = 0xFFFF; ctx->cols[1].red = ctx->cols[1].green = ctx->cols[1].blue = 0; ctx->cols[2].red = ctx->cols[2].green = ctx->cols[2].blue = 0x8000; gdk_colormap_alloc_colors(ctx->colmap, ctx->cols, 2, false, true, success); if (!success[0] || !success[1]) return "unable to allocate colours"; } #endif action_area = our_dialog_make_action_hbox(GTK_WINDOW(ctx->dialog)); for (i = 0; i < N_DRAWING_AREAS; i++) { ctx->drawingareas[i].area = gtk_drawing_area_new(); #ifndef DRAW_DEFAULT_CAIRO ctx->drawingareas[i].cols = ctx->cols; #endif ctx->drawingareas[i].state = GREYED_OUT; ctx->drawingareas[i].width = ctx->drawingareas[i].height = 0; /* It would be nice to choose this size in some more * context-sensitive way, like measuring the size of some * piece of template text. */ gtk_widget_set_size_request(ctx->drawingareas[i].area, 32, 32); gtk_box_pack_end(action_area, ctx->drawingareas[i].area, true, true, 5); g_signal_connect(G_OBJECT(ctx->drawingareas[i].area), "configure_event", G_CALLBACK(configure_area), &ctx->drawingareas[i]); #if GTK_CHECK_VERSION(3,0,0) g_signal_connect(G_OBJECT(ctx->drawingareas[i].area), "draw", G_CALLBACK(draw_area), &ctx->drawingareas[i]); #else g_signal_connect(G_OBJECT(ctx->drawingareas[i].area), "expose_event", G_CALLBACK(expose_area), &ctx->drawingareas[i]); #endif #if GTK_CHECK_VERSION(3,0,0) g_object_set(G_OBJECT(ctx->drawingareas[i].area), "margin-bottom", 8, (const char *)NULL); #endif gtk_widget_show(ctx->drawingareas[i].area); } ctx->active_area = -1; /* * Arrange to receive key events. We don't really need to worry * from a UI perspective about which widget gets the events, as * long as we know which it is so we can catch them. So we'll pick * the prompt label at random, and we'll use gtk_grab_add to * ensure key events go to it. */ gtk_widget_set_sensitive(ctx->dialog, true); #if GTK_CHECK_VERSION(2,0,0) gtk_window_set_keep_above(GTK_WINDOW(ctx->dialog), true); #endif /* * Wait for the key-receiving widget to actually be created, in * order to call gtk_grab_add on it. */ g_signal_connect(G_OBJECT(ctx->dialog), "realize", G_CALLBACK(realize), ctx); /* * Show the window. */ gtk_widget_show(ctx->dialog); return NULL; } static void gtk_askpass_cleanup(struct askpass_ctx *ctx) { #if GTK_CHECK_VERSION(3,20,0) if (ctx->seat) gdk_seat_ungrab(ctx->seat); #elif GTK_CHECK_VERSION(3,0,0) if (ctx->keyboard) gdk_device_ungrab(ctx->keyboard, GDK_CURRENT_TIME); #else gdk_keyboard_ungrab(GDK_CURRENT_TIME); #endif gtk_grab_remove(ctx->promptlabel); if (ctx->passphrase) { assert(ctx->passlen < ctx->passsize); ctx->passphrase[ctx->passlen] = '\0'; } gtk_widget_destroy(ctx->dialog); } static bool setup_gtk(const char *display) { static bool gtk_initialised = false; int argc; char *real_argv[3]; char **argv = real_argv; bool ret; if (gtk_initialised) return true; argc = 0; argv[argc++] = dupstr("dummy"); argv[argc++] = dupprintf("--display=%s", display); argv[argc] = NULL; ret = gtk_init_check(&argc, &argv); while (argc > 0) sfree(argv[--argc]); gtk_initialised = ret; return ret; } const bool buildinfo_gtk_relevant = true; char *gtk_askpass_main(const char *display, const char *wintitle, const char *prompt, bool *success) { struct askpass_ctx ctx[1]; const char *err; ctx->passphrase = NULL; ctx->error_message = NULL; /* In case gtk_init hasn't been called yet by the program */ if (!setup_gtk(display)) { *success = false; return dupstr("unable to initialise GTK"); } if ((err = gtk_askpass_setup(ctx, wintitle, prompt)) != NULL) { *success = false; return dupprintf("%s", err); } setup_keypress_prng(); gtk_main(); cleanup_keypress_prng(); gtk_askpass_cleanup(ctx); if (ctx->passphrase) { *success = true; return ctx->passphrase; } else { *success = false; return ctx->error_message; } } #ifdef TEST_ASKPASS void modalfatalbox(const char *p, ...) { va_list ap; fprintf(stderr, "FATAL ERROR: "); va_start(ap, p); vfprintf(stderr, p, ap); va_end(ap); fputc('\n', stderr); exit(1); } int main(int argc, char **argv) { bool success; int exitcode; char *ret; gtk_init(&argc, &argv); if (argc != 2) { success = false; ret = dupprintf("usage: %s ", argv[0]); } else { ret = gtk_askpass_main(NULL, "Enter passphrase", argv[1], &success); } if (!success) { fputs(ret, stderr); fputc('\n', stderr); exitcode = 1; } else { fputs(ret, stdout); fputc('\n', stdout); exitcode = 0; } smemclr(ret, strlen(ret)); return exitcode; } #endif putty-0.76/unix/gtkcfg.c0000644000175000017500000001372714072266313012166 00000000000000/* * gtkcfg.c - the GTK-specific parts of the PuTTY configuration * box. */ #include #include #include "putty.h" #include "dialog.h" #include "storage.h" static void about_handler(union control *ctrl, dlgparam *dlg, void *data, int event) { if (event == EVENT_ACTION) { about_box(ctrl->generic.context.p); } } void gtk_setup_config_box(struct controlbox *b, bool midsession, void *win) { struct controlset *s, *s2; union control *c; int i; if (!midsession) { /* * Add the About button to the standard panel. */ s = ctrl_getset(b, "", "", ""); c = ctrl_pushbutton(s, "About", 'a', HELPCTX(no_help), about_handler, P(win)); c->generic.column = 0; } /* * 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, (s->ncontrols-i-2)*sizeof(union control *)); s->ctrls[i+1] = c; } break; } } /* * X requires three more fonts: bold, wide, and wide-bold; also * we need the fiddly shadow-bold-offset control. This would * make the Window/Appearance panel rather unwieldy and large, * so I think the sensible thing here is to _move_ this * controlset into a separate Window/Fonts panel! */ s2 = ctrl_getset(b, "Window/Appearance", "font", "Font settings"); /* Remove this controlset from b. */ for (i = 0; i < b->nctrlsets; i++) { if (b->ctrlsets[i] == s2) { memmove(b->ctrlsets+i, b->ctrlsets+i+1, (b->nctrlsets-i-1) * sizeof(*b->ctrlsets)); b->nctrlsets--; ctrl_free_set(s2); break; } } 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)); #ifdef OSX_META_KEY_CONFIG /* * On OS X, there are multiple reasonable opinions about whether * Option or Command (or both, or neither) should act as a Meta * key, or whether they should have their normal OS functions. */ s = ctrl_getset(b, "Terminal/Keyboard", "meta", "Choose the Meta key:"); ctrl_checkbox(s, "Option key acts as Meta", 'p', HELPCTX(no_help), conf_checkbox_handler, I(CONF_osx_option_meta)); ctrl_checkbox(s, "Command key acts as Meta", 'm', HELPCTX(no_help), conf_checkbox_handler, I(CONF_osx_command_meta)); #endif 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)); } } putty-0.76/unix/gtkcols.c0000644000175000017500000010776114072266313012371 00000000000000/* * gtkcols.c - implementation of the `Columns' GTK layout container. */ #include #include #include "defs.h" #include "gtkcompat.h" #include "gtkcols.h" #if GTK_CHECK_VERSION(2,0,0) /* The "focus" method lives in GtkWidget from GTK 2 onwards, but it * was in GtkContainer in GTK 1 */ #define FOCUS_METHOD_SUPERCLASS GtkWidget #define FOCUS_METHOD_LOCATION widget_class /* used in columns_init */ #define CHILD_FOCUS(cont, dir) gtk_widget_child_focus(GTK_WIDGET(cont), dir) #else #define FOCUS_METHOD_SUPERCLASS GtkContainer #define FOCUS_METHOD_LOCATION container_class #define CHILD_FOCUS(cont, dir) gtk_container_focus(GTK_CONTAINER(cont), dir) #endif static void columns_init(Columns *cols); static void columns_class_init(ColumnsClass *klass); #if !GTK_CHECK_VERSION(2,0,0) static void columns_finalize(GtkObject *object); #else static void columns_finalize(GObject *object); #endif static void columns_map(GtkWidget *widget); static void columns_unmap(GtkWidget *widget); #if !GTK_CHECK_VERSION(2,0,0) static void columns_draw(GtkWidget *widget, GdkRectangle *area); static gint columns_expose(GtkWidget *widget, GdkEventExpose *event); #endif static void columns_base_add(GtkContainer *container, GtkWidget *widget); static void columns_remove(GtkContainer *container, GtkWidget *widget); static void columns_forall(GtkContainer *container, gboolean include_internals, GtkCallback callback, gpointer callback_data); static gint columns_focus(FOCUS_METHOD_SUPERCLASS *container, GtkDirectionType dir); static GType columns_child_type(GtkContainer *container); #if GTK_CHECK_VERSION(3,0,0) static void columns_get_preferred_width(GtkWidget *widget, gint *min, gint *nat); static void columns_get_preferred_height(GtkWidget *widget, gint *min, gint *nat); static void columns_get_preferred_width_for_height(GtkWidget *widget, gint height, gint *min, gint *nat); static void columns_get_preferred_height_for_width(GtkWidget *widget, gint width, gint *min, gint *nat); #else static void columns_size_request(GtkWidget *widget, GtkRequisition *req); #endif static void columns_size_allocate(GtkWidget *widget, GtkAllocation *alloc); static GtkContainerClass *parent_class = NULL; #if !GTK_CHECK_VERSION(2,0,0) GType columns_get_type(void) { static GType columns_type = 0; if (!columns_type) { static const GtkTypeInfo columns_info = { "Columns", sizeof(Columns), sizeof(ColumnsClass), (GtkClassInitFunc) columns_class_init, (GtkObjectInitFunc) columns_init, /* reserved_1 */ NULL, /* reserved_2 */ NULL, (GtkClassInitFunc) NULL, }; columns_type = gtk_type_unique(GTK_TYPE_CONTAINER, &columns_info); } return columns_type; } #else GType columns_get_type(void) { static GType columns_type = 0; if (!columns_type) { static const GTypeInfo columns_info = { sizeof(ColumnsClass), NULL, NULL, (GClassInitFunc) columns_class_init, NULL, NULL, sizeof(Columns), 0, (GInstanceInitFunc)columns_init, }; columns_type = g_type_register_static(GTK_TYPE_CONTAINER, "Columns", &columns_info, 0); } return columns_type; } #endif static gint (*columns_inherited_focus)(FOCUS_METHOD_SUPERCLASS *container, GtkDirectionType direction); static void columns_class_init(ColumnsClass *klass) { #if !GTK_CHECK_VERSION(2,0,0) GtkObjectClass *object_class = (GtkObjectClass *)klass; GtkWidgetClass *widget_class = (GtkWidgetClass *)klass; GtkContainerClass *container_class = (GtkContainerClass *)klass; #else GObjectClass *object_class = G_OBJECT_CLASS(klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass); GtkContainerClass *container_class = GTK_CONTAINER_CLASS(klass); #endif #if !GTK_CHECK_VERSION(2,0,0) parent_class = gtk_type_class(GTK_TYPE_CONTAINER); #else parent_class = g_type_class_peek_parent(klass); #endif object_class->finalize = columns_finalize; widget_class->map = columns_map; widget_class->unmap = columns_unmap; #if !GTK_CHECK_VERSION(2,0,0) widget_class->draw = columns_draw; widget_class->expose_event = columns_expose; #endif #if GTK_CHECK_VERSION(3,0,0) widget_class->get_preferred_width = columns_get_preferred_width; widget_class->get_preferred_height = columns_get_preferred_height; widget_class->get_preferred_width_for_height = columns_get_preferred_width_for_height; widget_class->get_preferred_height_for_width = columns_get_preferred_height_for_width; #else widget_class->size_request = columns_size_request; #endif widget_class->size_allocate = columns_size_allocate; container_class->add = columns_base_add; container_class->remove = columns_remove; container_class->forall = columns_forall; container_class->child_type = columns_child_type; /* Save the previous value of this method. */ if (!columns_inherited_focus) columns_inherited_focus = FOCUS_METHOD_LOCATION->focus; FOCUS_METHOD_LOCATION->focus = columns_focus; } static void columns_init(Columns *cols) { gtk_widget_set_has_window(GTK_WIDGET(cols), false); cols->children = NULL; cols->spacing = 0; } static void columns_child_free(gpointer vchild) { ColumnsChild *child = (ColumnsChild *)vchild; if (child->percentages) g_free(child->percentages); g_free(child); } static void columns_finalize( #if !GTK_CHECK_VERSION(2,0,0) GtkObject *object #else GObject *object #endif ) { Columns *cols; g_return_if_fail(object != NULL); g_return_if_fail(IS_COLUMNS(object)); cols = COLUMNS(object); #if !GTK_CHECK_VERSION(2,0,0) { GList *node; for (node = cols->children; node; node = node->next) if (node->data) columns_child_free(node->data); } g_list_free(cols->children); #else g_list_free_full(cols->children, columns_child_free); #endif cols->children = NULL; #if !GTK_CHECK_VERSION(2,0,0) GTK_OBJECT_CLASS(parent_class)->finalize(object); #else G_OBJECT_CLASS(parent_class)->finalize(object); #endif } /* * These appear to be thoroughly tedious functions; the only reason * we have to reimplement them at all is because we defined our own * format for our GList of children... */ static void columns_map(GtkWidget *widget) { Columns *cols; ColumnsChild *child; GList *children; g_return_if_fail(widget != NULL); g_return_if_fail(IS_COLUMNS(widget)); cols = COLUMNS(widget); gtk_widget_set_mapped(GTK_WIDGET(cols), true); for (children = cols->children; children && (child = children->data); children = children->next) { if (child->widget && gtk_widget_get_visible(child->widget) && !gtk_widget_get_mapped(child->widget)) gtk_widget_map(child->widget); } } static void columns_unmap(GtkWidget *widget) { Columns *cols; ColumnsChild *child; GList *children; g_return_if_fail(widget != NULL); g_return_if_fail(IS_COLUMNS(widget)); cols = COLUMNS(widget); gtk_widget_set_mapped(GTK_WIDGET(cols), false); for (children = cols->children; children && (child = children->data); children = children->next) { if (child->widget && gtk_widget_get_visible(child->widget) && gtk_widget_get_mapped(child->widget)) gtk_widget_unmap(child->widget); } } #if !GTK_CHECK_VERSION(2,0,0) static void columns_draw(GtkWidget *widget, GdkRectangle *area) { Columns *cols; ColumnsChild *child; GList *children; GdkRectangle child_area; g_return_if_fail(widget != NULL); g_return_if_fail(IS_COLUMNS(widget)); if (GTK_WIDGET_DRAWABLE(widget)) { cols = COLUMNS(widget); for (children = cols->children; children && (child = children->data); children = children->next) { if (child->widget && GTK_WIDGET_DRAWABLE(child->widget) && gtk_widget_intersect(child->widget, area, &child_area)) gtk_widget_draw(child->widget, &child_area); } } } static gint columns_expose(GtkWidget *widget, GdkEventExpose *event) { Columns *cols; ColumnsChild *child; GList *children; GdkEventExpose child_event; g_return_val_if_fail(widget != NULL, false); g_return_val_if_fail(IS_COLUMNS(widget), false); g_return_val_if_fail(event != NULL, false); if (GTK_WIDGET_DRAWABLE(widget)) { cols = COLUMNS(widget); child_event = *event; for (children = cols->children; children && (child = children->data); children = children->next) { if (child->widget && GTK_WIDGET_DRAWABLE(child->widget) && GTK_WIDGET_NO_WINDOW(child->widget) && gtk_widget_intersect(child->widget, &event->area, &child_event.area)) gtk_widget_event(child->widget, (GdkEvent *)&child_event); } } return false; } #endif static void columns_base_add(GtkContainer *container, GtkWidget *widget) { Columns *cols; g_return_if_fail(container != NULL); g_return_if_fail(IS_COLUMNS(container)); g_return_if_fail(widget != NULL); cols = COLUMNS(container); /* * Default is to add a new widget spanning all columns. */ columns_add(cols, widget, 0, 0); /* 0 means ncols */ } static void columns_remove(GtkContainer *container, GtkWidget *widget) { Columns *cols; ColumnsChild *child; GtkWidget *childw; GList *children; bool was_visible; g_return_if_fail(container != NULL); g_return_if_fail(IS_COLUMNS(container)); g_return_if_fail(widget != NULL); cols = COLUMNS(container); for (children = cols->children; children && (child = children->data); children = children->next) { if (child->widget != widget) continue; was_visible = gtk_widget_get_visible(widget); gtk_widget_unparent(widget); cols->children = g_list_remove_link(cols->children, children); g_list_free(children); if (child->same_height_as) { g_return_if_fail(child->same_height_as->same_height_as == child); child->same_height_as->same_height_as = NULL; if (gtk_widget_get_visible(child->same_height_as->widget)) gtk_widget_queue_resize(GTK_WIDGET(container)); } g_free(child); if (was_visible) gtk_widget_queue_resize(GTK_WIDGET(container)); break; } for (children = cols->taborder; children && (childw = children->data); children = children->next) { if (childw != widget) continue; cols->taborder = g_list_remove_link(cols->taborder, children); g_list_free(children); break; } } static void columns_forall(GtkContainer *container, gboolean include_internals, GtkCallback callback, gpointer callback_data) { Columns *cols; ColumnsChild *child; GList *children, *next; g_return_if_fail(container != NULL); g_return_if_fail(IS_COLUMNS(container)); g_return_if_fail(callback != NULL); cols = COLUMNS(container); for (children = cols->children; children && (child = children->data); children = next) { /* * We can't wait until after the callback to assign * `children = children->next', because the callback might * be gtk_widget_destroy, which would remove the link * `children' from the list! So instead we must get our * hands on the value of the `next' pointer _before_ the * callback. */ next = children->next; if (child->widget) callback(child->widget, callback_data); } } static GType columns_child_type(GtkContainer *container) { return GTK_TYPE_WIDGET; } GtkWidget *columns_new(gint spacing) { Columns *cols; #if !GTK_CHECK_VERSION(2,0,0) cols = gtk_type_new(columns_get_type()); #else cols = g_object_new(TYPE_COLUMNS, NULL); #endif cols->spacing = spacing; return GTK_WIDGET(cols); } void columns_set_cols(Columns *cols, gint ncols, const gint *percentages) { ColumnsChild *childdata; gint i; g_return_if_fail(cols != NULL); g_return_if_fail(IS_COLUMNS(cols)); g_return_if_fail(ncols > 0); g_return_if_fail(percentages != NULL); childdata = g_new(ColumnsChild, 1); childdata->widget = NULL; childdata->ncols = ncols; childdata->percentages = g_new(gint, ncols); childdata->force_left = false; for (i = 0; i < ncols; i++) childdata->percentages[i] = percentages[i]; cols->children = g_list_append(cols->children, childdata); } void columns_add(Columns *cols, GtkWidget *child, gint colstart, gint colspan) { ColumnsChild *childdata; g_return_if_fail(cols != NULL); g_return_if_fail(IS_COLUMNS(cols)); g_return_if_fail(child != NULL); g_return_if_fail(gtk_widget_get_parent(child) == NULL); childdata = g_new(ColumnsChild, 1); childdata->widget = child; childdata->colstart = colstart; childdata->colspan = colspan; childdata->force_left = false; childdata->same_height_as = NULL; childdata->percentages = NULL; cols->children = g_list_append(cols->children, childdata); cols->taborder = g_list_append(cols->taborder, child); gtk_widget_set_parent(child, GTK_WIDGET(cols)); if (gtk_widget_get_realized(GTK_WIDGET(cols))) gtk_widget_realize(child); if (gtk_widget_get_visible(GTK_WIDGET(cols)) && gtk_widget_get_visible(child)) { if (gtk_widget_get_mapped(GTK_WIDGET(cols))) gtk_widget_map(child); gtk_widget_queue_resize(child); } } static ColumnsChild *columns_find_child(Columns *cols, GtkWidget *widget) { GList *children; ColumnsChild *child; for (children = cols->children; children && (child = children->data); children = children->next) { if (child->widget == widget) return child; } return NULL; } void columns_force_left_align(Columns *cols, GtkWidget *widget) { ColumnsChild *child; g_return_if_fail(cols != NULL); g_return_if_fail(IS_COLUMNS(cols)); g_return_if_fail(widget != NULL); child = columns_find_child(cols, widget); g_return_if_fail(child != NULL); child->force_left = true; if (gtk_widget_get_visible(widget)) gtk_widget_queue_resize(GTK_WIDGET(cols)); } void columns_force_same_height(Columns *cols, GtkWidget *cw1, GtkWidget *cw2) { ColumnsChild *child1, *child2; g_return_if_fail(cols != NULL); g_return_if_fail(IS_COLUMNS(cols)); g_return_if_fail(cw1 != NULL); g_return_if_fail(cw2 != NULL); child1 = columns_find_child(cols, cw1); g_return_if_fail(child1 != NULL); child2 = columns_find_child(cols, cw2); g_return_if_fail(child2 != NULL); child1->same_height_as = child2; child2->same_height_as = child1; if (gtk_widget_get_visible(cw1) || gtk_widget_get_visible(cw2)) gtk_widget_queue_resize(GTK_WIDGET(cols)); } void columns_taborder_last(Columns *cols, GtkWidget *widget) { GtkWidget *childw; GList *children; g_return_if_fail(cols != NULL); g_return_if_fail(IS_COLUMNS(cols)); g_return_if_fail(widget != NULL); for (children = cols->taborder; children && (childw = children->data); children = children->next) { if (childw != widget) continue; cols->taborder = g_list_remove_link(cols->taborder, children); g_list_free(children); cols->taborder = g_list_append(cols->taborder, widget); break; } } /* * Override GtkContainer's focus movement so the user can * explicitly specify the tab order. */ static gint columns_focus(FOCUS_METHOD_SUPERCLASS *super, GtkDirectionType dir) { Columns *cols; GList *pos; GtkWidget *focuschild; g_return_val_if_fail(super != NULL, false); g_return_val_if_fail(IS_COLUMNS(super), false); cols = COLUMNS(super); if (!gtk_widget_is_drawable(GTK_WIDGET(cols)) || !gtk_widget_is_sensitive(GTK_WIDGET(cols))) return false; if (!gtk_widget_get_can_focus(GTK_WIDGET(cols)) && (dir == GTK_DIR_TAB_FORWARD || dir == GTK_DIR_TAB_BACKWARD)) { focuschild = gtk_container_get_focus_child(GTK_CONTAINER(cols)); gtk_container_set_focus_child(GTK_CONTAINER(cols), NULL); if (dir == GTK_DIR_TAB_FORWARD) pos = cols->taborder; else pos = g_list_last(cols->taborder); while (pos) { GtkWidget *child = pos->data; if (focuschild) { if (focuschild == child) { focuschild = NULL; /* now we can start looking in here */ if (gtk_widget_is_drawable(child) && GTK_IS_CONTAINER(child) && !gtk_widget_has_focus(child)) { if (CHILD_FOCUS(child, dir)) return true; } } } else if (gtk_widget_is_drawable(child)) { if (GTK_IS_CONTAINER(child)) { if (CHILD_FOCUS(child, dir)) return true; } else if (gtk_widget_get_can_focus(child)) { gtk_widget_grab_focus(child); return true; } } if (dir == GTK_DIR_TAB_FORWARD) pos = pos->next; else pos = pos->prev; } return false; } else return columns_inherited_focus(super, dir); } /* * Underlying parts of the layout algorithm, to compute the Columns * container's width or height given the widths or heights of its * children. These will be called in various ways with different * notions of width and height in use, so we abstract them out and * pass them a 'get width' or 'get height' function pointer. */ typedef gint (*widget_dim_fn_t)(ColumnsChild *child); static gint columns_compute_width(Columns *cols, widget_dim_fn_t get_width) { ColumnsChild *child; GList *children; gint i, ncols, colspan, retwidth, childwidth; const gint *percentages; static const gint onecol[] = { 100 }; #ifdef COLUMNS_WIDTH_DIAGNOSTICS printf("compute_width(%p): start\n", cols); #endif retwidth = 0; ncols = 1; percentages = onecol; for (children = cols->children; children && (child = children->data); children = children->next) { if (!child->widget) { /* Column reconfiguration. */ ncols = child->ncols; percentages = child->percentages; continue; } /* Only take visible widgets into account. */ if (!gtk_widget_get_visible(child->widget)) continue; childwidth = get_width(child); colspan = child->colspan ? child->colspan : ncols-child->colstart; assert(colspan > 0); #ifdef COLUMNS_WIDTH_DIAGNOSTICS printf("compute_width(%p): ", cols); if (GTK_IS_LABEL(child->widget)) printf("label %p '%s' wrap=%s: ", child->widget, gtk_label_get_text(GTK_LABEL(child->widget)), (gtk_label_get_line_wrap(GTK_LABEL(child->widget)) ? "true" : "false")); else printf("widget %p: ", child->widget); { gint min, nat; gtk_widget_get_preferred_width(child->widget, &min, &nat); printf("minwidth=%d natwidth=%d ", min, nat); } printf("thiswidth=%d span=%d\n", childwidth, colspan); #endif /* * To compute width: we know that childwidth + cols->spacing * needs to equal a certain percentage of the full width of * the container. So we work this value out, figure out how * wide the container will need to be to make that percentage * of it equal to that width, and ensure our returned width is * at least that much. Very simple really. */ { int percent, thiswid, fullwid; percent = 0; for (i = 0; i < colspan; i++) percent += percentages[child->colstart+i]; thiswid = childwidth + cols->spacing; /* * Since childwidth is (at least sometimes) the _minimum_ * size the child needs, we must ensure that it gets _at * least_ that size. Hence, when scaling thiswid up to * fullwid, we must round up, which means adding percent-1 * before dividing by percent. */ fullwid = (thiswid * 100 + percent - 1) / percent; #ifdef COLUMNS_WIDTH_DIAGNOSTICS printf("compute_width(%p): after %p, thiswid=%d fullwid=%d\n", cols, child->widget, thiswid, fullwid); #endif /* * The above calculation assumes every widget gets * cols->spacing on the right. So we subtract * cols->spacing here to account for the extra load of * spacing on the right. */ if (retwidth < fullwid - cols->spacing) retwidth = fullwid - cols->spacing; } } retwidth += 2*gtk_container_get_border_width(GTK_CONTAINER(cols)); #ifdef COLUMNS_WIDTH_DIAGNOSTICS printf("compute_width(%p): done, returning %d\n", cols, retwidth); #endif return retwidth; } static void columns_alloc_horiz(Columns *cols, gint ourwidth, widget_dim_fn_t get_width) { ColumnsChild *child; GList *children; gint i, ncols, colspan, border, *colxpos, childwidth; border = gtk_container_get_border_width(GTK_CONTAINER(cols)); ncols = 1; /* colxpos gives the starting x position of each column. * We supply n+1 of them, so that we can find the RH edge easily. * All ending x positions are expected to be adjusted afterwards by * subtracting the spacing. */ colxpos = g_new(gint, 2); colxpos[0] = 0; colxpos[1] = ourwidth - 2*border + cols->spacing; for (children = cols->children; children && (child = children->data); children = children->next) { if (!child->widget) { gint percent; /* Column reconfiguration. */ ncols = child->ncols; colxpos = g_renew(gint, colxpos, ncols + 1); colxpos[0] = 0; percent = 0; for (i = 0; i < ncols; i++) { percent += child->percentages[i]; colxpos[i+1] = (((ourwidth - 2*border) + cols->spacing) * percent / 100); } continue; } /* Only take visible widgets into account. */ if (!gtk_widget_get_visible(child->widget)) continue; childwidth = get_width(child); colspan = child->colspan ? child->colspan : ncols-child->colstart; /* * Starting x position is cols[colstart]. * Ending x position is cols[colstart+colspan] - spacing. * * Unless we're forcing left, in which case the width is * exactly the requisition width. */ child->x = colxpos[child->colstart]; if (child->force_left) child->w = childwidth; else child->w = (colxpos[child->colstart+colspan] - colxpos[child->colstart] - cols->spacing); } g_free(colxpos); } static gint columns_compute_height(Columns *cols, widget_dim_fn_t get_height) { ColumnsChild *child; GList *children; gint i, ncols, colspan, *colypos, retheight, childheight; retheight = cols->spacing; ncols = 1; colypos = g_new(gint, 1); colypos[0] = 0; for (children = cols->children; children && (child = children->data); children = children->next) { if (!child->widget) { /* Column reconfiguration. */ for (i = 1; i < ncols; i++) { if (colypos[0] < colypos[i]) colypos[0] = colypos[i]; } ncols = child->ncols; colypos = g_renew(gint, colypos, ncols); for (i = 1; i < ncols; i++) colypos[i] = colypos[0]; continue; } /* Only take visible widgets into account. */ if (!gtk_widget_get_visible(child->widget)) continue; childheight = get_height(child); if (child->same_height_as) { gint childheight2 = get_height(child->same_height_as); if (childheight < childheight2) childheight = childheight2; } colspan = child->colspan ? child->colspan : ncols-child->colstart; /* * To compute height: the widget's top will be positioned at * the largest y value so far reached in any of the columns it * crosses. Then it will go down by childheight plus padding; * and the point it reaches at the bottom is the new y value * in all those columns, and minus the padding it is also a * lower bound on our own height. */ { int topy, boty; topy = 0; for (i = 0; i < colspan; i++) { if (topy < colypos[child->colstart+i]) topy = colypos[child->colstart+i]; } boty = topy + childheight + cols->spacing; for (i = 0; i < colspan; i++) { colypos[child->colstart+i] = boty; } if (retheight < boty - cols->spacing) retheight = boty - cols->spacing; } } retheight += 2*gtk_container_get_border_width(GTK_CONTAINER(cols)); g_free(colypos); return retheight; } static void columns_alloc_vert(Columns *cols, gint ourheight, widget_dim_fn_t get_height) { ColumnsChild *child; GList *children; gint i, ncols, colspan, *colypos, realheight, fakeheight; ncols = 1; /* As in size_request, colypos is the lowest y reached in each column. */ colypos = g_new(gint, 1); colypos[0] = 0; for (children = cols->children; children && (child = children->data); children = children->next) { if (!child->widget) { /* Column reconfiguration. */ for (i = 1; i < ncols; i++) { if (colypos[0] < colypos[i]) colypos[0] = colypos[i]; } ncols = child->ncols; colypos = g_renew(gint, colypos, ncols); for (i = 1; i < ncols; i++) colypos[i] = colypos[0]; continue; } /* Only take visible widgets into account. */ if (!gtk_widget_get_visible(child->widget)) continue; realheight = fakeheight = get_height(child); if (child->same_height_as) { gint childheight2 = get_height(child->same_height_as); if (fakeheight < childheight2) fakeheight = childheight2; } colspan = child->colspan ? child->colspan : ncols-child->colstart; /* * To compute height: the widget's top will be positioned * at the largest y value so far reached in any of the * columns it crosses. Then it will go down by creq.height * plus padding; and the point it reaches at the bottom is * the new y value in all those columns. */ { int topy, boty; topy = 0; for (i = 0; i < colspan; i++) { if (topy < colypos[child->colstart+i]) topy = colypos[child->colstart+i]; } child->y = topy + fakeheight/2 - realheight/2; child->h = realheight; boty = topy + fakeheight + cols->spacing; for (i = 0; i < colspan; i++) { colypos[child->colstart+i] = boty; } } } g_free(colypos); } /* * Now here comes the interesting bit. The actual layout part is * done in the following two functions: * * columns_size_request() examines the list of widgets held in the * Columns, and returns a requisition stating the absolute minimum * size it can bear to be. * * columns_size_allocate() is given an allocation telling it what * size the whole container is going to be, and it calls * gtk_widget_size_allocate() on all of its (visible) children to * set their size and position relative to the top left of the * container. */ #if !GTK_CHECK_VERSION(3,0,0) static gint columns_gtk2_get_width(ColumnsChild *child) { GtkRequisition creq; gtk_widget_size_request(child->widget, &creq); return creq.width; } static gint columns_gtk2_get_height(ColumnsChild *child) { GtkRequisition creq; gtk_widget_size_request(child->widget, &creq); return creq.height; } static void columns_size_request(GtkWidget *widget, GtkRequisition *req) { Columns *cols; g_return_if_fail(widget != NULL); g_return_if_fail(IS_COLUMNS(widget)); g_return_if_fail(req != NULL); cols = COLUMNS(widget); req->width = columns_compute_width(cols, columns_gtk2_get_width); req->height = columns_compute_height(cols, columns_gtk2_get_height); } static void columns_size_allocate(GtkWidget *widget, GtkAllocation *alloc) { Columns *cols; ColumnsChild *child; GList *children; gint border; g_return_if_fail(widget != NULL); g_return_if_fail(IS_COLUMNS(widget)); g_return_if_fail(alloc != NULL); cols = COLUMNS(widget); gtk_widget_set_allocation(widget, alloc); border = gtk_container_get_border_width(GTK_CONTAINER(cols)); columns_alloc_horiz(cols, alloc->width, columns_gtk2_get_width); columns_alloc_vert(cols, alloc->height, columns_gtk2_get_height); for (children = cols->children; children && (child = children->data); children = children->next) { if (child->widget && gtk_widget_get_visible(child->widget)) { GtkAllocation call; call.x = alloc->x + border + child->x; call.y = alloc->y + border + child->y; call.width = child->w; call.height = child->h; gtk_widget_size_allocate(child->widget, &call); } } } #else /* GTK_CHECK_VERSION(3,0,0) */ static gint columns_gtk3_get_min_width(ColumnsChild *child) { gint ret; gtk_widget_get_preferred_width(child->widget, &ret, NULL); return ret; } static gint columns_gtk3_get_nat_width(ColumnsChild *child) { gint ret; if ((GTK_IS_LABEL(child->widget) && gtk_label_get_line_wrap(GTK_LABEL(child->widget))) || GTK_IS_ENTRY(child->widget)) { /* * We treat wrapping GtkLabels as a special case in this * layout class, because the whole point of those is that I * _don't_ want them to take up extra horizontal space for * long text, but instead to wrap it to whatever size is used * by the rest of the layout. * * GtkEntry gets similar treatment, because in OS X GTK I've * found that it requests a natural width regardless of the * output of gtk_entry_set_width_chars. */ gtk_widget_get_preferred_width(child->widget, &ret, NULL); } else { gtk_widget_get_preferred_width(child->widget, NULL, &ret); } return ret; } static gint columns_gtk3_get_minfh_width(ColumnsChild *child) { gint ret; gtk_widget_get_preferred_width_for_height(child->widget, child->h, &ret, NULL); return ret; } static gint columns_gtk3_get_natfh_width(ColumnsChild *child) { gint ret; gtk_widget_get_preferred_width_for_height(child->widget, child->h, NULL, &ret); return ret; } static gint columns_gtk3_get_min_height(ColumnsChild *child) { gint ret; gtk_widget_get_preferred_height(child->widget, &ret, NULL); return ret; } static gint columns_gtk3_get_nat_height(ColumnsChild *child) { gint ret; gtk_widget_get_preferred_height(child->widget, NULL, &ret); return ret; } static gint columns_gtk3_get_minfw_height(ColumnsChild *child) { gint ret; gtk_widget_get_preferred_height_for_width(child->widget, child->w, &ret, NULL); return ret; } static gint columns_gtk3_get_natfw_height(ColumnsChild *child) { gint ret; gtk_widget_get_preferred_height_for_width(child->widget, child->w, NULL, &ret); return ret; } static void columns_get_preferred_width(GtkWidget *widget, gint *min, gint *nat) { Columns *cols; g_return_if_fail(widget != NULL); g_return_if_fail(IS_COLUMNS(widget)); cols = COLUMNS(widget); if (min) *min = columns_compute_width(cols, columns_gtk3_get_min_width); if (nat) *nat = columns_compute_width(cols, columns_gtk3_get_nat_width); } static void columns_get_preferred_height(GtkWidget *widget, gint *min, gint *nat) { Columns *cols; g_return_if_fail(widget != NULL); g_return_if_fail(IS_COLUMNS(widget)); cols = COLUMNS(widget); if (min) *min = columns_compute_height(cols, columns_gtk3_get_min_height); if (nat) *nat = columns_compute_height(cols, columns_gtk3_get_nat_height); } static void columns_get_preferred_width_for_height(GtkWidget *widget, gint height, gint *min, gint *nat) { Columns *cols; g_return_if_fail(widget != NULL); g_return_if_fail(IS_COLUMNS(widget)); cols = COLUMNS(widget); /* FIXME: which one should the get-height function here be? */ columns_alloc_vert(cols, height, columns_gtk3_get_nat_height); if (min) *min = columns_compute_width(cols, columns_gtk3_get_minfh_width); if (nat) *nat = columns_compute_width(cols, columns_gtk3_get_natfh_width); } static void columns_get_preferred_height_for_width(GtkWidget *widget, gint width, gint *min, gint *nat) { Columns *cols; g_return_if_fail(widget != NULL); g_return_if_fail(IS_COLUMNS(widget)); cols = COLUMNS(widget); /* FIXME: which one should the get-height function here be? */ columns_alloc_horiz(cols, width, columns_gtk3_get_nat_width); if (min) *min = columns_compute_height(cols, columns_gtk3_get_minfw_height); if (nat) *nat = columns_compute_height(cols, columns_gtk3_get_natfw_height); } static void columns_size_allocate(GtkWidget *widget, GtkAllocation *alloc) { Columns *cols; ColumnsChild *child; GList *children; gint border; g_return_if_fail(widget != NULL); g_return_if_fail(IS_COLUMNS(widget)); g_return_if_fail(alloc != NULL); cols = COLUMNS(widget); gtk_widget_set_allocation(widget, alloc); border = gtk_container_get_border_width(GTK_CONTAINER(cols)); columns_alloc_horiz(cols, alloc->width, columns_gtk3_get_min_width); columns_alloc_vert(cols, alloc->height, columns_gtk3_get_minfw_height); for (children = cols->children; children && (child = children->data); children = children->next) { if (child->widget && gtk_widget_get_visible(child->widget)) { GtkAllocation call; call.x = alloc->x + border + child->x; call.y = alloc->y + border + child->y; call.width = child->w; call.height = child->h; gtk_widget_size_allocate(child->widget, &call); } } } #endif putty-0.76/unix/gtkcols.h0000644000175000017500000000411014072266313012356 00000000000000/* * 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 #include #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ #define TYPE_COLUMNS (columns_get_type()) #define COLUMNS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_COLUMNS, Columns)) #define COLUMNS_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_COLUMNS, ColumnsClass)) #define IS_COLUMNS(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_COLUMNS)) #define IS_COLUMNS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_COLUMNS)) typedef struct Columns_tag Columns; typedef struct ColumnsClass_tag ColumnsClass; typedef struct ColumnsChild_tag ColumnsChild; struct Columns_tag { GtkContainer container; /* private after here */ GList *children; /* this holds ColumnsChild structures */ GList *taborder; /* this just holds GtkWidgets */ gint spacing; }; struct ColumnsClass_tag { GtkContainerClass parent_class; }; struct ColumnsChild_tag { /* If `widget' is non-NULL, this entry represents an actual widget. */ GtkWidget *widget; gint colstart, colspan; bool force_left; /* for recalcitrant GtkLabels */ ColumnsChild *same_height_as; /* Otherwise, this entry represents a change in the column setup. */ gint ncols; gint *percentages; gint x, y, w, h; /* used during an individual size computation */ }; GType columns_get_type(void); GtkWidget *columns_new(gint spacing); void columns_set_cols(Columns *cols, gint ncols, const gint *percentages); void columns_add(Columns *cols, GtkWidget *child, gint colstart, gint colspan); void columns_taborder_last(Columns *cols, GtkWidget *child); void columns_force_left_align(Columns *cols, GtkWidget *child); void columns_force_same_height(Columns *cols, GtkWidget *ch1, GtkWidget *ch2); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* COLUMNS_H */ putty-0.76/unix/gtkcomm.c0000644000175000017500000001405714072266313012357 00000000000000/* * gtkcomm.c: machinery in the GTK front end which is common to all * programs that run a session in a terminal window, and also common * across all _sessions_ rather than specific to one session. (Timers, * uxsel etc.) */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if !GTK_CHECK_VERSION(3,0,0) #include #endif #if GTK_CHECK_VERSION(2,0,0) #include #endif #define MAY_REFER_TO_GTK_IN_HEADERS #include "putty.h" #include "terminal.h" #include "gtkcompat.h" #include "gtkfont.h" #include "gtkmisc.h" #ifndef NOT_X_WINDOWS #include #include #include #include #endif #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 /* ---------------------------------------------------------------------- * File descriptors and uxsel. */ struct uxsel_id { #if GTK_CHECK_VERSION(2,0,0) GIOChannel *chan; guint watch_id; #else int id; #endif }; #if GTK_CHECK_VERSION(2,0,0) gboolean fd_input_func(GIOChannel *source, GIOCondition condition, gpointer data) { int sourcefd = g_io_channel_unix_get_fd(source); /* * We must process exceptional notifications before ordinary * readability ones, or we may go straight past the urgent * marker. */ if (condition & G_IO_PRI) select_result(sourcefd, SELECT_X); if (condition & (G_IO_IN | G_IO_HUP)) select_result(sourcefd, SELECT_R); if (condition & G_IO_OUT) select_result(sourcefd, SELECT_W); return true; } #else void fd_input_func(gpointer data, gint sourcefd, GdkInputCondition condition) { if (condition & GDK_INPUT_EXCEPTION) select_result(sourcefd, SELECT_X); if (condition & GDK_INPUT_READ) select_result(sourcefd, SELECT_R); if (condition & GDK_INPUT_WRITE) select_result(sourcefd, SELECT_W); } #endif uxsel_id *uxsel_input_add(int fd, int rwx) { uxsel_id *id = snew(uxsel_id); #if GTK_CHECK_VERSION(2,0,0) int flags = 0; if (rwx & SELECT_R) flags |= G_IO_IN | G_IO_HUP; if (rwx & SELECT_W) flags |= G_IO_OUT; if (rwx & SELECT_X) flags |= G_IO_PRI; id->chan = g_io_channel_unix_new(fd); g_io_channel_set_encoding(id->chan, NULL, NULL); id->watch_id = g_io_add_watch_full(id->chan, GDK_PRIORITY_REDRAW+1, flags, fd_input_func, NULL, NULL); #else int flags = 0; if (rwx & SELECT_R) flags |= GDK_INPUT_READ; if (rwx & SELECT_W) flags |= GDK_INPUT_WRITE; if (rwx & SELECT_X) flags |= GDK_INPUT_EXCEPTION; assert(flags); id->id = gdk_input_add(fd, flags, fd_input_func, NULL); #endif return id; } void uxsel_input_remove(uxsel_id *id) { #if GTK_CHECK_VERSION(2,0,0) g_source_remove(id->watch_id); g_io_channel_unref(id->chan); #else gdk_input_remove(id->id); #endif sfree(id); } /* ---------------------------------------------------------------------- * Timers. */ static guint timer_id = 0; static gint timer_trigger(gpointer data) { unsigned long now = GPOINTER_TO_LONG(data); unsigned long next, then; long ticks; /* * Destroy the timer we got here on. */ if (timer_id) { g_source_remove(timer_id); timer_id = 0; } /* * run_timers() may cause a call to timer_change_notify, in which * case a new timer will already have been set up and left in * timer_id. If it hasn't, and run_timers reports that some timing * still needs to be done, we do it ourselves. */ if (run_timers(now, &next) && !timer_id) { then = now; now = GETTICKCOUNT(); if (now - then > next - then) ticks = 0; else ticks = next - now; timer_id = g_timeout_add(ticks, timer_trigger, LONG_TO_GPOINTER(next)); } /* * Returning false means 'don't call this timer again', which * _should_ be redundant given that we removed it above, but just * in case, return false anyway. */ return false; } void timer_change_notify(unsigned long next) { long ticks; if (timer_id) g_source_remove(timer_id); ticks = next - GETTICKCOUNT(); if (ticks <= 0) ticks = 1; /* just in case */ timer_id = g_timeout_add(ticks, timer_trigger, LONG_TO_GPOINTER(next)); } /* ---------------------------------------------------------------------- * Toplevel callbacks. */ static guint toplevel_callback_idle_id; static bool idle_fn_scheduled; static void notify_toplevel_callback(void *); static gint idle_toplevel_callback_func(gpointer data) { run_toplevel_callbacks(); /* * If we've emptied our toplevel callback queue, unschedule * ourself. Otherwise, leave ourselves pending so we'll be called * again to deal with more callbacks after another round of the * event loop. */ if (!toplevel_callback_pending() && idle_fn_scheduled) { g_source_remove(toplevel_callback_idle_id); idle_fn_scheduled = false; } return true; } static void notify_toplevel_callback(void *vctx) { if (!idle_fn_scheduled) { toplevel_callback_idle_id = g_idle_add(idle_toplevel_callback_func, NULL); idle_fn_scheduled = true; } } /* ---------------------------------------------------------------------- * Setup function. The real main program must call this. */ void gtkcomm_setup(void) { uxsel_init(); request_callback_notifications(notify_toplevel_callback, NULL); } putty-0.76/unix/gtkcompat.h0000644000175000017500000002230314072266313012705 00000000000000/* * Header file to make compatibility with older GTK versions less * painful, by #defining various things that are pure spelling changes * between GTK1 and GTK2, or between 2 and 3. */ #if !GTK_CHECK_VERSION(2,0,0) #include #include /* Helper macro used in definitions below. Note that although it * *expands* w and flag twice, it does not *evaluate* them twice * because the evaluations are in exclusive branches of ?:. So it's * a side-effect-safe macro. */ #define gtk1_widget_set_unset_flag(w, flag, b) \ ((b) ? GTK_WIDGET_SET_FLAGS(w, flag) : GTK_WIDGET_UNSET_FLAGS(w, flag)) #define GType GtkType #define GObject GtkObject #define G_CALLBACK(x) GTK_SIGNAL_FUNC(x) #define G_OBJECT(x) GTK_OBJECT(x) #define G_TYPE_CHECK_INSTANCE_TYPE GTK_CHECK_TYPE #define G_TYPE_CHECK_INSTANCE_CAST GTK_CHECK_CAST #define G_TYPE_CHECK_CLASS_TYPE GTK_CHECK_CLASS_TYPE #define G_TYPE_CHECK_CLASS_CAST GTK_CHECK_CLASS_CAST #define g_ascii_isspace(x) (isspace((unsigned char)(x))) #define g_signal_connect gtk_signal_connect #define g_signal_connect_swapped gtk_signal_connect_object #define g_signal_stop_emission_by_name gtk_signal_emit_stop_by_name #define g_signal_emit_by_name gtk_signal_emit_by_name #define g_signal_handler_disconnect gtk_signal_disconnect #define g_object_get_data gtk_object_get_data #define g_object_set_data gtk_object_set_data #define g_object_set_data_full gtk_object_set_data_full #define g_object_ref_sink(x) do { \ gtk_object_ref(x); \ gtk_object_sink(x); \ } while (0) #define GDK_GRAB_SUCCESS GrabSuccess #define GDK_WINDOW_XID GDK_WINDOW_XWINDOW #define gtk_widget_set_size_request gtk_widget_set_usize #define gtk_radio_button_get_group gtk_radio_button_group #define gtk_notebook_set_current_page gtk_notebook_set_page #define gtk_color_selection_set_has_opacity_control \ gtk_color_selection_set_opacity #define gtk_dialog_get_content_area(dlg) ((dlg)->vbox) #define gtk_dialog_get_action_area(dlg) ((dlg)->action_area) #define gtk_dialog_set_can_default(dlg) ((dlg)->action_area) #define gtk_widget_get_window(w) ((w)->window) #define gtk_widget_get_parent(w) ((w)->parent) #define gtk_widget_set_allocation(w, a) ((w)->allocation = *(a)) #define gtk_container_get_border_width(c) ((c)->border_width) #define gtk_container_get_focus_child(c) ((c)->focus_child) #define gtk_bin_get_child(b) ((b)->child) #define gtk_color_selection_dialog_get_color_selection(cs) ((cs)->colorsel) #define gtk_selection_data_get_target(sd) ((sd)->target) #define gtk_selection_data_get_data_type(sd) ((sd)->type) #define gtk_selection_data_get_data(sd) ((sd)->data) #define gtk_selection_data_get_length(sd) ((sd)->length) #define gtk_selection_data_get_format(sd) ((sd)->format) #define gtk_adjustment_set_lower(a, val) ((a)->lower = (val)) #define gtk_adjustment_set_upper(a, val) ((a)->upper = (val)) #define gtk_adjustment_set_page_size(a, val) ((a)->page_size = (val)) #define gtk_adjustment_set_page_increment(a, val) ((a)->page_increment = (val)) #define gtk_adjustment_set_step_increment(a, val) ((a)->step_increment = (val)) #define gtk_adjustment_get_value(a) ((a)->value) #define gtk_selection_data_get_selection(a) ((a)->selection) #define gdk_display_beep(disp) gdk_beep() #define gtk_widget_set_has_window(w, b) \ gtk1_widget_set_unset_flag(w, GTK_NO_WINDOW, !(b)) #define gtk_widget_set_mapped(w, b) \ gtk1_widget_set_unset_flag(w, GTK_MAPPED, (b)) #define gtk_widget_set_can_default(w, b) \ gtk1_widget_set_unset_flag(w, GTK_CAN_DEFAULT, (b)) #define gtk_widget_get_visible(w) GTK_WIDGET_VISIBLE(w) #define gtk_widget_get_mapped(w) GTK_WIDGET_MAPPED(w) #define gtk_widget_get_realized(w) GTK_WIDGET_REALIZED(w) #define gtk_widget_get_state(w) GTK_WIDGET_STATE(w) #define gtk_widget_get_can_focus(w) GTK_WIDGET_CAN_FOCUS(w) #define gtk_widget_is_drawable(w) GTK_WIDGET_DRAWABLE(w) #define gtk_widget_is_sensitive(w) GTK_WIDGET_IS_SENSITIVE(w) #define gtk_widget_has_focus(w) GTK_WIDGET_HAS_FOCUS(w) /* This is a bit of a bodge because it relies on us only calling this * macro as GDK_DISPLAY_XDISPLAY(gdk_display_get_default()), so under * GTK1 it makes sense to omit the contained function call and just * return the GDK default display. */ #define GDK_DISPLAY_XDISPLAY(x) GDK_DISPLAY() #define GDK_KEY_C ('C') #define GDK_KEY_V ('V') #define GDK_KEY_c ('c') #define GDK_KEY_v ('v') #endif /* 2.0 */ #if !GTK_CHECK_VERSION(2,22,0) #define gdk_visual_get_depth(v) ((v)->depth) #endif /* 2.22 */ #if !GTK_CHECK_VERSION(2,24,0) #define GDK_KEY_Alt_L GDK_Alt_L #define GDK_KEY_Alt_R GDK_Alt_R #define GDK_KEY_BackSpace GDK_BackSpace #define GDK_KEY_Begin GDK_Begin #define GDK_KEY_Break GDK_Break #define GDK_KEY_Delete GDK_Delete #define GDK_KEY_Down GDK_Down #define GDK_KEY_End GDK_End #define GDK_KEY_Escape GDK_Escape #define GDK_KEY_F10 GDK_F10 #define GDK_KEY_F11 GDK_F11 #define GDK_KEY_F12 GDK_F12 #define GDK_KEY_F13 GDK_F13 #define GDK_KEY_F14 GDK_F14 #define GDK_KEY_F15 GDK_F15 #define GDK_KEY_F16 GDK_F16 #define GDK_KEY_F17 GDK_F17 #define GDK_KEY_F18 GDK_F18 #define GDK_KEY_F19 GDK_F19 #define GDK_KEY_F1 GDK_F1 #define GDK_KEY_F20 GDK_F20 #define GDK_KEY_F2 GDK_F2 #define GDK_KEY_F3 GDK_F3 #define GDK_KEY_F4 GDK_F4 #define GDK_KEY_F5 GDK_F5 #define GDK_KEY_F6 GDK_F6 #define GDK_KEY_F7 GDK_F7 #define GDK_KEY_F8 GDK_F8 #define GDK_KEY_F9 GDK_F9 #define GDK_KEY_Home GDK_Home #define GDK_KEY_Insert GDK_Insert #define GDK_KEY_ISO_Left_Tab GDK_ISO_Left_Tab #define GDK_KEY_KP_0 GDK_KP_0 #define GDK_KEY_KP_1 GDK_KP_1 #define GDK_KEY_KP_2 GDK_KP_2 #define GDK_KEY_KP_3 GDK_KP_3 #define GDK_KEY_KP_4 GDK_KP_4 #define GDK_KEY_KP_5 GDK_KP_5 #define GDK_KEY_KP_6 GDK_KP_6 #define GDK_KEY_KP_7 GDK_KP_7 #define GDK_KEY_KP_8 GDK_KP_8 #define GDK_KEY_KP_9 GDK_KP_9 #define GDK_KEY_KP_Add GDK_KP_Add #define GDK_KEY_KP_Begin GDK_KP_Begin #define GDK_KEY_KP_Decimal GDK_KP_Decimal #define GDK_KEY_KP_Delete GDK_KP_Delete #define GDK_KEY_KP_Divide GDK_KP_Divide #define GDK_KEY_KP_Down GDK_KP_Down #define GDK_KEY_KP_End GDK_KP_End #define GDK_KEY_KP_Enter GDK_KP_Enter #define GDK_KEY_KP_Home GDK_KP_Home #define GDK_KEY_KP_Insert GDK_KP_Insert #define GDK_KEY_KP_Left GDK_KP_Left #define GDK_KEY_KP_Multiply GDK_KP_Multiply #define GDK_KEY_KP_Page_Down GDK_KP_Page_Down #define GDK_KEY_KP_Page_Up GDK_KP_Page_Up #define GDK_KEY_KP_Right GDK_KP_Right #define GDK_KEY_KP_Subtract GDK_KP_Subtract #define GDK_KEY_KP_Up GDK_KP_Up #define GDK_KEY_Left GDK_Left #define GDK_KEY_Meta_L GDK_Meta_L #define GDK_KEY_Meta_R GDK_Meta_R #define GDK_KEY_Num_Lock GDK_Num_Lock #define GDK_KEY_Page_Down GDK_Page_Down #define GDK_KEY_Page_Up GDK_Page_Up #define GDK_KEY_Return GDK_Return #define GDK_KEY_Right GDK_Right #define GDK_KEY_Tab GDK_Tab #define GDK_KEY_Up GDK_Up #define GDK_KEY_greater GDK_greater #define GDK_KEY_less GDK_less #define gdk_window_get_screen(w) gdk_drawable_get_screen(w) #define gtk_combo_box_new_with_model_and_entry(t) gtk_combo_box_entry_new_with_model(t, 1) #endif /* 2.24 */ #if !GTK_CHECK_VERSION(3,0,0) #define GDK_IS_X11_WINDOW(window) (1) #endif #if GTK_CHECK_VERSION(3,0,0) #define STANDARD_OK_LABEL "_OK" #define STANDARD_OPEN_LABEL "_Open" #define STANDARD_CANCEL_LABEL "_Cancel" #else #define STANDARD_OK_LABEL GTK_STOCK_OK #define STANDARD_OPEN_LABEL GTK_STOCK_OPEN #define STANDARD_CANCEL_LABEL GTK_STOCK_CANCEL #endif #if GTK_CHECK_VERSION(3,0,0) #define gtk_hseparator_new() gtk_separator_new(GTK_ORIENTATION_HORIZONTAL) /* Fortunately, my hboxes and vboxes never actually set homogeneous to * true, so I can just wrap these deprecated constructors with a macro * without also having to arrange a call to gtk_box_set_homogeneous. */ #define gtk_hbox_new(homogeneous, spacing) \ gtk_box_new(GTK_ORIENTATION_HORIZONTAL, spacing) #define gtk_vbox_new(homogeneous, spacing) \ gtk_box_new(GTK_ORIENTATION_VERTICAL, spacing) #define gtk_vscrollbar_new(adjust) \ gtk_scrollbar_new(GTK_ORIENTATION_VERTICAL, adjust) #define gdk_get_display() gdk_display_get_name(gdk_display_get_default()) #define gdk_cursor_new(cur) \ gdk_cursor_new_for_display(gdk_display_get_default(), cur) #endif /* 3.0 */ putty-0.76/unix/gtkdlg.c0000644000175000017500000043377114072266313012202 00000000000000/* * gtkdlg.c - GTK implementation of the PuTTY configuration box. */ #include #include #include #include #include #if !GTK_CHECK_VERSION(3,0,0) #include #endif #define MAY_REFER_TO_GTK_IN_HEADERS #include "putty.h" #include "gtkcompat.h" #include "gtkcols.h" #include "gtkfont.h" #include "gtkmisc.h" #ifndef NOT_X_WINDOWS #include #include #include #include "x11misc.h" #endif #include "storage.h" #include "dialog.h" #include "tree234.h" #include "licence.h" #include "ssh.h" #if GTK_CHECK_VERSION(2,0,0) /* Decide which of GtkFileChooserDialog and GtkFileSelection to use */ #define USE_GTK_FILE_CHOOSER_DIALOG #endif struct Shortcut { GtkWidget *widget; struct uctrl *uc; int action; }; struct Shortcuts { struct Shortcut sc[128]; }; struct selparam; struct uctrl { union control *ctrl; GtkWidget *toplevel; GtkWidget **buttons; int nbuttons; /* for radio buttons */ GtkWidget *entry; /* for editbox, filesel, fontsel */ GtkWidget *button; /* for filesel, fontsel */ #if !GTK_CHECK_VERSION(2,4,0) GtkWidget *list; /* for listbox (in GTK1), combobox (<=GTK2.3) */ GtkWidget *menu; /* for optionmenu (==droplist) */ GtkWidget *optmenu; /* also for optionmenu */ #else GtkWidget *combo; /* for combo box (either editable or not) */ #endif #if GTK_CHECK_VERSION(2,0,0) GtkWidget *treeview; /* for listbox (GTK2), droplist+combo (>=2.4) */ GtkListStore *listmodel; /* for all types of list box */ #endif GtkWidget *text; /* for text */ GtkWidget *label; /* for dlg_label_change */ GtkAdjustment *adj; /* for the scrollbar in a list box */ struct selparam *sp; /* which switchable pane of the box we're in */ guint entrysig; guint textsig; int nclicks; }; struct dlgparam { tree234 *byctrl, *bywidget; void *data; struct { unsigned char r, g, b; /* 0-255 */ bool ok; } coloursel_result; /* `flags' are set to indicate when a GTK signal handler is being called * due to automatic processing and should not flag a user event. */ int flags; struct Shortcuts *shortcuts; GtkWidget *window, *cancelbutton; union control *currfocus, *lastfocus; #if !GTK_CHECK_VERSION(2,0,0) GtkWidget *currtreeitem, **treeitems; int ntreeitems; #else size_t nselparams; struct selparam **selparams; #endif struct selparam *curr_panel; struct controlbox *ctrlbox; int retval; post_dialog_fn_t after; void *afterctx; }; #define FLAG_UPDATING_COMBO_LIST 1 #define FLAG_UPDATING_LISTBOX 2 enum { /* values for Shortcut.action */ SHORTCUT_EMPTY, /* no shortcut on this key */ SHORTCUT_TREE, /* focus a tree item */ SHORTCUT_FOCUS, /* focus the supplied widget */ SHORTCUT_UCTRL, /* do something sane with uctrl */ SHORTCUT_UCTRL_UP, /* uctrl is a draglist, move Up */ SHORTCUT_UCTRL_DOWN, /* uctrl is a draglist, move Down */ }; #if GTK_CHECK_VERSION(2,0,0) enum { TREESTORE_PATH, TREESTORE_PARAMS, TREESTORE_NUM }; #endif /* * Forward references. */ static gboolean widget_focus(GtkWidget *widget, GdkEventFocus *event, gpointer data); static void shortcut_add(struct Shortcuts *scs, GtkWidget *labelw, int chr, int action, void *ptr); static void shortcut_highlight(GtkWidget *label, int chr); #if !GTK_CHECK_VERSION(2,0,0) static gboolean listitem_single_key(GtkWidget *item, GdkEventKey *event, gpointer data); static gboolean listitem_multi_key(GtkWidget *item, GdkEventKey *event, gpointer data); static gboolean listitem_button_press(GtkWidget *item, GdkEventButton *event, gpointer data); static gboolean listitem_button_release(GtkWidget *item, GdkEventButton *event, gpointer data); #endif #if !GTK_CHECK_VERSION(2,4,0) static void menuitem_activate(GtkMenuItem *item, gpointer data); #endif #if GTK_CHECK_VERSION(3,0,0) static void colourchoose_response(GtkDialog *dialog, gint response_id, gpointer data); #else static void coloursel_ok(GtkButton *button, gpointer data); static void coloursel_cancel(GtkButton *button, gpointer data); #endif static void dlgparam_destroy(GtkWidget *widget, gpointer data); static int get_listitemheight(GtkWidget *widget); static int uctrl_cmp_byctrl(void *av, void *bv) { struct uctrl *a = (struct uctrl *)av; struct uctrl *b = (struct uctrl *)bv; if (a->ctrl < b->ctrl) return -1; else if (a->ctrl > b->ctrl) return +1; return 0; } static int uctrl_cmp_byctrl_find(void *av, void *bv) { union control *a = (union control *)av; struct uctrl *b = (struct uctrl *)bv; if (a < b->ctrl) return -1; else if (a > b->ctrl) return +1; return 0; } static int uctrl_cmp_bywidget(void *av, void *bv) { struct uctrl *a = (struct uctrl *)av; struct uctrl *b = (struct uctrl *)bv; if (a->toplevel < b->toplevel) return -1; else if (a->toplevel > b->toplevel) return +1; return 0; } static int uctrl_cmp_bywidget_find(void *av, void *bv) { GtkWidget *a = (GtkWidget *)av; struct uctrl *b = (struct uctrl *)bv; if (a < b->toplevel) return -1; else if (a > b->toplevel) return +1; return 0; } static void dlg_init(struct dlgparam *dp) { dp->byctrl = newtree234(uctrl_cmp_byctrl); dp->bywidget = newtree234(uctrl_cmp_bywidget); dp->coloursel_result.ok = false; dp->window = dp->cancelbutton = NULL; #if !GTK_CHECK_VERSION(2,0,0) dp->treeitems = NULL; dp->currtreeitem = NULL; #endif dp->curr_panel = NULL; dp->flags = 0; dp->currfocus = NULL; } static void dlg_cleanup(struct dlgparam *dp) { struct uctrl *uc; freetree234(dp->byctrl); /* doesn't free the uctrls inside */ dp->byctrl = NULL; while ( (uc = index234(dp->bywidget, 0)) != NULL) { del234(dp->bywidget, uc); sfree(uc->buttons); sfree(uc); } freetree234(dp->bywidget); dp->bywidget = NULL; #if !GTK_CHECK_VERSION(2,0,0) sfree(dp->treeitems); #endif } static void dlg_add_uctrl(struct dlgparam *dp, struct uctrl *uc) { add234(dp->byctrl, uc); add234(dp->bywidget, uc); } static struct uctrl *dlg_find_byctrl(struct dlgparam *dp, union control *ctrl) { if (!dp->byctrl) return NULL; return find234(dp->byctrl, ctrl, uctrl_cmp_byctrl_find); } static struct uctrl *dlg_find_bywidget(struct dlgparam *dp, GtkWidget *w) { struct uctrl *ret = NULL; if (!dp->bywidget) return NULL; do { ret = find234(dp->bywidget, w, uctrl_cmp_bywidget_find); if (ret) return ret; w = gtk_widget_get_parent(w); } while (w); return ret; } union control *dlg_last_focused(union control *ctrl, dlgparam *dp) { if (dp->currfocus != ctrl) return dp->currfocus; else return dp->lastfocus; } void dlg_radiobutton_set(union control *ctrl, dlgparam *dp, int which) { struct uctrl *uc = dlg_find_byctrl(dp, ctrl); assert(uc->ctrl->generic.type == CTRL_RADIO); assert(uc->buttons != NULL); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(uc->buttons[which]), true); } int dlg_radiobutton_get(union control *ctrl, dlgparam *dp) { struct uctrl *uc = dlg_find_byctrl(dp, ctrl); int i; assert(uc->ctrl->generic.type == CTRL_RADIO); assert(uc->buttons != NULL); for (i = 0; i < uc->nbuttons; i++) if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(uc->buttons[i]))) return i; return 0; /* got to return something */ } void dlg_checkbox_set(union control *ctrl, dlgparam *dp, bool checked) { struct uctrl *uc = dlg_find_byctrl(dp, ctrl); assert(uc->ctrl->generic.type == CTRL_CHECKBOX); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(uc->toplevel), checked); } bool dlg_checkbox_get(union control *ctrl, dlgparam *dp) { struct uctrl *uc = dlg_find_byctrl(dp, ctrl); assert(uc->ctrl->generic.type == CTRL_CHECKBOX); return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(uc->toplevel)); } void dlg_editbox_set(union control *ctrl, dlgparam *dp, char const *text) { struct uctrl *uc = dlg_find_byctrl(dp, ctrl); GtkWidget *entry; char *tmpstring; assert(uc->ctrl->generic.type == CTRL_EDITBOX); #if GTK_CHECK_VERSION(2,4,0) if (uc->combo) entry = gtk_bin_get_child(GTK_BIN(uc->combo)); else #endif entry = uc->entry; assert(entry != NULL); /* * GTK 2 implements gtk_entry_set_text by means of two separate * 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, dlgparam *dp) { 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) { return dupstr(gtk_entry_get_text (GTK_ENTRY(gtk_bin_get_child(GTK_BIN(uc->combo))))); } #endif if (uc->entry) { return dupstr(gtk_entry_get_text(GTK_ENTRY(uc->entry))); } unreachable("bad control type in editbox_get"); } #if !GTK_CHECK_VERSION(2,4,0) static void container_remove_and_destroy(GtkWidget *w, gpointer data) { GtkContainer *cont = GTK_CONTAINER(data); /* gtk_container_remove will unref the widget for us; we need not. */ gtk_container_remove(cont, w); } #endif /* The `listbox' functions can also apply to combo boxes. */ void dlg_listbox_clear(union control *ctrl, dlgparam *dp) { struct uctrl *uc = dlg_find_byctrl(dp, ctrl); assert(uc->ctrl->generic.type == CTRL_EDITBOX || uc->ctrl->generic.type == CTRL_LISTBOX); #if !GTK_CHECK_VERSION(2,4,0) if (uc->menu) { gtk_container_foreach(GTK_CONTAINER(uc->menu), container_remove_and_destroy, GTK_CONTAINER(uc->menu)); return; } if (uc->list) { gtk_list_clear_items(GTK_LIST(uc->list), 0, -1); return; } #endif #if GTK_CHECK_VERSION(2,0,0) if (uc->listmodel) { gtk_list_store_clear(uc->listmodel); return; } #endif unreachable("bad control type in listbox_clear"); } void dlg_listbox_del(union control *ctrl, dlgparam *dp, int index) { struct uctrl *uc = dlg_find_byctrl(dp, ctrl); assert(uc->ctrl->generic.type == CTRL_EDITBOX || uc->ctrl->generic.type == CTRL_LISTBOX); #if !GTK_CHECK_VERSION(2,4,0) if (uc->menu) { gtk_container_remove (GTK_CONTAINER(uc->menu), g_list_nth_data(GTK_MENU_SHELL(uc->menu)->children, index)); return; } if (uc->list) { gtk_list_clear_items(GTK_LIST(uc->list), index, index+1); return; } #endif #if GTK_CHECK_VERSION(2,0,0) if (uc->listmodel) { GtkTreePath *path; GtkTreeIter iter; assert(uc->listmodel != NULL); path = gtk_tree_path_new_from_indices(index, -1); gtk_tree_model_get_iter(GTK_TREE_MODEL(uc->listmodel), &iter, path); gtk_list_store_remove(uc->listmodel, &iter); gtk_tree_path_free(path); return; } #endif unreachable("bad control type in listbox_del"); } void dlg_listbox_add(union control *ctrl, dlgparam *dp, char const *text) { dlg_listbox_addwithid(ctrl, dp, text, 0); } /* * 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, dlgparam *dp, char const *text, int id) { struct uctrl *uc = dlg_find_byctrl(dp, ctrl); assert(uc->ctrl->generic.type == CTRL_EDITBOX || uc->ctrl->generic.type == CTRL_LISTBOX); /* * This routine is long and complicated in both GTK 1 and 2, * and completely different. Sigh. */ dp->flags |= FLAG_UPDATING_COMBO_LIST; #if !GTK_CHECK_VERSION(2,4,0) if (uc->menu) { /* * List item in a drop-down (but non-combo) list. Tabs are * ignored; we just provide a standard menu item with the * text. */ GtkWidget *menuitem = gtk_menu_item_new_with_label(text); gtk_container_add(GTK_CONTAINER(uc->menu), menuitem); gtk_widget_show(menuitem); g_object_set_data(G_OBJECT(menuitem), "user-data", GINT_TO_POINTER(id)); g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(menuitem_activate), dp); goto done; } if (uc->list && uc->entry) { /* * List item in a combo-box list, which means the sensible * thing to do is make it a perfectly normal label. Hence * tabs are disregarded. */ GtkWidget *listitem = gtk_list_item_new_with_label(text); gtk_container_add(GTK_CONTAINER(uc->list), listitem); gtk_widget_show(listitem); g_object_set_data(G_OBJECT(listitem), "user-data", GINT_TO_POINTER(id)); goto done; } #endif #if !GTK_CHECK_VERSION(2,0,0) if (uc->list) { /* * List item in a non-combo-box list box. We make all of * these Columns containing GtkLabels. This allows us to do * the nasty force_left hack irrespective of whether there * are tabs in the thing. */ GtkWidget *listitem = gtk_list_item_new(); GtkWidget *cols = columns_new(10); gint *percents; int i, ncols; /* Count the tabs in the text, and hence determine # of columns. */ ncols = 1; for (i = 0; text[i]; i++) if (text[i] == '\t') ncols++; assert(ncols <= (uc->ctrl->listbox.ncols ? uc->ctrl->listbox.ncols : 1)); percents = snewn(ncols, gint); percents[ncols-1] = 100; for (i = 0; i < ncols-1; i++) { percents[i] = uc->ctrl->listbox.percentages[i]; percents[ncols-1] -= percents[i]; } columns_set_cols(COLUMNS(cols), ncols, percents); sfree(percents); for (i = 0; i < ncols; i++) { int len = strcspn(text, "\t"); char *dup = dupprintf("%.*s", len, text); GtkWidget *label; text += len; if (*text) text++; label = gtk_label_new(dup); sfree(dup); columns_add(COLUMNS(cols), label, i, 1); columns_force_left_align(COLUMNS(cols), label); gtk_widget_show(label); } gtk_container_add(GTK_CONTAINER(listitem), cols); gtk_widget_show(cols); gtk_container_add(GTK_CONTAINER(uc->list), listitem); gtk_widget_show(listitem); if (ctrl->listbox.multisel) { g_signal_connect(G_OBJECT(listitem), "key_press_event", G_CALLBACK(listitem_multi_key), uc->adj); } else { g_signal_connect(G_OBJECT(listitem), "key_press_event", G_CALLBACK(listitem_single_key), uc->adj); } g_signal_connect(G_OBJECT(listitem), "focus_in_event", G_CALLBACK(widget_focus), dp); g_signal_connect(G_OBJECT(listitem), "button_press_event", G_CALLBACK(listitem_button_press), dp); g_signal_connect(G_OBJECT(listitem), "button_release_event", G_CALLBACK(listitem_button_release), dp); g_object_set_data(G_OBJECT(listitem), "user-data", GINT_TO_POINTER(id)); goto done; } #else if (uc->listmodel) { GtkTreeIter iter; int i, cols; dp->flags |= FLAG_UPDATING_LISTBOX;/* inhibit drag-list update */ gtk_list_store_append(uc->listmodel, &iter); dp->flags &= ~FLAG_UPDATING_LISTBOX; gtk_list_store_set(uc->listmodel, &iter, 0, id, -1); /* * Now go through text and divide it into columns at the tabs, * as necessary. */ cols = (uc->ctrl->generic.type == CTRL_LISTBOX ? ctrl->listbox.ncols : 1); cols = cols ? cols : 1; for (i = 0; i < cols; i++) { int collen = strcspn(text, "\t"); char *tmpstr = snewn(collen+1, char); memcpy(tmpstr, text, collen); tmpstr[collen] = '\0'; gtk_list_store_set(uc->listmodel, &iter, i+1, tmpstr, -1); sfree(tmpstr); text += collen; if (*text) text++; } goto done; } #endif unreachable("bad control type in listbox_addwithid"); done: dp->flags &= ~FLAG_UPDATING_COMBO_LIST; } int dlg_listbox_getid(union control *ctrl, dlgparam *dp, int index) { struct uctrl *uc = dlg_find_byctrl(dp, ctrl); assert(uc->ctrl->generic.type == CTRL_EDITBOX || uc->ctrl->generic.type == CTRL_LISTBOX); #if !GTK_CHECK_VERSION(2,4,0) if (uc->menu || uc->list) { GList *children; GObject *item; children = gtk_container_children(GTK_CONTAINER(uc->menu ? uc->menu : uc->list)); item = G_OBJECT(g_list_nth_data(children, index)); g_list_free(children); return GPOINTER_TO_INT(g_object_get_data(G_OBJECT(item), "user-data")); } #endif #if GTK_CHECK_VERSION(2,0,0) if (uc->listmodel) { GtkTreePath *path; GtkTreeIter iter; int ret; path = gtk_tree_path_new_from_indices(index, -1); gtk_tree_model_get_iter(GTK_TREE_MODEL(uc->listmodel), &iter, path); gtk_tree_model_get(GTK_TREE_MODEL(uc->listmodel), &iter, 0, &ret, -1); gtk_tree_path_free(path); return ret; } #endif unreachable("bad control type in listbox_getid"); return -1; /* placate dataflow analysis */ } /* dlg_listbox_index returns <0 if no single element is selected. */ int dlg_listbox_index(union control *ctrl, dlgparam *dp) { struct uctrl *uc = dlg_find_byctrl(dp, ctrl); assert(uc->ctrl->generic.type == CTRL_EDITBOX || uc->ctrl->generic.type == CTRL_LISTBOX); #if !GTK_CHECK_VERSION(2,4,0) if (uc->menu || uc->list) { GList *children; GtkWidget *item, *activeitem; int i; int selected = -1; if (uc->menu) activeitem = gtk_menu_get_active(GTK_MENU(uc->menu)); else activeitem = NULL; /* unnecessarily placate gcc */ children = gtk_container_children(GTK_CONTAINER(uc->menu ? uc->menu : uc->list)); for (i = 0; children!=NULL && (item = GTK_WIDGET(children->data))!=NULL; i++, children = children->next) { if (uc->menu ? activeitem == item : GTK_WIDGET_STATE(item) == GTK_STATE_SELECTED) { if (selected == -1) selected = i; else selected = -2; } } g_list_free(children); return selected < 0 ? -1 : selected; } #else if (uc->combo) { /* * This API function already does the right thing in the * case of no current selection. */ return gtk_combo_box_get_active(GTK_COMBO_BOX(uc->combo)); } #endif #if GTK_CHECK_VERSION(2,0,0) if (uc->treeview) { GtkTreeSelection *treesel; GtkTreePath *path; GtkTreeModel *model; GList *sellist; gint *indices; int ret; assert(uc->treeview != NULL); treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(uc->treeview)); if (gtk_tree_selection_count_selected_rows(treesel) != 1) return -1; sellist = gtk_tree_selection_get_selected_rows(treesel, &model); assert(sellist && sellist->data); path = sellist->data; if (gtk_tree_path_get_depth(path) != 1) { ret = -1; } else { indices = gtk_tree_path_get_indices(path); if (!indices) { ret = -1; } else { ret = indices[0]; } } g_list_foreach(sellist, (GFunc)gtk_tree_path_free, NULL); g_list_free(sellist); return ret; } #endif unreachable("bad control type in listbox_index"); return -1; /* placate dataflow analysis */ } bool dlg_listbox_issel(union control *ctrl, dlgparam *dp, int index) { struct uctrl *uc = dlg_find_byctrl(dp, ctrl); assert(uc->ctrl->generic.type == CTRL_EDITBOX || uc->ctrl->generic.type == CTRL_LISTBOX); #if !GTK_CHECK_VERSION(2,4,0) if (uc->menu || uc->list) { GList *children; GtkWidget *item, *activeitem; assert(uc->ctrl->generic.type == CTRL_EDITBOX || uc->ctrl->generic.type == CTRL_LISTBOX); assert(uc->menu != NULL || uc->list != NULL); children = gtk_container_children(GTK_CONTAINER(uc->menu ? uc->menu : uc->list)); item = GTK_WIDGET(g_list_nth_data(children, index)); g_list_free(children); if (uc->menu) { activeitem = gtk_menu_get_active(GTK_MENU(uc->menu)); return item == activeitem; } else { return GTK_WIDGET_STATE(item) == GTK_STATE_SELECTED; } } #else if (uc->combo) { /* * This API function already does the right thing in the * case of no current selection. */ return gtk_combo_box_get_active(GTK_COMBO_BOX(uc->combo)) == index; } #endif #if GTK_CHECK_VERSION(2,0,0) if (uc->treeview) { GtkTreeSelection *treesel; GtkTreePath *path; bool ret; assert(uc->treeview != NULL); treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(uc->treeview)); path = gtk_tree_path_new_from_indices(index, -1); ret = gtk_tree_selection_path_is_selected(treesel, path); gtk_tree_path_free(path); return ret; } #endif unreachable("bad control type in listbox_issel"); return false; /* placate dataflow analysis */ } void dlg_listbox_select(union control *ctrl, dlgparam *dp, int index) { struct uctrl *uc = dlg_find_byctrl(dp, ctrl); assert(uc->ctrl->generic.type == CTRL_EDITBOX || uc->ctrl->generic.type == CTRL_LISTBOX); #if !GTK_CHECK_VERSION(2,4,0) if (uc->optmenu) { gtk_option_menu_set_history(GTK_OPTION_MENU(uc->optmenu), index); return; } if (uc->list) { int nitems; GList *items; gdouble newtop, newbot; gtk_list_select_item(GTK_LIST(uc->list), index); /* * Scroll the list box if necessary to ensure the newly * selected item is visible. */ items = gtk_container_children(GTK_CONTAINER(uc->list)); nitems = g_list_length(items); if (nitems > 0) { bool modified = false; g_list_free(items); newtop = uc->adj->lower + (uc->adj->upper - uc->adj->lower) * index / nitems; newbot = uc->adj->lower + (uc->adj->upper - uc->adj->lower) * (index+1) / nitems; if (uc->adj->value > newtop) { modified = true; uc->adj->value = newtop; } else if (uc->adj->value < newbot - uc->adj->page_size) { modified = true; uc->adj->value = newbot - uc->adj->page_size; } if (modified) gtk_adjustment_value_changed(uc->adj); } return; } #else if (uc->combo) { gtk_combo_box_set_active(GTK_COMBO_BOX(uc->combo), index); return; } #endif #if GTK_CHECK_VERSION(2,0,0) if (uc->treeview) { GtkTreeSelection *treesel; GtkTreePath *path; treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(uc->treeview)); path = gtk_tree_path_new_from_indices(index, -1); gtk_tree_selection_select_path(treesel, path); gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(uc->treeview), path, NULL, false, 0.0, 0.0); gtk_tree_path_free(path); return; } #endif unreachable("bad control type in listbox_select"); } void dlg_text_set(union control *ctrl, dlgparam *dp, char const *text) { struct uctrl *uc = dlg_find_byctrl(dp, ctrl); assert(uc->ctrl->generic.type == CTRL_TEXT); assert(uc->text != NULL); gtk_label_set_text(GTK_LABEL(uc->text), text); } void dlg_label_change(union control *ctrl, dlgparam *dp, char const *text) { struct uctrl *uc = dlg_find_byctrl(dp, ctrl); switch (uc->ctrl->generic.type) { case CTRL_BUTTON: gtk_label_set_text(GTK_LABEL(uc->toplevel), text); shortcut_highlight(uc->toplevel, ctrl->button.shortcut); break; case CTRL_CHECKBOX: gtk_label_set_text(GTK_LABEL(uc->toplevel), text); shortcut_highlight(uc->toplevel, ctrl->checkbox.shortcut); break; case CTRL_RADIO: gtk_label_set_text(GTK_LABEL(uc->label), text); shortcut_highlight(uc->label, ctrl->radio.shortcut); break; case CTRL_EDITBOX: gtk_label_set_text(GTK_LABEL(uc->label), text); shortcut_highlight(uc->label, ctrl->editbox.shortcut); break; case CTRL_FILESELECT: gtk_label_set_text(GTK_LABEL(uc->label), text); shortcut_highlight(uc->label, ctrl->fileselect.shortcut); break; case CTRL_FONTSELECT: gtk_label_set_text(GTK_LABEL(uc->label), text); shortcut_highlight(uc->label, ctrl->fontselect.shortcut); break; case CTRL_LISTBOX: gtk_label_set_text(GTK_LABEL(uc->label), text); shortcut_highlight(uc->label, ctrl->listbox.shortcut); break; default: unreachable("bad control type in label_change"); } } void dlg_filesel_set(union control *ctrl, dlgparam *dp, Filename *fn) { 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, dlgparam *dp) { 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, dlgparam *dp, FontSpec *fs) { 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, dlgparam *dp) { 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. */ void dlg_update_start(union control *ctrl, dlgparam *dp) { /* * Apparently we can't do this at all in GTK. GtkCList supports * freeze and thaw, but not GtkList. Bah. */ } void dlg_update_done(union control *ctrl, dlgparam *dp) { /* * Apparently we can't do this at all in GTK. GtkCList supports * freeze and thaw, but not GtkList. Bah. */ } void dlg_set_focus(union control *ctrl, dlgparam *dp) { struct uctrl *uc = dlg_find_byctrl(dp, ctrl); switch (ctrl->generic.type) { case CTRL_CHECKBOX: case CTRL_BUTTON: /* Check boxes and buttons get the focus _and_ get toggled. */ gtk_widget_grab_focus(uc->toplevel); break; case CTRL_FILESELECT: case CTRL_FONTSELECT: case CTRL_EDITBOX: if (uc->entry) { /* Anything containing an edit box gets that focused. */ gtk_widget_grab_focus(uc->entry); } #if GTK_CHECK_VERSION(2,4,0) else if (uc->combo) { /* Failing that, there'll be a combo box. */ gtk_widget_grab_focus(uc->combo); } #endif break; case CTRL_RADIO: /* * Radio buttons: we find the currently selected button and * focus it. */ for (int i = 0; i < ctrl->radio.nbuttons; i++) if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(uc->buttons[i]))) { gtk_widget_grab_focus(uc->buttons[i]); } break; case CTRL_LISTBOX: #if !GTK_CHECK_VERSION(2,4,0) if (uc->optmenu) { gtk_widget_grab_focus(uc->optmenu); break; } #else if (uc->combo) { gtk_widget_grab_focus(uc->combo); break; } #endif #if !GTK_CHECK_VERSION(2,0,0) if (uc->list) { /* * For GTK-1 style list boxes, we tell it to focus one * of its children, which appears to do the Right * Thing. */ gtk_container_focus(GTK_CONTAINER(uc->list), GTK_DIR_TAB_FORWARD); break; } #else if (uc->treeview) { gtk_widget_grab_focus(uc->treeview); break; } #endif unreachable("bad control type in set_focus"); } } /* * During event processing, you might well want to give an error * indication to the user. dlg_beep() is a quick and easy generic * error; dlg_error() puts up a message-box or equivalent. */ void dlg_beep(dlgparam *dp) { gdk_display_beep(gdk_display_get_default()); } static void set_transient_window_pos(GtkWidget *parent, GtkWidget *child) { #if !GTK_CHECK_VERSION(2,0,0) gint x, y, w, h, dx, dy; GtkRequisition req; gtk_window_set_position(GTK_WINDOW(child), GTK_WIN_POS_NONE); gtk_widget_size_request(GTK_WIDGET(child), &req); gdk_window_get_origin(gtk_widget_get_window(GTK_WIDGET(parent)), &x, &y); gdk_window_get_size(gtk_widget_get_window(GTK_WIDGET(parent)), &w, &h); /* * One corner of the transient will be offset inwards, by 1/4 * of the parent window's size, from the corresponding corner * of the parent window. The corner will be chosen so as to * place the transient closer to the centre of the screen; this * should avoid transients going off the edge of the screen on * a regular basis. */ if (x + w/2 < gdk_screen_width() / 2) dx = x + w/4; /* work from left edges */ else dx = x + 3*w/4 - req.width; /* work from right edges */ if (y + h/2 < gdk_screen_height() / 2) dy = y + h/4; /* work from top edges */ else dy = y + 3*h/4 - req.height; /* work from bottom edges */ gtk_widget_set_uposition(GTK_WIDGET(child), dx, dy); #endif } void trivial_post_dialog_fn(void *vctx, int result) { } void dlg_error_msg(dlgparam *dp, const char *msg) { create_message_box( dp->window, "Error", msg, string_width("Some sort of text about a config-box error message"), false, &buttons_ok, trivial_post_dialog_fn, NULL); } /* * This function signals to the front end that the dialog's * processing is completed, and passes an integer value (typically * a success status). */ void dlg_end(dlgparam *dp, int value) { dp->retval = value; gtk_widget_destroy(dp->window); } void dlg_refresh(union control *ctrl, dlgparam *dp) { struct uctrl *uc; if (ctrl) { if (ctrl->generic.handler != NULL) ctrl->generic.handler(ctrl, dp, dp->data, EVENT_REFRESH); } else { int i; for (i = 0; (uc = index234(dp->byctrl, i)) != NULL; i++) { assert(uc->ctrl != NULL); if (uc->ctrl->generic.handler != NULL) uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_REFRESH); } } } void dlg_coloursel_start(union control *ctrl, dlgparam *dp, int r, int g, int b) { struct uctrl *uc = dlg_find_byctrl(dp, ctrl); #if GTK_CHECK_VERSION(3,0,0) GtkWidget *coloursel = gtk_color_chooser_dialog_new("Select a colour", GTK_WINDOW(dp->window)); gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(coloursel), false); #else GtkWidget *okbutton, *cancelbutton; GtkWidget *coloursel = gtk_color_selection_dialog_new("Select a colour"); GtkColorSelectionDialog *ccs = GTK_COLOR_SELECTION_DIALOG(coloursel); GtkColorSelection *cs = GTK_COLOR_SELECTION (gtk_color_selection_dialog_get_color_selection(ccs)); gtk_color_selection_set_has_opacity_control(cs, false); #endif dp->coloursel_result.ok = false; gtk_window_set_modal(GTK_WINDOW(coloursel), true); #if GTK_CHECK_VERSION(3,0,0) { GdkRGBA rgba; rgba.red = r / 255.0; rgba.green = g / 255.0; rgba.blue = b / 255.0; rgba.alpha = 1.0; /* fully opaque! */ gtk_color_chooser_set_rgba(GTK_COLOR_CHOOSER(coloursel), &rgba); } #elif GTK_CHECK_VERSION(2,0,0) { GdkColor col; col.red = r * 0x0101; col.green = g * 0x0101; col.blue = b * 0x0101; gtk_color_selection_set_current_color(cs, &col); } #else { gdouble cvals[4]; cvals[0] = r / 255.0; cvals[1] = g / 255.0; cvals[2] = b / 255.0; cvals[3] = 1.0; /* fully opaque! */ gtk_color_selection_set_color(cs, cvals); } #endif g_object_set_data(G_OBJECT(coloursel), "user-data", (gpointer)uc); #if GTK_CHECK_VERSION(3,0,0) g_signal_connect(G_OBJECT(coloursel), "response", G_CALLBACK(colourchoose_response), (gpointer)dp); #else #if GTK_CHECK_VERSION(2,0,0) g_object_get(G_OBJECT(ccs), "ok-button", &okbutton, "cancel-button", &cancelbutton, (const char *)NULL); #else okbutton = ccs->ok_button; cancelbutton = ccs->cancel_button; #endif g_object_set_data(G_OBJECT(okbutton), "user-data", (gpointer)coloursel); g_object_set_data(G_OBJECT(cancelbutton), "user-data", (gpointer)coloursel); g_signal_connect(G_OBJECT(okbutton), "clicked", G_CALLBACK(coloursel_ok), (gpointer)dp); g_signal_connect(G_OBJECT(cancelbutton), "clicked", G_CALLBACK(coloursel_cancel), (gpointer)dp); g_signal_connect_swapped(G_OBJECT(okbutton), "clicked", G_CALLBACK(gtk_widget_destroy), (gpointer)coloursel); g_signal_connect_swapped(G_OBJECT(cancelbutton), "clicked", G_CALLBACK(gtk_widget_destroy), (gpointer)coloursel); #endif gtk_widget_show(coloursel); } bool dlg_coloursel_results(union control *ctrl, dlgparam *dp, int *r, int *g, int *b) { if (dp->coloursel_result.ok) { *r = dp->coloursel_result.r; *g = dp->coloursel_result.g; *b = dp->coloursel_result.b; return true; } else return false; } /* ---------------------------------------------------------------------- * Signal handlers while the dialog box is active. */ static gboolean widget_focus(GtkWidget *widget, GdkEventFocus *event, gpointer data) { struct dlgparam *dp = (struct dlgparam *)data; struct uctrl *uc = dlg_find_bywidget(dp, widget); union control *focus; if (uc && uc->ctrl) focus = uc->ctrl; else focus = NULL; if (focus != dp->currfocus) { dp->lastfocus = dp->currfocus; dp->currfocus = focus; } return false; } static void button_clicked(GtkButton *button, gpointer data) { struct dlgparam *dp = (struct dlgparam *)data; struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(button)); uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_ACTION); } static void button_toggled(GtkToggleButton *tb, gpointer data) { struct dlgparam *dp = (struct dlgparam *)data; struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(tb)); uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_VALCHANGE); } static gboolean editbox_key(GtkWidget *widget, GdkEventKey *event, gpointer data) { /* * GtkEntry has a nasty habit of eating the Return key, which * is unhelpful since it doesn't actually _do_ anything with it * (it calls gtk_widget_activate, but our edit boxes never need * activating). So I catch Return before GtkEntry sees it, and * pass it straight on to the parent widget. Effect: hitting * Return in an edit box will now activate the default button * in the dialog just like it will everywhere else. */ GtkWidget *parent = gtk_widget_get_parent(widget); if (event->keyval == GDK_KEY_Return && parent != NULL) { gboolean return_val; g_signal_stop_emission_by_name(G_OBJECT(widget), "key_press_event"); g_signal_emit_by_name(G_OBJECT(parent), "key_press_event", event, &return_val); return return_val; } return false; } static void editbox_changed(GtkEditable *ed, gpointer data) { struct dlgparam *dp = (struct dlgparam *)data; if (!(dp->flags & FLAG_UPDATING_COMBO_LIST)) { struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(ed)); uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_VALCHANGE); } } static gboolean editbox_lostfocus(GtkWidget *ed, GdkEventFocus *event, gpointer data) { struct dlgparam *dp = (struct dlgparam *)data; struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(ed)); uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_REFRESH); return false; } #if !GTK_CHECK_VERSION(2,0,0) /* * GTK 1 list box event handlers. */ static gboolean listitem_key(GtkWidget *item, GdkEventKey *event, gpointer data, bool multiple) { GtkAdjustment *adj = GTK_ADJUSTMENT(data); if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up || event->keyval == GDK_Down || event->keyval == GDK_KP_Down || event->keyval == GDK_Page_Up || event->keyval == GDK_KP_Page_Up || event->keyval == GDK_Page_Down || event->keyval == GDK_KP_Page_Down) { /* * Up, Down, PgUp or PgDn have been pressed on a ListItem * in a list box. So, if the list box is single-selection: * * - if the list item in question isn't already selected, * we simply select it. * - otherwise, we find the next one (or next * however-far-away) in whichever direction we're going, * and select that. * + in this case, we must also fiddle with the * scrollbar to ensure the newly selected item is * actually visible. * * If it's multiple-selection, we do all of the above * except actually selecting anything, so we move the focus * and fiddle the scrollbar to follow it. */ GtkWidget *list = item->parent; g_signal_stop_emission_by_name(G_OBJECT(item), "key_press_event"); if (!multiple && GTK_WIDGET_STATE(item) != GTK_STATE_SELECTED) { gtk_list_select_child(GTK_LIST(list), item); } else { int direction = (event->keyval==GDK_Up || event->keyval==GDK_KP_Up || event->keyval==GDK_Page_Up || event->keyval==GDK_KP_Page_Up) ? -1 : +1; int step = (event->keyval==GDK_Page_Down || event->keyval==GDK_KP_Page_Down || event->keyval==GDK_Page_Up || event->keyval==GDK_KP_Page_Up) ? 2 : 1; int i, n; GList *children, *chead; chead = children = gtk_container_children(GTK_CONTAINER(list)); n = g_list_length(children); if (step == 2) { /* * Figure out how many list items to a screenful, * and adjust the step appropriately. */ step = 0.5 + adj->page_size * n / (adj->upper - adj->lower); step--; /* go by one less than that */ } i = 0; while (children != NULL) { if (item == children->data) break; children = children->next; i++; } while (step > 0) { if (direction < 0 && i > 0) children = children->prev, i--; else if (direction > 0 && i < n-1) children = children->next, i++; step--; } if (children && children->data) { if (!multiple) gtk_list_select_child(GTK_LIST(list), GTK_WIDGET(children->data)); gtk_widget_grab_focus(GTK_WIDGET(children->data)); gtk_adjustment_clamp_page (adj, adj->lower + (adj->upper-adj->lower) * i / n, adj->lower + (adj->upper-adj->lower) * (i+1) / n); } g_list_free(chead); } return true; } return false; } static gboolean listitem_single_key(GtkWidget *item, GdkEventKey *event, gpointer data) { return listitem_key(item, event, data, false); } static gboolean listitem_multi_key(GtkWidget *item, GdkEventKey *event, gpointer data) { return listitem_key(item, event, data, true); } static gboolean listitem_button_press(GtkWidget *item, GdkEventButton *event, gpointer data) { struct dlgparam *dp = (struct dlgparam *)data; struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(item)); switch (event->type) { default: case GDK_BUTTON_PRESS: uc->nclicks = 1; break; case GDK_2BUTTON_PRESS: uc->nclicks = 2; break; case GDK_3BUTTON_PRESS: uc->nclicks = 3; break; } return false; } static gboolean listitem_button_release(GtkWidget *item, GdkEventButton *event, gpointer data) { struct dlgparam *dp = (struct dlgparam *)data; struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(item)); if (uc->nclicks>1) { uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_ACTION); return true; } return false; } static void list_selchange(GtkList *list, gpointer data) { struct dlgparam *dp = (struct dlgparam *)data; struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(list)); if (!uc) return; uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_SELCHANGE); } static void draglist_move(struct dlgparam *dp, struct uctrl *uc, int direction) { int index = dlg_listbox_index(uc->ctrl, dp); GList *children = gtk_container_children(GTK_CONTAINER(uc->list)); GtkWidget *child; if ((index < 0) || (index == 0 && direction < 0) || (index == g_list_length(children)-1 && direction > 0)) { gdk_display_beep(gdk_display_get_default()); return; } child = g_list_nth_data(children, index); gtk_widget_ref(child); gtk_list_clear_items(GTK_LIST(uc->list), index, index+1); g_list_free(children); children = NULL; children = g_list_append(children, child); gtk_list_insert_items(GTK_LIST(uc->list), children, index + direction); gtk_list_select_item(GTK_LIST(uc->list), index + direction); uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_VALCHANGE); } static void draglist_up(GtkButton *button, gpointer data) { struct dlgparam *dp = (struct dlgparam *)data; struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(button)); draglist_move(dp, uc, -1); } static void draglist_down(GtkButton *button, gpointer data) { struct dlgparam *dp = (struct dlgparam *)data; struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(button)); draglist_move(dp, uc, +1); } #else /* !GTK_CHECK_VERSION(2,0,0) */ /* * GTK 2 list box event handlers. */ static void listbox_doubleclick(GtkTreeView *treeview, GtkTreePath *path, GtkTreeViewColumn *column, gpointer data) { struct dlgparam *dp = (struct dlgparam *)data; struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(treeview)); if (uc) uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_ACTION); } static void listbox_selchange(GtkTreeSelection *treeselection, gpointer data) { struct dlgparam *dp = (struct dlgparam *)data; GtkTreeView *tree = gtk_tree_selection_get_tree_view(treeselection); struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(tree)); if (uc) uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_SELCHANGE); } struct draglist_valchange_ctx { struct uctrl *uc; struct dlgparam *dp; }; static gboolean draglist_valchange(gpointer data) { struct draglist_valchange_ctx *ctx = (struct draglist_valchange_ctx *)data; ctx->uc->ctrl->generic.handler(ctx->uc->ctrl, ctx->dp, ctx->dp->data, EVENT_VALCHANGE); sfree(ctx); return false; } static void listbox_reorder(GtkTreeModel *treemodel, GtkTreePath *path, GtkTreeIter *iter, gpointer data) { struct dlgparam *dp = (struct dlgparam *)data; gpointer tree; struct uctrl *uc; if (dp->flags & FLAG_UPDATING_LISTBOX) return; /* not a user drag operation */ tree = g_object_get_data(G_OBJECT(treemodel), "user-data"); uc = dlg_find_bywidget(dp, GTK_WIDGET(tree)); if (uc) { /* * We should cause EVENT_VALCHANGE on the list box, now * that its rows have been reordered. However, the GTK 2 * docs say that at the point this signal is received the * new row might not have actually been filled in yet. * * (So what smegging use is it then, eh? Don't suppose it * occurred to you at any point that letting the * application know _after_ the reordering was compelete * might be helpful to someone?) * * To get round this, I schedule an idle function, which I * hope won't be called until the main event loop is * re-entered after the drag-and-drop handler has finished * furtling with the list store. */ struct draglist_valchange_ctx *ctx = snew(struct draglist_valchange_ctx); ctx->uc = uc; ctx->dp = dp; g_idle_add(draglist_valchange, ctx); } } #endif /* !GTK_CHECK_VERSION(2,0,0) */ #if !GTK_CHECK_VERSION(2,4,0) static void menuitem_activate(GtkMenuItem *item, gpointer data) { struct dlgparam *dp = (struct dlgparam *)data; GtkWidget *menushell = GTK_WIDGET(item)->parent; gpointer optmenu = g_object_get_data(G_OBJECT(menushell), "user-data"); struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(optmenu)); uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_SELCHANGE); } #else static void droplist_selchange(GtkComboBox *combo, gpointer data) { struct dlgparam *dp = (struct dlgparam *)data; struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(combo)); if (uc) uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_SELCHANGE); } #endif /* !GTK_CHECK_VERSION(2,4,0) */ #ifdef USE_GTK_FILE_CHOOSER_DIALOG static void filechoose_response(GtkDialog *dialog, gint response, gpointer data) { /* struct dlgparam *dp = (struct dlgparam *)data; */ struct uctrl *uc = g_object_get_data(G_OBJECT(dialog), "user-data"); if (response == GTK_RESPONSE_ACCEPT) { gchar *name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); gtk_entry_set_text(GTK_ENTRY(uc->entry), name); g_free(name); } gtk_widget_destroy(GTK_WIDGET(dialog)); } #else static void filesel_ok(GtkButton *button, gpointer data) { /* struct dlgparam *dp = (struct dlgparam *)data; */ gpointer filesel = g_object_get_data(G_OBJECT(button), "user-data"); struct uctrl *uc = g_object_get_data(G_OBJECT(filesel), "user-data"); const char *name = gtk_file_selection_get_filename (GTK_FILE_SELECTION(filesel)); gtk_entry_set_text(GTK_ENTRY(uc->entry), name); } #endif static void fontsel_ok(GtkButton *button, gpointer data) { /* struct dlgparam *dp = (struct dlgparam *)data; */ #if !GTK_CHECK_VERSION(2,0,0) gpointer fontsel = g_object_get_data(G_OBJECT(button), "user-data"); struct uctrl *uc = g_object_get_data(G_OBJECT(fontsel), "user-data"); const char *name = gtk_font_selection_dialog_get_font_name (GTK_FONT_SELECTION_DIALOG(fontsel)); gtk_entry_set_text(GTK_ENTRY(uc->entry), name); #else unifontsel *fontsel = (unifontsel *)g_object_get_data (G_OBJECT(button), "user-data"); struct uctrl *uc = (struct uctrl *)fontsel->user_data; char *name = unifontsel_get_name(fontsel); assert(name); /* should always be ok after OK pressed */ gtk_entry_set_text(GTK_ENTRY(uc->entry), name); sfree(name); #endif } #if GTK_CHECK_VERSION(3,0,0) static void colourchoose_response(GtkDialog *dialog, gint response_id, gpointer data) { struct dlgparam *dp = (struct dlgparam *)data; struct uctrl *uc = g_object_get_data(G_OBJECT(dialog), "user-data"); if (response_id == GTK_RESPONSE_OK) { GdkRGBA rgba; gtk_color_chooser_get_rgba(GTK_COLOR_CHOOSER(dialog), &rgba); dp->coloursel_result.r = (int) (255 * rgba.red); dp->coloursel_result.g = (int) (255 * rgba.green); dp->coloursel_result.b = (int) (255 * rgba.blue); dp->coloursel_result.ok = true; } else { dp->coloursel_result.ok = false; } uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_CALLBACK); gtk_widget_destroy(GTK_WIDGET(dialog)); } #else /* GTK 1/2 coloursel response handlers */ static void coloursel_ok(GtkButton *button, gpointer data) { struct dlgparam *dp = (struct dlgparam *)data; gpointer coloursel = g_object_get_data(G_OBJECT(button), "user-data"); struct uctrl *uc = g_object_get_data(G_OBJECT(coloursel), "user-data"); #if GTK_CHECK_VERSION(2,0,0) { GtkColorSelection *cs = GTK_COLOR_SELECTION (gtk_color_selection_dialog_get_color_selection (GTK_COLOR_SELECTION_DIALOG(coloursel))); GdkColor col; gtk_color_selection_get_current_color(cs, &col); dp->coloursel_result.r = col.red / 0x0100; dp->coloursel_result.g = col.green / 0x0100; dp->coloursel_result.b = col.blue / 0x0100; } #else { GtkColorSelection *cs = GTK_COLOR_SELECTION (gtk_color_selection_dialog_get_color_selection (GTK_COLOR_SELECTION_DIALOG(coloursel))); gdouble cvals[4]; gtk_color_selection_get_color(cs, cvals); dp->coloursel_result.r = (int) (255 * cvals[0]); dp->coloursel_result.g = (int) (255 * cvals[1]); dp->coloursel_result.b = (int) (255 * cvals[2]); } #endif dp->coloursel_result.ok = true; uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_CALLBACK); } static void coloursel_cancel(GtkButton *button, gpointer data) { struct dlgparam *dp = (struct dlgparam *)data; gpointer coloursel = g_object_get_data(G_OBJECT(button), "user-data"); struct uctrl *uc = g_object_get_data(G_OBJECT(coloursel), "user-data"); dp->coloursel_result.ok = false; uc->ctrl->generic.handler(uc->ctrl, dp, dp->data, EVENT_CALLBACK); } #endif /* end of coloursel response handlers */ static void filefont_clicked(GtkButton *button, gpointer data) { struct dlgparam *dp = (struct dlgparam *)data; struct uctrl *uc = dlg_find_bywidget(dp, GTK_WIDGET(button)); if (uc->ctrl->generic.type == CTRL_FILESELECT) { #ifdef USE_GTK_FILE_CHOOSER_DIALOG GtkWidget *filechoose = gtk_file_chooser_dialog_new (uc->ctrl->fileselect.title, GTK_WINDOW(dp->window), (uc->ctrl->fileselect.for_writing ? GTK_FILE_CHOOSER_ACTION_SAVE : GTK_FILE_CHOOSER_ACTION_OPEN), STANDARD_CANCEL_LABEL, GTK_RESPONSE_CANCEL, STANDARD_OPEN_LABEL, GTK_RESPONSE_ACCEPT, (const gchar *)NULL); gtk_window_set_modal(GTK_WINDOW(filechoose), true); g_object_set_data(G_OBJECT(filechoose), "user-data", (gpointer)uc); g_signal_connect(G_OBJECT(filechoose), "response", G_CALLBACK(filechoose_response), (gpointer)dp); gtk_widget_show(filechoose); #else GtkWidget *filesel = gtk_file_selection_new(uc->ctrl->fileselect.title); gtk_window_set_modal(GTK_WINDOW(filesel), true); g_object_set_data (G_OBJECT(GTK_FILE_SELECTION(filesel)->ok_button), "user-data", (gpointer)filesel); g_object_set_data(G_OBJECT(filesel), "user-data", (gpointer)uc); g_signal_connect (G_OBJECT(GTK_FILE_SELECTION(filesel)->ok_button), "clicked", G_CALLBACK(filesel_ok), (gpointer)dp); g_signal_connect_swapped (G_OBJECT(GTK_FILE_SELECTION(filesel)->ok_button), "clicked", G_CALLBACK(gtk_widget_destroy), (gpointer)filesel); g_signal_connect_swapped (G_OBJECT(GTK_FILE_SELECTION(filesel)->cancel_button), "clicked", G_CALLBACK(gtk_widget_destroy), (gpointer)filesel); gtk_widget_show(filesel); #endif } if (uc->ctrl->generic.type == CTRL_FONTSELECT) { const gchar *fontname = gtk_entry_get_text(GTK_ENTRY(uc->entry)); #if !GTK_CHECK_VERSION(2,0,0) /* * Use the GTK 1 standard font selector. */ gchar *spacings[] = { "c", "m", NULL }; GtkWidget *fontsel = gtk_font_selection_dialog_new("Select a font"); gtk_window_set_modal(GTK_WINDOW(fontsel), true); gtk_font_selection_dialog_set_filter (GTK_FONT_SELECTION_DIALOG(fontsel), GTK_FONT_FILTER_BASE, GTK_FONT_ALL, NULL, NULL, NULL, NULL, spacings, NULL); if (!gtk_font_selection_dialog_set_font_name (GTK_FONT_SELECTION_DIALOG(fontsel), fontname)) { /* * If the font name wasn't found as it was, try opening * it and extracting its FONT property. This should * have the effect of mapping short aliases into true * XLFDs. */ GdkFont *font = gdk_font_load(fontname); if (font) { XFontStruct *xfs = GDK_FONT_XFONT(font); Display *disp = get_x11_display(); Atom fontprop = XInternAtom(disp, "FONT", False); unsigned long ret; assert(disp); /* this is GTK1! */ gdk_font_ref(font); if (XGetFontProperty(xfs, fontprop, &ret)) { char *name = XGetAtomName(disp, (Atom)ret); if (name) gtk_font_selection_dialog_set_font_name (GTK_FONT_SELECTION_DIALOG(fontsel), name); } gdk_font_unref(font); } } g_object_set_data (G_OBJECT(GTK_FONT_SELECTION_DIALOG(fontsel)->ok_button), "user-data", (gpointer)fontsel); g_object_set_data(G_OBJECT(fontsel), "user-data", (gpointer)uc); g_signal_connect (G_OBJECT(GTK_FONT_SELECTION_DIALOG(fontsel)->ok_button), "clicked", G_CALLBACK(fontsel_ok), (gpointer)dp); g_signal_connect_swapped (G_OBJECT(GTK_FONT_SELECTION_DIALOG(fontsel)->ok_button), "clicked", G_CALLBACK(gtk_widget_destroy), (gpointer)fontsel); g_signal_connect_swapped (G_OBJECT(GTK_FONT_SELECTION_DIALOG(fontsel)->cancel_button), "clicked", G_CALLBACK(gtk_widget_destroy), (gpointer)fontsel); gtk_widget_show(fontsel); #else /* !GTK_CHECK_VERSION(2,0,0) */ /* * Use the unifontsel code provided in gtkfont.c. */ unifontsel *fontsel = unifontsel_new("Select a font"); gtk_window_set_modal(fontsel->window, true); unifontsel_set_name(fontsel, fontname); g_object_set_data(G_OBJECT(fontsel->ok_button), "user-data", (gpointer)fontsel); fontsel->user_data = uc; g_signal_connect(G_OBJECT(fontsel->ok_button), "clicked", G_CALLBACK(fontsel_ok), (gpointer)dp); g_signal_connect_swapped(G_OBJECT(fontsel->ok_button), "clicked", G_CALLBACK(unifontsel_destroy), (gpointer)fontsel); g_signal_connect_swapped(G_OBJECT(fontsel->cancel_button),"clicked", G_CALLBACK(unifontsel_destroy), (gpointer)fontsel); gtk_widget_show(GTK_WIDGET(fontsel->window)); #endif /* !GTK_CHECK_VERSION(2,0,0) */ } } #if !GTK_CHECK_VERSION(3,0,0) static void label_sizealloc(GtkWidget *widget, GtkAllocation *alloc, gpointer data) { struct dlgparam *dp = (struct dlgparam *)data; struct uctrl *uc = dlg_find_bywidget(dp, widget); gtk_widget_set_size_request(uc->text, alloc->width, -1); gtk_label_set_text(GTK_LABEL(uc->text), uc->ctrl->generic.label); g_signal_handler_disconnect(G_OBJECT(uc->text), uc->textsig); } #endif /* ---------------------------------------------------------------------- * This function does the main layout work: it reads a controlset, * it creates the relevant GTK controls, and returns a GtkWidget * containing the result. (This widget might be a title of some * sort, it might be a Columns containing many controls, or it * might be a GtkFrame containing a Columns; whatever it is, it's * definitely a GtkWidget and should probably be added to a * GtkVbox.) * * `win' is required for setting the default button. If it is * non-NULL, all buttons created will be default-capable (so they * have extra space round them for the default highlight). */ GtkWidget *layout_ctrls( struct dlgparam *dp, struct selparam *sp, struct Shortcuts *scs, struct controlset *s, GtkWindow *win) { Columns *cols; GtkWidget *ret; int i; if (!s->boxname) { /* This controlset is a panel title. */ assert(s->boxtitle); return gtk_label_new(s->boxtitle); } /* * Otherwise, we expect to be laying out actual controls, so * we'll start by creating a Columns for the purpose. */ cols = COLUMNS(columns_new(4)); ret = GTK_WIDGET(cols); gtk_widget_show(ret); /* * Create a containing frame if we have a box name. */ if (*s->boxname) { ret = gtk_frame_new(s->boxtitle); /* NULL is valid here */ gtk_container_set_border_width(GTK_CONTAINER(cols), 4); gtk_container_add(GTK_CONTAINER(ret), GTK_WIDGET(cols)); gtk_widget_show(ret); } /* * Now iterate through the controls themselves, create them, * and add them to the Columns. */ for (i = 0; i < s->ncontrols; i++) { union control *ctrl = s->ctrls[i]; struct uctrl *uc; bool left = false; GtkWidget *w = NULL; switch (ctrl->generic.type) { case CTRL_COLUMNS: { static const int simplecols[1] = { 100 }; columns_set_cols(cols, ctrl->columns.ncols, (ctrl->columns.percentages ? ctrl->columns.percentages : simplecols)); continue; /* no actual control created */ } case CTRL_TABDELAY: { struct uctrl *uc = dlg_find_byctrl(dp, ctrl->tabdelay.ctrl); if (uc) columns_taborder_last(cols, uc->toplevel); continue; /* no actual control created */ } } uc = snew(struct uctrl); uc->sp = sp; uc->ctrl = ctrl; uc->buttons = NULL; uc->entry = NULL; #if !GTK_CHECK_VERSION(2,4,0) uc->list = uc->menu = uc->optmenu = NULL; #else uc->combo = NULL; #endif #if GTK_CHECK_VERSION(2,0,0) uc->treeview = NULL; uc->listmodel = NULL; #endif uc->button = uc->text = NULL; uc->label = NULL; uc->nclicks = 0; switch (ctrl->generic.type) { case CTRL_BUTTON: w = gtk_button_new_with_label(ctrl->generic.label); if (win) { gtk_widget_set_can_default(w, true); if (ctrl->button.isdefault) gtk_window_set_default(win, w); if (ctrl->button.iscancel) dp->cancelbutton = w; } g_signal_connect(G_OBJECT(w), "clicked", G_CALLBACK(button_clicked), dp); g_signal_connect(G_OBJECT(w), "focus_in_event", G_CALLBACK(widget_focus), dp); shortcut_add(scs, gtk_bin_get_child(GTK_BIN(w)), ctrl->button.shortcut, SHORTCUT_UCTRL, uc); break; case CTRL_CHECKBOX: w = gtk_check_button_new_with_label(ctrl->generic.label); g_signal_connect(G_OBJECT(w), "toggled", G_CALLBACK(button_toggled), dp); g_signal_connect(G_OBJECT(w), "focus_in_event", G_CALLBACK(widget_focus), dp); shortcut_add(scs, gtk_bin_get_child(GTK_BIN(w)), ctrl->checkbox.shortcut, SHORTCUT_UCTRL, uc); left = true; break; case CTRL_RADIO: { /* * Radio buttons get to go inside their own Columns, no * matter what. */ gint i, *percentages; GSList *group; w = columns_new(0); if (ctrl->generic.label) { GtkWidget *label = gtk_label_new(ctrl->generic.label); columns_add(COLUMNS(w), label, 0, 1); columns_force_left_align(COLUMNS(w), label); gtk_widget_show(label); shortcut_add(scs, label, ctrl->radio.shortcut, SHORTCUT_UCTRL, uc); uc->label = label; } percentages = g_new(gint, ctrl->radio.ncolumns); for (i = 0; i < ctrl->radio.ncolumns; i++) { percentages[i] = ((100 * (i+1) / ctrl->radio.ncolumns) - 100 * i / ctrl->radio.ncolumns); } columns_set_cols(COLUMNS(w), ctrl->radio.ncolumns, percentages); g_free(percentages); group = NULL; uc->nbuttons = ctrl->radio.nbuttons; uc->buttons = snewn(uc->nbuttons, GtkWidget *); for (i = 0; i < ctrl->radio.nbuttons; i++) { GtkWidget *b; gint colstart; b = (gtk_radio_button_new_with_label (group, ctrl->radio.buttons[i])); uc->buttons[i] = b; group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(b)); colstart = i % ctrl->radio.ncolumns; columns_add(COLUMNS(w), b, colstart, (i == ctrl->radio.nbuttons-1 ? ctrl->radio.ncolumns - colstart : 1)); columns_force_left_align(COLUMNS(w), b); gtk_widget_show(b); g_signal_connect(G_OBJECT(b), "toggled", G_CALLBACK(button_toggled), dp); g_signal_connect(G_OBJECT(b), "focus_in_event", G_CALLBACK(widget_focus), dp); if (ctrl->radio.shortcuts) { shortcut_add(scs, gtk_bin_get_child(GTK_BIN(b)), ctrl->radio.shortcuts[i], SHORTCUT_UCTRL, uc); } } break; } case CTRL_EDITBOX: { GtkWidget *signalobject; if (ctrl->editbox.has_list) { #if !GTK_CHECK_VERSION(2,4,0) /* * GTK 1 combo box. */ w = gtk_combo_new(); gtk_combo_set_value_in_list(GTK_COMBO(w), false, true); uc->entry = GTK_COMBO(w)->entry; uc->list = GTK_COMBO(w)->list; signalobject = uc->entry; #else /* * GTK 2 combo box. */ uc->listmodel = gtk_list_store_new(2, G_TYPE_INT, G_TYPE_STRING); w = gtk_combo_box_new_with_model_and_entry (GTK_TREE_MODEL(uc->listmodel)); g_object_set(G_OBJECT(w), "entry-text-column", 1, (const char *)NULL); /* We cannot support password combo boxes. */ assert(!ctrl->editbox.password); uc->combo = w; signalobject = uc->combo; #endif } else { w = gtk_entry_new(); if (ctrl->editbox.password) gtk_entry_set_visibility(GTK_ENTRY(w), false); uc->entry = w; signalobject = w; } uc->entrysig = g_signal_connect(G_OBJECT(signalobject), "changed", G_CALLBACK(editbox_changed), dp); g_signal_connect(G_OBJECT(signalobject), "key_press_event", G_CALLBACK(editbox_key), dp); g_signal_connect(G_OBJECT(signalobject), "focus_in_event", G_CALLBACK(widget_focus), dp); g_signal_connect(G_OBJECT(signalobject), "focus_out_event", G_CALLBACK(editbox_lostfocus), dp); g_signal_connect(G_OBJECT(signalobject), "focus_out_event", G_CALLBACK(editbox_lostfocus), dp); #if !GTK_CHECK_VERSION(3,0,0) /* * Edit boxes, for some strange reason, have a minimum * width of 150 in GTK 1.2. We don't want this - we'd * rather the edit boxes acquired their natural width * from the column layout of the rest of the box. */ { GtkRequisition req; gtk_widget_size_request(w, &req); gtk_widget_set_size_request(w, 10, req.height); } #else /* * In GTK 3, this is still true, but there's a special * method for GtkEntry in particular to fix it. */ if (GTK_IS_ENTRY(w)) gtk_entry_set_width_chars(GTK_ENTRY(w), 1); #endif if (ctrl->generic.label) { GtkWidget *label, *container; label = gtk_label_new(ctrl->generic.label); shortcut_add(scs, label, ctrl->editbox.shortcut, SHORTCUT_FOCUS, uc->entry); container = columns_new(4); if (ctrl->editbox.percentwidth == 100) { columns_add(COLUMNS(container), label, 0, 1); columns_force_left_align(COLUMNS(container), label); columns_add(COLUMNS(container), w, 0, 1); } else { gint percentages[2]; percentages[1] = ctrl->editbox.percentwidth; percentages[0] = 100 - ctrl->editbox.percentwidth; columns_set_cols(COLUMNS(container), 2, percentages); columns_add(COLUMNS(container), label, 0, 1); columns_force_left_align(COLUMNS(container), label); columns_add(COLUMNS(container), w, 1, 1); columns_force_same_height(COLUMNS(container), label, w); } gtk_widget_show(label); gtk_widget_show(w); w = container; uc->label = label; } break; } case CTRL_FILESELECT: case CTRL_FONTSELECT: { GtkWidget *ww; const char *browsebtn = (ctrl->generic.type == CTRL_FILESELECT ? "Browse..." : "Change..."); gint percentages[] = { 75, 25 }; w = columns_new(4); columns_set_cols(COLUMNS(w), 2, percentages); if (ctrl->generic.label) { ww = gtk_label_new(ctrl->generic.label); columns_add(COLUMNS(w), ww, 0, 2); columns_force_left_align(COLUMNS(w), ww); gtk_widget_show(ww); shortcut_add(scs, ww, (ctrl->generic.type == CTRL_FILESELECT ? ctrl->fileselect.shortcut : ctrl->fontselect.shortcut), SHORTCUT_UCTRL, uc); uc->label = ww; } uc->entry = ww = gtk_entry_new(); #if !GTK_CHECK_VERSION(3,0,0) { GtkRequisition req; gtk_widget_size_request(ww, &req); gtk_widget_set_size_request(ww, 10, req.height); } #else gtk_entry_set_width_chars(GTK_ENTRY(ww), 1); #endif columns_add(COLUMNS(w), ww, 0, 1); gtk_widget_show(ww); uc->button = ww = gtk_button_new_with_label(browsebtn); columns_add(COLUMNS(w), ww, 1, 1); gtk_widget_show(ww); columns_force_same_height(COLUMNS(w), uc->entry, uc->button); g_signal_connect(G_OBJECT(uc->entry), "key_press_event", G_CALLBACK(editbox_key), dp); uc->entrysig = g_signal_connect(G_OBJECT(uc->entry), "changed", G_CALLBACK(editbox_changed), dp); g_signal_connect(G_OBJECT(uc->entry), "focus_in_event", G_CALLBACK(widget_focus), dp); g_signal_connect(G_OBJECT(uc->button), "focus_in_event", G_CALLBACK(widget_focus), dp); g_signal_connect(G_OBJECT(ww), "clicked", G_CALLBACK(filefont_clicked), dp); break; } case CTRL_LISTBOX: #if GTK_CHECK_VERSION(2,0,0) /* * First construct the list data store, with the right * number of columns. */ # if !GTK_CHECK_VERSION(2,4,0) /* (For GTK 2.0 to 2.3, we do this for full listboxes only, * because combo boxes are still done the old GTK1 way.) */ if (ctrl->listbox.height > 0) # endif { GType *types; int i; int cols; cols = ctrl->listbox.ncols; cols = cols ? cols : 1; types = snewn(1 + cols, GType); types[0] = G_TYPE_INT; for (i = 0; i < cols; i++) types[i+1] = G_TYPE_STRING; uc->listmodel = gtk_list_store_newv(1 + cols, types); sfree(types); } #endif /* * See if it's a drop-down list (non-editable combo * box). */ if (ctrl->listbox.height == 0) { #if !GTK_CHECK_VERSION(2,4,0) /* * GTK1 and early-GTK2 option-menu style of * drop-down list. */ uc->optmenu = w = gtk_option_menu_new(); uc->menu = gtk_menu_new(); gtk_option_menu_set_menu(GTK_OPTION_MENU(w), uc->menu); g_object_set_data(G_OBJECT(uc->menu), "user-data", (gpointer)uc->optmenu); g_signal_connect(G_OBJECT(uc->optmenu), "focus_in_event", G_CALLBACK(widget_focus), dp); #else /* * Late-GTK2 style using a GtkComboBox. */ GtkCellRenderer *cr; /* * Create a non-editable GtkComboBox (that is, not * its subclass GtkComboBoxEntry). */ w = gtk_combo_box_new_with_model (GTK_TREE_MODEL(uc->listmodel)); uc->combo = w; /* * Tell it how to render a list item (i.e. which * column to look at in the list model). */ cr = gtk_cell_renderer_text_new(); gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(w), cr, true); gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(w), cr, "text", 1, NULL); /* * And tell it to notify us when the selection * changes. */ g_signal_connect(G_OBJECT(w), "changed", G_CALLBACK(droplist_selchange), dp); g_signal_connect(G_OBJECT(w), "focus_in_event", G_CALLBACK(widget_focus), dp); #endif } else { #if !GTK_CHECK_VERSION(2,0,0) /* * GTK1-style full list box. */ uc->list = gtk_list_new(); if (ctrl->listbox.multisel == 2) { gtk_list_set_selection_mode(GTK_LIST(uc->list), GTK_SELECTION_EXTENDED); } else if (ctrl->listbox.multisel == 1) { gtk_list_set_selection_mode(GTK_LIST(uc->list), GTK_SELECTION_MULTIPLE); } else { gtk_list_set_selection_mode(GTK_LIST(uc->list), GTK_SELECTION_SINGLE); } w = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(w), uc->list); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(w), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); uc->adj = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW(w)); gtk_widget_show(uc->list); g_signal_connect(G_OBJECT(uc->list), "selection-changed", G_CALLBACK(list_selchange), dp); g_signal_connect(G_OBJECT(uc->list), "focus_in_event", G_CALLBACK(widget_focus), dp); /* * Adjust the height of the scrolled window to the * minimum given by the height parameter. * * This piece of guesswork is a horrid hack based * on looking inside the GTK 1.2 sources * (specifically gtkviewport.c, which appears to be * the widget which provides the border around the * scrolling area). Anyone lets me know how I can * do this in a way which isn't at risk from GTK * upgrades, I'd be grateful. */ { int edge; edge = GTK_WIDGET(uc->list)->style->klass->ythickness; gtk_widget_set_size_request(w, 10, 2*edge + (ctrl->listbox.height * get_listitemheight(w))); } if (ctrl->listbox.draglist) { /* * GTK doesn't appear to make it easy to * implement a proper draggable list; so * instead I'm just going to have to put an Up * and a Down button to the right of the actual * list box. Ah well. */ GtkWidget *cols, *button; static const gint percentages[2] = { 80, 20 }; cols = columns_new(4); columns_set_cols(COLUMNS(cols), 2, percentages); columns_add(COLUMNS(cols), w, 0, 1); gtk_widget_show(w); button = gtk_button_new_with_label("Up"); columns_add(COLUMNS(cols), button, 1, 1); gtk_widget_show(button); g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(draglist_up), dp); g_signal_connect(G_OBJECT(button), "focus_in_event", G_CALLBACK(widget_focus), dp); button = gtk_button_new_with_label("Down"); columns_add(COLUMNS(cols), button, 1, 1); gtk_widget_show(button); g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(draglist_down), dp); g_signal_connect(G_OBJECT(button), "focus_in_event", G_CALLBACK(widget_focus), dp); w = cols; } #else /* * GTK2 treeview-based full list box. */ GtkTreeSelection *sel; /* * Create the list box itself, its columns, and * its containing scrolled window. */ w = gtk_tree_view_new_with_model (GTK_TREE_MODEL(uc->listmodel)); g_object_set_data(G_OBJECT(uc->listmodel), "user-data", (gpointer)w); gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(w), false); sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(w)); gtk_tree_selection_set_mode (sel, ctrl->listbox.multisel ? GTK_SELECTION_MULTIPLE : GTK_SELECTION_SINGLE); uc->treeview = w; g_signal_connect(G_OBJECT(w), "row-activated", G_CALLBACK(listbox_doubleclick), dp); g_signal_connect(G_OBJECT(w), "focus_in_event", G_CALLBACK(widget_focus), dp); g_signal_connect(G_OBJECT(sel), "changed", G_CALLBACK(listbox_selchange), dp); if (ctrl->listbox.draglist) { gtk_tree_view_set_reorderable(GTK_TREE_VIEW(w), true); g_signal_connect(G_OBJECT(uc->listmodel), "row-inserted", G_CALLBACK(listbox_reorder), dp); } { int i; int cols; cols = ctrl->listbox.ncols; cols = cols ? cols : 1; for (i = 0; i < cols; i++) { GtkTreeViewColumn *column; GtkCellRenderer *cellrend; /* * It appears that GTK 2 doesn't leave us any * particularly sensible way to honour the * "percentages" specification in the ctrl * structure. */ cellrend = gtk_cell_renderer_text_new(); if (!ctrl->listbox.hscroll) { g_object_set(G_OBJECT(cellrend), "ellipsize", PANGO_ELLIPSIZE_END, "ellipsize-set", true, (const char *)NULL); } column = gtk_tree_view_column_new_with_attributes ("heading", cellrend, "text", i+1, (char *)NULL); gtk_tree_view_column_set_sizing (column, GTK_TREE_VIEW_COLUMN_GROW_ONLY); gtk_tree_view_append_column(GTK_TREE_VIEW(w), column); } } { GtkWidget *scroll; scroll = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW(scroll), GTK_SHADOW_IN); gtk_widget_show(w); gtk_container_add(GTK_CONTAINER(scroll), w); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); gtk_widget_set_size_request (scroll, -1, ctrl->listbox.height * get_listitemheight(w)); w = scroll; } #endif } if (ctrl->generic.label) { GtkWidget *label, *container; label = gtk_label_new(ctrl->generic.label); #if GTK_CHECK_VERSION(3,0,0) gtk_label_set_width_chars(GTK_LABEL(label), 3); #endif shortcut_add(scs, label, ctrl->listbox.shortcut, SHORTCUT_UCTRL, uc); container = columns_new(4); if (ctrl->listbox.percentwidth == 100) { columns_add(COLUMNS(container), label, 0, 1); columns_force_left_align(COLUMNS(container), label); columns_add(COLUMNS(container), w, 0, 1); } else { gint percentages[2]; percentages[1] = ctrl->listbox.percentwidth; percentages[0] = 100 - ctrl->listbox.percentwidth; columns_set_cols(COLUMNS(container), 2, percentages); columns_add(COLUMNS(container), label, 0, 1); columns_force_left_align(COLUMNS(container), label); columns_add(COLUMNS(container), w, 1, 1); columns_force_same_height(COLUMNS(container), label, w); } gtk_widget_show(label); gtk_widget_show(w); w = container; uc->label = label; } break; case CTRL_TEXT: #if !GTK_CHECK_VERSION(3,0,0) /* * Wrapping text widgets don't sit well with the GTK2 * layout model, in which widgets state a minimum size * and the whole window then adjusts to the smallest * size it can sensibly take given its contents. A * wrapping text widget _has_ no clear minimum size; * instead it has a range of possibilities. It can be * one line deep but 2000 wide, or two lines deep and * 1000 pixels, or three by 867, or four by 500 and so * on. It can be as short as you like provided you * don't mind it being wide, or as narrow as you like * provided you don't mind it being tall. * * Therefore, it fits very badly into the layout model. * Hence the only thing to do is pick a width and let * it choose its own number of lines. To do this I'm * going to cheat a little. All new wrapping text * widgets will be created with a minimal text content * "X"; then, after the rest of the dialog box is set * up and its size calculated, the text widgets will be * told their width and given their real text, which * will cause the size to be recomputed in the y * direction (because many of them will expand to more * than one line). */ uc->text = w = gtk_label_new("X"); uc->textsig = g_signal_connect(G_OBJECT(w), "size-allocate", G_CALLBACK(label_sizealloc), dp); #else /* * In GTK3, this is all fixed, because the main aim of the * new 'height-for-width' geometry management is to make * wrapping labels behave sensibly. So now we can just do * the obvious thing. */ uc->text = w = gtk_label_new(uc->ctrl->generic.label); #endif align_label_left(GTK_LABEL(w)); gtk_label_set_line_wrap(GTK_LABEL(w), true); break; } assert(w != NULL); columns_add(cols, w, COLUMN_START(ctrl->generic.column), COLUMN_SPAN(ctrl->generic.column)); if (left) columns_force_left_align(cols, w); if (ctrl->generic.align_next_to) { /* * Implement align_next_to by simply forcing the two * controls to have the same height of size allocation. At * least for the controls we're currently doing this with, * the GTK layout system will automatically vertically * centre each control within its allocation, which will * get the two controls aligned alongside each other * reasonably well. */ struct uctrl *uc2 = dlg_find_byctrl( dp, ctrl->generic.align_next_to); assert(uc2); columns_force_same_height(cols, w, uc2->toplevel); #if GTK_CHECK_VERSION(3, 10, 0) /* Slightly nicer to align baselines than just vertically * centring, where the option is available */ gtk_widget_set_valign(w, GTK_ALIGN_BASELINE); gtk_widget_set_valign(uc2->toplevel, GTK_ALIGN_BASELINE); #endif } gtk_widget_show(w); uc->toplevel = w; dlg_add_uctrl(dp, uc); } return ret; } struct selparam { struct dlgparam *dp; GtkNotebook *panels; GtkWidget *panel; #if !GTK_CHECK_VERSION(2,0,0) GtkWidget *treeitem; #else int depth; GtkTreePath *treepath; #endif struct Shortcuts shortcuts; }; #if GTK_CHECK_VERSION(2,0,0) static void treeselection_changed(GtkTreeSelection *treeselection, gpointer data) { struct selparam **sps = (struct selparam **)data, *sp; GtkTreeModel *treemodel; GtkTreeIter treeiter; gint spindex; gint page_num; if (!gtk_tree_selection_get_selected(treeselection, &treemodel, &treeiter)) return; gtk_tree_model_get(treemodel, &treeiter, TREESTORE_PARAMS, &spindex, -1); sp = sps[spindex]; page_num = gtk_notebook_page_num(sp->panels, sp->panel); gtk_notebook_set_current_page(sp->panels, page_num); sp->dp->curr_panel = sp; dlg_refresh(NULL, sp->dp); sp->dp->shortcuts = &sp->shortcuts; } #else static void treeitem_sel(GtkItem *item, gpointer data) { struct selparam *sp = (struct selparam *)data; gint page_num; page_num = gtk_notebook_page_num(sp->panels, sp->panel); gtk_notebook_set_page(sp->panels, page_num); sp->dp->curr_panel = sp; dlg_refresh(NULL, sp->dp); sp->dp->shortcuts = &sp->shortcuts; sp->dp->currtreeitem = sp->treeitem; } #endif bool dlg_is_visible(union control *ctrl, dlgparam *dp) { struct uctrl *uc = dlg_find_byctrl(dp, ctrl); /* * A control is visible if it belongs to _no_ notebook page (i.e. * it's one of the config-box-global buttons like Load or About), * or if it belongs to the currently selected page. */ return uc->sp == NULL || uc->sp == dp->curr_panel; } #if !GTK_CHECK_VERSION(2,0,0) static bool tree_grab_focus(struct dlgparam *dp) { int i, f; /* * See if any of the treeitems has the focus. */ f = -1; for (i = 0; i < dp->ntreeitems; i++) if (GTK_WIDGET_HAS_FOCUS(dp->treeitems[i])) { f = i; break; } if (f >= 0) return false; else { gtk_widget_grab_focus(dp->currtreeitem); return true; } } gint tree_focus(GtkContainer *container, GtkDirectionType direction, gpointer data) { struct dlgparam *dp = (struct dlgparam *)data; g_signal_stop_emission_by_name(G_OBJECT(container), "focus"); /* * If there's a focused treeitem, we return false to cause the * focus to move on to some totally other control. If not, we * focus the selected one. */ return tree_grab_focus(dp); } #endif gint win_key_press(GtkWidget *widget, GdkEventKey *event, gpointer data) { struct dlgparam *dp = (struct dlgparam *)data; if (event->keyval == GDK_KEY_Escape && dp->cancelbutton) { g_signal_emit_by_name(G_OBJECT(dp->cancelbutton), "clicked"); return true; } if ((event->state & GDK_MOD1_MASK) && (unsigned char)event->string[0] > 0 && (unsigned char)event->string[0] <= 127) { int schr = (unsigned char)event->string[0]; struct Shortcut *sc = &dp->shortcuts->sc[schr]; switch (sc->action) { case SHORTCUT_TREE: #if GTK_CHECK_VERSION(2,0,0) gtk_widget_grab_focus(sc->widget); #else tree_grab_focus(dp); #endif break; case SHORTCUT_FOCUS: gtk_widget_grab_focus(sc->widget); break; case SHORTCUT_UCTRL: /* * We must do something sensible with a uctrl. * Precisely what this is depends on the type of * control. */ switch (sc->uc->ctrl->generic.type) { case CTRL_CHECKBOX: case CTRL_BUTTON: /* Check boxes and buttons get the focus _and_ get toggled. */ gtk_widget_grab_focus(sc->uc->toplevel); g_signal_emit_by_name(G_OBJECT(sc->uc->toplevel), "clicked"); break; case CTRL_FILESELECT: case CTRL_FONTSELECT: /* File/font selectors have their buttons pressed (ooer), * and focus transferred to the edit box. */ g_signal_emit_by_name(G_OBJECT(sc->uc->button), "clicked"); gtk_widget_grab_focus(sc->uc->entry); break; case CTRL_RADIO: /* * Radio buttons are fun, because they have * multiple shortcuts. We must find whether the * activated shortcut is the shortcut for the whole * group, or for a particular button. In the former * case, we find the currently selected button and * focus it; in the latter, we focus-and-click the * button whose shortcut was pressed. */ if (schr == sc->uc->ctrl->radio.shortcut) { int i; for (i = 0; i < sc->uc->ctrl->radio.nbuttons; i++) if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(sc->uc->buttons[i]))) { gtk_widget_grab_focus(sc->uc->buttons[i]); } } else if (sc->uc->ctrl->radio.shortcuts) { int i; for (i = 0; i < sc->uc->ctrl->radio.nbuttons; i++) if (schr == sc->uc->ctrl->radio.shortcuts[i]) { gtk_widget_grab_focus(sc->uc->buttons[i]); g_signal_emit_by_name (G_OBJECT(sc->uc->buttons[i]), "clicked"); } } break; case CTRL_LISTBOX: #if !GTK_CHECK_VERSION(2,4,0) if (sc->uc->optmenu) { GdkEventButton bev; gint returnval; gtk_widget_grab_focus(sc->uc->optmenu); /* Option menus don't work using the "clicked" signal. * We need to manufacture a button press event :-/ */ bev.type = GDK_BUTTON_PRESS; bev.button = 1; g_signal_emit_by_name(G_OBJECT(sc->uc->optmenu), "button_press_event", &bev, &returnval); break; } #else if (sc->uc->combo) { gtk_widget_grab_focus(sc->uc->combo); gtk_combo_box_popup(GTK_COMBO_BOX(sc->uc->combo)); break; } #endif #if !GTK_CHECK_VERSION(2,0,0) if (sc->uc->list) { /* * For GTK-1 style list boxes, we tell it to * focus one of its children, which appears to * do the Right Thing. */ gtk_container_focus(GTK_CONTAINER(sc->uc->list), GTK_DIR_TAB_FORWARD); break; } #else if (sc->uc->treeview) { gtk_widget_grab_focus(sc->uc->treeview); break; } #endif unreachable("bad listbox type in win_key_press"); } break; } } return false; } #if !GTK_CHECK_VERSION(2,0,0) gint tree_key_press(GtkWidget *widget, GdkEventKey *event, gpointer data) { struct dlgparam *dp = (struct dlgparam *)data; if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up || event->keyval == GDK_Down || event->keyval == GDK_KP_Down) { int dir, i, j = -1; for (i = 0; i < dp->ntreeitems; i++) if (widget == dp->treeitems[i]) break; if (i < dp->ntreeitems) { if (event->keyval == GDK_Up || event->keyval == GDK_KP_Up) dir = -1; else dir = +1; while (1) { i += dir; if (i < 0 || i >= dp->ntreeitems) break; /* nothing in that dir to select */ /* * Determine if this tree item is visible. */ { GtkWidget *w = dp->treeitems[i]; bool vis = true; while (w && (GTK_IS_TREE_ITEM(w) || GTK_IS_TREE(w))) { if (!GTK_WIDGET_VISIBLE(w)) { vis = false; break; } w = w->parent; } if (vis) { j = i; /* got one */ break; } } } } g_signal_stop_emission_by_name(G_OBJECT(widget), "key_press_event"); if (j >= 0) { g_signal_emit_by_name(G_OBJECT(dp->treeitems[j]), "toggle"); gtk_widget_grab_focus(dp->treeitems[j]); } return true; } /* * It's nice for Left and Right to expand and collapse tree * branches. */ if (event->keyval == GDK_Left || event->keyval == GDK_KP_Left) { g_signal_stop_emission_by_name(G_OBJECT(widget), "key_press_event"); gtk_tree_item_collapse(GTK_TREE_ITEM(widget)); return true; } if (event->keyval == GDK_Right || event->keyval == GDK_KP_Right) { g_signal_stop_emission_by_name(G_OBJECT(widget), "key_press_event"); gtk_tree_item_expand(GTK_TREE_ITEM(widget)); return true; } return false; } #endif static void shortcut_highlight(GtkWidget *labelw, int chr) { GtkLabel *label = GTK_LABEL(labelw); const gchar *currstr; gchar *pattern; int i; #if !GTK_CHECK_VERSION(2,0,0) { gchar *currstr_nonconst; gtk_label_get(label, &currstr_nonconst); currstr = currstr_nonconst; } #else currstr = gtk_label_get_text(label); #endif for (i = 0; currstr[i]; i++) if (tolower((unsigned char)currstr[i]) == chr) { pattern = dupprintf("%*s_", i, ""); gtk_label_set_pattern(label, pattern); sfree(pattern); break; } } void shortcut_add(struct Shortcuts *scs, GtkWidget *labelw, int chr, int action, void *ptr) { if (chr == NO_SHORTCUT) return; chr = tolower((unsigned char)chr); assert(scs->sc[chr].action == SHORTCUT_EMPTY); scs->sc[chr].action = action; if (action == SHORTCUT_FOCUS || action == SHORTCUT_TREE) { scs->sc[chr].uc = NULL; scs->sc[chr].widget = (GtkWidget *)ptr; } else { scs->sc[chr].widget = NULL; scs->sc[chr].uc = (struct uctrl *)ptr; } shortcut_highlight(labelw, chr); } static int get_listitemheight(GtkWidget *w) { #if !GTK_CHECK_VERSION(2,0,0) GtkWidget *listitem = gtk_list_item_new_with_label("foo"); GtkRequisition req; gtk_widget_size_request(listitem, &req); g_object_ref_sink(G_OBJECT(listitem)); return req.height; #else int height; GtkCellRenderer *cr = gtk_cell_renderer_text_new(); #if GTK_CHECK_VERSION(3,0,0) { GtkRequisition req; /* * Since none of my list items wraps in this GUI, no * interesting width-for-height behaviour should be happening, * so I don't think it should matter here whether I ask for * the minimum or natural height. */ gtk_cell_renderer_get_preferred_size(cr, w, &req, NULL); height = req.height; } #else gtk_cell_renderer_get_size(cr, w, NULL, NULL, NULL, NULL, &height); #endif g_object_ref(G_OBJECT(cr)); g_object_ref_sink(G_OBJECT(cr)); g_object_unref(G_OBJECT(cr)); return height; #endif } #if GTK_CHECK_VERSION(2,0,0) void initial_treeview_collapse(struct dlgparam *dp, GtkWidget *tree) { /* * Collapse the deeper branches of the treeview into the state we * like them to start off in. See comment below in do_config_box. */ int i; for (i = 0; i < dp->nselparams; i++) if (dp->selparams[i]->depth >= 2) gtk_tree_view_collapse_row(GTK_TREE_VIEW(tree), dp->selparams[i]->treepath); } #endif #if GTK_CHECK_VERSION(3,0,0) void treeview_map_event(GtkWidget *tree, gpointer data) { struct dlgparam *dp = (struct dlgparam *)data; GtkAllocation alloc; gtk_widget_get_allocation(tree, &alloc); gtk_widget_set_size_request(tree, alloc.width, -1); initial_treeview_collapse(dp, tree); } #endif GtkWidget *create_config_box(const char *title, Conf *conf, bool midsession, int protcfginfo, post_dialog_fn_t after, void *afterctx) { GtkWidget *window, *hbox, *vbox, *cols, *label, *tree, *treescroll, *panels, *panelvbox; int index, level, protocol; char *path; #if GTK_CHECK_VERSION(2,0,0) GtkTreeStore *treestore; GtkCellRenderer *treerenderer; GtkTreeViewColumn *treecolumn; GtkTreeSelection *treeselection; GtkTreeIter treeiterlevels[8]; #else GtkTreeItem *treeitemlevels[8]; GtkTree *treelevels[8]; #endif struct dlgparam *dp; struct Shortcuts scs; struct selparam **selparams = NULL; size_t nselparams = 0, selparamsize = 0; dp = snew(struct dlgparam); dp->after = after; dp->afterctx = afterctx; dlg_init(dp); for (index = 0; index < lenof(scs.sc); index++) { scs.sc[index].action = SHORTCUT_EMPTY; } window = our_dialog_new(); dp->ctrlbox = ctrl_new_box(); protocol = conf_get_int(conf, CONF_protocol); setup_config_box(dp->ctrlbox, midsession, protocol, protcfginfo); unix_setup_config_box(dp->ctrlbox, midsession, protocol); gtk_setup_config_box(dp->ctrlbox, midsession, window); gtk_window_set_title(GTK_WINDOW(window), title); hbox = gtk_hbox_new(false, 4); our_dialog_add_to_content_area(GTK_WINDOW(window), hbox, true, true, 0); gtk_container_set_border_width(GTK_CONTAINER(hbox), 10); gtk_widget_show(hbox); vbox = gtk_vbox_new(false, 4); gtk_box_pack_start(GTK_BOX(hbox), vbox, false, false, 0); gtk_widget_show(vbox); cols = columns_new(4); gtk_box_pack_start(GTK_BOX(vbox), cols, false, false, 0); gtk_widget_show(cols); label = gtk_label_new("Category:"); columns_add(COLUMNS(cols), label, 0, 1); columns_force_left_align(COLUMNS(cols), label); gtk_widget_show(label); treescroll = gtk_scrolled_window_new(NULL, NULL); #if GTK_CHECK_VERSION(2,0,0) treestore = gtk_tree_store_new (TREESTORE_NUM, G_TYPE_STRING, G_TYPE_INT); tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(treestore)); gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree), false); treerenderer = gtk_cell_renderer_text_new(); treecolumn = gtk_tree_view_column_new_with_attributes ("Label", treerenderer, "text", 0, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW(tree), treecolumn); treeselection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree)); gtk_tree_selection_set_mode(treeselection, GTK_SELECTION_BROWSE); gtk_container_add(GTK_CONTAINER(treescroll), tree); #else tree = gtk_tree_new(); gtk_tree_set_view_mode(GTK_TREE(tree), GTK_TREE_VIEW_ITEM); gtk_tree_set_selection_mode(GTK_TREE(tree), GTK_SELECTION_BROWSE); g_signal_connect(G_OBJECT(tree), "focus", G_CALLBACK(tree_focus), dp); #endif g_signal_connect(G_OBJECT(tree), "focus_in_event", G_CALLBACK(widget_focus), dp); shortcut_add(&scs, label, 'g', SHORTCUT_TREE, tree); gtk_widget_show(treescroll); gtk_box_pack_start(GTK_BOX(vbox), treescroll, true, true, 0); panels = gtk_notebook_new(); gtk_notebook_set_show_tabs(GTK_NOTEBOOK(panels), false); gtk_notebook_set_show_border(GTK_NOTEBOOK(panels), false); gtk_box_pack_start(GTK_BOX(hbox), panels, true, true, 0); gtk_widget_show(panels); panelvbox = NULL; path = NULL; level = 0; for (index = 0; index < dp->ctrlbox->nctrlsets; index++) { struct controlset *s = dp->ctrlbox->ctrlsets[index]; GtkWidget *w; if (!*s->pathname) { w = layout_ctrls(dp, NULL, &scs, s, GTK_WINDOW(window)); our_dialog_set_action_area(GTK_WINDOW(window), w); } else { int j = path ? ctrl_path_compare(s->pathname, path) : 0; if (j != INT_MAX) { /* add to treeview, start new panel */ char *c; #if GTK_CHECK_VERSION(2,0,0) GtkTreeIter treeiter; #else GtkWidget *treeitem; #endif bool first; /* * We expect never to find an implicit path * component. For example, we expect never to see * A/B/C followed by A/D/E, because that would * _implicitly_ create A/D. All our path prefixes * are expected to contain actual controls and be * selectable in the treeview; so we would expect * to see A/D _explicitly_ before encountering * A/D/E. */ assert(j == ctrl_path_elements(s->pathname) - 1); c = strrchr(s->pathname, '/'); if (!c) c = s->pathname; else c++; path = s->pathname; first = (panelvbox == NULL); panelvbox = gtk_vbox_new(false, 4); gtk_widget_show(panelvbox); gtk_notebook_append_page(GTK_NOTEBOOK(panels), panelvbox, NULL); struct selparam *sp = snew(struct selparam); if (first) { gint page_num; page_num = gtk_notebook_page_num(GTK_NOTEBOOK(panels), panelvbox); gtk_notebook_set_current_page(GTK_NOTEBOOK(panels), page_num); dp->curr_panel = sp; } sgrowarray(selparams, selparamsize, nselparams); selparams[nselparams] = sp; sp->dp = dp; sp->panels = GTK_NOTEBOOK(panels); sp->panel = panelvbox; sp->shortcuts = scs; /* structure copy */ assert(j-1 < level); #if GTK_CHECK_VERSION(2,0,0) if (j > 0) /* treeiterlevels[j-1] will always be valid because we * don't allow implicit path components; see above. */ gtk_tree_store_append(treestore, &treeiter, &treeiterlevels[j-1]); else gtk_tree_store_append(treestore, &treeiter, NULL); gtk_tree_store_set(treestore, &treeiter, TREESTORE_PATH, c, TREESTORE_PARAMS, nselparams, -1); treeiterlevels[j] = treeiter; sp->depth = j; if (j > 0) { sp->treepath = gtk_tree_model_get_path( GTK_TREE_MODEL(treestore), &treeiterlevels[j-1]); /* * We are going to collapse all tree branches * at depth greater than 2, but not _yet_; see * the comment at the call to * gtk_tree_view_collapse_row below. */ gtk_tree_view_expand_row(GTK_TREE_VIEW(tree), sp->treepath, false); } else { sp->treepath = NULL; } #else treeitem = gtk_tree_item_new_with_label(c); if (j > 0) { if (!treelevels[j-1]) { treelevels[j-1] = GTK_TREE(gtk_tree_new()); gtk_tree_item_set_subtree (treeitemlevels[j-1], GTK_WIDGET(treelevels[j-1])); if (j < 2) gtk_tree_item_expand(treeitemlevels[j-1]); else gtk_tree_item_collapse(treeitemlevels[j-1]); } gtk_tree_append(treelevels[j-1], treeitem); } else { gtk_tree_append(GTK_TREE(tree), treeitem); } treeitemlevels[j] = GTK_TREE_ITEM(treeitem); treelevels[j] = NULL; g_signal_connect(G_OBJECT(treeitem), "key_press_event", G_CALLBACK(tree_key_press), dp); g_signal_connect(G_OBJECT(treeitem), "focus_in_event", G_CALLBACK(widget_focus), dp); gtk_widget_show(treeitem); if (first) gtk_tree_select_child(GTK_TREE(tree), treeitem); sp->treeitem = treeitem; #endif level = j+1; nselparams++; } w = layout_ctrls(dp, selparams[nselparams-1], &selparams[nselparams-1]->shortcuts, s, NULL); gtk_box_pack_start(GTK_BOX(panelvbox), w, false, false, 0); gtk_widget_show(w); } } #if GTK_CHECK_VERSION(2,0,0) /* * We want our tree view to come up with all branches at depth 2 * or more collapsed. However, if we start off with those branches * collapsed, then the tree view's size request will be calculated * based on the width of the collapsed tree, and then when the * collapsed branches are expanded later, the tree view will * jarringly change size. * * So instead we start with everything expanded; then, once the * tree view has computed its resulting width requirement, we * collapse the relevant rows, but force the width to be the value * we just retrieved. This arranges that the tree view is wide * enough to have all branches expanded without further resizing. */ dp->nselparams = nselparams; dp->selparams = selparams; #if !GTK_CHECK_VERSION(3,0,0) { /* * In GTK2, we can just do the job right now. */ GtkRequisition req; gtk_widget_size_request(tree, &req); initial_treeview_collapse(dp, tree); gtk_widget_set_size_request(tree, req.width, -1); } #else /* * But in GTK3, we have to wait until the widget is about to be * mapped, because the size computation won't have been done yet. */ g_signal_connect(G_OBJECT(tree), "map", G_CALLBACK(treeview_map_event), dp); #endif /* GTK 2 vs 3 */ #endif /* GTK 2+ vs 1 */ #if GTK_CHECK_VERSION(2,0,0) g_signal_connect(G_OBJECT(treeselection), "changed", G_CALLBACK(treeselection_changed), selparams); #else dp->ntreeitems = nselparams; dp->treeitems = snewn(dp->ntreeitems, GtkWidget *); for (index = 0; index < nselparams; index++) { g_signal_connect(G_OBJECT(selparams[index]->treeitem), "select", G_CALLBACK(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; dp->retval = -1; dp->window = window; set_window_icon(window, cfg_icon, n_cfg_icon); #if !GTK_CHECK_VERSION(2,0,0) gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(treescroll), tree); #endif gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(treescroll), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); gtk_widget_show(tree); gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); gtk_widget_show(window); /* * Set focus into the first available control. */ for (index = 0; index < dp->ctrlbox->nctrlsets; index++) { struct controlset *s = dp->ctrlbox->ctrlsets[index]; bool done = false; int j; if (*s->pathname) { for (j = 0; j < s->ncontrols; j++) if (s->ctrls[j]->generic.type != CTRL_TABDELAY && s->ctrls[j]->generic.type != CTRL_COLUMNS && s->ctrls[j]->generic.type != CTRL_TEXT) { dlg_set_focus(s->ctrls[j], dp); dp->lastfocus = s->ctrls[j]; done = true; break; } } if (done) break; } g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(dlgparam_destroy), dp); g_signal_connect(G_OBJECT(window), "key_press_event", G_CALLBACK(win_key_press), dp); return window; } static void dlgparam_destroy(GtkWidget *widget, gpointer data) { struct dlgparam *dp = (struct dlgparam *)data; dp->after(dp->afterctx, dp->retval); dlg_cleanup(dp); ctrl_free_box(dp->ctrlbox); #if GTK_CHECK_VERSION(2,0,0) if (dp->selparams) { for (size_t i = 0; i < dp->nselparams; i++) { if (dp->selparams[i]->treepath) gtk_tree_path_free(dp->selparams[i]->treepath); sfree(dp->selparams[i]); } sfree(dp->selparams); } #endif sfree(dp); } static void messagebox_handler(union control *ctrl, dlgparam *dp, void *data, int event) { if (event == EVENT_ACTION) dlg_end(dp, ctrl->generic.context.i); } static const struct message_box_button button_array_yn[] = { {"Yes", 'y', +1, 1}, {"No", 'n', -1, 0}, }; const struct message_box_buttons buttons_yn = { button_array_yn, lenof(button_array_yn), }; static const struct message_box_button button_array_ok[] = { {"OK", 'o', 1, 1}, }; const struct message_box_buttons buttons_ok = { button_array_ok, lenof(button_array_ok), }; static GtkWidget *create_message_box_general( GtkWidget *parentwin, const char *title, const char *msg, int minwid, bool selectable, const struct message_box_buttons *buttons, post_dialog_fn_t after, void *afterctx, GtkWidget *(*action_postproc)(GtkWidget *, void *), void *postproc_ctx) { GtkWidget *window, *w0, *w1; struct controlset *s0, *s1; union control *c, *textctrl; struct dlgparam *dp; struct Shortcuts scs; int i, index, ncols, min_type; dp = snew(struct dlgparam); dp->after = after; dp->afterctx = afterctx; dlg_init(dp); for (index = 0; index < lenof(scs.sc); index++) { scs.sc[index].action = SHORTCUT_EMPTY; } dp->ctrlbox = ctrl_new_box(); /* * Count up the number of buttons and find out what kinds there * are. */ ncols = 0; min_type = +1; for (i = 0; i < buttons->nbuttons; i++) { const struct message_box_button *button = &buttons->buttons[i]; ncols++; if (min_type > button->type) min_type = button->type; assert(button->value >= 0); /* <0 means no return value available */ } s0 = ctrl_getset(dp->ctrlbox, "", "", ""); c = ctrl_columns(s0, 2, 50, 50); c->columns.ncols = s0->ncolumns = ncols; c->columns.percentages = sresize(c->columns.percentages, ncols, int); for (index = 0; index < ncols; index++) c->columns.percentages[index] = (index+1)*100/ncols - index*100/ncols; index = 0; for (i = 0; i < buttons->nbuttons; i++) { const struct message_box_button *button = &buttons->buttons[i]; c = ctrl_pushbutton(s0, button->title, button->shortcut, HELPCTX(no_help), messagebox_handler, I(button->value)); c->generic.column = index++; if (button->type > 0) c->button.isdefault = true; /* We always arrange that _some_ button is labelled as * 'iscancel', so that pressing Escape will always cause * win_key_press to do something. The button we choose is * whichever has the smallest type value: this means that real * cancel buttons (labelled -1) will be picked if one is * there, or in cases where the options are yes/no (1,0) then * no will be picked, and if there's only one option (a box * that really is just showing a _message_ and not even asking * a question) then that will be picked. */ if (button->type == min_type) c->button.iscancel = true; } s1 = ctrl_getset(dp->ctrlbox, "x", "", ""); textctrl = ctrl_text(s1, msg, HELPCTX(no_help)); window = our_dialog_new(); gtk_window_set_title(GTK_WINDOW(window), title); w0 = layout_ctrls(dp, NULL, &scs, s0, GTK_WINDOW(window)); if (action_postproc) w0 = action_postproc(w0, postproc_ctx); our_dialog_set_action_area(GTK_WINDOW(window), w0); gtk_widget_show(w0); w1 = layout_ctrls(dp, NULL, &scs, s1, GTK_WINDOW(window)); gtk_container_set_border_width(GTK_CONTAINER(w1), 10); gtk_widget_set_size_request(w1, minwid+20, -1); our_dialog_add_to_content_area(GTK_WINDOW(window), w1, true, true, 0); gtk_widget_show(w1); dp->shortcuts = &scs; dp->lastfocus = NULL; dp->retval = 0; dp->window = window; if (selectable) { #if GTK_CHECK_VERSION(2,0,0) struct uctrl *uc = dlg_find_byctrl(dp, textctrl); gtk_label_set_selectable(GTK_LABEL(uc->text), true); /* * GTK selectable labels have a habit of selecting their * entire contents when they gain focus. It's ugly to have * text in a message box start up all selected, so we suppress * this by manually selecting none of it - but we must do this * when the widget _already has_ focus, otherwise our work * will be undone when it gains it shortly. */ gtk_widget_grab_focus(uc->text); gtk_label_select_region(GTK_LABEL(uc->text), 0, 0); #else (void)textctrl; /* placate warning */ #endif } if (parentwin) { set_transient_window_pos(parentwin, window); gtk_window_set_transient_for(GTK_WINDOW(window), GTK_WINDOW(parentwin)); } else gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); gtk_container_set_focus_child(GTK_CONTAINER(window), NULL); gtk_widget_show(window); gtk_window_set_focus(GTK_WINDOW(window), NULL); #if !GTK_CHECK_VERSION(2,0,0) dp->currtreeitem = NULL; dp->treeitems = NULL; #else dp->selparams = NULL; #endif g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(dlgparam_destroy), dp); g_signal_connect(G_OBJECT(window), "key_press_event", G_CALLBACK(win_key_press), dp); return window; } GtkWidget *create_message_box( GtkWidget *parentwin, const char *title, const char *msg, int minwid, bool selectable, const struct message_box_buttons *buttons, post_dialog_fn_t after, void *afterctx) { return create_message_box_general( parentwin, title, msg, minwid, selectable, buttons, after, afterctx, NULL /* action_postproc */, NULL /* postproc_ctx */); } struct verify_ssh_host_key_dialog_ctx { char *host; int port; char *keytype; char *keystr; char *more_info; void (*callback)(void *callback_ctx, int result); void *callback_ctx; Seat *seat; GtkWidget *main_dialog; GtkWidget *more_info_dialog; }; static void verify_ssh_host_key_result_callback(void *vctx, int result) { struct verify_ssh_host_key_dialog_ctx *ctx = (struct verify_ssh_host_key_dialog_ctx *)vctx; if (result >= 0) { int logical_result; /* * Convert the dialog-box return value (one of three * possibilities) into the return value we pass back to the SSH * code (one of only two possibilities, because the SSH code * doesn't care whether we saved the host key or not). */ if (result == 2) { store_host_key(ctx->host, ctx->port, ctx->keytype, ctx->keystr); logical_result = 1; /* continue with connection */ } else if (result == 1) { logical_result = 1; /* continue with connection */ } else { logical_result = 0; /* do not continue with connection */ } ctx->callback(ctx->callback_ctx, logical_result); } /* * Clean up this context structure, whether or not a result was * ever actually delivered from the dialog box. */ unregister_dialog(ctx->seat, DIALOG_SLOT_NETWORK_PROMPT); if (ctx->more_info_dialog) gtk_widget_destroy(ctx->more_info_dialog); sfree(ctx->host); sfree(ctx->keytype); sfree(ctx->keystr); sfree(ctx->more_info); sfree(ctx); } static GtkWidget *add_more_info_button(GtkWidget *w, void *vctx) { GtkWidget *box = gtk_hbox_new(false, 10); gtk_widget_show(box); gtk_box_pack_end(GTK_BOX(box), w, false, true, 0); GtkWidget *button = gtk_button_new_with_label("More info..."); gtk_widget_show(button); gtk_box_pack_start(GTK_BOX(box), button, false, true, 0); *(GtkWidget **)vctx = button; return box; } static void more_info_closed(void *vctx, int result) { struct verify_ssh_host_key_dialog_ctx *ctx = (struct verify_ssh_host_key_dialog_ctx *)vctx; ctx->more_info_dialog = NULL; } static void more_info_button_clicked(GtkButton *button, gpointer vctx) { struct verify_ssh_host_key_dialog_ctx *ctx = (struct verify_ssh_host_key_dialog_ctx *)vctx; if (ctx->more_info_dialog) return; ctx->more_info_dialog = create_message_box( ctx->main_dialog, "Host key information", ctx->more_info, string_width("SHA256 fingerprint: ecdsa-sha2-nistp521 521 " "abcdefghkmnopqrsuvwxyzABCDEFGHJKLMNOPQRSTUW"), true, &buttons_ok, more_info_closed, ctx); } int gtk_seat_verify_ssh_host_key( Seat *seat, const char *host, int port, const char *keytype, char *keystr, const char *keydisp, char **fingerprints, void (*callback)(void *ctx, int result), void *ctx) { static const char absenttxt[] = "The server's host key is not cached. You have no guarantee " "that the server is the computer you think it is.\n" "The server's %s key fingerprint is:\n" "%s\n" "If you trust this host, press \"Accept\" to add the key to " "PuTTY's cache and carry on connecting.\n" "If you want to carry on connecting just once, without " "adding the key to the cache, press \"Connect Once\".\n" "If you do not trust this host, press \"Cancel\" to abandon the " "connection."; static const char wrongtxt[] = "WARNING - POTENTIAL SECURITY BREACH!\n" "The server's host key does not match the one PuTTY has " "cached. This means that either the server administrator " "has changed the host key, or you have actually connected " "to another computer pretending to be the server.\n" "The new %s key fingerprint is:\n" "%s\n" "If you were expecting this change and trust the new key, " "press \"Accept\" to update PuTTY's cache and continue connecting.\n" "If you want to carry on connecting but without updating " "the cache, press \"Connect Once\".\n" "If you want to abandon the connection completely, press " "\"Cancel\" to cancel. Pressing \"Cancel\" is the ONLY guaranteed " "safe choice."; static const struct message_box_button button_array_hostkey[] = { {"Accept", 'a', 0, 2}, {"Connect Once", 'o', 0, 1}, {"Cancel", 'c', -1, 0}, }; static const struct message_box_buttons buttons_hostkey = { button_array_hostkey, lenof(button_array_hostkey), }; char *text; int ret; struct verify_ssh_host_key_dialog_ctx *result_ctx; GtkWidget *mainwin, *msgbox; /* * Verify the key. */ ret = verify_host_key(host, port, keytype, keystr); if (ret == 0) /* success - key matched OK */ return 1; FingerprintType fptype_default = ssh2_pick_default_fingerprint(fingerprints); text = dupprintf((ret == 2 ? wrongtxt : absenttxt), keytype, fingerprints[fptype_default]); result_ctx = snew(struct verify_ssh_host_key_dialog_ctx); result_ctx->callback = callback; result_ctx->callback_ctx = ctx; result_ctx->host = dupstr(host); result_ctx->port = port; result_ctx->keytype = dupstr(keytype); result_ctx->keystr = dupstr(keystr); result_ctx->seat = seat; mainwin = GTK_WIDGET(gtk_seat_get_window(seat)); GtkWidget *more_info_button = NULL; msgbox = create_message_box_general( mainwin, "PuTTY Security Alert", text, string_width(fingerprints[fptype_default]), true, &buttons_hostkey, verify_ssh_host_key_result_callback, result_ctx, add_more_info_button, &more_info_button); result_ctx->main_dialog = msgbox; result_ctx->more_info_dialog = NULL; strbuf *sb = strbuf_new(); if (fingerprints[SSH_FPTYPE_SHA256]) strbuf_catf(sb, "SHA256 fingerprint: %s\n", fingerprints[SSH_FPTYPE_SHA256]); if (fingerprints[SSH_FPTYPE_MD5]) strbuf_catf(sb, "MD5 fingerprint: %s\n", fingerprints[SSH_FPTYPE_MD5]); strbuf_catf(sb, "Full text of host's public key:"); /* We have to manually wrap the public key, or else the GtkLabel * will resize itself to accommodate the longest word, which will * lead to a hilariously wide message box. */ for (const char *p = keydisp, *q = p + strlen(p); p < q ;) { size_t linelen = q-p; if (linelen > 72) linelen = 72; put_byte(sb, '\n'); put_data(sb, p, linelen); p += linelen; } result_ctx->more_info = strbuf_to_str(sb); g_signal_connect(G_OBJECT(more_info_button), "clicked", G_CALLBACK(more_info_button_clicked), result_ctx); register_dialog(seat, DIALOG_SLOT_NETWORK_PROMPT, msgbox); sfree(text); return -1; /* dialog still in progress */ } struct simple_prompt_result_ctx { void (*callback)(void *callback_ctx, int result); void *callback_ctx; Seat *seat; enum DialogSlot dialog_slot; }; static void simple_prompt_result_callback(void *vctx, int result) { struct simple_prompt_result_ctx *ctx = (struct simple_prompt_result_ctx *)vctx; unregister_dialog(ctx->seat, ctx->dialog_slot); if (result >= 0) ctx->callback(ctx->callback_ctx, result); /* * Clean up this context structure, whether or not a result was * ever actually delivered from the dialog box. */ sfree(ctx); } /* * Ask whether the selected algorithm is acceptable (since it was * below the configured 'warn' threshold). */ int gtk_seat_confirm_weak_crypto_primitive( Seat *seat, const char *algtype, const char *algname, void (*callback)(void *ctx, int result), void *ctx) { static const char msg[] = "The first %s supported by the server is " "%s, which is below the configured warning threshold.\n" "Continue with connection?"; char *text; struct simple_prompt_result_ctx *result_ctx; GtkWidget *mainwin, *msgbox; text = dupprintf(msg, algtype, algname); result_ctx = snew(struct simple_prompt_result_ctx); result_ctx->callback = callback; result_ctx->callback_ctx = ctx; result_ctx->seat = seat; result_ctx->dialog_slot = DIALOG_SLOT_NETWORK_PROMPT; mainwin = GTK_WIDGET(gtk_seat_get_window(seat)); msgbox = create_message_box( mainwin, "PuTTY Security Alert", text, string_width("Reasonably long line of text as a width template"), false, &buttons_yn, simple_prompt_result_callback, result_ctx); register_dialog(seat, result_ctx->dialog_slot, msgbox); sfree(text); return -1; /* dialog still in progress */ } int gtk_seat_confirm_weak_cached_hostkey( Seat *seat, const char *algname, const char *betteralgs, void (*callback)(void *ctx, int result), void *ctx) { static const char msg[] = "The first host key type we have stored for this server\n" "is %s, which is below the configured warning threshold.\n" "The server also provides the following types of host key\n" "above the threshold, which we do not have stored:\n" "%s\n" "Continue with connection?"; char *text; struct simple_prompt_result_ctx *result_ctx; GtkWidget *mainwin, *msgbox; text = dupprintf(msg, algname, betteralgs); result_ctx = snew(struct simple_prompt_result_ctx); result_ctx->callback = callback; result_ctx->callback_ctx = ctx; result_ctx->seat = seat; result_ctx->dialog_slot = DIALOG_SLOT_NETWORK_PROMPT; mainwin = GTK_WIDGET(gtk_seat_get_window(seat)); msgbox = create_message_box( mainwin, "PuTTY Security Alert", text, string_width("is ecdsa-nistp521, which is below the configured" " warning threshold."), false, &buttons_yn, simple_prompt_result_callback, result_ctx); register_dialog(seat, result_ctx->dialog_slot, msgbox); sfree(text); return -1; /* dialog still in progress */ } void old_keyfile_warning(void) { /* * This should never happen on Unix. We hope. */ } void nonfatal_message_box(void *window, const char *msg) { char *title = dupcat(appname, " Error"); create_message_box( window, title, msg, string_width("REASONABLY LONG LINE OF TEXT FOR BASIC SANITY"), false, &buttons_ok, trivial_post_dialog_fn, NULL); sfree(title); } void nonfatal(const 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_window_destroyed(GtkWidget *widget, gpointer data) { aboutbox = NULL; } static void about_close_clicked(GtkButton *button, gpointer data) { gtk_widget_destroy(aboutbox); aboutbox = NULL; } static void about_key_press(GtkWidget *widget, GdkEventKey *event, gpointer data) { if (event->keyval == GDK_KEY_Escape && aboutbox) { gtk_widget_destroy(aboutbox); aboutbox = NULL; } } static void licence_clicked(GtkButton *button, gpointer data) { char *title; title = dupcat(appname, " Licence"); assert(aboutbox != NULL); create_message_box(aboutbox, title, LICENCE_TEXT("\n\n"), string_width("LONGISH LINE OF TEXT SO THE LICENCE" " BOX ISN'T EXCESSIVELY TALL AND THIN"), true, &buttons_ok, trivial_post_dialog_fn, NULL); sfree(title); } void about_box(void *window) { GtkWidget *w; GtkBox *action_area; char *title; if (aboutbox) { gtk_widget_grab_focus(aboutbox); return; } aboutbox = our_dialog_new(); gtk_container_set_border_width(GTK_CONTAINER(aboutbox), 10); title = dupcat("About ", appname); gtk_window_set_title(GTK_WINDOW(aboutbox), title); sfree(title); g_signal_connect(G_OBJECT(aboutbox), "destroy", G_CALLBACK(about_window_destroyed), NULL); w = gtk_button_new_with_label("Close"); gtk_widget_set_can_default(w, true); gtk_window_set_default(GTK_WINDOW(aboutbox), w); action_area = our_dialog_make_action_hbox(GTK_WINDOW(aboutbox)); gtk_box_pack_end(action_area, w, false, false, 0); g_signal_connect(G_OBJECT(w), "clicked", G_CALLBACK(about_close_clicked), NULL); gtk_widget_show(w); w = gtk_button_new_with_label("View Licence"); gtk_widget_set_can_default(w, true); gtk_box_pack_end(action_area, w, false, false, 0); g_signal_connect(G_OBJECT(w), "clicked", G_CALLBACK(licence_clicked), NULL); gtk_widget_show(w); { char *buildinfo_text = buildinfo("\n"); char *label_text = dupprintf ("%s\n\n%s\n\n%s\n\n%s", appname, ver, buildinfo_text, "Copyright " SHORT_COPYRIGHT_DETAILS ". All rights reserved"); w = gtk_label_new(label_text); gtk_label_set_justify(GTK_LABEL(w), GTK_JUSTIFY_CENTER); #if GTK_CHECK_VERSION(2,0,0) gtk_label_set_selectable(GTK_LABEL(w), true); #endif sfree(label_text); } our_dialog_add_to_content_area(GTK_WINDOW(aboutbox), w, false, false, 0); #if GTK_CHECK_VERSION(2,0,0) /* * Same precautions against initial select-all as in * create_message_box(). */ gtk_widget_grab_focus(w); gtk_label_select_region(GTK_LABEL(w), 0, 0); #endif gtk_widget_show(w); g_signal_connect(G_OBJECT(aboutbox), "key_press_event", G_CALLBACK(about_key_press), NULL); set_transient_window_pos(GTK_WIDGET(window), aboutbox); if (window) gtk_window_set_transient_for(GTK_WINDOW(aboutbox), GTK_WINDOW(window)); gtk_container_set_focus_child(GTK_CONTAINER(aboutbox), NULL); gtk_widget_show(aboutbox); gtk_window_set_focus(GTK_WINDOW(aboutbox), NULL); } #define LOGEVENT_INITIAL_MAX 128 #define LOGEVENT_CIRCULAR_MAX 128 struct eventlog_stuff { GtkWidget *parentwin, *window; struct controlbox *eventbox; struct Shortcuts scs; struct dlgparam dp; union control *listctrl; char **events_initial; char **events_circular; int ninitial, ncircular, circular_first; strbuf *seldata; int sellen; bool ignore_selchange; }; static void eventlog_destroy(GtkWidget *widget, gpointer data) { eventlog_stuff *es = (eventlog_stuff *)data; es->window = NULL; dlg_cleanup(&es->dp); ctrl_free_box(es->eventbox); } static void eventlog_ok_handler(union control *ctrl, dlgparam *dp, void *data, int event) { if (event == EVENT_ACTION) dlg_end(dp, 0); } static void eventlog_list_handler(union control *ctrl, dlgparam *dp, void *data, int event) { eventlog_stuff *es = (eventlog_stuff *)data; if (event == EVENT_REFRESH) { int i; dlg_update_start(ctrl, dp); dlg_listbox_clear(ctrl, dp); for (i = 0; i < es->ninitial; i++) { dlg_listbox_add(ctrl, dp, es->events_initial[i]); } for (i = 0; i < es->ncircular; i++) { dlg_listbox_add(ctrl, dp, es->events_circular[(es->circular_first + i) % LOGEVENT_CIRCULAR_MAX]); } dlg_update_done(ctrl, dp); } else if (event == EVENT_SELCHANGE) { int i; /* * If this SELCHANGE event is happening as a result of * deliberate deselection because someone else has grabbed * the selection, the last thing we want to do is pre-empt * them. */ if (es->ignore_selchange) return; /* * Construct the data to use as the selection. */ strbuf_clear(es->seldata); for (i = 0; i < es->ninitial; i++) { if (dlg_listbox_issel(ctrl, dp, i)) strbuf_catf(es->seldata, "%s\n", es->events_initial[i]); } for (i = 0; i < es->ncircular; i++) { if (dlg_listbox_issel(ctrl, dp, es->ninitial + i)) { int j = (es->circular_first + i) % LOGEVENT_CIRCULAR_MAX; strbuf_catf(es->seldata, "%s\n", es->events_circular[j]); } } if (gtk_selection_owner_set(es->window, GDK_SELECTION_PRIMARY, GDK_CURRENT_TIME)) { gtk_selection_add_target(es->window, GDK_SELECTION_PRIMARY, GDK_SELECTION_TYPE_STRING, 1); gtk_selection_add_target(es->window, GDK_SELECTION_PRIMARY, compound_text_atom, 1); } } } void eventlog_selection_get(GtkWidget *widget, GtkSelectionData *seldata, guint info, guint time_stamp, gpointer data) { eventlog_stuff *es = (eventlog_stuff *)data; gtk_selection_data_set(seldata, gtk_selection_data_get_target(seldata), 8, es->seldata->u, es->seldata->len); } gint eventlog_selection_clear(GtkWidget *widget, GdkEventSelection *seldata, gpointer data) { eventlog_stuff *es = (eventlog_stuff *)data; struct uctrl *uc; /* * Deselect everything in the list box. */ uc = dlg_find_byctrl(&es->dp, es->listctrl); es->ignore_selchange = true; #if !GTK_CHECK_VERSION(2,0,0) assert(uc->list); gtk_list_unselect_all(GTK_LIST(uc->list)); #else assert(uc->treeview); gtk_tree_selection_unselect_all (gtk_tree_view_get_selection(GTK_TREE_VIEW(uc->treeview))); #endif es->ignore_selchange = false; return true; } void showeventlog(eventlog_stuff *es, void *parentwin) { GtkWidget *window, *w0, *w1; GtkWidget *parent = GTK_WIDGET(parentwin); struct controlset *s0, *s1; union control *c; int index; char *title; if (es->window) { gtk_widget_grab_focus(es->window); return; } dlg_init(&es->dp); for (index = 0; index < lenof(es->scs.sc); index++) { es->scs.sc[index].action = SHORTCUT_EMPTY; } es->eventbox = ctrl_new_box(); s0 = ctrl_getset(es->eventbox, "", "", ""); ctrl_columns(s0, 3, 33, 34, 33); c = ctrl_pushbutton(s0, "Close", 'c', HELPCTX(no_help), eventlog_ok_handler, P(NULL)); c->button.column = 1; c->button.isdefault = true; s1 = ctrl_getset(es->eventbox, "x", "", ""); es->listctrl = c = ctrl_listbox(s1, NULL, NO_SHORTCUT, HELPCTX(no_help), eventlog_list_handler, P(es)); c->listbox.height = 10; c->listbox.multisel = 2; c->listbox.ncols = 3; c->listbox.percentages = snewn(3, int); c->listbox.percentages[0] = 25; c->listbox.percentages[1] = 10; c->listbox.percentages[2] = 65; es->window = window = our_dialog_new(); title = dupcat(appname, " Event Log"); gtk_window_set_title(GTK_WINDOW(window), title); sfree(title); w0 = layout_ctrls(&es->dp, NULL, &es->scs, s0, GTK_WINDOW(window)); our_dialog_set_action_area(GTK_WINDOW(window), w0); gtk_widget_show(w0); w1 = layout_ctrls(&es->dp, NULL, &es->scs, s1, GTK_WINDOW(window)); gtk_container_set_border_width(GTK_CONTAINER(w1), 10); gtk_widget_set_size_request(w1, 20 + string_width ("LINE OF TEXT GIVING WIDTH OF EVENT LOG IS " "QUITE LONG 'COS SSH LOG ENTRIES ARE WIDE"), -1); our_dialog_add_to_content_area(GTK_WINDOW(window), w1, true, true, 0); gtk_widget_show(w1); es->dp.data = es; es->dp.shortcuts = &es->scs; es->dp.lastfocus = NULL; es->dp.retval = 0; es->dp.window = window; dlg_refresh(NULL, &es->dp); if (parent) { set_transient_window_pos(parent, window); gtk_window_set_transient_for(GTK_WINDOW(window), GTK_WINDOW(parent)); } else gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); gtk_widget_show(window); g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(eventlog_destroy), es); g_signal_connect(G_OBJECT(window), "key_press_event", G_CALLBACK(win_key_press), &es->dp); g_signal_connect(G_OBJECT(window), "selection_get", G_CALLBACK(eventlog_selection_get), es); g_signal_connect(G_OBJECT(window), "selection_clear_event", G_CALLBACK(eventlog_selection_clear), es); } eventlog_stuff *eventlogstuff_new(void) { eventlog_stuff *es = snew(eventlog_stuff); memset(es, 0, sizeof(*es)); es->seldata = strbuf_new(); return es; } void eventlogstuff_free(eventlog_stuff *es) { int i; if (es->events_initial) { for (i = 0; i < LOGEVENT_INITIAL_MAX; i++) sfree(es->events_initial[i]); sfree(es->events_initial); } if (es->events_circular) { for (i = 0; i < LOGEVENT_CIRCULAR_MAX; i++) sfree(es->events_circular[i]); sfree(es->events_circular); } strbuf_free(es->seldata); sfree(es); } void logevent_dlg(eventlog_stuff *es, const char *string) { char timebuf[40]; struct tm tm; char **location; size_t i; if (es->ninitial == 0) { es->events_initial = sresize(es->events_initial, LOGEVENT_INITIAL_MAX, char *); for (i = 0; i < LOGEVENT_INITIAL_MAX; i++) es->events_initial[i] = NULL; es->events_circular = sresize(es->events_circular, LOGEVENT_CIRCULAR_MAX, char *); for (i = 0; i < LOGEVENT_CIRCULAR_MAX; i++) es->events_circular[i] = NULL; } if (es->ninitial < LOGEVENT_INITIAL_MAX) location = &es->events_initial[es->ninitial]; else location = &es->events_circular[(es->circular_first + es->ncircular) % LOGEVENT_CIRCULAR_MAX]; tm=ltime(); strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S\t", &tm); sfree(*location); *location = dupcat(timebuf, string); if (es->window) { dlg_listbox_add(es->listctrl, &es->dp, *location); } if (es->ninitial < LOGEVENT_INITIAL_MAX) { es->ninitial++; } else if (es->ncircular < LOGEVENT_CIRCULAR_MAX) { es->ncircular++; } else if (es->ncircular == LOGEVENT_CIRCULAR_MAX) { es->circular_first = (es->circular_first + 1) % LOGEVENT_CIRCULAR_MAX; sfree(es->events_circular[es->circular_first]); es->events_circular[es->circular_first] = dupstr(".."); } } int gtkdlg_askappend(Seat *seat, 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."; static const struct message_box_button button_array_append[] = { {"Overwrite", 'o', 1, 2}, {"Append", 'a', 0, 1}, {"Disable", 'd', -1, 0}, }; static const struct message_box_buttons buttons_append = { button_array_append, lenof(button_array_append), }; char *message; char *mbtitle; struct simple_prompt_result_ctx *result_ctx; GtkWidget *mainwin, *msgbox; message = dupprintf(msgtemplate, FILENAME_MAX, filename->path); mbtitle = dupprintf("%s Log to File", appname); result_ctx = snew(struct simple_prompt_result_ctx); result_ctx->callback = callback; result_ctx->callback_ctx = ctx; result_ctx->seat = seat; result_ctx->dialog_slot = DIALOG_SLOT_LOGFILE_PROMPT; mainwin = GTK_WIDGET(gtk_seat_get_window(seat)); msgbox = create_message_box( mainwin, mbtitle, message, string_width("LINE OF TEXT SUITABLE FOR THE ASKAPPEND WIDTH"), false, &buttons_append, simple_prompt_result_callback, result_ctx); register_dialog(seat, result_ctx->dialog_slot, msgbox); sfree(message); sfree(mbtitle); return -1; /* dialog still in progress */ } putty-0.76/unix/gtkfont.c0000644000175000017500000037732414072266313012403 00000000000000/* * Unified font management for GTK. * * PuTTY is willing to use both old-style X server-side bitmap * fonts _and_ GTK2/Pango client-side fonts. This requires us to * do a bit of work to wrap the two wildly different APIs into * forms the rest of the code can switch between seamlessly, and * also requires a custom font selector capable of handling both * types of font. */ #include #include #include #include #if !GTK_CHECK_VERSION(3,0,0) #include #endif #define MAY_REFER_TO_GTK_IN_HEADERS #include "putty.h" #include "gtkfont.h" #include "gtkcompat.h" #include "gtkmisc.h" #include "tree234.h" #ifndef NOT_X_WINDOWS #include #include #include #include #include "x11misc.h" #endif /* * 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. */ #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 an element of a larger structure containing data * specific to the subtype. */ #define FONTFLAG_CLIENTSIDE 0x0001 #define FONTFLAG_SERVERSIDE 0x0002 #define FONTFLAG_SERVERALIAS 0x0004 #define FONTFLAG_NONMONOSPACED 0x0008 #define FONTFLAG_SORT_MASK 0x0007 /* used to disambiguate font families */ typedef void (*fontsel_add_entry)(void *ctx, const char *realfontname, const char *family, const char *charset, const char *style, const char *stylekey, int size, int flags, const struct UnifontVtable *fontclass); struct UnifontVtable { /* * `Methods' of the `class'. */ unifont *(*create)(GtkWidget *widget, const char *name, bool wide, bool bold, int shadowoffset, bool shadowalways); unifont *(*create_fallback)(GtkWidget *widget, int height, bool wide, bool bold, int shadowoffset, bool shadowalways); void (*destroy)(unifont *font); bool (*has_glyph)(unifont *font, wchar_t glyph); void (*draw_text)(unifont_drawctx *ctx, unifont *font, int x, int y, const wchar_t *string, int len, bool wide, bool bold, int cellwidth); void (*draw_combining)(unifont_drawctx *ctx, unifont *font, int x, int y, const wchar_t *string, int len, bool wide, bool 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, bool resolve_aliases); char *(*scale_fontname)(GtkWidget *widget, const char *name, int size); char *(*size_increment)(unifont *font, int increment); /* * `Static data members' of the `class'. */ const char *prefix; }; #ifndef NOT_X_WINDOWS /* ---------------------------------------------------------------------- * X11 font implementation, directly using Xlib calls. Conditioned out * if X11 fonts aren't available at all (e.g. building with GTK3 for a * back end other than X). */ static bool x11font_has_glyph(unifont *font, wchar_t glyph); static void x11font_draw_text(unifont_drawctx *ctx, unifont *font, int x, int y, const wchar_t *string, int len, bool wide, bool bold, int cellwidth); static void x11font_draw_combining(unifont_drawctx *ctx, unifont *font, int x, int y, const wchar_t *string, int len, bool wide, bool bold, int cellwidth); static unifont *x11font_create(GtkWidget *widget, const char *name, bool wide, bool bold, int shadowoffset, bool shadowalways); static void x11font_destroy(unifont *font); static void x11font_enum_fonts(GtkWidget *widget, fontsel_add_entry callback, void *callback_ctx); static char *x11font_canonify_fontname(GtkWidget *widget, const char *name, int *size, int *flags, bool resolve_aliases); static char *x11font_scale_fontname(GtkWidget *widget, const char *name, int size); static char *x11font_size_increment(unifont *font, int increment); #ifdef DRAW_TEXT_CAIRO struct cairo_cached_glyph { cairo_surface_t *surface; unsigned char *bitmap; }; #endif /* * Structure storing a single physical XFontStruct, plus associated * data. */ typedef struct x11font_individual { /* The XFontStruct itself. */ XFontStruct *xfs; /* * The `allocated' flag indicates whether we've tried to fetch * this subfont already (thus distinguishing xfs==NULL because we * haven't tried yet from xfs==NULL because we tried and failed, * so that we don't keep trying and failing subsequently). */ bool allocated; #ifdef DRAW_TEXT_CAIRO /* * A cache of glyph bitmaps downloaded from the X server when * we're in Cairo rendering mode. If glyphcache itself is * non-NULL, then entries in [0,nglyphs) are expected to be * initialised to either NULL or a bitmap pointer. */ struct cairo_cached_glyph *glyphcache; int nglyphs; /* * X server paraphernalia for actually downloading the glyphs. */ Pixmap pixmap; GC gc; int pixwidth, pixheight, pixoriginx, pixoriginy; /* * Paraphernalia for loading the resulting bitmaps into Cairo. */ int rowsize, allsize, indexflip; #endif } x11font_individual; struct x11font { /* * Copy of the X display handle, so we don't have to keep * extracting it from GDK. */ Display *disp; /* * Individual physical X fonts. We store a number of these, for * automatically guessed bold and wide variants. */ x11font_individual fonts[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. */ bool 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. */ bool 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 shadowoffset; bool wide, bold, shadowalways; unifont u; }; static const UnifontVtable x11font_vtable = { .create = x11font_create, .create_fallback = NULL, /* no fallback fonts in X11 */ .destroy = x11font_destroy, .has_glyph = x11font_has_glyph, .draw_text = x11font_draw_text, .draw_combining = x11font_draw_combining, .enum_fonts = x11font_enum_fonts, .canonify_fontname = x11font_canonify_fontname, .scale_fontname = x11font_scale_fontname, .size_increment = x11font_size_increment, .prefix = "server", }; #define XLFD_STRING_PARTS_LIST(S,I) \ S(foundry) \ S(family_name) \ S(weight_name) \ S(slant) \ S(setwidth_name) \ S(add_style_name) \ I(pixel_size) \ I(point_size) \ I(resolution_x) \ I(resolution_y) \ S(spacing) \ I(average_width) \ S(charset_registry) \ S(charset_encoding) \ /* end of list */ /* Special value for int fields that xlfd_recompose will render as "*" */ #define XLFD_INT_WILDCARD INT_MIN struct xlfd_decomposed { #define STR_FIELD(f) const char *f; #define INT_FIELD(f) int f; XLFD_STRING_PARTS_LIST(STR_FIELD, INT_FIELD) #undef STR_FIELD #undef INT_FIELD }; static struct xlfd_decomposed *xlfd_decompose(const char *xlfd) { char *p, *components[14]; struct xlfd_decomposed *dec; int i; if (!xlfd) return NULL; dec = snew_plus(struct xlfd_decomposed, strlen(xlfd) + 1); p = snew_plus_get_aux(dec); strcpy(p, xlfd); for (i = 0; i < 14; i++) { if (*p != '-') { /* Malformed XLFD: not enough '-' */ sfree(dec); return NULL; } *p++ = '\0'; components[i] = p; p += strcspn(p, "-"); } if (*p) { /* Malformed XLFD: too many '-' */ sfree(dec); return NULL; } i = 0; #define STORE_STR(f) dec->f = components[i++]; #define STORE_INT(f) dec->f = atoi(components[i++]); XLFD_STRING_PARTS_LIST(STORE_STR, STORE_INT) #undef STORE_STR #undef STORE_INT return dec; } static char *xlfd_recompose(const struct xlfd_decomposed *dec) { #define FMT_STR(f) "-%s" #define ARG_STR(f) , dec->f #define FMT_INT(f) "%s%.*d" #define ARG_INT(f) \ , dec->f == XLFD_INT_WILDCARD ? "-*" : "-" \ , dec->f == XLFD_INT_WILDCARD ? 0 : 1 \ , dec->f == XLFD_INT_WILDCARD ? 0 : dec->f return dupprintf(XLFD_STRING_PARTS_LIST(FMT_STR, FMT_INT) XLFD_STRING_PARTS_LIST(ARG_STR, ARG_INT)); #undef FMT_STR #undef ARG_STR #undef FMT_INT #undef ARG_INT } static char *x11_guess_derived_font_name(Display *disp, XFontStruct *xfs, bool bold, bool wide) { Atom fontprop = XInternAtom(disp, "FONT", False); unsigned long ret; if (XGetFontProperty(xfs, fontprop, &ret)) { char *name = XGetAtomName(disp, (Atom)ret); struct xlfd_decomposed *xlfd = xlfd_decompose(name); if (!xlfd) return NULL; if (bold) xlfd->weight_name = "bold"; if (wide) { /* Width name obviously may have changed. */ /* Additional style may now become e.g. `ja' or `ko'. */ xlfd->setwidth_name = xlfd->add_style_name = "*"; /* Expect to double the average width. */ xlfd->average_width *= 2; } { char *ret = xlfd_recompose(xlfd); sfree(xlfd); return ret; } } return NULL; } static int x11_font_width(XFontStruct *xfs, bool 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 const XCharStruct *x11_char_struct( XFontStruct *xfs, unsigned char byte1, unsigned char byte2) { int index; /* * 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 NULL; 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 NULL; 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 &xfs->max_bounds; return &xfs->per_char[index]; } static bool x11_font_has_glyph( XFontStruct *xfs, unsigned char byte1, unsigned char byte2) { /* * 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. */ const XCharStruct *xcs = x11_char_struct(xfs, byte1, byte2); return xcs && (xcs->ascent + xcs->descent > 0 || xcs->width > 0); } static unifont *x11font_create(GtkWidget *widget, const char *name, bool wide, bool bold, int shadowoffset, bool shadowalways) { struct x11font *xfont; XFontStruct *xfs; Display *disp; Atom charset_registry, charset_encoding, spacing; unsigned long registry_ret, encoding_ret, spacing_ret; int pubcs, realcs; bool sixteen_bit, variable; int i; if ((disp = get_x11_display()) == NULL) return NULL; 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; if (XGetFontProperty(xfs, charset_registry, ®istry_ret) && XGetFontProperty(xfs, charset_encoding, &encoding_ret)) { char *reg, *enc; reg = XGetAtomName(disp, (Atom)registry_ret); enc = XGetAtomName(disp, (Atom)encoding_ret); if (reg && enc) { char *encoding = dupcat(reg, "-", enc); pubcs = realcs = charset_from_xenc(encoding); /* * iso10646-1 is the only wide font encoding we * support. In this case, we expect clients to give us * UTF-8, which this module must internally convert * into 16-bit Unicode. */ 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->u.strikethrough_y = xfont->u.ascent - (xfont->u.ascent * 3 / 8); #ifdef DRAW_TEXT_GDK xfont->u.preferred_drawtype = DRAWTYPE_GDK; #elif defined DRAW_TEXT_CAIRO xfont->u.preferred_drawtype = DRAWTYPE_CAIRO; #else #error No drawtype available at all #endif xfont->disp = disp; xfont->real_charset = realcs; xfont->sixteen_bit = sixteen_bit; xfont->variable = variable; xfont->wide = wide; xfont->bold = bold; xfont->shadowoffset = shadowoffset; xfont->shadowalways = shadowalways; for (i = 0; i < lenof(xfont->fonts); i++) { xfont->fonts[i].xfs = NULL; xfont->fonts[i].allocated = false; #ifdef DRAW_TEXT_CAIRO xfont->fonts[i].glyphcache = NULL; xfont->fonts[i].nglyphs = 0; xfont->fonts[i].pixmap = None; xfont->fonts[i].gc = None; #endif } xfont->fonts[0].xfs = xfs; xfont->fonts[0].allocated = true; return &xfont->u; } static void x11font_destroy(unifont *font) { struct x11font *xfont = container_of(font, struct x11font, u); Display *disp = xfont->disp; int i; for (i = 0; i < lenof(xfont->fonts); i++) { if (xfont->fonts[i].xfs) XFreeFont(disp, xfont->fonts[i].xfs); #ifdef DRAW_TEXT_CAIRO if (xfont->fonts[i].gc != None) XFreeGC(disp, xfont->fonts[i].gc); if (xfont->fonts[i].pixmap != None) XFreePixmap(disp, xfont->fonts[i].pixmap); if (xfont->fonts[i].glyphcache) { int j; for (j = 0; j < xfont->fonts[i].nglyphs; j++) { cairo_surface_destroy(xfont->fonts[i].glyphcache[j].surface); sfree(xfont->fonts[i].glyphcache[j].bitmap); } sfree(xfont->fonts[i].glyphcache); } #endif } sfree(xfont); } static void x11_alloc_subfont(struct x11font *xfont, int sfid) { Display *disp = xfont->disp; char *derived_name = x11_guess_derived_font_name (disp, xfont->fonts[0].xfs, sfid & 1, !!(sfid & 2)); xfont->fonts[sfid].xfs = XLoadQueryFont(disp, derived_name); xfont->fonts[sfid].allocated = true; sfree(derived_name); /* Note that xfont->fonts[sfid].xfs may still be NULL, if XLQF failed. */ } static bool x11font_has_glyph(unifont *font, wchar_t glyph) { struct x11font *xfont = container_of(font, struct x11font, u); 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].xfs, 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); if (sblen == 0 || !sbstring[0]) return false; /* not even in the charset */ return x11_font_has_glyph(xfont->fonts[0].xfs, 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 */ #elif GTK_CHECK_VERSION(3,0,0) #define GDK_DRAWABLE_XID(d) GDK_WINDOW_XID(d) /* GTK3's name for this */ #endif static int x11font_width_16(unifont_drawctx *ctx, x11font_individual *xfi, const void *vstring, int start, int length) { const XChar2b *string = (const XChar2b *)vstring; return XTextWidth16(xfi->xfs, string+start, length); } static int x11font_width_8(unifont_drawctx *ctx, x11font_individual *xfi, const void *vstring, int start, int length) { const char *string = (const char *)vstring; return XTextWidth(xfi->xfs, string+start, length); } #ifdef DRAW_TEXT_GDK static void x11font_gdk_setup(unifont_drawctx *ctx, x11font_individual *xfi, Display *disp) { XSetFont(disp, GDK_GC_XGC(ctx->u.gdk.gc), xfi->xfs->fid); } static void x11font_gdk_draw_16(unifont_drawctx *ctx, x11font_individual *xfi, Display *disp, int x, int y, const void *vstring, int start, int length) { const XChar2b *string = (const XChar2b *)vstring; XDrawString16(disp, GDK_DRAWABLE_XID(ctx->u.gdk.target), GDK_GC_XGC(ctx->u.gdk.gc), x, y, string+start, length); } static void x11font_gdk_draw_8(unifont_drawctx *ctx, x11font_individual *xfi, Display *disp, int x, int y, const void *vstring, int start, int length) { const char *string = (const char *)vstring; XDrawString(disp, GDK_DRAWABLE_XID(ctx->u.gdk.target), GDK_GC_XGC(ctx->u.gdk.gc), x, y, string+start, length); } #endif #ifdef DRAW_TEXT_CAIRO static void x11font_cairo_setup( unifont_drawctx *ctx, x11font_individual *xfi, Display *disp) { if (xfi->pixmap == None) { XGCValues gcvals; GdkWindow *widgetwin = gtk_widget_get_window(ctx->u.cairo.widget); int widgetscr = GDK_SCREEN_XNUMBER(gdk_window_get_screen(widgetwin)); xfi->pixwidth = xfi->xfs->max_bounds.rbearing - xfi->xfs->min_bounds.lbearing; xfi->pixheight = xfi->xfs->max_bounds.ascent + xfi->xfs->max_bounds.descent; xfi->pixoriginx = -xfi->xfs->min_bounds.lbearing; xfi->pixoriginy = xfi->xfs->max_bounds.ascent; xfi->rowsize = cairo_format_stride_for_width(CAIRO_FORMAT_A1, xfi->pixwidth); xfi->allsize = xfi->rowsize * xfi->pixheight; { /* * Test host endianness and use it to set xfi->indexflip, * which is XORed into our left-shift counts in order to * implement the CAIRO_FORMAT_A1 specification, in which * each bitmap byte is oriented LSB-first on little-endian * platforms and MSB-first on big-endian ones. * * This is the same technique Cairo itself uses to test * endianness, so hopefully it'll work in any situation * where Cairo is usable at all. */ static const int endianness_test = 1; xfi->indexflip = (*((char *) &endianness_test) == 1) ? 0 : 7; } xfi->pixmap = XCreatePixmap (disp, GDK_DRAWABLE_XID(gtk_widget_get_window(ctx->u.cairo.widget)), xfi->pixwidth, xfi->pixheight, 1); gcvals.foreground = WhitePixel(disp, widgetscr); gcvals.background = BlackPixel(disp, widgetscr); gcvals.font = xfi->xfs->fid; xfi->gc = XCreateGC(disp, xfi->pixmap, GCForeground | GCBackground | GCFont, &gcvals); } } static void x11font_cairo_cache_glyph( Display *disp, x11font_individual *xfi, int glyphindex) { XImage *image; int x, y; unsigned char *bitmap; const XCharStruct *xcs = x11_char_struct(xfi->xfs, glyphindex >> 8, glyphindex & 0xFF); bitmap = snewn(xfi->allsize, unsigned char); memset(bitmap, 0, xfi->allsize); image = XGetImage(disp, xfi->pixmap, 0, 0, xfi->pixwidth, xfi->pixheight, AllPlanes, XYPixmap); for (y = xfi->pixoriginy - xcs->ascent; y < xfi->pixoriginy + xcs->descent; y++) { for (x = xfi->pixoriginx + xcs->lbearing; x < xfi->pixoriginx + xcs->rbearing; x++) { unsigned long pixel = XGetPixel(image, x, y); if (pixel) { int byteindex = y * xfi->rowsize + x/8; int bitindex = (x & 7) ^ xfi->indexflip; bitmap[byteindex] |= 1U << bitindex; } } } XDestroyImage(image); if (xfi->nglyphs <= glyphindex) { /* Round up to the next multiple of 256 on the general * principle that Unicode characters come in contiguous blocks * often used together */ int old_nglyphs = xfi->nglyphs; xfi->nglyphs = (glyphindex + 0x100) & ~0xFF; xfi->glyphcache = sresize(xfi->glyphcache, xfi->nglyphs, struct cairo_cached_glyph); while (old_nglyphs < xfi->nglyphs) { xfi->glyphcache[old_nglyphs].surface = NULL; xfi->glyphcache[old_nglyphs].bitmap = NULL; old_nglyphs++; } } xfi->glyphcache[glyphindex].bitmap = bitmap; xfi->glyphcache[glyphindex].surface = cairo_image_surface_create_for_data (bitmap, CAIRO_FORMAT_A1, xfi->pixwidth, xfi->pixheight, xfi->rowsize); } static void x11font_cairo_draw_glyph(unifont_drawctx *ctx, x11font_individual *xfi, int x, int y, int glyphindex) { if (xfi->glyphcache[glyphindex].surface) { cairo_mask_surface(ctx->u.cairo.cr, xfi->glyphcache[glyphindex].surface, x - xfi->pixoriginx, y - xfi->pixoriginy); } } static void x11font_cairo_draw_16( unifont_drawctx *ctx, x11font_individual *xfi, Display *disp, int x, int y, const void *vstring, int start, int length) { const XChar2b *string = (const XChar2b *)vstring + start; int i; for (i = 0; i < length; i++) { if (x11_font_has_glyph(xfi->xfs, string[i].byte1, string[i].byte2)) { int glyphindex = (256 * (unsigned char)string[i].byte1 + (unsigned char)string[i].byte2); if (glyphindex >= xfi->nglyphs || !xfi->glyphcache[glyphindex].surface) { XDrawImageString16(disp, xfi->pixmap, xfi->gc, xfi->pixoriginx, xfi->pixoriginy, string+i, 1); x11font_cairo_cache_glyph(disp, xfi, glyphindex); } x11font_cairo_draw_glyph(ctx, xfi, x, y, glyphindex); x += XTextWidth16(xfi->xfs, string+i, 1); } } } static void x11font_cairo_draw_8( unifont_drawctx *ctx, x11font_individual *xfi, Display *disp, int x, int y, const void *vstring, int start, int length) { const char *string = (const char *)vstring + start; int i; for (i = 0; i < length; i++) { if (x11_font_has_glyph(xfi->xfs, 0, string[i])) { int glyphindex = (unsigned char)string[i]; if (glyphindex >= xfi->nglyphs || !xfi->glyphcache[glyphindex].surface) { XDrawImageString(disp, xfi->pixmap, xfi->gc, xfi->pixoriginx, xfi->pixoriginy, string+i, 1); x11font_cairo_cache_glyph(disp, xfi, glyphindex); } x11font_cairo_draw_glyph(ctx, xfi, x, y, glyphindex); x += XTextWidth(xfi->xfs, string+i, 1); } } } #endif /* DRAW_TEXT_CAIRO */ struct x11font_drawfuncs { int (*width)(unifont_drawctx *ctx, x11font_individual *xfi, const void *vstring, int start, int length); void (*setup)(unifont_drawctx *ctx, x11font_individual *xfi, Display *disp); void (*draw)(unifont_drawctx *ctx, x11font_individual *xfi, Display *disp, int x, int y, const void *vstring, int start, int length); }; /* * This array has two entries per compiled-in drawtype; of each pair, * the first is for an 8-bit font and the second for 16-bit. */ static const struct x11font_drawfuncs x11font_drawfuncs[2*DRAWTYPE_NTYPES] = { #ifdef DRAW_TEXT_GDK /* gdk, 8-bit */ { x11font_width_8, x11font_gdk_setup, x11font_gdk_draw_8, }, /* gdk, 16-bit */ { x11font_width_16, x11font_gdk_setup, x11font_gdk_draw_16, }, #endif #ifdef DRAW_TEXT_CAIRO /* cairo, 8-bit */ { x11font_width_8, x11font_cairo_setup, x11font_cairo_draw_8, }, /* [3] cairo, 16-bit */ { x11font_width_16, x11font_cairo_setup, x11font_cairo_draw_16, }, #endif }; static void x11font_really_draw_text( const struct x11font_drawfuncs *dfns, unifont_drawctx *ctx, x11font_individual *xfi, Display *disp, int x, int y, const void *string, int nchars, int shadowoffset, bool fontvariable, int cellwidth) { int start = 0, step, nsteps; bool 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; } dfns->setup(ctx, xfi, disp); while (nsteps-- > 0) { int X = x; if (centre) X += (cellwidth - dfns->width(ctx, xfi, string, start, step)) / 2; dfns->draw(ctx, xfi, disp, X, y, string, start, step); if (shadowoffset) dfns->draw(ctx, xfi, disp, X + shadowoffset, y, string, start, step); x += cellwidth; start += step; } } static void x11font_draw_text(unifont_drawctx *ctx, unifont *font, int x, int y, const wchar_t *string, int len, bool wide, bool bold, int cellwidth) { struct x11font *xfont = container_of(font, struct x11font, u); int sfid; int shadowoffset = 0; int mult = (wide ? 2 : 1); int index = 2 * (int)ctx->type; wide = wide && !xfont->wide; bold = 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 = false; } sfid = 2 * wide + bold; if (!xfont->fonts[sfid].allocated) x11_alloc_subfont(xfont, sfid); if (bold && !xfont->fonts[sfid].xfs) { bold = false; shadowoffset = xfont->shadowoffset; sfid = 2 * wide + bold; if (!xfont->fonts[sfid].allocated) x11_alloc_subfont(xfont, sfid); } if (!xfont->fonts[sfid].xfs) return; /* we've tried our best, but no luck */ 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(x11font_drawfuncs + index + 1, ctx, &xfont->fonts[sfid], xfont->disp, 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); x11font_really_draw_text(x11font_drawfuncs + index + 0, ctx, &xfont->fonts[sfid], xfont->disp, x, y, sbstring, sblen, shadowoffset, xfont->variable, cellwidth * mult); sfree(sbstring); } } static void x11font_draw_combining(unifont_drawctx *ctx, unifont *font, int x, int y, const wchar_t *string, int len, bool wide, bool bold, int cellwidth) { /* * For server-side fonts, there's no sophisticated system for * combining characters intelligently, so the best we can do is to * overprint them on each other in the obvious way. */ int i; for (i = 0; i < len; i++) x11font_draw_text(ctx, font, x, y, string+i, 1, wide, bold, cellwidth); } static void x11font_enum_fonts(GtkWidget *widget, fontsel_add_entry callback, void *callback_ctx) { Display *disp; char **fontnames; char *tmp = NULL; int nnames, i, max, tmpsize; if ((disp = get_x11_display()) == NULL) return; max = 32768; while (1) { fontnames = XListFonts(disp, "*", max, &nnames); if (nnames >= max) { XFreeFontNames(fontnames); max *= 2; } else break; } tmpsize = 0; for (i = 0; i < nnames; i++) { struct xlfd_decomposed *xlfd = xlfd_decompose(fontnames[i]); if (xlfd) { char *p, *font, *style, *stylekey, *charset; int weightkey, slantkey, setwidthkey; int thistmpsize; /* * Convert a dismembered XLFD into the format we'll be * using in the font selector. */ thistmpsize = 4 * strlen(fontnames[i]) + 256; if (tmpsize < thistmpsize) { tmpsize = thistmpsize; tmp = sresize(tmp, tmpsize, char); } p = tmp; /* * Font name is in the form "family (foundry)". (This is * what the GTK 1.2 X font selector does, and it seems to * come out looking reasonably sensible.) */ font = p; p += 1 + sprintf(p, "%s (%s)", xlfd->family_name, xlfd->foundry); /* * Character set. */ charset = p; p += 1 + sprintf(p, "%s-%s", xlfd->charset_registry, xlfd->charset_encoding); /* * Style is a mixture of quite a lot of the fields, * with some strange formatting. */ style = p; p += sprintf(p, "%s", xlfd->weight_name[0] ? xlfd->weight_name : "regular"); if (!g_ascii_strcasecmp(xlfd->slant, "i")) p += sprintf(p, " italic"); else if (!g_ascii_strcasecmp(xlfd->slant, "o")) p += sprintf(p, " oblique"); else if (!g_ascii_strcasecmp(xlfd->slant, "ri")) p += sprintf(p, " reverse italic"); else if (!g_ascii_strcasecmp(xlfd->slant, "ro")) p += sprintf(p, " reverse oblique"); else if (!g_ascii_strcasecmp(xlfd->slant, "ot")) p += sprintf(p, " other-slant"); if (xlfd->setwidth_name[0] && g_ascii_strcasecmp(xlfd->setwidth_name, "normal")) p += sprintf(p, " %s", xlfd->setwidth_name); if (!g_ascii_strcasecmp(xlfd->spacing, "m")) p += sprintf(p, " [M]"); if (!g_ascii_strcasecmp(xlfd->spacing, "c")) p += sprintf(p, " [C]"); if (xlfd->add_style_name[0]) p += sprintf(p, " %s", xlfd->add_style_name); /* * 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(xlfd->weight_name, "medium") || !g_ascii_strcasecmp(xlfd->weight_name, "regular") || !g_ascii_strcasecmp(xlfd->weight_name, "normal") || !g_ascii_strcasecmp(xlfd->weight_name, "book")) weightkey = 0; else if (!g_ascii_strncasecmp(xlfd->weight_name, "demi", 4) || !g_ascii_strncasecmp(xlfd->weight_name, "semi", 4)) weightkey = 1; else weightkey = 2; if (!g_ascii_strcasecmp(xlfd->slant, "r")) slantkey = 0; else if (!g_ascii_strncasecmp(xlfd->slant, "r", 1)) slantkey = 2; else slantkey = 1; if (!g_ascii_strcasecmp(xlfd->setwidth_name, "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(xlfd->weight_name), xlfd->weight_name, slantkey, (int)strlen(xlfd->slant), xlfd->slant, setwidthkey, (int)strlen(xlfd->setwidth_name), xlfd->setwidth_name, (int)strlen(xlfd->spacing), xlfd->spacing, (int)strlen(xlfd->add_style_name), xlfd->add_style_name); assert(p - tmp < thistmpsize); /* * Flags: we need to know whether this is a monospaced * font, which we do by examining the spacing field * again. */ int flags = FONTFLAG_SERVERSIDE; if (!strchr("CcMm", xlfd->spacing[0])) flags |= FONTFLAG_NONMONOSPACED; /* * Some fonts have a pixel size of zero, meaning they're * treated as scalable. For these purposes, we only want * fonts whose pixel size we actually know, so filter * those out. */ if (xlfd->pixel_size) callback(callback_ctx, fontnames[i], font, charset, style, stylekey, xlfd->pixel_size, flags, &x11font_vtable); sfree(xlfd); } else { /* * This isn't an XLFD, so it must be an alias. * Transmit it with mostly null data. * * It would be nice to work out if it's monospaced * here, but at the moment I can't see that being * anything but computationally hideous. Ah well. */ callback(callback_ctx, fontnames[i], fontnames[i], NULL, NULL, NULL, 0, FONTFLAG_SERVERALIAS, &x11font_vtable); sfree(xlfd); } } XFreeFontNames(fontnames); sfree(tmp); } static char *x11font_canonify_fontname(GtkWidget *widget, const char *name, int *size, int *flags, bool resolve_aliases) { /* * When given an X11 font name to try to make sense of for a * font selector, we must attempt to load it (to see if it * exists), and then canonify it by extracting its FONT * 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; Atom fontprop, fontprop2; unsigned long ret; if ((disp = get_x11_display()) == NULL) return NULL; 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 */ } static char *x11font_size_increment(unifont *font, int increment) { struct x11font *xfont = container_of(font, struct x11font, u); Display *disp = xfont->disp; Atom fontprop = XInternAtom(disp, "FONT", False); char *returned_name = NULL; unsigned long ret; if (XGetFontProperty(xfont->fonts[0].xfs, fontprop, &ret)) { struct xlfd_decomposed *xlfd; struct xlfd_decomposed *xlfd_best; char *wc; char **fontnames; int nnames, i, max; xlfd = xlfd_decompose(XGetAtomName(disp, (Atom)ret)); if (!xlfd) return NULL; /* * Form a wildcard consisting of everything in the * original XLFD except for the size-related fields. */ { struct xlfd_decomposed xlfd_wc = *xlfd; /* structure copy */ xlfd_wc.pixel_size = XLFD_INT_WILDCARD; xlfd_wc.point_size = XLFD_INT_WILDCARD; xlfd_wc.average_width = XLFD_INT_WILDCARD; wc = xlfd_recompose(&xlfd_wc); } /* * Fetch all the font names matching that wildcard. */ max = 32768; while (1) { fontnames = XListFonts(disp, wc, max, &nnames); if (nnames >= max) { XFreeFontNames(fontnames); max *= 2; } else break; } sfree(wc); /* * Iterate over those to find the one closest in size to the * original font, in the correct direction. */ #define FLIPPED_SIZE(xlfd) \ (((xlfd)->pixel_size + (xlfd)->point_size) * \ (increment < 0 ? -1 : +1)) xlfd_best = NULL; for (i = 0; i < nnames; i++) { struct xlfd_decomposed *xlfd2 = xlfd_decompose(fontnames[i]); if (!xlfd2) continue; if (xlfd2->pixel_size != 0 && FLIPPED_SIZE(xlfd2) > FLIPPED_SIZE(xlfd) && (!xlfd_best || FLIPPED_SIZE(xlfd2)u.vt->prefix, ":", bare_returned_name); sfree(bare_returned_name); } XFreeFontNames(fontnames); sfree(xlfd); sfree(xlfd_best); } return returned_name; } #endif /* NOT_X_WINDOWS */ #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 bool pangofont_has_glyph(unifont *font, wchar_t glyph); static void pangofont_draw_text(unifont_drawctx *ctx, unifont *font, int x, int y, const wchar_t *string, int len, bool wide, bool bold, int cellwidth); static void pangofont_draw_combining(unifont_drawctx *ctx, unifont *font, int x, int y, const wchar_t *string, int len, bool wide, bool bold, int cellwidth); static unifont *pangofont_create(GtkWidget *widget, const char *name, bool wide, bool bold, int shadowoffset, bool shadowalways); static unifont *pangofont_create_fallback(GtkWidget *widget, int height, bool wide, bool bold, int shadowoffset, bool 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, bool resolve_aliases); static char *pangofont_scale_fontname(GtkWidget *widget, const char *name, int size); static char *pangofont_size_increment(unifont *font, int increment); struct pangofont { /* * Pango objects. */ PangoFontDescription *desc; PangoFontset *fset; /* * The containing widget. */ GtkWidget *widget; /* * Data passed in to unifont_create(). */ int shadowoffset; bool bold, shadowalways; /* * Cache of character widths, indexed by Unicode code point. In * pixels; -1 means we haven't asked Pango about this character * before. */ int *widthcache; unsigned nwidthcache; struct unifont u; }; static const UnifontVtable pangofont_vtable = { .create = pangofont_create, .create_fallback = pangofont_create_fallback, .destroy = pangofont_destroy, .has_glyph = pangofont_has_glyph, .draw_text = pangofont_draw_text, .draw_combining = pangofont_draw_combining, .enum_fonts = pangofont_enum_fonts, .canonify_fontname = pangofont_canonify_fontname, .scale_fontname = pangofont_scale_fontname, .size_increment = pangofont_size_increment, .prefix = "client", }; /* * This function is used to rigorously validate a * PangoFontDescription. Later versions of Pango have a nasty * habit of accepting _any_ old string as input to * pango_font_description_from_string and returning a font * description which can actually be used to display text, even if * they have to do it by falling back to their most default font. * This is doubtless helpful in some situations, but not here, * because we need to know if a Pango font string actually _makes * sense_ in order to fall back to treating it as an X font name * if it doesn't. So we check that the font family is actually one * supported by Pango. */ static bool pangofont_check_desc_makes_sense(PangoContext *ctx, PangoFontDescription *desc) { #ifndef PANGO_PRE_1POINT6 PangoFontMap *map; #endif PangoFontFamily **families; int i, nfamilies; bool matched; /* * Ask Pango for a list of font families, and iterate through * them to see if one of them matches the family in the * PangoFontDescription. */ #ifndef PANGO_PRE_1POINT6 map = pango_context_get_font_map(ctx); if (!map) return false; 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, bool wide, bool bold, int shadowoffset, bool 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, pango_context_get_language(ctx)); #else fset = pango_context_load_fontset(ctx, desc, pango_context_get_language(ctx)); #endif if (!fset) { pango_font_description_free(desc); return NULL; } metrics = pango_fontset_get_metrics(fset); if (!metrics || pango_font_metrics_get_approximate_digit_width(metrics) == 0) { pango_font_description_free(desc); g_object_unref(fset); return NULL; } 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_CEIL(pango_font_metrics_get_ascent(metrics)); pfont->u.descent = PANGO_PIXELS_CEIL(pango_font_metrics_get_descent(metrics)); pfont->u.height = pfont->u.ascent + pfont->u.descent; pfont->u.strikethrough_y = PANGO_PIXELS(pango_font_metrics_get_ascent(metrics) - pango_font_metrics_get_strikethrough_position(metrics)); pfont->u.want_fallback = false; #ifdef DRAW_TEXT_CAIRO pfont->u.preferred_drawtype = DRAWTYPE_CAIRO; #elif defined DRAW_TEXT_GDK pfont->u.preferred_drawtype = DRAWTYPE_GDK; #else #error No drawtype available at all #endif /* 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; pfont->widthcache = NULL; pfont->nwidthcache = 0; pango_font_metrics_unref(metrics); return &pfont->u; } static unifont *pangofont_create(GtkWidget *widget, const char *name, bool wide, bool bold, int shadowoffset, bool 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, bool wide, bool bold, int shadowoffset, bool 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 = container_of(font, struct pangofont, u); pango_font_description_free(pfont->desc); sfree(pfont->widthcache); g_object_unref(pfont->fset); sfree(pfont); } static int pangofont_char_width(PangoLayout *layout, struct pangofont *pfont, wchar_t uchr, const char *utfchr, int utflen) { /* * Here we check whether a character has the same width as the * character cell it'll be drawn in. Because profiling showed that * asking Pango for text sizes was a huge bottleneck when we were * calling it every time we needed to know this, we instead call * it only on characters we don't already know about, and cache * the results. */ if ((unsigned)uchr >= pfont->nwidthcache) { unsigned newsize = ((int)uchr + 0x100) & ~0xFF; pfont->widthcache = sresize(pfont->widthcache, newsize, int); while (pfont->nwidthcache < newsize) pfont->widthcache[pfont->nwidthcache++] = -1; } if (pfont->widthcache[uchr] < 0) { PangoRectangle rect; pango_layout_set_text(layout, utfchr, utflen); pango_layout_get_extents(layout, NULL, &rect); pfont->widthcache[uchr] = rect.width; } return pfont->widthcache[uchr]; } static bool pangofont_has_glyph(unifont *font, wchar_t glyph) { /* Pango implements font fallback, so assume it has everything */ return true; } #ifdef DRAW_TEXT_GDK static void pango_gdk_draw_layout(unifont_drawctx *ctx, gint x, gint y, PangoLayout *layout) { gdk_draw_layout(ctx->u.gdk.target, ctx->u.gdk.gc, x, y, layout); } #endif #ifdef DRAW_TEXT_CAIRO static void pango_cairo_draw_layout(unifont_drawctx *ctx, gint x, gint y, PangoLayout *layout) { cairo_move_to(ctx->u.cairo.cr, x, y); pango_cairo_show_layout(ctx->u.cairo.cr, layout); } #endif static void pangofont_draw_internal(unifont_drawctx *ctx, unifont *font, int x, int y, const wchar_t *string, int len, bool wide, bool bold, int cellwidth, bool combining) { struct pangofont *pfont = container_of(font, struct pangofont, u); PangoLayout *layout; PangoRectangle rect; char *utfstring, *utfptr; int utflen; bool shadowbold = false; void (*draw_layout)(unifont_drawctx *ctx, gint x, gint y, PangoLayout *layout) = NULL; #ifdef DRAW_TEXT_GDK if (ctx->type == DRAWTYPE_GDK) { draw_layout = pango_gdk_draw_layout; } #endif #ifdef DRAW_TEXT_CAIRO if (ctx->type == DRAWTYPE_CAIRO) { draw_layout = pango_cairo_draw_layout; } #endif assert(draw_layout); 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); utfptr = utfstring; while (utflen > 0) { int clen, n; int desired = cellwidth * PANGO_SCALE; /* * 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 * properly fixed-width, we can draw many characters in one * go which is much faster. * * This still isn't really ideal. If you look at what * happens in the X protocol as a result of all of this, you * find - naturally enough - that each call to * gdk_draw_layout() generates a separate set of X RENDER * operations involving creating a picture, setting a clip * rectangle, doing some drawing and undoing the whole lot. * In an ideal world, we should _always_ be able to turn the * contents of this loop into a single RenderCompositeGlyphs * operation which internally specifies inter-character * deltas to get the spacing right, which would give us full * speed _even_ in the worst case of a non-fixed-width font. * However, Pango's architecture and documentation are so * unhelpful that I have no idea how if at all to persuade * them to do that. */ if (combining) { /* * For a character with combining stuff, we just dump the * whole lot in one go, and expect it to take up just one * character cell. */ clen = utflen; n = 1; } else { /* * 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 (is_rtl(string[0]) || pangofont_char_width(layout, pfont, string[n-1], utfptr, clen) != desired) { /* * If this character is a right-to-left one, or has an * unusual width, then we must display it on its own. */ } else { /* * Try to amalgamate a contiguous string of characters * with the expected sensible width, for the common case * in which we're using a monospaced font and everything * works as expected. */ 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++; if (is_rtl(string[n-1]) || pangofont_char_width(layout, pfont, string[n-1], utfptr + oldclen, clen - oldclen) != desired) { clen = oldclen; n--; break; } } } } pango_layout_set_text(layout, utfptr, clen); pango_layout_get_pixel_extents(layout, NULL, &rect); draw_layout(ctx, x + (n*cellwidth - rect.width)/2, y + (pfont->u.height - rect.height)/2, layout); if (shadowbold) draw_layout(ctx, 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); } static void pangofont_draw_text(unifont_drawctx *ctx, unifont *font, int x, int y, const wchar_t *string, int len, bool wide, bool bold, int cellwidth) { pangofont_draw_internal(ctx, font, x, y, string, len, wide, bold, cellwidth, false); } static void pangofont_draw_combining(unifont_drawctx *ctx, unifont *font, int x, int y, const wchar_t *string, int len, bool wide, bool bold, int cellwidth) { wchar_t *tmpstring = NULL; if (mk_wcwidth(string[0]) == 0) { /* * If we've been told to draw a sequence of _only_ combining * characters, prefix a space so that they have something to * combine with. */ tmpstring = snewn(len+1, wchar_t); memcpy(tmpstring+1, string, len * sizeof(wchar_t)); tmpstring[0] = L' '; string = tmpstring; len++; } pangofont_draw_internal(ctx, font, x, y, string, len, wide, bold, cellwidth, true); sfree(tmpstring); } /* * Dummy size value to be used when converting a * PangoFontDescription of a scalable font to a string for * internal use. */ #define PANGO_DUMMY_SIZE 12 static void pangofont_enum_fonts(GtkWidget *widget, fontsel_add_entry callback, void *callback_ctx) { PangoContext *ctx; #ifndef PANGO_PRE_1POINT6 PangoFontMap *map; #endif PangoFontFamily **families; int i, nfamilies; ctx = gtk_widget_get_pango_context(widget); if (!ctx) return; /* * Ask Pango for a list of font families, and iterate through * them. */ #ifndef PANGO_PRE_1POINT6 map = pango_context_get_font_map(ctx); if (!map) return; pango_font_map_list_families(map, &families, &nfamilies); #else pango_context_list_families(ctx, &families, &nfamilies); #endif for (i = 0; i < nfamilies; i++) { PangoFontFamily *family = families[i]; const char *familyname; int flags; PangoFontFace **faces; int j, nfaces; /* * Set up our flags for this font family, and get the name * string. */ flags = FONTFLAG_CLIENTSIDE; #ifndef PANGO_PRE_1POINT4 /* * In very early versions of Pango, we can't tell * monospaced fonts from non-monospaced. */ if (!pango_font_family_is_monospace(family)) flags |= FONTFLAG_NONMONOSPACED; #endif familyname = pango_font_family_get_name(family); /* * Go through the available font faces in this family. */ pango_font_family_list_faces(family, &faces, &nfaces); for (j = 0; j < nfaces; j++) { PangoFontFace *face = faces[j]; PangoFontDescription *desc; const char *facename; int *sizes; int k, nsizes, dummysize; /* * Get the face name string. */ facename = pango_font_face_get_face_name(face); /* * Set up a font description with what we've got so * far. We'll fill in the size field manually and then * call pango_font_description_to_string() to give the * full real name of the specific font. */ desc = pango_font_face_describe(face); /* * See if this font has a list of specific sizes. */ #ifndef PANGO_PRE_1POINT4 pango_font_face_list_sizes(face, &sizes, &nsizes); #else /* * In early versions of Pango, that call wasn't * supported; we just have to assume everything is * scalable. */ sizes = NULL; #endif if (!sizes) { /* * Write a single entry with a dummy size. */ dummysize = PANGO_DUMMY_SIZE * PANGO_SCALE; sizes = &dummysize; nsizes = 1; } /* * If so, go through them one by one. */ for (k = 0; k < nsizes; k++) { char *fullname, *stylekey; pango_font_description_set_size(desc, sizes[k]); fullname = pango_font_description_to_string(desc); /* * Construct the sorting key for font styles. */ { strbuf *buf = strbuf_new(); int weight = pango_font_description_get_weight(desc); /* Weight: normal, then lighter, then bolder */ if (weight <= PANGO_WEIGHT_NORMAL) weight = PANGO_WEIGHT_NORMAL - weight; strbuf_catf(buf, "%4d", weight); strbuf_catf(buf, " %2d", pango_font_description_get_style(desc)); int stretch = pango_font_description_get_stretch(desc); /* Stretch: closer to normal sorts earlier */ stretch = 2 * abs(PANGO_STRETCH_NORMAL - stretch) + (stretch < PANGO_STRETCH_NORMAL); strbuf_catf(buf, " %2d", stretch); strbuf_catf(buf, " %2d", pango_font_description_get_variant(desc)); stylekey = strbuf_to_str(buf); } /* * Got everything. Hand off to the callback. * (The charset string is NULL, because only * server-side X fonts use it.) */ callback(callback_ctx, fullname, familyname, NULL, facename, stylekey, (sizes == &dummysize ? 0 : PANGO_PIXELS(sizes[k])), flags, &pangofont_vtable); sfree(stylekey); g_free(fullname); } if (sizes != &dummysize) g_free(sizes); pango_font_description_free(desc); } g_free(faces); } g_free(families); } static char *pangofont_canonify_fontname(GtkWidget *widget, const char *name, int *size, int *flags, bool resolve_aliases) { /* * When given a Pango font name to try to make sense of for a * font selector, we must normalise it to PANGO_DUMMY_SIZE and * extract its original size (in pixels) into the `size' field. */ PangoContext *ctx; #ifndef PANGO_PRE_1POINT6 PangoFontMap *map; #endif PangoFontDescription *desc; PangoFontset *fset; PangoFontMetrics *metrics; char *newname, *retname; 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; } #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, pango_context_get_language(ctx)); #else fset = pango_context_load_fontset(ctx, desc, pango_context_get_language(ctx)); #endif if (!fset) { pango_font_description_free(desc); return NULL; } metrics = pango_fontset_get_metrics(fset); if (!metrics || pango_font_metrics_get_approximate_digit_width(metrics) == 0) { pango_font_description_free(desc); g_object_unref(fset); return NULL; } *size = PANGO_PIXELS(pango_font_description_get_size(desc)); *flags = FONTFLAG_CLIENTSIDE; pango_font_description_set_size(desc, PANGO_DUMMY_SIZE * PANGO_SCALE); newname = pango_font_description_to_string(desc); retname = dupstr(newname); g_free(newname); pango_font_metrics_unref(metrics); pango_font_description_free(desc); g_object_unref(fset); return retname; } static char *pangofont_scale_fontname(GtkWidget *widget, const char *name, int size) { PangoFontDescription *desc; char *newname, *retname; desc = pango_font_description_from_string(name); if (!desc) return NULL; pango_font_description_set_size(desc, size * PANGO_SCALE); newname = pango_font_description_to_string(desc); retname = dupstr(newname); g_free(newname); pango_font_description_free(desc); return retname; } static char *pangofont_size_increment(unifont *font, int increment) { struct pangofont *pfont = container_of(font, struct pangofont, u); PangoFontDescription *desc; int size; char *newname, *retname; desc = pango_font_description_copy_static(pfont->desc); size = pango_font_description_get_size(desc); size += PANGO_SCALE * increment; if (size <= 0) { retname = NULL; } else { pango_font_description_set_size(desc, size); newname = pango_font_description_to_string(desc); retname = dupcat(pfont->u.vt->prefix, ":", newname); g_free(newname); } pango_font_description_free(desc); return retname; } #endif /* GTK_CHECK_VERSION(2,0,0) */ /* ---------------------------------------------------------------------- * Outermost functions which do the vtable dispatch. */ /* * 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 UnifontVtable *unifont_types[] = { #if GTK_CHECK_VERSION(2,0,0) &pangofont_vtable, #endif #ifndef NOT_X_WINDOWS &x11font_vtable, #endif }; /* * Function which takes a font name and processes the optional * scheme prefix. Returns the tail of the font name suitable for * passing to individual font scheme functions, and also provides * a subrange of the unifont_types[] array above. * * The return values `start' and `end' denote a half-open interval * in unifont_types[]; that is, the correct way to iterate over * them is * * for (i = start; i < end; i++) {...} */ static const char *unifont_do_prefix(const char *name, int *start, int *end) { int colonpos = strcspn(name, ":"); int i; if (name[colonpos]) { /* * There's a colon prefix on the font name. Use it to work * out which subclass to use. */ for (i = 0; i < lenof(unifont_types); i++) { if (strlen(unifont_types[i]->prefix) == colonpos && !strncmp(unifont_types[i]->prefix, name, colonpos)) { *start = i; *end = i+1; return name + colonpos + 1; } } /* * None matched, so return an empty scheme list to prevent * any scheme from being called at all. */ *start = *end = 0; return name + colonpos + 1; } else { /* * No colon prefix, so just use all the subclasses. */ *start = 0; *end = lenof(unifont_types); return name; } } unifont *unifont_create(GtkWidget *widget, const char *name, bool wide, bool bold, int shadowoffset, bool shadowalways) { int i, start, end; name = unifont_do_prefix(name, &start, &end); for (i = start; i < end; i++) { unifont *ret = unifont_types[i]->create(widget, name, wide, bold, shadowoffset, shadowalways); if (ret) return ret; } return NULL; /* font not found in any scheme */ } void unifont_destroy(unifont *font) { font->vt->destroy(font); } void unifont_draw_text(unifont_drawctx *ctx, unifont *font, int x, int y, const wchar_t *string, int len, bool wide, bool bold, int cellwidth) { font->vt->draw_text(ctx, font, x, y, string, len, wide, bold, cellwidth); } void unifont_draw_combining(unifont_drawctx *ctx, unifont *font, int x, int y, const wchar_t *string, int len, bool wide, bool bold, int cellwidth) { font->vt->draw_combining(ctx, font, x, y, string, len, wide, bold, cellwidth); } char *unifont_size_increment(unifont *font, int increment) { return font->vt->size_increment(font, increment); } /* ---------------------------------------------------------------------- * 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(unifont_drawctx *ctx, unifont *font, int x, int y, const wchar_t *string, int len, bool wide, bool bold, int cellwidth); static void multifont_draw_combining(unifont_drawctx *ctx, unifont *font, int x, int y, const wchar_t *string, int len, bool wide, bool bold, int cellwidth); static void multifont_destroy(unifont *font); static char *multifont_size_increment(unifont *font, int increment); struct multifont { unifont *main; unifont *fallback; struct unifont u; }; static const UnifontVtable multifont_vtable = { .create = NULL, /* creation is done specially */ .create_fallback = NULL, .destroy = multifont_destroy, .has_glyph = NULL, .draw_text = multifont_draw_text, .draw_combining = multifont_draw_combining, .enum_fonts = NULL, .canonify_fontname = NULL, .scale_fontname = NULL, .size_increment = multifont_size_increment, .prefix = "client", }; unifont *multifont_create(GtkWidget *widget, const char *name, bool wide, bool bold, int shadowoffset, bool 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.strikethrough_y = font->strikethrough_y; mfont->u.public_charset = font->public_charset; mfont->u.want_fallback = false; /* shouldn't be needed, but just in case */ mfont->u.preferred_drawtype = font->preferred_drawtype; mfont->main = font; mfont->fallback = fallback; return &mfont->u; } static void multifont_destroy(unifont *font) { struct multifont *mfont = container_of(font, struct multifont, u); unifont_destroy(mfont->main); if (mfont->fallback) unifont_destroy(mfont->fallback); sfree(mfont); } typedef void (*unifont_draw_func_t)(unifont_drawctx *ctx, unifont *font, int x, int y, const wchar_t *string, int len, bool wide, bool bold, int cellwidth); static void multifont_draw_main(unifont_drawctx *ctx, unifont *font, int x, int y, const wchar_t *string, int len, bool wide, bool bold, int cellwidth, int cellinc, unifont_draw_func_t draw) { struct multifont *mfont = container_of(font, struct multifont, u); unifont *f; bool ok; int 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. */ f = ok ? mfont->main : mfont->fallback; if (f) draw(ctx, f, x, y, string, i, wide, bold, cellwidth); string += i; len -= i; x += i * cellinc; } } static void multifont_draw_text(unifont_drawctx *ctx, unifont *font, int x, int y, const wchar_t *string, int len, bool wide, bool bold, int cellwidth) { multifont_draw_main(ctx, font, x, y, string, len, wide, bold, cellwidth, cellwidth, unifont_draw_text); } static void multifont_draw_combining(unifont_drawctx *ctx, unifont *font, int x, int y, const wchar_t *string, int len, bool wide, bool bold, int cellwidth) { multifont_draw_main(ctx, font, x, y, string, len, wide, bold, cellwidth, 0, unifont_draw_combining); } static char *multifont_size_increment(unifont *font, int increment) { struct multifont *mfont = container_of(font, struct multifont, u); return unifont_size_increment(mfont->main, increment); } #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. */ typedef struct fontinfo fontinfo; typedef struct unifontsel_internal { GtkListStore *family_model, *style_model, *size_model; GtkWidget *family_list, *style_list, *size_entry, *size_list; GtkWidget *filter_buttons[4]; int n_filter_buttons; GtkWidget *preview_area; #ifndef NO_BACKING_PIXMAPS GdkPixmap *preview_pixmap; #endif int preview_width, preview_height; GdkColor preview_fg, preview_bg; int filter_flags; tree234 *fonts_by_realname, *fonts_by_selorder; fontinfo *selected; int selsize, intendedsize; bool inhibit_response; /* inhibit callbacks when we change GUI controls */ unifontsel u; } unifontsel_internal; /* * The structure held in the tree234s. All the string members are * part of the same allocated area, so don't need freeing * separately. */ struct fontinfo { char *realname; char *family, *charset, *style, *stylekey; int size, flags; /* * Fallback sorting key, to permit multiple identical entries * to exist in the selorder tree. */ int index; /* * Indices mapping fontinfo structures to indices in the list * boxes. sizeindex is irrelevant if the font is scalable * (size==0). */ int familyindex, styleindex, sizeindex; /* * The class of font. */ const struct UnifontVtable *fontclass; }; struct fontinfo_realname_find { const char *realname; int flags; }; static int strnullcasecmp(const char *a, const char *b) { int i; /* * If exactly one of the inputs is NULL, it compares before * the other one. */ if ((i = (!b) - (!a)) != 0) return i; /* * NULL compares equal. */ 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; if ((i = strnullcasecmp(a->realname, b->realname)) != 0) return i; if ((a->flags & FONTFLAG_SORT_MASK) != (b->flags & FONTFLAG_SORT_MASK)) return ((a->flags & FONTFLAG_SORT_MASK) < (b->flags & FONTFLAG_SORT_MASK) ? -1 : +1); return 0; } static int fontinfo_realname_find(void *av, void *bv) { struct fontinfo_realname_find *a = (struct fontinfo_realname_find *)av; fontinfo *b = (fontinfo *)bv; int i; if ((i = strnullcasecmp(a->realname, b->realname)) != 0) return i; if ((a->flags & FONTFLAG_SORT_MASK) != (b->flags & FONTFLAG_SORT_MASK)) return ((a->flags & FONTFLAG_SORT_MASK) < (b->flags & FONTFLAG_SORT_MASK) ? -1 : +1); return 0; } static int fontinfo_selorder_compare(void *av, void *bv) { fontinfo *a = (fontinfo *)av; fontinfo *b = (fontinfo *)bv; int i; if ((i = strnullcasecmp(a->family, b->family)) != 0) return i; /* * Font class comes immediately after family, so that fonts * from different classes with the same family */ if ((a->flags & FONTFLAG_SORT_MASK) != (b->flags & FONTFLAG_SORT_MASK)) return ((a->flags & FONTFLAG_SORT_MASK) < (b->flags & FONTFLAG_SORT_MASK) ? -1 : +1); if ((i = strnullcasecmp(a->charset, b->charset)) != 0) return i; if ((i = strnullcasecmp(a->stylekey, b->stylekey)) != 0) return i; if ((i = strnullcasecmp(a->style, b->style)) != 0) return i; if (a->size != b->size) return (a->size < b->size ? -1 : +1); if (a->index != b->index) return (a->index < b->index ? -1 : +1); return 0; } static void unifontsel_draw_preview_text(unifontsel_internal *fs); static void unifontsel_deselect(unifontsel_internal *fs) { fs->selected = NULL; gtk_list_store_clear(fs->style_model); gtk_list_store_clear(fs->size_model); gtk_widget_set_sensitive(fs->u.ok_button, false); gtk_widget_set_sensitive(fs->size_entry, false); unifontsel_draw_preview_text(fs); } static void unifontsel_setup_familylist(unifontsel_internal *fs) { GtkTreeIter iter; int i, listindex, minpos = -1, maxpos = -1; char *currfamily = NULL; int currflags = -1; fontinfo *info; fs->inhibit_response = true; gtk_list_store_clear(fs->family_model); listindex = 0; /* * Search through the font tree for anything matching our * current filter criteria. When we find one, add its font * name to the list box. */ for (i = 0 ;; i++) { info = (fontinfo *)index234(fs->fonts_by_selorder, i); /* * info may be NULL if we've just run off the end of the * tree. We must still do a processing pass in that * situation, in case we had an unfinished font record in * progress. */ if (info && (info->flags &~ fs->filter_flags)) { info->familyindex = -1; continue; /* we're filtering out this font */ } if (!info || strnullcasecmp(currfamily, info->family) || currflags != (info->flags & FONTFLAG_SORT_MASK)) { /* * We've either finished a family, or started a new * one, or both. */ if (currfamily) { gtk_list_store_append(fs->family_model, &iter); gtk_list_store_set(fs->family_model, &iter, 0, currfamily, 1, minpos, 2, maxpos+1, -1); listindex++; } if (info) { minpos = i; currfamily = info->family; currflags = info->flags & FONTFLAG_SORT_MASK; } } if (!info) break; /* now we're done */ info->familyindex = listindex; maxpos = i; } /* * If we've just filtered out the previously selected font, * deselect it thoroughly. */ if (fs->selected && fs->selected->familyindex < 0) unifontsel_deselect(fs); fs->inhibit_response = false; } static void unifontsel_setup_stylelist(unifontsel_internal *fs, int start, int end) { GtkTreeIter iter; int i, listindex, minpos = -1, maxpos = -1; bool started = false; char *currcs = NULL, *currstyle = NULL; fontinfo *info; gtk_list_store_clear(fs->style_model); listindex = 0; started = false; /* * Search through the font tree for anything matching our * current filter criteria. When we find one, add its charset * and/or style name to the list box. */ for (i = start; i <= end; i++) { if (i == end) info = NULL; else info = (fontinfo *)index234(fs->fonts_by_selorder, i); /* * info may be NULL if we've just run off the end of the * relevant data. We must still do a processing pass in * that situation, in case we had an unfinished font * record in progress. */ if (info && (info->flags &~ fs->filter_flags)) { info->styleindex = -1; continue; /* we're filtering out this font */ } if (!info || !started || strnullcasecmp(currcs, info->charset) || strnullcasecmp(currstyle, info->style)) { /* * We've either finished a style/charset, or started a * new one, or both. */ started = true; if (currstyle) { gtk_list_store_append(fs->style_model, &iter); gtk_list_store_set(fs->style_model, &iter, 0, currstyle, 1, minpos, 2, maxpos+1, 3, true, 4, PANGO_WEIGHT_NORMAL, -1); listindex++; } if (info) { minpos = i; if (info->charset && strnullcasecmp(currcs, info->charset)) { gtk_list_store_append(fs->style_model, &iter); gtk_list_store_set(fs->style_model, &iter, 0, info->charset, 1, -1, 2, -1, 3, false, 4, PANGO_WEIGHT_BOLD, -1); listindex++; } currcs = info->charset; currstyle = info->style; } } if (!info) break; /* now we're done */ info->styleindex = listindex; maxpos = i; } } static const int unifontsel_default_sizes[] = { 10, 12, 14, 16, 20, 24, 32 }; static void unifontsel_setup_sizelist(unifontsel_internal *fs, int start, int end) { GtkTreeIter iter; int i, listindex; char sizetext[40]; fontinfo *info; gtk_list_store_clear(fs->size_model); listindex = 0; /* * Search through the font tree for anything matching our * current filter criteria. When we find one, add its font * name to the list box. */ for (i = start; i < end; i++) { info = (fontinfo *)index234(fs->fonts_by_selorder, i); if (!info) { /* _shouldn't_ happen unless font list is completely funted */ break; } if (info->flags &~ fs->filter_flags) { info->sizeindex = -1; continue; /* we're filtering out this font */ } if (info->size) { sprintf(sizetext, "%d", info->size); info->sizeindex = listindex; gtk_list_store_append(fs->size_model, &iter); gtk_list_store_set(fs->size_model, &iter, 0, sizetext, 1, i, 2, info->size, -1); listindex++; } else { int j; assert(i == start); assert(i+1 == end); for (j = 0; j < lenof(unifontsel_default_sizes); j++) { sprintf(sizetext, "%d", unifontsel_default_sizes[j]); gtk_list_store_append(fs->size_model, &iter); gtk_list_store_set(fs->size_model, &iter, 0, sizetext, 1, i, 2, unifontsel_default_sizes[j], -1); listindex++; } } } } static void unifontsel_set_filter_buttons(unifontsel_internal *fs) { int i; for (i = 0; i < fs->n_filter_buttons; i++) { int flagbit = GPOINTER_TO_INT(g_object_get_data (G_OBJECT(fs->filter_buttons[i]), "user-data")); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(fs->filter_buttons[i]), !!(fs->filter_flags & flagbit)); } } static void unifontsel_draw_preview_text_inner(unifont_drawctx *dctx, unifontsel_internal *fs) { unifont *font; char *sizename = NULL; fontinfo *info = fs->selected; if (info) { sizename = info->fontclass->scale_fontname (GTK_WIDGET(fs->u.window), info->realname, fs->selsize); font = info->fontclass->create(GTK_WIDGET(fs->u.window), sizename ? sizename : info->realname, false, false, 0, 0); } else font = NULL; #ifdef DRAW_TEXT_GDK if (dctx->type == DRAWTYPE_GDK) { gdk_gc_set_foreground(dctx->u.gdk.gc, &fs->preview_bg); gdk_draw_rectangle(dctx->u.gdk.target, dctx->u.gdk.gc, 1, 0, 0, fs->preview_width, fs->preview_height); gdk_gc_set_foreground(dctx->u.gdk.gc, &fs->preview_fg); } #endif #ifdef DRAW_TEXT_CAIRO if (dctx->type == DRAWTYPE_CAIRO) { cairo_set_source_rgb(dctx->u.cairo.cr, fs->preview_bg.red / 65535.0, fs->preview_bg.green / 65535.0, fs->preview_bg.blue / 65535.0); cairo_paint(dctx->u.cairo.cr); cairo_set_source_rgb(dctx->u.cairo.cr, fs->preview_fg.red / 65535.0, fs->preview_fg.green / 65535.0, fs->preview_fg.blue / 65535.0); } #endif if (font) { /* * The pangram used here is rather carefully * constructed: it contains a sequence of very narrow * letters (`jil') and a pair of adjacent very wide * letters (`wm'). * * If the user selects a proportional font, it will be * coerced into fixed-width character cells when used * in the actual terminal window. We therefore display * it the same way in the preview pane, so as to show * it the way it will actually be displayed - and we * deliberately pick a pangram which will show the * resulting miskerning at its worst. * * We aren't trying to sell people these fonts; we're * trying to let them make an informed choice. Better * 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(dctx, font, 0, font->ascent, L"bankrupt jilted showmen quiz convex fogey", 41, false, false, font->width); info->fontclass->draw_text(dctx, 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(dctx, font, 0, font->ascent + font->height * 2, L"0123456789!?,.:;<>()[]{}\\/`'\"+*-=~#_@|%&^$", 42, false, false, font->width); info->fontclass->destroy(font); } sfree(sizename); } static void unifontsel_draw_preview_text(unifontsel_internal *fs) { unifont_drawctx dctx; GdkWindow *target; #ifndef NO_BACKING_PIXMAPS target = fs->preview_pixmap; #else target = gtk_widget_get_window(fs->preview_area); #endif if (!target) /* we may be called when we haven't created everything yet */ return; dctx.type = DRAWTYPE_DEFAULT; #ifdef DRAW_TEXT_GDK if (dctx.type == DRAWTYPE_GDK) { dctx.u.gdk.target = target; dctx.u.gdk.gc = gdk_gc_new(target); } #endif #ifdef DRAW_TEXT_CAIRO if (dctx.type == DRAWTYPE_CAIRO) { #if GTK_CHECK_VERSION(3,22,0) cairo_region_t *region; #endif dctx.u.cairo.widget = GTK_WIDGET(fs->preview_area); #if GTK_CHECK_VERSION(3,22,0) dctx.u.cairo.gdkwin = gtk_widget_get_window(dctx.u.cairo.widget); region = gdk_window_get_clip_region(dctx.u.cairo.gdkwin); dctx.u.cairo.drawctx = gdk_window_begin_draw_frame( dctx.u.cairo.gdkwin, region); dctx.u.cairo.cr = gdk_drawing_context_get_cairo_context( dctx.u.cairo.drawctx); cairo_region_destroy(region); #else dctx.u.cairo.cr = gdk_cairo_create(target); #endif } #endif unifontsel_draw_preview_text_inner(&dctx, fs); #ifdef DRAW_TEXT_GDK if (dctx.type == DRAWTYPE_GDK) { gdk_gc_unref(dctx.u.gdk.gc); } #endif #ifdef DRAW_TEXT_CAIRO if (dctx.type == DRAWTYPE_CAIRO) { #if GTK_CHECK_VERSION(3,22,0) gdk_window_end_draw_frame(dctx.u.cairo.gdkwin, dctx.u.cairo.drawctx); #else cairo_destroy(dctx.u.cairo.cr); #endif } #endif gdk_window_invalidate_rect(gtk_widget_get_window(fs->preview_area), NULL, false); } static void unifontsel_select_font(unifontsel_internal *fs, fontinfo *info, int size, int leftlist, bool size_is_explicit) { int index; int minval, maxval; gboolean success; GtkTreePath *treepath; GtkTreeIter iter; fs->inhibit_response = true; fs->selected = info; fs->selsize = size; if (size_is_explicit) fs->intendedsize = size; gtk_widget_set_sensitive(fs->u.ok_button, true); /* * Find the index of this fontinfo in the selorder list. */ index = -1; findpos234(fs->fonts_by_selorder, info, NULL, &index); assert(index >= 0); /* * Adjust the font selector flags and redo the font family * list box, if necessary. */ if (leftlist <= 0 && (fs->filter_flags | info->flags) != fs->filter_flags) { fs->filter_flags |= info->flags; unifontsel_set_filter_buttons(fs); unifontsel_setup_familylist(fs); } /* * Find the appropriate family name and select it in the list. */ assert(info->familyindex >= 0); treepath = gtk_tree_path_new_from_indices(info->familyindex, -1); gtk_tree_selection_select_path (gtk_tree_view_get_selection(GTK_TREE_VIEW(fs->family_list)), treepath); gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(fs->family_list), treepath, NULL, false, 0.0, 0.0); success = gtk_tree_model_get_iter(GTK_TREE_MODEL(fs->family_model), &iter, treepath); assert(success); gtk_tree_path_free(treepath); /* * Now set up the font style list. */ gtk_tree_model_get(GTK_TREE_MODEL(fs->family_model), &iter, 1, &minval, 2, &maxval, -1); if (leftlist <= 1) unifontsel_setup_stylelist(fs, minval, maxval); /* * Find the appropriate style name and select it in the list. */ if (info->style) { assert(info->styleindex >= 0); treepath = gtk_tree_path_new_from_indices(info->styleindex, -1); gtk_tree_selection_select_path (gtk_tree_view_get_selection(GTK_TREE_VIEW(fs->style_list)), treepath); gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(fs->style_list), treepath, NULL, false, 0.0, 0.0); gtk_tree_model_get_iter(GTK_TREE_MODEL(fs->style_model), &iter, treepath); gtk_tree_path_free(treepath); /* * And set up the size list. */ gtk_tree_model_get(GTK_TREE_MODEL(fs->style_model), &iter, 1, &minval, 2, &maxval, -1); if (leftlist <= 2) unifontsel_setup_sizelist(fs, minval, maxval); /* * Find the appropriate size, and select it in the list. */ if (info->size) { assert(info->sizeindex >= 0); treepath = gtk_tree_path_new_from_indices(info->sizeindex, -1); gtk_tree_selection_select_path (gtk_tree_view_get_selection(GTK_TREE_VIEW(fs->size_list)), treepath); gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(fs->size_list), treepath, NULL, false, 0.0, 0.0); gtk_tree_path_free(treepath); size = info->size; } else { int j; for (j = 0; j < lenof(unifontsel_default_sizes); j++) if (unifontsel_default_sizes[j] == size) { treepath = gtk_tree_path_new_from_indices(j, -1); gtk_tree_view_set_cursor(GTK_TREE_VIEW(fs->size_list), treepath, NULL, false); gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(fs->size_list), treepath, NULL, false, 0.0, 0.0); gtk_tree_path_free(treepath); } } /* * And set up the font size text entry box. */ { char sizetext[40]; sprintf(sizetext, "%d", size); gtk_entry_set_text(GTK_ENTRY(fs->size_entry), sizetext); } } else { if (leftlist <= 2) unifontsel_setup_sizelist(fs, 0, 0); gtk_entry_set_text(GTK_ENTRY(fs->size_entry), ""); } /* * Grey out the font size edit box if we're not using a * scalable font. */ gtk_editable_set_editable(GTK_EDITABLE(fs->size_entry), fs->selected->size == 0); gtk_widget_set_sensitive(fs->size_entry, fs->selected->size == 0); unifontsel_draw_preview_text(fs); fs->inhibit_response = false; } static void unifontsel_button_toggled(GtkToggleButton *tb, gpointer data) { unifontsel_internal *fs = (unifontsel_internal *)data; bool newstate = gtk_toggle_button_get_active(tb); int newflags; int flagbit = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(tb), "user-data")); if (newstate) newflags = fs->filter_flags | flagbit; else newflags = fs->filter_flags & ~flagbit; if (fs->filter_flags != newflags) { fs->filter_flags = newflags; unifontsel_setup_familylist(fs); } } static void unifontsel_add_entry(void *ctx, const char *realfontname, const char *family, const char *charset, const char *style, const char *stylekey, int size, int flags, const struct UnifontVtable *fontclass) { unifontsel_internal *fs = (unifontsel_internal *)ctx; fontinfo *info; int totalsize; char *p; totalsize = sizeof(fontinfo) + strlen(realfontname) + (family ? strlen(family) : 0) + (charset ? strlen(charset) : 0) + (style ? strlen(style) : 0) + (stylekey ? strlen(stylekey) : 0) + 10; info = (fontinfo *)smalloc(totalsize); info->fontclass = fontclass; p = (char *)info + sizeof(fontinfo); info->realname = p; strcpy(p, realfontname); p += 1+strlen(p); if (family) { info->family = p; strcpy(p, family); p += 1+strlen(p); } else info->family = NULL; if (charset) { info->charset = p; strcpy(p, charset); p += 1+strlen(p); } else info->charset = NULL; if (style) { info->style = p; strcpy(p, style); p += 1+strlen(p); } else info->style = NULL; if (stylekey) { info->stylekey = p; strcpy(p, stylekey); p += 1+strlen(p); } else info->stylekey = NULL; assert(p - (char *)info <= totalsize); info->size = size; info->flags = flags; info->index = count234(fs->fonts_by_selorder); /* * It's just conceivable that a misbehaving font enumerator * might tell us about the same font real name more than once, * in which case we should silently drop the new one. */ if (add234(fs->fonts_by_realname, info) != info) { sfree(info); return; } /* * However, we should never get a duplicate key in the * selorder tree, because the index field carefully * disambiguates otherwise identical records. */ add234(fs->fonts_by_selorder, info); } static fontinfo *update_for_intended_size(unifontsel_internal *fs, fontinfo *info) { fontinfo info2, *below, *above; int pos; /* * Copy the info structure. This doesn't copy its dynamic * string fields, but that's unimportant because all we're * going to do is to adjust the size field and use it in one * tree search. */ info2 = *info; info2.size = fs->intendedsize; /* * 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 * (and the disambiguating index field). Bear in mind, also, * that either one might _already_ be NULL if we're at the * extreme ends of the font list. */ if (below) { info2.size = below->size; info2.index = below->index; if (fontinfo_selorder_compare(&info2, below)) below = NULL; } if (above) { info2.size = above->size; info2.index = above->index; if (fontinfo_selorder_compare(&info2, above)) above = NULL; } /* * Now return whichever of above and below is non-NULL, if * that's unambiguous. */ if (!above) return below; if (!below) return above; /* * And now we really do have to make a choice about whether to * round up or down. We'll do it by rounding to nearest, * breaking ties by rounding up. */ if (above->size - fs->intendedsize <= fs->intendedsize - below->size) return above; else return below; } static void family_changed(GtkTreeSelection *treeselection, gpointer data) { unifontsel_internal *fs = (unifontsel_internal *)data; GtkTreeModel *treemodel; GtkTreeIter treeiter; int minval; fontinfo *info; if (fs->inhibit_response) /* we made this change ourselves */ return; if (!gtk_tree_selection_get_selected(treeselection, &treemodel, &treeiter)) return; gtk_tree_model_get(treemodel, &treeiter, 1, &minval, -1); info = (fontinfo *)index234(fs->fonts_by_selorder, minval); if (!info) return; /* _shouldn't_ happen unless font list is completely funted */ info = update_for_intended_size(fs, info); if (!info) return; /* similarly shouldn't happen */ if (!info->size) fs->selsize = fs->intendedsize; /* font is scalable */ unifontsel_select_font(fs, info, info->size ? info->size : fs->selsize, 1, false); } static void style_changed(GtkTreeSelection *treeselection, gpointer data) { unifontsel_internal *fs = (unifontsel_internal *)data; GtkTreeModel *treemodel; GtkTreeIter treeiter; int minval; fontinfo *info; if (fs->inhibit_response) /* we made this change ourselves */ return; if (!gtk_tree_selection_get_selected(treeselection, &treemodel, &treeiter)) return; gtk_tree_model_get(treemodel, &treeiter, 1, &minval, -1); if (minval < 0) return; /* somehow a charset heading got clicked */ info = (fontinfo *)index234(fs->fonts_by_selorder, minval); if (!info) return; /* _shouldn't_ happen unless font list is completely funted */ info = update_for_intended_size(fs, info); if (!info) return; /* similarly shouldn't happen */ if (!info->size) fs->selsize = fs->intendedsize; /* font is scalable */ unifontsel_select_font(fs, info, info->size ? info->size : fs->selsize, 2, false); } static void size_changed(GtkTreeSelection *treeselection, gpointer data) { unifontsel_internal *fs = (unifontsel_internal *)data; GtkTreeModel *treemodel; GtkTreeIter treeiter; int minval, size; fontinfo *info; if (fs->inhibit_response) /* we made this change ourselves */ return; if (!gtk_tree_selection_get_selected(treeselection, &treemodel, &treeiter)) return; gtk_tree_model_get(treemodel, &treeiter, 1, &minval, 2, &size, -1); info = (fontinfo *)index234(fs->fonts_by_selorder, minval); if (!info) return; /* _shouldn't_ happen unless font list is completely funted */ unifontsel_select_font(fs, info, info->size ? info->size : size, 3, true); } static void size_entry_changed(GtkEditable *ed, gpointer data) { unifontsel_internal *fs = (unifontsel_internal *)data; const char *text; int size; if (fs->inhibit_response) /* we made this change ourselves */ return; text = gtk_entry_get_text(GTK_ENTRY(ed)); size = atoi(text); if (size > 0) { assert(fs->selected->size == 0); unifontsel_select_font(fs, fs->selected, size, 3, true); } } static void alias_resolve(GtkTreeView *treeview, GtkTreePath *path, GtkTreeViewColumn *column, gpointer data) { unifontsel_internal *fs = (unifontsel_internal *)data; GtkTreeIter iter; int minval, newsize; fontinfo *info, *newinfo; char *newname; if (fs->inhibit_response) /* we made this change ourselves */ return; gtk_tree_model_get_iter(GTK_TREE_MODEL(fs->family_model), &iter, path); gtk_tree_model_get(GTK_TREE_MODEL(fs->family_model), &iter, 1,&minval, -1); info = (fontinfo *)index234(fs->fonts_by_selorder, minval); if (info) { int flags; struct fontinfo_realname_find f; newname = info->fontclass->canonify_fontname (GTK_WIDGET(fs->u.window), info->realname, &newsize, &flags, true); f.realname = newname; f.flags = flags; newinfo = find234(fs->fonts_by_realname, &f, fontinfo_realname_find); sfree(newname); if (!newinfo) return; /* font name not in our index */ if (newinfo == info) return; /* didn't change under canonification => not an alias */ unifontsel_select_font(fs, newinfo, newinfo->size ? newinfo->size : newsize, 1, true); } } #if GTK_CHECK_VERSION(3,0,0) static gint unifontsel_draw_area(GtkWidget *widget, cairo_t *cr, gpointer data) { unifontsel_internal *fs = (unifontsel_internal *)data; unifont_drawctx dctx; dctx.type = DRAWTYPE_CAIRO; dctx.u.cairo.widget = widget; dctx.u.cairo.cr = cr; unifontsel_draw_preview_text_inner(&dctx, fs); return true; } #else static gint unifontsel_expose_area(GtkWidget *widget, GdkEventExpose *event, gpointer data) { unifontsel_internal *fs = (unifontsel_internal *)data; #ifndef NO_BACKING_PIXMAPS if (fs->preview_pixmap) { gdk_draw_pixmap(gtk_widget_get_window(widget), (gtk_widget_get_style(widget)->fg_gc [gtk_widget_get_state(widget)]), fs->preview_pixmap, event->area.x, event->area.y, event->area.x, event->area.y, event->area.width, event->area.height); } #else unifontsel_draw_preview_text(fs); #endif return true; } #endif static gint unifontsel_configure_area(GtkWidget *widget, GdkEventConfigure *event, gpointer data) { #ifndef NO_BACKING_PIXMAPS unifontsel_internal *fs = (unifontsel_internal *)data; int ox, oy, nx, ny, x, y; /* * Enlarge the pixmap, but never shrink it. */ ox = fs->preview_width; oy = fs->preview_height; x = event->width; y = event->height; if (x > ox || y > oy) { if (fs->preview_pixmap) gdk_pixmap_unref(fs->preview_pixmap); nx = (x > ox ? x : ox); ny = (y > oy ? y : oy); fs->preview_pixmap = gdk_pixmap_new(gtk_widget_get_window(widget), nx, ny, -1); fs->preview_width = nx; fs->preview_height = ny; unifontsel_draw_preview_text(fs); } #endif gdk_window_invalidate_rect(gtk_widget_get_window(widget), NULL, false); return true; } unifontsel *unifontsel_new(const char *wintitle) { unifontsel_internal *fs = snew(unifontsel_internal); GtkWidget *table, *label, *w, *ww, *scroll; GtkListStore *model; GtkTreeViewColumn *column; int lists_height, preview_height, font_width, style_width, size_width; int i; fs->inhibit_response = false; fs->selected = NULL; { int width, height; /* * Invent some magic size constants. */ get_label_text_dimensions("Quite Long Font Name (Foundry)", &width, &height); font_width = width; lists_height = 14 * height; preview_height = 5 * height; get_label_text_dimensions("Italic Extra Condensed", &width, &height); style_width = width; get_label_text_dimensions("48000", &width, &height); size_width = width; } /* * Create the dialog box and initialise the user-visible * fields in the returned structure. */ fs->u.user_data = NULL; fs->u.window = GTK_WINDOW(gtk_dialog_new()); gtk_window_set_title(fs->u.window, wintitle); fs->u.cancel_button = gtk_dialog_add_button (GTK_DIALOG(fs->u.window), STANDARD_CANCEL_LABEL, GTK_RESPONSE_CANCEL); fs->u.ok_button = gtk_dialog_add_button (GTK_DIALOG(fs->u.window), STANDARD_OK_LABEL, GTK_RESPONSE_OK); gtk_widget_grab_default(fs->u.ok_button); /* * Now set up the internal fields, including in particular all * the controls that actually allow the user to select fonts. */ #if GTK_CHECK_VERSION(3,0,0) table = gtk_grid_new(); gtk_grid_set_column_spacing(GTK_GRID(table), 8); #else table = gtk_table_new(8, 3, false); gtk_table_set_col_spacings(GTK_TABLE(table), 8); #endif gtk_widget_show(table); #if GTK_CHECK_VERSION(3,0,0) /* GtkAlignment has become deprecated and we use the "margin" * property */ w = table; g_object_set(G_OBJECT(w), "margin", 8, (const char *)NULL); #elif GTK_CHECK_VERSION(2,4,0) /* GtkAlignment seems to be the simplest way to put padding round things */ w = gtk_alignment_new(0, 0, 1, 1); gtk_alignment_set_padding(GTK_ALIGNMENT(w), 8, 8, 8, 8); gtk_container_add(GTK_CONTAINER(w), table); gtk_widget_show(w); #else /* In GTK < 2.4, even that isn't available */ w = table; #endif gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area (GTK_DIALOG(fs->u.window))), w, true, true, 0); label = gtk_label_new_with_mnemonic("_Font:"); gtk_widget_show(label); align_label_left(GTK_LABEL(label)); #if GTK_CHECK_VERSION(3,0,0) gtk_grid_attach(GTK_GRID(table), label, 0, 0, 1, 1); g_object_set(G_OBJECT(label), "hexpand", true, (const char *)NULL); #else gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, GTK_FILL, 0, 0, 0); #endif /* * The Font list box displays only a string, but additionally * stores two integers which give the limits within the * tree234 of the font entries covered by this list entry. */ model = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT); w = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model)); gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(w), false); gtk_label_set_mnemonic_widget(GTK_LABEL(label), w); gtk_widget_show(w); column = gtk_tree_view_column_new_with_attributes ("Font", gtk_cell_renderer_text_new(), "text", 0, (char *)NULL); gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE); gtk_tree_view_append_column(GTK_TREE_VIEW(w), column); g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(w))), "changed", G_CALLBACK(family_changed), fs); g_signal_connect(G_OBJECT(w), "row-activated", G_CALLBACK(alias_resolve), fs); scroll = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scroll), GTK_SHADOW_IN); gtk_container_add(GTK_CONTAINER(scroll), w); gtk_widget_show(scroll); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); gtk_widget_set_size_request(scroll, font_width, lists_height); #if GTK_CHECK_VERSION(3,0,0) gtk_grid_attach(GTK_GRID(table), scroll, 0, 1, 1, 2); g_object_set(G_OBJECT(scroll), "expand", true, (const char *)NULL); #else gtk_table_attach(GTK_TABLE(table), scroll, 0, 1, 1, 3, GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); #endif fs->family_model = model; fs->family_list = w; label = gtk_label_new_with_mnemonic("_Style:"); gtk_widget_show(label); align_label_left(GTK_LABEL(label)); #if GTK_CHECK_VERSION(3,0,0) gtk_grid_attach(GTK_GRID(table), label, 1, 0, 1, 1); g_object_set(G_OBJECT(label), "hexpand", true, (const char *)NULL); #else gtk_table_attach(GTK_TABLE(table), label, 1, 2, 0, 1, GTK_FILL, 0, 0, 0); #endif /* * The Style list box can contain insensitive elements (character * set headings for server-side fonts), so we add an extra column * to the list store to hold that information. Also, since GTK3 at * least doesn't seem to display insensitive elements differently * by default, we add a further column to change their style. */ model = gtk_list_store_new(5, G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT, G_TYPE_BOOLEAN, G_TYPE_INT); w = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model)); gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(w), false); gtk_label_set_mnemonic_widget(GTK_LABEL(label), w); gtk_widget_show(w); column = gtk_tree_view_column_new_with_attributes ("Style", gtk_cell_renderer_text_new(), "text", 0, "sensitive", 3, "weight", 4, (char *)NULL); gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE); gtk_tree_view_append_column(GTK_TREE_VIEW(w), column); g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(w))), "changed", G_CALLBACK(style_changed), fs); scroll = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scroll), GTK_SHADOW_IN); gtk_container_add(GTK_CONTAINER(scroll), w); gtk_widget_show(scroll); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); gtk_widget_set_size_request(scroll, style_width, lists_height); #if GTK_CHECK_VERSION(3,0,0) gtk_grid_attach(GTK_GRID(table), scroll, 1, 1, 1, 2); g_object_set(G_OBJECT(scroll), "expand", true, (const char *)NULL); #else gtk_table_attach(GTK_TABLE(table), scroll, 1, 2, 1, 3, GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); #endif fs->style_model = model; fs->style_list = w; label = gtk_label_new_with_mnemonic("Si_ze:"); gtk_widget_show(label); align_label_left(GTK_LABEL(label)); #if GTK_CHECK_VERSION(3,0,0) gtk_grid_attach(GTK_GRID(table), label, 2, 0, 1, 1); g_object_set(G_OBJECT(label), "hexpand", true, (const char *)NULL); #else gtk_table_attach(GTK_TABLE(table), label, 2, 3, 0, 1, GTK_FILL, 0, 0, 0); #endif /* * The Size label attaches primarily to a text input box so * that the user can select a size of their choice. The list * of available sizes is secondary. */ fs->size_entry = w = gtk_entry_new(); gtk_label_set_mnemonic_widget(GTK_LABEL(label), w); gtk_widget_set_size_request(w, size_width, -1); gtk_widget_show(w); #if GTK_CHECK_VERSION(3,0,0) gtk_grid_attach(GTK_GRID(table), w, 2, 1, 1, 1); g_object_set(G_OBJECT(w), "hexpand", true, (const char *)NULL); #else gtk_table_attach(GTK_TABLE(table), w, 2, 3, 1, 2, GTK_FILL, 0, 0, 0); #endif g_signal_connect(G_OBJECT(w), "changed", G_CALLBACK(size_entry_changed), fs); model = gtk_list_store_new(3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT); w = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model)); gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(w), false); gtk_widget_show(w); column = gtk_tree_view_column_new_with_attributes ("Size", gtk_cell_renderer_text_new(), "text", 0, (char *)NULL); gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE); gtk_tree_view_append_column(GTK_TREE_VIEW(w), column); g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(w))), "changed", G_CALLBACK(size_changed), fs); scroll = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scroll), GTK_SHADOW_IN); gtk_container_add(GTK_CONTAINER(scroll), w); gtk_widget_show(scroll); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); #if GTK_CHECK_VERSION(3,0,0) gtk_grid_attach(GTK_GRID(table), scroll, 2, 2, 1, 1); g_object_set(G_OBJECT(scroll), "expand", true, (const char *)NULL); #else gtk_table_attach(GTK_TABLE(table), scroll, 2, 3, 2, 3, GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); #endif fs->size_model = model; fs->size_list = w; /* * Preview widget. */ fs->preview_area = gtk_drawing_area_new(); #ifndef NO_BACKING_PIXMAPS fs->preview_pixmap = NULL; #endif fs->preview_width = 0; fs->preview_height = 0; fs->preview_fg.pixel = fs->preview_bg.pixel = 0; fs->preview_fg.red = fs->preview_fg.green = fs->preview_fg.blue = 0x0000; fs->preview_bg.red = fs->preview_bg.green = fs->preview_bg.blue = 0xFFFF; #if !GTK_CHECK_VERSION(3,0,0) gdk_colormap_alloc_color(gdk_colormap_get_system(), &fs->preview_fg, false, false); gdk_colormap_alloc_color(gdk_colormap_get_system(), &fs->preview_bg, false, false); #endif #if GTK_CHECK_VERSION(3,0,0) g_signal_connect(G_OBJECT(fs->preview_area), "draw", G_CALLBACK(unifontsel_draw_area), fs); #else g_signal_connect(G_OBJECT(fs->preview_area), "expose_event", G_CALLBACK(unifontsel_expose_area), fs); #endif g_signal_connect(G_OBJECT(fs->preview_area), "configure_event", G_CALLBACK(unifontsel_configure_area), fs); gtk_widget_set_size_request(fs->preview_area, 1, preview_height); gtk_widget_show(fs->preview_area); ww = fs->preview_area; w = gtk_frame_new(NULL); gtk_container_add(GTK_CONTAINER(w), ww); gtk_widget_show(w); #if GTK_CHECK_VERSION(3,0,0) /* GtkAlignment has become deprecated and we use the "margin" * property */ g_object_set(G_OBJECT(w), "margin", 8, (const char *)NULL); #elif GTK_CHECK_VERSION(2,4,0) ww = w; /* GtkAlignment seems to be the simplest way to put padding round things */ w = gtk_alignment_new(0, 0, 1, 1); gtk_alignment_set_padding(GTK_ALIGNMENT(w), 8, 8, 8, 8); gtk_container_add(GTK_CONTAINER(w), ww); gtk_widget_show(w); #endif ww = w; w = gtk_frame_new("Preview of font"); gtk_container_add(GTK_CONTAINER(w), ww); gtk_widget_show(w); #if GTK_CHECK_VERSION(3,0,0) gtk_grid_attach(GTK_GRID(table), w, 0, 3, 3, 1); g_object_set(G_OBJECT(w), "expand", true, (const char *)NULL); #else gtk_table_attach(GTK_TABLE(table), w, 0, 3, 3, 4, GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 8); #endif /* * We only provide the checkboxes for client- and server-side * fonts if we have the X11 back end available, because that's the * only situation in which more than one class of font is * available anyway. */ fs->n_filter_buttons = 0; #ifndef NOT_X_WINDOWS w = gtk_check_button_new_with_label("Show client-side fonts"); g_object_set_data(G_OBJECT(w), "user-data", GINT_TO_POINTER(FONTFLAG_CLIENTSIDE)); g_signal_connect(G_OBJECT(w), "toggled", G_CALLBACK(unifontsel_button_toggled), fs); gtk_widget_show(w); fs->filter_buttons[fs->n_filter_buttons++] = w; #if GTK_CHECK_VERSION(3,0,0) gtk_grid_attach(GTK_GRID(table), w, 0, 4, 3, 1); g_object_set(G_OBJECT(w), "hexpand", true, (const char *)NULL); #else gtk_table_attach(GTK_TABLE(table), w, 0, 3, 4, 5, GTK_FILL, 0, 0, 0); #endif w = gtk_check_button_new_with_label("Show server-side fonts"); g_object_set_data(G_OBJECT(w), "user-data", GINT_TO_POINTER(FONTFLAG_SERVERSIDE)); g_signal_connect(G_OBJECT(w), "toggled", G_CALLBACK(unifontsel_button_toggled), fs); gtk_widget_show(w); fs->filter_buttons[fs->n_filter_buttons++] = w; #if GTK_CHECK_VERSION(3,0,0) gtk_grid_attach(GTK_GRID(table), w, 0, 5, 3, 1); g_object_set(G_OBJECT(w), "hexpand", true, (const char *)NULL); #else gtk_table_attach(GTK_TABLE(table), w, 0, 3, 5, 6, GTK_FILL, 0, 0, 0); #endif w = gtk_check_button_new_with_label("Show server-side font aliases"); g_object_set_data(G_OBJECT(w), "user-data", GINT_TO_POINTER(FONTFLAG_SERVERALIAS)); g_signal_connect(G_OBJECT(w), "toggled", G_CALLBACK(unifontsel_button_toggled), fs); gtk_widget_show(w); fs->filter_buttons[fs->n_filter_buttons++] = w; #if GTK_CHECK_VERSION(3,0,0) gtk_grid_attach(GTK_GRID(table), w, 0, 6, 3, 1); g_object_set(G_OBJECT(w), "hexpand", true, (const char *)NULL); #else gtk_table_attach(GTK_TABLE(table), w, 0, 3, 6, 7, GTK_FILL, 0, 0, 0); #endif #endif /* NOT_X_WINDOWS */ w = gtk_check_button_new_with_label("Show non-monospaced fonts"); g_object_set_data(G_OBJECT(w), "user-data", GINT_TO_POINTER(FONTFLAG_NONMONOSPACED)); g_signal_connect(G_OBJECT(w), "toggled", G_CALLBACK(unifontsel_button_toggled), fs); gtk_widget_show(w); fs->filter_buttons[fs->n_filter_buttons++] = w; #if GTK_CHECK_VERSION(3,0,0) gtk_grid_attach(GTK_GRID(table), w, 0, 7, 3, 1); g_object_set(G_OBJECT(w), "hexpand", true, (const char *)NULL); #else gtk_table_attach(GTK_TABLE(table), w, 0, 3, 7, 8, GTK_FILL, 0, 0, 0); #endif assert(fs->n_filter_buttons <= lenof(fs->filter_buttons)); fs->filter_flags = FONTFLAG_CLIENTSIDE | FONTFLAG_SERVERSIDE | FONTFLAG_SERVERALIAS; unifontsel_set_filter_buttons(fs); /* * Go and find all the font names, and set up our master font * list. */ fs->fonts_by_realname = newtree234(fontinfo_realname_compare); fs->fonts_by_selorder = newtree234(fontinfo_selorder_compare); for (i = 0; i < lenof(unifont_types); i++) unifont_types[i]->enum_fonts(GTK_WIDGET(fs->u.window), unifontsel_add_entry, fs); /* * And set up the initial font names list. */ unifontsel_setup_familylist(fs); fs->selsize = fs->intendedsize = 13; /* random default */ gtk_widget_set_sensitive(fs->u.ok_button, false); return &fs->u; } void unifontsel_destroy(unifontsel *fontsel) { unifontsel_internal *fs = container_of(fontsel, unifontsel_internal, u); fontinfo *info; #ifndef NO_BACKING_PIXMAPS if (fs->preview_pixmap) gdk_pixmap_unref(fs->preview_pixmap); #endif freetree234(fs->fonts_by_selorder); while ((info = delpos234(fs->fonts_by_realname, 0)) != NULL) sfree(info); freetree234(fs->fonts_by_realname); gtk_widget_destroy(GTK_WIDGET(fs->u.window)); sfree(fs); } void unifontsel_set_name(unifontsel *fontsel, const char *fontname) { unifontsel_internal *fs = container_of(fontsel, unifontsel_internal, u); int i, start, end, size, flags; const char *fontname2 = NULL; fontinfo *info; /* * Provide a default if given an empty or null font name. */ if (!fontname || !*fontname) fontname = DEFAULT_GTK_FONT; /* * Call the canonify_fontname function. */ fontname = unifont_do_prefix(fontname, &start, &end); for (i = start; i < end; i++) { fontname2 = unifont_types[i]->canonify_fontname (GTK_WIDGET(fs->u.window), fontname, &size, &flags, false); if (fontname2) break; } if (i == end) return; /* font name not recognised */ /* * Now look up the canonified font name in our index. */ { struct fontinfo_realname_find f; f.realname = fontname2; f.flags = flags; info = find234(fs->fonts_by_realname, &f, fontinfo_realname_find); } /* * If we've found the font, and its size field is either * correct or zero (the latter indicating a scalable font), * then we're done. Otherwise, try looking up the original * font name instead. */ if (!info || (info->size != size && info->size != 0)) { struct fontinfo_realname_find f; f.realname = fontname; f.flags = flags; info = find234(fs->fonts_by_realname, &f, fontinfo_realname_find); if (!info || info->size != size) return; /* font name not in our index */ } /* * Now we've got a fontinfo structure and a font size, so we * know everything we need to fill in all the fields in the * dialog. */ unifontsel_select_font(fs, info, size, 0, true); } char *unifontsel_get_name(unifontsel *fontsel) { unifontsel_internal *fs = container_of(fontsel, unifontsel_internal, u); char *name; if (!fs->selected) return NULL; if (fs->selected->size == 0) { name = fs->selected->fontclass->scale_fontname (GTK_WIDGET(fs->u.window), fs->selected->realname, fs->selsize); if (name) { char *ret = dupcat(fs->selected->fontclass->prefix, ":", name); sfree(name); return ret; } } return dupcat(fs->selected->fontclass->prefix, ":", fs->selected->realname); } #endif /* GTK_CHECK_VERSION(2,0,0) */ putty-0.76/unix/gtkfont.h0000644000175000017500000001424014072266313012371 00000000000000/* * Header file for gtkfont.c. Has to be separate from unix.h * because it depends on GTK data types, hence can't be included * from cross-platform code (which doesn't go near GTK). */ #ifndef PUTTY_GTKFONT_H #define PUTTY_GTKFONT_H /* * We support two entirely different drawing systems: the old * GDK1/GDK2 one which works on server-side X drawables, and the * new-style Cairo one. GTK1 only supports GDK drawing; GTK3 only * supports Cairo; GTK2 supports both, but deprecates GTK, so we only * enable it if we aren't trying on purpose to compile without the * deprecated functions. * * Our different font classes may prefer different drawing systems: X * server-side fonts are a lot faster to draw with GDK, but for * everything else we prefer Cairo, on general grounds of modernness * and also in particular because its matrix-based scaling system * gives much nicer results for double-width and double-height text * when a scalable font is in use. */ #if !GTK_CHECK_VERSION(3,0,0) && !defined GDK_DISABLE_DEPRECATED #define DRAW_TEXT_GDK #endif #if GTK_CHECK_VERSION(2,8,0) #define DRAW_TEXT_CAIRO #endif #if GTK_CHECK_VERSION(3,0,0) || defined GDK_DISABLE_DEPRECATED /* * Where the facility is available, we prefer to render text on to a * persistent server-side pixmap, and redraw windows by simply * blitting rectangles of that pixmap into them as needed. This is * better for performance since we avoid expensive font rendering * calls where possible, and it's particularly good over a non-local X * connection because the response to an expose event can now be a * very simple rectangle-copy operation rather than a lot of fiddly * drawing or bitmap transfer. * * However, GTK is deprecating the use of server-side pixmaps, so we * have to disable this mode under some circumstances. */ #define NO_BACKING_PIXMAPS #endif /* * Exports from gtkfont.c. */ typedef struct UnifontVtable UnifontVtable; /* contents internal to * gtkfont.c */ typedef struct unifont { const struct UnifontVtable *vt; /* * `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, strikethrough_y; /* * 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. */ bool want_fallback; /* * Preferred drawing API to use when this class of font is active. * (See the enum below, in unifont_drawctx.) */ int preferred_drawtype; } unifont; /* A default drawtype, for the case where no font exists to make the * decision with. */ #ifdef DRAW_TEXT_CAIRO #define DRAW_DEFAULT_CAIRO #define DRAWTYPE_DEFAULT DRAWTYPE_CAIRO #elif defined DRAW_TEXT_GDK #define DRAW_DEFAULT_GDK #define DRAWTYPE_DEFAULT DRAWTYPE_GDK #else #error No drawtype available at all #endif /* * Drawing context passed in to unifont_draw_text, which contains * everything required to know where and how to draw the requested * text. */ typedef struct unifont_drawctx { enum { #ifdef DRAW_TEXT_GDK DRAWTYPE_GDK, #endif #ifdef DRAW_TEXT_CAIRO DRAWTYPE_CAIRO, #endif DRAWTYPE_NTYPES } type; union { #ifdef DRAW_TEXT_GDK struct { GdkDrawable *target; GdkGC *gc; } gdk; #endif #ifdef DRAW_TEXT_CAIRO struct { /* Need an actual widget, in order to backtrack to its X * screen number when creating server-side pixmaps */ GtkWidget *widget; cairo_t *cr; cairo_matrix_t origmatrix; #if GTK_CHECK_VERSION(3,22,0) GdkWindow *gdkwin; GdkDrawingContext *drawctx; #endif } cairo; #endif } u; } unifont_drawctx; unifont *unifont_create(GtkWidget *widget, const char *name, bool wide, bool bold, int shadowoffset, bool shadowalways); void unifont_destroy(unifont *font); void unifont_draw_text(unifont_drawctx *ctx, unifont *font, int x, int y, const wchar_t *string, int len, bool wide, bool bold, int cellwidth); /* Same as unifont_draw_text, but expects 'string' to contain one * normal char plus combining chars, and overdraws them all in the * same character cell. */ void unifont_draw_combining(unifont_drawctx *ctx, unifont *font, int x, int y, const wchar_t *string, int len, bool wide, bool bold, int cellwidth); /* Return a name that will select a bigger/smaller font than this one, * or NULL if no such name is available. */ char *unifont_size_increment(unifont *font, int increment); /* * 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, bool wide, bool bold, int shadowoffset, bool 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 * the implementation.) */ typedef struct unifontsel { void *user_data; /* settable by the user */ GtkWindow *window; GtkWidget *ok_button, *cancel_button; } unifontsel; unifontsel *unifontsel_new(const char *wintitle); void unifontsel_destroy(unifontsel *fontsel); void unifontsel_set_name(unifontsel *fontsel, const char *fontname); char *unifontsel_get_name(unifontsel *fontsel); #endif /* PUTTY_GTKFONT_H */ putty-0.76/unix/gtkmain.c0000644000175000017500000004624114072266313012350 00000000000000/* * gtkmain.c: the common main-program code between the straight-up * Unix PuTTY and pterm, which they do not share with the * multi-session gtkapp.c. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if !GTK_CHECK_VERSION(3,0,0) #include #endif #if GTK_CHECK_VERSION(2,0,0) #include #endif #define MAY_REFER_TO_GTK_IN_HEADERS #include "putty.h" #include "terminal.h" #include "gtkcompat.h" #include "gtkfont.h" #include "gtkmisc.h" #ifndef NOT_X_WINDOWS #include #include #include #include #include "x11misc.h" #endif static char *progname, **gtkargvstart; static int ngtkargs; static const char *app_name = "pterm"; char *x_get_default(const char *key) { #ifndef NOT_X_WINDOWS Display *disp; if ((disp = get_x11_display()) == NULL) return NULL; return XGetDefault(disp, app_name, key); #else return NULL; #endif } void fork_and_exec_self(int fd_to_close, ...) { /* * Re-execing ourself is not an exact science under Unix. I do * the best I can by using /proc/self/exe if available and by * assuming argv[0] can be found on $PATH if not. * * Note that we also have to reconstruct the elements of the * original argv which gtk swallowed, since the user wants the * new session to appear on the same X display as the old one. */ char **args; va_list ap; int i, n; int pid; /* * Collect the arguments with which to re-exec ourself. */ va_start(ap, fd_to_close); n = 2; /* progname and terminating NULL */ n += ngtkargs; while (va_arg(ap, char *) != NULL) n++; va_end(ap); args = snewn(n, char *); args[0] = progname; args[n-1] = NULL; for (i = 0; i < ngtkargs; i++) args[i+1] = gtkargvstart[i]; i++; va_start(ap, fd_to_close); while ((args[i++] = va_arg(ap, char *)) != NULL); va_end(ap); assert(i == n); /* * 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"); _exit(1); } else if (pid2 > 0) { /* * First child has successfully forked second child. My * Work Here Is Done. Note the use of _exit rather than * exit: the latter appears to cause destroy messages * to be sent to the X server. I suspect gtk uses * atexit. */ _exit(0); } /* * If we reach here, we are the second child, so we now * actually perform the exec. */ if (fd_to_close >= 0) close(fd_to_close); execv("/proc/self/exe", args); execvp(progname, args); perror("exec"); _exit(127); } else { int status; sfree(args); waitpid(pid, &status, 0); } } void launch_duplicate_session(Conf *conf) { /* * 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; strbuf *serialised; char option[80]; int pipefd[2]; if (pipe(pipefd) < 0) { perror("pipe"); return; } serialised = strbuf_new(); conf_serialise(BinarySink_UPCAST(serialised), conf); if (use_pty_argv && pty_argv) for (i = 0; pty_argv[i]; i++) put_asciz(serialised, pty_argv[i]); sprintf(option, "---[%d,%zu]", pipefd[0], serialised->len); noncloexec(pipefd[0]); fork_and_exec_self(pipefd[1], option, NULL); close(pipefd[0]); i = ret = 0; while (i < serialised->len && (ret = write(pipefd[1], serialised->s + i, serialised->len - i)) > 0) i += ret; if (ret < 0) perror("write to pipe"); close(pipefd[1]); strbuf_free(serialised); } void launch_new_session(void) { fork_and_exec_self(-1, NULL); } void launch_saved_session(const char *str) { fork_and_exec_self(-1, "-load", str, NULL); } int read_dupsession_data(Conf *conf, char *arg) { int fd, i, ret, size; char *data; BinarySource src[1]; 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); } BinarySource_BARE_INIT(src, data, size); if (!conf_deserialise(conf, src)) { fprintf(stderr, "%s: malformed Duplicate Session data\n", appname); exit(1); } if (use_pty_argv) { int pty_argc = 0; size_t argv_startpos = src->pos; while (get_asciz(src), !get_err(src)) pty_argc++; src->err = BSE_NO_ERROR; if (pty_argc > 0) { src->pos = argv_startpos; pty_argv = snewn(pty_argc + 1, char *); pty_argv[pty_argc] = NULL; for (i = 0; i < pty_argc; i++) pty_argv[i] = dupstr(get_asciz(src)); } } if (get_err(src) || get_avail(src) > 0) { fprintf(stderr, "%s: malformed Duplicate Session data\n", appname); exit(1); } sfree(data); return 0; } static void help(FILE *fp) { if(fprintf(fp, "pterm option summary:\n" "\n" " --display DISPLAY Specify X display to use (note '--')\n" " -name PREFIX Prefix when looking up resources (default: pterm)\n" " -fn FONT Normal text font\n" " -fb FONT Bold text font\n" " -geometry GEOMETRY Position and size of window (size in characters)\n" " -sl LINES Number of lines of scrollback\n" " -fg COLOUR, -bg COLOUR Foreground/background colour\n" " -bfg COLOUR, -bbg COLOUR Foreground/background bold colour\n" " -cfg COLOUR, -bfg COLOUR Foreground/background cursor colour\n" " -T TITLE Window title\n" " -ut, +ut Do(default) or do not update utmp\n" " -ls, +ls Do(default) or do not make shell a login shell\n" " -sb, +sb Do(default) or do not display a scrollbar\n" " -log PATH, -sessionlog PATH Log all output to a file\n" " -nethack Map numeric keypad to hjklyubn direction keys\n" " -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) { char *buildinfo_text = buildinfo("\n"); if(fprintf(fp, "%s: %s\n%s\n", appname, ver, buildinfo_text) < 0 || fflush(fp) < 0) { perror("output error"); exit(1); } sfree(buildinfo_text); } static const char *geometry_string; void cmdline_error(const char *p, ...) { va_list ap; fprintf(stderr, "%s: ", appname); va_start(ap, p); vfprintf(stderr, p, ap); va_end(ap); fputc('\n', stderr); exit(1); } void window_setup_error(const char *errmsg) { fprintf(stderr, "%s: %s\n", appname, errmsg); exit(1); } bool do_cmdline(int argc, char **argv, bool do_everything, Conf *conf) { bool err = false; char *val; /* * Macros to make argument handling easier. * * Note that because they need to call `continue', they cannot be * contained in the usual do {...} while (0) wrapper to make them * syntactically single statements. I use the alternative if (1) * {...} else ((void)0). */ #define EXPECTS_ARG if (1) { \ if (--argc <= 0) { \ err = true; \ fprintf(stderr, "%s: %s expects an argument\n", appname, p); \ continue; \ } else \ val = *++argv; \ } else ((void)0) #define SECOND_PASS_ONLY if (1) { \ if (!do_everything) \ continue; \ } else ((void)0) while (--argc > 0) { const char *p = *++argv; int ret; /* * Shameless cheating. Debian requires all X terminal * emulators to support `-T title'; but * cmdline_process_param will eat -T (it means no-pty) and * complain that pterm doesn't support it. So, in pterm * 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")) { EXPECTS_ARG; SECOND_PASS_ONLY; geometry_string = val; } 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")) { EXPECTS_ARG; SECOND_PASS_ONLY; { #if GTK_CHECK_VERSION(3,0,0) GdkRGBA rgba; bool success = gdk_rgba_parse(&rgba, val); #else GdkColor col; bool success = gdk_color_parse(val, &col); #endif if (!success) { err = true; fprintf(stderr, "%s: unable to parse colour \"%s\"\n", appname, val); } else { #if GTK_CHECK_VERSION(3,0,0) int r = rgba.red * 255; int g = rgba.green * 255; int b = rgba.blue * 255; #else int r = col.red / 256; int g = col.green / 256; int b = col.blue / 256; #endif int index; 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, r); conf_set_int_int(conf, CONF_colours, index*3+1, g); conf_set_int_int(conf, CONF_colours, index*3+2, b); } } } else if (use_pty_argv && !strcmp(p, "-e")) { /* This option swallows all further arguments. */ if (!do_everything) break; if (--argc > 0) { int i; pty_argv = snewn(argc+1, char *); ++argv; for (i = 0; i < argc; i++) pty_argv[i] = argv[i]; pty_argv[argc] = NULL; break; /* finished command-line processing */ } else err = true, 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_bool(conf, CONF_stamp_utmp, false); } else if (!strcmp(p, "-ut")) { SECOND_PASS_ONLY; conf_set_bool(conf, CONF_stamp_utmp, true); } else if (!strcmp(p, "-ls-") || !strcmp(p, "+ls")) { SECOND_PASS_ONLY; conf_set_bool(conf, CONF_login_shell, false); } else if (!strcmp(p, "-ls")) { SECOND_PASS_ONLY; conf_set_bool(conf, CONF_login_shell, true); } else if (!strcmp(p, "-nethack")) { SECOND_PASS_ONLY; conf_set_bool(conf, CONF_nethack_keypad, true); } else if (!strcmp(p, "-sb-") || !strcmp(p, "+sb")) { SECOND_PASS_ONLY; conf_set_bool(conf, CONF_scrollbar, false); } else if (!strcmp(p, "-sb")) { SECOND_PASS_ONLY; conf_set_bool(conf, CONF_scrollbar, true); } else if (!strcmp(p, "-name")) { EXPECTS_ARG; app_name = val; } else if (!strcmp(p, "-xrm")) { EXPECTS_ARG; provide_xrm_string(val, appname); } 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] != '-') { /* Non-option arguments not handled by cmdline.c are errors. */ if (do_everything) { err = true; fprintf(stderr, "%s: unexpected non-option argument '%s'\n", appname, p); } } else { err = true; fprintf(stderr, "%s: unrecognized option '%s'\n", appname, p); } } return err; } GtkWidget *make_gtk_toplevel_window(GtkFrontend *frontend) { return gtk_window_new(GTK_WINDOW_TOPLEVEL); } const bool buildinfo_gtk_relevant = true; struct post_initial_config_box_ctx { Conf *conf; const char *geometry_string; }; static void post_initial_config_box(void *vctx, int result) { struct post_initial_config_box_ctx ctx = *(struct post_initial_config_box_ctx *)vctx; sfree(vctx); if (result > 0) { new_session_window(ctx.conf, ctx.geometry_string); } else { /* In this main(), which only runs one session in total, a * negative result from the initial config box means we simply * terminate. */ conf_free(ctx.conf); gtk_main_quit(); } } void session_window_closed(void) { gtk_main_quit(); } int main(int argc, char **argv) { Conf *conf; bool need_config_box; setlocale(LC_CTYPE, ""); /* Call the function in ux{putty,pterm}.c to do app-type * specific setup */ setup(true); /* true means we are a one-session process */ progname = argv[0]; /* * Copy the original argv before letting gtk_init fiddle with * it. It will be required later. */ { int i, oldargc; gtkargvstart = snewn(argc-1, char *); for (i = 1; i < argc; i++) gtkargvstart[i-1] = dupstr(argv[i]); oldargc = argc; gtk_init(&argc, &argv); ngtkargs = oldargc - argc; } conf = conf_new(); gtkcomm_setup(); /* * Block SIGPIPE: if we attempt Duplicate Session or similar and * it falls over in some way, we certainly don't want SIGPIPE * terminating the main pterm/PuTTY. However, we'll have to * unblock it again when pterm forks. */ block_signal(SIGPIPE, true); if (argc > 1 && !strncmp(argv[1], "---", 3)) { read_dupsession_data(conf, argv[1]); /* Splatter this argument so it doesn't clutter a ps listing */ smemclr(argv[1], strlen(argv[1])); assert(!dup_check_launchable || conf_launchable(conf)); need_config_box = false; } else { if (do_cmdline(argc, argv, false, conf)) exit(1); /* pre-defaults pass to get -class */ do_defaults(NULL, conf); if (do_cmdline(argc, argv, true, conf)) exit(1); /* post-defaults, do everything */ cmdline_run_saved(conf); if (cmdline_tooltype & TOOLTYPE_HOST_ARG) need_config_box = !cmdline_host_ok(conf); else need_config_box = false; } if (need_config_box) { /* * Put up the initial config box, which will pass the provided * parameters (with conf updated) to new_session_window() when * (if) the user selects Open. Or it might close without * creating a session window, if the user selects Cancel. Or * it might just create the session window immediately if this * is a pterm-style app which doesn't have an initial config * box at all. */ struct post_initial_config_box_ctx *ctx = snew(struct post_initial_config_box_ctx); ctx->conf = conf; ctx->geometry_string = geometry_string; initial_config_box(conf, post_initial_config_box, ctx); } else { /* * No initial config needed; just create the session window * now. */ new_session_window(conf, geometry_string); } gtk_main(); return 0; } putty-0.76/unix/gtkmisc.c0000644000175000017500000001601114072266313012347 00000000000000/* * Miscellaneous GTK helper functions. */ #include #include #include #include #include #if !GTK_CHECK_VERSION(3,0,0) #include #endif #include "putty.h" #include "gtkcompat.h" #ifndef NOT_X_WINDOWS #include #include #endif void get_label_text_dimensions(const char *text, int *width, int *height) { /* * Determine the dimensions of a piece of text in the standard * font used in GTK interface elements like labels. We do this by * instantiating an actual GtkLabel, and then querying its size. * * But GTK2 and GTK3 require us to query the size completely * differently. I'm sure there ought to be an easier approach than * the way I'm doing this in GTK3, too! */ GtkWidget *label = gtk_label_new(text); #if GTK_CHECK_VERSION(3,0,0) PangoLayout *layout = gtk_label_get_layout(GTK_LABEL(label)); PangoRectangle logrect; pango_layout_get_extents(layout, NULL, &logrect); if (width) *width = logrect.width / PANGO_SCALE; if (height) *height = logrect.height / PANGO_SCALE; #else GtkRequisition req; gtk_widget_size_request(label, &req); if (width) *width = req.width; if (height) *height = req.height; #endif g_object_ref_sink(G_OBJECT(label)); #if GTK_CHECK_VERSION(2,10,0) g_object_unref(label); #endif } int string_width(const char *text) { int ret; get_label_text_dimensions(text, &ret, NULL); return ret; } void align_label_left(GtkLabel *label) { #if GTK_CHECK_VERSION(3,16,0) gtk_label_set_xalign(label, 0.0); #elif GTK_CHECK_VERSION(3,14,0) gtk_widget_set_halign(GTK_WIDGET(label), GTK_ALIGN_START); #else gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0); #endif } /* ---------------------------------------------------------------------- * Functions to arrange controls in a basically dialog-like window. * * The best method for doing this has varied wildly with versions of * GTK, hence the set of wrapper functions here. * * In GTK 1, a GtkDialog has an 'action_area' at the bottom, which is * a GtkHBox which stretches to cover the full width of the dialog. So * we can either add buttons or other widgets to that box directly, or * alternatively we can fill the hbox with some layout class of our * own such as a Columns widget. * * In GTK 2, the action area has become a GtkHButtonBox, and its * layout behaviour seems to be different and not what we want. So * instead we abandon the dialog's action area completely: we * gtk_widget_hide() it in the below code, and we also call * gtk_dialog_set_has_separator() to remove the separator above it. We * then insert our own action area into the end of the dialog's main * vbox, and add our own separator above that. * * In GTK 3, we typically don't even want to use GtkDialog at all, * because GTK 3 has become a lot more restrictive about what you can * sensibly use GtkDialog for - it deprecates direct access to the * action area in favour of making you provide nothing but * dialog-ending buttons in the form of (text, response code) pairs, * so you can't put any other kind of control in there, or fiddle with * alignment and positioning, or even have a button that _doesn't_ end * the dialog (e.g. 'View Licence' in our About box). So instead of * GtkDialog, we use a straight-up GtkWindow and have it contain a * vbox as its (unique) child widget; and we implement the action area * by adding a separator and another widget at the bottom of that * vbox. */ GtkWidget *our_dialog_new(void) { #if GTK_CHECK_VERSION(3,0,0) /* * See comment in our_dialog_set_action_area(): in GTK 3, we use * GtkWindow in place of GtkDialog for most purposes. */ GtkWidget *w = gtk_window_new(GTK_WINDOW_TOPLEVEL); GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 8); gtk_container_add(GTK_CONTAINER(w), vbox); gtk_widget_show(vbox); return w; #else return gtk_dialog_new(); #endif } void our_dialog_set_action_area(GtkWindow *dlg, GtkWidget *w) { #if !GTK_CHECK_VERSION(2,0,0) gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dlg)->action_area), w, true, true, 0); #elif !GTK_CHECK_VERSION(3,0,0) GtkWidget *align; align = gtk_alignment_new(0, 0, 1, 1); gtk_container_add(GTK_CONTAINER(align), w); /* * The purpose of this GtkAlignment is to provide padding * around the buttons. The padding we use is twice the padding * used in our GtkColumns, because we nest two GtkColumns most * of the time (one separating the tree view from the main * controls, and another for the main controls themselves). */ #if GTK_CHECK_VERSION(2,4,0) gtk_alignment_set_padding(GTK_ALIGNMENT(align), 8, 8, 8, 8); #endif gtk_widget_show(align); gtk_box_pack_end(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dlg))), align, false, true, 0); w = gtk_hseparator_new(); gtk_box_pack_end(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dlg))), w, false, true, 0); gtk_widget_show(w); gtk_widget_hide(gtk_dialog_get_action_area(GTK_DIALOG(dlg))); g_object_set(G_OBJECT(dlg), "has-separator", true, (const char *)NULL); #else /* GTK 3 */ /* GtkWindow is a GtkBin, hence contains exactly one child, which * here we always expect to be a vbox */ GtkBox *vbox = GTK_BOX(gtk_bin_get_child(GTK_BIN(dlg))); GtkWidget *sep; g_object_set(G_OBJECT(w), "margin", 8, (const char *)NULL); gtk_box_pack_end(vbox, w, false, true, 0); sep = gtk_hseparator_new(); gtk_box_pack_end(vbox, sep, false, true, 0); gtk_widget_show(sep); #endif } GtkBox *our_dialog_make_action_hbox(GtkWindow *dlg) { #if GTK_CHECK_VERSION(3,0,0) GtkWidget *hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); our_dialog_set_action_area(dlg, hbox); g_object_set(G_OBJECT(hbox), "margin", 0, (const char *)NULL); g_object_set(G_OBJECT(hbox), "spacing", 8, (const char *)NULL); gtk_widget_show(hbox); return GTK_BOX(hbox); #else /* not GTK 3 */ return GTK_BOX(gtk_dialog_get_action_area(GTK_DIALOG(dlg))); #endif } void our_dialog_add_to_content_area(GtkWindow *dlg, GtkWidget *w, bool expand, bool fill, guint padding) { #if GTK_CHECK_VERSION(3,0,0) /* GtkWindow is a GtkBin, hence contains exactly one child, which * here we always expect to be a vbox */ GtkBox *vbox = GTK_BOX(gtk_bin_get_child(GTK_BIN(dlg))); gtk_box_pack_start(vbox, w, expand, fill, padding); #else gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dlg))), w, expand, fill, padding); #endif } char *buildinfo_gtk_version(void) { return dupprintf("%d.%d.%d", GTK_MAJOR_VERSION, GTK_MINOR_VERSION, GTK_MICRO_VERSION); } #ifndef NOT_X_WINDOWS Display *get_x11_display(void) { #if GTK_CHECK_VERSION(3,0,0) if (!GDK_IS_X11_DISPLAY(gdk_display_get_default())) return NULL; #endif return GDK_DISPLAY_XDISPLAY(gdk_display_get_default()); } #endif putty-0.76/unix/gtkmisc.h0000644000175000017500000000107614072266313012361 00000000000000/* * Miscellaneous helper functions for GTK. */ #ifndef PUTTY_GTKMISC_H #define PUTTY_GTKMISC_H int string_width(const char *text); void get_label_text_dimensions(const char *text, int *width, int *height); void align_label_left(GtkLabel *label); GtkWidget *our_dialog_new(void); void our_dialog_add_to_content_area(GtkWindow *dlg, GtkWidget *w, bool expand, bool fill, guint padding); void our_dialog_set_action_area(GtkWindow *dlg, GtkWidget *w); GtkBox *our_dialog_make_action_hbox(GtkWindow *dlg); #endif /* PUTTY_GTKMISC_H */ putty-0.76/unix/gtkwin.c0000644000175000017500000055103714072266313012225 00000000000000/* * gtkwin.c: the main code that runs a PuTTY terminal emulator and * backend in a GTK window. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if !GTK_CHECK_VERSION(3,0,0) #include #endif #if GTK_CHECK_VERSION(2,0,0) #include #endif #define MAY_REFER_TO_GTK_IN_HEADERS #include "putty.h" #include "terminal.h" #include "gtkcompat.h" #include "gtkfont.h" #include "gtkmisc.h" #ifndef NOT_X_WINDOWS #include #include #include #include #endif #include "x11misc.h" GdkAtom compound_text_atom, utf8_string_atom; static GdkAtom clipboard_atom #if GTK_CHECK_VERSION(2,0,0) /* GTK1 will have to fill this in at startup */ = GDK_SELECTION_CLIPBOARD #endif ; #ifdef JUST_USE_GTK_CLIPBOARD_UTF8 /* * Because calling gtk_clipboard_set_with_data triggers a call to the * clipboard_clear function from the last time, we need to arrange a * way to distinguish a real call to clipboard_clear for the _new_ * instance of the clipboard data from the leftover call for the * outgoing one. We do this by setting the user data field in our * gtk_clipboard_set_with_data() call, instead of the obvious pointer * to 'inst', to one of these. */ struct clipboard_data_instance { char *pasteout_data_utf8; int pasteout_data_utf8_len; struct clipboard_state *state; struct clipboard_data_instance *next, *prev; }; #endif struct clipboard_state { GtkFrontend *inst; int clipboard; GdkAtom atom; #ifdef JUST_USE_GTK_CLIPBOARD_UTF8 GtkClipboard *gtkclipboard; struct clipboard_data_instance *current_cdi; #else char *pasteout_data, *pasteout_data_ctext, *pasteout_data_utf8; int pasteout_data_len, pasteout_data_ctext_len, pasteout_data_utf8_len; #endif }; typedef struct XpmHolder XpmHolder; /* only used for GTK 1 */ struct GtkFrontend { GtkWidget *window, *area, *sbar; gboolean sbar_visible; gboolean drawing_area_got_size, drawing_area_realised; gboolean drawing_area_setup_needed; GtkBox *hbox; GtkAdjustment *sbar_adjust; GtkWidget *menu, *specialsmenu, *specialsitem1, *specialsitem2, *restartitem; GtkWidget *sessionsmenu; #ifndef NOT_X_WINDOWS Display *disp; #endif #ifndef NO_BACKING_PIXMAPS /* * Server-side pixmap which we use to cache the terminal window's * contents. When we draw text in the terminal, we draw it to this * pixmap first, and then blit from there to the actual window; * this way, X expose events can be handled with an absolute * minimum of network traffic, by just sending a command to * re-blit an appropriate rectangle from this pixmap. */ GdkPixmap *pixmap; #endif #ifdef DRAW_TEXT_CAIRO /* * If we're drawing using Cairo, we cache the same image on the * client side in a Cairo surface. * * In GTK2+Cairo, this happens _as well_ as having the server-side * pixmap cache above; in GTK3+Cairo, server-side pixmaps are * deprecated, so we _just_ have this client-side cache. In the * latter case that means we have to transmit a big wodge of * bitmap data over the X connection on every expose event; but * GTK3 apparently deliberately provides no way to avoid that * inefficiency, and at least this way we don't _also_ have to * redo any font rendering just because the window was temporarily * covered. */ cairo_surface_t *surface; #endif int backing_w, backing_h; #if GTK_CHECK_VERSION(2,0,0) GtkIMContext *imc; #endif unifont *fonts[4]; /* normal, bold, wide, widebold */ int xpos, ypos, gravity; bool gotpos; GdkCursor *rawcursor, *textcursor, *blankcursor, *waitcursor, *currcursor; GdkColor cols[OSC4_NCOLOURS]; /* indexed by xterm colour indices */ #if !GTK_CHECK_VERSION(3,0,0) GdkColormap *colmap; #endif bool direct_to_font; struct clipboard_state clipstates[N_CLIPBOARDS]; #ifdef JUST_USE_GTK_CLIPBOARD_UTF8 /* Remember all clipboard_data_instance structures currently * associated with this GtkFrontend, in case they're still around * when it gets destroyed */ struct clipboard_data_instance cdi_headtail; #endif int clipboard_ctrlshiftins, clipboard_ctrlshiftcv; int font_width, font_height; int width, height, scale; bool ignore_sbar; bool mouseptr_visible; BusyStatus busy_status; int alt_keycode; int alt_digits; char *wintitle; char *icontitle; int master_fd, master_func_id; Ldisc *ldisc; Backend *backend; Terminal *term; LogContext *logctx; bool exited; struct unicode_data ucsdata; Conf *conf; eventlog_stuff *eventlogstuff; guint32 input_event_time; /* Timestamp of the most recent input event. */ GtkWidget *dialogs[DIALOG_SLOT_LIMIT]; #if GTK_CHECK_VERSION(3,4,0) gdouble cumulative_scroll; #endif /* Cached things out of conf that we refer to a lot */ int bold_style; int window_border; int cursor_type; int drawtype; int meta_mod_mask; #ifdef OSX_META_KEY_CONFIG int system_mod_mask; #endif bool send_raw_mouse; bool pointer_indicates_raw_mouse; unifont_drawctx uctx; #if GTK_CHECK_VERSION(2,0,0) GdkPixbuf *trust_sigil_pb; #else GdkPixmap *trust_sigil_pm; #endif int trust_sigil_w, trust_sigil_h; Seat seat; TermWin termwin; LogPolicy logpolicy; }; static void cache_conf_values(GtkFrontend *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); #ifdef OSX_META_KEY_CONFIG inst->meta_mod_mask = 0; if (conf_get_bool(inst->conf, CONF_osx_option_meta)) inst->meta_mod_mask |= GDK_MOD1_MASK; if (conf_get_bool(inst->conf, CONF_osx_command_meta)) inst->meta_mod_mask |= GDK_MOD2_MASK; inst->system_mod_mask = GDK_MOD2_MASK & ~inst->meta_mod_mask; #else inst->meta_mod_mask = GDK_MOD1_MASK; #endif } static void start_backend(GtkFrontend *inst); static void exit_callback(void *vinst); static void destroy_inst_connection(GtkFrontend *inst); static void delete_inst(GtkFrontend *inst); static void post_fatal_message_box_toplevel(void *vctx) { GtkFrontend *inst = (GtkFrontend *)vctx; gtk_widget_destroy(inst->window); } static void post_fatal_message_box(void *vctx, int result) { GtkFrontend *inst = (GtkFrontend *)vctx; unregister_dialog(&inst->seat, DIALOG_SLOT_CONNECTION_FATAL); queue_toplevel_callback(post_fatal_message_box_toplevel, inst); } static void common_connfatal_message_box( GtkFrontend *inst, const char *msg, post_dialog_fn_t postfn) { char *title = dupcat(appname, " Fatal Error"); GtkWidget *dialog = create_message_box( inst->window, title, msg, string_width("REASONABLY LONG LINE OF TEXT FOR BASIC SANITY"), false, &buttons_ok, postfn, inst); register_dialog(&inst->seat, DIALOG_SLOT_CONNECTION_FATAL, dialog); sfree(title); } void fatal_message_box(GtkFrontend *inst, const char *msg) { common_connfatal_message_box(inst, msg, post_fatal_message_box); } static void connection_fatal_callback(void *vctx) { GtkFrontend *inst = (GtkFrontend *)vctx; destroy_inst_connection(inst); } static void post_nonfatal_message_box(void *vctx, int result) { GtkFrontend *inst = (GtkFrontend *)vctx; unregister_dialog(&inst->seat, DIALOG_SLOT_CONNECTION_FATAL); } static void gtk_seat_connection_fatal(Seat *seat, const char *msg) { GtkFrontend *inst = container_of(seat, GtkFrontend, seat); if (conf_get_int(inst->conf, CONF_close_on_exit) == FORCE_ON) { fatal_message_box(inst, msg); } else { common_connfatal_message_box(inst, msg, post_nonfatal_message_box); } inst->exited = true; /* suppress normal exit handling */ queue_toplevel_callback(connection_fatal_callback, inst); } /* * Default settings that are specific to pterm. */ FontSpec *platform_default_fontspec(const char *name) { if (!strcmp(name, "Font")) return fontspec_new(DEFAULT_GTK_FONT); 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; } bool platform_default_b(const char *name, bool def) { if (!strcmp(name, "WinNameAlways")) { /* X natively supports icon titles, so use 'em by default */ return false; } return def; } int platform_default_i(const char *name, int def) { if (!strcmp(name, "CloseOnExit")) return 2; /* maps to FORCE_ON after painful rearrangement :-( */ return def; } static char *gtk_seat_get_ttymode(Seat *seat, const char *mode) { GtkFrontend *inst = container_of(seat, GtkFrontend, seat); return term_get_ttymode(inst->term, mode); } static size_t gtk_seat_output(Seat *seat, bool is_stderr, const void *data, size_t len) { GtkFrontend *inst = container_of(seat, GtkFrontend, seat); return term_data(inst->term, is_stderr, data, len); } static bool gtk_seat_eof(Seat *seat) { /* GtkFrontend *inst = container_of(seat, GtkFrontend, seat); */ return true; /* do respond to incoming EOF with outgoing */ } static int gtk_seat_get_userpass_input(Seat *seat, prompts_t *p, bufchain *input) { GtkFrontend *inst = container_of(seat, GtkFrontend, seat); int ret; ret = cmdline_get_passwd_input(p); if (ret == -1) ret = term_get_userpass_input(inst->term, p, input); return ret; } static bool gtk_seat_is_utf8(Seat *seat) { GtkFrontend *inst = container_of(seat, GtkFrontend, seat); return inst->ucsdata.line_codepage == CS_UTF8; } static void get_window_pixel_size(GtkFrontend *inst, int *w, int *h) { /* * I assume that when the GTK version of this call is available * we should use it. Not sure how it differs from the GDK one, * though. */ #if GTK_CHECK_VERSION(2,0,0) gtk_window_get_size(GTK_WINDOW(inst->window), w, h); #else gdk_window_get_size(gtk_widget_get_window(inst->window), w, h); #endif } static bool gtk_seat_get_window_pixel_size(Seat *seat, int *w, int *h) { GtkFrontend *inst = container_of(seat, GtkFrontend, seat); get_window_pixel_size(inst, w, h); return true; } StripCtrlChars *gtk_seat_stripctrl_new( Seat *seat, BinarySink *bs_out, SeatInteractionContext sic) { GtkFrontend *inst = container_of(seat, GtkFrontend, seat); return stripctrl_new_term(bs_out, false, 0, inst->term); } static void gtk_seat_notify_remote_exit(Seat *seat); static void gtk_seat_update_specials_menu(Seat *seat); static void gtk_seat_set_busy_status(Seat *seat, BusyStatus status); static const char *gtk_seat_get_x_display(Seat *seat); #ifndef NOT_X_WINDOWS static bool gtk_seat_get_windowid(Seat *seat, long *id); #endif static bool gtk_seat_set_trust_status(Seat *seat, bool trusted); static bool gtk_seat_get_cursor_position(Seat *seat, int *x, int *y); static const SeatVtable gtk_seat_vt = { .output = gtk_seat_output, .eof = gtk_seat_eof, .get_userpass_input = gtk_seat_get_userpass_input, .notify_remote_exit = gtk_seat_notify_remote_exit, .connection_fatal = gtk_seat_connection_fatal, .update_specials_menu = gtk_seat_update_specials_menu, .get_ttymode = gtk_seat_get_ttymode, .set_busy_status = gtk_seat_set_busy_status, .verify_ssh_host_key = gtk_seat_verify_ssh_host_key, .confirm_weak_crypto_primitive = gtk_seat_confirm_weak_crypto_primitive, .confirm_weak_cached_hostkey = gtk_seat_confirm_weak_cached_hostkey, .is_utf8 = gtk_seat_is_utf8, .echoedit_update = nullseat_echoedit_update, .get_x_display = gtk_seat_get_x_display, #ifdef NOT_X_WINDOWS .get_windowid = nullseat_get_windowid, #else .get_windowid = gtk_seat_get_windowid, #endif .get_window_pixel_size = gtk_seat_get_window_pixel_size, .stripctrl_new = gtk_seat_stripctrl_new, .set_trust_status = gtk_seat_set_trust_status, .verbose = nullseat_verbose_yes, .interactive = nullseat_interactive_yes, .get_cursor_position = gtk_seat_get_cursor_position, }; static void gtk_eventlog(LogPolicy *lp, const char *string) { GtkFrontend *inst = container_of(lp, GtkFrontend, logpolicy); logevent_dlg(inst->eventlogstuff, string); } static int gtk_askappend(LogPolicy *lp, Filename *filename, void (*callback)(void *ctx, int result), void *ctx) { GtkFrontend *inst = container_of(lp, GtkFrontend, logpolicy); return gtkdlg_askappend(&inst->seat, filename, callback, ctx); } static void gtk_logging_error(LogPolicy *lp, const char *event) { GtkFrontend *inst = container_of(lp, GtkFrontend, logpolicy); /* Send 'can't open log file' errors to the terminal window. * (Marked as stderr, although terminal.c won't care.) */ seat_stderr_pl(&inst->seat, ptrlen_from_asciz(event)); seat_stderr_pl(&inst->seat, PTRLEN_LITERAL("\r\n")); } static const LogPolicyVtable gtk_logpolicy_vt = { .eventlog = gtk_eventlog, .askappend = gtk_askappend, .logging_error = gtk_logging_error, .verbose = null_lp_verbose_yes, }; /* * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT) * into a cooked one (SELECT, EXTEND, PASTE). * * In Unix, this is not configurable; the X button arrangement is * rock-solid across all applications, everyone has a three-button * mouse or a means of faking it, and there is no need to switch * buttons around at all. */ static Mouse_Button translate_button(Mouse_Button button) { if (button == MBT_LEFT) return MBT_SELECT; if (button == MBT_MIDDLE) return MBT_PASTE; if (button == MBT_RIGHT) return MBT_EXTEND; return 0; /* shouldn't happen */ } /* * Return the top-level GtkWindow associated with a particular * front end instance. */ GtkWidget *gtk_seat_get_window(Seat *seat) { GtkFrontend *inst = container_of(seat, GtkFrontend, seat); return inst->window; } /* * Set and clear a pointer to a dialog box created as a result of the * network code wanting to ask an asynchronous user question (e.g. * 'what about this dodgy host key, then?'). */ void register_dialog(Seat *seat, enum DialogSlot slot, GtkWidget *dialog) { GtkFrontend *inst; assert(seat->vt == >k_seat_vt); inst = container_of(seat, GtkFrontend, seat); assert(slot < DIALOG_SLOT_LIMIT); assert(!inst->dialogs[slot]); inst->dialogs[slot] = dialog; } void unregister_dialog(Seat *seat, enum DialogSlot slot) { GtkFrontend *inst; assert(seat->vt == >k_seat_vt); inst = container_of(seat, GtkFrontend, seat); assert(slot < DIALOG_SLOT_LIMIT); assert(inst->dialogs[slot]); inst->dialogs[slot] = NULL; } /* * Minimise or restore the window in response to a server-side * request. */ static void gtkwin_set_minimised(TermWin *tw, bool minimised) { /* * GTK 1.2 doesn't know how to do this. */ #if GTK_CHECK_VERSION(2,0,0) GtkFrontend *inst = container_of(tw, GtkFrontend, termwin); if (minimised) gtk_window_iconify(GTK_WINDOW(inst->window)); else gtk_window_deiconify(GTK_WINDOW(inst->window)); #endif } /* * Move the window in response to a server-side request. */ static void gtkwin_move(TermWin *tw, int x, int y) { GtkFrontend *inst = container_of(tw, GtkFrontend, termwin); /* * I assume that when the GTK version of this call is available * we should use it. Not sure how it differs from the GDK one, * though. */ #if GTK_CHECK_VERSION(2,0,0) /* in case we reset this at startup due to a geometry string */ gtk_window_set_gravity(GTK_WINDOW(inst->window), GDK_GRAVITY_NORTH_EAST); gtk_window_move(GTK_WINDOW(inst->window), x, y); #else gdk_window_move(gtk_widget_get_window(inst->window), x, y); #endif } /* * Move the window to the top or bottom of the z-order in response * to a server-side request. */ static void gtkwin_set_zorder(TermWin *tw, bool top) { GtkFrontend *inst = container_of(tw, GtkFrontend, termwin); if (top) gdk_window_raise(gtk_widget_get_window(inst->window)); else gdk_window_lower(gtk_widget_get_window(inst->window)); } /* * Refresh the window in response to a server-side request. */ static void gtkwin_refresh(TermWin *tw) { GtkFrontend *inst = container_of(tw, GtkFrontend, termwin); term_invalidate(inst->term); } /* * Maximise or restore the window in response to a server-side * request. */ static void gtkwin_set_maximised(TermWin *tw, bool maximised) { /* * GTK 1.2 doesn't know how to do this. */ #if GTK_CHECK_VERSION(2,0,0) GtkFrontend *inst = container_of(tw, GtkFrontend, termwin); if (maximised) gtk_window_maximize(GTK_WINDOW(inst->window)); else gtk_window_unmaximize(GTK_WINDOW(inst->window)); #endif } /* * Find out whether a dialog box already exists for this window in a * particular DialogSlot. If it does, uniconify it (if we can) and * raise it, so that the user realises they've already been asked this * question. */ static bool find_and_raise_dialog(GtkFrontend *inst, enum DialogSlot slot) { GtkWidget *dialog = inst->dialogs[slot]; if (!dialog) return false; #if GTK_CHECK_VERSION(2,0,0) gtk_window_deiconify(GTK_WINDOW(dialog)); #endif gdk_window_raise(gtk_widget_get_window(dialog)); return true; } static void warn_on_close_callback(void *vctx, int result) { GtkFrontend *inst = (GtkFrontend *)vctx; unregister_dialog(&inst->seat, DIALOG_SLOT_WARN_ON_CLOSE); if (result) gtk_widget_destroy(inst->window); } /* * Handle the 'delete window' event (e.g. user clicking the WM close * button). The return value false means the window should close, and * true means it shouldn't. * * (That's counterintuitive, but really, in GTK terms, true means 'I * have done everything necessary to handle this event, so the default * handler need not do anything', i.e. 'suppress default handler', * i.e. 'do not close the window'.) */ gint delete_window(GtkWidget *widget, GdkEvent *event, GtkFrontend *inst) { if (!inst->exited && conf_get_bool(inst->conf, CONF_warn_on_close)) { /* * We're not going to exit right now. We must put up a * warn-on-close dialog, unless one already exists, in which * case we'll just re-emphasise that one. */ if (!find_and_raise_dialog(inst, DIALOG_SLOT_WARN_ON_CLOSE)) { char *title = dupcat(appname, " Exit Confirmation"); char *msg, *additional = NULL; if (inst->backend && inst->backend->vt->close_warn_text) { additional = inst->backend->vt->close_warn_text(inst->backend); } msg = dupprintf("Are you sure you want to close this session?%s%s", additional ? "\n" : "", additional ? additional : ""); GtkWidget *dialog = create_message_box( inst->window, title, msg, string_width("Most of the width of the above text"), false, &buttons_yn, warn_on_close_callback, inst); register_dialog(&inst->seat, DIALOG_SLOT_WARN_ON_CLOSE, dialog); sfree(title); sfree(msg); sfree(additional); } return true; } return false; } #if GTK_CHECK_VERSION(2,0,0) static void window_state_event(GtkWidget *widget, GdkEventWindowState *event, gpointer user_data) { GtkFrontend *inst = (GtkFrontend *)user_data; term_notify_minimised( inst->term, event->new_window_state & GDK_WINDOW_STATE_ICONIFIED); } #endif static void update_mouseptr(GtkFrontend *inst) { switch (inst->busy_status) { case BUSY_NOT: if (!inst->mouseptr_visible) { gdk_window_set_cursor(gtk_widget_get_window(inst->area), inst->blankcursor); } else if (inst->pointer_indicates_raw_mouse) { gdk_window_set_cursor(gtk_widget_get_window(inst->area), inst->rawcursor); } else { gdk_window_set_cursor(gtk_widget_get_window(inst->area), inst->textcursor); } break; case BUSY_WAITING: /* XXX can we do better? */ case BUSY_CPU: /* We always display these cursors. */ gdk_window_set_cursor(gtk_widget_get_window(inst->area), inst->waitcursor); break; default: unreachable("Bad busy_status"); } } static void show_mouseptr(GtkFrontend *inst, bool show) { if (!conf_get_bool(inst->conf, CONF_hide_mouseptr)) show = true; inst->mouseptr_visible = show; update_mouseptr(inst); } static void draw_backing_rect(GtkFrontend *inst); static void drawing_area_setup(GtkFrontend *inst, int width, int height) { int w, h, new_scale; bool need_size = false; /* * See if the terminal size has changed. */ w = (width - 2*inst->window_border) / inst->font_width; h = (height - 2*inst->window_border) / inst->font_height; if (w != inst->width || h != inst->height) { /* * Update conf. */ inst->width = w; inst->height = h; conf_set_int(inst->conf, CONF_width, inst->width); conf_set_int(inst->conf, CONF_height, inst->height); /* * We'll need to tell terminal.c about the resize below. */ need_size = true; /* * And we must refresh the window's backing image. */ inst->drawing_area_setup_needed = true; } #if GTK_CHECK_VERSION(3,10,0) new_scale = gtk_widget_get_scale_factor(inst->area); if (new_scale != inst->scale) inst->drawing_area_setup_needed = true; #else new_scale = 1; #endif int new_backing_w = w * inst->font_width + 2*inst->window_border; int new_backing_h = h * inst->font_height + 2*inst->window_border; new_backing_w *= new_scale; new_backing_h *= new_scale; if (inst->backing_w != new_backing_w || inst->backing_h != new_backing_h) inst->drawing_area_setup_needed = true; /* * This event might be spurious; some GTK setups have been known * to call it when nothing at all has changed. Check if we have * any reason to proceed. */ if (!inst->drawing_area_setup_needed) return; inst->drawing_area_setup_needed = false; inst->scale = new_scale; inst->backing_w = new_backing_w; inst->backing_h = new_backing_h; #ifndef NO_BACKING_PIXMAPS if (inst->pixmap) { gdk_pixmap_unref(inst->pixmap); inst->pixmap = NULL; } inst->pixmap = gdk_pixmap_new(gtk_widget_get_window(inst->area), inst->backing_w, inst->backing_h, -1); #endif #ifdef DRAW_TEXT_CAIRO if (inst->surface) { cairo_surface_destroy(inst->surface); inst->surface = NULL; } inst->surface = cairo_image_surface_create( CAIRO_FORMAT_ARGB32, inst->backing_w, inst->backing_h); #endif 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, gtk_widget_get_window(inst->area)); #endif } static void drawing_area_setup_simple(GtkFrontend *inst) { /* * Wrapper on drawing_area_setup which fetches the width and * height of the drawing area. We go directly to the inner version * in the case where a new size allocation comes in (just in case * GTK hasn't installed it in the normal place yet). */ #if GTK_CHECK_VERSION(2,0,0) GdkRectangle alloc; gtk_widget_get_allocation(inst->area, &alloc); #else GtkAllocation alloc = inst->area->allocation; #endif drawing_area_setup(inst, alloc.width, alloc.height); } static void area_realised(GtkWidget *widget, GtkFrontend *inst) { inst->drawing_area_realised = true; if (inst->drawing_area_realised && inst->drawing_area_got_size && inst->drawing_area_setup_needed) drawing_area_setup_simple(inst); } static void area_size_allocate( GtkWidget *widget, GdkRectangle *alloc, GtkFrontend *inst) { inst->drawing_area_got_size = true; if (inst->drawing_area_realised && inst->drawing_area_got_size) drawing_area_setup(inst, alloc->width, alloc->height); } #if GTK_CHECK_VERSION(3,10,0) static void area_check_scale(GtkFrontend *inst) { if (!inst->drawing_area_setup_needed && inst->scale != gtk_widget_get_scale_factor(inst->area)) { drawing_area_setup_simple(inst); if (inst->term) { term_invalidate(inst->term); term_update(inst->term); } } } #endif static gboolean window_configured( GtkWidget *widget, GdkEventConfigure *event, gpointer data) { GtkFrontend *inst = (GtkFrontend *)data; if (inst->term) { term_notify_window_pos(inst->term, event->x, event->y); term_notify_window_size_pixels( inst->term, event->width, event->height); } return false; } #if GTK_CHECK_VERSION(3,10,0) static gboolean area_configured( GtkWidget *widget, GdkEventConfigure *event, gpointer data) { GtkFrontend *inst = (GtkFrontend *)data; area_check_scale(inst); return false; } #endif #ifdef DRAW_TEXT_CAIRO static void cairo_setup_draw_ctx(GtkFrontend *inst) { cairo_get_matrix(inst->uctx.u.cairo.cr, &inst->uctx.u.cairo.origmatrix); cairo_set_line_width(inst->uctx.u.cairo.cr, 1.0); cairo_set_line_cap(inst->uctx.u.cairo.cr, CAIRO_LINE_CAP_SQUARE); cairo_set_line_join(inst->uctx.u.cairo.cr, CAIRO_LINE_JOIN_MITER); /* This antialiasing setting appears to be ignored for Pango * font rendering but honoured for stroking and filling paths; * I don't quite understand the logic of that, but I won't * complain since it's exactly what I happen to want */ cairo_set_antialias(inst->uctx.u.cairo.cr, CAIRO_ANTIALIAS_NONE); } #endif #if GTK_CHECK_VERSION(3,0,0) static gint draw_area(GtkWidget *widget, cairo_t *cr, gpointer data) { GtkFrontend *inst = (GtkFrontend *)data; #if GTK_CHECK_VERSION(3,10,0) /* * This may be the first we hear of the window scale having * changed, in which case we must hastily reconstruct our backing * surface before we copy the wrong one into the newly resized * real window. */ area_check_scale(inst); #endif /* * GTK3 window redraw: we always expect Cairo to be enabled, so * that inst->surface exists, and pixmaps to be disabled, so that * inst->pixmap does not exist. Hence, we just blit from * inst->surface to the window. */ if (inst->surface) { GdkRectangle dirtyrect; cairo_surface_t *target_surface; double orig_sx, orig_sy; cairo_matrix_t m; /* * Furtle around in the Cairo setup to force the device scale * back to 1, so that when we blit a collection of pixels from * our backing surface into the window, they really are * _pixels_ and not some confusing antialiased slightly-offset * 2x2 rectangle of pixeloids. * * I have no idea whether GTK expects me not to mess with the * device scale in the cairo_surface_t backing its window, so * I carefully put it back when I've finished. * * In some GTK setups, the Cairo context we're given may not * have a zero translation offset in its matrix, in which case * we have to adjust that to compensate for the change of * scale, or else the old translation offset (designed for the * old scale) will be multiplied by the new scale instead and * put everything in the wrong place. */ target_surface = cairo_get_target(cr); cairo_get_matrix(cr, &m); cairo_surface_get_device_scale(target_surface, &orig_sx, &orig_sy); cairo_surface_set_device_scale(target_surface, 1.0, 1.0); cairo_translate(cr, m.x0 * (orig_sx - 1.0), m.y0 * (orig_sy - 1.0)); gdk_cairo_get_clip_rectangle(cr, &dirtyrect); cairo_set_source_surface(cr, inst->surface, 0, 0); cairo_rectangle(cr, dirtyrect.x, dirtyrect.y, dirtyrect.width, dirtyrect.height); cairo_fill(cr); cairo_surface_set_device_scale(target_surface, orig_sx, orig_sy); } return true; } #else gint expose_area(GtkWidget *widget, GdkEventExpose *event, gpointer data) { GtkFrontend *inst = (GtkFrontend *)data; #ifndef NO_BACKING_PIXMAPS /* * Draw to the exposed part of the window from the server-side * backing pixmap. */ if (inst->pixmap) { gdk_draw_pixmap(gtk_widget_get_window(widget), (gtk_widget_get_style(widget)->fg_gc [gtk_widget_get_state(widget)]), inst->pixmap, event->area.x, event->area.y, event->area.x, event->area.y, event->area.width, event->area.height); } #else /* * Failing that, draw from the client-side Cairo surface. (We * should never be compiled in a context where we have _neither_ * inst->surface nor inst->pixmap.) */ if (inst->surface) { cairo_t *cr = gdk_cairo_create(gtk_widget_get_window(widget)); cairo_set_source_surface(cr, inst->surface, 0, 0); cairo_rectangle(cr, event->area.x, event->area.y, event->area.width, event->area.height); cairo_fill(cr); cairo_destroy(cr); } #endif return true; } #endif #define KEY_PRESSED(k) \ (inst->keystate[(k) / 32] & (1 << ((k) % 32))) #ifdef KEY_EVENT_DIAGNOSTICS char *dup_keyval_name(guint keyval) { const char *name = gdk_keyval_name(keyval); if (name) return dupstr(name); else return dupprintf("UNKNOWN[%u]", (unsigned)keyval); } #endif static void change_font_size(GtkFrontend *inst, int increment); static void key_pressed(GtkFrontend *inst); /* Subroutine used in key_event */ static int return_key(GtkFrontend *inst, char *output, bool *special) { int end; /* Ugly label so we can come here as a fallback from * numeric keypad Enter handling */ if (inst->term->cr_lf_return) { #ifdef KEY_EVENT_DIAGNOSTICS debug(" - Return in cr_lf_return mode, translating as 0d 0a\n"); #endif output[1] = '\015'; output[2] = '\012'; end = 3; } else { #ifdef KEY_EVENT_DIAGNOSTICS debug(" - Return special case, translating as 0d + special\n"); #endif output[1] = '\015'; end = 2; *special = true; } return end; } gint key_event(GtkWidget *widget, GdkEventKey *event, gpointer data) { GtkFrontend *inst = (GtkFrontend *)data; char output[256]; wchar_t ucsoutput[2]; int ucsval, start, end, output_charset; bool special, use_ucsoutput; bool force_format_numeric_keypad = false; bool generated_something = false; char num_keypad_key = '\0'; const char *event_string = event->string ? event->string : ""; noise_ultralight(NOISE_SOURCE_KEY, event->keyval); #ifdef OSX_META_KEY_CONFIG if (event->state & inst->system_mod_mask) return false; /* let GTK process OS X Command key */ #endif /* 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; #ifdef KEY_EVENT_DIAGNOSTICS { char *type_string, *state_string, *keyval_string, *string_string; type_string = (event->type == GDK_KEY_PRESS ? dupstr("PRESS") : event->type == GDK_KEY_RELEASE ? dupstr("RELEASE") : dupprintf("UNKNOWN[%d]", (int)event->type)); { static const struct { int mod_bit; const char *name; } mod_bits[] = { {GDK_SHIFT_MASK, "SHIFT"}, {GDK_LOCK_MASK, "LOCK"}, {GDK_CONTROL_MASK, "CONTROL"}, {GDK_MOD1_MASK, "MOD1"}, {GDK_MOD2_MASK, "MOD2"}, {GDK_MOD3_MASK, "MOD3"}, {GDK_MOD4_MASK, "MOD4"}, {GDK_MOD5_MASK, "MOD5"}, {GDK_SUPER_MASK, "SUPER"}, {GDK_HYPER_MASK, "HYPER"}, {GDK_META_MASK, "META"}, }; int i; int val = event->state; state_string = dupstr(""); for (i = 0; i < lenof(mod_bits); i++) { if (val & mod_bits[i].mod_bit) { char *old = state_string; state_string = dupcat(state_string, state_string[0] ? "|" : "", mod_bits[i].name); sfree(old); val &= ~mod_bits[i].mod_bit; } } if (val || !state_string[0]) { char *old = state_string; state_string = dupprintf("%s%s%d", state_string, state_string[0] ? "|" : "", val); sfree(old); } } keyval_string = dup_keyval_name(event->keyval); string_string = dupstr(""); { int i; for (i = 0; event_string[i]; i++) { char *old = string_string; string_string = dupprintf("%s%s%02x", string_string, string_string[0] ? " " : "", (unsigned)event_string[i] & 0xFF); sfree(old); } } debug("key_event: type=%s keyval=%s state=%s " "hardware_keycode=%d is_modifier=%s string=[%s]\n", type_string, keyval_string, state_string, (int)event->hardware_keycode, event->is_modifier ? "true" : "false", string_string); sfree(type_string); sfree(state_string); sfree(keyval_string); sfree(string_string); } #endif /* KEY_EVENT_DIAGNOSTICS */ /* * 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_KEY_Meta_L || event->keyval == GDK_KEY_Meta_R || event->keyval == GDK_KEY_Alt_L || event->keyval == GDK_KEY_Alt_R) && inst->alt_keycode >= 0 && inst->alt_digits > 1) { #ifdef KEY_EVENT_DIAGNOSTICS debug(" - modifier release terminates Alt+numberpad input, " "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) #ifdef KEY_EVENT_DIAGNOSTICS debug(" - key release, passing to IM\n"); #endif if (gtk_im_context_filter_keypress(inst->imc, event)) { #ifdef KEY_EVENT_DIAGNOSTICS debug(" - key release accepted by IM\n"); #endif return true; } else { #ifdef KEY_EVENT_DIAGNOSTICS debug(" - key release not accepted by IM\n"); #endif } #endif } if (event->type == GDK_KEY_PRESS) { /* * If Alt has just been pressed, we start potentially * accumulating an Alt+numberpad code. We do this by * setting alt_keycode to -1 (nothing yet but plausible). */ if ((event->keyval == GDK_KEY_Meta_L || event->keyval == GDK_KEY_Meta_R || event->keyval == GDK_KEY_Alt_L || event->keyval == GDK_KEY_Alt_R)) { #ifdef KEY_EVENT_DIAGNOSTICS debug(" - modifier press potentially begins Alt+numberpad " "input\n"); #endif inst->alt_keycode = -1; inst->alt_digits = 0; goto done; /* this generates nothing else */ } /* * If we're seeing a numberpad key press with Meta down, * consider adding it to alt_keycode if that's sensible. * Anything _else_ with Meta down cancels any possibility * of an ALT keycode: we set alt_keycode to -2. */ if ((event->state & inst->meta_mod_mask) && inst->alt_keycode != -2) { int digit = -1; switch (event->keyval) { case GDK_KEY_KP_0: case GDK_KEY_KP_Insert: digit = 0; break; case GDK_KEY_KP_1: case GDK_KEY_KP_End: digit = 1; break; case GDK_KEY_KP_2: case GDK_KEY_KP_Down: digit = 2; break; case GDK_KEY_KP_3: case GDK_KEY_KP_Page_Down: digit = 3; break; case GDK_KEY_KP_4: case GDK_KEY_KP_Left: digit = 4; break; case GDK_KEY_KP_5: case GDK_KEY_KP_Begin: digit = 5; break; case GDK_KEY_KP_6: case GDK_KEY_KP_Right: digit = 6; break; case GDK_KEY_KP_7: case GDK_KEY_KP_Home: digit = 7; break; case GDK_KEY_KP_8: case GDK_KEY_KP_Up: digit = 8; break; case GDK_KEY_KP_9: case GDK_KEY_KP_Page_Up: digit = 9; break; } if (digit < 0) inst->alt_keycode = -2; /* it's invalid */ else { #if defined(DEBUG) && defined(KEY_EVENT_DIAGNOSTICS) int old_keycode = inst->alt_keycode; #endif if (inst->alt_keycode == -1) inst->alt_keycode = digit; /* one-digit code */ else inst->alt_keycode = inst->alt_keycode * 10 + digit; inst->alt_digits++; #ifdef KEY_EVENT_DIAGNOSTICS debug(" - Alt+numberpad digit %d added to keycode %d" " gives %d\n", digit, old_keycode, inst->alt_keycode); #endif /* Having used this digit, we now do nothing more with it. */ goto done; } } if (event->keyval == GDK_KEY_greater && (event->state & GDK_CONTROL_MASK)) { #ifdef KEY_EVENT_DIAGNOSTICS debug(" - Ctrl->: increase font size\n"); #endif change_font_size(inst, +1); return true; } if (event->keyval == GDK_KEY_less && (event->state & GDK_CONTROL_MASK)) { #ifdef KEY_EVENT_DIAGNOSTICS debug(" - Ctrl-<: increase font size\n"); #endif change_font_size(inst, -1); return true; } /* * Shift-PgUp and Shift-PgDn don't even generate keystrokes * at all. */ if (event->keyval == GDK_KEY_Page_Up && ((event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) == (GDK_CONTROL_MASK | GDK_SHIFT_MASK))) { #ifdef KEY_EVENT_DIAGNOSTICS debug(" - Ctrl-Shift-PgUp scroll\n"); #endif term_scroll(inst->term, 1, 0); return true; } if (event->keyval == GDK_KEY_Page_Up && (event->state & GDK_SHIFT_MASK)) { #ifdef KEY_EVENT_DIAGNOSTICS debug(" - Shift-PgUp scroll\n"); #endif term_scroll(inst->term, 0, -inst->height/2); return true; } if (event->keyval == GDK_KEY_Page_Up && (event->state & GDK_CONTROL_MASK)) { #ifdef KEY_EVENT_DIAGNOSTICS debug(" - Ctrl-PgUp scroll\n"); #endif term_scroll(inst->term, 0, -1); return true; } if (event->keyval == GDK_KEY_Page_Down && ((event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)) == (GDK_CONTROL_MASK | GDK_SHIFT_MASK))) { #ifdef KEY_EVENT_DIAGNOSTICS debug(" - Ctrl-shift-PgDn scroll\n"); #endif term_scroll(inst->term, -1, 0); return true; } if (event->keyval == GDK_KEY_Page_Down && (event->state & GDK_SHIFT_MASK)) { #ifdef KEY_EVENT_DIAGNOSTICS debug(" - Shift-PgDn scroll\n"); #endif term_scroll(inst->term, 0, +inst->height/2); return true; } if (event->keyval == GDK_KEY_Page_Down && (event->state & GDK_CONTROL_MASK)) { #ifdef KEY_EVENT_DIAGNOSTICS debug(" - Ctrl-PgDn scroll\n"); #endif term_scroll(inst->term, 0, +1); return true; } /* * Neither do Shift-Ins or Ctrl-Ins (if enabled). */ if (event->keyval == GDK_KEY_Insert && (event->state & GDK_SHIFT_MASK)) { int cfgval = conf_get_int(inst->conf, CONF_ctrlshiftins); switch (cfgval) { case CLIPUI_IMPLICIT: #ifdef KEY_EVENT_DIAGNOSTICS debug(" - Shift-Insert: paste from PRIMARY\n"); #endif term_request_paste(inst->term, CLIP_PRIMARY); return true; case CLIPUI_EXPLICIT: #ifdef KEY_EVENT_DIAGNOSTICS debug(" - Shift-Insert: paste from CLIPBOARD\n"); #endif term_request_paste(inst->term, CLIP_CLIPBOARD); return true; case CLIPUI_CUSTOM: #ifdef KEY_EVENT_DIAGNOSTICS debug(" - Shift-Insert: paste from custom clipboard\n"); #endif term_request_paste(inst->term, inst->clipboard_ctrlshiftins); return true; default: #ifdef KEY_EVENT_DIAGNOSTICS debug(" - Shift-Insert: no paste action\n"); #endif break; } } if (event->keyval == GDK_KEY_Insert && (event->state & GDK_CONTROL_MASK)) { static const int clips_clipboard[] = { CLIP_CLIPBOARD }; int cfgval = conf_get_int(inst->conf, CONF_ctrlshiftins); switch (cfgval) { case CLIPUI_IMPLICIT: /* do nothing; re-copy to PRIMARY is not needed */ #ifdef KEY_EVENT_DIAGNOSTICS debug(" - Ctrl-Insert: non-copy to PRIMARY\n"); #endif return true; case CLIPUI_EXPLICIT: #ifdef KEY_EVENT_DIAGNOSTICS debug(" - Ctrl-Insert: copy to CLIPBOARD\n"); #endif term_request_copy(inst->term, clips_clipboard, lenof(clips_clipboard)); return true; case CLIPUI_CUSTOM: #ifdef KEY_EVENT_DIAGNOSTICS debug(" - Ctrl-Insert: copy to custom clipboard\n"); #endif term_request_copy(inst->term, &inst->clipboard_ctrlshiftins, 1); return true; default: #ifdef KEY_EVENT_DIAGNOSTICS debug(" - Ctrl-Insert: no copy action\n"); #endif break; } } /* * Another pair of copy-paste keys. */ if ((event->state & GDK_SHIFT_MASK) && (event->state & GDK_CONTROL_MASK) && (event->keyval == GDK_KEY_C || event->keyval == GDK_KEY_c || event->keyval == GDK_KEY_V || event->keyval == GDK_KEY_v)) { int cfgval = conf_get_int(inst->conf, CONF_ctrlshiftcv); bool paste = (event->keyval == GDK_KEY_V || event->keyval == GDK_KEY_v); switch (cfgval) { case CLIPUI_IMPLICIT: if (paste) { #ifdef KEY_EVENT_DIAGNOSTICS debug(" - Ctrl-Shift-V: paste from PRIMARY\n"); #endif term_request_paste(inst->term, CLIP_PRIMARY); } else { #ifdef KEY_EVENT_DIAGNOSTICS debug(" - Ctrl-Shift-C: non-copy to PRIMARY\n"); #endif } return true; case CLIPUI_EXPLICIT: if (paste) { #ifdef KEY_EVENT_DIAGNOSTICS debug(" - Ctrl-Shift-V: paste from CLIPBOARD\n"); #endif term_request_paste(inst->term, CLIP_CLIPBOARD); } else { static const int clips[] = { CLIP_CLIPBOARD }; #ifdef KEY_EVENT_DIAGNOSTICS debug(" - Ctrl-Shift-C: copy to CLIPBOARD\n"); #endif term_request_copy(inst->term, clips, lenof(clips)); } return true; case CLIPUI_CUSTOM: if (paste) { #ifdef KEY_EVENT_DIAGNOSTICS debug(" - Ctrl-Shift-V: paste from custom clipboard\n"); #endif term_request_paste(inst->term, inst->clipboard_ctrlshiftcv); } else { #ifdef KEY_EVENT_DIAGNOSTICS debug(" - Ctrl-Shift-C: copy to custom clipboard\n"); #endif term_request_copy(inst->term, &inst->clipboard_ctrlshiftcv, 1); } return true; } } special = false; use_ucsoutput = false; /* 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 /* !GTK_CHECK_VERSION(2,0,0) */ /* * 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. */ bool numeric = false; bool nethack_mode = conf_get_bool(inst->conf, CONF_nethack_keypad); bool app_keypad_mode = (inst->term->app_keypad_keys && !conf_get_bool(inst->conf, CONF_no_applic_k)); switch (event->keyval) { case GDK_KEY_Num_Lock: num_keypad_key = 'G'; break; case GDK_KEY_KP_Divide: num_keypad_key = '/'; break; case GDK_KEY_KP_Multiply: num_keypad_key = '*'; break; case GDK_KEY_KP_Subtract: num_keypad_key = '-'; break; case GDK_KEY_KP_Add: num_keypad_key = '+'; break; case GDK_KEY_KP_Enter: num_keypad_key = '\r'; break; case GDK_KEY_KP_0: num_keypad_key = '0'; numeric = true; break; case GDK_KEY_KP_Insert: num_keypad_key = '0'; break; case GDK_KEY_KP_1: num_keypad_key = '1'; numeric = true; break; case GDK_KEY_KP_End: num_keypad_key = '1'; break; case GDK_KEY_KP_2: num_keypad_key = '2'; numeric = true; break; case GDK_KEY_KP_Down: num_keypad_key = '2'; break; case GDK_KEY_KP_3: num_keypad_key = '3'; numeric = true; break; case GDK_KEY_KP_Page_Down: num_keypad_key = '3'; break; case GDK_KEY_KP_4: num_keypad_key = '4'; numeric = true; break; case GDK_KEY_KP_Left: num_keypad_key = '4'; break; case GDK_KEY_KP_5: num_keypad_key = '5'; numeric = true; break; case GDK_KEY_KP_Begin: num_keypad_key = '5'; break; case GDK_KEY_KP_6: num_keypad_key = '6'; numeric = true; break; case GDK_KEY_KP_Right: num_keypad_key = '6'; break; case GDK_KEY_KP_7: num_keypad_key = '7'; numeric = true; break; case GDK_KEY_KP_Home: num_keypad_key = '7'; break; case GDK_KEY_KP_8: num_keypad_key = '8'; numeric = true; break; case GDK_KEY_KP_Up: num_keypad_key = '8'; break; case GDK_KEY_KP_9: num_keypad_key = '9'; numeric = true; break; case GDK_KEY_KP_Page_Up: num_keypad_key = '9'; break; case GDK_KEY_KP_Decimal: num_keypad_key = '.'; numeric = true; break; case GDK_KEY_KP_Delete: num_keypad_key = '.'; break; } if ((app_keypad_mode && num_keypad_key && (numeric || inst->term->funky_type != FUNKY_XTERM)) || (nethack_mode && num_keypad_key >= '1' && num_keypad_key <= '9')) { /* In these modes, we override the keypad handling: * regardless of Num Lock, the keys are handled by * format_numeric_keypad_key below. */ force_format_numeric_keypad = true; } else { bool try_filter = true; #ifdef META_MANUAL_MASK if (event->state & META_MANUAL_MASK & inst->meta_mod_mask) { /* * If this key event had a Meta modifier bit set which * is also in META_MANUAL_MASK, that means passing * such an event to the GtkIMContext will be unhelpful * (it will eat the keystroke and turn it into * something not what we wanted). */ #ifdef KEY_EVENT_DIAGNOSTICS debug(" - Meta modifier requiring manual intervention, " "suppressing IM filtering\n"); #endif try_filter = false; } #endif if (try_filter) { #ifdef KEY_EVENT_DIAGNOSTICS debug(" - general key press, passing to IM\n"); #endif if (gtk_im_context_filter_keypress(inst->imc, event)) { #ifdef KEY_EVENT_DIAGNOSTICS debug(" - key press accepted by IM\n"); #endif return true; } else { #ifdef KEY_EVENT_DIAGNOSTICS debug(" - key press not accepted by IM\n"); #endif } } } /* * 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); #ifdef KEY_EVENT_DIAGNOSTICS { char *string_string = dupstr(""); int i; for (i = 0; i < wlen; i++) { char *old = string_string; string_string = dupprintf("%s%s%04x", string_string, string_string[0] ? " " : "", (unsigned)widedata[i]); sfree(old); } debug(" - string translated into Unicode = [%s]\n", string_string); sfree(string_string); } #endif wp = widedata; ulen = charset_from_unicode(&wp, &wlen, output+1, lenof(output)-2, CS_UTF8, NULL, NULL, 0); #ifdef KEY_EVENT_DIAGNOSTICS { char *string_string = dupstr(""); int i; for (i = 0; i < ulen; i++) { char *old = string_string; string_string = dupprintf("%s%s%02x", string_string, string_string[0] ? " " : "", (unsigned)output[i+1] & 0xFF); sfree(old); } debug(" - string translated into UTF-8 = [%s]\n", string_string); sfree(string_string); } #endif output[1+ulen] = '\0'; } #endif /* !GTK_CHECK_VERSION(2,0,0) */ if (!output[1] && (ucsval = keysym_to_unicode(event->keyval)) >= 0) { ucsoutput[0] = '\033'; ucsoutput[1] = ucsval; #ifdef KEY_EVENT_DIAGNOSTICS debug(" - keysym_to_unicode gave %04x\n", (unsigned)ucsoutput[1]); #endif use_ucsoutput = true; end = 2; } else { output[lenof(output)-1] = '\0'; end = strlen(output); } if (event->state & inst->meta_mod_mask) { start = 0; if (end == 1) end = 0; #ifdef META_MANUAL_MASK if (event->state & META_MANUAL_MASK) { /* * Key events which have a META_MANUAL_MASK meta bit * set may have a keyval reflecting that, e.g. on OS X * the Option key acts as an AltGr-like modifier and * causes different Unicode characters to be output. * * To work around this, we clear the dangerous * modifier bit and retranslate from the hardware * keycode as if the key had been pressed without that * modifier. Then we prefix Esc to *that*. */ guint new_keyval; GdkModifierType consumed; if (gdk_keymap_translate_keyboard_state (gdk_keymap_get_for_display(gdk_display_get_default()), event->hardware_keycode, event->state & ~META_MANUAL_MASK, 0, &new_keyval, NULL, NULL, &consumed)) { ucsoutput[0] = '\033'; ucsoutput[1] = gdk_keyval_to_unicode(new_keyval); #ifdef KEY_EVENT_DIAGNOSTICS { char *keyval_name = dup_keyval_name(new_keyval); debug(" - retranslation for manual Meta: " "new keyval = %s, Unicode = %04x\n", keyval_name, (unsigned)ucsoutput[1]); sfree(keyval_name); } #endif use_ucsoutput = true; end = 2; } } #endif } else start = 1; /* Control-` is the same as Control-\ (unless gtk has a better idea) */ if (!output[1] && event->keyval == '`' && (event->state & GDK_CONTROL_MASK)) { #ifdef KEY_EVENT_DIAGNOSTICS debug(" - Ctrl-` special case, translating as 1c\n"); #endif output[1] = '\x1C'; use_ucsoutput = false; end = 2; } /* Some GTK backends (e.g. Quartz) do not change event->string * in response to the Control modifier. So we do it ourselves * here, if it's not already happened. * * The translations below are in line with X11 policy as far * as I know. */ if ((event->state & GDK_CONTROL_MASK) && end == 2) { int orig = use_ucsoutput ? ucsoutput[1] : output[1]; int new = orig; if (new >= '3' && new <= '7') { /* ^3,...,^7 map to 0x1B,...,0x1F */ new += '\x1B' - '3'; } else if (new == '2' || new == ' ') { /* ^2 and ^Space are both ^@, i.e. \0 */ new = '\0'; } else if (new == '8') { /* ^8 is DEL */ new = '\x7F'; } else if (new == '/') { /* ^/ is the same as ^_ */ new = '\x1F'; } else if (new >= 0x40 && new < 0x7F) { /* Everything anywhere near the alphabetics just gets * masked. */ new &= 0x1F; } /* Anything else, e.g. '0', is unchanged. */ if (orig == new) { #ifdef KEY_EVENT_DIAGNOSTICS debug(" - manual Ctrl key handling did nothing\n"); #endif } else { #ifdef KEY_EVENT_DIAGNOSTICS debug(" - manual Ctrl key handling: %02x -> %02x\n", (unsigned)orig, (unsigned)new); #endif output[1] = new; use_ucsoutput = false; } } /* Control-Break sends a Break special to the backend */ if (event->keyval == GDK_KEY_Break && (event->state & GDK_CONTROL_MASK)) { #ifdef KEY_EVENT_DIAGNOSTICS debug(" - Ctrl-Break special case, sending SS_BRK\n"); #endif if (inst->backend) backend_special(inst->backend, SS_BRK, 0); return true; } /* We handle Return ourselves, because it needs to be flagged as * special to ldisc. */ if (event->keyval == GDK_KEY_Return) { end = return_key(inst, output, &special); use_ucsoutput = false; } /* Control-2, Control-Space and Control-@ are NUL */ if (!output[1] && (event->keyval == ' ' || event->keyval == '2' || event->keyval == '@') && (event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK)) == GDK_CONTROL_MASK) { #ifdef KEY_EVENT_DIAGNOSTICS debug(" - Ctrl-{space,2,@} special case, translating as 00\n"); #endif output[1] = '\0'; use_ucsoutput = false; end = 2; } /* Control-Shift-Space is 160 (ISO8859 nonbreaking space) */ if (!output[1] && event->keyval == ' ' && (event->state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK)) == (GDK_SHIFT_MASK | GDK_CONTROL_MASK)) { #ifdef KEY_EVENT_DIAGNOSTICS debug(" - Ctrl-Shift-space special case, translating as 00a0\n"); #endif output[1] = '\240'; output_charset = CS_ISO8859_1; use_ucsoutput = false; end = 2; } /* We don't let GTK tell us what Backspace is! We know better. */ if (event->keyval == GDK_KEY_BackSpace && !(event->state & GDK_SHIFT_MASK)) { output[1] = conf_get_bool(inst->conf, CONF_bksp_is_delete) ? '\x7F' : '\x08'; #ifdef KEY_EVENT_DIAGNOSTICS debug(" - Backspace, translating as %02x\n", (unsigned)output[1]); #endif use_ucsoutput = false; end = 2; special = true; } /* For Shift Backspace, do opposite of what is configured. */ if (event->keyval == GDK_KEY_BackSpace && (event->state & GDK_SHIFT_MASK)) { output[1] = conf_get_bool(inst->conf, CONF_bksp_is_delete) ? '\x08' : '\x7F'; #ifdef KEY_EVENT_DIAGNOSTICS debug(" - Shift-Backspace, translating as %02x\n", (unsigned)output[1]); #endif use_ucsoutput = false; end = 2; special = true; } /* Shift-Tab is ESC [ Z */ if (event->keyval == GDK_KEY_ISO_Left_Tab || (event->keyval == GDK_KEY_Tab && (event->state & GDK_SHIFT_MASK))) { #ifdef KEY_EVENT_DIAGNOSTICS debug(" - Shift-Tab, translating as ESC [ Z\n"); #endif end = 1 + sprintf(output+1, "\033[Z"); use_ucsoutput = false; } /* And normal Tab is Tab, if the keymap hasn't already told us. * (Curiously, at least one version of the MacOS 10.5 X server * doesn't translate Tab for us. */ if (event->keyval == GDK_KEY_Tab && end <= 1) { #ifdef KEY_EVENT_DIAGNOSTICS debug(" - Tab, translating as 09\n"); #endif output[1] = '\t'; end = 2; } if (num_keypad_key && force_format_numeric_keypad) { end = 1 + format_numeric_keypad_key( output+1, inst->term, num_keypad_key, event->state & GDK_SHIFT_MASK, event->state & GDK_CONTROL_MASK); #ifdef KEY_EVENT_DIAGNOSTICS debug(" - numeric keypad key"); #endif use_ucsoutput = false; goto done; } switch (event->keyval) { int fkey_number; case GDK_KEY_F1: fkey_number = 1; goto numbered_function_key; case GDK_KEY_F2: fkey_number = 2; goto numbered_function_key; case GDK_KEY_F3: fkey_number = 3; goto numbered_function_key; case GDK_KEY_F4: fkey_number = 4; goto numbered_function_key; case GDK_KEY_F5: fkey_number = 5; goto numbered_function_key; case GDK_KEY_F6: fkey_number = 6; goto numbered_function_key; case GDK_KEY_F7: fkey_number = 7; goto numbered_function_key; case GDK_KEY_F8: fkey_number = 8; goto numbered_function_key; case GDK_KEY_F9: fkey_number = 9; goto numbered_function_key; case GDK_KEY_F10: fkey_number = 10; goto numbered_function_key; case GDK_KEY_F11: fkey_number = 11; goto numbered_function_key; case GDK_KEY_F12: fkey_number = 12; goto numbered_function_key; case GDK_KEY_F13: fkey_number = 13; goto numbered_function_key; case GDK_KEY_F14: fkey_number = 14; goto numbered_function_key; case GDK_KEY_F15: fkey_number = 15; goto numbered_function_key; case GDK_KEY_F16: fkey_number = 16; goto numbered_function_key; case GDK_KEY_F17: fkey_number = 17; goto numbered_function_key; case GDK_KEY_F18: fkey_number = 18; goto numbered_function_key; case GDK_KEY_F19: fkey_number = 19; goto numbered_function_key; case GDK_KEY_F20: fkey_number = 20; goto numbered_function_key; numbered_function_key: end = 1 + format_function_key(output+1, inst->term, fkey_number, event->state & GDK_SHIFT_MASK, event->state & GDK_CONTROL_MASK); #ifdef KEY_EVENT_DIAGNOSTICS debug(" - function key F%d", fkey_number); #endif use_ucsoutput = false; goto done; SmallKeypadKey sk_key; case GDK_KEY_Home: case GDK_KEY_KP_Home: sk_key = SKK_HOME; goto small_keypad_key; case GDK_KEY_Insert: case GDK_KEY_KP_Insert: sk_key = SKK_INSERT; goto small_keypad_key; case GDK_KEY_Delete: case GDK_KEY_KP_Delete: sk_key = SKK_DELETE; goto small_keypad_key; case GDK_KEY_End: case GDK_KEY_KP_End: sk_key = SKK_END; goto small_keypad_key; case GDK_KEY_Page_Up: case GDK_KEY_KP_Page_Up: sk_key = SKK_PGUP; goto small_keypad_key; case GDK_KEY_Page_Down: case GDK_KEY_KP_Page_Down: sk_key = SKK_PGDN; goto small_keypad_key; small_keypad_key: /* These keys don't generate terminal input with Ctrl */ if (event->state & GDK_CONTROL_MASK) break; end = 1 + format_small_keypad_key(output+1, inst->term, sk_key); #ifdef KEY_EVENT_DIAGNOSTICS debug(" - small keypad key"); #endif use_ucsoutput = false; goto done; int xkey; case GDK_KEY_Up: case GDK_KEY_KP_Up: xkey = 'A'; goto arrow_key; case GDK_KEY_Down: case GDK_KEY_KP_Down: xkey = 'B'; goto arrow_key; case GDK_KEY_Right: case GDK_KEY_KP_Right: xkey = 'C'; goto arrow_key; case GDK_KEY_Left: case GDK_KEY_KP_Left: xkey = 'D'; goto arrow_key; case GDK_KEY_Begin: case GDK_KEY_KP_Begin: xkey = 'G'; goto arrow_key; arrow_key: end = 1 + format_arrow_key(output+1, inst->term, xkey, event->state & GDK_CONTROL_MASK); #ifdef KEY_EVENT_DIAGNOSTICS debug(" - arrow key"); #endif use_ucsoutput = false; goto done; } if (num_keypad_key) { end = 1 + format_numeric_keypad_key( output+1, inst->term, num_keypad_key, event->state & GDK_SHIFT_MASK, event->state & GDK_CONTROL_MASK); #ifdef KEY_EVENT_DIAGNOSTICS debug(" - numeric keypad key"); #endif if (end == 1 && num_keypad_key == '\r') { /* Keypad Enter, lacking any other translation, * becomes the same special Return code as normal * Return. */ end = return_key(inst, output, &special); use_ucsoutput = false; } use_ucsoutput = false; goto done; } goto done; } done: if (end-start > 0) { if (special) { #ifdef KEY_EVENT_DIAGNOSTICS char *string_string = dupstr(""); int i; for (i = start; i < end; i++) { char *old = string_string; string_string = dupprintf("%s%s%02x", string_string, string_string[0] ? " " : "", (unsigned)output[i] & 0xFF); sfree(old); } debug(" - final output, special, generic encoding = [%s]\n", string_string); sfree(string_string); #endif /* * For special control characters, the character set * should never matter. */ output[end] = '\0'; /* NUL-terminate */ generated_something = true; term_keyinput(inst->term, -1, output+start, -2); } else if (!inst->direct_to_font) { if (!use_ucsoutput) { #ifdef KEY_EVENT_DIAGNOSTICS char *string_string = dupstr(""); int i; for (i = start; i < end; i++) { char *old = string_string; string_string = dupprintf("%s%s%02x", string_string, string_string[0] ? " " : "", (unsigned)output[i] & 0xFF); sfree(old); } debug(" - final output in %s = [%s]\n", charset_to_localenc(output_charset), string_string); sfree(string_string); #endif generated_something = true; term_keyinput(inst->term, output_charset, output+start, end-start); } else { #ifdef KEY_EVENT_DIAGNOSTICS char *string_string = dupstr(""); int i; for (i = start; i < end; i++) { char *old = string_string; string_string = dupprintf("%s%s%04x", string_string, string_string[0] ? " " : "", (unsigned)ucsoutput[i]); sfree(old); } debug(" - final output in Unicode = [%s]\n", string_string); sfree(string_string); #endif /* * We generated our own Unicode key data from the * keysym, so use that instead. */ generated_something = true; term_keyinputw(inst->term, ucsoutput+start, end-start); } } else { /* * In direct-to-font mode, we just send the string * exactly as we received it. */ #ifdef KEY_EVENT_DIAGNOSTICS char *string_string = dupstr(""); int i; for (i = start; i < end; i++) { char *old = string_string; string_string = dupprintf("%s%s%02x", string_string, string_string[0] ? " " : "", (unsigned)output[i] & 0xFF); sfree(old); } debug(" - final output in direct-to-font encoding = [%s]\n", string_string); sfree(string_string); #endif generated_something = true; term_keyinput(inst->term, -1, output+start, end-start); } show_mouseptr(inst, false); } if (generated_something) key_pressed(inst); return true; } #if GTK_CHECK_VERSION(2,0,0) void input_method_commit_event(GtkIMContext *imc, gchar *str, gpointer data) { GtkFrontend *inst = (GtkFrontend *)data; #ifdef KEY_EVENT_DIAGNOSTICS char *string_string = dupstr(""); int i; for (i = 0; str[i]; i++) { char *old = string_string; string_string = dupprintf("%s%s%02x", string_string, string_string[0] ? " " : "", (unsigned)str[i] & 0xFF); sfree(old); } debug(" - IM commit event in UTF-8 = [%s]\n", string_string); sfree(string_string); #endif term_keyinput(inst->term, CS_UTF8, str, strlen(str)); show_mouseptr(inst, false); key_pressed(inst); } #endif #define SCROLL_INCREMENT_LINES 5 #if GTK_CHECK_VERSION(3,4,0) gboolean scroll_internal(GtkFrontend *inst, gdouble delta, guint state, gdouble ex, gdouble ey) { int x, y; bool shift, ctrl, alt, raw_mouse_mode; show_mouseptr(inst, true); shift = state & GDK_SHIFT_MASK; ctrl = state & GDK_CONTROL_MASK; alt = state & inst->meta_mod_mask; x = (ex - inst->window_border) / inst->font_width; y = (ey - inst->window_border) / inst->font_height; raw_mouse_mode = (inst->send_raw_mouse && !(shift && conf_get_bool(inst->conf, CONF_mouse_override))); inst->cumulative_scroll += delta * SCROLL_INCREMENT_LINES; if (!raw_mouse_mode) { int scroll_lines = (int)inst->cumulative_scroll; /* rounds toward 0 */ if (scroll_lines) { term_scroll(inst->term, 0, scroll_lines); inst->cumulative_scroll -= scroll_lines; } return true; } else { int scroll_events = (int)(inst->cumulative_scroll / SCROLL_INCREMENT_LINES); if (scroll_events) { int button; inst->cumulative_scroll -= scroll_events * SCROLL_INCREMENT_LINES; if (scroll_events > 0) { button = MBT_WHEEL_DOWN; } else { button = MBT_WHEEL_UP; scroll_events = -scroll_events; } while (scroll_events-- > 0) { term_mouse(inst->term, button, translate_button(button), MA_CLICK, x, y, shift, ctrl, alt); } } return true; } } #endif static gboolean button_internal(GtkFrontend *inst, GdkEventButton *event) { bool shift, ctrl, alt, raw_mouse_mode; int x, y, button, act; /* Remember the timestamp. */ inst->input_event_time = event->time; noise_ultralight(NOISE_SOURCE_MOUSEBUTTON, event->button); show_mouseptr(inst, true); shift = event->state & GDK_SHIFT_MASK; ctrl = event->state & GDK_CONTROL_MASK; alt = event->state & inst->meta_mod_mask; raw_mouse_mode = (inst->send_raw_mouse && !(shift && conf_get_bool(inst->conf, CONF_mouse_override))); if (!raw_mouse_mode) { if (event->button == 4 && event->type == GDK_BUTTON_PRESS) { term_scroll(inst->term, 0, -SCROLL_INCREMENT_LINES); return true; } if (event->button == 5 && event->type == GDK_BUTTON_PRESS) { term_scroll(inst->term, 0, +SCROLL_INCREMENT_LINES); return true; } } if (event->button == 3 && ctrl) { #if GTK_CHECK_VERSION(3,22,0) gtk_menu_popup_at_pointer(GTK_MENU(inst->menu), (GdkEvent *)event); #else gtk_menu_popup(GTK_MENU(inst->menu), NULL, NULL, NULL, NULL, event->button, event->time); #endif return true; } if (event->button == 1) button = MBT_LEFT; else if (event->button == 2) button = MBT_MIDDLE; else if (event->button == 3) button = MBT_RIGHT; else if (event->button == 4) button = MBT_WHEEL_UP; else if (event->button == 5) button = MBT_WHEEL_DOWN; else return false; /* don't even know what button! */ switch (event->type) { 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 (raw_mouse_mode && act != MA_CLICK && act != MA_RELEASE) return true; /* we ignore these in raw mouse mode */ 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), act, x, y, shift, ctrl, alt); return true; } gboolean button_event(GtkWidget *widget, GdkEventButton *event, gpointer data) { GtkFrontend *inst = (GtkFrontend *)data; return button_internal(inst, event); } #if GTK_CHECK_VERSION(2,0,0) /* * In GTK 2, mouse wheel events have become a new type of event. * This handler translates them back into button-4 and button-5 * presses so that I don't have to change my old code too much :-) */ gboolean scroll_event(GtkWidget *widget, GdkEventScroll *event, gpointer data) { GtkFrontend *inst = (GtkFrontend *)data; GdkScrollDirection dir; #if GTK_CHECK_VERSION(3,4,0) gdouble dx, dy; if (gdk_event_get_scroll_deltas((GdkEvent *)event, &dx, &dy)) { return scroll_internal(inst, dy, event->state, event->x, event->y); } else if (!gdk_event_get_scroll_direction((GdkEvent *)event, &dir)) { return false; } #else dir = event->direction; #endif guint button; GdkEventButton *event_button; gboolean ret; if (dir == GDK_SCROLL_UP) button = 4; else if (dir == GDK_SCROLL_DOWN) button = 5; else return false; event_button = (GdkEventButton *)gdk_event_new(GDK_BUTTON_PRESS); event_button->window = g_object_ref(event->window); event_button->send_event = event->send_event; event_button->time = event->time; event_button->x = event->x; event_button->y = event->y; event_button->axes = NULL; event_button->state = event->state; event_button->button = button; event_button->device = g_object_ref(event->device); event_button->x_root = event->x_root; event_button->y_root = event->y_root; ret = button_internal(inst, event_button); gdk_event_free((GdkEvent *)event_button); return ret; } #endif gint motion_event(GtkWidget *widget, GdkEventMotion *event, gpointer data) { GtkFrontend *inst = (GtkFrontend *)data; bool shift, ctrl, alt; int x, y, button; /* Remember the timestamp. */ inst->input_event_time = event->time; noise_ultralight(NOISE_SOURCE_MOUSEPOS, ((uint32_t)event->x << 16) | (uint32_t)event->y); show_mouseptr(inst, true); shift = event->state & GDK_SHIFT_MASK; ctrl = event->state & GDK_CONTROL_MASK; alt = event->state & inst->meta_mod_mask; if (event->state & GDK_BUTTON1_MASK) button = MBT_LEFT; 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; } static void key_pressed(GtkFrontend *inst) { /* * If our child process has exited but not closed, terminate on * any keypress. * * This is a UI feature specific to GTK PuTTY, because GTK PuTTY * will (at least sometimes) be running under X, and under X the * window manager is sometimes absent (very occasionally on * purpose, more usually temporarily because it's crashed). So * it's useful to have a way to close an application window * without depending on protocols like WM_DELETE_WINDOW that are * typically generated by the WM (e.g. in response to a close * button in the window frame). */ if (inst->exited) gtk_widget_destroy(inst->window); } static void exit_callback(void *vctx) { GtkFrontend *inst = (GtkFrontend *)vctx; int exitcode, close_on_exit; if (!inst->exited && (exitcode = backend_exitcode(inst->backend)) >= 0) { destroy_inst_connection(inst); 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_widget_destroy(inst->window); } } } static void gtk_seat_notify_remote_exit(Seat *seat) { GtkFrontend *inst = container_of(seat, GtkFrontend, seat); queue_toplevel_callback(exit_callback, inst); } static void destroy_inst_connection(GtkFrontend *inst) { inst->exited = true; if (inst->ldisc) { ldisc_free(inst->ldisc); inst->ldisc = NULL; } if (inst->backend) { backend_free(inst->backend); inst->backend = NULL; } if (inst->term) term_provide_backend(inst->term, NULL); if (inst->menu) { seat_update_specials_menu(&inst->seat); gtk_widget_set_sensitive(inst->restartitem, true); } } static void delete_inst(GtkFrontend *inst) { int dialog_slot; for (dialog_slot = 0; dialog_slot < DIALOG_SLOT_LIMIT; dialog_slot++) { if (inst->dialogs[dialog_slot]) { gtk_widget_destroy(inst->dialogs[dialog_slot]); inst->dialogs[dialog_slot] = NULL; } } if (inst->window) { gtk_widget_destroy(inst->window); inst->window = NULL; } if (inst->menu) { gtk_widget_destroy(inst->menu); inst->menu = NULL; } destroy_inst_connection(inst); if (inst->term) { term_free(inst->term); inst->term = NULL; } if (inst->conf) { conf_free(inst->conf); inst->conf = NULL; } if (inst->logctx) { log_free(inst->logctx); inst->logctx = NULL; } #if GTK_CHECK_VERSION(2,0,0) if (inst->trust_sigil_pb) { g_object_unref(G_OBJECT(inst->trust_sigil_pb)); inst->trust_sigil_pb = NULL; } #else if (inst->trust_sigil_pm) { gdk_pixmap_unref(inst->trust_sigil_pm); inst->trust_sigil_pm = NULL; } #endif #ifdef JUST_USE_GTK_CLIPBOARD_UTF8 /* * Clear up any in-flight clipboard_data_instances. We can't * actually _free_ them, but we detach them from the inst that's * about to be destroyed. */ while (inst->cdi_headtail.next != &inst->cdi_headtail) { struct clipboard_data_instance *cdi = inst->cdi_headtail.next; cdi->state = NULL; cdi->next->prev = cdi->prev; cdi->prev->next = cdi->next; cdi->next = cdi->prev = cdi; } #endif /* * Delete any top-level callbacks associated with inst, which * would otherwise become stale-pointer dereferences waiting to * happen. We do this last, because some of the above cleanups * (notably shutting down the backend) might themelves queue such * callbacks, so we need to make sure they don't do that _after_ * we're supposed to have cleaned everything up. */ delete_callbacks_for_context(inst); eventlogstuff_free(inst->eventlogstuff); sfree(inst); } void destroy(GtkWidget *widget, gpointer data) { GtkFrontend *inst = (GtkFrontend *)data; inst->window = NULL; delete_inst(inst); session_window_closed(); } gint focus_event(GtkWidget *widget, GdkEventFocus *event, gpointer data) { GtkFrontend *inst = (GtkFrontend *)data; term_set_focus(inst->term, event->in); term_update(inst->term); show_mouseptr(inst, true); return false; } static void gtk_seat_set_busy_status(Seat *seat, BusyStatus status) { GtkFrontend *inst = container_of(seat, GtkFrontend, seat); inst->busy_status = status; update_mouseptr(inst); } static void gtkwin_set_raw_mouse_mode(TermWin *tw, bool activate) { GtkFrontend *inst = container_of(tw, GtkFrontend, termwin); inst->send_raw_mouse = activate; } static void gtkwin_set_raw_mouse_mode_pointer(TermWin *tw, bool activate) { GtkFrontend *inst = container_of(tw, GtkFrontend, termwin); inst->pointer_indicates_raw_mouse = activate; update_mouseptr(inst); } #if GTK_CHECK_VERSION(2,0,0) static void compute_whole_window_size(GtkFrontend *inst, int wchars, int hchars, int *wpix, int *hpix); #endif static void gtkwin_request_resize(TermWin *tw, int w, int h) { GtkFrontend *inst = container_of(tw, GtkFrontend, termwin); #if !GTK_CHECK_VERSION(3,0,0) int large_x, large_y; int offset_x, offset_y; int area_x, area_y; GtkRequisition inner, outer; /* * This is a heinous hack dreamed up by the gnome-terminal * people to get around a limitation in gtk. The problem is * that in order to set the size correctly we really need to be * calling gtk_window_resize - but that needs to know the size * of the _whole window_, not the drawing area. So what we do * is to set an artificially huge size request on the drawing * area, recompute the resulting size request on the window, * and look at the difference between the two. That gives us * the x and y offsets we need to translate drawing area size * into window size for real, and then we call * gtk_window_resize. */ /* * We start by retrieving the current size of the whole window. * Adding a bit to _that_ will give us a value we can use as a * bogus size request which guarantees to be bigger than the * current size of the drawing area. */ get_window_pixel_size(inst, &large_x, &large_y); large_x += 32; large_y += 32; gtk_widget_set_size_request(inst->area, large_x, large_y); 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. */ gtk_widget_set_size_request(inst->area, area_x, area_y); #if GTK_CHECK_VERSION(2,0,0) gtk_window_resize(GTK_WINDOW(inst->window), area_x + offset_x, area_y + offset_y); #else gtk_drawing_area_size(GTK_DRAWING_AREA(inst->area), area_x, area_y); /* * I can no longer remember what this call to * gtk_container_dequeue_resize_handler is for. It was * introduced in r3092 with no comment, and the commit log * message was uninformative. I'm _guessing_ its purpose is to * prevent gratuitous resize processing on the window given * that we're about to resize it anyway, but I have no idea * why that's so incredibly vital. * * I've tried removing the call, and nothing seems to go * wrong. I've backtracked to r3092 and tried removing the * call there, and still nothing goes wrong. So I'm going to * adopt the working hypothesis that it's superfluous; I won't * actually remove it from the GTK 1.2 code, but I won't * attempt to replicate its functionality in the GTK 2 code * above. */ gtk_container_dequeue_resize_handler(GTK_CONTAINER(inst->window)); gdk_window_resize(gtk_widget_get_window(inst->window), area_x + offset_x, area_y + offset_y); #endif #else /* GTK_CHECK_VERSION(3,0,0) */ int wp, hp; compute_whole_window_size(inst, w, h, &wp, &hp); gtk_window_resize(GTK_WINDOW(inst->window), wp, hp); #endif } #if GTK_CHECK_VERSION(3,0,0) char *colour_to_css(const GdkColor *col) { GdkRGBA rgba; rgba.red = col->red / 65535.0; rgba.green = col->green / 65535.0; rgba.blue = col->blue / 65535.0; rgba.alpha = 1.0; return gdk_rgba_to_string(&rgba); } #endif void set_gtk_widget_background(GtkWidget *widget, const GdkColor *col) { #if GTK_CHECK_VERSION(3,0,0) GtkCssProvider *provider = gtk_css_provider_new(); char *col_css = colour_to_css(col); char *data = dupprintf( "#drawing-area, #top-level { background-color: %s; }\n", col_css); gtk_css_provider_load_from_data(provider, data, -1, NULL); GtkStyleContext *context = gtk_widget_get_style_context(widget); gtk_style_context_add_provider(context, GTK_STYLE_PROVIDER(provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); free(data); free(col_css); #else if (gtk_widget_get_window(widget)) { /* For GTK1, which doesn't have a 'const' on * gdk_window_set_background's second parameter type. */ GdkColor col_mutable = *col; gdk_window_set_background(gtk_widget_get_window(widget), &col_mutable); } #endif } void set_window_background(GtkFrontend *inst) { if (inst->area) set_gtk_widget_background(GTK_WIDGET(inst->area), &inst->cols[258]); if (inst->window) set_gtk_widget_background(GTK_WIDGET(inst->window), &inst->cols[258]); } static void gtkwin_palette_set(TermWin *tw, unsigned start, unsigned ncolours, const rgb *colours) { GtkFrontend *inst = container_of(tw, GtkFrontend, termwin); assert(start <= OSC4_NCOLOURS); assert(ncolours <= OSC4_NCOLOURS - start); #if !GTK_CHECK_VERSION(3,0,0) if (!inst->colmap) { inst->colmap = gdk_colormap_get_system(); } else { gdk_colormap_free_colors(inst->colmap, inst->cols, OSC4_NCOLOURS); } #endif for (unsigned i = 0; i < ncolours; i++) { const rgb *in = &colours[i]; GdkColor *out = &inst->cols[start + i]; out->red = in->r * 0x0101; out->green = in->g * 0x0101; out->blue = in->b * 0x0101; } #if !GTK_CHECK_VERSION(3,0,0) { gboolean success[OSC4_NCOLOURS]; gdk_colormap_alloc_colors(inst->colmap, inst->cols + start, ncolours, false, true, success); for (unsigned i = 0; i < ncolours; i++) { if (!success[i]) g_error("%s: couldn't allocate colour %d (#%02x%02x%02x)\n", appname, start + 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)); } } #endif if (start <= OSC4_COLOUR_bg && OSC4_COLOUR_bg < start + ncolours) { /* Default Background has changed, so ensure that space between text * area and window border is refreshed. */ set_window_background(inst); if (inst->area && gtk_widget_get_window(inst->area)) { draw_backing_rect(inst); gtk_widget_queue_draw(inst->area); } } } static void gtkwin_palette_get_overrides(TermWin *tw, Terminal *term) { /* GTK has no analogue of Windows's 'standard system colours', so GTK PuTTY * has no config option to override the normally configured colours from * it */ } static struct clipboard_state *clipboard_from_atom( GtkFrontend *inst, GdkAtom atom) { int i; for (i = 0; i < N_CLIPBOARDS; i++) { struct clipboard_state *state = &inst->clipstates[i]; if (state->inst == inst && state->atom == atom) return state; } return NULL; } #ifdef JUST_USE_GTK_CLIPBOARD_UTF8 /* ---------------------------------------------------------------------- * Clipboard handling, using the high-level GtkClipboard interface in * as hands-off a way as possible. We write and read the clipboard as * UTF-8 text, and let GTK deal with converting to any other text * formats it feels like. */ void set_clipboard_atom(GtkFrontend *inst, int clipboard, GdkAtom atom) { struct clipboard_state *state = &inst->clipstates[clipboard]; state->inst = inst; state->clipboard = clipboard; state->atom = atom; if (state->atom != GDK_NONE) { state->gtkclipboard = gtk_clipboard_get_for_display( gdk_display_get_default(), state->atom); g_object_set_data(G_OBJECT(state->gtkclipboard), "user-data", state); } else { state->gtkclipboard = NULL; } } int init_clipboard(GtkFrontend *inst) { set_clipboard_atom(inst, CLIP_PRIMARY, GDK_SELECTION_PRIMARY); set_clipboard_atom(inst, CLIP_CLIPBOARD, clipboard_atom); return true; } static void clipboard_provide_data(GtkClipboard *clipboard, GtkSelectionData *selection_data, guint info, gpointer data) { struct clipboard_data_instance *cdi = (struct clipboard_data_instance *)data; if (cdi->state && cdi->state->current_cdi == cdi) { gtk_selection_data_set_text(selection_data, cdi->pasteout_data_utf8, cdi->pasteout_data_utf8_len); } } static void clipboard_clear(GtkClipboard *clipboard, gpointer data) { struct clipboard_data_instance *cdi = (struct clipboard_data_instance *)data; if (cdi->state && cdi->state->current_cdi == cdi) { if (cdi->state->inst && cdi->state->inst->term) { term_lost_clipboard_ownership(cdi->state->inst->term, cdi->state->clipboard); } cdi->state->current_cdi = NULL; } sfree(cdi->pasteout_data_utf8); cdi->next->prev = cdi->prev; cdi->prev->next = cdi->next; sfree(cdi); } static void gtkwin_clip_write( TermWin *tw, int clipboard, wchar_t *data, int *attr, truecolour *truecolour, int len, bool must_deselect) { GtkFrontend *inst = container_of(tw, GtkFrontend, termwin); struct clipboard_state *state = &inst->clipstates[clipboard]; struct clipboard_data_instance *cdi; if (inst->direct_to_font) { /* In this clipboard mode, we just can't paste if we're in * direct-to-font mode. Fortunately, that shouldn't be * important, because we'll only use this clipboard handling * code on systems where that kind of font doesn't exist * anyway. */ return; } if (!state->gtkclipboard) return; cdi = snew(struct clipboard_data_instance); cdi->state = state; state->current_cdi = cdi; cdi->pasteout_data_utf8 = snewn(len*6, char); cdi->prev = inst->cdi_headtail.prev; cdi->next = &inst->cdi_headtail; cdi->next->prev = cdi; cdi->prev->next = cdi; { const wchar_t *tmp = data; int tmplen = len; cdi->pasteout_data_utf8_len = charset_from_unicode(&tmp, &tmplen, cdi->pasteout_data_utf8, len*6, CS_UTF8, NULL, NULL, 0); } /* * It would be nice to just call gtk_clipboard_set_text() in place * of all of the faffing below. Unfortunately, that won't give me * access to the clipboard-clear event, which we use to visually * deselect text in the terminal. */ { GtkTargetList *targetlist; GtkTargetEntry *targettable; gint n_targets; targetlist = gtk_target_list_new(NULL, 0); gtk_target_list_add_text_targets(targetlist, 0); targettable = gtk_target_table_new_from_list(targetlist, &n_targets); gtk_clipboard_set_with_data(state->gtkclipboard, targettable, n_targets, clipboard_provide_data, clipboard_clear, cdi); gtk_target_table_free(targettable, n_targets); gtk_target_list_unref(targetlist); } } static void clipboard_text_received(GtkClipboard *clipboard, const gchar *text, gpointer data) { GtkFrontend *inst = (GtkFrontend *)data; wchar_t *paste; int paste_len; int length; if (!text) return; length = strlen(text); paste = snewn(length, wchar_t); paste_len = mb_to_wc(CS_UTF8, 0, text, length, paste, length); term_do_paste(inst->term, paste, paste_len); sfree(paste); } static void gtkwin_clip_request_paste(TermWin *tw, int clipboard) { GtkFrontend *inst = container_of(tw, GtkFrontend, termwin); struct clipboard_state *state = &inst->clipstates[clipboard]; if (!state->gtkclipboard) return; gtk_clipboard_request_text(state->gtkclipboard, clipboard_text_received, inst); } #else /* JUST_USE_GTK_CLIPBOARD_UTF8 */ /* ---------------------------------------------------------------------- * Clipboard handling for X, using the low-level gtk_selection_* * interface, handling conversions to fiddly things like compound text * ourselves, and storing in X cut buffers too. * * This version of the clipboard code has to be kept around for GTK1, * which doesn't have the higher-level GtkClipboard interface at all. * And since it works on GTK2 and GTK3 too and has had a good few * years of shakedown and bug fixing, we might as well keep using it * where it's applicable. * * It's _possible_ that we might be able to replicate all the * important wrinkles of this code in GtkClipboard. (In particular, * cut buffers or local analogue look as if they might be accessible * via gtk_clipboard_set_can_store(), and delivering text in * non-Unicode formats only in the direct-to-font case ought to be * possible if we can figure out the right set of things to put in the * GtkTargetList.) But that work can wait until there's a need for it! */ #ifndef NOT_X_WINDOWS /* Store the data in a cut-buffer. */ static void store_cutbuffer(GtkFrontend *inst, char *ptr, int len) { if (inst->disp) { /* ICCCM says we must rotate the buffers before storing to buffer 0. */ XRotateBuffers(inst->disp, 1); XStoreBytes(inst->disp, ptr, len); } } /* Retrieve data from a cut-buffer. * Returned data needs to be freed with XFree(). */ static char *retrieve_cutbuffer(GtkFrontend *inst, int *nbytes) { char *ptr; if (!inst->disp) { *nbytes = 0; return NULL; } ptr = XFetchBytes(inst->disp, nbytes); if (*nbytes <= 0 && ptr != 0) { XFree(ptr); ptr = 0; } return ptr; } #endif /* NOT_X_WINDOWS */ static void gtkwin_clip_write( TermWin *tw, int clipboard, wchar_t *data, int *attr, truecolour *truecolour, int len, bool must_deselect) { GtkFrontend *inst = container_of(tw, GtkFrontend, termwin); struct clipboard_state *state = &inst->clipstates[clipboard]; if (state->pasteout_data) sfree(state->pasteout_data); if (state->pasteout_data_ctext) sfree(state->pasteout_data_ctext); if (state->pasteout_data_utf8) sfree(state->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; #ifndef NOT_X_WINDOWS XTextProperty tp; char *list[1]; #endif state->pasteout_data_utf8 = snewn(len*6, char); state->pasteout_data_utf8_len = len*6; state->pasteout_data_utf8_len = charset_from_unicode(&tmp, &tmplen, state->pasteout_data_utf8, state->pasteout_data_utf8_len, CS_UTF8, NULL, NULL, 0); if (state->pasteout_data_utf8_len == 0) { sfree(state->pasteout_data_utf8); state->pasteout_data_utf8 = NULL; } else { state->pasteout_data_utf8 = sresize(state->pasteout_data_utf8, state->pasteout_data_utf8_len + 1, char); state->pasteout_data_utf8[state->pasteout_data_utf8_len] = '\0'; } /* * Now let Xlib convert our UTF-8 data into compound text. */ #ifndef NOT_X_WINDOWS list[0] = state->pasteout_data_utf8; if (inst->disp && Xutf8TextListToTextProperty( inst->disp, list, 1, XCompoundTextStyle, &tp) == 0) { state->pasteout_data_ctext = snewn(tp.nitems+1, char); memcpy(state->pasteout_data_ctext, tp.value, tp.nitems); state->pasteout_data_ctext_len = tp.nitems; XFree(tp.value); } else #endif { state->pasteout_data_ctext = NULL; state->pasteout_data_ctext_len = 0; } } else { state->pasteout_data_utf8 = NULL; state->pasteout_data_utf8_len = 0; state->pasteout_data_ctext = NULL; state->pasteout_data_ctext_len = 0; } state->pasteout_data = snewn(len*6, char); state->pasteout_data_len = len*6; state->pasteout_data_len = wc_to_mb(inst->ucsdata.line_codepage, 0, data, len, state->pasteout_data, state->pasteout_data_len, NULL, NULL); if (state->pasteout_data_len == 0) { sfree(state->pasteout_data); state->pasteout_data = NULL; } else { state->pasteout_data = sresize(state->pasteout_data, state->pasteout_data_len, char); } #ifndef NOT_X_WINDOWS /* The legacy X cut buffers go with PRIMARY, not any other clipboard */ if (state->atom == GDK_SELECTION_PRIMARY) store_cutbuffer(inst, state->pasteout_data, state->pasteout_data_len); #endif if (gtk_selection_owner_set(inst->area, state->atom, inst->input_event_time)) { #if GTK_CHECK_VERSION(2,0,0) gtk_selection_clear_targets(inst->area, state->atom); #endif gtk_selection_add_target(inst->area, state->atom, GDK_SELECTION_TYPE_STRING, 1); if (state->pasteout_data_ctext) gtk_selection_add_target(inst->area, state->atom, compound_text_atom, 1); if (state->pasteout_data_utf8) gtk_selection_add_target(inst->area, state->atom, utf8_string_atom, 1); } if (must_deselect) term_lost_clipboard_ownership(inst->term, clipboard); } static void selection_get(GtkWidget *widget, GtkSelectionData *seldata, guint info, guint time_stamp, gpointer data) { GtkFrontend *inst = (GtkFrontend *)data; GdkAtom target = gtk_selection_data_get_target(seldata); struct clipboard_state *state = clipboard_from_atom( inst, gtk_selection_data_get_selection(seldata)); if (!state) return; if (target == utf8_string_atom) gtk_selection_data_set(seldata, target, 8, (unsigned char *)state->pasteout_data_utf8, state->pasteout_data_utf8_len); else if (target == compound_text_atom) gtk_selection_data_set(seldata, target, 8, (unsigned char *)state->pasteout_data_ctext, state->pasteout_data_ctext_len); else gtk_selection_data_set(seldata, target, 8, (unsigned char *)state->pasteout_data, state->pasteout_data_len); } static gint selection_clear(GtkWidget *widget, GdkEventSelection *seldata, gpointer data) { GtkFrontend *inst = (GtkFrontend *)data; struct clipboard_state *state = clipboard_from_atom( inst, seldata->selection); if (!state) return true; term_lost_clipboard_ownership(inst->term, state->clipboard); if (state->pasteout_data) sfree(state->pasteout_data); if (state->pasteout_data_ctext) sfree(state->pasteout_data_ctext); if (state->pasteout_data_utf8) sfree(state->pasteout_data_utf8); state->pasteout_data = NULL; state->pasteout_data_len = 0; state->pasteout_data_ctext = NULL; state->pasteout_data_ctext_len = 0; state->pasteout_data_utf8 = NULL; state->pasteout_data_utf8_len = 0; return true; } static void gtkwin_clip_request_paste(TermWin *tw, int clipboard) { GtkFrontend *inst = container_of(tw, GtkFrontend, termwin); struct clipboard_state *state = &inst->clipstates[clipboard]; /* * In Unix, pasting is asynchronous: all we can do at the * moment is to call gtk_selection_convert(), and when the data * comes back _then_ we can call term_do_paste(). */ if (!inst->direct_to_font) { /* * First we attempt to retrieve the selection as a UTF-8 * string (which we will convert to the correct code page * before sending to the session, of course). If that * fails, selection_received() will be informed and will * fall back to an ordinary string. */ gtk_selection_convert(inst->area, state->atom, utf8_string_atom, inst->input_event_time); } else { /* * If we're in direct-to-font mode, we disable UTF-8 * pasting, and go straight to ordinary string data. */ gtk_selection_convert(inst->area, state->atom, GDK_SELECTION_TYPE_STRING, inst->input_event_time); } } static void selection_received(GtkWidget *widget, GtkSelectionData *seldata, guint time, gpointer data) { GtkFrontend *inst = (GtkFrontend *)data; char *text; int length; #ifndef NOT_X_WINDOWS char **list; bool free_list_required = false; bool free_required = false; #endif int charset; GdkAtom seldata_target = gtk_selection_data_get_target(seldata); GdkAtom seldata_type = gtk_selection_data_get_data_type(seldata); const guchar *seldata_data = gtk_selection_data_get_data(seldata); gint seldata_length = gtk_selection_data_get_length(seldata); wchar_t *paste; int paste_len; struct clipboard_state *state = clipboard_from_atom( inst, gtk_selection_data_get_selection(seldata)); if (!state) return; if (seldata_target == utf8_string_atom && seldata_length <= 0) { /* * Failed to get a UTF-8 selection string. Try compound * text next. */ gtk_selection_convert(inst->area, state->atom, compound_text_atom, inst->input_event_time); return; } if (seldata_target == compound_text_atom && seldata_length <= 0) { /* * Failed to get UTF-8 or compound text. Try an ordinary * string. */ gtk_selection_convert(inst->area, state->atom, GDK_SELECTION_TYPE_STRING, inst->input_event_time); return; } /* * If we have data, but it's not of a type we can deal with, * we have to ignore the data. */ if (seldata_length > 0 && seldata_type != GDK_SELECTION_TYPE_STRING && seldata_type != compound_text_atom && seldata_type != utf8_string_atom) return; /* * If we have no data, try looking in a cut buffer. */ if (seldata_length <= 0) { #ifndef NOT_X_WINDOWS text = retrieve_cutbuffer(inst, &length); if (length == 0) return; /* Xterm is rumoured to expect Latin-1, though I havn't checked the * source, so use that as a de-facto standard. */ charset = CS_ISO8859_1; free_required = true; #else return; #endif } else { /* * Convert COMPOUND_TEXT into UTF-8. */ if (seldata_type == compound_text_atom) { #ifndef NOT_X_WINDOWS XTextProperty tp; int ret, count; tp.value = (unsigned char *)seldata_data; tp.encoding = (Atom) seldata_type; tp.format = gtk_selection_data_get_format(seldata); tp.nitems = seldata_length; ret = inst->disp == NULL ? -1 : Xutf8TextPropertyToTextList(inst->disp, &tp, &list, &count); if (ret == 0 && count == 1) { text = list[0]; length = strlen(list[0]); charset = CS_UTF8; free_list_required = true; } else #endif { /* * Compound text failed; fall back to STRING. */ gtk_selection_convert(inst->area, state->atom, GDK_SELECTION_TYPE_STRING, inst->input_event_time); return; } } else { text = (char *)seldata_data; length = seldata_length; charset = (seldata_type == utf8_string_atom ? CS_UTF8 : inst->ucsdata.line_codepage); } } paste = snewn(length, wchar_t); paste_len = mb_to_wc(charset, 0, text, length, paste, length); term_do_paste(inst->term, paste, paste_len); sfree(paste); #ifndef NOT_X_WINDOWS if (free_list_required) XFreeStringList(list); if (free_required) XFree(text); #endif } static void init_one_clipboard(GtkFrontend *inst, int clipboard) { struct clipboard_state *state = &inst->clipstates[clipboard]; state->inst = inst; state->clipboard = clipboard; } void set_clipboard_atom(GtkFrontend *inst, int clipboard, GdkAtom atom) { struct clipboard_state *state = &inst->clipstates[clipboard]; state->inst = inst; state->clipboard = clipboard; state->atom = atom; } void init_clipboard(GtkFrontend *inst) { #ifndef NOT_X_WINDOWS /* * Ensure that all the cut buffers exist - according to the ICCCM, * we must do this before we start using cut buffers. */ if (inst->disp) { unsigned char empty[] = ""; x11_ignore_error(inst->disp, BadMatch); XChangeProperty(inst->disp, GDK_ROOT_WINDOW(), XA_CUT_BUFFER0, XA_STRING, 8, PropModeAppend, empty, 0); x11_ignore_error(inst->disp, BadMatch); XChangeProperty(inst->disp, GDK_ROOT_WINDOW(), XA_CUT_BUFFER1, XA_STRING, 8, PropModeAppend, empty, 0); x11_ignore_error(inst->disp, BadMatch); XChangeProperty(inst->disp, GDK_ROOT_WINDOW(), XA_CUT_BUFFER2, XA_STRING, 8, PropModeAppend, empty, 0); x11_ignore_error(inst->disp, BadMatch); XChangeProperty(inst->disp, GDK_ROOT_WINDOW(), XA_CUT_BUFFER3, XA_STRING, 8, PropModeAppend, empty, 0); x11_ignore_error(inst->disp, BadMatch); XChangeProperty(inst->disp, GDK_ROOT_WINDOW(), XA_CUT_BUFFER4, XA_STRING, 8, PropModeAppend, empty, 0); x11_ignore_error(inst->disp, BadMatch); XChangeProperty(inst->disp, GDK_ROOT_WINDOW(), XA_CUT_BUFFER5, XA_STRING, 8, PropModeAppend, empty, 0); x11_ignore_error(inst->disp, BadMatch); XChangeProperty(inst->disp, GDK_ROOT_WINDOW(), XA_CUT_BUFFER6, XA_STRING, 8, PropModeAppend, empty, 0); x11_ignore_error(inst->disp, BadMatch); XChangeProperty(inst->disp, GDK_ROOT_WINDOW(), XA_CUT_BUFFER7, XA_STRING, 8, PropModeAppend, empty, 0); } #endif inst->clipstates[CLIP_PRIMARY].atom = GDK_SELECTION_PRIMARY; inst->clipstates[CLIP_CLIPBOARD].atom = clipboard_atom; init_one_clipboard(inst, CLIP_PRIMARY); init_one_clipboard(inst, CLIP_CLIPBOARD); g_signal_connect(G_OBJECT(inst->area), "selection_received", G_CALLBACK(selection_received), inst); g_signal_connect(G_OBJECT(inst->area), "selection_get", G_CALLBACK(selection_get), inst); g_signal_connect(G_OBJECT(inst->area), "selection_clear_event", G_CALLBACK(selection_clear), inst); } /* * End of selection/clipboard handling. * ---------------------------------------------------------------------- */ #endif /* JUST_USE_GTK_CLIPBOARD_UTF8 */ static void set_window_titles(GtkFrontend *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_bool(inst->conf, CONF_win_name_always)) gdk_window_set_icon_name(gtk_widget_get_window(inst->window), inst->icontitle); } static void gtkwin_set_title(TermWin *tw, const char *title) { GtkFrontend *inst = container_of(tw, GtkFrontend, termwin); sfree(inst->wintitle); inst->wintitle = dupstr(title); set_window_titles(inst); } static void gtkwin_set_icon_title(TermWin *tw, const char *title) { GtkFrontend *inst = container_of(tw, GtkFrontend, termwin); sfree(inst->icontitle); inst->icontitle = dupstr(title); set_window_titles(inst); } static void gtkwin_set_scrollbar(TermWin *tw, int total, int start, int page) { GtkFrontend *inst = container_of(tw, GtkFrontend, termwin); if (!conf_get_bool(inst->conf, CONF_scrollbar)) return; inst->ignore_sbar = true; gtk_adjustment_set_lower(inst->sbar_adjust, 0); gtk_adjustment_set_upper(inst->sbar_adjust, total); gtk_adjustment_set_value(inst->sbar_adjust, start); gtk_adjustment_set_page_size(inst->sbar_adjust, page); gtk_adjustment_set_step_increment(inst->sbar_adjust, 1); gtk_adjustment_set_page_increment(inst->sbar_adjust, page/2); #if !GTK_CHECK_VERSION(3,18,0) gtk_adjustment_changed(inst->sbar_adjust); #endif inst->ignore_sbar = false; } void scrollbar_moved(GtkAdjustment *adj, GtkFrontend *inst) { if (!conf_get_bool(inst->conf, CONF_scrollbar)) return; if (!inst->ignore_sbar) term_scroll(inst->term, 1, (int)gtk_adjustment_get_value(adj)); } static void show_scrollbar(GtkFrontend *inst, gboolean visible) { inst->sbar_visible = visible; if (visible) gtk_widget_show(inst->sbar); else gtk_widget_hide(inst->sbar); } static void gtkwin_set_cursor_pos(TermWin *tw, int x, int y) { /* * This is meaningless under X. */ } /* * This is still called when mode==BELL_VISUAL, even though the * visual bell is handled entirely within terminal.c, because we * may want to perform additional actions on any kind of bell (for * example, taskbar flashing in Windows). */ static void gtkwin_bell(TermWin *tw, int mode) { /* GtkFrontend *inst = container_of(tw, GtkFrontend, termwin); */ if (mode == BELL_DEFAULT) gdk_display_beep(gdk_display_get_default()); } static int gtkwin_char_width(TermWin *tw, int uc) { /* * In this front end, double-width characters are handled using a * separate font, so this can safely just return 1 always. */ return 1; } static bool gtkwin_setup_draw_ctx(TermWin *tw) { GtkFrontend *inst = container_of(tw, GtkFrontend, termwin); if (!gtk_widget_get_window(inst->area)) return false; inst->uctx.type = inst->drawtype; #ifdef DRAW_TEXT_GDK if (inst->uctx.type == DRAWTYPE_GDK) { /* If we're doing GDK-based drawing, then we also expect * inst->pixmap to exist. */ inst->uctx.u.gdk.target = inst->pixmap; inst->uctx.u.gdk.gc = gdk_gc_new(gtk_widget_get_window(inst->area)); } #endif #ifdef DRAW_TEXT_CAIRO if (inst->uctx.type == DRAWTYPE_CAIRO) { inst->uctx.u.cairo.widget = GTK_WIDGET(inst->area); /* If we're doing Cairo drawing, we expect inst->surface to * exist, and we draw to that first, regardless of whether we * subsequently copy the results to inst->pixmap. */ inst->uctx.u.cairo.cr = cairo_create(inst->surface); cairo_scale(inst->uctx.u.cairo.cr, inst->scale, inst->scale); cairo_setup_draw_ctx(inst); } #endif return true; } static void gtkwin_free_draw_ctx(TermWin *tw) { GtkFrontend *inst = container_of(tw, GtkFrontend, termwin); #ifdef DRAW_TEXT_GDK if (inst->uctx.type == DRAWTYPE_GDK) { gdk_gc_unref(inst->uctx.u.gdk.gc); } #endif #ifdef DRAW_TEXT_CAIRO if (inst->uctx.type == DRAWTYPE_CAIRO) { cairo_destroy(inst->uctx.u.cairo.cr); } #endif } static void draw_update(GtkFrontend *inst, int x, int y, int w, int h) { #if defined DRAW_TEXT_CAIRO && !defined NO_BACKING_PIXMAPS if (inst->uctx.type == DRAWTYPE_CAIRO) { /* * If inst->surface and inst->pixmap both exist, then we've * just drawn new content to the former which we must copy to * the latter. */ cairo_t *cr = gdk_cairo_create(inst->pixmap); cairo_set_source_surface(cr, inst->surface, 0, 0); cairo_rectangle(cr, x, y, w, h); cairo_fill(cr); cairo_destroy(cr); } #endif /* * Now we just queue a window redraw, which will cause * inst->surface or inst->pixmap (whichever is appropriate for our * compile mode) to be copied to the real window when we receive * the resulting "expose" or "draw" event. * * Amazingly, this one API call is actually valid in all versions * of GTK :-) */ gtk_widget_queue_draw_area(inst->area, x, y, w, h); } #ifdef DRAW_TEXT_CAIRO static void cairo_set_source_rgb_dim(cairo_t *cr, double r, double g, double b, bool dim) { if (dim) cairo_set_source_rgb(cr, r * 2 / 3, g * 2 / 3, b * 2 / 3); else cairo_set_source_rgb(cr, r, g, b); } #endif static void draw_set_colour(GtkFrontend *inst, int col, bool dim) { #ifdef DRAW_TEXT_GDK if (inst->uctx.type == DRAWTYPE_GDK) { if (dim) { #if GTK_CHECK_VERSION(2,0,0) GdkColor color; color.red = inst->cols[col].red * 2 / 3; color.green = inst->cols[col].green * 2 / 3; color.blue = inst->cols[col].blue * 2 / 3; gdk_gc_set_rgb_fg_color(inst->uctx.u.gdk.gc, &color); #else /* Poor GTK1 fallback */ gdk_gc_set_foreground(inst->uctx.u.gdk.gc, &inst->cols[col]); #endif } else { gdk_gc_set_foreground(inst->uctx.u.gdk.gc, &inst->cols[col]); } } #endif #ifdef DRAW_TEXT_CAIRO if (inst->uctx.type == DRAWTYPE_CAIRO) { cairo_set_source_rgb_dim(inst->uctx.u.cairo.cr, inst->cols[col].red / 65535.0, inst->cols[col].green / 65535.0, inst->cols[col].blue / 65535.0, dim); } #endif } static void draw_set_colour_rgb(GtkFrontend *inst, optionalrgb orgb, bool dim) { #ifdef DRAW_TEXT_GDK if (inst->uctx.type == DRAWTYPE_GDK) { #if GTK_CHECK_VERSION(2,0,0) GdkColor color; color.red = orgb.r * 256; color.green = orgb.g * 256; color.blue = orgb.b * 256; if (dim) { color.red = color.red * 2 / 3; color.green = color.green * 2 / 3; color.blue = color.blue * 2 / 3; } gdk_gc_set_rgb_fg_color(inst->uctx.u.gdk.gc, &color); #else /* Poor GTK1 fallback */ gdk_gc_set_foreground(inst->uctx.u.gdk.gc, &inst->cols[256]); #endif } #endif #ifdef DRAW_TEXT_CAIRO if (inst->uctx.type == DRAWTYPE_CAIRO) { cairo_set_source_rgb_dim(inst->uctx.u.cairo.cr, orgb.r / 255.0, orgb.g / 255.0, orgb.b / 255.0, dim); } #endif } static void draw_rectangle(GtkFrontend *inst, bool filled, int x, int y, int w, int h) { #ifdef DRAW_TEXT_GDK if (inst->uctx.type == DRAWTYPE_GDK) { gdk_draw_rectangle(inst->uctx.u.gdk.target, inst->uctx.u.gdk.gc, filled, x, y, w, h); } #endif #ifdef DRAW_TEXT_CAIRO if (inst->uctx.type == DRAWTYPE_CAIRO) { cairo_new_path(inst->uctx.u.cairo.cr); if (filled) { cairo_rectangle(inst->uctx.u.cairo.cr, x, y, w, h); cairo_fill(inst->uctx.u.cairo.cr); } else { cairo_rectangle(inst->uctx.u.cairo.cr, x + 0.5, y + 0.5, w, h); cairo_close_path(inst->uctx.u.cairo.cr); cairo_stroke(inst->uctx.u.cairo.cr); } } #endif } static void draw_clip(GtkFrontend *inst, int x, int y, int w, int h) { #ifdef DRAW_TEXT_GDK if (inst->uctx.type == DRAWTYPE_GDK) { GdkRectangle r; r.x = x; r.y = y; r.width = w; r.height = h; gdk_gc_set_clip_rectangle(inst->uctx.u.gdk.gc, &r); } #endif #ifdef DRAW_TEXT_CAIRO if (inst->uctx.type == DRAWTYPE_CAIRO) { cairo_reset_clip(inst->uctx.u.cairo.cr); cairo_new_path(inst->uctx.u.cairo.cr); cairo_rectangle(inst->uctx.u.cairo.cr, x, y, w, h); cairo_clip(inst->uctx.u.cairo.cr); } #endif } static void draw_point(GtkFrontend *inst, int x, int y) { #ifdef DRAW_TEXT_GDK if (inst->uctx.type == DRAWTYPE_GDK) { gdk_draw_point(inst->uctx.u.gdk.target, inst->uctx.u.gdk.gc, x, y); } #endif #ifdef DRAW_TEXT_CAIRO if (inst->uctx.type == DRAWTYPE_CAIRO) { cairo_new_path(inst->uctx.u.cairo.cr); cairo_rectangle(inst->uctx.u.cairo.cr, x, y, 1, 1); cairo_fill(inst->uctx.u.cairo.cr); } #endif } static void draw_line(GtkFrontend *inst, int x0, int y0, int x1, int y1) { #ifdef DRAW_TEXT_GDK if (inst->uctx.type == DRAWTYPE_GDK) { gdk_draw_line(inst->uctx.u.gdk.target, inst->uctx.u.gdk.gc, x0, y0, x1, y1); } #endif #ifdef DRAW_TEXT_CAIRO if (inst->uctx.type == DRAWTYPE_CAIRO) { cairo_new_path(inst->uctx.u.cairo.cr); cairo_move_to(inst->uctx.u.cairo.cr, x0 + 0.5, y0 + 0.5); cairo_line_to(inst->uctx.u.cairo.cr, x1 + 0.5, y1 + 0.5); cairo_stroke(inst->uctx.u.cairo.cr); } #endif } static void draw_stretch_before(GtkFrontend *inst, int x, int y, int w, bool wdouble, int h, bool hdouble, bool hbothalf) { #ifdef DRAW_TEXT_CAIRO if (inst->uctx.type == DRAWTYPE_CAIRO) { cairo_matrix_t matrix; matrix.xy = 0; matrix.yx = 0; if (wdouble) { matrix.xx = 2; matrix.x0 = -x; } else { matrix.xx = 1; matrix.x0 = 0; } if (hdouble) { matrix.yy = 2; if (hbothalf) { matrix.y0 = -(y+h); } else { matrix.y0 = -y; } } else { matrix.yy = 1; matrix.y0 = 0; } cairo_transform(inst->uctx.u.cairo.cr, &matrix); } #endif } static void draw_stretch_after(GtkFrontend *inst, int x, int y, int w, bool wdouble, int h, bool hdouble, bool hbothalf) { #ifdef DRAW_TEXT_GDK #ifndef NO_BACKING_PIXMAPS if (inst->uctx.type == DRAWTYPE_GDK) { /* * 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; if (wdouble) { for (i = 0; i < w; i++) { gdk_draw_pixmap(inst->uctx.u.gdk.target, inst->uctx.u.gdk.gc, inst->uctx.u.gdk.target, x + 2*i, y, x + 2*i+1, y, w - i, h); } w *= 2; } if (hdouble) { int dt, db; /* Now stretch vertically, in the same way. */ if (hbothalf) dt = 0, db = 1; else dt = 1, db = 0; for (i = 0; i < h; i += 2) { gdk_draw_pixmap(inst->uctx.u.gdk.target, inst->uctx.u.gdk.gc, inst->uctx.u.gdk.target, x, y + dt*i + db, x, y + dt*(i+1), w, h-i-1); } } } #else #error No way to implement stretching in GDK without a reliable backing pixmap #endif #endif /* DRAW_TEXT_GDK */ #ifdef DRAW_TEXT_CAIRO if (inst->uctx.type == DRAWTYPE_CAIRO) { cairo_set_matrix(inst->uctx.u.cairo.cr, &inst->uctx.u.cairo.origmatrix); } #endif } static void draw_backing_rect(GtkFrontend *inst) { int w, h; if (!win_setup_draw_ctx(&inst->termwin)) return; w = inst->width * inst->font_width + 2*inst->window_border; h = inst->height * inst->font_height + 2*inst->window_border; draw_set_colour(inst, 258, false); draw_rectangle(inst, true, 0, 0, w, h); draw_update(inst, 0, 0, w, h); win_free_draw_ctx(&inst->termwin); } /* * Draw a line of text in the window, at given character * coordinates, in given attributes. * * We are allowed to fiddle with the contents of `text'. */ static void do_text_internal( GtkFrontend *inst, int x, int y, wchar_t *text, int len, unsigned long attr, int lattr, truecolour truecolour) { int ncombining; int nfg, nbg, t, fontid, rlen, widefactor; bool bold; bool monochrome = gdk_visual_get_depth(gtk_widget_get_visual(inst->area)) == 1; if (attr & TATTR_COMBINING) { ncombining = len; len = 1; } else ncombining = 1; if (monochrome) truecolour.fg = truecolour.bg = optionalrgb_none; 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))) { struct optionalrgb trgb; t = nfg; nfg = nbg; nbg = t; trgb = truecolour.fg; truecolour.fg = truecolour.bg; truecolour.bg = trgb; } 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) { truecolour.fg = truecolour.bg = optionalrgb_none; nfg = 260; nbg = 261; attr &= ~ATTR_DIM; /* don't dim the cursor */ } fontid = 0; if (attr & ATTR_WIDE) { widefactor = 2; fontid |= 2; } else { widefactor = 1; } if ((attr & ATTR_BOLD) && (inst->bold_style & 1)) { bold = true; fontid |= 1; } else { bold = false; } if (!inst->fonts[fontid]) { int i; /* * Fall back through font ids with subsets of this one's * set bits, in order. */ for (i = fontid; i-- > 0 ;) { if (i & ~fontid) continue; /* some other bit is set */ if (inst->fonts[i]) { fontid = i; break; } } assert(inst->fonts[fontid]); /* we should at least have hit zero */ } if ((lattr & LATTR_MODE) != LATTR_NORM) { x *= 2; 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 */ if (len == 0) return; /* rounded down half a double-width char to zero */ } rlen = len * 2; } else rlen = len; draw_clip(inst, x*inst->font_width+inst->window_border, y*inst->font_height+inst->window_border, rlen*widefactor*inst->font_width, inst->font_height); if ((lattr & LATTR_MODE) != LATTR_NORM) { draw_stretch_before(inst, x*inst->font_width+inst->window_border, y*inst->font_height+inst->window_border, rlen*widefactor*inst->font_width, true, inst->font_height, ((lattr & LATTR_MODE) != LATTR_WIDE), ((lattr & LATTR_MODE) == LATTR_BOT)); } if (truecolour.bg.enabled) draw_set_colour_rgb(inst, truecolour.bg, attr & ATTR_DIM); else draw_set_colour(inst, nbg, attr & ATTR_DIM); draw_rectangle(inst, true, x*inst->font_width+inst->window_border, y*inst->font_height+inst->window_border, rlen*widefactor*inst->font_width, inst->font_height); if (truecolour.fg.enabled) draw_set_colour_rgb(inst, truecolour.fg, attr & ATTR_DIM); else draw_set_colour(inst, nfg, attr & ATTR_DIM); if (ncombining > 1) { assert(len == 1); unifont_draw_combining(&inst->uctx, inst->fonts[fontid], x*inst->font_width+inst->window_border, (y*inst->font_height+inst->window_border+ inst->fonts[0]->ascent), text, ncombining, widefactor > 1, bold, inst->font_width); } else { unifont_draw_text(&inst->uctx, inst->fonts[fontid], x*inst->font_width+inst->window_border, (y*inst->font_height+inst->window_border+ inst->fonts[0]->ascent), text, 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; draw_line(inst, 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 (attr & ATTR_STRIKE) { int sheight = inst->fonts[fontid]->strikethrough_y; draw_line(inst, x*inst->font_width+inst->window_border, y*inst->font_height + sheight + inst->window_border, (x+len)*widefactor*inst->font_width-1+inst->window_border, y*inst->font_height + sheight + inst->window_border); } if ((lattr & LATTR_MODE) != LATTR_NORM) { draw_stretch_after(inst, x*inst->font_width+inst->window_border, y*inst->font_height+inst->window_border, rlen*widefactor*inst->font_width, true, inst->font_height, ((lattr & LATTR_MODE) != LATTR_WIDE), ((lattr & LATTR_MODE) == LATTR_BOT)); } } static void gtkwin_draw_text( TermWin *tw, int x, int y, wchar_t *text, int len, unsigned long attr, int lattr, truecolour truecolour) { GtkFrontend *inst = container_of(tw, GtkFrontend, termwin); int widefactor; do_text_internal(inst, x, y, text, len, attr, lattr, truecolour); if (attr & ATTR_WIDE) { widefactor = 2; } else { widefactor = 1; } if ((lattr & LATTR_MODE) != LATTR_NORM) { x *= 2; 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; } draw_update(inst, x*inst->font_width+inst->window_border, y*inst->font_height+inst->window_border, len*widefactor*inst->font_width, inst->font_height); } static void gtkwin_draw_cursor( TermWin *tw, int x, int y, wchar_t *text, int len, unsigned long attr, int lattr, truecolour truecolour) { GtkFrontend *inst = container_of(tw, GtkFrontend, termwin); bool active, passive; int widefactor; if (attr & TATTR_PASCURS) { attr &= ~TATTR_PASCURS; passive = true; } else passive = false; if ((attr & TATTR_ACTCURS) && inst->cursor_type != 0) { attr &= ~TATTR_ACTCURS; active = true; } else active = false; do_text_internal(inst, x, y, text, len, attr, lattr, truecolour); if (attr & TATTR_COMBINING) len = 1; if (attr & ATTR_WIDE) { widefactor = 2; } else { widefactor = 1; } if ((lattr & LATTR_MODE) != LATTR_NORM) { x *= 2; 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) { draw_set_colour(inst, 261, false); draw_rectangle(inst, false, 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; } draw_set_colour(inst, 261, false); if (passive) { for (i = 0; i < length; i++) { if (i % 2 == 0) { draw_point(inst, startx, starty); } startx += dx; starty += dy; } } else if (active) { draw_line(inst, startx, starty, startx + (length-1) * dx, starty + (length-1) * dy); } /* else no cursor (e.g., blinked off) */ } draw_update(inst, 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 } #if !GTK_CHECK_VERSION(2,0,0) /* * For GTK 1, manual code to scale an in-memory XPM, producing a new * one as output. It will be ugly, but good enough to use as a trust * sigil. */ struct XpmHolder { char **strings; size_t nstrings; }; static void xpmholder_free(XpmHolder *xh) { for (size_t i = 0; i < xh->nstrings; i++) sfree(xh->strings[i]); sfree(xh->strings); sfree(xh); } static XpmHolder *xpm_scale(const char *const *xpm, int wo, int ho) { /* Get image dimensions, # colours, and chars-per-pixel */ int wi = 0, hi = 0, nc = 0, cpp = 0; int retd = sscanf(xpm[0], "%d %d %d %d", &wi, &hi, &nc, &cpp); assert(retd == 4); /* Make output XpmHolder */ XpmHolder *xh = snew(XpmHolder); xh->nstrings = 1 + nc + ho; xh->strings = snewn(xh->nstrings, char *); /* Set up header */ xh->strings[0] = dupprintf("%d %d %d %d", wo, ho, nc, cpp); for (int i = 0; i < nc; i++) xh->strings[1 + i] = dupstr(xpm[1 + i]); /* Scale image */ for (int yo = 0; yo < ho; yo++) { int yi = yo * hi / ho; char *ro = snewn(cpp * wo + 1, char); ro[cpp * wo] = '\0'; xh->strings[1 + nc + yo] = ro; const char *ri = xpm[1 + nc + yi]; for (int xo = 0; xo < wo; xo++) { int xi = xo * wi / wo; memcpy(ro + cpp * xo, ri + cpp * xi, cpp); } } return xh; } #endif /* !GTK_CHECK_VERSION(2,0,0) */ static void gtkwin_draw_trust_sigil(TermWin *tw, int cx, int cy) { GtkFrontend *inst = container_of(tw, GtkFrontend, termwin); int x = cx * inst->font_width + inst->window_border; int y = cy * inst->font_height + inst->window_border; int w = 2*inst->font_width, h = inst->font_height; if (inst->trust_sigil_w != w || inst->trust_sigil_h != h || #if GTK_CHECK_VERSION(2,0,0) !inst->trust_sigil_pb #else !inst->trust_sigil_pm #endif ) { #if GTK_CHECK_VERSION(2,0,0) if (inst->trust_sigil_pb) g_object_unref(G_OBJECT(inst->trust_sigil_pb)); #else if (inst->trust_sigil_pm) gdk_pixmap_unref(inst->trust_sigil_pm); #endif int best_icon_index = 0; unsigned score = UINT_MAX; for (int i = 0; i < n_main_icon; i++) { int iw, ih; if (sscanf(main_icon[i][0], "%d %d", &iw, &ih) == 2) { int this_excess = (iw + ih) - (w + h); unsigned this_score = (abs(this_excess) | (this_excess > 0 ? 0 : 0x80000000U)); if (this_score < score) { best_icon_index = i; score = this_score; } } } #if GTK_CHECK_VERSION(2,0,0) GdkPixbuf *icon_unscaled = gdk_pixbuf_new_from_xpm_data( (const gchar **)main_icon[best_icon_index]); inst->trust_sigil_pb = gdk_pixbuf_scale_simple( icon_unscaled, w, h, GDK_INTERP_BILINEAR); g_object_unref(G_OBJECT(icon_unscaled)); #else XpmHolder *xh = xpm_scale(main_icon[best_icon_index], w, h); inst->trust_sigil_pm = gdk_pixmap_create_from_xpm_d( gtk_widget_get_window(inst->window), NULL, &inst->cols[258], xh->strings); xpmholder_free(xh); #endif inst->trust_sigil_w = w; inst->trust_sigil_h = h; } #ifdef DRAW_TEXT_GDK if (inst->uctx.type == DRAWTYPE_GDK) { #if GTK_CHECK_VERSION(2,0,0) gdk_draw_pixbuf(inst->uctx.u.gdk.target, inst->uctx.u.gdk.gc, inst->trust_sigil_pb, 0, 0, x, y, w, h, GDK_RGB_DITHER_NORMAL, 0, 0); #else gdk_draw_pixmap(inst->uctx.u.gdk.target, inst->uctx.u.gdk.gc, inst->trust_sigil_pm, 0, 0, x, y, w, h); #endif } #endif #ifdef DRAW_TEXT_CAIRO if (inst->uctx.type == DRAWTYPE_CAIRO) { inst->uctx.u.cairo.widget = GTK_WIDGET(inst->area); cairo_save(inst->uctx.u.cairo.cr); cairo_translate(inst->uctx.u.cairo.cr, x, y); gdk_cairo_set_source_pixbuf(inst->uctx.u.cairo.cr, inst->trust_sigil_pb, 0, 0); cairo_rectangle(inst->uctx.u.cairo.cr, 0, 0, w, h); cairo_fill(inst->uctx.u.cairo.cr); cairo_restore(inst->uctx.u.cairo.cr); } #endif draw_update(inst, x, y, w, h); } GdkCursor *make_mouse_ptr(GtkFrontend *inst, int cursor_val) { if (cursor_val == -1) { #if GTK_CHECK_VERSION(2,16,0) cursor_val = GDK_BLANK_CURSOR; #else /* * Work around absence of GDK_BLANK_CURSOR by inventing a * blank pixmap. */ GdkCursor *ret; GdkColor bg = { 0, 0, 0, 0 }; GdkPixmap *pm = gdk_pixmap_new(NULL, 1, 1, 1); GdkGC *gc = gdk_gc_new(pm); gdk_gc_set_foreground(gc, &bg); gdk_draw_rectangle(pm, gc, 1, 0, 0, 1, 1); gdk_gc_unref(gc); ret = gdk_cursor_new_from_pixmap(pm, pm, &bg, &bg, 1, 1); gdk_pixmap_unref(pm); return ret; #endif } return gdk_cursor_new(cursor_val); } void modalfatalbox(const char *p, ...) { va_list ap; fprintf(stderr, "FATAL ERROR: "); va_start(ap, p); vfprintf(stderr, p, ap); va_end(ap); fputc('\n', stderr); exit(1); } static const char *gtk_seat_get_x_display(Seat *seat) { return gdk_get_display(); } #ifndef NOT_X_WINDOWS static bool gtk_seat_get_windowid(Seat *seat, long *id) { GtkFrontend *inst = container_of(seat, GtkFrontend, seat); GdkWindow *window = gtk_widget_get_window(inst->area); if (!GDK_IS_X11_WINDOW(window)) return false; *id = GDK_WINDOW_XID(window); return true; } #endif char *setup_fonts_ucs(GtkFrontend *inst) { bool shadowbold = conf_get_bool(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]; } if (inst->font_width != inst->fonts[0]->width || inst->font_height != inst->fonts[0]->height) { inst->font_width = inst->fonts[0]->width; inst->font_height = inst->fonts[0]->height; /* * The font size has changed, so force the next call to * drawing_area_setup to regenerate the backing surface. */ inst->drawing_area_setup_needed = true; } inst->direct_to_font = init_ucs(&inst->ucsdata, conf_get_str(inst->conf, CONF_line_codepage), conf_get_bool(inst->conf, CONF_utf8_override), inst->fonts[0]->public_charset, conf_get_int(inst->conf, CONF_vtmode)); inst->drawtype = inst->fonts[0]->preferred_drawtype; return NULL; } #if GTK_CHECK_VERSION(3,0,0) struct find_app_menu_bar_ctx { GtkWidget *area, *menubar; }; static void find_app_menu_bar(GtkWidget *widget, gpointer data) { struct find_app_menu_bar_ctx *ctx = (struct find_app_menu_bar_ctx *)data; if (widget != ctx->area && GTK_IS_MENU_BAR(widget)) ctx->menubar = widget; } #endif static void compute_geom_hints(GtkFrontend *inst, GdkGeometry *geom) { /* * Unused fields in geom. */ geom->max_width = geom->max_height = -1; geom->min_aspect = geom->max_aspect = 0; /* * Set up the geometry fields we care about, with reference to * just the drawing area. We'll correct for other widgets in a * moment. */ geom->min_width = inst->font_width + 2*inst->window_border; geom->min_height = inst->font_height + 2*inst->window_border; 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; /* * If we've got a scrollbar visible, then we must include its * width as part of the base and min width, and also ensure that * our window's minimum height is at least the height required by * the scrollbar. * * In the latter case, we must also take care to arrange that * (geom->min_height - geom->base_height) is an integer multiple of * geom->height_inc, because if it's not, then some window managers * (we know of xfwm4) get confused, with the effect that they * resize our window to a height based on min_height instead of * base_height, which we then round down and the window ends up * too short. */ if (inst->sbar_visible) { GtkRequisition req; int min_sb_height; #if GTK_CHECK_VERSION(3,0,0) gtk_widget_get_preferred_size(inst->sbar, &req, NULL); #else gtk_widget_size_request(inst->sbar, &req); #endif /* Compute rounded-up scrollbar height. */ min_sb_height = req.height; min_sb_height += geom->height_inc - 1; min_sb_height -= ((min_sb_height - geom->base_height%geom->height_inc) % geom->height_inc); geom->min_width += req.width; geom->base_width += req.width; if (geom->min_height < min_sb_height) geom->min_height = min_sb_height; } #if GTK_CHECK_VERSION(3,0,0) /* * And if we're running a gtkapp.c based program and * GtkApplicationWindow has given us a menu bar inside the window, * then we must take that into account as well. * * In its unbounded wisdom, GtkApplicationWindow doesn't actually * give us a direct function call to _find_ the menu bar widget. * Fortunately, we can find it by enumerating the children of the * top-level window and looking for one we didn't put there * ourselves. */ { struct find_app_menu_bar_ctx ctx[1]; ctx->area = inst->area; ctx->menubar = NULL; gtk_container_foreach(GTK_CONTAINER(inst->window), find_app_menu_bar, ctx); if (ctx->menubar) { GtkRequisition req; int min_menu_width; gtk_widget_get_preferred_size(ctx->menubar, NULL, &req); /* * This time, the height adjustment is easy (the menu bar * sits above everything), but we have to take care with * the _width_ to ensure we keep min_width and base_width * congruent modulo width_inc. */ geom->min_height += req.height; geom->base_height += req.height; min_menu_width = req.width; min_menu_width += geom->width_inc - 1; min_menu_width -= ((min_menu_width - geom->base_width%geom->width_inc) % geom->width_inc); if (geom->min_width < min_menu_width) geom->min_width = min_menu_width; } } #endif } void set_geom_hints(GtkFrontend *inst) { const struct BackendVtable *vt; GdkGeometry geom; gint flags = GDK_HINT_MIN_SIZE | GDK_HINT_BASE_SIZE | GDK_HINT_RESIZE_INC; compute_geom_hints(inst, &geom); #if GTK_CHECK_VERSION(2,0,0) if (inst->gotpos) flags |= GDK_HINT_USER_POS; #endif vt = backend_vt_from_proto(conf_get_int(inst->conf, CONF_protocol)); if (vt && vt->flags & BACKEND_RESIZE_FORBIDDEN) { /* Window resizing forbidden. Set both minimum and maximum * dimensions to be the initial size. */ geom.min_width = inst->width*inst->font_width + 2*inst->window_border; geom.min_height = inst->height*inst->font_height + 2*inst->window_border; geom.max_width = geom.min_width; geom.max_height = geom.min_height; flags |= GDK_HINT_MAX_SIZE; } gtk_window_set_geometry_hints(GTK_WINDOW(inst->window), NULL, &geom, flags); } #if GTK_CHECK_VERSION(2,0,0) static void compute_whole_window_size(GtkFrontend *inst, int wchars, int hchars, int *wpix, int *hpix) { GdkGeometry geom; compute_geom_hints(inst, &geom); if (wpix) *wpix = geom.base_width + wchars * geom.width_inc; if (hpix) *hpix = geom.base_height + hchars * geom.height_inc; } #endif void clear_scrollback_menuitem(GtkMenuItem *item, gpointer data) { GtkFrontend *inst = (GtkFrontend *)data; term_clrsb(inst->term); } void reset_terminal_menuitem(GtkMenuItem *item, gpointer data) { GtkFrontend *inst = (GtkFrontend *)data; term_pwron(inst->term, true); if (inst->ldisc) ldisc_echoedit_update(inst->ldisc); } void copy_clipboard_menuitem(GtkMenuItem *item, gpointer data) { GtkFrontend *inst = (GtkFrontend *)data; static const int clips[] = { MENU_CLIPBOARD }; term_request_copy(inst->term, clips, lenof(clips)); } void paste_clipboard_menuitem(GtkMenuItem *item, gpointer data) { GtkFrontend *inst = (GtkFrontend *)data; term_request_paste(inst->term, MENU_CLIPBOARD); } void copy_all_menuitem(GtkMenuItem *item, gpointer data) { GtkFrontend *inst = (GtkFrontend *)data; static const int clips[] = { COPYALL_CLIPBOARDS }; term_copyall(inst->term, clips, lenof(clips)); } void special_menuitem(GtkMenuItem *item, gpointer data) { GtkFrontend *inst = (GtkFrontend *)data; SessionSpecial *sc = g_object_get_data(G_OBJECT(item), "user-data"); if (inst->backend) backend_special(inst->backend, sc->code, sc->arg); } void about_menuitem(GtkMenuItem *item, gpointer data) { GtkFrontend *inst = (GtkFrontend *)data; about_box(inst->window); } void event_log_menuitem(GtkMenuItem *item, gpointer data) { GtkFrontend *inst = (GtkFrontend *)data; showeventlog(inst->eventlogstuff, inst->window); } void setup_clipboards(GtkFrontend *inst, Terminal *term, Conf *conf) { assert(term->mouse_select_clipboards[0] == CLIP_LOCAL); term->n_mouse_select_clipboards = 1; term->mouse_select_clipboards[ term->n_mouse_select_clipboards++] = MOUSE_SELECT_CLIPBOARD; if (conf_get_bool(conf, CONF_mouseautocopy)) { term->mouse_select_clipboards[ term->n_mouse_select_clipboards++] = CLIP_CLIPBOARD; } set_clipboard_atom(inst, CLIP_CUSTOM_1, GDK_NONE); set_clipboard_atom(inst, CLIP_CUSTOM_2, GDK_NONE); set_clipboard_atom(inst, CLIP_CUSTOM_3, GDK_NONE); switch (conf_get_int(conf, CONF_mousepaste)) { case CLIPUI_IMPLICIT: term->mouse_paste_clipboard = MOUSE_PASTE_CLIPBOARD; break; case CLIPUI_EXPLICIT: term->mouse_paste_clipboard = CLIP_CLIPBOARD; break; case CLIPUI_CUSTOM: term->mouse_paste_clipboard = CLIP_CUSTOM_1; set_clipboard_atom(inst, CLIP_CUSTOM_1, gdk_atom_intern( conf_get_str(conf, CONF_mousepaste_custom), false)); break; default: term->mouse_paste_clipboard = CLIP_NULL; break; } if (conf_get_int(conf, CONF_ctrlshiftins) == CLIPUI_CUSTOM) { GdkAtom atom = gdk_atom_intern( conf_get_str(conf, CONF_ctrlshiftins_custom), false); struct clipboard_state *state = clipboard_from_atom(inst, atom); if (state) { inst->clipboard_ctrlshiftins = state->clipboard; } else { inst->clipboard_ctrlshiftins = CLIP_CUSTOM_2; set_clipboard_atom(inst, CLIP_CUSTOM_2, atom); } } if (conf_get_int(conf, CONF_ctrlshiftcv) == CLIPUI_CUSTOM) { GdkAtom atom = gdk_atom_intern( conf_get_str(conf, CONF_ctrlshiftcv_custom), false); struct clipboard_state *state = clipboard_from_atom(inst, atom); if (state) { inst->clipboard_ctrlshiftins = state->clipboard; } else { inst->clipboard_ctrlshiftcv = CLIP_CUSTOM_3; set_clipboard_atom(inst, CLIP_CUSTOM_3, atom); } } } struct after_change_settings_dialog_ctx { GtkFrontend *inst; Conf *newconf; }; static void after_change_settings_dialog(void *vctx, int retval); void change_settings_menuitem(GtkMenuItem *item, gpointer data) { GtkFrontend *inst = (GtkFrontend *)data; struct after_change_settings_dialog_ctx *ctx; GtkWidget *dialog; char *title; if (find_and_raise_dialog(inst, DIALOG_SLOT_RECONFIGURE)) return; title = dupcat(appname, " Reconfiguration"); ctx = snew(struct after_change_settings_dialog_ctx); ctx->inst = inst; ctx->newconf = conf_copy(inst->conf); term_pre_reconfig(inst->term, ctx->newconf); dialog = create_config_box( title, ctx->newconf, true, inst->backend ? backend_cfg_info(inst->backend) : 0, after_change_settings_dialog, ctx); register_dialog(&inst->seat, DIALOG_SLOT_RECONFIGURE, dialog); sfree(title); } static void after_change_settings_dialog(void *vctx, int retval) { struct after_change_settings_dialog_ctx ctx = *(struct after_change_settings_dialog_ctx *)vctx; GtkFrontend *inst = ctx.inst; Conf *oldconf = inst->conf, *newconf = ctx.newconf; bool need_size; sfree(vctx); /* we've copied this already */ unregister_dialog(&inst->seat, DIALOG_SLOT_RECONFIGURE); if (retval > 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_echoedit_update(inst->ldisc); } /* Pass new config data to the terminal */ term_reconfig(inst->term, inst->conf); setup_clipboards(inst, inst->term, inst->conf); /* Pass new config data to the back end */ if (inst->backend) backend_reconfig(inst->backend, inst->conf); cache_conf_values(inst); need_size = false; /* * 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_bool(oldconf, CONF_scrollbar) != conf_get_bool(newconf, CONF_scrollbar)) { show_scrollbar(inst, conf_get_bool(newconf, CONF_scrollbar)); need_size = true; } if (conf_get_bool(oldconf, CONF_scrollbar_on_left) != conf_get_bool(newconf, CONF_scrollbar_on_left)) { gtk_box_reorder_child(inst->hbox, inst->sbar, conf_get_bool(newconf, CONF_scrollbar_on_left) ? 0 : 1); } /* * Redo the whole tangled fonts and Unicode mess if * necessary. */ 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_bool(oldconf, CONF_utf8_override) != conf_get_bool(newconf, CONF_utf8_override) || conf_get_int(oldconf, CONF_vtmode) != conf_get_int(newconf, CONF_vtmode) || conf_get_bool(oldconf, CONF_shadowbold) != conf_get_bool(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); create_message_box( inst->window, "Font setup error", msgboxtext, string_width("Could not change fonts in terminal window:"), false, &buttons_ok, trivial_post_dialog_fn, 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); win_request_resize(&inst->termwin, 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); } } static void change_font_size(GtkFrontend *inst, int increment) { static const int conf_keys[lenof(inst->fonts)] = { CONF_font, CONF_boldfont, CONF_widefont, CONF_wideboldfont, }; FontSpec *oldfonts[lenof(inst->fonts)]; FontSpec *newfonts[lenof(inst->fonts)]; char *errmsg = NULL; int i; for (i = 0; i < lenof(newfonts); i++) oldfonts[i] = newfonts[i] = NULL; for (i = 0; i < lenof(inst->fonts); i++) { if (inst->fonts[i]) { char *newname = unifont_size_increment(inst->fonts[i], increment); if (!newname) goto cleanup; newfonts[i] = fontspec_new(newname); sfree(newname); } } for (i = 0; i < lenof(newfonts); i++) { if (newfonts[i]) { oldfonts[i] = fontspec_copy( conf_get_fontspec(inst->conf, conf_keys[i])); conf_set_fontspec(inst->conf, conf_keys[i], newfonts[i]); } } errmsg = setup_fonts_ucs(inst); if (errmsg) goto cleanup; /* Success, so suppress putting everything back */ for (i = 0; i < lenof(newfonts); i++) { if (oldfonts[i]) { fontspec_free(oldfonts[i]); oldfonts[i] = NULL; } } set_geom_hints(inst); win_request_resize(&inst->termwin, conf_get_int(inst->conf, CONF_width), conf_get_int(inst->conf, CONF_height)); term_invalidate(inst->term); gtk_widget_queue_draw(inst->area); cleanup: for (i = 0; i < lenof(oldfonts); i++) { if (oldfonts[i]) { conf_set_fontspec(inst->conf, conf_keys[i], oldfonts[i]); fontspec_free(oldfonts[i]); } if (newfonts[i]) fontspec_free(newfonts[i]); } sfree(errmsg); } void dup_session_menuitem(GtkMenuItem *item, gpointer gdata) { GtkFrontend *inst = (GtkFrontend *)gdata; launch_duplicate_session(inst->conf); } void new_session_menuitem(GtkMenuItem *item, gpointer data) { launch_new_session(); } void restart_session_menuitem(GtkMenuItem *item, gpointer data) { GtkFrontend *inst = (GtkFrontend *)data; if (!inst->backend) { logevent(inst->logctx, "----- Session restarted -----"); term_pwron(inst->term, false); start_backend(inst); inst->exited = false; } } void saved_session_menuitem(GtkMenuItem *item, gpointer data) { char *str = (char *)g_object_get_data(G_OBJECT(item), "user-data"); launch_saved_session(str); } void saved_session_freedata(GtkMenuItem *item, gpointer data) { char *str = (char *)g_object_get_data(G_OBJECT(item), "user-data"); sfree(str); } void app_menu_action(GtkFrontend *frontend, enum MenuAction action) { GtkFrontend *inst = (GtkFrontend *)frontend; switch (action) { case MA_COPY: copy_clipboard_menuitem(NULL, inst); break; case MA_PASTE: paste_clipboard_menuitem(NULL, inst); break; case MA_COPY_ALL: copy_all_menuitem(NULL, inst); break; case MA_DUPLICATE_SESSION: dup_session_menuitem(NULL, inst); break; case MA_RESTART_SESSION: restart_session_menuitem(NULL, inst); break; case MA_CHANGE_SETTINGS: change_settings_menuitem(NULL, inst); break; case MA_CLEAR_SCROLLBACK: clear_scrollback_menuitem(NULL, inst); break; case MA_RESET_TERMINAL: reset_terminal_menuitem(NULL, inst); break; case MA_EVENT_LOG: event_log_menuitem(NULL, inst); break; } } static void update_savedsess_menu(GtkMenuItem *menuitem, gpointer data) { GtkFrontend *inst = (GtkFrontend *)data; struct sesslist sesslist; int i; gtk_container_foreach(GTK_CONTAINER(inst->sessionsmenu), (GtkCallback)gtk_widget_destroy, NULL); get_sesslist(&sesslist, true); /* skip sesslist.sessions[0] == Default Settings */ for (i = 1; i < sesslist.nsessions; i++) { GtkWidget *menuitem = gtk_menu_item_new_with_label(sesslist.sessions[i]); gtk_container_add(GTK_CONTAINER(inst->sessionsmenu), menuitem); gtk_widget_show(menuitem); g_object_set_data(G_OBJECT(menuitem), "user-data", dupstr(sesslist.sessions[i])); g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(saved_session_menuitem), inst); g_signal_connect(G_OBJECT(menuitem), "destroy", G_CALLBACK(saved_session_freedata), inst); } if (sesslist.nsessions <= 1) { GtkWidget *menuitem = gtk_menu_item_new_with_label("(No sessions)"); gtk_widget_set_sensitive(menuitem, false); gtk_container_add(GTK_CONTAINER(inst->sessionsmenu), menuitem); gtk_widget_show(menuitem); } get_sesslist(&sesslist, false); /* free up */ } void set_window_icon(GtkWidget *window, const char *const *const *icon, int n_icon) { #if GTK_CHECK_VERSION(2,0,0) GList *iconlist; int n; #else GdkPixmap *iconpm; GdkBitmap *iconmask; #endif if (!n_icon) return; gtk_widget_realize(window); #if GTK_CHECK_VERSION(2,0,0) gtk_window_set_icon(GTK_WINDOW(window), gdk_pixbuf_new_from_xpm_data((const gchar **)icon[0])); #else iconpm = gdk_pixmap_create_from_xpm_d(gtk_widget_get_window(window), &iconmask, NULL, (gchar **)icon[0]); gdk_window_set_icon(gtk_widget_get_window(window), NULL, iconpm, iconmask); #endif #if GTK_CHECK_VERSION(2,0,0) iconlist = NULL; for (n = 0; n < n_icon; n++) { iconlist = g_list_append(iconlist, gdk_pixbuf_new_from_xpm_data((const gchar **) icon[n])); } gtk_window_set_icon_list(GTK_WINDOW(window), iconlist); #endif } static void free_special_cmd(gpointer data) { sfree(data); } static void gtk_seat_update_specials_menu(Seat *seat) { GtkFrontend *inst = container_of(seat, GtkFrontend, seat); const SessionSpecial *specials; if (inst->backend) specials = backend_get_specials(inst->backend); else specials = NULL; /* I believe this disposes of submenus too. */ gtk_container_foreach(GTK_CONTAINER(inst->specialsmenu), (GtkCallback)gtk_widget_destroy, NULL); if (specials) { int i; GtkWidget *menu = inst->specialsmenu; /* A lame "stack" for submenus that will do for now. */ GtkWidget *saved_menu = NULL; int nesting = 1; for (i = 0; nesting > 0; i++) { GtkWidget *menuitem = NULL; switch (specials[i].code) { case SS_SUBMENU: assert (nesting < 2); saved_menu = menu; /* XXX lame stacking */ menu = gtk_menu_new(); menuitem = gtk_menu_item_new_with_label(specials[i].name); gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), menu); gtk_container_add(GTK_CONTAINER(saved_menu), menuitem); gtk_widget_show(menuitem); menuitem = NULL; nesting++; break; case SS_EXITMENU: nesting--; if (nesting) { menu = saved_menu; /* XXX lame stacking */ saved_menu = NULL; } break; case SS_SEP: menuitem = gtk_menu_item_new(); break; default: { menuitem = gtk_menu_item_new_with_label(specials[i].name); SessionSpecial *sc = snew(SessionSpecial); *sc = specials[i]; /* structure copy */ g_object_set_data_full(G_OBJECT(menuitem), "user-data", sc, free_special_cmd); g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(special_menuitem), inst); break; } } if (menuitem) { gtk_container_add(GTK_CONTAINER(menu), menuitem); gtk_widget_show(menuitem); } } gtk_widget_show(inst->specialsitem1); gtk_widget_show(inst->specialsitem2); } else { gtk_widget_hide(inst->specialsitem1); gtk_widget_hide(inst->specialsitem2); } } static void start_backend(GtkFrontend *inst) { const struct BackendVtable *vt; char *error, *realhost; vt = select_backend(inst->conf); seat_set_trust_status(&inst->seat, true); error = backend_init(vt, &inst->seat, &inst->backend, inst->logctx, inst->conf, conf_get_str(inst->conf, CONF_host), conf_get_int(inst->conf, CONF_port), &realhost, conf_get_bool(inst->conf, CONF_tcp_nodelay), conf_get_bool(inst->conf, CONF_tcp_keepalives)); if (error) { seat_connection_fatal(&inst->seat, "Unable to open connection to %s:\n%s", conf_dest(inst->conf), error); sfree(error); inst->exited = true; return; } term_setup_window_titles(inst->term, realhost); sfree(realhost); term_provide_backend(inst->term, inst->backend); inst->ldisc = ldisc_create(inst->conf, inst->term, inst->backend, &inst->seat); gtk_widget_set_sensitive(inst->restartitem, false); } #if GTK_CHECK_VERSION(2,0,0) static void get_monitor_geometry(GtkWidget *widget, GdkRectangle *geometry) { #if GTK_CHECK_VERSION(3,4,0) GdkDisplay *display = gtk_widget_get_display(widget); GdkWindow *gdkwindow = gtk_widget_get_window(widget); # if GTK_CHECK_VERSION(3,22,0) GdkMonitor *monitor; if (gdkwindow) monitor = gdk_display_get_monitor_at_window(display, gdkwindow); else monitor = gdk_display_get_monitor(display, 0); gdk_monitor_get_geometry(monitor, geometry); # else GdkScreen *screen = gdk_display_get_default_screen(display); gint monitor_num = gdk_screen_get_monitor_at_window(screen, gdkwindow); gdk_screen_get_monitor_geometry(screen, monitor_num, geometry); # endif #else geometry->x = geometry->y = 0; geometry->width = gdk_screen_width(); geometry->height = gdk_screen_height(); #endif } #endif static const TermWinVtable gtk_termwin_vt = { .setup_draw_ctx = gtkwin_setup_draw_ctx, .draw_text = gtkwin_draw_text, .draw_cursor = gtkwin_draw_cursor, .draw_trust_sigil = gtkwin_draw_trust_sigil, .char_width = gtkwin_char_width, .free_draw_ctx = gtkwin_free_draw_ctx, .set_cursor_pos = gtkwin_set_cursor_pos, .set_raw_mouse_mode = gtkwin_set_raw_mouse_mode, .set_raw_mouse_mode_pointer = gtkwin_set_raw_mouse_mode_pointer, .set_scrollbar = gtkwin_set_scrollbar, .bell = gtkwin_bell, .clip_write = gtkwin_clip_write, .clip_request_paste = gtkwin_clip_request_paste, .refresh = gtkwin_refresh, .request_resize = gtkwin_request_resize, .set_title = gtkwin_set_title, .set_icon_title = gtkwin_set_icon_title, .set_minimised = gtkwin_set_minimised, .set_maximised = gtkwin_set_maximised, .move = gtkwin_move, .set_zorder = gtkwin_set_zorder, .palette_set = gtkwin_palette_set, .palette_get_overrides = gtkwin_palette_get_overrides, }; void new_session_window(Conf *conf, const char *geometry_string) { GtkFrontend *inst; prepare_session(conf); /* * Create an instance structure and initialise to zeroes */ inst = snew(GtkFrontend); memset(inst, 0, sizeof(*inst)); #ifdef JUST_USE_GTK_CLIPBOARD_UTF8 inst->cdi_headtail.next = inst->cdi_headtail.prev = &inst->cdi_headtail; #endif inst->alt_keycode = -1; /* this one needs _not_ to be zero */ inst->busy_status = BUSY_NOT; inst->conf = conf; inst->wintitle = inst->icontitle = NULL; inst->drawtype = DRAWTYPE_DEFAULT; #if GTK_CHECK_VERSION(3,4,0) inst->cumulative_scroll = 0.0; #endif inst->drawing_area_setup_needed = true; inst->termwin.vt = >k_termwin_vt; inst->seat.vt = >k_seat_vt; inst->logpolicy.vt = >k_logpolicy_vt; #ifndef NOT_X_WINDOWS inst->disp = get_x11_display(); if (geometry_string) { int flags, x, y; unsigned int w, h; flags = XParseGeometry(geometry_string, &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)); } } #endif 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); if (!clipboard_atom) clipboard_atom = gdk_atom_intern("CLIPBOARD", false); inst->area = gtk_drawing_area_new(); gtk_widget_set_name(GTK_WIDGET(inst->area), "drawing-area"); { char *errmsg = setup_fonts_ucs(inst); if (errmsg) { window_setup_error(errmsg); sfree(errmsg); gtk_widget_destroy(inst->area); sfree(inst); return; } } #if GTK_CHECK_VERSION(2,0,0) inst->imc = gtk_im_multicontext_new(); #endif inst->window = make_gtk_toplevel_window(inst); gtk_widget_set_name(GTK_WIDGET(inst->window), "top-level"); { const char *winclass = conf_get_str(inst->conf, CONF_winclass); if (*winclass) { #if GTK_CHECK_VERSION(3,22,0) #ifndef NOT_X_WINDOWS GdkWindow *gdkwin; gtk_widget_realize(GTK_WIDGET(inst->window)); gdkwin = gtk_widget_get_window(GTK_WIDGET(inst->window)); if (inst->disp && gdk_window_ensure_native(gdkwin)) { XClassHint *xch = XAllocClassHint(); xch->res_name = (char *)winclass; xch->res_class = (char *)winclass; XSetClassHint(inst->disp, GDK_WINDOW_XID(gdkwin), xch); XFree(xch); } #endif /* * If we do have NOT_X_WINDOWS set, then we don't have any * function in GTK 3.22 equivalent to the above. But then, * surely in that situation the deprecated * gtk_window_set_wmclass wouldn't have done anything * meaningful in previous GTKs either. */ #else gtk_window_set_wmclass(GTK_WINDOW(inst->window), winclass, winclass); #endif } } inst->width = conf_get_int(inst->conf, CONF_width); inst->height = conf_get_int(inst->conf, CONF_height); cache_conf_values(inst); init_clipboard(inst); 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_bool(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_bool(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)); gtk_widget_show(inst->area); show_scrollbar(inst, conf_get_bool(inst->conf, CONF_scrollbar)); gtk_widget_show(GTK_WIDGET(inst->hbox)); /* * We must call gtk_widget_realize before setting up the geometry * hints, so that GtkApplicationWindow will have actually created * its menu bar (if it's going to) and hence compute_geom_hints * can find it to take its size into account. */ gtk_widget_realize(inst->window); set_geom_hints(inst); #if GTK_CHECK_VERSION(3,0,0) { int wp, hp; compute_whole_window_size(inst, inst->width, inst->height, &wp, &hp); gtk_window_set_default_size(GTK_WINDOW(inst->window), wp, hp); } #else { int w = inst->font_width * inst->width + 2*inst->window_border; int h = inst->font_height * inst->height + 2*inst->window_border; #if GTK_CHECK_VERSION(2,0,0) gtk_widget_set_size_request(inst->area, w, h); #else gtk_drawing_area_size(GTK_DRAWING_AREA(inst->area), w, h); #endif } #endif #if GTK_CHECK_VERSION(2,0,0) if (inst->gotpos) { static const GdkGravity gravities[] = { GDK_GRAVITY_NORTH_WEST, GDK_GRAVITY_NORTH_EAST, GDK_GRAVITY_SOUTH_WEST, GDK_GRAVITY_SOUTH_EAST, }; int x = inst->xpos, y = inst->ypos; int wp, hp; GdkRectangle monitor_geometry; compute_whole_window_size(inst, inst->width, inst->height, &wp, &hp); get_monitor_geometry(GTK_WIDGET(inst->window), &monitor_geometry); if (inst->gravity & 1) x += (monitor_geometry.width - wp); if (inst->gravity & 2) y += (monitor_geometry.height - hp); gtk_window_set_gravity(GTK_WINDOW(inst->window), gravities[inst->gravity & 3]); gtk_window_move(GTK_WINDOW(inst->window), x, y); } #else if (inst->gotpos) { int x = inst->xpos, y = inst->ypos; GtkRequisition req; gtk_widget_size_request(GTK_WIDGET(inst->window), &req); if (inst->gravity & 1) x += gdk_screen_width() - req.width; if (inst->gravity & 2) y += gdk_screen_height() - req.height; gtk_window_set_position(GTK_WINDOW(inst->window), GTK_WIN_POS_NONE); gtk_widget_set_uposition(GTK_WIDGET(inst->window), x, y); } #endif g_signal_connect(G_OBJECT(inst->window), "destroy", G_CALLBACK(destroy), inst); g_signal_connect(G_OBJECT(inst->window), "delete_event", G_CALLBACK(delete_window), inst); g_signal_connect(G_OBJECT(inst->window), "key_press_event", G_CALLBACK(key_event), inst); g_signal_connect(G_OBJECT(inst->window), "key_release_event", G_CALLBACK(key_event), inst); g_signal_connect(G_OBJECT(inst->window), "focus_in_event", G_CALLBACK(focus_event), inst); g_signal_connect(G_OBJECT(inst->window), "focus_out_event", G_CALLBACK(focus_event), inst); g_signal_connect(G_OBJECT(inst->area), "realize", G_CALLBACK(area_realised), inst); g_signal_connect(G_OBJECT(inst->area), "size_allocate", G_CALLBACK(area_size_allocate), inst); g_signal_connect(G_OBJECT(inst->window), "configure_event", G_CALLBACK(window_configured), inst); #if GTK_CHECK_VERSION(3,10,0) g_signal_connect(G_OBJECT(inst->area), "configure_event", G_CALLBACK(area_configured), inst); #endif #if GTK_CHECK_VERSION(3,0,0) g_signal_connect(G_OBJECT(inst->area), "draw", G_CALLBACK(draw_area), inst); #else g_signal_connect(G_OBJECT(inst->area), "expose_event", G_CALLBACK(expose_area), inst); #endif g_signal_connect(G_OBJECT(inst->area), "button_press_event", G_CALLBACK(button_event), inst); g_signal_connect(G_OBJECT(inst->area), "button_release_event", G_CALLBACK(button_event), inst); #if GTK_CHECK_VERSION(2,0,0) g_signal_connect(G_OBJECT(inst->area), "scroll_event", G_CALLBACK(scroll_event), inst); #endif g_signal_connect(G_OBJECT(inst->area), "motion_notify_event", G_CALLBACK(motion_event), 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_bool(inst->conf, CONF_scrollbar)) g_signal_connect(G_OBJECT(inst->sbar_adjust), "value_changed", G_CALLBACK(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 #if GTK_CHECK_VERSION(3,4,0) | GDK_SMOOTH_SCROLL_MASK #endif ); set_window_icon(inst->window, main_icon, n_main_icon); gtk_widget_show(inst->window); set_window_background(inst); /* * Set up the Ctrl+rightclick context menu. */ { GtkWidget *menuitem; char *s; inst->menu = gtk_menu_new(); #define MKMENUITEM(title, func) do \ { \ menuitem = gtk_menu_item_new_with_label(title); \ gtk_container_add(GTK_CONTAINER(inst->menu), menuitem); \ gtk_widget_show(menuitem); \ g_signal_connect(G_OBJECT(menuitem), "activate", \ G_CALLBACK(func), inst); \ } while (0) #define MKSUBMENU(title) do \ { \ menuitem = gtk_menu_item_new_with_label(title); \ gtk_container_add(GTK_CONTAINER(inst->menu), menuitem); \ gtk_widget_show(menuitem); \ } while (0) #define MKSEP() do \ { \ menuitem = gtk_menu_item_new(); \ gtk_container_add(GTK_CONTAINER(inst->menu), menuitem); \ gtk_widget_show(menuitem); \ } while (0) if (new_session) MKMENUITEM("New Session...", new_session_menuitem); MKMENUITEM("Restart Session", restart_session_menuitem); inst->restartitem = menuitem; gtk_widget_set_sensitive(inst->restartitem, false); MKMENUITEM("Duplicate Session", dup_session_menuitem); if (saved_sessions) { inst->sessionsmenu = gtk_menu_new(); /* sessionsmenu will be updated when it's invoked */ /* XXX is this the right way to do dynamic menus in Gtk? */ MKMENUITEM("Saved Sessions", update_savedsess_menu); gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), inst->sessionsmenu); } MKSEP(); MKMENUITEM("Change Settings...", change_settings_menuitem); MKSEP(); if (use_event_log) MKMENUITEM("Event Log", event_log_menuitem); MKSUBMENU("Special Commands"); inst->specialsmenu = gtk_menu_new(); gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), inst->specialsmenu); inst->specialsitem1 = menuitem; MKSEP(); inst->specialsitem2 = menuitem; gtk_widget_hide(inst->specialsitem1); gtk_widget_hide(inst->specialsitem2); MKMENUITEM("Clear Scrollback", clear_scrollback_menuitem); MKMENUITEM("Reset Terminal", reset_terminal_menuitem); MKSEP(); MKMENUITEM("Copy to " CLIPNAME_EXPLICIT_OBJECT, copy_clipboard_menuitem); MKMENUITEM("Paste from " CLIPNAME_EXPLICIT_OBJECT, paste_clipboard_menuitem); MKMENUITEM("Copy All", copy_all_menuitem); MKSEP(); s = dupcat("About ", appname); MKMENUITEM(s, about_menuitem); sfree(s); #undef MKMENUITEM #undef MKSUBMENU #undef MKSEP } inst->textcursor = make_mouse_ptr(inst, GDK_XTERM); inst->rawcursor = make_mouse_ptr(inst, GDK_LEFT_PTR); inst->waitcursor = make_mouse_ptr(inst, GDK_WATCH); inst->blankcursor = make_mouse_ptr(inst, -1); inst->currcursor = inst->textcursor; show_mouseptr(inst, true); inst->eventlogstuff = eventlogstuff_new(); inst->term = term_init(inst->conf, &inst->ucsdata, &inst->termwin); setup_clipboards(inst, inst->term, inst->conf); inst->logctx = log_init(&inst->logpolicy, inst->conf); term_provide_logctx(inst->term, inst->logctx); term_size(inst->term, inst->height, inst->width, conf_get_int(inst->conf, CONF_savelines)); #if GTK_CHECK_VERSION(2,0,0) /* Delay this signal connection until after inst->term exists */ g_signal_connect(G_OBJECT(inst->window), "window_state_event", G_CALLBACK(window_state_event), inst); #endif inst->exited = false; start_backend(inst); if (inst->ldisc) /* early backend failure might make this NULL already */ ldisc_echoedit_update(inst->ldisc); /* cause ldisc to notice changes */ } static bool gtk_seat_set_trust_status(Seat *seat, bool trusted) { GtkFrontend *inst = container_of(seat, GtkFrontend, seat); term_set_trust_status(inst->term, trusted); return true; } static bool gtk_seat_get_cursor_position(Seat *seat, int *x, int *y) { GtkFrontend *inst = container_of(seat, GtkFrontend, seat); if (inst->term) { term_get_cursor_position(inst->term, x, y); return true; } return false; } putty-0.76/unix/osxlaunch.c0000644000175000017500000003607414072266313012725 00000000000000/* * Launcher program for OS X application bundles of PuTTY. */ /* * The 'gtk-mac-bundler' utility arranges to build an OS X application * bundle containing a program compiled against the Quartz GTK * backend. It does this by including all the necessary GTK shared * libraries and data files inside the bundle as well as the binary. * * But the GTK program won't start up unless all those shared * libraries etc are already pointed to by environment variables like * GTK_PATH and PANGO_LIBDIR and things like that, which won't be set * up when the bundle is launched. * * Hence, gtk-mac-bundler expects to install the program in the bundle * under a name like 'Contents/MacOS/Program-bin'; and the file called * 'Contents/MacOS/Program', which is the one actually executed when * the bundle is launched, is a wrapper script that sets up the * environment before running the actual GTK-using program. * * In our case, however, that's not good enough. pterm will want to * launch subprocesses with general-purpose shell sessions in them, * and those subprocesses _won't_ want the random stuff dumped in the * environment by the gtk-mac-bundler standard wrapper script. So I * have to provide my own wrapper, which has a more complicated job: * not only setting up the environment for the GTK app, but also * preserving all details of the _previous_ environment, so that when * pterm forks off a subprocess to run in a terminal session, it can * restore the environment that was in force before the wrapper * started messing about. This source file implements that wrapper, * and does it in C so as to make string processing more reliable and * less annoying. * * My strategy for saving the old environment is to pick a prefix * that's unused by anything currently in the environment; let's * suppose it's "P" for this discussion. Any environment variable I * overwrite, say "VAR", I will either set "PsVAR=old value", or * "PuVAR=" ("s" and "u" for "set" and "unset"). Then I pass the * prefix itself as a command-line argument to the main GTK * application binary, which then knows how to restore the original * environment in pterm subprocesses. */ #include #include #include #include #include #if !defined __APPLE__ && !defined TEST_COMPILE_ON_LINUX /* When we're not compiling for OS X, it's easier to just turn this * program into a trivial hello-world by ifdef in the source than it * is to remove it in the makefile edifice. */ int main(int argc, char **argv) { fprintf(stderr, "launcher does nothing on non-OSX platforms\n"); return 1; } #else /* __APPLE__ */ #include #include #ifdef __APPLE__ #include #else /* For Linux, a bodge to let as much of this code still run as * possible, so that you can run it under friendly debugging tools * like valgrind. */ int _NSGetExecutablePath(char *out, uint32_t *outlen) { static const char toret[] = "/proc/self/exe"; if (out != NULL && *outlen < sizeof(toret)) return -1; *outlen = sizeof(toret); if (out) memcpy(out, toret, sizeof(toret)); return 0; } #endif /* ---------------------------------------------------------------------- * Find an alphabetic prefix unused by any environment variable name. */ /* * This linked-list based system is a bit overkill, but I enjoy an * algorithmic challenge. We essentially do an incremental radix sort * of all the existing environment variable names: initially divide * them into 26 buckets by their first letter (discarding those that * don't have a letter at that position), then subdivide each bucket * in turn into 26 sub-buckets, and so on. We maintain each bucket as * a linked list, and link their heads together into a secondary list * that functions as a queue (meaning that we go breadth-first, * processing all the buckets of a given depth before moving on to the * next depth down). At any stage, if we find one of our 26 * sub-buckets is empty, that's our unused prefix. * * The running time is O(number of strings * length of output), and I * doubt it's possible to do better. */ #define FANOUT 26 int char_index(int ch) { if (ch >= 'A' && ch <= 'Z') return ch - 'A'; else if (ch >= 'a' && ch <= 'z') return ch - 'a'; else return -1; } struct bucket { int prefixlen; struct bucket *next_bucket; struct node *first_node; }; struct node { const char *string; int len, prefixlen; struct node *next; }; struct node *new_node(struct node *prev_head, const char *string, int len) { struct node *ret = (struct node *)malloc(sizeof(struct node)); if (!ret) { fprintf(stderr, "out of memory\n"); exit(1); } ret->next = prev_head; ret->string = string; ret->len = len; return ret; } char *get_unused_env_prefix(void) { struct bucket *qhead, *qtail; extern char **environ; char **e; qhead = (struct bucket *)malloc(sizeof(struct bucket)); if (!qhead) { fprintf(stderr, "out of memory\n"); exit(1); } qhead->prefixlen = 0; qhead->first_node = NULL; qhead->next_bucket = NULL; for (e = environ; *e; e++) qhead->first_node = new_node(qhead->first_node, *e, strcspn(*e, "=")); qtail = qhead; while (1) { struct bucket *buckets[FANOUT]; struct node *bucketnode; int i, index; for (i = 0; i < FANOUT; i++) { buckets[i] = (struct bucket *)malloc(sizeof(struct bucket)); if (!buckets[i]) { fprintf(stderr, "out of memory\n"); exit(1); } buckets[i]->prefixlen = qhead->prefixlen + 1; buckets[i]->first_node = NULL; qtail->next_bucket = buckets[i]; qtail = buckets[i]; } qtail->next_bucket = NULL; bucketnode = qhead->first_node; while (bucketnode) { struct node *node = bucketnode; bucketnode = bucketnode->next; if (node->len <= qhead->prefixlen) continue; index = char_index(node->string[qhead->prefixlen]); if (!(index >= 0 && index < FANOUT)) continue; node->prefixlen++; node->next = buckets[index]->first_node; buckets[index]->first_node = node; } for (i = 0; i < FANOUT; i++) { if (!buckets[i]->first_node) { char *ret = malloc(qhead->prefixlen + 2); if (!ret) { fprintf(stderr, "out of memory\n"); exit(1); } memcpy(ret, qhead->first_node->string, qhead->prefixlen); ret[qhead->prefixlen] = i + 'A'; ret[qhead->prefixlen + 1] = '\0'; /* This would be where we freed everything, if we * didn't know it didn't matter because we were * imminently going to exec another program */ return ret; } } qhead = qhead->next_bucket; } } /* ---------------------------------------------------------------------- * Get the pathname of this executable, so we can locate the rest of * the app bundle relative to it. */ /* * There are several ways to try to retrieve the pathname to the * running executable: * * (a) Declare main() as taking four arguments int main(int argc, char * **argv, char **envp, char **apple); and look at apple[0]. * * (b) Use sysctl(KERN_PROCARGS) to get the process arguments for the * current pid. This involves two steps: * - sysctl(mib, 2, &argmax, &argmax_size, NULL, 0) * + mib is an array[2] of int containing * { CTL_KERN, KERN_ARGMAX } * + argmax is an int * + argmax_size is a size_t initialised to sizeof(argmax) * + returns in argmax the amount of memory you need for the next * call. * - sysctl(mib, 3, procargs, &procargs_size, NULL, 0) * + mib is an array[3] of int containing * { CTL_KERN, KERN_PROCARGS, current pid } * + procargs is a buffer of size 'argmax' * + procargs_size is a size_t initialised to argmax * + returns in the procargs buffer a collection of * zero-terminated strings of which the first is the program * name. * * (c) Call _NSGetExecutablePath, once to find out the needed buffer * size and again to fetch the actual path. * * (d) Use Objective-C and Cocoa and call * [[[NSProcessInfo processInfo] arguments] objectAtIndex: 0]. * * So, how do those work in various cases? Experiments show: * * - if you run the program as 'binary' (or whatever you called it) * and rely on the shell to search your PATH, all four methods * return a sensible-looking absolute pathname. * * - if you run the program as './binary', (a) and (b) return just * "./binary", which has a particularly bad race condition if you * try to convert it into an absolute pathname using realpath(3). * (c) returns "/full/path/to/./binary", which still needs * realpath(3)ing to get rid of that ".", but at least it's * _trying_ to be fully qualified. (d) returns * "/full/path/to/binary" - full marks! * + Similar applies if you run it via a more interesting relative * path such as one with a ".." in: (c) gives you an absolute * path containing a ".." element, whereas (d) has sorted that * out. * * - if you run the program via a path with a symlink on, _none_ of * these options successfully returns a path without the symlink. * * That last point suggests that even (d) is not a perfect solution on * its own, and you'll have to realpath() whatever you get back from * it regardless. * * And (d) is extra inconvenient because it returns an NSString, which * is implicitly Unicode, so it's not clear how you turn that back * into a char * representing a correct Unix pathname (what charset * should you interpret it in?). Also because you have to bring in all * of ObjC and Cocoa, which for a low-level Unix API client like this * seems like overkill. * * So my conclusion is that (c) is most practical for these purposes. */ char *get_program_path(void) { char *our_path; uint32_t pathlen = 0; _NSGetExecutablePath(NULL, &pathlen); our_path = malloc(pathlen); if (!our_path) { fprintf(stderr, "out of memory\n"); exit(1); } if (_NSGetExecutablePath(our_path, &pathlen)) { fprintf(stderr, "unable to get launcher executable path\n"); exit(1); } /* OS X guarantees to malloc the return value if we pass NULL */ char *our_real_path = realpath(our_path, NULL); if (!our_real_path) { fprintf(stderr, "realpath failed\n"); exit(1); } free(our_path); return our_real_path; } /* ---------------------------------------------------------------------- * Wrapper on dirname(3) which mallocs its return value to whatever * size is needed. */ char *dirname_wrapper(const char *path) { char *path_copy = malloc(strlen(path) + 1); if (!path_copy) { fprintf(stderr, "out of memory\n"); exit(1); } strcpy(path_copy, path); char *ret_orig = dirname(path_copy); char *ret = malloc(strlen(ret_orig) + 1); if (!ret) { fprintf(stderr, "out of memory\n"); exit(1); } strcpy(ret, ret_orig); free(path_copy); return ret; } /* ---------------------------------------------------------------------- * mallocing string concatenation function. */ char *alloc_cat(const char *str1, const char *str2) { int len1 = strlen(str1), len2 = strlen(str2); char *ret = malloc(len1 + len2 + 1); if (!ret) { fprintf(stderr, "out of memory\n"); exit(1); } strcpy(ret, str1); strcpy(ret + len1, str2); return ret; } /* ---------------------------------------------------------------------- * Overwrite an environment variable, preserving the old one for the * real app to restore. */ void setenv_wrap(const char *name, const char *value) { #ifdef DEBUG_OSXLAUNCH printf("setenv(\"%s\",\"%s\")\n", name, value); #endif setenv(name, value, 1); } void unsetenv_wrap(const char *name) { #ifdef DEBUG_OSXLAUNCH printf("unsetenv(\"%s\")\n", name); #endif unsetenv(name); } char *prefix, *prefixset, *prefixunset; void overwrite_env(const char *name, const char *value) { const char *oldvalue = getenv(name); if (oldvalue) { setenv_wrap(alloc_cat(prefixset, name), oldvalue); } else { setenv_wrap(alloc_cat(prefixunset, name), ""); } if (value) setenv_wrap(name, value); else unsetenv_wrap(name); } /* ---------------------------------------------------------------------- * Main program. */ int main(int argc, char **argv) { prefix = get_unused_env_prefix(); prefixset = alloc_cat(prefix, "s"); prefixunset = alloc_cat(prefix, "u"); #ifdef DEBUG_OSXLAUNCH printf("Environment prefixes: main=\"%s\", set=\"%s\", unset=\"%s\"\n", prefix, prefixset, prefixunset); #endif char *prog_path = get_program_path(); // /Contents/MacOS/ char *macos = dirname_wrapper(prog_path); // /Contents/MacOS char *contents = dirname_wrapper(macos); // /Contents // char *bundle = dirname_wrapper(contents); // char *resources = alloc_cat(contents, "/Resources"); // char *bin = alloc_cat(resources, "/bin"); char *etc = alloc_cat(resources, "/etc"); char *lib = alloc_cat(resources, "/lib"); char *share = alloc_cat(resources, "/share"); char *xdg = alloc_cat(etc, "/xdg"); // char *gtkrc = alloc_cat(etc, "/gtk-2.0/gtkrc"); char *locale = alloc_cat(share, "/locale"); char *realbin = alloc_cat(prog_path, "-bin"); // overwrite_env("DYLD_LIBRARY_PATH", lib); overwrite_env("XDG_CONFIG_DIRS", xdg); overwrite_env("XDG_DATA_DIRS", share); overwrite_env("GTK_DATA_PREFIX", resources); overwrite_env("GTK_EXE_PREFIX", resources); overwrite_env("GTK_PATH", resources); overwrite_env("PANGO_LIBDIR", lib); overwrite_env("PANGO_SYSCONFDIR", etc); overwrite_env("I18NDIR", locale); overwrite_env("LANG", NULL); overwrite_env("LC_MESSAGES", NULL); overwrite_env("LC_MONETARY", NULL); overwrite_env("LC_COLLATE", NULL); char **new_argv = malloc((argc + 16) * sizeof(const char *)); if (!new_argv) { fprintf(stderr, "out of memory\n"); exit(1); } int j = 0; new_argv[j++] = realbin; #ifdef DEBUG_OSXLAUNCH printf("argv[%d] = \"%s\"\n", j-1, new_argv[j-1]); #endif { int i = 1; if (i < argc && !strncmp(argv[i], "-psn_", 5)) i++; for (; i < argc; i++) { new_argv[j++] = argv[i]; #ifdef DEBUG_OSXLAUNCH printf("argv[%d] = \"%s\"\n", j-1, new_argv[j-1]); #endif } } new_argv[j++] = prefix; #ifdef DEBUG_OSXLAUNCH printf("argv[%d] = \"%s\"\n", j-1, new_argv[j-1]); #endif new_argv[j++] = NULL; #ifdef DEBUG_OSXLAUNCH printf("executing \"%s\"\n", realbin); #endif execv(realbin, new_argv); perror("execv"); free(new_argv); free(contents); free(macos); return 127; } #endif /* __APPLE__ */ putty-0.76/unix/procnet.c0000644000175000017500000001531614072266313012367 00000000000000/* * Locally authenticate a TCP socket via /proc/net. * * Obviously, if a TCP connection comes from a different host, there's * no way to find out the identity of the thing at the other end (or * even really to assign that concept a meaning) except by the usual * method of speaking a protocol over the socket itself which involves * some form of (preferably cryptographic) authentication exchange. * * But if the connection comes from localhost, then on at least some * operating systems, you can do better. On Linux, /proc/net/tcp and * /proc/net/tcp6 list the full set of active TCP connection * endpoints, and they list an owning uid for each one. So once you've * accepted a connection to a listening socket and found that the * other end of it is a localhost address, you can look up the _other_ * endpoint in the right one of those files, and find out which uid * owns it. */ #include #include #include #include #include #include #include #include #include "misc.h" static ptrlen get_space_separated_field(ptrlen *string) { const char *p = string->ptr, *end = p + string->len; while (p < end && isspace((unsigned char)*p)) p++; if (p == end) return PTRLEN_LITERAL(""); const char *start = p; while (p < end && !isspace((unsigned char)*p)) p++; *string = make_ptrlen(p, end - p); return make_ptrlen(start, p - start); } enum { GOT_LOCAL_UID = 1, GOT_REMOTE_UID = 2 }; /* * Open a file formatted like /proc/net/tcp{,6}, and search it for * both ends of a particular connection. * * The operands 'local' and 'remote' give the expected string * representations of the local and remote addresses of the connection * we're looking for. * * Return value is the bitwise OR of 1 if we found the local end of * the connection and 2 if we found the remote. Each output uid_t * parameter is filled in iff the corresponding bit is set in the * return value. */ static int lookup_uids_in_procnet_file( const char *path, ptrlen local, ptrlen remote, uid_t *local_uid, uid_t *remote_uid) { FILE *fp = NULL; int toret = 0; ptrlen line, field; enum { GF_LOCAL = 1, GF_REMOTE = 2, GF_UID = 4 }; fp = fopen(path, "r"); if (!fp) goto out; /* Expected indices of fields in /proc/net/tcp* */ const int LOCAL_ADDR_INDEX = 1; const int REMOTE_ADDR_INDEX = 2; const int UID_INDEX = 7; for (char *linez; (linez = chomp(fgetline(fp))) != NULL ;) { line = ptrlen_from_asciz(linez); int gotfields = 0; ptrlen local_addr = PTRLEN_LITERAL(""); ptrlen remote_addr = PTRLEN_LITERAL(""); long uid = -1; for (int i = 0; (field = get_space_separated_field(&line)).len != 0; i++) { if (i == LOCAL_ADDR_INDEX) { gotfields |= GF_LOCAL; local_addr = field; } else if (i == REMOTE_ADDR_INDEX) { gotfields |= GF_REMOTE; remote_addr = field; } else if (i == UID_INDEX) { uid = 0; for (const char *p = field.ptr, *end = p + field.len; p < end; p++) { if (!isdigit((unsigned char)*p)) { uid = -1; break; } int dval = *p - '0'; if (uid > LONG_MAX/10) { uid = -1; break; } uid *= 10; if (uid > LONG_MAX - dval) { uid = -1; break; } uid += dval; } gotfields |= GF_UID; } } if (gotfields == (GF_LOCAL | GF_REMOTE | GF_UID)) { if (ptrlen_eq_ptrlen(local_addr, local) && ptrlen_eq_ptrlen(remote_addr, remote)) { *local_uid = uid; toret |= GOT_LOCAL_UID; } if (ptrlen_eq_ptrlen(local_addr, remote) && ptrlen_eq_ptrlen(remote_addr, local)) { *remote_uid = uid; toret |= GOT_REMOTE_UID; } } sfree(linez); } fclose(fp); fp = NULL; out: if (fp) fclose(fp); return toret; } static const char *procnet_path(int family) { switch (family) { case AF_INET: return "/proc/net/tcp"; case AF_INET6: return "/proc/net/tcp6"; default: return NULL; } } static char *format_sockaddr(const void *addr, int family) { if (family == AF_INET) { const struct sockaddr_in *a = (const struct sockaddr_in *)addr; assert(a->sin_family == family); /* Linux /proc/net formats the IP address native-endian, so we * don't use ntohl */ return dupprintf("%08X:%04X", a->sin_addr.s_addr, ntohs(a->sin_port)); } else if (family == AF_INET6) { struct sockaddr_in6 *a = (struct sockaddr_in6 *)addr; assert(a->sin6_family == family); strbuf *sb = strbuf_new(); const uint32_t *addrwords = (const uint32_t *)a->sin6_addr.s6_addr; for (int i = 0; i < 4; i++) strbuf_catf(sb, "%08X", addrwords[i]); strbuf_catf(sb, ":%04X", ntohs(a->sin6_port)); return strbuf_to_str(sb); } else { return NULL; } } bool socket_peer_is_same_user(int fd) { struct sockaddr_storage addr; socklen_t addrlen; int family; bool toret = false; char *local = NULL, *remote = NULL; const char *path; addrlen = sizeof(addr); if (getsockname(fd, (struct sockaddr *)&addr, &addrlen) != 0) goto out; family = addr.ss_family; if ((path = procnet_path(family)) == NULL) goto out; local = format_sockaddr(&addr, family); if (!local) goto out; addrlen = sizeof(addr); if (getpeername(fd, (struct sockaddr *)&addr, &addrlen) != 0) goto out; if (addr.ss_family != family) goto out; remote = format_sockaddr(&addr, family); if (!remote) goto out; ptrlen locpl = ptrlen_from_asciz(local); ptrlen rempl = ptrlen_from_asciz(remote); /* * Check that _both_ end of the socket are the uid we expect, as a * sanity check on the /proc/net file being reasonable at all. */ uid_t our_uid = getuid(); uid_t local_uid = -1, remote_uid = -1; int got = lookup_uids_in_procnet_file( path, locpl, rempl, &local_uid, &remote_uid); if (got == (GOT_LOCAL_UID | GOT_REMOTE_UID) && local_uid == our_uid && remote_uid == our_uid) toret = true; out: sfree(local); sfree(remote); return toret; } putty-0.76/unix/pterm.bundle0000644000175000017500000000247214072266313013072 00000000000000 ${env:PUTTY_GTK_PREFIX_FROM_MAKEFILE} gtk+-3.0 ${project}/../osxlaunch ${project}/pterm.plist ${project}/../ptermapp ${prefix}/lib/${gtkdir}/${pkg:${gtk}:gtk_binary_version}/immodules/*.so ${prefix}/lib/${gtkdir}/${pkg:${gtk}:gtk_binary_version}/printbackends/*.so ${prefix}/share/themes/Adwaita ${project}/../icons/Pterm.icns Adwaita putty-0.76/unix/pterm.plist0000644000175000017500000000201714072266313012747 00000000000000 CFBundleIconFile Pterm.icns CFBundleName Pterm CFBundleDisplayName Pterm CFBundleExecutable Pterm CFBundleVersion Unidentified build CFBundleShortVersionString Unidentified build CFBundleDevelopmentRegion en CFBundleIdentifier org.tartarus.projects.putty.macpterm CFBundleInfoDictionaryVersion 6.0 CFBundlePackageType APPL CFBundleSignature ???? NSHumanReadableCopyright © 1997-2015 Simon Tatham. All rights reserved. putty-0.76/unix/putty.bundle0000644000175000017500000000511114072266313013121 00000000000000 ${env:PUTTY_GTK_PREFIX_FROM_MAKEFILE} gtk+-3.0 ${project}/../osxlaunch ${project}/putty.plist ${project}/../puttyapp ${prefix}/lib/${gtkdir}/${pkg:${gtk}:gtk_binary_version}/immodules/*.so ${prefix}/lib/${gtkdir}/${pkg:${gtk}:gtk_binary_version}/printbackends/*.so ${prefix}/share/themes/Adwaita ${project}/../icons/PuTTY.icns Adwaita putty-0.76/unix/putty.plist0000644000175000017500000000201714072266313013005 00000000000000 CFBundleIconFile PuTTY.icns CFBundleName PuTTY CFBundleDisplayName PuTTY CFBundleExecutable PuTTY CFBundleVersion Unidentified build CFBundleShortVersionString Unidentified build CFBundleDevelopmentRegion en CFBundleIdentifier org.tartarus.projects.putty.macputty CFBundleInfoDictionaryVersion 6.0 CFBundlePackageType APPL CFBundleSignature ???? NSHumanReadableCopyright © 1997-2015 Simon Tatham. All rights reserved. putty-0.76/unix/unix.h0000644000175000017500000003737314072266313011714 00000000000000#ifndef PUTTY_UNIX_H #define PUTTY_UNIX_H #ifdef HAVE_CONFIG_H # include "uxconfig.h" /* Space to hide it from mkfiles.pl */ #endif #include /* for FILENAME_MAX */ #include /* C99 int types */ #ifndef NO_LIBDL #include /* Dynamic library loading */ #endif /* NO_LIBDL */ #include "charset.h" #include /* for mode_t */ #ifdef OSX_GTK /* * Assorted tweaks to various parts of the GTK front end which all * need to be enabled when compiling on OS X. Because I might need the * same tweaks on other systems in future, I don't want to * conditionalise all of them on OSX_GTK directly, so instead, each * one has its own name and we enable them all centrally here if * OSX_GTK is defined at configure time. */ #define NOT_X_WINDOWS /* of course, all the X11 stuff should be disabled */ #define NO_PTY_PRE_INIT /* OS X gets very huffy if we try to set[ug]id */ #define SET_NONBLOCK_VIA_OPENPT /* work around missing fcntl functionality */ #define OSX_META_KEY_CONFIG /* two possible Meta keys to choose from */ /* this potential one of the Meta keys needs manual handling */ #define META_MANUAL_MASK (GDK_MOD1_MASK) #define JUST_USE_GTK_CLIPBOARD_UTF8 /* low-level gdk_selection_* fails */ #define BUILDINFO_PLATFORM_GTK "OS X (GTK)" #define BUILDINFO_GTK #elif defined NOT_X_WINDOWS #define BUILDINFO_PLATFORM_GTK "Unix (pure GTK)" #define BUILDINFO_GTK #else #define BUILDINFO_PLATFORM_GTK "Unix (GTK + X11)" #define BUILDINFO_GTK #endif /* BUILDINFO_PLATFORM varies its expansion between the GTK and * pure-CLI utilities, so that Unix Plink, PSFTP etc don't announce * themselves incongruously as having something to do with GTK. */ #define BUILDINFO_PLATFORM_CLI "Unix" extern const bool buildinfo_gtk_relevant; #define BUILDINFO_PLATFORM (buildinfo_gtk_relevant ? \ BUILDINFO_PLATFORM_GTK : BUILDINFO_PLATFORM_CLI) char *buildinfo_gtk_version(void); struct Filename { char *path; }; FILE *f_open(const struct Filename *, char const *, bool); struct FontSpec { char *name; /* may be "" to indicate no selected font at all */ }; struct FontSpec *fontspec_new(const char *name); extern const struct BackendVtable pty_backend; #define BROKEN_PIPE_ERROR_CODE EPIPE /* used in sshshare.c */ /* * Under GTK, we send MA_CLICK _and_ MA_2CLK, or MA_CLICK _and_ * MA_3CLK, when a button is pressed for the second or third time. */ #define MULTICLICK_ONLY_EVENT 0 /* * Under GTK, there is no context help available. */ #define HELPCTX(x) P(NULL) #define FILTER_KEY_FILES NULL /* FIXME */ #define FILTER_DYNLIB_FILES NULL /* FIXME */ /* * Under X, selection data must not be NUL-terminated. */ #define SELECTION_NUL_TERMINATED 0 /* * Under X, copying to the clipboard terminates lines with just LF. */ #define SEL_NL { 10 } /* Simple wraparound timer function */ unsigned long getticks(void); #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 #define PLATFORM_CLIPBOARDS(X) \ X(CLIP_PRIMARY, "X11 primary selection") \ X(CLIP_CLIPBOARD, "XDG clipboard") \ X(CLIP_CUSTOM_1, "") \ X(CLIP_CUSTOM_2, "") \ X(CLIP_CUSTOM_3, "") \ /* end of list */ #ifdef OSX_GTK /* OS X has no PRIMARY selection */ #define MOUSE_SELECT_CLIPBOARD CLIP_NULL #define MOUSE_PASTE_CLIPBOARD CLIP_LOCAL #define CLIPNAME_IMPLICIT "Last selected text" #define CLIPNAME_EXPLICIT "System clipboard" #define CLIPNAME_EXPLICIT_OBJECT "system clipboard" /* These defaults are the ones that more or less comply with the OS X * Human Interface Guidelines, i.e. copy/paste to the system clipboard * is _not_ implicit but requires a specific UI action. This is at * odds with all other PuTTY front ends' defaults, but on OS X there * is no multi-decade precedent for PuTTY working the other way. */ #define CLIPUI_DEFAULT_AUTOCOPY false #define CLIPUI_DEFAULT_MOUSE CLIPUI_IMPLICIT #define CLIPUI_DEFAULT_INS CLIPUI_EXPLICIT #define MENU_CLIPBOARD CLIP_CLIPBOARD #define COPYALL_CLIPBOARDS CLIP_CLIPBOARD #else #define MOUSE_SELECT_CLIPBOARD CLIP_PRIMARY #define MOUSE_PASTE_CLIPBOARD CLIP_PRIMARY #define CLIPNAME_IMPLICIT "PRIMARY" #define CLIPNAME_EXPLICIT "CLIPBOARD" #define CLIPNAME_EXPLICIT_OBJECT "CLIPBOARD" /* These defaults are the ones Unix PuTTY has historically had since * it was first thought of in 2002 */ #define CLIPUI_DEFAULT_AUTOCOPY false #define CLIPUI_DEFAULT_MOUSE CLIPUI_IMPLICIT #define CLIPUI_DEFAULT_INS CLIPUI_IMPLICIT #define MENU_CLIPBOARD CLIP_CLIPBOARD #define COPYALL_CLIPBOARDS CLIP_PRIMARY, CLIP_CLIPBOARD /* X11 supports arbitrary named clipboards */ #define NAMED_CLIPBOARDS #endif /* The per-session frontend structure managed by gtkwin.c */ typedef struct GtkFrontend GtkFrontend; /* Callback when a dialog box finishes, and a no-op implementation of it */ typedef void (*post_dialog_fn_t)(void *ctx, int result); void trivial_post_dialog_fn(void *vctx, int result); /* Start up a session window, with or without a preliminary config box */ void initial_config_box(Conf *conf, post_dialog_fn_t after, void *afterctx); void new_session_window(Conf *conf, const char *geometry_string); /* Defined in gtkmain.c */ void launch_duplicate_session(Conf *conf); void launch_new_session(void); void launch_saved_session(const char *str); void session_window_closed(void); void window_setup_error(const char *errmsg); #ifdef MAY_REFER_TO_GTK_IN_HEADERS GtkWidget *make_gtk_toplevel_window(GtkFrontend *frontend); #endif const struct BackendVtable *select_backend(Conf *conf); /* Defined in gtkcomm.c */ void gtkcomm_setup(void); /* Used to pass application-menu operations from gtkapp.c to gtkwin.c */ enum MenuAction { MA_COPY, MA_PASTE, MA_COPY_ALL, MA_DUPLICATE_SESSION, MA_RESTART_SESSION, MA_CHANGE_SETTINGS, MA_CLEAR_SCROLLBACK, MA_RESET_TERMINAL, MA_EVENT_LOG }; void app_menu_action(GtkFrontend *frontend, enum MenuAction); /* Arrays of pixmap data used for GTK window icons. (main_icon is for * the process's main window; cfg_icon is the modified icon used for * its config box.) */ extern const char *const *const main_icon[]; extern const char *const *const cfg_icon[]; extern const int n_main_icon, n_cfg_icon; /* Things gtkdlg.c needs from gtkwin.c */ #ifdef MAY_REFER_TO_GTK_IN_HEADERS enum DialogSlot { DIALOG_SLOT_RECONFIGURE, DIALOG_SLOT_NETWORK_PROMPT, DIALOG_SLOT_LOGFILE_PROMPT, DIALOG_SLOT_WARN_ON_CLOSE, DIALOG_SLOT_CONNECTION_FATAL, DIALOG_SLOT_LIMIT /* must remain last */ }; GtkWidget *gtk_seat_get_window(Seat *seat); void register_dialog(Seat *seat, enum DialogSlot slot, GtkWidget *dialog); void unregister_dialog(Seat *seat, enum DialogSlot slot); void set_window_icon(GtkWidget *window, const char *const *const *icon, int n_icon); extern GdkAtom compound_text_atom; #endif /* Things gtkwin.c needs from gtkdlg.c */ #ifdef MAY_REFER_TO_GTK_IN_HEADERS GtkWidget *create_config_box(const char *title, Conf *conf, bool midsession, int protcfginfo, post_dialog_fn_t after, void *afterctx); #endif void nonfatal_message_box(void *window, const char *msg); void about_box(void *window); typedef struct eventlog_stuff eventlog_stuff; eventlog_stuff *eventlogstuff_new(void); void eventlogstuff_free(eventlog_stuff *); void showeventlog(eventlog_stuff *estuff, void *parentwin); void logevent_dlg(eventlog_stuff *estuff, const char *string); int gtkdlg_askappend(Seat *seat, Filename *filename, void (*callback)(void *ctx, int result), void *ctx); int gtk_seat_verify_ssh_host_key( Seat *seat, const char *host, int port, const char *keytype, char *keystr, const char *keydisp, char **fingerprints, void (*callback)(void *ctx, int result), void *ctx); int gtk_seat_confirm_weak_crypto_primitive( Seat *seat, const char *algtype, const char *algname, void (*callback)(void *ctx, int result), void *ctx); int gtk_seat_confirm_weak_cached_hostkey( Seat *seat, const char *algname, const char *betteralgs, void (*callback)(void *ctx, int result), void *ctx); #ifdef MAY_REFER_TO_GTK_IN_HEADERS struct message_box_button { const char *title; char shortcut; int type; /* more negative means more appropriate to be the Esc action */ int value; /* message box's return value if this is pressed */ }; struct message_box_buttons { const struct message_box_button *buttons; int nbuttons; }; extern const struct message_box_buttons buttons_yn, buttons_ok; GtkWidget *create_message_box( GtkWidget *parentwin, const char *title, const char *msg, int minwid, bool selectable, const struct message_box_buttons *buttons, post_dialog_fn_t after, void *afterctx); #endif /* gtkwin.c needs this special function in xkeysym.c */ int keysym_to_unicode(int keysym); /* Things uxstore.c needs from gtkwin.c */ char *x_get_default(const char *key); /* Things uxstore.c provides to gtkwin.c */ void provide_xrm_string(const char *string, const char *progname); /* Function that {gtkapp,gtkmain}.c needs from ux{pterm,putty}.c. Does * early process setup that varies between applications (e.g. * pty_pre_init or sk_init), and is passed a boolean by the caller * indicating whether this is an OS X style multi-session monolithic * process or an ordinary Unix one-shot. */ void setup(bool single_session_in_this_process); /* * Per-application constants that affect behaviour of shared modules. */ /* Do we need an Event Log menu item? (yes for PuTTY, no for pterm) */ extern const bool use_event_log; /* Do we need a New Session menu item? (yes for PuTTY, no for pterm) */ extern const bool new_session; /* Do we need a Saved Sessions menu item? (yes for PuTTY, no for pterm) */ extern const bool saved_sessions; /* When we Duplicate Session, do we need to double-check that the Conf * is in a launchable state? (no for pterm, because conf_launchable * returns an irrelevant answer, since we'll force use of the pty * backend which ignores all the relevant settings) */ extern const bool dup_check_launchable; /* In the Duplicate Session serialised data, do we send/receive an * argv array after the main Conf? (yes for pterm, no for PuTTY) */ extern const bool use_pty_argv; /* * OS X environment munging: this is the prefix we expect to find on * environment variable names that were changed by osxlaunch. * Extracted from the command line of the OS X pterm main binary, and * used in uxpty.c to restore the original environment before * launching its subprocess. */ extern char *pty_osx_envrestore_prefix; /* Things provided by uxcons.c */ struct termios; void stderr_tty_init(void); /* call at startup if stderr might be a tty */ void premsg(struct termios *); void postmsg(struct termios *); /* The interface used by uxsel.c */ typedef struct uxsel_id uxsel_id; void uxsel_init(void); typedef void (*uxsel_callback_fn)(int fd, int event); void uxsel_set(int fd, int rwx, uxsel_callback_fn callback); void uxsel_del(int fd); enum { SELECT_R = 1, SELECT_W = 2, SELECT_X = 4 }; void select_result(int fd, int event); int first_fd(int *state, int *rwx); int next_fd(int *state, int *rwx); /* The following are expected to be provided _to_ uxsel.c by the frontend */ uxsel_id *uxsel_input_add(int fd, int rwx); /* returns an id */ void uxsel_input_remove(uxsel_id *id); /* uxcfg.c */ struct controlbox; void unix_setup_config_box( struct controlbox *b, bool midsession, int protocol); /* gtkcfg.c */ void gtk_setup_config_box( struct controlbox *b, bool midsession, void *window); /* * In the Unix Unicode layer, DEFAULT_CODEPAGE is a special value * which causes mb_to_wc and wc_to_mb to call _libc_ rather than * libcharset. That way, we can interface the various charsets * supported by libcharset with the one supported by mbstowcs and * wcstombs (which will be the character set in which stuff read * from the command line or config files is assumed to be encoded). */ #define DEFAULT_CODEPAGE 0xFFFF #define CP_UTF8 CS_UTF8 /* from libcharset */ #define strnicmp strncasecmp #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, bool block_it); /* uxmisc.c */ void cloexec(int); void noncloexec(int); bool nonblock(int); bool no_nonblock(int); char *make_dir_and_check_ours(const char *dirname); char *make_dir_path(const char *path, mode_t mode); /* * Exports from unicode.c. */ struct unicode_data; bool init_ucs(struct unicode_data *ucsdata, char *line_codepage, bool utf8_override, int font_charset, int vtmode); /* * Spare functions exported directly from uxnet.c. */ void *sk_getxdmdata(Socket *sock, int *lenp); int sk_net_get_fd(Socket *sock); SockAddr *unix_sock_addr(const char *path); Socket *new_unix_listener(SockAddr *listenaddr, Plug *plug); /* * General helpful Unix stuff: more helpful version of the FD_SET * macro, which also handles maxfd. */ #define FD_SET_MAX(fd, max, set) do { \ FD_SET(fd, &set); \ if (max < fd + 1) max = fd + 1; \ } while (0) /* * Exports from uxser.c. */ extern const struct BackendVtable serial_backend; /* * uxpeer.c, wrapping getsockopt(SO_PEERCRED). */ bool so_peercred(int fd, int *pid, int *uid, int *gid); /* * uxfdsock.c. */ Socket *make_fd_socket(int infd, int outfd, int inerrfd, Plug *plug); /* * Default font setting, which can vary depending on NOT_X_WINDOWS. */ #ifdef NOT_X_WINDOWS #define DEFAULT_GTK_FONT "client:Monospace 12" #else #define DEFAULT_GTK_FONT "server:fixed" #endif /* * uxpty.c. */ void pty_pre_init(void); /* pty+utmp setup before dropping privilege */ /* Pass in the argv[] for an instance of the pty backend created by * the standard vtable constructor. Only called from (non-OSX) pterm, * which will construct exactly one such instance, and initialises * this from the command line. */ extern char **pty_argv; /* * gtkask.c. */ char *gtk_askpass_main(const char *display, const char *wintitle, const char *prompt, bool *success); /* * procnet.c. */ bool socket_peer_is_same_user(int fd); static inline bool sk_peer_trusted(Socket *sock) { int fd = sk_net_get_fd(sock); return fd >= 0 && socket_peer_is_same_user(fd); } /* * uxsftpserver.c. */ extern const SftpServerVtable unix_live_sftpserver_vt; /* * uxpoll.c. */ typedef struct pollwrapper pollwrapper; pollwrapper *pollwrap_new(void); void pollwrap_free(pollwrapper *pw); void pollwrap_clear(pollwrapper *pw); void pollwrap_add_fd_events(pollwrapper *pw, int fd, int events); void pollwrap_add_fd_rwx(pollwrapper *pw, int fd, int rwx); int pollwrap_poll_instant(pollwrapper *pw); int pollwrap_poll_endless(pollwrapper *pw); int pollwrap_poll_timeout(pollwrapper *pw, int milliseconds); int pollwrap_get_fd_events(pollwrapper *pw, int fd); int pollwrap_get_fd_rwx(pollwrapper *pw, int fd); static inline bool pollwrap_check_fd_rwx(pollwrapper *pw, int fd, int rwx) { return (pollwrap_get_fd_rwx(pw, fd) & rwx) != 0; } /* * uxcliloop.c. */ typedef bool (*cliloop_pw_setup_t)(void *ctx, pollwrapper *pw); typedef void (*cliloop_pw_check_t)(void *ctx, pollwrapper *pw); typedef bool (*cliloop_continue_t)(void *ctx, bool found_any_fd, bool ran_any_callback); void cli_main_loop(cliloop_pw_setup_t pw_setup, cliloop_pw_check_t pw_check, cliloop_continue_t cont, void *ctx); bool cliloop_no_pw_setup(void *ctx, pollwrapper *pw); void cliloop_no_pw_check(void *ctx, pollwrapper *pw); bool cliloop_always_continue(void *ctx, bool, bool); #endif /* PUTTY_UNIX_H */ putty-0.76/unix/ux_x11.c0000644000175000017500000001302214072266313012032 00000000000000/* * ux_x11.c: fetch local auth data for X forwarding. */ #include #include #include #include #include #include #include #include "putty.h" #include "ssh.h" #include "network.h" void platform_get_x11_auth(struct X11Display *disp, Conf *conf) { char *xauthfile; bool needs_free; /* * Find the .Xauthority file. */ needs_free = false; xauthfile = getenv("XAUTHORITY"); if (!xauthfile) { xauthfile = getenv("HOME"); if (xauthfile) { xauthfile = dupcat(xauthfile, "/.Xauthority"); needs_free = true; } } if (xauthfile) { x11_get_auth_from_authfile(disp, xauthfile); if (needs_free) sfree(xauthfile); } } const bool platform_uses_x11_unix_by_default = true; int platform_make_x11_server(Plug *plug, const char *progname, int mindisp, const char *screen_number_suffix, ptrlen authproto, ptrlen authdata, Socket **sockets, Conf *conf) { char *tmpdir; char *authfilename = NULL; strbuf *authfiledata = NULL; char *unix_path = NULL; SockAddr *a_tcp = NULL, *a_unix = NULL; int authfd; FILE *authfp; int displayno; authfiledata = strbuf_new_nm(); int nsockets = 0; /* * Look for a free TCP port to run our server on. */ for (displayno = mindisp;; displayno++) { const char *err; int tcp_port = displayno + 6000; int addrtype = ADDRTYPE_IPV4; sockets[nsockets] = new_listener( NULL, tcp_port, plug, false, conf, addrtype); err = sk_socket_error(sockets[nsockets]); if (!err) { char *hostname = get_hostname(); if (hostname) { char *canonicalname = NULL; a_tcp = sk_namelookup(hostname, &canonicalname, addrtype); sfree(canonicalname); } sfree(hostname); nsockets++; break; /* success! */ } else { sk_close(sockets[nsockets]); } if (!strcmp(err, strerror(EADDRINUSE))) /* yuck! */ goto out; } if (a_tcp) { x11_format_auth_for_authfile( BinarySink_UPCAST(authfiledata), a_tcp, displayno, authproto, authdata); } /* * Try to establish the Unix-domain analogue. That may or may not * work - file permissions in /tmp may prevent it, for example - * but it's worth a try, and we don't consider it a fatal error if * it doesn't work. */ unix_path = dupprintf("/tmp/.X11-unix/X%d", displayno); a_unix = unix_sock_addr(unix_path); sockets[nsockets] = new_unix_listener(a_unix, plug); if (!sk_socket_error(sockets[nsockets])) { x11_format_auth_for_authfile( BinarySink_UPCAST(authfiledata), a_unix, displayno, authproto, authdata); nsockets++; } else { sk_close(sockets[nsockets]); sfree(unix_path); unix_path = NULL; } /* * Decide where the authority data will be written. */ tmpdir = getenv("TMPDIR"); if (!tmpdir || !*tmpdir) tmpdir = "/tmp"; authfilename = dupcat(tmpdir, "/", progname, "-Xauthority-XXXXXX"); { int oldumask = umask(077); authfd = mkstemp(authfilename); umask(oldumask); } if (authfd < 0) { while (nsockets-- > 0) sk_close(sockets[nsockets]); goto out; } /* * Spawn a subprocess which will try to reliably delete our * auth file when we terminate, in case we die unexpectedly. */ { int cleanup_pipe[2]; pid_t pid; /* Don't worry if pipe or fork fails; it's not _that_ critical. */ if (!pipe(cleanup_pipe)) { if ((pid = fork()) == 0) { int buf[1024]; /* * Our parent process holds the writing end of * this pipe, and writes nothing to it. Hence, * we expect read() to return EOF as soon as * that process terminates. */ close(0); close(1); close(2); setpgid(0, 0); close(cleanup_pipe[1]); close(authfd); while (read(cleanup_pipe[0], buf, sizeof(buf)) > 0); unlink(authfilename); if (unix_path) unlink(unix_path); _exit(0); } else if (pid < 0) { close(cleanup_pipe[0]); close(cleanup_pipe[1]); } else { close(cleanup_pipe[0]); cloexec(cleanup_pipe[1]); } } } authfp = fdopen(authfd, "wb"); fwrite(authfiledata->u, 1, authfiledata->len, authfp); fclose(authfp); { char *display = dupprintf(":%d%s", displayno, screen_number_suffix); conf_set_str_str(conf, CONF_environmt, "DISPLAY", display); sfree(display); } conf_set_str_str(conf, CONF_environmt, "XAUTHORITY", authfilename); /* * FIXME: return at least the DISPLAY and XAUTHORITY env settings, * and perhaps also the display number */ out: if (a_tcp) sk_addr_free(a_tcp); /* a_unix doesn't need freeing, because new_unix_listener took it over */ sfree(authfilename); strbuf_free(authfiledata); sfree(unix_path); return nsockets; } putty-0.76/unix/uxagentc.c0000644000175000017500000001335514072266313012534 00000000000000/* * SSH agent client code. */ #include #include #include #include #include #include #include #include "putty.h" #include "misc.h" #include "tree234.h" #include "puttymem.h" bool agent_exists(void) { const char *p = getenv("SSH_AUTH_SOCK"); if (p && *p) return true; return false; } static tree234 *agent_pending_queries; struct agent_pending_query { int fd; char *retbuf; char sizebuf[4]; int retsize, retlen; void (*callback)(void *, void *, int); void *callback_ctx; }; static int agent_conncmp(void *av, void *bv) { agent_pending_query *a = (agent_pending_query *) av; agent_pending_query *b = (agent_pending_query *) bv; if (a->fd < b->fd) return -1; if (a->fd > b->fd) return +1; return 0; } static int agent_connfind(void *av, void *bv) { int afd = *(int *) av; agent_pending_query *b = (agent_pending_query *) bv; if (afd < b->fd) return -1; if (afd > b->fd) return +1; return 0; } /* * Attempt to read from an agent socket fd. Returns false if the * expected response is as yet incomplete; returns true if it's either * complete (conn->retbuf non-NULL and filled with something useful) * or has failed totally (conn->retbuf is NULL). */ static bool agent_try_read(agent_pending_query *conn) { int ret; ret = read(conn->fd, conn->retbuf+conn->retlen, conn->retsize-conn->retlen); if (ret <= 0) { if (conn->retbuf != conn->sizebuf) sfree(conn->retbuf); conn->retbuf = NULL; conn->retlen = 0; return true; } conn->retlen += ret; if (conn->retsize == 4 && conn->retlen == 4) { conn->retsize = toint(GET_32BIT_MSB_FIRST(conn->retbuf) + 4); if (conn->retsize <= 0) { conn->retbuf = NULL; conn->retlen = 0; return true; /* way too large */ } assert(conn->retbuf == conn->sizebuf); conn->retbuf = snewn(conn->retsize, char); memcpy(conn->retbuf, conn->sizebuf, 4); } if (conn->retlen < conn->retsize) return false; /* more data to come */ return true; } void agent_cancel_query(agent_pending_query *conn) { uxsel_del(conn->fd); close(conn->fd); del234(agent_pending_queries, conn); if (conn->retbuf && conn->retbuf != conn->sizebuf) sfree(conn->retbuf); sfree(conn); } static void agent_select_result(int fd, int event) { agent_pending_query *conn; assert(event == SELECT_R); /* not selecting for anything but R */ conn = find234(agent_pending_queries, &fd, agent_connfind); if (!conn) { uxsel_del(fd); return; } if (!agent_try_read(conn)) return; /* more data to come */ /* * We have now completed the agent query. Do the callback. */ conn->callback(conn->callback_ctx, conn->retbuf, conn->retlen); /* Null out conn->retbuf, since ownership of that buffer has * passed to the callback. */ conn->retbuf = NULL; agent_cancel_query(conn); } static const char *agent_socket_path(void) { return getenv("SSH_AUTH_SOCK"); } Socket *agent_connect(Plug *plug) { const char *path = agent_socket_path(); if (!path) return new_error_socket_fmt(plug, "SSH_AUTH_SOCK not set"); return sk_new(unix_sock_addr(path), 0, false, false, false, false, plug); } agent_pending_query *agent_query( strbuf *query, void **out, int *outlen, void (*callback)(void *, void *, int), void *callback_ctx) { const char *name; int sock; struct sockaddr_un addr; int done; agent_pending_query *conn; name = agent_socket_path(); if (!name || strlen(name) >= sizeof(addr.sun_path)) goto failure; sock = socket(PF_UNIX, SOCK_STREAM, 0); if (sock < 0) { perror("socket(PF_UNIX)"); exit(1); } cloexec(sock); addr.sun_family = AF_UNIX; strcpy(addr.sun_path, name); if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { close(sock); goto failure; } strbuf_finalise_agent_query(query); for (done = 0; done < query->len ;) { int ret = write(sock, query->s + done, query->len - done); if (ret <= 0) { close(sock); goto failure; } done += ret; } conn = snew(agent_pending_query); conn->fd = sock; conn->retbuf = conn->sizebuf; conn->retsize = 4; conn->retlen = 0; conn->callback = callback; conn->callback_ctx = callback_ctx; if (!callback) { /* * Bodge to permit making deliberately synchronous agent * requests. Used by Unix Pageant in command-line client mode, * which is legit because it really is true that no other part * of the program is trying to get anything useful done * simultaneously. But this special case shouldn't be used in * any more general program. */ no_nonblock(conn->fd); while (!agent_try_read(conn)) /* empty loop body */; *out = conn->retbuf; *outlen = conn->retlen; sfree(conn); return NULL; } /* * Otherwise do it properly: add conn to the tree of agent * connections currently in flight, return 0 to indicate that the * response hasn't been received yet, and call the callback when * select_result comes back to us. */ if (!agent_pending_queries) agent_pending_queries = newtree234(agent_conncmp); add234(agent_pending_queries, conn); uxsel_set(sock, SELECT_R, agent_select_result); return conn; failure: *out = NULL; *outlen = 0; return NULL; } putty-0.76/unix/uxagentsock.c0000644000175000017500000000464414072266313013252 00000000000000#include #include #include #include #include #include #include #include "putty.h" #include "ssh.h" #include "misc.h" #include "pageant.h" Socket *platform_make_agent_socket( Plug *plug, const char *dirprefix, char **error, char **name) { char *username, *socketdir, *socketname, *errw; const char *err; Socket *sock; *name = NULL; username = get_username(); socketdir = dupprintf("%s.%s", dirprefix, username); sfree(username); assert(*socketdir == '/'); if ((errw = make_dir_and_check_ours(socketdir)) != NULL) { *error = dupprintf("%s: %s\n", socketdir, errw); sfree(errw); sfree(socketdir); return NULL; } socketname = dupprintf("%s/pageant.%d", socketdir, (int)getpid()); sock = new_unix_listener(unix_sock_addr(socketname), plug); if ((err = sk_socket_error(sock)) != NULL) { *error = dupprintf("%s: %s\n", socketname, err); sk_close(sock); sfree(socketname); rmdir(socketdir); sfree(socketdir); return NULL; } /* * Spawn a subprocess which will try to reliably delete our socket * and its containing directory when we terminate, in case we die * unexpectedly. */ { int cleanup_pipe[2]; pid_t pid; /* Don't worry if pipe or fork fails; it's not _that_ critical. */ if (!pipe(cleanup_pipe)) { if ((pid = fork()) == 0) { int buf[1024]; /* * Our parent process holds the writing end of * this pipe, and writes nothing to it. Hence, * we expect read() to return EOF as soon as * that process terminates. */ close(0); close(1); close(2); setpgid(0, 0); close(cleanup_pipe[1]); while (read(cleanup_pipe[0], buf, sizeof(buf)) > 0); unlink(socketname); rmdir(socketdir); _exit(0); } else if (pid < 0) { close(cleanup_pipe[0]); close(cleanup_pipe[1]); } else { close(cleanup_pipe[0]); cloexec(cleanup_pipe[1]); } } } *name = socketname; *error = NULL; sfree(socketdir); return sock; } putty-0.76/unix/uxcfg.c0000644000175000017500000000452014072266313012024 00000000000000/* * uxcfg.c - the Unix-specific parts of the PuTTY configuration * box. */ #include #include #include "putty.h" #include "dialog.h" #include "storage.h" void unix_setup_config_box(struct controlbox *b, bool 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 = false; /* * 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; } } } } putty-0.76/unix/uxcliloop.c0000644000175000017500000000673714072266313012742 00000000000000#include #include "putty.h" void cli_main_loop(cliloop_pw_setup_t pw_setup, cliloop_pw_check_t pw_check, cliloop_continue_t cont, void *ctx) { unsigned long now = GETTICKCOUNT(); int *fdlist = NULL; size_t fdsize = 0; pollwrapper *pw = pollwrap_new(); while (true) { int rwx; int ret; int fdstate; unsigned long next; pollwrap_clear(pw); if (!pw_setup(ctx, pw)) break; /* our client signalled emergency exit */ /* Count the currently active fds. */ size_t nfds = 0; for (int fd = first_fd(&fdstate, &rwx); fd >= 0; fd = next_fd(&fdstate, &rwx)) nfds++; /* Expand the fdlist buffer if necessary. */ sgrowarray(fdlist, fdsize, nfds); /* * Add all currently open uxsel fds to pw, and store them in * fdlist as well. */ size_t fdcount = 0; for (int fd = first_fd(&fdstate, &rwx); fd >= 0; fd = next_fd(&fdstate, &rwx)) { fdlist[fdcount++] = fd; pollwrap_add_fd_rwx(pw, fd, rwx); } if (toplevel_callback_pending()) { ret = pollwrap_poll_instant(pw); } else if (run_timers(now, &next)) { do { unsigned long then; long ticks; then = now; now = GETTICKCOUNT(); if (now - then > next - then) ticks = 0; else ticks = next - now; bool overflow = false; if (ticks > INT_MAX) { ticks = INT_MAX; overflow = true; } ret = pollwrap_poll_timeout(pw, ticks); if (ret == 0 && !overflow) now = next; else now = GETTICKCOUNT(); } while (ret < 0 && errno == EINTR); } else { ret = pollwrap_poll_endless(pw); } if (ret < 0 && errno == EINTR) continue; if (ret < 0) { perror("poll"); exit(1); } bool found_fd = (ret > 0); for (size_t i = 0; i < fdcount; i++) { int fd = fdlist[i]; int rwx = pollwrap_get_fd_rwx(pw, fd); /* * We must process exceptional notifications before * ordinary readability ones, or we may go straight * past the urgent marker. */ if (rwx & SELECT_X) select_result(fd, SELECT_X); if (rwx & SELECT_R) select_result(fd, SELECT_R); if (rwx & SELECT_W) select_result(fd, SELECT_W); } pw_check(ctx, pw); bool ran_callback = run_toplevel_callbacks(); if (!cont(ctx, found_fd, ran_callback)) break; } pollwrap_free(pw); sfree(fdlist); } bool cliloop_no_pw_setup(void *ctx, pollwrapper *pw) { return true; } void cliloop_no_pw_check(void *ctx, pollwrapper *pw) {} bool cliloop_always_continue(void *ctx, bool fd, bool cb) { return true; } /* * Any application using this main loop doesn't need to do anything * when uxsel adds or removes an fd, because we synchronously re-check * the current list every time we go round the main loop above. */ uxsel_id *uxsel_input_add(int fd, int rwx) { return NULL; } void uxsel_input_remove(uxsel_id *id) { } putty-0.76/unix/uxcons.c0000644000175000017500000003445314072266313012237 00000000000000/* * uxcons.c: various interactive-prompt routines shared between the * Unix console PuTTY tools */ #include #include #include #include #include #include #include #include #include "putty.h" #include "storage.h" #include "ssh.h" #include "console.h" static struct termios orig_termios_stderr; static bool stderr_is_a_tty; void stderr_tty_init() { /* Ensure that if stderr is a tty, we can get it back to a sane state. */ if (isatty(STDERR_FILENO)) { stderr_is_a_tty = true; tcgetattr(STDERR_FILENO, &orig_termios_stderr); } } void premsg(struct termios *cf) { if (stderr_is_a_tty) { tcgetattr(STDERR_FILENO, cf); tcsetattr(STDERR_FILENO, TCSADRAIN, &orig_termios_stderr); } } void postmsg(struct termios *cf) { if (stderr_is_a_tty) tcsetattr(STDERR_FILENO, TCSADRAIN, cf); } void cleanup_exit(int code) { /* * Clean up. */ sk_cleanup(); random_save_seed(); exit(code); } void console_print_error_msg(const char *prefix, const char *msg) { struct termios cf; premsg(&cf); fputs(prefix, stderr); fputs(": ", stderr); fputs(msg, stderr); fputc('\n', stderr); fflush(stderr); postmsg(&cf); } /* * Wrapper around Unix read(2), suitable for use on a file descriptor * that's been set into nonblocking mode. Handles EAGAIN/EWOULDBLOCK * by means of doing a one-fd poll and then trying again; all other * errors (including errors from poll) are returned to the caller. */ static int block_and_read(int fd, void *buf, size_t len) { int ret; pollwrapper *pw = pollwrap_new(); while ((ret = read(fd, buf, len)) < 0 && ( #ifdef EAGAIN (errno == EAGAIN) || #endif #ifdef EWOULDBLOCK (errno == EWOULDBLOCK) || #endif false)) { pollwrap_clear(pw); pollwrap_add_fd_rwx(pw, fd, SELECT_R); do { ret = pollwrap_poll_endless(pw); } while (ret < 0 && errno == EINTR); assert(ret != 0); if (ret < 0) { pollwrap_free(pw); return ret; } assert(pollwrap_check_fd_rwx(pw, fd, SELECT_R)); } pollwrap_free(pw); return ret; } int console_verify_ssh_host_key( Seat *seat, const char *host, int port, const char *keytype, char *keystr, const char *keydisp, char **fingerprints, void (*callback)(void *ctx, int result), void *ctx) { int ret; char line[32]; struct termios cf; const char *common_fmt, *intro, *prompt; /* * Verify the key. */ ret = verify_host_key(host, port, keytype, keystr); if (ret == 0) /* success - key matched OK */ return 1; premsg(&cf); if (ret == 2) { /* key was different */ common_fmt = hk_wrongmsg_common_fmt; intro = hk_wrongmsg_interactive_intro; prompt = hk_wrongmsg_interactive_prompt; } else { /* key was absent */ common_fmt = hk_absentmsg_common_fmt; intro = hk_absentmsg_interactive_intro; prompt = hk_absentmsg_interactive_prompt; } FingerprintType fptype_default = ssh2_pick_default_fingerprint(fingerprints); fprintf(stderr, common_fmt, keytype, fingerprints[fptype_default]); if (console_batch_mode) { fputs(console_abandoned_msg, stderr); return 0; } fputs(intro, stderr); fflush(stderr); while (true) { fputs(prompt, stderr); fflush(stderr); struct termios oldmode, newmode; tcgetattr(0, &oldmode); newmode = oldmode; newmode.c_lflag |= ECHO | ISIG | ICANON; tcsetattr(0, TCSANOW, &newmode); line[0] = '\0'; if (block_and_read(0, line, sizeof(line) - 1) <= 0) /* handled below */; tcsetattr(0, TCSANOW, &oldmode); if (line[0] == 'i' || line[0] == 'I') { fprintf(stderr, "Full public key:\n%s\n", keydisp); if (fingerprints[SSH_FPTYPE_SHA256]) fprintf(stderr, "SHA256 key fingerprint:\n%s\n", fingerprints[SSH_FPTYPE_SHA256]); if (fingerprints[SSH_FPTYPE_MD5]) fprintf(stderr, "MD5 key fingerprint:\n%s\n", fingerprints[SSH_FPTYPE_MD5]); } else { break; } } /* In case of misplaced reflexes from another program, also recognise 'q' * as 'abandon connection rather than trust this key' */ if (line[0] != '\0' && line[0] != '\r' && line[0] != '\n' && line[0] != 'q' && line[0] != 'Q') { if (line[0] == 'y' || line[0] == 'Y') store_host_key(host, port, keytype, keystr); postmsg(&cf); return 1; } else { fputs(console_abandoned_msg, stderr); postmsg(&cf); return 0; } } int console_confirm_weak_crypto_primitive( Seat *seat, const char *algtype, const char *algname, void (*callback)(void *ctx, int result), void *ctx) { char line[32]; struct termios cf; premsg(&cf); fprintf(stderr, weakcrypto_msg_common_fmt, algtype, algname); if (console_batch_mode) { fputs(console_abandoned_msg, stderr); postmsg(&cf); return 0; } fputs(console_continue_prompt, stderr); fflush(stderr); { struct termios oldmode, newmode; tcgetattr(0, &oldmode); newmode = oldmode; newmode.c_lflag |= ECHO | ISIG | ICANON; tcsetattr(0, TCSANOW, &newmode); line[0] = '\0'; if (block_and_read(0, line, sizeof(line) - 1) <= 0) /* handled below */; tcsetattr(0, TCSANOW, &oldmode); } if (line[0] == 'y' || line[0] == 'Y') { postmsg(&cf); return 1; } else { fputs(console_abandoned_msg, stderr); postmsg(&cf); return 0; } } int console_confirm_weak_cached_hostkey( Seat *seat, const char *algname, const char *betteralgs, void (*callback)(void *ctx, int result), void *ctx) { char line[32]; struct termios cf; premsg(&cf); fprintf(stderr, weakhk_msg_common_fmt, algname, betteralgs); if (console_batch_mode) { fputs(console_abandoned_msg, stderr); postmsg(&cf); return 0; } fputs(console_continue_prompt, stderr); fflush(stderr); { struct termios oldmode, newmode; tcgetattr(0, &oldmode); newmode = oldmode; newmode.c_lflag |= ECHO | ISIG | ICANON; tcsetattr(0, TCSANOW, &newmode); line[0] = '\0'; if (block_and_read(0, line, sizeof(line) - 1) <= 0) /* handled below */; tcsetattr(0, TCSANOW, &oldmode); } if (line[0] == 'y' || line[0] == 'Y') { postmsg(&cf); return 1; } else { fputs(console_abandoned_msg, stderr); postmsg(&cf); 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 console_askappend(LogPolicy *lp, 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; tcsetattr(0, TCSANOW, &newmode); line[0] = '\0'; if (block_and_read(0, line, sizeof(line) - 1) <= 0) /* handled below */; tcsetattr(0, TCSANOW, &oldmode); } postmsg(&cf); if (line[0] == 'y' || line[0] == 'Y') return 2; else if (line[0] == 'n' || line[0] == 'N') return 1; else return 0; } bool console_antispoof_prompt = true; bool console_set_trust_status(Seat *seat, bool trusted) { if (console_batch_mode || !is_interactive() || !console_antispoof_prompt) { /* * In batch mode, we don't need to worry about the server * mimicking our interactive authentication, because the user * already knows not to expect any. * * If standard input isn't connected to a terminal, likewise, * because even if the server did send a spoof authentication * prompt, the user couldn't respond to it via the terminal * anyway. * * We also vacuously return success if the user has purposely * disabled the antispoof prompt. */ return true; } return false; } /* * Warn about the obsolescent key file format. * * Uniquely among these functions, this one does _not_ expect a * frontend handle. This means that if PuTTY is ported to a * platform which requires frontend handles, this function will be * an anomaly. Fortunately, the problem it addresses will not have * been present on that platform, so it can plausibly be * implemented as an empty function. */ void old_keyfile_warning(void) { static const char message[] = "You are loading an SSH-2 private key which has an\n" "old version of the file format. This means your key\n" "file is not fully tamperproof. Future versions of\n" "PuTTY may stop supporting this private key format,\n" "so we recommend you convert your key to the new\n" "format.\n" "\n" "Once the key is loaded into PuTTYgen, you can perform\n" "this conversion simply by saving it again.\n"; struct termios cf; premsg(&cf); fputs(message, stderr); postmsg(&cf); } void console_logging_error(LogPolicy *lp, const char *string) { /* Errors setting up logging are considered important, so they're * displayed to standard error even when not in verbose mode */ struct termios cf; premsg(&cf); fprintf(stderr, "%s\n", string); fflush(stderr); postmsg(&cf); } void console_eventlog(LogPolicy *lp, const char *string) { /* Ordinary Event Log entries are displayed in the same way as * logging errors, but only in verbose mode */ if (lp_verbose(lp)) console_logging_error(lp, string); } StripCtrlChars *console_stripctrl_new( Seat *seat, BinarySink *bs_out, SeatInteractionContext sic) { return stripctrl_new(bs_out, false, 0); } /* * 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_write(FILE *outfp, ptrlen data) { fwrite(data.ptr, 1, data.len, outfp); fflush(outfp); } int console_get_userpass_input(prompts_t *p) { 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) { ptrlen plname = ptrlen_from_asciz(p->name); console_write(outfp, plname); if (!ptrlen_endswith(plname, PTRLEN_LITERAL("\n"), NULL)) console_write(outfp, PTRLEN_LITERAL("\n")); } /* ...but we always print any `instruction'. */ if (p->instruction) { ptrlen plinst = ptrlen_from_asciz(p->instruction); console_write(outfp, plinst); if (!ptrlen_endswith(plinst, PTRLEN_LITERAL("\n"), NULL)) console_write(outfp, PTRLEN_LITERAL("\n")); } for (curr_prompt = 0; curr_prompt < p->n_prompts; curr_prompt++) { struct termios oldmode, newmode; 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_write(outfp, ptrlen_from_asciz(pr->prompt)); bool failed = false; while (1) { size_t toread = 65536; size_t prev_result_len = pr->result->len; void *ptr = strbuf_append(pr->result, toread); int ret = read(infd, ptr, toread); if (ret <= 0) { failed = true; break; } strbuf_shrink_to(pr->result, prev_result_len + ret); if (strbuf_chomp(pr->result, '\n')) break; } tcsetattr(infd, TCSANOW, &oldmode); if (!pr->echo) console_write(outfp, PTRLEN_LITERAL("\n")); if (failed) { console_close(outfp, infd); return 0; /* failure due to read error */ } } console_close(outfp, infd); return 1; /* success */ } bool is_interactive(void) { return isatty(0); } /* * X11-forwarding-related things suitable for console. */ char *platform_get_x_display(void) { return dupstr(getenv("DISPLAY")); } putty-0.76/unix/uxfdsock.c0000644000175000017500000002063114072266313012537 00000000000000/* * uxfdsock.c: implementation of Socket that just talks to two * existing input and output file descriptors. */ #include #include #include #include #include #include "tree234.h" #include "putty.h" #include "network.h" typedef struct FdSocket { int outfd, infd, inerrfd; bufchain pending_output_data; bufchain pending_input_data; ProxyStderrBuf psb; enum { EOF_NO, EOF_PENDING, EOF_SENT } outgoingeof; int pending_error; Plug *plug; Socket sock; } FdSocket; static void fdsocket_select_result_input(int fd, int event); static void fdsocket_select_result_output(int fd, int event); static void fdsocket_select_result_input_error(int fd, int event); /* * Trees to look up the fds in. */ static tree234 *fdsocket_by_outfd; static tree234 *fdsocket_by_infd; static tree234 *fdsocket_by_inerrfd; static int fdsocket_infd_cmp(void *av, void *bv) { FdSocket *a = (FdSocket *)av; FdSocket *b = (FdSocket *)bv; if (a->infd < b->infd) return -1; if (a->infd > b->infd) return +1; return 0; } static int fdsocket_infd_find(void *av, void *bv) { int a = *(int *)av; FdSocket *b = (FdSocket *)bv; if (a < b->infd) return -1; if (a > b->infd) return +1; return 0; } static int fdsocket_inerrfd_cmp(void *av, void *bv) { FdSocket *a = (FdSocket *)av; FdSocket *b = (FdSocket *)bv; if (a->inerrfd < b->inerrfd) return -1; if (a->inerrfd > b->inerrfd) return +1; return 0; } static int fdsocket_inerrfd_find(void *av, void *bv) { int a = *(int *)av; FdSocket *b = (FdSocket *)bv; if (a < b->inerrfd) return -1; if (a > b->inerrfd) return +1; return 0; } static int fdsocket_outfd_cmp(void *av, void *bv) { FdSocket *a = (FdSocket *)av; FdSocket *b = (FdSocket *)bv; if (a->outfd < b->outfd) return -1; if (a->outfd > b->outfd) return +1; return 0; } static int fdsocket_outfd_find(void *av, void *bv) { int a = *(int *)av; FdSocket *b = (FdSocket *)bv; if (a < b->outfd) return -1; if (a > b->outfd) return +1; return 0; } static Plug *fdsocket_plug(Socket *s, Plug *p) { FdSocket *fds = container_of(s, FdSocket, sock); Plug *ret = fds->plug; if (p) fds->plug = p; return ret; } static void fdsocket_close(Socket *s) { FdSocket *fds = container_of(s, FdSocket, sock); if (fds->outfd >= 0) { del234(fdsocket_by_outfd, fds); uxsel_del(fds->outfd); close(fds->outfd); } if (fds->infd >= 0) { del234(fdsocket_by_infd, fds); uxsel_del(fds->infd); close(fds->infd); } if (fds->inerrfd >= 0) { del234(fdsocket_by_inerrfd, fds); uxsel_del(fds->inerrfd); close(fds->inerrfd); } bufchain_clear(&fds->pending_input_data); bufchain_clear(&fds->pending_output_data); delete_callbacks_for_context(fds); sfree(fds); } static void fdsocket_error_callback(void *vs) { FdSocket *fds = (FdSocket *)vs; /* * Just in case other socket work has caused this socket to vanish * or become somehow non-erroneous before this callback arrived... */ if (!fds->pending_error) return; /* * An error has occurred on this socket. Pass it to the plug. */ plug_closing(fds->plug, strerror(fds->pending_error), fds->pending_error, 0); } static int fdsocket_try_send(FdSocket *fds) { int sent = 0; while (bufchain_size(&fds->pending_output_data) > 0) { ssize_t ret; ptrlen data = bufchain_prefix(&fds->pending_output_data); ret = write(fds->outfd, data.ptr, data.len); noise_ultralight(NOISE_SOURCE_IOID, ret); if (ret < 0 && errno != EWOULDBLOCK) { if (!fds->pending_error) { fds->pending_error = errno; queue_toplevel_callback(fdsocket_error_callback, fds); } return 0; } else if (ret <= 0) { break; } else { bufchain_consume(&fds->pending_output_data, ret); sent += ret; } } if (fds->outgoingeof == EOF_PENDING) { del234(fdsocket_by_outfd, fds); close(fds->outfd); uxsel_del(fds->outfd); fds->outfd = -1; fds->outgoingeof = EOF_SENT; } if (bufchain_size(&fds->pending_output_data) == 0) uxsel_del(fds->outfd); else uxsel_set(fds->outfd, SELECT_W, fdsocket_select_result_output); return sent; } static size_t fdsocket_write(Socket *s, const void *data, size_t len) { FdSocket *fds = container_of(s, FdSocket, sock); assert(fds->outgoingeof == EOF_NO); bufchain_add(&fds->pending_output_data, data, len); fdsocket_try_send(fds); return bufchain_size(&fds->pending_output_data); } static size_t fdsocket_write_oob(Socket *s, const void *data, size_t len) { /* * oob data is treated as inband; nasty, but nothing really * better we can do */ return fdsocket_write(s, data, len); } static void fdsocket_write_eof(Socket *s) { FdSocket *fds = container_of(s, FdSocket, sock); assert(fds->outgoingeof == EOF_NO); fds->outgoingeof = EOF_PENDING; fdsocket_try_send(fds); } static void fdsocket_set_frozen(Socket *s, bool is_frozen) { FdSocket *fds = container_of(s, FdSocket, sock); if (fds->infd < 0) return; if (is_frozen) uxsel_del(fds->infd); else uxsel_set(fds->infd, SELECT_R, fdsocket_select_result_input); } static const char *fdsocket_socket_error(Socket *s) { return NULL; } static void fdsocket_select_result_input(int fd, int event) { FdSocket *fds; char buf[20480]; int retd; if (!(fds = find234(fdsocket_by_infd, &fd, fdsocket_infd_find))) return; retd = read(fds->infd, buf, sizeof(buf)); if (retd > 0) { plug_receive(fds->plug, 0, buf, retd); } else { if (retd < 0) { plug_closing(fds->plug, strerror(errno), errno, 0); } else { plug_closing(fds->plug, NULL, 0, 0); } del234(fdsocket_by_infd, fds); uxsel_del(fds->infd); close(fds->infd); fds->infd = -1; } } static void fdsocket_select_result_output(int fd, int event) { FdSocket *fds; if (!(fds = find234(fdsocket_by_outfd, &fd, fdsocket_outfd_find))) return; if (fdsocket_try_send(fds)) plug_sent(fds->plug, bufchain_size(&fds->pending_output_data)); } static void fdsocket_select_result_input_error(int fd, int event) { FdSocket *fds; char buf[20480]; int retd; if (!(fds = find234(fdsocket_by_inerrfd, &fd, fdsocket_inerrfd_find))) return; retd = read(fd, buf, sizeof(buf)); if (retd > 0) { log_proxy_stderr(fds->plug, &fds->psb, buf, retd); } else { del234(fdsocket_by_inerrfd, fds); uxsel_del(fds->inerrfd); close(fds->inerrfd); fds->inerrfd = -1; } } static const SocketVtable FdSocket_sockvt = { .plug = fdsocket_plug, .close = fdsocket_close, .write = fdsocket_write, .write_oob = fdsocket_write_oob, .write_eof = fdsocket_write_eof, .set_frozen = fdsocket_set_frozen, .socket_error = fdsocket_socket_error, .peer_info = NULL, }; Socket *make_fd_socket(int infd, int outfd, int inerrfd, Plug *plug) { FdSocket *fds; fds = snew(FdSocket); fds->sock.vt = &FdSocket_sockvt; fds->plug = plug; fds->outgoingeof = EOF_NO; fds->pending_error = 0; fds->infd = infd; fds->outfd = outfd; fds->inerrfd = inerrfd; bufchain_init(&fds->pending_input_data); bufchain_init(&fds->pending_output_data); psb_init(&fds->psb); if (fds->outfd >= 0) { if (!fdsocket_by_outfd) fdsocket_by_outfd = newtree234(fdsocket_outfd_cmp); add234(fdsocket_by_outfd, fds); } if (fds->infd >= 0) { if (!fdsocket_by_infd) fdsocket_by_infd = newtree234(fdsocket_infd_cmp); add234(fdsocket_by_infd, fds); uxsel_set(fds->infd, SELECT_R, fdsocket_select_result_input); } if (fds->inerrfd >= 0) { assert(fds->inerrfd != fds->infd); if (!fdsocket_by_inerrfd) fdsocket_by_inerrfd = newtree234(fdsocket_inerrfd_cmp); add234(fdsocket_by_inerrfd, fds); uxsel_set(fds->inerrfd, SELECT_R, fdsocket_select_result_input_error); } return &fds->sock; } putty-0.76/unix/uxgen.c0000644000175000017500000000260314072266313012036 00000000000000/* * uxgen.c: Unix implementation of get_heavy_noise() from cmdgen.c. */ #include #include #include #include #include "putty.h" char *get_random_data(int len, const char *device) { char *buf = snewn(len, char); int fd; int ngot, ret; if (!device) { static const char *const default_devices[] = { "/dev/urandom", "/dev/random" }; size_t i; for (i = 0; i < lenof(default_devices); i++) { if (access(default_devices[i], R_OK) == 0) { device = default_devices[i]; break; } } if (!device) { sfree(buf); fprintf(stderr, "puttygen: cannot find a readable " "random number source; use --random-device\n"); return NULL; } } fd = open(device, O_RDONLY); if (fd < 0) { sfree(buf); fprintf(stderr, "puttygen: %s: open: %s\n", device, strerror(errno)); return NULL; } ngot = 0; while (ngot < len) { ret = read(fd, buf+ngot, len-ngot); if (ret < 0) { close(fd); sfree(buf); fprintf(stderr, "puttygen: %s: read: %s\n", device, strerror(errno)); return NULL; } ngot += ret; } close(fd); return buf; } putty-0.76/unix/uxgss.c0000644000175000017500000001147214072266313012065 00000000000000#include "putty.h" #ifndef NO_GSSAPI #include "pgssapi.h" #include "sshgss.h" #include "sshgssc.h" /* Unix code to set up the GSSAPI library list. */ #if !defined NO_LIBDL && !defined NO_GSSAPI const int ngsslibs = 4; const char *const gsslibnames[4] = { "libgssapi (Heimdal)", "libgssapi_krb5 (MIT Kerberos)", "libgss (Sun)", "User-specified GSSAPI library", }; const struct keyvalwhere gsslibkeywords[] = { { "libgssapi", 0, -1, -1 }, { "libgssapi_krb5", 1, -1, -1 }, { "libgss", 2, -1, -1 }, { "custom", 3, -1, -1 }, }; /* * Run-time binding against a choice of GSSAPI implementations. We * try loading several libraries, and produce an entry in * ssh_gss_libraries[] for each one. */ static void gss_init(struct ssh_gss_library *lib, void *dlhandle, int id, const char *msg) { lib->id = id; lib->gsslogmsg = msg; lib->handle = dlhandle; #define BIND_GSS_FN(name) \ lib->u.gssapi.name = (t_gss_##name) dlsym(dlhandle, "gss_" #name) BIND_GSS_FN(delete_sec_context); BIND_GSS_FN(display_status); BIND_GSS_FN(get_mic); BIND_GSS_FN(verify_mic); BIND_GSS_FN(import_name); BIND_GSS_FN(init_sec_context); BIND_GSS_FN(release_buffer); BIND_GSS_FN(release_cred); BIND_GSS_FN(release_name); BIND_GSS_FN(acquire_cred); BIND_GSS_FN(inquire_cred_by_mech); #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) gss_init(&list->libraries[list->nlibraries++], gsslib, 0, "Using GSSAPI from libgssapi.so.2"); /* MIT Kerberos's GSSAPI Library */ if ((gsslib = dlopen("libgssapi_krb5.so.2", RTLD_LAZY)) != NULL) gss_init(&list->libraries[list->nlibraries++], gsslib, 1, "Using GSSAPI from libgssapi_krb5.so.2"); /* 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; /* * dlopen and dlclose are defined to employ reference counting * in the case where the same library is repeatedly dlopened, so * even in a multiple-sessions-per-process context it's safe to * naively dlclose everything here without worrying about * destroying it under the feet of another SSH instance still * using it. */ for (i = 0; i < list->nlibraries; i++) { dlclose(list->libraries[i].handle); if (list->libraries[i].id == 3) { /* The 'custom' id involves a dynamically allocated message. * Note that we must cast away the 'const' to free it. */ sfree((char *)list->libraries[i].gsslogmsg); } } sfree(list->libraries); sfree(list); } #elif !defined NO_GSSAPI const int ngsslibs = 1; const char *const gsslibnames[1] = { "static", }; const struct keyvalwhere gsslibkeywords[] = { { "static", 0, -1, -1 }, }; /* * Link-time binding against GSSAPI. Here we just construct a single * library structure containing pointers to the functions we linked * against. */ #include /* 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"; #define BIND_GSS_FN(name) \ list->libraries[0].u.gssapi.name = (t_gss_##name) gss_##name BIND_GSS_FN(delete_sec_context); BIND_GSS_FN(display_status); BIND_GSS_FN(get_mic); BIND_GSS_FN(verify_mic); BIND_GSS_FN(import_name); BIND_GSS_FN(init_sec_context); BIND_GSS_FN(release_buffer); BIND_GSS_FN(release_cred); BIND_GSS_FN(release_name); BIND_GSS_FN(acquire_cred); BIND_GSS_FN(inquire_cred_by_mech); #undef BIND_GSS_FN ssh_gssapi_bind_fns(&list->libraries[0]); return list; } void ssh_gss_cleanup(struct ssh_gss_liblist *list) { sfree(list->libraries); sfree(list); } #endif /* NO_LIBDL */ #endif /* NO_GSSAPI */ putty-0.76/unix/uxmisc.c0000644000175000017500000002361614072266313012227 00000000000000/* * PuTTY miscellaneous Unix stuff */ #include #include #include #include #include #include #include #include #include #include #include #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; } bool filename_equal(const Filename *f1, const Filename *f2) { return !strcmp(f1->path, f2->path); } bool filename_is_null(const Filename *fn) { return !fn->path[0]; } void filename_free(Filename *fn) { sfree(fn->path); sfree(fn); } void filename_serialise(BinarySink *bs, const Filename *f) { put_asciz(bs, f->path); } Filename *filename_deserialise(BinarySource *src) { return filename_from_str(get_asciz(src)); } char filename_char_sanitise(char c) { if (c == '/') return '.'; return c; } #ifdef DEBUG static FILE *debug_fp = NULL; void dputs(const char *buf) { if (!debug_fp) { debug_fp = fopen("debug.log", "w"); } if (write(1, buf, strlen(buf)) < 0) {} /* 'error check' to placate gcc */ fputs(buf, debug_fp); fflush(debug_fp); } #endif char *get_username(void) { struct passwd *p; uid_t uid = getuid(); char *user, *ret = NULL; /* * First, find who we think we are using getlogin. If this * agrees with our uid, we'll go along with it. This should * allow sharing of uids between several login names whilst * coping correctly with people who have su'ed. */ user = getlogin(); #if HAVE_SETPWENT setpwent(); #endif if (user) p = getpwnam(user); else p = NULL; if (p && p->pw_uid == uid) { /* * The result of getlogin() really does correspond to * our uid. Fine. */ ret = user; } else { /* * If that didn't work, for whatever reason, we'll do * the simpler version: look up our uid in the password * file and map it straight to a name. */ p = getpwuid(uid); if (!p) return NULL; ret = p->pw_name; } #if HAVE_ENDPWENT endpwent(); #endif return dupstr(ret); } /* * Display the fingerprints of the PGP Master Keys to the user. * (This is here rather than in uxcons because it's appropriate even for * Unix GUI apps.) */ void pgp_fingerprints(void) { fputs("These are the fingerprints of the PuTTY PGP Master Keys. They can\n" "be used to establish a trust path from this executable to another\n" "one. See the manual for more information.\n" "(Note: these fingerprints have nothing to do with SSH!)\n" "\n" "PuTTY Master Key as of " PGP_MASTER_KEY_YEAR " (" PGP_MASTER_KEY_DETAILS "):\n" " " PGP_MASTER_KEY_FP "\n\n" "Previous Master Key (" PGP_PREV_MASTER_KEY_YEAR ", " PGP_PREV_MASTER_KEY_DETAILS "):\n" " " PGP_PREV_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); } } bool 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; } bool 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, bool 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); } void fontspec_serialise(BinarySink *bs, FontSpec *f) { put_asciz(bs, f->name); } FontSpec *fontspec_deserialise(BinarySource *src) { return fontspec_new(get_asciz(src)); } 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; } char *make_dir_path(const char *path, mode_t mode) { int pos = 0; char *prefix; while (1) { pos += strcspn(path + pos, "/"); if (pos > 0) { prefix = dupprintf("%.*s", pos, path); if (mkdir(prefix, mode) < 0 && errno != EEXIST) { char *ret = dupprintf("%s: mkdir: %s", prefix, strerror(errno)); sfree(prefix); return ret; } sfree(prefix); } if (!path[pos]) return NULL; pos += strspn(path + pos, "/"); } } bool open_for_write_would_lose_data(const Filename *fn) { struct stat st; if (stat(fn->path, &st) < 0) { /* * If the file doesn't even exist, we obviously want to return * false. If we failed to stat it for any other reason, * ignoring the precise error code and returning false still * doesn't seem too unreasonable, because then we'll try to * open the file for writing and report _that_ error, which is * likely to be more to the point. */ return false; } /* * OK, something exists at this pathname and we've found out * something about it. But an open-for-write will only * destructively truncate it if it's a regular file with nonzero * size. If it's empty, or some other kind of special thing like a * character device (e.g. /dev/tty) or a named pipe, then opening * it for write is already non-destructive and it's pointless and * annoying to warn about it just because the same file can be * opened for reading. (Indeed, if it's a named pipe, opening it * for reading actually _causes inconvenience_ in its own right, * even before the question of whether it gives misleading * information.) */ if (S_ISREG(st.st_mode) && st.st_size > 0) { return true; } return false; } putty-0.76/unix/uxnet.c0000644000175000017500000013764414072266313012071 00000000000000/* * Unix networking abstraction. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "putty.h" #include "network.h" #include "tree234.h" /* Solaris needs for SIOCATMARK. */ #ifndef SIOCATMARK #include #endif #ifndef X11_UNIX_PATH # define X11_UNIX_PATH "/tmp/.X11-unix/X" #endif /* * Access to sockaddr types without breaking C strict aliasing rules. */ union sockaddr_union { struct sockaddr_storage storage; struct sockaddr sa; struct sockaddr_in sin; #ifndef NO_IPV6 struct sockaddr_in6 sin6; #endif struct sockaddr_un su; }; /* * Mutable state that goes with a SockAddr: stores information * about where in the list of candidate IP(v*) addresses we've * currently got to. */ typedef struct SockAddrStep_tag SockAddrStep; struct SockAddrStep_tag { #ifndef NO_IPV6 struct addrinfo *ai; /* steps along addr->ais */ #endif int curraddr; }; typedef struct NetSocket NetSocket; struct NetSocket { const char *error; int s; Plug *plug; bufchain output_data; bool connected; /* irrelevant for listening sockets */ bool writable; bool frozen; /* this causes readability notifications to be ignored */ bool localhost_only; /* for listening sockets */ char oobdata[1]; size_t sending_oob; bool oobpending; /* is there OOB data available to read? */ bool oobinline; enum { EOF_NO, EOF_PENDING, EOF_SENT } outgoingeof; bool incomingeof; int pending_error; /* in case send() returns error */ bool listener; bool nodelay, keepalive; /* for connect()-type sockets */ bool privport; int port; /* and again */ SockAddr *addr; SockAddrStep step; /* * 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. */ NetSocket *parent, *child; Socket sock; }; struct SockAddr { int refcount; const char *error; enum { UNRESOLVED, UNIX, IP } superfamily; #ifndef NO_IPV6 struct addrinfo *ais; /* Addresses IPv6 style. */ #else unsigned long *addresses; /* Addresses IPv4 style. */ int naddresses; #endif char hostname[512]; /* Store an unresolved host name. */ }; /* * Which address family this address belongs to. AF_INET for IPv4; * AF_INET6 for IPv6; AF_UNSPEC indicates that name resolution has * not been done and a simple host name is held in this SockAddr * structure. */ #ifndef NO_IPV6 #define SOCKADDR_FAMILY(addr, step) \ ((addr)->superfamily == UNRESOLVED ? AF_UNSPEC : \ (addr)->superfamily == UNIX ? AF_UNIX : \ (step).ai ? (step).ai->ai_family : AF_INET) #else /* Here we gratuitously reference 'step' to avoid gcc warnings about * 'set but not used' when compiling -DNO_IPV6 */ #define SOCKADDR_FAMILY(addr, step) \ ((addr)->superfamily == UNRESOLVED ? AF_UNSPEC : \ (addr)->superfamily == UNIX ? AF_UNIX : \ (step).curraddr ? AF_INET : AF_INET) #endif /* * Start a SockAddrStep structure to step through multiple * addresses. */ #ifndef NO_IPV6 #define START_STEP(addr, step) \ ((step).ai = (addr)->ais, (step).curraddr = 0) #else #define START_STEP(addr, step) \ ((step).curraddr = 0) #endif static tree234 *sktree; static void uxsel_tell(NetSocket *s); static int cmpfortree(void *av, void *bv) { NetSocket *a = (NetSocket *) av, *b = (NetSocket *) bv; int as = a->s, bs = b->s; if (as < bs) return -1; if (as > bs) return +1; if (a < b) return -1; if (a > b) return +1; return 0; } static int cmpforsearch(void *av, void *bv) { NetSocket *b = (NetSocket *) bv; int as = *(int *)av, bs = b->s; if (as < bs) return -1; if (as > bs) return +1; return 0; } void sk_init(void) { sktree = newtree234(cmpfortree); } void sk_cleanup(void) { NetSocket *s; int i; if (sktree) { for (i = 0; (s = index234(sktree, i)) != NULL; i++) { close(s->s); } } } SockAddr *sk_namelookup(const char *host, char **canonicalname, int address_family) { if (host[0] == '/') { *canonicalname = dupstr(host); return unix_sock_addr(host); } SockAddr *ret = snew(SockAddr); #ifndef NO_IPV6 struct addrinfo hints; int err; #else unsigned long a; struct hostent *h = NULL; int n; #endif strbuf *realhost = strbuf_new(); /* Clear the structure and default to IPv4. */ memset(ret, 0, sizeof(SockAddr)); ret->superfamily = UNRESOLVED; ret->error = NULL; ret->refcount = 1; #ifndef NO_IPV6 hints.ai_flags = AI_CANONNAME; hints.ai_family = (address_family == ADDRTYPE_IPV4 ? AF_INET : address_family == ADDRTYPE_IPV6 ? AF_INET6 : AF_UNSPEC); hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = 0; hints.ai_addrlen = 0; hints.ai_addr = NULL; hints.ai_canonname = NULL; hints.ai_next = NULL; { char *trimmed_host = host_strduptrim(host); /* strip [] on literals */ err = getaddrinfo(trimmed_host, NULL, &hints, &ret->ais); sfree(trimmed_host); } if (err != 0) { ret->error = gai_strerror(err); strbuf_free(realhost); return ret; } ret->superfamily = IP; if (ret->ais->ai_canonname != NULL) strbuf_catf(realhost, "%s", ret->ais->ai_canonname); else strbuf_catf(realhost, "%s", host); #else if ((a = inet_addr(host)) == (unsigned long)(in_addr_t)(-1)) { /* * Otherwise use the IPv4-only gethostbyname... (NOTE: * we don't use gethostbyname as a fallback!) */ if (ret->superfamily == UNRESOLVED) { /*debug("Resolving \"%s\" with gethostbyname() (IPv4 only)...\n", host); */ if ( (h = gethostbyname(host)) ) ret->superfamily = IP; } if (ret->superfamily == UNRESOLVED) { ret->error = (h_errno == HOST_NOT_FOUND || h_errno == NO_DATA || h_errno == NO_ADDRESS ? "Host does not exist" : h_errno == TRY_AGAIN ? "Temporary name service failure" : "gethostbyname: unknown error"); strbuf_free(realhost); return ret; } /* This way we are always sure the h->h_name is valid :) */ strbuf_clear(realhost); strbuf_catf(realhost, "%s", h->h_name); for (n = 0; h->h_addr_list[n]; n++); ret->addresses = snewn(n, unsigned long); ret->naddresses = n; for (n = 0; n < ret->naddresses; n++) { memcpy(&a, h->h_addr_list[n], sizeof(a)); ret->addresses[n] = ntohl(a); } } else { /* * This must be a numeric IPv4 address because it caused a * success return from inet_addr. */ ret->superfamily = IP; strbuf_clear(realhost); strbuf_catf(realhost, "%s", host); ret->addresses = snew(unsigned long); ret->naddresses = 1; ret->addresses[0] = ntohl(a); } #endif *canonicalname = strbuf_to_str(realhost); return ret; } SockAddr *sk_nonamelookup(const char *host) { SockAddr *ret = snew(SockAddr); ret->error = NULL; ret->superfamily = UNRESOLVED; strncpy(ret->hostname, host, lenof(ret->hostname)); ret->hostname[lenof(ret->hostname)-1] = '\0'; #ifndef NO_IPV6 ret->ais = NULL; #else ret->addresses = NULL; #endif ret->refcount = 1; return ret; } static bool sk_nextaddr(SockAddr *addr, SockAddrStep *step) { #ifndef NO_IPV6 if (step->ai && step->ai->ai_next) { step->ai = step->ai->ai_next; return true; } else return false; #else if (step->curraddr+1 < addr->naddresses) { step->curraddr++; return true; } else { 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'; strncat(buf, "", buflen - 1); } #else struct in_addr a; SockAddrStep step; START_STEP(addr, step); assert(SOCKADDR_FAMILY(addr, step) == AF_INET); a.s_addr = htonl(addr->addresses[0]); strncpy(buf, inet_ntoa(a), buflen); buf[buflen-1] = '\0'; #endif } } /* * This constructs a SockAddr that points at one specific sub-address * of a parent SockAddr. The returned SockAddr does not own all its * own memory: it points into the old one's data structures, so it * MUST NOT be used after the old one is freed, and it MUST NOT be * passed to sk_addr_free. (The latter is why it's returned by value * rather than dynamically allocated - that should clue in anyone * writing a call to it that something is weird about it.) */ static SockAddr sk_extractaddr_tmp( SockAddr *addr, const SockAddrStep *step) { SockAddr toret; toret = *addr; /* structure copy */ toret.refcount = 1; if (addr->superfamily == IP) { #ifndef NO_IPV6 toret.ais = step->ai; #else assert(SOCKADDR_FAMILY(addr, *step) == AF_INET); toret.addresses += step->curraddr; #endif } return toret; } bool sk_addr_needs_port(SockAddr *addr) { if (addr->superfamily == UNRESOLVED || addr->superfamily == UNIX) { return false; } else { return true; } } bool sk_hostname_is_local(const char *name) { return !strcmp(name, "localhost") || !strcmp(name, "::1") || !strncmp(name, "127.", 4); } #define ipv4_is_loopback(addr) \ (((addr).s_addr & htonl(0xff000000)) == htonl(0x7f000000)) static bool sockaddr_is_loopback(struct sockaddr *sa) { union sockaddr_union *u = (union sockaddr_union *)sa; switch (u->sa.sa_family) { case AF_INET: return ipv4_is_loopback(u->sin.sin_addr); #ifndef NO_IPV6 case AF_INET6: return IN6_IS_ADDR_LOOPBACK(&u->sin6.sin6_addr); #endif case AF_UNIX: return true; default: return false; } } bool sk_address_is_local(SockAddr *addr) { if (addr->superfamily == UNRESOLVED) return false; /* we don't know; assume not */ else if (addr->superfamily == UNIX) return true; else { #ifndef NO_IPV6 return sockaddr_is_loopback(addr->ais->ai_addr); #else struct in_addr a; SockAddrStep step; START_STEP(addr, step); assert(SOCKADDR_FAMILY(addr, step) == AF_INET); a.s_addr = htonl(addr->addresses[0]); return ipv4_is_loopback(a); #endif } } bool 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); return (family == AF_INET ? ADDRTYPE_IPV4 : #ifndef NO_IPV6 family == AF_INET6 ? ADDRTYPE_IPV6 : #endif ADDRTYPE_NAME); } void sk_addrcopy(SockAddr *addr, char *buf) { SockAddrStep step; int family; START_STEP(addr, step); family = SOCKADDR_FAMILY(addr, step); #ifndef NO_IPV6 if (family == AF_INET) memcpy(buf, &((struct sockaddr_in *)step.ai->ai_addr)->sin_addr, sizeof(struct in_addr)); else if (family == AF_INET6) memcpy(buf, &((struct sockaddr_in6 *)step.ai->ai_addr)->sin6_addr, sizeof(struct in6_addr)); else unreachable("bad address family in sk_addrcopy"); #else struct in_addr a; assert(family == AF_INET); a.s_addr = htonl(addr->addresses[step.curraddr]); memcpy(buf, (char*) &a.s_addr, 4); #endif } void sk_addr_free(SockAddr *addr) { if (--addr->refcount > 0) return; #ifndef NO_IPV6 if (addr->ais != NULL) freeaddrinfo(addr->ais); #else sfree(addr->addresses); #endif sfree(addr); } SockAddr *sk_addr_dup(SockAddr *addr) { addr->refcount++; return addr; } static Plug *sk_net_plug(Socket *sock, Plug *p) { NetSocket *s = container_of(sock, NetSocket, sock); Plug *ret = s->plug; if (p) s->plug = p; return ret; } static void sk_net_close(Socket *s); static size_t sk_net_write(Socket *s, const void *data, size_t len); static size_t sk_net_write_oob(Socket *s, const void *data, size_t len); static void sk_net_write_eof(Socket *s); static void sk_net_set_frozen(Socket *s, bool is_frozen); static SocketPeerInfo *sk_net_peer_info(Socket *s); static const char *sk_net_socket_error(Socket *s); static const SocketVtable NetSocket_sockvt = { .plug = sk_net_plug, .close = sk_net_close, .write = sk_net_write, .write_oob = sk_net_write_oob, .write_eof = sk_net_write_eof, .set_frozen = sk_net_set_frozen, .socket_error = sk_net_socket_error, .peer_info = sk_net_peer_info, }; static Socket *sk_net_accept(accept_ctx_t ctx, Plug *plug) { int sockfd = ctx.i; NetSocket *ret; /* * Create NetSocket structure. */ ret = snew(NetSocket); ret->sock.vt = &NetSocket_sockvt; ret->error = NULL; ret->plug = plug; bufchain_init(&ret->output_data); ret->writable = true; /* to start with */ ret->sending_oob = 0; ret->frozen = true; ret->localhost_only = false; /* unused, but best init anyway */ ret->pending_error = 0; ret->oobpending = false; ret->outgoingeof = EOF_NO; ret->incomingeof = false; ret->listener = false; ret->parent = ret->child = NULL; ret->addr = NULL; ret->connected = true; ret->s = sockfd; if (ret->s < 0) { ret->error = strerror(errno); return &ret->sock; } ret->oobinline = false; uxsel_tell(ret); add234(sktree, ret); return &ret->sock; } static int try_connect(NetSocket *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. */ del234(sktree, sock); if (sock->s >= 0) close(sock->s); { SockAddr thisaddr = sk_extractaddr_tmp( sock->addr, &sock->step); plug_log(sock->plug, PLUGLOG_CONNECT_TRYING, &thisaddr, sock->port, NULL, 0); } /* * Open socket. */ family = SOCKADDR_FAMILY(sock->addr, sock->step); assert(family != AF_UNSPEC); s = socket(family, SOCK_STREAM, 0); sock->s = s; if (s < 0) { err = errno; goto ret; } cloexec(s); if (sock->oobinline) { int b = 1; if (setsockopt(s, SOL_SOCKET, SO_OOBINLINE, (void *) &b, sizeof(b)) < 0) { err = errno; close(s); goto ret; } } if (sock->nodelay && family != AF_UNIX) { int b = 1; if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (void *) &b, sizeof(b)) < 0) { err = errno; close(s); goto ret; } } if (sock->keepalive) { int b = 1; 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 */ else localport = 0; /* just use port 0 (ie kernel picks) */ /* BSD IP stacks need sockaddr_in zeroed before filling in */ memset(&u,'\0',sizeof(u)); /* We don't try to bind to a local address for UNIX domain sockets. (Why * do we bother doing the bind when localport == 0 anyway?) */ if (family != AF_UNIX) { /* Loop round trying to bind */ while (1) { int retcode; #ifndef NO_IPV6 if (family == AF_INET6) { /* XXX use getaddrinfo to get a local address? */ u.sin6.sin6_family = AF_INET6; u.sin6.sin6_addr = in6addr_any; u.sin6.sin6_port = htons(localport); retcode = bind(s, &u.sa, sizeof(u.sin6)); } else #endif { assert(family == AF_INET); u.sin.sin_family = AF_INET; u.sin.sin_addr.s_addr = htonl(INADDR_ANY); u.sin.sin_port = htons(localport); retcode = bind(s, &u.sa, sizeof(u.sin)); } if (retcode >= 0) { err = 0; break; /* done */ } else { err = errno; if (err != EADDRINUSE) /* failed, for a bad reason */ break; } if (localport == 0) break; /* we're only looping once */ localport--; if (localport == 0) break; /* we might have got to the end */ } if (err) goto ret; } /* * Connect to remote address. */ switch(family) { #ifndef NO_IPV6 case AF_INET: /* XXX would be better to have got getaddrinfo() to fill in the port. */ ((struct sockaddr_in *)sock->step.ai->ai_addr)->sin_port = htons(sock->port); sa = (const union sockaddr_union *)sock->step.ai->ai_addr; salen = sock->step.ai->ai_addrlen; break; case AF_INET6: ((struct sockaddr_in *)sock->step.ai->ai_addr)->sin_port = htons(sock->port); sa = (const union sockaddr_union *)sock->step.ai->ai_addr; salen = sock->step.ai->ai_addrlen; break; #else case AF_INET: u.sin.sin_family = AF_INET; u.sin.sin_addr.s_addr = htonl(sock->addr->addresses[sock->step.curraddr]); u.sin.sin_port = htons((short) sock->port); sa = &u; salen = sizeof u.sin; break; #endif case AF_UNIX: assert(strlen(sock->addr->hostname) < sizeof u.su.sun_path); u.su.sun_family = AF_UNIX; strcpy(u.su.sun_path, sock->addr->hostname); sa = &u; salen = sizeof u.su; break; default: unreachable("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 { /* * If we _don't_ get EWOULDBLOCK, the connect has completed * and we should set the socket as connected and writable. */ sock->connected = true; sock->writable = true; SockAddr thisaddr = sk_extractaddr_tmp(sock->addr, &sock->step); plug_log(sock->plug, PLUGLOG_CONNECT_SUCCESS, &thisaddr, sock->port, NULL, 0); } uxsel_tell(sock); ret: /* * No matter what happened, put the socket back in the tree. */ add234(sktree, sock); if (err) { SockAddr thisaddr = sk_extractaddr_tmp( sock->addr, &sock->step); plug_log(sock->plug, PLUGLOG_CONNECT_FAILED, &thisaddr, sock->port, strerror(err), err); } return err; } Socket *sk_new(SockAddr *addr, int port, bool privport, bool oobinline, bool nodelay, bool keepalive, Plug *plug) { NetSocket *ret; int err; /* * Create NetSocket structure. */ ret = snew(NetSocket); ret->sock.vt = &NetSocket_sockvt; ret->error = NULL; ret->plug = plug; bufchain_init(&ret->output_data); ret->connected = false; /* to start with */ ret->writable = false; /* to start with */ ret->sending_oob = 0; ret->frozen = false; ret->localhost_only = false; /* 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 = false; ret->addr = addr; START_STEP(ret->addr, ret->step); ret->s = -1; ret->oobinline = oobinline; ret->nodelay = nodelay; ret->keepalive = keepalive; ret->privport = privport; ret->port = port; do { err = try_connect(ret); } while (err && sk_nextaddr(ret->addr, &ret->step)); if (err) ret->error = strerror(err); return &ret->sock; } Socket *sk_newlistener(const char *srcaddr, int port, Plug *plug, bool local_host_only, int orig_address_family) { int s; #ifndef NO_IPV6 struct addrinfo hints, *ai = NULL; char portstr[6]; #endif union sockaddr_union u; union sockaddr_union *addr; int addrlen; NetSocket *ret; int retcode; int address_family; int on = 1; /* * Create NetSocket structure. */ ret = snew(NetSocket); ret->sock.vt = &NetSocket_sockvt; ret->error = NULL; ret->plug = plug; bufchain_init(&ret->output_data); ret->writable = false; /* to start with */ ret->sending_oob = 0; ret->frozen = false; 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 = true; 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 orig_address_family == ADDRTYPE_IPV6 ? AF_INET6 : #endif AF_UNSPEC); #ifndef NO_IPV6 /* Let's default to IPv6. * If the stack doesn't support IPv6, we will fall back to IPv4. */ if (address_family == AF_UNSPEC) address_family = AF_INET6; #else /* No other choice, default to IPv4 */ if (address_family == AF_UNSPEC) address_family = AF_INET; #endif /* * Open socket. */ s = socket(address_family, SOCK_STREAM, 0); #ifndef NO_IPV6 /* If the host doesn't support IPv6 try fallback to IPv4. */ if (s < 0 && address_family == AF_INET6) { address_family = AF_INET; s = socket(address_family, SOCK_STREAM, 0); } #endif if (s < 0) { ret->error = strerror(errno); return &ret->sock; } cloexec(s); ret->oobinline = false; if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *)&on, sizeof(on)) < 0) { ret->error = strerror(errno); close(s); return &ret->sock; } retcode = -1; addr = NULL; addrlen = -1; /* placate optimiser */ if (srcaddr != NULL) { #ifndef NO_IPV6 hints.ai_flags = AI_NUMERICHOST; hints.ai_family = address_family; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = 0; hints.ai_addrlen = 0; hints.ai_addr = NULL; hints.ai_canonname = NULL; hints.ai_next = NULL; assert(port >= 0 && port <= 99999); sprintf(portstr, "%d", port); { char *trimmed_addr = host_strduptrim(srcaddr); retcode = getaddrinfo(trimmed_addr, portstr, &hints, &ai); sfree(trimmed_addr); } if (retcode == 0) { addr = (union sockaddr_union *)ai->ai_addr; addrlen = ai->ai_addrlen; } #else memset(&u,'\0',sizeof u); u.sin.sin_family = AF_INET; u.sin.sin_port = htons(port); u.sin.sin_addr.s_addr = inet_addr(srcaddr); if (u.sin.sin_addr.s_addr != (in_addr_t)(-1)) { /* Override localhost_only with specified listen addr. */ ret->localhost_only = ipv4_is_loopback(u.sin.sin_addr); } addr = &u; addrlen = sizeof(u.sin); retcode = 0; #endif } if (retcode != 0) { memset(&u,'\0',sizeof u); #ifndef NO_IPV6 if (address_family == AF_INET6) { u.sin6.sin6_family = AF_INET6; u.sin6.sin6_port = htons(port); if (local_host_only) u.sin6.sin6_addr = in6addr_loopback; else u.sin6.sin6_addr = in6addr_any; addr = &u; addrlen = sizeof(u.sin6); } else #endif { u.sin.sin_family = AF_INET; u.sin.sin_port = htons(port); if (local_host_only) u.sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); else u.sin.sin_addr.s_addr = htonl(INADDR_ANY); addr = &u; addrlen = sizeof(u.sin); } } retcode = bind(s, &addr->sa, addrlen); #ifndef NO_IPV6 if (ai) freeaddrinfo(ai); #endif if (retcode < 0) { close(s); ret->error = strerror(errno); return &ret->sock; } if (listen(s, SOMAXCONN) < 0) { close(s); ret->error = strerror(errno); return &ret->sock; } #ifndef NO_IPV6 /* * If we were given ADDRTYPE_UNSPEC, we must also create an * IPv4 listening socket and link it to this one. */ if (address_family == AF_INET6 && orig_address_family == ADDRTYPE_UNSPEC) { NetSocket *other; other = container_of( sk_newlistener(srcaddr, port, plug, local_host_only, ADDRTYPE_IPV4), NetSocket, sock); if (other) { if (!other->error) { other->parent = ret; ret->child = other; } else { /* If we couldn't create a listening socket on IPv4 as well * as IPv6, we must return an error overall. */ close(s); sfree(ret); return &other->sock; } } } #endif ret->s = s; uxsel_tell(ret); add234(sktree, ret); return &ret->sock; } static void sk_net_close(Socket *sock) { NetSocket *s = container_of(sock, NetSocket, sock); if (s->child) sk_net_close(&s->child->sock); bufchain_clear(&s->output_data); del234(sktree, s); if (s->s >= 0) { uxsel_del(s->s); close(s->s); } if (s->addr) sk_addr_free(s->addr); delete_callbacks_for_context(s); sfree(s); } void *sk_getxdmdata(Socket *sock, int *lenp) { NetSocket *s; union sockaddr_union u; socklen_t addrlen; char *buf; static unsigned int unix_addr = 0xFFFFFFFF; /* * We must check that this socket really _is_ a NetSocket before * downcasting it. */ if (sock->vt != &NetSocket_sockvt) return NULL; /* failure */ s = container_of(sock, NetSocket, sock); addrlen = sizeof(u); if (getsockname(s->s, &u.sa, &addrlen) < 0) return NULL; switch(u.sa.sa_family) { case AF_INET: *lenp = 6; buf = snewn(*lenp, char); PUT_32BIT_MSB_FIRST(buf, ntohl(u.sin.sin_addr.s_addr)); PUT_16BIT_MSB_FIRST(buf+4, ntohs(u.sin.sin_port)); break; #ifndef NO_IPV6 case AF_INET6: *lenp = 6; buf = snewn(*lenp, char); if (IN6_IS_ADDR_V4MAPPED(&u.sin6.sin6_addr)) { memcpy(buf, u.sin6.sin6_addr.s6_addr + 12, 4); PUT_16BIT_MSB_FIRST(buf+4, ntohs(u.sin6.sin6_port)); } else /* This is stupid, but it's what XLib does. */ memset(buf, 0, 6); break; #endif case AF_UNIX: *lenp = 6; buf = snewn(*lenp, char); PUT_32BIT_MSB_FIRST(buf, unix_addr--); PUT_16BIT_MSB_FIRST(buf+4, getpid()); break; /* XXX IPV6 */ default: return NULL; } return buf; } /* * Deal with socket errors detected in try_send(). */ static void socket_error_callback(void *vs) { NetSocket *s = (NetSocket *)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(NetSocket *s) { while (s->sending_oob || bufchain_size(&s->output_data) > 0) { int nsent; int err; const void *data; size_t len; int urgentflag; if (s->sending_oob) { urgentflag = MSG_OOB; len = s->sending_oob; data = &s->oobdata; } else { urgentflag = 0; ptrlen bufdata = bufchain_prefix(&s->output_data); data = bufdata.ptr; len = bufdata.len; } nsent = send(s->s, data, len, urgentflag); noise_ultralight(NOISE_SOURCE_IOLEN, nsent); if (nsent <= 0) { err = (nsent < 0 ? errno : 0); if (err == EWOULDBLOCK) { /* * Perfectly normal: we've sent all we can for the moment. */ s->writable = false; return; } else { /* * We unfortunately can't just call plug_closing(), * because it's quite likely that we're currently * _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 size_t sk_net_write(Socket *sock, const void *buf, size_t len) { NetSocket *s = container_of(sock, NetSocket, 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); /* * Update the select() status to correctly reflect whether or * not we should be selecting for write. */ uxsel_tell(s); return bufchain_size(&s->output_data); } static size_t sk_net_write_oob(Socket *sock, const void *buf, size_t len) { NetSocket *s = container_of(sock, NetSocket, 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); /* * 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_net_write_eof(Socket *sock) { NetSocket *s = container_of(sock, NetSocket, 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 void net_select_result(int fd, int event) { int ret; char buf[20480]; /* nice big buffer for plenty of speed */ NetSocket *s; bool atmark = true; /* Find the Socket structure */ s = find234(sktree, &fd, cmpforsearch); if (!s) return; /* boggle */ noise_ultralight(NOISE_SOURCE_IOID, fd); switch (event) { case SELECT_X: /* exceptional */ if (!s->oobinline) { /* * On a non-oobinline socket, this indicates that we * can immediately perform an OOB read and get back OOB * data, which we will send to the back end with * type==2 (urgent data). */ ret = recv(s->s, buf, sizeof(buf), MSG_OOB); noise_ultralight(NOISE_SOURCE_IOLEN, ret); if (ret <= 0) { plug_closing(s->plug, ret == 0 ? "Internal networking trouble" : strerror(errno), errno, 0); } else { /* * Receiving actual data on a socket means we can * stop falling back through the candidate * addresses to connect to. */ if (s->addr) { sk_addr_free(s->addr); s->addr = NULL; } plug_receive(s->plug, 2, buf, ret); } break; } /* * If we reach here, this is an oobinline socket, which * means we should set s->oobpending and then deal with it * when we get called for the readability event (which * should also occur). */ s->oobpending = true; break; case SELECT_R: /* readable; also acceptance */ 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_net_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). */ if (s->oobinline && s->oobpending) { int atmark_from_ioctl; if (ioctl(s->s, SIOCATMARK, &atmark_from_ioctl) == 0) { atmark = atmark_from_ioctl; if (atmark) s->oobpending = false; /* clear this indicator */ } } else atmark = true; ret = recv(s->s, buf, s->oobpending ? 1 : sizeof(buf), 0); noise_ultralight(NOISE_SOURCE_IOLEN, ret); if (ret < 0) { if (errno == EWOULDBLOCK) { break; } } if (ret < 0) { plug_closing(s->plug, strerror(errno), errno, 0); } else if (0 == ret) { s->incomingeof = true; /* stop trying to read now */ uxsel_tell(s); 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. */ if (s->addr) { sk_addr_free(s->addr); s->addr = NULL; } plug_receive(s->plug, atmark ? 0 : 1, buf, ret); } break; case SELECT_W: /* writable */ if (!s->connected) { /* * select/poll reports a socket as _writable_ when an * asynchronous connect() attempt either completes or * fails. So first we must find out which. */ { int err; socklen_t errlen = sizeof(err); char *errmsg = NULL; if (getsockopt(s->s, SOL_SOCKET, SO_ERROR, &err, &errlen)<0) { errmsg = dupprintf("getsockopt(SO_ERROR): %s", strerror(errno)); err = errno; /* got to put something in here */ } else if (err != 0) { errmsg = dupstr(strerror(err)); } if (errmsg) { /* * The asynchronous connection attempt failed. * Report the problem via plug_log, and try again * with the next candidate address, if we have * more than one. */ SockAddr thisaddr; assert(s->addr); thisaddr = sk_extractaddr_tmp(s->addr, &s->step); plug_log(s->plug, PLUGLOG_CONNECT_FAILED, &thisaddr, s->port, errmsg, err); while (err && s->addr && sk_nextaddr(s->addr, &s->step)) { err = try_connect(s); } if (err) { plug_closing(s->plug, strerror(err), err, 0); return; /* socket is now presumably defunct */ } if (!s->connected) return; /* another async attempt in progress */ } else { /* * The connection attempt succeeded. */ SockAddr thisaddr = sk_extractaddr_tmp(s->addr, &s->step); plug_log(s->plug, PLUGLOG_CONNECT_SUCCESS, &thisaddr, s->port, NULL, 0); } } /* * If we get here, we've managed to make a connection. */ if (s->addr) { sk_addr_free(s->addr); s->addr = NULL; } s->connected = true; s->writable = true; uxsel_tell(s); } else { size_t bufsize_before, bufsize_after; s->writable = true; bufsize_before = s->sending_oob + bufchain_size(&s->output_data); try_send(s); bufsize_after = s->sending_oob + bufchain_size(&s->output_data); if (bufsize_after < bufsize_before) plug_sent(s->plug, bufsize_after); } break; } } /* * 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) { return addr->error; } static const char *sk_net_socket_error(Socket *sock) { NetSocket *s = container_of(sock, NetSocket, sock); return s->error; } static void sk_net_set_frozen(Socket *sock, bool is_frozen) { NetSocket *s = container_of(sock, NetSocket, sock); if (s->frozen == is_frozen) return; s->frozen = is_frozen; uxsel_tell(s); } static SocketPeerInfo *sk_net_peer_info(Socket *sock) { NetSocket *s = container_of(sock, NetSocket, sock); union sockaddr_union addr; socklen_t addrlen = sizeof(addr); #ifndef NO_IPV6 char buf[INET6_ADDRSTRLEN]; #endif SocketPeerInfo *pi; if (getpeername(s->s, &addr.sa, &addrlen) < 0) return NULL; pi = snew(SocketPeerInfo); pi->addressfamily = ADDRTYPE_UNSPEC; pi->addr_text = NULL; pi->port = -1; pi->log_text = NULL; if (addr.storage.ss_family == AF_INET) { pi->addressfamily = ADDRTYPE_IPV4; memcpy(pi->addr_bin.ipv4, &addr.sin.sin_addr, 4); pi->port = ntohs(addr.sin.sin_port); pi->addr_text = dupstr(inet_ntoa(addr.sin.sin_addr)); pi->log_text = dupprintf("%s:%d", pi->addr_text, pi->port); #ifndef NO_IPV6 } else if (addr.storage.ss_family == AF_INET6) { pi->addressfamily = ADDRTYPE_IPV6; memcpy(pi->addr_bin.ipv6, &addr.sin6.sin6_addr, 16); pi->port = ntohs(addr.sin6.sin6_port); pi->addr_text = dupstr( inet_ntop(AF_INET6, &addr.sin6.sin6_addr, buf, sizeof(buf))); pi->log_text = dupprintf("[%s]:%d", pi->addr_text, pi->port); #endif } else if (addr.storage.ss_family == AF_UNIX) { pi->addressfamily = ADDRTYPE_LOCAL; /* * For Unix sockets, the source address is unlikely to be * helpful, so we leave addr_txt NULL (and we certainly can't * fill in port, obviously). Instead, we try SO_PEERCRED and * try to get the source pid, and put that in the log text. */ int pid, uid, gid; if (so_peercred(s->s, &pid, &uid, &gid)) { char uidbuf[64], gidbuf[64]; sprintf(uidbuf, "%d", uid); sprintf(gidbuf, "%d", gid); struct passwd *pw = getpwuid(uid); struct group *gr = getgrgid(gid); pi->log_text = dupprintf("pid %d (%s:%s)", pid, pw ? pw->pw_name : uidbuf, gr ? gr->gr_name : gidbuf); } } else { sfree(pi); return NULL; } return pi; } int sk_net_get_fd(Socket *sock) { /* This function is not fully general: it only works on NetSocket */ if (sock->vt != &NetSocket_sockvt) return -1; /* failure */ NetSocket *s = container_of(sock, NetSocket, sock); return s->s; } static void uxsel_tell(NetSocket *s) { int rwx = 0; if (!s->pending_error) { if (s->listener) { rwx |= SELECT_R; /* read == accept */ } else { if (!s->connected) rwx |= SELECT_W; /* write == connect */ if (s->connected && !s->frozen && !s->incomingeof) rwx |= SELECT_R | SELECT_X; if (bufchain_size(&s->output_data)) rwx |= SELECT_W; } } uxsel_set(s->s, rwx, net_select_result); } int net_service_lookup(char *service) { struct servent *se; se = getservbyname(service, NULL); if (se != NULL) return ntohs(se->s_port); else return 0; } char *get_hostname(void) { size_t size = 0; char *hostname = NULL; do { sgrowarray(hostname, size, size); if ((gethostname(hostname, size) < 0) && (errno != ENAMETOOLONG)) { sfree(hostname); hostname = NULL; break; } } while (strlen(hostname) >= size-1); return hostname; } SockAddr *platform_get_x11_unix_address(const char *sockpath, int displaynum) { SockAddr *ret = snew(SockAddr); int n; memset(ret, 0, sizeof *ret); ret->superfamily = UNIX; /* * In special circumstances (notably Mac OS X Leopard), we'll * have been passed an explicit Unix socket path. */ if (sockpath) { n = snprintf(ret->hostname, sizeof ret->hostname, "%s", sockpath); } else { n = snprintf(ret->hostname, sizeof ret->hostname, "%s%d", X11_UNIX_PATH, displaynum); } if (n < 0) ret->error = "snprintf failed"; else if (n >= sizeof ret->hostname) ret->error = "X11 UNIX name too long"; #ifndef NO_IPV6 ret->ais = NULL; #else ret->addresses = NULL; ret->naddresses = 0; #endif ret->refcount = 1; return ret; } SockAddr *unix_sock_addr(const char *path) { SockAddr *ret = snew(SockAddr); 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 || n >= sizeof(((struct sockaddr_un *)0)->sun_path)) 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; NetSocket *ret; int retcode; /* * Create NetSocket structure. */ ret = snew(NetSocket); ret->sock.vt = &NetSocket_sockvt; ret->error = NULL; ret->plug = plug; bufchain_init(&ret->output_data); ret->writable = false; /* to start with */ ret->sending_oob = 0; ret->frozen = false; 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 = true; 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 &ret->sock; } cloexec(s); ret->oobinline = false; memset(&u, '\0', sizeof(u)); u.su.sun_family = AF_UNIX; #if __GNUC__ >= 8 # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wstringop-truncation" #endif // __GNUC__ >= 8 strncpy(u.su.sun_path, listenaddr->hostname, sizeof(u.su.sun_path)-1); #if __GNUC__ >= 8 # pragma GCC diagnostic pop #endif // __GNUC__ >= 8 addr = &u; addrlen = sizeof(u.su); if (unlink(u.su.sun_path) < 0 && errno != ENOENT) { close(s); ret->error = strerror(errno); return &ret->sock; } retcode = bind(s, &addr->sa, addrlen); if (retcode < 0) { close(s); ret->error = strerror(errno); return &ret->sock; } if (listen(s, SOMAXCONN) < 0) { close(s); ret->error = strerror(errno); return &ret->sock; } ret->s = s; uxsel_tell(ret); add234(sktree, ret); return &ret->sock; } putty-0.76/unix/uxnogtk.c0000644000175000017500000000027114072266313012406 00000000000000/* * uxnogtk.c: link into non-GUI Unix programs so that they can tell * buildinfo about a lack of GTK. */ #include "putty.h" char *buildinfo_gtk_version(void) { return NULL; } putty-0.76/unix/uxnoise.c0000644000175000017500000000612514072266313012405 00000000000000/* * Noise generation for PuTTY's cryptographic random number * generator. */ #include #include #include #include #include #include #include #include "putty.h" #include "ssh.h" #include "storage.h" static bool read_dev_urandom(char *buf, int len) { int fd; int ngot, ret; fd = open("/dev/urandom", O_RDONLY); if (fd < 0) return false; ngot = 0; while (ngot < len) { ret = read(fd, buf+ngot, len-ngot); if (ret < 0) { close(fd); return false; } ngot += ret; } close(fd); return true; } /* * This function is called once, at PuTTY startup. It will do some * slightly silly things such as fetching an entire process listing * and scanning /tmp, load the saved random seed from disk, and * also read 32 bytes out of /dev/urandom. */ void noise_get_heavy(void (*func) (void *, int)) { char buf[512]; FILE *fp; int ret; bool got_dev_urandom = false; if (read_dev_urandom(buf, 32)) { got_dev_urandom = true; func(buf, 32); } fp = popen("ps -axu 2>/dev/null", "r"); if (fp) { while ( (ret = fread(buf, 1, sizeof(buf), fp)) > 0) func(buf, ret); pclose(fp); } else if (!got_dev_urandom) { fprintf(stderr, "popen: %s\n" "Unable to access fallback entropy source\n", strerror(errno)); exit(1); } fp = popen("ls -al /tmp 2>/dev/null", "r"); if (fp) { while ( (ret = fread(buf, 1, sizeof(buf), fp)) > 0) func(buf, ret); pclose(fp); } else if (!got_dev_urandom) { fprintf(stderr, "popen: %s\n" "Unable to access fallback entropy source\n", strerror(errno)); exit(1); } read_random_seed(func); } /* * This function is called on a timer, and grabs as much changeable * system data as it can quickly get its hands on. */ void noise_regular(void) { int fd; int ret; char buf[512]; struct rusage rusage; if ((fd = open("/proc/meminfo", O_RDONLY)) >= 0) { while ( (ret = read(fd, buf, sizeof(buf))) > 0) random_add_noise(NOISE_SOURCE_MEMINFO, buf, ret); close(fd); } if ((fd = open("/proc/stat", O_RDONLY)) >= 0) { while ( (ret = read(fd, buf, sizeof(buf))) > 0) random_add_noise(NOISE_SOURCE_STAT, buf, ret); close(fd); } getrusage(RUSAGE_SELF, &rusage); random_add_noise(NOISE_SOURCE_RUSAGE, &rusage, sizeof(rusage)); } /* * This function is called on every keypress or mouse move, and * will add the current time to the noise pool. It gets the scan * code or mouse position passed in, and adds that too. */ void noise_ultralight(NoiseSourceId id, unsigned long data) { struct timeval tv; gettimeofday(&tv, NULL); random_add_noise(NOISE_SOURCE_TIME, &tv, sizeof(tv)); random_add_noise(id, &data, sizeof(data)); } uint64_t prng_reseed_time_ms(void) { struct timeval tv; gettimeofday(&tv, NULL); return tv.tv_sec * 1000 + tv.tv_usec / 1000; } putty-0.76/unix/uxpeer.c0000644000175000017500000000122114072266313012213 00000000000000/* * Unix: wrapper for getsockopt(SO_PEERCRED), conditionalised on * appropriate autoconfery. */ #ifdef HAVE_CONFIG_H # include "uxconfig.h" /* leading space prevents mkfiles.pl trying to follow */ #endif #ifdef HAVE_SO_PEERCRED #define _GNU_SOURCE #include #endif #include #include "putty.h" bool so_peercred(int fd, int *pid, int *uid, int *gid) { #ifdef HAVE_SO_PEERCRED struct ucred cr; socklen_t crlen = sizeof(cr); if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cr, &crlen) == 0) { *pid = cr.pid; *uid = cr.uid; *gid = cr.gid; return true; } #endif return false; } putty-0.76/unix/uxpgnt.c0000644000175000017500000014212314072266313012237 00000000000000/* * Unix Pageant, more or less similar to ssh-agent. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "putty.h" #include "ssh.h" #include "misc.h" #include "pageant.h" void cmdline_error(const char *fmt, ...) { va_list ap; va_start(ap, fmt); console_print_error_msg_fmt_v("pageant", fmt, ap); va_end(ap); exit(1); } static void setup_sigchld_handler(void); typedef enum RuntimePromptType { RTPROMPT_UNAVAILABLE, RTPROMPT_DEBUG, RTPROMPT_GUI, } RuntimePromptType; static const char *progname; struct uxpgnt_client { FILE *logfp; strbuf *prompt_buf; RuntimePromptType prompt_type; bool prompt_active; PageantClientDialogId *dlgid; int passphrase_fd; int termination_pid; PageantListenerClient plc; }; static void uxpgnt_log(PageantListenerClient *plc, const char *fmt, va_list ap) { struct uxpgnt_client *upc = container_of(plc, struct uxpgnt_client, plc); if (!upc->logfp) return; fprintf(upc->logfp, "pageant: "); vfprintf(upc->logfp, fmt, ap); fprintf(upc->logfp, "\n"); } static int make_pipe_to_askpass(const char *msg) { int pipefds[2]; setup_sigchld_handler(); if (pipe(pipefds) < 0) return -1; pid_t pid = fork(); if (pid < 0) { close(pipefds[0]); close(pipefds[1]); return -1; } if (pid == 0) { const char *args[5] = { progname, "--gui-prompt", "--askpass", msg, NULL }; dup2(pipefds[1], 1); cloexec(pipefds[0]); cloexec(pipefds[1]); /* * See comment in fork_and_exec_self() in gtkmain.c. */ execv("/proc/self/exe", (char **)args); execvp(progname, (char **)args); perror("exec"); _exit(127); } close(pipefds[1]); return pipefds[0]; } static bool uxpgnt_ask_passphrase( PageantListenerClient *plc, PageantClientDialogId *dlgid, const char *comment) { struct uxpgnt_client *upc = container_of(plc, struct uxpgnt_client, plc); assert(!upc->dlgid); /* Pageant core should be serialising requests */ char *msg = dupprintf( "A client of Pageant wants to use the following encrypted key:\n" "%s\n" "If you intended this, enter the passphrase to decrypt the key.", comment); switch (upc->prompt_type) { case RTPROMPT_UNAVAILABLE: sfree(msg); return false; case RTPROMPT_GUI: upc->passphrase_fd = make_pipe_to_askpass(msg); sfree(msg); if (upc->passphrase_fd < 0) return false; /* something went wrong */ break; case RTPROMPT_DEBUG: fprintf(upc->logfp, "pageant passphrase request: %s\n", msg); sfree(msg); break; } upc->prompt_active = true; upc->dlgid = dlgid; return true; } static void passphrase_done(struct uxpgnt_client *upc, bool success) { PageantClientDialogId *dlgid = upc->dlgid; upc->dlgid = NULL; upc->prompt_active = false; if (upc->logfp) fprintf(upc->logfp, "pageant passphrase response: %s\n", success ? "success" : "failure"); if (success) pageant_passphrase_request_success( dlgid, ptrlen_from_strbuf(upc->prompt_buf)); else pageant_passphrase_request_refused(dlgid); strbuf_free(upc->prompt_buf); upc->prompt_buf = strbuf_new_nm(); } static const PageantListenerClientVtable uxpgnt_vtable = { .log = uxpgnt_log, .ask_passphrase = uxpgnt_ask_passphrase, }; /* * More stubs. */ void random_save_seed(void) {} void random_destroy_seed(void) {} char *platform_default_s(const char *name) { return NULL; } bool platform_default_b(const char *name, bool def) { return def; } 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) { return filename_from_str(""); } char *x_get_default(const char *key) { return NULL; } /* * Short description of parameters. */ static void usage(void) { printf("Pageant: SSH agent\n"); printf("%s\n", ver); printf("Usage: pageant [[--encrypted] key files]\n"); printf(" pageant [[--encrypted] key files] --exec [args]\n"); printf(" pageant -a [--encrypted] [key files]\n"); printf(" pageant -d [key identifiers]\n"); printf(" pageant -D\n"); printf(" pageant -r [key identifiers]\n"); printf(" pageant -R\n"); printf(" pageant --public [key identifiers]\n"); printf(" pageant ( --public-openssh | -L ) [key identifiers]\n"); printf(" pageant -l [-E fptype]\n"); printf("Lifetime options, for running Pageant as an agent:\n"); printf(" -X run with the lifetime of the X server\n"); printf(" -T run with the lifetime of the controlling tty\n"); printf(" --permanent run permanently\n"); printf(" --debug run in debugging mode, without forking\n"); printf(" --exec run with the lifetime of that command\n"); printf("Client options, for talking to an existing agent:\n"); printf(" -a add key(s) to the existing agent\n"); printf(" -l list currently loaded key fingerprints and comments\n"); printf(" --public print public keys in RFC 4716 format\n"); printf(" --public-openssh, -L print public keys in OpenSSH format\n"); printf(" -d delete key(s) from the agent\n"); printf(" -D delete all keys from the agent\n"); printf(" -r re-encrypt keys in the agent (forget cleartext\n"); printf(" -R re-encrypt all possible keys in the agent\n"); printf("Other options:\n"); printf(" -v verbose mode (in agent mode)\n"); printf(" -s -c force POSIX or C shell syntax (in agent mode)\n"); printf(" --symlink path create symlink to socket (in agent mode)\n"); printf(" --encrypted when adding keys, don't decrypt\n"); printf(" -E alg, --fptype alg fingerprint type for -l (sha256, md5)\n"); printf(" --tty-prompt force tty-based passphrase prompt\n"); printf(" --gui-prompt force GUI-based passphrase prompt\n"); printf(" --askpass behave like a standalone askpass program\n"); exit(1); } static void version(void) { char *buildinfo_text = buildinfo("\n"); printf("pageant: %s\n%s\n", ver, buildinfo_text); sfree(buildinfo_text); exit(0); } void keylist_update(void) { /* Nothing needs doing in Unix Pageant */ } #define PAGEANT_DIR_PREFIX "/tmp/pageant" const char *const appname = "Pageant"; static bool time_to_die = false; /* Stub functions to permit linking against x11fwd.c. These never get * used, because in LIFE_X11 mode we connect to the X server using a * straightforward Socket and don't try to create an ersatz SSH * forwarding too. */ void chan_remotely_opened_confirmation(Channel *chan) { } void chan_remotely_opened_failure(Channel *chan, const char *err) { } bool chan_default_want_close(Channel *chan, bool s, bool r) { return false; } bool chan_no_exit_status(Channel *ch, int s) { return false; } bool chan_no_exit_signal(Channel *ch, ptrlen s, bool c, ptrlen m) { return false; } bool chan_no_exit_signal_numeric(Channel *ch, int s, bool c, ptrlen m) { return false; } bool chan_no_run_shell(Channel *chan) { return false; } bool chan_no_run_command(Channel *chan, ptrlen command) { return false; } bool chan_no_run_subsystem(Channel *chan, ptrlen subsys) { return false; } bool chan_no_enable_x11_forwarding( Channel *chan, bool oneshot, ptrlen authproto, ptrlen authdata, unsigned screen_number) { return false; } bool chan_no_enable_agent_forwarding(Channel *chan) { return false; } bool chan_no_allocate_pty( Channel *chan, ptrlen termtype, unsigned width, unsigned height, unsigned pixwidth, unsigned pixheight, struct ssh_ttymodes modes) { return false; } bool chan_no_set_env(Channel *chan, ptrlen var, ptrlen value) { return false; } bool chan_no_send_break(Channel *chan, unsigned length) { return false; } bool chan_no_send_signal(Channel *chan, ptrlen signame) { return false; } bool chan_no_change_window_size( Channel *chan, unsigned width, unsigned height, unsigned pixwidth, unsigned pixheight) { return false; } void chan_no_request_response(Channel *chan, bool success) {} /* * These functions are part of the plug for our connection to the X * display, so they do get called. They needn't actually do anything, * except that x11_closing has to signal back to the main loop that * it's time to terminate. */ static void x11_log(Plug *p, PlugLogType type, SockAddr *addr, int port, const char *error_msg, int error_code) {} static void x11_receive(Plug *plug, int urgent, const char *data, size_t len) {} static void x11_sent(Plug *plug, size_t bufsize) {} static void x11_closing(Plug *plug, const char *error_msg, int error_code, bool calling_back) { time_to_die = true; } struct X11Connection { Plug plug; }; static char *socketname; static enum { SHELL_AUTO, SHELL_SH, SHELL_CSH } shell_type = SHELL_AUTO; void pageant_print_env(int pid) { if (shell_type == SHELL_AUTO) { /* Same policy as OpenSSH: if $SHELL ends in "csh" then assume * it's csh-shaped. */ const char *shell = getenv("SHELL"); if (shell && strlen(shell) >= 3 && !strcmp(shell + strlen(shell) - 3, "csh")) shell_type = SHELL_CSH; else shell_type = SHELL_SH; } /* * These shell snippets could usefully pay some attention to * escaping of interesting characters. I don't think it causes a * problem at the moment, because the pathnames we use are so * utterly boring, but it's a lurking bug waiting to happen once * a bit more flexibility turns up. */ switch (shell_type) { case SHELL_SH: printf("SSH_AUTH_SOCK=%s; export SSH_AUTH_SOCK;\n" "SSH_AGENT_PID=%d; export SSH_AGENT_PID;\n", socketname, pid); break; case SHELL_CSH: printf("setenv SSH_AUTH_SOCK %s;\n" "setenv SSH_AGENT_PID %d;\n", socketname, pid); break; case SHELL_AUTO: unreachable("SHELL_AUTO should have been eliminated by now"); break; } } void pageant_fork_and_print_env(bool retain_tty) { pid_t pid = fork(); if (pid == -1) { perror("fork"); exit(1); } else if (pid != 0) { pageant_print_env(pid); exit(0); } /* * Having forked off, we now daemonise ourselves as best we can. * It's good practice in general to setsid() ourself out of any * process group we didn't want to be part of, and to chdir("/") * to avoid holding any directories open that we don't need in * case someone wants to umount them; also, we should definitely * close standard output (because it will very likely be pointing * at a pipe from which some parent process is trying to read our * environment variable dump, so if we hold open another copy of * it then that process will never finish reading). We close * standard input too on general principles, but not standard * error, since we might need to shout a panicky error message * down that one. */ if (chdir("/") < 0) { /* should there be an error condition, nothing we can do about * it anyway */ } close(0); close(1); if (retain_tty) { /* Get out of our previous process group, to avoid being * blasted by passing signals. But keep our controlling tty, * so we can keep checking to see if we still have one. */ setpgrp(); } else { /* Do that, but also leave our entire session and detach from * the controlling tty (if any). */ setsid(); } } static int signalpipe[2] = { -1, -1 }; static void sigchld(int signum) { if (write(signalpipe[1], "x", 1) <= 0) /* not much we can do about it */; } static void setup_sigchld_handler(void) { if (signalpipe[0] >= 0) return; /* * Set up the pipe we'll use to tell us about SIGCHLD. */ if (pipe(signalpipe) < 0) { perror("pipe"); exit(1); } putty_signal(SIGCHLD, sigchld); } #define TTY_LIFE_POLL_INTERVAL (TICKSPERSEC * 30) static void *dummy_timer_ctx; static void tty_life_timer(void *ctx, unsigned long now) { schedule_timer(TTY_LIFE_POLL_INTERVAL, tty_life_timer, &dummy_timer_ctx); } typedef enum { KEYACT_AGENT_LOAD, KEYACT_AGENT_LOAD_ENCRYPTED, KEYACT_CLIENT_BASE, KEYACT_CLIENT_ADD = KEYACT_CLIENT_BASE, KEYACT_CLIENT_ADD_ENCRYPTED, KEYACT_CLIENT_DEL, KEYACT_CLIENT_DEL_ALL, KEYACT_CLIENT_LIST, KEYACT_CLIENT_PUBLIC_OPENSSH, KEYACT_CLIENT_PUBLIC, KEYACT_CLIENT_SIGN, KEYACT_CLIENT_REENCRYPT, KEYACT_CLIENT_REENCRYPT_ALL, } keyact; struct cmdline_key_action { struct cmdline_key_action *next; keyact action; const char *filename; }; bool is_agent_action(keyact action) { return action < KEYACT_CLIENT_BASE; } static struct cmdline_key_action *keyact_head = NULL, *keyact_tail = NULL; static uint32_t sign_flags = 0; void add_keyact(keyact action, const char *filename) { struct cmdline_key_action *a = snew(struct cmdline_key_action); a->action = action; a->filename = filename; a->next = NULL; if (keyact_tail) keyact_tail->next = a; else keyact_head = a; keyact_tail = a; } bool have_controlling_tty(void) { int fd = open("/dev/tty", O_RDONLY); if (fd < 0) { if (errno != ENXIO) { perror("/dev/tty: open"); exit(1); } return false; } else { close(fd); return true; } } static char **exec_args = NULL; static enum { LIFE_UNSPEC, LIFE_X11, LIFE_TTY, LIFE_DEBUG, LIFE_PERM, LIFE_EXEC } life = LIFE_UNSPEC; static const char *display = NULL; static enum { PROMPT_UNSPEC, PROMPT_TTY, PROMPT_GUI } prompt_type = PROMPT_UNSPEC; static FingerprintType key_list_fptype = SSH_FPTYPE_DEFAULT; static char *askpass_tty(const char *prompt) { int ret; prompts_t *p = new_prompts(); p->to_server = false; p->from_server = false; p->name = dupstr("Pageant passphrase prompt"); add_prompt(p, dupcat(prompt, ": "), false); ret = console_get_userpass_input(p); assert(ret >= 0); if (!ret) { perror("pageant: unable to read passphrase"); free_prompts(p); return NULL; } else { char *passphrase = prompt_get_result(p->prompts[0]); free_prompts(p); return passphrase; } } static char *askpass_gui(const char *prompt) { char *passphrase; bool success; passphrase = gtk_askpass_main( display, "Pageant passphrase prompt", prompt, &success); if (!success) { /* return value is error message */ fprintf(stderr, "%s\n", passphrase); sfree(passphrase); passphrase = NULL; } return passphrase; } static char *askpass(const char *prompt) { if (prompt_type == PROMPT_TTY) { if (!have_controlling_tty()) { fprintf(stderr, "no controlling terminal available " "for passphrase prompt\n"); return NULL; } return askpass_tty(prompt); } if (prompt_type == PROMPT_GUI) { if (!display) { fprintf(stderr, "no graphical display available " "for passphrase prompt\n"); return NULL; } return askpass_gui(prompt); } if (have_controlling_tty()) { return askpass_tty(prompt); } else if (display) { return askpass_gui(prompt); } else { fprintf(stderr, "no way to read a passphrase without tty or " "X display\n"); return NULL; } } static bool unix_add_keyfile(const char *filename_str, bool add_encrypted) { Filename *filename = filename_from_str(filename_str); int status; bool ret; char *err; ret = true; /* * Try without a passphrase. */ status = pageant_add_keyfile(filename, NULL, &err, add_encrypted); if (status == PAGEANT_ACTION_OK) { goto cleanup; } else if (status == PAGEANT_ACTION_FAILURE) { fprintf(stderr, "pageant: %s: %s\n", filename_str, err); ret = false; goto cleanup; } /* * And now try prompting for a passphrase. */ while (1) { char *prompt = dupprintf( "Enter passphrase to load key '%s'", err); char *passphrase = askpass(prompt); sfree(err); sfree(prompt); err = NULL; if (!passphrase) break; status = pageant_add_keyfile(filename, passphrase, &err, add_encrypted); smemclr(passphrase, strlen(passphrase)); sfree(passphrase); passphrase = NULL; if (status == PAGEANT_ACTION_OK) { goto cleanup; } else if (status == PAGEANT_ACTION_FAILURE) { fprintf(stderr, "pageant: %s: %s\n", filename_str, err); ret = false; goto cleanup; } } cleanup: sfree(err); filename_free(filename); return ret; } void key_list_callback(void *ctx, char **fingerprints, const char *comment, uint32_t ext_flags, struct pageant_pubkey *key) { const char *mode = ""; if (ext_flags & LIST_EXTENDED_FLAG_HAS_NO_CLEARTEXT_KEY) mode = " (encrypted)"; else if (ext_flags & LIST_EXTENDED_FLAG_HAS_ENCRYPTED_KEY_FILE) mode = " (re-encryptable)"; FingerprintType this_type = ssh2_pick_fingerprint(fingerprints, key_list_fptype); printf("%s %s%s\n", fingerprints[this_type], comment, mode); } struct key_find_ctx { const char *string; bool match_fp, match_comment; bool match_fptypes[SSH_N_FPTYPES]; struct pageant_pubkey *found; int nfound; }; static bool match_fingerprint_string( const char *string_orig, char **fingerprints, const struct key_find_ctx *ctx) { const char *hash; for (unsigned fptype = 0; fptype < SSH_N_FPTYPES; fptype++) { if (!ctx->match_fptypes[fptype]) continue; const char *fingerprint = fingerprints[fptype]; if (!fingerprint) continue; /* Find the hash in the fingerprint string. It'll be the word * at the end. */ hash = strrchr(fingerprint, ' '); assert(hash); hash++; const char *string = string_orig; bool case_sensitive; const char *ignore_chars = ""; switch (fptype) { case SSH_FPTYPE_MD5: /* MD5 fingerprints are in hex, so disregard case differences. */ case_sensitive = false; /* And we don't really need to force the user to type the * colons in between the digits, which are always the * same. */ ignore_chars = ":"; break; case SSH_FPTYPE_SHA256: /* Skip over the "SHA256:" prefix, which we don't really * want to force the user to type. On the other hand, * tolerate it on the input string. */ assert(strstartswith(hash, "SHA256:")); hash += 7; if (strstartswith(string, "SHA256:")) string += 7; /* SHA256 fingerprints are base64, which is intrinsically * case sensitive. */ case_sensitive = true; break; } /* Now see if the search string is a prefix of the full hash, * neglecting colons and (where appropriate) case differences. */ while (1) { string += strspn(string, ignore_chars); hash += strspn(hash, ignore_chars); if (!*string) return true; char sc = *string, hc = *hash; if (!case_sensitive) { sc = tolower((unsigned char)sc); hc = tolower((unsigned char)hc); } if (sc != hc) break; string++; hash++; } } return false; } void key_find_callback(void *vctx, char **fingerprints, const char *comment, uint32_t ext_flags, struct pageant_pubkey *key) { struct key_find_ctx *ctx = (struct key_find_ctx *)vctx; if ((ctx->match_comment && !strcmp(ctx->string, comment)) || (ctx->match_fp && match_fingerprint_string(ctx->string, fingerprints, ctx))) { if (!ctx->found) ctx->found = pageant_pubkey_copy(key); ctx->nfound++; } } struct pageant_pubkey *find_key(const char *string, char **retstr) { struct key_find_ctx ctx[1]; struct pageant_pubkey key_in, *key_ret; bool try_file = true, try_fp = true, try_comment = true; bool file_errors = false; bool try_all_fptypes = true; FingerprintType fptype = SSH_FPTYPE_DEFAULT; /* * Trim off disambiguating prefixes telling us how to interpret * the provided string. */ if (!strncmp(string, "file:", 5)) { string += 5; try_fp = false; try_comment = false; file_errors = true; /* also report failure to load the file */ } else if (!strncmp(string, "comment:", 8)) { string += 8; try_file = false; try_fp = false; } else if (!strncmp(string, "fp:", 3)) { string += 3; try_file = false; try_comment = false; } else if (!strncmp(string, "fingerprint:", 12)) { string += 12; try_file = false; try_comment = false; } else if (!strnicmp(string, "md5:", 4)) { string += 4; try_file = false; try_comment = false; try_all_fptypes = false; fptype = SSH_FPTYPE_MD5; } else if (!strncmp(string, "sha256:", 7)) { string += 7; try_file = false; try_comment = false; try_all_fptypes = false; fptype = SSH_FPTYPE_SHA256; } /* * Try interpreting the string as a key file name. */ if (try_file) { Filename *fn = filename_from_str(string); int keytype = key_type(fn); if (keytype == SSH_KEYTYPE_SSH1 || keytype == SSH_KEYTYPE_SSH1_PUBLIC) { const char *error; key_in.blob = strbuf_new(); if (!rsa1_loadpub_f(fn, BinarySink_UPCAST(key_in.blob), NULL, &error)) { strbuf_free(key_in.blob); key_in.blob = NULL; if (file_errors) { *retstr = dupprintf("unable to load file '%s': %s", string, error); filename_free(fn); return NULL; } } else { /* * If we've successfully loaded the file, stop here - we * already have a key blob and need not go to the agent to * list things. */ key_in.ssh_version = 1; key_in.comment = NULL; key_ret = pageant_pubkey_copy(&key_in); strbuf_free(key_in.blob); key_in.blob = NULL; filename_free(fn); return key_ret; } } else if (keytype == SSH_KEYTYPE_SSH2 || keytype == SSH_KEYTYPE_SSH2_PUBLIC_RFC4716 || keytype == SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH) { const char *error; key_in.blob = strbuf_new(); if (!ppk_loadpub_f(fn, NULL, BinarySink_UPCAST(key_in.blob), NULL, &error)) { strbuf_free(key_in.blob); key_in.blob = NULL; if (file_errors) { *retstr = dupprintf("unable to load file '%s': %s", string, error); filename_free(fn); return NULL; } } else { /* * If we've successfully loaded the file, stop here - we * already have a key blob and need not go to the agent to * list things. */ key_in.ssh_version = 2; key_in.comment = NULL; key_ret = pageant_pubkey_copy(&key_in); strbuf_free(key_in.blob); key_in.blob = NULL; filename_free(fn); return key_ret; } } else { if (file_errors) { *retstr = dupprintf("unable to load key file '%s': %s", string, key_type_to_str(keytype)); filename_free(fn); return NULL; } } filename_free(fn); } /* * Failing that, go through the keys in the agent, and match * against fingerprints and comments as appropriate. */ ctx->string = string; ctx->match_fp = try_fp; ctx->match_comment = try_comment; for (unsigned i = 0; i < SSH_N_FPTYPES; i++) ctx->match_fptypes[i] = (try_all_fptypes || i == fptype); ctx->found = NULL; ctx->nfound = 0; if (pageant_enum_keys(key_find_callback, ctx, retstr) == PAGEANT_ACTION_FAILURE) return NULL; if (ctx->nfound == 0) { *retstr = dupstr("no key matched"); assert(!ctx->found); return NULL; } else if (ctx->nfound > 1) { *retstr = dupstr("multiple keys matched"); assert(ctx->found); pageant_pubkey_free(ctx->found); return NULL; } assert(ctx->found); return ctx->found; } void run_client(void) { const struct cmdline_key_action *act; struct pageant_pubkey *key; bool errors = false; char *retstr; LoadedFile *message = lf_new(AGENT_MAX_MSGLEN); bool message_loaded = false, message_ok = false; strbuf *signature = strbuf_new(); if (!agent_exists()) { fprintf(stderr, "pageant: no agent running to talk to\n"); exit(1); } for (act = keyact_head; act; act = act->next) { switch (act->action) { case KEYACT_CLIENT_ADD: case KEYACT_CLIENT_ADD_ENCRYPTED: if (!unix_add_keyfile(act->filename, act->action == KEYACT_CLIENT_ADD_ENCRYPTED)) errors = true; break; case KEYACT_CLIENT_LIST: if (pageant_enum_keys(key_list_callback, NULL, &retstr) == PAGEANT_ACTION_FAILURE) { fprintf(stderr, "pageant: listing keys: %s\n", retstr); sfree(retstr); errors = true; } break; case KEYACT_CLIENT_DEL: key = NULL; if (!(key = find_key(act->filename, &retstr)) || pageant_delete_key(key, &retstr) == PAGEANT_ACTION_FAILURE) { fprintf(stderr, "pageant: deleting key '%s': %s\n", act->filename, retstr); sfree(retstr); errors = true; } if (key) pageant_pubkey_free(key); break; case KEYACT_CLIENT_REENCRYPT: key = NULL; if (!(key = find_key(act->filename, &retstr)) || pageant_reencrypt_key(key, &retstr) == PAGEANT_ACTION_FAILURE) { fprintf(stderr, "pageant: re-encrypting key '%s': %s\n", act->filename, retstr); sfree(retstr); errors = true; } if (key) pageant_pubkey_free(key); break; case KEYACT_CLIENT_PUBLIC_OPENSSH: case KEYACT_CLIENT_PUBLIC: key = NULL; if (!(key = find_key(act->filename, &retstr))) { fprintf(stderr, "pageant: finding key '%s': %s\n", act->filename, retstr); sfree(retstr); errors = true; } else { FILE *fp = stdout; /* FIXME: add a -o option? */ if (key->ssh_version == 1) { BinarySource src[1]; RSAKey rkey; BinarySource_BARE_INIT(src, key->blob->u, key->blob->len); memset(&rkey, 0, sizeof(rkey)); rkey.comment = dupstr(key->comment); get_rsa_ssh1_pub(src, &rkey, RSA_SSH1_EXPONENT_FIRST); ssh1_write_pubkey(fp, &rkey); freersakey(&rkey); } else { ssh2_write_pubkey(fp, key->comment, key->blob->u, key->blob->len, (act->action == KEYACT_CLIENT_PUBLIC ? SSH_KEYTYPE_SSH2_PUBLIC_RFC4716 : SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH)); } pageant_pubkey_free(key); } break; case KEYACT_CLIENT_DEL_ALL: if (pageant_delete_all_keys(&retstr) == PAGEANT_ACTION_FAILURE) { fprintf(stderr, "pageant: deleting all keys: %s\n", retstr); sfree(retstr); errors = true; } break; case KEYACT_CLIENT_REENCRYPT_ALL: { int status = pageant_reencrypt_all_keys(&retstr); if (status == PAGEANT_ACTION_FAILURE) { fprintf(stderr, "pageant: re-encrypting all keys: " "%s\n", retstr); sfree(retstr); errors = true; } else if (status == PAGEANT_ACTION_WARNING) { fprintf(stderr, "pageant: re-encrypting all keys: " "warning: %s\n", retstr); sfree(retstr); } break; } case KEYACT_CLIENT_SIGN: key = NULL; if (!message_loaded) { message_loaded = true; switch(lf_load_fp(message, stdin)) { case LF_TOO_BIG: fprintf(stderr, "pageant: message to sign is too big\n"); errors = true; break; case LF_ERROR: fprintf(stderr, "pageant: reading message to sign: %s\n", strerror(errno)); errors = true; break; case LF_OK: message_ok = true; break; } } if (!message_ok) break; strbuf_clear(signature); if (!(key = find_key(act->filename, &retstr)) || pageant_sign(key, ptrlen_from_lf(message), signature, sign_flags, &retstr) == PAGEANT_ACTION_FAILURE) { fprintf(stderr, "pageant: signing with key '%s': %s\n", act->filename, retstr); sfree(retstr); errors = true; } else { fwrite(signature->s, 1, signature->len, stdout); } if (key) pageant_pubkey_free(key); break; default: unreachable("Invalid client action found"); } } lf_free(message); strbuf_free(signature); if (errors) exit(1); } static const PlugVtable X11Connection_plugvt = { .log = x11_log, .closing = x11_closing, .receive = x11_receive, .sent = x11_sent, }; static bool agent_loop_pw_setup(void *vctx, pollwrapper *pw) { struct uxpgnt_client *upc = (struct uxpgnt_client *)vctx; if (signalpipe[0] >= 0) { pollwrap_add_fd_rwx(pw, signalpipe[0], SELECT_R); } if (upc->prompt_active) pollwrap_add_fd_rwx(pw, upc->passphrase_fd, SELECT_R); return true; } static void agent_loop_pw_check(void *vctx, pollwrapper *pw) { struct uxpgnt_client *upc = (struct uxpgnt_client *)vctx; if (life == LIFE_TTY) { /* * Every time we wake up (whether it was due to tty_timer * elapsing or for any other reason), poll to see if we still * have a controlling terminal. If we don't, then our * containing tty session has ended, so it's time to clean up * and leave. */ if (!have_controlling_tty()) { time_to_die = true; return; } } if (signalpipe[0] >= 0 && pollwrap_check_fd_rwx(pw, signalpipe[0], SELECT_R)) { char c[1]; if (read(signalpipe[0], c, 1) <= 0) /* ignore error */; /* ignore its value; it'll be `x' */ while (1) { int status; pid_t pid; pid = waitpid(-1, &status, WNOHANG); if (pid <= 0) break; if (pid == upc->termination_pid) time_to_die = true; } } if (upc->prompt_active && pollwrap_check_fd_rwx(pw, upc->passphrase_fd, SELECT_R)) { char c; int retd = read(upc->passphrase_fd, &c, 1); switch (upc->prompt_type) { case RTPROMPT_GUI: if (retd <= 0) { close(upc->passphrase_fd); upc->passphrase_fd = -1; bool ok = (retd == 0); if (!strbuf_chomp(upc->prompt_buf, '\n')) ok = false; passphrase_done(upc, ok); } else { put_byte(upc->prompt_buf, c); } break; case RTPROMPT_DEBUG: if (retd <= 0) { passphrase_done(upc, false); /* Now never try to read from stdin again */ upc->prompt_type = RTPROMPT_UNAVAILABLE; break; } switch (c) { case '\n': case '\r': passphrase_done(upc, true); break; case '\004': passphrase_done(upc, false); break; case '\b': case '\177': strbuf_shrink_by(upc->prompt_buf, 1); break; case '\025': strbuf_clear(upc->prompt_buf); break; default: put_byte(upc->prompt_buf, c); break; } break; case RTPROMPT_UNAVAILABLE: unreachable("Should never have started a prompt at all"); } } } static bool agent_loop_continue(void *vctx, bool fd, bool cb) { return !time_to_die; } void run_agent(FILE *logfp, const char *symlink_path) { const char *err; char *errw; struct pageant_listen_state *pl; Plug *pl_plug; Socket *sock; bool errors = false; Conf *conf; const struct cmdline_key_action *act; pageant_init(); /* * Start by loading any keys provided on the command line. */ for (act = keyact_head; act; act = act->next) { assert(act->action == KEYACT_AGENT_LOAD || act->action == KEYACT_AGENT_LOAD_ENCRYPTED); if (!unix_add_keyfile(act->filename, act->action == KEYACT_AGENT_LOAD_ENCRYPTED)) errors = true; } if (errors) exit(1); /* * Set up a listening socket and run Pageant on it. */ struct uxpgnt_client upc[1]; memset(upc, 0, sizeof(upc)); upc->plc.vt = &uxpgnt_vtable; upc->logfp = logfp; upc->passphrase_fd = -1; upc->termination_pid = -1; upc->prompt_buf = strbuf_new_nm(); upc->prompt_type = display ? RTPROMPT_GUI : RTPROMPT_UNAVAILABLE; pl = pageant_listener_new(&pl_plug, &upc->plc); sock = platform_make_agent_socket(pl_plug, PAGEANT_DIR_PREFIX, &errw, &socketname); if (!sock) { fprintf(stderr, "pageant: %s\n", errw); sfree(errw); exit(1); } pageant_listener_got_socket(pl, sock); if (symlink_path) { /* * Try to make a symlink to the Unix socket, in a location of * the user's choosing. * * If the link already exists, we want to replace it. There * are two ways we could do this: either make it under another * name and then rename it over the top, or remove the old * link first. The former is what 'ln -sf' does, on the * grounds that it's more atomic. But I think in this case, * where the expected use case is that the previous agent has * long since shut down, atomicity isn't a critical concern * compared to not accidentally overwriting some non-symlink * that might have important data in it! */ struct stat st; if (lstat(symlink_path, &st) == 0 && S_ISLNK(st.st_mode)) unlink(symlink_path); if (symlink(socketname, symlink_path) < 0) fprintf(stderr, "pageant: making symlink %s: %s\n", symlink_path, strerror(errno)); } conf = conf_new(); conf_set_int(conf, CONF_proxy_type, PROXY_NONE); /* * Lifetime preparations. */ if (life == LIFE_X11) { struct X11Display *disp; void *greeting; int greetinglen; Socket *s; struct X11Connection *conn; char *x11_setup_err; if (!display) { fprintf(stderr, "pageant: no DISPLAY for -X mode\n"); exit(1); } disp = x11_setup_display(display, conf, &x11_setup_err); if (!disp) { fprintf(stderr, "pageant: unable to connect to X server: %s\n", x11_setup_err); sfree(x11_setup_err); exit(1); } conn = snew(struct X11Connection); conn->plug.vt = &X11Connection_plugvt; s = new_connection(sk_addr_dup(disp->addr), disp->realhost, disp->port, false, true, false, false, &conn->plug, conf); if ((err = sk_socket_error(s)) != NULL) { fprintf(stderr, "pageant: unable to connect to X server: %s", err); exit(1); } greeting = x11_make_greeting('B', 11, 0, disp->localauthproto, disp->localauthdata, disp->localauthdatalen, NULL, 0, &greetinglen); sk_write(s, greeting, greetinglen); smemclr(greeting, greetinglen); sfree(greeting); pageant_fork_and_print_env(false); } else if (life == LIFE_TTY) { schedule_timer(TTY_LIFE_POLL_INTERVAL, tty_life_timer, &dummy_timer_ctx); pageant_fork_and_print_env(true); } else if (life == LIFE_PERM) { pageant_fork_and_print_env(false); } else if (life == LIFE_DEBUG) { pageant_print_env(getpid()); upc->logfp = stdout; struct termios orig_termios; upc->passphrase_fd = fileno(stdin); if (tcgetattr(upc->passphrase_fd, &orig_termios) == 0) { struct termios new_termios = orig_termios; new_termios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL | ICANON); /* * Try to set up a watchdog process that will restore * termios if we crash or are killed. If successful, turn * off echo, for runtime passphrase prompts. */ int pipefd[2]; if (pipe(pipefd) == 0) { pid_t pid = fork(); if (pid == 0) { tcsetattr(upc->passphrase_fd, TCSADRAIN, &new_termios); close(pipefd[1]); char buf[4096]; while (read(pipefd[0], buf, sizeof(buf)) > 0); tcsetattr(upc->passphrase_fd, TCSADRAIN, &new_termios); _exit(0); } else if (pid > 0) { upc->prompt_type = RTPROMPT_DEBUG; } close(pipefd[0]); if (pid < 0) close(pipefd[1]); } } } else if (life == LIFE_EXEC) { pid_t agentpid, pid; agentpid = getpid(); setup_sigchld_handler(); pid = fork(); if (pid < 0) { perror("fork"); exit(1); } else if (pid == 0) { setenv("SSH_AUTH_SOCK", socketname, true); setenv("SSH_AGENT_PID", dupprintf("%d", (int)agentpid), true); execvp(exec_args[0], exec_args); perror("exec"); _exit(127); } else { upc->termination_pid = pid; } } if (!upc->logfp) upc->plc.suppress_logging = true; cli_main_loop(agent_loop_pw_setup, agent_loop_pw_check, agent_loop_continue, upc); /* * Before terminating, clean up our Unix socket file if possible. */ if (unlink(socketname) < 0) { fprintf(stderr, "pageant: %s: %s\n", socketname, strerror(errno)); exit(1); } strbuf_free(upc->prompt_buf); conf_free(conf); } int main(int argc, char **argv) { bool doing_opts = true; keyact curr_keyact = KEYACT_AGENT_LOAD; const char *standalone_askpass_prompt = NULL; const char *symlink_path = NULL; FILE *logfp = NULL; progname = argv[0]; /* * Process the command line. */ while (--argc > 0) { char *p = *++argv; if (*p == '-' && doing_opts) { if (!strcmp(p, "-V") || !strcmp(p, "--version")) { version(); } else if (!strcmp(p, "--help")) { usage(); exit(0); } else if (!strcmp(p, "-v")) { logfp = stderr; } else if (!strcmp(p, "-a")) { curr_keyact = KEYACT_CLIENT_ADD; } else if (!strcmp(p, "-d")) { curr_keyact = KEYACT_CLIENT_DEL; } else if (!strcmp(p, "-r")) { curr_keyact = KEYACT_CLIENT_REENCRYPT; } else if (!strcmp(p, "-s")) { shell_type = SHELL_SH; } else if (!strcmp(p, "-c")) { shell_type = SHELL_CSH; } else if (!strcmp(p, "-D")) { add_keyact(KEYACT_CLIENT_DEL_ALL, NULL); } else if (!strcmp(p, "-R")) { add_keyact(KEYACT_CLIENT_REENCRYPT_ALL, NULL); } else if (!strcmp(p, "-l")) { add_keyact(KEYACT_CLIENT_LIST, NULL); } else if (!strcmp(p, "--public")) { curr_keyact = KEYACT_CLIENT_PUBLIC; } else if (!strcmp(p, "--public-openssh") || !strcmp(p, "-L")) { curr_keyact = KEYACT_CLIENT_PUBLIC_OPENSSH; } else if (!strcmp(p, "-X")) { life = LIFE_X11; } else if (!strcmp(p, "-T")) { life = LIFE_TTY; } else if (!strcmp(p, "--no-decrypt") || !strcmp(p, "-no-decrypt") || !strcmp(p, "--no_decrypt") || !strcmp(p, "-no_decrypt") || !strcmp(p, "--nodecrypt") || !strcmp(p, "-nodecrypt") || !strcmp(p, "--encrypted") || !strcmp(p, "-encrypted")) { if (curr_keyact == KEYACT_AGENT_LOAD) curr_keyact = KEYACT_AGENT_LOAD_ENCRYPTED; else if (curr_keyact == KEYACT_CLIENT_ADD) curr_keyact = KEYACT_CLIENT_ADD_ENCRYPTED; else { fprintf(stderr, "pageant: unexpected -E while not adding " "keys\n"); exit(1); } } else if (!strcmp(p, "--debug")) { life = LIFE_DEBUG; } else if (!strcmp(p, "--test-sign")) { curr_keyact = KEYACT_CLIENT_SIGN; sign_flags = 0; } else if (strstartswith(p, "--test-sign-with-flags=")) { curr_keyact = KEYACT_CLIENT_SIGN; sign_flags = atoi(p + strlen("--test-sign-with-flags=")); } else if (!strcmp(p, "--permanent")) { life = LIFE_PERM; } else if (!strcmp(p, "--exec")) { life = LIFE_EXEC; /* Now all subsequent arguments go to the exec command. */ if (--argc > 0) { exec_args = ++argv; argc = 0; /* force end of option processing */ } else { fprintf(stderr, "pageant: expected a command " "after --exec\n"); exit(1); } } else if (!strcmp(p, "--tty-prompt")) { prompt_type = PROMPT_TTY; } else if (!strcmp(p, "--gui-prompt")) { prompt_type = PROMPT_GUI; } else if (!strcmp(p, "--askpass")) { if (--argc > 0) { standalone_askpass_prompt = *++argv; } else { fprintf(stderr, "pageant: expected a prompt message " "after --askpass\n"); exit(1); } } else if (!strcmp(p, "--symlink")) { if (--argc > 0) { symlink_path = *++argv; } else { fprintf(stderr, "pageant: expected a pathname " "after --symlink\n"); exit(1); } } else if (!strcmp(p, "-E") || !strcmp(p, "--fptype")) { const char *keyword; if (--argc > 0) { keyword = *++argv; } else { fprintf(stderr, "pageant: expected a type string " "after %s\n", p); exit(1); } if (!strcmp(keyword, "md5")) key_list_fptype = SSH_FPTYPE_MD5; else if (!strcmp(keyword, "sha256")) key_list_fptype = SSH_FPTYPE_SHA256; else { fprintf(stderr, "pageant: unknown fingerprint type `%s'\n", keyword); exit(1); } } else if (!strcmp(p, "--")) { doing_opts = false; } else { fprintf(stderr, "pageant: unrecognised option '%s'\n", p); exit(1); } } else { /* * Non-option arguments (apart from those after --exec, * which are treated specially above) are interpreted as * the names of private key files to either add or delete * from an agent. */ add_keyact(curr_keyact, p); } } if (life == LIFE_EXEC && !exec_args) { fprintf(stderr, "pageant: expected a command with --exec\n"); exit(1); } if (!display) { display = getenv("DISPLAY"); if (display && !*display) display = NULL; } /* * Deal with standalone-askpass mode. */ if (standalone_askpass_prompt) { char *passphrase = askpass(standalone_askpass_prompt); if (!passphrase) return 1; puts(passphrase); fflush(stdout); smemclr(passphrase, strlen(passphrase)); sfree(passphrase); return 0; } /* * Block SIGPIPE, so that we'll get EPIPE individually on * particular network connections that go wrong. */ putty_signal(SIGPIPE, SIG_IGN); sk_init(); uxsel_init(); /* * Now distinguish our two main running modes. Either we're * actually starting up an agent, in which case we should have a * lifetime mode, and no key actions of KEYACT_CLIENT_* type; or * else we're contacting an existing agent to add or remove keys, * in which case we should have no lifetime mode, and no key * actions of KEYACT_AGENT_* type. */ { bool has_agent_actions = false; bool has_client_actions = false; bool has_lifetime = false; const struct cmdline_key_action *act; for (act = keyact_head; act; act = act->next) { if (is_agent_action(act->action)) has_agent_actions = true; else has_client_actions = true; } if (life != LIFE_UNSPEC) has_lifetime = true; if (has_lifetime && has_client_actions) { fprintf(stderr, "pageant: client key actions (-a, -d, -D, -r, -R, " "-l, -L) do not go with an agent lifetime option\n"); exit(1); } if (!has_lifetime && has_agent_actions) { fprintf(stderr, "pageant: expected an agent lifetime option with" " bare key file arguments\n"); exit(1); } if (!has_lifetime && !has_client_actions) { fprintf(stderr, "pageant: expected an agent lifetime option" " or a client key action\n"); exit(1); } if (has_lifetime) { run_agent(logfp, symlink_path); } else if (has_client_actions) { run_client(); } } return 0; } putty-0.76/unix/uxplink.c0000644000175000017500000007367314072266313012421 00000000000000/* * PLink - a command-line (stdin/stdout) variant of PuTTY. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "putty.h" #include "ssh.h" #include "storage.h" #include "tree234.h" #define MAX_STDIN_BACKLOG 4096 static LogContext *logctx; static struct termios orig_termios; void cmdline_error(const char *fmt, ...) { va_list ap; va_start(ap, fmt); console_print_error_msg_fmt_v("plink", fmt, ap); va_end(ap); exit(1); } static bool local_tty = false; /* do we have a local tty? */ static Backend *backend; static Conf *conf; /* * Default settings that are specific to Unix plink. */ char *platform_default_s(const char *name) { if (!strcmp(name, "TermType")) return dupstr(getenv("TERM")); if (!strcmp(name, "SerialLine")) return dupstr("/dev/ttyS0"); return NULL; } bool platform_default_b(const char *name, bool def) { return def; } 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 */ } static void plink_echoedit_update(Seat *seat, bool echo, bool edit) { /* Update stdin read mode to reflect changes in line discipline. */ struct termios mode; if (!local_tty) return; mode = orig_termios; if (echo) mode.c_lflag |= ECHO; else mode.c_lflag &= ~ECHO; if (edit) { mode.c_iflag |= ICRNL; mode.c_lflag |= ISIG | ICANON; mode.c_oflag |= OPOST; } else { mode.c_iflag &= ~ICRNL; mode.c_lflag &= ~(ISIG | ICANON); mode.c_oflag &= ~OPOST; /* Solaris sets these to unhelpful values */ mode.c_cc[VMIN] = 1; mode.c_cc[VTIME] = 0; /* FIXME: perhaps what we do with IXON/IXOFF should be an * argument to the echoedit_update() method, to allow * implementation of SSH-2 "xon-xoff" and Rlogin's * equivalent? */ mode.c_iflag &= ~IXON; mode.c_iflag &= ~IXOFF; } /* * Mark parity errors and (more important) BREAK on input. This * is more complex than it need be because POSIX-2001 suggests * that escaping of valid 0xff in the input stream is dependent on * IGNPAR being clear even though marking of BREAK isn't. NetBSD * 2.0 goes one worse and makes it dependent on INPCK too. We * deal with this by forcing these flags into a useful state and * then faking the state in which we found them in from_tty() if * we get passed a parity or framing error. */ mode.c_iflag = (mode.c_iflag | INPCK | PARMRK) & ~IGNPAR; tcsetattr(STDIN_FILENO, TCSANOW, &mode); } /* 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); } static char *plink_get_ttymode(Seat *seat, const char *mode) { /* * Propagate appropriate terminal modes from the local terminal, * if any. */ if (!local_tty) return NULL; #define GET_CHAR(ourname, uxname) \ do { \ if (strcmp(mode, ourname) == 0) \ return get_ttychar(&orig_termios, uxname); \ } while(0) #define GET_BOOL(ourname, uxname, uxmemb, transform) \ do { \ if (strcmp(mode, ourname) == 0) { \ bool b = (orig_termios.uxmemb & uxname) != 0; \ transform; \ return dupprintf("%d", b); \ } \ } while (0) /* * Modes that want to be the same on all terminal devices involved. */ /* All the special characters supported by SSH */ #if defined(VINTR) GET_CHAR("INTR", VINTR); #endif #if defined(VQUIT) GET_CHAR("QUIT", VQUIT); #endif #if defined(VERASE) GET_CHAR("ERASE", VERASE); #endif #if defined(VKILL) GET_CHAR("KILL", VKILL); #endif #if defined(VEOF) GET_CHAR("EOF", VEOF); #endif #if defined(VEOL) GET_CHAR("EOL", VEOL); #endif #if defined(VEOL2) GET_CHAR("EOL2", VEOL2); #endif #if defined(VSTART) GET_CHAR("START", VSTART); #endif #if defined(VSTOP) GET_CHAR("STOP", VSTOP); #endif #if defined(VSUSP) GET_CHAR("SUSP", VSUSP); #endif #if defined(VDSUSP) GET_CHAR("DSUSP", VDSUSP); #endif #if defined(VREPRINT) GET_CHAR("REPRINT", VREPRINT); #endif #if defined(VWERASE) GET_CHAR("WERASE", VWERASE); #endif #if defined(VLNEXT) GET_CHAR("LNEXT", VLNEXT); #endif #if defined(VFLUSH) GET_CHAR("FLUSH", VFLUSH); #endif #if defined(VSWTCH) GET_CHAR("SWTCH", VSWTCH); #endif #if defined(VSTATUS) GET_CHAR("STATUS", VSTATUS); #endif #if defined(VDISCARD) GET_CHAR("DISCARD", VDISCARD); #endif /* Modes that "configure" other major modes. These should probably be * considered as user preferences. */ /* Configuration of ICANON */ #if defined(ECHOK) GET_BOOL("ECHOK", ECHOK, c_lflag, ); #endif #if defined(ECHOKE) GET_BOOL("ECHOKE", ECHOKE, c_lflag, ); #endif #if defined(ECHOE) GET_BOOL("ECHOE", ECHOE, c_lflag, ); #endif #if defined(ECHONL) GET_BOOL("ECHONL", ECHONL, c_lflag, ); #endif #if defined(XCASE) GET_BOOL("XCASE", XCASE, c_lflag, ); #endif #if defined(IUTF8) GET_BOOL("IUTF8", IUTF8, c_iflag, ); #endif /* Configuration of ECHO */ #if defined(ECHOCTL) GET_BOOL("ECHOCTL", ECHOCTL, c_lflag, ); #endif /* Configuration of IXON/IXOFF */ #if defined(IXANY) GET_BOOL("IXANY", IXANY, c_iflag, ); #endif /* Configuration of OPOST */ #if defined(OLCUC) GET_BOOL("OLCUC", OLCUC, c_oflag, ); #endif #if defined(ONLCR) GET_BOOL("ONLCR", ONLCR, c_oflag, ); #endif #if defined(OCRNL) GET_BOOL("OCRNL", OCRNL, c_oflag, ); #endif #if defined(ONOCR) GET_BOOL("ONOCR", ONOCR, c_oflag, ); #endif #if defined(ONLRET) GET_BOOL("ONLRET", ONLRET, c_oflag, ); #endif /* * Modes that want to be set in only one place, and that we have * squashed locally. */ #if defined(ISIG) GET_BOOL("ISIG", ISIG, c_lflag, ); #endif #if defined(ICANON) GET_BOOL("ICANON", ICANON, c_lflag, ); #endif #if defined(ECHO) GET_BOOL("ECHO", ECHO, c_lflag, ); #endif #if defined(IXON) GET_BOOL("IXON", IXON, c_iflag, ); #endif #if defined(IXOFF) GET_BOOL("IXOFF", IXOFF, c_iflag, ); #endif #if defined(OPOST) GET_BOOL("OPOST", OPOST, c_oflag, ); #endif /* * We do not propagate the following modes: * - Parity/serial settings, which are a local affair and don't * make sense propagated over SSH's 8-bit byte-stream. * IGNPAR PARMRK INPCK CS7 CS8 PARENB PARODD * - Things that want to be enabled in one place that we don't * squash locally. * IUCLC * - Status bits. * PENDIN * - Things I don't know what to do with. (FIXME) * ISTRIP IMAXBEL NOFLSH TOSTOP IEXTEN * INLCR IGNCR ICRNL */ #undef GET_CHAR #undef GET_BOOL /* Fall through to here for unrecognised names, or ones that are * unsupported on this platform */ return NULL; } void cleanup_termios(void) { if (local_tty) tcsetattr(STDIN_FILENO, TCSANOW, &orig_termios); } static bufchain stdout_data, stderr_data; static bufchain_sink stdout_bcs, stderr_bcs; static StripCtrlChars *stdout_scc, *stderr_scc; static BinarySink *stdout_bs, *stderr_bs; static enum { EOF_NO, EOF_PENDING, EOF_SENT } outgoingeof; size_t try_output(bool is_stderr) { bufchain *chain = (is_stderr ? &stderr_data : &stdout_data); int fd = (is_stderr ? STDERR_FILENO : STDOUT_FILENO); ssize_t ret; if (bufchain_size(chain) > 0) { bool prev_nonblock = nonblock(fd); ptrlen senddata; do { senddata = bufchain_prefix(chain); ret = write(fd, senddata.ptr, senddata.len); if (ret > 0) bufchain_consume(chain, ret); } while (ret == senddata.len && 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); } static size_t plink_output( Seat *seat, bool is_stderr, const void *data, size_t len) { assert(is_stderr || outgoingeof == EOF_NO); BinarySink *bs = is_stderr ? stderr_bs : stdout_bs; put_data(bs, data, len); return try_output(is_stderr); } static bool plink_eof(Seat *seat) { assert(outgoingeof == EOF_NO); outgoingeof = EOF_PENDING; try_output(false); return false; /* do not respond to incoming EOF with outgoing */ } static int plink_get_userpass_input(Seat *seat, prompts_t *p, bufchain *input) { int ret; ret = cmdline_get_passwd_input(p); if (ret == -1) ret = console_get_userpass_input(p); return ret; } static bool plink_seat_interactive(Seat *seat) { return (!*conf_get_str(conf, CONF_remote_cmd) && !*conf_get_str(conf, CONF_remote_cmd2) && !*conf_get_str(conf, CONF_ssh_nc_host)); } static const SeatVtable plink_seat_vt = { .output = plink_output, .eof = plink_eof, .get_userpass_input = plink_get_userpass_input, .notify_remote_exit = nullseat_notify_remote_exit, .connection_fatal = console_connection_fatal, .update_specials_menu = nullseat_update_specials_menu, .get_ttymode = plink_get_ttymode, .set_busy_status = nullseat_set_busy_status, .verify_ssh_host_key = console_verify_ssh_host_key, .confirm_weak_crypto_primitive = console_confirm_weak_crypto_primitive, .confirm_weak_cached_hostkey = console_confirm_weak_cached_hostkey, .is_utf8 = nullseat_is_never_utf8, .echoedit_update = plink_echoedit_update, .get_x_display = nullseat_get_x_display, .get_windowid = nullseat_get_windowid, .get_window_pixel_size = nullseat_get_window_pixel_size, .stripctrl_new = console_stripctrl_new, .set_trust_status = console_set_trust_status, .verbose = cmdline_seat_verbose, .interactive = plink_seat_interactive, .get_cursor_position = nullseat_get_cursor_position, }; static Seat plink_seat[1] = {{ &plink_seat_vt }}; /* * Handle data from a local tty in PARMRK format. */ static void from_tty(void *vbuf, unsigned len) { char *p, *q, *end, *buf = vbuf; static enum {NORMAL, FF, FF00} state = NORMAL; p = buf; end = buf + len; while (p < end) { switch (state) { case NORMAL: if (*p == '\xff') { p++; state = FF; } else { q = memchr(p, '\xff', end - p); if (q == NULL) q = end; backend_send(backend, p, q - p); p = q; } break; case FF: if (*p == '\xff') { backend_send(backend, p, 1); p++; state = NORMAL; } else if (*p == '\0') { p++; state = FF00; } else abort(); break; case FF00: if (*p == '\0') { backend_special(backend, SS_BRK, 0); } else { /* * Pretend that PARMRK wasn't set. This involves * faking what INPCK and IGNPAR would have done if * we hadn't overridden them. Unfortunately, we * can't do this entirely correctly because INPCK * distinguishes between framing and parity * errors, but PARMRK format represents both in * the same way. We assume that parity errors are * more common than framing errors, and hence * treat all input errors as being subject to * INPCK. */ if (orig_termios.c_iflag & INPCK) { /* If IGNPAR is set, we throw away the character. */ if (!(orig_termios.c_iflag & IGNPAR)) { /* PE/FE get passed on as NUL. */ *p = 0; backend_send(backend, p, 1); } } else { /* INPCK not set. Assume we got a parity error. */ backend_send(backend, p, 1); } } p++; state = NORMAL; } } } static int signalpipe[2]; void sigwinch(int signum) { if (write(signalpipe[1], "x", 1) <= 0) /* not much we can do about it */; } /* * Short description of parameters. */ static void usage(void) { printf("Plink: command-line connection utility\n"); printf("%s\n", ver); printf("Usage: plink [options] [user@]host [command]\n"); printf(" (\"host\" can also be a PuTTY saved session name)\n"); printf("Options:\n"); printf(" -V print version information and exit\n"); printf(" -pgpfp print PGP key fingerprints and exit\n"); printf(" -v show verbose messages\n"); printf(" -load sessname Load settings from saved session\n"); printf(" -ssh -telnet -rlogin -raw -serial\n"); printf(" force use of a particular protocol\n"); printf(" -ssh-connection\n"); printf(" force use of the bare ssh-connection protocol\n"); printf(" -P port connect to specified port\n"); printf(" -l user connect with specified username\n"); printf(" -batch disable all interactive prompts\n"); printf(" -proxycmd command\n"); printf(" use 'command' as local proxy\n"); printf(" -sercfg configuration-string (e.g. 19200,8,n,1,X)\n"); printf(" Specify the serial configuration (serial only)\n"); printf("The following options only apply to SSH connections:\n"); printf(" -pw passw login with specified password\n"); printf(" -D [listen-IP:]listen-port\n"); printf(" Dynamic SOCKS-based port forwarding\n"); printf(" -L [listen-IP:]listen-port:host:port\n"); printf(" Forward local port to remote address\n"); printf(" -R [listen-IP:]listen-port:host:port\n"); printf(" Forward remote port to local address\n"); printf(" -X -x enable / disable X11 forwarding\n"); printf(" -A -a enable / disable agent forwarding\n"); printf(" -t -T enable / disable pty allocation\n"); printf(" -1 -2 force use of particular SSH protocol version\n"); printf(" -4 -6 force use of IPv4 or IPv6\n"); printf(" -C enable compression\n"); printf(" -i key private key file for user authentication\n"); printf(" -noagent disable use of Pageant\n"); printf(" -agent enable use of Pageant\n"); printf(" -no-trivial-auth\n"); printf(" disconnect if SSH authentication succeeds trivially\n"); printf(" -noshare disable use of connection sharing\n"); printf(" -share enable use of connection sharing\n"); printf(" -hostkey keyid\n"); printf(" manually specify a host key (may be repeated)\n"); printf(" -sanitise-stderr, -sanitise-stdout, " "-no-sanitise-stderr, -no-sanitise-stdout\n"); printf(" do/don't strip control chars from standard " "output/error\n"); printf(" -no-antispoof omit anti-spoofing prompt after " "authentication\n"); printf(" -m file read remote command(s) from file\n"); printf(" -s remote command is an SSH subsystem (SSH-2 only)\n"); printf(" -N don't start a shell/command (SSH-2 only)\n"); printf(" -nc host:port\n"); printf(" open tunnel in place of session (SSH-2 only)\n"); printf(" -sshlog file\n"); printf(" -sshrawlog file\n"); printf(" log protocol details to a file\n"); printf(" -logoverwrite\n"); printf(" -logappend\n"); printf(" control what happens when a log file already exists\n"); printf(" -shareexists\n"); printf(" test whether a connection-sharing upstream exists\n"); exit(1); } static void version(void) { char *buildinfo_text = buildinfo("\n"); printf("plink: %s\n%s\n", ver, buildinfo_text); sfree(buildinfo_text); exit(0); } void frontend_net_error_pending(void) {} const bool share_can_be_downstream = true; const bool share_can_be_upstream = true; const bool buildinfo_gtk_relevant = false; const unsigned cmdline_tooltype = TOOLTYPE_HOST_ARG | TOOLTYPE_HOST_ARG_CAN_BE_SESSION | TOOLTYPE_HOST_ARG_PROTOCOL_PREFIX | TOOLTYPE_HOST_ARG_FROM_LAUNCHABLE_LOAD; static bool seen_stdin_eof = false; static bool plink_pw_setup(void *vctx, pollwrapper *pw) { pollwrap_add_fd_rwx(pw, signalpipe[0], SELECT_R); if (!seen_stdin_eof && backend_connected(backend) && backend_sendok(backend) && backend_sendbuffer(backend) < MAX_STDIN_BACKLOG) { /* If we're OK to send, then try to read from stdin. */ pollwrap_add_fd_rwx(pw, STDIN_FILENO, SELECT_R); } if (bufchain_size(&stdout_data) > 0) { /* If we have data for stdout, try to write to stdout. */ pollwrap_add_fd_rwx(pw, STDOUT_FILENO, SELECT_W); } if (bufchain_size(&stderr_data) > 0) { /* If we have data for stderr, try to write to stderr. */ pollwrap_add_fd_rwx(pw, STDERR_FILENO, SELECT_W); } return true; } static void plink_pw_check(void *vctx, pollwrapper *pw) { if (pollwrap_check_fd_rwx(pw, signalpipe[0], SELECT_R)) { 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) backend_size(backend, size.ws_col, size.ws_row); } if (pollwrap_check_fd_rwx(pw, STDIN_FILENO, SELECT_R)) { char buf[4096]; int ret; if (backend_connected(backend)) { ret = read(STDIN_FILENO, buf, sizeof(buf)); noise_ultralight(NOISE_SOURCE_IOLEN, ret); if (ret < 0) { perror("stdin: read"); exit(1); } else if (ret == 0) { backend_special(backend, SS_EOF, 0); seen_stdin_eof = true; } else { if (local_tty) from_tty(buf, ret); else backend_send(backend, buf, ret); } } } if (pollwrap_check_fd_rwx(pw, STDOUT_FILENO, SELECT_W)) { backend_unthrottle(backend, try_output(false)); } if (pollwrap_check_fd_rwx(pw, STDERR_FILENO, SELECT_W)) { backend_unthrottle(backend, try_output(true)); } } static bool plink_continue(void *vctx, bool found_any_fd, bool ran_any_callback) { if (!backend_connected(backend) && bufchain_size(&stdout_data) == 0 && bufchain_size(&stderr_data) == 0) return false; /* terminate main loop */ return true; } int main(int argc, char **argv) { int exitcode; bool errors; enum TriState sanitise_stdout = AUTO, sanitise_stderr = AUTO; bool use_subsystem = false; bool just_test_share_exists = false; struct winsize size; const struct BackendVtable *backvt; /* * Initialise port and protocol to sensible defaults. (These * will be overridden by more or less anything.) */ settings_set_default_protocol(PROT_SSH); settings_set_default_port(22); bufchain_init(&stdout_data); bufchain_init(&stderr_data); bufchain_sink_init(&stdout_bcs, &stdout_data); bufchain_sink_init(&stderr_bcs, &stderr_data); stdout_bs = BinarySink_UPCAST(&stdout_bcs); stderr_bs = BinarySink_UPCAST(&stderr_bcs); outgoingeof = EOF_NO; stderr_tty_init(); /* * Process the command line. */ conf = conf_new(); do_defaults(NULL, conf); settings_set_default_protocol(conf_get_int(conf, CONF_protocol)); settings_set_default_port(conf_get_int(conf, CONF_port)); errors = false; { /* * Override the default protocol if PLINK_PROTOCOL is set. */ char *p = getenv("PLINK_PROTOCOL"); if (p) { const struct BackendVtable *vt = backend_vt_from_name(p); if (vt) { settings_set_default_protocol(vt->protocol); settings_set_default_port(vt->default_port); conf_set_int(conf, CONF_protocol, vt->protocol); conf_set_int(conf, CONF_port, vt->default_port); } } } while (--argc) { char *p = *++argv; 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 = true; } else if (ret == 2) { --argc, ++argv; } else if (ret == 1) { continue; } else if (!strcmp(p, "-batch")) { console_batch_mode = true; } else if (!strcmp(p, "-s")) { /* Save status to write to conf later. */ use_subsystem = true; } 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 = true; } else { --argc; /* Explicitly pass "plink" in place of appname for * error reporting purposes. appname will have been * set by be_foo.c to something more generic, probably * "PuTTY". */ provide_xrm_string(*++argv, "plink"); } } else if (!strcmp(p, "-shareexists")) { just_test_share_exists = true; } else if (!strcmp(p, "-fuzznet")) { conf_set_int(conf, CONF_proxy_type, PROXY_FUZZ); conf_set_str(conf, CONF_proxy_telnet_command, "%host"); } else if (!strcmp(p, "-sanitise-stdout") || !strcmp(p, "-sanitize-stdout")) { sanitise_stdout = FORCE_ON; } else if (!strcmp(p, "-no-sanitise-stdout") || !strcmp(p, "-no-sanitize-stdout")) { sanitise_stdout = FORCE_OFF; } else if (!strcmp(p, "-sanitise-stderr") || !strcmp(p, "-sanitize-stderr")) { sanitise_stderr = FORCE_ON; } else if (!strcmp(p, "-no-sanitise-stderr") || !strcmp(p, "-no-sanitize-stderr")) { sanitise_stderr = FORCE_OFF; } else if (!strcmp(p, "-no-antispoof")) { console_antispoof_prompt = false; } else if (*p != '-') { strbuf *cmdbuf = strbuf_new(); while (argc > 0) { if (cmdbuf->len > 0) put_byte(cmdbuf, ' '); /* add space separator */ put_datapl(cmdbuf, ptrlen_from_asciz(p)); if (--argc > 0) p = *++argv; } conf_set_str(conf, CONF_remote_cmd, cmdbuf->s); conf_set_str(conf, CONF_remote_cmd2, ""); conf_set_bool(conf, CONF_nopty, true); /* command => no tty */ strbuf_free(cmdbuf); break; /* done with cmdline */ } else { fprintf(stderr, "plink: unknown option \"%s\"\n", p); errors = true; } } if (errors) return 1; if (!cmdline_host_ok(conf)) { usage(); } prepare_session(conf); /* * Perform command-line overrides on session configuration. */ cmdline_run_saved(conf); /* * If we have no better ideas for the remote username, use the local * one, as 'ssh' does. */ if (conf_get_str(conf, CONF_username)[0] == '\0') { char *user = get_username(); if (user) { conf_set_str(conf, CONF_username, user); sfree(user); } } /* * Apply subsystem status. */ if (use_subsystem) conf_set_bool(conf, CONF_ssh_subsys, true); /* * Select protocol. This is farmed out into a table in a * separate file to enable an ssh-free variant. */ backvt = backend_vt_from_proto(conf_get_int(conf, CONF_protocol)); if (!backvt) { fprintf(stderr, "Internal fault: Unsupported protocol found\n"); return 1; } if (backvt->flags & BACKEND_NEEDS_TERMINAL) { fprintf(stderr, "Plink doesn't support %s, which needs terminal emulation\n", backvt->displayname); return 1; } /* * 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); } /* We don't want the signal handler to block if the pipe's full. */ nonblock(signalpipe[0]); nonblock(signalpipe[1]); cloexec(signalpipe[0]); cloexec(signalpipe[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); } /* * Decide whether to sanitise control sequences out of standard * output and standard error. * * If we weren't given a command-line override, we do this if (a) * the fd in question is pointing at a terminal, and (b) we aren't * trying to allocate a terminal as part of the session. * * (Rationale: the risk of control sequences is that they cause * confusion when sent to a local terminal, so if there isn't one, * no problem. Also, if we allocate a remote terminal, then we * sent a terminal type, i.e. we told it what kind of escape * sequences we _like_, i.e. we were expecting to receive some.) */ if (sanitise_stdout == FORCE_ON || (sanitise_stdout == AUTO && isatty(STDOUT_FILENO) && conf_get_bool(conf, CONF_nopty))) { stdout_scc = stripctrl_new(stdout_bs, true, L'\0'); stdout_bs = BinarySink_UPCAST(stdout_scc); } if (sanitise_stderr == FORCE_ON || (sanitise_stderr == AUTO && isatty(STDERR_FILENO) && conf_get_bool(conf, CONF_nopty))) { stderr_scc = stripctrl_new(stderr_bs, true, L'\0'); stderr_bs = BinarySink_UPCAST(stderr_scc); } sk_init(); uxsel_init(); /* * 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_bool(conf, CONF_x11_forward) && !conf_get_bool(conf, CONF_agentfwd) && !conf_get_str_nthstrkey(conf, CONF_portfwd, 0)) conf_set_bool(conf, CONF_ssh_simple, true); if (just_test_share_exists) { if (!backvt->test_for_upstream) { fprintf(stderr, "Connection sharing not supported for this " "connection type (%s)'\n", backvt->displayname); return 1; } if (backvt->test_for_upstream(conf_get_str(conf, CONF_host), conf_get_int(conf, CONF_port), conf)) return 0; else return 1; } /* * Start up the connection. */ logctx = log_init(console_cli_logpolicy, conf); { char *error, *realhost; /* nodelay is only useful if stdin is a terminal device */ bool nodelay = conf_get_bool(conf, CONF_tcp_nodelay) && isatty(0); /* This is a good place for a fuzzer to fork us. */ #ifdef __AFL_HAVE_MANUAL_CONTROL __AFL_INIT(); #endif error = backend_init(backvt, plink_seat, &backend, logctx, conf, conf_get_str(conf, CONF_host), conf_get_int(conf, CONF_port), &realhost, nodelay, conf_get_bool(conf, CONF_tcp_keepalives)); if (error) { fprintf(stderr, "Unable to open connection:\n%s\n", error); sfree(error); return 1; } ldisc_create(conf, NULL, backend, plink_seat); sfree(realhost); } /* * 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); seat_echoedit_update(plink_seat, 1, 1); cli_main_loop(plink_pw_setup, plink_pw_check, plink_continue, NULL); exitcode = backend_exitcode(backend); if (exitcode < 0) { fprintf(stderr, "Remote process exit code unavailable\n"); exitcode = 1; /* this is an error condition */ } cleanup_exit(exitcode); return exitcode; /* shouldn't happen, but placates gcc */ } putty-0.76/unix/uxpoll.c0000644000175000017500000000762214072266313012241 00000000000000/* On some systems this is needed to get poll.h to define eg.. POLLRDNORM */ #define _XOPEN_SOURCE #include #include "putty.h" #include "tree234.h" struct pollwrapper { struct pollfd *fds; size_t nfd, fdsize; tree234 *fdtopos; }; typedef struct pollwrap_fdtopos pollwrap_fdtopos; struct pollwrap_fdtopos { int fd; size_t pos; }; static int pollwrap_fd_cmp(void *av, void *bv) { pollwrap_fdtopos *a = (pollwrap_fdtopos *)av; pollwrap_fdtopos *b = (pollwrap_fdtopos *)bv; return a->fd < b->fd ? -1 : a->fd > b->fd ? +1 : 0; } pollwrapper *pollwrap_new(void) { pollwrapper *pw = snew(pollwrapper); pw->fdsize = 16; pw->nfd = 0; pw->fds = snewn(pw->fdsize, struct pollfd); pw->fdtopos = newtree234(pollwrap_fd_cmp); return pw; } void pollwrap_free(pollwrapper *pw) { pollwrap_clear(pw); freetree234(pw->fdtopos); sfree(pw->fds); sfree(pw); } void pollwrap_clear(pollwrapper *pw) { pw->nfd = 0; for (pollwrap_fdtopos *f2p; (f2p = delpos234(pw->fdtopos, 0)) != NULL ;) sfree(f2p); } void pollwrap_add_fd_events(pollwrapper *pw, int fd, int events) { pollwrap_fdtopos *f2p, f2p_find; assert(fd >= 0); f2p_find.fd = fd; f2p = find234(pw->fdtopos, &f2p_find, NULL); if (!f2p) { sgrowarray(pw->fds, pw->fdsize, pw->nfd); size_t index = pw->nfd++; pw->fds[index].fd = fd; pw->fds[index].events = pw->fds[index].revents = 0; f2p = snew(pollwrap_fdtopos); f2p->fd = fd; f2p->pos = index; pollwrap_fdtopos *added = add234(pw->fdtopos, f2p); assert(added == f2p); } pw->fds[f2p->pos].events |= events; } /* Omit any of the POLL{RD,WR}{NORM,BAND} flag values that are still * not defined by poll.h, just in case */ #ifndef POLLRDNORM #define POLLRDNORM 0 #endif #ifndef POLLRDBAND #define POLLRDBAND 0 #endif #ifndef POLLWRNORM #define POLLWRNORM 0 #endif #ifndef POLLWRBAND #define POLLWRBAND 0 #endif #define SELECT_R_IN (POLLIN | POLLRDNORM | POLLRDBAND) #define SELECT_W_IN (POLLOUT | POLLWRNORM | POLLWRBAND) #define SELECT_X_IN (POLLPRI) #define SELECT_R_OUT (SELECT_R_IN | POLLERR | POLLHUP) #define SELECT_W_OUT (SELECT_W_IN | POLLERR) #define SELECT_X_OUT (SELECT_X_IN) void pollwrap_add_fd_rwx(pollwrapper *pw, int fd, int rwx) { int events = 0; if (rwx & SELECT_R) events |= SELECT_R_IN; if (rwx & SELECT_W) events |= SELECT_W_IN; if (rwx & SELECT_X) events |= SELECT_X_IN; pollwrap_add_fd_events(pw, fd, events); } int pollwrap_poll_instant(pollwrapper *pw) { return poll(pw->fds, pw->nfd, 0); } int pollwrap_poll_endless(pollwrapper *pw) { return poll(pw->fds, pw->nfd, -1); } int pollwrap_poll_timeout(pollwrapper *pw, int milliseconds) { assert(milliseconds >= 0); return poll(pw->fds, pw->nfd, milliseconds); } static void pollwrap_get_fd_events_revents(pollwrapper *pw, int fd, int *events_p, int *revents_p) { pollwrap_fdtopos *f2p, f2p_find; int events = 0, revents = 0; assert(fd >= 0); f2p_find.fd = fd; f2p = find234(pw->fdtopos, &f2p_find, NULL); if (f2p) { events = pw->fds[f2p->pos].events; revents = pw->fds[f2p->pos].revents; } if (events_p) *events_p = events; if (revents_p) *revents_p = revents; } int pollwrap_get_fd_events(pollwrapper *pw, int fd) { int revents; pollwrap_get_fd_events_revents(pw, fd, NULL, &revents); return revents; } int pollwrap_get_fd_rwx(pollwrapper *pw, int fd) { int events, revents; pollwrap_get_fd_events_revents(pw, fd, &events, &revents); int rwx = 0; if ((events & POLLIN) && (revents & SELECT_R_OUT)) rwx |= SELECT_R; if ((events & POLLOUT) && (revents & SELECT_W_OUT)) rwx |= SELECT_W; if ((events & POLLPRI) && (revents & SELECT_X_OUT)) rwx |= SELECT_X; return rwx; } putty-0.76/unix/uxprint.c0000644000175000017500000000230714072266313012422 00000000000000/* * Printing interface for PuTTY. */ #include #include #include "putty.h" struct printer_job_tag { FILE *fp; }; printer_job *printer_start_job(char *printer) { printer_job *ret = snew(printer_job); /* * On Unix, we treat the printer string as the name of a * command to pipe to - typically lpr, of course. */ ret->fp = popen(printer, "w"); if (!ret->fp) { sfree(ret); ret = NULL; } return ret; } void printer_job_data(printer_job *pj, const void *data, size_t len) { if (!pj) return; if (fwrite(data, 1, len, pj->fp) < len) /* ignore */; } void printer_finish_job(printer_job *pj) { if (!pj) return; pclose(pj->fp); sfree(pj); } /* * There's no sensible way to enumerate printers under Unix, since * practically any valid Unix command is a valid printer :-) So * these are useless stub functions, and uxcfg.c will disable the * drop-down list in the printer configurer. */ printer_enum *printer_start_enum(int *nprinters_ptr) { *nprinters_ptr = 0; return NULL; } char *printer_get_name(printer_enum *pe, int i) { return NULL; } void printer_finish_enum(printer_enum *pe) { } putty-0.76/unix/uxproxy.c0000644000175000017500000000570214072266313012451 00000000000000/* * uxproxy.c: Unix implementation of platform_new_connection(), * supporting an OpenSSH-like proxy command. */ #include #include #include #include #include #include "tree234.h" #include "putty.h" #include "network.h" #include "proxy.h" Socket *platform_new_connection(SockAddr *addr, const char *hostname, int port, bool privport, bool oobinline, bool nodelay, bool keepalive, Plug *plug, Conf *conf) { char *cmd; int to_cmd_pipe[2], from_cmd_pipe[2], cmd_err_pipe[2], pid, proxytype; int infd, outfd, inerrfd; proxytype = conf_get_int(conf, CONF_proxy_type); if (proxytype != PROXY_CMD && proxytype != PROXY_FUZZ) return NULL; if (proxytype == PROXY_CMD) { cmd = format_telnet_command(addr, port, conf); { char *logmsg = dupprintf("Starting local proxy command: %s", cmd); plug_log(plug, PLUGLOG_PROXY_MSG, NULL, 0, logmsg, 0); sfree(logmsg); } /* * Create the pipes to the proxy command, and spawn the proxy * command process. */ if (pipe(to_cmd_pipe) < 0 || pipe(from_cmd_pipe) < 0 || pipe(cmd_err_pipe) < 0) { sfree(cmd); return new_error_socket_fmt(plug, "pipe: %s", strerror(errno)); } cloexec(to_cmd_pipe[1]); cloexec(from_cmd_pipe[0]); cloexec(cmd_err_pipe[0]); pid = fork(); 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]); dup2(cmd_err_pipe[1], 2); noncloexec(0); noncloexec(1); execl("/bin/sh", "sh", "-c", cmd, (void *)NULL); _exit(255); } sfree(cmd); if (pid < 0) return new_error_socket_fmt(plug, "fork: %s", strerror(errno)); close(to_cmd_pipe[0]); close(from_cmd_pipe[1]); close(cmd_err_pipe[1]); outfd = to_cmd_pipe[1]; infd = from_cmd_pipe[0]; inerrfd = cmd_err_pipe[0]; } else { cmd = format_telnet_command(addr, port, conf); outfd = open("/dev/null", O_WRONLY); if (outfd == -1) { sfree(cmd); return new_error_socket_fmt( plug, "/dev/null: %s", strerror(errno)); } infd = open(cmd, O_RDONLY); if (infd == -1) { Socket *toret = new_error_socket_fmt( plug, "%s: %s", cmd, strerror(errno)); sfree(cmd); close(outfd); return toret; } sfree(cmd); inerrfd = -1; } /* We are responsible for this and don't need it any more */ sk_addr_free(addr); return make_fd_socket(infd, outfd, inerrfd, plug); } putty-0.76/unix/uxpsusan.c0000644000175000017500000002757414072266313012614 00000000000000/* * 'psusan': Pseudo Ssh for Untappable, Separately Authenticated Networks * * This is a standalone application that speaks on its standard I/O * (or a listening Unix-domain socket) the server end of the bare * ssh-connection protocol used by PuTTY's connection sharing. * * The idea of this tool is that you can use it to communicate across * any 8-bit-clean data channel between two inconveniently separated * domains, provided the channel is already (as the name suggests) * adequately secured against eavesdropping and modification and * already authenticated as the right user. * * If you're sitting at one end of such a channel and want to type * commands into the other end, the most obvious thing to do is to run * a terminal session directly over it. But if you run psusan at one * end, and a PuTTY (or compatible) client at the other end, then you * not only get a single terminal session: you get all the other SSH * amenities, like the ability to spawn extra terminal sessions, * forward ports or X11 connections, even forward an SSH agent. * * There are a surprising number of channels of that kind; see the man * page for some examples. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "putty.h" #include "mpint.h" #include "ssh.h" #include "sshserver.h" const char *const appname = "psusan"; void modalfatalbox(const char *p, ...) { va_list ap; fprintf(stderr, "FATAL ERROR: "); va_start(ap, p); vfprintf(stderr, p, ap); va_end(ap); fputc('\n', stderr); exit(1); } void nonfatal(const char *p, ...) { va_list ap; fprintf(stderr, "ERROR: "); va_start(ap, p); vfprintf(stderr, p, ap); va_end(ap); fputc('\n', stderr); } char *platform_default_s(const char *name) { return NULL; } bool platform_default_b(const char *name, bool def) { return def; } 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) { return filename_from_str(""); } char *x_get_default(const char *key) { return NULL; /* this is a stub */ } void old_keyfile_warning(void) { } void timer_change_notify(unsigned long next) { } char *platform_get_x_display(void) { return NULL; } void make_unix_sftp_filehandle_key(void *vdata, size_t size) { /* psusan runs without a random number generator, so we can't make * this up by random_read. Fortunately, psusan is also * non-adversarial, so it's safe to generate this trivially. */ unsigned char *data = (unsigned char *)vdata; for (size_t i = 0; i < size; i++) data[i] = (unsigned)rand() / ((unsigned)RAND_MAX / 256); } static bool verbose; struct server_instance { unsigned id; LogPolicy logpolicy; }; static void log_to_stderr(unsigned id, const char *msg) { if (!verbose) return; if (id != (unsigned)-1) fprintf(stderr, "#%u: ", id); fputs(msg, stderr); fputc('\n', stderr); fflush(stderr); } static void server_eventlog(LogPolicy *lp, const char *event) { struct server_instance *inst = container_of( lp, struct server_instance, logpolicy); if (verbose) log_to_stderr(inst->id, event); } static void server_logging_error(LogPolicy *lp, const char *event) { struct server_instance *inst = container_of( lp, struct server_instance, logpolicy); log_to_stderr(inst->id, event); /* unconditional */ } static int server_askappend( LogPolicy *lp, Filename *filename, void (*callback)(void *ctx, int result), void *ctx) { return 2; /* always overwrite (FIXME: could make this a cmdline option) */ } static const LogPolicyVtable server_logpolicy_vt = { .eventlog = server_eventlog, .askappend = server_askappend, .logging_error = server_logging_error, .verbose = null_lp_verbose_no, }; static void show_help(FILE *fp) { fputs("usage: psusan [options]\n" "options: --listen SOCKETPATH listen for connections on a Unix-domain socket\n" " --listen-once (with --listen) stop after one connection\n" " --verbose print log messages to standard error\n" " --sessiondir DIR cwd for session subprocess (default $HOME)\n" " --sshlog FILE write ssh-connection packet log to FILE\n" " --sshrawlog FILE write packets and raw data log to FILE\n" "also: psusan --help show this text\n" " psusan --version show version information\n", fp); } static void show_version_and_exit(void) { char *buildinfo_text = buildinfo("\n"); printf("%s: %s\n%s\n", appname, ver, buildinfo_text); sfree(buildinfo_text); exit(0); } const bool buildinfo_gtk_relevant = false; static bool listening = false, listen_once = false; static bool finished = false; void server_instance_terminated(LogPolicy *lp) { struct server_instance *inst = container_of( lp, struct server_instance, logpolicy); if (listening && !listen_once) { log_to_stderr(inst->id, "connection terminated"); } else { finished = true; } sfree(inst); } bool psusan_continue(void *ctx, bool fd, bool cb) { return !finished; } static bool longoptarg(const char *arg, const char *expected, const char **val, int *argcp, char ***argvp) { int len = strlen(expected); if (memcmp(arg, expected, len)) return false; if (arg[len] == '=') { *val = arg + len + 1; return true; } else if (arg[len] == '\0') { if (--*argcp > 0) { *val = *++*argvp; return true; } else { fprintf(stderr, "%s: option %s expects an argument\n", appname, expected); exit(1); } } return false; } static bool longoptnoarg(const char *arg, const char *expected) { int len = strlen(expected); if (memcmp(arg, expected, len)) return false; if (arg[len] == '=') { fprintf(stderr, "%s: option %s expects no argument\n", appname, expected); exit(1); } else if (arg[len] == '\0') { return true; } return false; } struct server_config { Conf *conf; const SshServerConfig *ssc; unsigned next_id; Socket *listening_socket; Plug listening_plug; }; static Plug *server_conn_plug( struct server_config *cfg, struct server_instance **inst_out) { struct server_instance *inst = snew(struct server_instance); memset(inst, 0, sizeof(*inst)); inst->id = cfg->next_id++; inst->logpolicy.vt = &server_logpolicy_vt; if (inst_out) *inst_out = inst; return ssh_server_plug( cfg->conf, cfg->ssc, NULL, 0, NULL, NULL, &inst->logpolicy, &unix_live_sftpserver_vt); } static void server_log(Plug *plug, PlugLogType type, SockAddr *addr, int port, const char *error_msg, int error_code) { log_to_stderr(-1, error_msg); } static void server_closing(Plug *plug, const char *error_msg, int error_code, bool calling_back) { log_to_stderr(-1, error_msg); } static int server_accepting(Plug *p, accept_fn_t constructor, accept_ctx_t ctx) { struct server_config *cfg = container_of( p, struct server_config, listening_plug); Socket *s; const char *err; struct server_instance *inst; if (listen_once) { if (!cfg->listening_socket) /* in case of rapid double-accept */ return 1; sk_close(cfg->listening_socket); cfg->listening_socket = NULL; } Plug *plug = server_conn_plug(cfg, &inst); s = constructor(ctx, plug); if ((err = sk_socket_error(s)) != NULL) return 1; SocketPeerInfo *pi = sk_peer_info(s); char *msg = dupprintf("new connection from %s", pi->log_text); log_to_stderr(inst->id, msg); sfree(msg); sk_free_peer_info(pi); sk_set_frozen(s, false); ssh_server_start(plug, s); return 0; } static const PlugVtable server_plugvt = { .log = server_log, .closing = server_closing, .accepting = server_accepting, }; unsigned auth_methods(AuthPolicy *ap) { return 0; } bool auth_none(AuthPolicy *ap, ptrlen username) { return false; } int auth_password(AuthPolicy *ap, ptrlen username, ptrlen password, ptrlen *new_password_opt) { return 0; } bool auth_publickey(AuthPolicy *ap, ptrlen username, ptrlen public_blob) { return false; } RSAKey *auth_publickey_ssh1( AuthPolicy *ap, ptrlen username, mp_int *rsa_modulus) { return NULL; } AuthKbdInt *auth_kbdint_prompts(AuthPolicy *ap, ptrlen username) { return NULL; } int auth_kbdint_responses(AuthPolicy *ap, const ptrlen *responses) { return -1; } char *auth_ssh1int_challenge(AuthPolicy *ap, unsigned method, ptrlen username) { return NULL; } bool auth_ssh1int_response(AuthPolicy *ap, ptrlen response) { return false; } bool auth_successful(AuthPolicy *ap, ptrlen username, unsigned method) { return false; } int main(int argc, char **argv) { const char *listen_socket = NULL; SshServerConfig ssc; Conf *conf = make_ssh_server_conf(); memset(&ssc, 0, sizeof(ssc)); ssc.application_name = "PSUSAN"; ssc.session_starting_dir = getenv("HOME"); ssc.bare_connection = true; while (--argc > 0) { const char *arg = *++argv; const char *val; if (longoptnoarg(arg, "--help")) { show_help(stdout); exit(0); } else if (longoptnoarg(arg, "--version")) { show_version_and_exit(); } else if (longoptnoarg(arg, "--verbose") || !strcmp(arg, "-v")) { verbose = true; } else if (longoptarg(arg, "--sessiondir", &val, &argc, &argv)) { ssc.session_starting_dir = val; } else if (longoptarg(arg, "--sshlog", &val, &argc, &argv) || longoptarg(arg, "-sshlog", &val, &argc, &argv)) { Filename *logfile = filename_from_str(val); conf_set_filename(conf, CONF_logfilename, logfile); filename_free(logfile); conf_set_int(conf, CONF_logtype, LGTYP_PACKETS); conf_set_int(conf, CONF_logxfovr, LGXF_OVR); } else if (longoptarg(arg, "--sshrawlog", &val, &argc, &argv) || longoptarg(arg, "-sshrawlog", &val, &argc, &argv)) { Filename *logfile = filename_from_str(val); conf_set_filename(conf, CONF_logfilename, logfile); filename_free(logfile); conf_set_int(conf, CONF_logtype, LGTYP_SSHRAW); conf_set_int(conf, CONF_logxfovr, LGXF_OVR); } else if (longoptarg(arg, "--listen", &val, &argc, &argv)) { listen_socket = val; } else if (!strcmp(arg, "--listen-once")) { listen_once = true; } else { fprintf(stderr, "%s: unrecognised option '%s'\n", appname, arg); exit(1); } } sk_init(); uxsel_init(); struct server_config scfg; scfg.conf = conf; scfg.ssc = &ssc; scfg.next_id = 0; if (listen_socket) { listening = true; scfg.listening_plug.vt = &server_plugvt; SockAddr *addr = unix_sock_addr(listen_socket); scfg.listening_socket = new_unix_listener(addr, &scfg.listening_plug); char *msg = dupprintf("listening on Unix socket %s", listen_socket); log_to_stderr(-1, msg); sfree(msg); } else { struct server_instance *inst; Plug *plug = server_conn_plug(&scfg, &inst); ssh_server_start(plug, make_fd_socket(0, 1, -1, plug)); log_to_stderr(inst->id, "running directly on stdio"); } cli_main_loop(cliloop_no_pw_setup, cliloop_no_pw_check, psusan_continue, NULL); return 0; } putty-0.76/unix/uxpterm.c0000644000175000017500000000236514072266313012421 00000000000000/* * pterm main program. */ #include #include #include "putty.h" const char *const appname = "pterm"; const bool use_event_log = false; /* pterm doesn't need it */ const bool new_session = false, saved_sessions = false; /* or these */ const bool dup_check_launchable = false; /* no need to check host name * in conf */ const bool use_pty_argv = true; const unsigned cmdline_tooltype = TOOLTYPE_NONNETWORK; /* gtkwin.c will call this, and in pterm it's not needed */ void noise_ultralight(NoiseSourceId id, unsigned long data) { } const struct BackendVtable *select_backend(Conf *conf) { return &pty_backend; } void initial_config_box(Conf *conf, post_dialog_fn_t after, void *afterctx) { /* * 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. So we do that and then just immediately call the * post-dialog function with a positive result. */ conf_set_int(conf, CONF_protocol, -1); after(afterctx, 1); } void cleanup_exit(int code) { exit(code); } void setup(bool single) { settings_set_default_protocol(-1); if (single) pty_pre_init(); } putty-0.76/unix/uxpty.c0000644000175000017500000013514014072266313012104 00000000000000/* * Pseudo-tty backend for pterm. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "putty.h" #include "ssh.h" #include "sshserver.h" /* to check the prototypes of server-needed things */ #include "tree234.h" #ifndef OMIT_UTMP #include #endif /* updwtmpx() needs the name of the wtmp file. Try to find it. */ #ifndef WTMPX_FILE #ifdef _PATH_WTMPX #define WTMPX_FILE _PATH_WTMPX #else #define WTMPX_FILE "/var/log/wtmpx" #endif #endif #ifndef LASTLOG_FILE #ifdef _PATH_LASTLOG #define LASTLOG_FILE _PATH_LASTLOG #else #define LASTLOG_FILE "/var/log/lastlog" #endif #endif /* * Set up a default for vaguely sane systems. The idea is that if * OMIT_UTMP is not defined, then at least one of the symbols which * enable particular forms of utmp processing should be, if only so * that a link error can warn you that you should have defined * OMIT_UTMP if you didn't want any. Currently HAVE_PUTUTLINE is * the only such symbol. */ #ifndef OMIT_UTMP #if !defined HAVE_PUTUTLINE #define HAVE_PUTUTLINE #endif #endif typedef struct Pty Pty; /* * 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 */ typedef struct PtyFd { int fd; Pty *pty; } PtyFd; struct Pty { Conf *conf; int master_fd, slave_fd; int pipefds[6]; PtyFd fds[3]; int master_i, master_o, master_e; Seat *seat; char name[FILENAME_MAX]; pid_t child_pid; int term_width, term_height; bool child_dead, finished; int exit_code; bufchain output_data; bool pending_eof; Backend backend; }; /* * We store all the (active) PtyFd structures in a tree sorted by fd, * so that when we get an uxsel notification we know which backend * instance is the owner of the pty that caused it, and then we can * find out which fd is the relevant one too. */ static int ptyfd_compare(void *av, void *bv) { PtyFd *a = (PtyFd *)av; PtyFd *b = (PtyFd *)bv; if (a->fd < b->fd) return -1; else if (a->fd > b->fd) return +1; return 0; } static int ptyfd_find(void *av, void *bv) { int a = *(int *)av; PtyFd *b = (PtyFd *)bv; if (a < b->fd) return -1; else if (a > b->fd) return +1; return 0; } static tree234 *ptyfds = NULL; /* * We also have a tree of Pty structures themselves, sorted by child * pid, so that when we wait() in response to the signal we know which * backend instance is the owner of the process that caused the * signal. */ static int pty_compare_by_pid(void *av, void *bv) { Pty *a = (Pty *)av; Pty *b = (Pty *)bv; if (a->child_pid < b->child_pid) return -1; else if (a->child_pid > b->child_pid) return +1; return 0; } static int pty_find_by_pid(void *av, void *bv) { pid_t a = *(pid_t *)av; Pty *b = (Pty *)bv; if (a < b->child_pid) return -1; else if (a > b->child_pid) return +1; return 0; } static tree234 *ptys_by_pid = NULL; /* * If we are using pty_pre_init(), it will need to have already * allocated a pty structure, which we must then return from * pty_init() rather than allocating a new one. Here we store that * structure between allocation and use. * * Note that although most of this module is entirely capable of * handling multiple ptys in a single process, pty_pre_init() is * fundamentally _dependent_ on there being at most one pty per * process, so the normal static-data constraints don't apply. * * 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 bool 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 * make sense outside a one-pty-per-process setup. */ char **pty_argv; char *pty_osx_envrestore_prefix; static void pty_close(Pty *pty); static void pty_try_write(Pty *pty); #ifndef OMIT_UTMP static void setup_utmp(char *ttyname, char *location) { #ifdef HAVE_LASTLOG struct lastlog lastlog_entry; FILE *lastlog; #endif struct passwd *pw; struct timeval tv; pw = getpwuid(getuid()); if (!pw) return; /* can't stamp utmp if we don't have a username */ memset(&utmp_entry, 0, sizeof(utmp_entry)); utmp_entry.ut_type = USER_PROCESS; utmp_entry.ut_pid = getpid(); #if __GNUC__ >= 8 # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wstringop-truncation" #endif // __GNUC__ >= 8 strncpy(utmp_entry.ut_line, ttyname+5, lenof(utmp_entry.ut_line)); strncpy(utmp_entry.ut_id, ttyname+8, lenof(utmp_entry.ut_id)); strncpy(utmp_entry.ut_user, pw->pw_name, lenof(utmp_entry.ut_user)); strncpy(utmp_entry.ut_host, location, lenof(utmp_entry.ut_host)); #if __GNUC__ >= 8 # pragma GCC diagnostic pop #endif // __GNUC__ >= 8 /* * Apparently there are some architectures where (struct * utmpx).ut_tv is not essentially struct timeval (e.g. Linux * amd64). Hence the temporary. */ gettimeofday(&tv, NULL); utmp_entry.ut_tv.tv_sec = tv.tv_sec; utmp_entry.ut_tv.tv_usec = tv.tv_usec; setutxent(); pututxline(&utmp_entry); endutxent(); updwtmpx(WTMPX_FILE, &utmp_entry); #ifdef HAVE_LASTLOG memset(&lastlog_entry, 0, sizeof(lastlog_entry)); strncpy(lastlog_entry.ll_line, ttyname+5, lenof(lastlog_entry.ll_line)); strncpy(lastlog_entry.ll_host, location, lenof(lastlog_entry.ll_host)); time(&lastlog_entry.ll_time); if ((lastlog = fopen(LASTLOG_FILE, "r+")) != NULL) { fseek(lastlog, sizeof(lastlog_entry) * getuid(), SEEK_SET); fwrite(&lastlog_entry, 1, sizeof(lastlog_entry), lastlog); fclose(lastlog); } #endif pty_stamped_utmp = true; } static void cleanup_utmp(void) { struct timeval tv; if (!pty_stamped_utmp) return; utmp_entry.ut_type = DEAD_PROCESS; memset(utmp_entry.ut_user, 0, lenof(utmp_entry.ut_user)); gettimeofday(&tv, NULL); utmp_entry.ut_tv.tv_sec = tv.tv_sec; utmp_entry.ut_tv.tv_usec = tv.tv_usec; updwtmpx(WTMPX_FILE, &utmp_entry); memset(utmp_entry.ut_line, 0, lenof(utmp_entry.ut_line)); utmp_entry.ut_tv.tv_sec = 0; utmp_entry.ut_tv.tv_usec = 0; setutxent(); pututxline(&utmp_entry); endutxent(); pty_stamped_utmp = false; /* ensure we never double-cleanup */ } #endif static void sigchld_handler(int signum) { if (write(pty_signal_pipe[1], "x", 1) <= 0) /* not much we can do about it */; } static void pty_setup_sigchld_handler(void) { static bool setup = false; if (!setup) { putty_signal(SIGCHLD, sigchld_handler); setup = true; } } #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) { pty->slave_fd = open(pty->name, O_RDWR); cloexec(pty->slave_fd); } return pty->slave_fd; } static void pty_open_master(Pty *pty) { #ifdef BSD_PTYS const char chars1[] = "pqrstuvwxyz"; const char chars2[] = "0123456789abcdef"; const char *p1, *p2; char master_name[20]; struct group *gp; for (p1 = chars1; *p1; p1++) for (p2 = chars2; *p2; p2++) { sprintf(master_name, "/dev/pty%c%c", *p1, *p2); pty->master_fd = open(master_name, O_RDWR); if (pty->master_fd >= 0) { if (geteuid() == 0 || access(master_name, R_OK | W_OK) == 0) { /* * We must also check at this point that we are * able to open the slave side of the pty. We * wouldn't want to allocate the wrong master, * get all the way down to forking, and _then_ * find we're unable to open the slave. */ strcpy(pty->name, master_name); pty->name[5] = 't'; /* /dev/ptyXX -> /dev/ttyXX */ cloexec(pty->master_fd); if (pty_open_slave(pty) >= 0 && access(pty->name, R_OK | W_OK) == 0) goto got_one; if (pty->slave_fd > 0) close(pty->slave_fd); pty->slave_fd = -1; } close(pty->master_fd); } } /* If we get here, we couldn't get a tty at all. */ fprintf(stderr, "pterm: unable to open a pseudo-terminal device\n"); exit(1); 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 #ifdef SET_NONBLOCK_VIA_OPENPT /* * OS X, as of 10.10 at least, doesn't permit me to set O_NONBLOCK * on pty master fds via the usual fcntl mechanism. Fortunately, * it does let me work around this by adding O_NONBLOCK to the * posix_openpt flags parameter, which isn't a documented use of * the API but seems to work. So we'll do that for now. */ pty->master_fd = posix_openpt(flags | O_NONBLOCK); #else pty->master_fd = posix_openpt(flags); #endif 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 #ifndef SET_NONBLOCK_VIA_OPENPT nonblock(pty->master_fd); #endif } static Pty *new_pty_struct(void) { Pty *pty = snew(Pty); pty->conf = NULL; pty->pending_eof = false; bufchain_init(&pty->output_data); return pty; } /* * Pre-initialisation. This is here to get around the fact that GTK * doesn't like being run in setuid/setgid programs (probably * sensibly). So before we initialise GTK - and therefore before we * even process the command line - we check to see if we're running * set[ug]id. If so, we open our pty master _now_, chown it as * necessary, and drop privileges. We can always close it again * later. If we're potentially going to be doing utmp as well, we * also fork off a utmp helper process and communicate with it by * means of a pipe; the utmp helper will keep privileges in order * to clean up utmp when we exit (i.e. when its end of our pipe * closes). */ void pty_pre_init(void) { #ifndef NO_PTY_PRE_INIT Pty *pty; #ifndef OMIT_UTMP pid_t pid; int pipefd[2]; #endif pty = single_pty = new_pty_struct(); /* set the child signal handler straight away; it needs to be set * before we ever fork. */ pty_setup_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 } #endif /* NO_PTY_PRE_INIT */ } static void pty_try_wait(void); static void pty_real_select_result(Pty *pty, int fd, int event, int status) { char buf[4096]; int ret; bool finished = false; if (event < 0) { /* * We've been called because our child process did * something. `status' tells us what. */ if ((WIFEXITED(status) || WIFSIGNALED(status))) { /* * The primary child process died. */ pty->child_dead = true; del234(ptys_by_pid, pty); pty->exit_code = status; /* * If this is an ordinary pty session, this is also the * moment to terminate the whole backend. * * We _could_ instead keep the terminal open for remaining * subprocesses to output to, but conventional wisdom * seems to feel that that's the Wrong Thing for an * xterm-alike, so we bail out now (though we don't * necessarily _close_ the window, depending on the state * of Close On Exit). This would be easy enough to change * or make configurable if necessary. */ if (pty->master_fd >= 0) finished = true; } } else { if (event == SELECT_R) { bool is_stdout = (fd == pty->master_o); ret = read(fd, buf, sizeof(buf)); /* * Treat EIO on a pty master as equivalent to EOF (because * that's how the kernel seems to report the event where * the last process connected to the other end of the pty * went away). */ if (fd == pty->master_fd && ret < 0 && errno == EIO) ret = 0; if (ret == 0) { /* * EOF on this input fd, so to begin with, we may as * well close it, and remove all references to it in * the pty's fd fields. */ uxsel_del(fd); close(fd); if (pty->master_fd == fd) pty->master_fd = -1; if (pty->master_o == fd) pty->master_o = -1; if (pty->master_e == fd) pty->master_e = -1; if (is_stdout) { /* * We assume a clean exit if the pty (or stdout * pipe) has closed, but the actual child process * hasn't. The only way I can imagine this * happening is if it detaches itself from the pty * and goes daemonic - in which case the expected * usage model would precisely _not_ be for the * pterm window to hang around! */ finished = true; pty_try_wait(); /* one last effort to collect exit code */ if (!pty->child_dead) pty->exit_code = 0; } } else if (ret < 0) { perror("read pty master"); exit(1); } else if (ret > 0) { seat_output(pty->seat, !is_stdout, buf, ret); } } else if (event == SELECT_W) { /* * Attempt to send data down the pty. */ pty_try_write(pty); } } if (finished && !pty->finished) { int close_on_exit; int i; for (i = 0; i < 3; i++) if (pty->fds[i].fd >= 0) uxsel_del(pty->fds[i].fd); pty_close(pty); 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; if (WIFEXITED(pty->exit_code)) { message = dupprintf( "\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 message = dupprintf( "\r\n[pterm: process terminated on signal %d]\r\n", WTERMSIG(pty->exit_code)); #else message = dupprintf( "\r\n[pterm: process terminated on signal %d (%s)]\r\n", WTERMSIG(pty->exit_code), strsignal(WTERMSIG(pty->exit_code))); #endif } else { /* _Shouldn't_ happen, but if it does, a vague message * is better than no message at all */ message = dupprintf("\r\n[pterm: process terminated]\r\n"); } seat_stdout_pl(pty->seat, ptrlen_from_asciz(message)); sfree(message); } seat_eof(pty->seat); seat_notify_remote_exit(pty->seat); } } static void pty_try_wait(void) { Pty *pty; pid_t pid; int status; do { pid = waitpid(-1, &status, WNOHANG); pty = find234(ptys_by_pid, &pid, pty_find_by_pid); if (pty) pty_real_select_result(pty, -1, -1, status); } while (pid > 0); } void pty_select_result(int fd, int event) { if (fd == pty_signal_pipe[0]) { char c[1]; if (read(pty_signal_pipe[0], c, 1) <= 0) /* ignore error */; /* ignore its value; it'll be `x' */ pty_try_wait(); } else { PtyFd *ptyfd = find234(ptyfds, &fd, ptyfd_find); if (ptyfd) pty_real_select_result(ptyfd->pty, fd, event, 0); } } static void pty_uxsel_setup_fd(Pty *pty, int fd) { int rwx = 0; if (fd < 0) return; /* read from standard output and standard error pipes */ if (pty->master_o == fd || pty->master_e == fd) rwx |= SELECT_R; /* write to standard input pipe if we have any data */ if (pty->master_i == fd && bufchain_size(&pty->output_data)) rwx |= SELECT_W; uxsel_set(fd, rwx, pty_select_result); } static void pty_uxsel_setup(Pty *pty) { /* * We potentially have three separate fds here, but on the other * hand, some of them might be the same (if they're a pty master). * So we can't just call uxsel_set(master_o, SELECT_R) and then * uxsel_set(master_i, SELECT_W), without the latter potentially * undoing the work of the former if master_o == master_i. * * Instead, here we call a single uxsel on each one of these fds * (if it exists at all), and for each one, check it against all * three to see which bits to set. */ pty_uxsel_setup_fd(pty, pty->master_o); pty_uxsel_setup_fd(pty, pty->master_e); pty_uxsel_setup_fd(pty, pty->master_i); /* * In principle this only needs calling once for all pty * backend instances, but it's simplest just to call it every * time; uxsel won't mind. */ uxsel_set(pty_signal_pipe[0], SELECT_R, pty_select_result); } static void copy_ttymodes_into_termios( struct termios *attrs, struct ssh_ttymodes modes) { #define TTYMODE_CHAR(name, ssh_opcode, cc_index) { \ if (modes.have_mode[ssh_opcode]) { \ unsigned value = modes.mode_val[ssh_opcode]; \ /* normalise wire value of 255 to local _POSIX_VDISABLE */ \ attrs->c_cc[cc_index] = (value == 255 ? \ _POSIX_VDISABLE : value); \ } \ } #define TTYMODE_FLAG(flagval, ssh_opcode, field, flagmask) { \ if (modes.have_mode[ssh_opcode]) { \ attrs->c_##field##flag &= ~flagmask; \ if (modes.mode_val[ssh_opcode]) \ attrs->c_##field##flag |= flagval; \ } \ } #define TTYMODES_LOCAL_ONLY /* omit any that this platform doesn't know */ #include "sshttymodes.h" #undef TTYMODES_LOCAL_ONLY #undef TTYMODE_CHAR #undef TTYMODE_FLAG if (modes.have_mode[TTYMODE_ISPEED]) cfsetispeed(attrs, modes.mode_val[TTYMODE_ISPEED]); if (modes.have_mode[TTYMODE_OSPEED]) cfsetospeed(attrs, modes.mode_val[TTYMODE_OSPEED]); } /* * The main setup function for the pty back end. This doesn't match * the signature of backend_init(), partly because it has to be able * to take extra arguments such as an argv array, and also because * once we're changing the type signature _anyway_ we can discard the * stuff that's not really applicable to this backend like host names * and port numbers. */ Backend *pty_backend_create( Seat *seat, LogContext *logctx, Conf *conf, char **argv, const char *cmd, struct ssh_ttymodes ttymodes, bool pipes_instead, const char *dir, const char *const *env_vars_to_unset) { int slavefd; pid_t pid, pgrp; #ifndef NOT_X_WINDOWS /* for Mac OS X native compilation */ bool got_windowid; long windowid; #endif Pty *pty; int i; /* No local authentication phase in this protocol */ seat_set_trust_status(seat, false); if (single_pty) { pty = single_pty; assert(pty->conf == NULL); } else { pty = new_pty_struct(); pty->master_fd = pty->slave_fd = -1; #ifndef OMIT_UTMP pty_stamped_utmp = false; #endif } for (i = 0; i < 6; i++) pty->pipefds[i] = -1; for (i = 0; i < 3; i++) { pty->fds[i].fd = -1; pty->fds[i].pty = pty; } if (pty_signal_pipe[0] < 0) { if (pipe(pty_signal_pipe) < 0) { perror("pipe"); exit(1); } cloexec(pty_signal_pipe[0]); cloexec(pty_signal_pipe[1]); } pty->seat = seat; pty->backend.vt = &pty_backend; pty->conf = conf_copy(conf); pty->term_width = conf_get_int(conf, CONF_width); pty->term_height = conf_get_int(conf, CONF_height); if (!ptyfds) ptyfds = newtree234(ptyfd_compare); if (pipes_instead) { if (pty->master_fd >= 0) { /* If somehow we've got a pty master already and don't * need it, throw it away! */ close(pty->master_fd); #ifndef OMIT_UTMP if (pty_utmp_helper_pipe >= 0) { close(pty_utmp_helper_pipe); /* don't need this either */ pty_utmp_helper_pipe = -1; } #endif } for (i = 0; i < 6; i += 2) { if (pipe(pty->pipefds + i) < 0) { backend_free(&pty->backend); return NULL; } } pty->fds[0].fd = pty->master_i = pty->pipefds[1]; pty->fds[1].fd = pty->master_o = pty->pipefds[2]; pty->fds[2].fd = pty->master_e = pty->pipefds[4]; add234(ptyfds, &pty->fds[0]); add234(ptyfds, &pty->fds[1]); add234(ptyfds, &pty->fds[2]); } else { if (pty->master_fd < 0) pty_open_master(pty); #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_bool(conf, CONF_stamp_utmp)) { /* We're not stamping utmp, so just let the child * process die that was waiting to unstamp it later. */ close(pty_utmp_helper_pipe); pty_utmp_helper_pipe = -1; } else { const char *location = seat_get_x_display(pty->seat); 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 pty->master_i = pty->master_fd; pty->master_o = pty->master_fd; pty->master_e = -1; pty->fds[0].fd = pty->master_fd; add234(ptyfds, &pty->fds[0]); } #ifndef NOT_X_WINDOWS /* for Mac OS X native compilation */ got_windowid = seat_get_windowid(pty->seat, &windowid); #endif /* * Set up the signal handler to catch SIGCHLD, if pty_pre_init * didn't already do it. */ pty_setup_sigchld_handler(); /* * Fork and execute the command. */ pid = fork(); if (pid < 0) { perror("fork"); exit(1); } if (pid == 0) { struct termios attrs; /* * We are the child. */ if (pty_osx_envrestore_prefix) { int plen = strlen(pty_osx_envrestore_prefix); extern char **environ; char **ep; restart_osx_env_restore: for (ep = environ; *ep; ep++) { char *e = *ep; if (!strncmp(e, pty_osx_envrestore_prefix, plen)) { bool unset = (e[plen] == 'u'); char *pname = dupprintf("%.*s", (int)strcspn(e, "="), e); char *name = pname + plen + 1; char *value = e + strcspn(e, "="); if (*value) value++; value = dupstr(value); if (unset) unsetenv(name); else setenv(name, value, 1); unsetenv(pname); sfree(pname); sfree(value); goto restart_osx_env_restore; } } } pgrp = getpid(); if (pipes_instead) { int i; dup2(pty->pipefds[0], 0); dup2(pty->pipefds[3], 1); dup2(pty->pipefds[5], 2); for (i = 0; i < 6; i++) close(pty->pipefds[i]); setsid(); } else { 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 tcsetpgrp(0, pgrp); /* * Set up configuration-dependent termios settings on the new * pty. Linux would have let us do this on the pty master * before we forked, but that fails on OS X, so we do it here * instead. */ if (tcgetattr(0, &attrs) == 0) { /* * Set the backspace character to be whichever of ^H and * ^? is specified by bksp_is_delete. */ attrs.c_cc[VERASE] = conf_get_bool(conf, CONF_bksp_is_delete) ? '\177' : '\010'; /* * Set the IUTF8 bit iff the character set is UTF-8. */ #ifdef IUTF8 if (seat_is_utf8(seat)) attrs.c_iflag |= IUTF8; else attrs.c_iflag &= ~IUTF8; #endif copy_ttymodes_into_termios(&attrs, ttymodes); tcsetattr(0, TCSANOW, &attrs); } } setpgid(pgrp, pgrp); if (!pipes_instead) { int ptyfd = open(pty->name, O_WRONLY, 0); if (ptyfd >= 0) close(ptyfd); } setpgid(pgrp, pgrp); if (env_vars_to_unset) for (const char *const *p = env_vars_to_unset; *p; p++) unsetenv(*p); if (!pipes_instead) { 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 */ if (got_windowid) { 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. */ } { /* * In case we were invoked with a --display argument that * doesn't match DISPLAY in our actual environment, we * should set DISPLAY for processes running inside the * terminal to match the display the terminal itself is * on. */ const char *x_display = seat_get_x_display(pty->seat); if (x_display) { char *x_display_env_var = dupprintf("DISPLAY=%s", x_display); putenv(x_display_env_var); /* As above, we don't free this. */ } else { unsetenv("DISPLAY"); } } #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); 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. */ } } if (dir) { if (chdir(dir) < 0) { /* Ignore the error - nothing we can sensibly do about it, * and our existing cwd is as good a fallback as any. */ } } /* * SIGINT, SIGQUIT and SIGPIPE may have been set to ignored by * our parent, particularly by things like sh -c 'pterm &' and * some window or session managers. SIGPIPE was also * (potentially) blocked by us during 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(SIGPIPE, false); if (argv || cmd) { /* * If we were given a separated argument list, try to exec * it. */ if (argv) { execvp(argv[0], argv); } /* * Otherwise, if we were given a single command string, * try passing that to $SHELL -c. * * In the case of pterm, this system of fallbacks 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). We arrange this by setting argv to the full * argument list, and also setting cmd to the single * element of argv if it's a length-1 list. * * 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'. * * In other use cases, a caller can set only one of argv * and cmd to get a fixed handling of the input. */ if (cmd) { char *shell = getenv("SHELL"); if (shell) execl(shell, shell, "-c", cmd, (void *)NULL); } } else { const char *shell = getenv("SHELL"); if (!shell) shell = "/bin/sh"; char *shellname; if (conf_get_bool(conf, CONF_login_shell)) { const char *p = strrchr(shell, '/'); shellname = snewn(2+strlen(shell), char); p = p ? p+1 : shell; sprintf(shellname, "-%s", p); } else shellname = (char *)shell; execl(shell, shellname, (void *)NULL); } /* * If we're here, exec has gone badly foom. */ perror("exec"); _exit(127); } else { pty->child_pid = pid; pty->child_dead = false; pty->finished = false; if (pty->slave_fd > 0) close(pty->slave_fd); if (!ptys_by_pid) ptys_by_pid = newtree234(pty_compare_by_pid); if (pty->pipefds[0] >= 0) { close(pty->pipefds[0]); pty->pipefds[0] = -1; } if (pty->pipefds[3] >= 0) { close(pty->pipefds[3]); pty->pipefds[3] = -1; } if (pty->pipefds[5] >= 0) { close(pty->pipefds[5]); pty->pipefds[5] = -1; } add234(ptys_by_pid, pty); } pty_uxsel_setup(pty); return &pty->backend; } /* * This is the pty backend's _official_ init method, for BackendVtable * purposes. Its job is just to be an API converter, ignoring the * irrelevant input parameters and making up auxiliary outputs. Also * it gets the argv array from the global variable pty_argv, expecting * that it will have been invoked by pterm. */ static char *pty_init(const BackendVtable *vt, Seat *seat, Backend **backend_handle, LogContext *logctx, Conf *conf, const char *host, int port, char **realhost, bool nodelay, bool keepalive) { const char *cmd = NULL; struct ssh_ttymodes modes; memset(&modes, 0, sizeof(modes)); if (pty_argv && pty_argv[0] && !pty_argv[1]) cmd = pty_argv[0]; assert(vt == &pty_backend); *backend_handle = pty_backend_create( seat, logctx, conf, pty_argv, cmd, modes, false, NULL, NULL); *realhost = dupstr(""); return NULL; } static void pty_reconfig(Backend *be, Conf *conf) { Pty *pty = container_of(be, Pty, backend); /* * 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(Backend *be) { Pty *pty = container_of(be, Pty, backend); int i; pty_close(pty); /* Either of these may fail `not found'. That's fine with us. */ del234(ptys_by_pid, pty); for (i = 0; i < 3; i++) if (pty->fds[i].fd >= 0) del234(ptyfds, &pty->fds[i]); bufchain_clear(&pty->output_data); 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) { ssize_t ret; assert(pty->master_i >= 0); while (bufchain_size(&pty->output_data) > 0) { ptrlen data = bufchain_prefix(&pty->output_data); ret = write(pty->master_i, data.ptr, data.len); if (ret < 0 && (errno == EWOULDBLOCK)) { /* * We've sent all we can for the moment. */ break; } if (ret < 0) { perror("write pty master"); exit(1); } bufchain_consume(&pty->output_data, ret); } if (pty->pending_eof && bufchain_size(&pty->output_data) == 0) { /* This should only happen if pty->master_i is a pipe that * doesn't alias either output fd */ assert(pty->master_i != pty->master_o); assert(pty->master_i != pty->master_e); uxsel_del(pty->master_i); close(pty->master_i); pty->master_i = -1; pty->pending_eof = false; } pty_uxsel_setup(pty); } /* * Called to send data down the pty. */ static size_t pty_send(Backend *be, const char *buf, size_t len) { Pty *pty = container_of(be, Pty, backend); if (pty->master_i < 0 || pty->pending_eof) return 0; /* ignore all writes if fd closed */ bufchain_add(&pty->output_data, buf, len); pty_try_write(pty); return bufchain_size(&pty->output_data); } static void pty_close(Pty *pty) { int i; if (pty->master_o >= 0) uxsel_del(pty->master_o); if (pty->master_e >= 0) uxsel_del(pty->master_e); if (pty->master_i >= 0) uxsel_del(pty->master_i); if (pty->master_fd >= 0) { close(pty->master_fd); pty->master_fd = -1; } for (i = 0; i < 6; i++) { if (pty->pipefds[i] >= 0) close(pty->pipefds[i]); pty->pipefds[i] = -1; } pty->master_i = pty->master_o = pty->master_e = -1; #ifndef OMIT_UTMP if (pty_utmp_helper_pipe >= 0) { close(pty_utmp_helper_pipe); /* this causes utmp to be cleaned up */ pty_utmp_helper_pipe = -1; } #endif } /* * Called to query the current socket sendability status. */ static size_t pty_sendbuffer(Backend *be) { /* Pty *pty = container_of(be, Pty, backend); */ return 0; } /* * Called to set the size of the window */ static void pty_size(Backend *be, int width, int height) { Pty *pty = container_of(be, Pty, backend); struct winsize size; int xpixel = 0, ypixel = 0; pty->term_width = width; pty->term_height = height; if (pty->master_fd < 0) return; seat_get_window_pixel_size(pty->seat, &xpixel, &ypixel); size.ws_row = (unsigned short)pty->term_height; size.ws_col = (unsigned short)pty->term_width; size.ws_xpixel = (unsigned short)xpixel; size.ws_ypixel = (unsigned short)ypixel; ioctl(pty->master_fd, TIOCSWINSZ, (void *)&size); return; } /* * Send special codes. */ static void pty_special(Backend *be, SessionSpecialCode code, int arg) { Pty *pty = container_of(be, Pty, backend); if (code == SS_BRK) { if (pty->master_fd >= 0) tcsendbreak(pty->master_fd, 0); return; } if (code == SS_EOF) { if (pty->master_i >= 0 && pty->master_i != pty->master_fd) { pty->pending_eof = true; pty_try_write(pty); } return; } { int sig = -1; #define SIGNAL_SUB(name) if (code == SS_SIG ## name) sig = SIG ## name; #define SIGNAL_MAIN(name, text) SIGNAL_SUB(name) #define SIGNALS_LOCAL_ONLY #include "sshsignals.h" #undef SIGNAL_SUB #undef SIGNAL_MAIN #undef SIGNALS_LOCAL_ONLY if (sig != -1) { if (!pty->child_dead) kill(pty->child_pid, sig); return; } } return; } /* * Return a list of the special codes that make sense in this * protocol. */ static const SessionSpecial *pty_get_specials(Backend *be) { /* Pty *pty = container_of(be, Pty, backend); */ /* * Hmm. When I get round to having this actually usable, it * might be quite nice to have the ability to deliver a few * well chosen signals to the child process - SIGINT, SIGTERM, * SIGKILL at least. */ return NULL; } static bool pty_connected(Backend *be) { /* Pty *pty = container_of(be, Pty, backend); */ return true; } static bool pty_sendok(Backend *be) { /* Pty *pty = container_of(be, Pty, backend); */ return true; } static void pty_unthrottle(Backend *be, size_t backlog) { /* Pty *pty = container_of(be, Pty, backend); */ /* do nothing */ } static bool pty_ldisc(Backend *be, int option) { /* Pty *pty = container_of(be, Pty, backend); */ return false; /* neither editing nor echoing */ } static void pty_provide_ldisc(Backend *be, Ldisc *ldisc) { /* Pty *pty = container_of(be, Pty, backend); */ /* This is a stub. */ } static int pty_exitcode(Backend *be) { Pty *pty = container_of(be, Pty, backend); if (!pty->finished) return -1; /* not dead yet */ else if (WIFSIGNALED(pty->exit_code)) return 128 + WTERMSIG(pty->exit_code); else return WEXITSTATUS(pty->exit_code); } int pty_backend_exit_signum(Backend *be) { Pty *pty = container_of(be, Pty, backend); if (!pty->finished || !WIFSIGNALED(pty->exit_code)) return -1; return WTERMSIG(pty->exit_code); } ptrlen pty_backend_exit_signame(Backend *be, char **aux_msg) { *aux_msg = NULL; int sig = pty_backend_exit_signum(be); if (sig < 0) return PTRLEN_LITERAL(""); #define SIGNAL_SUB(s) { \ if (sig == SIG ## s) \ return PTRLEN_LITERAL(#s); \ } #define SIGNAL_MAIN(s, desc) SIGNAL_SUB(s) #define SIGNALS_LOCAL_ONLY #include "sshsignals.h" #undef SIGNAL_MAIN #undef SIGNAL_SUB #undef SIGNALS_LOCAL_ONLY *aux_msg = dupprintf("untranslatable signal number %d: %s", sig, strsignal(sig)); return PTRLEN_LITERAL("HUP"); /* need some kind of default */ } static int pty_cfg_info(Backend *be) { /* Pty *pty = container_of(be, Pty, backend); */ return 0; } const BackendVtable pty_backend = { .init = pty_init, .free = pty_free, .reconfig = pty_reconfig, .send = pty_send, .sendbuffer = pty_sendbuffer, .size = pty_size, .special = pty_special, .get_specials = pty_get_specials, .connected = pty_connected, .exitcode = pty_exitcode, .sendok = pty_sendok, .ldisc_option_state = pty_ldisc, .provide_ldisc = pty_provide_ldisc, .unthrottle = pty_unthrottle, .cfg_info = pty_cfg_info, .id = "pty", .displayname = "pty", .protocol = -1, }; putty-0.76/unix/uxputty.c0000644000175000017500000000404214072266313012451 00000000000000/* * Unix PuTTY main program. */ #include #include #include #include #include #include #include #define MAY_REFER_TO_GTK_IN_HEADERS #include "putty.h" #include "ssh.h" #include "storage.h" #include "gtkcompat.h" /* * Stubs to avoid uxpty.c needing to be linked in. */ const bool use_pty_argv = false; char **pty_argv; /* never used */ char *pty_osx_envrestore_prefix; /* * Clean up and exit. */ void cleanup_exit(int code) { /* * Clean up. */ sk_cleanup(); random_save_seed(); exit(code); } const struct BackendVtable *select_backend(Conf *conf) { const struct BackendVtable *vt = backend_vt_from_proto(conf_get_int(conf, CONF_protocol)); assert(vt != NULL); return vt; } void initial_config_box(Conf *conf, post_dialog_fn_t after, void *afterctx) { char *title = dupcat(appname, " Configuration"); create_config_box(title, conf, false, 0, after, afterctx); sfree(title); } const bool use_event_log = true, new_session = true, saved_sessions = true; const bool dup_check_launchable = true; /* * X11-forwarding-related things suitable for Gtk app. */ char *platform_get_x_display(void) { const char *display; /* 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 bool share_can_be_downstream = true; const bool share_can_be_upstream = true; const unsigned cmdline_tooltype = TOOLTYPE_HOST_ARG | TOOLTYPE_PORT_ARG | TOOLTYPE_NO_VERBOSE_OPTION; void setup(bool single) { sk_init(); settings_set_default_protocol(be_default_protocol); /* Find the appropriate default port. */ { const struct BackendVtable *vt = backend_vt_from_proto(be_default_protocol); settings_set_default_port(0); /* illegal */ if (vt) settings_set_default_port(vt->default_port); } } putty-0.76/unix/uxsel.c0000644000175000017500000000561214072266313012053 00000000000000/* * uxsel.c * * This module is a sort of all-purpose interchange for file * descriptors. At one end it talks to uxnet.c and pty.c and * anything else which might have one or more fds that need * select() or poll()-type things doing to them during an extended * program run; at the other end it talks to pterm.c or uxplink.c or * anything else which might have its own means of actually doing * those select()-type things. */ #include #include "putty.h" #include "tree234.h" struct fd { int fd; int rwx; /* 4=except 2=write 1=read */ uxsel_callback_fn callback; uxsel_id *id; /* for uxsel_input_remove */ }; static tree234 *fds; static int uxsel_fd_cmp(void *av, void *bv) { struct fd *a = (struct fd *)av; struct fd *b = (struct fd *)bv; if (a->fd < b->fd) return -1; if (a->fd > b->fd) return +1; return 0; } static int uxsel_fd_findcmp(void *av, void *bv) { int *a = (int *)av; struct fd *b = (struct fd *)bv; if (*a < b->fd) return -1; if (*a > b->fd) return +1; return 0; } void uxsel_init(void) { fds = newtree234(uxsel_fd_cmp); } /* * Here is the interface to fd-supplying modules. They supply an * fd, a set of read/write/execute states, and a callback function * for when the fd satisfies one of those states. Repeated calls to * uxsel_set on the same fd are perfectly legal and serve to change * the rwx state (typically you only want to select an fd for * writing when you actually have pending data you want to write to * it!). */ void uxsel_set(int fd, int rwx, uxsel_callback_fn callback) { struct fd *newfd; assert(fd >= 0); uxsel_del(fd); if (rwx) { newfd = snew(struct fd); newfd->fd = fd; newfd->rwx = rwx; newfd->callback = callback; newfd->id = uxsel_input_add(fd, rwx); add234(fds, newfd); } } void uxsel_del(int fd) { struct fd *oldfd = find234(fds, &fd, uxsel_fd_findcmp); if (oldfd) { if (oldfd->id) uxsel_input_remove(oldfd->id); del234(fds, oldfd); sfree(oldfd); } } /* * And here is the interface to select-functionality-supplying * modules. */ int next_fd(int *state, int *rwx) { struct fd *fd; fd = index234(fds, (*state)++); if (fd) { *rwx = fd->rwx; return fd->fd; } else return -1; } int first_fd(int *state, int *rwx) { *state = 0; return next_fd(state, rwx); } void select_result(int fd, int event) { struct fd *fdstruct = find234(fds, &fd, uxsel_fd_findcmp); noise_ultralight(NOISE_SOURCE_IOID, fd); /* * Apparently this can sometimes be NULL. Can't see how, but I * assume it means I need to ignore the event since it's on an * fd I've stopped being interested in. Sigh. */ if (fdstruct) fdstruct->callback(fd, event); } putty-0.76/unix/uxser.c0000644000175000017500000003272514072266313012066 00000000000000/* * Serial back end (Unix-specific). */ #include #include #include #include #include #include #include #include #include "putty.h" #include "tree234.h" #define SERIAL_MAX_BACKLOG 4096 typedef struct Serial Serial; struct Serial { Seat *seat; LogContext *logctx; int fd; bool finished; size_t inbufsize; bufchain output_data; Backend backend; }; /* * We store our serial backends in a tree sorted by fd, so that * when we get an uxsel notification we know which backend instance * is the owner of the serial port that caused it. */ static int serial_compare_by_fd(void *av, void *bv) { Serial *a = (Serial *)av; Serial *b = (Serial *)bv; if (a->fd < b->fd) return -1; else if (a->fd > b->fd) return +1; return 0; } static int serial_find_by_fd(void *av, void *bv) { int a = *(int *)av; Serial *b = (Serial *)bv; if (a < b->fd) return -1; else if (a > b->fd) return +1; return 0; } static tree234 *serial_by_fd = NULL; static void serial_select_result(int fd, int event); static void serial_uxsel_setup(Serial *serial); static void serial_try_write(Serial *serial); static char *serial_configure(Serial *serial, Conf *conf) { struct termios options; int bflag, bval, speed, flow, parity; const char *str; if (serial->fd < 0) return dupstr("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 #ifdef B134 CHECKBAUD(134); #endif #ifdef B150 CHECKBAUD(150); #endif #ifdef B200 CHECKBAUD(200); #endif #ifdef B300 CHECKBAUD(300); #endif #ifdef B600 CHECKBAUD(600); #endif #ifdef B1200 CHECKBAUD(1200); #endif #ifdef B1800 CHECKBAUD(1800); #endif #ifdef B2400 CHECKBAUD(2400); #endif #ifdef B4800 CHECKBAUD(4800); #endif #ifdef B9600 CHECKBAUD(9600); #endif #ifdef B19200 CHECKBAUD(19200); #endif #ifdef B38400 CHECKBAUD(38400); #endif #ifdef B57600 CHECKBAUD(57600); #endif #ifdef B76800 CHECKBAUD(76800); #endif #ifdef B115200 CHECKBAUD(115200); #endif #ifdef B153600 CHECKBAUD(153600); #endif #ifdef B230400 CHECKBAUD(230400); #endif #ifdef B307200 CHECKBAUD(307200); #endif #ifdef B460800 CHECKBAUD(460800); #endif #ifdef B500000 CHECKBAUD(500000); #endif #ifdef B576000 CHECKBAUD(576000); #endif #ifdef B921600 CHECKBAUD(921600); #endif #ifdef B1000000 CHECKBAUD(1000000); #endif #ifdef B1152000 CHECKBAUD(1152000); #endif #ifdef B1500000 CHECKBAUD(1500000); #endif #ifdef B2000000 CHECKBAUD(2000000); #endif #ifdef B2500000 CHECKBAUD(2500000); #endif #ifdef B3000000 CHECKBAUD(3000000); #endif #ifdef B3500000 CHECKBAUD(3500000); #endif #ifdef B4000000 CHECKBAUD(4000000); #endif #undef CHECKBAUD #undef SETBAUD cfsetispeed(&options, bflag); cfsetospeed(&options, bflag); logeventf(serial->logctx, "Configuring baud rate %d", bval); 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 dupstr("Invalid number of data bits " "(need 5, 6, 7 or 8)"); } logeventf(serial->logctx, "Configuring %d data bits", conf_get_int(conf, CONF_serdatabits)); if (conf_get_int(conf, CONF_serstopbits) >= 4) { options.c_cflag |= CSTOPB; } else { options.c_cflag &= ~CSTOPB; } logeventf(serial->logctx, "Configuring %s", (options.c_cflag & CSTOPB ? "2 stop bits" : "1 stop bit")); 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"; logeventf(serial->logctx, "Configuring %s flow control", str); /* 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"; } logeventf(serial->logctx, "Configuring %s parity", str); options.c_cflag |= CLOCAL | CREAD; options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); options.c_iflag &= ~(ISTRIP | IGNCR | INLCR | ICRNL #ifdef IUCLC | IUCLC #endif ); options.c_oflag &= ~(OPOST #ifdef ONLCR | ONLCR #endif #ifdef OCRNL | OCRNL #endif #ifdef ONOCR | ONOCR #endif #ifdef ONLRET | ONLRET #endif ); options.c_cc[VMIN] = 1; options.c_cc[VTIME] = 0; if (tcsetattr(serial->fd, TCSANOW, &options) < 0) return dupprintf("Configuring serial port: %s", strerror(errno)); return NULL; } /* * Called to set up the serial 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 char *serial_init(const BackendVtable *vt, Seat *seat, Backend **backend_handle, LogContext *logctx, Conf *conf, const char *host, int port, char **realhost, bool nodelay, bool keepalive) { Serial *serial; char *err; char *line; /* No local authentication phase in this protocol */ seat_set_trust_status(seat, false); serial = snew(Serial); serial->backend.vt = vt; *backend_handle = &serial->backend; serial->seat = seat; serial->logctx = logctx; serial->finished = false; serial->inbufsize = 0; bufchain_init(&serial->output_data); line = conf_get_str(conf, CONF_serline); logeventf(serial->logctx, "Opening serial device %s", line); serial->fd = open(line, O_RDWR | O_NOCTTY | O_NDELAY | O_NONBLOCK); if (serial->fd < 0) return dupprintf("Opening serial port '%s': %s", line, strerror(errno)); 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); /* * Specials are always available. */ seat_update_specials_menu(serial->seat); return NULL; } static void serial_close(Serial *serial) { if (serial->fd >= 0) { uxsel_del(serial->fd); close(serial->fd); serial->fd = -1; } } static void serial_free(Backend *be) { Serial *serial = container_of(be, Serial, backend); serial_close(serial); bufchain_clear(&serial->output_data); sfree(serial); } static void serial_reconfig(Backend *be, Conf *conf) { Serial *serial = container_of(be, Serial, backend); char *err = serial_configure(serial, conf); if (err) { /* * FIXME: apart from freeing the dynamically allocated * message, what should we do if this returns an error? */ sfree(err); } } static void serial_select_result(int fd, int event) { Serial *serial; char buf[4096]; int ret; bool finished = false; serial = find234(serial_by_fd, &fd, serial_find_by_fd); if (!serial) return; /* spurious event; keep going */ if (event == 1) { ret = read(serial->fd, buf, sizeof(buf)); if (ret == 0) { /* * Shouldn't happen on a real serial port, but I'm open * to the idea that there might be two-way devices we * can treat _like_ serial ports which can return EOF. */ finished = true; } else if (ret < 0) { #ifdef EAGAIN if (errno == EAGAIN) return; /* spurious */ #endif #ifdef EWOULDBLOCK if (errno == EWOULDBLOCK) return; /* spurious */ #endif perror("read serial port"); exit(1); } else if (ret > 0) { serial->inbufsize = seat_stdout(serial->seat, buf, ret); serial_uxsel_setup(serial); /* might acquire backlog and freeze */ } } else if (event == 2) { /* * Attempt to send data down the pty. */ serial_try_write(serial); } if (finished) { serial_close(serial); serial->finished = true; seat_notify_remote_exit(serial->seat); } } static void serial_uxsel_setup(Serial *serial) { int rwx = 0; if (serial->inbufsize <= SERIAL_MAX_BACKLOG) rwx |= SELECT_R; if (bufchain_size(&serial->output_data)) rwx |= SELECT_W; /* might also want to write to it */ uxsel_set(serial->fd, rwx, serial_select_result); } static void serial_try_write(Serial *serial) { ssize_t ret; assert(serial->fd >= 0); while (bufchain_size(&serial->output_data) > 0) { ptrlen data = bufchain_prefix(&serial->output_data); ret = write(serial->fd, data.ptr, data.len); if (ret < 0 && (errno == EWOULDBLOCK)) { /* * We've sent all we can for the moment. */ break; } if (ret < 0) { perror("write serial port"); exit(1); } bufchain_consume(&serial->output_data, ret); } serial_uxsel_setup(serial); } /* * Called to send data down the serial connection. */ static size_t serial_send(Backend *be, const char *buf, size_t len) { Serial *serial = container_of(be, Serial, backend); if (serial->fd < 0) return 0; bufchain_add(&serial->output_data, buf, len); serial_try_write(serial); return bufchain_size(&serial->output_data); } /* * Called to query the current sendability status. */ static size_t serial_sendbuffer(Backend *be) { Serial *serial = container_of(be, Serial, backend); return bufchain_size(&serial->output_data); } /* * Called to set the size of the window */ static void serial_size(Backend *be, int width, int height) { /* Do nothing! */ return; } /* * Send serial special codes. */ static void serial_special(Backend *be, SessionSpecialCode code, int arg) { Serial *serial = container_of(be, Serial, backend); if (serial->fd >= 0 && code == SS_BRK) { tcsendbreak(serial->fd, 0); logevent(serial->logctx, "Sending serial break at user request"); } return; } /* * Return a list of the special codes that make sense in this * protocol. */ static const SessionSpecial *serial_get_specials(Backend *be) { static const struct SessionSpecial specials[] = { {"Break", SS_BRK}, {NULL, SS_EXITMENU} }; return specials; } static bool serial_connected(Backend *be) { return true; /* always connected */ } static bool serial_sendok(Backend *be) { return true; } static void serial_unthrottle(Backend *be, size_t backlog) { Serial *serial = container_of(be, Serial, backend); serial->inbufsize = backlog; serial_uxsel_setup(serial); } static bool serial_ldisc(Backend *be, int option) { /* * Local editing and local echo are off by default. */ return false; } static void serial_provide_ldisc(Backend *be, Ldisc *ldisc) { /* This is a stub. */ } static int serial_exitcode(Backend *be) { Serial *serial = container_of(be, Serial, backend); if (serial->fd >= 0) return -1; /* still connected */ else /* Exit codes are a meaningless concept with serial ports */ return INT_MAX; } /* * cfg_info for Serial does nothing at all. */ static int serial_cfg_info(Backend *be) { return 0; } const BackendVtable serial_backend = { .init = serial_init, .free = serial_free, .reconfig = serial_reconfig, .send = serial_send, .sendbuffer = serial_sendbuffer, .size = serial_size, .special = serial_special, .get_specials = serial_get_specials, .connected = serial_connected, .exitcode = serial_exitcode, .sendok = serial_sendok, .ldisc_option_state = serial_ldisc, .provide_ldisc = serial_provide_ldisc, .unthrottle = serial_unthrottle, .cfg_info = serial_cfg_info, .id = "serial", .displayname = "Serial", .protocol = PROT_SERIAL, .serial_parity_mask = ((1 << SER_PAR_NONE) | (1 << SER_PAR_ODD) | (1 << SER_PAR_EVEN)), .serial_flow_mask = ((1 << SER_FLOW_NONE) | (1 << SER_FLOW_XONXOFF) | (1 << SER_FLOW_RTSCTS)), }; putty-0.76/unix/uxserver.c0000644000175000017500000006675514072266313012615 00000000000000/* * SSH server for Unix: main program. * * ====================================================================== * * This server is NOT SECURE! * * DO NOT DEPLOY IT IN A HOSTILE-FACING ENVIRONMENT! * * Its purpose is to speak the server end of everything PuTTY speaks * on the client side, so that I can test that I haven't broken PuTTY * when I reorganise its code, even things like RSA key exchange or * chained auth methods which it's hard to find a server that speaks * at all. * * It has no interaction with the OS's authentication system: the * authentications it will accept are configurable by command-line * option, and once you authenticate, it will run the connection * protocol - including all subprocesses and shells - under the same * Unix user id you started it under. * * It really is only suitable for testing the actual SSH protocol. * Don't use it for anything more serious! * * ====================================================================== */ #include #include #include #include #include #include #include #include #include #include #include #include #include "putty.h" #include "mpint.h" #include "ssh.h" #include "sshserver.h" const char *const appname = "uppity"; void modalfatalbox(const char *p, ...) { va_list ap; fprintf(stderr, "FATAL ERROR: "); va_start(ap, p); vfprintf(stderr, p, ap); va_end(ap); fputc('\n', stderr); exit(1); } void nonfatal(const char *p, ...) { va_list ap; fprintf(stderr, "ERROR: "); va_start(ap, p); vfprintf(stderr, p, ap); va_end(ap); fputc('\n', stderr); } char *platform_default_s(const char *name) { return NULL; } bool platform_default_b(const char *name, bool def) { return def; } 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) { return filename_from_str(""); } char *x_get_default(const char *key) { return NULL; /* this is a stub */ } void old_keyfile_warning(void) { } void timer_change_notify(unsigned long next) { } char *platform_get_x_display(void) { return NULL; } void make_unix_sftp_filehandle_key(void *data, size_t size) { random_read(data, size); } static bool verbose; struct AuthPolicyShared { struct AuthPolicy_ssh1_pubkey *ssh1keys; struct AuthPolicy_ssh2_pubkey *ssh2keys; }; struct AuthPolicy { struct AuthPolicyShared *shared; int kbdint_state; }; struct server_instance { unsigned id; AuthPolicy ap; LogPolicy logpolicy; }; static void log_to_stderr(unsigned id, const char *msg) { if (id != (unsigned)-1) fprintf(stderr, "#%u: ", id); fputs(msg, stderr); fputc('\n', stderr); fflush(stderr); } static void server_eventlog(LogPolicy *lp, const char *event) { struct server_instance *inst = container_of( lp, struct server_instance, logpolicy); if (verbose) log_to_stderr(inst->id, event); } static void server_logging_error(LogPolicy *lp, const char *event) { struct server_instance *inst = container_of( lp, struct server_instance, logpolicy); log_to_stderr(inst->id, event); /* unconditional */ } static int server_askappend( LogPolicy *lp, Filename *filename, void (*callback)(void *ctx, int result), void *ctx) { return 2; /* always overwrite (FIXME: could make this a cmdline option) */ } static const LogPolicyVtable server_logpolicy_vt = { .eventlog = server_eventlog, .askappend = server_askappend, .logging_error = server_logging_error, .verbose = null_lp_verbose_no, }; struct AuthPolicy_ssh1_pubkey { RSAKey key; struct AuthPolicy_ssh1_pubkey *next; }; struct AuthPolicy_ssh2_pubkey { ptrlen public_blob; struct AuthPolicy_ssh2_pubkey *next; }; unsigned auth_methods(AuthPolicy *ap) { return (AUTHMETHOD_PUBLICKEY | AUTHMETHOD_PASSWORD | AUTHMETHOD_KBDINT | AUTHMETHOD_TIS | AUTHMETHOD_CRYPTOCARD); } bool auth_none(AuthPolicy *ap, ptrlen username) { return false; } int auth_password(AuthPolicy *ap, ptrlen username, ptrlen password, ptrlen *new_password_opt) { const char *PHONY_GOOD_PASSWORD = "weasel"; const char *PHONY_BAD_PASSWORD = "ferret"; if (!new_password_opt) { /* Accept login with our preconfigured good password */ if (ptrlen_eq_string(password, PHONY_GOOD_PASSWORD)) return 1; /* Don't outright reject the bad password, but insist on a change */ if (ptrlen_eq_string(password, PHONY_BAD_PASSWORD)) return 2; /* Reject anything else */ return 0; } else { /* In a password-change request, expect the bad password as input */ if (!ptrlen_eq_string(password, PHONY_BAD_PASSWORD)) return 0; /* Accept a request to change it to the good password */ if (ptrlen_eq_string(*new_password_opt, PHONY_GOOD_PASSWORD)) return 1; /* Outright reject a request to change it to the same password * as it already 'was' */ if (ptrlen_eq_string(*new_password_opt, PHONY_BAD_PASSWORD)) return 0; /* Anything else, pretend the new pw wasn't good enough, and * re-request a change */ return 2; } } bool auth_publickey(AuthPolicy *ap, ptrlen username, ptrlen public_blob) { struct AuthPolicy_ssh2_pubkey *iter; for (iter = ap->shared->ssh2keys; iter; iter = iter->next) { if (ptrlen_eq_ptrlen(public_blob, iter->public_blob)) return true; } return false; } RSAKey *auth_publickey_ssh1( AuthPolicy *ap, ptrlen username, mp_int *rsa_modulus) { struct AuthPolicy_ssh1_pubkey *iter; for (iter = ap->shared->ssh1keys; iter; iter = iter->next) { if (mp_cmp_eq(rsa_modulus, iter->key.modulus)) return &iter->key; } return NULL; } AuthKbdInt *auth_kbdint_prompts(AuthPolicy *ap, ptrlen username) { AuthKbdInt *aki; switch (ap->kbdint_state) { case 0: aki = snew(AuthKbdInt); aki->title = dupstr("Initial double prompt"); aki->instruction = dupstr("First prompt should echo, second should not"); aki->nprompts = 2; aki->prompts = snewn(aki->nprompts, AuthKbdIntPrompt); aki->prompts[0].prompt = dupstr("Echoey prompt: "); aki->prompts[0].echo = true; aki->prompts[1].prompt = dupstr("Silent prompt: "); aki->prompts[1].echo = false; return aki; case 1: aki = snew(AuthKbdInt); aki->title = dupstr("Zero-prompt step"); aki->instruction = dupstr("Shouldn't see any prompts this time"); aki->nprompts = 0; aki->prompts = NULL; return aki; default: ap->kbdint_state = 0; return NULL; } } int auth_kbdint_responses(AuthPolicy *ap, const ptrlen *responses) { switch (ap->kbdint_state) { case 0: if (ptrlen_eq_string(responses[0], "stoat") && ptrlen_eq_string(responses[1], "weasel")) { ap->kbdint_state++; return 0; /* those are the expected responses */ } else { ap->kbdint_state = 0; return -1; } break; case 1: return +1; /* succeed after the zero-prompt step */ default: ap->kbdint_state = 0; return -1; } } char *auth_ssh1int_challenge(AuthPolicy *ap, unsigned method, ptrlen username) { /* FIXME: test returning a challenge string without \n, and ensure * it gets printed as a prompt in its own right, without PuTTY * making up a "Response: " prompt to follow it */ return dupprintf("This is a dummy %s challenge!\n", (method == AUTHMETHOD_TIS ? "TIS" : "CryptoCard")); } bool auth_ssh1int_response(AuthPolicy *ap, ptrlen response) { return ptrlen_eq_string(response, "otter"); } bool auth_successful(AuthPolicy *ap, ptrlen username, unsigned method) { return true; } static void safety_warning(FILE *fp) { fputs(" =================================================\n" " THIS SSH SERVER IS NOT WRITTEN TO BE SECURE!\n" " DO NOT DEPLOY IT IN A HOSTILE-FACING ENVIRONMENT!\n" " =================================================\n", fp); } static void show_help(FILE *fp) { safety_warning(fp); fputs("\n" "usage: uppity [options]\n" "options: --listen [PORT|PATH] listen to a port on localhost, or Unix socket\n" " --listen-once (with --listen) stop after one " "connection\n" " --hostkey KEY SSH host key (need at least one)\n" " --rsakexkey KEY key for SSH-2 RSA key exchange " "(in SSH-1 format)\n" " --userkey KEY public key" " acceptable for user authentication\n" " --sessiondir DIR cwd for session subprocess (default $HOME)\n" " --bannertext TEXT send TEXT as SSH-2 auth banner\n" " --bannerfile FILE send contents of FILE as SSH-2 auth " "banner\n" " --kexinit-kex STR override list of SSH-2 KEX methods\n" " --kexinit-hostkey STR override list of SSH-2 host key " "types\n" " --kexinit-cscipher STR override list of SSH-2 " "client->server ciphers\n" " --kexinit-sccipher STR override list of SSH-2 " "server->client ciphers\n" " --kexinit-csmac STR override list of SSH-2 " "client->server MACs\n" " --kexinit-scmac STR override list of SSH-2 " "server->client MACs\n" " --kexinit-cscomp STR override list of SSH-2 " "c->s compression types\n" " --kexinit-sccomp STR override list of SSH-2 " "s->c compression types\n" " --ssh1-ciphers STR override list of SSH-1 ciphers\n" " --ssh1-no-compression forbid compression in SSH-1\n" " --exitsignum send buggy numeric \"exit-signal\" " "message\n" " --verbose print event log messages to standard " "error\n" " --sshlog FILE write SSH packet log to FILE\n" " --sshrawlog FILE write SSH packets + raw data log" " to FILE\n" "also: uppity --help show this text\n" " uppity --version show version information\n" "\n", fp); safety_warning(fp); } static void show_version_and_exit(void) { char *buildinfo_text = buildinfo("\n"); printf("%s: %s\n%s\n", appname, ver, buildinfo_text); sfree(buildinfo_text); exit(0); } const bool buildinfo_gtk_relevant = false; static bool listening = false, listen_once = false; static bool finished = false; void server_instance_terminated(LogPolicy *lp) { struct server_instance *inst = container_of( lp, struct server_instance, logpolicy); if (listening && !listen_once) { log_to_stderr(inst->id, "connection terminated"); } else { finished = true; } sfree(inst); } static bool longoptarg(const char *arg, const char *expected, const char **val, int *argcp, char ***argvp) { int len = strlen(expected); if (memcmp(arg, expected, len)) return false; if (arg[len] == '=') { *val = arg + len + 1; return true; } else if (arg[len] == '\0') { if (--*argcp > 0) { *val = *++*argvp; return true; } else { fprintf(stderr, "%s: option %s expects an argument\n", appname, expected); exit(1); } } return false; } static bool longoptnoarg(const char *arg, const char *expected) { int len = strlen(expected); if (memcmp(arg, expected, len)) return false; if (arg[len] == '=') { fprintf(stderr, "%s: option %s expects no argument\n", appname, expected); exit(1); } else if (arg[len] == '\0') { return true; } return false; } struct server_config { Conf *conf; const SshServerConfig *ssc; ssh_key **hostkeys; int nhostkeys; RSAKey *hostkey1; struct AuthPolicyShared *ap_shared; unsigned next_id; Socket *listening_socket; Plug listening_plug; }; static Plug *server_conn_plug( struct server_config *cfg, struct server_instance **inst_out) { struct server_instance *inst = snew(struct server_instance); memset(inst, 0, sizeof(*inst)); inst->id = cfg->next_id++; inst->ap.shared = cfg->ap_shared; inst->logpolicy.vt = &server_logpolicy_vt; if (inst_out) *inst_out = inst; return ssh_server_plug( cfg->conf, cfg->ssc, cfg->hostkeys, cfg->nhostkeys, cfg->hostkey1, &inst->ap, &inst->logpolicy, &unix_live_sftpserver_vt); } static void server_log(Plug *plug, PlugLogType type, SockAddr *addr, int port, const char *error_msg, int error_code) { log_to_stderr((unsigned)-1, error_msg); } static void server_closing(Plug *plug, const char *error_msg, int error_code, bool calling_back) { log_to_stderr((unsigned)-1, error_msg); } static int server_accepting(Plug *p, accept_fn_t constructor, accept_ctx_t ctx) { struct server_config *cfg = container_of( p, struct server_config, listening_plug); Socket *s; const char *err; struct server_instance *inst; if (listen_once) { if (!cfg->listening_socket) /* in case of rapid double-accept */ return 1; sk_close(cfg->listening_socket); cfg->listening_socket = NULL; } unsigned old_next_id = cfg->next_id; Plug *plug = server_conn_plug(cfg, &inst); s = constructor(ctx, plug); if ((err = sk_socket_error(s)) != NULL) return 1; SocketPeerInfo *pi = sk_peer_info(s); if (pi->addressfamily != ADDRTYPE_LOCAL && !sk_peer_trusted(s)) { fprintf(stderr, "rejected connection from %s (untrustworthy peer)\n", pi->log_text); sk_free_peer_info(pi); sk_close(s); cfg->next_id = old_next_id; return 1; } char *msg = dupprintf("new connection from %s", pi->log_text); log_to_stderr(inst->id, msg); sfree(msg); sk_free_peer_info(pi); sk_set_frozen(s, false); ssh_server_start(plug, s); return 0; } static const PlugVtable server_plugvt = { .log = server_log, .closing = server_closing, .accepting = server_accepting, }; int main(int argc, char **argv) { int listen_port = -1; const char *listen_socket = NULL; ssh_key **hostkeys = NULL; size_t nhostkeys = 0, hostkeysize = 0; RSAKey *hostkey1 = NULL; struct AuthPolicyShared aps; SshServerConfig ssc; Conf *conf = make_ssh_server_conf(); aps.ssh1keys = NULL; aps.ssh2keys = NULL; memset(&ssc, 0, sizeof(ssc)); ssc.application_name = "Uppity"; ssc.session_starting_dir = getenv("HOME"); ssc.ssh1_cipher_mask = SSH1_SUPPORTED_CIPHER_MASK; ssc.ssh1_allow_compression = true; if (argc <= 1) { /* * We're going to terminate with an error message below, * because there are no host keys. But we'll display the help * as additional standard-error output, if nothing else so * that people see the giant safety warning. */ show_help(stderr); fputc('\n', stderr); } while (--argc > 0) { const char *arg = *++argv; const char *val; if (!strcmp(arg, "--help")) { show_help(stdout); exit(0); } else if (longoptnoarg(arg, "--version")) { show_version_and_exit(); } else if (longoptnoarg(arg, "--verbose") || !strcmp(arg, "-v")) { verbose = true; } else if (longoptarg(arg, "--listen", &val, &argc, &argv)) { if (val[0] == '/') { listen_port = -1; listen_socket = val; } else { listen_port = atoi(val); listen_socket = NULL; } } else if (!strcmp(arg, "--listen-once")) { listen_once = true; } else if (longoptarg(arg, "--hostkey", &val, &argc, &argv)) { Filename *keyfile; int keytype; const char *error; keyfile = filename_from_str(val); keytype = key_type(keyfile); if (keytype == SSH_KEYTYPE_SSH2) { ssh2_userkey *uk; ssh_key *key; uk = ppk_load_f(keyfile, NULL, &error); filename_free(keyfile); if (!uk || !uk->key) { fprintf(stderr, "%s: unable to load host key '%s': " "%s\n", appname, val, error); exit(1); } char *invalid = ssh_key_invalid(uk->key, 0); if (invalid) { fprintf(stderr, "%s: host key '%s' is unusable: " "%s\n", appname, val, invalid); exit(1); } key = uk->key; sfree(uk->comment); sfree(uk); for (int i = 0; i < nhostkeys; i++) if (ssh_key_alg(hostkeys[i]) == ssh_key_alg(key)) { fprintf(stderr, "%s: host key '%s' duplicates key " "type %s\n", appname, val, ssh_key_alg(key)->ssh_id); exit(1); } sgrowarray(hostkeys, hostkeysize, nhostkeys); hostkeys[nhostkeys++] = key; } else if (keytype == SSH_KEYTYPE_SSH1) { if (hostkey1) { fprintf(stderr, "%s: host key '%s' is a redundant " "SSH-1 host key\n", appname, val); exit(1); } hostkey1 = snew(RSAKey); if (!rsa1_load_f(keyfile, hostkey1, NULL, &error)) { fprintf(stderr, "%s: unable to load host key '%s': " "%s\n", appname, val, error); exit(1); } } else { fprintf(stderr, "%s: '%s' is not loadable as a " "private key (%s)", appname, val, key_type_to_str(keytype)); exit(1); } } else if (longoptarg(arg, "--rsakexkey", &val, &argc, &argv)) { Filename *keyfile; int keytype; const char *error; keyfile = filename_from_str(val); keytype = key_type(keyfile); if (keytype != SSH_KEYTYPE_SSH1) { fprintf(stderr, "%s: '%s' is not loadable as an SSH-1 format " "private key (%s)", appname, val, key_type_to_str(keytype)); exit(1); } if (ssc.rsa_kex_key) { freersakey(ssc.rsa_kex_key); } else { ssc.rsa_kex_key = snew(RSAKey); } if (!rsa1_load_f(keyfile, ssc.rsa_kex_key, NULL, &error)) { fprintf(stderr, "%s: unable to load RSA kex key '%s': " "%s\n", appname, val, error); exit(1); } ssc.rsa_kex_key->sshk.vt = &ssh_rsa; } else if (longoptarg(arg, "--userkey", &val, &argc, &argv)) { Filename *keyfile; int keytype; const char *error; keyfile = filename_from_str(val); keytype = key_type(keyfile); if (keytype == SSH_KEYTYPE_SSH2_PUBLIC_RFC4716 || keytype == SSH_KEYTYPE_SSH2_PUBLIC_OPENSSH) { strbuf *sb = strbuf_new(); struct AuthPolicy_ssh2_pubkey *node; void *blob; if (!ppk_loadpub_f(keyfile, NULL, BinarySink_UPCAST(sb), NULL, &error)) { fprintf(stderr, "%s: unable to load user key '%s': " "%s\n", appname, val, error); exit(1); } node = snew_plus(struct AuthPolicy_ssh2_pubkey, sb->len); blob = snew_plus_get_aux(node); memcpy(blob, sb->u, sb->len); node->public_blob = make_ptrlen(blob, sb->len); node->next = aps.ssh2keys; aps.ssh2keys = node; strbuf_free(sb); } else if (keytype == SSH_KEYTYPE_SSH1_PUBLIC) { strbuf *sb = strbuf_new(); BinarySource src[1]; struct AuthPolicy_ssh1_pubkey *node; if (!rsa1_loadpub_f(keyfile, BinarySink_UPCAST(sb), NULL, &error)) { fprintf(stderr, "%s: unable to load user key '%s': " "%s\n", appname, val, error); exit(1); } node = snew(struct AuthPolicy_ssh1_pubkey); BinarySource_BARE_INIT(src, sb->u, sb->len); get_rsa_ssh1_pub(src, &node->key, RSA_SSH1_EXPONENT_FIRST); node->next = aps.ssh1keys; aps.ssh1keys = node; strbuf_free(sb); } else { fprintf(stderr, "%s: '%s' is not loadable as a public key " "(%s)\n", appname, val, key_type_to_str(keytype)); exit(1); } } else if (longoptarg(arg, "--bannerfile", &val, &argc, &argv)) { FILE *fp = fopen(val, "r"); if (!fp) { fprintf(stderr, "%s: %s: open: %s\n", appname, val, strerror(errno)); exit(1); } strbuf *sb = strbuf_new(); if (!read_file_into(BinarySink_UPCAST(sb), fp)) { fprintf(stderr, "%s: %s: read: %s\n", appname, val, strerror(errno)); exit(1); } fclose(fp); ssc.banner = ptrlen_from_strbuf(sb); } else if (longoptarg(arg, "--bannertext", &val, &argc, &argv)) { ssc.banner = ptrlen_from_asciz(val); } else if (longoptarg(arg, "--sessiondir", &val, &argc, &argv)) { ssc.session_starting_dir = val; } else if (longoptarg(arg, "--kexinit-kex", &val, &argc, &argv)) { ssc.kex_override[KEXLIST_KEX] = ptrlen_from_asciz(val); } else if (longoptarg(arg, "--kexinit-hostkey", &val, &argc, &argv)) { ssc.kex_override[KEXLIST_HOSTKEY] = ptrlen_from_asciz(val); } else if (longoptarg(arg, "--kexinit-cscipher", &val, &argc, &argv)) { ssc.kex_override[KEXLIST_CSCIPHER] = ptrlen_from_asciz(val); } else if (longoptarg(arg, "--kexinit-csmac", &val, &argc, &argv)) { ssc.kex_override[KEXLIST_CSMAC] = ptrlen_from_asciz(val); } else if (longoptarg(arg, "--kexinit-cscomp", &val, &argc, &argv)) { ssc.kex_override[KEXLIST_CSCOMP] = ptrlen_from_asciz(val); } else if (longoptarg(arg, "--kexinit-sccipher", &val, &argc, &argv)) { ssc.kex_override[KEXLIST_SCCIPHER] = ptrlen_from_asciz(val); } else if (longoptarg(arg, "--kexinit-scmac", &val, &argc, &argv)) { ssc.kex_override[KEXLIST_SCMAC] = ptrlen_from_asciz(val); } else if (longoptarg(arg, "--kexinit-sccomp", &val, &argc, &argv)) { ssc.kex_override[KEXLIST_SCCOMP] = ptrlen_from_asciz(val); } else if (longoptarg(arg, "--ssh1-ciphers", &val, &argc, &argv)) { ptrlen list = ptrlen_from_asciz(val); ptrlen word; unsigned long mask = 0; while (word = ptrlen_get_word(&list, ","), word.len != 0) { #define SSH1_CIPHER_CASE(bitpos, name) \ if (ptrlen_eq_string(word, name)) { \ mask |= 1U << bitpos; \ continue; \ } SSH1_SUPPORTED_CIPHER_LIST(SSH1_CIPHER_CASE); #undef SSH1_CIPHER_CASE fprintf(stderr, "%s: unrecognised SSH-1 cipher '%.*s'\n", appname, PTRLEN_PRINTF(word)); exit(1); } ssc.ssh1_cipher_mask = mask; } else if (longoptnoarg(arg, "--ssh1-no-compression")) { ssc.ssh1_allow_compression = false; } else if (longoptnoarg(arg, "--exitsignum")) { ssc.exit_signal_numeric = true; } else if (longoptarg(arg, "--sshlog", &val, &argc, &argv) || longoptarg(arg, "-sshlog", &val, &argc, &argv)) { Filename *logfile = filename_from_str(val); conf_set_filename(conf, CONF_logfilename, logfile); filename_free(logfile); conf_set_int(conf, CONF_logtype, LGTYP_PACKETS); conf_set_int(conf, CONF_logxfovr, LGXF_OVR); } else if (longoptarg(arg, "--sshrawlog", &val, &argc, &argv) || longoptarg(arg, "-sshrawlog", &val, &argc, &argv)) { Filename *logfile = filename_from_str(val); conf_set_filename(conf, CONF_logfilename, logfile); filename_free(logfile); conf_set_int(conf, CONF_logtype, LGTYP_SSHRAW); conf_set_int(conf, CONF_logxfovr, LGXF_OVR); } else if (!strcmp(arg, "--pretend-to-accept-any-pubkey")) { ssc.stunt_pretend_to_accept_any_pubkey = true; } else if (!strcmp(arg, "--open-unconditional-agent-socket")) { ssc.stunt_open_unconditional_agent_socket = true; } else { fprintf(stderr, "%s: unrecognised option '%s'\n", appname, arg); exit(1); } } if (nhostkeys == 0 && !hostkey1) { fprintf(stderr, "%s: specify at least one host key\n", appname); exit(1); } random_ref(); /* * Block SIGPIPE, so that we'll get EPIPE individually on * particular network connections that go wrong. */ putty_signal(SIGPIPE, SIG_IGN); sk_init(); uxsel_init(); struct server_config scfg; scfg.conf = conf; scfg.ssc = &ssc; scfg.hostkeys = hostkeys; scfg.nhostkeys = nhostkeys; scfg.hostkey1 = hostkey1; scfg.ap_shared = &aps; scfg.next_id = 0; if (listen_port >= 0 || listen_socket) { listening = true; scfg.listening_plug.vt = &server_plugvt; char *msg; if (listen_port >= 0) { scfg.listening_socket = sk_newlistener( NULL, listen_port, &scfg.listening_plug, true, ADDRTYPE_UNSPEC); msg = dupprintf("%s: listening on port %d", appname, listen_port); } else { SockAddr *addr = unix_sock_addr(listen_socket); scfg.listening_socket = new_unix_listener( addr, &scfg.listening_plug); msg = dupprintf("%s: listening on Unix socket %s", appname, listen_socket); } log_to_stderr(-1, msg); sfree(msg); } else { struct server_instance *inst; Plug *plug = server_conn_plug(&scfg, &inst); ssh_server_start(plug, make_fd_socket(0, 1, -1, plug)); log_to_stderr(inst->id, "speaking SSH on stdio"); } cli_main_loop(cliloop_no_pw_setup, cliloop_no_pw_check, cliloop_always_continue, NULL); return 0; } putty-0.76/unix/uxsftp.c0000644000175000017500000002773014072266313012251 00000000000000/* * uxsftp.c: the Unix-specific parts of PSFTP and PSCP. */ #include #include #include #include #include #include #include #include #include #include #include "putty.h" #include "ssh.h" #include "psftp.h" #if HAVE_GLOB_H #include #endif 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 bool platform_uses_x11_unix_by_default = true; /* * Default settings that are specific to PSFTP. */ char *platform_default_s(const char *name) { return NULL; } bool platform_default_b(const char *name, bool def) { return def; } 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(""); } int filexfer_get_userpass_input(Seat *seat, prompts_t *p, bufchain *input) { int ret; ret = cmdline_get_passwd_input(p); if (ret == -1) ret = console_get_userpass_input(p); return ret; } /* * Set local current directory. Returns NULL on success, or else an * error message which must be freed after printing. */ char *psftp_lcd(char *dir) { if (chdir(dir) < 0) return dupprintf("%s: chdir: %s", dir, strerror(errno)); else return NULL; } /* * Get local current directory. Returns a string which must be * freed. */ char *psftp_getcwd(void) { char *buffer, *ret; size_t size = 256; buffer = snewn(size, char); while (1) { ret = getcwd(buffer, size); if (ret != NULL) return ret; if (errno != ERANGE) { sfree(buffer); return dupprintf("[cwd unavailable: %s]", strerror(errno)); } /* * Otherwise, ERANGE was returned, meaning the buffer * wasn't big enough. */ sgrowarray(buffer, size, size); } } struct RFile { int fd; }; RFile *open_existing_file(const char *name, uint64_t *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 = 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) { return read(f->fd, buffer, length); } void close_rfile(RFile *f) { close(f->fd); sfree(f); } struct WFile { int fd; char *name; }; WFile *open_new_file(const 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); return ret; } WFile *open_existing_wfile(const char *name, uint64_t *size) { int fd; WFile *ret; fd = open(name, O_APPEND | O_WRONLY); if (fd < 0) return NULL; ret = snew(WFile); ret->fd = fd; ret->name = dupstr(name); if (size) { struct stat statbuf; if (fstat(fd, &statbuf) < 0) { fprintf(stderr, "%s: stat: %s\n", name, strerror(errno)); memset(&statbuf, 0, sizeof(statbuf)); } *size = statbuf.st_size; } return ret; } int write_to_file(WFile *f, void *buffer, int length) { char *p = (char *)buffer; int so_far = 0; /* Keep trying until we've really written as much as we can. */ while (length > 0) { int ret = write(f->fd, p, length); if (ret < 0) return ret; if (ret == 0) break; p += ret; length -= ret; so_far += ret; } return so_far; } void set_file_times(WFile *f, unsigned long mtime, unsigned long atime) { struct utimbuf ut; ut.actime = atime; ut.modtime = mtime; utime(f->name, &ut); } /* Closes and frees the WFile */ void close_wfile(WFile *f) { close(f->fd); sfree(f->name); sfree(f); } /* Seek offset bytes through file, from whence, where whence is FROM_START, FROM_CURRENT, or FROM_END */ int seek_file(WFile *f, uint64_t offset, int whence) { int lseek_whence; switch (whence) { case FROM_START: lseek_whence = SEEK_SET; break; case FROM_CURRENT: lseek_whence = SEEK_CUR; break; case FROM_END: lseek_whence = SEEK_END; break; default: return -1; } return lseek(f->fd, offset, lseek_whence) >= 0 ? 0 : -1; } uint64_t get_file_posn(WFile *f) { return lseek(f->fd, (off_t) 0, SEEK_CUR); } int file_type(const char *name) { struct stat statbuf; if (stat(name, &statbuf) < 0) { if (errno != ENOENT) fprintf(stderr, "%s: stat: %s\n", name, strerror(errno)); return FILE_TYPE_NONEXISTENT; } if (S_ISREG(statbuf.st_mode)) return FILE_TYPE_FILE; if (S_ISDIR(statbuf.st_mode)) return FILE_TYPE_DIRECTORY; return FILE_TYPE_WEIRD; } struct DirHandle { DIR *dir; }; DirHandle *open_directory(const char *name, const char **errmsg) { DIR *dir; DirHandle *ret; dir = opendir(name); if (!dir) { *errmsg = strerror(errno); return NULL; } ret = snew(DirHandle); ret->dir = dir; return ret; } char *read_filename(DirHandle *dir) { struct dirent *de; do { de = readdir(dir->dir); if (de == NULL) return NULL; } while ((de->d_name[0] == '.' && (de->d_name[1] == '\0' || (de->d_name[1] == '.' && de->d_name[2] == '\0')))); return dupstr(de->d_name); } void close_directory(DirHandle *dir) { closedir(dir->dir); sfree(dir); } int test_wildcard(const char *name, bool cmdline) { struct stat statbuf; if (stat(name, &statbuf) == 0) { return WCTYPE_FILENAME; } else if (cmdline) { /* * On Unix, we never need to parse wildcards coming from * the command line, because the shell will have expanded * them into a filename list already. */ return WCTYPE_NONEXISTENT; } else { #if HAVE_GLOB_H glob_t globbed; int ret = WCTYPE_NONEXISTENT; if (glob(name, GLOB_ERR, NULL, &globbed) == 0) { if (globbed.gl_pathc > 0) ret = WCTYPE_WILDCARD; globfree(&globbed); } return ret; #else /* On a system without glob.h, we just have to return a * failure code */ return WCTYPE_NONEXISTENT; #endif } } /* * Actually return matching file names for a local wildcard. */ #if HAVE_GLOB_H struct WildcardMatcher { glob_t globbed; int i; }; WildcardMatcher *begin_wildcard_matching(const char *name) { WildcardMatcher *ret = snew(WildcardMatcher); if (glob(name, 0, NULL, &ret->globbed) < 0) { sfree(ret); return NULL; } ret->i = 0; return ret; } char *wildcard_get_filename(WildcardMatcher *dir) { if (dir->i < dir->globbed.gl_pathc) { return dupstr(dir->globbed.gl_pathv[dir->i++]); } else return NULL; } void finish_wildcard_matching(WildcardMatcher *dir) { globfree(&dir->globbed); sfree(dir); } #else WildcardMatcher *begin_wildcard_matching(const char *name) { return NULL; } char *wildcard_get_filename(WildcardMatcher *dir) { unreachable("Can't construct a valid WildcardMatcher without "); } void finish_wildcard_matching(WildcardMatcher *dir) { unreachable("Can't construct a valid WildcardMatcher without "); } #endif char *stripslashes(const char *str, bool local) { char *p; /* * On Unix, we do the same thing regardless of the 'local' * parameter. */ p = strrchr(str, '/'); if (p) str = p+1; return (char *)str; } bool vet_filename(const char *name) { if (strchr(name, '/')) return false; if (name[0] == '.' && (!name[1] || (name[1] == '.' && !name[2]))) return false; return true; } bool create_directory(const char *name) { return mkdir(name, 0777) == 0; } char *dir_file_cat(const char *dir, const char *file) { ptrlen dir_pl = ptrlen_from_asciz(dir); return dupcat( dir, ptrlen_endswith(dir_pl, PTRLEN_LITERAL("/"), NULL) ? "" : "/", file); } /* * Do a select() between all currently active network fds and * optionally stdin, using cli_main_loop. */ struct ssh_sftp_mainloop_ctx { bool include_stdin, no_fds_ok; int toret; }; static bool ssh_sftp_pw_setup(void *vctx, pollwrapper *pw) { struct ssh_sftp_mainloop_ctx *ctx = (struct ssh_sftp_mainloop_ctx *)vctx; int fdstate, rwx; if (!ctx->no_fds_ok && !toplevel_callback_pending() && first_fd(&fdstate, &rwx) < 0) { ctx->toret = -1; return false; /* terminate cli_main_loop */ } if (ctx->include_stdin) pollwrap_add_fd_rwx(pw, 0, SELECT_R); return true; } static void ssh_sftp_pw_check(void *vctx, pollwrapper *pw) { struct ssh_sftp_mainloop_ctx *ctx = (struct ssh_sftp_mainloop_ctx *)vctx; if (ctx->include_stdin && pollwrap_check_fd_rwx(pw, 0, SELECT_R)) ctx->toret = 1; } static bool ssh_sftp_mainloop_continue(void *vctx, bool found_any_fd, bool ran_any_callback) { struct ssh_sftp_mainloop_ctx *ctx = (struct ssh_sftp_mainloop_ctx *)vctx; if (ctx->toret != 0 || found_any_fd || ran_any_callback) return false; /* finish the loop */ return true; } static int ssh_sftp_do_select(bool include_stdin, bool no_fds_ok) { struct ssh_sftp_mainloop_ctx ctx[1]; ctx->include_stdin = include_stdin; ctx->no_fds_ok = no_fds_ok; ctx->toret = 0; cli_main_loop(ssh_sftp_pw_setup, ssh_sftp_pw_check, ssh_sftp_mainloop_continue, ctx); return ctx->toret; } /* * Wait for some network data and process it. */ int ssh_sftp_loop_iteration(void) { return ssh_sftp_do_select(false, false); } /* * Read a PSFTP command line from stdin. */ char *ssh_sftp_get_cmdline(const char *prompt, bool no_fds_ok) { char *buf; size_t buflen, bufsize; int ret; fputs(prompt, stdout); fflush(stdout); 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) { sgrowarray(buf, bufsize, buflen); 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) {} void platform_psftp_pre_conn_setup(LogPolicy *lp) {} const bool buildinfo_gtk_relevant = false; /* * Main program: do platform-specific initialisation and then call * psftp_main(). */ int main(int argc, char *argv[]) { uxsel_init(); return psftp_main(argc, argv); } putty-0.76/unix/uxsftpserver.c0000644000175000017500000004735114072266314013502 00000000000000/* * Implement the SftpServer abstraction, in the 'live' form (i.e. * really operating on the Unix filesystem). */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "putty.h" #include "ssh.h" #include "sshserver.h" #include "sftp.h" #include "tree234.h" typedef struct UnixSftpServer UnixSftpServer; struct UnixSftpServer { unsigned *fdseqs; bool *fdsopen; size_t fdsize; tree234 *dirhandles; int last_dirhandle_index; char handlekey[8]; SftpServer srv; }; struct uss_dirhandle { int index; DIR *dp; }; #define USS_DIRHANDLE_SEQ (0xFFFFFFFFU) static int uss_dirhandle_cmp(void *av, void *bv) { struct uss_dirhandle *a = (struct uss_dirhandle *)av; struct uss_dirhandle *b = (struct uss_dirhandle *)bv; if (a->index < b->index) return -1; if (a->index > b->index) return +1; return 0; } static SftpServer *uss_new(const SftpServerVtable *vt) { UnixSftpServer *uss = snew(UnixSftpServer); memset(uss, 0, sizeof(UnixSftpServer)); uss->dirhandles = newtree234(uss_dirhandle_cmp); uss->srv.vt = vt; make_unix_sftp_filehandle_key(uss->handlekey, sizeof(uss->handlekey)); return &uss->srv; } static void uss_free(SftpServer *srv) { UnixSftpServer *uss = container_of(srv, UnixSftpServer, srv); struct uss_dirhandle *udh; for (size_t i = 0; i < uss->fdsize; i++) if (uss->fdsopen[i]) close(i); sfree(uss->fdseqs); while ((udh = delpos234(uss->dirhandles, 0)) != NULL) { closedir(udh->dp); sfree(udh); } sfree(uss); } static void uss_return_handle_raw( UnixSftpServer *uss, SftpReplyBuilder *reply, int index, unsigned seq) { unsigned char handlebuf[8]; PUT_32BIT_MSB_FIRST(handlebuf, index); PUT_32BIT_MSB_FIRST(handlebuf + 4, seq); des_encrypt_xdmauth(uss->handlekey, handlebuf, 8); fxp_reply_handle(reply, make_ptrlen(handlebuf, 8)); } static bool uss_decode_handle( UnixSftpServer *uss, ptrlen handle, int *index, unsigned *seq) { unsigned char handlebuf[8]; if (handle.len != 8) return false; memcpy(handlebuf, handle.ptr, 8); des_decrypt_xdmauth(uss->handlekey, handlebuf, 8); *index = toint(GET_32BIT_MSB_FIRST(handlebuf)); *seq = GET_32BIT_MSB_FIRST(handlebuf + 4); return true; } static void uss_return_new_handle( UnixSftpServer *uss, SftpReplyBuilder *reply, int fd) { assert(fd >= 0); if (fd >= uss->fdsize) { size_t old_size = uss->fdsize; sgrowarray(uss->fdseqs, uss->fdsize, fd); uss->fdsopen = sresize(uss->fdsopen, uss->fdsize, bool); while (old_size < uss->fdsize) { uss->fdseqs[old_size] = 0; uss->fdsopen[old_size] = false; old_size++; } } assert(!uss->fdsopen[fd]); uss->fdsopen[fd] = true; if (++uss->fdseqs[fd] == USS_DIRHANDLE_SEQ) uss->fdseqs[fd] = 0; uss_return_handle_raw(uss, reply, fd, uss->fdseqs[fd]); } static int uss_try_lookup_fd(UnixSftpServer *uss, ptrlen handle) { int fd; unsigned seq; if (!uss_decode_handle(uss, handle, &fd, &seq) || fd < 0 || fd >= uss->fdsize || !uss->fdsopen[fd] || uss->fdseqs[fd] != seq) return -1; return fd; } static int uss_lookup_fd(UnixSftpServer *uss, SftpReplyBuilder *reply, ptrlen handle) { int fd = uss_try_lookup_fd(uss, handle); if (fd < 0) fxp_reply_error(reply, SSH_FX_FAILURE, "invalid file handle"); return fd; } static void uss_return_new_dirhandle( UnixSftpServer *uss, SftpReplyBuilder *reply, DIR *dp) { struct uss_dirhandle *udh = snew(struct uss_dirhandle); udh->index = uss->last_dirhandle_index++; udh->dp = dp; struct uss_dirhandle *added = add234(uss->dirhandles, udh); assert(added == udh); uss_return_handle_raw(uss, reply, udh->index, USS_DIRHANDLE_SEQ); } static struct uss_dirhandle *uss_try_lookup_dirhandle( UnixSftpServer *uss, ptrlen handle) { struct uss_dirhandle key, *udh; unsigned seq; if (!uss_decode_handle(uss, handle, &key.index, &seq) || seq != USS_DIRHANDLE_SEQ || (udh = find234(uss->dirhandles, &key, NULL)) == NULL) return NULL; return udh; } static struct uss_dirhandle *uss_lookup_dirhandle( UnixSftpServer *uss, SftpReplyBuilder *reply, ptrlen handle) { struct uss_dirhandle *udh = uss_try_lookup_dirhandle(uss, handle); if (!udh) fxp_reply_error(reply, SSH_FX_FAILURE, "invalid file handle"); return udh; } static void uss_error(UnixSftpServer *uss, SftpReplyBuilder *reply) { unsigned code = SSH_FX_FAILURE; switch (errno) { case ENOENT: code = SSH_FX_NO_SUCH_FILE; break; case EPERM: code = SSH_FX_PERMISSION_DENIED; break; } fxp_reply_error(reply, code, strerror(errno)); } static void uss_realpath(SftpServer *srv, SftpReplyBuilder *reply, ptrlen path) { UnixSftpServer *uss = container_of(srv, UnixSftpServer, srv); char *inpath = mkstr(path); char *outpath = realpath(inpath, NULL); free(inpath); if (!outpath) { uss_error(uss, reply); } else { fxp_reply_simple_name(reply, ptrlen_from_asciz(outpath)); free(outpath); } } static void uss_open(SftpServer *srv, SftpReplyBuilder *reply, ptrlen path, unsigned flags, struct fxp_attrs attrs) { UnixSftpServer *uss = container_of(srv, UnixSftpServer, srv); int openflags = 0; if (!((SSH_FXF_READ | SSH_FXF_WRITE) &~ flags)) openflags |= O_RDWR; else if (flags & SSH_FXF_WRITE) openflags |= O_WRONLY; else if (flags & SSH_FXF_READ) openflags |= O_RDONLY; if (flags & SSH_FXF_APPEND) openflags |= O_APPEND; if (flags & SSH_FXF_CREAT) openflags |= O_CREAT; if (flags & SSH_FXF_TRUNC) openflags |= O_TRUNC; if (flags & SSH_FXF_EXCL) openflags |= O_EXCL; char *pathstr = mkstr(path); int fd = open(pathstr, openflags, GET_PERMISSIONS(attrs, 0777)); free(pathstr); if (fd < 0) { uss_error(uss, reply); } else { uss_return_new_handle(uss, reply, fd); } } static void uss_opendir(SftpServer *srv, SftpReplyBuilder *reply, ptrlen path) { UnixSftpServer *uss = container_of(srv, UnixSftpServer, srv); char *pathstr = mkstr(path); DIR *dp = opendir(pathstr); free(pathstr); if (!dp) { uss_error(uss, reply); } else { uss_return_new_dirhandle(uss, reply, dp); } } static void uss_close(SftpServer *srv, SftpReplyBuilder *reply, ptrlen handle) { UnixSftpServer *uss = container_of(srv, UnixSftpServer, srv); int fd; struct uss_dirhandle *udh; if ((udh = uss_try_lookup_dirhandle(uss, handle)) != NULL) { closedir(udh->dp); del234(uss->dirhandles, udh); sfree(udh); fxp_reply_ok(reply); } else if ((fd = uss_lookup_fd(uss, reply, handle)) >= 0) { close(fd); assert(0 <= fd && fd <= uss->fdsize); uss->fdsopen[fd] = false; fxp_reply_ok(reply); } /* if both failed, uss_lookup_fd will have filled in an error response */ } static void uss_mkdir(SftpServer *srv, SftpReplyBuilder *reply, ptrlen path, struct fxp_attrs attrs) { UnixSftpServer *uss = container_of(srv, UnixSftpServer, srv); char *pathstr = mkstr(path); int status = mkdir(pathstr, GET_PERMISSIONS(attrs, 0777)); free(pathstr); if (status < 0) { uss_error(uss, reply); } else { fxp_reply_ok(reply); } } static void uss_rmdir(SftpServer *srv, SftpReplyBuilder *reply, ptrlen path) { UnixSftpServer *uss = container_of(srv, UnixSftpServer, srv); char *pathstr = mkstr(path); int status = rmdir(pathstr); free(pathstr); if (status < 0) { uss_error(uss, reply); } else { fxp_reply_ok(reply); } } static void uss_remove(SftpServer *srv, SftpReplyBuilder *reply, ptrlen path) { UnixSftpServer *uss = container_of(srv, UnixSftpServer, srv); char *pathstr = mkstr(path); int status = unlink(pathstr); free(pathstr); if (status < 0) { uss_error(uss, reply); } else { fxp_reply_ok(reply); } } static void uss_rename(SftpServer *srv, SftpReplyBuilder *reply, ptrlen srcpath, ptrlen dstpath) { UnixSftpServer *uss = container_of(srv, UnixSftpServer, srv); char *srcstr = mkstr(srcpath), *dststr = mkstr(dstpath); int status = rename(srcstr, dststr); free(srcstr); free(dststr); if (status < 0) { uss_error(uss, reply); } else { fxp_reply_ok(reply); } } static struct fxp_attrs uss_translate_struct_stat(const struct stat *st) { struct fxp_attrs attrs; attrs.flags = (SSH_FILEXFER_ATTR_SIZE | SSH_FILEXFER_ATTR_PERMISSIONS | SSH_FILEXFER_ATTR_UIDGID | SSH_FILEXFER_ATTR_ACMODTIME); attrs.size = st->st_size; attrs.permissions = st->st_mode; attrs.uid = st->st_uid; attrs.gid = st->st_gid; attrs.atime = st->st_atime; attrs.mtime = st->st_mtime; return attrs; } static void uss_reply_struct_stat(SftpReplyBuilder *reply, const struct stat *st) { fxp_reply_attrs(reply, uss_translate_struct_stat(st)); } static void uss_stat(SftpServer *srv, SftpReplyBuilder *reply, ptrlen path, bool follow_symlinks) { UnixSftpServer *uss = container_of(srv, UnixSftpServer, srv); struct stat st; char *pathstr = mkstr(path); int status = (follow_symlinks ? stat : lstat) (pathstr, &st); free(pathstr); if (status < 0) { uss_error(uss, reply); } else { uss_reply_struct_stat(reply, &st); } } static void uss_fstat(SftpServer *srv, SftpReplyBuilder *reply, ptrlen handle) { UnixSftpServer *uss = container_of(srv, UnixSftpServer, srv); struct stat st; int fd; if ((fd = uss_lookup_fd(uss, reply, handle)) < 0) return; int status = fstat(fd, &st); if (status < 0) { uss_error(uss, reply); } else { uss_reply_struct_stat(reply, &st); } } #if !HAVE_FUTIMES static inline int futimes(int fd, const struct timeval tv[2]) { /* If the OS doesn't support futimes(3) then we have to pretend it * always returns failure */ errno = EINVAL; return -1; } #endif /* * The guts of setstat and fsetstat, macroised so that they can call * fchown(fd,...) or chown(path,...) depending on parameters. */ #define SETSTAT_GUTS(api_prefix, api_arg, attrs, success) do \ { \ if (attrs.flags & SSH_FILEXFER_ATTR_SIZE) \ if (api_prefix(truncate)(api_arg, attrs.size) < 0) \ success = false; \ if (attrs.flags & SSH_FILEXFER_ATTR_UIDGID) \ if (api_prefix(chown)(api_arg, attrs.uid, attrs.gid) < 0) \ success = false; \ if (attrs.flags & SSH_FILEXFER_ATTR_PERMISSIONS) \ if (api_prefix(chmod)(api_arg, attrs.permissions) < 0) \ success = false; \ if (attrs.flags & SSH_FILEXFER_ATTR_ACMODTIME) { \ struct timeval tv[2]; \ tv[0].tv_sec = attrs.atime; \ tv[1].tv_sec = attrs.mtime; \ tv[0].tv_usec = tv[1].tv_usec = 0; \ if (api_prefix(utimes)(api_arg, tv) < 0) \ success = false; \ } \ } while (0) #define PATH_PREFIX(func) func #define FD_PREFIX(func) f ## func static void uss_setstat(SftpServer *srv, SftpReplyBuilder *reply, ptrlen path, struct fxp_attrs attrs) { UnixSftpServer *uss = container_of(srv, UnixSftpServer, srv); char *pathstr = mkstr(path); bool success = true; SETSTAT_GUTS(PATH_PREFIX, pathstr, attrs, success); free(pathstr); if (!success) { uss_error(uss, reply); } else { fxp_reply_ok(reply); } } static void uss_fsetstat(SftpServer *srv, SftpReplyBuilder *reply, ptrlen handle, struct fxp_attrs attrs) { UnixSftpServer *uss = container_of(srv, UnixSftpServer, srv); int fd; if ((fd = uss_lookup_fd(uss, reply, handle)) < 0) return; bool success = true; SETSTAT_GUTS(FD_PREFIX, fd, attrs, success); if (!success) { uss_error(uss, reply); } else { fxp_reply_ok(reply); } } static void uss_read(SftpServer *srv, SftpReplyBuilder *reply, ptrlen handle, uint64_t offset, unsigned length) { UnixSftpServer *uss = container_of(srv, UnixSftpServer, srv); int fd; char *buf; if ((fd = uss_lookup_fd(uss, reply, handle)) < 0) return; if ((buf = malloc(length)) == NULL) { /* A rare case in which I bother to check malloc failure, * because in this case we can localise the problem easily by * turning it into a failure response from this one sftp * request */ fxp_reply_error(reply, SSH_FX_FAILURE, "Out of memory for read buffer"); return; } char *p = buf; int status = lseek(fd, offset, SEEK_SET); if (status >= 0 || errno == ESPIPE) { bool seekable = (status >= 0); while (length > 0) { status = read(fd, p, length); if (status <= 0) break; unsigned bytes_read = status; assert(bytes_read <= length); length -= bytes_read; p += bytes_read; if (!seekable) { /* * If the seek failed because the file is fundamentally * not a seekable kind of thing, abandon this loop after * one attempt, i.e. we just read whatever we could get * and we don't mind returning a short buffer. */ } } } if (status < 0) { uss_error(uss, reply); } else if (p == buf) { fxp_reply_error(reply, SSH_FX_EOF, "End of file"); } else { fxp_reply_data(reply, make_ptrlen(buf, p - buf)); } free(buf); } static void uss_write(SftpServer *srv, SftpReplyBuilder *reply, ptrlen handle, uint64_t offset, ptrlen data) { UnixSftpServer *uss = container_of(srv, UnixSftpServer, srv); int fd; if ((fd = uss_lookup_fd(uss, reply, handle)) < 0) return; const char *p = data.ptr; unsigned length = data.len; int status = lseek(fd, offset, SEEK_SET); if (status >= 0 || errno == ESPIPE) { while (length > 0) { status = write(fd, p, length); assert(status != 0); if (status < 0) break; unsigned bytes_written = status; assert(bytes_written <= length); length -= bytes_written; p += bytes_written; } } if (status < 0) { uss_error(uss, reply); } else { fxp_reply_ok(reply); } } static void uss_readdir(SftpServer *srv, SftpReplyBuilder *reply, ptrlen handle, int max_entries, bool omit_longname) { UnixSftpServer *uss = container_of(srv, UnixSftpServer, srv); struct dirent *de; struct uss_dirhandle *udh; if ((udh = uss_lookup_dirhandle(uss, reply, handle)) == NULL) return; errno = 0; de = readdir(udh->dp); if (!de) { if (errno == 0) { fxp_reply_error(reply, SSH_FX_EOF, "End of directory"); } else { uss_error(uss, reply); } } else { ptrlen longname = PTRLEN_LITERAL(""); char *longnamebuf = NULL; struct fxp_attrs attrs = no_attrs; #if defined HAVE_FSTATAT && defined HAVE_DIRFD struct stat st; if (!fstatat(dirfd(udh->dp), de->d_name, &st, AT_SYMLINK_NOFOLLOW)) { char perms[11], *uidbuf = NULL, *gidbuf = NULL; struct passwd *pwd; struct group *grp; const char *user, *group; struct tm tm; attrs = uss_translate_struct_stat(&st); if (!omit_longname) { strcpy(perms, "----------"); switch (st.st_mode & S_IFMT) { case S_IFBLK: perms[0] = 'b'; break; case S_IFCHR: perms[0] = 'c'; break; case S_IFDIR: perms[0] = 'd'; break; case S_IFIFO: perms[0] = 'p'; break; case S_IFLNK: perms[0] = 'l'; break; case S_IFSOCK: perms[0] = 's'; break; } if (st.st_mode & S_IRUSR) perms[1] = 'r'; if (st.st_mode & S_IWUSR) perms[2] = 'w'; if (st.st_mode & S_IXUSR) perms[3] = (st.st_mode & S_ISUID ? 's' : 'x'); else perms[3] = (st.st_mode & S_ISUID ? 'S' : '-'); if (st.st_mode & S_IRGRP) perms[4] = 'r'; if (st.st_mode & S_IWGRP) perms[5] = 'w'; if (st.st_mode & S_IXGRP) perms[6] = (st.st_mode & S_ISGID ? 's' : 'x'); else perms[6] = (st.st_mode & S_ISGID ? 'S' : '-'); if (st.st_mode & S_IROTH) perms[7] = 'r'; if (st.st_mode & S_IWOTH) perms[8] = 'w'; if (st.st_mode & S_IXOTH) perms[9] = 'x'; if ((pwd = getpwuid(st.st_uid)) != NULL) user = pwd->pw_name; else user = uidbuf = dupprintf("%u", (unsigned)st.st_uid); if ((grp = getgrgid(st.st_gid)) != NULL) group = grp->gr_name; else group = gidbuf = dupprintf("%u", (unsigned)st.st_gid); tm = *localtime(&st.st_mtime); longnamebuf = dupprintf( "%s %3u %-8s %-8s %8"PRIuMAX" %.3s %2d %02d:%02d %s", perms, (unsigned)st.st_nlink, user, group, (uintmax_t)st.st_size, (&"JanFebMarAprMayJunJulAugSepOctNovDec"[3*tm.tm_mon]), tm.tm_mday, tm.tm_hour, tm.tm_min, de->d_name); longname = ptrlen_from_asciz(longnamebuf); sfree(uidbuf); sfree(gidbuf); } } #endif /* FIXME: be able to return more than one, in which case we * must also check max_entries */ fxp_reply_name_count(reply, 1); fxp_reply_full_name(reply, ptrlen_from_asciz(de->d_name), longname, attrs); sfree(longnamebuf); } } const SftpServerVtable unix_live_sftpserver_vt = { .new = uss_new, .free = uss_free, .realpath = uss_realpath, .open = uss_open, .opendir = uss_opendir, .close = uss_close, .mkdir = uss_mkdir, .rmdir = uss_rmdir, .remove = uss_remove, .rename = uss_rename, .stat = uss_stat, .fstat = uss_fstat, .setstat = uss_setstat, .fsetstat = uss_fsetstat, .read = uss_read, .write = uss_write, .readdir = uss_readdir, }; putty-0.76/unix/uxshare.c0000644000175000017500000002672214072266314012400 00000000000000/* * Unix implementation of SSH connection-sharing IPC setup. */ #include #include #include #include #include #include #include #include #include #include "tree234.h" #include "putty.h" #include "network.h" #include "proxy.h" #include "ssh.h" #define CONNSHARE_SOCKETDIR_PREFIX "/tmp/putty-connshare" #define SALT_FILENAME "salt" #define SALT_SIZE 64 #ifndef PIPE_BUF #define PIPE_BUF _POSIX_PIPE_BUF #endif static char *make_parentdir_name(void) { char *username, *parent; username = get_username(); parent = dupprintf("%s.%s", CONNSHARE_SOCKETDIR_PREFIX, username); sfree(username); assert(*parent == '/'); return parent; } static char *make_dirname(const char *pi_name, char **logtext) { char *name, *parentdirname, *dirname, *err; /* * First, create the top-level directory for all shared PuTTY * connections owned by this user. */ parentdirname = make_parentdir_name(); if ((err = make_dir_and_check_ours(parentdirname)) != NULL) { *logtext = err; sfree(parentdirname); return NULL; } /* * Transform the platform-independent version of the connection * identifier into the name we'll actually use for the directory * containing the Unix socket. * * We do this by hashing the identifier with some user-specific * secret information, to avoid the privacy leak of having * "user@host" strings show up in 'netstat -x'. (Irritatingly, the * full pathname of a Unix-domain socket _does_ show up in the * 'netstat -x' output, at least on Linux, even if that socket is * in a directory not readable to the user running netstat. You'd * think putting things inside an 0700 directory would hide their * names from other users, but no.) * * The secret information we use to salt the hash lives in a file * inside the top-level directory we just created, so we must * first create that file (with some fresh random data in it) if * it's not already been done by a previous PuTTY. */ { unsigned char saltbuf[SALT_SIZE]; char *saltname; int saltfd, i, ret; saltname = dupprintf("%s/%s", parentdirname, SALT_FILENAME); saltfd = open(saltname, O_RDONLY); if (saltfd < 0) { char *tmpname; int pid; if (errno != ENOENT) { *logtext = dupprintf("%s: open: %s", saltname, strerror(errno)); sfree(saltname); sfree(parentdirname); return NULL; } /* * The salt file doesn't already exist, so try to create * it. Another process may be attempting the same thing * simultaneously, so we must do this carefully: we write * a salt file under a different name, then hard-link it * into place, which guarantees that we won't change the * contents of an existing salt file. */ pid = getpid(); for (i = 0;; i++) { tmpname = dupprintf("%s/%s.tmp.%d.%d", parentdirname, SALT_FILENAME, pid, i); saltfd = open(tmpname, O_WRONLY | O_EXCL | O_CREAT, 0400); if (saltfd >= 0) break; if (errno != EEXIST) { *logtext = dupprintf("%s: open: %s", tmpname, strerror(errno)); sfree(tmpname); sfree(saltname); sfree(parentdirname); return NULL; } sfree(tmpname); /* go round and try again with i+1 */ } /* * Invent some random data. */ random_read(saltbuf, SALT_SIZE); ret = write(saltfd, saltbuf, SALT_SIZE); /* POSIX atomicity guarantee: because we wrote less than * PIPE_BUF bytes, the write either completed in full or * failed. */ assert(SALT_SIZE < PIPE_BUF); assert(ret < 0 || ret == SALT_SIZE); if (ret < 0) { close(saltfd); *logtext = dupprintf("%s: write: %s", tmpname, strerror(errno)); sfree(tmpname); sfree(saltname); sfree(parentdirname); return NULL; } if (close(saltfd) < 0) { *logtext = dupprintf("%s: close: %s", tmpname, strerror(errno)); sfree(tmpname); sfree(saltname); sfree(parentdirname); return NULL; } /* * Now attempt to hard-link our temp file into place. We * tolerate EEXIST as an outcome, because that just means * another PuTTY got their attempt in before we did (and * we only care that there is a valid salt file we can * agree on, no matter who created it). */ if (link(tmpname, saltname) < 0 && errno != EEXIST) { *logtext = dupprintf("%s: link: %s", saltname, strerror(errno)); sfree(tmpname); sfree(saltname); sfree(parentdirname); return NULL; } /* * Whether that succeeded or not, get rid of our temp file. */ if (unlink(tmpname) < 0) { *logtext = dupprintf("%s: unlink: %s", tmpname, strerror(errno)); sfree(tmpname); sfree(saltname); sfree(parentdirname); return NULL; } /* * And now we've arranged for there to be a salt file, so * we can try to open it for reading again and this time * expect it to work. */ sfree(tmpname); saltfd = open(saltname, O_RDONLY); if (saltfd < 0) { *logtext = dupprintf("%s: open: %s", saltname, strerror(errno)); sfree(saltname); sfree(parentdirname); return NULL; } } for (i = 0; i < SALT_SIZE; i++) { ret = read(saltfd, saltbuf, SALT_SIZE); if (ret <= 0) { close(saltfd); *logtext = dupprintf("%s: read: %s", saltname, ret == 0 ? "unexpected EOF" : strerror(errno)); sfree(saltname); sfree(parentdirname); return NULL; } assert(0 < ret && ret <= SALT_SIZE - i); i += ret; } close(saltfd); sfree(saltname); /* * Now we've got our salt, hash it with the connection * identifier to produce our actual socket name. */ { unsigned char digest[32]; char retbuf[65]; ssh_hash *h = ssh_hash_new(&ssh_sha256); put_string(h, saltbuf, SALT_SIZE); put_stringz(h, pi_name); ssh_hash_final(h, digest); /* * And make it printable. */ for (i = 0; i < 32; i++) { sprintf(retbuf + 2*i, "%02x", digest[i]); /* the last of those will also write the trailing NUL */ } name = dupstr(retbuf); } smemclr(saltbuf, sizeof(saltbuf)); } dirname = dupprintf("%s/%s", parentdirname, name); sfree(parentdirname); sfree(name); return dirname; } int platform_ssh_share(const char *pi_name, Conf *conf, Plug *downplug, Plug *upplug, Socket **sock, char **logtext, char **ds_err, char **us_err, bool can_upstream, bool can_downstream) { char *dirname, *lockname, *sockname, *err; int lockfd; Socket *retsock; /* * Sort out what we're going to call the directory in which we * keep the socket. This has the side effect of potentially * creating its top-level containing dir and/or the salt file * within that, if they don't already exist. */ dirname = make_dirname(pi_name, logtext); if (!dirname) { return SHARE_NONE; } /* * Now make sure the subdirectory exists. */ if ((err = make_dir_and_check_ours(dirname)) != NULL) { *logtext = err; sfree(dirname); return SHARE_NONE; } /* * Acquire a lock on a file in that directory. */ lockname = dupcat(dirname, "/lock"); 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); 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); return SHARE_NONE; } sockname = dupprintf("%s/socket", dirname); *logtext = NULL; if (can_downstream) { retsock = new_connection(unix_sock_addr(sockname), "", 0, false, true, false, false, downplug, conf); if (sk_socket_error(retsock) == NULL) { sfree(*logtext); *logtext = sockname; *sock = retsock; sfree(dirname); sfree(lockname); close(lockfd); 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); 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); return SHARE_NONE; } void platform_ssh_share_cleanup(const char *name) { char *dirname, *filename, *logtext; dirname = make_dirname(name, &logtext); if (!dirname) { sfree(logtext); /* we can't do much with this */ return; } filename = dupcat(dirname, "/socket"); remove(filename); sfree(filename); filename = dupcat(dirname, "/lock"); remove(filename); sfree(filename); rmdir(dirname); /* * We deliberately _don't_ clean up the parent directory * /tmp/putty-connshare., 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. Also, the salt * file in it can be reused. */ sfree(dirname); } putty-0.76/unix/uxsignal.c0000644000175000017500000000213514072266314012543 00000000000000#include #include #include #include "defs.h" /* * Calling signal() is non-portable, as it varies in meaning * between platforms and depending on feature macros, and has * stupid semantics at least some of the time. * * This function provides the same interface as the libc function, * but provides consistent semantics. It assumes POSIX semantics * for sigaction() (so you might need to do some more work if you * port to something ancient like SunOS 4) */ void (*putty_signal(int sig, void (*func)(int)))(int) { struct sigaction sa; struct sigaction old; sa.sa_handler = func; if(sigemptyset(&sa.sa_mask) < 0) return SIG_ERR; sa.sa_flags = SA_RESTART; if(sigaction(sig, &sa, &old) < 0) return SIG_ERR; return old.sa_handler; } void block_signal(int sig, bool block_it) { sigset_t ss; sigemptyset(&ss); sigaddset(&ss, sig); if(sigprocmask(block_it ? SIG_BLOCK : SIG_UNBLOCK, &ss, 0) < 0) { perror("sigprocmask"); exit(1); } } /* Local Variables: c-basic-offset:4 comment-column:40 End: */ putty-0.76/unix/uxsocks.c0000644000175000017500000001002014072266314012400 00000000000000/* * Main program for Unix psocks. */ #include #include #include #include #include #include #include #include "putty.h" #include "ssh.h" #include "psocks.h" const bool buildinfo_gtk_relevant = false; typedef struct PsocksDataSinkPopen { stdio_sink sink[2]; PsocksDataSink pds; } PsocksDataSinkPopen; static void popen_free(PsocksDataSink *pds) { PsocksDataSinkPopen *pdsp = container_of(pds, PsocksDataSinkPopen, pds); for (size_t i = 0; i < 2; i++) pclose(pdsp->sink[i].fp); sfree(pdsp); } static PsocksDataSink *open_pipes( const char *cmd, const char *const *direction_args, const char *index_arg, char **err) { FILE *fp[2]; char *errmsg = NULL; for (size_t i = 0; i < 2; i++) { /* No escaping needed: the provided command is already * shell-quoted, and our extra arguments are simple */ char *command = dupprintf("%s %s %s", cmd, direction_args[i], index_arg); fp[i] = popen(command, "w"); sfree(command); if (!fp[i]) { if (!errmsg) errmsg = dupprintf("%s", strerror(errno)); } } if (errmsg) { for (size_t i = 0; i < 2; i++) if (fp[i]) pclose(fp[i]); *err = errmsg; return NULL; } PsocksDataSinkPopen *pdsp = snew(PsocksDataSinkPopen); for (size_t i = 0; i < 2; i++) { setvbuf(fp[i], NULL, _IONBF, 0); stdio_sink_init(&pdsp->sink[i], fp[i]); pdsp->pds.s[i] = BinarySink_UPCAST(&pdsp->sink[i]); } pdsp->pds.free = popen_free; return &pdsp->pds; } static int signalpipe[2] = { -1, -1 }; static void sigchld(int signum) { if (write(signalpipe[1], "x", 1) <= 0) /* not much we can do about it */; } static pid_t subcommand_pid = -1; static bool still_running = true; static void start_subcommand(strbuf *args) { pid_t pid; /* * Set up the pipe we'll use to tell us about SIGCHLD. */ if (pipe(signalpipe) < 0) { perror("pipe"); exit(1); } putty_signal(SIGCHLD, sigchld); /* * Make an array of argument pointers that execvp will like. */ size_t nargs = 0; for (size_t i = 0; i < args->len; i++) if (args->s[i] == '\0') nargs++; char **exec_args = snewn(nargs + 1, char *); char *p = args->s; for (size_t a = 0; a < nargs; a++) { exec_args[a] = p; size_t len = strlen(p); assert(len < args->len - (p - args->s)); p += 1 + len; } exec_args[nargs] = NULL; pid = fork(); if (pid < 0) { perror("fork"); exit(1); } else if (pid == 0) { execvp(exec_args[0], exec_args); perror("exec"); _exit(127); } else { subcommand_pid = pid; sfree(exec_args); } } static const PsocksPlatform platform = { open_pipes, start_subcommand, }; static bool psocks_pw_setup(void *ctx, pollwrapper *pw) { if (signalpipe[0] >= 0) pollwrap_add_fd_rwx(pw, signalpipe[0], SELECT_R); return true; } static void psocks_pw_check(void *ctx, pollwrapper *pw) { if (signalpipe[0] >= 0 && pollwrap_check_fd_rwx(pw, signalpipe[0], SELECT_R)) { while (true) { int status; pid_t pid = waitpid(-1, &status, WNOHANG); if (pid <= 0) break; if (pid == subcommand_pid) still_running = false; } } } static bool psocks_continue(void *ctx, bool found_any_fd, bool ran_any_callback) { return still_running; } typedef bool (*cliloop_continue_t)(void *ctx, bool found_any_fd, bool ran_any_callback); int main(int argc, char **argv) { psocks_state *ps = psocks_new(&platform); psocks_cmdline(ps, argc, argv); sk_init(); uxsel_init(); psocks_start(ps); cli_main_loop(psocks_pw_setup, psocks_pw_check, psocks_continue, NULL); } putty-0.76/unix/uxstore.c0000644000175000017500000005024614072266314012430 00000000000000/* * uxstore.c: Unix-specific implementation of the interface defined * in storage.h. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "putty.h" #include "storage.h" #include "tree234.h" #ifdef PATH_MAX #define FNLEN PATH_MAX #else #define FNLEN 1024 /* XXX */ #endif enum { INDEX_DIR, INDEX_HOSTKEYS, INDEX_HOSTKEYS_TMP, INDEX_RANDSEED, INDEX_SESSIONDIR, INDEX_SESSION, }; static const char hex[16] = "0123456789ABCDEF"; static void make_session_filename(const char *in, strbuf *out) { if (!in || !*in) in = "Default Settings"; while (*in) { /* * There are remarkably few punctuation characters that * aren't shell-special in some way or likely to be used as * separators in some file format or another! Hence we use * opt-in for safe characters rather than opt-out for * specific unsafe ones... */ if (*in!='+' && *in!='-' && *in!='.' && *in!='@' && *in!='_' && !(*in >= '0' && *in <= '9') && !(*in >= 'A' && *in <= 'Z') && !(*in >= 'a' && *in <= 'z')) { put_byte(out, '%'); put_byte(out, hex[((unsigned char) *in) >> 4]); put_byte(out, hex[((unsigned char) *in) & 15]); } else put_byte(out, *in); in++; } } static void decode_session_filename(const char *in, strbuf *out) { while (*in) { if (*in == '%' && in[1] && in[2]) { int i, j; i = in[1] - '0'; i -= (i > 9 ? 7 : 0); j = in[2] - '0'; j -= (j > 9 ? 7 : 0); put_byte(out, (i << 4) + j); in += 3; } else { put_byte(out, *in++); } } } static char *make_filename(int index, const char *subname) { char *env, *tmp, *ret; /* * Allow override of the PuTTY configuration location, and of * specific subparts of it, by means of environment variables. */ if (index == INDEX_DIR) { struct passwd *pwd; char *xdg_dir, *old_dir, *old_dir2, *old_dir3, *home, *pwd_home; env = getenv("PUTTYDIR"); if (env) return dupstr(env); home = getenv("HOME"); pwd = getpwuid(getuid()); if (pwd && pwd->pw_dir) { pwd_home = pwd->pw_dir; } else { pwd_home = NULL; } xdg_dir = NULL; env = getenv("XDG_CONFIG_HOME"); if (env && *env) { xdg_dir = dupprintf("%s/putty", env); } if (!xdg_dir) { if (home) { tmp = home; } else if (pwd_home) { tmp = pwd_home; } else { tmp = ""; } xdg_dir = dupprintf("%s/.config/putty", tmp); } if (xdg_dir && access(xdg_dir, F_OK) == 0) { return xdg_dir; } old_dir = old_dir2 = old_dir3 = NULL; if (home) { old_dir = dupprintf("%s/.putty", home); } if (pwd_home) { old_dir2 = dupprintf("%s/.putty", pwd_home); } old_dir3 = dupstr("/.putty"); if (old_dir && access(old_dir, F_OK) == 0) { ret = old_dir; goto out; } if (old_dir2 && access(old_dir2, F_OK) == 0) { ret = old_dir2; goto out; } if (access(old_dir3, F_OK) == 0) { ret = old_dir3; goto out; } #ifdef XDG_DEFAULT if (xdg_dir) { ret = xdg_dir; goto out; } #endif ret = old_dir ? old_dir : (old_dir2 ? old_dir2 : old_dir3); out: if (ret != old_dir) sfree(old_dir); if (ret != old_dir2) sfree(old_dir2); if (ret != old_dir3) sfree(old_dir3); if (ret != xdg_dir) sfree(xdg_dir); return ret; } if (index == INDEX_SESSIONDIR) { env = getenv("PUTTYSESSIONS"); if (env) return dupstr(env); tmp = make_filename(INDEX_DIR, NULL); ret = dupprintf("%s/sessions", tmp); sfree(tmp); return ret; } if (index == INDEX_SESSION) { strbuf *sb = strbuf_new(); tmp = make_filename(INDEX_SESSIONDIR, NULL); strbuf_catf(sb, "%s/", tmp); sfree(tmp); make_session_filename(subname, sb); return strbuf_to_str(sb); } if (index == INDEX_HOSTKEYS) { env = getenv("PUTTYSSHHOSTKEYS"); if (env) return dupstr(env); tmp = make_filename(INDEX_DIR, NULL); ret = dupprintf("%s/sshhostkeys", tmp); sfree(tmp); return ret; } if (index == INDEX_HOSTKEYS_TMP) { tmp = make_filename(INDEX_HOSTKEYS, NULL); ret = dupprintf("%s.tmp", tmp); sfree(tmp); return ret; } if (index == INDEX_RANDSEED) { env = getenv("PUTTYRANDOMSEED"); if (env) return dupstr(env); tmp = make_filename(INDEX_DIR, NULL); ret = dupprintf("%s/randomseed", tmp); sfree(tmp); return ret; } tmp = make_filename(INDEX_DIR, NULL); ret = dupprintf("%s/ERROR", tmp); sfree(tmp); return ret; } struct settings_w { FILE *fp; }; settings_w *open_settings_w(const char *sessionname, char **errmsg) { char *filename, *err; FILE *fp; *errmsg = NULL; /* * Start by making sure the .putty directory and its sessions * subdir actually exist. */ filename = make_filename(INDEX_DIR, NULL); if ((err = make_dir_path(filename, 0700)) != NULL) { *errmsg = dupprintf("Unable to save session: %s", err); sfree(err); sfree(filename); return NULL; } sfree(filename); filename = make_filename(INDEX_SESSIONDIR, NULL); if ((err = make_dir_path(filename, 0700)) != NULL) { *errmsg = dupprintf("Unable to save session: %s", err); sfree(err); 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); settings_w *toret = snew(settings_w); toret->fp = fp; return toret; } void write_setting_s(settings_w *handle, const char *key, const char *value) { fprintf(handle->fp, "%s=%s\n", key, value); } void write_setting_i(settings_w *handle, const char *key, int value) { fprintf(handle->fp, "%s=%d\n", key, value); } void close_settings_w(settings_w *handle) { fclose(handle->fp); sfree(handle); } /* ---------------------------------------------------------------------- * System for treating X resources as a fallback source of defaults, * after data read from a saved-session disk file. * * The read_setting_* functions will call get_setting(key) as a * fallback if the setting isn't in the file they loaded. That in turn * will hand on to x_get_default, which the front end application * provides, and which actually reads resources from the X server (if * appropriate). In between, there's a tree234 of X-resource shaped * settings living locally in this file: the front end can call * provide_xrm_string() to insert a setting into this tree (typically * in response to an -xrm command line option or similar), and those * will override the actual X resources. */ struct skeyval { const char *key; const char *value; }; static tree234 *xrmtree = NULL; static int keycmp(void *av, void *bv) { struct skeyval *a = (struct skeyval *)av; struct skeyval *b = (struct skeyval *)bv; return strcmp(a->key, b->key); } void provide_xrm_string(const char *string, const char *progname) { const char *p, *q; char *key; struct skeyval *xrms, *ret; p = q = strchr(string, ':'); if (!q) { fprintf(stderr, "%s: expected a colon in resource string" " \"%s\"\n", progname, string); return; } q++; while (p > string && p[-1] != '.' && p[-1] != '*') p--; xrms = snew(struct skeyval); key = snewn(q-p, char); memcpy(key, p, q-p); key[q-p-1] = '\0'; xrms->key = key; while (*q && isspace((unsigned char)*q)) q++; xrms->value = dupstr(q); if (!xrmtree) xrmtree = newtree234(keycmp); ret = add234(xrmtree, xrms); if (ret) { /* Override an existing string. */ del234(xrmtree, ret); add234(xrmtree, xrms); } } static const char *get_setting(const char *key) { struct skeyval tmp, *ret; tmp.key = key; if (xrmtree) { ret = find234(xrmtree, &tmp, NULL); if (ret) return ret->value; } return x_get_default(key); } /* ---------------------------------------------------------------------- * Main code for reading settings from a disk file, calling the above * get_setting() as a fallback if necessary. */ struct settings_r { tree234 *t; }; settings_r *open_settings_r(const char *sessionname) { char *filename; FILE *fp; char *line; settings_r *toret; filename = make_filename(INDEX_SESSION, sessionname); fp = fopen(filename, "r"); sfree(filename); if (!fp) return NULL; /* can't open */ toret = snew(settings_r); toret->t = 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(toret->t, kv); sfree(line); } fclose(fp); return toret; } char *read_setting_s(settings_r *handle, const char *key) { const char *val; struct skeyval tmp, *kv; tmp.key = key; if (handle != NULL && (kv = find234(handle->t, &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(settings_r *handle, const char *key, int defvalue) { const char *val; struct skeyval tmp, *kv; tmp.key = key; if (handle != NULL && (kv = find234(handle->t, &tmp, NULL)) != NULL) { val = kv->value; assert(val != NULL); } else val = get_setting(key); if (!val) return defvalue; else return atoi(val); } FontSpec *read_setting_fontspec(settings_r *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"); 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); FontSpec *fs = fontspec_new(tmp2); sfree(tmp2); sfree(tmp); return fs; } else { sfree(tmp); return NULL; } } Filename *read_setting_filename(settings_r *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(settings_w *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"); write_setting_s(handle, suffname, fs->name); sfree(suffname); } void write_setting_filename(settings_w *handle, const char *name, Filename *result) { write_setting_s(handle, name, result->path); } void close_settings_r(settings_r *handle) { struct skeyval *kv; if (!handle) return; while ( (kv = index234(handle->t, 0)) != NULL) { del234(handle->t, kv); sfree((char *)kv->key); sfree((char *)kv->value); sfree(kv); } freetree234(handle->t); sfree(handle); } void del_settings(const char *sessionname) { char *filename; filename = make_filename(INDEX_SESSION, sessionname); unlink(filename); sfree(filename); } struct settings_e { DIR *dp; }; settings_e *enum_settings_start(void) { DIR *dp; char *filename; filename = make_filename(INDEX_SESSIONDIR, NULL); dp = opendir(filename); sfree(filename); settings_e *toret = snew(settings_e); toret->dp = dp; return toret; } bool enum_settings_next(settings_e *handle, strbuf *out) { struct dirent *de; struct stat st; strbuf *fullpath; if (!handle->dp) return NULL; fullpath = strbuf_new(); char *sessiondir = make_filename(INDEX_SESSIONDIR, NULL); put_datapl(fullpath, ptrlen_from_asciz(sessiondir)); sfree(sessiondir); put_byte(fullpath, '/'); size_t baselen = fullpath->len; while ( (de = readdir(handle->dp)) != NULL ) { strbuf_shrink_to(fullpath, baselen); put_datapl(fullpath, ptrlen_from_asciz(de->d_name)); if (stat(fullpath->s, &st) < 0 || !S_ISREG(st.st_mode)) continue; /* try another one */ decode_session_filename(de->d_name, out); strbuf_free(fullpath); return true; } strbuf_free(fullpath); return false; } void enum_settings_finish(settings_e *handle) { if (handle->dp) closedir(handle->dp); sfree(handle); } /* * Lines in the host keys file are of the form * * type@port:hostname keydata * * e.g. * * rsa@22:foovax.example.org 0x23,0x293487364395345345....2343 */ int verify_host_key(const char *hostname, int port, const char *keytype, const char *key) { FILE *fp; char *filename; char *line; int ret; filename = make_filename(INDEX_HOSTKEYS, NULL); fp = fopen(filename, "r"); sfree(filename); if (!fp) return 1; /* key does not exist */ ret = 1; while ( (line = fgetline(fp)) ) { int i; char *p = line; char porttext[20]; line[strcspn(line, "\n")] = '\0'; /* strip trailing newline */ i = strlen(keytype); if (strncmp(p, keytype, i)) goto done; p += i; if (*p != '@') goto done; p++; sprintf(porttext, "%d", port); i = strlen(porttext); if (strncmp(p, porttext, i)) goto done; p += i; if (*p != ':') goto done; p++; i = strlen(hostname); if (strncmp(p, hostname, i)) goto done; p += i; if (*p != ' ') goto done; p++; /* * Found the key. Now just work out whether it's the right * one or not. */ if (!strcmp(p, key)) ret = 0; /* key matched OK */ else ret = 2; /* key mismatch */ done: sfree(line); if (ret != 1) break; } fclose(fp); return ret; } bool have_ssh_host_key(const char *hostname, int port, const char *keytype) { /* * If we have a host key, verify_host_key will return 0 or 2. * If we don't have one, it'll return 1. */ return verify_host_key(hostname, port, keytype, "") != 1; } void store_host_key(const char *hostname, int port, 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, *errmsg; dir = make_filename(INDEX_DIR, NULL); if ((errmsg = make_dir_path(dir, 0700)) != NULL) { nonfatal("Unable to store host key: %s", errmsg); sfree(errmsg); sfree(dir); sfree(tmpfilename); return; } sfree(dir); wfp = fopen(tmpfilename, "w"); } if (!wfp) { nonfatal("Unable to store host key: open(\"%s\") " "returned '%s'", tmpfilename, strerror(errno)); 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) { nonfatal("Unable to store host key: rename(\"%s\",\"%s\")" " returned '%s'", tmpfilename, filename, strerror(errno)); } sfree(tmpfilename); sfree(filename); sfree(newtext); } void read_random_seed(noise_consumer_t consumer) { int fd; char *fname; fname = make_filename(INDEX_RANDSEED, NULL); fd = open(fname, O_RDONLY); sfree(fname); if (fd >= 0) { char buf[512]; int ret; while ( (ret = read(fd, buf, sizeof(buf))) > 0) consumer(buf, ret); close(fd); } } void write_random_seed(void *data, int len) { int fd; char *fname; fname = make_filename(INDEX_RANDSEED, NULL); /* * 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) { nonfatal("Unable to write random seed: open(\"%s\") " "returned '%s'", fname, strerror(errno)); sfree(fname); return; } char *dir, *errmsg; dir = make_filename(INDEX_DIR, NULL); if ((errmsg = make_dir_path(dir, 0700)) != NULL) { nonfatal("Unable to write random seed: %s", errmsg); sfree(errmsg); sfree(fname); sfree(dir); return; } sfree(dir); fd = open(fname, O_CREAT | O_WRONLY, 0600); if (fd < 0) { nonfatal("Unable to write random seed: open(\"%s\") " "returned '%s'", fname, strerror(errno)); sfree(fname); return; } } while (len > 0) { int ret = write(fd, data, len); if (ret < 0) { nonfatal("Unable to write random seed: write " "returned '%s'", strerror(errno)); break; } len -= ret; data = (char *)data + len; } close(fd); sfree(fname); } void cleanup_all(void) { } putty-0.76/unix/uxucs.c0000644000175000017500000001713514072266314012066 00000000000000#include #include #include #include #include #include #include #include "putty.h" #include "charset.h" #include "terminal.h" #include "misc.h" /* * Unix Unicode-handling routines. */ bool is_dbcs_leadbyte(int codepage, char byte) { return false; /* 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, const char *defchr, struct unicode_data *ucsdata) { if (codepage == DEFAULT_CODEPAGE) { char output[MB_LEN_MAX]; mbstate_t state; int n = 0; memset(&state, 0, sizeof state); while (wclen > 0) { size_t 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) mbstr[n++] = *defchr; wcstr++; wclen--; } return n; } else { return charset_from_unicode(&wcstr, &wclen, mbstr, mblen, codepage, NULL, defchr?defchr:NULL, defchr?1:0); } } /* * Return value is true if pterm is to run in direct-to-font mode. */ bool init_ucs(struct unicode_data *ucsdata, char *linecharset, bool utf8_override, int font_charset, int vtmode) { int i; bool ret = false; /* * In the platform-independent parts of the code, font_codepage * is used only for system DBCS support - which we don't * support at all. So we set this to something which will never * be used. */ ucsdata->font_codepage = -1; /* * If utf8_override is set and the POSIX locale settings * dictate a UTF-8 character set, then just go straight for * UTF-8. */ ucsdata->line_codepage = CS_NONE; if (utf8_override) { const char *s; if (((s = getenv("LC_ALL")) && *s) || ((s = getenv("LC_CTYPE")) && *s) || ((s = getenv("LANG")) && *s)) { 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 * we use that. If it's still CS_NONE after _that_ - i.e. the * font we were given had an incomprehensible charset - then we * fall back to using the D800 page. */ if (ucsdata->line_codepage == CS_NONE) ucsdata->line_codepage = font_charset; if (ucsdata->line_codepage == CS_NONE) ret = true; /* * 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; else if (1 == charset_to_unicode(&p, &len, wc, 1, ucsdata->line_codepage, NULL, L"", 0)) ucsdata->unitab_line[i] = wc[0]; else ucsdata->unitab_line[i] = 0xFFFD; } /* * Set up unitab_xterm. This is the same as unitab_line except * in the line-drawing regions, where it follows the Unicode * encoding. * * (Note that the strange X encoding of line-drawing characters * in the bottom 32 glyphs of ISO8859-1 fonts is taken care of * by the font encoding, which will spot such a font and act as * if it were in a variant encoding of ISO8859-1.) */ for (i = 0; i < 256; i++) { static const wchar_t unitab_xterm_std[32] = { 0x2666, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0, 0x00b1, 0x2424, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c, 0x23ba, 0x23bb, 0x2500, 0x23bc, 0x23bd, 0x251c, 0x2524, 0x2534, 0x252c, 0x2502, 0x2264, 0x2265, 0x03c0, 0x2260, 0x00a3, 0x00b7, 0x0020 }; static const wchar_t unitab_xterm_poorman[32] = L"*#****o~**+++++-----++++|****L. "; const wchar_t *ptr; if (vtmode == VT_POORMAN) ptr = unitab_xterm_poorman; else ptr = unitab_xterm_std; if (i >= 0x5F && i < 0x7F) ucsdata->unitab_xterm[i] = ptr[i & 0x1F]; else ucsdata->unitab_xterm[i] = ucsdata->unitab_line[i]; } /* * 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]; else ucsdata->unitab_scoacs[i] = 0xFFFD; } /* * Find the control characters in the line codepage. For * direct-to-font mode using the D800 hack, we assume 00-1F and * 7F are controls, but allow 80-9F through. (It's as good a * guess as anything; and my bet is that half the weird fonts * used in this way will be IBM or MS code pages anyway.) */ for (i = 0; i < 256; i++) { int lineval = ucsdata->unitab_line[i]; if (lineval < ' ' || (lineval >= 0x7F && lineval < 0xA0) || (lineval >= 0xD800 && lineval < 0xD820) || (lineval == 0xD87F)) ucsdata->unitab_ctrl[i] = i; else ucsdata->unitab_ctrl[i] = 0xFF; } return ret; } const char *cp_name(int codepage) { if (codepage == CS_NONE) 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); } putty-0.76/unix/uxutils.c0000644000175000017500000000274414072266314012434 00000000000000#include "putty.h" #include "ssh.h" #include "uxutils.h" #if defined __arm__ || defined __aarch64__ bool platform_aes_hw_available(void) { #if defined HWCAP_AES return getauxval(AT_HWCAP) & HWCAP_AES; #elif defined HWCAP2_AES return getauxval(AT_HWCAP2) & HWCAP2_AES; #elif defined __APPLE__ /* M1 macOS defines no optional sysctl flag indicating presence of * the AES extension, which I assume to be because it's always * present */ return true; #else return false; #endif } bool platform_sha256_hw_available(void) { #if defined HWCAP_SHA2 return getauxval(AT_HWCAP) & HWCAP_SHA2; #elif defined HWCAP2_SHA2 return getauxval(AT_HWCAP2) & HWCAP2_SHA2; #elif defined __APPLE__ /* Assume always present on M1 macOS, similarly to AES */ return true; #else return false; #endif } bool platform_sha1_hw_available(void) { #if defined HWCAP_SHA1 return getauxval(AT_HWCAP) & HWCAP_SHA1; #elif defined HWCAP2_SHA1 return getauxval(AT_HWCAP2) & HWCAP2_SHA1; #elif defined __APPLE__ /* Assume always present on M1 macOS, similarly to AES */ return true; #else return false; #endif } bool platform_sha512_hw_available(void) { #if defined HWCAP_SHA512 return getauxval(AT_HWCAP) & HWCAP_SHA512; #elif defined HWCAP2_SHA512 return getauxval(AT_HWCAP2) & HWCAP2_SHA512; #elif defined __APPLE__ return test_sysctl_flag("hw.optional.armv8_2_sha512"); #else return false; #endif } #endif /* defined __arm__ || defined __aarch64__ */ putty-0.76/unix/uxutils.h0000644000175000017500000000323014072266314012430 00000000000000/* * uxutils.h: header included only by uxutils.c. * * The only reason this is a header file instead of a source file is * so that I can define 'static inline' functions which may or may not * be used, without provoking a compiler warning when I turn out not * to use them in the subsequent source file. */ #ifndef PUTTY_UXUTILS_H #define PUTTY_UXUTILS_H #if defined __APPLE__ #ifdef HAVE_SYS_SYSCTL_H #include #endif #endif /* defined __APPLE__ */ #if defined __arm__ || defined __aarch64__ #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_SYS_AUXV_H #include #endif #ifdef HAVE_ASM_HWCAP_H #include #endif #if defined HAVE_GETAUXVAL /* No code needed: getauxval has just the API we want already */ #elif defined HAVE_ELF_AUX_INFO /* Implement the simple getauxval API in terms of FreeBSD elf_aux_info */ static inline u_long getauxval(int which) { u_long toret; if (elf_aux_info(which, &toret, sizeof(toret)) != 0) return 0; /* elf_aux_info didn't work */ return toret; } #else /* Implement a stub getauxval which returns no capabilities */ static inline u_long getauxval(int which) { return 0; } #endif #endif /* defined __arm__ || defined __aarch64__ */ #if defined __APPLE__ static inline bool test_sysctl_flag(const char *flagname) { #ifdef HAVE_SYSCTLBYNAME int value; size_t size = sizeof(value); return (sysctlbyname(flagname, &value, &size, NULL, 0) == 0 && size == sizeof(value) && value != 0); #else /* HAVE_SYSCTLBYNAME */ return false; #endif /* HAVE_SYSCTLBYNAME */ } #endif /* defined __APPLE__ */ #endif /* PUTTY_UXUTILS_H */ putty-0.76/unix/x11misc.c0000644000175000017500000000406414072266314012201 00000000000000/* * x11misc.c: miscellaneous stuff for dealing directly with X servers. */ #include #include #include #include #include #include "putty.h" #ifndef NOT_X_WINDOWS #include #include #include #include "x11misc.h" /* ---------------------------------------------------------------------- * Error handling mechanism which permits us to ignore specific X11 * errors from particular requests. We maintain a list of upcoming * potential error events that we want to not treat as fatal errors. */ static int (*orig_x11_error_handler)(Display *thisdisp, XErrorEvent *err); struct x11_err_to_ignore { Display *display; unsigned char error_code; unsigned long serial; }; static struct x11_err_to_ignore *errs; static size_t nerrs, errsize; static int x11_error_handler(Display *thisdisp, XErrorEvent *err) { for (size_t i = 0; i < nerrs; i++) { if (thisdisp == errs[i].display && err->serial == errs[i].serial && err->error_code == errs[i].error_code) { /* Ok, this is an error we're happy to ignore */ return 0; } } return (*orig_x11_error_handler)(thisdisp, err); } void x11_ignore_error(Display *disp, unsigned char errcode) { /* * Install our error handler, if we haven't already. */ if (!orig_x11_error_handler) orig_x11_error_handler = XSetErrorHandler(x11_error_handler); /* * This is as good a moment as any to winnow the ignore list based * on requests we know to have been processed. */ { unsigned long last = LastKnownRequestProcessed(disp); size_t i, j; for (i = j = 0; i < nerrs; i++) { if (errs[i].display == disp && errs[i].serial <= last) continue; errs[j++] = errs[i]; } nerrs = j; } sgrowarray(errs, errsize, nerrs); errs[nerrs].display = disp; errs[nerrs].error_code = errcode; errs[nerrs].serial = NextRequest(disp); nerrs++; } #endif putty-0.76/unix/x11misc.h0000644000175000017500000000067414072266314012211 00000000000000/* * x11misc.h: header file for functions that need to refer to Xlib * data types. Has to be separate from unix.h so that we can include * it only after including the X headers, which in turn has to be done * after putty.h has told us whether NOT_X_WINDOWS is defined. */ #ifndef NOT_X_WINDOWS /* * x11misc.c. */ void x11_ignore_error(Display *disp, unsigned char errcode); /* * gtkmisc.c */ Display *get_x11_display(void); #endif putty-0.76/unix/xkeysym.c0000644000175000017500000005002614072266314012424 00000000000000/* * xkeysym.c: mapping from X keysyms to Unicode values * * The basic idea of this is shamelessly cribbed from xterm. The * actual character data is generated from Markus Kuhn's proposed * redraft of the X11 keysym mapping table, using the following * piece of Perl/sh code: wget -q -O - http://www.cl.cam.ac.uk/~mgk25/ucs/X11.keysyms | \ perl -ne '/^(\d+)\s+(\d+)\s+[\d\/]+\s+U\+([\dA-Fa-f]+)/ and' \ -e ' do { $a{$1 * 256+ $2} = hex $3; };' \ -e 'END { foreach $i (sort {$a <=> $b} keys %a) {' \ -e ' printf " {0x%x, 0x%x},\n", $i, $a{$i} } }' \ -e 'BEGIN { $a{0x13a4} = 0x20ac }' * (The BEGIN clause inserts a mapping for the Euro sign which for * some reason isn't in the list but xterm supports. *shrug*.) */ #include "misc.h" struct keysym { /* * Currently nothing in here is above 0xFFFF, so I'll use * `unsigned short' to save space. */ unsigned short keysym; unsigned short unicode; }; static struct keysym keysyms[] = { {0x20, 0x20}, {0x21, 0x21}, {0x22, 0x22}, {0x23, 0x23}, {0x24, 0x24}, {0x25, 0x25}, {0x26, 0x26}, {0x27, 0x27}, {0x28, 0x28}, {0x29, 0x29}, {0x2a, 0x2a}, {0x2b, 0x2b}, {0x2c, 0x2c}, {0x2d, 0x2d}, {0x2e, 0x2e}, {0x2f, 0x2f}, {0x30, 0x30}, {0x31, 0x31}, {0x32, 0x32}, {0x33, 0x33}, {0x34, 0x34}, {0x35, 0x35}, {0x36, 0x36}, {0x37, 0x37}, {0x38, 0x38}, {0x39, 0x39}, {0x3a, 0x3a}, {0x3b, 0x3b}, {0x3c, 0x3c}, {0x3d, 0x3d}, {0x3e, 0x3e}, {0x3f, 0x3f}, {0x40, 0x40}, {0x41, 0x41}, {0x42, 0x42}, {0x43, 0x43}, {0x44, 0x44}, {0x45, 0x45}, {0x46, 0x46}, {0x47, 0x47}, {0x48, 0x48}, {0x49, 0x49}, {0x4a, 0x4a}, {0x4b, 0x4b}, {0x4c, 0x4c}, {0x4d, 0x4d}, {0x4e, 0x4e}, {0x4f, 0x4f}, {0x50, 0x50}, {0x51, 0x51}, {0x52, 0x52}, {0x53, 0x53}, {0x54, 0x54}, {0x55, 0x55}, {0x56, 0x56}, {0x57, 0x57}, {0x58, 0x58}, {0x59, 0x59}, {0x5a, 0x5a}, {0x5b, 0x5b}, {0x5c, 0x5c}, {0x5d, 0x5d}, {0x5e, 0x5e}, {0x5f, 0x5f}, {0x60, 0x60}, {0x61, 0x61}, {0x62, 0x62}, {0x63, 0x63}, {0x64, 0x64}, {0x65, 0x65}, {0x66, 0x66}, {0x67, 0x67}, {0x68, 0x68}, {0x69, 0x69}, {0x6a, 0x6a}, {0x6b, 0x6b}, {0x6c, 0x6c}, {0x6d, 0x6d}, {0x6e, 0x6e}, {0x6f, 0x6f}, {0x70, 0x70}, {0x71, 0x71}, {0x72, 0x72}, {0x73, 0x73}, {0x74, 0x74}, {0x75, 0x75}, {0x76, 0x76}, {0x77, 0x77}, {0x78, 0x78}, {0x79, 0x79}, {0x7a, 0x7a}, {0x7b, 0x7b}, {0x7c, 0x7c}, {0x7d, 0x7d}, {0x7e, 0x7e}, {0xa0, 0xa0}, {0xa1, 0xa1}, {0xa2, 0xa2}, {0xa3, 0xa3}, {0xa4, 0xa4}, {0xa5, 0xa5}, {0xa6, 0xa6}, {0xa7, 0xa7}, {0xa8, 0xa8}, {0xa9, 0xa9}, {0xaa, 0xaa}, {0xab, 0xab}, {0xac, 0xac}, {0xad, 0xad}, {0xae, 0xae}, {0xaf, 0xaf}, {0xb0, 0xb0}, {0xb1, 0xb1}, {0xb2, 0xb2}, {0xb3, 0xb3}, {0xb4, 0xb4}, {0xb5, 0xb5}, {0xb6, 0xb6}, {0xb7, 0xb7}, {0xb8, 0xb8}, {0xb9, 0xb9}, {0xba, 0xba}, {0xbb, 0xbb}, {0xbc, 0xbc}, {0xbd, 0xbd}, {0xbe, 0xbe}, {0xbf, 0xbf}, {0xc0, 0xc0}, {0xc1, 0xc1}, {0xc2, 0xc2}, {0xc3, 0xc3}, {0xc4, 0xc4}, {0xc5, 0xc5}, {0xc6, 0xc6}, {0xc7, 0xc7}, {0xc8, 0xc8}, {0xc9, 0xc9}, {0xca, 0xca}, {0xcb, 0xcb}, {0xcc, 0xcc}, {0xcd, 0xcd}, {0xce, 0xce}, {0xcf, 0xcf}, {0xd0, 0xd0}, {0xd1, 0xd1}, {0xd2, 0xd2}, {0xd3, 0xd3}, {0xd4, 0xd4}, {0xd5, 0xd5}, {0xd6, 0xd6}, {0xd7, 0xd7}, {0xd8, 0xd8}, {0xd9, 0xd9}, {0xda, 0xda}, {0xdb, 0xdb}, {0xdc, 0xdc}, {0xdd, 0xdd}, {0xde, 0xde}, {0xdf, 0xdf}, {0xe0, 0xe0}, {0xe1, 0xe1}, {0xe2, 0xe2}, {0xe3, 0xe3}, {0xe4, 0xe4}, {0xe5, 0xe5}, {0xe6, 0xe6}, {0xe7, 0xe7}, {0xe8, 0xe8}, {0xe9, 0xe9}, {0xea, 0xea}, {0xeb, 0xeb}, {0xec, 0xec}, {0xed, 0xed}, {0xee, 0xee}, {0xef, 0xef}, {0xf0, 0xf0}, {0xf1, 0xf1}, {0xf2, 0xf2}, {0xf3, 0xf3}, {0xf4, 0xf4}, {0xf5, 0xf5}, {0xf6, 0xf6}, {0xf7, 0xf7}, {0xf8, 0xf8}, {0xf9, 0xf9}, {0xfa, 0xfa}, {0xfb, 0xfb}, {0xfc, 0xfc}, {0xfd, 0xfd}, {0xfe, 0xfe}, {0xff, 0xff}, {0x1a1, 0x104}, {0x1a2, 0x2d8}, {0x1a3, 0x141}, {0x1a5, 0x13d}, {0x1a6, 0x15a}, {0x1a9, 0x160}, {0x1aa, 0x15e}, {0x1ab, 0x164}, {0x1ac, 0x179}, {0x1ae, 0x17d}, {0x1af, 0x17b}, {0x1b1, 0x105}, {0x1b2, 0x2db}, {0x1b3, 0x142}, {0x1b5, 0x13e}, {0x1b6, 0x15b}, {0x1b7, 0x2c7}, {0x1b9, 0x161}, {0x1ba, 0x15f}, {0x1bb, 0x165}, {0x1bc, 0x17a}, {0x1bd, 0x2dd}, {0x1be, 0x17e}, {0x1bf, 0x17c}, {0x1c0, 0x154}, {0x1c3, 0x102}, {0x1c5, 0x139}, {0x1c6, 0x106}, {0x1c8, 0x10c}, {0x1ca, 0x118}, {0x1cc, 0x11a}, {0x1cf, 0x10e}, {0x1d0, 0x110}, {0x1d1, 0x143}, {0x1d2, 0x147}, {0x1d5, 0x150}, {0x1d8, 0x158}, {0x1d9, 0x16e}, {0x1db, 0x170}, {0x1de, 0x162}, {0x1e0, 0x155}, {0x1e3, 0x103}, {0x1e5, 0x13a}, {0x1e6, 0x107}, {0x1e8, 0x10d}, {0x1ea, 0x119}, {0x1ec, 0x11b}, {0x1ef, 0x10f}, {0x1f0, 0x111}, {0x1f1, 0x144}, {0x1f2, 0x148}, {0x1f5, 0x151}, {0x1f8, 0x159}, {0x1f9, 0x16f}, {0x1fb, 0x171}, {0x1fe, 0x163}, {0x1ff, 0x2d9}, {0x2a1, 0x126}, {0x2a6, 0x124}, {0x2a9, 0x130}, {0x2ab, 0x11e}, {0x2ac, 0x134}, {0x2b1, 0x127}, {0x2b6, 0x125}, {0x2b9, 0x131}, {0x2bb, 0x11f}, {0x2bc, 0x135}, {0x2c5, 0x10a}, {0x2c6, 0x108}, {0x2d5, 0x120}, {0x2d8, 0x11c}, {0x2dd, 0x16c}, {0x2de, 0x15c}, {0x2e5, 0x10b}, {0x2e6, 0x109}, {0x2f5, 0x121}, {0x2f8, 0x11d}, {0x2fd, 0x16d}, {0x2fe, 0x15d}, {0x3a2, 0x138}, {0x3a3, 0x156}, {0x3a5, 0x128}, {0x3a6, 0x13b}, {0x3aa, 0x112}, {0x3ab, 0x122}, {0x3ac, 0x166}, {0x3b3, 0x157}, {0x3b5, 0x129}, {0x3b6, 0x13c}, {0x3ba, 0x113}, {0x3bb, 0x123}, {0x3bc, 0x167}, {0x3bd, 0x14a}, {0x3bf, 0x14b}, {0x3c0, 0x100}, {0x3c7, 0x12e}, {0x3cc, 0x116}, {0x3cf, 0x12a}, {0x3d1, 0x145}, {0x3d2, 0x14c}, {0x3d3, 0x136}, {0x3d9, 0x172}, {0x3dd, 0x168}, {0x3de, 0x16a}, {0x3e0, 0x101}, {0x3e7, 0x12f}, {0x3ec, 0x117}, {0x3ef, 0x12b}, {0x3f1, 0x146}, {0x3f2, 0x14d}, {0x3f3, 0x137}, {0x3f9, 0x173}, {0x3fd, 0x169}, {0x3fe, 0x16b}, {0x47e, 0x203e}, {0x4a1, 0x3002}, {0x4a2, 0x300c}, {0x4a3, 0x300d}, {0x4a4, 0x3001}, {0x4a5, 0x30fb}, {0x4a6, 0x30f2}, {0x4a7, 0x30a1}, {0x4a8, 0x30a3}, {0x4a9, 0x30a5}, {0x4aa, 0x30a7}, {0x4ab, 0x30a9}, {0x4ac, 0x30e3}, {0x4ad, 0x30e5}, {0x4ae, 0x30e7}, {0x4af, 0x30c3}, {0x4b0, 0x30fc}, {0x4b1, 0x30a2}, {0x4b2, 0x30a4}, {0x4b3, 0x30a6}, {0x4b4, 0x30a8}, {0x4b5, 0x30aa}, {0x4b6, 0x30ab}, {0x4b7, 0x30ad}, {0x4b8, 0x30af}, {0x4b9, 0x30b1}, {0x4ba, 0x30b3}, {0x4bb, 0x30b5}, {0x4bc, 0x30b7}, {0x4bd, 0x30b9}, {0x4be, 0x30bb}, {0x4bf, 0x30bd}, {0x4c0, 0x30bf}, {0x4c1, 0x30c1}, {0x4c2, 0x30c4}, {0x4c3, 0x30c6}, {0x4c4, 0x30c8}, {0x4c5, 0x30ca}, {0x4c6, 0x30cb}, {0x4c7, 0x30cc}, {0x4c8, 0x30cd}, {0x4c9, 0x30ce}, {0x4ca, 0x30cf}, {0x4cb, 0x30d2}, {0x4cc, 0x30d5}, {0x4cd, 0x30d8}, {0x4ce, 0x30db}, {0x4cf, 0x30de}, {0x4d0, 0x30df}, {0x4d1, 0x30e0}, {0x4d2, 0x30e1}, {0x4d3, 0x30e2}, {0x4d4, 0x30e4}, {0x4d5, 0x30e6}, {0x4d6, 0x30e8}, {0x4d7, 0x30e9}, {0x4d8, 0x30ea}, {0x4d9, 0x30eb}, {0x4da, 0x30ec}, {0x4db, 0x30ed}, {0x4dc, 0x30ef}, {0x4dd, 0x30f3}, {0x4de, 0x309b}, {0x4df, 0x309c}, {0x5ac, 0x60c}, {0x5bb, 0x61b}, {0x5bf, 0x61f}, {0x5c1, 0x621}, {0x5c2, 0x622}, {0x5c3, 0x623}, {0x5c4, 0x624}, {0x5c5, 0x625}, {0x5c6, 0x626}, {0x5c7, 0x627}, {0x5c8, 0x628}, {0x5c9, 0x629}, {0x5ca, 0x62a}, {0x5cb, 0x62b}, {0x5cc, 0x62c}, {0x5cd, 0x62d}, {0x5ce, 0x62e}, {0x5cf, 0x62f}, {0x5d0, 0x630}, {0x5d1, 0x631}, {0x5d2, 0x632}, {0x5d3, 0x633}, {0x5d4, 0x634}, {0x5d5, 0x635}, {0x5d6, 0x636}, {0x5d7, 0x637}, {0x5d8, 0x638}, {0x5d9, 0x639}, {0x5da, 0x63a}, {0x5e0, 0x640}, {0x5e1, 0x641}, {0x5e2, 0x642}, {0x5e3, 0x643}, {0x5e4, 0x644}, {0x5e5, 0x645}, {0x5e6, 0x646}, {0x5e7, 0x647}, {0x5e8, 0x648}, {0x5e9, 0x649}, {0x5ea, 0x64a}, {0x5eb, 0x64b}, {0x5ec, 0x64c}, {0x5ed, 0x64d}, {0x5ee, 0x64e}, {0x5ef, 0x64f}, {0x5f0, 0x650}, {0x5f1, 0x651}, {0x5f2, 0x652}, {0x6a1, 0x452}, {0x6a2, 0x453}, {0x6a3, 0x451}, {0x6a4, 0x454}, {0x6a5, 0x455}, {0x6a6, 0x456}, {0x6a7, 0x457}, {0x6a8, 0x458}, {0x6a9, 0x459}, {0x6aa, 0x45a}, {0x6ab, 0x45b}, {0x6ac, 0x45c}, {0x6ae, 0x45e}, {0x6af, 0x45f}, {0x6b0, 0x2116}, {0x6b1, 0x402}, {0x6b2, 0x403}, {0x6b3, 0x401}, {0x6b4, 0x404}, {0x6b5, 0x405}, {0x6b6, 0x406}, {0x6b7, 0x407}, {0x6b8, 0x408}, {0x6b9, 0x409}, {0x6ba, 0x40a}, {0x6bb, 0x40b}, {0x6bc, 0x40c}, {0x6be, 0x40e}, {0x6bf, 0x40f}, {0x6c0, 0x44e}, {0x6c1, 0x430}, {0x6c2, 0x431}, {0x6c3, 0x446}, {0x6c4, 0x434}, {0x6c5, 0x435}, {0x6c6, 0x444}, {0x6c7, 0x433}, {0x6c8, 0x445}, {0x6c9, 0x438}, {0x6ca, 0x439}, {0x6cb, 0x43a}, {0x6cc, 0x43b}, {0x6cd, 0x43c}, {0x6ce, 0x43d}, {0x6cf, 0x43e}, {0x6d0, 0x43f}, {0x6d1, 0x44f}, {0x6d2, 0x440}, {0x6d3, 0x441}, {0x6d4, 0x442}, {0x6d5, 0x443}, {0x6d6, 0x436}, {0x6d7, 0x432}, {0x6d8, 0x44c}, {0x6d9, 0x44b}, {0x6da, 0x437}, {0x6db, 0x448}, {0x6dc, 0x44d}, {0x6dd, 0x449}, {0x6de, 0x447}, {0x6df, 0x44a}, {0x6e0, 0x42e}, {0x6e1, 0x410}, {0x6e2, 0x411}, {0x6e3, 0x426}, {0x6e4, 0x414}, {0x6e5, 0x415}, {0x6e6, 0x424}, {0x6e7, 0x413}, {0x6e8, 0x425}, {0x6e9, 0x418}, {0x6ea, 0x419}, {0x6eb, 0x41a}, {0x6ec, 0x41b}, {0x6ed, 0x41c}, {0x6ee, 0x41d}, {0x6ef, 0x41e}, {0x6f0, 0x41f}, {0x6f1, 0x42f}, {0x6f2, 0x420}, {0x6f3, 0x421}, {0x6f4, 0x422}, {0x6f5, 0x423}, {0x6f6, 0x416}, {0x6f7, 0x412}, {0x6f8, 0x42c}, {0x6f9, 0x42b}, {0x6fa, 0x417}, {0x6fb, 0x428}, {0x6fc, 0x42d}, {0x6fd, 0x429}, {0x6fe, 0x427}, {0x6ff, 0x42a}, {0x7a1, 0x386}, {0x7a2, 0x388}, {0x7a3, 0x389}, {0x7a4, 0x38a}, {0x7a5, 0x3aa}, {0x7a7, 0x38c}, {0x7a8, 0x38e}, {0x7a9, 0x3ab}, {0x7ab, 0x38f}, {0x7ae, 0x385}, {0x7af, 0x2015}, {0x7b1, 0x3ac}, {0x7b2, 0x3ad}, {0x7b3, 0x3ae}, {0x7b4, 0x3af}, {0x7b5, 0x3ca}, {0x7b6, 0x390}, {0x7b7, 0x3cc}, {0x7b8, 0x3cd}, {0x7b9, 0x3cb}, {0x7ba, 0x3b0}, {0x7bb, 0x3ce}, {0x7c1, 0x391}, {0x7c2, 0x392}, {0x7c3, 0x393}, {0x7c4, 0x394}, {0x7c5, 0x395}, {0x7c6, 0x396}, {0x7c7, 0x397}, {0x7c8, 0x398}, {0x7c9, 0x399}, {0x7ca, 0x39a}, {0x7cb, 0x39b}, {0x7cc, 0x39c}, {0x7cd, 0x39d}, {0x7ce, 0x39e}, {0x7cf, 0x39f}, {0x7d0, 0x3a0}, {0x7d1, 0x3a1}, {0x7d2, 0x3a3}, {0x7d4, 0x3a4}, {0x7d5, 0x3a5}, {0x7d6, 0x3a6}, {0x7d7, 0x3a7}, {0x7d8, 0x3a8}, {0x7d9, 0x3a9}, {0x7e1, 0x3b1}, {0x7e2, 0x3b2}, {0x7e3, 0x3b3}, {0x7e4, 0x3b4}, {0x7e5, 0x3b5}, {0x7e6, 0x3b6}, {0x7e7, 0x3b7}, {0x7e8, 0x3b8}, {0x7e9, 0x3b9}, {0x7ea, 0x3ba}, {0x7eb, 0x3bb}, {0x7ec, 0x3bc}, {0x7ed, 0x3bd}, {0x7ee, 0x3be}, {0x7ef, 0x3bf}, {0x7f0, 0x3c0}, {0x7f1, 0x3c1}, {0x7f2, 0x3c3}, {0x7f3, 0x3c2}, {0x7f4, 0x3c4}, {0x7f5, 0x3c5}, {0x7f6, 0x3c6}, {0x7f7, 0x3c7}, {0x7f8, 0x3c8}, {0x7f9, 0x3c9}, {0x8a1, 0x23b7}, {0x8a2, 0x250c}, {0x8a3, 0x2500}, {0x8a4, 0x2320}, {0x8a5, 0x2321}, {0x8a6, 0x2502}, {0x8a7, 0x23a1}, {0x8a8, 0x23a3}, {0x8a9, 0x23a4}, {0x8aa, 0x23a6}, {0x8ab, 0x239b}, {0x8ac, 0x239d}, {0x8ad, 0x239e}, {0x8ae, 0x23a0}, {0x8af, 0x23a8}, {0x8b0, 0x23ac}, {0x8bc, 0x2264}, {0x8bd, 0x2260}, {0x8be, 0x2265}, {0x8bf, 0x222b}, {0x8c0, 0x2234}, {0x8c1, 0x221d}, {0x8c2, 0x221e}, {0x8c5, 0x2207}, {0x8c8, 0x223c}, {0x8c9, 0x2243}, {0x8cd, 0x21d4}, {0x8ce, 0x21d2}, {0x8cf, 0x2261}, {0x8d6, 0x221a}, {0x8da, 0x2282}, {0x8db, 0x2283}, {0x8dc, 0x2229}, {0x8dd, 0x222a}, {0x8de, 0x2227}, {0x8df, 0x2228}, {0x8ef, 0x2202}, {0x8f6, 0x192}, {0x8fb, 0x2190}, {0x8fc, 0x2191}, {0x8fd, 0x2192}, {0x8fe, 0x2193}, {0x9e0, 0x25c6}, {0x9e1, 0x2592}, {0x9e2, 0x2409}, {0x9e3, 0x240c}, {0x9e4, 0x240d}, {0x9e5, 0x240a}, {0x9e8, 0x2424}, {0x9e9, 0x240b}, {0x9ea, 0x2518}, {0x9eb, 0x2510}, {0x9ec, 0x250c}, {0x9ed, 0x2514}, {0x9ee, 0x253c}, {0x9ef, 0x23ba}, {0x9f0, 0x23bb}, {0x9f1, 0x2500}, {0x9f2, 0x23bc}, {0x9f3, 0x23bd}, {0x9f4, 0x251c}, {0x9f5, 0x2524}, {0x9f6, 0x2534}, {0x9f7, 0x252c}, {0x9f8, 0x2502}, {0xaa1, 0x2003}, {0xaa2, 0x2002}, {0xaa3, 0x2004}, {0xaa4, 0x2005}, {0xaa5, 0x2007}, {0xaa6, 0x2008}, {0xaa7, 0x2009}, {0xaa8, 0x200a}, {0xaa9, 0x2014}, {0xaaa, 0x2013}, {0xaae, 0x2026}, {0xaaf, 0x2025}, {0xab0, 0x2153}, {0xab1, 0x2154}, {0xab2, 0x2155}, {0xab3, 0x2156}, {0xab4, 0x2157}, {0xab5, 0x2158}, {0xab6, 0x2159}, {0xab7, 0x215a}, {0xab8, 0x2105}, {0xabb, 0x2012}, {0xabc, 0x2329}, {0xabe, 0x232a}, {0xac3, 0x215b}, {0xac4, 0x215c}, {0xac5, 0x215d}, {0xac6, 0x215e}, {0xac9, 0x2122}, {0xaca, 0x2613}, {0xacc, 0x25c1}, {0xacd, 0x25b7}, {0xace, 0x25cb}, {0xacf, 0x25af}, {0xad0, 0x2018}, {0xad1, 0x2019}, {0xad2, 0x201c}, {0xad3, 0x201d}, {0xad4, 0x211e}, {0xad6, 0x2032}, {0xad7, 0x2033}, {0xad9, 0x271d}, {0xadb, 0x25ac}, {0xadc, 0x25c0}, {0xadd, 0x25b6}, {0xade, 0x25cf}, {0xadf, 0x25ae}, {0xae0, 0x25e6}, {0xae1, 0x25ab}, {0xae2, 0x25ad}, {0xae3, 0x25b3}, {0xae4, 0x25bd}, {0xae5, 0x2606}, {0xae6, 0x2022}, {0xae7, 0x25aa}, {0xae8, 0x25b2}, {0xae9, 0x25bc}, {0xaea, 0x261c}, {0xaeb, 0x261e}, {0xaec, 0x2663}, {0xaed, 0x2666}, {0xaee, 0x2665}, {0xaf0, 0x2720}, {0xaf1, 0x2020}, {0xaf2, 0x2021}, {0xaf3, 0x2713}, {0xaf4, 0x2717}, {0xaf5, 0x266f}, {0xaf6, 0x266d}, {0xaf7, 0x2642}, {0xaf8, 0x2640}, {0xaf9, 0x260e}, {0xafa, 0x2315}, {0xafb, 0x2117}, {0xafc, 0x2038}, {0xafd, 0x201a}, {0xafe, 0x201e}, {0xba3, 0x3c}, {0xba6, 0x3e}, {0xba8, 0x2228}, {0xba9, 0x2227}, {0xbc0, 0xaf}, {0xbc2, 0x22a5}, {0xbc3, 0x2229}, {0xbc4, 0x230a}, {0xbc6, 0x5f}, {0xbca, 0x2218}, {0xbcc, 0x2395}, {0xbce, 0x22a4}, {0xbcf, 0x25cb}, {0xbd3, 0x2308}, {0xbd6, 0x222a}, {0xbd8, 0x2283}, {0xbda, 0x2282}, {0xbdc, 0x22a2}, {0xbfc, 0x22a3}, {0xcdf, 0x2017}, {0xce0, 0x5d0}, {0xce1, 0x5d1}, {0xce2, 0x5d2}, {0xce3, 0x5d3}, {0xce4, 0x5d4}, {0xce5, 0x5d5}, {0xce6, 0x5d6}, {0xce7, 0x5d7}, {0xce8, 0x5d8}, {0xce9, 0x5d9}, {0xcea, 0x5da}, {0xceb, 0x5db}, {0xcec, 0x5dc}, {0xced, 0x5dd}, {0xcee, 0x5de}, {0xcef, 0x5df}, {0xcf0, 0x5e0}, {0xcf1, 0x5e1}, {0xcf2, 0x5e2}, {0xcf3, 0x5e3}, {0xcf4, 0x5e4}, {0xcf5, 0x5e5}, {0xcf6, 0x5e6}, {0xcf7, 0x5e7}, {0xcf8, 0x5e8}, {0xcf9, 0x5e9}, {0xcfa, 0x5ea}, {0xda1, 0xe01}, {0xda2, 0xe02}, {0xda3, 0xe03}, {0xda4, 0xe04}, {0xda5, 0xe05}, {0xda6, 0xe06}, {0xda7, 0xe07}, {0xda8, 0xe08}, {0xda9, 0xe09}, {0xdaa, 0xe0a}, {0xdab, 0xe0b}, {0xdac, 0xe0c}, {0xdad, 0xe0d}, {0xdae, 0xe0e}, {0xdaf, 0xe0f}, {0xdb0, 0xe10}, {0xdb1, 0xe11}, {0xdb2, 0xe12}, {0xdb3, 0xe13}, {0xdb4, 0xe14}, {0xdb5, 0xe15}, {0xdb6, 0xe16}, {0xdb7, 0xe17}, {0xdb8, 0xe18}, {0xdb9, 0xe19}, {0xdba, 0xe1a}, {0xdbb, 0xe1b}, {0xdbc, 0xe1c}, {0xdbd, 0xe1d}, {0xdbe, 0xe1e}, {0xdbf, 0xe1f}, {0xdc0, 0xe20}, {0xdc1, 0xe21}, {0xdc2, 0xe22}, {0xdc3, 0xe23}, {0xdc4, 0xe24}, {0xdc5, 0xe25}, {0xdc6, 0xe26}, {0xdc7, 0xe27}, {0xdc8, 0xe28}, {0xdc9, 0xe29}, {0xdca, 0xe2a}, {0xdcb, 0xe2b}, {0xdcc, 0xe2c}, {0xdcd, 0xe2d}, {0xdce, 0xe2e}, {0xdcf, 0xe2f}, {0xdd0, 0xe30}, {0xdd1, 0xe31}, {0xdd2, 0xe32}, {0xdd3, 0xe33}, {0xdd4, 0xe34}, {0xdd5, 0xe35}, {0xdd6, 0xe36}, {0xdd7, 0xe37}, {0xdd8, 0xe38}, {0xdd9, 0xe39}, {0xdda, 0xe3a}, {0xddf, 0xe3f}, {0xde0, 0xe40}, {0xde1, 0xe41}, {0xde2, 0xe42}, {0xde3, 0xe43}, {0xde4, 0xe44}, {0xde5, 0xe45}, {0xde6, 0xe46}, {0xde7, 0xe47}, {0xde8, 0xe48}, {0xde9, 0xe49}, {0xdea, 0xe4a}, {0xdeb, 0xe4b}, {0xdec, 0xe4c}, {0xded, 0xe4d}, {0xdf0, 0xe50}, {0xdf1, 0xe51}, {0xdf2, 0xe52}, {0xdf3, 0xe53}, {0xdf4, 0xe54}, {0xdf5, 0xe55}, {0xdf6, 0xe56}, {0xdf7, 0xe57}, {0xdf8, 0xe58}, {0xdf9, 0xe59}, {0xea1, 0x3131}, {0xea2, 0x3132}, {0xea3, 0x3133}, {0xea4, 0x3134}, {0xea5, 0x3135}, {0xea6, 0x3136}, {0xea7, 0x3137}, {0xea8, 0x3138}, {0xea9, 0x3139}, {0xeaa, 0x313a}, {0xeab, 0x313b}, {0xeac, 0x313c}, {0xead, 0x313d}, {0xeae, 0x313e}, {0xeaf, 0x313f}, {0xeb0, 0x3140}, {0xeb1, 0x3141}, {0xeb2, 0x3142}, {0xeb3, 0x3143}, {0xeb4, 0x3144}, {0xeb5, 0x3145}, {0xeb6, 0x3146}, {0xeb7, 0x3147}, {0xeb8, 0x3148}, {0xeb9, 0x3149}, {0xeba, 0x314a}, {0xebb, 0x314b}, {0xebc, 0x314c}, {0xebd, 0x314d}, {0xebe, 0x314e}, {0xebf, 0x314f}, {0xec0, 0x3150}, {0xec1, 0x3151}, {0xec2, 0x3152}, {0xec3, 0x3153}, {0xec4, 0x3154}, {0xec5, 0x3155}, {0xec6, 0x3156}, {0xec7, 0x3157}, {0xec8, 0x3158}, {0xec9, 0x3159}, {0xeca, 0x315a}, {0xecb, 0x315b}, {0xecc, 0x315c}, {0xecd, 0x315d}, {0xece, 0x315e}, {0xecf, 0x315f}, {0xed0, 0x3160}, {0xed1, 0x3161}, {0xed2, 0x3162}, {0xed3, 0x3163}, {0xed4, 0x11a8}, {0xed5, 0x11a9}, {0xed6, 0x11aa}, {0xed7, 0x11ab}, {0xed8, 0x11ac}, {0xed9, 0x11ad}, {0xeda, 0x11ae}, {0xedb, 0x11af}, {0xedc, 0x11b0}, {0xedd, 0x11b1}, {0xede, 0x11b2}, {0xedf, 0x11b3}, {0xee0, 0x11b4}, {0xee1, 0x11b5}, {0xee2, 0x11b6}, {0xee3, 0x11b7}, {0xee4, 0x11b8}, {0xee5, 0x11b9}, {0xee6, 0x11ba}, {0xee7, 0x11bb}, {0xee8, 0x11bc}, {0xee9, 0x11bd}, {0xeea, 0x11be}, {0xeeb, 0x11bf}, {0xeec, 0x11c0}, {0xeed, 0x11c1}, {0xeee, 0x11c2}, {0xeef, 0x316d}, {0xef0, 0x3171}, {0xef1, 0x3178}, {0xef2, 0x317f}, {0xef3, 0x3181}, {0xef4, 0x3184}, {0xef5, 0x3186}, {0xef6, 0x318d}, {0xef7, 0x318e}, {0xef8, 0x11eb}, {0xef9, 0x11f0}, {0xefa, 0x11f9}, {0xeff, 0x20a9}, {0x13a4, 0x20ac}, {0x13bc, 0x152}, {0x13bd, 0x153}, {0x13be, 0x178}, {0x20a0, 0x20a0}, {0x20a1, 0x20a1}, {0x20a2, 0x20a2}, {0x20a3, 0x20a3}, {0x20a4, 0x20a4}, {0x20a5, 0x20a5}, {0x20a6, 0x20a6}, {0x20a7, 0x20a7}, {0x20a8, 0x20a8}, {0x20aa, 0x20aa}, {0x20ab, 0x20ab}, {0x20ac, 0x20ac}, }; int keysym_to_unicode(int keysym) { int i, j, k; i = -1; j = lenof(keysyms); while (j - i >= 2) { k = (j + i) / 2; if (keysyms[k].keysym == keysym) return keysyms[k].unicode; else if (keysyms[k].keysym < keysym) i = k; else j = k; } return -1; } putty-0.76/unix/xpmptcfg.c0000644000175000017500000001132714072266314012544 00000000000000/* XPM */ static const char *const cfg_icon_0[] = { /* columns rows colors chars-per-pixel */ "16 16 9 1", " c black", ". c navy", "X c blue", "o c #808000", "O c yellow", "+ c #808080", "@ c #C0C0C0", "# c gray100", "$ c None", /* pixels */ "$$$ $$$$$$$$$$$", "$$ OO $$$$", "$ +oO+###@+ $$$", " o #.oO.XX@+ $$$", " oO+.OO.XX@+ $$$", "$ oOOOO.XX@+ $$$", "$$ oooOO.X@+ $$$", "$$ +..oOO.@+ $$$", "$$ @@@+oOO++ $$", "$ +++++ oOO #+ $", " #######+oOO++ $", " #@@@@@++ oOO $", " @++++++++ oOO $", "$ oOO ", "$$$$$$$$$$$$ oO ", "$$$$$$$$$$$$$ $" }; /* XPM */ static const char *const cfg_icon_1[] = { /* columns rows colors chars-per-pixel */ "32 32 9 1", " c black", ". c navy", "X c blue", "o c #808000", "O c yellow", "+ c #808080", "@ c #C0C0C0", "# c gray100", "$ c None", /* pixels */ "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$", "$$$$$$ $$$$$$$$$$$$$$$$$$$$$$$$", "$$$$$ OO $$$$$$$$$$$$$$$$$$$$$$", "$$$$$ ooOO $$$$$$$$$$$$$$$$$$$$$", "$$$$$$ ooOO $$$$$$", "$$ $$$ oOO @@@@@@@@@@@@@+ $$$$$", "$ oO $$ oOOO @@@@@@@@@@@++ $$$$$", "$ oOO oOOOO #########@+++ $$$$$", "$$ oOOOOOOO ..........@+++ $$$$$", "$$ ooOOOOOOO XXXXXXXXX@+++ $$$$$", "$$$ ooooooOOO XXXXXXXX@+++ $$$$$", "$$$$ oo ooOOO XXXXXXX@+++ $$$$$", "$$$$$$ . ooOOO XXXXXX@+++ $$$$$", "$$$$$$ #.X ooOOO XXXXX@+++ $$$$$", "$$$$$$ #.XX ooOOO XXXX@+++ $$$$$", "$$$$$$ #.XXX ooOOO XXX@+++ $$$$$", "$$$$$$ #.XXXX ooOOO XX@+++ $$$$$", "$$$$$$ ####### ooOOO #@+++ $$$", "$$$$$ #@@@@@@@ ooOOO +++ @#+ $$", "$$$$ @ @++++++++ ooOOO + @#++ $$", "$$$ @@ ooOOO @#+++ $$", "$$ ############### ooOOO @+++ $$", "$$ #@@@@@@@@@@@@@@@ ooOOO +++ $$", "$$ #@@@@@@@@@@@@@@@@ ooOOO + $$$", "$$ #@@@@@@@@@@@@+ ooOOO $$$$", "$$ @++++++++++++++++++ ooOOO $$$", "$$$ ooOOO $$", "$$$$$$$$$$$$$$$$$$$$$$$$ ooO $$$", "$$$$$$$$$$$$$$$$$$$$$$$$$ o $$$$", "$$$$$$$$$$$$$$$$$$$$$$$$$$ $$$$$", "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$", "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$" }; /* XPM */ static const char *const cfg_icon_2[] = { /* columns rows colors chars-per-pixel */ "48 48 9 1", " c black", ". c navy", "X c blue", "o c #808000", "O c yellow", "+ c #808080", "@ c #C0C0C0", "# c gray100", "$ c None", /* pixels */ "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$", "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$", "$$$$$$$$$ $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$", "$$$$$$$$ OO $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$", "$$$$$$$$ oOOOO $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$", "$$$$$$$$$ ooOOO $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$", "$$$$$$$$$$ ooOOO $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$", "$$$$$$$$$$$ oOOO $$$$$$$$$$", "$$$ $$$$$$ oOOO @@@@@@@@@@@@@@@@@@@@+ $$$$$$$$$", "$$ oO $$$$$ oOOOO @@@@@@@@@@@@@@@@@@++ $$$$$$$$$", "$$ ooO $$$ oOOOO @@@@@@@@@@@@@@@@@+++ $$$$$$$$$", "$$$ oOO OOOOO ################@++++ $$$$$$$$$", "$$$ ooOOOOOOOOOOO ++++++++++++++@+++++ $$$$$$$$$", "$$$ ooOOOOOOOOOOOO .............#+++++ $$$$$$$$$", "$$$$ oooOOOOoOOOOOO XXXXXXXXXXXX#+++++ $$$$$$$$$", "$$$$$ oooooooOOOOOOO XXXXXXXXXXX#+++++ $$$$$$$$$", "$$$$$$ oo ooOOOOOOO XXXXXXXXXX#+++++ $$$$$$$$$", "$$$$$$$$$ + ooOOOOOOO XXXXXXXXX#+++++ $$$$$$$$$", "$$$$$$$$$ #+. ooOOOOOOO XXXXXXXX#+++++ $$$$$$$$$", "$$$$$$$$$ #+.X ooOOOOOOO XXXXXXX#+++++ $$$$$$$$$", "$$$$$$$$$ #+.XX ooOOOOOOO XXXXXX#+++++ $$$$$$$$$", "$$$$$$$$$ #+.XXX ooOOOOOOO XXXXX#+++++ $$$$$$$$$", "$$$$$$$$$ #+.XXXX ooOOOOOOO XXXX#+++++ $$$$$$$$$", "$$$$$$$$$ #+.XXXXX ooOOOOOOO XXX#+++++ $$$$$$$$$", "$$$$$$$$$ #+.XXXXXX ooOOOOOOO XX#+++++ $$$$$$$$$", "$$$$$$$$$ #+.XXXXXXX ooOOOOOOO X#+++++ $$$$$$$$$", "$$$$$$$$$ #+.XXXXXXXX ooOOOOOOO #+++++ $$$$$$$$$", "$$$$$$$$ #@########## ooOOOOOOO +++++ $$$$$", "$$$$$$$ @ #@@@@@@@@@@@@ ooOOOOOOO +++ @@##+ $$$$", "$$$$$$ @@ #@@@@@@@@@@@@@ ooOOOOOOO + @@##++ $$$$", "$$$$$ @@@ @++++++++++++++ ooOOOOOOO @@##+++ $$$$", "$$$$ @@@@ ooOOOOOOO ##++++ $$$$", "$$$ ####################### ooOOOOOOO @++++ $$$$", "$$$ ######################## ooOOOOOOO ++++ $$$$", "$$$ ##@@@@@@@@@@@@@@@@@@@@@@@ ooOOOOOOO +++ $$$$", "$$$ ##@@@@@@@@@@@@@@@@@@@@@@@@ ooOOOOOOO ++ $$$$", "$$$ ##@@@@@@@@@@@@@@@@@@@@@@@@@ ooOOOOOOO $$$$$", "$$$ ##@@@@@@@@@@@@@@@@@@ ooOOOOOOO $$$$$", "$$$ @@+++++++++++++++++++++++++++ ooOOOOOOO $$$$", "$$$ @@++++++++++++++++++++++++++++ ooOOOOOOO $$$", "$$$$ ooOOOOO $$$$", "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ooOOO $$$$$", "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ ooO $$$$$$", "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ o $$$$$$$", "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ $$$$$$$$", "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$", "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$", "$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$" }; const char *const *const cfg_icon[] = { cfg_icon_0, cfg_icon_1, cfg_icon_2, }; const int n_cfg_icon = 3; putty-0.76/unix/xpmpterm.c0000644000175000017500000001117414072266314012570 00000000000000/* XPM */ static const char *const main_icon_0[] = { /* columns rows colors chars-per-pixel */ "16 16 6 1", " c black", ". c blue", "X c #808080", "o c #C0C0C0", "O c gray100", "+ c None", /* pixels */ "++++++++++++++++", "+++ ++++", "++ OOOOOOOoX +++", "++ O......oX +++", "++ O......oX +++", "++ O......oX +++", "++ O......oX +++", "++ O......oX +++", "++ ooooooooX ++", "+ XXXXXXXXXXOX +", " OOOOOOOOOOOoX +", " OoooooXXXXoXX +", " oXXXXXXXXXXX ++", "+ +++", "++++++++++++++++", "++++++++++++++++" }; /* XPM */ static const char *const main_icon_1[] = { /* columns rows colors chars-per-pixel */ "32 32 7 1", " c black", ". c navy", "X c blue", "o c #808080", "O c #C0C0C0", "+ c gray100", "@ c None", /* pixels */ "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", "@@@@@@@@@ @@@@@@", "@@@@@@@@ OOOOOOOOOOOOOOOOo @@@@@", "@@@@@@@ OOOOOOOOOOOOOOOOoo @@@@@", "@@@@@@ +++++++++++++++Oooo @@@@@", "@@@@@@ +..............Oooo @@@@@", "@@@@@@ +.XXXXXXXXXXXXXOooo @@@@@", "@@@@@@ +.XXXXXXXXXXXXXOooo @@@@@", "@@@@@@ +.XXXXXXXXXXXXXOooo @@@@@", "@@@@@@ +.XXXXXXXXXXXXXOooo @@@@@", "@@@@@@ +.XXXXXXXXXXXXXOooo @@@@@", "@@@@@@ +.XXXXXXXXXXXXXOooo @@@@@", "@@@@@@ +.XXXXXXXXXXXXXOooo @@@@@", "@@@@@@ +.XXXXXXXXXXXXXOooo @@@@@", "@@@@@@ +++++++++++++++Oooo @@@", "@@@@@ +OOOOOOOOOOOOOOooo O+o @@", "@@@@ O Ooooooooooooooooo O+oo @@", "@@@ OO O+ooo @@", "@@ ++++++++++++++++++++++Oooo @@", "@@ +OOOOOOOOOOOOOOOOOOOOOoooo @@", "@@ +OOOOOOOOOOOOOOOOOOOOOooo @@@", "@@ +OOOOOOOOOOOOo oOoo @@@@", "@@ Ooooooooooooooooooooooo @@@@@", "@@@ @@@@@@", "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" }; /* XPM */ static const char *const main_icon_2[] = { /* columns rows colors chars-per-pixel */ "48 48 7 1", " c black", ". c navy", "X c blue", "o c #808080", "O c #C0C0C0", "+ c gray100", "@ c None", /* pixels */ "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", "@@@@@@@@@@@@@ @@@@@@@@@@", "@@@@@@@@@@@@ OOOOOOOOOOOOOOOOOOOOOOOOo @@@@@@@@@", "@@@@@@@@@@@ OOOOOOOOOOOOOOOOOOOOOOOOoo @@@@@@@@@", "@@@@@@@@@@ OOOOOOOOOOOOOOOOOOOOOOOOooo @@@@@@@@@", "@@@@@@@@@ +++++++++++++++++++++++Ooooo @@@@@@@@@", "@@@@@@@@@ +oooooooooooooooooooooOooooo @@@@@@@@@", "@@@@@@@@@ +o....................+ooooo @@@@@@@@@", "@@@@@@@@@ +o.XXXXXXXXXXXXXXXXXXX+ooooo @@@@@@@@@", "@@@@@@@@@ +o.XXXXXXXXXXXXXXXXXXX+ooooo @@@@@@@@@", "@@@@@@@@@ +o.XXXXXXXXXXXXXXXXXXX+ooooo @@@@@@@@@", "@@@@@@@@@ +o.XXXXXXXXXXXXXXXXXXX+ooooo @@@@@@@@@", "@@@@@@@@@ +o.XXXXXXXXXXXXXXXXXXX+ooooo @@@@@@@@@", "@@@@@@@@@ +o.XXXXXXXXXXXXXXXXXXX+ooooo @@@@@@@@@", "@@@@@@@@@ +o.XXXXXXXXXXXXXXXXXXX+ooooo @@@@@@@@@", "@@@@@@@@@ +o.XXXXXXXXXXXXXXXXXXX+ooooo @@@@@@@@@", "@@@@@@@@@ +o.XXXXXXXXXXXXXXXXXXX+ooooo @@@@@@@@@", "@@@@@@@@@ +o.XXXXXXXXXXXXXXXXXXX+ooooo @@@@@@@@@", "@@@@@@@@@ +o.XXXXXXXXXXXXXXXXXXX+ooooo @@@@@@@@@", "@@@@@@@@@ +o.XXXXXXXXXXXXXXXXXXX+ooooo @@@@@@@@@", "@@@@@@@@@ +o.XXXXXXXXXXXXXXXXXXX+ooooo @@@@@@@@@", "@@@@@@@@ +O+++++++++++++++++++++ooooo @@@@@", "@@@@@@@ O +OOOOOOOOOOOOOOOOOOOOOOoooo OO++o @@@@", "@@@@@@ OO +OOOOOOOOOOOOOOOOOOOOOOooo OO++oo @@@@", "@@@@@ OOO Ooooooooooooooooooooooooo OO++ooo @@@@", "@@@@ OOOO OO++oooo @@@@", "@@@ ++++++++++++++++++++++++++++++++++Ooooo @@@@", "@@@ +++++++++++++++++++++++++++++++++Oooooo @@@@", "@@@ ++OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOoooooo @@@@", "@@@ ++OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOoooooo @@@@", "@@@ ++OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOooooo @@@@@", "@@@ ++OOOOOOOOOOOOOOOOOO oOOoooo @@@@@@", "@@@ OOoooooooooooooooooooooooooooooooooo @@@@@@@", "@@@ OOooooooooooooooooooooooooooooooooo @@@@@@@@", "@@@@ @@@@@@@@@", "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@", "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@" }; const char *const *const main_icon[] = { main_icon_0, main_icon_1, main_icon_2, }; const int n_main_icon = 3; putty-0.76/unix/xpmpucfg.c0000644000175000017500000001132714072266314012545 00000000000000/* XPM */ static const char *const cfg_icon_0[] = { /* columns rows colors chars-per-pixel */ "16 16 9 1", " c black", ". c navy", "X c blue", "o c #808000", "O c yellow", "+ c #808080", "@ c #C0C0C0", "# c gray100", "$ c None", /* pixels */ "$$$ $$ $$", "$$ OO #####@+ $", "$ $ oO #XX..@+ $", " o $ oO+X.O.@+ $", " oO OO .O.X@+ $", "$ oOOOOoO++@@+ $", "$$ oooOOoOO +++ ", "$ # oooOO +++++ ", "$ #X..ooOO +++ $", "$ #X.O. oOO $$", "$ #.O.X@ oOO $$$", "$ @++@@@+ oOO $$", "$ ++++++++ oOO $", " #####++++ oOO ", " @+++++++ $$ oO ", "$ $$$$ $" }; /* XPM */ static const char *const cfg_icon_1[] = { /* columns rows colors chars-per-pixel */ "32 32 9 1", " c black", ". c navy", "X c blue", "o c #808000", "O c yellow", "+ c #808080", "@ c #C0C0C0", "# c gray100", "$ c None", /* pixels */ "$$$$$$$$$$$$$$$$ $$$$", "$$$$$$ $$$$$$$ @@@@@@@@@@@+ $$$", "$$$$$ OO $$$$ ##########@++ $$$", "$$$$$ ooOO $$$ #.........@++ $$$", "$$$$$$ ooOO $$ #.XXXXXXXX@++ $$$", "$$ $$$ oOO $$ #.XXXX XX@++ $$$", "$ oO $$ oOOO $ #.XXX O XX@++ $$$", "$ oOO oOOOO $ #.X O XXX@++ $$$", "$$ oOOOOOOO $$ #. OO XXXX@++ $$$", "$$ ooOOOOOOO $ # OO XXXXX@++ $$$", "$$$ ooooooOOO OO ######@++ $", "$$$$ oo ooOOO OO +++++++++ @#+ ", "$$$$$$ $ ooOOO @#++ ", "$$$$$$$$$$ ooOOO OOOO ######@++ ", "$$$$$ O ooOOO O @@@@@@@+++ ", "$$$$ @@@@@ ooOOO @@+ +@++ $", "$$$ ######### ooOOO +++++++++ $$", "$$$ #....... O ooOOO $$$", "$$$ #.XXXXX OO ooOOO $$$$$$$$$$", "$$$ #.XXXX OO @+ ooOOO $$$$$$$$$", "$$$ #.XXX O X@++ ooOOO $$$$$$$$", "$$$ #.XX O XXX@++ ooOOO $$$$$$$", "$$$ #.XX XXXX@++ $ ooOOO $$$$$$", "$$$ #.XXXXXXXX@++ $$ ooOOO $$$$$", "$$$ ##########@++ $ ooOOO $$$$", "$$ @+++++++++++ @#+ $ ooOOO $$$", "$ @ @#++ $$ ooOOO $$", " ################@++ $$$ ooO $$$", " #@@@@@@@@@@@@@@@+++ $$$$ o $$$$", " #@@@@@@@@+ +@++ $$$$$$ $$$$$", " @++++++++++++++++ $$$$$$$$$$$$$", "$ $$$$$$$$$$$$$$" }; /* XPM */ static const char *const cfg_icon_2[] = { /* columns rows colors chars-per-pixel */ "48 48 9 1", " c black", ". c navy", "X c blue", "o c #808000", "O c yellow", "+ c #808080", "@ c #C0C0C0", "# c gray100", "$ c None", /* pixels */ "$$$$$$$$$$$$$$$$$$$$$$$$$ $$$$$", "$$$$$$$$$$$$$$$$$$$$$$$$ @@@@@@@@@@@@@@@@@+ $$$$", "$$$$$$$$$ $$$$$$$$$$$$ @@@@@@@@@@@@@@@@@++ $$$$", "$$$$$$$$ OO $$$$$$$$ ################@+++ $$$$", "$$$$$$$$ oOOOO $$$$$$$ #++++++++++++++@++++ $$$$", "$$$$$$$$$ ooOOO $$$$$$ #+.............#++++ $$$$", "$$$$$$$$$$ ooOOO $$$$$ #+.XXXXXXXXXXXX#++++ $$$$", "$$$$$$$$$$$ oOOO $$$$$ #+.XXXXXXXXXXXX#++++ $$$$", "$$$ $$$$$$ oOOO $$$$$ #+.XXXXXXX XXX#++++ $$$$", "$$ oO $$$$$ oOOOO $$$$ #+.XXXXXX O XXX#++++ $$$$", "$$ ooO $$$$ oOOOO $$$$ #+.XXXXX O XXXX#++++ $$$$", "$$$ oOO OOOOO $$$$$ #+.XXX O XXXXX#++++ $$$$", "$$$ ooOOOOOOOOOOO $$$$ #+.XX OO XXXXXX#++++ $$$$", "$$$ ooOOOOOOOOOOOO $$$ #+.X OO XXXXXXX#++++ $$$$", "$$$$ oooOOOOoOOOOOO $$ #@ OO #########++++ $", "$$$$$ oooooooOOOOOOO # OOO @@@@@@@@@@+++ @##+ ", "$$$$$$ oo ooOOOOOOO OO +++++++++++++ @##++ ", "$$$$$$$$$ $ ooOOOOOOO OO @##+++ ", "$$$$$$$$$$$$$ ooOOOOOOO ############@+++ ", "$$$$$$$$$$$$$$ ooOOOOOOO OOOOOO ##########@++++ ", "$$$$$$$$$$$$$$$ ooOOOOOOO OOO @@+ @++++ $", "$$$$$$$$$$$$$$$$ ooOOOOOOO O ++++++++++++++++ $$", "$$$$$$$$$$$$$$$ O ooOOOOOOO ++++++++++++++++ $$$", "$$$$$$$$$$$$$$$$ ooOOOOOOO $$$$", "$$$$$$$ ooOOOOOOO $$$$$$$$$$$$$$$$$$", "$$$$$$ @@@@@@@@@@@@ ooOOOOOOO $$$$$$$$$$$$$$$$$", "$$$$$ @@@@@@@@@@@@ OO ooOOOOOOO $$$$$$$$$$$$$$$$", "$$$$ ############ OO ooOOOOOOO $$$$$$$$$$$$$$$", "$$$$ #++++++++++ OO @++ ooOOOOOOO $$$$$$$$$$$$$$", "$$$$ #+........ OO .#+++ ooOOOOOOO $$$$$$$$$$$$$", "$$$$ #+.XXXXXX O XX#++++ ooOOOOOOO $$$$$$$$$$$$", "$$$$ #+.XXXXX O XXXX#++++ ooOOOOOOO $$$$$$$$$$$", "$$$$ #+.XXXX O XXXXX#++++ $ ooOOOOOOO $$$$$$$$$$", "$$$$ #+.XXXX XXXXXX#++++ $$ ooOOOOOOO $$$$$$$$$", "$$$$ #+.XXXXXXXXXXXX#++++ $$$ ooOOOOOOO $$$$$$$$", "$$$$ #+.XXXXXXXXXXXX#++++ $$$$ ooOOOOOOO $$$$$$$", "$$$$ #+.XXXXXXXXXXXX#++++ $$$$$ ooOOOOOOO $$$$$$", "$$$$ #+.XXXXXXXXXXXX#++++ $$$$$$ ooOOOOOOO $$$$$", "$$$$ #@##############++++ $$$$ ooOOOOOOO $$$$", "$$$ #@@@@@@@@@@@@@@@+++ @##+ $$$$ ooOOOOOOO $$$", "$$ @ @+++++++++++++++++ @##++ $$$$$ ooOOOOO $$$$", "$ @@ @##+++ $$$$$$ ooOOO $$$$$", " ########################@+++ $$$$$$$ ooO $$$$$$", " #######################@++++ $$$$$$$$ o $$$$$$$", " ##@@@@@@@@@@@@+ @++++ $$$$$$$$$$ $$$$$$$$", " @@++++++++++++++++++++++++ $$$$$$$$$$$$$$$$$$$$", " @@+++++++++++++++++++++++ $$$$$$$$$$$$$$$$$$$$$", "$ $$$$$$$$$$$$$$$$$$$$$$" }; const char *const *const cfg_icon[] = { cfg_icon_0, cfg_icon_1, cfg_icon_2, }; const int n_cfg_icon = 3; putty-0.76/unix/xpmputty.c0000644000175000017500000001126214072266314012624 00000000000000/* XPM */ static const char *const main_icon_0[] = { /* columns rows colors chars-per-pixel */ "16 16 8 1", " c black", ". c navy", "X c blue", "o c yellow", "O c #808080", "+ c #C0C0C0", "@ c gray100", "# c None", /* pixels */ "####### ##", "###### @@@@@+O #", "###### @XX..+O #", "###### @X.o.+O #", "###### O.o.X+O #", "###### ooOO++O #", "## ooooo OOO ", "# @Oooooo OOOOO ", "# @X..oo OOOO #", "# @X.o.OO ##", "# @.o.X+O ######", "# +OO+++O ######", "# OOOOOOOO #####", " @@@@@OOOO #####", " +OOOOOOO ######", "# #######" }; /* XPM */ static const char *const main_icon_1[] = { /* columns rows colors chars-per-pixel */ "32 32 8 1", " c black", ". c navy", "X c blue", "o c yellow", "O c #808080", "+ c #C0C0C0", "@ c gray100", "# c None", /* pixels */ "################ ####", "############### +++++++++++O ###", "############## @@@@@@@@@@+OO ###", "############## @.........+OO ###", "############## @.XXXXXXXX+OO ###", "############## @.XXXX XX+OO ###", "############## @.XXX o XX+OO ###", "############## @.X o XXX+OO ###", "############## @. oo XXXX+OO ###", "############## @ oo XXXXX+OO ###", "############## oo @@@@@@+OO #", "############# ooo OOOOOOOOO +@O ", "############ ooo +@OO ", "########## ooooooooo @@@@@@+OO ", "##### ooooooooo +++++++OOO ", "#### +++++ ooo ++O O+OO #", "### @@@@@@@@@ ooo OOOOOOOOOOO ##", "### @....... oo ###", "### @.XXXXX oo OO ##############", "### @.XXXX oo +OO ##############", "### @.XXX o X+OO ##############", "### @.XX o XXX+OO ##############", "### @.XX XXXX+OO ##############", "### @.XXXXXXXX+OO ##############", "### @@@@@@@@@@+OO ############", "## +OOOOOOOOOOO +@O ###########", "# + +@OO ###########", " @@@@@@@@@@@@@@@@+OO ###########", " @+++++++++++++++OOO ###########", " @++++++++O O+OO ############", " +OOOOOOOOOOOOOOOO #############", "# ##############" }; /* XPM */ static const char *const main_icon_2[] = { /* columns rows colors chars-per-pixel */ "48 48 8 1", " c black", ". c navy", "X c blue", "o c yellow", "O c #808080", "+ c #C0C0C0", "@ c gray100", "# c None", /* pixels */ "######################### #####", "######################## +++++++++++++++++O ####", "####################### +++++++++++++++++OO ####", "###################### @@@@@@@@@@@@@@@@+OOO ####", "###################### @OOOOOOOOOOOOOO+OOOO ####", "###################### @O.............@OOOO ####", "###################### @O.XXXXXXXXXXXX@OOOO ####", "###################### @O.XXXXXXXXXXXX@OOOO ####", "###################### @O.XXXXXXX XXX@OOOO ####", "###################### @O.XXXXXX o XXX@OOOO ####", "###################### @O.XXXXX o XXXX@OOOO ####", "###################### @O.XXX o XXXXX@OOOO ####", "###################### @O.XX oo XXXXXX@OOOO ####", "###################### @O.X oo XXXXXXX@OOOO ####", "###################### @+ oo @@@@@@@@@OOOO #", "##################### @ ooo ++++++++++OOO +@@O ", "#################### + oo OOOOOOOOOOOOO +@@OO ", "################### + oo +@@OOO ", "################## @ ooo @@@@@@@@@@@@+OOO ", "################## ooooooooooo @@@@@@@@@@+OOOO ", "################## oooooooooo ++O +OOOO #", "################ oooooooooo OOOOOOOOOOOOOOOO ##", "############### ooooooooooo OOOOOOOOOOOOOOOO ###", "################ ooo ####", "####### oo ######################", "###### ++++++++++++ oo O ######################", "##### ++++++++++++ ooo OO ######################", "#### @@@@@@@@@@@@ oo OOO ######################", "#### @OOOOOOOOOO oo +OOOO ######################", "#### @O........ oo .@OOOO ######################", "#### @O.XXXXXX o XX@OOOO ######################", "#### @O.XXXXX o XXXX@OOOO ######################", "#### @O.XXXX o XXXXX@OOOO ######################", "#### @O.XXXX XXXXXX@OOOO ######################", "#### @O.XXXXXXXXXXXX@OOOO ######################", "#### @O.XXXXXXXXXXXX@OOOO ######################", "#### @O.XXXXXXXXXXXX@OOOO ######################", "#### @O.XXXXXXXXXXXX@OOOO ######################", "#### @+@@@@@@@@@@@@@@OOOO ###################", "### @+++++++++++++++OOO +@@O ##################", "## + +OOOOOOOOOOOOOOOOO +@@OO ##################", "# ++ +@@OOO ##################", " @@@@@@@@@@@@@@@@@@@@@@@@+OOO ##################", " @@@@@@@@@@@@@@@@@@@@@@@+OOOO ##################", " @@++++++++++++O +OOOO ###################", " ++OOOOOOOOOOOOOOOOOOOOOOOO ####################", " ++OOOOOOOOOOOOOOOOOOOOOOO #####################", "# ######################" }; const char *const *const main_icon[] = { main_icon_0, main_icon_1, main_icon_2, }; const int n_main_icon = 3; putty-0.76/unix/Makefile.gtk0000644000175000017500000030606514072266315013003 00000000000000# Makefile for putty under X/GTK and Unix. # # This file was created by `mkfiles.pl' from the `Recipe' file. # DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead. # # Extra options you can set: # # - COMPAT=-DAUTO_WINSOCK (Windows only) # Causes PuTTY to assume that includes its own WinSock # header file, so that it won't try to include . # # - COMPAT=-DWINSOCK_TWO (Windows only) # Causes the PuTTY utilities to include instead of # , except Plink which _needs_ WinSock 2 so it already # does this. # # - COMPAT=-DNO_SECURITY (Windows only) # Disables use of , which is not available with some # development environments (such as very old versions of the # mingw/Cygwin GNU toolchain). This has the following effects: # - Pageant won't care about the local user ID of processes # accessing it; a version of Pageant built with this option # will therefore refuse to run under NT-series OSes on # security grounds (although it will run fine on Win95-series # OSes where there is no access control anyway). # - SSH connection sharing is disabled. # - There is no support for restriction of the process ACLs. # # - COMPAT=-DNO_MULTIMON (Windows only) # Disables PuTTY's use of , which is not available # with some development environments. This means that PuTTY's # full-screen mode (configurable to work on Alt-Enter) will # not behave usefully in a multi-monitor environment. # # - COMPAT=-DNO_HTMLHELP (Windows only) # Disables PuTTY's use of , which is not available # with some development environments. # # If you don't have this header, you may be able to use the copy # supplied with HTML Help Workshop. # # - RCFL=-DNO_MANIFESTS (Windows only) # Disables inclusion of XML application manifests in the PuTTY # binaries. This may be necessary to build for 64-bit Windows; # the manifests are only included to use the XP GUI style on # Windows XP, and the architecture tags are a lie on 64-bit. # # - COMPAT=-DNO_IPV6 # Disables PuTTY's ability to make IPv6 connections, enabling # it to compile under development environments which do not # support IPv6 in their header files. # # - COMPAT=-DNO_GSSAPI # Disables PuTTY's ability to use GSSAPI functions for # authentication and key exchange. # # - COMPAT=-DSTATIC_GSSAPI # Causes PuTTY to try to link statically against the GSSAPI # library instead of the default of doing it at run time. # # - COMPAT=-DMSVC4 (Windows only) # - RCFL=-DMSVC4 # Makes a couple of minor changes so that PuTTY compiles using # MSVC 4. You will also need -DNO_SECURITY and -DNO_MULTIMON. # # - COMPAT=-DNO_SECUREZEROMEMORY (Windows only) # Disables PuTTY's use of SecureZeroMemory(), which is missing # from some environments' header files. # # - XFLAGS=-DDEBUG # Causes PuTTY to enable internal debugging. # # - XFLAGS=-DMALLOC_LOG # Causes PuTTY to emit a file called putty_mem.log, logging every # memory allocation and free, so you can track memory leaks. # # - XFLAGS=-DMINEFIELD (Windows only) # Causes PuTTY to use a custom memory allocator, similar in # concept to Electric Fence, in place of regular malloc(). Wastes # huge amounts of RAM, but should cause heap-corruption bugs to # show up as GPFs at the point of failure rather than appearing # later on as second-level damage. # # - XFLAGS=-DFUZZING # Builds a version of PuTTY with some tweaks to make fuzz testing # easier: the SSH random number generator is replaced by one that # always returns the same thing. Note that this makes SSH # completely insecure -- a FUZZING build should never be used to # connect to a real server. # You can define this path to point at your tools if you need to # TOOLPATH = /opt/gcc/bin CC = $(TOOLPATH)cc # If necessary set the path to krb5-config here KRB5CONFIG=krb5-config # You can manually set this to `gtk-config' or `pkg-config gtk+-1.2' # (depending on what works on your system) if you want to enforce # building with GTK 1.2, or you can set it to `pkg-config gtk+-2.0 x11' # if you want to enforce 2.0. The default is to try 2.0 and fall back # to 1.2 if it isn't found. GTK_CONFIG = sh -c 'pkg-config gtk+-3.0 x11 $$0 2>/dev/null || pkg-config gtk+-2.0 x11 $$0 2>/dev/null || gtk-config $$0' -include Makefile.local unexport CFLAGS # work around a weird issue with krb5-config CFLAGS = -O2 -Wall -std=gnu99 -Wvla -g -I.././ -I../charset/ -I../windows/ \ -I../unix/ $(shell $(GTK_CONFIG) --cflags) -D _FILE_OFFSET_BITS=64 XLDFLAGS = $(LDFLAGS) $(shell $(GTK_CONFIG) --libs) ULDFLAGS = $(LDFLAGS) ifeq (,$(findstring NO_GSSAPI,$(COMPAT))) ifeq (,$(findstring STATIC_GSSAPI,$(COMPAT))) XLDFLAGS+= -ldl ULDFLAGS+= -ldl else CFLAGS+= -DNO_LIBDL $(shell $(KRB5CONFIG) --cflags gssapi) XLDFLAGS+= $(shell $(KRB5CONFIG) --libs gssapi) ULDFLAGS+= $(shell $(KRB5CONFIG) --libs gssapi) endif endif INSTALL=install INSTALL_PROGRAM=$(INSTALL) INSTALL_DATA=$(INSTALL) prefix=/usr/local exec_prefix=$(prefix) bindir=$(exec_prefix)/bin mandir=$(prefix)/man man1dir=$(mandir)/man1 .SUFFIXES: all: cgtest fuzzterm osxlaunch pageant plink pscp psftp psocks psusan pterm \ ptermapp putty puttyapp puttygen puttytel testcrypt testsc \ testzlib uppity cgtest: cgtest.o conf.o console.o ecc.o import.o marshal.o memory.o \ millerrabin.o misc.o mpint.o mpunsafe.o notiming.o pockle.o \ primecandidate.o smallprimes.o sshaes.o sshargon2.o \ sshauxcrypt.o sshbcrypt.o sshblake2.o sshblowf.o sshdes.o \ sshdss.o sshdssg.o sshecc.o sshecdsag.o sshhmac.o sshmd5.o \ sshprime.o sshprng.o sshpubk.o sshrand.o sshrsa.o sshrsag.o \ sshsh256.o sshsh512.o sshsha.o sshsha3.o stripctrl.o time.o \ tree234.o utils.o uxcons.o uxgen.o uxmisc.o uxnogtk.o \ uxnoise.o uxpoll.o uxstore.o uxutils.o version.o wcwidth.o $(CC) -o $@ cgtest.o conf.o console.o ecc.o import.o marshal.o \ memory.o millerrabin.o misc.o mpint.o mpunsafe.o notiming.o \ pockle.o primecandidate.o smallprimes.o sshaes.o sshargon2.o \ sshauxcrypt.o sshbcrypt.o sshblake2.o sshblowf.o sshdes.o \ sshdss.o sshdssg.o sshecc.o sshecdsag.o sshhmac.o sshmd5.o \ sshprime.o sshprng.o sshpubk.o sshrand.o sshrsa.o sshrsag.o \ sshsh256.o sshsh512.o sshsha.o sshsha3.o stripctrl.o time.o \ tree234.o utils.o uxcons.o uxgen.o uxmisc.o uxnogtk.o \ uxnoise.o uxpoll.o uxstore.o uxutils.o version.o wcwidth.o \ $(ULDFLAGS) fuzzterm: be_none.o callback.o conf.o config.o dialog.o fromucs.o fuzzterm.o \ localenc.o logging.o macenc.o marshal.o memory.o mimeenc.o \ minibidi.o misc.o miscucs.o sbcs.o sbcsdat.o settings.o \ slookup.o stripctrl.o terminal.o time.o timing.o toucs.o \ tree234.o utf8.o utils.o uxcfg.o uxmisc.o uxnogtk.o \ uxprint.o uxstore.o uxucs.o version.o wcwidth.o xenc.o $(CC) -o $@ be_none.o callback.o conf.o config.o dialog.o fromucs.o \ fuzzterm.o localenc.o logging.o macenc.o marshal.o memory.o \ mimeenc.o minibidi.o misc.o miscucs.o sbcs.o sbcsdat.o \ settings.o slookup.o stripctrl.o terminal.o time.o timing.o \ toucs.o tree234.o utf8.o utils.o uxcfg.o uxmisc.o uxnogtk.o \ uxprint.o uxstore.o uxucs.o version.o wcwidth.o xenc.o \ $(ULDFLAGS) osxlaunch: osxlaunch.o $(CC) -o $@ osxlaunch.o $(ULDFLAGS) pageant: aqsync.o be_misc.o be_none.o callback.o conf.o console.o ecc.o \ errsock.o gtkask.o gtkmisc.o logging.o marshal.o memory.o \ misc.o mpint.o nocproxy.o nogss.o nullplug.o pageant.o \ proxy.o settings.o sshaes.o sshargon2.o sshauxcrypt.o \ sshblake2.o sshdes.o sshdss.o sshecc.o sshhmac.o sshmd5.o \ sshprng.o sshpubk.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \ sshsha3.o stripctrl.o time.o timing.o tree234.o utils.o \ ux_x11.o uxagentc.o uxagentsock.o uxcliloop.o uxcons.o \ uxfdsock.o uxmisc.o uxnet.o uxnoise.o uxpeer.o uxpgnt.o \ uxpoll.o uxproxy.o uxsel.o uxsignal.o uxstore.o uxutils.o \ version.o wcwidth.o x11fwd.o $(CC) -o $@ aqsync.o be_misc.o be_none.o callback.o conf.o console.o \ ecc.o errsock.o gtkask.o gtkmisc.o logging.o marshal.o \ memory.o misc.o mpint.o nocproxy.o nogss.o nullplug.o \ pageant.o proxy.o settings.o sshaes.o sshargon2.o \ sshauxcrypt.o sshblake2.o sshdes.o sshdss.o sshecc.o \ sshhmac.o sshmd5.o sshprng.o sshpubk.o sshrsa.o sshsh256.o \ sshsh512.o sshsha.o sshsha3.o stripctrl.o time.o timing.o \ tree234.o utils.o ux_x11.o uxagentc.o uxagentsock.o \ uxcliloop.o uxcons.o uxfdsock.o uxmisc.o uxnet.o uxnoise.o \ uxpeer.o uxpgnt.o uxpoll.o uxproxy.o uxsel.o uxsignal.o \ uxstore.o uxutils.o version.o wcwidth.o x11fwd.o $(XLDFLAGS) plink: agentf.o aqsync.o be_all_s.o be_misc.o callback.o clicons.o cmdline.o \ conf.o console.o cproxy.o ecc.o errsock.o ldisc.o logging.o \ mainchan.o marshal.o memory.o misc.o mpint.o noterm.o \ nullplug.o pgssapi.o pinger.o portfwd.o proxy.o raw.o \ rlogin.o sessprep.o settings.o ssh.o ssh1bpp.o ssh1censor.o \ ssh1connection.o ssh1connection-client.o ssh1login.o \ ssh2bpp.o ssh2bpp-bare.o ssh2censor.o ssh2connection.o \ ssh2connection-client.o ssh2kex-client.o ssh2transhk.o \ ssh2transport.o ssh2userauth.o sshaes.o ssharcf.o \ sshargon2.o sshauxcrypt.o sshblake2.o sshblowf.o sshccp.o \ sshcommon.o sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o \ sshecc.o sshgssc.o sshhmac.o sshmac.o sshmd5.o sshprng.o \ sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \ sshsha3.o sshshare.o sshutils.o sshverstring.o sshzlib.o \ stripctrl.o supdup.o telnet.o time.o timing.o tree234.o \ utils.o ux_x11.o uxagentc.o uxcliloop.o uxcons.o uxfdsock.o \ uxgss.o uxmisc.o uxnet.o uxnogtk.o uxnoise.o uxpeer.o \ uxplink.o uxpoll.o uxproxy.o uxsel.o uxser.o uxshare.o \ uxsignal.o uxstore.o uxutils.o version.o wcwidth.o \ wildcard.o x11fwd.o $(CC) -o $@ agentf.o aqsync.o be_all_s.o be_misc.o callback.o \ clicons.o cmdline.o conf.o console.o cproxy.o ecc.o \ errsock.o ldisc.o logging.o mainchan.o marshal.o memory.o \ misc.o mpint.o noterm.o nullplug.o pgssapi.o pinger.o \ portfwd.o proxy.o raw.o rlogin.o sessprep.o settings.o ssh.o \ ssh1bpp.o ssh1censor.o ssh1connection.o \ ssh1connection-client.o ssh1login.o ssh2bpp.o ssh2bpp-bare.o \ ssh2censor.o ssh2connection.o ssh2connection-client.o \ ssh2kex-client.o ssh2transhk.o ssh2transport.o \ ssh2userauth.o sshaes.o ssharcf.o sshargon2.o sshauxcrypt.o \ sshblake2.o sshblowf.o sshccp.o sshcommon.o sshcrc.o \ sshcrcda.o sshdes.o sshdh.o sshdss.o sshecc.o sshgssc.o \ sshhmac.o sshmac.o sshmd5.o sshprng.o sshpubk.o sshrand.o \ sshrsa.o sshsh256.o sshsh512.o sshsha.o sshsha3.o sshshare.o \ sshutils.o sshverstring.o sshzlib.o stripctrl.o supdup.o \ telnet.o time.o timing.o tree234.o utils.o ux_x11.o \ uxagentc.o uxcliloop.o uxcons.o uxfdsock.o uxgss.o uxmisc.o \ uxnet.o uxnogtk.o uxnoise.o uxpeer.o uxplink.o uxpoll.o \ uxproxy.o uxsel.o uxser.o uxshare.o uxsignal.o uxstore.o \ uxutils.o version.o wcwidth.o wildcard.o x11fwd.o \ $(ULDFLAGS) pscp: agentf.o aqsync.o be_misc.o be_ssh.o callback.o clicons.o cmdline.o \ conf.o console.o cproxy.o ecc.o errsock.o logging.o \ mainchan.o marshal.o memory.o misc.o mpint.o nullplug.o \ pgssapi.o pinger.o portfwd.o proxy.o pscp.o psftpcommon.o \ settings.o sftp.o sftpcommon.o ssh.o ssh1bpp.o ssh1censor.o \ ssh1connection.o ssh1connection-client.o ssh1login.o \ ssh2bpp.o ssh2bpp-bare.o ssh2censor.o ssh2connection.o \ ssh2connection-client.o ssh2kex-client.o ssh2transhk.o \ ssh2transport.o ssh2userauth.o sshaes.o ssharcf.o \ sshargon2.o sshauxcrypt.o sshblake2.o sshblowf.o sshccp.o \ sshcommon.o sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o \ sshecc.o sshgssc.o sshhmac.o sshmac.o sshmd5.o sshprng.o \ sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \ sshsha3.o sshshare.o sshutils.o sshverstring.o sshzlib.o \ stripctrl.o time.o timing.o tree234.o utils.o uxagentc.o \ uxcliloop.o uxcons.o uxfdsock.o uxgss.o uxmisc.o uxnet.o \ uxnogtk.o uxnoise.o uxpeer.o uxpoll.o uxproxy.o uxsel.o \ uxsftp.o uxshare.o uxstore.o uxutils.o version.o wcwidth.o \ wildcard.o x11fwd.o $(CC) -o $@ agentf.o aqsync.o be_misc.o be_ssh.o callback.o \ clicons.o cmdline.o conf.o console.o cproxy.o ecc.o \ errsock.o logging.o mainchan.o marshal.o memory.o misc.o \ mpint.o nullplug.o pgssapi.o pinger.o portfwd.o proxy.o \ pscp.o psftpcommon.o settings.o sftp.o sftpcommon.o ssh.o \ ssh1bpp.o ssh1censor.o ssh1connection.o \ ssh1connection-client.o ssh1login.o ssh2bpp.o ssh2bpp-bare.o \ ssh2censor.o ssh2connection.o ssh2connection-client.o \ ssh2kex-client.o ssh2transhk.o ssh2transport.o \ ssh2userauth.o sshaes.o ssharcf.o sshargon2.o sshauxcrypt.o \ sshblake2.o sshblowf.o sshccp.o sshcommon.o sshcrc.o \ sshcrcda.o sshdes.o sshdh.o sshdss.o sshecc.o sshgssc.o \ sshhmac.o sshmac.o sshmd5.o sshprng.o sshpubk.o sshrand.o \ sshrsa.o sshsh256.o sshsh512.o sshsha.o sshsha3.o sshshare.o \ sshutils.o sshverstring.o sshzlib.o stripctrl.o time.o \ timing.o tree234.o utils.o uxagentc.o uxcliloop.o uxcons.o \ uxfdsock.o uxgss.o uxmisc.o uxnet.o uxnogtk.o uxnoise.o \ uxpeer.o uxpoll.o uxproxy.o uxsel.o uxsftp.o uxshare.o \ uxstore.o uxutils.o version.o wcwidth.o wildcard.o x11fwd.o \ $(ULDFLAGS) psftp: agentf.o aqsync.o be_misc.o be_ssh.o callback.o clicons.o cmdline.o \ conf.o console.o cproxy.o ecc.o errsock.o logging.o \ mainchan.o marshal.o memory.o misc.o mpint.o nullplug.o \ pgssapi.o pinger.o portfwd.o proxy.o psftp.o psftpcommon.o \ settings.o sftp.o sftpcommon.o ssh.o ssh1bpp.o ssh1censor.o \ ssh1connection.o ssh1connection-client.o ssh1login.o \ ssh2bpp.o ssh2bpp-bare.o ssh2censor.o ssh2connection.o \ ssh2connection-client.o ssh2kex-client.o ssh2transhk.o \ ssh2transport.o ssh2userauth.o sshaes.o ssharcf.o \ sshargon2.o sshauxcrypt.o sshblake2.o sshblowf.o sshccp.o \ sshcommon.o sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o \ sshecc.o sshgssc.o sshhmac.o sshmac.o sshmd5.o sshprng.o \ sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \ sshsha3.o sshshare.o sshutils.o sshverstring.o sshzlib.o \ stripctrl.o time.o timing.o tree234.o utils.o uxagentc.o \ uxcliloop.o uxcons.o uxfdsock.o uxgss.o uxmisc.o uxnet.o \ uxnogtk.o uxnoise.o uxpeer.o uxpoll.o uxproxy.o uxsel.o \ uxsftp.o uxshare.o uxstore.o uxutils.o version.o wcwidth.o \ wildcard.o x11fwd.o $(CC) -o $@ agentf.o aqsync.o be_misc.o be_ssh.o callback.o \ clicons.o cmdline.o conf.o console.o cproxy.o ecc.o \ errsock.o logging.o mainchan.o marshal.o memory.o misc.o \ mpint.o nullplug.o pgssapi.o pinger.o portfwd.o proxy.o \ psftp.o psftpcommon.o settings.o sftp.o sftpcommon.o ssh.o \ ssh1bpp.o ssh1censor.o ssh1connection.o \ ssh1connection-client.o ssh1login.o ssh2bpp.o ssh2bpp-bare.o \ ssh2censor.o ssh2connection.o ssh2connection-client.o \ ssh2kex-client.o ssh2transhk.o ssh2transport.o \ ssh2userauth.o sshaes.o ssharcf.o sshargon2.o sshauxcrypt.o \ sshblake2.o sshblowf.o sshccp.o sshcommon.o sshcrc.o \ sshcrcda.o sshdes.o sshdh.o sshdss.o sshecc.o sshgssc.o \ sshhmac.o sshmac.o sshmd5.o sshprng.o sshpubk.o sshrand.o \ sshrsa.o sshsh256.o sshsh512.o sshsha.o sshsha3.o sshshare.o \ sshutils.o sshverstring.o sshzlib.o stripctrl.o time.o \ timing.o tree234.o utils.o uxagentc.o uxcliloop.o uxcons.o \ uxfdsock.o uxgss.o uxmisc.o uxnet.o uxnogtk.o uxnoise.o \ uxpeer.o uxpoll.o uxproxy.o uxsel.o uxsftp.o uxshare.o \ uxstore.o uxutils.o version.o wcwidth.o wildcard.o x11fwd.o \ $(ULDFLAGS) psocks: be_misc.o callback.o conf.o console.o errsock.o logging.o marshal.o \ memory.o misc.o nocproxy.o norand.o portfwd.o proxy.o \ psocks.o sshutils.o stripctrl.o time.o timing.o tree234.o \ utils.o uxcliloop.o uxcons.o uxfdsock.o uxmisc.o uxnet.o \ uxnogtk.o uxpeer.o uxpoll.o uxproxy.o uxsel.o uxsignal.o \ uxsocks.o version.o wcwidth.o $(CC) -o $@ be_misc.o callback.o conf.o console.o errsock.o \ logging.o marshal.o memory.o misc.o nocproxy.o norand.o \ portfwd.o proxy.o psocks.o sshutils.o stripctrl.o time.o \ timing.o tree234.o utils.o uxcliloop.o uxcons.o uxfdsock.o \ uxmisc.o uxnet.o uxnogtk.o uxpeer.o uxpoll.o uxproxy.o \ uxsel.o uxsignal.o uxsocks.o version.o wcwidth.o $(ULDFLAGS) psusan: be_misc.o be_none.o callback.o conf.o cproxy.o ecc.o errsock.o \ logging.o marshal.o memory.o millerrabin.o misc.o mpint.o \ mpunsafe.o nogss.o nullplug.o pgssapi.o pockle.o portfwd.o \ primecandidate.o procnet.o proxy.o scpserver.o sesschan.o \ settings.o sftpcommon.o sftpserver.o smallprimes.o ssh1bpp.o \ ssh1censor.o ssh1connection.o ssh1connection-server.o \ ssh1login-server.o ssh2bpp.o ssh2bpp-bare.o ssh2censor.o \ ssh2connection.o ssh2connection-server.o ssh2kex-server.o \ ssh2transhk.o ssh2transport.o ssh2userauth-server.o sshaes.o \ ssharcf.o sshargon2.o sshauxcrypt.o sshblake2.o sshblowf.o \ sshccp.o sshcommon.o sshcrc.o sshcrcda.o sshdes.o sshdh.o \ sshdss.o sshecc.o sshgssc.o sshhmac.o sshmac.o sshmd5.o \ sshprime.o sshprng.o sshpubk.o sshrand.o sshrsa.o sshrsag.o \ sshserver.o sshsh256.o sshsh512.o sshsha.o sshsha3.o \ sshutils.o sshverstring.o sshzlib.o stripctrl.o time.o \ timing.o tree234.o utils.o ux_x11.o uxagentsock.o \ uxcliloop.o uxfdsock.o uxmisc.o uxnet.o uxnogtk.o uxnoise.o \ uxpeer.o uxpoll.o uxproxy.o uxpsusan.o uxpty.o uxsel.o \ uxsftpserver.o uxsignal.o uxstore.o uxutils.o version.o \ wcwidth.o wildcard.o x11fwd.o $(CC) -o $@ be_misc.o be_none.o callback.o conf.o cproxy.o ecc.o \ errsock.o logging.o marshal.o memory.o millerrabin.o misc.o \ mpint.o mpunsafe.o nogss.o nullplug.o pgssapi.o pockle.o \ portfwd.o primecandidate.o procnet.o proxy.o scpserver.o \ sesschan.o settings.o sftpcommon.o sftpserver.o \ smallprimes.o ssh1bpp.o ssh1censor.o ssh1connection.o \ ssh1connection-server.o ssh1login-server.o ssh2bpp.o \ ssh2bpp-bare.o ssh2censor.o ssh2connection.o \ ssh2connection-server.o ssh2kex-server.o ssh2transhk.o \ ssh2transport.o ssh2userauth-server.o sshaes.o ssharcf.o \ sshargon2.o sshauxcrypt.o sshblake2.o sshblowf.o sshccp.o \ sshcommon.o sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o \ sshecc.o sshgssc.o sshhmac.o sshmac.o sshmd5.o sshprime.o \ sshprng.o sshpubk.o sshrand.o sshrsa.o sshrsag.o sshserver.o \ sshsh256.o sshsh512.o sshsha.o sshsha3.o sshutils.o \ sshverstring.o sshzlib.o stripctrl.o time.o timing.o \ tree234.o utils.o ux_x11.o uxagentsock.o uxcliloop.o \ uxfdsock.o uxmisc.o uxnet.o uxnogtk.o uxnoise.o uxpeer.o \ uxpoll.o uxproxy.o uxpsusan.o uxpty.o uxsel.o uxsftpserver.o \ uxsignal.o uxstore.o uxutils.o version.o wcwidth.o \ wildcard.o x11fwd.o $(ULDFLAGS) pterm: be_none.o callback.o cmdline.o conf.o config.o dialog.o fromucs.o \ gtkcfg.o gtkcols.o gtkcomm.o gtkdlg.o gtkfont.o gtkmain.o \ gtkmisc.o gtkwin.o ldisc.o localenc.o logging.o macenc.o \ marshal.o memory.o mimeenc.o minibidi.o misc.o miscucs.o \ nocproxy.o nogss.o sbcs.o sbcsdat.o sessprep.o settings.o \ slookup.o stripctrl.o terminal.o time.o timing.o toucs.o \ tree234.o utf8.o utils.o uxcfg.o uxmisc.o uxprint.o \ uxpterm.o uxpty.o uxsel.o uxsignal.o uxstore.o uxucs.o \ version.o wcwidth.o x11misc.o xenc.o xkeysym.o xpmptcfg.o \ xpmpterm.o $(CC) -o $@ be_none.o callback.o cmdline.o conf.o config.o dialog.o \ fromucs.o gtkcfg.o gtkcols.o gtkcomm.o gtkdlg.o gtkfont.o \ gtkmain.o gtkmisc.o gtkwin.o ldisc.o localenc.o logging.o \ macenc.o marshal.o memory.o mimeenc.o minibidi.o misc.o \ miscucs.o nocproxy.o nogss.o sbcs.o sbcsdat.o sessprep.o \ settings.o slookup.o stripctrl.o terminal.o time.o timing.o \ toucs.o tree234.o utf8.o utils.o uxcfg.o uxmisc.o uxprint.o \ uxpterm.o uxpty.o uxsel.o uxsignal.o uxstore.o uxucs.o \ version.o wcwidth.o x11misc.o xenc.o xkeysym.o xpmptcfg.o \ xpmpterm.o $(XLDFLAGS) ptermapp: be_none.o callback.o conf.o config.o dialog.o fromucs.o gtkapp.o \ gtkcfg.o gtkcols.o gtkcomm.o gtkdlg.o gtkfont.o gtkmisc.o \ gtkwin.o ldisc.o localenc.o logging.o macenc.o marshal.o \ memory.o mimeenc.o minibidi.o misc.o miscucs.o nocmdline.o \ nocproxy.o nogss.o sbcs.o sbcsdat.o sessprep.o settings.o \ slookup.o stripctrl.o terminal.o time.o timing.o toucs.o \ tree234.o utf8.o utils.o uxcfg.o uxmisc.o uxprint.o \ uxpterm.o uxpty.o uxsel.o uxsignal.o uxstore.o uxucs.o \ version.o wcwidth.o x11misc.o xenc.o xkeysym.o xpmptcfg.o \ xpmpterm.o $(CC) -o $@ be_none.o callback.o conf.o config.o dialog.o fromucs.o \ gtkapp.o gtkcfg.o gtkcols.o gtkcomm.o gtkdlg.o gtkfont.o \ gtkmisc.o gtkwin.o ldisc.o localenc.o logging.o macenc.o \ marshal.o memory.o mimeenc.o minibidi.o misc.o miscucs.o \ nocmdline.o nocproxy.o nogss.o sbcs.o sbcsdat.o sessprep.o \ settings.o slookup.o stripctrl.o terminal.o time.o timing.o \ toucs.o tree234.o utf8.o utils.o uxcfg.o uxmisc.o uxprint.o \ uxpterm.o uxpty.o uxsel.o uxsignal.o uxstore.o uxucs.o \ version.o wcwidth.o x11misc.o xenc.o xkeysym.o xpmptcfg.o \ xpmpterm.o $(XLDFLAGS) putty: agentf.o aqsync.o be_all_s.o be_misc.o callback.o cmdline.o conf.o \ config.o cproxy.o dialog.o ecc.o errsock.o fromucs.o \ gtkcfg.o gtkcols.o gtkcomm.o gtkdlg.o gtkfont.o gtkmain.o \ gtkmisc.o gtkwin.o ldisc.o localenc.o logging.o macenc.o \ mainchan.o marshal.o memory.o mimeenc.o minibidi.o misc.o \ miscucs.o mpint.o nullplug.o pgssapi.o pinger.o portfwd.o \ proxy.o raw.o rlogin.o sbcs.o sbcsdat.o sessprep.o \ settings.o slookup.o ssh.o ssh1bpp.o ssh1censor.o \ ssh1connection.o ssh1connection-client.o ssh1login.o \ ssh2bpp.o ssh2bpp-bare.o ssh2censor.o ssh2connection.o \ ssh2connection-client.o ssh2kex-client.o ssh2transhk.o \ ssh2transport.o ssh2userauth.o sshaes.o ssharcf.o \ sshargon2.o sshauxcrypt.o sshblake2.o sshblowf.o sshccp.o \ sshcommon.o sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o \ sshecc.o sshgssc.o sshhmac.o sshmac.o sshmd5.o sshprng.o \ sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \ sshsha3.o sshshare.o sshutils.o sshverstring.o sshzlib.o \ stripctrl.o supdup.o telnet.o terminal.o time.o timing.o \ toucs.o tree234.o utf8.o utils.o ux_x11.o uxagentc.o uxcfg.o \ uxfdsock.o uxgss.o uxmisc.o uxnet.o uxnoise.o uxpeer.o \ uxpoll.o uxprint.o uxproxy.o uxputty.o uxsel.o uxser.o \ uxshare.o uxsignal.o uxstore.o uxucs.o uxutils.o version.o \ wcwidth.o wildcard.o x11fwd.o x11misc.o xenc.o xkeysym.o \ xpmpucfg.o xpmputty.o $(CC) -o $@ agentf.o aqsync.o be_all_s.o be_misc.o callback.o \ cmdline.o conf.o config.o cproxy.o dialog.o ecc.o errsock.o \ fromucs.o gtkcfg.o gtkcols.o gtkcomm.o gtkdlg.o gtkfont.o \ gtkmain.o gtkmisc.o gtkwin.o ldisc.o localenc.o logging.o \ macenc.o mainchan.o marshal.o memory.o mimeenc.o minibidi.o \ misc.o miscucs.o mpint.o nullplug.o pgssapi.o pinger.o \ portfwd.o proxy.o raw.o rlogin.o sbcs.o sbcsdat.o sessprep.o \ settings.o slookup.o ssh.o ssh1bpp.o ssh1censor.o \ ssh1connection.o ssh1connection-client.o ssh1login.o \ ssh2bpp.o ssh2bpp-bare.o ssh2censor.o ssh2connection.o \ ssh2connection-client.o ssh2kex-client.o ssh2transhk.o \ ssh2transport.o ssh2userauth.o sshaes.o ssharcf.o \ sshargon2.o sshauxcrypt.o sshblake2.o sshblowf.o sshccp.o \ sshcommon.o sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o \ sshecc.o sshgssc.o sshhmac.o sshmac.o sshmd5.o sshprng.o \ sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \ sshsha3.o sshshare.o sshutils.o sshverstring.o sshzlib.o \ stripctrl.o supdup.o telnet.o terminal.o time.o timing.o \ toucs.o tree234.o utf8.o utils.o ux_x11.o uxagentc.o uxcfg.o \ uxfdsock.o uxgss.o uxmisc.o uxnet.o uxnoise.o uxpeer.o \ uxpoll.o uxprint.o uxproxy.o uxputty.o uxsel.o uxser.o \ uxshare.o uxsignal.o uxstore.o uxucs.o uxutils.o version.o \ wcwidth.o wildcard.o x11fwd.o x11misc.o xenc.o xkeysym.o \ xpmpucfg.o xpmputty.o $(XLDFLAGS) puttyapp: agentf.o aqsync.o be_all_s.o be_misc.o callback.o conf.o config.o \ cproxy.o dialog.o ecc.o errsock.o fromucs.o gtkapp.o \ gtkcfg.o gtkcols.o gtkcomm.o gtkdlg.o gtkfont.o gtkmisc.o \ gtkwin.o ldisc.o localenc.o logging.o macenc.o mainchan.o \ marshal.o memory.o mimeenc.o minibidi.o misc.o miscucs.o \ mpint.o nocmdline.o nullplug.o pgssapi.o pinger.o portfwd.o \ proxy.o raw.o rlogin.o sbcs.o sbcsdat.o sessprep.o \ settings.o slookup.o ssh.o ssh1bpp.o ssh1censor.o \ ssh1connection.o ssh1connection-client.o ssh1login.o \ ssh2bpp.o ssh2bpp-bare.o ssh2censor.o ssh2connection.o \ ssh2connection-client.o ssh2kex-client.o ssh2transhk.o \ ssh2transport.o ssh2userauth.o sshaes.o ssharcf.o \ sshargon2.o sshauxcrypt.o sshblake2.o sshblowf.o sshccp.o \ sshcommon.o sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o \ sshecc.o sshgssc.o sshhmac.o sshmac.o sshmd5.o sshprng.o \ sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \ sshsha3.o sshshare.o sshutils.o sshverstring.o sshzlib.o \ stripctrl.o supdup.o telnet.o terminal.o time.o timing.o \ toucs.o tree234.o utf8.o utils.o ux_x11.o uxagentc.o uxcfg.o \ uxfdsock.o uxgss.o uxmisc.o uxnet.o uxnoise.o uxpeer.o \ uxpoll.o uxprint.o uxproxy.o uxputty.o uxsel.o uxser.o \ uxshare.o uxsignal.o uxstore.o uxucs.o uxutils.o version.o \ wcwidth.o wildcard.o x11fwd.o x11misc.o xenc.o xkeysym.o \ xpmpucfg.o xpmputty.o $(CC) -o $@ agentf.o aqsync.o be_all_s.o be_misc.o callback.o conf.o \ config.o cproxy.o dialog.o ecc.o errsock.o fromucs.o \ gtkapp.o gtkcfg.o gtkcols.o gtkcomm.o gtkdlg.o gtkfont.o \ gtkmisc.o gtkwin.o ldisc.o localenc.o logging.o macenc.o \ mainchan.o marshal.o memory.o mimeenc.o minibidi.o misc.o \ miscucs.o mpint.o nocmdline.o nullplug.o pgssapi.o pinger.o \ portfwd.o proxy.o raw.o rlogin.o sbcs.o sbcsdat.o sessprep.o \ settings.o slookup.o ssh.o ssh1bpp.o ssh1censor.o \ ssh1connection.o ssh1connection-client.o ssh1login.o \ ssh2bpp.o ssh2bpp-bare.o ssh2censor.o ssh2connection.o \ ssh2connection-client.o ssh2kex-client.o ssh2transhk.o \ ssh2transport.o ssh2userauth.o sshaes.o ssharcf.o \ sshargon2.o sshauxcrypt.o sshblake2.o sshblowf.o sshccp.o \ sshcommon.o sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o \ sshecc.o sshgssc.o sshhmac.o sshmac.o sshmd5.o sshprng.o \ sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \ sshsha3.o sshshare.o sshutils.o sshverstring.o sshzlib.o \ stripctrl.o supdup.o telnet.o terminal.o time.o timing.o \ toucs.o tree234.o utf8.o utils.o ux_x11.o uxagentc.o uxcfg.o \ uxfdsock.o uxgss.o uxmisc.o uxnet.o uxnoise.o uxpeer.o \ uxpoll.o uxprint.o uxproxy.o uxputty.o uxsel.o uxser.o \ uxshare.o uxsignal.o uxstore.o uxucs.o uxutils.o version.o \ wcwidth.o wildcard.o x11fwd.o x11misc.o xenc.o xkeysym.o \ xpmpucfg.o xpmputty.o $(XLDFLAGS) puttygen: cmdgen.o conf.o console.o ecc.o import.o marshal.o memory.o \ millerrabin.o misc.o mpint.o mpunsafe.o notiming.o pockle.o \ primecandidate.o smallprimes.o sshaes.o sshargon2.o \ sshauxcrypt.o sshbcrypt.o sshblake2.o sshblowf.o sshdes.o \ sshdss.o sshdssg.o sshecc.o sshecdsag.o sshhmac.o sshmd5.o \ sshprime.o sshprng.o sshpubk.o sshrand.o sshrsa.o sshrsag.o \ sshsh256.o sshsh512.o sshsha.o sshsha3.o stripctrl.o time.o \ tree234.o utils.o uxcons.o uxgen.o uxmisc.o uxnogtk.o \ uxnoise.o uxpoll.o uxstore.o uxutils.o version.o wcwidth.o $(CC) -o $@ cmdgen.o conf.o console.o ecc.o import.o marshal.o \ memory.o millerrabin.o misc.o mpint.o mpunsafe.o notiming.o \ pockle.o primecandidate.o smallprimes.o sshaes.o sshargon2.o \ sshauxcrypt.o sshbcrypt.o sshblake2.o sshblowf.o sshdes.o \ sshdss.o sshdssg.o sshecc.o sshecdsag.o sshhmac.o sshmd5.o \ sshprime.o sshprng.o sshpubk.o sshrand.o sshrsa.o sshrsag.o \ sshsh256.o sshsh512.o sshsha.o sshsha3.o stripctrl.o time.o \ tree234.o utils.o uxcons.o uxgen.o uxmisc.o uxnogtk.o \ uxnoise.o uxpoll.o uxstore.o uxutils.o version.o wcwidth.o \ $(ULDFLAGS) puttytel: be_misc.o be_nos_s.o callback.o cmdline.o conf.o config.o dialog.o \ errsock.o fromucs.o gtkcfg.o gtkcols.o gtkcomm.o gtkdlg.o \ gtkfont.o gtkmain.o gtkmisc.o gtkwin.o ldisc.o localenc.o \ logging.o macenc.o marshal.o memory.o mimeenc.o minibidi.o \ misc.o miscucs.o nocproxy.o nogss.o norand.o pinger.o \ proxy.o raw.o rlogin.o sbcs.o sbcsdat.o sessprep.o \ settings.o slookup.o stripctrl.o supdup.o telnet.o \ terminal.o time.o timing.o toucs.o tree234.o utf8.o utils.o \ uxcfg.o uxfdsock.o uxmisc.o uxnet.o uxpeer.o uxpoll.o \ uxprint.o uxproxy.o uxputty.o uxsel.o uxser.o uxsignal.o \ uxstore.o uxucs.o uxutils.o version.o wcwidth.o x11misc.o \ xenc.o xkeysym.o xpmpucfg.o xpmputty.o $(CC) -o $@ be_misc.o be_nos_s.o callback.o cmdline.o conf.o \ config.o dialog.o errsock.o fromucs.o gtkcfg.o gtkcols.o \ gtkcomm.o gtkdlg.o gtkfont.o gtkmain.o gtkmisc.o gtkwin.o \ ldisc.o localenc.o logging.o macenc.o marshal.o memory.o \ mimeenc.o minibidi.o misc.o miscucs.o nocproxy.o nogss.o \ norand.o pinger.o proxy.o raw.o rlogin.o sbcs.o sbcsdat.o \ sessprep.o settings.o slookup.o stripctrl.o supdup.o \ telnet.o terminal.o time.o timing.o toucs.o tree234.o utf8.o \ utils.o uxcfg.o uxfdsock.o uxmisc.o uxnet.o uxpeer.o \ uxpoll.o uxprint.o uxproxy.o uxputty.o uxsel.o uxser.o \ uxsignal.o uxstore.o uxucs.o uxutils.o version.o wcwidth.o \ x11misc.o xenc.o xkeysym.o xpmpucfg.o xpmputty.o $(XLDFLAGS) testcrypt: ecc.o marshal.o memory.o millerrabin.o mpint.o mpunsafe.o \ pockle.o primecandidate.o smallprimes.o sshaes.o ssharcf.o \ sshargon2.o sshauxcrypt.o sshblake2.o sshblowf.o sshccp.o \ sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o sshdssg.o \ sshecc.o sshecdsag.o sshhmac.o sshmd5.o sshprime.o sshprng.o \ sshpubk.o sshrsa.o sshrsag.o sshsh256.o sshsh512.o sshsha.o \ sshsha3.o testcrypt.o tree234.o utils.o uxutils.o $(CC) -o $@ ecc.o marshal.o memory.o millerrabin.o mpint.o \ mpunsafe.o pockle.o primecandidate.o smallprimes.o sshaes.o \ ssharcf.o sshargon2.o sshauxcrypt.o sshblake2.o sshblowf.o \ sshccp.o sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o \ sshdssg.o sshecc.o sshecdsag.o sshhmac.o sshmd5.o sshprime.o \ sshprng.o sshpubk.o sshrsa.o sshrsag.o sshsh256.o sshsh512.o \ sshsha.o sshsha3.o testcrypt.o tree234.o utils.o uxutils.o \ $(ULDFLAGS) testsc: ecc.o marshal.o memory.o mpint.o sshaes.o ssharcf.o sshargon2.o \ sshauxcrypt.o sshblake2.o sshblowf.o sshccp.o sshcrc.o \ sshcrcda.o sshdes.o sshdh.o sshdss.o sshecc.o sshhmac.o \ sshmac.o sshmd5.o sshpubk.o sshrsa.o sshsh256.o sshsh512.o \ sshsha.o sshsha3.o testsc.o tree234.o utils.o uxutils.o \ wildcard.o $(CC) -o $@ ecc.o marshal.o memory.o mpint.o sshaes.o ssharcf.o \ sshargon2.o sshauxcrypt.o sshblake2.o sshblowf.o sshccp.o \ sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o sshecc.o \ sshhmac.o sshmac.o sshmd5.o sshpubk.o sshrsa.o sshsh256.o \ sshsh512.o sshsha.o sshsha3.o testsc.o tree234.o utils.o \ uxutils.o wildcard.o $(ULDFLAGS) testzlib: marshal.o memory.o sshzlib.o testzlib.o utils.o $(CC) -o $@ marshal.o memory.o sshzlib.o testzlib.o utils.o \ $(ULDFLAGS) uppity: be_misc.o be_none.o callback.o conf.o cproxy.o ecc.o errsock.o \ logging.o marshal.o memory.o millerrabin.o misc.o mpint.o \ mpunsafe.o nullplug.o pgssapi.o pockle.o portfwd.o \ primecandidate.o procnet.o proxy.o scpserver.o sesschan.o \ settings.o sftpcommon.o sftpserver.o smallprimes.o ssh1bpp.o \ ssh1censor.o ssh1connection.o ssh1connection-server.o \ ssh1login-server.o ssh2bpp.o ssh2bpp-bare.o ssh2censor.o \ ssh2connection.o ssh2connection-server.o ssh2kex-server.o \ ssh2transhk.o ssh2transport.o ssh2userauth-server.o sshaes.o \ ssharcf.o sshargon2.o sshauxcrypt.o sshblake2.o sshblowf.o \ sshccp.o sshcommon.o sshcrc.o sshcrcda.o sshdes.o sshdh.o \ sshdss.o sshecc.o sshgssc.o sshhmac.o sshmac.o sshmd5.o \ sshprime.o sshprng.o sshpubk.o sshrand.o sshrsa.o sshrsag.o \ sshserver.o sshsh256.o sshsh512.o sshsha.o sshsha3.o \ sshutils.o sshverstring.o sshzlib.o stripctrl.o time.o \ timing.o tree234.o utils.o ux_x11.o uxagentsock.o \ uxcliloop.o uxfdsock.o uxgss.o uxmisc.o uxnet.o uxnogtk.o \ uxnoise.o uxpeer.o uxpoll.o uxproxy.o uxpty.o uxsel.o \ uxserver.o uxsftpserver.o uxsignal.o uxstore.o uxutils.o \ version.o wcwidth.o wildcard.o x11fwd.o $(CC) -o $@ be_misc.o be_none.o callback.o conf.o cproxy.o ecc.o \ errsock.o logging.o marshal.o memory.o millerrabin.o misc.o \ mpint.o mpunsafe.o nullplug.o pgssapi.o pockle.o portfwd.o \ primecandidate.o procnet.o proxy.o scpserver.o sesschan.o \ settings.o sftpcommon.o sftpserver.o smallprimes.o ssh1bpp.o \ ssh1censor.o ssh1connection.o ssh1connection-server.o \ ssh1login-server.o ssh2bpp.o ssh2bpp-bare.o ssh2censor.o \ ssh2connection.o ssh2connection-server.o ssh2kex-server.o \ ssh2transhk.o ssh2transport.o ssh2userauth-server.o sshaes.o \ ssharcf.o sshargon2.o sshauxcrypt.o sshblake2.o sshblowf.o \ sshccp.o sshcommon.o sshcrc.o sshcrcda.o sshdes.o sshdh.o \ sshdss.o sshecc.o sshgssc.o sshhmac.o sshmac.o sshmd5.o \ sshprime.o sshprng.o sshpubk.o sshrand.o sshrsa.o sshrsag.o \ sshserver.o sshsh256.o sshsh512.o sshsha.o sshsha3.o \ sshutils.o sshverstring.o sshzlib.o stripctrl.o time.o \ timing.o tree234.o utils.o ux_x11.o uxagentsock.o \ uxcliloop.o uxfdsock.o uxgss.o uxmisc.o uxnet.o uxnogtk.o \ uxnoise.o uxpeer.o uxpoll.o uxproxy.o uxpty.o uxsel.o \ uxserver.o uxsftpserver.o uxsignal.o uxstore.o uxutils.o \ version.o wcwidth.o wildcard.o x11fwd.o $(ULDFLAGS) agentf.o: ../agentf.c ../putty.h ../ssh.h ../pageant.h ../sshchan.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../agentf.c aqsync.o: ../aqsync.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../aqsync.c be_all_s.o: ../be_all_s.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../be_all_s.c be_misc.o: ../be_misc.c ../putty.h ../network.h ../defs.h ../puttyps.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../be_misc.c be_none.o: ../be_none.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../be_none.c be_nos_s.o: ../be_nos_s.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../be_nos_s.c be_ssh.o: ../be_ssh.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../be_ssh.c callback.o: ../callback.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../callback.c cgtest.o: ../cgtest.c ../cmdgen.c ../putty.h ../ssh.h ../sshkeygen.h \ ../mpint.h ../defs.h ../puttyps.h ../network.h ../misc.h \ ../marshal.h ../sshsignals.h ../puttymem.h ../tree234.h \ ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../cgtest.c clicons.o: ../clicons.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../clicons.c cmdgen.o: ../cmdgen.c ../putty.h ../ssh.h ../sshkeygen.h ../mpint.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../cmdgen.c cmdline.o: ../cmdline.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../cmdline.c conf.o: ../conf.c ../tree234.h ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../conf.c config.o: ../config.c ../putty.h ../dialog.h ../storage.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../config.c console.o: ../console.c ../putty.h ../misc.h ../console.h ../defs.h \ ../puttyps.h ../network.h ../marshal.h ../sshsignals.h \ ../puttymem.h ../windows/winstuff.h ../unix/unix.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../console.c cproxy.o: ../cproxy.c ../putty.h ../ssh.h ../network.h ../proxy.h \ ../marshal.h ../defs.h ../puttyps.h ../misc.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../cproxy.c dialog.o: ../dialog.c ../putty.h ../dialog.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../dialog.c ecc.o: ../ecc.c ../ssh.h ../mpint.h ../ecc.h ../puttymem.h ../tree234.h \ ../network.h ../misc.h ../sshttymodes.h ../defs.h \ ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ecc.c errsock.o: ../errsock.c ../tree234.h ../putty.h ../network.h ../defs.h \ ../puttyps.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../errsock.c fromucs.o: ../charset/fromucs.c ../charset/charset.h ../charset/internal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/fromucs.c fuzzterm.o: ../fuzzterm.c ../putty.h ../dialog.h ../terminal.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../tree234.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../fuzzterm.c gtkapp.o: ../unix/gtkapp.c ../putty.h ../unix/gtkmisc.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkapp.c gtkask.o: ../unix/gtkask.c ../defs.h ../unix/gtkfont.h ../unix/gtkcompat.h \ ../unix/gtkmisc.h ../putty.h ../ssh.h ../misc.h ../puttyps.h \ ../network.h ../marshal.h ../sshsignals.h ../puttymem.h \ ../tree234.h ../sshttymodes.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkask.c gtkcfg.o: ../unix/gtkcfg.c ../putty.h ../dialog.h ../storage.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkcfg.c gtkcols.o: ../unix/gtkcols.c ../defs.h ../unix/gtkcompat.h ../unix/gtkcols.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkcols.c gtkcomm.o: ../unix/gtkcomm.c ../putty.h ../terminal.h ../unix/gtkcompat.h \ ../unix/gtkfont.h ../unix/gtkmisc.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../tree234.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkcomm.c gtkdlg.o: ../unix/gtkdlg.c ../putty.h ../unix/gtkcompat.h ../unix/gtkcols.h \ ../unix/gtkfont.h ../unix/gtkmisc.h ../unix/x11misc.h \ ../storage.h ../dialog.h ../tree234.h ../licence.h ../ssh.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkdlg.c gtkfont.o: ../unix/gtkfont.c ../putty.h ../unix/gtkfont.h \ ../unix/gtkcompat.h ../unix/gtkmisc.h ../tree234.h \ ../unix/x11misc.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkfont.c gtkmain.o: ../unix/gtkmain.c ../putty.h ../terminal.h ../unix/gtkcompat.h \ ../unix/gtkfont.h ../unix/gtkmisc.h ../unix/x11misc.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../tree234.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkmain.c gtkmisc.o: ../unix/gtkmisc.c ../putty.h ../unix/gtkcompat.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkmisc.c gtkwin.o: ../unix/gtkwin.c ../putty.h ../terminal.h ../unix/gtkcompat.h \ ../unix/gtkfont.h ../unix/gtkmisc.h ../unix/x11misc.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../tree234.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkwin.c import.o: ../import.c ../putty.h ../ssh.h ../mpint.h ../misc.h ../defs.h \ ../puttyps.h ../network.h ../marshal.h ../sshsignals.h \ ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../import.c ldisc.o: ../ldisc.c ../putty.h ../terminal.h ../ldisc.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../tree234.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ldisc.c localenc.o: ../charset/localenc.c ../charset/charset.h ../charset/internal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/localenc.c logging.o: ../logging.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../logging.c macenc.o: ../charset/macenc.c ../charset/charset.h ../charset/internal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/macenc.c mainchan.o: ../mainchan.c ../putty.h ../ssh.h ../sshppl.h ../sshchan.h \ ../sshsignals.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../puttymem.h ../tree234.h \ ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../mainchan.c marshal.o: ../marshal.c ../marshal.h ../misc.h ../defs.h ../puttymem.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../marshal.c memory.o: ../memory.c ../defs.h ../puttymem.h ../misc.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../memory.c millerrabin.o: ../millerrabin.c ../ssh.h ../sshkeygen.h ../mpint.h \ ../mpunsafe.h ../puttymem.h ../tree234.h ../network.h \ ../misc.h ../sshttymodes.h ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../millerrabin.c mimeenc.o: ../charset/mimeenc.c ../charset/charset.h ../charset/internal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/mimeenc.c minibidi.o: ../minibidi.c ../putty.h ../misc.h ../defs.h ../puttyps.h \ ../network.h ../marshal.h ../sshsignals.h ../puttymem.h \ ../windows/winstuff.h ../unix/unix.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../minibidi.c misc.o: ../misc.c ../defs.h ../putty.h ../misc.h ../puttyps.h ../network.h \ ../marshal.h ../sshsignals.h ../puttymem.h \ ../windows/winstuff.h ../unix/unix.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../misc.c miscucs.o: ../miscucs.c ../putty.h ../misc.h ../defs.h ../puttyps.h \ ../network.h ../marshal.h ../sshsignals.h ../puttymem.h \ ../windows/winstuff.h ../unix/unix.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../miscucs.c mpint.o: ../mpint.c ../defs.h ../misc.h ../puttymem.h ../mpint.h \ ../mpint_i.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../mpint.c mpunsafe.o: ../mpunsafe.c ../defs.h ../misc.h ../puttymem.h ../mpint.h \ ../mpint_i.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../mpunsafe.c nocmdline.o: ../nocmdline.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../nocmdline.c nocproxy.o: ../nocproxy.c ../putty.h ../network.h ../proxy.h ../defs.h \ ../puttyps.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../nocproxy.c nogss.o: ../nogss.c ../putty.h ../defs.h ../puttyps.h ../network.h ../misc.h \ ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../nogss.c norand.o: ../norand.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../norand.c noterm.o: ../noterm.c ../putty.h ../terminal.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../tree234.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../noterm.c notiming.o: ../notiming.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../notiming.c nullplug.o: ../nullplug.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../nullplug.c osxlaunch.o: ../unix/osxlaunch.c $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/osxlaunch.c pageant.o: ../pageant.c ../putty.h ../mpint.h ../ssh.h ../sshcr.h \ ../pageant.h ../defs.h ../puttyps.h ../network.h ../misc.h \ ../marshal.h ../sshsignals.h ../puttymem.h ../tree234.h \ ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../pageant.c pgssapi.o: ../pgssapi.c ../putty.h ../pgssapi.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../pgssapi.c pinger.o: ../pinger.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../pinger.c pockle.o: ../pockle.c ../ssh.h ../sshkeygen.h ../mpint.h ../mpunsafe.h \ ../tree234.h ../puttymem.h ../network.h ../misc.h \ ../sshttymodes.h ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../pockle.c portfwd.o: ../portfwd.c ../putty.h ../ssh.h ../sshchan.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../portfwd.c primecandidate.o: ../primecandidate.c ../ssh.h ../mpint.h ../mpunsafe.h \ ../sshkeygen.h ../puttymem.h ../tree234.h ../network.h \ ../misc.h ../sshttymodes.h ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../primecandidate.c procnet.o: ../unix/procnet.c ../misc.h ../defs.h ../puttymem.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/procnet.c proxy.o: ../proxy.c ../putty.h ../network.h ../proxy.h ../defs.h \ ../puttyps.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../proxy.c pscp.o: ../pscp.c ../putty.h ../psftp.h ../ssh.h ../sftp.h ../storage.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../pscp.c psftp.o: ../psftp.c ../putty.h ../psftp.h ../storage.h ../ssh.h ../sftp.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../psftp.c psftpcommon.o: ../psftpcommon.c ../putty.h ../sftp.h ../psftp.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../psftpcommon.c psocks.o: ../psocks.c ../putty.h ../misc.h ../ssh.h ../sshchan.h ../psocks.h \ ../defs.h ../puttyps.h ../network.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../psocks.c raw.o: ../raw.c ../putty.h ../defs.h ../puttyps.h ../network.h ../misc.h \ ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../raw.c rlogin.o: ../rlogin.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../rlogin.c sbcs.o: ../charset/sbcs.c ../charset/charset.h ../charset/internal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/sbcs.c sbcsdat.o: ../charset/sbcsdat.c ../charset/charset.h ../charset/internal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/sbcsdat.c scpserver.o: ../scpserver.c ../putty.h ../ssh.h ../sshcr.h ../sshchan.h \ ../sftp.h ../defs.h ../puttyps.h ../network.h ../misc.h \ ../marshal.h ../sshsignals.h ../puttymem.h ../tree234.h \ ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../scpserver.c sesschan.o: ../sesschan.c ../putty.h ../ssh.h ../sshchan.h ../sshserver.h \ ../sftp.h ../sshsignals.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../puttymem.h \ ../tree234.h ../sshttymodes.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sesschan.c sessprep.o: ../sessprep.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sessprep.c settings.o: ../settings.c ../putty.h ../storage.h ../sshgssc.h ../sshgss.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../pgssapi.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../settings.c sftp.o: ../sftp.c ../misc.h ../tree234.h ../sftp.h ../defs.h ../puttymem.h \ ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sftp.c sftpcommon.o: ../sftpcommon.c ../misc.h ../sftp.h ../defs.h ../puttymem.h \ ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sftpcommon.c sftpserver.o: ../sftpserver.c ../putty.h ../ssh.h ../sftp.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sftpserver.c sizetip.o: ../windows/sizetip.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/sizetip.c slookup.o: ../charset/slookup.c ../charset/charset.h ../charset/internal.h \ ../charset/enum.c ../charset/sbcsdat.c ../charset/utf8.c $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/slookup.c smallprimes.o: ../smallprimes.c ../ssh.h ../sshkeygen.h ../puttymem.h \ ../tree234.h ../network.h ../misc.h ../sshttymodes.h \ ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../smallprimes.c ssh.o: ../ssh.c ../putty.h ../pageant.h ../tree234.h ../storage.h \ ../marshal.h ../ssh.h ../sshcr.h ../sshbpp.h ../sshppl.h \ ../sshchan.h ../sshgssc.h ../sshgss.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../sshsignals.h ../puttymem.h \ ../sshttymodes.h ../pgssapi.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh.c ssh1bpp.o: ../ssh1bpp.c ../putty.h ../ssh.h ../sshbpp.h ../sshcr.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh1bpp.c ssh1censor.o: ../ssh1censor.c ../putty.h ../ssh.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh1censor.c ssh1connection.o: ../ssh1connection.c ../putty.h ../ssh.h ../sshbpp.h \ ../sshppl.h ../sshchan.h ../sshcr.h ../ssh1connection.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh1connection.c ssh1connection-client.o: ../ssh1connection-client.c ../putty.h ../ssh.h \ ../sshbpp.h ../sshppl.h ../sshchan.h ../sshcr.h \ ../ssh1connection.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ ../tree234.h ../sshttymodes.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh1connection-client.c ssh1connection-server.o: ../ssh1connection-server.c ../putty.h ../ssh.h \ ../sshbpp.h ../sshppl.h ../sshchan.h ../sshcr.h \ ../ssh1connection.h ../sshserver.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh1connection-server.c ssh1login.o: ../ssh1login.c ../putty.h ../ssh.h ../mpint.h ../sshbpp.h \ ../sshppl.h ../sshcr.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ ../tree234.h ../sshttymodes.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh1login.c ssh1login-server.o: ../ssh1login-server.c ../putty.h ../mpint.h ../ssh.h \ ../sshbpp.h ../sshppl.h ../sshcr.h ../sshserver.h \ ../sshkeygen.h ../defs.h ../puttyps.h ../network.h ../misc.h \ ../marshal.h ../sshsignals.h ../puttymem.h ../tree234.h \ ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh1login-server.c ssh2bpp.o: ../ssh2bpp.c ../putty.h ../ssh.h ../sshbpp.h ../sshcr.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh2bpp.c ssh2bpp-bare.o: ../ssh2bpp-bare.c ../putty.h ../ssh.h ../sshbpp.h ../sshcr.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh2bpp-bare.c ssh2censor.o: ../ssh2censor.c ../putty.h ../ssh.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh2censor.c ssh2connection.o: ../ssh2connection.c ../putty.h ../ssh.h ../sshbpp.h \ ../sshppl.h ../sshchan.h ../sshcr.h ../ssh2connection.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh2connection.c ssh2connection-client.o: ../ssh2connection-client.c ../putty.h ../ssh.h \ ../sshbpp.h ../sshppl.h ../sshchan.h ../sshcr.h \ ../ssh2connection.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ ../tree234.h ../sshttymodes.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh2connection-client.c ssh2connection-server.o: ../ssh2connection-server.c ../putty.h ../ssh.h \ ../sshbpp.h ../sshppl.h ../sshchan.h ../sshcr.h \ ../ssh2connection.h ../sshserver.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh2connection-server.c ssh2kex-client.o: ../ssh2kex-client.c ../putty.h ../ssh.h ../sshbpp.h \ ../sshppl.h ../sshcr.h ../storage.h ../ssh2transport.h \ ../mpint.h ../defs.h ../puttyps.h ../network.h ../misc.h \ ../marshal.h ../sshsignals.h ../puttymem.h ../tree234.h \ ../sshttymodes.h ../sshgssc.h ../sshgss.h \ ../windows/winstuff.h ../unix/unix.h ../pgssapi.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh2kex-client.c ssh2kex-server.o: ../ssh2kex-server.c ../putty.h ../ssh.h ../sshbpp.h \ ../sshppl.h ../sshcr.h ../sshserver.h ../sshkeygen.h \ ../storage.h ../ssh2transport.h ../mpint.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../sshgssc.h ../sshgss.h ../windows/winstuff.h \ ../unix/unix.h ../pgssapi.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh2kex-server.c ssh2transhk.o: ../ssh2transhk.c ../putty.h ../ssh.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh2transhk.c ssh2transport.o: ../ssh2transport.c ../putty.h ../ssh.h ../sshbpp.h \ ../sshppl.h ../sshcr.h ../sshserver.h ../storage.h \ ../ssh2transport.h ../mpint.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../puttymem.h ../tree234.h ../sshttymodes.h ../sshgssc.h \ ../sshgss.h ../windows/winstuff.h ../unix/unix.h \ ../pgssapi.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh2transport.c ssh2userauth.o: ../ssh2userauth.c ../putty.h ../ssh.h ../sshbpp.h \ ../sshppl.h ../sshcr.h ../sshgssc.h ../sshgss.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../pgssapi.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh2userauth.c ssh2userauth-server.o: ../ssh2userauth-server.c ../putty.h ../ssh.h \ ../sshbpp.h ../sshppl.h ../sshcr.h ../sshserver.h \ ../sshgssc.h ../sshgss.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ ../tree234.h ../sshttymodes.h ../pgssapi.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh2userauth-server.c sshaes.o: ../sshaes.c ../ssh.h ../mpint_i.h ../puttymem.h ../tree234.h \ ../network.h ../misc.h ../sshttymodes.h ../defs.h \ ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshaes.c ssharcf.o: ../ssharcf.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ ../misc.h ../sshttymodes.h ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssharcf.c sshargon2.o: ../sshargon2.c ../putty.h ../ssh.h ../marshal.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../sshsignals.h \ ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshargon2.c sshauxcrypt.o: ../sshauxcrypt.c ../ssh.h ../puttymem.h ../tree234.h \ ../network.h ../misc.h ../sshttymodes.h ../defs.h \ ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshauxcrypt.c sshbcrypt.o: ../sshbcrypt.c ../ssh.h ../sshblowf.h ../puttymem.h \ ../tree234.h ../network.h ../misc.h ../sshttymodes.h \ ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshbcrypt.c sshblake2.o: ../sshblake2.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ ../misc.h ../sshttymodes.h ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshblake2.c sshblowf.o: ../sshblowf.c ../ssh.h ../sshblowf.h ../puttymem.h ../tree234.h \ ../network.h ../misc.h ../sshttymodes.h ../defs.h \ ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshblowf.c sshccp.o: ../sshccp.c ../ssh.h ../mpint_i.h ../puttymem.h ../tree234.h \ ../network.h ../misc.h ../sshttymodes.h ../defs.h \ ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshccp.c sshcommon.o: ../sshcommon.c ../putty.h ../mpint.h ../ssh.h ../sshbpp.h \ ../sshppl.h ../sshchan.h ../sshttymodes.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshcommon.c sshcrc.o: ../sshcrc.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ ../misc.h ../sshttymodes.h ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshcrc.c sshcrcda.o: ../sshcrcda.c ../misc.h ../ssh.h ../defs.h ../puttymem.h \ ../marshal.h ../tree234.h ../network.h ../sshttymodes.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshcrcda.c sshdes.o: ../sshdes.c ../ssh.h ../mpint_i.h ../puttymem.h ../tree234.h \ ../network.h ../misc.h ../sshttymodes.h ../defs.h \ ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshdes.c sshdh.o: ../sshdh.c ../ssh.h ../misc.h ../mpint.h ../puttymem.h ../tree234.h \ ../network.h ../sshttymodes.h ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshdh.c sshdss.o: ../sshdss.c ../ssh.h ../mpint.h ../misc.h ../puttymem.h \ ../tree234.h ../network.h ../sshttymodes.h ../defs.h \ ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshdss.c sshdssg.o: ../sshdssg.c ../misc.h ../ssh.h ../sshkeygen.h ../mpint.h \ ../defs.h ../puttymem.h ../marshal.h ../tree234.h \ ../network.h ../sshttymodes.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshdssg.c sshecc.o: ../sshecc.c ../ssh.h ../mpint.h ../ecc.h ../puttymem.h \ ../tree234.h ../network.h ../misc.h ../sshttymodes.h \ ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshecc.c sshecdsag.o: ../sshecdsag.c ../ssh.h ../sshkeygen.h ../mpint.h ../puttymem.h \ ../tree234.h ../network.h ../misc.h ../sshttymodes.h \ ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshecdsag.c sshgssc.o: ../sshgssc.c ../putty.h ../sshgssc.h ../misc.h ../defs.h \ ../puttyps.h ../network.h ../marshal.h ../sshsignals.h \ ../pgssapi.h ../sshgss.h ../puttymem.h ../windows/winstuff.h \ ../unix/unix.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshgssc.c sshhmac.o: ../sshhmac.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ ../misc.h ../sshttymodes.h ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshhmac.c sshmac.o: ../sshmac.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ ../misc.h ../sshttymodes.h ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshmac.c sshmd5.o: ../sshmd5.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ ../misc.h ../sshttymodes.h ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshmd5.c sshprime.o: ../sshprime.c ../ssh.h ../mpint.h ../mpunsafe.h ../sshkeygen.h \ ../puttymem.h ../tree234.h ../network.h ../misc.h \ ../sshttymodes.h ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshprime.c sshprng.o: ../sshprng.c ../putty.h ../ssh.h ../mpint_i.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshprng.c sshpubk.o: ../sshpubk.c ../putty.h ../mpint.h ../ssh.h ../misc.h ../defs.h \ ../puttyps.h ../network.h ../marshal.h ../sshsignals.h \ ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshpubk.c sshrand.o: ../sshrand.c ../putty.h ../ssh.h ../storage.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshrand.c sshrsa.o: ../sshrsa.c ../ssh.h ../mpint.h ../misc.h ../puttymem.h \ ../tree234.h ../network.h ../sshttymodes.h ../defs.h \ ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshrsa.c sshrsag.o: ../sshrsag.c ../ssh.h ../sshkeygen.h ../mpint.h ../puttymem.h \ ../tree234.h ../network.h ../misc.h ../sshttymodes.h \ ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshrsag.c sshserver.o: ../sshserver.c ../putty.h ../ssh.h ../sshbpp.h ../sshppl.h \ ../sshchan.h ../sshserver.h ../sshgssc.h ../sshgss.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../pgssapi.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshserver.c sshsh256.o: ../sshsh256.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ ../misc.h ../sshttymodes.h ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshsh256.c sshsh512.o: ../sshsh512.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ ../misc.h ../sshttymodes.h ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshsh512.c sshsha.o: ../sshsha.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ ../misc.h ../sshttymodes.h ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshsha.c sshsha3.o: ../sshsha3.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ ../misc.h ../sshttymodes.h ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshsha3.c sshshare.o: ../sshshare.c ../putty.h ../tree234.h ../ssh.h ../sshcr.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshshare.c sshutils.o: ../sshutils.c ../putty.h ../ssh.h ../sshchan.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshutils.c sshverstring.o: ../sshverstring.c ../putty.h ../ssh.h ../sshbpp.h ../sshcr.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshverstring.c sshzlib.o: ../sshzlib.c ../defs.h ../ssh.h ../puttymem.h ../tree234.h \ ../network.h ../misc.h ../sshttymodes.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshzlib.c stripctrl.o: ../stripctrl.c ../putty.h ../terminal.h ../misc.h ../marshal.h \ ../defs.h ../puttyps.h ../network.h ../sshsignals.h \ ../tree234.h ../puttymem.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../stripctrl.c supdup.o: ../supdup.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../supdup.c telnet.o: ../telnet.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../telnet.c terminal.o: ../terminal.c ../putty.h ../terminal.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../tree234.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../terminal.c testcrypt.o: ../testcrypt.c ../defs.h ../ssh.h ../sshkeygen.h ../misc.h \ ../mpint.h ../ecc.h ../testcrypt.h ../puttymem.h \ ../tree234.h ../network.h ../sshttymodes.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../testcrypt.c testsc.o: ../testsc.c ../defs.h ../putty.h ../ssh.h ../misc.h ../mpint.h \ ../ecc.h ../puttyps.h ../network.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../testsc.c testzlib.o: ../testzlib.c ../defs.h ../ssh.h ../puttymem.h ../tree234.h \ ../network.h ../misc.h ../sshttymodes.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../testzlib.c time.o: ../time.c $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../time.c timing.o: ../timing.c ../putty.h ../tree234.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../timing.c toucs.o: ../charset/toucs.c ../charset/charset.h ../charset/internal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/toucs.c tree234.o: ../tree234.c ../defs.h ../tree234.h ../puttymem.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../tree234.c utf8.o: ../charset/utf8.c ../charset/charset.h ../charset/internal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/utf8.c utils.o: ../utils.c ../defs.h ../misc.h ../ssh.h ../puttymem.h ../marshal.h \ ../tree234.h ../network.h ../sshttymodes.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../utils.c ux_x11.o: ../unix/ux_x11.c ../putty.h ../ssh.h ../network.h ../defs.h \ ../puttyps.h ../misc.h ../marshal.h ../sshsignals.h \ ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/ux_x11.c uxagentc.o: ../unix/uxagentc.c ../putty.h ../misc.h ../tree234.h \ ../puttymem.h ../defs.h ../puttyps.h ../network.h \ ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxagentc.c uxagentsock.o: ../unix/uxagentsock.c ../putty.h ../ssh.h ../misc.h \ ../pageant.h ../defs.h ../puttyps.h ../network.h \ ../marshal.h ../sshsignals.h ../puttymem.h ../tree234.h \ ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxagentsock.c uxcfg.o: ../unix/uxcfg.c ../putty.h ../dialog.h ../storage.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxcfg.c uxcliloop.o: ../unix/uxcliloop.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxcliloop.c uxcons.o: ../unix/uxcons.c ../putty.h ../storage.h ../ssh.h ../console.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxcons.c uxfdsock.o: ../unix/uxfdsock.c ../tree234.h ../putty.h ../network.h \ ../defs.h ../puttyps.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxfdsock.c uxgen.o: ../unix/uxgen.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxgen.c uxgss.o: ../unix/uxgss.c ../putty.h ../pgssapi.h ../sshgss.h ../sshgssc.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxgss.c uxmisc.o: ../unix/uxmisc.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxmisc.c uxnet.o: ../unix/uxnet.c ../putty.h ../network.h ../tree234.h ../defs.h \ ../puttyps.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxnet.c uxnogtk.o: ../unix/uxnogtk.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxnogtk.c uxnoise.o: ../unix/uxnoise.c ../putty.h ../ssh.h ../storage.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxnoise.c uxpeer.o: ../unix/uxpeer.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxpeer.c uxpgnt.o: ../unix/uxpgnt.c ../putty.h ../ssh.h ../misc.h ../pageant.h \ ../defs.h ../puttyps.h ../network.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxpgnt.c uxplink.o: ../unix/uxplink.c ../putty.h ../ssh.h ../storage.h ../tree234.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxplink.c uxpoll.o: ../unix/uxpoll.c ../putty.h ../tree234.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxpoll.c uxprint.o: ../unix/uxprint.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxprint.c uxproxy.o: ../unix/uxproxy.c ../tree234.h ../putty.h ../network.h ../proxy.h \ ../defs.h ../puttyps.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxproxy.c uxpsusan.o: ../unix/uxpsusan.c ../putty.h ../mpint.h ../ssh.h ../sshserver.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxpsusan.c uxpterm.o: ../unix/uxpterm.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxpterm.c uxpty.o: ../unix/uxpty.c ../putty.h ../ssh.h ../sshserver.h ../tree234.h \ ../sshttymodes.h ../sshsignals.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../puttymem.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxpty.c uxputty.o: ../unix/uxputty.c ../putty.h ../ssh.h ../storage.h \ ../unix/gtkcompat.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ ../tree234.h ../sshttymodes.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxputty.c uxsel.o: ../unix/uxsel.c ../putty.h ../tree234.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxsel.c uxser.o: ../unix/uxser.c ../putty.h ../tree234.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxser.c uxserver.o: ../unix/uxserver.c ../putty.h ../mpint.h ../ssh.h ../sshserver.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxserver.c uxsftp.o: ../unix/uxsftp.c ../putty.h ../ssh.h ../psftp.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxsftp.c uxsftpserver.o: ../unix/uxsftpserver.c ../putty.h ../ssh.h ../sshserver.h \ ../sftp.h ../tree234.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxsftpserver.c uxshare.o: ../unix/uxshare.c ../tree234.h ../putty.h ../network.h ../proxy.h \ ../ssh.h ../defs.h ../puttyps.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxshare.c uxsignal.o: ../unix/uxsignal.c ../defs.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxsignal.c uxsocks.o: ../unix/uxsocks.c ../putty.h ../ssh.h ../psocks.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxsocks.c uxstore.o: ../unix/uxstore.c ../putty.h ../storage.h ../tree234.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxstore.c uxucs.o: ../unix/uxucs.c ../putty.h ../charset/charset.h ../terminal.h \ ../misc.h ../defs.h ../puttyps.h ../network.h ../marshal.h \ ../sshsignals.h ../tree234.h ../puttymem.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxucs.c uxutils.o: ../unix/uxutils.c ../putty.h ../ssh.h ../unix/uxutils.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxutils.c version.o: ../version.c ../putty.h ../ssh.h ../empty.h ../version.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../version.c wcwidth.o: ../wcwidth.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../wcwidth.c wildcard.o: ../wildcard.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../wildcard.c wincapi.o: ../windows/wincapi.c ../putty.h ../ssh.h ../windows/wincapi.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/wincapi.c wincfg.o: ../windows/wincfg.c ../putty.h ../dialog.h ../storage.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/wincfg.c wincliloop.o: ../windows/wincliloop.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/wincliloop.c wincons.o: ../windows/wincons.c ../putty.h ../storage.h ../ssh.h \ ../console.h ../defs.h ../puttyps.h ../network.h ../misc.h \ ../marshal.h ../sshsignals.h ../puttymem.h ../tree234.h \ ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/wincons.c winctrls.o: ../windows/winctrls.c ../putty.h ../misc.h ../dialog.h ../defs.h \ ../puttyps.h ../network.h ../marshal.h ../sshsignals.h \ ../puttymem.h ../windows/winstuff.h ../unix/unix.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winctrls.c windefs.o: ../windows/windefs.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/windefs.c windlg.o: ../windows/windlg.c ../putty.h ../ssh.h ../windows/win_res.h \ ../windows/winseat.h ../storage.h ../dialog.h ../licence.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/windlg.c window.o: ../windows/window.c ../putty.h ../terminal.h ../storage.h \ ../windows/win_res.h ../windows/winsecur.h \ ../windows/winseat.h ../tree234.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/window.c wingss.o: ../windows/wingss.c ../putty.h ../pgssapi.h ../sshgss.h \ ../sshgssc.h ../misc.h ../defs.h ../puttyps.h ../network.h \ ../marshal.h ../sshsignals.h ../puttymem.h \ ../windows/winstuff.h ../unix/unix.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/wingss.c winhandl.o: ../windows/winhandl.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winhandl.c winhelp.o: ../windows/winhelp.c ../putty.h ../windows/win_res.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winhelp.c winhsock.o: ../windows/winhsock.c ../tree234.h ../putty.h ../network.h \ ../defs.h ../puttyps.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winhsock.c winjump.o: ../windows/winjump.c ../putty.h ../storage.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winjump.c winmisc.o: ../windows/winmisc.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winmisc.c winmiscs.o: ../windows/winmiscs.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winmiscs.c winnet.o: ../windows/winnet.c ../putty.h ../network.h ../tree234.h ../ssh.h \ ../defs.h ../puttyps.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winnet.c winnohlp.o: ../windows/winnohlp.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winnohlp.c winnoise.o: ../windows/winnoise.c ../putty.h ../ssh.h ../storage.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winnoise.c winnojmp.o: ../windows/winnojmp.c $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winnojmp.c winnpc.o: ../windows/winnpc.c ../tree234.h ../putty.h ../network.h \ ../proxy.h ../ssh.h ../windows/winsecur.h ../defs.h \ ../puttyps.h ../misc.h ../marshal.h ../sshsignals.h \ ../puttymem.h ../sshttymodes.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winnpc.c winnps.o: ../windows/winnps.c ../tree234.h ../putty.h ../network.h \ ../proxy.h ../ssh.h ../windows/winsecur.h ../defs.h \ ../puttyps.h ../misc.h ../marshal.h ../sshsignals.h \ ../puttymem.h ../sshttymodes.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winnps.c winpgen.o: ../windows/winpgen.c ../putty.h ../ssh.h ../sshkeygen.h \ ../licence.h ../windows/winsecur.h ../windows/puttygen-rc.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winpgen.c winpgnt.o: ../windows/winpgnt.c ../putty.h ../ssh.h ../misc.h ../tree234.h \ ../windows/winsecur.h ../windows/wincapi.h ../pageant.h \ ../licence.h ../windows/pageant-rc.h ../defs.h ../puttyps.h \ ../network.h ../marshal.h ../sshsignals.h ../puttymem.h \ ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winpgnt.c winpgntc.o: ../windows/winpgntc.c ../putty.h ../pageant.h \ ../windows/winsecur.h ../windows/wincapi.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winpgntc.c winplink.o: ../windows/winplink.c ../putty.h ../storage.h ../tree234.h \ ../windows/winsecur.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winplink.c winprint.o: ../windows/winprint.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winprint.c winproxy.o: ../windows/winproxy.c ../tree234.h ../putty.h ../network.h \ ../proxy.h ../defs.h ../puttyps.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winproxy.c winsecur.o: ../windows/winsecur.c ../putty.h ../windows/winsecur.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winsecur.c winselcli.o: ../windows/winselcli.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winselcli.c winselgui.o: ../windows/winselgui.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winselgui.c winser.o: ../windows/winser.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winser.c winsftp.o: ../windows/winsftp.c ../putty.h ../psftp.h ../ssh.h \ ../windows/winsecur.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ ../tree234.h ../sshttymodes.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winsftp.c winshare.o: ../windows/winshare.c ../tree234.h ../putty.h ../network.h \ ../proxy.h ../ssh.h ../windows/wincapi.h \ ../windows/winsecur.h ../noshare.c ../defs.h ../puttyps.h \ ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winshare.c winsocks.o: ../windows/winsocks.c ../putty.h ../ssh.h ../psocks.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winsocks.c winstore.o: ../windows/winstore.c ../putty.h ../storage.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winstore.c wintime.o: ../windows/wintime.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/wintime.c winucs.o: ../windows/winucs.c ../putty.h ../terminal.h ../misc.h ../defs.h \ ../puttyps.h ../network.h ../marshal.h ../sshsignals.h \ ../tree234.h ../puttymem.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winucs.c winutils.o: ../windows/winutils.c ../putty.h ../misc.h ../defs.h \ ../puttyps.h ../network.h ../marshal.h ../sshsignals.h \ ../puttymem.h ../windows/winstuff.h ../unix/unix.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winutils.c winx11.o: ../windows/winx11.c ../putty.h ../ssh.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winx11.c x11fwd.o: ../x11fwd.c ../putty.h ../ssh.h ../sshchan.h ../tree234.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../x11fwd.c x11misc.o: ../unix/x11misc.c ../putty.h ../unix/x11misc.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/x11misc.c xenc.o: ../charset/xenc.c ../charset/charset.h ../charset/internal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/xenc.c xkeysym.o: ../unix/xkeysym.c ../misc.h ../defs.h ../puttymem.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/xkeysym.c xpmptcfg.o: ../unix/xpmptcfg.c $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/xpmptcfg.c xpmpterm.o: ../unix/xpmpterm.c $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/xpmpterm.c xpmpucfg.o: ../unix/xpmpucfg.c $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/xpmpucfg.c xpmputty.o: ../unix/xpmputty.c $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/xpmputty.c install: mkdir -p $(DESTDIR)$(bindir) $(DESTDIR)$(man1dir) $(INSTALL_PROGRAM) -m 755 pageant $(DESTDIR)$(bindir)/pageant $(INSTALL_PROGRAM) -m 755 plink $(DESTDIR)$(bindir)/plink $(INSTALL_PROGRAM) -m 755 pscp $(DESTDIR)$(bindir)/pscp $(INSTALL_PROGRAM) -m 755 psftp $(DESTDIR)$(bindir)/psftp $(INSTALL_PROGRAM) -m 755 pterm $(DESTDIR)$(bindir)/pterm if test -n "$(UTMP_GROUP)"; then \ chgrp $(UTMP_GROUP) $(DESTDIR)$(bindir)/pterm && \ chmod 2755 $(DESTDIR)$(bindir)/pterm; \ elif test -n "$(UTMP_USER)"; then \ chown $(UTMP_USER) $(DESTDIR)$(bindir)/pterm && \ chmod 4755 $(DESTDIR)$(bindir)/pterm; \ fi $(INSTALL_PROGRAM) -m 755 putty $(DESTDIR)$(bindir)/putty $(INSTALL_PROGRAM) -m 755 puttygen $(DESTDIR)$(bindir)/puttygen $(INSTALL_PROGRAM) -m 755 puttytel $(DESTDIR)$(bindir)/puttytel $(INSTALL_DATA) -m 644 ../doc/pageant.1 $(DESTDIR)$(man1dir)/pageant.1 $(INSTALL_DATA) -m 644 ../doc/plink.1 $(DESTDIR)$(man1dir)/plink.1 $(INSTALL_DATA) -m 644 ../doc/pscp.1 $(DESTDIR)$(man1dir)/pscp.1 $(INSTALL_DATA) -m 644 ../doc/psftp.1 $(DESTDIR)$(man1dir)/psftp.1 $(INSTALL_DATA) -m 644 ../doc/pterm.1 $(DESTDIR)$(man1dir)/pterm.1 $(INSTALL_DATA) -m 644 ../doc/putty.1 $(DESTDIR)$(man1dir)/putty.1 $(INSTALL_DATA) -m 644 ../doc/puttygen.1 $(DESTDIR)$(man1dir)/puttygen.1 $(INSTALL_DATA) -m 644 ../doc/puttytel.1 $(DESTDIR)$(man1dir)/puttytel.1 install-strip: $(MAKE) install INSTALL_PROGRAM="$(INSTALL_PROGRAM) -s" clean: rm -f *.o cgtest fuzzterm osxlaunch pageant plink pscp psftp psocks psusan pterm ptermapp putty puttyapp puttygen puttytel testcrypt testsc testzlib uppity FORCE: putty-0.76/unix/Makefile.ux0000644000175000017500000025414314072266315012651 00000000000000# Makefile for putty under Unix. # # This file was created by `mkfiles.pl' from the `Recipe' file. # DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead. # # Extra options you can set: # # - COMPAT=-DAUTO_WINSOCK (Windows only) # Causes PuTTY to assume that includes its own WinSock # header file, so that it won't try to include . # # - COMPAT=-DWINSOCK_TWO (Windows only) # Causes the PuTTY utilities to include instead of # , except Plink which _needs_ WinSock 2 so it already # does this. # # - COMPAT=-DNO_SECURITY (Windows only) # Disables use of , which is not available with some # development environments (such as very old versions of the # mingw/Cygwin GNU toolchain). This has the following effects: # - Pageant won't care about the local user ID of processes # accessing it; a version of Pageant built with this option # will therefore refuse to run under NT-series OSes on # security grounds (although it will run fine on Win95-series # OSes where there is no access control anyway). # - SSH connection sharing is disabled. # - There is no support for restriction of the process ACLs. # # - COMPAT=-DNO_MULTIMON (Windows only) # Disables PuTTY's use of , which is not available # with some development environments. This means that PuTTY's # full-screen mode (configurable to work on Alt-Enter) will # not behave usefully in a multi-monitor environment. # # - COMPAT=-DNO_HTMLHELP (Windows only) # Disables PuTTY's use of , which is not available # with some development environments. # # If you don't have this header, you may be able to use the copy # supplied with HTML Help Workshop. # # - RCFL=-DNO_MANIFESTS (Windows only) # Disables inclusion of XML application manifests in the PuTTY # binaries. This may be necessary to build for 64-bit Windows; # the manifests are only included to use the XP GUI style on # Windows XP, and the architecture tags are a lie on 64-bit. # # - COMPAT=-DNO_IPV6 # Disables PuTTY's ability to make IPv6 connections, enabling # it to compile under development environments which do not # support IPv6 in their header files. # # - COMPAT=-DNO_GSSAPI # Disables PuTTY's ability to use GSSAPI functions for # authentication and key exchange. # # - COMPAT=-DSTATIC_GSSAPI # Causes PuTTY to try to link statically against the GSSAPI # library instead of the default of doing it at run time. # # - COMPAT=-DMSVC4 (Windows only) # - RCFL=-DMSVC4 # Makes a couple of minor changes so that PuTTY compiles using # MSVC 4. You will also need -DNO_SECURITY and -DNO_MULTIMON. # # - COMPAT=-DNO_SECUREZEROMEMORY (Windows only) # Disables PuTTY's use of SecureZeroMemory(), which is missing # from some environments' header files. # # - XFLAGS=-DDEBUG # Causes PuTTY to enable internal debugging. # # - XFLAGS=-DMALLOC_LOG # Causes PuTTY to emit a file called putty_mem.log, logging every # memory allocation and free, so you can track memory leaks. # # - XFLAGS=-DMINEFIELD (Windows only) # Causes PuTTY to use a custom memory allocator, similar in # concept to Electric Fence, in place of regular malloc(). Wastes # huge amounts of RAM, but should cause heap-corruption bugs to # show up as GPFs at the point of failure rather than appearing # later on as second-level damage. # # - XFLAGS=-DFUZZING # Builds a version of PuTTY with some tweaks to make fuzz testing # easier: the SSH random number generator is replaced by one that # always returns the same thing. Note that this makes SSH # completely insecure -- a FUZZING build should never be used to # connect to a real server. # You can define this path to point at your tools if you need to # TOOLPATH = /opt/gcc/bin CC = $(TOOLPATH)cc -include Makefile.local unexport CFLAGS # work around a weird issue with krb5-config CFLAGS = -O2 -Wall -std=gnu99 -Wvla -g -I.././ -I../charset/ -I../windows/ \ -I../unix/ -D _FILE_OFFSET_BITS=64 ULDFLAGS = $(LDFLAGS) INSTALL=install INSTALL_PROGRAM=$(INSTALL) INSTALL_DATA=$(INSTALL) prefix=/usr/local exec_prefix=$(prefix) bindir=$(exec_prefix)/bin mandir=$(prefix)/man man1dir=$(mandir)/man1 .SUFFIXES: all: cgtest fuzzterm osxlaunch plink pscp psftp psocks psusan puttygen \ testcrypt testsc testzlib uppity cgtest: cgtest.o conf.o console.o ecc.o import.o marshal.o memory.o \ millerrabin.o misc.o mpint.o mpunsafe.o notiming.o pockle.o \ primecandidate.o smallprimes.o sshaes.o sshargon2.o \ sshauxcrypt.o sshbcrypt.o sshblake2.o sshblowf.o sshdes.o \ sshdss.o sshdssg.o sshecc.o sshecdsag.o sshhmac.o sshmd5.o \ sshprime.o sshprng.o sshpubk.o sshrand.o sshrsa.o sshrsag.o \ sshsh256.o sshsh512.o sshsha.o sshsha3.o stripctrl.o time.o \ tree234.o utils.o uxcons.o uxgen.o uxmisc.o uxnogtk.o \ uxnoise.o uxpoll.o uxstore.o uxutils.o version.o wcwidth.o $(CC) -o $@ cgtest.o conf.o console.o ecc.o import.o marshal.o \ memory.o millerrabin.o misc.o mpint.o mpunsafe.o notiming.o \ pockle.o primecandidate.o smallprimes.o sshaes.o sshargon2.o \ sshauxcrypt.o sshbcrypt.o sshblake2.o sshblowf.o sshdes.o \ sshdss.o sshdssg.o sshecc.o sshecdsag.o sshhmac.o sshmd5.o \ sshprime.o sshprng.o sshpubk.o sshrand.o sshrsa.o sshrsag.o \ sshsh256.o sshsh512.o sshsha.o sshsha3.o stripctrl.o time.o \ tree234.o utils.o uxcons.o uxgen.o uxmisc.o uxnogtk.o \ uxnoise.o uxpoll.o uxstore.o uxutils.o version.o wcwidth.o \ $(ULDFLAGS) fuzzterm: be_none.o callback.o conf.o config.o dialog.o fromucs.o fuzzterm.o \ localenc.o logging.o macenc.o marshal.o memory.o mimeenc.o \ minibidi.o misc.o miscucs.o sbcs.o sbcsdat.o settings.o \ slookup.o stripctrl.o terminal.o time.o timing.o toucs.o \ tree234.o utf8.o utils.o uxcfg.o uxmisc.o uxnogtk.o \ uxprint.o uxstore.o uxucs.o version.o wcwidth.o xenc.o $(CC) -o $@ be_none.o callback.o conf.o config.o dialog.o fromucs.o \ fuzzterm.o localenc.o logging.o macenc.o marshal.o memory.o \ mimeenc.o minibidi.o misc.o miscucs.o sbcs.o sbcsdat.o \ settings.o slookup.o stripctrl.o terminal.o time.o timing.o \ toucs.o tree234.o utf8.o utils.o uxcfg.o uxmisc.o uxnogtk.o \ uxprint.o uxstore.o uxucs.o version.o wcwidth.o xenc.o \ $(ULDFLAGS) osxlaunch: osxlaunch.o $(CC) -o $@ osxlaunch.o $(ULDFLAGS) plink: agentf.o aqsync.o be_all_s.o be_misc.o callback.o clicons.o cmdline.o \ conf.o console.o cproxy.o ecc.o errsock.o ldisc.o logging.o \ mainchan.o marshal.o memory.o misc.o mpint.o noterm.o \ nullplug.o pgssapi.o pinger.o portfwd.o proxy.o raw.o \ rlogin.o sessprep.o settings.o ssh.o ssh1bpp.o ssh1censor.o \ ssh1connection.o ssh1connection-client.o ssh1login.o \ ssh2bpp.o ssh2bpp-bare.o ssh2censor.o ssh2connection.o \ ssh2connection-client.o ssh2kex-client.o ssh2transhk.o \ ssh2transport.o ssh2userauth.o sshaes.o ssharcf.o \ sshargon2.o sshauxcrypt.o sshblake2.o sshblowf.o sshccp.o \ sshcommon.o sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o \ sshecc.o sshgssc.o sshhmac.o sshmac.o sshmd5.o sshprng.o \ sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \ sshsha3.o sshshare.o sshutils.o sshverstring.o sshzlib.o \ stripctrl.o supdup.o telnet.o time.o timing.o tree234.o \ utils.o ux_x11.o uxagentc.o uxcliloop.o uxcons.o uxfdsock.o \ uxgss.o uxmisc.o uxnet.o uxnogtk.o uxnoise.o uxpeer.o \ uxplink.o uxpoll.o uxproxy.o uxsel.o uxser.o uxshare.o \ uxsignal.o uxstore.o uxutils.o version.o wcwidth.o \ wildcard.o x11fwd.o $(CC) -o $@ agentf.o aqsync.o be_all_s.o be_misc.o callback.o \ clicons.o cmdline.o conf.o console.o cproxy.o ecc.o \ errsock.o ldisc.o logging.o mainchan.o marshal.o memory.o \ misc.o mpint.o noterm.o nullplug.o pgssapi.o pinger.o \ portfwd.o proxy.o raw.o rlogin.o sessprep.o settings.o ssh.o \ ssh1bpp.o ssh1censor.o ssh1connection.o \ ssh1connection-client.o ssh1login.o ssh2bpp.o ssh2bpp-bare.o \ ssh2censor.o ssh2connection.o ssh2connection-client.o \ ssh2kex-client.o ssh2transhk.o ssh2transport.o \ ssh2userauth.o sshaes.o ssharcf.o sshargon2.o sshauxcrypt.o \ sshblake2.o sshblowf.o sshccp.o sshcommon.o sshcrc.o \ sshcrcda.o sshdes.o sshdh.o sshdss.o sshecc.o sshgssc.o \ sshhmac.o sshmac.o sshmd5.o sshprng.o sshpubk.o sshrand.o \ sshrsa.o sshsh256.o sshsh512.o sshsha.o sshsha3.o sshshare.o \ sshutils.o sshverstring.o sshzlib.o stripctrl.o supdup.o \ telnet.o time.o timing.o tree234.o utils.o ux_x11.o \ uxagentc.o uxcliloop.o uxcons.o uxfdsock.o uxgss.o uxmisc.o \ uxnet.o uxnogtk.o uxnoise.o uxpeer.o uxplink.o uxpoll.o \ uxproxy.o uxsel.o uxser.o uxshare.o uxsignal.o uxstore.o \ uxutils.o version.o wcwidth.o wildcard.o x11fwd.o \ $(ULDFLAGS) pscp: agentf.o aqsync.o be_misc.o be_ssh.o callback.o clicons.o cmdline.o \ conf.o console.o cproxy.o ecc.o errsock.o logging.o \ mainchan.o marshal.o memory.o misc.o mpint.o nullplug.o \ pgssapi.o pinger.o portfwd.o proxy.o pscp.o psftpcommon.o \ settings.o sftp.o sftpcommon.o ssh.o ssh1bpp.o ssh1censor.o \ ssh1connection.o ssh1connection-client.o ssh1login.o \ ssh2bpp.o ssh2bpp-bare.o ssh2censor.o ssh2connection.o \ ssh2connection-client.o ssh2kex-client.o ssh2transhk.o \ ssh2transport.o ssh2userauth.o sshaes.o ssharcf.o \ sshargon2.o sshauxcrypt.o sshblake2.o sshblowf.o sshccp.o \ sshcommon.o sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o \ sshecc.o sshgssc.o sshhmac.o sshmac.o sshmd5.o sshprng.o \ sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \ sshsha3.o sshshare.o sshutils.o sshverstring.o sshzlib.o \ stripctrl.o time.o timing.o tree234.o utils.o uxagentc.o \ uxcliloop.o uxcons.o uxfdsock.o uxgss.o uxmisc.o uxnet.o \ uxnogtk.o uxnoise.o uxpeer.o uxpoll.o uxproxy.o uxsel.o \ uxsftp.o uxshare.o uxstore.o uxutils.o version.o wcwidth.o \ wildcard.o x11fwd.o $(CC) -o $@ agentf.o aqsync.o be_misc.o be_ssh.o callback.o \ clicons.o cmdline.o conf.o console.o cproxy.o ecc.o \ errsock.o logging.o mainchan.o marshal.o memory.o misc.o \ mpint.o nullplug.o pgssapi.o pinger.o portfwd.o proxy.o \ pscp.o psftpcommon.o settings.o sftp.o sftpcommon.o ssh.o \ ssh1bpp.o ssh1censor.o ssh1connection.o \ ssh1connection-client.o ssh1login.o ssh2bpp.o ssh2bpp-bare.o \ ssh2censor.o ssh2connection.o ssh2connection-client.o \ ssh2kex-client.o ssh2transhk.o ssh2transport.o \ ssh2userauth.o sshaes.o ssharcf.o sshargon2.o sshauxcrypt.o \ sshblake2.o sshblowf.o sshccp.o sshcommon.o sshcrc.o \ sshcrcda.o sshdes.o sshdh.o sshdss.o sshecc.o sshgssc.o \ sshhmac.o sshmac.o sshmd5.o sshprng.o sshpubk.o sshrand.o \ sshrsa.o sshsh256.o sshsh512.o sshsha.o sshsha3.o sshshare.o \ sshutils.o sshverstring.o sshzlib.o stripctrl.o time.o \ timing.o tree234.o utils.o uxagentc.o uxcliloop.o uxcons.o \ uxfdsock.o uxgss.o uxmisc.o uxnet.o uxnogtk.o uxnoise.o \ uxpeer.o uxpoll.o uxproxy.o uxsel.o uxsftp.o uxshare.o \ uxstore.o uxutils.o version.o wcwidth.o wildcard.o x11fwd.o \ $(ULDFLAGS) psftp: agentf.o aqsync.o be_misc.o be_ssh.o callback.o clicons.o cmdline.o \ conf.o console.o cproxy.o ecc.o errsock.o logging.o \ mainchan.o marshal.o memory.o misc.o mpint.o nullplug.o \ pgssapi.o pinger.o portfwd.o proxy.o psftp.o psftpcommon.o \ settings.o sftp.o sftpcommon.o ssh.o ssh1bpp.o ssh1censor.o \ ssh1connection.o ssh1connection-client.o ssh1login.o \ ssh2bpp.o ssh2bpp-bare.o ssh2censor.o ssh2connection.o \ ssh2connection-client.o ssh2kex-client.o ssh2transhk.o \ ssh2transport.o ssh2userauth.o sshaes.o ssharcf.o \ sshargon2.o sshauxcrypt.o sshblake2.o sshblowf.o sshccp.o \ sshcommon.o sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o \ sshecc.o sshgssc.o sshhmac.o sshmac.o sshmd5.o sshprng.o \ sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \ sshsha3.o sshshare.o sshutils.o sshverstring.o sshzlib.o \ stripctrl.o time.o timing.o tree234.o utils.o uxagentc.o \ uxcliloop.o uxcons.o uxfdsock.o uxgss.o uxmisc.o uxnet.o \ uxnogtk.o uxnoise.o uxpeer.o uxpoll.o uxproxy.o uxsel.o \ uxsftp.o uxshare.o uxstore.o uxutils.o version.o wcwidth.o \ wildcard.o x11fwd.o $(CC) -o $@ agentf.o aqsync.o be_misc.o be_ssh.o callback.o \ clicons.o cmdline.o conf.o console.o cproxy.o ecc.o \ errsock.o logging.o mainchan.o marshal.o memory.o misc.o \ mpint.o nullplug.o pgssapi.o pinger.o portfwd.o proxy.o \ psftp.o psftpcommon.o settings.o sftp.o sftpcommon.o ssh.o \ ssh1bpp.o ssh1censor.o ssh1connection.o \ ssh1connection-client.o ssh1login.o ssh2bpp.o ssh2bpp-bare.o \ ssh2censor.o ssh2connection.o ssh2connection-client.o \ ssh2kex-client.o ssh2transhk.o ssh2transport.o \ ssh2userauth.o sshaes.o ssharcf.o sshargon2.o sshauxcrypt.o \ sshblake2.o sshblowf.o sshccp.o sshcommon.o sshcrc.o \ sshcrcda.o sshdes.o sshdh.o sshdss.o sshecc.o sshgssc.o \ sshhmac.o sshmac.o sshmd5.o sshprng.o sshpubk.o sshrand.o \ sshrsa.o sshsh256.o sshsh512.o sshsha.o sshsha3.o sshshare.o \ sshutils.o sshverstring.o sshzlib.o stripctrl.o time.o \ timing.o tree234.o utils.o uxagentc.o uxcliloop.o uxcons.o \ uxfdsock.o uxgss.o uxmisc.o uxnet.o uxnogtk.o uxnoise.o \ uxpeer.o uxpoll.o uxproxy.o uxsel.o uxsftp.o uxshare.o \ uxstore.o uxutils.o version.o wcwidth.o wildcard.o x11fwd.o \ $(ULDFLAGS) psocks: be_misc.o callback.o conf.o console.o errsock.o logging.o marshal.o \ memory.o misc.o nocproxy.o norand.o portfwd.o proxy.o \ psocks.o sshutils.o stripctrl.o time.o timing.o tree234.o \ utils.o uxcliloop.o uxcons.o uxfdsock.o uxmisc.o uxnet.o \ uxnogtk.o uxpeer.o uxpoll.o uxproxy.o uxsel.o uxsignal.o \ uxsocks.o version.o wcwidth.o $(CC) -o $@ be_misc.o callback.o conf.o console.o errsock.o \ logging.o marshal.o memory.o misc.o nocproxy.o norand.o \ portfwd.o proxy.o psocks.o sshutils.o stripctrl.o time.o \ timing.o tree234.o utils.o uxcliloop.o uxcons.o uxfdsock.o \ uxmisc.o uxnet.o uxnogtk.o uxpeer.o uxpoll.o uxproxy.o \ uxsel.o uxsignal.o uxsocks.o version.o wcwidth.o $(ULDFLAGS) psusan: be_misc.o be_none.o callback.o conf.o cproxy.o ecc.o errsock.o \ logging.o marshal.o memory.o millerrabin.o misc.o mpint.o \ mpunsafe.o nogss.o nullplug.o pgssapi.o pockle.o portfwd.o \ primecandidate.o procnet.o proxy.o scpserver.o sesschan.o \ settings.o sftpcommon.o sftpserver.o smallprimes.o ssh1bpp.o \ ssh1censor.o ssh1connection.o ssh1connection-server.o \ ssh1login-server.o ssh2bpp.o ssh2bpp-bare.o ssh2censor.o \ ssh2connection.o ssh2connection-server.o ssh2kex-server.o \ ssh2transhk.o ssh2transport.o ssh2userauth-server.o sshaes.o \ ssharcf.o sshargon2.o sshauxcrypt.o sshblake2.o sshblowf.o \ sshccp.o sshcommon.o sshcrc.o sshcrcda.o sshdes.o sshdh.o \ sshdss.o sshecc.o sshgssc.o sshhmac.o sshmac.o sshmd5.o \ sshprime.o sshprng.o sshpubk.o sshrand.o sshrsa.o sshrsag.o \ sshserver.o sshsh256.o sshsh512.o sshsha.o sshsha3.o \ sshutils.o sshverstring.o sshzlib.o stripctrl.o time.o \ timing.o tree234.o utils.o ux_x11.o uxagentsock.o \ uxcliloop.o uxfdsock.o uxmisc.o uxnet.o uxnogtk.o uxnoise.o \ uxpeer.o uxpoll.o uxproxy.o uxpsusan.o uxpty.o uxsel.o \ uxsftpserver.o uxsignal.o uxstore.o uxutils.o version.o \ wcwidth.o wildcard.o x11fwd.o $(CC) -o $@ be_misc.o be_none.o callback.o conf.o cproxy.o ecc.o \ errsock.o logging.o marshal.o memory.o millerrabin.o misc.o \ mpint.o mpunsafe.o nogss.o nullplug.o pgssapi.o pockle.o \ portfwd.o primecandidate.o procnet.o proxy.o scpserver.o \ sesschan.o settings.o sftpcommon.o sftpserver.o \ smallprimes.o ssh1bpp.o ssh1censor.o ssh1connection.o \ ssh1connection-server.o ssh1login-server.o ssh2bpp.o \ ssh2bpp-bare.o ssh2censor.o ssh2connection.o \ ssh2connection-server.o ssh2kex-server.o ssh2transhk.o \ ssh2transport.o ssh2userauth-server.o sshaes.o ssharcf.o \ sshargon2.o sshauxcrypt.o sshblake2.o sshblowf.o sshccp.o \ sshcommon.o sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o \ sshecc.o sshgssc.o sshhmac.o sshmac.o sshmd5.o sshprime.o \ sshprng.o sshpubk.o sshrand.o sshrsa.o sshrsag.o sshserver.o \ sshsh256.o sshsh512.o sshsha.o sshsha3.o sshutils.o \ sshverstring.o sshzlib.o stripctrl.o time.o timing.o \ tree234.o utils.o ux_x11.o uxagentsock.o uxcliloop.o \ uxfdsock.o uxmisc.o uxnet.o uxnogtk.o uxnoise.o uxpeer.o \ uxpoll.o uxproxy.o uxpsusan.o uxpty.o uxsel.o uxsftpserver.o \ uxsignal.o uxstore.o uxutils.o version.o wcwidth.o \ wildcard.o x11fwd.o $(ULDFLAGS) puttygen: cmdgen.o conf.o console.o ecc.o import.o marshal.o memory.o \ millerrabin.o misc.o mpint.o mpunsafe.o notiming.o pockle.o \ primecandidate.o smallprimes.o sshaes.o sshargon2.o \ sshauxcrypt.o sshbcrypt.o sshblake2.o sshblowf.o sshdes.o \ sshdss.o sshdssg.o sshecc.o sshecdsag.o sshhmac.o sshmd5.o \ sshprime.o sshprng.o sshpubk.o sshrand.o sshrsa.o sshrsag.o \ sshsh256.o sshsh512.o sshsha.o sshsha3.o stripctrl.o time.o \ tree234.o utils.o uxcons.o uxgen.o uxmisc.o uxnogtk.o \ uxnoise.o uxpoll.o uxstore.o uxutils.o version.o wcwidth.o $(CC) -o $@ cmdgen.o conf.o console.o ecc.o import.o marshal.o \ memory.o millerrabin.o misc.o mpint.o mpunsafe.o notiming.o \ pockle.o primecandidate.o smallprimes.o sshaes.o sshargon2.o \ sshauxcrypt.o sshbcrypt.o sshblake2.o sshblowf.o sshdes.o \ sshdss.o sshdssg.o sshecc.o sshecdsag.o sshhmac.o sshmd5.o \ sshprime.o sshprng.o sshpubk.o sshrand.o sshrsa.o sshrsag.o \ sshsh256.o sshsh512.o sshsha.o sshsha3.o stripctrl.o time.o \ tree234.o utils.o uxcons.o uxgen.o uxmisc.o uxnogtk.o \ uxnoise.o uxpoll.o uxstore.o uxutils.o version.o wcwidth.o \ $(ULDFLAGS) testcrypt: ecc.o marshal.o memory.o millerrabin.o mpint.o mpunsafe.o \ pockle.o primecandidate.o smallprimes.o sshaes.o ssharcf.o \ sshargon2.o sshauxcrypt.o sshblake2.o sshblowf.o sshccp.o \ sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o sshdssg.o \ sshecc.o sshecdsag.o sshhmac.o sshmd5.o sshprime.o sshprng.o \ sshpubk.o sshrsa.o sshrsag.o sshsh256.o sshsh512.o sshsha.o \ sshsha3.o testcrypt.o tree234.o utils.o uxutils.o $(CC) -o $@ ecc.o marshal.o memory.o millerrabin.o mpint.o \ mpunsafe.o pockle.o primecandidate.o smallprimes.o sshaes.o \ ssharcf.o sshargon2.o sshauxcrypt.o sshblake2.o sshblowf.o \ sshccp.o sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o \ sshdssg.o sshecc.o sshecdsag.o sshhmac.o sshmd5.o sshprime.o \ sshprng.o sshpubk.o sshrsa.o sshrsag.o sshsh256.o sshsh512.o \ sshsha.o sshsha3.o testcrypt.o tree234.o utils.o uxutils.o \ $(ULDFLAGS) testsc: ecc.o marshal.o memory.o mpint.o sshaes.o ssharcf.o sshargon2.o \ sshauxcrypt.o sshblake2.o sshblowf.o sshccp.o sshcrc.o \ sshcrcda.o sshdes.o sshdh.o sshdss.o sshecc.o sshhmac.o \ sshmac.o sshmd5.o sshpubk.o sshrsa.o sshsh256.o sshsh512.o \ sshsha.o sshsha3.o testsc.o tree234.o utils.o uxutils.o \ wildcard.o $(CC) -o $@ ecc.o marshal.o memory.o mpint.o sshaes.o ssharcf.o \ sshargon2.o sshauxcrypt.o sshblake2.o sshblowf.o sshccp.o \ sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o sshecc.o \ sshhmac.o sshmac.o sshmd5.o sshpubk.o sshrsa.o sshsh256.o \ sshsh512.o sshsha.o sshsha3.o testsc.o tree234.o utils.o \ uxutils.o wildcard.o $(ULDFLAGS) testzlib: marshal.o memory.o sshzlib.o testzlib.o utils.o $(CC) -o $@ marshal.o memory.o sshzlib.o testzlib.o utils.o \ $(ULDFLAGS) uppity: be_misc.o be_none.o callback.o conf.o cproxy.o ecc.o errsock.o \ logging.o marshal.o memory.o millerrabin.o misc.o mpint.o \ mpunsafe.o nullplug.o pgssapi.o pockle.o portfwd.o \ primecandidate.o procnet.o proxy.o scpserver.o sesschan.o \ settings.o sftpcommon.o sftpserver.o smallprimes.o ssh1bpp.o \ ssh1censor.o ssh1connection.o ssh1connection-server.o \ ssh1login-server.o ssh2bpp.o ssh2bpp-bare.o ssh2censor.o \ ssh2connection.o ssh2connection-server.o ssh2kex-server.o \ ssh2transhk.o ssh2transport.o ssh2userauth-server.o sshaes.o \ ssharcf.o sshargon2.o sshauxcrypt.o sshblake2.o sshblowf.o \ sshccp.o sshcommon.o sshcrc.o sshcrcda.o sshdes.o sshdh.o \ sshdss.o sshecc.o sshgssc.o sshhmac.o sshmac.o sshmd5.o \ sshprime.o sshprng.o sshpubk.o sshrand.o sshrsa.o sshrsag.o \ sshserver.o sshsh256.o sshsh512.o sshsha.o sshsha3.o \ sshutils.o sshverstring.o sshzlib.o stripctrl.o time.o \ timing.o tree234.o utils.o ux_x11.o uxagentsock.o \ uxcliloop.o uxfdsock.o uxgss.o uxmisc.o uxnet.o uxnogtk.o \ uxnoise.o uxpeer.o uxpoll.o uxproxy.o uxpty.o uxsel.o \ uxserver.o uxsftpserver.o uxsignal.o uxstore.o uxutils.o \ version.o wcwidth.o wildcard.o x11fwd.o $(CC) -o $@ be_misc.o be_none.o callback.o conf.o cproxy.o ecc.o \ errsock.o logging.o marshal.o memory.o millerrabin.o misc.o \ mpint.o mpunsafe.o nullplug.o pgssapi.o pockle.o portfwd.o \ primecandidate.o procnet.o proxy.o scpserver.o sesschan.o \ settings.o sftpcommon.o sftpserver.o smallprimes.o ssh1bpp.o \ ssh1censor.o ssh1connection.o ssh1connection-server.o \ ssh1login-server.o ssh2bpp.o ssh2bpp-bare.o ssh2censor.o \ ssh2connection.o ssh2connection-server.o ssh2kex-server.o \ ssh2transhk.o ssh2transport.o ssh2userauth-server.o sshaes.o \ ssharcf.o sshargon2.o sshauxcrypt.o sshblake2.o sshblowf.o \ sshccp.o sshcommon.o sshcrc.o sshcrcda.o sshdes.o sshdh.o \ sshdss.o sshecc.o sshgssc.o sshhmac.o sshmac.o sshmd5.o \ sshprime.o sshprng.o sshpubk.o sshrand.o sshrsa.o sshrsag.o \ sshserver.o sshsh256.o sshsh512.o sshsha.o sshsha3.o \ sshutils.o sshverstring.o sshzlib.o stripctrl.o time.o \ timing.o tree234.o utils.o ux_x11.o uxagentsock.o \ uxcliloop.o uxfdsock.o uxgss.o uxmisc.o uxnet.o uxnogtk.o \ uxnoise.o uxpeer.o uxpoll.o uxproxy.o uxpty.o uxsel.o \ uxserver.o uxsftpserver.o uxsignal.o uxstore.o uxutils.o \ version.o wcwidth.o wildcard.o x11fwd.o $(ULDFLAGS) agentf.o: ../agentf.c ../putty.h ../ssh.h ../pageant.h ../sshchan.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../agentf.c aqsync.o: ../aqsync.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../aqsync.c be_all_s.o: ../be_all_s.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../be_all_s.c be_misc.o: ../be_misc.c ../putty.h ../network.h ../defs.h ../puttyps.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../be_misc.c be_none.o: ../be_none.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../be_none.c be_nos_s.o: ../be_nos_s.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../be_nos_s.c be_ssh.o: ../be_ssh.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../be_ssh.c callback.o: ../callback.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../callback.c cgtest.o: ../cgtest.c ../cmdgen.c ../putty.h ../ssh.h ../sshkeygen.h \ ../mpint.h ../defs.h ../puttyps.h ../network.h ../misc.h \ ../marshal.h ../sshsignals.h ../puttymem.h ../tree234.h \ ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../cgtest.c clicons.o: ../clicons.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../clicons.c cmdgen.o: ../cmdgen.c ../putty.h ../ssh.h ../sshkeygen.h ../mpint.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../cmdgen.c cmdline.o: ../cmdline.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../cmdline.c conf.o: ../conf.c ../tree234.h ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../conf.c config.o: ../config.c ../putty.h ../dialog.h ../storage.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../config.c console.o: ../console.c ../putty.h ../misc.h ../console.h ../defs.h \ ../puttyps.h ../network.h ../marshal.h ../sshsignals.h \ ../puttymem.h ../windows/winstuff.h ../unix/unix.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../console.c cproxy.o: ../cproxy.c ../putty.h ../ssh.h ../network.h ../proxy.h \ ../marshal.h ../defs.h ../puttyps.h ../misc.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../cproxy.c dialog.o: ../dialog.c ../putty.h ../dialog.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../dialog.c ecc.o: ../ecc.c ../ssh.h ../mpint.h ../ecc.h ../puttymem.h ../tree234.h \ ../network.h ../misc.h ../sshttymodes.h ../defs.h \ ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ecc.c errsock.o: ../errsock.c ../tree234.h ../putty.h ../network.h ../defs.h \ ../puttyps.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../errsock.c fromucs.o: ../charset/fromucs.c ../charset/charset.h ../charset/internal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/fromucs.c fuzzterm.o: ../fuzzterm.c ../putty.h ../dialog.h ../terminal.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../tree234.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../fuzzterm.c gtkapp.o: ../unix/gtkapp.c ../putty.h ../unix/gtkmisc.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkapp.c gtkask.o: ../unix/gtkask.c ../defs.h ../unix/gtkfont.h ../unix/gtkcompat.h \ ../unix/gtkmisc.h ../putty.h ../ssh.h ../misc.h ../puttyps.h \ ../network.h ../marshal.h ../sshsignals.h ../puttymem.h \ ../tree234.h ../sshttymodes.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkask.c gtkcfg.o: ../unix/gtkcfg.c ../putty.h ../dialog.h ../storage.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkcfg.c gtkcols.o: ../unix/gtkcols.c ../defs.h ../unix/gtkcompat.h ../unix/gtkcols.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkcols.c gtkcomm.o: ../unix/gtkcomm.c ../putty.h ../terminal.h ../unix/gtkcompat.h \ ../unix/gtkfont.h ../unix/gtkmisc.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../tree234.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkcomm.c gtkdlg.o: ../unix/gtkdlg.c ../putty.h ../unix/gtkcompat.h ../unix/gtkcols.h \ ../unix/gtkfont.h ../unix/gtkmisc.h ../unix/x11misc.h \ ../storage.h ../dialog.h ../tree234.h ../licence.h ../ssh.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkdlg.c gtkfont.o: ../unix/gtkfont.c ../putty.h ../unix/gtkfont.h \ ../unix/gtkcompat.h ../unix/gtkmisc.h ../tree234.h \ ../unix/x11misc.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkfont.c gtkmain.o: ../unix/gtkmain.c ../putty.h ../terminal.h ../unix/gtkcompat.h \ ../unix/gtkfont.h ../unix/gtkmisc.h ../unix/x11misc.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../tree234.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkmain.c gtkmisc.o: ../unix/gtkmisc.c ../putty.h ../unix/gtkcompat.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkmisc.c gtkwin.o: ../unix/gtkwin.c ../putty.h ../terminal.h ../unix/gtkcompat.h \ ../unix/gtkfont.h ../unix/gtkmisc.h ../unix/x11misc.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../tree234.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkwin.c import.o: ../import.c ../putty.h ../ssh.h ../mpint.h ../misc.h ../defs.h \ ../puttyps.h ../network.h ../marshal.h ../sshsignals.h \ ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../import.c ldisc.o: ../ldisc.c ../putty.h ../terminal.h ../ldisc.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../tree234.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ldisc.c localenc.o: ../charset/localenc.c ../charset/charset.h ../charset/internal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/localenc.c logging.o: ../logging.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../logging.c macenc.o: ../charset/macenc.c ../charset/charset.h ../charset/internal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/macenc.c mainchan.o: ../mainchan.c ../putty.h ../ssh.h ../sshppl.h ../sshchan.h \ ../sshsignals.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../puttymem.h ../tree234.h \ ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../mainchan.c marshal.o: ../marshal.c ../marshal.h ../misc.h ../defs.h ../puttymem.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../marshal.c memory.o: ../memory.c ../defs.h ../puttymem.h ../misc.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../memory.c millerrabin.o: ../millerrabin.c ../ssh.h ../sshkeygen.h ../mpint.h \ ../mpunsafe.h ../puttymem.h ../tree234.h ../network.h \ ../misc.h ../sshttymodes.h ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../millerrabin.c mimeenc.o: ../charset/mimeenc.c ../charset/charset.h ../charset/internal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/mimeenc.c minibidi.o: ../minibidi.c ../putty.h ../misc.h ../defs.h ../puttyps.h \ ../network.h ../marshal.h ../sshsignals.h ../puttymem.h \ ../windows/winstuff.h ../unix/unix.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../minibidi.c misc.o: ../misc.c ../defs.h ../putty.h ../misc.h ../puttyps.h ../network.h \ ../marshal.h ../sshsignals.h ../puttymem.h \ ../windows/winstuff.h ../unix/unix.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../misc.c miscucs.o: ../miscucs.c ../putty.h ../misc.h ../defs.h ../puttyps.h \ ../network.h ../marshal.h ../sshsignals.h ../puttymem.h \ ../windows/winstuff.h ../unix/unix.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../miscucs.c mpint.o: ../mpint.c ../defs.h ../misc.h ../puttymem.h ../mpint.h \ ../mpint_i.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../mpint.c mpunsafe.o: ../mpunsafe.c ../defs.h ../misc.h ../puttymem.h ../mpint.h \ ../mpint_i.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../mpunsafe.c nocmdline.o: ../nocmdline.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../nocmdline.c nocproxy.o: ../nocproxy.c ../putty.h ../network.h ../proxy.h ../defs.h \ ../puttyps.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../nocproxy.c nogss.o: ../nogss.c ../putty.h ../defs.h ../puttyps.h ../network.h ../misc.h \ ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../nogss.c norand.o: ../norand.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../norand.c noterm.o: ../noterm.c ../putty.h ../terminal.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../tree234.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../noterm.c notiming.o: ../notiming.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../notiming.c nullplug.o: ../nullplug.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../nullplug.c osxlaunch.o: ../unix/osxlaunch.c $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/osxlaunch.c pageant.o: ../pageant.c ../putty.h ../mpint.h ../ssh.h ../sshcr.h \ ../pageant.h ../defs.h ../puttyps.h ../network.h ../misc.h \ ../marshal.h ../sshsignals.h ../puttymem.h ../tree234.h \ ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../pageant.c pgssapi.o: ../pgssapi.c ../putty.h ../pgssapi.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../pgssapi.c pinger.o: ../pinger.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../pinger.c pockle.o: ../pockle.c ../ssh.h ../sshkeygen.h ../mpint.h ../mpunsafe.h \ ../tree234.h ../puttymem.h ../network.h ../misc.h \ ../sshttymodes.h ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../pockle.c portfwd.o: ../portfwd.c ../putty.h ../ssh.h ../sshchan.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../portfwd.c primecandidate.o: ../primecandidate.c ../ssh.h ../mpint.h ../mpunsafe.h \ ../sshkeygen.h ../puttymem.h ../tree234.h ../network.h \ ../misc.h ../sshttymodes.h ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../primecandidate.c procnet.o: ../unix/procnet.c ../misc.h ../defs.h ../puttymem.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/procnet.c proxy.o: ../proxy.c ../putty.h ../network.h ../proxy.h ../defs.h \ ../puttyps.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../proxy.c pscp.o: ../pscp.c ../putty.h ../psftp.h ../ssh.h ../sftp.h ../storage.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../pscp.c psftp.o: ../psftp.c ../putty.h ../psftp.h ../storage.h ../ssh.h ../sftp.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../psftp.c psftpcommon.o: ../psftpcommon.c ../putty.h ../sftp.h ../psftp.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../psftpcommon.c psocks.o: ../psocks.c ../putty.h ../misc.h ../ssh.h ../sshchan.h ../psocks.h \ ../defs.h ../puttyps.h ../network.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../psocks.c raw.o: ../raw.c ../putty.h ../defs.h ../puttyps.h ../network.h ../misc.h \ ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../raw.c rlogin.o: ../rlogin.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../rlogin.c sbcs.o: ../charset/sbcs.c ../charset/charset.h ../charset/internal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/sbcs.c sbcsdat.o: ../charset/sbcsdat.c ../charset/charset.h ../charset/internal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/sbcsdat.c scpserver.o: ../scpserver.c ../putty.h ../ssh.h ../sshcr.h ../sshchan.h \ ../sftp.h ../defs.h ../puttyps.h ../network.h ../misc.h \ ../marshal.h ../sshsignals.h ../puttymem.h ../tree234.h \ ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../scpserver.c sesschan.o: ../sesschan.c ../putty.h ../ssh.h ../sshchan.h ../sshserver.h \ ../sftp.h ../sshsignals.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../puttymem.h \ ../tree234.h ../sshttymodes.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sesschan.c sessprep.o: ../sessprep.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sessprep.c settings.o: ../settings.c ../putty.h ../storage.h ../sshgssc.h ../sshgss.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../pgssapi.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../settings.c sftp.o: ../sftp.c ../misc.h ../tree234.h ../sftp.h ../defs.h ../puttymem.h \ ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sftp.c sftpcommon.o: ../sftpcommon.c ../misc.h ../sftp.h ../defs.h ../puttymem.h \ ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sftpcommon.c sftpserver.o: ../sftpserver.c ../putty.h ../ssh.h ../sftp.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sftpserver.c sizetip.o: ../windows/sizetip.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/sizetip.c slookup.o: ../charset/slookup.c ../charset/charset.h ../charset/internal.h \ ../charset/enum.c ../charset/sbcsdat.c ../charset/utf8.c $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/slookup.c smallprimes.o: ../smallprimes.c ../ssh.h ../sshkeygen.h ../puttymem.h \ ../tree234.h ../network.h ../misc.h ../sshttymodes.h \ ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../smallprimes.c ssh.o: ../ssh.c ../putty.h ../pageant.h ../tree234.h ../storage.h \ ../marshal.h ../ssh.h ../sshcr.h ../sshbpp.h ../sshppl.h \ ../sshchan.h ../sshgssc.h ../sshgss.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../sshsignals.h ../puttymem.h \ ../sshttymodes.h ../pgssapi.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh.c ssh1bpp.o: ../ssh1bpp.c ../putty.h ../ssh.h ../sshbpp.h ../sshcr.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh1bpp.c ssh1censor.o: ../ssh1censor.c ../putty.h ../ssh.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh1censor.c ssh1connection.o: ../ssh1connection.c ../putty.h ../ssh.h ../sshbpp.h \ ../sshppl.h ../sshchan.h ../sshcr.h ../ssh1connection.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh1connection.c ssh1connection-client.o: ../ssh1connection-client.c ../putty.h ../ssh.h \ ../sshbpp.h ../sshppl.h ../sshchan.h ../sshcr.h \ ../ssh1connection.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ ../tree234.h ../sshttymodes.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh1connection-client.c ssh1connection-server.o: ../ssh1connection-server.c ../putty.h ../ssh.h \ ../sshbpp.h ../sshppl.h ../sshchan.h ../sshcr.h \ ../ssh1connection.h ../sshserver.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh1connection-server.c ssh1login.o: ../ssh1login.c ../putty.h ../ssh.h ../mpint.h ../sshbpp.h \ ../sshppl.h ../sshcr.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ ../tree234.h ../sshttymodes.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh1login.c ssh1login-server.o: ../ssh1login-server.c ../putty.h ../mpint.h ../ssh.h \ ../sshbpp.h ../sshppl.h ../sshcr.h ../sshserver.h \ ../sshkeygen.h ../defs.h ../puttyps.h ../network.h ../misc.h \ ../marshal.h ../sshsignals.h ../puttymem.h ../tree234.h \ ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh1login-server.c ssh2bpp.o: ../ssh2bpp.c ../putty.h ../ssh.h ../sshbpp.h ../sshcr.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh2bpp.c ssh2bpp-bare.o: ../ssh2bpp-bare.c ../putty.h ../ssh.h ../sshbpp.h ../sshcr.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh2bpp-bare.c ssh2censor.o: ../ssh2censor.c ../putty.h ../ssh.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh2censor.c ssh2connection.o: ../ssh2connection.c ../putty.h ../ssh.h ../sshbpp.h \ ../sshppl.h ../sshchan.h ../sshcr.h ../ssh2connection.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh2connection.c ssh2connection-client.o: ../ssh2connection-client.c ../putty.h ../ssh.h \ ../sshbpp.h ../sshppl.h ../sshchan.h ../sshcr.h \ ../ssh2connection.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ ../tree234.h ../sshttymodes.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh2connection-client.c ssh2connection-server.o: ../ssh2connection-server.c ../putty.h ../ssh.h \ ../sshbpp.h ../sshppl.h ../sshchan.h ../sshcr.h \ ../ssh2connection.h ../sshserver.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh2connection-server.c ssh2kex-client.o: ../ssh2kex-client.c ../putty.h ../ssh.h ../sshbpp.h \ ../sshppl.h ../sshcr.h ../storage.h ../ssh2transport.h \ ../mpint.h ../defs.h ../puttyps.h ../network.h ../misc.h \ ../marshal.h ../sshsignals.h ../puttymem.h ../tree234.h \ ../sshttymodes.h ../sshgssc.h ../sshgss.h \ ../windows/winstuff.h ../unix/unix.h ../pgssapi.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh2kex-client.c ssh2kex-server.o: ../ssh2kex-server.c ../putty.h ../ssh.h ../sshbpp.h \ ../sshppl.h ../sshcr.h ../sshserver.h ../sshkeygen.h \ ../storage.h ../ssh2transport.h ../mpint.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../sshgssc.h ../sshgss.h ../windows/winstuff.h \ ../unix/unix.h ../pgssapi.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh2kex-server.c ssh2transhk.o: ../ssh2transhk.c ../putty.h ../ssh.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh2transhk.c ssh2transport.o: ../ssh2transport.c ../putty.h ../ssh.h ../sshbpp.h \ ../sshppl.h ../sshcr.h ../sshserver.h ../storage.h \ ../ssh2transport.h ../mpint.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../puttymem.h ../tree234.h ../sshttymodes.h ../sshgssc.h \ ../sshgss.h ../windows/winstuff.h ../unix/unix.h \ ../pgssapi.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh2transport.c ssh2userauth.o: ../ssh2userauth.c ../putty.h ../ssh.h ../sshbpp.h \ ../sshppl.h ../sshcr.h ../sshgssc.h ../sshgss.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../pgssapi.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh2userauth.c ssh2userauth-server.o: ../ssh2userauth-server.c ../putty.h ../ssh.h \ ../sshbpp.h ../sshppl.h ../sshcr.h ../sshserver.h \ ../sshgssc.h ../sshgss.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ ../tree234.h ../sshttymodes.h ../pgssapi.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh2userauth-server.c sshaes.o: ../sshaes.c ../ssh.h ../mpint_i.h ../puttymem.h ../tree234.h \ ../network.h ../misc.h ../sshttymodes.h ../defs.h \ ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshaes.c ssharcf.o: ../ssharcf.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ ../misc.h ../sshttymodes.h ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssharcf.c sshargon2.o: ../sshargon2.c ../putty.h ../ssh.h ../marshal.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../sshsignals.h \ ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshargon2.c sshauxcrypt.o: ../sshauxcrypt.c ../ssh.h ../puttymem.h ../tree234.h \ ../network.h ../misc.h ../sshttymodes.h ../defs.h \ ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshauxcrypt.c sshbcrypt.o: ../sshbcrypt.c ../ssh.h ../sshblowf.h ../puttymem.h \ ../tree234.h ../network.h ../misc.h ../sshttymodes.h \ ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshbcrypt.c sshblake2.o: ../sshblake2.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ ../misc.h ../sshttymodes.h ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshblake2.c sshblowf.o: ../sshblowf.c ../ssh.h ../sshblowf.h ../puttymem.h ../tree234.h \ ../network.h ../misc.h ../sshttymodes.h ../defs.h \ ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshblowf.c sshccp.o: ../sshccp.c ../ssh.h ../mpint_i.h ../puttymem.h ../tree234.h \ ../network.h ../misc.h ../sshttymodes.h ../defs.h \ ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshccp.c sshcommon.o: ../sshcommon.c ../putty.h ../mpint.h ../ssh.h ../sshbpp.h \ ../sshppl.h ../sshchan.h ../sshttymodes.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshcommon.c sshcrc.o: ../sshcrc.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ ../misc.h ../sshttymodes.h ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshcrc.c sshcrcda.o: ../sshcrcda.c ../misc.h ../ssh.h ../defs.h ../puttymem.h \ ../marshal.h ../tree234.h ../network.h ../sshttymodes.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshcrcda.c sshdes.o: ../sshdes.c ../ssh.h ../mpint_i.h ../puttymem.h ../tree234.h \ ../network.h ../misc.h ../sshttymodes.h ../defs.h \ ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshdes.c sshdh.o: ../sshdh.c ../ssh.h ../misc.h ../mpint.h ../puttymem.h ../tree234.h \ ../network.h ../sshttymodes.h ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshdh.c sshdss.o: ../sshdss.c ../ssh.h ../mpint.h ../misc.h ../puttymem.h \ ../tree234.h ../network.h ../sshttymodes.h ../defs.h \ ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshdss.c sshdssg.o: ../sshdssg.c ../misc.h ../ssh.h ../sshkeygen.h ../mpint.h \ ../defs.h ../puttymem.h ../marshal.h ../tree234.h \ ../network.h ../sshttymodes.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshdssg.c sshecc.o: ../sshecc.c ../ssh.h ../mpint.h ../ecc.h ../puttymem.h \ ../tree234.h ../network.h ../misc.h ../sshttymodes.h \ ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshecc.c sshecdsag.o: ../sshecdsag.c ../ssh.h ../sshkeygen.h ../mpint.h ../puttymem.h \ ../tree234.h ../network.h ../misc.h ../sshttymodes.h \ ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshecdsag.c sshgssc.o: ../sshgssc.c ../putty.h ../sshgssc.h ../misc.h ../defs.h \ ../puttyps.h ../network.h ../marshal.h ../sshsignals.h \ ../pgssapi.h ../sshgss.h ../puttymem.h ../windows/winstuff.h \ ../unix/unix.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshgssc.c sshhmac.o: ../sshhmac.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ ../misc.h ../sshttymodes.h ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshhmac.c sshmac.o: ../sshmac.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ ../misc.h ../sshttymodes.h ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshmac.c sshmd5.o: ../sshmd5.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ ../misc.h ../sshttymodes.h ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshmd5.c sshprime.o: ../sshprime.c ../ssh.h ../mpint.h ../mpunsafe.h ../sshkeygen.h \ ../puttymem.h ../tree234.h ../network.h ../misc.h \ ../sshttymodes.h ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshprime.c sshprng.o: ../sshprng.c ../putty.h ../ssh.h ../mpint_i.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshprng.c sshpubk.o: ../sshpubk.c ../putty.h ../mpint.h ../ssh.h ../misc.h ../defs.h \ ../puttyps.h ../network.h ../marshal.h ../sshsignals.h \ ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshpubk.c sshrand.o: ../sshrand.c ../putty.h ../ssh.h ../storage.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshrand.c sshrsa.o: ../sshrsa.c ../ssh.h ../mpint.h ../misc.h ../puttymem.h \ ../tree234.h ../network.h ../sshttymodes.h ../defs.h \ ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshrsa.c sshrsag.o: ../sshrsag.c ../ssh.h ../sshkeygen.h ../mpint.h ../puttymem.h \ ../tree234.h ../network.h ../misc.h ../sshttymodes.h \ ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshrsag.c sshserver.o: ../sshserver.c ../putty.h ../ssh.h ../sshbpp.h ../sshppl.h \ ../sshchan.h ../sshserver.h ../sshgssc.h ../sshgss.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../pgssapi.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshserver.c sshsh256.o: ../sshsh256.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ ../misc.h ../sshttymodes.h ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshsh256.c sshsh512.o: ../sshsh512.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ ../misc.h ../sshttymodes.h ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshsh512.c sshsha.o: ../sshsha.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ ../misc.h ../sshttymodes.h ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshsha.c sshsha3.o: ../sshsha3.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ ../misc.h ../sshttymodes.h ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshsha3.c sshshare.o: ../sshshare.c ../putty.h ../tree234.h ../ssh.h ../sshcr.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshshare.c sshutils.o: ../sshutils.c ../putty.h ../ssh.h ../sshchan.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshutils.c sshverstring.o: ../sshverstring.c ../putty.h ../ssh.h ../sshbpp.h ../sshcr.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshverstring.c sshzlib.o: ../sshzlib.c ../defs.h ../ssh.h ../puttymem.h ../tree234.h \ ../network.h ../misc.h ../sshttymodes.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshzlib.c stripctrl.o: ../stripctrl.c ../putty.h ../terminal.h ../misc.h ../marshal.h \ ../defs.h ../puttyps.h ../network.h ../sshsignals.h \ ../tree234.h ../puttymem.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../stripctrl.c supdup.o: ../supdup.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../supdup.c telnet.o: ../telnet.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../telnet.c terminal.o: ../terminal.c ../putty.h ../terminal.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../tree234.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../terminal.c testcrypt.o: ../testcrypt.c ../defs.h ../ssh.h ../sshkeygen.h ../misc.h \ ../mpint.h ../ecc.h ../testcrypt.h ../puttymem.h \ ../tree234.h ../network.h ../sshttymodes.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../testcrypt.c testsc.o: ../testsc.c ../defs.h ../putty.h ../ssh.h ../misc.h ../mpint.h \ ../ecc.h ../puttyps.h ../network.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../testsc.c testzlib.o: ../testzlib.c ../defs.h ../ssh.h ../puttymem.h ../tree234.h \ ../network.h ../misc.h ../sshttymodes.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../testzlib.c time.o: ../time.c $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../time.c timing.o: ../timing.c ../putty.h ../tree234.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../timing.c toucs.o: ../charset/toucs.c ../charset/charset.h ../charset/internal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/toucs.c tree234.o: ../tree234.c ../defs.h ../tree234.h ../puttymem.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../tree234.c utf8.o: ../charset/utf8.c ../charset/charset.h ../charset/internal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/utf8.c utils.o: ../utils.c ../defs.h ../misc.h ../ssh.h ../puttymem.h ../marshal.h \ ../tree234.h ../network.h ../sshttymodes.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../utils.c ux_x11.o: ../unix/ux_x11.c ../putty.h ../ssh.h ../network.h ../defs.h \ ../puttyps.h ../misc.h ../marshal.h ../sshsignals.h \ ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/ux_x11.c uxagentc.o: ../unix/uxagentc.c ../putty.h ../misc.h ../tree234.h \ ../puttymem.h ../defs.h ../puttyps.h ../network.h \ ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxagentc.c uxagentsock.o: ../unix/uxagentsock.c ../putty.h ../ssh.h ../misc.h \ ../pageant.h ../defs.h ../puttyps.h ../network.h \ ../marshal.h ../sshsignals.h ../puttymem.h ../tree234.h \ ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxagentsock.c uxcfg.o: ../unix/uxcfg.c ../putty.h ../dialog.h ../storage.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxcfg.c uxcliloop.o: ../unix/uxcliloop.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxcliloop.c uxcons.o: ../unix/uxcons.c ../putty.h ../storage.h ../ssh.h ../console.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxcons.c uxfdsock.o: ../unix/uxfdsock.c ../tree234.h ../putty.h ../network.h \ ../defs.h ../puttyps.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxfdsock.c uxgen.o: ../unix/uxgen.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxgen.c uxgss.o: ../unix/uxgss.c ../putty.h ../pgssapi.h ../sshgss.h ../sshgssc.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxgss.c uxmisc.o: ../unix/uxmisc.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxmisc.c uxnet.o: ../unix/uxnet.c ../putty.h ../network.h ../tree234.h ../defs.h \ ../puttyps.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxnet.c uxnogtk.o: ../unix/uxnogtk.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxnogtk.c uxnoise.o: ../unix/uxnoise.c ../putty.h ../ssh.h ../storage.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxnoise.c uxpeer.o: ../unix/uxpeer.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxpeer.c uxpgnt.o: ../unix/uxpgnt.c ../putty.h ../ssh.h ../misc.h ../pageant.h \ ../defs.h ../puttyps.h ../network.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxpgnt.c uxplink.o: ../unix/uxplink.c ../putty.h ../ssh.h ../storage.h ../tree234.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxplink.c uxpoll.o: ../unix/uxpoll.c ../putty.h ../tree234.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxpoll.c uxprint.o: ../unix/uxprint.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxprint.c uxproxy.o: ../unix/uxproxy.c ../tree234.h ../putty.h ../network.h ../proxy.h \ ../defs.h ../puttyps.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxproxy.c uxpsusan.o: ../unix/uxpsusan.c ../putty.h ../mpint.h ../ssh.h ../sshserver.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxpsusan.c uxpterm.o: ../unix/uxpterm.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxpterm.c uxpty.o: ../unix/uxpty.c ../putty.h ../ssh.h ../sshserver.h ../tree234.h \ ../sshttymodes.h ../sshsignals.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../puttymem.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxpty.c uxputty.o: ../unix/uxputty.c ../putty.h ../ssh.h ../storage.h \ ../unix/gtkcompat.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ ../tree234.h ../sshttymodes.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxputty.c uxsel.o: ../unix/uxsel.c ../putty.h ../tree234.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxsel.c uxser.o: ../unix/uxser.c ../putty.h ../tree234.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxser.c uxserver.o: ../unix/uxserver.c ../putty.h ../mpint.h ../ssh.h ../sshserver.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxserver.c uxsftp.o: ../unix/uxsftp.c ../putty.h ../ssh.h ../psftp.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxsftp.c uxsftpserver.o: ../unix/uxsftpserver.c ../putty.h ../ssh.h ../sshserver.h \ ../sftp.h ../tree234.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxsftpserver.c uxshare.o: ../unix/uxshare.c ../tree234.h ../putty.h ../network.h ../proxy.h \ ../ssh.h ../defs.h ../puttyps.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxshare.c uxsignal.o: ../unix/uxsignal.c ../defs.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxsignal.c uxsocks.o: ../unix/uxsocks.c ../putty.h ../ssh.h ../psocks.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxsocks.c uxstore.o: ../unix/uxstore.c ../putty.h ../storage.h ../tree234.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxstore.c uxucs.o: ../unix/uxucs.c ../putty.h ../charset/charset.h ../terminal.h \ ../misc.h ../defs.h ../puttyps.h ../network.h ../marshal.h \ ../sshsignals.h ../tree234.h ../puttymem.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxucs.c uxutils.o: ../unix/uxutils.c ../putty.h ../ssh.h ../unix/uxutils.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxutils.c version.o: ../version.c ../putty.h ../ssh.h ../empty.h ../version.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../version.c wcwidth.o: ../wcwidth.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../wcwidth.c wildcard.o: ../wildcard.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../wildcard.c wincapi.o: ../windows/wincapi.c ../putty.h ../ssh.h ../windows/wincapi.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/wincapi.c wincfg.o: ../windows/wincfg.c ../putty.h ../dialog.h ../storage.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/wincfg.c wincliloop.o: ../windows/wincliloop.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/wincliloop.c wincons.o: ../windows/wincons.c ../putty.h ../storage.h ../ssh.h \ ../console.h ../defs.h ../puttyps.h ../network.h ../misc.h \ ../marshal.h ../sshsignals.h ../puttymem.h ../tree234.h \ ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/wincons.c winctrls.o: ../windows/winctrls.c ../putty.h ../misc.h ../dialog.h ../defs.h \ ../puttyps.h ../network.h ../marshal.h ../sshsignals.h \ ../puttymem.h ../windows/winstuff.h ../unix/unix.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winctrls.c windefs.o: ../windows/windefs.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/windefs.c windlg.o: ../windows/windlg.c ../putty.h ../ssh.h ../windows/win_res.h \ ../windows/winseat.h ../storage.h ../dialog.h ../licence.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/windlg.c window.o: ../windows/window.c ../putty.h ../terminal.h ../storage.h \ ../windows/win_res.h ../windows/winsecur.h \ ../windows/winseat.h ../tree234.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/window.c wingss.o: ../windows/wingss.c ../putty.h ../pgssapi.h ../sshgss.h \ ../sshgssc.h ../misc.h ../defs.h ../puttyps.h ../network.h \ ../marshal.h ../sshsignals.h ../puttymem.h \ ../windows/winstuff.h ../unix/unix.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/wingss.c winhandl.o: ../windows/winhandl.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winhandl.c winhelp.o: ../windows/winhelp.c ../putty.h ../windows/win_res.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winhelp.c winhsock.o: ../windows/winhsock.c ../tree234.h ../putty.h ../network.h \ ../defs.h ../puttyps.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winhsock.c winjump.o: ../windows/winjump.c ../putty.h ../storage.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winjump.c winmisc.o: ../windows/winmisc.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winmisc.c winmiscs.o: ../windows/winmiscs.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winmiscs.c winnet.o: ../windows/winnet.c ../putty.h ../network.h ../tree234.h ../ssh.h \ ../defs.h ../puttyps.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winnet.c winnohlp.o: ../windows/winnohlp.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winnohlp.c winnoise.o: ../windows/winnoise.c ../putty.h ../ssh.h ../storage.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winnoise.c winnojmp.o: ../windows/winnojmp.c $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winnojmp.c winnpc.o: ../windows/winnpc.c ../tree234.h ../putty.h ../network.h \ ../proxy.h ../ssh.h ../windows/winsecur.h ../defs.h \ ../puttyps.h ../misc.h ../marshal.h ../sshsignals.h \ ../puttymem.h ../sshttymodes.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winnpc.c winnps.o: ../windows/winnps.c ../tree234.h ../putty.h ../network.h \ ../proxy.h ../ssh.h ../windows/winsecur.h ../defs.h \ ../puttyps.h ../misc.h ../marshal.h ../sshsignals.h \ ../puttymem.h ../sshttymodes.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winnps.c winpgen.o: ../windows/winpgen.c ../putty.h ../ssh.h ../sshkeygen.h \ ../licence.h ../windows/winsecur.h ../windows/puttygen-rc.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winpgen.c winpgnt.o: ../windows/winpgnt.c ../putty.h ../ssh.h ../misc.h ../tree234.h \ ../windows/winsecur.h ../windows/wincapi.h ../pageant.h \ ../licence.h ../windows/pageant-rc.h ../defs.h ../puttyps.h \ ../network.h ../marshal.h ../sshsignals.h ../puttymem.h \ ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winpgnt.c winpgntc.o: ../windows/winpgntc.c ../putty.h ../pageant.h \ ../windows/winsecur.h ../windows/wincapi.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winpgntc.c winplink.o: ../windows/winplink.c ../putty.h ../storage.h ../tree234.h \ ../windows/winsecur.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winplink.c winprint.o: ../windows/winprint.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winprint.c winproxy.o: ../windows/winproxy.c ../tree234.h ../putty.h ../network.h \ ../proxy.h ../defs.h ../puttyps.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winproxy.c winsecur.o: ../windows/winsecur.c ../putty.h ../windows/winsecur.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winsecur.c winselcli.o: ../windows/winselcli.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winselcli.c winselgui.o: ../windows/winselgui.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winselgui.c winser.o: ../windows/winser.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winser.c winsftp.o: ../windows/winsftp.c ../putty.h ../psftp.h ../ssh.h \ ../windows/winsecur.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ ../tree234.h ../sshttymodes.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winsftp.c winshare.o: ../windows/winshare.c ../tree234.h ../putty.h ../network.h \ ../proxy.h ../ssh.h ../windows/wincapi.h \ ../windows/winsecur.h ../noshare.c ../defs.h ../puttyps.h \ ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winshare.c winsocks.o: ../windows/winsocks.c ../putty.h ../ssh.h ../psocks.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winsocks.c winstore.o: ../windows/winstore.c ../putty.h ../storage.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winstore.c wintime.o: ../windows/wintime.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/wintime.c winucs.o: ../windows/winucs.c ../putty.h ../terminal.h ../misc.h ../defs.h \ ../puttyps.h ../network.h ../marshal.h ../sshsignals.h \ ../tree234.h ../puttymem.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winucs.c winutils.o: ../windows/winutils.c ../putty.h ../misc.h ../defs.h \ ../puttyps.h ../network.h ../marshal.h ../sshsignals.h \ ../puttymem.h ../windows/winstuff.h ../unix/unix.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winutils.c winx11.o: ../windows/winx11.c ../putty.h ../ssh.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winx11.c x11fwd.o: ../x11fwd.c ../putty.h ../ssh.h ../sshchan.h ../tree234.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../x11fwd.c x11misc.o: ../unix/x11misc.c ../putty.h ../unix/x11misc.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/x11misc.c xenc.o: ../charset/xenc.c ../charset/charset.h ../charset/internal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/xenc.c xkeysym.o: ../unix/xkeysym.c ../misc.h ../defs.h ../puttymem.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/xkeysym.c xpmptcfg.o: ../unix/xpmptcfg.c $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/xpmptcfg.c xpmpterm.o: ../unix/xpmpterm.c $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/xpmpterm.c xpmpucfg.o: ../unix/xpmpucfg.c $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/xpmpucfg.c xpmputty.o: ../unix/xpmputty.c $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/xpmputty.c clean: rm -f *.o cgtest fuzzterm osxlaunch plink pscp psftp psocks psusan puttygen testcrypt testsc testzlib uppity FORCE: putty-0.76/windows/0000755000175000017500000000000014072266316011335 500000000000000putty-0.76/windows/README-msi.txt0000644000175000017500000000304514072266314013541 00000000000000PuTTY README ============ This is the README file for the PuTTY MSI installer distribution. If you're reading this, you've probably just run our installer and installed PuTTY on your system. What should I do next? ---------------------- If you want to use PuTTY to connect to other computers, or use PSFTP to transfer files, you should just be able to run them from the Start menu. If you want to use the command-line file transfer utility PSCP, you will need to run this from a Command Prompt or equivalent, because it will not do anything useful without command-line options telling it what files to copy to and from where. You can do this by just running the command 'pscp' from a Command Prompt, if you used the installer's option to put the PuTTY installation directory on your PATH. Alternatively, you can always run pscp.exe by its full pathname, e.g. "C:\Program Files\PuTTY\pscp.exe". (Note that a Command Prompt that was already open before you ran the installer will not have inherited the update of PATH.) What do I do if it doesn't work? -------------------------------- The PuTTY home web site is https://www.chiark.greenend.org.uk/~sgtatham/putty/ Here you will find our list of known bugs and pending feature requests. If your problem is not listed in there, or in the FAQ, or in the manuals, read the Feedback page to find out how to report bugs to us. PLEASE read the Feedback page carefully: it is there to save you time as well as us. Do not send us one-line bug reports telling us `it doesn't work'. putty-0.76/windows/installer.wxs0000644000175000017500000007341014072266314014020 00000000000000 1 "1"]]> 1 NOT Installed Installed 1 1 NOT WIXUI_DONTVALIDATEPATH "1"]]> WIXUI_DONTVALIDATEPATH OR WIXUI_INSTALLDIR_VALID="1" 1 1 1 Installed NOT Installed 1 NOT Installed Installed AND NOT PATCH Installed AND PATCH 1 1 1 1 WIXUI_EXITDIALOGOPTIONALCHECKBOX = 1 and NOT Installed putty-0.76/windows/make_install_images.sh0000755000175000017500000000142014072266314015577 00000000000000#!/bin/sh # Script to make the bitmap files that go into the PuTTY MSI installer. set -e # For convenience, allow this script to be run from the Windows # subdirectory as well as the top level of the source tree. if test -f installer.wxs -a ! -f putty.h -a -f ../putty.h; then cd .. fi convert -size 164x312 'gradient:blue-white' -distort SRT -90 -swirl 180 \ \( icons/putty-48.png -geometry +28+24 \) -composite \ \( icons/pscp-48.png -geometry +88+96 \) -composite \ \( icons/puttygen-48.png -geometry +28+168 \) -composite \ \( icons/pageant-48.png -geometry +88+240 \) -composite \ windows/msidialog.bmp convert -size 493x58 canvas:white \ \( icons/putty-48.png -geometry +440+5 \) -composite \ windows/msibanner.bmp putty-0.76/windows/msifixup.py0000755000175000017500000000462714072266314013505 00000000000000#!/usr/bin/env python3 import argparse import os import tempfile import shutil import subprocess import pipes def run(command, verbose): if verbose: sys.stdout.write("$ {}\n".format(" ".join( pipes.quote(word) for word in command))) out = subprocess.check_output(command) if verbose: sys.stdout.write("".join( "> {}\n".format(line) for line in out.splitlines())) def make_changes(msi, args): run(["msidump", "-t", msi], args.verbose) build_cmd = ["msibuild", msi] def change_table(filename): with open(filename) as fh: lines = [line.rstrip("\r\n").split("\t") for line in iter(fh.readline, "")] for line in lines[3:]: yield line with open(filename, "w") as fh: for line in lines: fh.write("\t".join(line) + "\r\n") build_cmd.extend(["-i", filename]) if args.platform is not None: for line in change_table("_SummaryInformation.idt"): if line[0] == "7": line[1] = ";".join([args.platform] + line[1].split(";", 1)[1:]) if args.dialog_bmp_width is not None: for line in change_table("Control.idt"): if line[9] == "WixUI_Bmp_Dialog": line[5] = args.dialog_bmp_width run(build_cmd, args.verbose) def main(): parser = argparse.ArgumentParser( description='Change the platform field of an MSI installer package.') parser.add_argument("msi", help="MSI installer file.") parser.add_argument("--platform", help="Change the platform field.") parser.add_argument("--dialog-bmp-width", help="Change the width field" " in all uses of WixUI_Bmp_Dialog.") parser.add_argument("-v", "--verbose", action="store_true", help="Log what this script is doing.") parser.add_argument("-k", "--keep", action="store_true", help="Don't delete the temporary working directory.") args = parser.parse_args() msi = os.path.abspath(args.msi) msidir = os.path.dirname(msi) try: tempdir = tempfile.mkdtemp(dir=msidir) os.chdir(tempdir) make_changes(msi, args) finally: if args.keep: sys.stdout.write( "Retained temporary directory {}\n".format(tempdir)) else: shutil.rmtree(tempdir) if __name__ == '__main__': main() putty-0.76/windows/pageant-rc.h0000755000175000017500000000146414072266314013455 00000000000000/* * Constant definitions for the Pageant resource file. */ #define IDI_MAINICON 200 #define IDI_TRAYICON 201 #define IDD_KEYLIST 211 #define IDD_LOAD_PASSPHRASE 210 #define IDD_ONDEMAND_PASSPHRASE 212 #define IDD_ABOUT 213 #define IDD_LICENCE 214 #define IDC_PASSPHRASE_STATIC1 100 #define IDC_PASSPHRASE_FINGERPRINT 101 #define IDC_PASSPHRASE_STATIC2 102 #define IDC_PASSPHRASE_STATIC3 103 #define IDC_PASSPHRASE_EDITBOX 104 #define IDC_KEYLIST_LISTBOX 100 #define IDC_KEYLIST_ADDKEY 101 #define IDC_KEYLIST_ADDKEY_ENC 110 #define IDC_KEYLIST_REENCRYPT 106 #define IDC_KEYLIST_REMOVE 102 #define IDC_KEYLIST_HELP 103 #define IDC_KEYLIST_FPTYPE_STATIC 104 #define IDC_KEYLIST_FPTYPE 105 #define IDC_ABOUT_LICENCE 101 #define IDC_ABOUT_WEBSITE 102 #define IDC_ABOUT_TEXTBOX 1000 #define IDC_LICENCE_TEXTBOX 1000 putty-0.76/windows/pageant.ico0000644000175000017500000000775614072266314013405 00000000000000(f èŽ00hv°Þ 0Ž 000¾ ( €€€€€€€€€€ÀÀÀ€€€ÿÿÿÿÿÿÿÿÿÿÿÿˆˆˆˆ€ÿÿˆˆxˆˆˆˆøwwwxüÌÌxüÌÌxüÌÌxüÌÌxÿÿˆw€àÀÀàààààààð€€üüþÿ( @€€€€€€€€€ÀÀÀ€€€ÿÿÿÿÿÿÿÿÿÿÿÿˆˆˆˆˆˆˆˆˆ€wwwww€‡ˆwwwwwwwwwˆ€ÿÿÿÿÿÿÿÿÿx€pø€xˆˆˆˆˆˆ€€ÿÿÿÿÿÿ÷ˆôÌÌÌÌÌLjôÌÌÌÌÌLjôÌÌÌÌÌLjôÌÌÌÌÌLjôÌÌÌÌÌLjôÌÌÌÌÌLjôÌÌÌÌÌLjôDDDDDG€ÿÿÿÿÿwwˆpˆÿðÿÿx€x€ÿÿÿÿÿÿÿÿøð?ðððøüþþþþþþþþcþþÿ?ÿ€ÿøÿÀÿÀÿÿàÿÿðÿÿðÿÿøÿÿøcÿÿüÿÿÿÿÿÿÿÿÿÿ(0`€€€€€€€€€€ÀÀÀ€€€ÿÿÿÿÿÿÿÿÿÿÿÿxˆˆˆˆˆˆˆˆˆˆˆˆˆ€xˆˆˆˆˆˆˆˆˆˆˆˆˆˆ÷wwwwwwwxˆ€÷wwwwwwwwwwwwxˆˆÿÿÿÿÿÿÿÿÿÿÿÿÿ÷ˆˆ€ÿÿÿÿÿÿÿÿÿÿÿÿÿÿxˆ€wpøˆ€pxˆˆˆˆˆˆˆˆˆˆÿˆ€p÷wwwwwwwwwˆ€ø€÷wwwwwwwwwˆˆÿ€÷ÿÿÿÿÿÿÿÿÿˆˆ€øLÌÌÌÌÌÌÌψˆ€øLÌÌÌÌÌÌÌψˆ€øLÌÌÌÌÌÌÌψˆ€øLÌÌÌÌÌÌÌψˆ€øLÌÌÌÌÌÌÌψˆ€øLÌÌÌÌÌÌÌψˆ€øLÌÌÌÌÌÌÌψˆ€øLÌÌÌÌÌÌÌψˆ€øLÌÌÌÌÌÌÌψˆ€øLÌÌÌÌÌÌÌψˆ€øDDDDDDDDOˆˆ€øˆˆˆˆˆˆˆˆ‡ˆˆ€ÿÿÿÿÿÿÿÿÿÿpwwwwwwwpwwwwwwpˆpˆÿð‡ÿÿÿ‡ÿÿÿÿxÿÿÿÿx€ÿÿ÷ˆˆÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿüÿüÿüÿüü?ü?þ?ÿ?ÿ€?ÿÀ?ÿàÿàÿÿàÿÿàÿÿàÿÿàÿÿàÿÿàÿÿàÿÿàÿÿàÿÿàÇÿàÿàÿðÿøÿÿüÿÿþÿÿÀ?ÿü?ÿà?ÿàÿàÿÿÿÿÿÿ€ÿÿÿÀÿÿÿÀÿÿÿàÿÿÿàÿÿÿð0ÿÿÿÿðÿÿÿÿøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ( @ÿÿÿøü ð0000ÀàÀÀàààààààð€€üüþÿ( @€ÿÿÿÿÿ€ÿƒÀÿÿàÿÿààÿþàÿÿ€€€€€€€€ÿÀ|øàÿÿÿÿÿÿÿÿøð?ðððøüþþþþþþþþcþþÿ?ÿ€ÿøÿÀÿÀÿÿàÿÿðÿÿðÿÿøÿÿøcÿÿüÿÿÿÿÿÿÿÿÿÿ(0`€ÿÿÿÿÿÿøÿÿÿüÿÿ~ÿÿÿÿÿÿÿÿ€ÿÿÿÿ€à?€oÿÿ߀/ÿÿï€ÿÿ÷€ÿÿø ø ø ø ø ø ø ø ø ø ø øÿÿøÿÿ€ÿøÿà€€ÿÿð?ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿþÿüÿüÿüÿüü?ü?þ?ÿ?ÿ€?ÿÀ?ÿàÿàÿÿàÿÿàÿÿàÿÿàÿÿàÿÿàÿÿàÿÿàÿÿàÿÿàÇÿàÿàÿðÿøÿÿüÿÿþÿÿÀ?ÿü?ÿà?ÿàÿàÿÿÿÿÿÿ€ÿÿÿÀÿÿÿÀÿÿÿàÿÿÿàÿÿÿð0ÿÿÿÿðÿÿÿÿøÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿputty-0.76/windows/pageant.mft0000644000175000017500000000230714072266314013404 00000000000000 PuTTY SSH authentication agent true putty-0.76/windows/pageant.rc0000644000175000017500000000644014072266314013224 00000000000000/* * Windows resources for Pageant. */ #include "rcstuff.h" #define APPNAME "Pageant" #define APPDESC "PuTTY SSH authentication agent" #include "pageant-rc.h" #include "winhelp.rc2" IDI_MAINICON ICON "pageant.ico" IDI_TRAYICON ICON "pageants.ico" IDD_LOAD_PASSPHRASE DIALOG DISCARDABLE 0, 0, 140, 60 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Pageant: Loading Encrypted Key" FONT 8, "MS Shell Dlg" BEGIN CTEXT "Enter passphrase to load key", IDC_PASSPHRASE_STATIC1, 10, 6, 120, 8 CTEXT "", IDC_PASSPHRASE_FINGERPRINT, 10, 16, 120, 8 EDITTEXT IDC_PASSPHRASE_EDITBOX, 10, 26, 120, 12, ES_PASSWORD | ES_AUTOHSCROLL DEFPUSHBUTTON "O&K", IDOK, 20, 42, 40, 14 PUSHBUTTON "&Cancel", IDCANCEL, 80, 42, 40, 14 END IDD_ONDEMAND_PASSPHRASE DIALOG DISCARDABLE 0, 0, 250, 78 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Pageant: Decrypting Stored Key" FONT 8, "MS Shell Dlg" BEGIN CTEXT "A client of Pageant wants to use the following encrypted key:", IDC_PASSPHRASE_STATIC1, 10, 6, 230, 8 CTEXT "", IDC_PASSPHRASE_FINGERPRINT, 10, 16, 230, 8 CTEXT "If you intended this, click in this box to make sure it has", IDC_PASSPHRASE_STATIC2, 10, 26, 230, 8 CTEXT "input focus, then enter the passphrase to decrypt the key.", IDC_PASSPHRASE_STATIC3, 10, 34, 230, 8 EDITTEXT IDC_PASSPHRASE_EDITBOX, 10, 44, 230, 12, ES_PASSWORD | ES_AUTOHSCROLL DEFPUSHBUTTON "O&K", IDOK, 45, 60, 40, 14 PUSHBUTTON "&Cancel", IDCANCEL, 105, 60, 40, 14 PUSHBUTTON "&Help", IDHELP, 165, 60, 50, 14 END IDD_KEYLIST DIALOG DISCARDABLE 0, 0, 450, 236 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "Pageant Key List" FONT 8, "MS Shell Dlg" BEGIN LISTBOX 100, 10, 10, 420, 155, LBS_EXTENDEDSEL | LBS_HASSTRINGS | LBS_USETABSTOPS | WS_VSCROLL | WS_TABSTOP PUSHBUTTON "&Add Key", IDC_KEYLIST_ADDKEY, 10, 187, 60, 14 PUSHBUTTON "Add Key (&encrypted)", IDC_KEYLIST_ADDKEY_ENC, 75, 187, 80, 14 PUSHBUTTON "Re-e&ncrypt", IDC_KEYLIST_REENCRYPT, 315, 187, 60, 14 PUSHBUTTON "&Remove", IDC_KEYLIST_REMOVE, 380, 187, 60, 14 PUSHBUTTON "&Help", IDC_KEYLIST_HELP, 10, 212, 50, 14 DEFPUSHBUTTON "&Close", IDOK, 390, 212, 50, 14 LTEXT "&Fingerprint type:", IDC_KEYLIST_FPTYPE_STATIC, 10, 172, 60, 8 COMBOBOX IDC_KEYLIST_FPTYPE, 70, 170, 60, 12, CBS_DROPDOWNLIST END /* Accelerators used: cl */ IDD_ABOUT DIALOG DISCARDABLE 140, 40, 270, 136 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "About Pageant" FONT 8, "MS Shell Dlg" BEGIN DEFPUSHBUTTON "&Close", IDOK, 216, 118, 48, 14 PUSHBUTTON "View &Licence", IDC_ABOUT_LICENCE, 6, 118, 70, 14 PUSHBUTTON "Visit &Web Site", IDC_ABOUT_WEBSITE, 140, 118, 70, 14 EDITTEXT IDC_ABOUT_TEXTBOX, 10, 6, 250, 110, ES_READONLY | ES_MULTILINE | ES_CENTER, WS_EX_STATICEDGE END /* No accelerators used */ IDD_LICENCE DIALOG DISCARDABLE 50, 50, 326, 239 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "PuTTY Licence" FONT 8, "MS Shell Dlg" BEGIN DEFPUSHBUTTON "OK", IDOK, 148, 219, 44, 14 EDITTEXT IDC_LICENCE_TEXTBOX, 10, 10, 306, 200, ES_READONLY | ES_MULTILINE | ES_LEFT, WS_EX_STATICEDGE END #include "version.rc2" #ifndef NO_MANIFESTS 1 RT_MANIFEST "pageant.mft" #endif /* NO_MANIFESTS */ putty-0.76/windows/pageants.ico0000644000175000017500000000077614072266314013563 00000000000000(&°N( €€€€€€€€€€ÀÀÀ€€€ÿÿÿÿÿÿÿÿÿÿÿÿˆˆˆˆ€ÿÿˆˆxˆˆˆˆøwwwxüÌÌxüÌÌxüÌÌxüÌÌxÿÿˆw€àÀÀàààààààð€€üüþÿ( @ÿÿÿøü ð0000ÀàÀÀàààààààð€€üüþÿputty-0.76/windows/plink.rc0000644000175000017500000000023214072266314012713 00000000000000#include "rcstuff.h" #define APPNAME "Plink" #define APPDESC "Command-line SSH, Telnet, and Rlogin client" 200 ICON "putty.ico" #include "version.rc2" putty-0.76/windows/pscp.ico0000644000175000017500000000775614072266314012733 00000000000000(f èŽ00hv°Þ 0Ž 000¾ ( €€€€€€€€€€ÀÀÀ€€€ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿø¸ÿ‹ˆÿˆ»ˆˆ»»¸ˆˆ€»»°ˆ€ ¸‡xKLxÄ´xÌDxÿÿxðüüüüüþ( @€€€€€€€€€ÀÀÀ€€€ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿÿ ÿÿÿÿð°ÿ °ÿÿÿÿð»ÿÿÿ °ÿÿÿÿÿð»°ˆˆˆˆˆ€ »x‡ˆ»»»»°wwwˆ€»»»»°ÿÿÿx€ »ø€»°ˆˆˆˆ€€»ÿÿ÷ˆ °ÌÌLj@» ÌLjL°ÌLjLÌ ÇˆLÌÀ LjLÌÌÌLjDDDDGˆÿÿÿÿ÷ˆwwwwwxÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€ÿÀÿðÿøÿüÿüÿüÿüÿüÿüÿüÿüÿüÿþÿÿ(0`€€€€€€€€€€ÀÀÀ€€€ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿðÿÿðÿÿðÿÿðÿÿðÿÿÿÿÿÿÿÿÿððÿÿÿððÿÿÿðÿÿÿÿÿÿÿÿÿððÿððÿðÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿðÿÿÿÿÿÿðÿÿÿÿÿÿÿÿÿðÿÿÿÿÿÿðÿÿÿ ÿÿðÿÿÿÿÿð°ÿÿððÿ ÿððÿð»ðÿÿÿÿÿÿÿ °ððÿÿÿð»ðÿÿÿÿ »ÿÿÿÿÿÿÿÿð °» »»»»»»°ˆˆˆˆˆˆˆˆ»»»»»ˆˆˆˆˆˆˆ€ »»»»°xˆˆ»»»»»°ÿÿÿÿÿxˆ€ »ÿÿÿÿÿ÷ˆ€p °ÿˆ€»ˆˆˆˆˆˆ€ø€ »wwwwxˆÿ€p °ÿÿÿÿøˆ€„À» ÌÌÌøˆ€„Ì °ÌÌÌøˆ€„ÌÀ ÌÌøˆ€„ÌÌÀ°ÌÌøˆ€„ÌÌÌ Ìøˆ€„ÌÌÌÀ Ìøˆ€„ÌÌÌÌÌÌøˆ€„ÌÌÌÌÌÌøˆ€„DDDDDDøˆ€ˆˆˆˆˆˆˆxˆ€ÿÿÿÿÿÿÿ÷ˆ€wwwwwwwwx€wwwwwwww€ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ?ÿÿÿÿÿþÿÿÿÿÀÿÿÀÿÿÀÿÿàÿÿðÿÿøÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿþÿÿÿÿÿÿ€( @ÿÿÿcGkDs|žâœL, üðüüüüüþ( @€ÿÿÿüxü@|ü@ üaü<s\¤@ØìCöû¿øÜ<?çþûþ¿îßð`p0ppppppÿðÿðÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ€ÿÀÿðÿøÿüÿüÿüÿüÿüÿüÿüÿüÿüÿþÿÿ(0`€ÿÿÿÿø~ø~øÿø`ø`øÿø`8`8ÿøp?øp?øÿø|sø|uøúø` 8`Øÿh`°`ÜÿæÀÿïÿð?÷ÿøùÀ|þÿþÿþ €~Ïÿ¾wÿÞ›ÿàŒà†àà€ƒà€Cà€à€à€à€àÿÿàÿÿàÿÿàÿàÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ?ÿÿÿÿÿþÿÿÿÿÀÿÿÀÿÿÀÿÿàÿÿðÿÿøÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿþÿÿÿÿÿÿ€putty-0.76/windows/pscp.rc0000644000175000017500000000021114072266314012540 00000000000000#include "rcstuff.h" #define APPNAME "PSCP" #define APPDESC "Command-line SCP/SFTP client" 200 ICON "pscp.ico" #include "version.rc2" putty-0.76/windows/psftp.rc0000644000175000017500000000022214072266314012731 00000000000000#include "rcstuff.h" #define APPNAME "PSFTP" #define APPDESC "Command-line interactive SFTP client" 200 ICON "pscp.ico" #include "version.rc2" putty-0.76/windows/putty.ico0000644000175000017500000000775614072266314013153 00000000000000(f èŽ00hv°Þ 0Ž 000¾ ( €€€€€€€€€€ÀÀÀ€€€ÿÿÿÿÿÿÿÿÿÿÿÿˆˆˆ€ÿÿˆˆˆˆˆˆx‡w€ô´Ç€üKH€üD»ˆˆø»»°ˆˆ€»»°ˆ€ ¸‡xKLxÄ´xÌDxÿÿx€?€€?€?€€€Àüüüüüþ( @€€€€€€€€€ÀÀÀ€€€ÿÿÿÿÿÿÿÿÿÿÿÿˆˆˆˆˆˆˆˆwwww€x€wwwwwwwxˆÿÿÿÿÿÿÿ÷ˆpˆxˆˆˆˆˆøÿÿÿÿÿx€ôÌÌÌÌx€ôÌÌÌx€ôÌ Ìx€ôÌÀ° x€ôÌÌ °x€ôÌÌÀ»€ôDDD °ÿÿÿÿð»°ˆˆˆˆˆ€ww »x‡ˆ»»»»°wwwˆ€»»»»°ÿÿÿx€ »ø€»°ˆˆˆˆ€€»ÿÿ÷ˆ °ÌÌLj@» ÌLjL°ÌLjLÌ ÇˆLÌÀ LjLÌÌÌLjDDDDGˆÿÿÿÿ÷ˆwwwwwx€?ÿÿÿÿÿ€ÿÀÿàÿà?ÿà?ÿà?ÿà?ÿà?ÿà?ÿààðøÿÀÿðÿøÿüÿüÿüÿüÿüÿüÿüÿüÿüÿþÿÿ(0`€€€€€€€€€€ÀÀÀ€€€ÿÿÿÿÿÿÿÿÿÿÿÿxˆˆˆˆˆˆˆˆˆˆˆxˆˆˆˆˆˆˆˆˆˆˆ€÷wwwwwxˆˆÿÿÿÿÿÿÿÿÿÿÿxˆ€ÿÿÿÿÿÿÿÿÿÿÿ÷ˆ€wÿˆ€ˆˆˆˆˆˆˆˆ€ø€wwwwwwwxˆÿ€ÿÿÿÿÿÿøˆ€„ÌÌÌÌÌÌøˆ€„ÌÌÌÌÌÌøˆ€„ÌÌÌÌÌÌøˆ€„ÌÌÌÌÌÌøˆ€„ÌÌÌÌÌøˆ€„ÌÌ ÌÌøˆ€„ÌÌÀ°ÌÌøˆ€„ÌÌÌ Ìøˆ€„DDD@»øˆ€ˆˆˆˆˆ °xˆ€ÿÿÿÿÿ𻈀wwwwww »€wwwwwp °€» »»»»»»°ˆˆˆˆˆˆˆˆ»»»»»ˆˆˆˆˆˆˆ€ »»»»°xˆˆ»»»»»°ÿÿÿÿÿxˆ€ »ÿÿÿÿÿ÷ˆ€p °ÿˆ€»ˆˆˆˆˆˆ€ø€ »wwwwxˆÿ€p °ÿÿÿÿøˆ€„À» ÌÌÌøˆ€„Ì °ÌÌÌøˆ€„ÌÀ ÌÌøˆ€„ÌÌÀ°ÌÌøˆ€„ÌÌÌ Ìøˆ€„ÌÌÌÀ Ìøˆ€„ÌÌÌÌÌÌøˆ€„ÌÌÌÌÌÌøˆ€„DDDDDDøˆ€ˆˆˆˆˆˆˆxˆ€ÿÿÿÿÿÿÿ÷ˆ€wwwwwwwwx€wwwwwwww€€?ÿÿÿÿÿÿÿÿÿÿÿÿ€ÿÿÀÿÿàÿÿðÿÿð?ÿÿð?ÿÿð?ÿÿð?ÿÿð?ÿÿð?ÿÿð?ÿÿð?ÿÿð?ÿÿð?ÿÿð?ÿÿø?ÿÿü?ÿÿþ?ÿÿÿÿÿþÿÿÿÿÀÿÿÀÿÿÀÿÿàÿÿðÿÿøÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿþÿÿÿÿÿÿ€( @ÿÿÿ€À@'€)€$€#</žâœL, ü€?€€?€?€€€Àüüüüüþ( @€ÿÿÿÿÀáàÿðÿð ðÿpÿ€€€C€#€€ €û¿øÁÜ<?çþûþ¿îßð`p0ppppppÿðÿð€?ÿÿÿÿÿ€ÿÀÿàÿà?ÿà?ÿà?ÿà?ÿà?ÿà?ÿààðøÿÀÿðÿøÿüÿüÿüÿüÿüÿüÿüÿüÿüÿþÿÿ(0`€ÿÿÿÿÿÀÿÿàÿðÿÿøÿÿø0øÿþøÿÿxÿÿ€€€€€€€€€Ï€ÿo€ÿ³€ÿÝ€ÿæ€Àÿïÿð?÷ÿøùÀ|þÿþÿþ €~Ïÿ¾wÿÞ›ÿàŒà†àà€ƒà€Cà€à€à€à€àÿÿàÿÿàÿÿàÿà€?ÿÿÿÿÿÿÿÿÿÿÿÿ€ÿÿÀÿÿàÿÿðÿÿð?ÿÿð?ÿÿð?ÿÿð?ÿÿð?ÿÿð?ÿÿð?ÿÿð?ÿÿð?ÿÿð?ÿÿð?ÿÿø?ÿÿü?ÿÿþ?ÿÿÿÿÿþÿÿÿÿÀÿÿÀÿÿÀÿÿàÿÿðÿÿøÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿþÿÿÿÿÿÿ€putty-0.76/windows/putty.mft0000644000175000017500000000253114072266314013151 00000000000000 A network client and terminal emulator true PerMonitorV2 putty-0.76/windows/putty.rc0000644000175000017500000000034014072266314012763 00000000000000#include "rcstuff.h" #define APPNAME "PuTTY" #define APPDESC "SSH, Telnet, Rlogin, and SUPDUP client" #include "winhelp.rc2" #include "win_res.rc2" #ifndef NO_MANIFESTS 1 RT_MANIFEST "putty.mft" #endif /* NO_MANIFESTS */ putty-0.76/windows/puttycfg.ico0000644000175000017500000000775614072266314013633 00000000000000(f èŽ00hv°Þ 0Ž 000¾ ( €€€€€€€€€€ÀÀÀ€€€ÿÿÿÿÿÿÿÿÿÿÿÿˆˆˆ€°ÿÿˆˆ;°ˆˆˆˆ»x‡w€;°ô´Ç»üK@;°üD3»ˆð3;°ˆˆ€3»;°ˆ€;»³¸‡x° °KLx¸Ä´x;ÌDx °ÿÿx€y0€€€€€€À€ Àæ( @€€€€€€€€€ÀÀÀ€€€ÿÿÿÿÿÿÿÿÿÿÿÿˆˆˆˆˆˆˆˆwwww€x€wwwwwwwxˆ0ÿÿÿÿÿÿÿ÷ˆ;pˆ3»°xˆˆˆˆˆø;»ÿÿÿÿÿx€3»°ôÌÌÌÌx€;»ôÌÌÌx€3»°ôÌ Ìx€;»ôÌÀ° x€3»°ôÌÌ °x;»ôÌÌÀ»3»°ôDDD ;»ÿÿÿÿð3»°ˆˆˆˆ€ww;»x‡ˆ°3»°°wwwˆ€;» »°ÿÿÿx€3»°ø€3;» °ˆˆˆˆ€€333»°»ÿÿ÷ˆ;»»» °ÌÌLj»»»°@» ÌLj;°»»L°ÌLj;;»LÌ Çˆ;°LÌÀ Lj;°LÌÌÌLj3»DDDDGˆ»ÿÿÿÿ÷ˆwwwwwx€?ÿÿ߀Ààà0à ?ààÿàÿàÿààðøÿÀü€ðàÀÀ €†Î ü øø<üþÿÿ(0`€€€€€€€€€€ÀÀÀ€€€ÿÿÿÿÿÿÿÿÿÿÿÿxˆˆˆˆˆˆˆˆˆˆˆxˆˆˆˆˆˆˆˆˆˆˆ€÷wwwwwxˆˆÿÿÿÿÿÿÿÿÿÿÿxˆ€ÿÿÿÿÿÿÿÿÿÿÿ÷ˆ€3°wÿˆ€;»ˆˆˆˆˆˆˆˆ€ø€3»»°wwwwwwwxˆÿ€;»»»ÿÿÿÿÿÿøˆ€3»»»°„ÌÌÌÌÌÌøˆ€;»»»„ÌÌÌÌÌÌøˆ€3»»»°„ÌÌÌÌÌÌøˆ€;»»»„ÌÌÌÌÌÌøˆ€3»»»°„ÌÌÌÌÌøˆ€;»»»„ÌÌ ÌÌøˆ€3»»»°„ÌÌÀ°ÌÌøˆ€;»»»„ÌÌÌ Ìøˆ€3»»»°„DDD@»øˆ;»»»ˆˆˆˆˆ °x€3»»»°ÿÿÿÿÿð»;»»»wwwwww °3»»»°wwwwwp;»»»3»»»°;»»»°3»»»°ˆˆˆˆˆˆˆˆ;»»» ˆˆˆˆˆˆˆ€3»»»°»°xˆˆ;»»» »»°ÿÿÿÿÿxˆ€3»»»°ÿÿÿÿÿ÷ˆ€;»»» °ÿˆ€03»»»°»ˆˆˆˆˆˆ€ø€333;»»» »wwwwxˆÿ€3»»;»»°p °ÿÿÿÿøˆ€3»»»»»»„À» ÌÌÌøˆ€3»»»»»°„Ì °ÌÌÌøˆ€;° »»„ÌÀ ÌÌøˆ€;;»°„ÌÌÀ°ÌÌøˆ€°;»°„ÌÌÌ Ìøˆ€;»„ÌÌÌÀ Ìøˆ€;»„ÌÌÌÌÌÌøˆ€;»„ÌÌÌÌÌÌøˆ€3»°„DDDDDDøˆ€»»ˆˆˆˆˆˆˆxˆ€ °ÿÿÿÿÿÿÿ÷ˆ€wwwwwwwwx€wwwwwwww€€?ÿÿÿÿÿÿþÿüø?€ðÀààÀð€ð?ð>?ð<ð8ÿð0ÿð ÿðÿðÿðÿð?ÿðÿøÿÿüÿÿþÿÿÿÿÿþÿÿÿþÿüÿøÿüøð àà<à|Áà<Ãà<çà|ÿà|ÿÀ|ÿ€üÿüÿüÿŸþÿÿÿÿÿÿ€( @ÿÿÿ‚ÆL'˜)0$` Ü!žb>œ&L, ü€y0€€€€€€À€ Àæ( @€ÿÿÿÿÀáàÿðÿð0 ðxÿpðÿàƒÀ‡€C#ž< xðùïøÃÜ<'§þ{þ=¿îøßðñ`pá0pñpñpápápÁpÿðÿð€?ÿÿ߀Ààà0à ?ààÿàÿàÿààðøÿÀü€ðàÀÀ €†Î ü øø<üþÿÿ(0`€ÿÿÿÿÿÀÿÿàÿðÿÿøÿÿø€0øÀÿþøàÿÿxðÿÿ€à€?À€€€ÿþƒü‡øðŸàÏ?Àÿn€ÿ°ÿÿÙþÿãüøðŸïÿð?×ÿø¹À|ÿ~ÿþþÿþý€~øÏÿ¾ÿñwÿÞÿá›ÿàÿÁŒàÿ†àà €ƒà€Cà€à€à€à€à<ÿÿà`ÿÿàÿÿàÿà€?ÿÿÿÿÿÿþÿüø?€ðÀààÀð€ð?ð>?ð<ð8ÿð0ÿð ÿðÿðÿðÿð?ÿðÿøÿÿüÿÿþÿÿÿÿÿþÿÿÿþÿüÿøÿüøð àà<à|Áà<Ãà<çà|ÿà|ÿÀ|ÿ€üÿüÿüÿŸþÿÿÿÿÿÿ€putty-0.76/windows/puttygen-rc.h0000644000175000017500000000135414072266314013710 00000000000000#define IDC_PPKVER_STATIC 100 #define IDC_PPKVER_2 101 #define IDC_PPKVER_3 102 #define IDC_KDF_STATIC 103 #define IDC_KDF_ARGON2ID 104 #define IDC_KDF_ARGON2I 105 #define IDC_KDF_ARGON2D 106 #define IDC_ARGON2_MEM_STATIC 107 #define IDC_ARGON2_MEM 108 #define IDC_ARGON2_MEM_STATIC2 109 #define IDC_PPK_AUTO_STATIC 110 #define IDC_PPK_AUTO_YES 111 #define IDC_PPK_AUTO_NO 112 #define IDC_ARGON2_TIME_STATIC 113 #define IDC_ARGON2_TIME 114 #define IDC_ARGON2_PARALLEL_STATIC 115 #define IDC_ARGON2_PARALLEL 116 putty-0.76/windows/puttygen.ico0000644000175000017500000000775614072266314013645 00000000000000(f èŽ00hv°Þ 0Ž 000¾ ( €€€€€€€€€€ÀÀÀ€€€ÿÿÿÿÿÿÿÿÿÿÿÿˆˆˆ€ÿÿˆˆˆˆˆˆwˆw€üKG€üĸüÄK°ÿ‹»» »»»30333°003333 0003€?€€?€?€?€?ÿÁÿã( @€€€€€€€€€ÀÀÀ€€€ÿÿÿÿÿÿÿÿÿÿÿÿˆˆˆˆˆˆˆˆwwww€x€wwwwwwwxˆÿÿÿÿÿÿÿ÷ˆpˆxˆˆˆˆˆøÿÿÿÿÿx€ôÌÌÌÌx€ôÌÌÌx€ôÌÌ x€ôÌÌÀ°€ôÌÌÌ °€ôÌÌÌÀ»ôDDDD °ÿÿÿÿÿp»°www »»»»»°»»»»°0 »3333»°33033»3333303 °333303333»300333333333°303333333333 000033330333333033€?ÿÿÿÿÿ€ÿÀÿàÿà?ÿà?ÿà?ÿà?ÿà?ÿà?ÿàÿàÿðÿøÿŸ‚ €€À€€€€ÿÿðÿÿøÿÿüÿÿþÿÿÿ(0`€€€€€€€€€€ÀÀÀ€€€ÿÿÿÿÿÿÿÿÿÿÿÿxˆˆˆˆˆˆˆˆˆˆˆxˆˆˆˆˆˆˆˆˆˆˆ€÷wwwwwxˆˆÿÿÿÿÿÿÿÿÿÿÿxˆ€ÿÿÿÿÿÿÿÿÿÿÿ÷ˆ€wÿˆ€ˆˆˆˆˆˆˆˆ€ø€wwwwwwwxˆÿ€ÿÿÿÿÿÿøˆ€„ÌÌÌÌÌÌøˆ€„ÌÌÌÌÌÌøˆ€„ÌÌÌÌÌÌøˆ€„ÌÌÌÌÌÌøˆ€„ÌÌÌÀ Ìøˆ€„ÌÌÌÀ°Ìøˆ€„ÌÌÌÌ øˆ€„ÌÌÌÌÀ°ˆ€„DDDDD °ˆ€ˆˆˆˆˆˆ€»€ÿÿÿÿÿÿÿ °wwwwwwwp»°wwwwwww» °»° »»»»» »»»»°»»»»» »»»»»0»°3300»333300 °33330330»°3333333330»33333333030 °333333333330»33003333333333333°33033333333333333 30333333333333330°30333333333333333303330033333333333333333333330333333€?ÿÿÿÿÿÿÿÿÿÿÿÿ€ÿÿÀÿÿàÿÿðÿÿð?ÿÿð?ÿÿð?ÿÿð?ÿÿð?ÿÿð?ÿÿð?ÿÿð?ÿÿð?ÿÿð?ÿÿð?ÿÿø?ÿÿüÿÿþÿÿÿÿàÿÿÿÿÀÿÿÿÿàÿÿÿÿø?ÿóÿ<?ñþ>0ðü?àðü?ÁÀðH?à€ð?ðð?üððxðxðxðxðÿÿÿÿ€ÿÿÿÿ€ÿÿÿÿ€ÿÿÿÿÀÿÿÿÿàÿÿÿÿðÿÿÿÿü?( @ÿÿÿ€À@3€%€"!€7ÀAôTÎ*Ò&€?€€?€?€?€?ÿÁÿã( @€ÿÿÿÿÀáàÿðÿð ðÿpÿ€€€€ €€€þàðpøþ AÀð0Àáø4À3ü6Àþ?À æ?ÿòÂ?ÿýBfþüøð€?ÿÿÿÿÿ€ÿÀÿàÿà?ÿà?ÿà?ÿà?ÿà?ÿà?ÿàÿàÿðÿøÿŸ‚ €€À€€€€ÿÿðÿÿøÿÿüÿÿþÿÿÿ(0`€ÿÿÿÿÿÀÿÿàÿðÿÿøÿÿø0øÿþøÿÿxÿÿ€€€€€€€O€'€€ÿí€ÿöÿû€ÿüÀ`8üþÿÿÀ€àÀ€0ð€ø€?ü3€?ü{€¿üÿ€ß†ÿÿÿ/ÿÿÿ×ÿÿÿëÿÿÿó†?ü?ü?üøðÀ€?ÿÿÿÿÿÿÿÿÿÿÿÿ€ÿÿÀÿÿàÿÿðÿÿð?ÿÿð?ÿÿð?ÿÿð?ÿÿð?ÿÿð?ÿÿð?ÿÿð?ÿÿð?ÿÿð?ÿÿð?ÿÿø?ÿÿüÿÿþÿÿÿÿàÿÿÿÿÀÿÿÿÿàÿÿÿÿø?ÿóÿ<?ñþ>0ðü?àðü?ÁÀðH?à€ð?ðð?üððxðxðxðxðÿÿÿÿ€ÿÿÿÿ€ÿÿÿÿ€ÿÿÿÿÀÿÿÿÿàÿÿÿÿðÿÿÿÿü?putty-0.76/windows/puttygen.mft0000644000175000017500000000230514072266314013642 00000000000000 SSH key generator for PuTTY true putty-0.76/windows/puttygen.rc0000644000175000017500000000567314072266314013473 00000000000000/* * Windows resources for PuTTYgen. */ #include "rcstuff.h" #define APPNAME "PuTTYgen" #define APPDESC "PuTTY SSH key generation utility" #include "winhelp.rc2" #include "puttygen-rc.h" 200 ICON "puttygen.ico" 201 DIALOG DISCARDABLE 0, 0, 400, 270 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "PuTTY Key Generator" FONT 8, "MS Shell Dlg" BEGIN END 210 DIALOG DISCARDABLE 0, 0, 140, 60 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "PuTTYgen: Enter Passphrase" FONT 8, "MS Shell Dlg" BEGIN CTEXT "Enter passphrase for key", 100, 10, 6, 120, 8 CTEXT "", 101, 10, 16, 120, 8 EDITTEXT 102, 10, 26, 120, 12, ES_PASSWORD | ES_AUTOHSCROLL DEFPUSHBUTTON "O&K", IDOK, 20, 42, 40, 14 PUSHBUTTON "&Cancel", IDCANCEL, 80, 42, 40, 14 END /* Accelerators used: cl */ 213 DIALOG DISCARDABLE 140, 40, 270, 136 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "About PuTTYgen" FONT 8, "MS Shell Dlg" BEGIN DEFPUSHBUTTON "&Close", IDOK, 216, 118, 48, 14 PUSHBUTTON "View &Licence", 101, 6, 118, 70, 14 PUSHBUTTON "Visit &Web Site", 102, 140, 118, 70, 14 EDITTEXT 1000, 10, 6, 250, 110, ES_READONLY | ES_MULTILINE | ES_CENTER, WS_EX_STATICEDGE END /* No accelerators used */ 214 DIALOG DISCARDABLE 50, 50, 326, 239 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "PuTTY Licence" FONT 8, "MS Shell Dlg" BEGIN DEFPUSHBUTTON "OK", IDOK, 148, 219, 44, 14 EDITTEXT 1000, 10, 10, 306, 200, ES_READONLY | ES_MULTILINE | ES_LEFT, WS_EX_STATICEDGE END 215 DIALOG DISCARDABLE 0, 0, 259, 98 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "PuTTYgen: Private Key File Parameters" FONT 8, "MS Shell Dlg" BEGIN LTEXT "PPK file version:", IDC_PPKVER_STATIC, 5, 6, 119, 8 AUTORADIOBUTTON "2", IDC_PPKVER_2, 124, 5, 30, 10, WS_GROUP AUTORADIOBUTTON "3", IDC_PPKVER_3, 154, 5, 30, 10 LTEXT "Key derivation function:", IDC_KDF_STATIC, 5, 22, 119, 8 AUTORADIOBUTTON "Argon2id", IDC_KDF_ARGON2ID, 124, 21, 45, 10, WS_GROUP AUTORADIOBUTTON "Argon2i", IDC_KDF_ARGON2I, 169, 21, 45, 10, WS_GROUP AUTORADIOBUTTON "Argon2d", IDC_KDF_ARGON2D, 214, 21, 45, 10 LTEXT "Memory to use for passphrase hash:", IDC_ARGON2_MEM_STATIC, 5, 36, 119, 8 EDITTEXT IDC_ARGON2_MEM, 124, 34, 40, 12 LTEXT "Kbyte", IDC_ARGON2_MEM_STATIC2, 174, 36, 34, 8 LTEXT "Time to use for passphrase hash:", IDC_ARGON2_TIME_STATIC, 5, 50, 119, 8 EDITTEXT IDC_ARGON2_TIME, 124, 48, 40, 12 AUTORADIOBUTTON "ms", IDC_PPK_AUTO_YES, 174, 49, 20, 10, WS_GROUP AUTORADIOBUTTON "passes", IDC_PPK_AUTO_NO, 204, 49, 40, 10 LTEXT "Parallelism for passphrase hash:", IDC_ARGON2_PARALLEL_STATIC, 5, 64, 119, 8 EDITTEXT IDC_ARGON2_PARALLEL, 124, 62, 60, 12 DEFPUSHBUTTON "O&K", IDOK, 70, 80, 40, 14 PUSHBUTTON "&Cancel", IDCANCEL, 134, 80, 40, 14 END #include "version.rc2" #ifndef NO_MANIFESTS 1 RT_MANIFEST "puttygen.mft" #endif /* NO_MANIFESTS */ putty-0.76/windows/puttyins.ico0000644000175000017500000002632614072266314013657 00000000000000 h– ¨þ00¨¦(N èv00h^!°Æ' 0v(000¦)( @€@¿ ÿÿ €€€///___{óæ¿¿¿ÿÿÿ}îy      €€€€€€Ààðüüüüþ( @@€@¿ÿÿ€€€¿¿¿ÿÿÿéèåáÛÓòïëÝyöìä{úýðüþÿ÷ñê|vr     !"ÀÿÀÿÀÿÀÿÀÿÀÿÀÿ€ÿÿÿ€ÿ€ÿÀÀ?àðøüþÿàÿðÿüÿüÿüÿüÿüÿüÿüÿüÿüÿþÿÿ(0` @€@¿ÿÿ€€€¿¿¿ÿÿÿêéèæãàÛÖÐuñðîëçÝ×xôöízøúûõâ|ýþïäÿåü{vtqn    ! "#$%%&'# $((($ !)#$%%%$&' * ! "+++++,-./ðÿÿðÿÿðÿÿðÿÿðÿð?ÿðÿðÿðÿðÿ€ÿÿ€ÿ€ÿÀÿÀÿàÿàÿðÿðÿøüÿþÿÿÿÿ€ÿÀÿàÿÿÿÿ€ÿÿÀÿÿàÿÿðÿÿøÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿþÿÿÿÿÿÿ€( €€€€€€€€€€ÀÀÀ€€€ÿÿÿÿÿÿÿÿÿÿÿÿ11111111 ±±±±00110 »» »»0€»ˆ´ÌxKLxÄLxÿÿx€€€€€€Ààðüüüüþ( @€€€€€€€€€ÀÀÀ€€€ÿÿÿÿÿÿÿÿÿÿÿÿ1111111111111111111111111111 ±±±±±±±1±±±±±±±±10 1110»° »110ˆ€»»»»°‡ˆ»»»»°0wwˆ€ »ÿÿx€»°ø€»ˆˆˆˆ€€ °ÿÿÿ÷ˆ» ÌÌLj°ÌÌLjL ÌLjLÀ ÌLjLÌÌÌLjLÌÌÌLjDDDDGˆÿÿÿÿ÷ˆwwwwwxÀÿÀÿÀÿÀÿÀÿÀÿÀÿ€ÿÿÿ€ÿ€ÿÀÀ?àðøüþÿàÿðÿüÿüÿüÿüÿüÿüÿüÿüÿüÿþÿÿ(0`€€€€€€€€€€ÀÀÀ€€€ÿÿÿÿÿÿÿÿÿÿÿÿ1111111111111111111111111111111111111111111111111111111±±±±±±±±±±±11±±±±±±±±±±±±1 ±±±±±±±±±±±110±±±±±±±±±±±±1110 111110»°»11110 °»°110 »»»»» »»»»°0ˆˆˆˆ»»»»»ˆˆˆˆ€ »»»»»wxˆˆ»°ÿÿÿÿÿÿÿxˆ€»ÿÿÿÿÿÿÿÿÿ÷ˆ€ °ÿˆ€»°ˆˆˆˆˆˆˆ€ø€»wwwwwxˆÿ€ °ÿÿÿÿÿøˆ€€» ÌÌÌÌøˆ€„°ÌÌÌÌøˆ€„Ì ÌÌÌøˆ€„ÌÀ°ÌÌÌøˆ€„ÌÌÌÌÌøˆ€„ÌÌÌÌÌÌøˆ€„ÌÌÌÌÌÌøˆ€„ÌÌÌÌÌÌøˆ€„DDDDDDøˆ€ˆˆˆˆˆˆˆxˆ€ÿÿÿÿÿÿÿ÷ˆ€wwwwwwwwx€wwwwwwww€ðÿÿðÿÿðÿÿðÿÿðÿð?ÿðÿðÿðÿðÿ€ÿÿ€ÿ€ÿÀÿÀÿàÿàÿðÿðÿøüÿþÿÿÿÿ€ÿÀÿàÿÿÿÿ€ÿÿÀÿÿàÿÿðÿÿøÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿþÿÿÿÿÿÿ€( @ÿÿÿ*€@*€U@*ˆT*ÊŒL ü€€€€€€Ààðüüüüþ( @€ÿÿÿ ª€U@ ª€U@ ª€U@ ª€UU@*ª€UD*ªŠUU*€@ ¸ÿ…<?âþþî¿ðÀp ppppppÿðÿðÀÿÀÿÀÿÀÿÀÿÀÿÀÿ€ÿÿÿ€ÿ€ÿÀÀ?àðøüþÿàÿðÿüÿüÿüÿüÿüÿüÿüÿüÿüÿþÿÿ(0`€ÿÿÿªª UUPªª UUPªª UUPªª UUPªª UUP*ªª UUP*ªª UUP@ ªª  UUQP ªª¢¨UUUT ªàU0ªT¨ÿPÿ€/ðÿÀø÷À|8ÿþ ÿÿþ~¿ÿ¾ßÿÞoÿà°àˆà„à‚à€à€à€à€à€àÿÿàÿÿàÿÿàÿàðÿÿðÿÿðÿÿðÿÿðÿð?ÿðÿðÿðÿðÿ€ÿÿ€ÿ€ÿÀÿÀÿàÿàÿðÿðÿøüÿþÿÿÿÿ€ÿÀÿàÿÿÿÿ€ÿÿÀÿÿàÿÿðÿÿøÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿüÿÿþÿÿÿÿÿÿ€putty-0.76/windows/puttytel.mft0000644000175000017500000000253414072266314013661 00000000000000 A network client and terminal emulator true PerMonitorV2 putty-0.76/windows/puttytel.rc0000644000175000017500000000033014072266314013467 00000000000000#include "rcstuff.h" #define APPNAME "PuTTYtel" #define APPDESC "Telnet and Rlogin client" #include "winhelp.rc2" #include "win_res.rc2" #ifndef NO_MANIFESTS 1 RT_MANIFEST "puttytel.mft" #endif /* NO_MANIFESTS */ putty-0.76/windows/rcstuff.h0000644000175000017500000000154614072266314013106 00000000000000/* * Miscellaneous stuff to include in all .rc files. */ #ifndef PUTTY_RCSTUFF_H #define PUTTY_RCSTUFF_H #ifdef __LCC__ #include #else /* Some compilers don't have winresrc.h */ #ifndef NO_WINRESRC_H #ifndef MSVC4 #include #else #include #endif #endif #endif /* end #ifdef __LCC__ */ /* Some systems don't define this, so I do it myself if necessary */ #ifndef TCS_MULTILINE #define TCS_MULTILINE 0x0200 #endif /* Likewise */ #ifndef RT_MANIFEST #define RT_MANIFEST 24 #endif /* LCC is the offender here. */ #ifndef VS_FF_DEBUG #define VS_FF_DEBUG 1 #endif #ifndef VS_FF_PRERELEASE #define VS_FF_PRERELEASE 2 #endif #ifndef VS_FF_PRIVATEBUILD #define VS_FF_PRIVATEBUILD 8 #endif #ifndef VOS__WINDOWS32 #define VOS__WINDOWS32 4 #endif #ifndef VFT_APP #define VFT_APP 1 #endif #endif /* PUTTY_RCSTUFF_H */ putty-0.76/windows/sizetip.c0000644000175000017500000001054214072266314013110 00000000000000/* * sizetip.c - resize tips for PuTTY(tel) terminal window. */ #include #include #include #include "putty.h" static ATOM tip_class = 0; static HFONT tip_font; static COLORREF tip_bg; static COLORREF tip_text; static LRESULT CALLBACK SizeTipWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam) { switch (nMsg) { case WM_ERASEBKGND: return true; case WM_PAINT: { HBRUSH hbr; HGDIOBJ holdbr; RECT cr; int wtlen; LPTSTR wt; HDC hdc; PAINTSTRUCT ps; hdc = BeginPaint(hWnd, &ps); SelectObject(hdc, tip_font); SelectObject(hdc, GetStockObject(BLACK_PEN)); hbr = CreateSolidBrush(tip_bg); holdbr = SelectObject(hdc, hbr); GetClientRect(hWnd, &cr); Rectangle(hdc, cr.left, cr.top, cr.right, cr.bottom); wtlen = GetWindowTextLength(hWnd); wt = (LPTSTR) snewn(wtlen + 1, TCHAR); GetWindowText(hWnd, wt, wtlen + 1); SetTextColor(hdc, tip_text); SetBkColor(hdc, tip_bg); TextOut(hdc, cr.left + 3, cr.top + 3, wt, wtlen); sfree(wt); SelectObject(hdc, holdbr); DeleteObject(hbr); EndPaint(hWnd, &ps); return 0; } case WM_NCHITTEST: return HTTRANSPARENT; case WM_DESTROY: DeleteObject(tip_font); tip_font = NULL; break; case WM_SETTEXT: { LPCTSTR str = (LPCTSTR) lParam; SIZE sz; HDC hdc = CreateCompatibleDC(NULL); SelectObject(hdc, tip_font); GetTextExtentPoint32(hdc, str, _tcslen(str), &sz); SetWindowPos(hWnd, NULL, 0, 0, sz.cx + 6, sz.cy + 6, SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE); InvalidateRect(hWnd, NULL, false); DeleteDC(hdc); break; } } return DefWindowProc(hWnd, nMsg, wParam, lParam); } static HWND tip_wnd = NULL; static bool tip_enabled = false; void UpdateSizeTip(HWND src, int cx, int cy) { TCHAR str[32]; if (!tip_enabled) return; if (!tip_wnd) { NONCLIENTMETRICS nci; /* First make sure the window class is registered */ if (!tip_class) { WNDCLASS wc; wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = SizeTipWndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hinst; wc.hIcon = NULL; wc.hCursor = NULL; wc.hbrBackground = NULL; wc.lpszMenuName = NULL; wc.lpszClassName = "SizeTipClass"; tip_class = RegisterClass(&wc); } #if 0 /* Default values based on Windows Standard color scheme */ tip_font = GetStockObject(SYSTEM_FONT); tip_bg = RGB(255, 255, 225); tip_text = RGB(0, 0, 0); #endif /* Prepare other GDI objects and drawing info */ tip_bg = GetSysColor(COLOR_INFOBK); tip_text = GetSysColor(COLOR_INFOTEXT); memset(&nci, 0, sizeof(NONCLIENTMETRICS)); nci.cbSize = sizeof(NONCLIENTMETRICS); SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &nci, 0); tip_font = CreateFontIndirect(&nci.lfStatusFont); } /* Generate the tip text */ sprintf(str, "%dx%d", cx, cy); if (!tip_wnd) { HDC hdc; SIZE sz; RECT wr; int ix, iy; /* calculate the tip's size */ hdc = CreateCompatibleDC(NULL); GetTextExtentPoint32(hdc, str, _tcslen(str), &sz); DeleteDC(hdc); GetWindowRect(src, &wr); ix = wr.left; if (ix < 16) ix = 16; iy = wr.top - sz.cy; if (iy < 16) iy = 16; /* Create the tip window */ tip_wnd = CreateWindowEx(WS_EX_TOOLWINDOW | WS_EX_TOPMOST, MAKEINTRESOURCE(tip_class), str, WS_POPUP, ix, iy, sz.cx, sz.cy, NULL, NULL, hinst, NULL); ShowWindow(tip_wnd, SW_SHOWNOACTIVATE); } else { /* Tip already exists, just set the text */ SetWindowText(tip_wnd, str); } } void EnableSizeTip(bool bEnable) { if (tip_wnd && !bEnable) { DestroyWindow(tip_wnd); tip_wnd = NULL; } tip_enabled = bEnable; } putty-0.76/windows/version.rc20000644000175000017500000000420314072266314013347 00000000000000/* * Standard Windows version information. * (For inclusion in other .rc files with appropriate macro definitions.) * * This file has the more or less arbitrary extension '.rc2' to avoid * IDEs taking it to be a top-level resource script in its own right * (which has been known to happen if the extension was '.rc'), and * also to avoid the resource compiler ignoring everything included * from it (which happens if the extension is '.h'). */ #include "version.h" #include "licence.h" /* * The actual VERSIONINFO resource. */ VS_VERSION_INFO VERSIONINFO /* (None of this "fixed" info appears to be trivially user-visible on * Win98SE. The binary version does show up on Win2K.) */ FILEVERSION BINARY_VERSION PRODUCTVERSION BINARY_VERSION /* version of whole suite */ FILEFLAGSMASK VS_FF_DEBUG | VS_FF_PRERELEASE | VS_FF_PRIVATEBUILD FILEFLAGS 0x0L #if defined DEBUG | VS_FF_DEBUG #endif #if defined SNAPSHOT || defined PRERELEASE | VS_FF_PRERELEASE #elif !defined RELEASE | VS_FF_PRIVATEBUILD #endif FILEOS VOS__WINDOWS32 FILETYPE VFT_APP FILESUBTYPE 0x0L /* n/a for VFT_APP */ BEGIN /* (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 #if (defined HELPVER) /* FIXME: this doesn't seem to be visible in Win7/Win10's UI. * Oh well. */ VALUE "FileVersion", TEXTVER HELPVER #else VALUE "FileVersion", TEXTVER #endif VALUE "ProductVersion", TEXTVER VALUE "LegalCopyright", "Copyright \251 " SHORT_COPYRIGHT_DETAILS "." #if (!defined SNAPSHOT) && (!defined RELEASE) && (!defined PRERELEASE) /* Only if VS_FF_PRIVATEBUILD. */ VALUE "PrivateBuild", TEXTVER /* NBI */ #endif END END BLOCK "VarFileInfo" BEGIN /* Once again -- same meanings -- apparently necessary */ VALUE "Translation", 0x809, 1200 END END putty-0.76/windows/website.url0000644000175000017500000000015014072266314013435 00000000000000[InternetShortcut] URL=https://www.chiark.greenend.org.uk/~sgtatham/putty/ putty-0.76/windows/win_res.h0000644000175000017500000000212214072266314013067 00000000000000/* * win_res.h - constants shared between win_res.rc2 and the C code. */ #ifndef PUTTY_WIN_RES_H #define PUTTY_WIN_RES_H #define IDI_MAINICON 200 #define IDI_CFGICON 201 #define IDD_MAINBOX 102 #define IDD_LOGBOX 110 #define IDD_ABOUTBOX 111 #define IDD_RECONF 112 #define IDD_LICENCEBOX 113 #define IDD_HK_ABSENT 114 #define IDD_HK_WRONG 115 #define IDD_HK_MOREINFO 116 #define IDN_LIST 1001 #define IDN_COPY 1002 #define IDA_ICON 1001 #define IDA_TEXT 1002 #define IDA_LICENCE 1003 #define IDA_WEB 1004 #define IDC_TAB 1001 #define IDC_TABSTATIC1 1002 #define IDC_TABSTATIC2 1003 #define IDC_TABLIST 1004 #define IDC_HELPBTN 1005 #define IDC_ABOUT 1006 #define IDC_HK_ICON 98 #define IDC_HK_TITLE 99 #define IDC_HK_ACCEPT 1001 #define IDC_HK_ONCE 1000 #define IDC_HK_FINGERPRINT 1002 #define IDC_HK_MOREINFO 1003 #define IDC_HKI_SHA256 1000 #define IDC_HKI_MD5 1001 #define IDC_HKI_PUBKEY 1002 #define ID_CUSTOM_CHMFILE 2000 #define TYPE_CUSTOM_CHMFILE 2000 #endif putty-0.76/windows/win_res.rc20000644000175000017500000001323114072266314013331 00000000000000/* * Windows resources shared between PuTTY and PuTTYtel, to be #include'd * after defining appropriate macros. * * Note that many of these strings mention PuTTY. Due to restrictions in * VC's handling of string concatenation, this can't easily be fixed. * It's fixed up at runtime. * * This file has the more or less arbitrary extension '.rc2' to avoid * IDEs taking it to be a top-level resource script in its own right * (which has been known to happen if the extension was '.rc'), and * also to avoid the resource compiler ignoring everything included * from it (which happens if the extension is '.h'). */ #include "win_res.h" IDI_MAINICON ICON "putty.ico" IDI_CFGICON ICON "puttycfg.ico" /* Accelerators used: clw */ IDD_ABOUTBOX DIALOG DISCARDABLE 140, 40, 270, 136 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "About PuTTY" FONT 8, "MS Shell Dlg" BEGIN DEFPUSHBUTTON "&Close", IDOK, 216, 118, 48, 14 PUSHBUTTON "View &Licence", IDA_LICENCE, 6, 118, 70, 14 PUSHBUTTON "Visit &Web Site", IDA_WEB, 140, 118, 70, 14 EDITTEXT IDA_TEXT, 10, 6, 250, 110, ES_READONLY | ES_MULTILINE | ES_CENTER, WS_EX_STATICEDGE 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, 326, 239 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "PuTTY Licence" FONT 8, "MS Shell Dlg" BEGIN DEFPUSHBUTTON "OK", IDOK, 148, 219, 44, 14 EDITTEXT IDA_TEXT, 10, 10, 306, 200, ES_READONLY | ES_MULTILINE | ES_LEFT, WS_EX_STATICEDGE END /* No accelerators used */ IDD_HK_ABSENT DIALOG DISCARDABLE 50, 50, 340, 148 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "PuTTY Security Alert" FONT 8, "MS Shell Dlg" BEGIN LTEXT "The server's host key is not cached in the registry. You have no", 100, 40, 20, 300, 8 LTEXT "guarantee that the server is the computer you think it is.", 101, 40, 28, 300, 8 LTEXT "The server's {KEYTYPE} key fingerprint is:", 102, 40, 40, 300, 8 LTEXT "If you trust this host, press ""Accept"" to add the key to {APPNAME}'s", 103, 40, 60, 300, 8 LTEXT "cache and carry on connecting.", 104, 40, 68, 300, 8 LTEXT "If you want to carry on connecting just once, without adding the key", 105, 40, 80, 300, 8 LTEXT "to the cache, press ""Connect Once"".", 106, 40, 88, 300, 8 LTEXT "If you do not trust this host, press ""Cancel"" to abandon the connection.", 107, 40, 100, 300, 8 ICON "", IDC_HK_ICON, 10, 18, 0, 0 PUSHBUTTON "Cancel", IDCANCEL, 288, 128, 40, 14 PUSHBUTTON "Accept", IDC_HK_ACCEPT, 168, 128, 40, 14 PUSHBUTTON "Connect Once", IDC_HK_ONCE, 216, 128, 64, 14 PUSHBUTTON "More info...", IDC_HK_MOREINFO, 60, 128, 64, 14 PUSHBUTTON "Help", IDHELP, 12, 128, 40, 14 EDITTEXT IDC_HK_FINGERPRINT, 40, 48, 300, 12, ES_READONLY | ES_LEFT, 0 END /* No accelerators used */ IDD_HK_WRONG DIALOG DISCARDABLE 50, 50, 340, 188 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "PuTTY Security Alert" FONT 8, "MS Shell Dlg" BEGIN LTEXT "WARNING - POTENTIAL SECURITY BREACH!", IDC_HK_TITLE, 40, 20, 300, 12 LTEXT "The server's host key does not match the one {APPNAME} has cached in", 100, 40, 36, 300, 8 LTEXT "the registry. This means that either the server administrator has", 101, 40, 44, 300, 8 LTEXT "changed the host key, or you have actually connected to another", 102, 40, 52, 300, 8 LTEXT "computer pretending to be the server.", 103, 40, 60, 300, 8 LTEXT "The new {KEYTYPE} key fingerprint is:", 104, 40, 72, 300, 8 LTEXT "If you were expecting this change and trust the new key, press", 105, 40, 92, 300, 8 LTEXT """Accept"" to update {APPNAME}'s cache and continue connecting.", 106, 40, 100, 300, 8 LTEXT "If you want to carry on connecting but without updating the cache,", 107, 40, 112, 300, 8 LTEXT "press ""Connect Once"".", 108, 40, 120, 300, 8 LTEXT "If you want to abandon the connection completely, press ""Cancel"".", 109, 40, 132, 300, 8 LTEXT "Pressing ""Cancel"" is the ONLY guaranteed safe choice.", 110, 40, 140, 300, 8 ICON "", IDC_HK_ICON, 10, 16, 0, 0 PUSHBUTTON "Cancel", IDCANCEL, 288, 168, 40, 14 PUSHBUTTON "Accept", IDC_HK_ACCEPT, 168, 168, 40, 14 PUSHBUTTON "Connect Once", IDC_HK_ONCE, 216, 168, 64, 14 PUSHBUTTON "More info...", IDC_HK_MOREINFO, 60, 168, 64, 14 PUSHBUTTON "Help", IDHELP, 12, 168, 40, 14 EDITTEXT IDC_HK_FINGERPRINT, 40, 80, 300, 12, ES_READONLY | ES_LEFT, 0 END /* Accelerators used: clw */ IDD_HK_MOREINFO DIALOG DISCARDABLE 140, 40, 400, 156 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "PuTTY: information about the server's host key" FONT 8, "MS Shell Dlg" BEGIN LTEXT "SHA256 fingerprint:", 100, 12, 12, 80, 8 EDITTEXT IDC_HKI_SHA256, 100, 10, 288, 12, ES_READONLY LTEXT "MD5 fingerprint:", 101, 12, 28, 80, 8 EDITTEXT IDC_HKI_MD5, 100, 26, 288, 12, ES_READONLY LTEXT "Full public key:", 102, 12, 44, 376, 8 EDITTEXT IDC_HKI_PUBKEY, 12, 54, 376, 64, ES_READONLY | ES_MULTILINE | ES_LEFT | ES_AUTOVSCROLL, WS_EX_STATICEDGE DEFPUSHBUTTON "&Close", IDOK, 176, 130, 48, 14 END #include "version.rc2" putty-0.76/windows/wincapi.c0000644000175000017500000000504514072266314013055 00000000000000/* * wincapi.c: implementation of wincapi.h. */ #include "putty.h" #if !defined NO_SECURITY #include "putty.h" #include "ssh.h" #include "wincapi.h" DEF_WINDOWS_FUNCTION(CryptProtectMemory); bool got_crypt(void) { static bool attempted = false; static bool successful; static HMODULE crypt; if (!attempted) { attempted = true; crypt = load_system32_dll("crypt32.dll"); successful = crypt && GET_WINDOWS_FUNCTION(crypt, CryptProtectMemory); } return successful; } char *capi_obfuscate_string(const char *realname) { char *cryptdata; int cryptlen; 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. */ { ssh_hash *h = ssh_hash_new(&ssh_sha256); put_string(h, cryptdata, cryptlen); ssh_hash_final(h, 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); } #endif /* !defined NO_SECURITY */ putty-0.76/windows/wincapi.h0000644000175000017500000000216214072266314013057 00000000000000/* * wincapi.h: Windows Crypto API functions defined in wincapi.c that * use the crypt32 library. Also centralises the machinery for * dynamically loading that library, and our own functions using that * in turn. */ #if !defined NO_SECURITY DECL_WINDOWS_FUNCTION(extern, BOOL, CryptProtectMemory, (LPVOID,DWORD,DWORD)); bool got_crypt(void); /* * Function to obfuscate an input string into something usable as a * pathname for a Windows named pipe. Uses CryptProtectMemory to make * the obfuscation depend on a key Windows stores for the owning user, * and then hashes the string as well to make it have a manageable * length and be composed of filename-legal characters. * * Rationale: 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: in particular, if we used unobfuscated * names for the connection-sharing pipe names, it would permit one * user to know what username@host another user is SSHing to. * * The returned string is dynamically allocated. */ char *capi_obfuscate_string(const char *realname); #endif putty-0.76/windows/wincfg.c0000644000175000017500000004026014072266314012676 00000000000000/* * wincfg.c - the Windows-specific parts of the PuTTY configuration * box. */ #include #include #include "putty.h" #include "dialog.h" #include "storage.h" static void about_handler(union control *ctrl, dlgparam *dlg, void *data, int event) { HWND *hwndp = (HWND *)ctrl->generic.context.p; if (event == EVENT_ACTION) { modal_about_box(*hwndp); } } static void help_handler(union control *ctrl, dlgparam *dlg, void *data, int event) { HWND *hwndp = (HWND *)ctrl->generic.context.p; if (event == EVENT_ACTION) { show_help(*hwndp); } } static void variable_pitch_handler(union control *ctrl, dlgparam *dlg, void *data, int event) { if (event == EVENT_REFRESH) { dlg_checkbox_set(ctrl, dlg, !dlg_get_fixed_pitch_flag(dlg)); } else if (event == EVENT_VALCHANGE) { dlg_set_fixed_pitch_flag(dlg, !dlg_checkbox_get(ctrl, dlg)); } } void win_setup_config_box(struct controlbox *b, HWND *hwndp, bool has_help, bool midsession, int protocol) { const struct BackendVtable *backvt; bool resize_forbidden = false; struct controlset *s; union control *c; char *str; if (!midsession) { /* * Add the About and Help buttons to the standard panel. */ s = ctrl_getset(b, "", "", ""); c = ctrl_pushbutton(s, "About", 'a', HELPCTX(no_help), about_handler, P(hwndp)); c->generic.column = 0; if (has_help) { c = ctrl_pushbutton(s, "Help", 'h', HELPCTX(no_help), help_handler, P(hwndp)); c->generic.column = 1; } } /* * 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, (s->ncontrols-i-2)*sizeof(union control *)); s->ctrls[i+1] = c; } break; } } } /* * 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. * * Note that although this _looks_ like a hideous hack, it's * actually all above board. The well-defined interface to the * per-platform dialog box code is the _data structures_ `union * control', `struct controlset' and so on; so code like this * that reaches into those data structures and changes bits of * them is perfectly legitimate and crosses no boundaries. All * the ctrl_* routines that create most of the controls are * convenient shortcuts provided on the cross-platform side of * the interface, and template creation code is under no actual * obligation to use them. */ 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"); c->radio.buttondata = sresize(c->radio.buttondata, c->radio.nbuttons, intorptr); c->radio.buttondata[c->radio.nbuttons-1] = I(BELL_WAVEFILE); c->radio.buttondata[c->radio.nbuttons-2] = I(BELL_PCSPEAKER); if (c->radio.shortcuts) { c->radio.shortcuts = sresize(c->radio.shortcuts, c->radio.nbuttons, char); c->radio.shortcuts[c->radio.nbuttons-1] = NO_SHORTCUT; c->radio.shortcuts[c->radio.nbuttons-2] = NO_SHORTCUT; } 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"); ctrl_text(s, "(Codepages supported by Windows but not listed here, " "such as CP866 on many systems, can be entered manually)", HELPCTX(translation_codepage)); /* * Windows has the weird OEM font mode, which gives us some * additional options when working with line-drawing * characters. */ str = dupprintf("Adjust how %s displays line drawing characters", appname); 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"); c->radio.buttons[c->radio.nbuttons-1] = dupstr("Use font in OEM mode only"); c->radio.buttondata = sresize(c->radio.buttondata, c->radio.nbuttons, intorptr); c->radio.buttondata[c->radio.nbuttons-3] = I(VT_XWINDOWS); c->radio.buttondata[c->radio.nbuttons-2] = I(VT_OEMANSI); c->radio.buttondata[c->radio.nbuttons-1] = I(VT_OEMONLY); if (!c->radio.shortcuts) { int j; c->radio.shortcuts = snewn(c->radio.nbuttons, char); for (j = 0; j < c->radio.nbuttons; j++) c->radio.shortcuts[j] = NO_SHORTCUT; } else { c->radio.shortcuts = sresize(c->radio.shortcuts, c->radio.nbuttons, char); } c->radio.shortcuts[c->radio.nbuttons-3] = 'x'; c->radio.shortcuts[c->radio.nbuttons-2] = 'b'; c->radio.shortcuts[c->radio.nbuttons-1] = 'e'; break; } } } /* * RTF paste is Windows-specific. */ s = ctrl_getset(b, "Window/Selection/Copy", "format", "Formatting of copied characters"); ctrl_checkbox(s, "Copy to clipboard in RTF as well as plain text", 'f', HELPCTX(copy_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. */ backvt = backend_vt_from_proto(protocol); if (backvt) resize_forbidden = (backvt->flags & BACKEND_RESIZE_FORBIDDEN); if (!midsession || !resize_forbidden) { 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; } } } /* * $XAUTHORITY is not reliable on Windows, so we provide a * means to override it. */ if (!midsession && backend_vt_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)); } } putty-0.76/windows/wincliloop.c0000644000175000017500000001114614072266314013601 00000000000000#include "putty.h" void cli_main_loop(cliloop_pre_t pre, cliloop_post_t post, void *ctx) { SOCKET *sklist = NULL; size_t skcount = 0, sksize = 0; unsigned long now, next, then; now = GETTICKCOUNT(); while (true) { int nhandles; HANDLE *handles; DWORD n; DWORD ticks; const HANDLE *extra_handles = NULL; size_t n_extra_handles = 0; if (!pre(ctx, &extra_handles, &n_extra_handles)) break; if (toplevel_callback_pending()) { ticks = 0; next = now; } else if (run_timers(now, &next)) { then = now; now = GETTICKCOUNT(); if (now - then > next - then) ticks = 0; else ticks = next - now; } else { ticks = INFINITE; /* no need to initialise next here because we can never * get WAIT_TIMEOUT */ } handles = handle_get_events(&nhandles); size_t winselcli_index = -(size_t)1; size_t extra_base = nhandles; if (winselcli_event != INVALID_HANDLE_VALUE) { winselcli_index = extra_base++; handles = sresize(handles, extra_base, HANDLE); handles[winselcli_index] = winselcli_event; } size_t total_handles = extra_base + n_extra_handles; handles = sresize(handles, total_handles, HANDLE); for (size_t i = 0; i < n_extra_handles; i++) handles[extra_base + i] = extra_handles[i]; n = WaitForMultipleObjects(total_handles, handles, false, ticks); size_t extra_handle_index = n_extra_handles; if ((unsigned)(n - WAIT_OBJECT_0) < (unsigned)nhandles) { handle_got_event(handles[n - WAIT_OBJECT_0]); } else if (winselcli_event != INVALID_HANDLE_VALUE && n == WAIT_OBJECT_0 + winselcli_index) { WSANETWORKEVENTS things; SOCKET socket; int i, socketstate; /* * We must not call select_result() for any socket * until we have finished enumerating within the tree. * This is because select_result() may close the socket * and modify the tree. */ /* Count the active sockets. */ i = 0; for (socket = first_socket(&socketstate); socket != INVALID_SOCKET; socket = next_socket(&socketstate)) i++; /* Expand the buffer if necessary. */ sgrowarray(sklist, sksize, i); /* Retrieve the sockets into sklist. */ skcount = 0; for (socket = first_socket(&socketstate); socket != INVALID_SOCKET; socket = next_socket(&socketstate)) { sklist[skcount++] = socket; } /* Now we're done enumerating; go through the list. */ for (i = 0; i < skcount; i++) { WPARAM wp; socket = sklist[i]; wp = (WPARAM) socket; if (!p_WSAEnumNetworkEvents(socket, NULL, &things)) { static const struct { int bit, mask; } eventtypes[] = { {FD_CONNECT_BIT, FD_CONNECT}, {FD_READ_BIT, FD_READ}, {FD_CLOSE_BIT, FD_CLOSE}, {FD_OOB_BIT, FD_OOB}, {FD_WRITE_BIT, FD_WRITE}, {FD_ACCEPT_BIT, FD_ACCEPT}, }; int e; noise_ultralight(NOISE_SOURCE_IOID, socket); for (e = 0; e < lenof(eventtypes); e++) if (things.lNetworkEvents & eventtypes[e].mask) { LPARAM lp; int err = things.iErrorCode[eventtypes[e].bit]; lp = WSAMAKESELECTREPLY(eventtypes[e].mask, err); select_result(wp, lp); } } } } else if (n >= WAIT_OBJECT_0 + extra_base && n < WAIT_OBJECT_0 + extra_base + n_extra_handles) { extra_handle_index = n - (WAIT_OBJECT_0 + extra_base); } run_toplevel_callbacks(); if (n == WAIT_TIMEOUT) { now = next; } else { now = GETTICKCOUNT(); } sfree(handles); if (!post(ctx, extra_handle_index)) break; } sfree(sklist); } bool cliloop_null_pre(void *vctx, const HANDLE **eh, size_t *neh) { return true; } bool cliloop_null_post(void *vctx, size_t ehi) { return true; } putty-0.76/windows/wincons.c0000644000175000017500000003325114072266314013103 00000000000000/* * wincons.c - various interactive-prompt routines shared between * the Windows console PuTTY tools */ #include #include #include "putty.h" #include "storage.h" #include "ssh.h" #include "console.h" void cleanup_exit(int code) { /* * Clean up. */ sk_cleanup(); random_save_seed(); exit(code); } void console_print_error_msg(const char *prefix, const char *msg) { fputs(prefix, stderr); fputs(": ", stderr); fputs(msg, stderr); fputc('\n', stderr); fflush(stderr); } int console_verify_ssh_host_key( Seat *seat, const char *host, int port, const char *keytype, char *keystr, const char *keydisp, char **fingerprints, void (*callback)(void *ctx, int result), void *ctx) { int ret; HANDLE hin; DWORD savemode, i; const char *common_fmt, *intro, *prompt; char line[32]; /* * Verify the key against the registry. */ ret = verify_host_key(host, port, keytype, keystr); if (ret == 0) /* success - key matched OK */ return 1; if (ret == 2) { /* key was different */ common_fmt = hk_wrongmsg_common_fmt; intro = hk_wrongmsg_interactive_intro; prompt = hk_wrongmsg_interactive_prompt; } else { /* key was absent */ common_fmt = hk_absentmsg_common_fmt; intro = hk_absentmsg_interactive_intro; prompt = hk_absentmsg_interactive_prompt; } FingerprintType fptype_default = ssh2_pick_default_fingerprint(fingerprints); fprintf(stderr, common_fmt, keytype, fingerprints[fptype_default]); if (console_batch_mode) { fputs(console_abandoned_msg, stderr); return 0; } fputs(intro, stderr); fflush(stderr); while (true) { fputs(prompt, stderr); fflush(stderr); line[0] = '\0'; /* fail safe if ReadFile returns no data */ 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); SetConsoleMode(hin, savemode); if (line[0] == 'i' || line[0] == 'I') { fprintf(stderr, "Full public key:\n%s\n", keydisp); if (fingerprints[SSH_FPTYPE_SHA256]) fprintf(stderr, "SHA256 key fingerprint:\n%s\n", fingerprints[SSH_FPTYPE_SHA256]); if (fingerprints[SSH_FPTYPE_MD5]) fprintf(stderr, "MD5 key fingerprint:\n%s\n", fingerprints[SSH_FPTYPE_MD5]); } else { break; } } /* In case of misplaced reflexes from another program, also recognise 'q' * as 'abandon connection rather than trust this key' */ if (line[0] != '\0' && line[0] != '\r' && line[0] != '\n' && line[0] != 'q' && line[0] != 'Q') { if (line[0] == 'y' || line[0] == 'Y') store_host_key(host, port, keytype, keystr); return 1; } else { fputs(console_abandoned_msg, stderr); return 0; } } int console_confirm_weak_crypto_primitive( Seat *seat, const char *algtype, const char *algname, void (*callback)(void *ctx, int result), void *ctx) { HANDLE hin; DWORD savemode, i; char line[32]; fprintf(stderr, weakcrypto_msg_common_fmt, algtype, algname); if (console_batch_mode) { fputs(console_abandoned_msg, stderr); return 0; } fputs(console_continue_prompt, stderr); 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); SetConsoleMode(hin, savemode); if (line[0] == 'y' || line[0] == 'Y') { return 1; } else { fputs(console_abandoned_msg, stderr); return 0; } } int console_confirm_weak_cached_hostkey( Seat *seat, const char *algname, const char *betteralgs, void (*callback)(void *ctx, int result), void *ctx) { HANDLE hin; DWORD savemode, i; char line[32]; fprintf(stderr, weakhk_msg_common_fmt, algname, betteralgs); if (console_batch_mode) { fputs(console_abandoned_msg, stderr); return 0; } fputs(console_continue_prompt, stderr); 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); SetConsoleMode(hin, savemode); if (line[0] == 'y' || line[0] == 'Y') { return 1; } else { fputs(console_abandoned_msg, stderr); return 0; } } bool is_interactive(void) { return is_console_handle(GetStdHandle(STD_INPUT_HANDLE)); } bool console_antispoof_prompt = true; bool console_set_trust_status(Seat *seat, bool trusted) { if (console_batch_mode || !is_interactive() || !console_antispoof_prompt) { /* * In batch mode, we don't need to worry about the server * mimicking our interactive authentication, because the user * already knows not to expect any. * * If standard input isn't connected to a terminal, likewise, * because even if the server did send a spoof authentication * prompt, the user couldn't respond to it via the terminal * anyway. * * We also vacuously return success if the user has purposely * disabled the antispoof prompt. */ return true; } return false; } /* * 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 console_askappend(LogPolicy *lp, 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" "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]; 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); SetConsoleMode(hin, savemode); if (line[0] == 'y' || line[0] == 'Y') return 2; else if (line[0] == 'n' || line[0] == 'N') return 1; else return 0; } /* * Warn about the obsolescent key file format. * * Uniquely among these functions, this one does _not_ expect a * frontend handle. This means that if PuTTY is ported to a * platform which requires frontend handles, this function will be * an anomaly. Fortunately, the problem it addresses will not have * been present on that platform, so it can plausibly be * implemented as an empty function. */ void old_keyfile_warning(void) { static const char message[] = "You are loading an SSH-2 private key which has an\n" "old version of the file format. This means your key\n" "file is not fully tamperproof. Future versions of\n" "PuTTY may stop supporting this private key format,\n" "so we recommend you convert your key to the new\n" "format.\n" "\n" "Once the key is loaded into PuTTYgen, you can perform\n" "this conversion simply by saving it again.\n"; fputs(message, stderr); } /* * Display the fingerprints of the PGP Master Keys to the user. */ void pgp_fingerprints(void) { fputs("These are the fingerprints of the PuTTY PGP Master Keys. They can\n" "be used to establish a trust path from this executable to another\n" "one. See the manual for more information.\n" "(Note: these fingerprints have nothing to do with SSH!)\n" "\n" "PuTTY Master Key as of " PGP_MASTER_KEY_YEAR " (" PGP_MASTER_KEY_DETAILS "):\n" " " PGP_MASTER_KEY_FP "\n\n" "Previous Master Key (" PGP_PREV_MASTER_KEY_YEAR ", " PGP_PREV_MASTER_KEY_DETAILS "):\n" " " PGP_PREV_MASTER_KEY_FP "\n", stdout); } void console_logging_error(LogPolicy *lp, const char *string) { /* Ordinary Event Log entries are displayed in the same way as * logging errors, but only in verbose mode */ fprintf(stderr, "%s\n", string); fflush(stderr); } void console_eventlog(LogPolicy *lp, const char *string) { /* Ordinary Event Log entries are displayed in the same way as * logging errors, but only in verbose mode */ if (lp_verbose(lp)) console_logging_error(lp, string); } StripCtrlChars *console_stripctrl_new( Seat *seat, BinarySink *bs_out, SeatInteractionContext sic) { return stripctrl_new(bs_out, false, 0); } static void console_write(HANDLE hout, ptrlen data) { DWORD dummy; WriteFile(hout, data.ptr, data.len, &dummy, NULL); } int console_get_userpass_input(prompts_t *p) { HANDLE hin = INVALID_HANDLE_VALUE, hout = INVALID_HANDLE_VALUE; size_t curr_prompt; /* * 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. */ if (p->n_prompts) { if (console_batch_mode) return 0; hin = GetStdHandle(STD_INPUT_HANDLE); if (hin == INVALID_HANDLE_VALUE) { fprintf(stderr, "Cannot get standard input handle\n"); cleanup_exit(1); } } /* * And if we have anything to print, we need standard output. */ if ((p->name_reqd && p->name) || p->instruction || p->n_prompts) { hout = GetStdHandle(STD_OUTPUT_HANDLE); if (hout == INVALID_HANDLE_VALUE) { fprintf(stderr, "Cannot get standard output handle\n"); cleanup_exit(1); } } /* * Preamble. */ /* We only print the `name' caption if we have to... */ if (p->name_reqd && p->name) { ptrlen plname = ptrlen_from_asciz(p->name); console_write(hout, plname); if (!ptrlen_endswith(plname, PTRLEN_LITERAL("\n"), NULL)) console_write(hout, PTRLEN_LITERAL("\n")); } /* ...but we always print any `instruction'. */ if (p->instruction) { ptrlen plinst = ptrlen_from_asciz(p->instruction); console_write(hout, plinst); if (!ptrlen_endswith(plinst, PTRLEN_LITERAL("\n"), NULL)) console_write(hout, PTRLEN_LITERAL("\n")); } for (curr_prompt = 0; curr_prompt < p->n_prompts; curr_prompt++) { DWORD savemode, newmode; 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_write(hout, ptrlen_from_asciz(pr->prompt)); bool failed = false; while (1) { /* * Amount of data to try to read from the console in one * go. This isn't completely arbitrary: a user reported * that trying to read more than 31366 bytes at a time * would fail with ERROR_NOT_ENOUGH_MEMORY on Windows 7, * and Ruby's Win32 support module has evidence of a * similar workaround: * * https://github.com/ruby/ruby/blob/0aa5195262d4193d3accf3e6b9bad236238b816b/win32/win32.c#L6842 * * To keep things simple, I stick with a nice round power * of 2 rather than trying to go to the very limit of that * bug. (We're typically reading user passphrases and the * like here, so even this much is overkill really.) */ DWORD toread = 16384; size_t prev_result_len = pr->result->len; void *ptr = strbuf_append(pr->result, toread); DWORD ret = 0; if (!ReadFile(hin, ptr, toread, &ret, NULL) || ret == 0) { failed = true; break; } strbuf_shrink_to(pr->result, prev_result_len + ret); if (strbuf_chomp(pr->result, '\n')) { strbuf_chomp(pr->result, '\r'); break; } } SetConsoleMode(hin, savemode); if (!pr->echo) console_write(hout, PTRLEN_LITERAL("\r\n")); if (failed) { return 0; /* failure due to read error */ } } return 1; /* success */ } putty-0.76/windows/winctrls.c0000644000175000017500000024355414072266314013301 00000000000000/* * winctrls.c: routines to self-manage the controls in a dialog * box. */ /* * Possible TODO in new cross-platform config box stuff: * * - When lining up two controls alongside each other, I wonder if * we could conveniently arrange to centre them vertically? * Particularly ugly in the current setup is the `Add new * forwarded port:' static next to the rather taller `Remove' * button. */ #include #include #include "putty.h" #include "misc.h" #include "dialog.h" #include #define GAPBETWEEN 3 #define GAPWITHIN 1 #define GAPXBOX 7 #define GAPYBOX 4 #define DLGWIDTH 168 #define STATICHEIGHT 8 #define TITLEHEIGHT 12 #define CHECKBOXHEIGHT 8 #define RADIOHEIGHT 8 #define EDITHEIGHT 12 #define LISTHEIGHT 11 #define LISTINCREMENT 8 #define COMBOHEIGHT 12 #define PUSHBTNHEIGHT 14 #define PROGBARHEIGHT 14 DECL_WINDOWS_FUNCTION(static, void, InitCommonControls, (void)); DECL_WINDOWS_FUNCTION(static, BOOL, MakeDragList, (HWND)); DECL_WINDOWS_FUNCTION(static, int, LBItemFromPt, (HWND, POINT, BOOL)); DECL_WINDOWS_FUNCTION(static, void, DrawInsert, (HWND, HWND, int)); void init_common_controls(void) { HMODULE comctl32_module = load_system32_dll("comctl32.dll"); GET_WINDOWS_FUNCTION(comctl32_module, InitCommonControls); GET_WINDOWS_FUNCTION(comctl32_module, MakeDragList); GET_WINDOWS_FUNCTION(comctl32_module, LBItemFromPt); GET_WINDOWS_FUNCTION(comctl32_module, DrawInsert); p_InitCommonControls(); } void ctlposinit(struct ctlpos *cp, HWND hwnd, int leftborder, int rightborder, int topborder) { RECT r, r2; cp->hwnd = hwnd; cp->font = SendMessage(hwnd, WM_GETFONT, 0, 0); cp->ypos = topborder; GetClientRect(hwnd, &r); r2.left = r2.top = 0; r2.right = 4; r2.bottom = 8; MapDialogRect(hwnd, &r2); cp->dlu4inpix = r2.right; cp->width = (r.right * 4) / (r2.right) - 2 * GAPBETWEEN; cp->xoff = leftborder; cp->width -= leftborder + rightborder; } HWND doctl(struct ctlpos *cp, RECT r, char *wclass, int wstyle, int exstyle, char *wtext, int wid) { HWND ctl; /* * Note nonstandard use of RECT. This is deliberate: by * transforming the width and height directly we arrange to * have all supposedly same-sized controls really same-sized. */ r.left += cp->xoff; MapDialogRect(cp->hwnd, &r); /* * We can pass in cp->hwnd == NULL, to indicate a dry run * without creating any actual controls. */ if (cp->hwnd) { ctl = CreateWindowEx(exstyle, wclass, wtext, wstyle, r.left, r.top, r.right, r.bottom, cp->hwnd, (HMENU)(ULONG_PTR)wid, hinst, NULL); SendMessage(ctl, WM_SETFONT, cp->font, MAKELPARAM(true, 0)); if (!strcmp(wclass, "LISTBOX")) { /* * Bizarre Windows bug: the list box calculates its * number of lines based on the font it has at creation * time, but sending it WM_SETFONT doesn't cause it to * recalculate. So now, _after_ we've sent it * WM_SETFONT, we explicitly resize it (to the same * size it was already!) to force it to reconsider. */ SetWindowPos(ctl, NULL, 0, 0, r.right, r.bottom, SWP_NOACTIVATE | SWP_NOCOPYBITS | SWP_NOMOVE | SWP_NOZORDER); } } else ctl = NULL; return ctl; } /* * A title bar across the top of a sub-dialog. */ void bartitle(struct ctlpos *cp, char *name, int id) { RECT r; r.left = GAPBETWEEN; r.right = cp->width; r.top = cp->ypos; r.bottom = STATICHEIGHT; cp->ypos += r.bottom + GAPBETWEEN; doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, name, id); } /* * Begin a grouping box, with or without a group title. */ void beginbox(struct ctlpos *cp, char *name, int idbox) { cp->boxystart = cp->ypos; if (!name) cp->boxystart -= STATICHEIGHT / 2; if (name) cp->ypos += STATICHEIGHT; cp->ypos += GAPYBOX; cp->width -= 2 * GAPXBOX; cp->xoff += GAPXBOX; cp->boxid = idbox; cp->boxtext = name; } /* * End a grouping box. */ void endbox(struct ctlpos *cp) { RECT r; cp->xoff -= GAPXBOX; cp->width += 2 * GAPXBOX; cp->ypos += GAPYBOX - GAPBETWEEN; r.left = GAPBETWEEN; r.right = cp->width; r.top = cp->boxystart; r.bottom = cp->ypos - cp->boxystart; doctl(cp, r, "BUTTON", BS_GROUPBOX | WS_CHILD | WS_VISIBLE, 0, cp->boxtext ? cp->boxtext : "", cp->boxid); cp->ypos += GAPYBOX; } /* * A static line, followed by a full-width edit box. */ void editboxfw(struct ctlpos *cp, bool password, char *text, int staticid, int editid) { RECT r; r.left = GAPBETWEEN; r.right = cp->width; if (text) { r.top = cp->ypos; r.bottom = STATICHEIGHT; doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, text, staticid); cp->ypos += STATICHEIGHT + GAPWITHIN; } r.top = cp->ypos; r.bottom = EDITHEIGHT; doctl(cp, r, "EDIT", WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL | (password ? ES_PASSWORD : 0), WS_EX_CLIENTEDGE, "", editid); cp->ypos += EDITHEIGHT + GAPBETWEEN; } /* * A static line, followed by a full-width combo box. */ void combobox(struct ctlpos *cp, char *text, int staticid, int listid) { RECT r; r.left = GAPBETWEEN; r.right = cp->width; if (text) { r.top = cp->ypos; r.bottom = STATICHEIGHT; doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, text, staticid); cp->ypos += STATICHEIGHT + GAPWITHIN; } r.top = cp->ypos; r.bottom = COMBOHEIGHT * 10; doctl(cp, r, "COMBOBOX", WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | CBS_DROPDOWN | CBS_HASSTRINGS, WS_EX_CLIENTEDGE, "", listid); cp->ypos += COMBOHEIGHT + GAPBETWEEN; } struct radio { char *text; int id; }; static void radioline_common(struct ctlpos *cp, char *text, int id, int nacross, struct radio *buttons, int nbuttons) { RECT r; int group; int i; int j; r.left = GAPBETWEEN; r.top = cp->ypos; if (text) { r.right = cp->width; r.bottom = STATICHEIGHT; cp->ypos += r.bottom + GAPWITHIN; doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, text, id); } else { r.right = r.bottom = 0; } group = WS_GROUP; i = 0; for (j = 0; j < nbuttons; j++) { char *btext = buttons[j].text; int bid = buttons[j].id; if (i == nacross) { cp->ypos += r.bottom + (nacross > 1 ? GAPBETWEEN : GAPWITHIN); i = 0; } r.left = GAPBETWEEN + i * (cp->width + GAPBETWEEN) / nacross; if (j < nbuttons-1) r.right = (i + 1) * (cp->width + GAPBETWEEN) / nacross - r.left; else r.right = cp->width - r.left; r.top = cp->ypos; r.bottom = RADIOHEIGHT; doctl(cp, r, "BUTTON", BS_NOTIFY | BS_AUTORADIOBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP | group, 0, btext, bid); group = 0; i++; } cp->ypos += r.bottom + GAPBETWEEN; } /* * A set of radio buttons on the same line, with a static above * them. `nacross' dictates how many parts the line is divided into * (you might want this not to equal the number of buttons if you * needed to line up some 2s and some 3s to look good in the same * panel). * * There's a bit of a hack in here to ensure that if nacross * exceeds the actual number of buttons, the rightmost button * really does get all the space right to the edge of the line, so * you can do things like * * (*) Button1 (*) Button2 (*) ButtonWithReallyLongTitle */ void radioline(struct ctlpos *cp, char *text, int id, int nacross, ...) { va_list ap; struct radio *buttons; int i, nbuttons; va_start(ap, nacross); nbuttons = 0; while (1) { char *btext = va_arg(ap, char *); if (!btext) break; (void) va_arg(ap, int); /* id */ nbuttons++; } va_end(ap); buttons = snewn(nbuttons, struct radio); va_start(ap, nacross); for (i = 0; i < nbuttons; i++) { buttons[i].text = va_arg(ap, char *); buttons[i].id = va_arg(ap, int); } va_end(ap); radioline_common(cp, text, id, nacross, buttons, nbuttons); sfree(buttons); } /* * A set of radio buttons on the same line, without a static above * them. Otherwise just like radioline. */ void bareradioline(struct ctlpos *cp, int nacross, ...) { va_list ap; struct radio *buttons; int i, nbuttons; va_start(ap, nacross); nbuttons = 0; while (1) { char *btext = va_arg(ap, char *); if (!btext) break; (void) va_arg(ap, int); /* id */ nbuttons++; } va_end(ap); buttons = snewn(nbuttons, struct radio); va_start(ap, nacross); for (i = 0; i < nbuttons; i++) { buttons[i].text = va_arg(ap, char *); buttons[i].id = va_arg(ap, int); } va_end(ap); radioline_common(cp, NULL, 0, nacross, buttons, nbuttons); sfree(buttons); } /* * A set of radio buttons on multiple lines, with a static above * them. */ void radiobig(struct ctlpos *cp, char *text, int id, ...) { va_list ap; struct radio *buttons; int i, nbuttons; va_start(ap, id); nbuttons = 0; while (1) { char *btext = va_arg(ap, char *); if (!btext) break; (void) va_arg(ap, int); /* id */ nbuttons++; } va_end(ap); buttons = snewn(nbuttons, struct radio); va_start(ap, id); for (i = 0; i < nbuttons; i++) { buttons[i].text = va_arg(ap, char *); buttons[i].id = va_arg(ap, int); } va_end(ap); radioline_common(cp, text, id, 1, buttons, nbuttons); sfree(buttons); } /* * A single standalone checkbox. */ void checkbox(struct ctlpos *cp, char *text, int id) { RECT r; r.left = GAPBETWEEN; r.top = cp->ypos; r.right = cp->width; r.bottom = CHECKBOXHEIGHT; cp->ypos += r.bottom + GAPBETWEEN; doctl(cp, r, "BUTTON", BS_NOTIFY | BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 0, text, id); } /* * Wrap a piece of text for a static text control. Returns the * wrapped text (a malloc'ed string containing \ns), and also * returns the number of lines required. */ char *staticwrap(struct ctlpos *cp, HWND hwnd, char *text, int *lines) { HDC hdc = GetDC(hwnd); int width, nlines, j; INT *pwidths, nfit; SIZE size; char *ret, *p, *q; RECT r; HFONT oldfont, newfont; ret = snewn(1+strlen(text), char); p = text; q = ret; pwidths = snewn(1+strlen(text), INT); /* * Work out the width the text will need to fit in, by doing * the same adjustment that the `statictext' function itself * will perform. */ SetMapMode(hdc, MM_TEXT); /* ensure logical units == pixels */ r.left = r.top = r.bottom = 0; r.right = cp->width; MapDialogRect(hwnd, &r); width = r.right; nlines = 1; /* * We must select the correct font into the HDC before calling * GetTextExtent*, or silly things will happen. */ newfont = (HFONT)SendMessage(hwnd, WM_GETFONT, 0, 0); oldfont = SelectObject(hdc, newfont); while (*p) { if (!GetTextExtentExPoint(hdc, p, strlen(p), width, &nfit, pwidths, &size) || (size_t)nfit >= strlen(p)) { /* * Either GetTextExtentExPoint returned failure, or the * whole of the rest of the text fits on this line. * Either way, we stop wrapping, copy the remainder of * the input string unchanged to the output, and leave. */ strcpy(q, p); break; } /* * Now we search backwards along the string from `nfit', * looking for a space at which to break the line. If we * don't find one at all, that's fine - we'll just break * the line at `nfit'. */ for (j = nfit; j > 0; j--) { if (isspace((unsigned char)p[j])) { nfit = j; break; } } strncpy(q, p, nfit); q[nfit] = '\n'; q += nfit+1; p += nfit; while (*p && isspace((unsigned char)*p)) p++; nlines++; } 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) { RECT r; r.left = GAPBETWEEN; r.top = cp->ypos; r.right = cp->width; r.bottom = STATICHEIGHT * lines; cp->ypos += r.bottom + GAPBETWEEN; doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE | SS_LEFTNOWORDWRAP, 0, text, id); } /* * An owner-drawn static text control for a panel title. */ void paneltitle(struct ctlpos *cp, int id) { RECT r; r.left = GAPBETWEEN; r.top = cp->ypos; r.right = cp->width; r.bottom = TITLEHEIGHT; cp->ypos += r.bottom + GAPBETWEEN; doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE | SS_OWNERDRAW, 0, NULL, id); } /* * A button on the right hand side, with a static to its left. */ void staticbtn(struct ctlpos *cp, char *stext, int sid, char *btext, int bid) { const int height = (PUSHBTNHEIGHT > STATICHEIGHT ? PUSHBTNHEIGHT : STATICHEIGHT); RECT r; int lwid, rwid, rpos; rpos = GAPBETWEEN + 3 * (cp->width + GAPBETWEEN) / 4; lwid = rpos - 2 * GAPBETWEEN; rwid = cp->width + GAPBETWEEN - rpos; r.left = GAPBETWEEN; r.top = cp->ypos + (height - STATICHEIGHT) / 2; r.right = lwid; r.bottom = STATICHEIGHT; doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid); r.left = rpos; r.top = cp->ypos + (height - PUSHBTNHEIGHT) / 2; r.right = rwid; r.bottom = PUSHBTNHEIGHT; doctl(cp, r, "BUTTON", BS_NOTIFY | WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON, 0, btext, bid); cp->ypos += height + GAPBETWEEN; } /* * A simple push button. */ void button(struct ctlpos *cp, char *btext, int bid, bool defbtn) { RECT r; r.left = GAPBETWEEN; r.top = cp->ypos; r.right = cp->width; r.bottom = PUSHBTNHEIGHT; /* Q67655: the _dialog box_ must know which button is default * as well as the button itself knowing */ if (defbtn && cp->hwnd) SendMessage(cp->hwnd, DM_SETDEFID, bid, 0); doctl(cp, r, "BUTTON", BS_NOTIFY | WS_CHILD | WS_VISIBLE | WS_TABSTOP | (defbtn ? BS_DEFPUSHBUTTON : 0) | BS_PUSHBUTTON, 0, btext, bid); cp->ypos += PUSHBTNHEIGHT + GAPBETWEEN; } /* * Like staticbtn, but two buttons. */ void static2btn(struct ctlpos *cp, char *stext, int sid, char *btext1, int bid1, char *btext2, int bid2) { const int height = (PUSHBTNHEIGHT > STATICHEIGHT ? PUSHBTNHEIGHT : STATICHEIGHT); RECT r; int lwid, rwid1, rwid2, rpos1, rpos2; rpos1 = GAPBETWEEN + (cp->width + GAPBETWEEN) / 2; rpos2 = GAPBETWEEN + 3 * (cp->width + GAPBETWEEN) / 4; lwid = rpos1 - 2 * GAPBETWEEN; rwid1 = rpos2 - rpos1 - GAPBETWEEN; rwid2 = cp->width + GAPBETWEEN - rpos2; r.left = GAPBETWEEN; r.top = cp->ypos + (height - STATICHEIGHT) / 2; r.right = lwid; r.bottom = STATICHEIGHT; doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid); r.left = rpos1; r.top = cp->ypos + (height - PUSHBTNHEIGHT) / 2; r.right = rwid1; r.bottom = PUSHBTNHEIGHT; doctl(cp, r, "BUTTON", BS_NOTIFY | WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON, 0, btext1, bid1); r.left = rpos2; r.top = cp->ypos + (height - PUSHBTNHEIGHT) / 2; r.right = rwid2; r.bottom = PUSHBTNHEIGHT; doctl(cp, r, "BUTTON", BS_NOTIFY | WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON, 0, btext2, bid2); cp->ypos += height + GAPBETWEEN; } /* * An edit control on the right hand side, with a static to its left. */ static void staticedit_internal(struct ctlpos *cp, char *stext, int sid, int eid, int percentedit, int style) { const int height = (EDITHEIGHT > STATICHEIGHT ? EDITHEIGHT : STATICHEIGHT); RECT r; int lwid, rwid, rpos; rpos = GAPBETWEEN + (100 - percentedit) * (cp->width + GAPBETWEEN) / 100; lwid = rpos - 2 * GAPBETWEEN; rwid = cp->width + GAPBETWEEN - rpos; r.left = GAPBETWEEN; r.top = cp->ypos + (height - STATICHEIGHT) / 2; r.right = lwid; r.bottom = STATICHEIGHT; doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid); r.left = rpos; r.top = cp->ypos + (height - EDITHEIGHT) / 2; r.right = rwid; r.bottom = EDITHEIGHT; doctl(cp, r, "EDIT", WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL | style, WS_EX_CLIENTEDGE, "", eid); cp->ypos += height + GAPBETWEEN; } void staticedit(struct ctlpos *cp, char *stext, int sid, int eid, int percentedit) { staticedit_internal(cp, stext, sid, eid, percentedit, 0); } void staticpassedit(struct ctlpos *cp, char *stext, int sid, int eid, int percentedit) { staticedit_internal(cp, stext, sid, eid, percentedit, ES_PASSWORD); } /* * A drop-down list box on the right hand side, with a static to * its left. */ void staticddl(struct ctlpos *cp, char *stext, int sid, int lid, int percentlist) { const int height = (COMBOHEIGHT > STATICHEIGHT ? COMBOHEIGHT : STATICHEIGHT); RECT r; int lwid, rwid, rpos; rpos = GAPBETWEEN + (100 - percentlist) * (cp->width + GAPBETWEEN) / 100; lwid = rpos - 2 * GAPBETWEEN; rwid = cp->width + GAPBETWEEN - rpos; r.left = GAPBETWEEN; r.top = cp->ypos + (height - STATICHEIGHT) / 2; r.right = lwid; r.bottom = STATICHEIGHT; doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid); r.left = rpos; r.top = cp->ypos + (height - EDITHEIGHT) / 2; r.right = rwid; r.bottom = COMBOHEIGHT*4; doctl(cp, r, "COMBOBOX", WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_CLIENTEDGE, "", lid); cp->ypos += height + GAPBETWEEN; } /* * A combo box on the right hand side, with a static to its left. */ void staticcombo(struct ctlpos *cp, char *stext, int sid, int lid, int percentlist) { const int height = (COMBOHEIGHT > STATICHEIGHT ? COMBOHEIGHT : STATICHEIGHT); RECT r; int lwid, rwid, rpos; rpos = GAPBETWEEN + (100 - percentlist) * (cp->width + GAPBETWEEN) / 100; lwid = rpos - 2 * GAPBETWEEN; rwid = cp->width + GAPBETWEEN - rpos; r.left = GAPBETWEEN; r.top = cp->ypos + (height - STATICHEIGHT) / 2; r.right = lwid; r.bottom = STATICHEIGHT; doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid); r.left = rpos; r.top = cp->ypos + (height - EDITHEIGHT) / 2; r.right = rwid; r.bottom = COMBOHEIGHT*10; doctl(cp, r, "COMBOBOX", WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | CBS_DROPDOWN | CBS_HASSTRINGS, WS_EX_CLIENTEDGE, "", lid); cp->ypos += height + GAPBETWEEN; } /* * A static, with a full-width drop-down list box below it. */ void staticddlbig(struct ctlpos *cp, char *stext, int sid, int lid) { RECT r; if (stext) { r.left = GAPBETWEEN; r.top = cp->ypos; r.right = cp->width; r.bottom = STATICHEIGHT; doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid); cp->ypos += STATICHEIGHT; } r.left = GAPBETWEEN; r.top = cp->ypos; r.right = cp->width; r.bottom = COMBOHEIGHT*4; doctl(cp, r, "COMBOBOX", WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_CLIENTEDGE, "", lid); cp->ypos += COMBOHEIGHT + GAPBETWEEN; } /* * A big multiline edit control with a static labelling it. */ void bigeditctrl(struct ctlpos *cp, char *stext, int sid, int eid, int lines) { RECT r; if (stext) { r.left = GAPBETWEEN; r.top = cp->ypos; r.right = cp->width; r.bottom = STATICHEIGHT; cp->ypos += r.bottom + GAPWITHIN; doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid); } r.left = GAPBETWEEN; r.top = cp->ypos; r.right = cp->width; r.bottom = EDITHEIGHT + (lines - 1) * STATICHEIGHT; cp->ypos += r.bottom + GAPBETWEEN; doctl(cp, r, "EDIT", WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | ES_MULTILINE, WS_EX_CLIENTEDGE, "", eid); } /* * A list box with a static labelling it. */ void listbox(struct ctlpos *cp, char *stext, int sid, int lid, int lines, bool multi) { RECT r; if (stext != NULL) { r.left = GAPBETWEEN; r.top = cp->ypos; r.right = cp->width; r.bottom = STATICHEIGHT; cp->ypos += r.bottom + GAPWITHIN; doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid); } r.left = GAPBETWEEN; r.top = cp->ypos; r.right = cp->width; r.bottom = LISTHEIGHT + (lines - 1) * LISTINCREMENT; cp->ypos += r.bottom + GAPBETWEEN; doctl(cp, r, "LISTBOX", WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | LBS_NOTIFY | LBS_HASSTRINGS | LBS_USETABSTOPS | (multi ? LBS_MULTIPLESEL : 0), WS_EX_CLIENTEDGE, "", lid); } /* * A tab-control substitute when a real tab control is unavailable. */ void ersatztab(struct ctlpos *cp, char *stext, int sid, int lid, int s2id) { const int height = (COMBOHEIGHT > STATICHEIGHT ? COMBOHEIGHT : STATICHEIGHT); RECT r; int bigwid, lwid, rwid, rpos; static const int BIGGAP = 15; static const int MEDGAP = 3; bigwid = cp->width + 2 * GAPBETWEEN - 2 * BIGGAP; cp->ypos += MEDGAP; rpos = BIGGAP + (bigwid + BIGGAP) / 2; lwid = rpos - 2 * BIGGAP; rwid = bigwid + BIGGAP - rpos; r.left = BIGGAP; r.top = cp->ypos + (height - STATICHEIGHT) / 2; r.right = lwid; r.bottom = STATICHEIGHT; doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid); r.left = rpos; r.top = cp->ypos + (height - COMBOHEIGHT) / 2; r.right = rwid; r.bottom = COMBOHEIGHT * 10; doctl(cp, r, "COMBOBOX", WS_CHILD | WS_VISIBLE | WS_TABSTOP | CBS_DROPDOWNLIST | CBS_HASSTRINGS, WS_EX_CLIENTEDGE, "", lid); cp->ypos += height + MEDGAP + GAPBETWEEN; r.left = GAPBETWEEN; r.top = cp->ypos; r.right = cp->width; r.bottom = 2; doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE | SS_ETCHEDHORZ, 0, "", s2id); } /* * A static line, followed by an edit control on the left hand side * and a button on the right. */ void editbutton(struct ctlpos *cp, char *stext, int sid, int eid, char *btext, int bid) { const int height = (EDITHEIGHT > PUSHBTNHEIGHT ? EDITHEIGHT : PUSHBTNHEIGHT); RECT r; int lwid, rwid, rpos; r.left = GAPBETWEEN; r.top = cp->ypos; r.right = cp->width; r.bottom = STATICHEIGHT; cp->ypos += r.bottom + GAPWITHIN; doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid); rpos = GAPBETWEEN + 3 * (cp->width + GAPBETWEEN) / 4; lwid = rpos - 2 * GAPBETWEEN; rwid = cp->width + GAPBETWEEN - rpos; r.left = GAPBETWEEN; r.top = cp->ypos + (height - EDITHEIGHT) / 2; r.right = lwid; r.bottom = EDITHEIGHT; doctl(cp, r, "EDIT", WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL, WS_EX_CLIENTEDGE, "", eid); r.left = rpos; r.top = cp->ypos + (height - PUSHBTNHEIGHT) / 2; r.right = rwid; r.bottom = PUSHBTNHEIGHT; doctl(cp, r, "BUTTON", BS_NOTIFY | WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON, 0, btext, bid); cp->ypos += height + GAPBETWEEN; } /* * A special control for manipulating an ordered preference list * (eg. for cipher selection). * XXX: this is a rough hack and could be improved. */ void prefslist(struct prefslist *hdl, struct ctlpos *cp, int lines, char *stext, int sid, int listid, int upbid, int dnbid) { const static int percents[] = { 5, 75, 20 }; RECT r; int xpos, percent = 0, i; int listheight = LISTHEIGHT + (lines - 1) * LISTINCREMENT; const int BTNSHEIGHT = 2*PUSHBTNHEIGHT + GAPBETWEEN; int totalheight, buttonpos; /* Squirrel away IDs. */ hdl->listid = listid; hdl->upbid = upbid; hdl->dnbid = dnbid; /* The static label. */ if (stext != NULL) { r.left = GAPBETWEEN; r.top = cp->ypos; r.right = cp->width; r.bottom = STATICHEIGHT; cp->ypos += r.bottom + GAPWITHIN; doctl(cp, r, "STATIC", WS_CHILD | WS_VISIBLE, 0, stext, sid); } if (listheight > BTNSHEIGHT) { totalheight = listheight; buttonpos = (listheight - BTNSHEIGHT) / 2; } else { totalheight = BTNSHEIGHT; buttonpos = 0; } for (i=0; i<3; i++) { int left, wid; xpos = (cp->width + GAPBETWEEN) * percent / 100; left = xpos + GAPBETWEEN; percent += percents[i]; xpos = (cp->width + GAPBETWEEN) * percent / 100; wid = xpos - left; switch (i) { case 1: { /* The drag list box. */ r.left = left; r.right = wid; r.top = cp->ypos; r.bottom = listheight; HWND ctl = doctl(cp, r, "LISTBOX", WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | LBS_HASSTRINGS | LBS_USETABSTOPS, WS_EX_CLIENTEDGE, "", listid); p_MakeDragList(ctl); break; } case 2: /* The "Up" and "Down" buttons. */ /* XXX worry about accelerators if we have more than one * prefslist on a panel */ r.left = left; r.right = wid; r.top = cp->ypos + buttonpos; r.bottom = PUSHBTNHEIGHT; doctl(cp, r, "BUTTON", BS_NOTIFY | WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON, 0, "&Up", upbid); r.left = left; r.right = wid; r.top = cp->ypos + buttonpos + PUSHBTNHEIGHT + GAPBETWEEN; r.bottom = PUSHBTNHEIGHT; doctl(cp, r, "BUTTON", BS_NOTIFY | WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON, 0, "&Down", dnbid); break; } } cp->ypos += totalheight + GAPBETWEEN; } /* * Helper function for prefslist: move item in list box. */ static void pl_moveitem(HWND hwnd, int listid, int src, int dst) { int tlen, val; char *txt; /* Get the item's data. */ tlen = SendDlgItemMessage (hwnd, listid, LB_GETTEXTLEN, src, 0); txt = snewn(tlen+1, char); SendDlgItemMessage (hwnd, listid, LB_GETTEXT, src, (LPARAM) txt); val = SendDlgItemMessage (hwnd, listid, LB_GETITEMDATA, src, 0); /* Deselect old location. */ SendDlgItemMessage (hwnd, listid, LB_SETSEL, false, src); /* Delete it at the old location. */ SendDlgItemMessage (hwnd, listid, LB_DELETESTRING, src, 0); /* Insert it at new location. */ SendDlgItemMessage (hwnd, listid, LB_INSERTSTRING, dst, (LPARAM) txt); SendDlgItemMessage (hwnd, listid, LB_SETITEMDATA, dst, (LPARAM) val); /* Set selection. */ SendDlgItemMessage (hwnd, listid, LB_SETCURSEL, dst, 0); sfree (txt); } int pl_itemfrompt(HWND hwnd, POINT cursor, bool scroll) { int ret; POINT uppoint, downpoint; int updist, downdist, upitem, downitem, i; /* * Ghastly hackery to try to figure out not which * _item_, but which _gap between items_, the user * is pointing at. We do this by first working out * which list item is under the cursor, and then * working out how far the cursor would have to * move up or down before the answer was different. * Then we put the insertion point _above_ the * current item if the upper edge is closer than * the lower edge, or _below_ it if vice versa. */ ret = p_LBItemFromPt(hwnd, cursor, scroll); if (ret == -1) return ret; ret = p_LBItemFromPt(hwnd, cursor, false); updist = downdist = 0; for (i = 1; i < 4096 && (!updist || !downdist); i++) { uppoint = downpoint = cursor; uppoint.y -= i; downpoint.y += i; upitem = p_LBItemFromPt(hwnd, uppoint, false); downitem = p_LBItemFromPt(hwnd, downpoint, false); if (!updist && upitem != ret) updist = i; if (!downdist && downitem != ret) downdist = i; } if (downdist < updist) ret++; return ret; } /* * Handler for prefslist above. * * Return value has bit 0 set if the dialog box procedure needs to * return true from handling this message; it has bit 1 set if a * change may have been made in the contents of the list. */ int handle_prefslist(struct prefslist *hdl, int *array, int maxmemb, bool is_dlmsg, HWND hwnd, WPARAM wParam, LPARAM lParam) { int i; int ret = 0; if (is_dlmsg) { if ((int)wParam == hdl->listid) { DRAGLISTINFO *dlm = (DRAGLISTINFO *)lParam; int dest = 0; /* initialise to placate gcc */ switch (dlm->uNotification) { case DL_BEGINDRAG: /* Add a dummy item to make pl_itemfrompt() work * better. * FIXME: this causes scrollbar glitches if the count of * listbox contains >= its height. */ hdl->dummyitem = SendDlgItemMessage(hwnd, hdl->listid, LB_ADDSTRING, 0, (LPARAM) ""); hdl->srcitem = p_LBItemFromPt(dlm->hWnd, dlm->ptCursor, true); hdl->dragging = false; /* XXX hack Q183115 */ SetWindowLongPtr(hwnd, DWLP_MSGRESULT, true); ret |= 1; break; case DL_CANCELDRAG: p_DrawInsert(hwnd, dlm->hWnd, -1); /* Clear arrow */ SendDlgItemMessage(hwnd, hdl->listid, LB_DELETESTRING, hdl->dummyitem, 0); hdl->dragging = false; ret |= 1; break; case DL_DRAGGING: hdl->dragging = true; dest = pl_itemfrompt(dlm->hWnd, dlm->ptCursor, true); if (dest > hdl->dummyitem) dest = hdl->dummyitem; p_DrawInsert (hwnd, dlm->hWnd, dest); if (dest >= 0) SetWindowLongPtr(hwnd, DWLP_MSGRESULT, DL_MOVECURSOR); else SetWindowLongPtr(hwnd, DWLP_MSGRESULT, DL_STOPCURSOR); ret |= 1; break; case DL_DROPPED: if (hdl->dragging) { dest = pl_itemfrompt(dlm->hWnd, dlm->ptCursor, true); if (dest > hdl->dummyitem) dest = hdl->dummyitem; p_DrawInsert (hwnd, dlm->hWnd, -1); } SendDlgItemMessage(hwnd, hdl->listid, LB_DELETESTRING, hdl->dummyitem, 0); if (hdl->dragging) { hdl->dragging = false; if (dest >= 0) { /* Correct for "missing" item. */ if (dest > hdl->srcitem) dest--; pl_moveitem(hwnd, hdl->listid, hdl->srcitem, dest); } ret |= 2; } ret |= 1; break; } } } else { if (((LOWORD(wParam) == hdl->upbid) || (LOWORD(wParam) == hdl->dnbid)) && ((HIWORD(wParam) == BN_CLICKED) || (HIWORD(wParam) == BN_DOUBLECLICKED))) { /* Move an item up or down the list. */ /* Get the current selection, if any. */ int selection = SendDlgItemMessage (hwnd, hdl->listid, LB_GETCURSEL, 0, 0); if (selection == LB_ERR) { MessageBeep(0); } else { int nitems; /* Get the total number of items. */ nitems = SendDlgItemMessage (hwnd, hdl->listid, LB_GETCOUNT, 0, 0); /* Should we do anything? */ if (LOWORD(wParam) == hdl->upbid && (selection > 0)) pl_moveitem(hwnd, hdl->listid, selection, selection - 1); else if (LOWORD(wParam) == hdl->dnbid && (selection < nitems - 1)) pl_moveitem(hwnd, hdl->listid, selection, selection + 1); ret |= 2; } } } if (array) { /* Update array to match the list box. */ for (i=0; i < maxmemb; i++) array[i] = SendDlgItemMessage (hwnd, hdl->listid, LB_GETITEMDATA, i, 0); } return ret; } /* * A progress bar (from Common Controls). We like our progress bars * to be smooth and unbroken, without those ugly divisions; some * older compilers may not support that, but that's life. */ void progressbar(struct ctlpos *cp, int id) { RECT r; r.left = GAPBETWEEN; r.top = cp->ypos; r.right = cp->width; r.bottom = PROGBARHEIGHT; cp->ypos += r.bottom + GAPBETWEEN; doctl(cp, r, PROGRESS_CLASS, WS_CHILD | WS_VISIBLE #ifdef PBS_SMOOTH | PBS_SMOOTH #endif , WS_EX_CLIENTEDGE, "", id); } /* ---------------------------------------------------------------------- * Platform-specific side of portable dialog-box mechanism. */ /* * This function takes a string, escapes all the ampersands, and * places a single (unescaped) ampersand in front of the first * occurrence of the given shortcut character (which may be * NO_SHORTCUT). * * Return value is a malloc'ed copy of the processed version of the * string. */ static char *shortcut_escape(const char *text, char shortcut) { char *ret; char const *p; char *q; if (!text) return NULL; /* sfree won't choke on this */ ret = snewn(2*strlen(text)+1, char); /* size potentially doubles! */ shortcut = tolower((unsigned char)shortcut); p = text; q = ret; while (*p) { if (shortcut != NO_SHORTCUT && tolower((unsigned char)*p) == shortcut) { *q++ = '&'; shortcut = NO_SHORTCUT; /* stop it happening twice */ } else if (*p == '&') { *q++ = '&'; } *q++ = *p++; } *q = '\0'; return ret; } void winctrl_add_shortcuts(struct dlgparam *dp, struct winctrl *c) { int i; for (i = 0; i < lenof(c->shortcuts); i++) if (c->shortcuts[i] != NO_SHORTCUT) { unsigned char s = tolower((unsigned char)c->shortcuts[i]); assert(!dp->shortcuts[s]); dp->shortcuts[s] = true; } } void winctrl_rem_shortcuts(struct dlgparam *dp, struct winctrl *c) { int i; for (i = 0; i < lenof(c->shortcuts); i++) if (c->shortcuts[i] != NO_SHORTCUT) { unsigned char s = tolower((unsigned char)c->shortcuts[i]); assert(dp->shortcuts[s]); dp->shortcuts[s] = false; } } static int winctrl_cmp_byctrl(void *av, void *bv) { struct winctrl *a = (struct winctrl *)av; struct winctrl *b = (struct winctrl *)bv; if (a->ctrl < b->ctrl) return -1; else if (a->ctrl > b->ctrl) return +1; else return 0; } static int winctrl_cmp_byid(void *av, void *bv) { struct winctrl *a = (struct winctrl *)av; struct winctrl *b = (struct winctrl *)bv; if (a->base_id < b->base_id) return -1; else if (a->base_id > b->base_id) return +1; else return 0; } static int winctrl_cmp_byctrl_find(void *av, void *bv) { union control *a = (union control *)av; struct winctrl *b = (struct winctrl *)bv; if (a < b->ctrl) return -1; else if (a > b->ctrl) return +1; else return 0; } static int winctrl_cmp_byid_find(void *av, void *bv) { int *a = (int *)av; struct winctrl *b = (struct winctrl *)bv; if (*a < b->base_id) return -1; else if (*a >= b->base_id + b->num_ids) return +1; else return 0; } void winctrl_init(struct winctrls *wc) { wc->byctrl = newtree234(winctrl_cmp_byctrl); wc->byid = newtree234(winctrl_cmp_byid); } void winctrl_cleanup(struct winctrls *wc) { struct winctrl *c; while ((c = index234(wc->byid, 0)) != NULL) { winctrl_remove(wc, c); sfree(c->data); sfree(c); } freetree234(wc->byctrl); freetree234(wc->byid); wc->byctrl = wc->byid = NULL; } void winctrl_add(struct winctrls *wc, struct winctrl *c) { struct winctrl *ret; if (c->ctrl) { ret = add234(wc->byctrl, c); assert(ret == c); } ret = add234(wc->byid, c); assert(ret == c); } void winctrl_remove(struct winctrls *wc, struct winctrl *c) { struct winctrl *ret; ret = del234(wc->byctrl, c); ret = del234(wc->byid, c); assert(ret == c); } struct winctrl *winctrl_findbyctrl(struct winctrls *wc, union control *ctrl) { return find234(wc->byctrl, ctrl, winctrl_cmp_byctrl_find); } struct winctrl *winctrl_findbyid(struct winctrls *wc, int id) { return find234(wc->byid, &id, winctrl_cmp_byid_find); } struct winctrl *winctrl_findbyindex(struct winctrls *wc, int index) { return index234(wc->byid, index); } static void move_windows(HWND hwnd, int base_id, int num_ids, LONG dy) { if (!dy) return; for (int i = 0; i < num_ids; i++) { HWND win = GetDlgItem(hwnd, base_id + i); RECT rect; if (!GetWindowRect(win, &rect)) continue; POINT p; p.x = rect.left; p.y = rect.top + dy; if (!ScreenToClient(hwnd, &p)) continue; SetWindowPos(win, NULL, p.x, p.y, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOZORDER); } } void winctrl_layout(struct dlgparam *dp, struct winctrls *wc, struct ctlpos *cp, struct controlset *s, int *id) { struct ctlpos columns[16]; int ncols, colstart, colspan; struct ctlpos tabdelays[16]; union control *tabdelayed[16]; int ntabdelays; struct ctlpos pos; char shortcuts[MAX_SHORTCUTS_PER_CTRL]; int nshortcuts; char *escaped; int i, actual_base_id, base_id, num_ids, align_id_relative; void *data; base_id = *id; /* Start a containing box, if we have a boxname. */ if (s->boxname && *s->boxname) { struct winctrl *c = snew(struct winctrl); c->ctrl = NULL; c->base_id = c->align_id = base_id; c->num_ids = 1; c->data = NULL; memset(c->shortcuts, NO_SHORTCUT, lenof(c->shortcuts)); winctrl_add(wc, c); beginbox(cp, s->boxtitle, base_id); base_id++; } /* Draw a title, if we have one. */ if (!s->boxname && s->boxtitle) { struct winctrl *c = snew(struct winctrl); c->ctrl = NULL; c->base_id = c->align_id = base_id; c->num_ids = 1; c->data = dupstr(s->boxtitle); memset(c->shortcuts, NO_SHORTCUT, lenof(c->shortcuts)); winctrl_add(wc, c); paneltitle(cp, base_id); base_id++; } /* Initially we have just one column. */ ncols = 1; columns[0] = *cp; /* structure copy */ /* And initially, there are no pending tab-delayed controls. */ ntabdelays = 0; /* Loop over each control in the controlset. */ for (i = 0; i < s->ncontrols; i++) { union control *ctrl = s->ctrls[i]; /* * Generic processing that pertains to all control types. * At the end of this if statement, we'll have produced * `ctrl' (a pointer to the control we have to create, or * think about creating, in this iteration of the loop), * `pos' (a suitable ctlpos with which to position it), and * `c' (a winctrl structure to receive details of the * dialog IDs). Or we'll have done a `continue', if it was * CTRL_COLUMNS and doesn't require any control creation at * all. */ if (ctrl->generic.type == CTRL_COLUMNS) { assert((ctrl->columns.ncols == 1) ^ (ncols == 1)); if (ncols == 1) { /* * We're splitting into multiple columns. */ int lpercent, rpercent, lx, rx, i; ncols = ctrl->columns.ncols; assert(ncols <= lenof(columns)); for (i = 1; i < ncols; i++) columns[i] = columns[0]; /* structure copy */ lpercent = 0; for (i = 0; i < ncols; i++) { rpercent = lpercent + ctrl->columns.percentages[i]; lx = columns[i].xoff + lpercent * (columns[i].width + GAPBETWEEN) / 100; rx = columns[i].xoff + rpercent * (columns[i].width + GAPBETWEEN) / 100; columns[i].xoff = lx; columns[i].width = rx - lx - GAPBETWEEN; lpercent = rpercent; } } else { /* * We're recombining the various columns into one. */ int maxy = columns[0].ypos; int i; for (i = 1; i < ncols; i++) if (maxy < columns[i].ypos) maxy = columns[i].ypos; ncols = 1; columns[0] = *cp; /* structure copy */ columns[0].ypos = maxy; } continue; } else if (ctrl->generic.type == CTRL_TABDELAY) { int i; assert(!ctrl->generic.tabdelay); ctrl = ctrl->tabdelay.ctrl; for (i = 0; i < ntabdelays; i++) if (tabdelayed[i] == ctrl) break; assert(i < ntabdelays); /* we have to have found it */ pos = tabdelays[i]; /* structure copy */ colstart = colspan = -1; /* indicate this was tab-delayed */ } else { /* * If it wasn't one of those, it's a genuine control; * so we'll have to compute a position for it now, by * checking its column span. */ int col; colstart = COLUMN_START(ctrl->generic.column); colspan = COLUMN_SPAN(ctrl->generic.column); pos = columns[colstart]; /* structure copy */ pos.width = columns[colstart+colspan-1].width + (columns[colstart+colspan-1].xoff - columns[colstart].xoff); for (col = colstart; col < colstart+colspan; col++) if (pos.ypos < columns[col].ypos) pos.ypos = columns[col].ypos; /* * If this control is to be tabdelayed, add it to the * tabdelay list, and unset pos.hwnd to inhibit actual * control creation. */ if (ctrl->generic.tabdelay) { assert(ntabdelays < lenof(tabdelays)); tabdelays[ntabdelays] = pos; /* structure copy */ tabdelayed[ntabdelays] = ctrl; ntabdelays++; pos.hwnd = NULL; } } /* Most controls don't need anything in c->data. */ data = NULL; /* And they all start off with no shortcuts registered. */ memset(shortcuts, NO_SHORTCUT, lenof(shortcuts)); nshortcuts = 0; /* Almost all controls start at base_id. */ actual_base_id = base_id; /* For vertical alignment purposes, the most relevant control * in a group is usually the last one. But that can be * overridden occasionally. */ align_id_relative = -1; /* * Now we're ready to actually create the control, by * switching on its type. */ switch (ctrl->generic.type) { case CTRL_TEXT: { char *wrapped, *escaped; int lines; num_ids = 1; wrapped = staticwrap(&pos, cp->hwnd, ctrl->generic.label, &lines); escaped = shortcut_escape(wrapped, NO_SHORTCUT); statictext(&pos, escaped, lines, base_id); sfree(escaped); sfree(wrapped); break; } case CTRL_EDITBOX: num_ids = 2; /* static, edit */ escaped = shortcut_escape(ctrl->editbox.label, ctrl->editbox.shortcut); shortcuts[nshortcuts++] = ctrl->editbox.shortcut; if (ctrl->editbox.percentwidth == 100) { if (ctrl->editbox.has_list) combobox(&pos, escaped, base_id, base_id+1); else editboxfw(&pos, ctrl->editbox.password, escaped, base_id, base_id+1); } else { if (ctrl->editbox.has_list) { staticcombo(&pos, escaped, base_id, base_id+1, ctrl->editbox.percentwidth); } else { (ctrl->editbox.password ? staticpassedit : staticedit) (&pos, escaped, base_id, base_id+1, ctrl->editbox.percentwidth); } } sfree(escaped); break; case CTRL_RADIO: { num_ids = ctrl->radio.nbuttons + 1; /* label as well */ struct radio *buttons; int i; escaped = shortcut_escape(ctrl->radio.label, ctrl->radio.shortcut); shortcuts[nshortcuts++] = ctrl->radio.shortcut; buttons = snewn(ctrl->radio.nbuttons, struct radio); for (i = 0; i < ctrl->radio.nbuttons; i++) { buttons[i].text = shortcut_escape(ctrl->radio.buttons[i], (char)(ctrl->radio.shortcuts ? ctrl->radio.shortcuts[i] : NO_SHORTCUT)); buttons[i].id = base_id + 1 + i; if (ctrl->radio.shortcuts) { assert(nshortcuts < MAX_SHORTCUTS_PER_CTRL); shortcuts[nshortcuts++] = ctrl->radio.shortcuts[i]; } } radioline_common(&pos, escaped, base_id, ctrl->radio.ncolumns, buttons, ctrl->radio.nbuttons); for (i = 0; i < ctrl->radio.nbuttons; i++) { sfree(buttons[i].text); } sfree(buttons); sfree(escaped); break; } case CTRL_CHECKBOX: num_ids = 1; escaped = shortcut_escape(ctrl->checkbox.label, ctrl->checkbox.shortcut); shortcuts[nshortcuts++] = ctrl->checkbox.shortcut; checkbox(&pos, escaped, base_id); sfree(escaped); break; case CTRL_BUTTON: escaped = shortcut_escape(ctrl->button.label, ctrl->button.shortcut); shortcuts[nshortcuts++] = ctrl->button.shortcut; if (ctrl->button.iscancel) actual_base_id = IDCANCEL; num_ids = 1; button(&pos, escaped, actual_base_id, ctrl->button.isdefault); sfree(escaped); break; case CTRL_LISTBOX: num_ids = 2; escaped = shortcut_escape(ctrl->listbox.label, ctrl->listbox.shortcut); shortcuts[nshortcuts++] = ctrl->listbox.shortcut; if (ctrl->listbox.draglist) { data = snew(struct prefslist); num_ids = 4; prefslist(data, &pos, ctrl->listbox.height, escaped, base_id, base_id+1, base_id+2, base_id+3); shortcuts[nshortcuts++] = 'u'; /* Up */ shortcuts[nshortcuts++] = 'd'; /* Down */ } else if (ctrl->listbox.height == 0) { /* Drop-down list. */ if (ctrl->listbox.percentwidth == 100) { staticddlbig(&pos, escaped, base_id, base_id+1); } else { staticddl(&pos, escaped, base_id, base_id+1, ctrl->listbox.percentwidth); } } else { /* Ordinary list. */ listbox(&pos, escaped, base_id, base_id+1, ctrl->listbox.height, ctrl->listbox.multisel); } if (ctrl->listbox.ncols) { /* * This method of getting the box width is a bit of * a hack; we'd do better to try to retrieve the * actual width in dialog units from doctl() just * before MapDialogRect. But that's going to be no * fun, and this should be good enough accuracy. */ int width = cp->width * ctrl->listbox.percentwidth; int *tabarray; int i, percent; tabarray = snewn(ctrl->listbox.ncols-1, int); percent = 0; for (i = 0; i < ctrl->listbox.ncols-1; i++) { percent += ctrl->listbox.percentages[i]; tabarray[i] = width * percent / 10000; } SendDlgItemMessage(cp->hwnd, base_id+1, LB_SETTABSTOPS, ctrl->listbox.ncols-1, (LPARAM)tabarray); sfree(tabarray); } sfree(escaped); break; case CTRL_FILESELECT: num_ids = 3; escaped = shortcut_escape(ctrl->fileselect.label, ctrl->fileselect.shortcut); shortcuts[nshortcuts++] = ctrl->fileselect.shortcut; editbutton(&pos, escaped, base_id, base_id+1, "Bro&wse...", base_id+2); shortcuts[nshortcuts++] = 'w'; sfree(escaped); break; 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("", false, 0, 0); sfree(escaped); break; default: unreachable("bad control type in winctrl_layout"); } /* Translate the original align_id_relative of -1 into n-1 */ if (align_id_relative < 0) align_id_relative += num_ids; /* * Create a `struct winctrl' for this control, and advance * the dialog ID counter, if it's actually been created * (and isn't tabdelayed). */ if (pos.hwnd) { struct winctrl *c = snew(struct winctrl); c->ctrl = ctrl; c->base_id = actual_base_id; c->align_id = c->base_id + align_id_relative; 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; if (ctrl->generic.align_next_to) { /* * Implement align_next_to by looking at the y extents * of the two controls now that both are created, and * moving one or the other downwards so that they're * centred on a common horizontal line. */ struct winctrl *c2 = winctrl_findbyctrl( wc, ctrl->generic.align_next_to); HWND win1 = GetDlgItem(pos.hwnd, c->align_id); HWND win2 = GetDlgItem(pos.hwnd, c2->align_id); RECT rect1, rect2; if (win1 && win2 && GetWindowRect(win1, &rect1) && GetWindowRect(win2, &rect2)) { LONG top = (rect1.top < rect2.top ? rect1.top : rect2.top); LONG bottom = (rect1.bottom > rect2.bottom ? rect1.bottom : rect2.bottom); move_windows(pos.hwnd, c->base_id, c->num_ids, (top + bottom - rect1.top - rect1.bottom)/2); move_windows(pos.hwnd, c2->base_id, c2->num_ids, (top + bottom - rect2.top - rect2.bottom)/2); } } } else { sfree(data); } if (colstart >= 0) { /* * Update the ypos in all columns crossed by this * control. */ int i; for (i = colstart; i < colstart+colspan; i++) columns[i].ypos = pos.ypos; } } /* * We've now finished laying out the controls; so now update * the ctlpos and control ID that were passed in, terminate * any containing box, and return. */ for (i = 0; i < ncols; i++) if (cp->ypos < columns[i].ypos) cp->ypos = columns[i].ypos; *id = base_id; if (s->boxname && *s->boxname) endbox(cp); } static void winctrl_set_focus(union control *ctrl, struct dlgparam *dp, bool has_focus) { if (has_focus) { if (dp->focused) dp->lastfocused = dp->focused; dp->focused = ctrl; } else if (!has_focus && dp->focused == ctrl) { dp->lastfocused = dp->focused; dp->focused = NULL; } } union control *dlg_last_focused(union control *ctrl, dlgparam *dp) { return dp->focused == ctrl ? dp->lastfocused : dp->focused; } /* * The dialog-box procedure calls this function to handle Windows * messages on a control we manage. */ bool winctrl_handle_command(struct dlgparam *dp, UINT msg, WPARAM wParam, LPARAM lParam) { struct winctrl *c; union control *ctrl; int i, id; bool ret; static UINT draglistmsg = WM_NULL; /* * Filter out pointless window messages. Our interest is in * WM_COMMAND and the drag list message, and nothing else. */ if (draglistmsg == WM_NULL) draglistmsg = RegisterWindowMessage (DRAGLISTMSGSTRING); if (msg != draglistmsg && msg != WM_COMMAND && msg != WM_DRAWITEM) return false; /* * Look up the control ID in our data. */ c = NULL; for (i = 0; i < dp->nctrltrees; i++) { c = winctrl_findbyid(dp->controltrees[i], LOWORD(wParam)); if (c) break; } if (!c) return false; /* we have nothing to do */ if (msg == WM_DRAWITEM) { /* * Owner-draw request for a panel title. */ LPDRAWITEMSTRUCT di = (LPDRAWITEMSTRUCT) lParam; HDC hdc = di->hDC; RECT r = di->rcItem; SIZE s; SetMapMode(hdc, MM_TEXT); /* ensure logical units == pixels */ GetTextExtentPoint32(hdc, (char *)c->data, strlen((char *)c->data), &s); DrawEdge(hdc, &r, EDGE_ETCHED, BF_ADJUST | BF_RECT); TextOut(hdc, r.left + (r.right-r.left-s.cx)/2, r.top + (r.bottom-r.top-s.cy)/2, (char *)c->data, strlen((char *)c->data)); return true; } ctrl = c->ctrl; id = LOWORD(wParam) - c->base_id; if (!ctrl || !ctrl->generic.handler) return false; /* nothing we can do here */ /* * From here on we do not issue `return' statements until the * very end of the dialog box: any event handler is entitled to * ask for a colour selector, so we _must_ always allow control * to reach the end of this switch statement so that the * subsequent code can test dp->coloursel_wanted(). */ ret = false; dp->coloursel_wanted = false; /* * Now switch on the control type and the message. */ switch (ctrl->generic.type) { case CTRL_EDITBOX: if (msg == WM_COMMAND && !ctrl->editbox.has_list && (HIWORD(wParam) == EN_SETFOCUS || HIWORD(wParam) == EN_KILLFOCUS)) winctrl_set_focus(ctrl, dp, HIWORD(wParam) == EN_SETFOCUS); if (msg == WM_COMMAND && ctrl->editbox.has_list && (HIWORD(wParam)==CBN_SETFOCUS || HIWORD(wParam)==CBN_KILLFOCUS)) winctrl_set_focus(ctrl, dp, HIWORD(wParam) == CBN_SETFOCUS); if (msg == WM_COMMAND && !ctrl->editbox.has_list && HIWORD(wParam) == EN_CHANGE) ctrl->generic.handler(ctrl, dp, dp->data, EVENT_VALCHANGE); if (msg == WM_COMMAND && ctrl->editbox.has_list) { if (HIWORD(wParam) == CBN_SELCHANGE) { int index, len; char *text; index = SendDlgItemMessage(dp->hwnd, c->base_id+1, CB_GETCURSEL, 0, 0); len = SendDlgItemMessage(dp->hwnd, c->base_id+1, CB_GETLBTEXTLEN, index, 0); text = snewn(len+1, char); SendDlgItemMessage(dp->hwnd, c->base_id+1, CB_GETLBTEXT, index, (LPARAM)text); SetDlgItemText(dp->hwnd, c->base_id+1, text); sfree(text); ctrl->generic.handler(ctrl, dp, dp->data, EVENT_VALCHANGE); } else if (HIWORD(wParam) == CBN_EDITCHANGE) { ctrl->generic.handler(ctrl, dp, dp->data, EVENT_VALCHANGE); } else if (HIWORD(wParam) == CBN_KILLFOCUS) { ctrl->generic.handler(ctrl, dp, dp->data, EVENT_REFRESH); } } break; case CTRL_RADIO: if (msg == WM_COMMAND && (HIWORD(wParam) == BN_SETFOCUS || HIWORD(wParam) == BN_KILLFOCUS)) winctrl_set_focus(ctrl, dp, HIWORD(wParam) == BN_SETFOCUS); /* * We sometimes get spurious BN_CLICKED messages for the * radio button that is just about to _lose_ selection, if * we're switching using the arrow keys. Therefore we * double-check that the button in wParam is actually * checked before generating an event. */ if (msg == WM_COMMAND && (HIWORD(wParam) == BN_CLICKED || HIWORD(wParam) == BN_DOUBLECLICKED) && IsDlgButtonChecked(dp->hwnd, LOWORD(wParam))) { ctrl->generic.handler(ctrl, dp, dp->data, EVENT_VALCHANGE); } break; case CTRL_CHECKBOX: if (msg == WM_COMMAND && (HIWORD(wParam) == BN_SETFOCUS || HIWORD(wParam) == BN_KILLFOCUS)) winctrl_set_focus(ctrl, dp, HIWORD(wParam) == BN_SETFOCUS); if (msg == WM_COMMAND && (HIWORD(wParam) == BN_CLICKED || HIWORD(wParam) == BN_DOUBLECLICKED)) { ctrl->generic.handler(ctrl, dp, dp->data, EVENT_VALCHANGE); } break; case CTRL_BUTTON: if (msg == WM_COMMAND && (HIWORD(wParam) == BN_SETFOCUS || HIWORD(wParam) == BN_KILLFOCUS)) winctrl_set_focus(ctrl, dp, HIWORD(wParam) == BN_SETFOCUS); if (msg == WM_COMMAND && (HIWORD(wParam) == BN_CLICKED || HIWORD(wParam) == BN_DOUBLECLICKED)) { ctrl->generic.handler(ctrl, dp, dp->data, EVENT_ACTION); } break; case CTRL_LISTBOX: if (msg == WM_COMMAND && ctrl->listbox.height != 0 && (HIWORD(wParam)==LBN_SETFOCUS || HIWORD(wParam)==LBN_KILLFOCUS)) winctrl_set_focus(ctrl, dp, HIWORD(wParam) == LBN_SETFOCUS); if (msg == WM_COMMAND && ctrl->listbox.height == 0 && (HIWORD(wParam)==CBN_SETFOCUS || HIWORD(wParam)==CBN_KILLFOCUS)) winctrl_set_focus(ctrl, dp, HIWORD(wParam) == CBN_SETFOCUS); if (msg == WM_COMMAND && id >= 2 && (HIWORD(wParam) == BN_SETFOCUS || HIWORD(wParam) == BN_KILLFOCUS)) winctrl_set_focus(ctrl, dp, HIWORD(wParam) == BN_SETFOCUS); if (ctrl->listbox.draglist) { int pret; pret = handle_prefslist(c->data, NULL, 0, (msg != WM_COMMAND), dp->hwnd, wParam, lParam); if (pret & 2) ctrl->generic.handler(ctrl, dp, dp->data, EVENT_VALCHANGE); ret = pret & 1; } else { if (msg == WM_COMMAND && HIWORD(wParam) == LBN_DBLCLK) { SetCapture(dp->hwnd); ctrl->generic.handler(ctrl, dp, dp->data, EVENT_ACTION); } else if (msg == WM_COMMAND && HIWORD(wParam) == LBN_SELCHANGE) { ctrl->generic.handler(ctrl, dp, dp->data, EVENT_SELCHANGE); } } break; case CTRL_FILESELECT: if (msg == WM_COMMAND && id == 1 && (HIWORD(wParam) == EN_SETFOCUS || HIWORD(wParam) == EN_KILLFOCUS)) winctrl_set_focus(ctrl, dp, HIWORD(wParam) == EN_SETFOCUS); if (msg == WM_COMMAND && id == 2 && (HIWORD(wParam) == BN_SETFOCUS || HIWORD(wParam) == BN_KILLFOCUS)) winctrl_set_focus(ctrl, dp, HIWORD(wParam) == BN_SETFOCUS); if (msg == WM_COMMAND && id == 1 && HIWORD(wParam) == EN_CHANGE) ctrl->generic.handler(ctrl, dp, dp->data, EVENT_VALCHANGE); if (id == 2 && (msg == WM_COMMAND && (HIWORD(wParam) == BN_CLICKED || HIWORD(wParam) == BN_DOUBLECLICKED))) { OPENFILENAME of; char filename[FILENAME_MAX]; memset(&of, 0, sizeof(of)); of.hwndOwner = dp->hwnd; if (ctrl->fileselect.filter) of.lpstrFilter = ctrl->fileselect.filter; else of.lpstrFilter = "All Files (*.*)\0*\0\0\0"; of.lpstrCustomFilter = NULL; of.nFilterIndex = 1; of.lpstrFile = filename; GetDlgItemText(dp->hwnd, c->base_id+1, filename, lenof(filename)); filename[lenof(filename)-1] = '\0'; of.nMaxFile = lenof(filename); of.lpstrFileTitle = NULL; of.lpstrTitle = ctrl->fileselect.title; of.Flags = 0; if (request_file(NULL, &of, false, ctrl->fileselect.for_writing)) { SetDlgItemText(dp->hwnd, c->base_id + 1, filename); ctrl->generic.handler(ctrl, dp, dp->data, EVENT_VALCHANGE); } } break; case CTRL_FONTSELECT: if (msg == WM_COMMAND && id == 2 && (HIWORD(wParam) == BN_SETFOCUS || HIWORD(wParam) == BN_KILLFOCUS)) winctrl_set_focus(ctrl, dp, HIWORD(wParam) == BN_SETFOCUS); 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; } /* * If the above event handler has asked for a colour selector, * now is the time to generate one. */ if (dp->coloursel_wanted) { static CHOOSECOLOR cc; static DWORD custom[16] = { 0 }; /* zero initialisers */ cc.lStructSize = sizeof(cc); cc.hwndOwner = dp->hwnd; cc.hInstance = (HWND) hinst; cc.lpCustColors = custom; cc.rgbResult = RGB(dp->coloursel_result.r, dp->coloursel_result.g, dp->coloursel_result.b); cc.Flags = CC_FULLOPEN | CC_RGBINIT; if (ChooseColor(&cc)) { dp->coloursel_result.r = (unsigned char) (cc.rgbResult & 0xFF); dp->coloursel_result.g = (unsigned char) (cc.rgbResult >> 8) & 0xFF; dp->coloursel_result.b = (unsigned char) (cc.rgbResult >> 16) & 0xFF; dp->coloursel_result.ok = true; } else dp->coloursel_result.ok = false; ctrl->generic.handler(ctrl, dp, dp->data, EVENT_CALLBACK); } return ret; } /* * This function can be called to produce context help on a * control. Returns true if it has actually launched some help. */ bool winctrl_context_help(struct dlgparam *dp, HWND hwnd, int id) { int i; struct winctrl *c; /* * Look up the control ID in our data. */ c = NULL; for (i = 0; i < dp->nctrltrees; i++) { c = winctrl_findbyid(dp->controltrees[i], id); if (c) break; } if (!c) return false; /* we have nothing to do */ /* * This is the Windows front end, so we're allowed to assume * `helpctx.p' is a context string. */ if (!c->ctrl || !c->ctrl->generic.helpctx.p) return false; /* no help available for this ctrl */ launch_help(hwnd, c->ctrl->generic.helpctx.p); return true; } /* * Now the various functions that the platform-independent * mechanism can call to access the dialog box entries. */ static struct winctrl *dlg_findbyctrl(struct dlgparam *dp, union control *ctrl) { int i; for (i = 0; i < dp->nctrltrees; i++) { struct winctrl *c = winctrl_findbyctrl(dp->controltrees[i], ctrl); if (c) return c; } return NULL; } bool dlg_is_visible(union control *ctrl, dlgparam *dp) { /* * In this implementation of the dialog box, we physically * uncreate controls that aren't in a visible panel of the config * box. So we can tell if a control is visible just by checking if * it _exists_. */ return dlg_findbyctrl(dp, ctrl) != NULL; } void dlg_radiobutton_set(union control *ctrl, dlgparam *dp, int whichbutton) { struct winctrl *c = dlg_findbyctrl(dp, ctrl); assert(c && c->ctrl->generic.type == CTRL_RADIO); CheckRadioButton(dp->hwnd, c->base_id + 1, c->base_id + c->ctrl->radio.nbuttons, c->base_id + 1 + whichbutton); } int dlg_radiobutton_get(union control *ctrl, dlgparam *dp) { struct winctrl *c = dlg_findbyctrl(dp, ctrl); int i; assert(c && c->ctrl->generic.type == CTRL_RADIO); for (i = 0; i < c->ctrl->radio.nbuttons; i++) if (IsDlgButtonChecked(dp->hwnd, c->base_id + 1 + i)) return i; unreachable("no radio button was checked"); } void dlg_checkbox_set(union control *ctrl, dlgparam *dp, bool checked) { struct winctrl *c = dlg_findbyctrl(dp, ctrl); assert(c && c->ctrl->generic.type == CTRL_CHECKBOX); CheckDlgButton(dp->hwnd, c->base_id, checked); } bool dlg_checkbox_get(union control *ctrl, dlgparam *dp) { struct winctrl *c = dlg_findbyctrl(dp, ctrl); assert(c && c->ctrl->generic.type == CTRL_CHECKBOX); return 0 != IsDlgButtonChecked(dp->hwnd, c->base_id); } void dlg_editbox_set(union control *ctrl, dlgparam *dp, char const *text) { 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, dlgparam *dp) { 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, dlgparam *dp) { struct winctrl *c = dlg_findbyctrl(dp, ctrl); int msg; assert(c && (c->ctrl->generic.type == CTRL_LISTBOX || (c->ctrl->generic.type == CTRL_EDITBOX && c->ctrl->editbox.has_list))); msg = (c->ctrl->generic.type==CTRL_LISTBOX && c->ctrl->listbox.height!=0 ? LB_RESETCONTENT : CB_RESETCONTENT); SendDlgItemMessage(dp->hwnd, c->base_id+1, msg, 0, 0); } void dlg_listbox_del(union control *ctrl, dlgparam *dp, int index) { struct winctrl *c = dlg_findbyctrl(dp, ctrl); int msg; assert(c && (c->ctrl->generic.type == CTRL_LISTBOX || (c->ctrl->generic.type == CTRL_EDITBOX && c->ctrl->editbox.has_list))); msg = (c->ctrl->generic.type==CTRL_LISTBOX && c->ctrl->listbox.height!=0 ? LB_DELETESTRING : CB_DELETESTRING); SendDlgItemMessage(dp->hwnd, c->base_id+1, msg, index, 0); } void dlg_listbox_add(union control *ctrl, dlgparam *dp, char const *text) { struct winctrl *c = dlg_findbyctrl(dp, ctrl); int msg; assert(c && (c->ctrl->generic.type == CTRL_LISTBOX || (c->ctrl->generic.type == CTRL_EDITBOX && c->ctrl->editbox.has_list))); msg = (c->ctrl->generic.type==CTRL_LISTBOX && c->ctrl->listbox.height!=0 ? LB_ADDSTRING : CB_ADDSTRING); SendDlgItemMessage(dp->hwnd, c->base_id+1, msg, 0, (LPARAM)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, dlgparam *dp, char const *text, int id) { struct winctrl *c = dlg_findbyctrl(dp, ctrl); int msg, msg2, index; assert(c && (c->ctrl->generic.type == CTRL_LISTBOX || (c->ctrl->generic.type == CTRL_EDITBOX && c->ctrl->editbox.has_list))); msg = (c->ctrl->generic.type==CTRL_LISTBOX && c->ctrl->listbox.height!=0 ? LB_ADDSTRING : CB_ADDSTRING); msg2 = (c->ctrl->generic.type==CTRL_LISTBOX && c->ctrl->listbox.height!=0 ? LB_SETITEMDATA : CB_SETITEMDATA); index = SendDlgItemMessage(dp->hwnd, c->base_id+1, msg, 0, (LPARAM)text); SendDlgItemMessage(dp->hwnd, c->base_id+1, msg2, index, (LPARAM)id); } int dlg_listbox_getid(union control *ctrl, dlgparam *dp, int index) { struct winctrl *c = dlg_findbyctrl(dp, ctrl); int msg; assert(c && c->ctrl->generic.type == CTRL_LISTBOX); msg = (c->ctrl->listbox.height != 0 ? LB_GETITEMDATA : CB_GETITEMDATA); return SendDlgItemMessage(dp->hwnd, c->base_id+1, msg, index, 0); } /* dlg_listbox_index returns <0 if no single element is selected. */ int dlg_listbox_index(union control *ctrl, dlgparam *dp) { struct winctrl *c = dlg_findbyctrl(dp, ctrl); int msg, ret; assert(c && c->ctrl->generic.type == CTRL_LISTBOX); if (c->ctrl->listbox.multisel) { assert(c->ctrl->listbox.height != 0); /* not combo box */ ret = SendDlgItemMessage(dp->hwnd, c->base_id+1, LB_GETSELCOUNT, 0, 0); if (ret == LB_ERR || ret > 1) return -1; } msg = (c->ctrl->listbox.height != 0 ? LB_GETCURSEL : CB_GETCURSEL); ret = SendDlgItemMessage(dp->hwnd, c->base_id+1, msg, 0, 0); if (ret == LB_ERR) return -1; else return ret; } bool dlg_listbox_issel(union control *ctrl, dlgparam *dp, int index) { struct winctrl *c = dlg_findbyctrl(dp, ctrl); assert(c && c->ctrl->generic.type == CTRL_LISTBOX && c->ctrl->listbox.multisel && c->ctrl->listbox.height != 0); return SendDlgItemMessage(dp->hwnd, c->base_id+1, LB_GETSEL, index, 0); } void dlg_listbox_select(union control *ctrl, dlgparam *dp, int index) { struct winctrl *c = dlg_findbyctrl(dp, ctrl); int msg; assert(c && c->ctrl->generic.type == CTRL_LISTBOX && !c->ctrl->listbox.multisel); msg = (c->ctrl->listbox.height != 0 ? LB_SETCURSEL : CB_SETCURSEL); SendDlgItemMessage(dp->hwnd, c->base_id+1, msg, index, 0); } void dlg_text_set(union control *ctrl, dlgparam *dp, char const *text) { struct winctrl *c = dlg_findbyctrl(dp, ctrl); assert(c && c->ctrl->generic.type == CTRL_TEXT); SetDlgItemText(dp->hwnd, c->base_id, text); } void dlg_label_change(union control *ctrl, dlgparam *dp, char const *text) { struct winctrl *c = dlg_findbyctrl(dp, ctrl); char *escaped = NULL; int id = -1; assert(c); switch (c->ctrl->generic.type) { case CTRL_EDITBOX: escaped = shortcut_escape(text, c->ctrl->editbox.shortcut); id = c->base_id; break; case CTRL_RADIO: escaped = shortcut_escape(text, c->ctrl->radio.shortcut); id = c->base_id; break; case CTRL_CHECKBOX: escaped = shortcut_escape(text, ctrl->checkbox.shortcut); id = c->base_id; break; case CTRL_BUTTON: escaped = shortcut_escape(text, ctrl->button.shortcut); id = c->base_id; break; case CTRL_LISTBOX: escaped = shortcut_escape(text, ctrl->listbox.shortcut); id = c->base_id; break; case CTRL_FILESELECT: escaped = shortcut_escape(text, ctrl->fileselect.shortcut); id = c->base_id; break; case CTRL_FONTSELECT: escaped = shortcut_escape(text, ctrl->fontselect.shortcut); id = c->base_id; break; default: unreachable("bad control type in label_change"); } if (escaped) { SetDlgItemText(dp->hwnd, id, escaped); sfree(escaped); } } void dlg_filesel_set(union control *ctrl, dlgparam *dp, Filename *fn) { 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, dlgparam *dp) { 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, dlgparam *dp, FontSpec *fs) { char *buf, *boldstr; 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, dlgparam *dp) { 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. */ void dlg_update_start(union control *ctrl, dlgparam *dp) { struct winctrl *c = dlg_findbyctrl(dp, ctrl); if (c && c->ctrl->generic.type == CTRL_LISTBOX) { SendDlgItemMessage(dp->hwnd, c->base_id+1, WM_SETREDRAW, false, 0); } } void dlg_update_done(union control *ctrl, dlgparam *dp) { struct winctrl *c = dlg_findbyctrl(dp, ctrl); if (c && c->ctrl->generic.type == CTRL_LISTBOX) { HWND hw = GetDlgItem(dp->hwnd, c->base_id+1); SendMessage(hw, WM_SETREDRAW, true, 0); InvalidateRect(hw, NULL, true); } } void dlg_set_focus(union control *ctrl, dlgparam *dp) { 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; /* * In the theoretically-unlikely case that no button was * selected, id should come out of this as 1, which is a * reasonable enough choice. */ break; case CTRL_CHECKBOX: id = c->base_id; break; case CTRL_BUTTON: id = c->base_id; break; case CTRL_LISTBOX: id = c->base_id + 1; break; case CTRL_FILESELECT: id = c->base_id + 1; break; case CTRL_FONTSELECT: id = c->base_id + 2; break; default: id = c->base_id; break; } ctl = GetDlgItem(dp->hwnd, id); SetFocus(ctl); } /* * During event processing, you might well want to give an error * indication to the user. dlg_beep() is a quick and easy generic * error; dlg_error() puts up a message-box or equivalent. */ void dlg_beep(dlgparam *dp) { MessageBeep(0); } void dlg_error_msg(dlgparam *dp, const char *msg) { MessageBox(dp->hwnd, msg, dp->errtitle ? dp->errtitle : NULL, MB_OK | MB_ICONERROR); } /* * This function signals to the front end that the dialog's * processing is completed, and passes an integer value (typically * a success status). */ void dlg_end(dlgparam *dp, int value) { dp->ended = true; dp->endresult = value; } void dlg_refresh(union control *ctrl, dlgparam *dp) { int i, j; struct winctrl *c; if (!ctrl) { /* * Send EVENT_REFRESH to absolutely everything. */ for (j = 0; j < dp->nctrltrees; j++) { for (i = 0; (c = winctrl_findbyindex(dp->controltrees[j], i)) != NULL; i++) { if (c->ctrl && c->ctrl->generic.handler != NULL) c->ctrl->generic.handler(c->ctrl, dp, dp->data, EVENT_REFRESH); } } } else { /* * Send EVENT_REFRESH to a specific control. */ if (ctrl->generic.handler != NULL) ctrl->generic.handler(ctrl, dp, dp->data, EVENT_REFRESH); } } void dlg_coloursel_start(union control *ctrl, dlgparam *dp, int r, int g, int b) { dp->coloursel_wanted = true; dp->coloursel_result.r = r; dp->coloursel_result.g = g; dp->coloursel_result.b = b; } bool dlg_coloursel_results(union control *ctrl, dlgparam *dp, int *r, int *g, int *b) { if (dp->coloursel_result.ok) { *r = dp->coloursel_result.r; *g = dp->coloursel_result.g; *b = dp->coloursel_result.b; return true; } else return false; } void dlg_auto_set_fixed_pitch_flag(dlgparam *dp) { Conf *conf = (Conf *)dp->data; FontSpec *fs; int quality; HFONT hfont; HDC hdc; TEXTMETRIC tm; bool 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; } bool dlg_get_fixed_pitch_flag(dlgparam *dp) { return dp->fixed_pitch_fonts; } void dlg_set_fixed_pitch_flag(dlgparam *dp, bool flag) { dp->fixed_pitch_fonts = flag; } void dp_init(struct dlgparam *dp) { dp->nctrltrees = 0; dp->data = NULL; dp->ended = false; dp->focused = dp->lastfocused = NULL; memset(dp->shortcuts, 0, sizeof(dp->shortcuts)); dp->hwnd = NULL; dp->wintitle = dp->errtitle = NULL; dp->fixed_pitch_fonts = true; } void dp_add_tree(struct dlgparam *dp, struct winctrls *wc) { assert(dp->nctrltrees < lenof(dp->controltrees)); dp->controltrees[dp->nctrltrees++] = wc; } void dp_cleanup(struct dlgparam *dp) { sfree(dp->wintitle); sfree(dp->errtitle); } putty-0.76/windows/windefs.c0000644000175000017500000000143114072266314013055 00000000000000/* * windefs.c: default settings that are specific to Windows. */ #include "putty.h" #include FontSpec *platform_default_fontspec(const char *name) { if (!strcmp(name, "Font")) return fontspec_new("Courier New", false, 10, ANSI_CHARSET); else return fontspec_new("", false, 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; } bool platform_default_b(const char *name, bool def) { return def; } int platform_default_i(const char *name, int def) { return def; } putty-0.76/windows/windlg.c0000644000175000017500000010672714072266314012720 00000000000000/* * windlg.c - dialogs for PuTTY(tel), including the configuration dialog. */ #include #include #include #include #include #include #include "putty.h" #include "ssh.h" #include "win_res.h" #include "winseat.h" #include "storage.h" #include "dialog.h" #include "licence.h" #include #include #include #ifdef MSVC4 #define TVINSERTSTRUCT TV_INSERTSTRUCT #define TVITEM TV_ITEM #define ICON_BIG 1 #endif /* * These are the various bits of data required to handle the * portable-dialog stuff in the config box. Having them at file * scope in here isn't too bad a place to put them; if we were ever * to need more than one config box per process we could always * shift them to a per-config-box structure stored in GWL_USERDATA. */ static struct controlbox *ctrlbox; /* * ctrls_base holds the OK and Cancel buttons: the controls which * are present in all dialog panels. ctrls_panel holds the ones * which change from panel to panel. */ static struct winctrls ctrls_base, ctrls_panel; static struct dlgparam dp; #define LOGEVENT_INITIAL_MAX 128 #define LOGEVENT_CIRCULAR_MAX 128 static char *events_initial[LOGEVENT_INITIAL_MAX]; static char *events_circular[LOGEVENT_CIRCULAR_MAX]; static int ninitial = 0, ncircular = 0, circular_first = 0; #define PRINTER_DISABLED_STRING "None (printing disabled)" void force_normal(HWND hwnd) { static bool recurse = false; WINDOWPLACEMENT wp; if (recurse) return; recurse = true; wp.length = sizeof(wp); if (GetWindowPlacement(hwnd, &wp) && wp.showCmd == SW_SHOWMAXIMIZED) { wp.showCmd = SW_SHOWNORMAL; SetWindowPlacement(hwnd, &wp); } recurse = false; } static char *getevent(int i) { if (i < ninitial) return events_initial[i]; if ((i -= ninitial) < ncircular) return events_circular[(circular_first + i) % LOGEVENT_CIRCULAR_MAX]; return NULL; } static HWND logbox; HWND event_log_window(void) { return logbox; } static INT_PTR CALLBACK LogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { int i; switch (msg) { case WM_INITDIALOG: { char *str = dupprintf("%s Event Log", appname); SetWindowText(hwnd, str); sfree(str); static int tabs[4] = { 78, 108 }; SendDlgItemMessage(hwnd, IDN_LIST, LB_SETTABSTOPS, 2, (LPARAM) tabs); for (i = 0; i < ninitial; i++) SendDlgItemMessage(hwnd, IDN_LIST, LB_ADDSTRING, 0, (LPARAM) events_initial[i]); for (i = 0; i < ncircular; i++) SendDlgItemMessage(hwnd, IDN_LIST, LB_ADDSTRING, 0, (LPARAM) events_circular[(circular_first + i) % LOGEVENT_CIRCULAR_MAX]); return 1; } case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: case IDCANCEL: logbox = NULL; SetActiveWindow(GetParent(hwnd)); DestroyWindow(hwnd); return 0; case IDN_COPY: if (HIWORD(wParam) == BN_CLICKED || HIWORD(wParam) == BN_DOUBLECLICKED) { int selcount; int *selitems; selcount = SendDlgItemMessage(hwnd, IDN_LIST, LB_GETSELCOUNT, 0, 0); if (selcount == 0) { /* don't even try to copy zero items */ MessageBeep(0); break; } selitems = snewn(selcount, int); if (selitems) { int count = SendDlgItemMessage(hwnd, IDN_LIST, LB_GETSELITEMS, selcount, (LPARAM) selitems); int i; int size; char *clipdata; static unsigned char sel_nl[] = SEL_NL; if (count == 0) { /* can't copy zero stuff */ MessageBeep(0); break; } size = 0; for (i = 0; i < count; i++) size += strlen(getevent(selitems[i])) + sizeof(sel_nl); clipdata = snewn(size, char); if (clipdata) { char *p = clipdata; for (i = 0; i < count; i++) { char *q = getevent(selitems[i]); int qlen = strlen(q); memcpy(p, q, qlen); p += qlen; memcpy(p, sel_nl, sizeof(sel_nl)); p += sizeof(sel_nl); } write_aclip(CLIP_SYSTEM, clipdata, size, true); sfree(clipdata); } sfree(selitems); for (i = 0; i < (ninitial + ncircular); i++) SendDlgItemMessage(hwnd, IDN_LIST, LB_SETSEL, false, i); } } return 0; } return 0; case WM_CLOSE: logbox = NULL; SetActiveWindow(GetParent(hwnd)); DestroyWindow(hwnd); return 0; } return 0; } static INT_PTR CALLBACK LicenceProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_INITDIALOG: { char *str = dupprintf("%s Licence", appname); SetWindowText(hwnd, str); sfree(str); SetDlgItemText(hwnd, IDA_TEXT, LICENCE_TEXT("\r\n\r\n")); return 1; } case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: case IDCANCEL: EndDialog(hwnd, 1); return 0; } return 0; case WM_CLOSE: EndDialog(hwnd, 1); return 0; } return 0; } static INT_PTR CALLBACK AboutProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { char *str; switch (msg) { case WM_INITDIALOG: { str = dupprintf("About %s", appname); SetWindowText(hwnd, str); sfree(str); char *buildinfo_text = buildinfo("\r\n"); char *text = dupprintf ("%s\r\n\r\n%s\r\n\r\n%s\r\n\r\n%s", appname, ver, buildinfo_text, "\251 " SHORT_COPYRIGHT_DETAILS ". All rights reserved."); sfree(buildinfo_text); SetDlgItemText(hwnd, IDA_TEXT, text); MakeDlgItemBorderless(hwnd, IDA_TEXT); sfree(text); return 1; } case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: case IDCANCEL: EndDialog(hwnd, true); return 0; case IDA_LICENCE: EnableWindow(hwnd, 0); DialogBox(hinst, MAKEINTRESOURCE(IDD_LICENCEBOX), hwnd, LicenceProc); EnableWindow(hwnd, 1); SetActiveWindow(hwnd); return 0; case IDA_WEB: /* Load web browser */ ShellExecute(hwnd, "open", "https://www.chiark.greenend.org.uk/~sgtatham/putty/", 0, 0, SW_SHOWDEFAULT); return 0; } return 0; case WM_CLOSE: EndDialog(hwnd, true); return 0; } return 0; } static int SaneDialogBox(HINSTANCE hinst, LPCTSTR tmpl, HWND hwndparent, DLGPROC lpDialogFunc) { WNDCLASS wc; HWND hwnd; MSG msg; int flags; int ret; int gm; wc.style = CS_DBLCLKS | CS_SAVEBITS | CS_BYTEALIGNWINDOW; wc.lpfnWndProc = DefDlgProc; wc.cbClsExtra = 0; wc.cbWndExtra = DLGWINDOWEXTRA + 2*sizeof(LONG_PTR); wc.hInstance = hinst; wc.hIcon = NULL; wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH) (COLOR_BACKGROUND +1); wc.lpszMenuName = NULL; wc.lpszClassName = "PuTTYConfigBox"; RegisterClass(&wc); hwnd = CreateDialog(hinst, tmpl, hwndparent, lpDialogFunc); SetWindowLongPtr(hwnd, BOXFLAGS, 0); /* flags */ SetWindowLongPtr(hwnd, BOXRESULT, 0); /* result from SaneEndDialog */ while ((gm=GetMessage(&msg, NULL, 0, 0)) > 0) { flags=GetWindowLongPtr(hwnd, BOXFLAGS); if (!(flags & DF_END) && !IsDialogMessage(hwnd, &msg)) DispatchMessage(&msg); if (flags & DF_END) break; } if (gm == 0) PostQuitMessage(msg.wParam); /* We got a WM_QUIT, pass it on */ ret=GetWindowLongPtr(hwnd, BOXRESULT); DestroyWindow(hwnd); return ret; } static void SaneEndDialog(HWND hwnd, int ret) { SetWindowLongPtr(hwnd, BOXRESULT, ret); SetWindowLongPtr(hwnd, BOXFLAGS, DF_END); } /* * Null dialog procedure. */ static INT_PTR CALLBACK NullDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { return 0; } enum { IDCX_ABOUT = IDC_ABOUT, IDCX_TVSTATIC, IDCX_TREEVIEW, IDCX_STDBASE, IDCX_PANELBASE = IDCX_STDBASE + 32 }; struct treeview_faff { HWND treeview; HTREEITEM lastat[4]; }; static HTREEITEM treeview_insert(struct treeview_faff *faff, int level, char *text, char *path) { TVINSERTSTRUCT ins; int i; HTREEITEM newitem; ins.hParent = (level > 0 ? faff->lastat[level - 1] : TVI_ROOT); ins.hInsertAfter = faff->lastat[level]; #if _WIN32_IE >= 0x0400 && defined NONAMELESSUNION #define INSITEM DUMMYUNIONNAME.item #else #define INSITEM item #endif ins.INSITEM.mask = TVIF_TEXT | TVIF_PARAM; ins.INSITEM.pszText = text; ins.INSITEM.cchTextMax = strlen(text)+1; ins.INSITEM.lParam = (LPARAM)path; newitem = TreeView_InsertItem(faff->treeview, &ins); if (level > 0) TreeView_Expand(faff->treeview, faff->lastat[level - 1], (level > 1 ? TVE_COLLAPSE : TVE_EXPAND)); faff->lastat[level] = newitem; for (i = level + 1; i < 4; i++) faff->lastat[i] = NULL; return newitem; } /* * Create the panelfuls of controls in the configuration box. */ static void create_controls(HWND hwnd, char *path) { struct ctlpos cp; int index; int base_id; struct winctrls *wc; if (!path[0]) { /* * Here we must create the basic standard controls. */ ctlposinit(&cp, hwnd, 3, 3, 235); wc = &ctrls_base; base_id = IDCX_STDBASE; } else { /* * Otherwise, we're creating the controls for a particular * panel. */ ctlposinit(&cp, hwnd, 100, 3, 13); wc = &ctrls_panel; base_id = IDCX_PANELBASE; } for (index=-1; (index = ctrl_find_path(ctrlbox, path, index)) >= 0 ;) { struct controlset *s = ctrlbox->ctrlsets[index]; winctrl_layout(&dp, wc, &cp, s, &base_id); } } /* * This function is the configuration box. * (Being a dialog procedure, in general it returns 0 if the default * dialog processing should be performed, and 1 if it should not.) */ static INT_PTR CALLBACK GenericMainDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { HWND hw, treeview; struct treeview_faff tvfaff; int ret; switch (msg) { case WM_INITDIALOG: dp.hwnd = hwnd; create_controls(hwnd, ""); /* Open and Cancel buttons etc */ SetWindowText(hwnd, dp.wintitle); SetWindowLongPtr(hwnd, GWLP_USERDATA, 0); if (has_help()) SetWindowLongPtr(hwnd, GWL_EXSTYLE, GetWindowLongPtr(hwnd, GWL_EXSTYLE) | WS_EX_CONTEXTHELP); else { HWND item = GetDlgItem(hwnd, IDC_HELPBTN); if (item) DestroyWindow(item); } SendMessage(hwnd, WM_SETICON, (WPARAM) ICON_BIG, (LPARAM) LoadIcon(hinst, MAKEINTRESOURCE(IDI_CFGICON))); /* * Centre the window. */ { /* centre the window */ RECT rs, rd; hw = GetDesktopWindow(); if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd)) MoveWindow(hwnd, (rs.right + rs.left + rd.left - rd.right) / 2, (rs.bottom + rs.top + rd.top - rd.bottom) / 2, rd.right - rd.left, rd.bottom - rd.top, true); } /* * Create the tree view. */ { RECT r; WPARAM font; HWND tvstatic; r.left = 3; r.right = r.left + 95; r.top = 3; r.bottom = r.top + 10; MapDialogRect(hwnd, &r); tvstatic = CreateWindowEx(0, "STATIC", "Cate&gory:", WS_CHILD | WS_VISIBLE, r.left, r.top, r.right - r.left, r.bottom - r.top, hwnd, (HMENU) IDCX_TVSTATIC, hinst, NULL); font = SendMessage(hwnd, WM_GETFONT, 0, 0); SendMessage(tvstatic, WM_SETFONT, font, MAKELPARAM(true, 0)); r.left = 3; r.right = r.left + 95; r.top = 13; r.bottom = r.top + 219; MapDialogRect(hwnd, &r); treeview = CreateWindowEx(WS_EX_CLIENTEDGE, WC_TREEVIEW, "", WS_CHILD | WS_VISIBLE | WS_TABSTOP | TVS_HASLINES | TVS_DISABLEDRAGDROP | TVS_HASBUTTONS | TVS_LINESATROOT | TVS_SHOWSELALWAYS, r.left, r.top, r.right - r.left, r.bottom - r.top, hwnd, (HMENU) IDCX_TREEVIEW, hinst, NULL); font = SendMessage(hwnd, WM_GETFONT, 0, 0); SendMessage(treeview, WM_SETFONT, font, MAKELPARAM(true, 0)); tvfaff.treeview = treeview; memset(tvfaff.lastat, 0, sizeof(tvfaff.lastat)); } /* * Set up the tree view contents. */ { HTREEITEM hfirst = NULL; int i; char *path = NULL; char *firstpath = NULL; for (i = 0; i < ctrlbox->nctrlsets; i++) { struct controlset *s = ctrlbox->ctrlsets[i]; HTREEITEM item; int j; char *c; if (!s->pathname[0]) continue; j = path ? ctrl_path_compare(s->pathname, path) : 0; if (j == INT_MAX) continue; /* same path, nothing to add to tree */ /* * We expect never to find an implicit path * component. For example, we expect never to see * A/B/C followed by A/D/E, because that would * _implicitly_ create A/D. All our path prefixes * are expected to contain actual controls and be * selectable in the treeview; so we would expect * to see A/D _explicitly_ before encountering * A/D/E. */ assert(j == ctrl_path_elements(s->pathname) - 1); c = strrchr(s->pathname, '/'); if (!c) c = s->pathname; else c++; item = treeview_insert(&tvfaff, j, c, s->pathname); if (!hfirst) { hfirst = item; firstpath = s->pathname; } path = s->pathname; } /* * Put the treeview selection on to the first panel in the * ctrlbox. */ TreeView_SelectItem(treeview, hfirst); /* * And create the actual control set for that panel, to * match the initial treeview selection. */ assert(firstpath); /* config.c must have given us _something_ */ create_controls(hwnd, firstpath); dlg_refresh(NULL, &dp); /* and set up control values */ } /* * Set focus into the first available control. */ { int i; struct winctrl *c; for (i = 0; (c = winctrl_findbyindex(&ctrls_panel, i)) != NULL; i++) { if (c->ctrl) { dlg_set_focus(c->ctrl, &dp); break; } } } /* * Now we've finished creating our initial set of controls, * it's safe to actually show the window without risking setup * flicker. */ ShowWindow(hwnd, SW_SHOWNORMAL); /* * Set the flag that activates a couple of the other message * handlers below, which were disabled until now to avoid * spurious firing during the above setup procedure. */ SetWindowLongPtr(hwnd, GWLP_USERDATA, 1); return 0; case WM_LBUTTONUP: /* * Button release should trigger WM_OK if there was a * previous double click on the session list. */ ReleaseCapture(); if (dp.ended) SaneEndDialog(hwnd, dp.endresult ? 1 : 0); break; case WM_NOTIFY: if (LOWORD(wParam) == IDCX_TREEVIEW && ((LPNMHDR) lParam)->code == TVN_SELCHANGED) { /* * Selection-change events on the treeview cause us to do * a flurry of control deletion and creation - but only * after WM_INITDIALOG has finished. The initial * selection-change event(s) during treeview setup are * ignored. */ HTREEITEM i; TVITEM item; char buffer[64]; if (GetWindowLongPtr(hwnd, GWLP_USERDATA) != 1) return 0; i = TreeView_GetSelection(((LPNMHDR) lParam)->hwndFrom); SendMessage (hwnd, WM_SETREDRAW, false, 0); item.hItem = i; item.pszText = buffer; item.cchTextMax = sizeof(buffer); item.mask = TVIF_TEXT | TVIF_PARAM; TreeView_GetItem(((LPNMHDR) lParam)->hwndFrom, &item); { /* Destroy all controls in the currently visible panel. */ int k; HWND item; struct winctrl *c; while ((c = winctrl_findbyindex(&ctrls_panel, 0)) != NULL) { for (k = 0; k < c->num_ids; k++) { item = GetDlgItem(hwnd, c->base_id + k); if (item) DestroyWindow(item); } winctrl_rem_shortcuts(&dp, c); winctrl_remove(&ctrls_panel, c); sfree(c->data); sfree(c); } } create_controls(hwnd, (char *)item.lParam); dlg_refresh(NULL, &dp); /* set up control values */ SendMessage (hwnd, WM_SETREDRAW, true, 0); InvalidateRect (hwnd, NULL, true); SetFocus(((LPNMHDR) lParam)->hwndFrom); /* ensure focus stays */ return 0; } break; case WM_COMMAND: case WM_DRAWITEM: default: /* also handle drag list msg here */ /* * Only process WM_COMMAND once the dialog is fully formed. */ if (GetWindowLongPtr(hwnd, GWLP_USERDATA) == 1) { ret = winctrl_handle_command(&dp, msg, wParam, lParam); if (dp.ended && GetCapture() != hwnd) SaneEndDialog(hwnd, dp.endresult ? 1 : 0); } else ret = 0; return ret; case WM_HELP: if (!winctrl_context_help(&dp, hwnd, ((LPHELPINFO)lParam)->iCtrlId)) MessageBeep(0); break; case WM_CLOSE: quit_help(hwnd); SaneEndDialog(hwnd, 0); return 0; /* Grrr Explorer will maximize Dialogs! */ case WM_SIZE: if (wParam == SIZE_MAXIMIZED) force_normal(hwnd); return 0; } return 0; } void modal_about_box(HWND hwnd) { EnableWindow(hwnd, 0); DialogBox(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd, AboutProc); EnableWindow(hwnd, 1); SetActiveWindow(hwnd); } void show_help(HWND hwnd) { launch_help(hwnd, NULL); } void defuse_showwindow(void) { /* * Work around the fact that the app's first call to ShowWindow * will ignore the default in favour of the shell-provided * setting. */ { HWND hwnd; hwnd = CreateDialog(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX), NULL, NullDlgProc); ShowWindow(hwnd, SW_HIDE); SetActiveWindow(hwnd); DestroyWindow(hwnd); } } bool do_config(Conf *conf) { bool ret; ctrlbox = ctrl_new_box(); setup_config_box(ctrlbox, false, 0, 0); win_setup_config_box(ctrlbox, &dp.hwnd, has_help(), false, 0); 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; } bool do_reconfig(HWND hwnd, Conf *conf, int protcfginfo) { Conf *backup_conf; bool ret; int 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; } static void win_gui_eventlog(LogPolicy *lp, const char *string) { char timebuf[40]; char **location; struct tm tm; tm=ltime(); strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S\t", &tm); if (ninitial < LOGEVENT_INITIAL_MAX) location = &events_initial[ninitial]; else location = &events_circular[(circular_first + ncircular) % LOGEVENT_CIRCULAR_MAX]; if (*location) sfree(*location); *location = dupcat(timebuf, string); if (logbox) { int count; SendDlgItemMessage(logbox, IDN_LIST, LB_ADDSTRING, 0, (LPARAM) *location); count = SendDlgItemMessage(logbox, IDN_LIST, LB_GETCOUNT, 0, 0); SendDlgItemMessage(logbox, IDN_LIST, LB_SETTOPINDEX, count - 1, 0); } if (ninitial < LOGEVENT_INITIAL_MAX) { ninitial++; } else if (ncircular < LOGEVENT_CIRCULAR_MAX) { ncircular++; } else if (ncircular == LOGEVENT_CIRCULAR_MAX) { circular_first = (circular_first + 1) % LOGEVENT_CIRCULAR_MAX; sfree(events_circular[circular_first]); events_circular[circular_first] = dupstr(".."); } } static void win_gui_logging_error(LogPolicy *lp, const char *event) { WinGuiSeat *wgs = container_of(lp, WinGuiSeat, logpolicy); /* Send 'can't open log file' errors to the terminal window. * (Marked as stderr, although terminal.c won't care.) */ seat_stderr_pl(&wgs->seat, ptrlen_from_asciz(event)); seat_stderr_pl(&wgs->seat, PTRLEN_LITERAL("\r\n")); } void showeventlog(HWND hwnd) { if (!logbox) { logbox = CreateDialog(hinst, MAKEINTRESOURCE(IDD_LOGBOX), hwnd, LogProc); ShowWindow(logbox, SW_SHOWNORMAL); } SetActiveWindow(logbox); } void showabout(HWND hwnd) { DialogBox(hinst, MAKEINTRESOURCE(IDD_ABOUTBOX), hwnd, AboutProc); } struct hostkey_dialog_ctx { const char *const *keywords; const char *const *values; FingerprintType fptype_default; char **fingerprints; const char *keydisp; LPCTSTR iconid; const char *helpctx; }; static INT_PTR CALLBACK HostKeyMoreInfoProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_INITDIALOG: { const struct hostkey_dialog_ctx *ctx = (const struct hostkey_dialog_ctx *)lParam; SetWindowLongPtr(hwnd, GWLP_USERDATA, (INT_PTR)ctx); if (ctx->fingerprints[SSH_FPTYPE_SHA256]) SetDlgItemText(hwnd, IDC_HKI_SHA256, ctx->fingerprints[SSH_FPTYPE_SHA256]); if (ctx->fingerprints[SSH_FPTYPE_MD5]) SetDlgItemText(hwnd, IDC_HKI_MD5, ctx->fingerprints[SSH_FPTYPE_MD5]); SetDlgItemText(hwnd, IDA_TEXT, ctx->keydisp); return 1; } case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: EndDialog(hwnd, 0); return 0; } return 0; case WM_CLOSE: EndDialog(hwnd, 0); return 0; } return 0; } static INT_PTR CALLBACK HostKeyDialogProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_INITDIALOG: { strbuf *sb = strbuf_new(); const struct hostkey_dialog_ctx *ctx = (const struct hostkey_dialog_ctx *)lParam; SetWindowLongPtr(hwnd, GWLP_USERDATA, (INT_PTR)ctx); for (int id = 100;; id++) { char buf[256]; if (!GetDlgItemText(hwnd, id, buf, (int)lenof(buf))) break; strbuf_clear(sb); for (const char *p = buf; *p ;) { if (*p == '{') { for (size_t i = 0; ctx->keywords[i]; i++) { if (strstartswith(p, ctx->keywords[i])) { p += strlen(ctx->keywords[i]); put_datapl(sb, ptrlen_from_asciz(ctx->values[i])); goto matched; } } } else { put_byte(sb, *p++); } matched:; } SetDlgItemText(hwnd, id, sb->s); } strbuf_free(sb); SetDlgItemText(hwnd, IDC_HK_FINGERPRINT, ctx->fingerprints[ctx->fptype_default]); MakeDlgItemBorderless(hwnd, IDC_HK_FINGERPRINT); HANDLE icon = LoadImage( NULL, ctx->iconid, IMAGE_ICON, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON), LR_SHARED); SendDlgItemMessage(hwnd, IDC_HK_ICON, STM_SETICON, (WPARAM)icon, 0); if (!has_help()) { HWND item = GetDlgItem(hwnd, IDHELP); if (item) DestroyWindow(item); } return 1; } case WM_CTLCOLORSTATIC: { HDC hdc = (HDC)wParam; HWND control = (HWND)lParam; if (GetWindowLongPtr(control, GWLP_ID) == IDC_HK_TITLE) { SetBkMode(hdc, TRANSPARENT); HFONT prev_font = (HFONT)SelectObject( hdc, (HFONT)GetStockObject(SYSTEM_FONT)); LOGFONT lf; if (GetObject(prev_font, sizeof(lf), &lf)) { lf.lfWeight = FW_BOLD; lf.lfHeight = lf.lfHeight * 3 / 2; HFONT bold_font = CreateFontIndirect(&lf); if (bold_font) SelectObject(hdc, bold_font); } return (INT_PTR)GetSysColorBrush(COLOR_BTNFACE); } return 0; } case WM_COMMAND: switch (LOWORD(wParam)) { case IDC_HK_ACCEPT: case IDC_HK_ONCE: case IDCANCEL: EndDialog(hwnd, LOWORD(wParam)); return 0; case IDHELP: { const struct hostkey_dialog_ctx *ctx = (const struct hostkey_dialog_ctx *) GetWindowLongPtr(hwnd, GWLP_USERDATA); launch_help(hwnd, ctx->helpctx); return 0; } case IDC_HK_MOREINFO: { const struct hostkey_dialog_ctx *ctx = (const struct hostkey_dialog_ctx *) GetWindowLongPtr(hwnd, GWLP_USERDATA); DialogBoxParam(hinst, MAKEINTRESOURCE(IDD_HK_MOREINFO), hwnd, HostKeyMoreInfoProc, (LPARAM)ctx); } } return 0; case WM_CLOSE: EndDialog(hwnd, IDCANCEL); return 0; } return 0; } int win_seat_verify_ssh_host_key( Seat *seat, const char *host, int port, const char *keytype, char *keystr, const char *keydisp, char **fingerprints, void (*callback)(void *ctx, int result), void *ctx) { int ret; WinGuiSeat *wgs = container_of(seat, WinGuiSeat, seat); /* * Verify the key against the registry. */ ret = verify_host_key(host, port, keytype, keystr); if (ret == 0) /* success - key matched OK */ return 1; else { static const char *const keywords[] = { "{KEYTYPE}", "{APPNAME}", NULL }; const char *values[2]; values[0] = keytype; values[1] = appname; struct hostkey_dialog_ctx ctx[1]; ctx->keywords = keywords; ctx->values = values; ctx->fingerprints = fingerprints; ctx->fptype_default = ssh2_pick_default_fingerprint(fingerprints); ctx->keydisp = keydisp; ctx->iconid = (ret == 2 ? IDI_WARNING : IDI_QUESTION); ctx->helpctx = (ret == 2 ? WINHELP_CTX_errors_hostkey_changed : WINHELP_CTX_errors_hostkey_absent); int dlgid = (ret == 2 ? IDD_HK_WRONG : IDD_HK_ABSENT); int mbret = DialogBoxParam( hinst, MAKEINTRESOURCE(dlgid), wgs->term_hwnd, HostKeyDialogProc, (LPARAM)ctx); assert(mbret==IDC_HK_ACCEPT || mbret==IDC_HK_ONCE || mbret==IDCANCEL); if (mbret == IDC_HK_ACCEPT) { store_host_key(host, port, keytype, keystr); return 1; } else if (mbret == IDC_HK_ONCE) return 1; } return 0; /* abandon the connection */ } /* * Ask whether the selected algorithm is acceptable (since it was * below the configured 'warn' threshold). */ int win_seat_confirm_weak_crypto_primitive( Seat *seat, const char *algtype, const char *algname, void (*callback)(void *ctx, int result), void *ctx) { static const char mbtitle[] = "%s Security Alert"; static const char msg[] = "The first %s supported by the server\n" "is %s, which is below the configured\n" "warning threshold.\n" "Do you want to continue with this connection?\n"; char *message, *title; int mbret; message = dupprintf(msg, algtype, algname); title = dupprintf(mbtitle, appname); mbret = MessageBox(NULL, message, title, MB_ICONWARNING | MB_YESNO | MB_DEFBUTTON2); socket_reselect_all(); sfree(message); sfree(title); if (mbret == IDYES) return 1; else return 0; } int win_seat_confirm_weak_cached_hostkey( Seat *seat, const char *algname, const char *betteralgs, void (*callback)(void *ctx, int result), void *ctx) { static const char mbtitle[] = "%s Security Alert"; static const char msg[] = "The first host key type we have stored for this server\n" "is %s, which is below the configured warning threshold.\n" "The server also provides the following types of host key\n" "above the threshold, which we do not have stored:\n" "%s\n" "Do you want to continue with this connection?\n"; char *message, *title; int mbret; message = dupprintf(msg, algname, betteralgs); title = dupprintf(mbtitle, appname); mbret = MessageBox(NULL, message, title, MB_ICONWARNING | MB_YESNO | MB_DEFBUTTON2); socket_reselect_all(); sfree(message); sfree(title); if (mbret == IDYES) return 1; else 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). */ static int win_gui_askappend(LogPolicy *lp, 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(); sfree(message); sfree(mbtitle); if (mbret == IDYES) return 2; else if (mbret == IDNO) return 1; else return 0; } const LogPolicyVtable win_gui_logpolicy_vt = { .eventlog = win_gui_eventlog, .askappend = win_gui_askappend, .logging_error = win_gui_logging_error, .verbose = null_lp_verbose_yes, }; /* * Warn about the obsolescent key file format. * * Uniquely among these functions, this one does _not_ expect a * frontend handle. This means that if PuTTY is ported to a * platform which requires frontend handles, this function will be * an anomaly. Fortunately, the problem it addresses will not have * been present on that platform, so it can plausibly be * implemented as an empty function. */ void old_keyfile_warning(void) { static const char mbtitle[] = "%s Key File Warning"; static const char message[] = "You are loading an SSH-2 private key which has an\n" "old version of the file format. This means your key\n" "file is not fully tamperproof. Future versions of\n" "%s may stop supporting this private key format,\n" "so we recommend you convert your key to the new\n" "format.\n" "\n" "You can perform this conversion by loading the key\n" "into PuTTYgen and then saving it again."; char *msg, *title; msg = dupprintf(message, appname); title = dupprintf(mbtitle, appname); MessageBox(NULL, msg, title, MB_OK); socket_reselect_all(); sfree(msg); sfree(title); } putty-0.76/windows/window.c0000644000175000017500000061033714072266314012740 00000000000000/* * window.c - the PuTTY(tel) main program, which runs a PuTTY terminal * emulator and backend in a window. */ #include #include #include #include #include #include #ifdef __WINE__ #define NO_MULTIMON /* winelib doesn't have this */ #endif #ifndef NO_MULTIMON #define COMPILE_MULTIMON_STUBS #endif #include "putty.h" #include "terminal.h" #include "storage.h" #include "win_res.h" #include "winsecur.h" #include "winseat.h" #include "tree234.h" #ifndef NO_MULTIMON #include #endif #include #include #include #include /* From MSDN: In the WM_SYSCOMMAND message, the four low-order bits of * wParam are used by Windows, and should be masked off, so we shouldn't * attempt to store information in them. Hence all these identifiers have * the low 4 bits clear. Also, identifiers should < 0xF000. */ #define IDM_SHOWLOG 0x0010 #define IDM_NEWSESS 0x0020 #define IDM_DUPSESS 0x0030 #define IDM_RESTART 0x0040 #define IDM_RECONF 0x0050 #define IDM_CLRSB 0x0060 #define IDM_RESET 0x0070 #define IDM_HELP 0x0140 #define IDM_ABOUT 0x0150 #define IDM_SAVEDSESS 0x0160 #define IDM_COPYALL 0x0170 #define IDM_FULLSCREEN 0x0180 #define IDM_COPY 0x0190 #define IDM_PASTE 0x01A0 #define IDM_SPECIALSEP 0x0200 #define IDM_SPECIAL_MIN 0x0400 #define IDM_SPECIAL_MAX 0x0800 #define IDM_SAVED_MIN 0x1000 #define IDM_SAVED_MAX 0x5000 #define MENU_SAVED_STEP 16 /* Maximum number of sessions on saved-session submenu */ #define MENU_SAVED_MAX ((IDM_SAVED_MAX-IDM_SAVED_MIN) / MENU_SAVED_STEP) #define WM_IGNORE_CLIP (WM_APP + 2) #define WM_FULLSCR_ON_MAX (WM_APP + 3) #define WM_GOT_CLIPDATA (WM_APP + 4) /* Needed for Chinese support and apparently not always defined. */ #ifndef VK_PROCESSKEY #define VK_PROCESSKEY 0xE5 #endif /* Mouse wheel support. */ #ifndef WM_MOUSEWHEEL #define WM_MOUSEWHEEL 0x020A /* not defined in earlier SDKs */ #endif #ifndef WHEEL_DELTA #define WHEEL_DELTA 120 #endif /* DPI awareness support */ #ifndef WM_DPICHANGED #define WM_DPICHANGED 0x02E0 #define WM_DPICHANGED_BEFOREPARENT 0x02E2 #define WM_DPICHANGED_AFTERPARENT 0x02E3 #define WM_GETDPISCALEDSIZE 0x02E4 #endif /* VK_PACKET, used to send Unicode characters in WM_KEYDOWNs */ #ifndef VK_PACKET #define VK_PACKET 0xE7 #endif static Mouse_Button translate_button(Mouse_Button button); static void show_mouseptr(bool show); static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); static int TranslateKey(UINT message, WPARAM wParam, LPARAM lParam, unsigned char *output); static void init_palette(void); static void init_fonts(int, int); static void init_dpi_info(void); 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 bool is_full_screen(void); static void make_full_screen(void); static void clear_full_screen(void); static void flip_full_screen(void); static void process_clipdata(HGLOBAL clipdata, bool unicode); static void setup_clipboards(Terminal *, Conf *); /* Window layout information */ static void reset_window(int); static int extra_width, extra_height; static int font_width, font_height; static bool font_dualwidth, font_varpitch; static int offset_width, offset_height; static bool was_zoomed = false; static int prev_rows, prev_cols; static void flash_window(int mode); static void sys_cursor_update(void); static bool get_fullscreen_rect(RECT * ss); static int caret_x = -1, caret_y = -1; static int kbd_codepage; static Ldisc *ldisc; static Backend *backend; static struct unicode_data ucsdata; static bool session_closed; static bool reconfiguring = false; static const SessionSpecial *specials = NULL; static HMENU specials_menu = NULL; static int n_specials = 0; #define TIMING_TIMER_ID 1234 static long timing_next_time; static struct { HMENU menu; } popup_menus[2]; enum { SYSMENU, CTXMENU }; static HMENU savedsess_menu; static Conf *conf; static LogContext *logctx; static Terminal *term; struct wm_netevent_params { /* Used to pass data to wm_netevent_callback */ WPARAM wParam; LPARAM lParam; }; static void conf_cache_data(void); static int cursor_type; static int vtmode; static struct sesslist sesslist; /* for saved-session menu */ #define FONT_NORMAL 0 #define FONT_BOLD 1 #define FONT_UNDERLINE 2 #define FONT_BOLDUND 3 #define FONT_WIDE 0x04 #define FONT_HIGH 0x08 #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 bool fontflag[FONT_MAXNO]; static enum { BOLD_NONE, BOLD_SHADOW, BOLD_FONT } bold_font_mode; static bool bold_colours; static enum { UND_LINE, UND_FONT } und_mode; static int descent, font_strikethrough_y; static COLORREF colours[OSC4_NCOLOURS]; static HPALETTE pal; static LPLOGPALETTE logpal; bool tried_pal = false; COLORREF colorref_modifier = 0; enum MONITOR_DPI_TYPE { MDT_EFFECTIVE_DPI, MDT_ANGULAR_DPI, MDT_RAW_DPI, MDT_DEFAULT }; DECL_WINDOWS_FUNCTION(static, HRESULT, GetDpiForMonitor, (HMONITOR hmonitor, enum MONITOR_DPI_TYPE dpiType, UINT *dpiX, UINT *dpiY)); DECL_WINDOWS_FUNCTION(static, HRESULT, GetSystemMetricsForDpi, (int nIndex, UINT dpi)); DECL_WINDOWS_FUNCTION(static, HRESULT, AdjustWindowRectExForDpi, (LPRECT lpRect, DWORD dwStyle, BOOL bMenu, DWORD dwExStyle, UINT dpi)); static struct _dpi_info { POINT cur_dpi; RECT new_wnd_rect; } dpi_info; static HBITMAP caretbm; static int dbltime, lasttime, lastact; static Mouse_Button lastbtn; /* this allows xterm-style mouse handling. */ static bool send_raw_mouse = false; static int wheel_accumulator = 0; static bool pointer_indicates_raw_mouse = false; static BusyStatus busy_status = BUSY_NOT; 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 */ static bool wintw_setup_draw_ctx(TermWin *); static void wintw_draw_text(TermWin *, int x, int y, wchar_t *text, int len, unsigned long attrs, int lattrs, truecolour tc); static void wintw_draw_cursor(TermWin *, int x, int y, wchar_t *text, int len, unsigned long attrs, int lattrs, truecolour tc); static void wintw_draw_trust_sigil(TermWin *, int x, int y); static int wintw_char_width(TermWin *, int uc); static void wintw_free_draw_ctx(TermWin *); static void wintw_set_cursor_pos(TermWin *, int x, int y); static void wintw_set_raw_mouse_mode(TermWin *, bool enable); static void wintw_set_raw_mouse_mode_pointer(TermWin *, bool enable); static void wintw_set_scrollbar(TermWin *, int total, int start, int page); static void wintw_bell(TermWin *, int mode); static void wintw_clip_write( TermWin *, int clipboard, wchar_t *text, int *attrs, truecolour *colours, int len, bool must_deselect); static void wintw_clip_request_paste(TermWin *, int clipboard); static void wintw_refresh(TermWin *); static void wintw_request_resize(TermWin *, int w, int h); static void wintw_set_title(TermWin *, const char *title); static void wintw_set_icon_title(TermWin *, const char *icontitle); static void wintw_set_minimised(TermWin *, bool minimised); static void wintw_set_maximised(TermWin *, bool maximised); static void wintw_move(TermWin *, int x, int y); static void wintw_set_zorder(TermWin *, bool top); static void wintw_palette_set(TermWin *, unsigned, unsigned, const rgb *); static void wintw_palette_get_overrides(TermWin *, Terminal *); static const TermWinVtable windows_termwin_vt = { .setup_draw_ctx = wintw_setup_draw_ctx, .draw_text = wintw_draw_text, .draw_cursor = wintw_draw_cursor, .draw_trust_sigil = wintw_draw_trust_sigil, .char_width = wintw_char_width, .free_draw_ctx = wintw_free_draw_ctx, .set_cursor_pos = wintw_set_cursor_pos, .set_raw_mouse_mode = wintw_set_raw_mouse_mode, .set_raw_mouse_mode_pointer = wintw_set_raw_mouse_mode_pointer, .set_scrollbar = wintw_set_scrollbar, .bell = wintw_bell, .clip_write = wintw_clip_write, .clip_request_paste = wintw_clip_request_paste, .refresh = wintw_refresh, .request_resize = wintw_request_resize, .set_title = wintw_set_title, .set_icon_title = wintw_set_icon_title, .set_minimised = wintw_set_minimised, .set_maximised = wintw_set_maximised, .move = wintw_move, .set_zorder = wintw_set_zorder, .palette_set = wintw_palette_set, .palette_get_overrides = wintw_palette_get_overrides, }; static TermWin wintw[1]; static HDC wintw_hdc; static HICON trust_icon = INVALID_HANDLE_VALUE; const bool share_can_be_downstream = true; const bool share_can_be_upstream = true; static bool is_utf8(void) { return ucsdata.line_codepage == CP_UTF8; } static bool win_seat_is_utf8(Seat *seat) { return is_utf8(); } static char *win_seat_get_ttymode(Seat *seat, const char *mode) { return term_get_ttymode(term, mode); } static StripCtrlChars *win_seat_stripctrl_new( Seat *seat, BinarySink *bs_out, SeatInteractionContext sic) { return stripctrl_new_term(bs_out, false, 0, term); } static size_t win_seat_output( Seat *seat, bool is_stderr, const void *, size_t); static bool win_seat_eof(Seat *seat); static int win_seat_get_userpass_input( Seat *seat, prompts_t *p, bufchain *input); static void win_seat_notify_remote_exit(Seat *seat); static void win_seat_connection_fatal(Seat *seat, const char *msg); static void win_seat_update_specials_menu(Seat *seat); static void win_seat_set_busy_status(Seat *seat, BusyStatus status); static bool win_seat_set_trust_status(Seat *seat, bool trusted); static bool win_seat_get_cursor_position(Seat *seat, int *x, int *y); static bool win_seat_get_window_pixel_size(Seat *seat, int *x, int *y); static const SeatVtable win_seat_vt = { .output = win_seat_output, .eof = win_seat_eof, .get_userpass_input = win_seat_get_userpass_input, .notify_remote_exit = win_seat_notify_remote_exit, .connection_fatal = win_seat_connection_fatal, .update_specials_menu = win_seat_update_specials_menu, .get_ttymode = win_seat_get_ttymode, .set_busy_status = win_seat_set_busy_status, .verify_ssh_host_key = win_seat_verify_ssh_host_key, .confirm_weak_crypto_primitive = win_seat_confirm_weak_crypto_primitive, .confirm_weak_cached_hostkey = win_seat_confirm_weak_cached_hostkey, .is_utf8 = win_seat_is_utf8, .echoedit_update = nullseat_echoedit_update, .get_x_display = nullseat_get_x_display, .get_windowid = nullseat_get_windowid, .get_window_pixel_size = win_seat_get_window_pixel_size, .stripctrl_new = win_seat_stripctrl_new, .set_trust_status = win_seat_set_trust_status, .verbose = nullseat_verbose_yes, .interactive = nullseat_interactive_yes, .get_cursor_position = win_seat_get_cursor_position, }; static WinGuiSeat wgs = { .seat.vt = &win_seat_vt, .logpolicy.vt = &win_gui_logpolicy_vt }; static void start_backend(void) { const struct BackendVtable *vt; char *error, *realhost; int i; /* * Select protocol. This is farmed out into a table in a * separate file to enable an ssh-free variant. */ vt = backend_vt_from_proto(conf_get_int(conf, CONF_protocol)); if (!vt) { char *str = dupprintf("%s Internal Error", appname); MessageBox(NULL, "Unsupported protocol number found", str, MB_OK | MB_ICONEXCLAMATION); sfree(str); cleanup_exit(1); } seat_set_trust_status(&wgs.seat, true); error = backend_init(vt, &wgs.seat, &backend, logctx, conf, conf_get_str(conf, CONF_host), conf_get_int(conf, CONF_port), &realhost, conf_get_bool(conf, CONF_tcp_nodelay), conf_get_bool(conf, CONF_tcp_keepalives)); if (error) { char *str = dupprintf("%s Error", appname); char *msg = dupprintf("Unable to open connection to\n%s\n%s", conf_dest(conf), error); sfree(error); MessageBox(NULL, msg, str, MB_ICONERROR | MB_OK); sfree(str); sfree(msg); exit(0); } term_setup_window_titles(term, realhost); sfree(realhost); /* * Connect the terminal to the backend for resize purposes. */ term_provide_backend(term, backend); /* * Set up a line discipline. */ ldisc = ldisc_create(conf, term, backend, &wgs.seat); /* * 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 *newtitle; int i; session_closed = true; newtitle = dupprintf("%s (inactive)", appname); win_set_icon_title(wintw, newtitle); win_set_title(wintw, newtitle); sfree(newtitle); if (ldisc) { ldisc_free(ldisc); ldisc = NULL; } if (backend) { backend_free(backend); backend = NULL; term_provide_backend(term, NULL); seat_update_specials_menu(&wgs.seat); } /* * Show the Restart Session menu item. Do a precautionary * 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"); } } const unsigned cmdline_tooltype = TOOLTYPE_HOST_ARG | TOOLTYPE_PORT_ARG | TOOLTYPE_NO_VERBOSE_OPTION; HINSTANCE hinst; int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { MSG msg; HRESULT hr; int guess_width, guess_height; dll_hijacking_protection(); hinst = inst; sk_init(); init_common_controls(); /* Set Explicit App User Model Id so that jump lists don't cause PuTTY to hang on to removable media. */ set_explicit_app_user_model_id(); /* Ensure a Maximize setting in Explorer doesn't maximise the * config box. */ defuse_showwindow(); init_winver(); /* * If we're running a version of Windows that doesn't support * WM_MOUSEWHEEL, find out what message number we should be * using instead. */ if (osMajorVersion < 4 || (osMajorVersion == 4 && osPlatformId != 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); MessageBox(NULL, "Failed to initialize COM subsystem", str, MB_OK | MB_ICONEXCLAMATION); sfree(str); return 1; } /* * Process the command line. */ { char *p; bool special_launchable_argument = false; settings_set_default_protocol(be_default_protocol); /* Find the appropriate default port. */ { const struct BackendVtable *vt = backend_vt_from_proto(be_default_protocol); settings_set_default_port(0); /* illegal */ if (vt) settings_set_default_port(vt->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 * the internal-use-only &sharedmemoryhandle, plus the &R * prefix for -restrict-acl, all of which are used by PuTTYs * auto-launching each other via System-menu options. */ while (*p && isspace(*p)) p++; if (*p == '&' && p[1] == 'R' && (!p[2] || p[2] == '@' || p[2] == '&')) { /* &R restrict-acl prefix */ restrict_process_acl(); p += 2; } if (*p == '@') { /* * An initial @ means that the whole of the rest of the * command line should be treated as the name of a saved * session, with _no quoting or escaping_. This makes it a * 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(conf)) { cleanup_exit(0); } special_launchable_argument = true; } 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) { BinarySource src[1]; BinarySource_BARE_INIT(src, cp, cpsize); if (!conf_deserialise(conf, src)) modalfatalbox("Serialised configuration data was invalid"); UnmapViewOfFile(cp); CloseHandle(filemap); } else if (!do_config(conf)) { cleanup_exit(0); } special_launchable_argument = true; } else if (!*p) { /* Do-nothing case for an empty command line - or rather, * for a command line that's empty _after_ we strip off * the &R prefix. */ } else { /* * Otherwise, break up the command line and deal with * it sensibly. */ int argc, i; char **argv; 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 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; const struct BackendVtable *vt = backend_vt_from_proto(be_default_protocol); bool resize_forbidden = false; if (vt && vt->flags & BACKEND_RESIZE_FORBIDDEN) resize_forbidden = true; wchar_t *uappname = dup_mb_to_wc(DEFAULT_CODEPAGE, 0, appname); if (!conf_get_bool(conf, CONF_scrollbar)) winmode &= ~(WS_VSCROLL); if (conf_get_int(conf, CONF_resize_action) == RESIZE_DISABLED || resize_forbidden) winmode &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX); if (conf_get_bool(conf, CONF_alwaysontop)) exwinmode |= WS_EX_TOPMOST; if (conf_get_bool(conf, CONF_sunken_edge)) exwinmode |= WS_EX_CLIENTEDGE; wgs.term_hwnd = CreateWindowExW( exwinmode, uappname, uappname, winmode, CW_USEDEFAULT, CW_USEDEFAULT, guess_width, guess_height, NULL, NULL, inst, NULL); memset(&dpi_info, 0, sizeof(struct _dpi_info)); init_dpi_info(); sfree(uappname); } /* * Initialise the fonts, simultaneously correcting the guesses * for font_{width,height}. */ init_fonts(0,0); /* * Prepare a logical palette. */ init_palette(); /* * 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.) */ wintw->vt = &windows_termwin_vt; term = term_init(conf, &ucsdata, wintw); setup_clipboards(term, conf); logctx = log_init(&wgs.logpolicy, 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(wgs.term_hwnd, &wr); GetClientRect(wgs.term_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. */ guess_width = extra_width + font_width * term->cols; guess_height = extra_height + font_height * term->rows; SetWindowPos(wgs.term_hwnd, NULL, 0, 0, guess_width, guess_height, SWP_NOMOVE | SWP_NOREDRAW | SWP_NOZORDER); /* * Set up a caret bitmap, with no content. */ { char *bits; int size = (font_width + 15) / 16 * 2 * font_height; bits = snewn(size, char); memset(bits, 0, size); caretbm = CreateBitmap(font_width, font_height, 1, 1, bits); sfree(bits); } CreateCaret(wgs.term_hwnd, caretbm, font_width, font_height); /* * Initialise the scroll bar. */ { SCROLLINFO si; si.cbSize = sizeof(si); si.fMask = SIF_ALL | SIF_DISABLENOSCROLL; si.nMin = 0; si.nMax = term->rows - 1; si.nPage = term->rows; si.nPos = 0; SetScrollInfo(wgs.term_hwnd, SB_VERT, &si, false); } /* * Prepare the mouse handler. */ lastact = MA_NOTHING; lastbtn = MBT_NOTHING; dbltime = GetDoubleClickTime(); /* * Set up the session-control options on the system menu. */ { HMENU m; int j; char *str; popup_menus[SYSMENU].menu = GetSystemMenu(wgs.term_hwnd, false); popup_menus[CTXMENU].menu = CreatePopupMenu(); AppendMenu(popup_menus[CTXMENU].menu, MF_ENABLED, IDM_COPY, "&Copy"); AppendMenu(popup_menus[CTXMENU].menu, MF_ENABLED, IDM_PASTE, "&Paste"); savedsess_menu = CreateMenu(); get_sesslist(&sesslist, true); update_savedsess_menu(); for (j = 0; j < lenof(popup_menus); j++) { m = popup_menus[j].menu; AppendMenu(m, MF_SEPARATOR, 0, 0); AppendMenu(m, MF_ENABLED, IDM_SHOWLOG, "&Event Log"); AppendMenu(m, MF_SEPARATOR, 0, 0); AppendMenu(m, MF_ENABLED, IDM_NEWSESS, "Ne&w Session..."); AppendMenu(m, MF_ENABLED, IDM_DUPSESS, "&Duplicate Session"); AppendMenu(m, MF_POPUP | MF_ENABLED, (UINT_PTR) savedsess_menu, "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); } } if (restricted_acl()) { lp_eventlog(&wgs.logpolicy, "Running with restricted process ACL"); } winselgui_set_hwnd(wgs.term_hwnd); start_backend(); /* * Set up the initial input locale. */ set_input_locale(GetKeyboardLayout(0)); /* * Finally show the window! */ ShowWindow(wgs.term_hwnd, show); SetForegroundWindow(wgs.term_hwnd); term_set_focus(term, GetForegroundWindow() == wgs.term_hwnd); UpdateWindow(wgs.term_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() == wgs.term_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); while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message == WM_QUIT) goto finished; /* two-level break */ HWND logbox = event_log_window(); if (!(IsWindow(logbox) && IsDialogMessage(logbox, &msg))) DispatchMessageW(&msg); /* * WM_NETEVENT messages seem to jump ahead of others in * the message queue. I'm not sure why; the docs for * PeekMessage mention that messages are prioritised in * some way, but I'm unclear on which priorities go where. * * Anyway, in practice I observe that WM_NETEVENT seems to * jump to the head of the queue, which means that if we * were to only process one message every time round this * loop, we'd get nothing but NETEVENTs if the server * flooded us with data, and stop responding to any other * kind of window message. So instead, we keep on round * this loop until we've consumed at least one message * that _isn't_ a NETEVENT, or run out of messages * completely (whichever comes first). And we don't go to * run_toplevel_callbacks (which is where the netevents * are actually processed, causing fresh NETEVENT messages * to appear) until we've done this. */ if (msg.message != WM_NETEVENT) break; } run_toplevel_callbacks(); } finished: cleanup_exit(msg.wParam); /* this doesn't return... */ return msg.wParam; /* ... but optimiser doesn't know */ } static void setup_clipboards(Terminal *term, Conf *conf) { assert(term->mouse_select_clipboards[0] == CLIP_LOCAL); term->n_mouse_select_clipboards = 1; if (conf_get_bool(conf, CONF_mouseautocopy)) { term->mouse_select_clipboards[ term->n_mouse_select_clipboards++] = CLIP_SYSTEM; } switch (conf_get_int(conf, CONF_mousepaste)) { case CLIPUI_IMPLICIT: term->mouse_paste_clipboard = CLIP_LOCAL; break; case CLIPUI_EXPLICIT: term->mouse_paste_clipboard = CLIP_SYSTEM; break; default: term->mouse_paste_clipboard = CLIP_NULL; break; } } /* * Clean up and exit. */ void cleanup_exit(int code) { /* * Clean up. */ deinit_fonts(); sfree(logpal); if (pal) DeleteObject(pal); sk_cleanup(); if (conf_get_int(conf, CONF_protocol) == PROT_SSH) { random_save_seed(); } shutdown_help(); /* Clean up COM. */ CoUninitialize(); exit(code); } /* * Refresh the saved-session submenu from `sesslist'. */ static void update_savedsess_menu(void) { int i; while (DeleteMenu(savedsess_menu, 0, MF_BYPOSITION)) ; /* skip sesslist.sessions[0] == Default Settings */ for (i = 1; i < ((sesslist.nsessions <= MENU_SAVED_MAX+1) ? sesslist.nsessions : MENU_SAVED_MAX+1); i++) AppendMenu(savedsess_menu, MF_ENABLED, IDM_SAVED_MIN + (i-1)*MENU_SAVED_STEP, sesslist.sessions[i]); if (sesslist.nsessions <= 1) AppendMenu(savedsess_menu, MF_GRAYED, IDM_SAVED_MIN, "(No sessions)"); } /* * Update the Special Commands submenu. */ static void win_seat_update_specials_menu(Seat *seat) { HMENU new_menu; int i, j; if (backend) specials = backend_get_specials(backend); else specials = NULL; if (specials) { /* We can't use Windows to provide a stack for submenus, so * here's a lame "stack" that will do for now. */ HMENU saved_menu = NULL; int nesting = 1; new_menu = CreatePopupMenu(); for (i = 0; nesting > 0; i++) { assert(IDM_SPECIAL_MIN + 0x10 * i < IDM_SPECIAL_MAX); switch (specials[i].code) { case SS_SEP: AppendMenu(new_menu, MF_SEPARATOR, 0, 0); break; case SS_SUBMENU: assert(nesting < 2); nesting++; saved_menu = new_menu; /* XXX lame stacking */ new_menu = CreatePopupMenu(); AppendMenu(saved_menu, MF_POPUP | MF_ENABLED, (UINT_PTR) new_menu, specials[i].name); break; case SS_EXITMENU: nesting--; if (nesting) { new_menu = saved_menu; /* XXX lame stacking */ saved_menu = NULL; } break; default: AppendMenu(new_menu, MF_ENABLED, IDM_SPECIAL_MIN + 0x10 * i, specials[i].name); break; } } /* Squirrel the highest special. */ n_specials = i - 1; } else { new_menu = NULL; n_specials = 0; } for (j = 0; j < lenof(popup_menus); j++) { if (specials_menu) { /* XXX does this free up all submenus? */ DeleteMenu(popup_menus[j].menu, (UINT_PTR)specials_menu, MF_BYCOMMAND); DeleteMenu(popup_menus[j].menu, IDM_SPECIALSEP, MF_BYCOMMAND); } if (new_menu) { InsertMenu(popup_menus[j].menu, IDM_SHOWLOG, MF_BYCOMMAND | MF_POPUP | MF_ENABLED, (UINT_PTR) new_menu, "S&pecial Command"); InsertMenu(popup_menus[j].menu, IDM_SHOWLOG, MF_BYCOMMAND | MF_SEPARATOR, IDM_SPECIALSEP, 0); } } specials_menu = new_menu; } static void update_mouse_pointer(void) { LPTSTR curstype = NULL; bool force_visible = false; static bool forced_visible = false; switch (busy_status) { case BUSY_NOT: if (pointer_indicates_raw_mouse) curstype = IDC_ARROW; else curstype = IDC_IBEAM; break; case BUSY_WAITING: curstype = IDC_APPSTARTING; /* this may be an abuse */ force_visible = true; break; case BUSY_CPU: curstype = IDC_WAIT; force_visible = true; break; default: unreachable("Bad busy_status"); } { HCURSOR cursor = LoadCursor(NULL, curstype); SetClassLongPtr(wgs.term_hwnd, GCLP_HCURSOR, (LONG_PTR)cursor); SetCursor(cursor); /* force redraw of cursor at current posn */ } if (force_visible != forced_visible) { /* We want some cursor shapes to be visible always. * Along with show_mouseptr(), this manages the ShowCursor() * counter such that if we switch back to a non-force_visible * cursor, the previous visibility state is restored. */ ShowCursor(force_visible); forced_visible = force_visible; } } static void win_seat_set_busy_status(Seat *seat, BusyStatus status) { busy_status = status; update_mouse_pointer(); } static void wintw_set_raw_mouse_mode(TermWin *tw, bool activate) { send_raw_mouse = activate; } static void wintw_set_raw_mouse_mode_pointer(TermWin *tw, bool activate) { pointer_indicates_raw_mouse = activate; update_mouse_pointer(); } /* * Print a message box and close the connection. */ static void win_seat_connection_fatal(Seat *seat, const char *msg) { char *title = dupprintf("%s Fatal Error", appname); show_mouseptr(true); MessageBox(wgs.term_hwnd, msg, title, MB_ICONERROR | MB_OK); sfree(title); 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(const char *fmt, ...) { va_list ap; char *message, *title; va_start(ap, fmt); message = dupvprintf(fmt, ap); va_end(ap); title = dupprintf("%s Command Line Error", appname); MessageBox(wgs.term_hwnd, message, title, MB_ICONERROR | MB_OK); sfree(message); sfree(title); 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); } static inline rgb rgb_from_colorref(COLORREF cr) { rgb toret; toret.r = GetRValue(cr); toret.g = GetGValue(cr); toret.b = GetBValue(cr); return toret; } static void wintw_palette_get_overrides(TermWin *tw, Terminal *term) { if (conf_get_bool(conf, CONF_system_colour)) { rgb rgb; rgb = rgb_from_colorref(GetSysColor(COLOR_WINDOWTEXT)); term_palette_override(term, OSC4_COLOUR_fg, rgb); term_palette_override(term, OSC4_COLOUR_fg_bold, rgb); rgb = rgb_from_colorref(GetSysColor(COLOR_WINDOW)); term_palette_override(term, OSC4_COLOUR_bg, rgb); term_palette_override(term, OSC4_COLOUR_bg_bold, rgb); rgb = rgb_from_colorref(GetSysColor(COLOR_HIGHLIGHTTEXT)); term_palette_override(term, OSC4_COLOUR_cursor_fg, rgb); rgb = rgb_from_colorref(GetSysColor(COLOR_HIGHLIGHT)); term_palette_override(term, OSC4_COLOUR_cursor_bg, rgb); } } /* * This is a wrapper to ExtTextOut() to force Windows to display * the precise glyphs we give it. Otherwise it would do its own * bidi and Arabic shaping, and we would end up uncertain which * characters it had put where. */ static void exact_textout(HDC hdc, int x, int y, CONST RECT *lprc, unsigned short *lpString, UINT cbCount, CONST INT *lpDx, bool opaque) { #ifdef __LCC__ /* * The LCC include files apparently don't supply the * GCP_RESULTSW type, but we can make do with GCP_RESULTS * proper: the differences aren't important to us (the only * variable-width string parameter is one we don't use anyway). */ GCP_RESULTS gcpr; #else GCP_RESULTSW gcpr; #endif char *buffer = snewn(cbCount*2+2, char); char *classbuffer = snewn(cbCount, char); memset(&gcpr, 0, sizeof(gcpr)); memset(buffer, 0, cbCount*2+2); memset(classbuffer, GCPCLASS_NEUTRAL, cbCount); gcpr.lStructSize = sizeof(gcpr); gcpr.lpGlyphs = (void *)buffer; gcpr.lpClass = (void *)classbuffer; gcpr.nGlyphs = cbCount; GetCharacterPlacementW(hdc, lpString, cbCount, 0, &gcpr, FLI_MASK | GCP_CLASSIN | GCP_DIACRITIC); ExtTextOut(hdc, x, y, ETO_GLYPH_INDEX | ETO_CLIPPED | (opaque ? ETO_OPAQUE : 0), lprc, buffer, cbCount, lpDx); } /* * The exact_textout() wrapper, unfortunately, destroys the useful * Windows `font linking' behaviour: automatic handling of Unicode * code points not supported in this font by falling back to a font * which does contain them. Therefore, we adopt a multi-layered * approach: for any potentially-bidi text, we use exact_textout(), * and for everything else we use a simple ExtTextOut as we did * before exact_textout() was introduced. */ static void general_textout(HDC hdc, int x, int y, CONST RECT *lprc, unsigned short *lpString, UINT cbCount, CONST INT *lpDx, bool opaque) { int i, j, xp, xn; int bkmode = 0; bool got_bkmode = false; xp = xn = x; for (i = 0; i < (int)cbCount ;) { bool rtl = is_rtl(lpString[i]); xn += lpDx[i]; for (j = i+1; j < (int)cbCount; j++) { if (rtl != is_rtl(lpString[j])) break; xn += lpDx[j]; } /* * Now [i,j) indicates a maximal substring of lpString * which should be displayed using the same textout * function. */ if (rtl) { exact_textout(hdc, xp, y, lprc, lpString+i, j-i, font_varpitch ? NULL : lpDx+i, opaque); } else { ExtTextOutW(hdc, xp, y, ETO_CLIPPED | (opaque ? ETO_OPAQUE : 0), lprc, lpString+i, j-i, font_varpitch ? NULL : lpDx+i); } i = j; xp = xn; bkmode = GetBkMode(hdc); got_bkmode = true; SetBkMode(hdc, TRANSPARENT); opaque = false; } if (got_bkmode) SetBkMode(hdc, bkmode); } static int get_font_width(HDC hdc, const TEXTMETRIC *tm) { int ret; /* Note that the TMPF_FIXED_PITCH bit is defined upside down :-( */ if (!(tm->tmPitchAndFamily & TMPF_FIXED_PITCH)) { ret = tm->tmAveCharWidth; } else { #define FIRST '0' #define LAST '9' ABCFLOAT widths[LAST-FIRST + 1]; int j; font_varpitch = true; font_dualwidth = true; if (GetCharABCWidthsFloat(hdc, FIRST, LAST, widths)) { ret = 0; for (j = 0; j < lenof(widths); j++) { int width = (int)(0.5 + widths[j].abcfA + widths[j].abcfB + widths[j].abcfC); if (ret < width) ret = width; } } else { ret = tm->tmMaxCharWidth; } #undef FIRST #undef LAST } return ret; } static void init_dpi_info(void) { if (dpi_info.cur_dpi.x == 0 || dpi_info.cur_dpi.y == 0) { if (p_GetDpiForMonitor) { UINT dpiX, dpiY; HMONITOR currentMonitor = MonitorFromWindow( wgs.term_hwnd, MONITOR_DEFAULTTOPRIMARY); if (p_GetDpiForMonitor(currentMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY) == S_OK) { dpi_info.cur_dpi.x = (int)dpiX; dpi_info.cur_dpi.y = (int)dpiY; } } /* Fall back to system DPI */ if (dpi_info.cur_dpi.x == 0 || dpi_info.cur_dpi.y == 0) { HDC hdc = GetDC(wgs.term_hwnd); dpi_info.cur_dpi.x = GetDeviceCaps(hdc, LOGPIXELSX); dpi_info.cur_dpi.y = GetDeviceCaps(hdc, LOGPIXELSY); ReleaseDC(wgs.term_hwnd, hdc); } } } /* * Initialise all the fonts we will need initially. There may be as many as * three or as few as one. The other (potentially) twenty-one fonts are done * if/when they are needed. * * We also: * * - check the font width and height, correcting our guesses if * necessary. * * - verify that the bold font is the same width as the ordinary * one, and engage shadow bolding if not. * * - verify that the underlined font is the same width as the * ordinary one (manual underlining by means of line drawing can * be done in a pinch). * * - find a trust sigil icon that will look OK with the chosen font. */ static void init_fonts(int pick_width, int pick_height) { TEXTMETRIC tm; OUTLINETEXTMETRIC otm; 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(wgs.term_hwnd); if (pick_height) font_height = pick_height; else { font_height = font->height; if (font_height > 0) { font_height = -MulDiv(font_height, dpi_info.cur_dpi.y, 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); if (GetOutlineTextMetrics(hdc, sizeof(otm), &otm)) font_strikethrough_y = tm.tmAscent - otm.otmsStrikeoutPosition; else font_strikethrough_y = tm.tmAscent - (tm.tmAscent * 3 / 8); GetObject(fonts[FONT_NORMAL], sizeof(LOGFONT), &lfont); /* Note that the TMPF_FIXED_PITCH bit is defined upside down :-( */ if (!(tm.tmPitchAndFamily & TMPF_FIXED_PITCH)) { font_varpitch = false; font_dualwidth = (tm.tmAveCharWidth != tm.tmMaxCharWidth); } else { font_varpitch = true; font_dualwidth = true; } if (pick_width == 0 || pick_height == 0) { font_height = tm.tmHeight; font_width = get_font_width(hdc, &tm); } #ifdef RDB_DEBUG_PATCH debug("Primary font H=%d, AW=%d, MW=%d\n", tm.tmHeight, tm.tmAveCharWidth, tm.tmMaxCharWidth); #endif { CHARSETINFO info; DWORD cset = tm.tmCharSet; memset(&info, 0xFF, sizeof(info)); /* !!! Yes the next line is right */ if (cset == OEM_CHARSET) ucsdata.font_codepage = GetOEMCP(); else if (TranslateCharsetInfo ((DWORD *)(ULONG_PTR)cset, &info, TCI_SRCCHARSET)) ucsdata.font_codepage = info.ciACP; 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 * flag that causes underlines to be drawn by hand. * * Having tried other more sophisticated approaches (such * as examining the TEXTMETRIC structure or requesting the * height of a string), I think we'll do this the brute * force way: we create a small bitmap, draw an underlined * space on it, and test to see whether any pixels are * foreground-coloured. (Since we expect the underline to * go all the way across the character cell, we only search * down a single column of the bitmap, half way across.) */ { HDC und_dc; HBITMAP und_bm, und_oldbm; int i; bool gotit; COLORREF c; und_dc = CreateCompatibleDC(hdc); und_bm = CreateCompatibleBitmap(hdc, font_width, font_height); und_oldbm = SelectObject(und_dc, und_bm); SelectObject(und_dc, fonts[FONT_UNDERLINE]); SetTextAlign(und_dc, TA_TOP | TA_LEFT | TA_NOUPDATECP); SetTextColor(und_dc, RGB(255, 255, 255)); SetBkColor(und_dc, RGB(0, 0, 0)); SetBkMode(und_dc, OPAQUE); ExtTextOut(und_dc, 0, 0, ETO_OPAQUE, NULL, " ", 1, NULL); gotit = false; for (i = 0; i < font_height; i++) { c = GetPixel(und_dc, font_width / 2, i); if (c != RGB(0, 0, 0)) gotit = true; } SelectObject(und_dc, und_oldbm); DeleteObject(und_bm); DeleteDC(und_dc); 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; for (i = 0; i < 3; i++) { if (fonts[i]) { if (SelectObject(hdc, fonts[i]) && GetTextMetrics(hdc, &tm)) fontsize[i] = get_font_width(hdc, &tm) + 256 * tm.tmHeight; else fontsize[i] = -i; } else fontsize[i] = -i; } ReleaseDC(wgs.term_hwnd, hdc); if (trust_icon != INVALID_HANDLE_VALUE) { DestroyIcon(trust_icon); } trust_icon = LoadImage(hinst, MAKEINTRESOURCE(IDI_MAINICON), IMAGE_ICON, font_width*2, font_height, LR_DEFAULTCOLOR); 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] = true; fontflag[1] = true; fontflag[2] = true; init_ucs(conf, &ucsdata); } static void another_font(int fontno) { int basefont; int fw_dontcare, fw_bold, quality; int c, w, x; bool u; 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] = true; } static void deinit_fonts(void) { int i; for (i = 0; i < FONT_MAXNO; i++) { if (fonts[i]) DeleteObject(fonts[i]); fonts[i] = 0; fontflag[i] = false; } if (trust_icon != INVALID_HANDLE_VALUE) { DestroyIcon(trust_icon); } trust_icon = INVALID_HANDLE_VALUE; } static void wintw_request_resize(TermWin *tw, int w, int h) { const struct BackendVtable *vt; int width, height; /* If the window is maximized suppress resizing attempts */ if (IsZoomed(wgs.term_hwnd)) { if (conf_get_int(conf, CONF_resize_action) == RESIZE_TERM) return; } if (conf_get_int(conf, CONF_resize_action) == RESIZE_DISABLED) return; vt = backend_vt_from_proto(be_default_protocol); if (vt && vt->flags & BACKEND_RESIZE_FORBIDDEN) return; if (h == term->rows && w == term->cols) return; /* Sanity checks ... */ { static int first_time = 1; static RECT ss; switch (first_time) { case 1: /* Get the size of the screen */ if (get_fullscreen_rect(&ss)) /* first_time = 0 */ ; else { first_time = 2; break; } case 0: /* Make sure the values are sane */ width = (ss.right - ss.left - extra_width) / 4; height = (ss.bottom - ss.top - extra_height) / 6; if (w > width || h > height) return; 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(wgs.term_hwnd)) { width = extra_width + font_width * w; height = extra_height + font_height * h; SetWindowPos(wgs.term_hwnd, NULL, 0, 0, width, height, SWP_NOACTIVATE | SWP_NOCOPYBITS | SWP_NOMOVE | SWP_NOZORDER); } else reset_window(0); InvalidateRect(wgs.term_hwnd, NULL, true); } static void recompute_window_offset(void) { RECT cr; GetClientRect(wgs.term_hwnd, &cr); int win_width = cr.right - cr.left; int win_height = cr.bottom - cr.top; int new_offset_width = (win_width-font_width*term->cols)/2; int new_offset_height = (win_height-font_height*term->rows)/2; if (offset_width != new_offset_width || offset_height != new_offset_height) { offset_width = new_offset_width; offset_height = new_offset_height; InvalidateRect(wgs.term_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("reset_window()\n"); #endif /* Current window sizes ... */ GetWindowRect(wgs.term_hwnd, &wr); GetClientRect(wgs.term_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("reset_window() -- Forced deinit\n"); #endif deinit_fonts(); init_fonts(0,0); } /* Oh, looks like we're minimised */ if (win_width == 0 || win_height == 0) return; /* Is the window out of position ? */ if (!reinit) { recompute_window_offset(); #ifdef RDB_DEBUG_PATCH debug("reset_window() -> Reposition terminal\n"); #endif } if (IsZoomed(wgs.term_hwnd)) { /* 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(wgs.term_hwnd, NULL, true); #ifdef RDB_DEBUG_PATCH debug("reset_window() -> Z font resize to (%d, %d)\n", 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(wgs.term_hwnd, NULL, true); #ifdef RDB_DEBUG_PATCH debug("reset_window() -> Zoomed term_size\n"); #endif } } return; } /* Resize window after DPI change */ if (reinit == 3 && p_GetSystemMetricsForDpi && p_AdjustWindowRectExForDpi) { RECT rect; rect.left = rect.top = 0; rect.right = (font_width * term->cols); if (conf_get_bool(conf, CONF_scrollbar)) rect.right += p_GetSystemMetricsForDpi(SM_CXVSCROLL, dpi_info.cur_dpi.x); rect.bottom = (font_height * term->rows); p_AdjustWindowRectExForDpi( &rect, GetWindowLongPtr(wgs.term_hwnd, GWL_STYLE), FALSE, GetWindowLongPtr(wgs.term_hwnd, GWL_EXSTYLE), dpi_info.cur_dpi.x); rect.right += (window_border * 2); rect.bottom += (window_border * 2); OffsetRect(&dpi_info.new_wnd_rect, ((dpi_info.new_wnd_rect.right - dpi_info.new_wnd_rect.left) - (rect.right - rect.left)) / 2, ((dpi_info.new_wnd_rect.bottom - dpi_info.new_wnd_rect.top) - (rect.bottom - rect.top)) / 2); SetWindowPos(wgs.term_hwnd, NULL, dpi_info.new_wnd_rect.left, dpi_info.new_wnd_rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOZORDER); InvalidateRect(wgs.term_hwnd, NULL, true); 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("reset_window() -> Forced re-init\n"); #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 * allowed window size, we will then be back in here and resize * the font or terminal to fit. */ SetWindowPos(wgs.term_hwnd, NULL, 0, 0, font_width*term->cols + extra_width, font_height*term->rows + extra_height, SWP_NOMOVE | SWP_NOZORDER); } InvalidateRect(wgs.term_hwnd, NULL, true); 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("reset_window() -> term resize to (%d,%d)\n", height, width); #endif } } SetWindowPos(wgs.term_hwnd, NULL, 0, 0, font_width*term->cols + extra_width, font_height*term->rows + extra_height, SWP_NOMOVE | SWP_NOZORDER); InvalidateRect(wgs.term_hwnd, NULL, true); #ifdef RDB_DEBUG_PATCH debug("reset_window() -> window resize to (%d,%d)\n", font_width*term->cols + extra_width, font_height*term->rows + extra_height); #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(wgs.term_hwnd, NULL, true); #ifdef RDB_DEBUG_PATCH debug("reset_window() -> font resize to (%d,%d)\n", font_width, font_height); #endif } } static void set_input_locale(HKL kl) { char lbuf[20]; GetLocaleInfo(LOWORD(kl), LOCALE_IDEFAULTANSICODEPAGE, lbuf, sizeof(lbuf)); kbd_codepage = atoi(lbuf); } static void click(Mouse_Button b, int x, int y, bool shift, bool ctrl, bool alt) { int thistime = GetMessageTime(); if (send_raw_mouse && !(shift && conf_get_bool(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) { lastact = (lastact == MA_CLICK ? MA_2CLK : lastact == MA_2CLK ? MA_3CLK : lastact == MA_3CLK ? MA_CLICK : MA_NOTHING); } else { lastbtn = b; lastact = MA_CLICK; } if (lastact != MA_NOTHING) term_mouse(term, b, translate_button(b), lastact, x, y, shift, ctrl, alt); lasttime = thistime; } /* * Translate a raw mouse button designation (LEFT, MIDDLE, RIGHT) * 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(bool show) { /* NB that the counter in ShowCursor() is also frobbed by * update_mouse_pointer() */ static bool cursor_visible = true; if (!conf_get_bool(conf, CONF_hide_mouseptr)) show = true; /* override if this feature disabled */ if (cursor_visible && !show) ShowCursor(false); else if (!cursor_visible && show) ShowCursor(true); cursor_visible = show; } static bool is_alt_pressed(void) { BYTE keystate[256]; int r = GetKeyboardState(keystate); if (!r) return false; if (keystate[VK_MENU] & 0x80) return true; if (keystate[VK_RMENU] & 0x80) return true; return false; } static bool resizing; static void win_seat_notify_remote_exit(Seat *seat) { int exitcode, close_on_exit; if (!session_closed && (exitcode = backend_exitcode(backend)) >= 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) { show_mouseptr(true); MessageBox(wgs.term_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(wgs.term_hwnd, TIMING_TIMER_ID); SetTimer(wgs.term_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 const int clips_system[] = { CLIP_SYSTEM }; static HDC make_hdc(void) { HDC hdc; if (!wgs.term_hwnd) return NULL; hdc = GetDC(wgs.term_hwnd); if (!hdc) return NULL; SelectPalette(hdc, pal, false); return hdc; } static void free_hdc(HDC hdc) { assert(wgs.term_hwnd); SelectPalette(hdc, GetStockObject(DEFAULT_PALETTE), false); ReleaseDC(wgs.term_hwnd, hdc); } static bool need_backend_resize = false; static void wm_size_resize_term(LPARAM lParam, bool border) { int width = LOWORD(lParam); int height = HIWORD(lParam); int border_size = border ? conf_get_int(conf, CONF_window_border) : 0; int w = (width - border_size*2) / font_width; int h = (height - border_size*2) / font_height; if (w < 1) w = 1; if (h < 1) h = 1; if (resizing) { /* * If we're in the middle of an interactive resize, we don't * call term_size. This means that, firstly, the user can drag * the size back and forth indecisively without wiping out any * actual terminal contents, and secondly, the Terminal * doesn't call back->size in turn for each increment of the * resizing drag, so we don't spam the server with huge * numbers of resize events. */ 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)); } } static LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { HDC hdc; static bool ignore_clip = false; static bool fullscr_on_max = false; static bool processed_resize = false; static bool in_scrollbar_loop = 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 *title, *msg, *additional = NULL; show_mouseptr(true); title = dupprintf("%s Exit Confirmation", appname); if (backend && backend->vt->close_warn_text) { additional = backend->vt->close_warn_text(backend); } msg = dupprintf("Are you sure you want to close this session?%s%s", additional ? "\n" : "", additional ? additional : ""); if (session_closed || !conf_get_bool(conf, CONF_warn_on_close) || MessageBox(hwnd, msg, title, MB_ICONWARNING | MB_OKCANCEL | MB_DEFBUTTON1) == IDOK) DestroyWindow(hwnd); sfree(title); sfree(msg); sfree(additional); return 0; } case WM_DESTROY: show_mouseptr(true); PostQuitMessage(0); return 0; case WM_INITMENUPOPUP: if ((HMENU)wParam == savedsess_menu) { /* About to pop up Saved Sessions sub-menu. * Refresh the session list. */ get_sesslist(&sesslist, false); /* free */ get_sesslist(&sesslist, true); update_savedsess_menu(); return 0; } break; case WM_COMMAND: case WM_SYSCOMMAND: switch (wParam & ~0xF) { /* low 4 bits reserved to Windows */ case SC_VSCROLL: case SC_HSCROLL: if (message == WM_SYSCOMMAND) { /* As per the long comment in WM_VSCROLL handler: give * this message the default handling, which starts a * subsidiary message loop, but set a flag so that * when we're re-entered from that loop, scroll events * within an interactive scrollbar-drag can be handled * differently. */ in_scrollbar_loop = true; LRESULT result = DefWindowProcW(hwnd, message, wParam, lParam); in_scrollbar_loop = false; return result; } break; case IDM_SHOWLOG: showeventlog(hwnd); break; case IDM_NEWSESS: case IDM_DUPSESS: case IDM_SAVEDSESS: { char b[2048]; char *cl; const char *argprefix; bool inherit_handles; STARTUPINFO si; PROCESS_INFORMATION pi; HANDLE filemap = NULL; if (restricted_acl()) argprefix = "&R"; else argprefix = ""; if (wParam == IDM_DUPSESS) { /* * Allocate a file-mapping memory chunk for the * config structure. */ SECURITY_ATTRIBUTES sa; strbuf *serbuf; void *p; int size; serbuf = strbuf_new(); conf_serialise(BinarySink_UPCAST(serbuf), conf); size = serbuf->len; 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) { memcpy(p, serbuf->s, size); UnmapViewOfFile(p); } } strbuf_free(serbuf); inherit_handles = true; cl = dupprintf("putty %s&%p:%u", argprefix, filemap, (unsigned)size); } else if (wParam == IDM_SAVEDSESS) { unsigned int sessno = ((lParam - IDM_SAVED_MIN) / MENU_SAVED_STEP) + 1; if (sessno < (unsigned)sesslist.nsessions) { const char *session = sesslist.sessions[sessno]; cl = dupprintf("putty %s@%s", argprefix, session); inherit_handles = false; } else break; } else /* IDM_NEWSESS */ { cl = dupprintf("putty%s%s", *argprefix ? " " : "", argprefix); inherit_handles = false; } GetModuleFileName(NULL, b, sizeof(b) - 1); si.cb = sizeof(si); si.lpReserved = NULL; 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); sfree(cl); break; } case IDM_RESTART: if (!backend) { lp_eventlog(&wgs.logpolicy, "----- Session restarted -----"); term_pwron(term, false); start_backend(); } break; case IDM_RECONF: { Conf *prev_conf; int init_lvl = 1; bool reconfig_result; if (reconfiguring) break; else reconfiguring = true; term_pre_reconfig(term, conf); prev_conf = conf_copy(conf); reconfig_result = do_reconfig( hwnd, conf, backend ? backend_cfg_info(backend) : 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. */ if (ldisc) { ldisc_configure(ldisc, conf); ldisc_echoedit_update(ldisc); } if (conf_get_bool(conf, CONF_system_colour) != conf_get_bool(prev_conf, CONF_system_colour)) term_notify_palette_changed(term); /* Pass new config data to the terminal */ term_reconfig(term, conf); setup_clipboards(term, conf); /* Reinitialise the colour palette, in case the terminal * just read new settings out of Conf */ if (pal) DeleteObject(pal); logpal = NULL; pal = NULL; init_palette(); /* Pass new config data to the back end */ if (backend) backend_reconfig(backend, 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_bool(conf, CONF_alwaysontop) != conf_get_bool(prev_conf, CONF_alwaysontop)) { if (conf_get_bool(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_bool(conf, CONF_sunken_edge)) nexflag |= WS_EX_CLIENTEDGE; else nexflag &= ~(WS_EX_CLIENTEDGE); nflg = flag; if (conf_get_bool(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; } { 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, clips_system, lenof(clips_system)); break; case IDM_COPY: term_request_copy(term, clips_system, lenof(clips_system)); break; case IDM_PASTE: term_request_paste(term, CLIP_SYSTEM); break; case IDM_CLRSB: term_clrsb(term); break; case IDM_RESET: term_pwron(term, true); if (ldisc) ldisc_echoedit_update(ldisc); break; case IDM_ABOUT: showabout(hwnd); break; case IDM_HELP: launch_help(hwnd, NULL); break; case SC_MOUSEMENU: /* * We get this if the System menu has been activated * using the mouse. */ show_mouseptr(true); break; case SC_KEYMENU: /* * We get this if the System menu has been activated * using the keyboard. This might happen from within * TranslateKey, in which case it really wants to be * followed by a `space' character to actually _bring * the menu up_ rather than just sitting there in * `ready to appear' state. */ show_mouseptr(true); /* make sure pointer is visible */ if( lParam == 0 ) PostMessage(hwnd, WM_CHAR, ' ', 0); break; case IDM_FULLSCREEN: flip_full_screen(); break; default: if (wParam >= IDM_SAVED_MIN && wParam < IDM_SAVED_MAX) { SendMessage(hwnd, WM_SYSCOMMAND, IDM_SAVEDSESS, wParam); } if (wParam >= IDM_SPECIAL_MIN && wParam <= IDM_SPECIAL_MAX) { int i = (wParam - IDM_SPECIAL_MIN) / 0x10; /* * Ensure we haven't been sent a bogus SYSCOMMAND * which would cause us to reference invalid memory * and crash. Perhaps I'm just too paranoid here. */ if (i >= n_specials) break; if (backend) backend_special( backend, specials[i].code, specials[i].arg); } } 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(true); /* make sure pointer is visible */ GetCursorPos(&cursorpos); TrackPopupMenu(popup_menus[CTXMENU].menu, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RIGHTBUTTON, cursorpos.x, cursorpos.y, 0, hwnd, NULL); break; } { int button; bool press; switch (message) { case WM_LBUTTONDOWN: button = MBT_LEFT; wParam |= MK_LBUTTON; press = true; break; case WM_MBUTTONDOWN: button = MBT_MIDDLE; wParam |= MK_MBUTTON; press = true; break; case WM_RBUTTONDOWN: button = MBT_RIGHT; wParam |= MK_RBUTTON; press = true; break; case WM_LBUTTONUP: button = MBT_LEFT; wParam &= ~MK_LBUTTON; press = false; break; case WM_MBUTTONUP: button = MBT_MIDDLE; wParam &= ~MK_MBUTTON; press = false; break; case WM_RBUTTONUP: button = MBT_RIGHT; wParam &= ~MK_RBUTTON; press = false; break; default: /* shouldn't happen */ button = 0; press = false; } show_mouseptr(true); /* * Special case: in full-screen mode, if the left * button is clicked in the very top left corner of the * window, we put up the System menu instead of doing * selection. */ { bool mouse_on_hotspot = false; POINT pt; GetCursorPos(&pt); #ifndef NO_MULTIMON { HMONITOR mon; MONITORINFO mi; mon = MonitorFromPoint(pt, MONITOR_DEFAULTTONULL); if (mon != NULL) { mi.cbSize = sizeof(MONITORINFO); GetMonitorInfo(mon, &mi); if (mi.rcMonitor.left == pt.x && mi.rcMonitor.top == pt.y) { mouse_on_hotspot = true; } } } #else if (pt.x == 0 && pt.y == 0) { mouse_on_hotspot = true; } #endif if (is_full_screen() && press && button == MBT_LEFT && mouse_on_hotspot) { SendMessage(hwnd, WM_SYSCOMMAND, SC_MOUSEMENU, MAKELPARAM(pt.x, pt.y)); return 0; } } if (press) { click(button, TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT, wParam & MK_CONTROL, is_alt_pressed()); SetCapture(hwnd); } else { term_mouse(term, button, translate_button(button), MA_RELEASE, TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT, wParam & MK_CONTROL, is_alt_pressed()); if (!(wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON))) ReleaseCapture(); } } return 0; case WM_MOUSEMOVE: { /* * Windows seems to like to occasionally send MOUSEMOVE * events even if the mouse hasn't moved. Don't unhide * the mouse pointer in this case. */ static WPARAM wp = 0; static LPARAM lp = 0; if (wParam != wp || lParam != lp || last_mousemove != WM_MOUSEMOVE) { show_mouseptr(true); wp = wParam; lp = lParam; last_mousemove = WM_MOUSEMOVE; } /* * Add the mouse position and message time to the random * number noise. */ noise_ultralight(NOISE_SOURCE_MOUSEPOS, lParam); if (wParam & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON) && GetCapture() == hwnd) { Mouse_Button b; if (wParam & MK_LBUTTON) b = MBT_LEFT; else if (wParam & MK_MBUTTON) b = MBT_MIDDLE; else b = MBT_RIGHT; term_mouse(term, b, translate_button(b), MA_DRAG, TO_CHR_X(X_POS(lParam)), TO_CHR_Y(Y_POS(lParam)), wParam & MK_SHIFT, wParam & MK_CONTROL, is_alt_pressed()); } return 0; } case WM_NCMOUSEMOVE: { static WPARAM wp = 0; static LPARAM lp = 0; if (wParam != wp || lParam != lp || last_mousemove != WM_NCMOUSEMOVE) { show_mouseptr(true); wp = wParam; lp = lParam; last_mousemove = WM_NCMOUSEMOVE; } noise_ultralight(NOISE_SOURCE_MOUSEPOS, lParam); break; } case WM_IGNORE_CLIP: ignore_clip = wParam; /* don't panic on DESTROYCLIPBOARD */ break; case WM_DESTROYCLIPBOARD: if (!ignore_clip) term_lost_clipboard_ownership(term, CLIP_SYSTEM); ignore_clip = false; return 0; case WM_PAINT: { PAINTSTRUCT p; HideCaret(hwnd); hdc = BeginPaint(hwnd, &p); if (pal) { SelectPalette(hdc, pal, true); RealizePalette(hdc); } /* * We have to be careful about term_paint(). It will * set a bunch of character cells to INVALID and then * call do_paint(), which will redraw those cells and * _then mark them as done_. This may not be accurate: * when painting in WM_PAINT context we are restricted * to the rectangle which has just been exposed - so if * that only covers _part_ of a character cell and the * rest of it was already visible, that remainder will * not be redrawn at all. Accordingly, we must not * paint any character cell in a WM_PAINT context which * already has a pending update due to terminal output. * The simplest solution to this - and many, many * thanks to Hung-Te Lin for working all this out - is * not to do any actual painting at _all_ if there's a * pending terminal update: just mark the relevant * character cells as INVALID and wait for the * scheduled full update to sort it out. * * I have a suspicion this isn't the _right_ solution. * An alternative approach would be to have terminal.c * separately track what _should_ be on the terminal * screen and what _is_ on the terminal screen, and * have two completely different types of redraw (one * for full updates, which syncs the former with the * terminal itself, and one for WM_PAINT which syncs * the latter with the former); yet another possibility * would be to have the Windows front end do what the * GTK one already does, and maintain a bitmap of the * current terminal appearance so that WM_PAINT becomes * completely trivial. However, this should do for now. */ assert(!wintw_hdc); wintw_hdc = hdc; term_paint(term, (p.rcPaint.left-offset_width)/font_width, (p.rcPaint.top-offset_height)/font_height, (p.rcPaint.right-offset_width-1)/font_width, (p.rcPaint.bottom-offset_height-1)/font_height, !term->window_update_pending); wintw_hdc = NULL; if (p.fErase || p.rcPaint.left < offset_width || p.rcPaint.top < offset_height || p.rcPaint.right >= offset_width + font_width*term->cols || p.rcPaint.bottom>= offset_height + font_height*term->rows) { HBRUSH fillcolour, oldbrush; HPEN edge, oldpen; fillcolour = CreateSolidBrush ( colours[ATTR_DEFBG>>ATTR_BGSHIFT]); oldbrush = SelectObject(hdc, fillcolour); edge = CreatePen(PS_SOLID, 0, colours[ATTR_DEFBG>>ATTR_BGSHIFT]); oldpen = SelectObject(hdc, edge); /* * Jordan Russell reports that this apparently * ineffectual IntersectClipRect() call masks a * Windows NT/2K bug causing strange display * problems when the PuTTY window is taller than * the primary monitor. It seems harmless enough... */ IntersectClipRect(hdc, p.rcPaint.left, p.rcPaint.top, p.rcPaint.right, p.rcPaint.bottom); ExcludeClipRect(hdc, offset_width, offset_height, offset_width+font_width*term->cols, offset_height+font_height*term->rows); Rectangle(hdc, p.rcPaint.left, p.rcPaint.top, p.rcPaint.right, p.rcPaint.bottom); /* SelectClipRgn(hdc, NULL); */ SelectObject(hdc, oldbrush); DeleteObject(fillcolour); SelectObject(hdc, oldpen); DeleteObject(edge); } 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; term_update(term); break; case WM_KILLFOCUS: show_mouseptr(true); term_set_focus(term, false); DestroyCaret(); caret_x = caret_y = -1; /* ensure caret is replaced next time */ term_update(term); break; case WM_ENTERSIZEMOVE: #ifdef RDB_DEBUG_PATCH debug("WM_ENTERSIZEMOVE\n"); #endif EnableSizeTip(true); resizing = true; need_backend_resize = false; break; case WM_EXITSIZEMOVE: EnableSizeTip(false); resizing = false; #ifdef RDB_DEBUG_PATCH debug("WM_EXITSIZEMOVE\n"); #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); } recompute_window_offset(); 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; w = (width + font_width / 2) / font_width; if (w < 1) w = 1; h = (height + font_height / 2) / font_height; if (h < 1) h = 1; UpdateSizeTip(hwnd, w, h); ew = width - w * font_width; eh = height - h * font_height; if (ew != 0) { if (wParam == WMSZ_LEFT || wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT) r->left += ew; else r->right -= ew; } if (eh != 0) { if (wParam == WMSZ_TOP || wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT) r->top += eh; else r->bottom -= eh; } 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; if ( r->right != r->left + w*term->cols + ex_width) rv = 1; if (wParam == WMSZ_LEFT || wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_TOPLEFT) r->left = r->right - w*term->cols - ex_width; else r->right = r->left + w*term->cols + ex_width; if (r->bottom != r->top + h*term->rows + ex_height) rv = 1; if (wParam == WMSZ_TOP || wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT) r->top = r->bottom - h*term->rows - ex_height; else r->bottom = r->top + h*term->rows + ex_height; return rv; } /* break; (never reached) */ case WM_FULLSCR_ON_MAX: fullscr_on_max = true; break; case WM_MOVE: term_notify_window_pos(term, LOWORD(lParam), HIWORD(lParam)); sys_cursor_update(); break; case WM_SIZE: resize_action = conf_get_int(conf, CONF_resize_action); #ifdef RDB_DEBUG_PATCH debug("WM_SIZE %s (%d,%d)\n", (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 term_notify_minimised(term, wParam == SIZE_MINIMIZED); { /* * WM_SIZE's lParam tells us the size of the client area. * But historic PuTTY practice is that we want to tell the * terminal the size of the overall window. */ RECT r; GetWindowRect(hwnd, &r); term_notify_window_size_pixels( term, r.right - r.left, r.bottom - r.top); } if (wParam == SIZE_MINIMIZED) SetWindowText(hwnd, conf_get_bool(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) { /* * Inhibit normal processing of this WM_SIZE; a * secondary one was triggered just now by * clear_full_screen which contained the correct * client area size. */ return 0; } } if (wParam == SIZE_MAXIMIZED && fullscr_on_max) { fullscr_on_max = false; processed_resize = false; make_full_screen(); if (processed_resize) { /* * Inhibit normal processing of this WM_SIZE; a * secondary one was triggered just now by * make_full_screen which contained the correct client * area size. */ return 0; } } processed_resize = true; if (resize_action == RESIZE_DISABLED) { /* A resize, well it better be a minimize. */ reset_window(-1); } else { if (wParam == SIZE_MAXIMIZED) { was_zoomed = true; prev_rows = term->rows; prev_cols = term->cols; if (resize_action == RESIZE_TERM) wm_size_resize_term(lParam, false); reset_window(0); } else if (wParam == SIZE_RESTORED && was_zoomed) { was_zoomed = false; if (resize_action == RESIZE_TERM) { wm_size_resize_term(lParam, true); 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())) { wm_size_resize_term(lParam, true); /* * Sometimes, we can get a spontaneous resize event * outside a WM_SIZING interactive drag which wants to * set us to a new specific SIZE_RESTORED size. An * example is what happens if you press Windows+Right * and then Windows+Up: the first operation fits the * window to the right-hand half of the screen, and * the second one changes that for the top right * quadrant. In that situation, if we've responded * here by resizing the terminal, we may still need to * recompute the border around the window and do a * full redraw to clear the new border. */ if (!resizing) recompute_window_offset(); } else { reset_window(0); } } sys_cursor_update(); return 0; case WM_DPICHANGED: dpi_info.cur_dpi.x = LOWORD(wParam); dpi_info.cur_dpi.y = HIWORD(wParam); dpi_info.new_wnd_rect = *(RECT*)(lParam); reset_window(3); return 0; case WM_VSCROLL: switch (LOWORD(wParam)) { case SB_BOTTOM: term_scroll(term, -1, 0); break; case SB_TOP: term_scroll(term, +1, 0); break; case SB_LINEDOWN: term_scroll(term, 0, +1); break; case SB_LINEUP: term_scroll(term, 0, -1); break; case SB_PAGEDOWN: 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; } } if (in_scrollbar_loop) { /* * Allow window updates to happen during interactive * scroll. * * When the user takes hold of our window's scrollbar and * wobbles it interactively back and forth, or presses on * one of the arrow buttons at the ends, the first thing * that happens is that this window procedure receives * WM_SYSCOMMAND / SC_VSCROLL. [1] The default handler for * that window message starts a subsidiary message loop, * which continues to run until the user lets go of the * scrollbar again. All WM_VSCROLL / SB_THUMBTRACK * messages are generated by the handlers within that * subsidiary message loop. * * So, during that time, _our_ message loop is not * running, which means toplevel callbacks and timers and * so forth are not happening, which means that when we * redraw the window and set a timer to clear the cooldown * flag 20ms later, that timer never fires, and we aren't * able to keep redrawing the window. * * The 'obvious' answer would be to seize that SYSCOMMAND * ourselves and inhibit the default handler, so that our * message loop carries on running. But that would mean * we'd have to reimplement the whole of the scrollbar * handler! * * So instead we apply a bodge: set a static variable that * indicates that we're _in_ that sub-loop, and if so, * decide it's OK to manually call term_update() proper, * bypassing the timer and cooldown and rate-limiting * systems completely, whenever we see an SB_THUMBTRACK. * This shouldn't cause a rate overload, because we're * only doing it once per UI event! * * [1] Actually, there's an extra oddity where SC_HSCROLL * and SC_VSCROLL have their documented values the wrong * way round. Many people on the Internet have noticed * this, e.g. https://stackoverflow.com/q/55528397 */ term_update(term); } break; case WM_PALETTECHANGED: if ((HWND) wParam != hwnd && pal != NULL) { HDC hdc = make_hdc(); if (hdc) { if (RealizePalette(hdc) > 0) UpdateColors(hdc); free_hdc(hdc); } } break; case WM_QUERYNEWPALETTE: if (pal != NULL) { HDC hdc = make_hdc(); if (hdc) { if (RealizePalette(hdc) > 0) UpdateColors(hdc); free_hdc(hdc); return true; } } return false; case WM_KEYDOWN: case WM_SYSKEYDOWN: case WM_KEYUP: case WM_SYSKEYUP: /* * Add the scan code and keypress timing to the random * number noise. */ noise_ultralight(NOISE_SOURCE_KEY, lParam); /* * We don't do TranslateMessage since it disassociates the * resulting CHAR message from the KEYDOWN that sparked it, * which we occasionally don't want. Instead, we process * KEYDOWN, and call the Win32 translator functions so that * we get the translations under _our_ control. */ { unsigned char buf[20]; int len; if (wParam == VK_PROCESSKEY || /* IME PROCESS key */ wParam == VK_PACKET) { /* 'this key is a Unicode char' */ if (message == WM_KEYDOWN) { MSG m; m.hwnd = hwnd; m.message = WM_KEYDOWN; m.wParam = wParam; m.lParam = lParam & 0xdfff; TranslateMessage(&m); } else break; /* pass to Windows for default processing */ } else { len = TranslateKey(message, wParam, lParam, buf); if (len == -1) return DefWindowProcW(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_keyinput(term, -1, buf, len); show_mouseptr(false); } } } return 0; case WM_INPUTLANGCHANGE: /* wParam == Font number */ /* lParam == Locale */ set_input_locale((HKL)lParam); sys_cursor_update(); break; case WM_IME_STARTCOMPOSITION: { HIMC hImc = ImmGetContext(hwnd); ImmSetCompositionFont(hImc, &lfont); ImmReleaseContext(hwnd, hImc); break; } case WM_IME_COMPOSITION: { HIMC hIMC; int n; char *buff; if (osPlatformId == VER_PLATFORM_WIN32_WINDOWS || osPlatformId == VER_PLATFORM_WIN32s) break; /* no Unicode */ if ((lParam & GCS_RESULTSTR) == 0) /* Composition unfinished. */ break; /* fall back to DefWindowProc */ hIMC = ImmGetContext(hwnd); n = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, NULL, 0); if (n > 0) { int i; buff = snewn(n, char); ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, buff, n); /* * Jaeyoun Chung reports that Korean character * input doesn't work correctly if we do a single * term_keyinputw covering the whole of buff. So * instead we send the characters one by one. */ /* 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)) { term_keyinputw( term, (unsigned short *)(buff+i), 2); i += 2; continue; } } term_keyinputw( term, (unsigned short *)(buff+i), 1); } } free(buff); } ImmReleaseContext(hwnd, hIMC); return 1; } case WM_IME_CHAR: if (wParam & 0xFF00) { char buf[2]; buf[1] = wParam; buf[0] = wParam >> 8; term_keyinput(term, kbd_codepage, buf, 2); } else { char c = (unsigned char) wParam; term_seen_key_event(term); term_keyinput(term, kbd_codepage, &c, 1); } return (0); case WM_CHAR: case WM_SYSCHAR: /* * Nevertheless, we are prepared to deal with WM_CHAR * messages, should they crop up. So if someone wants to * post the things to us as part of a macro manoeuvre, * we're ready to cope. */ { static wchar_t pending_surrogate = 0; wchar_t c = wParam; if (IS_HIGH_SURROGATE(c)) { pending_surrogate = c; } else if (IS_SURROGATE_PAIR(pending_surrogate, c)) { wchar_t pair[2]; pair[0] = pending_surrogate; pair[1] = c; term_keyinputw(term, pair, 2); } else if (!IS_SURROGATE(c)) { term_keyinputw(term, &c, 1); } } return 0; case WM_SYSCOLORCHANGE: if (conf_get_bool(conf, CONF_system_colour)) { /* Refresh palette from system colours. */ term_notify_palette_changed(term); init_palette(); /* Force a repaint of the terminal window. */ term_invalidate(term); } break; case WM_GOT_CLIPDATA: process_clipdata((HGLOBAL)lParam, wParam); return 0; default: if (message == wm_mousewheel || message == WM_MOUSEWHEEL) { bool shift_pressed = false, control_pressed = false; if (message == WM_MOUSEWHEEL) { wheel_accumulator += (short)HIWORD(wParam); shift_pressed=LOWORD(wParam) & MK_SHIFT; control_pressed=LOWORD(wParam) & MK_CONTROL; } else { BYTE keys[256]; wheel_accumulator += (int)wParam; if (GetKeyboardState(keys)!=0) { shift_pressed=keys[VK_SHIFT]&0x80; control_pressed=keys[VK_CONTROL]&0x80; } } /* process events when the threshold is reached */ while (abs(wheel_accumulator) >= WHEEL_DELTA) { int b; /* reduce amount for next time */ if (wheel_accumulator > 0) { b = MBT_WHEEL_UP; wheel_accumulator -= WHEEL_DELTA; } else if (wheel_accumulator < 0) { b = MBT_WHEEL_DOWN; wheel_accumulator += WHEEL_DELTA; } else break; if (send_raw_mouse && !(conf_get_bool(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), MA_CLICK, TO_CHR_X(p.x), TO_CHR_Y(p.y), shift_pressed, control_pressed, is_alt_pressed()); } /* else: not sure when this can fail */ } else { /* trigger a scroll */ term_scroll(term, 0, b == MBT_WHEEL_UP ? -term->rows / 2 : term->rows / 2); } } return 0; } } /* * Any messages we don't process completely above are passed through to * DefWindowProc() for default processing. */ return DefWindowProcW(hwnd, message, wParam, lParam); } /* * Move the system caret. (We maintain one, even though it's * invisible, for the benefit of blind people: apparently some * helper software tracks the system caret, so we should arrange to * have one.) */ static void wintw_set_cursor_pos(TermWin *tw, int x, int y) { int cx, cy; if (!term->has_focus) return; /* * Avoid gratuitously re-updating the cursor position and IMM * window if there's no actual change required. */ cx = x * font_width + offset_width; cy = y * font_height + offset_height; if (cx == caret_x && cy == caret_y) return; caret_x = cx; caret_y = cy; sys_cursor_update(); } static void sys_cursor_update(void) { COMPOSITIONFORM cf; HIMC hIMC; if (!term->has_focus) return; if (caret_x < 0 || caret_y < 0) return; SetCaretPos(caret_x, caret_y); /* IMM calls on Win98 and beyond only */ if (osPlatformId == VER_PLATFORM_WIN32s) return; /* 3.11 */ if (osPlatformId == VER_PLATFORM_WIN32_WINDOWS && osMinorVersion == 0) return; /* 95 */ /* we should have the IMM functions */ hIMC = ImmGetContext(wgs.term_hwnd); cf.dwStyle = CFS_POINT; cf.ptCurrentPos.x = caret_x; cf.ptCurrentPos.y = caret_y; ImmSetCompositionWindow(hIMC, &cf); ImmReleaseContext(wgs.term_hwnd, hIMC); } static void draw_horizontal_line_on_text(int y, int lattr, RECT line_box, COLORREF colour) { if (lattr == LATTR_TOP || lattr == LATTR_BOT) { y *= 2; if (lattr == LATTR_BOT) y -= font_height; } if (!(0 <= y && y < font_height)) return; HPEN oldpen = SelectObject(wintw_hdc, CreatePen(PS_SOLID, 0, colour)); MoveToEx(wintw_hdc, line_box.left, line_box.top + y, NULL); LineTo(wintw_hdc, line_box.right, line_box.top + y); oldpen = SelectObject(wintw_hdc, oldpen); DeleteObject(oldpen); } /* * Draw a line of text in the window, at given character * coordinates, in given attributes. * * We are allowed to fiddle with the contents of `text'. */ static void do_text_internal( int x, int y, wchar_t *text, int len, unsigned long attr, int lattr, truecolour truecolour) { COLORREF fg, bg, t; int nfg, nbg, nfont; RECT line_box; bool force_manual_underline = false; int fnt_width, char_width; int text_adjust = 0; int xoffset = 0; int maxlen, remaining; bool opaque; bool is_cursor = false; static int *lpDx = NULL; static size_t 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)) { truecolour.fg = truecolour.bg = optionalrgb_none; attr &= ~(ATTR_REVERSE|ATTR_BLINK|ATTR_COLOURS|ATTR_DIM); /* 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: text_adjust = -1 * font_height / 5; break; case 0xBC: text_adjust = font_height / 5; break; case 0xBD: text_adjust = 2 * font_height / 5; break; } if (lattr == LATTR_TOP || lattr == LATTR_BOT) text_adjust *= 2; text[0] = ucsdata.unitab_xterm['q']; if (attr & ATTR_UNDER) { attr &= ~ATTR_UNDER; force_manual_underline = true; } } #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 = true; /* 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) { struct optionalrgb trgb; t = nfg; nfg = nbg; nbg = t; trgb = truecolour.fg; truecolour.fg = truecolour.bg; truecolour.bg = trgb; } 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; } if (!pal && truecolour.fg.enabled) fg = RGB(truecolour.fg.r, truecolour.fg.g, truecolour.fg.b); else fg = colours[nfg]; if (!pal && truecolour.bg.enabled) bg = RGB(truecolour.bg.r, truecolour.bg.g, truecolour.bg.b); else bg = colours[nbg]; if (!pal && (attr & ATTR_DIM)) { fg = RGB(GetRValue(fg) * 2 / 3, GetGValue(fg) * 2 / 3, GetBValue(fg) * 2 / 3); } SelectObject(wintw_hdc, fonts[nfont]); SetTextColor(wintw_hdc, fg); SetBkColor(wintw_hdc, bg); if (attr & TATTR_COMBINING) SetBkMode(wintw_hdc, TRANSPARENT); else SetBkMode(wintw_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) { /* * If we're using a variable-pitch font, we unconditionally * draw the glyphs one at a time and centre them in their * character cells (which means in particular that we must * disable the lpDx mechanism). This gives slightly odd but * generally reasonable results. */ xoffset = char_width / 2; SetTextAlign(wintw_hdc, TA_TOP | TA_CENTER | TA_NOUPDATECP); lpDx_maybe = NULL; maxlen = 1; } else { /* * In a fixed-pitch font, we draw the whole string in one go * in the normal way. */ xoffset = 0; SetTextAlign(wintw_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) { sgrowarray(lpDx, lpDx_len, len); 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; static int uni_len = 0; int nlen, mptr; if (len > uni_len) { sfree(uni_buf); uni_len = len; uni_buf = snewn(uni_len, wchar_t); } for(nlen = mptr = 0; mptr= 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(x, y, text, len0+1, attr, lattr, truecolour); 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(x, y, text, len0+2, attr, lattr, truecolour); 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(x, y, text, 2, attr | a, lattr, truecolour); len--; text++; } else do_text_internal(x, y, text, 1, attr | a, lattr, truecolour); text++; a = TATTR_COMBINING; } } else do_text_internal(x, y, text, len, attr, lattr, truecolour); } static void wintw_draw_cursor( TermWin *tw, int x, int y, wchar_t *text, int len, unsigned long attr, int lattr, truecolour truecolour) { int fnt_width; int char_width; int ctype = cursor_type; lattr &= LATTR_MODE; if ((attr & TATTR_ACTCURS) && (ctype == 0 || term->big_cursor)) { if (*text != UCSWIDE) { win_draw_text(tw, x, y, text, len, attr, lattr, truecolour); return; } ctype = 2; attr |= TATTR_RIGHTCURS; } fnt_width = char_width = font_width * (1 + (lattr != LATTR_NORM)); if (attr & ATTR_WIDE) char_width *= 2; x *= fnt_width; y *= font_height; x += offset_width; y += offset_height; if ((attr & TATTR_PASCURS) && (ctype == 0 || term->big_cursor)) { POINT pts[5]; HPEN oldpen; pts[0].x = pts[1].x = pts[4].x = x; pts[2].x = pts[3].x = x + char_width - 1; pts[0].y = pts[3].y = pts[4].y = y; pts[1].y = pts[2].y = y + font_height - 1; oldpen = SelectObject(wintw_hdc, CreatePen(PS_SOLID, 0, colours[261])); Polyline(wintw_hdc, pts, 5); oldpen = SelectObject(wintw_hdc, oldpen); DeleteObject(oldpen); } else if ((attr & (TATTR_ACTCURS | TATTR_PASCURS)) && ctype != 0) { int startx, starty, dx, dy, length, i; if (ctype == 1) { startx = x; starty = y + descent; dx = 1; dy = 0; length = char_width; } else { int xadjust = 0; if (attr & TATTR_RIGHTCURS) xadjust = char_width - 1; startx = x + xadjust; starty = y; dx = 0; dy = 1; length = font_height; } if (attr & TATTR_ACTCURS) { HPEN oldpen; oldpen = SelectObject(wintw_hdc, CreatePen(PS_SOLID, 0, colours[261])); MoveToEx(wintw_hdc, startx, starty, NULL); LineTo(wintw_hdc, startx + dx * length, starty + dy * length); oldpen = SelectObject(wintw_hdc, oldpen); DeleteObject(oldpen); } else { for (i = 0; i < length; i++) { if (i % 2 == 0) { SetPixel(wintw_hdc, startx, starty, colours[261]); } startx += dx; starty += dy; } } } } static void wintw_draw_trust_sigil(TermWin *tw, int x, int y) { x *= font_width; y *= font_height; x += offset_width; y += offset_height; DrawIconEx(wintw_hdc, x, y, trust_icon, font_width * 2, font_height, 0, NULL, DI_NORMAL); } /* This function gets the actual width of a character in the normal font. */ static int wintw_char_width(TermWin *tw, int uc) { int ibuf = 0; /* If the font max is the same as the font ave width then this * function is a no-op. */ if (!font_dualwidth) return 1; switch (uc & CSET_MASK) { case CSET_ASCII: uc = ucsdata.unitab_line[uc & 0xFF]; break; case CSET_LINEDRW: uc = ucsdata.unitab_xterm[uc & 0xFF]; break; case CSET_SCOACS: uc = ucsdata.unitab_scoacs[uc & 0xFF]; break; } if (DIRECT_FONT(uc)) { if (ucsdata.dbcs_screenfont) return 1; /* Speedup, I know of no font where ascii is the wrong width */ if ((uc&~CSET_MASK) >= ' ' && (uc&~CSET_MASK)<= '~') return 1; if ( (uc & CSET_MASK) == CSET_ACP ) { SelectObject(wintw_hdc, fonts[FONT_NORMAL]); } else if ( (uc & CSET_MASK) == CSET_OEMCP ) { another_font(FONT_OEM); if (!fonts[FONT_OEM]) return 0; SelectObject(wintw_hdc, fonts[FONT_OEM]); } else return 0; if (GetCharWidth32(wintw_hdc, uc & ~CSET_MASK, uc & ~CSET_MASK, &ibuf) != 1 && GetCharWidth(wintw_hdc, uc & ~CSET_MASK, uc & ~CSET_MASK, &ibuf) != 1) return 0; } else { /* Speedup, I know of no font where ascii is the wrong width */ if (uc >= ' ' && uc <= '~') return 1; SelectObject(wintw_hdc, fonts[FONT_NORMAL]); if (GetCharWidth32W(wintw_hdc, uc, uc, &ibuf) == 1) /* Okay that one worked */ ; else if (GetCharWidthW(wintw_hdc, uc, uc, &ibuf) == 1) /* This should work on 9x too, but it's "less accurate" */ ; else return 0; } 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)); DECL_WINDOWS_FUNCTION(static, BOOL, PlaySound, (LPCTSTR, HMODULE, DWORD)); static void init_winfuncs(void) { HMODULE user32_module = load_system32_dll("user32.dll"); HMODULE winmm_module = load_system32_dll("winmm.dll"); HMODULE shcore_module = load_system32_dll("shcore.dll"); GET_WINDOWS_FUNCTION(user32_module, FlashWindowEx); GET_WINDOWS_FUNCTION(user32_module, ToUnicodeEx); GET_WINDOWS_FUNCTION_PP(winmm_module, PlaySound); GET_WINDOWS_FUNCTION_NO_TYPECHECK(shcore_module, GetDpiForMonitor); GET_WINDOWS_FUNCTION_NO_TYPECHECK(user32_module, GetSystemMetricsForDpi); GET_WINDOWS_FUNCTION_NO_TYPECHECK(user32_module, AdjustWindowRectExForDpi); } /* * 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, shift_state; bool left_alt = false, key_down; int r, i; unsigned char *p = output; static int alt_sum = 0; int funky_type = conf_get_int(conf, CONF_funky_type); bool no_applic_k = conf_get_bool(conf, CONF_no_applic_k); bool ctrlaltkeys = conf_get_bool(conf, CONF_ctrlaltkeys); bool nethack_keypad = conf_get_bool(conf, CONF_nethack_keypad); char keypad_key = '\0'; 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 { /* Tell us all about key events */ static BYTE oldstate[256]; static int first = 1; static int scan; int ch; if (first) memcpy(oldstate, keystate, sizeof(oldstate)); first = 0; if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT) { debug("+"); } else if ((HIWORD(lParam) & KF_UP) && scan == (HIWORD(lParam) & 0xFF)) { debug(". U"); } else { debug(".\n"); if (wParam >= VK_F1 && wParam <= VK_F20) debug("K_F%d", wParam + 1 - VK_F1); else switch (wParam) { case VK_SHIFT: debug("SHIFT"); break; case VK_CONTROL: debug("CTRL"); break; case VK_MENU: debug("ALT"); break; default: debug("VK_%02x", wParam); } if (message == WM_SYSKEYDOWN || message == WM_SYSKEYUP) debug("*"); debug(", S%02x", scan = (HIWORD(lParam) & 0xFF)); 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"); if ((HIWORD(lParam) & KF_UP)) debug(", U"); } if ((HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT); else if ((HIWORD(lParam) & KF_UP)) oldstate[wParam & 0xFF] ^= 0x80; else oldstate[wParam & 0xFF] ^= 0x81; for (ch = 0; ch < 256; ch++) if (oldstate[ch] != keystate[ch]) debug(", M%02x=%02x", ch, keystate[ch]); memcpy(oldstate, keystate, sizeof(oldstate)); } #endif 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; } /* And write back the 'adjusted' state */ SetKeyboardState(keystate); } /* Disable Auto repeat if required */ if (term->repeat_off && (HIWORD(lParam) & (KF_UP | KF_REPEAT)) == KF_REPEAT) return 0; if ((HIWORD(lParam) & KF_ALTDOWN) && (keystate[VK_RMENU] & 0x80) == 0) left_alt = true; 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 = false; } } 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_bool(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: nParam = VK_NUMPAD1; break; case VK_DOWN: nParam = VK_NUMPAD2; break; case VK_NEXT: nParam = VK_NUMPAD3; break; case VK_LEFT: nParam = VK_NUMPAD4; break; case VK_CLEAR: nParam = VK_NUMPAD5; break; case VK_RIGHT: nParam = VK_NUMPAD6; break; case VK_HOME: nParam = VK_NUMPAD7; break; case VK_UP: nParam = VK_NUMPAD8; break; case VK_PRIOR: nParam = VK_NUMPAD9; break; case VK_DELETE: nParam = VK_DECIMAL; break; } if (nParam) { if (keystate[VK_NUMLOCK] & 1) shift_state |= 1; wParam = nParam; } } } /* If a key is pressed and AltGr is not active */ if (key_down && (keystate[VK_RMENU] & 0x80) == 0 && !compose_state) { /* Okay, prepare for most alts then ... */ if (left_alt) *p++ = '\033'; /* Lets see if it's a pattern we know all about ... */ if (wParam == VK_PRIOR && shift_state == 1) { SendMessage(wgs.term_hwnd, WM_VSCROLL, SB_PAGEUP, 0); return 0; } if (wParam == VK_PRIOR && shift_state == 3) { /* ctrl-shift-pageup */ SendMessage(wgs.term_hwnd, WM_VSCROLL, SB_TOP, 0); return 0; } if (wParam == VK_NEXT && shift_state == 3) { /* ctrl-shift-pagedown */ SendMessage(wgs.term_hwnd, WM_VSCROLL, SB_BOTTOM, 0); return 0; } if (wParam == VK_PRIOR && shift_state == 2) { SendMessage(wgs.term_hwnd, WM_VSCROLL, SB_LINEUP, 0); return 0; } if (wParam == VK_NEXT && shift_state == 1) { SendMessage(wgs.term_hwnd, WM_VSCROLL, SB_PAGEDOWN, 0); return 0; } if (wParam == VK_NEXT && shift_state == 2) { SendMessage(wgs.term_hwnd, WM_VSCROLL, SB_LINEDOWN, 0); return 0; } if ((wParam == VK_PRIOR || wParam == VK_NEXT) && shift_state == 3) { term_scroll_to_selection(term, (wParam == VK_PRIOR ? 0 : 1)); return 0; } if (wParam == VK_INSERT && shift_state == 2) { switch (conf_get_int(conf, CONF_ctrlshiftins)) { case CLIPUI_IMPLICIT: break; /* no need to re-copy to CLIP_LOCAL */ case CLIPUI_EXPLICIT: term_request_copy(term, clips_system, lenof(clips_system)); break; default: break; } return 0; } if (wParam == VK_INSERT && shift_state == 1) { switch (conf_get_int(conf, CONF_ctrlshiftins)) { case CLIPUI_IMPLICIT: term_request_paste(term, CLIP_LOCAL); break; case CLIPUI_EXPLICIT: term_request_paste(term, CLIP_SYSTEM); break; default: break; } return 0; } if (wParam == 'C' && shift_state == 3) { switch (conf_get_int(conf, CONF_ctrlshiftcv)) { case CLIPUI_IMPLICIT: break; /* no need to re-copy to CLIP_LOCAL */ case CLIPUI_EXPLICIT: term_request_copy(term, clips_system, lenof(clips_system)); break; default: break; } return 0; } if (wParam == 'V' && shift_state == 3) { switch (conf_get_int(conf, CONF_ctrlshiftcv)) { case CLIPUI_IMPLICIT: term_request_paste(term, CLIP_LOCAL); break; case CLIPUI_EXPLICIT: term_request_paste(term, CLIP_SYSTEM); break; default: break; } return 0; } if (left_alt && wParam == VK_F4 && conf_get_bool(conf, CONF_alt_f4)) { return -1; } if (left_alt && wParam == VK_SPACE && conf_get_bool(conf, CONF_alt_space)) { SendMessage(wgs.term_hwnd, WM_SYSCOMMAND, SC_KEYMENU, 0); return -1; } if (left_alt && wParam == VK_RETURN && conf_get_bool(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 = !term->app_keypad_keys; return 0; } if (wParam == VK_BACK && shift_state == 0) { /* Backspace */ *p++ = (conf_get_bool(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_bool(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'; return p - output; } if (wParam == VK_SPACE && shift_state == 2) { /* Ctrl-Space */ *p++ = 0; return p - output; } if (wParam == VK_SPACE && shift_state == 3) { /* Ctrl-Shift-Space */ *p++ = 160; return p - output; } if (wParam == VK_CANCEL && shift_state == 2) { /* Ctrl-Break */ if (backend) backend_special(backend, SS_BRK, 0); return 0; } if (wParam == VK_PAUSE) { /* Break/Pause */ *p++ = 26; *p++ = 0; return -2; } /* Control-2 to Control-8 are special */ if (shift_state == 2 && wParam >= '2' && wParam <= '8') { *p++ = "\000\033\034\035\036\037\177"[wParam - '2']; return p - output; } if (shift_state == 2 && (wParam == 0xBD || wParam == 0xBF)) { *p++ = 0x1F; return p - output; } if (shift_state == 2 && (wParam == 0xDF || wParam == 0xDC)) { *p++ = 0x1C; return p - output; } if (shift_state == 3 && wParam == 0xDE) { *p++ = 0x1E; /* Ctrl-~ == Ctrl-^ in xterm at least */ return p - output; } switch (wParam) { case VK_NUMPAD0: keypad_key = '0'; goto numeric_keypad; case VK_NUMPAD1: keypad_key = '1'; goto numeric_keypad; case VK_NUMPAD2: keypad_key = '2'; goto numeric_keypad; case VK_NUMPAD3: keypad_key = '3'; goto numeric_keypad; case VK_NUMPAD4: keypad_key = '4'; goto numeric_keypad; case VK_NUMPAD5: keypad_key = '5'; goto numeric_keypad; case VK_NUMPAD6: keypad_key = '6'; goto numeric_keypad; case VK_NUMPAD7: keypad_key = '7'; goto numeric_keypad; case VK_NUMPAD8: keypad_key = '8'; goto numeric_keypad; case VK_NUMPAD9: keypad_key = '9'; goto numeric_keypad; case VK_DECIMAL: keypad_key = '.'; goto numeric_keypad; case VK_ADD: keypad_key = '+'; goto numeric_keypad; case VK_SUBTRACT: keypad_key = '-'; goto numeric_keypad; case VK_MULTIPLY: keypad_key = '*'; goto numeric_keypad; case VK_DIVIDE: keypad_key = '/'; goto numeric_keypad; case VK_EXECUTE: keypad_key = 'G'; goto numeric_keypad; /* also the case for VK_RETURN below can sometimes come here */ numeric_keypad: /* Left Alt overrides all numeric keypad usage to act as * numeric character code input */ if (left_alt) { if (keypad_key >= '0' && keypad_key <= '9') alt_sum = alt_sum * 10 + keypad_key - '0'; else alt_sum = 0; break; } { int nchars = format_numeric_keypad_key( (char *)p, term, keypad_key, shift_state & 1, shift_state & 2); if (!nchars) { /* * If we didn't get an escape sequence out of the * numeric keypad key, then that must be because * we're in Num Lock mode without application * keypad enabled. In that situation we leave this * keypress to the ToUnicode/ToAsciiEx handler * below, which will translate it according to the * appropriate keypad layout (e.g. so that what a * Brit thinks of as keypad '.' can become ',' in * the German layout). * * An exception is the keypad Return key: if we * didn't get an escape sequence for that, we * treat it like ordinary Return, taking into * account Telnet special new line codes and * config options. */ if (keypad_key == '\r') goto ordinary_return_key; break; } p += nchars; return p - output; } int fkey_number; case VK_F1: fkey_number = 1; goto numbered_function_key; case VK_F2: fkey_number = 2; goto numbered_function_key; case VK_F3: fkey_number = 3; goto numbered_function_key; case VK_F4: fkey_number = 4; goto numbered_function_key; case VK_F5: fkey_number = 5; goto numbered_function_key; case VK_F6: fkey_number = 6; goto numbered_function_key; case VK_F7: fkey_number = 7; goto numbered_function_key; case VK_F8: fkey_number = 8; goto numbered_function_key; case VK_F9: fkey_number = 9; goto numbered_function_key; case VK_F10: fkey_number = 10; goto numbered_function_key; case VK_F11: fkey_number = 11; goto numbered_function_key; case VK_F12: fkey_number = 12; goto numbered_function_key; case VK_F13: fkey_number = 13; goto numbered_function_key; case VK_F14: fkey_number = 14; goto numbered_function_key; case VK_F15: fkey_number = 15; goto numbered_function_key; case VK_F16: fkey_number = 16; goto numbered_function_key; case VK_F17: fkey_number = 17; goto numbered_function_key; case VK_F18: fkey_number = 18; goto numbered_function_key; case VK_F19: fkey_number = 19; goto numbered_function_key; case VK_F20: fkey_number = 20; goto numbered_function_key; numbered_function_key: p += format_function_key((char *)p, term, fkey_number, shift_state & 1, shift_state & 2); return p - output; SmallKeypadKey sk_key; case VK_HOME: sk_key = SKK_HOME; goto small_keypad_key; case VK_END: sk_key = SKK_END; goto small_keypad_key; case VK_INSERT: sk_key = SKK_INSERT; goto small_keypad_key; case VK_DELETE: sk_key = SKK_DELETE; goto small_keypad_key; case VK_PRIOR: sk_key = SKK_PGUP; goto small_keypad_key; case VK_NEXT: sk_key = SKK_PGDN; goto small_keypad_key; small_keypad_key: /* These keys don't generate terminal input with Ctrl */ if (shift_state & 2) break; p += format_small_keypad_key((char *)p, term, sk_key); return p - output; char xkey; case VK_UP: xkey = 'A'; goto arrow_key; case VK_DOWN: xkey = 'B'; goto arrow_key; case VK_RIGHT: xkey = 'C'; goto arrow_key; case VK_LEFT: xkey = 'D'; goto arrow_key; case VK_CLEAR: xkey = 'G'; goto arrow_key; /* close enough */ arrow_key: p += format_arrow_key((char *)p, term, xkey, shift_state & 2); return p - output; case VK_RETURN: if (HIWORD(lParam) & KF_EXTENDED) { keypad_key = '\r'; goto numeric_keypad; } ordinary_return_key: if (shift_state == 0 && term->cr_lf_return) { *p++ = '\r'; *p++ = '\n'; return p - output; } else { *p++ = 0x0D; *p++ = 0; return -2; } } } /* Okay we've done everything interesting; let windows deal with * the boring stuff */ { bool capsOn = false; /* helg: clear CAPS LOCK state if caps lock switches to cyrillic */ if(keystate[VK_CAPITAL] != 0 && conf_get_bool(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 (osPlatformId == 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_keyinputw(term, &keybuf, 1); continue; } compose_state = 0; if (!key_down) { if (alt_sum) { if (in_utf(term) || ucsdata.dbcs_screenfont) { keybuf = alt_sum; term_keyinputw(term, &keybuf, 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_keyinput(term, -1, &ch, 1); } alt_sum = 0; } else { term_keyinputw(term, &wch, 1); } } else { if(capsOn && wch < 0x80) { WCHAR cbuf[2]; cbuf[0] = 27; cbuf[1] = xlat_uskbd2cyrllic(wch); term_keyinputw(term, cbuf+!left_alt, 1+!!left_alt); } else { WCHAR cbuf[2]; cbuf[0] = '\033'; cbuf[1] = wch; term_keyinputw(term, cbuf +!left_alt, 1+!!left_alt); } } show_mouseptr(false); } /* This is so the ALT-Numpad and dead keys work correctly. */ keys_unicode[0] = 0; return p - output; } /* If we're definitely 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_bool(conf, CONF_alt_only)) return 0; return -1; } static void wintw_set_title(TermWin *tw, const char *title) { sfree(window_name); window_name = dupstr(title); if (conf_get_bool(conf, CONF_win_name_always) || !IsIconic(wgs.term_hwnd)) SetWindowText(wgs.term_hwnd, title); } static void wintw_set_icon_title(TermWin *tw, const char *title) { sfree(icon_name); icon_name = dupstr(title); if (!conf_get_bool(conf, CONF_win_name_always) && IsIconic(wgs.term_hwnd)) SetWindowText(wgs.term_hwnd, title); } static void wintw_set_scrollbar(TermWin *tw, int total, int start, int page) { SCROLLINFO si; if (!conf_get_bool(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; si.nPos = start; if (wgs.term_hwnd) SetScrollInfo(wgs.term_hwnd, SB_VERT, &si, true); } static bool wintw_setup_draw_ctx(TermWin *tw) { assert(!wintw_hdc); wintw_hdc = make_hdc(); return wintw_hdc != NULL; } static void wintw_free_draw_ctx(TermWin *tw) { assert(wintw_hdc); free_hdc(wintw_hdc); wintw_hdc = NULL; } /* * Set up the colour palette. */ static void init_palette(void) { pal = NULL; logpal = snew_plus(LOGPALETTE, (OSC4_NCOLOURS - 1) * sizeof(PALETTEENTRY)); logpal->palVersion = 0x300; logpal->palNumEntries = OSC4_NCOLOURS; for (unsigned i = 0; i < OSC4_NCOLOURS; i++) logpal->palPalEntry[i].peFlags = PC_NOCOLLAPSE; } static void wintw_palette_set(TermWin *win, unsigned start, unsigned ncolours, const rgb *colours_in) { assert(start <= OSC4_NCOLOURS); assert(ncolours <= OSC4_NCOLOURS - start); for (unsigned i = 0; i < ncolours; i++) { const rgb *in = &colours_in[i]; PALETTEENTRY *out = &logpal->palPalEntry[i + start]; out->peRed = in->r; out->peGreen = in->g; out->peBlue = in->b; colours[i + start] = RGB(in->r, in->g, in->b) ^ colorref_modifier; } bool got_new_palette = false; if (!tried_pal && conf_get_bool(conf, CONF_try_palette)) { HDC hdc = GetDC(wgs.term_hwnd); if (GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) { pal = CreatePalette(logpal); if (pal) { SelectPalette(hdc, pal, false); RealizePalette(hdc); SelectPalette(hdc, GetStockObject(DEFAULT_PALETTE), false); /* Convert all RGB() values in colours[] into PALETTERGB(), * and ensure we stick to that later */ colorref_modifier = PALETTERGB(0, 0, 0) ^ RGB(0, 0, 0); for (unsigned i = 0; i < OSC4_NCOLOURS; i++) colours[i] ^= colorref_modifier; /* Inhibit the SetPaletteEntries call below */ got_new_palette = true; } } ReleaseDC(wgs.term_hwnd, hdc); tried_pal = true; } if (pal && !got_new_palette) { /* We already had a palette, so replace the changed colours in the * existing one. */ SetPaletteEntries(pal, start, ncolours, logpal->palPalEntry + start); HDC hdc = make_hdc(); UnrealizeObject(pal); RealizePalette(hdc); free_hdc(hdc); } if (start <= OSC4_COLOUR_bg && OSC4_COLOUR_bg < start + ncolours) { /* If Default Background changes, we need to ensure any space between * the text area and the window border is redrawn. */ InvalidateRect(wgs.term_hwnd, NULL, true); } } void write_aclip(int clipboard, char *data, int len, bool must_deselect) { HGLOBAL clipdata; void *lock; if (clipboard != CLIP_SYSTEM) return; clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len + 1); if (!clipdata) return; lock = GlobalLock(clipdata); if (!lock) return; memcpy(lock, data, len); ((unsigned char *) lock)[len] = 0; GlobalUnlock(clipdata); if (!must_deselect) SendMessage(wgs.term_hwnd, WM_IGNORE_CLIP, true, 0); if (OpenClipboard(wgs.term_hwnd)) { EmptyClipboard(); SetClipboardData(CF_TEXT, clipdata); CloseClipboard(); } else GlobalFree(clipdata); if (!must_deselect) SendMessage(wgs.term_hwnd, WM_IGNORE_CLIP, false, 0); } typedef struct _rgbindex { int index; COLORREF ref; } rgbindex; int cmpCOLORREF(void *va, void *vb) { COLORREF a = ((rgbindex *)va)->ref; COLORREF b = ((rgbindex *)vb)->ref; return (a < b) ? -1 : (a > b) ? +1 : 0; } /* * Note: unlike write_aclip() this will not append a nul. */ static void wintw_clip_write( TermWin *tw, int clipboard, wchar_t *data, int *attr, truecolour *truecolour, int len, bool must_deselect) { HGLOBAL clipdata, clipdata2, clipdata3; int len2; void *lock, *lock2, *lock3; if (clipboard != CLIP_SYSTEM) return; len2 = WideCharToMultiByte(CP_ACP, 0, data, len, 0, 0, NULL, NULL); clipdata = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len * sizeof(wchar_t)); clipdata2 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, len2); 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_bool(conf, CONF_rtf_paste)) { wchar_t unitab[256]; strbuf *rtf = strbuf_new(); unsigned char *tdata = (unsigned char *)lock2; wchar_t *udata = (wchar_t *)lock; int uindex = 0, tindex = 0; int multilen, blen, alen, totallen, i; char before[16], after[4]; int fgcolour, lastfgcolour = -1; int bgcolour, lastbgcolour = -1; COLORREF fg, lastfg = -1; COLORREF bg, lastbg = -1; int attrBold, lastAttrBold = 0; int attrUnder, lastAttrUnder = 0; int palette[OSC4_NCOLOURS]; int numcolours; tree234 *rgbtree = NULL; FontSpec *font = conf_get_fontspec(conf, CONF_font); get_unitab(CP_ACP, unitab, 0); strbuf_catf( 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;} */ /* * First - Determine all colours in use * o Foregound and background colours share the same palette */ if (attr) { memset(palette, 0, sizeof(palette)); for (i = 0; i < (len-1); i++) { fgcolour = ((attr[i] & ATTR_FGMASK) >> ATTR_FGSHIFT); bgcolour = ((attr[i] & ATTR_BGMASK) >> ATTR_BGSHIFT); 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)) { if (bgcolour < 8) /* ANSI colours */ bgcolour += 8; else if (bgcolour >= 256) /* Default colours */ bgcolour ++; } palette[fgcolour]++; palette[bgcolour]++; } if (truecolour) { rgbtree = newtree234(cmpCOLORREF); for (i = 0; i < (len-1); i++) { if (truecolour[i].fg.enabled) { rgbindex *rgbp = snew(rgbindex); rgbp->ref = RGB(truecolour[i].fg.r, truecolour[i].fg.g, truecolour[i].fg.b); if (add234(rgbtree, rgbp) != rgbp) sfree(rgbp); } if (truecolour[i].bg.enabled) { rgbindex *rgbp = snew(rgbindex); rgbp->ref = RGB(truecolour[i].bg.r, truecolour[i].bg.g, truecolour[i].bg.b); if (add234(rgbtree, rgbp) != rgbp) sfree(rgbp); } } } /* * Next - Create a reduced palette */ numcolours = 0; for (i = 0; i < OSC4_NCOLOURS; i++) { if (palette[i] != 0) palette[i] = ++numcolours; } if (rgbtree) { rgbindex *rgbp; for (i = 0; (rgbp = index234(rgbtree, i)) != NULL; i++) rgbp->index = ++numcolours; } /* * Finally - Write the colour table */ put_datapl(rtf, PTRLEN_LITERAL("{\\colortbl ;")); for (i = 0; i < OSC4_NCOLOURS; i++) { if (palette[i] != 0) { const PALETTEENTRY *pe = &logpal->palPalEntry[i]; strbuf_catf(rtf, "\\red%d\\green%d\\blue%d;", pe->peRed, pe->peGreen, pe->peBlue); } } if (rgbtree) { rgbindex *rgbp; for (i = 0; (rgbp = index234(rgbtree, i)) != NULL; i++) strbuf_catf(rtf, "\\red%d\\green%d\\blue%d;", GetRValue(rgbp->ref), GetGValue(rgbp->ref), GetBValue(rgbp->ref)); } put_datapl(rtf, PTRLEN_LITERAL("}")); } /* * We want to construct a piece of RTF that specifies the * same Unicode text. To do this we will read back in * parallel from the Unicode data in `udata' and the * non-Unicode data in `tdata'. For each character in * `tdata' which becomes the right thing in `udata' when * looked up in `unitab', we just copy straight over from * tdata. For each one that doesn't, we must WCToMB it * individually and produce a \u escape sequence. * * It would probably be more robust to just bite the bullet * and WCToMB each individual Unicode character one by one, * then MBToWC each one back to see if it was an accurate * translation; but that strikes me as a horrifying number * of Windows API calls so I want to see if this faster way * will work. If it screws up badly we can always revert to * the simple and slow way. */ while (tindex < len2 && uindex < len && tdata[tindex] && udata[uindex]) { if (tindex + 1 < len2 && tdata[tindex] == '\r' && tdata[tindex+1] == '\n') { tindex++; uindex++; } /* * Set text attributes */ if (attr) { /* * Determine foreground and background colours */ if (truecolour && truecolour[tindex].fg.enabled) { fgcolour = -1; fg = RGB(truecolour[tindex].fg.r, truecolour[tindex].fg.g, truecolour[tindex].fg.b); } else { fgcolour = ((attr[tindex] & ATTR_FGMASK) >> ATTR_FGSHIFT); fg = -1; } if (truecolour && truecolour[tindex].bg.enabled) { bgcolour = -1; bg = RGB(truecolour[tindex].bg.r, truecolour[tindex].bg.g, truecolour[tindex].bg.b); } else { bgcolour = ((attr[tindex] & ATTR_BGMASK) >> ATTR_BGSHIFT); bg = -1; } if (attr[tindex] & ATTR_REVERSE) { int tmpcolour = fgcolour; /* Swap foreground and background */ fgcolour = bgcolour; bgcolour = tmpcolour; COLORREF tmpref = fg; fg = bg; bg = tmpref; } if (bold_colours && (attr[tindex] & ATTR_BOLD) && (fgcolour >= 0)) { if (fgcolour < 8) /* ANSI colours */ fgcolour += 8; else if (fgcolour >= 256) /* Default colours */ fgcolour ++; } if ((attr[tindex] & ATTR_BLINK) && (bgcolour >= 0)) { 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 */ } } /* * Write RTF text attributes */ if ((lastfgcolour != fgcolour) || (lastfg != fg)) { lastfgcolour = fgcolour; lastfg = fg; if (fg == -1) { strbuf_catf(rtf, "\\cf%d ", (fgcolour >= 0) ? palette[fgcolour] : 0); } else { rgbindex rgb, *rgbp; rgb.ref = fg; if ((rgbp = find234(rgbtree, &rgb, NULL)) != NULL) strbuf_catf(rtf, "\\cf%d ", rgbp->index); } } if ((lastbgcolour != bgcolour) || (lastbg != bg)) { lastbgcolour = bgcolour; lastbg = bg; if (bg == -1) strbuf_catf(rtf, "\\highlight%d ", (bgcolour >= 0) ? palette[bgcolour] : 0); else { rgbindex rgb, *rgbp; rgb.ref = bg; if ((rgbp = find234(rgbtree, &rgb, NULL)) != NULL) strbuf_catf(rtf, "\\highlight%d ", rgbp->index); } } if (lastAttrBold != attrBold) { lastAttrBold = attrBold; put_datapl(rtf, attrBold ? PTRLEN_LITERAL("\\b ") : PTRLEN_LITERAL("\\b0 ")); } if (lastAttrUnder != attrUnder) { lastAttrUnder = attrUnder; put_datapl(rtf, attrUnder ? PTRLEN_LITERAL("\\ul ") : PTRLEN_LITERAL("\\ulnone ")); } } if (unitab[tdata[tindex]] == udata[uindex]) { multilen = 1; before[0] = '\0'; after[0] = '\0'; blen = alen = 0; } else { multilen = WideCharToMultiByte(CP_ACP, 0, unitab+uindex, 1, NULL, 0, NULL, NULL); if (multilen != 1) { blen = sprintf(before, "{\\uc%d\\u%d", (int)multilen, (int)udata[uindex]); alen = 1; strcpy(after, "}"); } else { blen = sprintf(before, "\\u%d", (int)udata[uindex]); alen = 0; after[0] = '\0'; } } assert(tindex + multilen <= len2); totallen = blen + alen; for (i = 0; i < multilen; i++) { if (tdata[tindex+i] == '\\' || tdata[tindex+i] == '{' || tdata[tindex+i] == '}') totallen += 2; else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A) totallen += 6; /* \par\r\n */ else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20) totallen += 4; else totallen++; } put_data(rtf, before, blen); for (i = 0; i < multilen; i++) { if (tdata[tindex+i] == '\\' || tdata[tindex+i] == '{' || tdata[tindex+i] == '}') { put_byte(rtf, '\\'); put_byte(rtf, tdata[tindex+i]); } else if (tdata[tindex+i] == 0x0D || tdata[tindex+i] == 0x0A) { put_datapl(rtf, PTRLEN_LITERAL("\\par\r\n")); } else if (tdata[tindex+i] > 0x7E || tdata[tindex+i] < 0x20) { strbuf_catf(rtf, "\\'%02x", tdata[tindex+i]); } else { put_byte(rtf, tdata[tindex+i]); } } put_data(rtf, after, alen); tindex += multilen; uindex++; } put_datapl(rtf, PTRLEN_LITERAL("}\0\0")); /* Terminate RTF stream */ clipdata3 = GlobalAlloc(GMEM_DDESHARE | GMEM_MOVEABLE, rtf->len); if (clipdata3 && (lock3 = GlobalLock(clipdata3)) != NULL) { memcpy(lock3, rtf->u, rtf->len); GlobalUnlock(clipdata3); } strbuf_free(rtf); if (rgbtree) { rgbindex *rgbp; while ((rgbp = delpos234(rgbtree, 0)) != NULL) sfree(rgbp); freetree234(rgbtree); } } else clipdata3 = NULL; GlobalUnlock(clipdata); GlobalUnlock(clipdata2); if (!must_deselect) SendMessage(wgs.term_hwnd, WM_IGNORE_CLIP, true, 0); if (OpenClipboard(wgs.term_hwnd)) { EmptyClipboard(); SetClipboardData(CF_UNICODETEXT, clipdata); SetClipboardData(CF_TEXT, clipdata2); if (clipdata3) SetClipboardData(RegisterClipboardFormat(CF_RTF), clipdata3); CloseClipboard(); } else { GlobalFree(clipdata); GlobalFree(clipdata2); } if (!must_deselect) SendMessage(wgs.term_hwnd, WM_IGNORE_CLIP, false, 0); } static DWORD WINAPI clipboard_read_threadfunc(void *param) { HWND hwnd = (HWND)param; HGLOBAL clipdata; if (OpenClipboard(NULL)) { if ((clipdata = GetClipboardData(CF_UNICODETEXT))) { SendMessage(hwnd, WM_GOT_CLIPDATA, (WPARAM)true, (LPARAM)clipdata); } else if ((clipdata = GetClipboardData(CF_TEXT))) { SendMessage(hwnd, WM_GOT_CLIPDATA, (WPARAM)false, (LPARAM)clipdata); } CloseClipboard(); } return 0; } static void process_clipdata(HGLOBAL clipdata, bool unicode) { wchar_t *clipboard_contents = NULL; size_t clipboard_length = 0; if (unicode) { wchar_t *p = GlobalLock(clipdata); wchar_t *p2; if (p) { /* Unwilling to rely on Windows having wcslen() */ for (p2 = p; *p2; p2++); clipboard_length = p2 - p; clipboard_contents = snewn(clipboard_length + 1, wchar_t); memcpy(clipboard_contents, p, clipboard_length * sizeof(wchar_t)); clipboard_contents[clipboard_length] = L'\0'; term_do_paste(term, clipboard_contents, clipboard_length); } } else { char *s = GlobalLock(clipdata); int i; if (s) { i = MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, 0, 0); clipboard_contents = snewn(i, wchar_t); MultiByteToWideChar(CP_ACP, 0, s, strlen(s) + 1, clipboard_contents, i); clipboard_length = i - 1; clipboard_contents[clipboard_length] = L'\0'; term_do_paste(term, clipboard_contents, clipboard_length); } } sfree(clipboard_contents); } static void wintw_clip_request_paste(TermWin *tw, int clipboard) { assert(clipboard == CLIP_SYSTEM); /* * I always thought pasting was synchronous in Windows; the * clipboard access functions certainly _look_ synchronous, * unlike the X ones. But in fact it seems that in some * situations the contents of the clipboard might not be * immediately available, and the clipboard-reading functions * may block. This leads to trouble if the application * delivering the clipboard data has to get hold of it by - * for example - talking over a network connection which is * forwarded through this very PuTTY. * * Hence, we spawn a subthread to read the clipboard, and do * our paste when it's finished. The thread will send a * message back to our main window when it terminates, and * that tells us it's OK to paste. */ DWORD in_threadid; /* required for Win9x */ HANDLE hThread = CreateThread(NULL, 0, clipboard_read_threadfunc, wgs.term_hwnd, 0, &in_threadid); if (hThread) CloseHandle(hThread); /* we don't need the thread handle */ } /* * Print a modal (Really Bad) message box and perform a fatal exit. */ void modalfatalbox(const char *fmt, ...) { va_list ap; char *message, *title; va_start(ap, fmt); message = dupvprintf(fmt, ap); va_end(ap); show_mouseptr(true); title = dupprintf("%s Fatal Error", appname); MessageBox(wgs.term_hwnd, message, title, MB_SYSTEMMODAL | MB_ICONERROR | MB_OK); sfree(message); sfree(title); cleanup_exit(1); } /* * Print a message box and don't close the connection. */ void nonfatal(const char *fmt, ...) { va_list ap; char *message, *title; va_start(ap, fmt); message = dupvprintf(fmt, ap); va_end(ap); show_mouseptr(true); title = dupprintf("%s Error", appname); MessageBox(wgs.term_hwnd, message, title, MB_ICONERROR | MB_OK); sfree(message); sfree(title); } static bool flash_window_ex(DWORD dwFlags, UINT uCount, DWORD dwTimeout) { if (p_FlashWindowEx) { FLASHWINFO fi; fi.cbSize = sizeof(fi); fi.hwnd = wgs.term_hwnd; fi.dwFlags = dwFlags; fi.uCount = uCount; fi.dwTimeout = dwTimeout; return (*p_FlashWindowEx)(&fi); } else return false; /* shrug */ } static void flash_window(int mode); static long next_flash; static bool flashing = false; /* * 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 = false; if (p_FlashWindowEx) flash_window_ex(FLASHW_STOP, 0, 0); else FlashWindow(wgs.term_hwnd, false); } } else if (mode == 2) { /* start */ if (!flashing) { flashing = true; if (p_FlashWindowEx) { /* 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(wgs.term_hwnd, true); next_flash = schedule_timer(450, flash_window_timer, wgs.term_hwnd); } } } else if ((mode == 1) && (beep_ind == B_IND_FLASH)) { /* maintain */ if (flashing && !p_FlashWindowEx) { FlashWindow(wgs.term_hwnd, true); /* toggle */ next_flash = schedule_timer(450, flash_window_timer, wgs.term_hwnd); } } } /* * Beep. */ static void wintw_bell(TermWin *tw, int mode) { if (mode == BELL_DEFAULT) { /* * For MessageBeep style bells, we want to be careful of * timing, because they don't have the nice property of * PlaySound bells that each one cancels the previous * active one. So we limit the rate to one per 50ms or so. */ static long lastbeep = 0; long beepdiff; beepdiff = GetTickCount() - lastbeep; if (beepdiff >= 0 && beepdiff < 50) return; 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 (!p_PlaySound || !p_PlaySound(bell_wavefile->path, NULL, SND_ASYNC | SND_FILENAME)) { char *buf, *otherbuf; show_mouseptr(true); buf = dupprintf( "Unable to play sound file\n%s\nUsing default sound instead", bell_wavefile->path); otherbuf = dupprintf("%s Sound Error", appname); MessageBox(wgs.term_hwnd, buf, otherbuf, MB_OK | MB_ICONEXCLAMATION); sfree(buf); sfree(otherbuf); 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) return; /* * We must beep in different ways depending on whether this * is a 95-series or NT-series OS. */ if (osPlatformId == VER_PLATFORM_WIN32_NT) Beep(800, 100); else MessageBeep(-1); lastbeep = GetTickCount(); } /* Otherwise, either visual bell or disabled; do nothing here */ if (!term->has_focus) { flash_window(2); /* start */ } } /* * Minimise or restore the window in response to a server-side * request. */ static void wintw_set_minimised(TermWin *tw, bool minimised) { if (IsIconic(wgs.term_hwnd)) { if (!minimised) ShowWindow(wgs.term_hwnd, SW_RESTORE); } else { if (minimised) ShowWindow(wgs.term_hwnd, SW_MINIMIZE); } } /* * Move the window in response to a server-side request. */ static void wintw_move(TermWin *tw, int x, int y) { int resize_action = conf_get_int(conf, CONF_resize_action); if (resize_action == RESIZE_DISABLED || resize_action == RESIZE_FONT || IsZoomed(wgs.term_hwnd)) return; SetWindowPos(wgs.term_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. */ static void wintw_set_zorder(TermWin *tw, bool top) { if (conf_get_bool(conf, CONF_alwaysontop)) return; /* ignore */ SetWindowPos(wgs.term_hwnd, top ? HWND_TOP : HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); } /* * Refresh the window in response to a server-side request. */ static void wintw_refresh(TermWin *tw) { InvalidateRect(wgs.term_hwnd, NULL, true); } /* * Maximise or restore the window in response to a server-side * request. */ static void wintw_set_maximised(TermWin *tw, bool maximised) { if (IsZoomed(wgs.term_hwnd)) { if (!maximised) ShowWindow(wgs.term_hwnd, SW_RESTORE); } else { if (maximised) ShowWindow(wgs.term_hwnd, SW_MAXIMIZE); } } /* * See if we're in full-screen mode. */ static bool is_full_screen() { if (!IsZoomed(wgs.term_hwnd)) return false; if (GetWindowLongPtr(wgs.term_hwnd, GWL_STYLE) & WS_CAPTION) return false; return true; } /* Get the rect/size of a full screen window using the nearest available * monitor in multimon systems; default to something sensible if only * one monitor is present. */ static bool get_fullscreen_rect(RECT * ss) { #if defined(MONITOR_DEFAULTTONEAREST) && !defined(NO_MULTIMON) HMONITOR mon; MONITORINFO mi; mon = MonitorFromWindow(wgs.term_hwnd, MONITOR_DEFAULTTONEAREST); mi.cbSize = sizeof(mi); GetMonitorInfo(mon, &mi); /* structure copy */ *ss = mi.rcMonitor; return true; #else /* could also use code like this: ss->left = ss->top = 0; ss->right = GetSystemMetrics(SM_CXSCREEN); ss->bottom = GetSystemMetrics(SM_CYSCREEN); */ return GetClientRect(GetDesktopWindow(), ss); #endif } /* * Go full-screen. This should only be called when we are already * maximised. */ static void make_full_screen() { DWORD style; RECT ss; assert(IsZoomed(wgs.term_hwnd)); if (is_full_screen()) return; /* Remove the window furniture. */ style = GetWindowLongPtr(wgs.term_hwnd, GWL_STYLE); style &= ~(WS_CAPTION | WS_BORDER | WS_THICKFRAME); if (conf_get_bool(conf, CONF_scrollbar_in_fullscreen)) style |= WS_VSCROLL; else style &= ~WS_VSCROLL; SetWindowLongPtr(wgs.term_hwnd, GWL_STYLE, style); /* Resize ourselves to exactly cover the nearest monitor. */ get_fullscreen_rect(&ss); SetWindowPos(wgs.term_hwnd, HWND_TOP, ss.left, ss.top, ss.right - ss.left, ss.bottom - ss.top, SWP_FRAMECHANGED); /* We may have changed size as a result */ reset_window(0); /* Tick the menu item in the System and context menus. */ { int i; for (i = 0; i < lenof(popup_menus); i++) CheckMenuItem(popup_menus[i].menu, IDM_FULLSCREEN, MF_CHECKED); } } /* * Clear the full-screen attributes. */ static void clear_full_screen() { DWORD oldstyle, style; /* Reinstate the window furniture. */ style = oldstyle = GetWindowLongPtr(wgs.term_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_bool(conf, CONF_scrollbar)) style |= WS_VSCROLL; else style &= ~WS_VSCROLL; if (style != oldstyle) { SetWindowLongPtr(wgs.term_hwnd, GWL_STYLE, style); SetWindowPos(wgs.term_hwnd, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); } /* Untick the menu item in the System and context menus. */ { int i; for (i = 0; i < lenof(popup_menus); i++) CheckMenuItem(popup_menus[i].menu, IDM_FULLSCREEN, MF_UNCHECKED); } } /* * Toggle full-screen mode. */ static void flip_full_screen() { if (is_full_screen()) { ShowWindow(wgs.term_hwnd, SW_RESTORE); } else if (IsZoomed(wgs.term_hwnd)) { make_full_screen(); } else { SendMessage(wgs.term_hwnd, WM_FULLSCR_ON_MAX, 0, 0); ShowWindow(wgs.term_hwnd, SW_MAXIMIZE); } } static size_t win_seat_output(Seat *seat, bool is_stderr, const void *data, size_t len) { return term_data(term, is_stderr, data, len); } static bool win_seat_eof(Seat *seat) { return true; /* do respond to incoming EOF with outgoing */ } static int win_seat_get_userpass_input( Seat *seat, prompts_t *p, bufchain *input) { int ret; ret = cmdline_get_passwd_input(p); if (ret == -1) ret = term_get_userpass_input(term, p, input); return ret; } static bool win_seat_set_trust_status(Seat *seat, bool trusted) { term_set_trust_status(term, trusted); return true; } static bool win_seat_get_cursor_position(Seat *seat, int *x, int *y) { term_get_cursor_position(term, x, y); return true; } static bool win_seat_get_window_pixel_size(Seat *seat, int *x, int *y) { RECT r; GetWindowRect(wgs.term_hwnd, &r); *x = r.right - r.left; *y = r.bottom - r.top; return true; } putty-0.76/windows/wingss.c0000644000175000017500000005726114072266314012744 00000000000000#ifndef NO_GSSAPI #include #include "putty.h" #define SECURITY_WIN32 #include #include "pgssapi.h" #include "sshgss.h" #include "sshgssc.h" #include "misc.h" #define UNIX_EPOCH 11644473600ULL /* Seconds from Windows epoch */ #define CNS_PERSEC 10000000ULL /* # 100ns per second */ /* * Note, as a special case, 0 relative to the Windows epoch (unspecified) maps * to 0 relative to the POSIX epoch (unspecified)! */ #define TIME_WIN_TO_POSIX(ft, t) do { \ ULARGE_INTEGER uli; \ uli.LowPart = (ft).dwLowDateTime; \ uli.HighPart = (ft).dwHighDateTime; \ if (uli.QuadPart != 0) \ uli.QuadPart = uli.QuadPart / CNS_PERSEC - UNIX_EPOCH; \ (t) = (time_t) uli.QuadPart; \ } while(0) /* Windows code to set up the GSSAPI library list. */ #ifdef _WIN64 #define MIT_KERB_SUFFIX "64" #else #define MIT_KERB_SUFFIX "32" #endif const int ngsslibs = 3; const char *const gsslibnames[3] = { "MIT Kerberos GSSAPI"MIT_KERB_SUFFIX".DLL", "Microsoft SSPI SECUR32.DLL", "User-specified GSSAPI DLL", }; const struct keyvalwhere gsslibkeywords[] = { { "gssapi32", 0, -1, -1 }, { "sspi", 1, -1, -1 }, { "custom", 2, -1, -1 }, }; DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS, AcquireCredentialsHandleA, (SEC_CHAR *, SEC_CHAR *, ULONG, PVOID, PVOID, SEC_GET_KEY_FN, PVOID, PCredHandle, PTimeStamp)); DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS, InitializeSecurityContextA, (PCredHandle, PCtxtHandle, SEC_CHAR *, ULONG, ULONG, ULONG, PSecBufferDesc, ULONG, PCtxtHandle, PSecBufferDesc, PULONG, PTimeStamp)); DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS, FreeContextBuffer, (PVOID)); DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS, FreeCredentialsHandle, (PCredHandle)); DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS, DeleteSecurityContext, (PCtxtHandle)); DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS, QueryContextAttributesA, (PCtxtHandle, ULONG, PVOID)); DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS, MakeSignature, (PCtxtHandle, ULONG, PSecBufferDesc, ULONG)); DECL_WINDOWS_FUNCTION(static, SECURITY_STATUS, VerifySignature, (PCtxtHandle, PSecBufferDesc, ULONG, PULONG)); DECL_WINDOWS_FUNCTION(static, DLL_DIRECTORY_COOKIE, AddDllDirectory, (PCWSTR)); typedef struct winSsh_gss_ctx { unsigned long maj_stat; unsigned long min_stat; CredHandle cred_handle; CtxtHandle context; PCtxtHandle context_handle; TimeStamp expiry; } winSsh_gss_ctx; 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); static tree234 *libraries_to_never_unload; static int library_to_never_unload_cmp(void *av, void *bv) { uintptr_t a = (uintptr_t)av, b = (uintptr_t)bv; return a < b ? -1 : a > b ? +1 : 0; } static void ensure_library_tree_exists(void) { if (!libraries_to_never_unload) libraries_to_never_unload = newtree234(library_to_never_unload_cmp); } static bool library_is_in_never_unload_tree(HMODULE module) { ensure_library_tree_exists(); return find234(libraries_to_never_unload, module, NULL); } static void add_library_to_never_unload_tree(HMODULE module) { ensure_library_tree_exists(); add234(libraries_to_never_unload, module); } 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; static HMODULE kernel32_module; if (!kernel32_module) { kernel32_module = load_system32_dll("kernel32.dll"); } #if defined _MSC_VER && _MSC_VER < 1900 /* Omit the type-check because older MSVCs don't have this function */ GET_WINDOWS_FUNCTION_NO_TYPECHECK(kernel32_module, AddDllDirectory); #else GET_WINDOWS_FUNCTION(kernel32_module, AddDllDirectory); #endif list->libraries = snewn(3, struct ssh_gss_library); list->nlibraries = 0; /* MIT Kerberos GSSAPI implementation */ module = NULL; if (RegOpenKey(HKEY_LOCAL_MACHINE, "SOFTWARE\\MIT\\Kerberos", ®key) == ERROR_SUCCESS) { DWORD type, size; LONG ret; char *buffer; /* Find out the string length */ ret = RegQueryValueEx(regkey, "InstallDir", NULL, &type, NULL, &size); if (ret == ERROR_SUCCESS && type == REG_SZ) { buffer = snewn(size + 20, char); ret = RegQueryValueEx(regkey, "InstallDir", NULL, &type, (LPBYTE)buffer, &size); if (ret == ERROR_SUCCESS && type == REG_SZ) { strcat (buffer, "\\bin"); if(p_AddDllDirectory) { /* Add MIT Kerberos' path to the DLL search path, * it loads its own DLLs further down the road */ wchar_t *dllPath = dup_mb_to_wc(DEFAULT_CODEPAGE, 0, buffer); p_AddDllDirectory(dllPath); sfree(dllPath); } strcat (buffer, "\\gssapi"MIT_KERB_SUFFIX".dll"); module = LoadLibraryEx (buffer, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32 | LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | LOAD_LIBRARY_SEARCH_USER_DIRS); /* * The MIT Kerberos DLL suffers an internal segfault * for some reason if you unload and reload one within * the same process. So, make sure that after we load * this library, we never free it. * * Or rather: after we've loaded it once, if any * _further_ load returns the same module handle, we * immediately free it again (to prevent the Windows * API's internal reference count growing without * bound). But on the other hand we never free it in * ssh_gss_cleanup. */ if (library_is_in_never_unload_tree(module)) FreeLibrary(module); add_library_to_never_unload_tree(module); } sfree(buffer); } RegCloseKey(regkey); } if (module) { struct ssh_gss_library *lib = &list->libraries[list->nlibraries++]; lib->id = 0; lib->gsslogmsg = "Using GSSAPI from GSSAPI"MIT_KERB_SUFFIX".DLL"; 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); BIND_GSS_FN(get_mic); BIND_GSS_FN(verify_mic); BIND_GSS_FN(import_name); BIND_GSS_FN(init_sec_context); BIND_GSS_FN(release_buffer); BIND_GSS_FN(release_cred); BIND_GSS_FN(release_name); BIND_GSS_FN(acquire_cred); BIND_GSS_FN(inquire_cred_by_mech); #undef BIND_GSS_FN ssh_gssapi_bind_fns(lib); } /* Microsoft SSPI Implementation */ module = load_system32_dll("secur32.dll"); if (module) { struct ssh_gss_library *lib = &list->libraries[list->nlibraries++]; lib->id = 1; lib->gsslogmsg = "Using SSPI from SECUR32.DLL"; lib->handle = (void *)module; GET_WINDOWS_FUNCTION(module, AcquireCredentialsHandleA); GET_WINDOWS_FUNCTION(module, InitializeSecurityContextA); GET_WINDOWS_FUNCTION(module, FreeContextBuffer); GET_WINDOWS_FUNCTION(module, FreeCredentialsHandle); GET_WINDOWS_FUNCTION(module, DeleteSecurityContext); GET_WINDOWS_FUNCTION(module, QueryContextAttributesA); GET_WINDOWS_FUNCTION(module, MakeSignature); GET_WINDOWS_FUNCTION(module, VerifySignature); ssh_sspi_bind_fns(lib); } /* * Custom GSSAPI DLL. */ module = NULL; path = conf_get_filename(conf, CONF_ssh_gss_custom)->path; if (*path) { if(p_AddDllDirectory) { /* Add the custom directory as well in case it chainloads * some other DLLs (e.g a non-installed MIT Kerberos * instance) */ int pathlen = strlen(path); while (pathlen > 0 && path[pathlen-1] != ':' && path[pathlen-1] != '\\') pathlen--; if (pathlen > 0 && path[pathlen-1] != '\\') pathlen--; if (pathlen > 0) { char *dirpath = dupprintf("%.*s", pathlen, path); wchar_t *dllPath = dup_mb_to_wc(DEFAULT_CODEPAGE, 0, dirpath); p_AddDllDirectory(dllPath); sfree(dllPath); sfree(dirpath); } } module = LoadLibraryEx(path, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32 | LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | LOAD_LIBRARY_SEARCH_USER_DIRS); } 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); BIND_GSS_FN(get_mic); BIND_GSS_FN(verify_mic); BIND_GSS_FN(import_name); BIND_GSS_FN(init_sec_context); BIND_GSS_FN(release_buffer); BIND_GSS_FN(release_cred); BIND_GSS_FN(release_name); BIND_GSS_FN(acquire_cred); BIND_GSS_FN(inquire_cred_by_mech); #undef BIND_GSS_FN ssh_gssapi_bind_fns(lib); } return list; } void ssh_gss_cleanup(struct ssh_gss_liblist *list) { int i; /* * LoadLibrary and FreeLibrary are defined to employ reference * counting in the case where the same library is repeatedly * loaded, so even in a multiple-sessions-per-process context * (not that we currently expect ever to have such a thing on * Windows) it's safe to naively FreeLibrary everything here * without worrying about destroying it under the feet of * another SSH instance still using it. */ for (i = 0; i < list->nlibraries; i++) { if (list->libraries[i].id != 0) { HMODULE module = (HMODULE)list->libraries[i].handle; if (!library_is_in_never_unload_tree(module)) FreeLibrary(module); } if (list->libraries[i].id == 2) { /* The 'custom' id involves a dynamically allocated message. * Note that we must cast away the 'const' to free it. */ sfree((char *)list->libraries[i].gsslogmsg); } } sfree(list->libraries); sfree(list); } static Ssh_gss_stat ssh_sspi_indicate_mech(struct ssh_gss_library *lib, Ssh_gss_buf *mech) { *mech = gss_mech_krb5; return SSH_GSS_OK; } static Ssh_gss_stat ssh_sspi_import_name(struct ssh_gss_library *lib, char *host, Ssh_gss_name *srv_name) { char *pStr; /* Check hostname */ if (host == NULL) return SSH_GSS_FAILURE; /* copy it into form host/FQDN */ pStr = dupcat("host/", host); *srv_name = (Ssh_gss_name) pStr; return SSH_GSS_OK; } static Ssh_gss_stat ssh_sspi_acquire_cred(struct ssh_gss_library *lib, Ssh_gss_ctx *ctx, time_t *expiry) { winSsh_gss_ctx *winctx = snew(winSsh_gss_ctx); memset(winctx, 0, sizeof(winSsh_gss_ctx)); /* prepare our "wrapper" structure */ winctx->maj_stat = winctx->min_stat = SEC_E_OK; winctx->context_handle = NULL; /* Specifying no principal name here means use the credentials of the current logged-in user */ winctx->maj_stat = p_AcquireCredentialsHandleA(NULL, "Kerberos", SECPKG_CRED_OUTBOUND, NULL, NULL, NULL, NULL, &winctx->cred_handle, NULL); if (winctx->maj_stat != SEC_E_OK) { p_FreeCredentialsHandle(&winctx->cred_handle); sfree(winctx); return SSH_GSS_FAILURE; } /* Windows does not return a valid expiration from AcquireCredentials */ if (expiry) *expiry = GSS_NO_EXPIRATION; *ctx = (Ssh_gss_ctx) winctx; return SSH_GSS_OK; } static void localexp_to_exp_lifetime(TimeStamp *localexp, time_t *expiry, unsigned long *lifetime) { FILETIME nowUTC; FILETIME expUTC; time_t now; time_t exp; time_t delta; if (!lifetime && !expiry) return; GetSystemTimeAsFileTime(&nowUTC); TIME_WIN_TO_POSIX(nowUTC, now); if (lifetime) *lifetime = 0; if (expiry) *expiry = GSS_NO_EXPIRATION; /* * Type oddity: localexp is a pointer to 'TimeStamp', whereas * LocalFileTimeToFileTime expects a pointer to FILETIME. However, * despite having different formal type names from the compiler's * point of view, these two structures are specified to be * isomorphic in the MS documentation, so it's legitimate to copy * between them: * * https://msdn.microsoft.com/en-us/library/windows/desktop/aa380511(v=vs.85).aspx */ { FILETIME localexp_ft; enum { vorpal_sword = 1 / (sizeof(*localexp) == sizeof(localexp_ft)) }; memcpy(&localexp_ft, localexp, sizeof(localexp_ft)); if (!LocalFileTimeToFileTime(&localexp_ft, &expUTC)) return; } TIME_WIN_TO_POSIX(expUTC, exp); delta = exp - now; if (exp == 0 || delta <= 0) return; if (expiry) *expiry = exp; if (lifetime) { if (delta <= ULONG_MAX) *lifetime = (unsigned long)delta; else *lifetime = ULONG_MAX; } } static Ssh_gss_stat ssh_sspi_init_sec_context(struct ssh_gss_library *lib, Ssh_gss_ctx *ctx, Ssh_gss_name srv_name, int to_deleg, Ssh_gss_buf *recv_tok, Ssh_gss_buf *send_tok, time_t *expiry, unsigned long *lifetime) { winSsh_gss_ctx *winctx = (winSsh_gss_ctx *) *ctx; SecBuffer wsend_tok = {send_tok->length,SECBUFFER_TOKEN,send_tok->value}; SecBuffer wrecv_tok = {recv_tok->length,SECBUFFER_TOKEN,recv_tok->value}; SecBufferDesc output_desc={SECBUFFER_VERSION,1,&wsend_tok}; SecBufferDesc input_desc ={SECBUFFER_VERSION,1,&wrecv_tok}; unsigned long flags=ISC_REQ_MUTUAL_AUTH|ISC_REQ_REPLAY_DETECT| ISC_REQ_CONFIDENTIALITY|ISC_REQ_ALLOCATE_MEMORY; unsigned long ret_flags=0; TimeStamp localexp; /* check if we have to delegate ... */ if (to_deleg) flags |= ISC_REQ_DELEGATE; winctx->maj_stat = p_InitializeSecurityContextA(&winctx->cred_handle, winctx->context_handle, (char*) srv_name, flags, 0, /* reserved */ SECURITY_NATIVE_DREP, &input_desc, 0, /* reserved */ &winctx->context, &output_desc, &ret_flags, &localexp); localexp_to_exp_lifetime(&localexp, expiry, lifetime); /* prepare for the next round */ winctx->context_handle = &winctx->context; send_tok->value = wsend_tok.pvBuffer; send_tok->length = wsend_tok.cbBuffer; /* check & return our status */ if (winctx->maj_stat==SEC_E_OK) return SSH_GSS_S_COMPLETE; if (winctx->maj_stat==SEC_I_CONTINUE_NEEDED) return SSH_GSS_S_CONTINUE_NEEDED; return SSH_GSS_FAILURE; } static Ssh_gss_stat ssh_sspi_free_tok(struct ssh_gss_library *lib, Ssh_gss_buf *send_tok) { /* check input */ if (send_tok == NULL) return SSH_GSS_FAILURE; /* free Windows buffer */ p_FreeContextBuffer(send_tok->value); SSH_GSS_CLEAR_BUF(send_tok); return SSH_GSS_OK; } static Ssh_gss_stat ssh_sspi_release_cred(struct ssh_gss_library *lib, Ssh_gss_ctx *ctx) { winSsh_gss_ctx *winctx= (winSsh_gss_ctx *) *ctx; /* check input */ if (winctx == NULL) return SSH_GSS_FAILURE; /* free Windows data */ p_FreeCredentialsHandle(&winctx->cred_handle); p_DeleteSecurityContext(&winctx->context); /* delete our "wrapper" structure */ sfree(winctx); *ctx = (Ssh_gss_ctx) NULL; return SSH_GSS_OK; } static Ssh_gss_stat ssh_sspi_release_name(struct ssh_gss_library *lib, Ssh_gss_name *srv_name) { char *pStr= (char *) *srv_name; if (pStr == NULL) return SSH_GSS_FAILURE; sfree(pStr); *srv_name = (Ssh_gss_name) NULL; return SSH_GSS_OK; } static Ssh_gss_stat ssh_sspi_display_status(struct ssh_gss_library *lib, Ssh_gss_ctx ctx, Ssh_gss_buf *buf) { winSsh_gss_ctx *winctx = (winSsh_gss_ctx *) ctx; const char *msg; if (winctx == NULL) return SSH_GSS_FAILURE; /* decode the error code */ switch (winctx->maj_stat) { case SEC_E_OK: msg="SSPI status OK"; break; case SEC_E_INVALID_HANDLE: msg="The handle passed to the function" " is invalid."; break; case SEC_E_TARGET_UNKNOWN: msg="The target was not recognized."; break; case SEC_E_LOGON_DENIED: msg="The logon failed."; break; case SEC_E_INTERNAL_ERROR: msg="The Local Security Authority cannot" " be contacted."; break; case SEC_E_NO_CREDENTIALS: msg="No credentials are available in the" " security package."; break; case SEC_E_NO_AUTHENTICATING_AUTHORITY: msg="No authority could be contacted for authentication." "The domain name of the authenticating party could be wrong," " the domain could be unreachable, or there might have been" " a trust relationship failure."; break; case SEC_E_INSUFFICIENT_MEMORY: msg="One or more of the SecBufferDesc structures passed as" " an OUT parameter has a buffer that is too small."; break; case SEC_E_INVALID_TOKEN: msg="The error is due to a malformed input token, such as a" " token corrupted in transit, a token" " of incorrect size, or a token passed into the wrong" " security package. Passing a token to" " the wrong package can happen if client and server did not" " negotiate the proper security package."; break; default: msg = "Internal SSPI error"; break; } buf->value = dupstr(msg); buf->length = strlen(buf->value); return SSH_GSS_OK; } static Ssh_gss_stat ssh_sspi_get_mic(struct ssh_gss_library *lib, Ssh_gss_ctx ctx, Ssh_gss_buf *buf, Ssh_gss_buf *hash) { winSsh_gss_ctx *winctx= (winSsh_gss_ctx *) ctx; SecPkgContext_Sizes ContextSizes; SecBufferDesc InputBufferDescriptor; SecBuffer InputSecurityToken[2]; if (winctx == NULL) return SSH_GSS_FAILURE; winctx->maj_stat = 0; memset(&ContextSizes, 0, sizeof(ContextSizes)); winctx->maj_stat = p_QueryContextAttributesA(&winctx->context, SECPKG_ATTR_SIZES, &ContextSizes); if (winctx->maj_stat != SEC_E_OK || ContextSizes.cbMaxSignature == 0) return winctx->maj_stat; InputBufferDescriptor.cBuffers = 2; InputBufferDescriptor.pBuffers = InputSecurityToken; InputBufferDescriptor.ulVersion = SECBUFFER_VERSION; InputSecurityToken[0].BufferType = SECBUFFER_DATA; InputSecurityToken[0].cbBuffer = buf->length; InputSecurityToken[0].pvBuffer = buf->value; InputSecurityToken[1].BufferType = SECBUFFER_TOKEN; InputSecurityToken[1].cbBuffer = ContextSizes.cbMaxSignature; InputSecurityToken[1].pvBuffer = snewn(ContextSizes.cbMaxSignature, char); winctx->maj_stat = p_MakeSignature(&winctx->context, 0, &InputBufferDescriptor, 0); if (winctx->maj_stat == SEC_E_OK) { hash->length = InputSecurityToken[1].cbBuffer; hash->value = InputSecurityToken[1].pvBuffer; } return winctx->maj_stat; } static Ssh_gss_stat ssh_sspi_verify_mic(struct ssh_gss_library *lib, Ssh_gss_ctx ctx, Ssh_gss_buf *buf, Ssh_gss_buf *mic) { winSsh_gss_ctx *winctx= (winSsh_gss_ctx *) ctx; SecBufferDesc InputBufferDescriptor; SecBuffer InputSecurityToken[2]; ULONG qop; if (winctx == NULL) return SSH_GSS_FAILURE; winctx->maj_stat = 0; InputBufferDescriptor.cBuffers = 2; InputBufferDescriptor.pBuffers = InputSecurityToken; InputBufferDescriptor.ulVersion = SECBUFFER_VERSION; InputSecurityToken[0].BufferType = SECBUFFER_DATA; InputSecurityToken[0].cbBuffer = buf->length; InputSecurityToken[0].pvBuffer = buf->value; InputSecurityToken[1].BufferType = SECBUFFER_TOKEN; InputSecurityToken[1].cbBuffer = mic->length; InputSecurityToken[1].pvBuffer = mic->value; winctx->maj_stat = p_VerifySignature(&winctx->context, &InputBufferDescriptor, 0, &qop); return winctx->maj_stat; } static Ssh_gss_stat ssh_sspi_free_mic(struct ssh_gss_library *lib, Ssh_gss_buf *hash) { sfree(hash->value); return SSH_GSS_OK; } static void ssh_sspi_bind_fns(struct ssh_gss_library *lib) { lib->indicate_mech = ssh_sspi_indicate_mech; lib->import_name = ssh_sspi_import_name; lib->release_name = ssh_sspi_release_name; lib->init_sec_context = ssh_sspi_init_sec_context; lib->free_tok = ssh_sspi_free_tok; lib->acquire_cred = ssh_sspi_acquire_cred; lib->release_cred = ssh_sspi_release_cred; lib->get_mic = ssh_sspi_get_mic; lib->verify_mic = ssh_sspi_verify_mic; lib->free_mic = ssh_sspi_free_mic; lib->display_status = ssh_sspi_display_status; } #else /* Dummy function so this source file defines something if NO_GSSAPI is defined. */ void ssh_gss_init(void) { } #endif putty-0.76/windows/winhandl.c0000644000175000017500000005563514072266314013241 00000000000000/* * winhandl.c: Module to give Windows front ends the general * ability to deal with consoles, pipes, serial ports, or any other * type of data stream accessed through a Windows API HANDLE rather * than a WinSock SOCKET. * * We do this by spawning a subthread to continuously try to read * from the handle. Every time a read successfully returns some * data, the subthread sets an event object which is picked up by * the main thread, and the main thread then sets an event in * return to instruct the subthread to resume reading. * * Output works precisely the other way round, in a second * subthread. The output subthread should not be attempting to * write all the time, because it hasn't always got data _to_ * write; so the output thread waits for an event object notifying * it to _attempt_ a write, and then it sets an event in return * when one completes. * * (It's terribly annoying having to spawn a subthread for each * direction of each handle. Technically it isn't necessary for * serial ports, since we could use overlapped I/O within the main * thread and wait directly on the event objects in the OVERLAPPED * structures. However, we can't use this trick for some types of * file handle at all - for some reason Windows restricts use of * OVERLAPPED to files which were opened with the overlapped flag - * and so we must use threads for those. This being the case, it's * simplest just to use threads for everything rather than trying * to keep track of multiple completely separate mechanisms.) */ #include #include "putty.h" /* ---------------------------------------------------------------------- * Generic definitions. */ /* * Maximum amount of backlog we will allow to build up on an input * handle before we stop reading from it. */ #define MAX_BACKLOG 32768 struct handle_generic { /* * Initial fields common to both handle_input and handle_output * structures. * * The three HANDLEs are set up at initialisation time and are * thereafter read-only to both main thread and subthread. * `moribund' is only used by the main thread; `done' is * written by the main thread before signalling to the * subthread. `defunct' and `busy' are used only by the main * thread. */ 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 */ bool moribund; /* are we going to kill this soon? */ bool done; /* request subthread to terminate */ bool defunct; /* has the subthread already gone? */ bool busy; /* operation currently in progress? */ void *privdata; /* for client to remember who they are */ }; typedef enum { HT_INPUT, HT_OUTPUT, HT_FOREIGN } HandleType; /* ---------------------------------------------------------------------- * Input threads. */ /* * Data required by an input thread. */ struct handle_input { /* * 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 */ bool moribund; /* are we going to kill this soon? */ bool done; /* request subthread to terminate */ bool defunct; /* has the subthread already gone? */ bool busy; /* operation currently in progress? */ void *privdata; /* for client to remember who they are */ /* * Data set at initialisation and then read-only. */ int flags; /* * Data set by the input thread before signalling ev_to_main, * and read by the main thread after receiving that signal. */ char buffer[4096]; /* the data read from the handle */ DWORD len; /* how much data that was */ int readerr; /* lets us know about read errors */ /* * Callback function called by this module when data arrives on * an input handle. */ handle_inputfn_t gotdata; }; /* * The actual thread procedure for an input thread. */ static DWORD WINAPI handle_input_threadfunc(void *param) { struct handle_input *ctx = (struct handle_input *) param; OVERLAPPED ovl, *povl; HANDLE oev; bool readret, finished; int readlen; if (ctx->flags & HANDLE_FLAG_OVERLAPPED) { povl = &ovl; oev = CreateEvent(NULL, true, false, NULL); } else { povl = NULL; } if (ctx->flags & HANDLE_FLAG_UNITBUFFER) readlen = 1; else readlen = sizeof(ctx->buffer); while (1) { if (povl) { memset(povl, 0, sizeof(OVERLAPPED)); povl->hEvent = oev; } readret = ReadFile(ctx->h, ctx->buffer,readlen, &ctx->len, povl); if (!readret) ctx->readerr = GetLastError(); else ctx->readerr = 0; if (povl && !readret && ctx->readerr == ERROR_IO_PENDING) { WaitForSingleObject(povl->hEvent, INFINITE); readret = GetOverlappedResult(ctx->h, povl, &ctx->len, false); if (!readret) ctx->readerr = GetLastError(); else ctx->readerr = 0; } if (!readret) { /* * Windows apparently sends ERROR_BROKEN_PIPE when a * pipe we're reading from is closed normally from the * writing end. This is ludicrous; if that situation * isn't a natural EOF, _nothing_ is. So if we get that * particular error, we pretend it's EOF. */ if (ctx->readerr == ERROR_BROKEN_PIPE) ctx->readerr = 0; ctx->len = 0; } if (readret && ctx->len == 0 && (ctx->flags & HANDLE_FLAG_IGNOREEOF)) continue; /* * If we just set ctx->len to 0, that means the read operation * has returned end-of-file. Telling that to the main thread * will cause it to set its 'defunct' flag and dispose of the * handle structure at the next opportunity, in which case we * mustn't touch ctx at all after the SetEvent. (Hence we do * even _this_ check before the SetEvent.) */ finished = (ctx->len == 0); SetEvent(ctx->ev_to_main); if (finished) break; WaitForSingleObject(ctx->ev_from_main, INFINITE); if (ctx->done) { /* * The main thread has asked us to shut down. Send back an * event indicating that we've done so. Hereafter we must * not touch ctx at all, because the main thread might * have freed it. */ SetEvent(ctx->ev_to_main); break; } } if (povl) CloseHandle(oev); return 0; } /* * This is called after a successful read, or from the * `unthrottle' function. It decides whether or not to begin a new * read operation. */ static void handle_throttle(struct handle_input *ctx, int backlog) { if (ctx->defunct) return; /* * If there's a read operation already in progress, do nothing: * when that completes, we'll come back here and be in a * position to make a better decision. */ if (ctx->busy) return; /* * Otherwise, we must decide whether to start a new read based * on the size of the backlog. */ if (backlog < MAX_BACKLOG) { SetEvent(ctx->ev_from_main); ctx->busy = true; } } /* ---------------------------------------------------------------------- * Output threads. */ /* * Data required by an output thread. */ struct handle_output { /* * 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 */ bool moribund; /* are we going to kill this soon? */ bool done; /* request subthread to terminate */ bool defunct; /* has the subthread already gone? */ bool busy; /* operation currently in progress? */ void *privdata; /* for client to remember who they are */ /* * Data set at initialisation and then read-only. */ int flags; /* * Data set by the main thread before signalling ev_from_main, * and read by the input thread after receiving that signal. */ const char *buffer; /* the data to write */ DWORD len; /* how much data there is */ /* * Data set by the input thread before signalling ev_to_main, * and read by the main thread after receiving that signal. */ 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; }; static DWORD WINAPI handle_output_threadfunc(void *param) { struct handle_output *ctx = (struct handle_output *) param; OVERLAPPED ovl, *povl; HANDLE oev; bool writeret; if (ctx->flags & HANDLE_FLAG_OVERLAPPED) { povl = &ovl; oev = CreateEvent(NULL, true, false, NULL); } else { povl = NULL; } while (1) { WaitForSingleObject(ctx->ev_from_main, INFINITE); if (ctx->done) { /* * The main thread has asked us to shut down. Send back an * event indicating that we've done so. Hereafter we must * not touch ctx at all, because the main thread might * have freed it. */ SetEvent(ctx->ev_to_main); break; } if (povl) { memset(povl, 0, sizeof(OVERLAPPED)); povl->hEvent = oev; } writeret = WriteFile(ctx->h, ctx->buffer, ctx->len, &ctx->lenwritten, povl); if (!writeret) ctx->writeerr = GetLastError(); else ctx->writeerr = 0; if (povl && !writeret && GetLastError() == ERROR_IO_PENDING) { writeret = GetOverlappedResult(ctx->h, povl, &ctx->lenwritten, true); if (!writeret) ctx->writeerr = GetLastError(); else ctx->writeerr = 0; } SetEvent(ctx->ev_to_main); if (!writeret) { /* * The write operation has suffered an error. Telling that * to the main thread will cause it to set its 'defunct' * flag and dispose of the handle structure at the next * opportunity, so we must not touch ctx at all after * this. */ break; } } if (povl) CloseHandle(oev); return 0; } static void handle_try_output(struct handle_output *ctx) { if (!ctx->busy && bufchain_size(&ctx->queued_data)) { ptrlen data = bufchain_prefix(&ctx->queued_data); ctx->buffer = data.ptr; ctx->len = min(data.len, ~(DWORD)0); 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 */ bool moribund; /* are we going to kill this soon? */ bool done; /* request subthread to terminate */ bool defunct; /* has the subthread already gone? */ bool 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) { struct handle *a = (struct handle *)av; struct handle *b = (struct handle *)bv; if ((uintptr_t)a->u.g.ev_to_main < (uintptr_t)b->u.g.ev_to_main) return -1; else if ((uintptr_t)a->u.g.ev_to_main > (uintptr_t)b->u.g.ev_to_main) return +1; else return 0; } static int handle_find_evtomain(void *av, void *bv) { HANDLE *a = (HANDLE *)av; struct handle *b = (struct handle *)bv; if ((uintptr_t)*a < (uintptr_t)b->u.g.ev_to_main) return -1; else if ((uintptr_t)*a > (uintptr_t)b->u.g.ev_to_main) return +1; else return 0; } 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 = HT_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; h->u.i.privdata = privdata; h->u.i.flags = flags; if (!handles_by_evtomain) handles_by_evtomain = newtree234(handle_cmp_evtomain); add234(handles_by_evtomain, h); HANDLE hThread = CreateThread(NULL, 0, handle_input_threadfunc, &h->u.i, 0, &in_threadid); if (hThread) CloseHandle(hThread); /* we don't need the thread handle */ h->u.i.busy = true; return h; } 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 = HT_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); HANDLE hThread = CreateThread(NULL, 0, handle_output_threadfunc, &h->u.o, 0, &out_threadid); if (hThread) CloseHandle(hThread); /* we don't need the thread handle */ return h; } struct handle *handle_add_foreign_event(HANDLE event, void (*callback)(void *), void *ctx) { struct handle *h = snew(struct handle); h->type = HT_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; } size_t handle_write(struct handle *h, const void *data, size_t len) { assert(h->type == HT_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 == HT_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; size_t n, size; /* * Go through our tree counting the handle objects currently * engaged in useful activity. */ ret = NULL; n = size = 0; if (handles_by_evtomain) { for (i = 0; (h = index234(handles_by_evtomain, i)) != NULL; i++) { if (h->u.g.busy) { sgrowarray(ret, size, n); ret[n++] = h->u.g.ev_to_main; } } } *nevents = n; return ret; } static void handle_destroy(struct handle *h) { if (h->type == HT_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); } void handle_free(struct handle *h) { assert(h && !h->u.g.moribund); if (h->u.g.busy && h->type != HT_FOREIGN) { /* * If the handle is currently busy, we cannot immediately free * it, because its subthread is in the middle of something. * (Exception: foreign handles don't have a subthread.) * * Instead we must wait until it's finished its current * operation, because otherwise the subthread will write to * invalid memory after we free its context from under it. So * we set the moribund flag, which will be noticed next time * an operation completes. */ h->u.g.moribund = true; } else if (h->u.g.defunct) { /* * There isn't even a subthread; we can go straight to * handle_destroy. */ handle_destroy(h); } else { /* * The subthread is alive but not busy, so we now signal it * to die. Set the moribund flag to indicate that it will * want destroying after that. */ h->u.g.moribund = true; h->u.g.done = true; h->u.g.busy = true; SetEvent(h->u.g.ev_from_main); } } void handle_got_event(HANDLE event) { struct handle *h; assert(handles_by_evtomain); h = find234(handles_by_evtomain, &event, handle_find_evtomain); if (!h) { /* * This isn't an error condition. If two or more event * objects were signalled during the same select operation, * and processing of the first caused the second handle to * be closed, then it will sometimes happen that we receive * an event notification here for a handle which is already * deceased. In that situation we simply do nothing. */ return; } if (h->u.g.moribund) { /* * A moribund handle is one which we have either already * signalled to die, or are waiting until its current I/O op * completes to do so. Either way, it's treated as already * dead from the external user's point of view, so we ignore * the actual I/O result. We just signal the thread to die if * we haven't yet done so, or destroy the handle if not. */ if (h->u.g.done) { handle_destroy(h); } else { h->u.g.done = true; h->u.g.busy = true; SetEvent(h->u.g.ev_from_main); } return; } switch (h->type) { int backlog; case HT_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.defunct = true; h->u.i.gotdata(h, NULL, 0, h->u.i.readerr); } else { backlog = h->u.i.gotdata(h, h->u.i.buffer, h->u.i.len, 0); handle_throttle(&h->u.i, backlog); } break; case HT_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.defunct = true; h->u.o.sentdata(h, 0, h->u.o.writeerr); } else { bufchain_consume(&h->u.o.queued_data, h->u.o.lenwritten); noise_ultralight(NOISE_SOURCE_IOLEN, h->u.o.lenwritten); h->u.o.sentdata(h, bufchain_size(&h->u.o.queued_data), 0); handle_try_output(&h->u.o); } break; case HT_FOREIGN: /* Just call the callback. */ h->u.f.callback(h->u.f.ctx); break; } } void handle_unthrottle(struct handle *h, size_t backlog) { assert(h->type == HT_INPUT); handle_throttle(&h->u.i, backlog); } size_t handle_backlog(struct handle *h) { assert(h->type == HT_OUTPUT); return bufchain_size(&h->u.o.queued_data); } void *handle_get_privdata(struct handle *h) { return h->u.g.privdata; } static void handle_sink_write(BinarySink *bs, const void *data, size_t len) { handle_sink *sink = BinarySink_DOWNCAST(bs, handle_sink); handle_write(sink->h, data, len); } void handle_sink_init(handle_sink *sink, struct handle *h) { sink->h = h; BinarySink_INIT(sink, handle_sink_write); } putty-0.76/windows/winhelp.c0000644000175000017500000001407614072266314013075 00000000000000/* * winhelp.c: centralised functions to launch Windows HTML Help files. */ #include #include #include #include #include "putty.h" #include "win_res.h" #ifdef NO_HTMLHELP /* If htmlhelp.h is not available, we can't do any of this at all */ bool has_help(void) { return false; } void init_help(void) { } void shutdown_help(void) { } void launch_help(HWND hwnd, const char *topic) { } void quit_help(HWND hwnd) { } #else #include static char *chm_path = NULL; static bool chm_created_by_us = false; static bool requested_help; DECL_WINDOWS_FUNCTION(static, HWND, HtmlHelpA, (HWND, LPCSTR, UINT, DWORD_PTR)); static HRSRC chm_hrsrc; static DWORD chm_resource_size = 0; static const void *chm_resource = NULL; int has_embedded_chm(void) { static bool checked = false; if (!checked) { checked = true; chm_hrsrc = FindResource( NULL, MAKEINTRESOURCE(ID_CUSTOM_CHMFILE), MAKEINTRESOURCE(TYPE_CUSTOM_CHMFILE)); } return chm_hrsrc != NULL ? 1 : 0; } static bool find_chm_resource(void) { static bool checked = false; if (checked) /* we've been here already */ goto out; checked = true; /* * Look for a CHM file embedded in this executable as a custom * resource. */ if (!has_embedded_chm()) /* set up chm_hrsrc and check if it's NULL */ goto out; chm_resource_size = SizeofResource(NULL, chm_hrsrc); if (chm_resource_size == 0) goto out; HGLOBAL chm_hglobal = LoadResource(NULL, chm_hrsrc); if (chm_hglobal == NULL) goto out; chm_resource = (const uint8_t *)LockResource(chm_hglobal); out: return chm_resource != NULL; } static bool load_chm_resource(void) { bool toret = false; char *filename = NULL; HANDLE filehandle = INVALID_HANDLE_VALUE; bool created = false; static bool tried_to_load = false; if (tried_to_load) goto out; tried_to_load = true; /* * We've found it! Now write it out into a separate file, so that * htmlhelp.exe can handle it. */ /* GetTempPath is documented as returning a size of up to * MAX_PATH+1 which does not count the NUL */ char tempdir[MAX_PATH + 2]; if (GetTempPath(sizeof(tempdir), tempdir) == 0) goto out; unsigned long pid = GetCurrentProcessId(); for (uint64_t counter = 0;; counter++) { filename = dupprintf( "%s\\putty_%lu_%"PRIu64".chm", tempdir, pid, counter); filehandle = CreateFile( filename, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); if (filehandle != INVALID_HANDLE_VALUE) break; /* success! */ if (GetLastError() != ERROR_FILE_EXISTS) goto out; /* failed for some other reason! */ sfree(filename); filename = NULL; } created = true; const uint8_t *p = (const uint8_t *)chm_resource; for (DWORD pos = 0; pos < chm_resource_size; pos++) { DWORD to_write = chm_resource_size - pos; DWORD written = 0; if (!WriteFile(filehandle, p + pos, to_write, &written, NULL)) goto out; pos += written; } chm_path = filename; filename = NULL; chm_created_by_us = true; toret = true; out: if (created && !toret) DeleteFile(filename); sfree(filename); if (filehandle != INVALID_HANDLE_VALUE) CloseHandle(filehandle); return toret; } static bool find_chm_from_installation(void) { static const char *const reg_paths[] = { "Software\\SimonTatham\\PuTTY64\\CHMPath", "Software\\SimonTatham\\PuTTY\\CHMPath", }; for (size_t i = 0; i < lenof(reg_paths); i++) { char *filename = registry_get_string( HKEY_LOCAL_MACHINE, reg_paths[i], NULL); if (filename) { chm_path = filename; chm_created_by_us = false; return true; } } return false; } void init_help(void) { /* Just in case of multiple calls */ static bool already_called = false; if (already_called) return; already_called = true; /* * Don't even try looking for the CHM file if we can't even find * the HtmlHelp() API function. */ HINSTANCE dllHH = load_system32_dll("hhctrl.ocx"); GET_WINDOWS_FUNCTION(dllHH, HtmlHelpA); if (!p_HtmlHelpA) { FreeLibrary(dllHH); return; } /* * If there's a CHM file embedded in this executable, we should * use that as the first choice. */ if (find_chm_resource()) return; /* * Otherwise, try looking for the CHM in the location that the * installer marked in the registry. */ if (find_chm_from_installation()) return; } void shutdown_help(void) { if (chm_path && chm_created_by_us) { p_HtmlHelpA(NULL, NULL, HH_CLOSE_ALL, 0); DeleteFile(chm_path); } sfree(chm_path); chm_path = NULL; chm_created_by_us = false; } bool has_help(void) { return chm_path != NULL || chm_resource != NULL; } void launch_help(HWND hwnd, const char *topic) { if (!chm_path && chm_resource) { /* * If we've been called without already having a file name for * the CHM file, that might be because we've located it in our * resource section but not written it to a temp file yet. Do * so now, on first use. */ load_chm_resource(); } /* If we _still_ don't have a CHM pathname, we just can't display help. */ if (!chm_path) return; if (topic) { char *fname = dupprintf( "%s::/%s.html>main", chm_path, topic); p_HtmlHelpA(hwnd, fname, HH_DISPLAY_TOPIC, 0); sfree(fname); } else { p_HtmlHelpA(hwnd, chm_path, HH_DISPLAY_TOPIC, 0); } requested_help = true; } void quit_help(HWND hwnd) { if (requested_help) p_HtmlHelpA(NULL, NULL, HH_CLOSE_ALL, 0); if (chm_path && chm_created_by_us) DeleteFile(chm_path); } #endif /* NO_HTMLHELP */ putty-0.76/windows/winhelp.h0000644000175000017500000002607314072266314013102 00000000000000/* * winhelp.h - define Windows Help context names. * Each definition is simply a string which matches up with the * section names in the Halibut source, and is used for HTML Help. */ /* Maximum length for WINHELP_CTX_foo strings */ #define WINHELP_CTX_MAXLEN 80 /* These are used in the cross-platform configuration dialog code. */ #define HELPCTX(x) P(WINHELP_CTX_ ## x) #define WINHELP_CTX_no_help NULL #define WINHELP_CTX_session_hostname "config-hostname" #define WINHELP_CTX_session_saved "config-saving" #define WINHELP_CTX_session_coe "config-closeonexit" #define WINHELP_CTX_logging_main "config-logging" #define WINHELP_CTX_logging_filename "config-logfilename" #define WINHELP_CTX_logging_exists "config-logfileexists" #define WINHELP_CTX_logging_flush "config-logflush" #define WINHELP_CTX_logging_header "config-logheader" #define WINHELP_CTX_logging_ssh_omit_password "config-logssh" #define WINHELP_CTX_logging_ssh_omit_data "config-logssh" #define WINHELP_CTX_keyboard_backspace "config-backspace" #define WINHELP_CTX_keyboard_homeend "config-homeend" #define WINHELP_CTX_keyboard_funkeys "config-funkeys" #define WINHELP_CTX_keyboard_appkeypad "config-appkeypad" #define WINHELP_CTX_keyboard_appcursor "config-appcursor" #define WINHELP_CTX_keyboard_nethack "config-nethack" #define WINHELP_CTX_keyboard_compose "config-compose" #define WINHELP_CTX_keyboard_ctrlalt "config-ctrlalt" #define WINHELP_CTX_features_application "config-features-application" #define WINHELP_CTX_features_mouse "config-features-mouse" #define WINHELP_CTX_features_resize "config-features-resize" #define WINHELP_CTX_features_altscreen "config-features-altscreen" #define WINHELP_CTX_features_retitle "config-features-retitle" #define WINHELP_CTX_features_qtitle "config-features-qtitle" #define WINHELP_CTX_features_dbackspace "config-features-dbackspace" #define WINHELP_CTX_features_charset "config-features-charset" #define WINHELP_CTX_features_clearscroll "config-features-clearscroll" #define WINHELP_CTX_features_arabicshaping "config-features-shaping" #define WINHELP_CTX_features_bidi "config-features-bidi" #define WINHELP_CTX_terminal_autowrap "config-autowrap" #define WINHELP_CTX_terminal_decom "config-decom" #define WINHELP_CTX_terminal_lfhascr "config-crlf" #define WINHELP_CTX_terminal_crhaslf "config-lfcr" #define WINHELP_CTX_terminal_bce "config-erase" #define WINHELP_CTX_terminal_blink "config-blink" #define WINHELP_CTX_terminal_answerback "config-answerback" #define WINHELP_CTX_terminal_localecho "config-localecho" #define WINHELP_CTX_terminal_localedit "config-localedit" #define WINHELP_CTX_terminal_printing "config-printing" #define WINHELP_CTX_supdup_location "supdup-location" #define WINHELP_CTX_supdup_ascii "supdup-ascii" #define WINHELP_CTX_supdup_more "supdup-more" #define WINHELP_CTX_supdup_scroll "supdup-scroll" #define WINHELP_CTX_bell_style "config-bellstyle" #define WINHELP_CTX_bell_taskbar "config-belltaskbar" #define WINHELP_CTX_bell_overload "config-bellovl" #define WINHELP_CTX_window_size "config-winsize" #define WINHELP_CTX_window_resize "config-winsizelock" #define WINHELP_CTX_window_scrollback "config-scrollback" #define WINHELP_CTX_window_erased "config-erasetoscrollback" #define WINHELP_CTX_behaviour_closewarn "config-warnonclose" #define WINHELP_CTX_behaviour_altf4 "config-altf4" #define WINHELP_CTX_behaviour_altspace "config-altspace" #define WINHELP_CTX_behaviour_altonly "config-altonly" #define WINHELP_CTX_behaviour_alwaysontop "config-alwaysontop" #define WINHELP_CTX_behaviour_altenter "config-fullscreen" #define WINHELP_CTX_appearance_cursor "config-cursor" #define WINHELP_CTX_appearance_font "config-font" #define WINHELP_CTX_appearance_title "config-title" #define WINHELP_CTX_appearance_hidemouse "config-mouseptr" #define WINHELP_CTX_appearance_border "config-winborder" #define WINHELP_CTX_connection_termtype "config-termtype" #define WINHELP_CTX_connection_termspeed "config-termspeed" #define WINHELP_CTX_connection_username "config-username" #define WINHELP_CTX_connection_username_from_env "config-username-from-env" #define WINHELP_CTX_connection_keepalive "config-keepalive" #define WINHELP_CTX_connection_nodelay "config-nodelay" #define WINHELP_CTX_connection_ipversion "config-address-family" #define WINHELP_CTX_connection_tcpkeepalive "config-tcp-keepalives" #define WINHELP_CTX_connection_loghost "config-loghost" #define WINHELP_CTX_proxy_type "config-proxy-type" #define WINHELP_CTX_proxy_main "config-proxy" #define WINHELP_CTX_proxy_exclude "config-proxy-exclude" #define WINHELP_CTX_proxy_dns "config-proxy-dns" #define WINHELP_CTX_proxy_auth "config-proxy-auth" #define WINHELP_CTX_proxy_command "config-proxy-command" #define WINHELP_CTX_proxy_logging "config-proxy-logging" #define WINHELP_CTX_telnet_environ "config-environ" #define WINHELP_CTX_telnet_oldenviron "config-oldenviron" #define WINHELP_CTX_telnet_passive "config-ptelnet" #define WINHELP_CTX_telnet_specialkeys "config-telnetkey" #define WINHELP_CTX_telnet_newline "config-telnetnl" #define WINHELP_CTX_rlogin_localuser "config-rlogin-localuser" #define WINHELP_CTX_ssh_nopty "config-ssh-pty" #define WINHELP_CTX_ssh_ttymodes "config-ttymodes" #define WINHELP_CTX_ssh_noshell "config-ssh-noshell" #define WINHELP_CTX_ssh_ciphers "config-ssh-encryption" #define WINHELP_CTX_ssh_protocol "config-ssh-prot" #define WINHELP_CTX_ssh_command "config-command" #define WINHELP_CTX_ssh_compress "config-ssh-comp" #define WINHELP_CTX_ssh_share "config-ssh-sharing" #define WINHELP_CTX_ssh_kexlist "config-ssh-kex-order" #define WINHELP_CTX_ssh_hklist "config-ssh-hostkey-order" #define WINHELP_CTX_ssh_hk_known "config-ssh-prefer-known-hostkeys" #define WINHELP_CTX_ssh_gssapi_kex_delegation "config-ssh-kex-gssapi-delegation" #define WINHELP_CTX_ssh_kex_repeat "config-ssh-kex-rekey" #define WINHELP_CTX_ssh_kex_manual_hostkeys "config-ssh-kex-manual-hostkeys" #define WINHELP_CTX_ssh_auth_bypass "config-ssh-noauth" #define WINHELP_CTX_ssh_no_trivial_userauth "config-ssh-notrivialauth" #define WINHELP_CTX_ssh_auth_banner "config-ssh-banner" #define WINHELP_CTX_ssh_auth_privkey "config-ssh-privkey" #define WINHELP_CTX_ssh_auth_agentfwd "config-ssh-agentfwd" #define WINHELP_CTX_ssh_auth_changeuser "config-ssh-changeuser" #define WINHELP_CTX_ssh_auth_pageant "config-ssh-tryagent" #define WINHELP_CTX_ssh_auth_tis "config-ssh-tis" #define WINHELP_CTX_ssh_auth_ki "config-ssh-ki" #define WINHELP_CTX_ssh_gssapi "config-ssh-auth-gssapi" #define WINHELP_CTX_ssh_gssapi_delegation "config-ssh-auth-gssapi-delegation" #define WINHELP_CTX_ssh_gssapi_libraries "config-ssh-auth-gssapi-libraries" #define WINHELP_CTX_selection_buttons "config-mouse" #define WINHELP_CTX_selection_shiftdrag "config-mouseshift" #define WINHELP_CTX_selection_rect "config-rectselect" #define WINHELP_CTX_selection_linedraw "config-linedrawpaste" #define WINHELP_CTX_selection_autocopy "config-selection-autocopy" #define WINHELP_CTX_selection_clipactions "config-selection-clipactions" #define WINHELP_CTX_selection_pastectrl "config-paste-ctrl-char" #define WINHELP_CTX_copy_charclasses "config-charclasses" #define WINHELP_CTX_copy_rtf "config-rtfcopy" #define WINHELP_CTX_colours_ansi "config-ansicolour" #define WINHELP_CTX_colours_xterm256 "config-xtermcolour" #define WINHELP_CTX_colours_truecolour "config-truecolour" #define WINHELP_CTX_colours_bold "config-boldcolour" #define WINHELP_CTX_colours_system "config-syscolour" #define WINHELP_CTX_colours_logpal "config-logpalette" #define WINHELP_CTX_colours_config "config-colourcfg" #define WINHELP_CTX_translation_codepage "config-charset" #define WINHELP_CTX_translation_cjk_ambig_wide "config-cjk-ambig-wide" #define WINHELP_CTX_translation_cyrillic "config-cyr" #define WINHELP_CTX_translation_linedraw "config-linedraw" #define WINHELP_CTX_translation_utf8linedraw "config-utf8linedraw" #define WINHELP_CTX_ssh_tunnels_x11 "config-ssh-x11" #define WINHELP_CTX_ssh_tunnels_x11auth "config-ssh-x11auth" #define WINHELP_CTX_ssh_tunnels_xauthority "config-ssh-xauthority" #define WINHELP_CTX_ssh_tunnels_portfwd "config-ssh-portfwd" #define WINHELP_CTX_ssh_tunnels_portfwd_localhost "config-ssh-portfwd-localhost" #define WINHELP_CTX_ssh_tunnels_portfwd_ipversion "config-ssh-portfwd-address-family" #define WINHELP_CTX_ssh_bugs_ignore1 "config-ssh-bug-ignore1" #define WINHELP_CTX_ssh_bugs_plainpw1 "config-ssh-bug-plainpw1" #define WINHELP_CTX_ssh_bugs_rsa1 "config-ssh-bug-rsa1" #define WINHELP_CTX_ssh_bugs_ignore2 "config-ssh-bug-ignore2" #define WINHELP_CTX_ssh_bugs_hmac2 "config-ssh-bug-hmac2" #define WINHELP_CTX_ssh_bugs_derivekey2 "config-ssh-bug-derivekey2" #define WINHELP_CTX_ssh_bugs_rsapad2 "config-ssh-bug-sig" #define WINHELP_CTX_ssh_bugs_pksessid2 "config-ssh-bug-pksessid2" #define WINHELP_CTX_ssh_bugs_rekey2 "config-ssh-bug-rekey" #define WINHELP_CTX_ssh_bugs_maxpkt2 "config-ssh-bug-maxpkt2" #define WINHELP_CTX_ssh_bugs_winadj "config-ssh-bug-winadj" #define WINHELP_CTX_ssh_bugs_chanreq "config-ssh-bug-chanreq" #define WINHELP_CTX_ssh_bugs_oldgex2 "config-ssh-bug-oldgex2" #define WINHELP_CTX_serial_line "config-serial-line" #define WINHELP_CTX_serial_speed "config-serial-speed" #define WINHELP_CTX_serial_databits "config-serial-databits" #define WINHELP_CTX_serial_stopbits "config-serial-stopbits" #define WINHELP_CTX_serial_parity "config-serial-parity" #define WINHELP_CTX_serial_flow "config-serial-flow" #define WINHELP_CTX_pageant_general "pageant" #define WINHELP_CTX_pageant_keylist "pageant-mainwin-keylist" #define WINHELP_CTX_pageant_addkey "pageant-mainwin-addkey" #define WINHELP_CTX_pageant_remkey "pageant-mainwin-remkey" #define WINHELP_CTX_pageant_deferred "pageant-deferred-decryption" #define WINHELP_CTX_pgpfingerprints "pgpkeys" #define WINHELP_CTX_puttygen_general "pubkey-puttygen" #define WINHELP_CTX_puttygen_keytype "puttygen-keytype" #define WINHELP_CTX_puttygen_bits "puttygen-strength" #define WINHELP_CTX_puttygen_generate "puttygen-generate" #define WINHELP_CTX_puttygen_fingerprint "puttygen-fingerprint" #define WINHELP_CTX_puttygen_comment "puttygen-comment" #define WINHELP_CTX_puttygen_passphrase "puttygen-passphrase" #define WINHELP_CTX_puttygen_savepriv "puttygen-savepriv" #define WINHELP_CTX_puttygen_savepub "puttygen-savepub" #define WINHELP_CTX_puttygen_pastekey "puttygen-pastekey" #define WINHELP_CTX_puttygen_load "puttygen-load" #define WINHELP_CTX_puttygen_conversions "puttygen-conversions" #define WINHELP_CTX_puttygen_ppkver "puttygen-save-ppk-version" #define WINHELP_CTX_puttygen_kdfparam "puttygen-save-passphrase-hashing" /* 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 #define WINHELP_CTX_errors_hostkey_absent "errors-hostkey-absent" #define WINHELP_CTXID_errors_hostkey_absent 1 #define WINHELP_CTX_errors_hostkey_changed "errors-hostkey-wrong" #define WINHELP_CTXID_errors_hostkey_changed 2 #define WINHELP_CTX_errors_cantloadkey "errors-cant-load-key" #define WINHELP_CTXID_errors_cantloadkey 3 #define WINHELP_CTX_option_cleanup "using-cleanup" #define WINHELP_CTXID_option_cleanup 4 #define WINHELP_CTX_pgp_fingerprints "pgpkeys" #define WINHELP_CTXID_pgp_fingerprints 5 putty-0.76/windows/winhelp.rc20000644000175000017500000000030014072266314013322 00000000000000#include "win_res.h" #ifdef EMBED_CHM ID_CUSTOM_CHMFILE TYPE_CUSTOM_CHMFILE "../doc/putty.chm" #define HELPVER " (with embedded help)" #else #define HELPVER " (without embedded help)" #endif putty-0.76/windows/winhsock.c0000644000175000017500000002335414072266314013253 00000000000000/* * General mechanism for wrapping up reading/writing of Windows * HANDLEs into a PuTTY Socket abstraction. */ #include #include #include #include "tree234.h" #include "putty.h" #include "network.h" typedef struct HandleSocket { HANDLE send_H, recv_H, stderr_H; struct handle *send_h, *recv_h, *stderr_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; /* Handle logging proxy error messages from stderr_H, if we have one. */ ProxyStderrBuf psb; bool defer_close, deferred_close; /* in case of re-entrance */ char *error; Plug *plug; Socket sock; } HandleSocket; static size_t handle_gotdata( struct handle *h, const void *data, size_t len, int err) { HandleSocket *hs = (HandleSocket *)handle_get_privdata(h); if (err) { plug_closing(hs->plug, "Read error from handle", 0, 0); return 0; } else if (len == 0) { plug_closing(hs->plug, NULL, 0, 0); return 0; } else { assert(hs->frozen != FROZEN && hs->frozen != THAWING); if (hs->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(&hs->inputdata, data, len); hs->frozen = FROZEN; /* * And return a very large backlog, to prevent further * data arriving from winhandl until we unfreeze. */ return INT_MAX; } else { plug_receive(hs->plug, 0, data, len); return 0; } } } static size_t handle_stderr( struct handle *h, const void *data, size_t len, int err) { HandleSocket *hs = (HandleSocket *)handle_get_privdata(h); if (!err && len > 0) log_proxy_stderr(hs->plug, &hs->psb, data, len); return 0; } static void handle_sentdata(struct handle *h, size_t new_backlog, int err) { HandleSocket *hs = (HandleSocket *)handle_get_privdata(h); if (err) { plug_closing(hs->plug, win_strerror(err), err, 0); return; } plug_sent(hs->plug, new_backlog); } static Plug *sk_handle_plug(Socket *s, Plug *p) { HandleSocket *hs = container_of(s, HandleSocket, sock); Plug *ret = hs->plug; if (p) hs->plug = p; return ret; } static void sk_handle_close(Socket *s) { HandleSocket *hs = container_of(s, HandleSocket, sock); if (hs->defer_close) { hs->deferred_close = true; return; } handle_free(hs->send_h); handle_free(hs->recv_h); CloseHandle(hs->send_H); if (hs->recv_H != hs->send_H) CloseHandle(hs->recv_H); bufchain_clear(&hs->inputdata); delete_callbacks_for_context(hs); sfree(hs); } static size_t sk_handle_write(Socket *s, const void *data, size_t len) { HandleSocket *hs = container_of(s, HandleSocket, sock); return handle_write(hs->send_h, data, len); } static size_t sk_handle_write_oob(Socket *s, const void *data, size_t 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) { HandleSocket *hs = container_of(s, HandleSocket, sock); handle_write_eof(hs->send_h); } static void handle_socket_unfreeze(void *hsv) { HandleSocket *hs = (HandleSocket *)hsv; /* * If we've been put into a state other than THAWING since the * last callback, then we're done. */ if (hs->frozen != THAWING) return; /* * Get some of the data we've buffered. */ ptrlen data = bufchain_prefix(&hs->inputdata); assert(data.len > 0); /* * Hand it off to the plug. Be careful of re-entrance - that might * have the effect of trying to close this socket. */ hs->defer_close = true; plug_receive(hs->plug, 0, data.ptr, data.len); bufchain_consume(&hs->inputdata, data.len); hs->defer_close = false; if (hs->deferred_close) { sk_handle_close(&hs->sock); return; } if (bufchain_size(&hs->inputdata) > 0) { /* * If there's still data in our buffer, stay in THAWING state, * and reschedule ourself. */ queue_toplevel_callback(handle_socket_unfreeze, hs); } else { /* * Otherwise, we've successfully thawed! */ hs->frozen = UNFROZEN; handle_unthrottle(hs->recv_h, 0); } } static void sk_handle_set_frozen(Socket *s, bool is_frozen) { HandleSocket *hs = container_of(s, HandleSocket, sock); if (is_frozen) { switch (hs->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. */ hs->frozen = FROZEN; break; case UNFROZEN: /* * The normal case. Go to FREEZING, and expect one more * load of data from winhandl if we're unlucky. */ hs->frozen = FREEZING; break; } } else { switch (hs->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(&hs->inputdata) == 0); hs->frozen = UNFROZEN; break; case FROZEN: /* * If we have buffered data, go to THAWING and start * releasing it in top-level callbacks. */ hs->frozen = THAWING; queue_toplevel_callback(handle_socket_unfreeze, hs); } } } static const char *sk_handle_socket_error(Socket *s) { HandleSocket *hs = container_of(s, HandleSocket, sock); return hs->error; } static SocketPeerInfo *sk_handle_peer_info(Socket *s) { HandleSocket *hs = container_of(s, HandleSocket, sock); ULONG pid; static HMODULE kernel32_module; DECL_WINDOWS_FUNCTION(static, BOOL, GetNamedPipeClientProcessId, (HANDLE, PULONG)); if (!kernel32_module) { kernel32_module = load_system32_dll("kernel32.dll"); #if (defined _MSC_VER && _MSC_VER < 1900) || defined __MINGW32__ /* For older Visual Studio, and MinGW too (at least as of * Ubuntu 16.04), this function isn't available in the header * files to type-check. Ditto the toolchain I use for * Coveritying the Windows code. */ GET_WINDOWS_FUNCTION_NO_TYPECHECK( kernel32_module, GetNamedPipeClientProcessId); #else GET_WINDOWS_FUNCTION( kernel32_module, GetNamedPipeClientProcessId); #endif } /* * Of course, not all handles managed by this module will be * server ends of named pipes, but if they are, then it's useful * to log what we can find out about the client end. */ if (p_GetNamedPipeClientProcessId && p_GetNamedPipeClientProcessId(hs->send_H, &pid)) { SocketPeerInfo *pi = snew(SocketPeerInfo); pi->addressfamily = ADDRTYPE_LOCAL; pi->addr_text = NULL; pi->port = -1; pi->log_text = dupprintf("process id %lu", (unsigned long)pid); return pi; } return NULL; } static const SocketVtable HandleSocket_sockvt = { .plug = sk_handle_plug, .close = sk_handle_close, .write = sk_handle_write, .write_oob = sk_handle_write_oob, .write_eof = sk_handle_write_eof, .set_frozen = sk_handle_set_frozen, .socket_error = sk_handle_socket_error, .peer_info = sk_handle_peer_info, }; Socket *make_handle_socket(HANDLE send_H, HANDLE recv_H, HANDLE stderr_H, Plug *plug, bool overlapped) { HandleSocket *hs; int flags = (overlapped ? HANDLE_FLAG_OVERLAPPED : 0); hs = snew(HandleSocket); hs->sock.vt = &HandleSocket_sockvt; hs->plug = plug; hs->error = NULL; hs->frozen = UNFROZEN; bufchain_init(&hs->inputdata); psb_init(&hs->psb); hs->recv_H = recv_H; hs->recv_h = handle_input_new(hs->recv_H, handle_gotdata, hs, flags); hs->send_H = send_H; hs->send_h = handle_output_new(hs->send_H, handle_sentdata, hs, flags); hs->stderr_H = stderr_H; if (hs->stderr_H) hs->stderr_h = handle_input_new(hs->stderr_H, handle_stderr, hs, flags); hs->defer_close = hs->deferred_close = false; return &hs->sock; } putty-0.76/windows/winjump.c0000644000175000017500000005637414072266314013127 00000000000000/* * winjump.c: support for Windows 7 jump lists. * * The Windows 7 jumplist is a customizable list defined by the * application. It is persistent across application restarts: the OS * maintains the list when the app is not running. The list is shown * when the user right-clicks on the taskbar button of a running app * or a pinned non-running application. We use the jumplist to * maintain a list of recently started saved sessions, started either * by doubleclicking on a saved session, or with the command line * "-load" parameter. * * Since the jumplist is write-only: it can only be replaced and the * current list cannot be read, we must maintain the contents of the * list persistantly in the registry. The file winstore.h contains * functions to directly manipulate these registry entries. This file * contains higher level functions to manipulate the jumplist. */ #include #include "putty.h" #include "storage.h" #define MAX_JUMPLIST_ITEMS 30 /* PuTTY will never show more items in * the jumplist than this, regardless of * user preferences. */ /* * COM structures and functions. */ #ifndef PROPERTYKEY_DEFINED #define PROPERTYKEY_DEFINED typedef struct _tagpropertykey { GUID fmtid; DWORD pid; } PROPERTYKEY; #endif #ifndef _REFPROPVARIANT_DEFINED #define _REFPROPVARIANT_DEFINED typedef PROPVARIANT *REFPROPVARIANT; #endif /* MinGW doesn't define this yet: */ #ifndef _PROPVARIANTINIT_DEFINED_ #define _PROPVARIANTINIT_DEFINED_ #define PropVariantInit(pvar) memset((pvar),0,sizeof(PROPVARIANT)) #endif #define IID_IShellLink IID_IShellLinkA typedef struct ICustomDestinationListVtbl { HRESULT ( __stdcall *QueryInterface ) ( /* [in] ICustomDestinationList*/ void *This, /* [in] */ const GUID * const riid, /* [out] */ void **ppvObject); ULONG ( __stdcall *AddRef )( /* [in] ICustomDestinationList*/ void *This); ULONG ( __stdcall *Release )( /* [in] ICustomDestinationList*/ void *This); HRESULT ( __stdcall *SetAppID )( /* [in] ICustomDestinationList*/ void *This, /* [string][in] */ LPCWSTR pszAppID); HRESULT ( __stdcall *BeginList )( /* [in] ICustomDestinationList*/ void *This, /* [out] */ UINT *pcMinSlots, /* [in] */ const GUID * const riid, /* [out] */ void **ppv); HRESULT ( __stdcall *AppendCategory )( /* [in] ICustomDestinationList*/ void *This, /* [string][in] */ LPCWSTR pszCategory, /* [in] IObjectArray*/ void *poa); HRESULT ( __stdcall *AppendKnownCategory )( /* [in] ICustomDestinationList*/ void *This, /* [in] KNOWNDESTCATEGORY*/ int category); HRESULT ( __stdcall *AddUserTasks )( /* [in] ICustomDestinationList*/ void *This, /* [in] IObjectArray*/ void *poa); HRESULT ( __stdcall *CommitList )( /* [in] ICustomDestinationList*/ void *This); HRESULT ( __stdcall *GetRemovedDestinations )( /* [in] ICustomDestinationList*/ void *This, /* [in] */ const IID * const riid, /* [out] */ void **ppv); HRESULT ( __stdcall *DeleteList )( /* [in] ICustomDestinationList*/ void *This, /* [string][unique][in] */ LPCWSTR pszAppID); HRESULT ( __stdcall *AbortList )( /* [in] ICustomDestinationList*/ void *This); } ICustomDestinationListVtbl; typedef struct ICustomDestinationList { ICustomDestinationListVtbl *lpVtbl; } ICustomDestinationList; typedef struct IObjectArrayVtbl { HRESULT ( __stdcall *QueryInterface )( /* [in] IObjectArray*/ void *This, /* [in] */ const GUID * const riid, /* [out] */ void **ppvObject); ULONG ( __stdcall *AddRef )( /* [in] IObjectArray*/ void *This); ULONG ( __stdcall *Release )( /* [in] IObjectArray*/ void *This); HRESULT ( __stdcall *GetCount )( /* [in] IObjectArray*/ void *This, /* [out] */ UINT *pcObjects); HRESULT ( __stdcall *GetAt )( /* [in] IObjectArray*/ void *This, /* [in] */ UINT uiIndex, /* [in] */ const GUID * const riid, /* [out] */ void **ppv); } IObjectArrayVtbl; typedef struct IObjectArray { IObjectArrayVtbl *lpVtbl; } IObjectArray; typedef struct IShellLinkVtbl { HRESULT ( __stdcall *QueryInterface )( /* [in] IShellLink*/ void *This, /* [in] */ const GUID * const riid, /* [out] */ void **ppvObject); ULONG ( __stdcall *AddRef )( /* [in] IShellLink*/ void *This); ULONG ( __stdcall *Release )( /* [in] IShellLink*/ void *This); HRESULT ( __stdcall *GetPath )( /* [in] IShellLink*/ void *This, /* [string][out] */ LPSTR pszFile, /* [in] */ int cch, /* [unique][out][in] */ WIN32_FIND_DATAA *pfd, /* [in] */ DWORD fFlags); HRESULT ( __stdcall *GetIDList )( /* [in] IShellLink*/ void *This, /* [out] LPITEMIDLIST*/ void **ppidl); HRESULT ( __stdcall *SetIDList )( /* [in] IShellLink*/ void *This, /* [in] LPITEMIDLIST*/ void *pidl); HRESULT ( __stdcall *GetDescription )( /* [in] IShellLink*/ void *This, /* [string][out] */ LPSTR pszName, /* [in] */ int cch); HRESULT ( __stdcall *SetDescription )( /* [in] IShellLink*/ void *This, /* [string][in] */ LPCSTR pszName); HRESULT ( __stdcall *GetWorkingDirectory )( /* [in] IShellLink*/ void *This, /* [string][out] */ LPSTR pszDir, /* [in] */ int cch); HRESULT ( __stdcall *SetWorkingDirectory )( /* [in] IShellLink*/ void *This, /* [string][in] */ LPCSTR pszDir); HRESULT ( __stdcall *GetArguments )( /* [in] IShellLink*/ void *This, /* [string][out] */ LPSTR pszArgs, /* [in] */ int cch); HRESULT ( __stdcall *SetArguments )( /* [in] IShellLink*/ void *This, /* [string][in] */ LPCSTR pszArgs); HRESULT ( __stdcall *GetHotkey )( /* [in] IShellLink*/ void *This, /* [out] */ WORD *pwHotkey); HRESULT ( __stdcall *SetHotkey )( /* [in] IShellLink*/ void *This, /* [in] */ WORD wHotkey); HRESULT ( __stdcall *GetShowCmd )( /* [in] IShellLink*/ void *This, /* [out] */ int *piShowCmd); HRESULT ( __stdcall *SetShowCmd )( /* [in] IShellLink*/ void *This, /* [in] */ int iShowCmd); HRESULT ( __stdcall *GetIconLocation )( /* [in] IShellLink*/ void *This, /* [string][out] */ LPSTR pszIconPath, /* [in] */ int cch, /* [out] */ int *piIcon); HRESULT ( __stdcall *SetIconLocation )( /* [in] IShellLink*/ void *This, /* [string][in] */ LPCSTR pszIconPath, /* [in] */ int iIcon); HRESULT ( __stdcall *SetRelativePath )( /* [in] IShellLink*/ void *This, /* [string][in] */ LPCSTR pszPathRel, /* [in] */ DWORD dwReserved); HRESULT ( __stdcall *Resolve )( /* [in] IShellLink*/ void *This, /* [unique][in] */ HWND hwnd, /* [in] */ DWORD fFlags); HRESULT ( __stdcall *SetPath )( /* [in] IShellLink*/ void *This, /* [string][in] */ LPCSTR pszFile); } IShellLinkVtbl; typedef struct IShellLink { IShellLinkVtbl *lpVtbl; } IShellLink; typedef struct IObjectCollectionVtbl { HRESULT ( __stdcall *QueryInterface )( /* [in] IShellLink*/ void *This, /* [in] */ const GUID * const riid, /* [out] */ void **ppvObject); ULONG ( __stdcall *AddRef )( /* [in] IShellLink*/ void *This); ULONG ( __stdcall *Release )( /* [in] IShellLink*/ void *This); HRESULT ( __stdcall *GetCount )( /* [in] IShellLink*/ void *This, /* [out] */ UINT *pcObjects); HRESULT ( __stdcall *GetAt )( /* [in] IShellLink*/ void *This, /* [in] */ UINT uiIndex, /* [in] */ const GUID * const riid, /* [iid_is][out] */ void **ppv); HRESULT ( __stdcall *AddObject )( /* [in] IShellLink*/ void *This, /* [in] */ void *punk); HRESULT ( __stdcall *AddFromArray )( /* [in] IShellLink*/ void *This, /* [in] */ IObjectArray *poaSource); HRESULT ( __stdcall *RemoveObjectAt )( /* [in] IShellLink*/ void *This, /* [in] */ UINT uiIndex); HRESULT ( __stdcall *Clear )( /* [in] IShellLink*/ void *This); } IObjectCollectionVtbl; typedef struct IObjectCollection { IObjectCollectionVtbl *lpVtbl; } IObjectCollection; typedef struct IPropertyStoreVtbl { HRESULT ( __stdcall *QueryInterface )( /* [in] IPropertyStore*/ void *This, /* [in] */ const GUID * const riid, /* [iid_is][out] */ void **ppvObject); ULONG ( __stdcall *AddRef )( /* [in] IPropertyStore*/ void *This); ULONG ( __stdcall *Release )( /* [in] IPropertyStore*/ void *This); HRESULT ( __stdcall *GetCount )( /* [in] IPropertyStore*/ void *This, /* [out] */ DWORD *cProps); HRESULT ( __stdcall *GetAt )( /* [in] IPropertyStore*/ void *This, /* [in] */ DWORD iProp, /* [out] */ PROPERTYKEY *pkey); HRESULT ( __stdcall *GetValue )( /* [in] IPropertyStore*/ void *This, /* [in] */ const PROPERTYKEY * const key, /* [out] */ PROPVARIANT *pv); HRESULT ( __stdcall *SetValue )( /* [in] IPropertyStore*/ void *This, /* [in] */ const PROPERTYKEY * const key, /* [in] */ REFPROPVARIANT propvar); HRESULT ( __stdcall *Commit )( /* [in] IPropertyStore*/ void *This); } IPropertyStoreVtbl; typedef struct IPropertyStore { IPropertyStoreVtbl *lpVtbl; } IPropertyStore; static const CLSID CLSID_DestinationList = { 0x77f10cf0, 0x3db5, 0x4966, {0xb5,0x20,0xb7,0xc5,0x4f,0xd3,0x5e,0xd6} }; static const CLSID CLSID_ShellLink = { 0x00021401, 0x0000, 0x0000, {0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46} }; static const CLSID CLSID_EnumerableObjectCollection = { 0x2d3468c1, 0x36a7, 0x43b6, {0xac,0x24,0xd3,0xf0,0x2f,0xd9,0x60,0x7a} }; static const IID IID_IObjectCollection = { 0x5632b1a4, 0xe38a, 0x400a, {0x92,0x8a,0xd4,0xcd,0x63,0x23,0x02,0x95} }; static const IID IID_IShellLink = { 0x000214ee, 0x0000, 0x0000, {0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46} }; static const IID IID_ICustomDestinationList = { 0x6332debf, 0x87b5, 0x4670, {0x90,0xc0,0x5e,0x57,0xb4,0x08,0xa4,0x9e} }; static const IID IID_IObjectArray = { 0x92ca9dcd, 0x5622, 0x4bba, {0xa8,0x05,0x5e,0x9f,0x54,0x1b,0xd8,0xc9} }; static const IID IID_IPropertyStore = { 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 * non-null then it will be assumed to be a filename in the same * directory as our own executable, and the return value will be NULL * if that file doesn't exist. * * If 'sessionname' is null then no command line will be passed to the * program. If it's non-null, the command line will be that text * prefixed with an @ (to load a PuTTY saved session). * * Hence, you can launch a saved session using make_shell_link(NULL, * sessionname), and launch another app using e.g. * make_shell_link("puttygen.exe", NULL). */ static IShellLink *make_shell_link(const char *appname, const char *sessionname) { IShellLink *ret; char *app_path, *param_string, *desc_string; IPropertyStore *pPS; PROPVARIANT pv; /* Retrieve path to executable. */ if (!putty_path[0]) GetModuleFileName(NULL, putty_path, sizeof(putty_path) - 1); if (appname) { char *p, *q = putty_path; FILE *fp; if ((p = strrchr(q, '\\')) != NULL) q = p+1; if ((p = strrchr(q, ':')) != NULL) q = p+1; app_path = dupprintf("%.*s%s", (int)(q - putty_path), putty_path, appname); if ((fp = fopen(app_path, "r")) == NULL) { sfree(app_path); return NULL; } fclose(fp); } else { app_path = dupstr(putty_path); } /* Check if this is a valid session, otherwise don't add. */ if (sessionname) { settings_r *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) { /* The leading space is reported to work around a Windows 10 * behaviour change in which an argument string starting with * '@' causes the SetArguments method to silently do the wrong * thing. */ param_string = dupcat(" @", sessionname); } else { param_string = dupstr(""); } ret->lpVtbl->SetArguments(ret, param_string); sfree(param_string); if (sessionname) { desc_string = dupcat("Connect to PuTTY session '", sessionname, "'"); } else { assert(appname); desc_string = dupprintf("Run %.*s", (int)strcspn(appname, "."), appname); } ret->lpVtbl->SetDescription(ret, desc_string); sfree(desc_string); ret->lpVtbl->SetIconLocation(ret, app_path, 0); /* To set the link title, we require the property store of the link. */ if (SUCCEEDED(ret->lpVtbl->QueryInterface(ret, COMPTR(IPropertyStore, &pPS)))) { PropVariantInit(&pv); pv.vt = VT_LPSTR; if (sessionname) { pv.pszVal = dupstr(sessionname); } else { assert(appname); pv.pszVal = dupprintf("Run %.*s", (int)strcspn(appname, "."), appname); } pPS->lpVtbl->SetValue(pPS, &PKEY_Title, &pv); sfree(pv.pszVal); pPS->lpVtbl->Commit(pPS); pPS->lpVtbl->Release(pPS); } sfree(app_path); return ret; } /* Updates jumplist from registry. */ static void update_jumplist_from_registry(void) { const char *piterator; UINT num_items; int jumplist_counter; UINT nremoved; /* Variables used by the cleanup code must be initialised to NULL, * so that we don't try to free or release them if they were never * set up. */ ICustomDestinationList *pCDL = NULL; char *pjumplist_reg_entries = NULL; IObjectCollection *collection = NULL; IObjectArray *array = NULL; IShellLink *link = NULL; IObjectArray *pRemoved = NULL; bool need_abort = false; /* * Create an ICustomDestinationList: the top-level object which * deals with jump list management. */ if (!SUCCEEDED(CoCreateInstance(&CLSID_DestinationList, NULL, CLSCTX_INPROC_SERVER, COMPTR(ICustomDestinationList, &pCDL)))) goto cleanup; /* * Call its BeginList method to start compiling a list. This gives * us back 'num_items' (a hint derived from systemwide * configuration about how many things to put on the list) and * 'pRemoved' (user configuration about things to leave off the * list). */ if (!SUCCEEDED(pCDL->lpVtbl->BeginList(pCDL, &num_items, COMPTR(IObjectArray, &pRemoved)))) goto cleanup; need_abort = true; if (!SUCCEEDED(pRemoved->lpVtbl->GetCount(pRemoved, &nremoved))) nremoved = 0; /* * Create an object collection to form the 'Recent Sessions' * category on the jump list. */ if (!SUCCEEDED(CoCreateInstance(&CLSID_EnumerableObjectCollection, NULL, CLSCTX_INPROC_SERVER, COMPTR(IObjectCollection, &collection)))) goto cleanup; /* * Go through the jump list entries from the registry and add each * one to the collection. */ pjumplist_reg_entries = get_jumplist_registry_entries(); piterator = pjumplist_reg_entries; jumplist_counter = 0; while (*piterator != '\0' && (jumplist_counter < min(MAX_JUMPLIST_ITEMS, (int) num_items))) { link = make_shell_link(NULL, piterator); if (link) { UINT i; bool found; /* * Check that the link isn't in the user-removed list. */ for (i = 0, found = false; i < nremoved && !found; i++) { IShellLink *rlink; if (SUCCEEDED(pRemoved->lpVtbl->GetAt (pRemoved, i, COMPTR(IShellLink, &rlink)))) { char desc1[2048], desc2[2048]; if (SUCCEEDED(link->lpVtbl->GetDescription (link, desc1, sizeof(desc1)-1)) && SUCCEEDED(rlink->lpVtbl->GetDescription (rlink, desc2, sizeof(desc2)-1)) && !strcmp(desc1, desc2)) { found = true; } rlink->lpVtbl->Release(rlink); } } if (!found) { collection->lpVtbl->AddObject(collection, link); jumplist_counter++; } link->lpVtbl->Release(link); link = NULL; } piterator += strlen(piterator) + 1; } sfree(pjumplist_reg_entries); pjumplist_reg_entries = NULL; /* * Get the array form of the collection we've just constructed, * and put it in the jump list. */ if (!SUCCEEDED(collection->lpVtbl->QueryInterface (collection, COMPTR(IObjectArray, &array)))) goto cleanup; pCDL->lpVtbl->AppendCategory(pCDL, L"Recent Sessions", array); /* * Create an object collection to form the 'Tasks' category on the * jump list. */ if (!SUCCEEDED(CoCreateInstance(&CLSID_EnumerableObjectCollection, NULL, CLSCTX_INPROC_SERVER, COMPTR(IObjectCollection, &collection)))) goto cleanup; /* * Add task entries for PuTTYgen and Pageant. */ piterator = "Pageant.exe\0PuTTYgen.exe\0\0"; while (*piterator != '\0') { link = make_shell_link(piterator, NULL); if (link) { collection->lpVtbl->AddObject(collection, link); link->lpVtbl->Release(link); link = NULL; } piterator += strlen(piterator) + 1; } /* * Get the array form of the collection we've just constructed, * and put it in the jump list. */ if (!SUCCEEDED(collection->lpVtbl->QueryInterface (collection, COMPTR(IObjectArray, &array)))) goto cleanup; pCDL->lpVtbl->AddUserTasks(pCDL, array); /* * Now we can clean up the array and collection variables, so as * to be able to reuse them. */ array->lpVtbl->Release(array); array = NULL; collection->lpVtbl->Release(collection); collection = NULL; /* * Create another object collection to form the user tasks * category. */ if (!SUCCEEDED(CoCreateInstance(&CLSID_EnumerableObjectCollection, NULL, CLSCTX_INPROC_SERVER, COMPTR(IObjectCollection, &collection)))) goto cleanup; /* * Get the array form of the collection we've just constructed, * and put it in the jump list. */ if (!SUCCEEDED(collection->lpVtbl->QueryInterface (collection, COMPTR(IObjectArray, &array)))) goto cleanup; pCDL->lpVtbl->AddUserTasks(pCDL, array); /* * Now we can clean up the array and collection variables, so as * to be able to reuse them. */ array->lpVtbl->Release(array); array = NULL; collection->lpVtbl->Release(collection); collection = NULL; /* * Commit the jump list. */ pCDL->lpVtbl->CommitList(pCDL); need_abort = false; /* * Clean up. */ cleanup: if (pRemoved) pRemoved->lpVtbl->Release(pRemoved); if (pCDL && need_abort) pCDL->lpVtbl->AbortList(pCDL); if (pCDL) pCDL->lpVtbl->Release(pCDL); if (collection) collection->lpVtbl->Release(collection); if (array) array->lpVtbl->Release(array); if (link) link->lpVtbl->Release(link); sfree(pjumplist_reg_entries); } /* Clears the entire jumplist. */ void clear_jumplist(void) { ICustomDestinationList *pCDL; if (CoCreateInstance(&CLSID_DestinationList, NULL, CLSCTX_INPROC_SERVER, COMPTR(ICustomDestinationList, &pCDL)) == S_OK) { pCDL->lpVtbl->DeleteList(pCDL, NULL); pCDL->lpVtbl->Release(pCDL); } } /* Adds a saved session to the Windows 7 jumplist. */ void add_session_to_jumplist(const char * const sessionname) { if ((osMajorVersion < 6) || (osMajorVersion == 6 && osMinorVersion < 1)) return; /* do nothing on pre-Win7 systems */ if (add_to_jumplist_registry(sessionname) == JUMPLISTREG_OK) { update_jumplist_from_registry(); } else { /* Make sure we don't leave the jumplist dangling. */ clear_jumplist(); } } /* Removes a saved session from the Windows jumplist. */ void remove_session_from_jumplist(const char * const sessionname) { if ((osMajorVersion < 6) || (osMajorVersion == 6 && osMinorVersion < 1)) return; /* do nothing on pre-Win7 systems */ if (remove_from_jumplist_registry(sessionname) == JUMPLISTREG_OK) { update_jumplist_from_registry(); } else { /* Make sure we don't leave the jumplist dangling. */ clear_jumplist(); } } /* Set Explicit App User Model Id to fix removable media error with jump lists */ bool set_explicit_app_user_model_id(void) { DECL_WINDOWS_FUNCTION(static, HRESULT, SetCurrentProcessExplicitAppUserModelID, (PCWSTR)); static HMODULE shell32_module = 0; if (!shell32_module) { shell32_module = load_system32_dll("Shell32.dll"); /* * We can't typecheck this function here, because it's defined * in , which we're not including due to clashes * with all the manual-COM machinery above. */ GET_WINDOWS_FUNCTION_NO_TYPECHECK( shell32_module, SetCurrentProcessExplicitAppUserModelID); } if (p_SetCurrentProcessExplicitAppUserModelID) { if (p_SetCurrentProcessExplicitAppUserModelID(L"SimonTatham.PuTTY") == S_OK) { return true; } return false; } /* Function doesn't exist, which is ok for Pre-7 systems */ return true; } putty-0.76/windows/winmisc.c0000644000175000017500000003207714072266314013101 00000000000000/* * winmisc.c: miscellaneous Windows-specific things */ #include #include #include #include "putty.h" #ifndef SECURITY_WIN32 #define SECURITY_WIN32 #endif #include DWORD osMajorVersion, osMinorVersion, osPlatformId; 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; } bool filename_equal(const Filename *f1, const Filename *f2) { return !strcmp(f1->path, f2->path); } bool filename_is_null(const Filename *fn) { return !*fn->path; } void filename_free(Filename *fn) { sfree(fn->path); sfree(fn); } void filename_serialise(BinarySink *bs, const Filename *f) { put_asciz(bs, f->path); } Filename *filename_deserialise(BinarySource *src) { return filename_from_str(get_asciz(src)); } char filename_char_sanitise(char c) { if (strchr("<>:\"/\\|?*", c)) return '.'; return c; } char *get_username(void) { DWORD namelen; char *user; bool got_username = false; DECL_WINDOWS_FUNCTION(static, BOOLEAN, GetUserNameExA, (EXTENDED_NAME_FORMAT, LPSTR, PULONG)); { static bool tried_usernameex = false; if (!tried_usernameex) { /* Not available on Win9x, so load dynamically */ HMODULE secur32 = load_system32_dll("secur32.dll"); /* If MIT Kerberos is installed, the following call to GET_WINDOWS_FUNCTION makes Windows implicitly load sspicli.dll WITHOUT proper path sanitizing, so better load it properly before */ HMODULE sspicli = load_system32_dll("sspicli.dll"); (void)sspicli; /* squash compiler warning about unused variable */ GET_WINDOWS_FUNCTION(secur32, GetUserNameExA); tried_usernameex = true; } } if (p_GetUserNameExA) { /* * If available, use the principal -- this avoids the problem * that the local username is case-insensitive but Kerberos * usernames are case-sensitive. */ /* Get the length */ namelen = 0; (void) p_GetUserNameExA(NameUserPrincipal, NULL, &namelen); user = snewn(namelen, char); got_username = p_GetUserNameExA(NameUserPrincipal, user, &namelen); if (got_username) { char *p = strchr(user, '@'); if (p) *p = 0; } else { sfree(user); } } if (!got_username) { /* Fall back to local user name */ namelen = 0; if (!GetUserName(NULL, &namelen)) { /* * Apparently this doesn't work at least on Windows XP SP2. * Thus assume a maximum of 256. It will fail again if it * doesn't fit. */ namelen = 256; } user = snewn(namelen, char); got_username = GetUserName(user, &namelen); if (!got_username) { sfree(user); } } return got_username ? user : NULL; } void dll_hijacking_protection(void) { /* * If the OS provides it, call SetDefaultDllDirectories() to * prevent DLLs from being loaded from the directory containing * our own binary, and instead only load from system32. * * This is a protection against hijacking attacks, if someone runs * PuTTY directly from their web browser's download directory * having previously been enticed into clicking on an unwise link * that downloaded a malicious DLL to the same directory under one * of various magic names that seem to be things that standard * Windows DLLs delegate to. * * It shouldn't break deliberate loading of user-provided DLLs * such as GSSAPI providers, because those are specified by their * full pathname by the user-provided configuration. */ static HMODULE kernel32_module; DECL_WINDOWS_FUNCTION(static, BOOL, SetDefaultDllDirectories, (DWORD)); if (!kernel32_module) { kernel32_module = load_system32_dll("kernel32.dll"); #if (defined _MSC_VER && _MSC_VER < 1900) /* For older Visual Studio, this function isn't available in * the header files to type-check */ GET_WINDOWS_FUNCTION_NO_TYPECHECK( kernel32_module, SetDefaultDllDirectories); #else GET_WINDOWS_FUNCTION(kernel32_module, SetDefaultDllDirectories); #endif } if (p_SetDefaultDllDirectories) { /* LOAD_LIBRARY_SEARCH_SYSTEM32 and explicitly specified * directories only */ p_SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_SYSTEM32 | LOAD_LIBRARY_SEARCH_USER_DIRS); } } void init_winver(void) { OSVERSIONINFO osVersion; static HMODULE kernel32_module; DECL_WINDOWS_FUNCTION(static, BOOL, GetVersionExA, (LPOSVERSIONINFO)); if (!kernel32_module) { kernel32_module = load_system32_dll("kernel32.dll"); /* Deliberately don't type-check this function, because that * would involve using its declaration in a header file which * triggers a deprecation warning. I know it's deprecated (see * below) and don't need telling. */ GET_WINDOWS_FUNCTION_NO_TYPECHECK(kernel32_module, GetVersionExA); } ZeroMemory(&osVersion, sizeof(osVersion)); osVersion.dwOSVersionInfoSize = sizeof (OSVERSIONINFO); if (p_GetVersionExA && p_GetVersionExA(&osVersion)) { osMajorVersion = osVersion.dwMajorVersion; osMinorVersion = osVersion.dwMinorVersion; osPlatformId = osVersion.dwPlatformId; } else { /* * GetVersionEx is deprecated, so allow for it perhaps going * away in future API versions. If it's not there, simply * assume that's because Windows is too _new_, so fill in the * variables we care about to a value that will always compare * higher than any given test threshold. * * Normally we should be checking against the presence of a * specific function if possible in any case. */ osMajorVersion = osMinorVersion = UINT_MAX; /* a very high number */ osPlatformId = VER_PLATFORM_WIN32_NT; /* not Win32s or Win95-like */ } } HMODULE load_system32_dll(const char *libname) { /* * Wrapper function to load a DLL out of c:\windows\system32 * without going through the full DLL search path. (Hence no * attack is possible by placing a substitute DLL earlier on that * path.) */ static char *sysdir = NULL; static size_t sysdirsize = 0; char *fullpath; HMODULE ret; if (!sysdir) { size_t len; while ((len = GetSystemDirectory(sysdir, sysdirsize)) >= sysdirsize) sgrowarray(sysdir, sysdirsize, len); } fullpath = dupcat(sysdir, "\\", libname); 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) { 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 %u)", (unsigned int)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; } FontSpec *fontspec_new(const char *name, bool 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); } void fontspec_serialise(BinarySink *bs, FontSpec *f) { put_asciz(bs, f->name); put_uint32(bs, f->isbold); put_uint32(bs, f->height); put_uint32(bs, f->charset); } FontSpec *fontspec_deserialise(BinarySource *src) { const char *name = get_asciz(src); unsigned isbold = get_uint32(src); unsigned height = get_uint32(src); unsigned charset = get_uint32(src); return fontspec_new(name, isbold, height, charset); } bool open_for_write_would_lose_data(const Filename *fn) { WIN32_FILE_ATTRIBUTE_DATA attrs; if (!GetFileAttributesEx(fn->path, GetFileExInfoStandard, &attrs)) { /* * Generally, if we don't identify a specific reason why we * should return true from this function, we return false, and * let the subsequent attempt to open the file for real give a * more useful error message. */ return false; } if (attrs.dwFileAttributes & (FILE_ATTRIBUTE_DEVICE | FILE_ATTRIBUTE_DIRECTORY)) { /* * File is something other than an ordinary disk file, so * opening it for writing will not cause truncation. (It may * not _succeed_ either, but that's not our problem here!) */ return false; } if (attrs.nFileSizeHigh == 0 && attrs.nFileSizeLow == 0) { /* * File is zero-length (or may be a named pipe, which * dwFileAttributes can't tell apart from a regular file), so * opening it for writing won't truncate any data away because * there's nothing to truncate anyway. */ return false; } return true; } void escape_registry_key(const char *in, strbuf *out) { bool candot = false; static const char hex[16] = "0123456789ABCDEF"; while (*in) { if (*in == ' ' || *in == '\\' || *in == '*' || *in == '?' || *in == '%' || *in < ' ' || *in > '~' || (*in == '.' && !candot)) { put_byte(out, '%'); put_byte(out, hex[((unsigned char) *in) >> 4]); put_byte(out, hex[((unsigned char) *in) & 15]); } else put_byte(out, *in); in++; candot = true; } } void unescape_registry_key(const char *in, strbuf *out) { while (*in) { if (*in == '%' && in[1] && in[2]) { int i, j; i = in[1] - '0'; i -= (i > 9 ? 7 : 0); j = in[2] - '0'; j -= (j > 9 ? 7 : 0); put_byte(out, (i << 4) + j); in += 3; } else { put_byte(out, *in++); } } } #ifdef DEBUG static FILE *debug_fp = NULL; static HANDLE debug_hdl = INVALID_HANDLE_VALUE; static int debug_got_console = 0; void dputs(const char *buf) { DWORD dw; if (!debug_got_console) { if (AllocConsole()) { debug_got_console = 1; debug_hdl = GetStdHandle(STD_OUTPUT_HANDLE); } } if (!debug_fp) { debug_fp = fopen("debug.log", "w"); } if (debug_hdl != INVALID_HANDLE_VALUE) { WriteFile(debug_hdl, buf, strlen(buf), &dw, NULL); } fputs(buf, debug_fp); fflush(debug_fp); } #endif char *registry_get_string(HKEY root, const char *path, const char *leaf) { HKEY key = root; bool need_close_key = false; char *toret = NULL, *str = NULL; if (path) { if (RegCreateKey(key, path, &key) != ERROR_SUCCESS) goto out; need_close_key = true; } DWORD type, size; if (RegQueryValueEx(key, leaf, 0, &type, NULL, &size) != ERROR_SUCCESS) goto out; if (type != REG_SZ) goto out; str = snewn(size + 1, char); DWORD size_got = size; if (RegQueryValueEx(key, leaf, 0, &type, (LPBYTE)str, &size_got) != ERROR_SUCCESS) goto out; if (type != REG_SZ || size_got > size) goto out; str[size_got] = '\0'; toret = str; str = NULL; out: if (need_close_key) RegCloseKey(key); sfree(str); return toret; } putty-0.76/windows/winmiscs.c0000644000175000017500000001623214072266314013257 00000000000000/* * winmiscs.c: Windows-specific standalone functions. Has the same * relationship to winmisc.c that utils.c does to misc.c, but the * corresponding name 'winutils.c' was already taken. */ #include "putty.h" #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 #ifdef MINEFIELD /* * Minefield - a Windows equivalent for Electric Fence */ #define PAGESIZE 4096 /* * Design: * * We start by reserving as much virtual address space as Windows * will sensibly (or not sensibly) let us have. We flag it all as * invalid memory. * * Any allocation attempt is satisfied by committing one or more * pages, with an uncommitted page on either side. The returned * memory region is jammed up against the _end_ of the pages. * * Freeing anything causes instantaneous decommitment of the pages * involved, so stale pointers are caught as soon as possible. */ static int minefield_initialised = 0; static void *minefield_region = NULL; static long minefield_size = 0; static long minefield_npages = 0; static long minefield_curpos = 0; static unsigned short *minefield_admin = NULL; static void *minefield_pages = NULL; static void minefield_admin_hide(int hide) { int access = hide ? PAGE_NOACCESS : PAGE_READWRITE; VirtualProtect(minefield_admin, minefield_npages * 2, access, NULL); } static void minefield_init(void) { int size; int admin_size; int i; for (size = 0x40000000; size > 0; size = ((size >> 3) * 7) & ~0xFFF) { minefield_region = VirtualAlloc(NULL, size, MEM_RESERVE, PAGE_NOACCESS); if (minefield_region) break; } minefield_size = size; /* * Firstly, allocate a section of that to be the admin block. * We'll need a two-byte field for each page. */ minefield_admin = minefield_region; minefield_npages = minefield_size / PAGESIZE; admin_size = (minefield_npages * 2 + PAGESIZE - 1) & ~(PAGESIZE - 1); minefield_npages = (minefield_size - admin_size) / PAGESIZE; minefield_pages = (char *) minefield_region + admin_size; /* * Commit the admin region. */ VirtualAlloc(minefield_admin, minefield_npages * 2, MEM_COMMIT, PAGE_READWRITE); /* * Mark all pages as unused (0xFFFF). */ for (i = 0; i < minefield_npages; i++) minefield_admin[i] = 0xFFFF; /* * Hide the admin region. */ minefield_admin_hide(1); minefield_initialised = 1; } static void minefield_bomb(void) { div(1, *(int *) minefield_pages); } static void *minefield_alloc(int size) { int npages; int pos, lim, region_end, region_start; int start; int i; npages = (size + PAGESIZE - 1) / PAGESIZE; minefield_admin_hide(0); /* * Search from current position until we find a contiguous * bunch of npages+2 unused pages. */ pos = minefield_curpos; lim = minefield_npages; while (1) { /* Skip over used pages. */ while (pos < lim && minefield_admin[pos] != 0xFFFF) pos++; /* Count unused pages. */ start = pos; while (pos < lim && pos - start < npages + 2 && minefield_admin[pos] == 0xFFFF) pos++; if (pos - start == npages + 2) break; /* If we've reached the limit, reset the limit or stop. */ if (pos >= lim) { if (lim == minefield_npages) { /* go round and start again at zero */ lim = minefield_curpos; pos = 0; } else { minefield_admin_hide(1); return NULL; } } } minefield_curpos = pos - 1; /* * We have npages+2 unused pages starting at start. We leave * the first and last of these alone and use the rest. */ region_end = (start + npages + 1) * PAGESIZE; region_start = region_end - size; /* FIXME: could align here if we wanted */ /* * Update the admin region. */ for (i = start + 2; i < start + npages + 1; i++) minefield_admin[i] = 0xFFFE; /* used but no region starts here */ minefield_admin[start + 1] = region_start % PAGESIZE; minefield_admin_hide(1); VirtualAlloc((char *) minefield_pages + region_start, size, MEM_COMMIT, PAGE_READWRITE); return (char *) minefield_pages + region_start; } static void minefield_free(void *ptr) { int region_start, i, j; minefield_admin_hide(0); region_start = (char *) ptr - (char *) minefield_pages; i = region_start / PAGESIZE; if (i < 0 || i >= minefield_npages || minefield_admin[i] != region_start % PAGESIZE) minefield_bomb(); for (j = i; j < minefield_npages && minefield_admin[j] != 0xFFFF; j++) { minefield_admin[j] = 0xFFFF; } VirtualFree(ptr, j * PAGESIZE - region_start, MEM_DECOMMIT); minefield_admin_hide(1); } static int minefield_get_size(void *ptr) { int region_start, i, j; minefield_admin_hide(0); region_start = (char *) ptr - (char *) minefield_pages; i = region_start / PAGESIZE; if (i < 0 || i >= minefield_npages || minefield_admin[i] != region_start % PAGESIZE) minefield_bomb(); for (j = i; j < minefield_npages && minefield_admin[j] != 0xFFFF; j++); minefield_admin_hide(1); return j * PAGESIZE - region_start; } void *minefield_c_malloc(size_t size) { if (!minefield_initialised) minefield_init(); return minefield_alloc(size); } void minefield_c_free(void *p) { if (!minefield_initialised) minefield_init(); minefield_free(p); } /* * realloc _always_ moves the chunk, for rapid detection of code * that assumes it won't. */ void *minefield_c_realloc(void *p, size_t size) { size_t oldsize; void *q; if (!minefield_initialised) minefield_init(); q = minefield_alloc(size); oldsize = minefield_get_size(p); memcpy(q, p, (oldsize < size ? oldsize : size)); minefield_free(p); return q; } #endif /* MINEFIELD */ #if defined _MSC_VER && _MSC_VER < 1800 /* * Work around lack of strtoumax in older MSVC libraries */ uintmax_t strtoumax(const char *nptr, char **endptr, int base) { return _strtoui64(nptr, endptr, base); } #endif #if defined _M_ARM || defined _M_ARM64 bool platform_aes_hw_available(void) { return IsProcessorFeaturePresent(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE); } bool platform_sha256_hw_available(void) { return IsProcessorFeaturePresent(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE); } bool platform_sha1_hw_available(void) { return IsProcessorFeaturePresent(PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE); } bool platform_sha512_hw_available(void) { /* As of 2020-12-24, as far as I can tell from docs.microsoft.com, * Windows on Arm does not yet provide a PF_ARM_V8_* flag for the * SHA-512 architecture extension. */ return false; } #endif bool is_console_handle(HANDLE handle) { DWORD ignored_output; if (GetConsoleMode(handle, &ignored_output)) return true; return false; } putty-0.76/windows/winnet.c0000644000175000017500000015456314072266314012741 00000000000000/* * Windows networking abstraction. * * For the IPv6 code in here I am indebted to Jeroen Massar and * unfix.org. */ #include /* need to put this first, for winelib builds */ #include #include #include #define NEED_DECLARATION_OF_SELECT /* in order to initialise it */ #include "putty.h" #include "network.h" #include "tree234.h" #include "ssh.h" #include #ifndef NO_IPV6 #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wmissing-braces" #endif const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT; const struct in6_addr in6addr_loopback = IN6ADDR_LOOPBACK_INIT; #ifdef __clang__ #pragma clang diagnostic pop #endif #endif #define ipv4_is_loopback(addr) \ ((p_ntohl(addr.s_addr) & 0xFF000000L) == 0x7F000000L) /* * Mutable state that goes with a SockAddr: stores information * about where in the list of candidate IP(v*) addresses we've * currently got to. */ typedef struct SockAddrStep_tag SockAddrStep; struct SockAddrStep_tag { #ifndef NO_IPV6 struct addrinfo *ai; /* steps along addr->ais */ #endif int curraddr; }; typedef struct NetSocket NetSocket; struct NetSocket { const char *error; SOCKET s; Plug *plug; bufchain output_data; bool connected; bool writable; bool frozen; /* this causes readability notifications to be ignored */ bool frozen_readable; /* this means we missed at least one readability * notification while we were frozen */ bool localhost_only; /* for listening sockets */ char oobdata[1]; size_t sending_oob; bool 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. */ NetSocket *parent, *child; Socket sock; }; struct SockAddr { int refcount; char *error; bool resolved; bool 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. */ }; /* * Which address family this address belongs to. AF_INET for IPv4; * AF_INET6 for IPv6; AF_UNSPEC indicates that name resolution has * not been done and a simple host name is held in this SockAddr * structure. */ #ifndef NO_IPV6 #define SOCKADDR_FAMILY(addr, step) \ (!(addr)->resolved ? AF_UNSPEC : \ (step).ai ? (step).ai->ai_family : AF_INET) #else #define SOCKADDR_FAMILY(addr, step) \ (!(addr)->resolved ? AF_UNSPEC : AF_INET) #endif /* * Start a SockAddrStep structure to step through multiple * addresses. */ #ifndef NO_IPV6 #define START_STEP(addr, step) \ ((step).ai = (addr)->ais, (step).curraddr = 0) #else #define START_STEP(addr, step) \ ((step).curraddr = 0) #endif static tree234 *sktree; static int cmpfortree(void *av, void *bv) { NetSocket *a = (NetSocket *)av, *b = (NetSocket *)bv; uintptr_t as = (uintptr_t) a->s, bs = (uintptr_t) b->s; if (as < bs) return -1; if (as > bs) return +1; if (a < b) return -1; if (a > b) return +1; return 0; } static int cmpforsearch(void *av, void *bv) { NetSocket *b = (NetSocket *)bv; uintptr_t as = (uintptr_t) av, bs = (uintptr_t) b->s; if (as < bs) return -1; if (as > bs) return +1; return 0; } DECL_WINDOWS_FUNCTION(static, int, WSAStartup, (WORD, LPWSADATA)); DECL_WINDOWS_FUNCTION(static, int, WSACleanup, (void)); DECL_WINDOWS_FUNCTION(static, int, closesocket, (SOCKET)); DECL_WINDOWS_FUNCTION(static, u_long, ntohl, (u_long)); DECL_WINDOWS_FUNCTION(static, u_long, htonl, (u_long)); DECL_WINDOWS_FUNCTION(static, u_short, htons, (u_short)); DECL_WINDOWS_FUNCTION(static, u_short, ntohs, (u_short)); DECL_WINDOWS_FUNCTION(static, int, gethostname, (char *, int)); DECL_WINDOWS_FUNCTION(static, struct hostent FAR *, gethostbyname, (const char FAR *)); DECL_WINDOWS_FUNCTION(static, struct servent FAR *, getservbyname, (const char FAR *, const char FAR *)); DECL_WINDOWS_FUNCTION(static, unsigned long, inet_addr, (const char FAR *)); DECL_WINDOWS_FUNCTION(static, char FAR *, inet_ntoa, (struct in_addr)); DECL_WINDOWS_FUNCTION(static, const char FAR *, inet_ntop, (int, void FAR *, char *, size_t)); DECL_WINDOWS_FUNCTION(static, int, connect, (SOCKET, const struct sockaddr FAR *, int)); 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, getpeername, (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, LPDWORD, LPWSAOVERLAPPED, LPWSAOVERLAPPED_COMPLETION_ROUTINE)); #ifndef NO_IPV6 DECL_WINDOWS_FUNCTION(static, int, getaddrinfo, (const char *nodename, const char *servname, const struct addrinfo *hints, struct addrinfo **res)); DECL_WINDOWS_FUNCTION(static, void, freeaddrinfo, (struct addrinfo *res)); DECL_WINDOWS_FUNCTION(static, int, getnameinfo, (const struct sockaddr FAR * sa, socklen_t salen, char FAR * host, DWORD hostlen, char FAR * serv, DWORD servlen, int flags)); DECL_WINDOWS_FUNCTION(static, char *, gai_strerror, (int ecode)); DECL_WINDOWS_FUNCTION(static, int, WSAAddressToStringA, (LPSOCKADDR, DWORD, LPWSAPROTOCOL_INFO, LPSTR, LPDWORD)); #endif static HMODULE winsock_module = NULL; static WSADATA wsadata; #ifndef NO_IPV6 static HMODULE winsock2_module = NULL; static HMODULE wship6_module = NULL; #endif static bool sk_startup(int hi, int lo) { WORD winsock_ver; winsock_ver = MAKEWORD(hi, lo); if (p_WSAStartup(winsock_ver, &wsadata)) { return false; } if (LOBYTE(wsadata.wVersion) != LOBYTE(winsock_ver)) { return false; } return true; } DEF_WINDOWS_FUNCTION(WSAAsyncSelect); DEF_WINDOWS_FUNCTION(WSAEventSelect); DEF_WINDOWS_FUNCTION(WSAGetLastError); DEF_WINDOWS_FUNCTION(WSAEnumNetworkEvents); DEF_WINDOWS_FUNCTION(select); void sk_init(void) { #ifndef NO_IPV6 winsock2_module = #endif winsock_module = load_system32_dll("ws2_32.dll"); if (!winsock_module) { winsock_module = load_system32_dll("wsock32.dll"); } if (!winsock_module) modalfatalbox("Unable to load any WinSock library"); #ifndef NO_IPV6 /* Check if we have getaddrinfo in Winsock */ if (GetProcAddress(winsock_module, "getaddrinfo") != NULL) { GET_WINDOWS_FUNCTION(winsock_module, getaddrinfo); GET_WINDOWS_FUNCTION(winsock_module, freeaddrinfo); GET_WINDOWS_FUNCTION_NO_TYPECHECK(winsock_module, getnameinfo); /* This function would fail its type-check if we did one, * because the VS header file provides an inline definition * which is __cdecl instead of WINAPI. */ GET_WINDOWS_FUNCTION_NO_TYPECHECK(winsock_module, gai_strerror); } else { /* Fall back to wship6.dll for Windows 2000 */ wship6_module = load_system32_dll("wship6.dll"); if (wship6_module) { GET_WINDOWS_FUNCTION(wship6_module, getaddrinfo); GET_WINDOWS_FUNCTION(wship6_module, freeaddrinfo); /* See comment above about type check */ GET_WINDOWS_FUNCTION_NO_TYPECHECK(wship6_module, getnameinfo); GET_WINDOWS_FUNCTION_NO_TYPECHECK(winsock_module, gai_strerror); } else { } } GET_WINDOWS_FUNCTION(winsock2_module, WSAAddressToStringA); #endif GET_WINDOWS_FUNCTION(winsock_module, WSAAsyncSelect); GET_WINDOWS_FUNCTION(winsock_module, WSAEventSelect); /* We don't type-check select because at least some MinGW versions * of the Windows API headers seem to disagree with the * documentation on whether the 'struct timeval *' pointer is * const or not. */ GET_WINDOWS_FUNCTION_NO_TYPECHECK(winsock_module, select); GET_WINDOWS_FUNCTION(winsock_module, WSAGetLastError); GET_WINDOWS_FUNCTION(winsock_module, WSAEnumNetworkEvents); GET_WINDOWS_FUNCTION(winsock_module, WSAStartup); GET_WINDOWS_FUNCTION(winsock_module, WSACleanup); GET_WINDOWS_FUNCTION(winsock_module, closesocket); GET_WINDOWS_FUNCTION(winsock_module, ntohl); GET_WINDOWS_FUNCTION(winsock_module, htonl); GET_WINDOWS_FUNCTION(winsock_module, htons); GET_WINDOWS_FUNCTION(winsock_module, ntohs); GET_WINDOWS_FUNCTION_NO_TYPECHECK(winsock_module, gethostname); GET_WINDOWS_FUNCTION(winsock_module, gethostbyname); GET_WINDOWS_FUNCTION(winsock_module, getservbyname); GET_WINDOWS_FUNCTION(winsock_module, inet_addr); GET_WINDOWS_FUNCTION(winsock_module, inet_ntoa); /* Older Visual Studio, and MinGW as of Ubuntu 16.04, don't know * about this function at all, so can't type-check it. Also there * seems to be some disagreement in the VS headers about whether * the second argument is void * or const void *, so I omit the * type check. */ GET_WINDOWS_FUNCTION_NO_TYPECHECK(winsock_module, inet_ntop); 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, getpeername); 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) && !sk_startup(2,0) && !sk_startup(1,1)) { modalfatalbox("Unable to initialise WinSock"); } sktree = newtree234(cmpfortree); } void sk_cleanup(void) { NetSocket *s; int i; if (sktree) { for (i = 0; (s = index234(sktree, i)) != NULL; i++) { p_closesocket(s->s); } freetree234(sktree); sktree = NULL; } if (p_WSACleanup) p_WSACleanup(); if (winsock_module) FreeLibrary(winsock_module); #ifndef NO_IPV6 if (wship6_module) FreeLibrary(wship6_module); #endif } const char *winsock_error_string(int error) { /* * 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"; case WSAEAFNOSUPPORT: return "Network error: Address family not supported by protocol family"; case WSAEALREADY: return "Network error: Operation already in progress"; case WSAECONNABORTED: return "Network error: Software caused connection abort"; case WSAECONNREFUSED: return "Network error: Connection refused"; case WSAECONNRESET: return "Network error: Connection reset by peer"; case WSAEDESTADDRREQ: return "Network error: Destination address required"; case WSAEFAULT: return "Network error: Bad address"; case WSAEHOSTDOWN: return "Network error: Host is down"; case WSAEHOSTUNREACH: return "Network error: No route to host"; case WSAEINPROGRESS: return "Network error: Operation now in progress"; case WSAEINTR: return "Network error: Interrupted function call"; case WSAEINVAL: return "Network error: Invalid argument"; case WSAEISCONN: return "Network error: Socket is already connected"; case WSAEMFILE: return "Network error: Too many open files"; case WSAEMSGSIZE: return "Network error: Message too long"; case WSAENETDOWN: return "Network error: Network is down"; case WSAENETRESET: return "Network error: Network dropped connection on reset"; case WSAENETUNREACH: return "Network error: Network is unreachable"; case WSAENOBUFS: return "Network error: No buffer space available"; case WSAENOPROTOOPT: return "Network error: Bad protocol option"; case WSAENOTCONN: return "Network error: Socket is not connected"; case WSAENOTSOCK: return "Network error: Socket operation on non-socket"; case WSAEOPNOTSUPP: return "Network error: Operation not supported"; case WSAEPFNOSUPPORT: return "Network error: Protocol family not supported"; case WSAEPROCLIM: return "Network error: Too many processes"; case WSAEPROTONOSUPPORT: return "Network error: Protocol not supported"; case WSAEPROTOTYPE: return "Network error: Protocol wrong type for socket"; case WSAESHUTDOWN: return "Network error: Cannot send after socket shutdown"; case WSAESOCKTNOSUPPORT: 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"; } /* * Handle any other error code by delegating to win_strerror. */ return win_strerror(error); } SockAddr *sk_namelookup(const char *host, char **canonicalname, int address_family) { SockAddr *ret = snew(SockAddr); unsigned long a; char realhost[8192]; int hint_family; /* Default to IPv4. */ hint_family = (address_family == ADDRTYPE_IPV4 ? AF_INET : #ifndef NO_IPV6 address_family == ADDRTYPE_IPV6 ? AF_INET6 : #endif AF_UNSPEC); /* Clear the structure and default to IPv4. */ memset(ret, 0, sizeof(SockAddr)); #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; int err = 0; #ifndef NO_IPV6 /* * Use getaddrinfo when it's available */ if (p_getaddrinfo) { struct addrinfo hints; memset(&hints, 0, sizeof(hints)); hints.ai_family = hint_family; hints.ai_flags = AI_CANONNAME; { /* strip [] on IPv6 address literals */ char *trimmed_host = host_strduptrim(host); err = p_getaddrinfo(trimmed_host, NULL, &hints, &ret->ais); sfree(trimmed_host); } if (err == 0) ret->resolved = true; } else #endif { /* * Otherwise use the IPv4-only gethostbyname... * (NOTE: we don't use gethostbyname as a fallback!) */ if ( (h = p_gethostbyname(host)) ) ret->resolved = true; else err = p_WSAGetLastError(); } if (!ret->resolved) { ret->error = (err == WSAENETDOWN ? "Network is down" : err == WSAHOST_NOT_FOUND ? "Host does not exist" : err == WSATRY_AGAIN ? "Host not found" : #ifndef NO_IPV6 p_getaddrinfo&&p_gai_strerror ? p_gai_strerror(err) : #endif "gethostbyname: unknown error"); } else { ret->error = NULL; #ifndef NO_IPV6 /* If we got an address info use that... */ if (ret->ais) { /* Are we in IPv4 fallback mode? */ /* We put the IPv4 address into the a variable so we can further-on use the IPv4 code... */ if (ret->ais->ai_family == AF_INET) memcpy(&a, (char *) &((SOCKADDR_IN *) ret->ais-> ai_addr)->sin_addr, sizeof(a)); if (ret->ais->ai_canonname) strncpy(realhost, ret->ais->ai_canonname, lenof(realhost)); else strncpy(realhost, host, lenof(realhost)); } /* We used the IPv4-only gethostbyname()... */ else #endif { int n; for (n = 0; h->h_addr_list[n]; n++); ret->addresses = snewn(n, unsigned long); ret->naddresses = n; for (n = 0; n < ret->naddresses; n++) { memcpy(&a, h->h_addr_list[n], sizeof(a)); ret->addresses[n] = p_ntohl(a); } memcpy(&a, h->h_addr, sizeof(a)); /* This way we are always sure the h->h_name is valid :) */ strncpy(realhost, h->h_name, sizeof(realhost)); } } } else { /* * This must be a numeric IPv4 address because it caused a * success return from inet_addr. */ ret->addresses = snewn(1, unsigned long); ret->naddresses = 1; ret->addresses[0] = p_ntohl(a); ret->resolved = true; strncpy(realhost, host, sizeof(realhost)); } realhost[lenof(realhost)-1] = '\0'; *canonicalname = dupstr(realhost); return ret; } SockAddr *sk_nonamelookup(const char *host) { SockAddr *ret = snew(SockAddr); 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(SockAddr); 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; } static bool sk_nextaddr(SockAddr *addr, SockAddrStep *step) { #ifndef NO_IPV6 if (step->ai) { if (step->ai->ai_next) { step->ai = step->ai->ai_next; return true; } else return false; } #endif if (step->curraddr+1 < addr->naddresses) { step->curraddr++; return true; } else { return false; } } void sk_getaddr(SockAddr *addr, char *buf, int buflen) { SockAddrStep step; START_STEP(addr, step); #ifndef NO_IPV6 if (step.ai) { int err = 0; if (p_WSAAddressToStringA) { DWORD dwbuflen = buflen; err = p_WSAAddressToStringA(step.ai->ai_addr, step.ai->ai_addrlen, NULL, buf, &dwbuflen); } else err = -1; if (err) { strncpy(buf, addr->hostname, buflen); if (!buf[0]) strncpy(buf, "", buflen); buf[buflen-1] = '\0'; } } else #endif if (SOCKADDR_FAMILY(addr, step) == AF_INET) { struct in_addr a; assert(addr->addresses && step.curraddr < addr->naddresses); a.s_addr = p_htonl(addr->addresses[step.curraddr]); strncpy(buf, p_inet_ntoa(a), buflen); buf[buflen-1] = '\0'; } else { strncpy(buf, addr->hostname, buflen); buf[buflen-1] = '\0'; } } /* * This constructs a SockAddr that points at one specific sub-address * of a parent SockAddr. The returned SockAddr does not own all its * own memory: it points into the old one's data structures, so it * MUST NOT be used after the old one is freed, and it MUST NOT be * passed to sk_addr_free. (The latter is why it's returned by value * rather than dynamically allocated - that should clue in anyone * writing a call to it that something is weird about it.) */ static SockAddr sk_extractaddr_tmp( SockAddr *addr, const SockAddrStep *step) { SockAddr toret; toret = *addr; /* structure copy */ toret.refcount = 1; #ifndef NO_IPV6 toret.ais = step->ai; #endif if (SOCKADDR_FAMILY(addr, *step) == AF_INET #ifndef NO_IPV6 && !toret.ais #endif ) toret.addresses += step->curraddr; return toret; } bool sk_addr_needs_port(SockAddr *addr) { return !addr->namedpipe; } bool sk_hostname_is_local(const char *name) { return !strcmp(name, "localhost") || !strcmp(name, "::1") || !strncmp(name, "127.", 4); } static INTERFACE_INFO local_interfaces[16]; static int n_local_interfaces; /* 0=not yet, -1=failed, >0=number */ static bool ipv4_is_local_addr(struct in_addr addr) { if (ipv4_is_loopback(addr)) return true; /* loopback addresses are local */ if (!n_local_interfaces) { SOCKET s = p_socket(AF_INET, SOCK_DGRAM, 0); DWORD retbytes; SetHandleInformation((HANDLE)s, HANDLE_FLAG_INHERIT, 0); if (p_WSAIoctl && p_WSAIoctl(s, SIO_GET_INTERFACE_LIST, NULL, 0, local_interfaces, sizeof(local_interfaces), &retbytes, NULL, NULL) == 0) n_local_interfaces = retbytes / sizeof(INTERFACE_INFO); else n_local_interfaces = -1; } if (n_local_interfaces > 0) { int i; for (i = 0; i < n_local_interfaces; i++) { SOCKADDR_IN *address = (SOCKADDR_IN *)&local_interfaces[i].iiAddress; if (address->sin_addr.s_addr == addr.s_addr) return true; /* this address is local */ } } return false; /* this address is not local */ } bool sk_address_is_local(SockAddr *addr) { 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 false; /* we don't know; assume not */ } } bool sk_address_is_special_local(SockAddr *addr) { return false; /* no Unix-domain socket analogue here */ } int sk_addrtype(SockAddr *addr) { SockAddrStep step; int family; START_STEP(addr, step); family = SOCKADDR_FAMILY(addr, step); return (family == AF_INET ? ADDRTYPE_IPV4 : #ifndef NO_IPV6 family == AF_INET6 ? ADDRTYPE_IPV6 : #endif ADDRTYPE_NAME); } void sk_addrcopy(SockAddr *addr, char *buf) { SockAddrStep step; int family; START_STEP(addr, step); family = SOCKADDR_FAMILY(addr, step); assert(family != AF_UNSPEC); #ifndef NO_IPV6 if (step.ai) { if (family == AF_INET) memcpy(buf, &((struct sockaddr_in *)step.ai->ai_addr)->sin_addr, sizeof(struct in_addr)); else if (family == AF_INET6) memcpy(buf, &((struct sockaddr_in6 *)step.ai->ai_addr)->sin6_addr, sizeof(struct in6_addr)); else unreachable("bad address family in sk_addrcopy"); } else #endif if (family == AF_INET) { struct in_addr a; assert(addr->addresses && step.curraddr < addr->naddresses); a.s_addr = p_htonl(addr->addresses[step.curraddr]); memcpy(buf, (char*) &a.s_addr, 4); } } void sk_addr_free(SockAddr *addr) { if (--addr->refcount > 0) return; #ifndef NO_IPV6 if (addr->ais && p_freeaddrinfo) p_freeaddrinfo(addr->ais); #endif if (addr->addresses) sfree(addr->addresses); sfree(addr); } SockAddr *sk_addr_dup(SockAddr *addr) { addr->refcount++; return addr; } static Plug *sk_net_plug(Socket *sock, Plug *p) { NetSocket *s = container_of(sock, NetSocket, sock); Plug *ret = s->plug; if (p) s->plug = p; return ret; } static void sk_net_close(Socket *s); static size_t sk_net_write(Socket *s, const void *data, size_t len); static size_t sk_net_write_oob(Socket *s, const void *data, size_t len); static void sk_net_write_eof(Socket *s); static void sk_net_set_frozen(Socket *s, bool is_frozen); static const char *sk_net_socket_error(Socket *s); static SocketPeerInfo *sk_net_peer_info(Socket *s); static const SocketVtable NetSocket_sockvt = { .plug = sk_net_plug, .close = sk_net_close, .write = sk_net_write, .write_oob = sk_net_write_oob, .write_eof = sk_net_write_eof, .set_frozen = sk_net_set_frozen, .socket_error = sk_net_socket_error, .peer_info = sk_net_peer_info, }; static Socket *sk_net_accept(accept_ctx_t ctx, Plug *plug) { DWORD err; const char *errstr; NetSocket *ret; /* * Create NetSocket structure. */ ret = snew(NetSocket); ret->sock.vt = &NetSocket_sockvt; ret->error = NULL; ret->plug = plug; bufchain_init(&ret->output_data); ret->writable = true; /* to start with */ ret->sending_oob = 0; ret->outgoingeof = EOF_NO; ret->frozen = true; ret->frozen_readable = false; ret->localhost_only = false; /* 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 &ret->sock; } ret->oobinline = false; /* Set up a select mechanism. This could be an AsyncSelect on a * window, or an EventSelect on an event object. */ errstr = do_select(ret->s, true); if (errstr) { ret->error = errstr; return &ret->sock; } add234(sktree, ret); return &ret->sock; } static DWORD try_connect(NetSocket *sock) { SOCKET s; #ifndef NO_IPV6 SOCKADDR_IN6 a6; #endif SOCKADDR_IN a; DWORD err; const char *errstr; short localport; int family; if (sock->s != INVALID_SOCKET) { do_select(sock->s, false); p_closesocket(sock->s); } { SockAddr thisaddr = sk_extractaddr_tmp( sock->addr, &sock->step); plug_log(sock->plug, PLUGLOG_CONNECT_TRYING, &thisaddr, sock->port, NULL, 0); } /* * Open socket. */ family = SOCKADDR_FAMILY(sock->addr, sock->step); /* * Remove the socket from the tree before we overwrite its * internal socket id, because that forms part of the tree's * sorting criterion. We'll add it back before exiting this * function, whether we changed anything or not. */ del234(sktree, sock); s = p_socket(family, SOCK_STREAM, 0); sock->s = s; if (s == INVALID_SOCKET) { err = p_WSAGetLastError(); sock->error = winsock_error_string(err); goto ret; } SetHandleInformation((HANDLE)s, HANDLE_FLAG_INHERIT, 0); if (sock->oobinline) { BOOL b = true; p_setsockopt(s, SOL_SOCKET, SO_OOBINLINE, (void *) &b, sizeof(b)); } if (sock->nodelay) { BOOL b = true; p_setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (void *) &b, sizeof(b)); } if (sock->keepalive) { BOOL b = true; p_setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (void *) &b, sizeof(b)); } /* * Bind to local address. */ if (sock->privport) localport = 1023; /* count from 1023 downwards */ else localport = 0; /* just use port 0 (ie winsock picks) */ /* Loop round trying to bind */ while (1) { int sockcode; #ifndef NO_IPV6 if (family == AF_INET6) { memset(&a6, 0, sizeof(a6)); a6.sin6_family = AF_INET6; /*a6.sin6_addr = in6addr_any; */ /* == 0 done by memset() */ a6.sin6_port = p_htons(localport); } else #endif { a.sin_family = AF_INET; a.sin_addr.s_addr = p_htonl(INADDR_ANY); a.sin_port = p_htons(localport); } #ifndef NO_IPV6 sockcode = p_bind(s, (family == AF_INET6 ? (struct sockaddr *) &a6 : (struct sockaddr *) &a), (family == AF_INET6 ? sizeof(a6) : sizeof(a))); #else sockcode = p_bind(s, (struct sockaddr *) &a, sizeof(a)); #endif if (sockcode != SOCKET_ERROR) { err = 0; break; /* done */ } else { err = p_WSAGetLastError(); if (err != WSAEADDRINUSE) /* failed, for a bad reason */ break; } if (localport == 0) break; /* we're only looping once */ localport--; if (localport == 0) break; /* we might have got to the end */ } if (err) { sock->error = winsock_error_string(err); goto ret; } /* * Connect to remote address. */ #ifndef NO_IPV6 if (sock->step.ai) { if (family == AF_INET6) { a6.sin6_family = AF_INET6; a6.sin6_port = p_htons((short) sock->port); a6.sin6_addr = ((struct sockaddr_in6 *) sock->step.ai->ai_addr)->sin6_addr; a6.sin6_flowinfo = ((struct sockaddr_in6 *) sock->step.ai->ai_addr)->sin6_flowinfo; a6.sin6_scope_id = ((struct sockaddr_in6 *) sock->step.ai->ai_addr)->sin6_scope_id; } else { a.sin_family = AF_INET; a.sin_addr = ((struct sockaddr_in *) sock->step.ai->ai_addr)->sin_addr; a.sin_port = p_htons((short) sock->port); } } else #endif { assert(sock->addr->addresses && sock->step.curraddr < sock->addr->naddresses); a.sin_family = AF_INET; a.sin_addr.s_addr = p_htonl(sock->addr->addresses[sock->step.curraddr]); a.sin_port = p_htons((short) sock->port); } /* Set up a select mechanism. This could be an AsyncSelect on a * window, or an EventSelect on an event object. */ errstr = do_select(s, true); if (errstr) { sock->error = errstr; err = 1; goto ret; } if (( #ifndef NO_IPV6 p_connect(s, ((family == AF_INET6) ? (struct sockaddr *) &a6 : (struct sockaddr *) &a), (family == AF_INET6) ? sizeof(a6) : sizeof(a)) #else p_connect(s, (struct sockaddr *) &a, sizeof(a)) #endif ) == SOCKET_ERROR) { err = p_WSAGetLastError(); /* * We expect a potential EWOULDBLOCK here, because the * chances are the front end has done a select for * FD_CONNECT, so that connect() will complete * asynchronously. */ if ( err != WSAEWOULDBLOCK ) { sock->error = winsock_error_string(err); goto ret; } } else { /* * If we _don't_ get EWOULDBLOCK, the connect has completed * and we should set the socket as writable. */ sock->writable = true; SockAddr thisaddr = sk_extractaddr_tmp(sock->addr, &sock->step); plug_log(sock->plug, PLUGLOG_CONNECT_SUCCESS, &thisaddr, sock->port, NULL, 0); } err = 0; ret: /* * No matter what happened, put the socket back in the tree. */ add234(sktree, sock); if (err) { SockAddr thisaddr = sk_extractaddr_tmp( sock->addr, &sock->step); plug_log(sock->plug, PLUGLOG_CONNECT_FAILED, &thisaddr, sock->port, sock->error, err); } return err; } Socket *sk_new(SockAddr *addr, int port, bool privport, bool oobinline, bool nodelay, bool keepalive, Plug *plug) { NetSocket *ret; DWORD err; /* * Create NetSocket structure. */ ret = snew(NetSocket); ret->sock.vt = &NetSocket_sockvt; ret->error = NULL; ret->plug = plug; bufchain_init(&ret->output_data); ret->connected = false; /* to start with */ ret->writable = false; /* to start with */ ret->sending_oob = 0; ret->outgoingeof = EOF_NO; ret->frozen = false; ret->frozen_readable = false; ret->localhost_only = false; /* unused, but best init anyway */ ret->pending_error = 0; ret->parent = ret->child = NULL; ret->oobinline = oobinline; ret->nodelay = nodelay; ret->keepalive = keepalive; ret->privport = privport; ret->port = port; ret->addr = addr; START_STEP(ret->addr, ret->step); ret->s = INVALID_SOCKET; err = 0; do { err = try_connect(ret); } while (err && sk_nextaddr(ret->addr, &ret->step)); return &ret->sock; } Socket *sk_newlistener(const char *srcaddr, int port, Plug *plug, bool local_host_only, int orig_address_family) { SOCKET s; #ifndef NO_IPV6 SOCKADDR_IN6 a6; #endif SOCKADDR_IN a; DWORD err; const char *errstr; NetSocket *ret; int retcode; int address_family; /* * Create NetSocket structure. */ ret = snew(NetSocket); ret->sock.vt = &NetSocket_sockvt; ret->error = NULL; ret->plug = plug; bufchain_init(&ret->output_data); ret->writable = false; /* to start with */ ret->sending_oob = 0; ret->outgoingeof = EOF_NO; ret->frozen = false; ret->frozen_readable = false; ret->localhost_only = local_host_only; ret->pending_error = 0; ret->parent = ret->child = NULL; ret->addr = NULL; /* * Translate address_family from platform-independent constants * into local reality. */ address_family = (orig_address_family == ADDRTYPE_IPV4 ? AF_INET : #ifndef NO_IPV6 orig_address_family == ADDRTYPE_IPV6 ? AF_INET6 : #endif AF_UNSPEC); /* * Our default, if passed the `don't care' value * ADDRTYPE_UNSPEC, is to listen on IPv4. If IPv6 is supported, * we will also set up a second socket listening on IPv6, but * the v4 one is primary since that ought to work even on * non-v6-supporting systems. */ if (address_family == AF_UNSPEC) address_family = AF_INET; /* * Open socket. */ s = p_socket(address_family, SOCK_STREAM, 0); ret->s = s; if (s == INVALID_SOCKET) { err = p_WSAGetLastError(); ret->error = winsock_error_string(err); return &ret->sock; } SetHandleInformation((HANDLE)s, HANDLE_FLAG_INHERIT, 0); ret->oobinline = false; { BOOL on = true; p_setsockopt(s, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (const char *)&on, sizeof(on)); } #ifndef NO_IPV6 if (address_family == AF_INET6) { memset(&a6, 0, sizeof(a6)); a6.sin6_family = AF_INET6; if (local_host_only) a6.sin6_addr = in6addr_loopback; else a6.sin6_addr = in6addr_any; if (srcaddr != NULL && p_getaddrinfo) { struct addrinfo hints; struct addrinfo *ai; int err; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET6; hints.ai_flags = 0; { /* strip [] on IPv6 address literals */ char *trimmed_addr = host_strduptrim(srcaddr); err = p_getaddrinfo(trimmed_addr, NULL, &hints, &ai); sfree(trimmed_addr); } if (err == 0 && ai->ai_family == AF_INET6) { a6.sin6_addr = ((struct sockaddr_in6 *)ai->ai_addr)->sin6_addr; } } a6.sin6_port = p_htons(port); } else #endif { bool got_addr = false; a.sin_family = AF_INET; /* * Bind to source address. First try an explicitly * specified one... */ if (srcaddr) { a.sin_addr.s_addr = p_inet_addr(srcaddr); if (a.sin_addr.s_addr != INADDR_NONE) { /* Override localhost_only with specified listen addr. */ ret->localhost_only = ipv4_is_loopback(a.sin_addr); got_addr = true; } } /* * ... and failing that, go with one of the standard ones. */ if (!got_addr) { if (local_host_only) a.sin_addr.s_addr = p_htonl(INADDR_LOOPBACK); else a.sin_addr.s_addr = p_htonl(INADDR_ANY); } a.sin_port = p_htons((short)port); } #ifndef NO_IPV6 retcode = p_bind(s, (address_family == AF_INET6 ? (struct sockaddr *) &a6 : (struct sockaddr *) &a), (address_family == AF_INET6 ? sizeof(a6) : sizeof(a))); #else retcode = p_bind(s, (struct sockaddr *) &a, sizeof(a)); #endif if (retcode != SOCKET_ERROR) { err = 0; } else { err = p_WSAGetLastError(); } if (err) { p_closesocket(s); ret->error = winsock_error_string(err); return &ret->sock; } if (p_listen(s, SOMAXCONN) == SOCKET_ERROR) { p_closesocket(s); ret->error = winsock_error_string(p_WSAGetLastError()); return &ret->sock; } /* Set up a select mechanism. This could be an AsyncSelect on a * window, or an EventSelect on an event object. */ errstr = do_select(s, true); if (errstr) { p_closesocket(s); ret->error = errstr; return &ret->sock; } add234(sktree, ret); #ifndef NO_IPV6 /* * If we were given ADDRTYPE_UNSPEC, we must also create an * IPv6 listening socket and link it to this one. */ if (address_family == AF_INET && orig_address_family == ADDRTYPE_UNSPEC) { Socket *other = sk_newlistener(srcaddr, port, plug, local_host_only, ADDRTYPE_IPV6); if (other) { NetSocket *ns = container_of(other, NetSocket, sock); if (!ns->error) { ns->parent = ret; ret->child = ns; } else { sfree(ns); } } } #endif return &ret->sock; } static void sk_net_close(Socket *sock) { NetSocket *s = container_of(sock, NetSocket, sock); if (s->child) sk_net_close(&s->child->sock); bufchain_clear(&s->output_data); del234(sktree, s); do_select(s->s, false); p_closesocket(s->s); if (s->addr) sk_addr_free(s->addr); delete_callbacks_for_context(s); sfree(s); } /* * Deal with socket errors detected in try_send(). */ static void socket_error_callback(void *vs) { NetSocket *s = (NetSocket *)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(NetSocket *s) { while (s->sending_oob || bufchain_size(&s->output_data) > 0) { int nsent; DWORD err; const void *data; size_t len; int urgentflag; if (s->sending_oob) { urgentflag = MSG_OOB; len = s->sending_oob; data = &s->oobdata; } else { urgentflag = 0; ptrlen bufdata = bufchain_prefix(&s->output_data); data = bufdata.ptr; len = bufdata.len; } len = min(len, INT_MAX); /* WinSock send() takes an int */ nsent = p_send(s->s, data, len, urgentflag); noise_ultralight(NOISE_SOURCE_IOLEN, nsent); if (nsent <= 0) { err = (nsent < 0 ? p_WSAGetLastError() : 0); if ((err < WSABASEERR && nsent < 0) || err == WSAEWOULDBLOCK) { /* * Perfectly normal: we've sent all we can for the moment. * * (Some WinSock send() implementations can return * <0 but leave no sensible error indication - * WSAGetLastError() is called but returns zero or * a small number - so we check that case and treat * it just like WSAEWOULDBLOCK.) */ s->writable = false; return; } else { /* * If send() returns a socket error, we unfortunately * can't just call plug_closing(), because it's quite * likely that we're currently _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 { 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 size_t sk_net_write(Socket *sock, const void *buf, size_t len) { NetSocket *s = container_of(sock, NetSocket, 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 size_t sk_net_write_oob(Socket *sock, const void *buf, size_t len) { NetSocket *s = container_of(sock, NetSocket, 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_net_write_eof(Socket *sock) { NetSocket *s = container_of(sock, NetSocket, 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); } void select_result(WPARAM wParam, LPARAM lParam) { int ret; DWORD err; char buf[20480]; /* nice big buffer for plenty of speed */ NetSocket *s; bool atmark; /* wParam is the socket itself */ if (wParam == 0) return; /* boggle */ s = find234(sktree, (void *) wParam, cmpforsearch); if (!s) return; /* boggle */ if ((err = WSAGETSELECTERROR(lParam)) != 0) { /* * An error has occurred on this socket. Pass it to the * plug. */ if (s->addr) { SockAddr thisaddr = sk_extractaddr_tmp( s->addr, &s->step); plug_log(s->plug, PLUGLOG_CONNECT_FAILED, &thisaddr, s->port, winsock_error_string(err), err); while (err && s->addr && sk_nextaddr(s->addr, &s->step)) { err = try_connect(s); } } if (err != 0) plug_closing(s->plug, winsock_error_string(err), err, 0); return; } noise_ultralight(NOISE_SOURCE_IOID, wParam); switch (WSAGETSELECTEVENT(lParam)) { case FD_CONNECT: s->connected = true; s->writable = true; /* * Once a socket is connected, we can stop falling back * through the candidate addresses to connect to. But first, * let the plug know we were successful. */ if (s->addr) { SockAddr thisaddr = sk_extractaddr_tmp( s->addr, &s->step); plug_log(s->plug, PLUGLOG_CONNECT_SUCCESS, &thisaddr, s->port, NULL, 0); sk_addr_free(s->addr); s->addr = NULL; } break; case FD_READ: /* In the case the socket is still frozen, we don't even bother */ if (s->frozen) { s->frozen_readable = true; 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). */ if (s->oobinline) { u_long atmark_from_ioctl = 1; p_ioctlsocket(s->s, SIOCATMARK, &atmark_from_ioctl); /* * Avoid checking the return value from ioctlsocket(), * on the grounds that some WinSock wrappers don't * support it. If it does nothing, we get atmark==1, * which is equivalent to `no OOB pending', so the * effect will be to non-OOB-ify any OOB data. */ atmark = atmark_from_ioctl; } else atmark = true; ret = p_recv(s->s, buf, sizeof(buf), 0); noise_ultralight(NOISE_SOURCE_IOLEN, ret); if (ret < 0) { err = p_WSAGetLastError(); if (err == WSAEWOULDBLOCK) { break; } } if (ret < 0) { plug_closing(s->plug, winsock_error_string(err), err, 0); } else if (0 == ret) { plug_closing(s->plug, NULL, 0, 0); } else { plug_receive(s->plug, atmark ? 0 : 1, buf, ret); } break; case FD_OOB: /* * This will only happen on a non-oobinline socket. It * indicates that we can immediately perform an OOB read * and get back OOB data, which we will send to the back * end with type==2 (urgent data). */ ret = p_recv(s->s, buf, sizeof(buf), MSG_OOB); noise_ultralight(NOISE_SOURCE_IOLEN, ret); if (ret <= 0) { int err = p_WSAGetLastError(); plug_closing(s->plug, winsock_error_string(err), err, 0); } else { plug_receive(s->plug, 2, buf, ret); } break; case FD_WRITE: { int bufsize_before, bufsize_after; s->writable = true; bufsize_before = s->sending_oob + bufchain_size(&s->output_data); try_send(s); bufsize_after = s->sending_oob + bufchain_size(&s->output_data); if (bufsize_after < bufsize_before) plug_sent(s->plug, bufsize_after); break; } case FD_CLOSE: /* Signal a close on the socket. First read any outstanding data. */ do { ret = p_recv(s->s, buf, sizeof(buf), 0); if (ret < 0) { err = p_WSAGetLastError(); if (err == WSAEWOULDBLOCK) break; plug_closing(s->plug, winsock_error_string(err), err, 0); } else { if (ret) plug_receive(s->plug, 0, buf, ret); else plug_closing(s->plug, NULL, 0, 0); } } while (ret > 0); return; case FD_ACCEPT: { #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_net_accept, actx)) { p_closesocket(t); /* denied or error */ } break; } } } /* * 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) { return addr->error; } static const char *sk_net_socket_error(Socket *sock) { NetSocket *s = container_of(sock, NetSocket, sock); return s->error; } static SocketPeerInfo *sk_net_peer_info(Socket *sock) { NetSocket *s = container_of(sock, NetSocket, sock); #ifdef NO_IPV6 struct sockaddr_in addr; #else struct sockaddr_storage addr; char buf[INET6_ADDRSTRLEN]; #endif int addrlen = sizeof(addr); SocketPeerInfo *pi; if (p_getpeername(s->s, (struct sockaddr *)&addr, &addrlen) < 0) return NULL; pi = snew(SocketPeerInfo); pi->addressfamily = ADDRTYPE_UNSPEC; pi->addr_text = NULL; pi->port = -1; pi->log_text = NULL; if (((struct sockaddr *)&addr)->sa_family == AF_INET) { pi->addressfamily = ADDRTYPE_IPV4; memcpy(pi->addr_bin.ipv4, &((struct sockaddr_in *)&addr)->sin_addr, 4); pi->port = p_ntohs(((struct sockaddr_in *)&addr)->sin_port); pi->addr_text = dupstr( p_inet_ntoa(((struct sockaddr_in *)&addr)->sin_addr)); pi->log_text = dupprintf("%s:%d", pi->addr_text, pi->port); #ifndef NO_IPV6 } else if (((struct sockaddr *)&addr)->sa_family == AF_INET6) { pi->addressfamily = ADDRTYPE_IPV6; memcpy(pi->addr_bin.ipv6, &((struct sockaddr_in6 *)&addr)->sin6_addr, 16); pi->port = p_ntohs(((struct sockaddr_in6 *)&addr)->sin6_port); pi->addr_text = dupstr( p_inet_ntop(AF_INET6, &((struct sockaddr_in6 *)&addr)->sin6_addr, buf, sizeof(buf))); pi->log_text = dupprintf("[%s]:%d", pi->addr_text, pi->port); #endif } else { sfree(pi); return NULL; } return pi; } static void sk_net_set_frozen(Socket *sock, bool is_frozen) { NetSocket *s = container_of(sock, NetSocket, sock); if (s->frozen == is_frozen) return; s->frozen = is_frozen; if (!is_frozen) { do_select(s->s, true); if (s->frozen_readable) { char c; p_recv(s->s, &c, 1, MSG_PEEK); } } s->frozen_readable = false; } void socket_reselect_all(void) { NetSocket *s; int i; for (i = 0; (s = index234(sktree, i)) != NULL; i++) { if (!s->frozen) do_select(s->s, true); } } /* * For Plink: enumerate all sockets currently active. */ SOCKET first_socket(int *state) { NetSocket *s; *state = 0; s = index234(sktree, (*state)++); return s ? s->s : INVALID_SOCKET; } SOCKET next_socket(int *state) { NetSocket *s = index234(sktree, (*state)++); return s ? s->s : INVALID_SOCKET; } bool socket_writable(SOCKET skt) { NetSocket *s = find234(sktree, (void *)skt, cmpforsearch); if (s) return bufchain_size(&s->output_data) > 0; else return false; } int net_service_lookup(char *service) { struct servent *se; se = p_getservbyname(service, NULL); if (se != NULL) return p_ntohs(se->s_port); else return 0; } char *get_hostname(void) { char hostbuf[256]; /* MSDN docs for gethostname() promise this is enough */ if (p_gethostname(hostbuf, sizeof(hostbuf)) < 0) return NULL; return dupstr(hostbuf); } SockAddr *platform_get_x11_unix_address(const char *display, int displaynum) { SockAddr *ret = snew(SockAddr); memset(ret, 0, sizeof(SockAddr)); ret->error = "unix sockets not supported on this platform"; ret->refcount = 1; return ret; } putty-0.76/windows/winnohlp.c0000644000175000017500000000057414072266314013263 00000000000000/* * nohelp.c: implement the has_embedded_chm() function for * applications that have no help file at all, so that misc.c's * buildinfo string knows not to talk meaninglessly about whether the * nonexistent help file is present. */ #include #include #include #include #include "putty.h" int has_embedded_chm(void) { return -1; } putty-0.76/windows/winnoise.c0000644000175000017500000001014114072266314013247 00000000000000/* * Noise generation for PuTTY's cryptographic random number * generator. */ #include #include "putty.h" #include "ssh.h" #include "storage.h" #include 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; bool win_read_random(void *buf, unsigned wanted) { bool toret = false; HCRYPTPROV crypt_provider; 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)) { toret = p_CryptGenRandom(crypt_provider, wanted, buf); p_CryptReleaseContext(crypt_provider, 0); } return toret; } /* * This function is called once, at PuTTY startup. */ void noise_get_heavy(void (*func) (void *, int)) { HANDLE srch; WIN32_FIND_DATA finddata; DWORD pid; char winpath[MAX_PATH + 3]; BYTE buf[32]; 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 (win_read_random(buf, sizeof(buf))) { func(buf, sizeof(buf)); smemclr(buf, sizeof(buf)); } read_random_seed(func); } /* * This function is called on a timer, and it will monitor * frequently changing quantities such as the state of physical and * virtual memory, the state of the process's message queue, which * window is in the foreground, which owns the clipboard, etc. */ void noise_regular(void) { HWND w; DWORD z; POINT pt; MEMORYSTATUS memstat; FILETIME times[4]; w = GetForegroundWindow(); random_add_noise(NOISE_SOURCE_FGWINDOW, &w, sizeof(w)); w = GetCapture(); random_add_noise(NOISE_SOURCE_CAPTURE, &w, sizeof(w)); w = GetClipboardOwner(); random_add_noise(NOISE_SOURCE_CLIPBOARD, &w, sizeof(w)); z = GetQueueStatus(QS_ALLEVENTS); random_add_noise(NOISE_SOURCE_QUEUE, &z, sizeof(z)); GetCursorPos(&pt); random_add_noise(NOISE_SOURCE_CURSORPOS, &pt, sizeof(pt)); GlobalMemoryStatus(&memstat); random_add_noise(NOISE_SOURCE_MEMINFO, &memstat, sizeof(memstat)); GetThreadTimes(GetCurrentThread(), times, times + 1, times + 2, times + 3); random_add_noise(NOISE_SOURCE_THREADTIME, ×, sizeof(times)); GetProcessTimes(GetCurrentProcess(), times, times + 1, times + 2, times + 3); random_add_noise(NOISE_SOURCE_PROCTIME, ×, sizeof(times)); } /* * This function is called on every keypress or mouse move, and * will add the current Windows time and performance monitor * counter to the noise pool. It gets the scan code or mouse * position passed in. */ void noise_ultralight(NoiseSourceId id, unsigned long data) { DWORD wintime; LARGE_INTEGER perftime; random_add_noise(id, &data, sizeof(DWORD)); wintime = GetTickCount(); random_add_noise(NOISE_SOURCE_TIME, &wintime, sizeof(DWORD)); if (QueryPerformanceCounter(&perftime)) random_add_noise(NOISE_SOURCE_PERFCOUNT, &perftime, sizeof(perftime)); } uint64_t prng_reseed_time_ms(void) { FILETIME ft; GetSystemTimeAsFileTime(&ft); uint64_t value = ft.dwHighDateTime; value = (value << 32) + ft.dwLowDateTime; return value / 10000; /* 1 millisecond / 100ns */ } putty-0.76/windows/winnojmp.c0000644000175000017500000000041614072266314013261 00000000000000/* * winnojmp.c: stub jump list functions for Windows executables that * don't update the jump list. */ void add_session_to_jumplist(const char * const sessionname) {} void remove_session_from_jumplist(const char * const sessionname) {} void clear_jumplist(void) {} putty-0.76/windows/winnpc.c0000644000175000017500000000554714072266314012730 00000000000000/* * Windows support module which deals with being a named-pipe client. */ #include #include #include "tree234.h" #include "putty.h" #include "network.h" #include "proxy.h" #include "ssh.h" #if !defined NO_SECURITY #include "winsecur.h" HANDLE connect_to_named_pipe(const char *pipename, char **err) { HANDLE pipehandle; PSID usersid, pipeowner; PSECURITY_DESCRIPTOR psd; 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())); return INVALID_HANDLE_VALUE; } /* * 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())); return INVALID_HANDLE_VALUE; } } if ((usersid = get_user_sid()) == NULL) { CloseHandle(pipehandle); *err = dupprintf( "Unable to get user SID: %s", win_strerror(GetLastError())); return INVALID_HANDLE_VALUE; } if (p_GetSecurityInfo(pipehandle, SE_KERNEL_OBJECT, OWNER_SECURITY_INFORMATION, &pipeowner, NULL, NULL, NULL, &psd) != ERROR_SUCCESS) { CloseHandle(pipehandle); *err = dupprintf( "Unable to get named pipe security information: %s", win_strerror(GetLastError())); return INVALID_HANDLE_VALUE; } if (!EqualSid(pipeowner, usersid)) { CloseHandle(pipehandle); LocalFree(psd); *err = dupprintf( "Owner of named pipe '%s' is not us", pipename); return INVALID_HANDLE_VALUE; } LocalFree(psd); return pipehandle; } Socket *new_named_pipe_client(const char *pipename, Plug *plug) { char *err = NULL; HANDLE pipehandle = connect_to_named_pipe(pipename, &err); if (pipehandle == INVALID_HANDLE_VALUE) return new_error_socket_consume_string(plug, err); else return make_handle_socket(pipehandle, pipehandle, NULL, plug, true); } #endif /* !defined NO_SECURITY */ putty-0.76/windows/winnps.c0000644000175000017500000001511414072266314012737 00000000000000/* * Windows support module which deals with being a named-pipe server. */ #include #include #include "tree234.h" #include "putty.h" #include "network.h" #include "proxy.h" #include "ssh.h" #if !defined NO_SECURITY #include "winsecur.h" typedef struct NamedPipeServerSocket { /* Parameters for (repeated) creation of named pipe objects */ PSECURITY_DESCRIPTOR psd; PACL acl; char *pipename; /* The current named pipe object + attempt to connect to it */ HANDLE pipehandle; OVERLAPPED connect_ovl; struct handle *callback_handle; /* winhandl.c's reference */ /* PuTTY Socket machinery */ Plug *plug; char *error; Socket sock; } NamedPipeServerSocket; static Plug *sk_namedpipeserver_plug(Socket *s, Plug *p) { NamedPipeServerSocket *ps = container_of(s, NamedPipeServerSocket, sock); Plug *ret = ps->plug; if (p) ps->plug = p; return ret; } static void sk_namedpipeserver_close(Socket *s) { NamedPipeServerSocket *ps = container_of(s, NamedPipeServerSocket, sock); if (ps->callback_handle) handle_free(ps->callback_handle); CloseHandle(ps->pipehandle); CloseHandle(ps->connect_ovl.hEvent); sfree(ps->error); sfree(ps->pipename); if (ps->acl) LocalFree(ps->acl); if (ps->psd) LocalFree(ps->psd); sfree(ps); } static const char *sk_namedpipeserver_socket_error(Socket *s) { NamedPipeServerSocket *ps = container_of(s, NamedPipeServerSocket, sock); return ps->error; } static SocketPeerInfo *sk_namedpipeserver_peer_info(Socket *s) { return NULL; } static bool create_named_pipe(NamedPipeServerSocket *ps, bool 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, NULL, plug, true); } static void named_pipe_accept_loop(NamedPipeServerSocket *ps, bool 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) { NamedPipeServerSocket *ps = (NamedPipeServerSocket *)vps; named_pipe_accept_loop(ps, true); } /* * This socket type is only used for listening, so it should never * be asked to write or set_frozen. */ static const SocketVtable NamedPipeServerSocket_sockvt = { .plug = sk_namedpipeserver_plug, .close = sk_namedpipeserver_close, .socket_error = sk_namedpipeserver_socket_error, .peer_info = sk_namedpipeserver_peer_info, }; Socket *new_named_pipe_listener(const char *pipename, Plug *plug) { NamedPipeServerSocket *ret = snew(NamedPipeServerSocket); ret->sock.vt = &NamedPipeServerSocket_sockvt; ret->plug = plug; ret->error = NULL; ret->psd = NULL; ret->pipename = dupstr(pipename); ret->acl = NULL; ret->callback_handle = NULL; assert(strncmp(pipename, "\\\\.\\pipe\\", 9) == 0); assert(strchr(pipename + 9, '\\') == NULL); if (!make_private_security_descriptor(GENERIC_READ | GENERIC_WRITE, &ret->psd, &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); ret->callback_handle = handle_add_foreign_event(ret->connect_ovl.hEvent, named_pipe_connect_callback, ret); named_pipe_accept_loop(ret, false); cleanup: return &ret->sock; } #endif /* !defined NO_SECURITY */ putty-0.76/windows/winpgen.c0000644000175000017500000022167614072266314013104 00000000000000/* * PuTTY key generation front end (Windows). */ #include #include #include #include #include "putty.h" #include "ssh.h" #include "sshkeygen.h" #include "licence.h" #include "winsecur.h" #include "puttygen-rc.h" #include #ifdef MSVC4 #define ICON_BIG 1 #endif #define WM_DONEKEY (WM_APP + 1) #define DEFAULT_KEY_BITS 2048 #define DEFAULT_ECCURVE_INDEX 0 #define DEFAULT_EDCURVE_INDEX 0 static char *cmdline_keyfile = NULL; /* * Print a modal (Really Bad) message box and perform a fatal exit. */ void modalfatalbox(const 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(const 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); } /* ---------------------------------------------------------------------- * ProgressReceiver implementation. */ #define PROGRESSRANGE 65535 struct progressphase { double startpoint, total; /* For exponential phases */ double exp_probability, exp_current_value; }; struct progress { size_t nphases, phasessize; struct progressphase *phases, *currphase; double scale; HWND progbar; ProgressReceiver rec; }; static ProgressPhase win_progress_add_linear( ProgressReceiver *prog, double overall_cost) { struct progress *p = container_of(prog, struct progress, rec); sgrowarray(p->phases, p->phasessize, p->nphases); int phase = p->nphases++; p->phases[phase].total = overall_cost; ProgressPhase ph = { .n = phase }; return ph; } static ProgressPhase win_progress_add_probabilistic( ProgressReceiver *prog, double cost_per_attempt, double probability) { struct progress *p = container_of(prog, struct progress, rec); sgrowarray(p->phases, p->phasessize, p->nphases); int phase = p->nphases++; p->phases[phase].exp_probability = 1.0 - probability; p->phases[phase].exp_current_value = 1.0; /* Expected number of attempts = 1 / probability of attempt succeeding */ p->phases[phase].total = cost_per_attempt / probability; ProgressPhase ph = { .n = phase }; return ph; } static void win_progress_ready(ProgressReceiver *prog) { struct progress *p = container_of(prog, struct progress, rec); double total = 0; for (int i = 0; i < p->nphases; i++) { p->phases[i].startpoint = total; total += p->phases[i].total; } p->scale = PROGRESSRANGE / total; SendMessage(p->progbar, PBM_SETRANGE, 0, MAKELPARAM(0, PROGRESSRANGE)); } static void win_progress_start_phase(ProgressReceiver *prog, ProgressPhase phase) { struct progress *p = container_of(prog, struct progress, rec); assert(phase.n < p->nphases); p->currphase = &p->phases[phase.n]; } static void win_progress_update(struct progress *p, double phasepos) { double position = (p->currphase->startpoint + p->currphase->total * phasepos); position *= p->scale; if (position < 0) position = 0; if (position > PROGRESSRANGE) position = PROGRESSRANGE; SendMessage(p->progbar, PBM_SETPOS, (WPARAM)position, 0); } static void win_progress_report(ProgressReceiver *prog, double progress) { struct progress *p = container_of(prog, struct progress, rec); win_progress_update(p, progress); } static void win_progress_report_attempt(ProgressReceiver *prog) { struct progress *p = container_of(prog, struct progress, rec); p->currphase->exp_current_value *= p->currphase->exp_probability; win_progress_update(p, 1.0 - p->currphase->exp_current_value); } static void win_progress_report_phase_complete(ProgressReceiver *prog) { struct progress *p = container_of(prog, struct progress, rec); win_progress_update(p, 1.0); } static const ProgressReceiverVtable win_progress_vt = { .add_linear = win_progress_add_linear, .add_probabilistic = win_progress_add_probabilistic, .ready = win_progress_ready, .start_phase = win_progress_start_phase, .report = win_progress_report, .report_attempt = win_progress_report_attempt, .report_phase_complete = win_progress_report_phase_complete, }; static void win_progress_initialise(struct progress *p) { p->nphases = p->phasessize = 0; p->phases = p->currphase = NULL; p->rec.vt = &win_progress_vt; } static void win_progress_cleanup(struct progress *p) { sfree(p->phases); } struct PassphraseProcStruct { char **passphrase; char *comment; }; /* * Dialog-box function for the passphrase box. */ static INT_PTR 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); /* * Centre the window. */ { /* centre the window */ RECT rs, rd; HWND hw; hw = GetDesktopWindow(); if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd)) MoveWindow(hwnd, (rs.right + rs.left + rd.left - rd.right) / 2, (rs.bottom + rs.top + rd.top - rd.bottom) / 2, 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; } return 0; } static void try_get_dlg_item_uint32(HWND hwnd, int id, uint32_t *out) { char buf[128]; if (!GetDlgItemText(hwnd, id, buf, sizeof(buf))) return; if (!*buf) return; char *end; unsigned long val = strtoul(buf, &end, 10); if (*end) return; if ((val >> 16) >> 16) return; *out = val; } static ppk_save_parameters save_params; struct PPKParams { ppk_save_parameters params; uint32_t time_passes, time_ms; }; /* * Dialog-box function for the passphrase box. */ static INT_PTR CALLBACK PPKParamsProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { struct PPKParams *pp; char *buf; if (msg == WM_INITDIALOG) { pp = (struct PPKParams *)lParam; SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pp); } else { pp = (struct PPKParams *)GetWindowLongPtr(hwnd, GWLP_USERDATA); } switch (msg) { case WM_INITDIALOG: SetForegroundWindow(hwnd); SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); if (has_help()) SetWindowLongPtr(hwnd, GWL_EXSTYLE, GetWindowLongPtr(hwnd, GWL_EXSTYLE) | WS_EX_CONTEXTHELP); /* * Centre the window. */ { /* centre the window */ RECT rs, rd; HWND hw; hw = GetDesktopWindow(); if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd)) MoveWindow(hwnd, (rs.right + rs.left + rd.left - rd.right) / 2, (rs.bottom + rs.top + rd.top - rd.bottom) / 2, rd.right - rd.left, rd.bottom - rd.top, true); } CheckRadioButton(hwnd, IDC_PPKVER_2, IDC_PPKVER_3, IDC_PPKVER_2 + (pp->params.fmt_version - 2)); CheckRadioButton( hwnd, IDC_KDF_ARGON2ID, IDC_KDF_ARGON2D, (pp->params.argon2_flavour == Argon2id ? IDC_KDF_ARGON2ID : pp->params.argon2_flavour == Argon2i ? IDC_KDF_ARGON2I : /* pp->params.argon2_flavour == Argon2d ? */ IDC_KDF_ARGON2D)); buf = dupprintf("%"PRIu32, pp->params.argon2_mem); SetDlgItemText(hwnd, IDC_ARGON2_MEM, buf); sfree(buf); if (pp->params.argon2_passes_auto) { CheckRadioButton(hwnd, IDC_PPK_AUTO_YES, IDC_PPK_AUTO_NO, IDC_PPK_AUTO_YES); buf = dupprintf("%"PRIu32, pp->time_ms); SetDlgItemText(hwnd, IDC_ARGON2_TIME, buf); sfree(buf); } else { CheckRadioButton(hwnd, IDC_PPK_AUTO_YES, IDC_PPK_AUTO_NO, IDC_PPK_AUTO_NO); buf = dupprintf("%"PRIu32, pp->time_passes); SetDlgItemText(hwnd, IDC_ARGON2_TIME, buf); sfree(buf); } buf = dupprintf("%"PRIu32, pp->params.argon2_parallelism); SetDlgItemText(hwnd, IDC_ARGON2_PARALLEL, buf); sfree(buf); return 0; case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: EndDialog(hwnd, 1); return 0; case IDCANCEL: EndDialog(hwnd, 0); return 0; case IDC_PPKVER_2: pp->params.fmt_version = 2; return 0; case IDC_PPKVER_3: pp->params.fmt_version = 3; return 0; case IDC_KDF_ARGON2ID: pp->params.argon2_flavour = Argon2id; return 0; case IDC_KDF_ARGON2I: pp->params.argon2_flavour = Argon2i; return 0; case IDC_KDF_ARGON2D: pp->params.argon2_flavour = Argon2d; return 0; case IDC_ARGON2_MEM: try_get_dlg_item_uint32(hwnd, IDC_ARGON2_MEM, &pp->params.argon2_mem); return 0; case IDC_PPK_AUTO_YES: pp->params.argon2_passes_auto = true; buf = dupprintf("%"PRIu32, pp->time_ms); SetDlgItemText(hwnd, IDC_ARGON2_TIME, buf); sfree(buf); return 0; case IDC_PPK_AUTO_NO: pp->params.argon2_passes_auto = false; buf = dupprintf("%"PRIu32, pp->time_passes); SetDlgItemText(hwnd, IDC_ARGON2_TIME, buf); sfree(buf); return 0; case IDC_ARGON2_TIME: try_get_dlg_item_uint32(hwnd, IDC_ARGON2_TIME, pp->params.argon2_passes_auto ? &pp->time_ms : &pp->time_passes); return 0; case IDC_ARGON2_PARALLEL: try_get_dlg_item_uint32(hwnd, IDC_ARGON2_PARALLEL, &pp->params.argon2_parallelism); return 0; } return 0; case WM_HELP: { int id = ((LPHELPINFO)lParam)->iCtrlId; const char *topic = NULL; switch (id) { case IDC_PPKVER_STATIC: case IDC_PPKVER_2: case IDC_PPKVER_3: topic = WINHELP_CTX_puttygen_ppkver; break; case IDC_KDF_STATIC: case IDC_KDF_ARGON2ID: case IDC_KDF_ARGON2I: case IDC_KDF_ARGON2D: case IDC_ARGON2_MEM_STATIC: case IDC_ARGON2_MEM: case IDC_ARGON2_MEM_STATIC2: case IDC_ARGON2_TIME_STATIC: case IDC_ARGON2_TIME: case IDC_PPK_AUTO_YES: case IDC_PPK_AUTO_NO: case IDC_ARGON2_PARALLEL_STATIC: case IDC_ARGON2_PARALLEL: topic = WINHELP_CTX_puttygen_kdfparam; break; } if (topic) { launch_help(hwnd, topic); } else { MessageBeep(0); } break; } case WM_CLOSE: EndDialog(hwnd, 0); return 0; } return 0; } /* * Prompt for a key file. Assumes the filename buffer is of size * FILENAME_MAX. */ static bool prompt_keyfile(HWND hwnd, char *dlgtitle, char *filename, bool save, bool ppk) { OPENFILENAME of; memset(&of, 0, sizeof(of)); of.hwndOwner = hwnd; if (ppk) { of.lpstrFilter = "PuTTY Private Key Files (*.ppk)\0*.ppk\0" "All Files (*.*)\0*\0\0\0"; of.lpstrDefExt = ".ppk"; } else { of.lpstrFilter = "All Files (*.*)\0*\0\0\0"; } of.lpstrCustomFilter = NULL; of.nFilterIndex = 1; of.lpstrFile = filename; *filename = '\0'; of.nMaxFile = FILENAME_MAX; of.lpstrFileTitle = NULL; of.lpstrTitle = dlgtitle; of.Flags = 0; return request_file(NULL, &of, false, save); } /* * Dialog-box function for the Licence box. */ static INT_PTR CALLBACK LicenceProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_INITDIALOG: { /* * Centre the window. */ RECT rs, rd; HWND hw; hw = GetDesktopWindow(); if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd)) MoveWindow(hwnd, (rs.right + rs.left + rd.left - rd.right) / 2, (rs.bottom + rs.top + rd.top - rd.bottom) / 2, rd.right - rd.left, rd.bottom - rd.top, true); SetDlgItemText(hwnd, 1000, LICENCE_TEXT("\r\n\r\n")); return 1; } case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: case IDCANCEL: EndDialog(hwnd, 1); return 0; } return 0; case WM_CLOSE: EndDialog(hwnd, 1); return 0; } return 0; } /* * Dialog-box function for the About box. */ static INT_PTR CALLBACK AboutProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_INITDIALOG: /* * Centre the window. */ { /* centre the window */ RECT rs, rd; HWND hw; hw = GetDesktopWindow(); if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd)) MoveWindow(hwnd, (rs.right + rs.left + rd.left - rd.right) / 2, (rs.bottom + rs.top + rd.top - rd.bottom) / 2, rd.right - rd.left, rd.bottom - rd.top, true); } { char *buildinfo_text = buildinfo("\r\n"); char *text = dupprintf ("PuTTYgen\r\n\r\n%s\r\n\r\n%s\r\n\r\n%s", ver, buildinfo_text, "\251 " SHORT_COPYRIGHT_DETAILS ". All rights reserved."); sfree(buildinfo_text); SetDlgItemText(hwnd, 1000, text); MakeDlgItemBorderless(hwnd, 1000); sfree(text); } return 1; case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: case IDCANCEL: EndDialog(hwnd, 1); return 0; case 101: EnableWindow(hwnd, 0); DialogBox(hinst, MAKEINTRESOURCE(214), hwnd, LicenceProc); EnableWindow(hwnd, 1); SetActiveWindow(hwnd); return 0; case 102: /* Load web browser */ ShellExecute(hwnd, "open", "https://www.chiark.greenend.org.uk/~sgtatham/putty/", 0, 0, SW_SHOWDEFAULT); return 0; } return 0; case WM_CLOSE: EndDialog(hwnd, 1); return 0; } return 0; } typedef enum {RSA, DSA, ECDSA, EDDSA} keytype; /* * Thread to generate a key. */ struct rsa_key_thread_params { HWND progressbar; /* notify this with progress */ HWND dialog; /* notify this on completion */ int key_bits; /* bits in key modulus (RSA, DSA) */ int curve_bits; /* bits in elliptic curve (ECDSA) */ keytype keytype; const PrimeGenerationPolicy *primepolicy; bool rsa_strong; union { RSAKey *key; struct dss_key *dsskey; struct ecdsa_key *eckey; struct eddsa_key *edkey; }; }; static DWORD WINAPI generate_key_thread(void *param) { struct rsa_key_thread_params *params = (struct rsa_key_thread_params *) param; struct progress prog; prog.progbar = params->progressbar; win_progress_initialise(&prog); PrimeGenerationContext *pgc = primegen_new_context(params->primepolicy); if (params->keytype == DSA) dsa_generate(params->dsskey, params->key_bits, pgc, &prog.rec); else if (params->keytype == ECDSA) ecdsa_generate(params->eckey, params->curve_bits); else if (params->keytype == EDDSA) eddsa_generate(params->edkey, params->curve_bits); else rsa_generate(params->key, params->key_bits, params->rsa_strong, pgc, &prog.rec); primegen_free_context(pgc); PostMessage(params->dialog, WM_DONEKEY, 0, 0); win_progress_cleanup(&prog); sfree(params); return 0; } struct MainDlgState { bool collecting_entropy; bool generation_thread_exists; bool key_exists; int entropy_got, entropy_required, entropy_size; int key_bits, curve_bits; bool ssh2; keytype keytype; const PrimeGenerationPolicy *primepolicy; bool rsa_strong; FingerprintType fptype; char **commentptr; /* points to key.comment or ssh2key.comment */ ssh2_userkey ssh2key; unsigned *entropy; union { RSAKey key; struct dss_key dsskey; struct ecdsa_key eckey; struct eddsa_key edkey; }; HMENU filemenu, keymenu, cvtmenu; }; static void hidemany(HWND hwnd, const int *ids, bool hideit) { while (*ids) { ShowWindow(GetDlgItem(hwnd, *ids++), (hideit ? SW_HIDE : SW_SHOW)); } } static void setupbigedit1(HWND hwnd, int id, int idstatic, RSAKey *key) { char *buffer = ssh1_pubkey_str(key); SetDlgItemText(hwnd, id, buffer); SetDlgItemText(hwnd, idstatic, "&Public key for pasting into authorized_keys file:"); sfree(buffer); } static void setupbigedit2(HWND hwnd, int id, int idstatic, ssh2_userkey *key) { char *buffer = ssh2_pubkey_openssh_str(key); SetDlgItemText(hwnd, id, buffer); SetDlgItemText(hwnd, idstatic, "&Public key for pasting into " "OpenSSH authorized_keys file:"); sfree(buffer); } /* * Warn about the obsolescent key file format. */ void old_keyfile_warning(void) { static const char mbtitle[] = "PuTTY Key File Warning"; static const char message[] = "You are loading an SSH-2 private key which has an\n" "old version of the file format. This means your key\n" "file is not fully tamperproof. Future versions of\n" "PuTTY may stop supporting this private key format,\n" "so we recommend you convert your key to the new\n" "format.\n" "\n" "Once the key is loaded into PuTTYgen, you can perform\n" "this conversion simply by saving it again."; MessageBox(NULL, message, mbtitle, MB_OK); } enum { controlidstart = 100, IDC_QUIT, IDC_TITLE, IDC_BOX_KEY, IDC_NOKEY, IDC_GENERATING, IDC_PROGRESS, IDC_PKSTATIC, IDC_KEYDISPLAY, IDC_FPSTATIC, IDC_FINGERPRINT, IDC_COMMENTSTATIC, IDC_COMMENTEDIT, IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT, IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, IDC_BOX_ACTIONS, IDC_GENSTATIC, IDC_GENERATE, IDC_LOADSTATIC, IDC_LOAD, IDC_SAVESTATIC, IDC_SAVE, IDC_SAVEPUB, IDC_BOX_PARAMS, IDC_TYPESTATIC, IDC_KEYSSH1, IDC_KEYSSH2RSA, IDC_KEYSSH2DSA, IDC_KEYSSH2ECDSA, IDC_KEYSSH2EDDSA, IDC_PRIMEGEN_PROB, IDC_PRIMEGEN_MAURER_SIMPLE, IDC_PRIMEGEN_MAURER_COMPLEX, IDC_RSA_STRONG, IDC_FPTYPE_SHA256, IDC_FPTYPE_MD5, IDC_PPK_PARAMS, IDC_BITSSTATIC, IDC_BITS, IDC_ECCURVESTATIC, IDC_ECCURVE, IDC_EDCURVESTATIC, IDC_EDCURVE, IDC_NOTHINGSTATIC, IDC_ABOUT, IDC_GIVEHELP, IDC_IMPORT, IDC_EXPORT_OPENSSH_AUTO, IDC_EXPORT_OPENSSH_NEW, IDC_EXPORT_SSHCOM }; static const int nokey_ids[] = { IDC_NOKEY, 0 }; static const int generating_ids[] = { IDC_GENERATING, IDC_PROGRESS, 0 }; static const int gotkey_ids[] = { IDC_PKSTATIC, IDC_KEYDISPLAY, IDC_FPSTATIC, IDC_FINGERPRINT, IDC_COMMENTSTATIC, IDC_COMMENTEDIT, IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT, IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 0 }; /* * Small UI helper function to switch the state of the main dialog * by enabling and disabling controls and menu items. */ void ui_set_state(HWND hwnd, struct MainDlgState *state, int status) { int type; switch (status) { case 0: /* no key */ hidemany(hwnd, nokey_ids, false); hidemany(hwnd, generating_ids, true); hidemany(hwnd, gotkey_ids, true); EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1); EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1); EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0); EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0); EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1); EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1); EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1); EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ECDSA), 1); EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2EDDSA), 1); EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1); EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND); EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND); EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_GRAYED|MF_BYCOMMAND); EnableMenuItem(state->keymenu, IDC_GENERATE, MF_ENABLED|MF_BYCOMMAND); EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND); EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_ENABLED|MF_BYCOMMAND); EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_ENABLED|MF_BYCOMMAND); EnableMenuItem(state->keymenu, IDC_KEYSSH2ECDSA, MF_ENABLED|MF_BYCOMMAND); EnableMenuItem(state->keymenu, IDC_KEYSSH2EDDSA, MF_ENABLED|MF_BYCOMMAND); EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND); EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH_AUTO, MF_GRAYED|MF_BYCOMMAND); EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH_NEW, MF_GRAYED|MF_BYCOMMAND); EnableMenuItem(state->cvtmenu, IDC_EXPORT_SSHCOM, MF_GRAYED|MF_BYCOMMAND); break; case 1: /* generating key */ hidemany(hwnd, nokey_ids, true); hidemany(hwnd, generating_ids, false); hidemany(hwnd, gotkey_ids, true); EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 0); EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 0); EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 0); EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 0); EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 0); EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 0); EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 0); EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ECDSA), 0); EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2EDDSA), 0); EnableWindow(GetDlgItem(hwnd, IDC_BITS), 0); EnableMenuItem(state->filemenu, IDC_LOAD, MF_GRAYED|MF_BYCOMMAND); EnableMenuItem(state->filemenu, IDC_SAVE, MF_GRAYED|MF_BYCOMMAND); EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_GRAYED|MF_BYCOMMAND); EnableMenuItem(state->keymenu, IDC_GENERATE, MF_GRAYED|MF_BYCOMMAND); EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_GRAYED|MF_BYCOMMAND); EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA, MF_GRAYED|MF_BYCOMMAND); EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA, MF_GRAYED|MF_BYCOMMAND); EnableMenuItem(state->keymenu, IDC_KEYSSH2ECDSA, MF_GRAYED|MF_BYCOMMAND); EnableMenuItem(state->keymenu, IDC_KEYSSH2EDDSA, MF_GRAYED|MF_BYCOMMAND); EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_GRAYED|MF_BYCOMMAND); EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH_AUTO, MF_GRAYED|MF_BYCOMMAND); EnableMenuItem(state->cvtmenu, IDC_EXPORT_OPENSSH_NEW, MF_GRAYED|MF_BYCOMMAND); EnableMenuItem(state->cvtmenu, IDC_EXPORT_SSHCOM, MF_GRAYED|MF_BYCOMMAND); break; case 2: hidemany(hwnd, nokey_ids, true); hidemany(hwnd, generating_ids, true); hidemany(hwnd, gotkey_ids, false); EnableWindow(GetDlgItem(hwnd, IDC_GENERATE), 1); EnableWindow(GetDlgItem(hwnd, IDC_LOAD), 1); EnableWindow(GetDlgItem(hwnd, IDC_SAVE), 1); EnableWindow(GetDlgItem(hwnd, IDC_SAVEPUB), 1); EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH1), 1); EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2RSA), 1); EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2DSA), 1); EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2ECDSA), 1); EnableWindow(GetDlgItem(hwnd, IDC_KEYSSH2EDDSA), 1); EnableWindow(GetDlgItem(hwnd, IDC_BITS), 1); EnableMenuItem(state->filemenu, IDC_LOAD, MF_ENABLED|MF_BYCOMMAND); EnableMenuItem(state->filemenu, IDC_SAVE, MF_ENABLED|MF_BYCOMMAND); EnableMenuItem(state->filemenu, IDC_SAVEPUB, MF_ENABLED|MF_BYCOMMAND); EnableMenuItem(state->keymenu, IDC_GENERATE, MF_ENABLED|MF_BYCOMMAND); EnableMenuItem(state->keymenu, IDC_KEYSSH1, MF_ENABLED|MF_BYCOMMAND); EnableMenuItem(state->keymenu, IDC_KEYSSH2RSA,MF_ENABLED|MF_BYCOMMAND); EnableMenuItem(state->keymenu, IDC_KEYSSH2DSA,MF_ENABLED|MF_BYCOMMAND); EnableMenuItem(state->keymenu, IDC_KEYSSH2ECDSA, MF_ENABLED|MF_BYCOMMAND); EnableMenuItem(state->keymenu, IDC_KEYSSH2EDDSA, MF_ENABLED|MF_BYCOMMAND); EnableMenuItem(state->cvtmenu, IDC_IMPORT, MF_ENABLED|MF_BYCOMMAND); /* * Enable export menu items if and only if the key type * supports this kind of export. */ type = state->ssh2 ? SSH_KEYTYPE_SSH2 : SSH_KEYTYPE_SSH1; #define do_export_menuitem(x,y) \ EnableMenuItem(state->cvtmenu, x, MF_BYCOMMAND | \ (import_target_type(y)==type?MF_ENABLED:MF_GRAYED)) do_export_menuitem(IDC_EXPORT_OPENSSH_AUTO, SSH_KEYTYPE_OPENSSH_AUTO); do_export_menuitem(IDC_EXPORT_OPENSSH_NEW, SSH_KEYTYPE_OPENSSH_NEW); do_export_menuitem(IDC_EXPORT_SSHCOM, SSH_KEYTYPE_SSHCOM); #undef do_export_menuitem break; } } /* * Helper functions to set the key type, taking care of keeping the * menu and radio button selections in sync and also showing/hiding * the appropriate size/curve control for the current key type. */ void ui_update_key_type_ctrls(HWND hwnd) { enum { BITS, ECCURVE, EDCURVE, NOTHING } which; static const int bits_ids[] = { IDC_BITSSTATIC, IDC_BITS, 0 }; static const int eccurve_ids[] = { IDC_ECCURVESTATIC, IDC_ECCURVE, 0 }; static const int edcurve_ids[] = { IDC_EDCURVESTATIC, IDC_EDCURVE, 0 }; static const int nothing_ids[] = { IDC_NOTHINGSTATIC, 0 }; if (IsDlgButtonChecked(hwnd, IDC_KEYSSH1) || IsDlgButtonChecked(hwnd, IDC_KEYSSH2RSA) || IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA)) { which = BITS; } else if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2ECDSA)) { which = ECCURVE; } else if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2EDDSA)) { which = EDCURVE; } else { /* Currently not used since Ed25519 stopped being the only * thing in its class, but I'll keep it here in case it comes * in useful again */ which = NOTHING; } hidemany(hwnd, bits_ids, which != BITS); hidemany(hwnd, eccurve_ids, which != ECCURVE); hidemany(hwnd, edcurve_ids, which != EDCURVE); hidemany(hwnd, nothing_ids, which != NOTHING); } void ui_set_key_type(HWND hwnd, struct MainDlgState *state, int button) { CheckRadioButton(hwnd, IDC_KEYSSH1, IDC_KEYSSH2EDDSA, button); CheckMenuRadioItem(state->keymenu, IDC_KEYSSH1, IDC_KEYSSH2EDDSA, button, MF_BYCOMMAND); ui_update_key_type_ctrls(hwnd); } void ui_set_primepolicy(HWND hwnd, struct MainDlgState *state, int option) { CheckMenuRadioItem(state->keymenu, IDC_PRIMEGEN_PROB, IDC_PRIMEGEN_MAURER_COMPLEX, option, MF_BYCOMMAND); switch (option) { case IDC_PRIMEGEN_PROB: state->primepolicy = &primegen_probabilistic; break; case IDC_PRIMEGEN_MAURER_SIMPLE: state->primepolicy = &primegen_provable_maurer_simple; break; case IDC_PRIMEGEN_MAURER_COMPLEX: state->primepolicy = &primegen_provable_maurer_complex; break; } } void ui_set_rsa_strong(HWND hwnd, struct MainDlgState *state, bool enable) { state->rsa_strong = enable; CheckMenuItem(state->keymenu, IDC_RSA_STRONG, (enable ? MF_CHECKED : 0) | MF_BYCOMMAND); } static FingerprintType idc_to_fptype(int option) { switch (option) { case IDC_FPTYPE_SHA256: return SSH_FPTYPE_SHA256; case IDC_FPTYPE_MD5: return SSH_FPTYPE_MD5; default: unreachable("bad control id in idc_to_fptype"); } } static int fptype_to_idc(FingerprintType fptype) { switch (fptype) { case SSH_FPTYPE_SHA256: return IDC_FPTYPE_SHA256; case SSH_FPTYPE_MD5: return IDC_FPTYPE_MD5; default: unreachable("bad fptype in fptype_to_idc"); } } void ui_set_fptype(HWND hwnd, struct MainDlgState *state, int option) { CheckMenuRadioItem(state->keymenu, IDC_FPTYPE_SHA256, IDC_FPTYPE_MD5, option, MF_BYCOMMAND); state->fptype = idc_to_fptype(option); if (state->key_exists && state->ssh2) { char *fp = ssh2_fingerprint(state->ssh2key.key, state->fptype); SetDlgItemText(hwnd, IDC_FINGERPRINT, fp); sfree(fp); } } void load_key_file(HWND hwnd, struct MainDlgState *state, Filename *filename, bool was_import_cmd) { char *passphrase; bool needs_pass; int type, realtype; int ret; const char *errmsg = NULL; char *comment; RSAKey newkey1; 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(hwnd, 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 = rsa1_encrypted_f(filename, &comment); else if (realtype == SSH_KEYTYPE_SSH2) needs_pass = ppk_encrypted_f(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 = rsa1_load_f(filename, &newkey1, passphrase, &errmsg); else ret = import_ssh1(filename, realtype, &newkey1, passphrase, &errmsg); } else { if (realtype == type) newkey2 = ppk_load_f(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; } } while (ret == -1); if (comment) sfree(comment); if (ret == 0) { char *msg = dupprintf("Couldn't load private key (%s)", errmsg); message_box(hwnd, msg, "PuTTYgen Error", MB_OK | MB_ICONERROR, HELPCTXID(errors_cantloadkey)); sfree(msg); } else if (ret == 1) { /* * Now update the key controls with all the * key data. */ { SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT, passphrase); SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT, passphrase); if (type == SSH_KEYTYPE_SSH1) { char *fingerprint, *savecomment; state->ssh2 = false; state->commentptr = &state->key.comment; state->key = newkey1; /* * Set the key fingerprint. */ savecomment = state->key.comment; state->key.comment = NULL; fingerprint = rsa_ssh1_fingerprint(&state->key); state->key.comment = savecomment; SetDlgItemText(hwnd, IDC_FINGERPRINT, fingerprint); sfree(fingerprint); /* * Construct a decimal representation * of the key, for pasting into * .ssh/authorized_keys on a Unix box. */ setupbigedit1(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC, &state->key); } else { char *fp; char *savecomment; state->ssh2 = true; state->commentptr = &state->ssh2key.comment; state->ssh2key = *newkey2; /* structure copy */ sfree(newkey2); savecomment = state->ssh2key.comment; state->ssh2key.comment = NULL; fp = ssh2_fingerprint(state->ssh2key.key, state->fptype); state->ssh2key.comment = savecomment; SetDlgItemText(hwnd, IDC_FINGERPRINT, fp); sfree(fp); setupbigedit2(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC, &state->ssh2key); } SetDlgItemText(hwnd, IDC_COMMENTEDIT, *state->commentptr); } /* * Finally, hide the progress bar and show * the key data. */ ui_set_state(hwnd, state, 2); state->key_exists = true; /* * If the user has imported a foreign key * using the Load command, let them know. * If they've used the Import command, be * silent. */ if (realtype != type && !was_import_cmd) { char msg[512]; sprintf(msg, "Successfully imported foreign key\n" "(%s).\n" "To use this key with PuTTY, you need to\n" "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); } static void start_generating_key(HWND hwnd, struct MainDlgState *state) { static const char generating_msg[] = "Please wait while a key is generated..."; struct rsa_key_thread_params *params; DWORD threadid; SetDlgItemText(hwnd, IDC_GENERATING, generating_msg); SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0, MAKELPARAM(0, PROGRESSRANGE)); SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0); params = snew(struct rsa_key_thread_params); params->progressbar = GetDlgItem(hwnd, IDC_PROGRESS); params->dialog = hwnd; params->key_bits = state->key_bits; params->curve_bits = state->curve_bits; params->keytype = state->keytype; params->primepolicy = state->primepolicy; params->rsa_strong = state->rsa_strong; params->key = &state->key; params->dsskey = &state->dsskey; HANDLE hThread = CreateThread(NULL, 0, generate_key_thread, params, 0, &threadid); if (!hThread) { MessageBox(hwnd, "Out of thread resources", "Key generation error", MB_OK | MB_ICONERROR); sfree(params); } else { CloseHandle(hThread); /* we don't need the thread handle */ state->generation_thread_exists = true; } } /* * Dialog-box function for the main PuTTYgen dialog box. */ static INT_PTR CALLBACK MainDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { static const char entropy_msg[] = "Please generate some randomness by moving the mouse over the blank area."; struct MainDlgState *state; switch (msg) { case WM_INITDIALOG: if (has_help()) SetWindowLongPtr(hwnd, GWL_EXSTYLE, GetWindowLongPtr(hwnd, GWL_EXSTYLE) | WS_EX_CONTEXTHELP); else { /* * If we add a Help button, this is where we destroy it * if the help file isn't present. */ } SendMessage(hwnd, WM_SETICON, (WPARAM) ICON_BIG, (LPARAM) LoadIcon(hinst, MAKEINTRESOURCE(200))); state = snew(struct MainDlgState); state->generation_thread_exists = false; state->collecting_entropy = false; state->entropy = NULL; state->key_exists = false; SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) state); { HMENU menu, menu1; menu = CreateMenu(); menu1 = CreateMenu(); AppendMenu(menu1, MF_ENABLED, IDC_LOAD, "&Load private key"); AppendMenu(menu1, MF_ENABLED, IDC_SAVEPUB, "Save p&ublic key"); AppendMenu(menu1, MF_ENABLED, IDC_SAVE, "&Save private key"); AppendMenu(menu1, MF_SEPARATOR, 0, 0); AppendMenu(menu1, MF_ENABLED, IDC_QUIT, "E&xit"); AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT_PTR) menu1, "&File"); state->filemenu = menu1; menu1 = CreateMenu(); AppendMenu(menu1, MF_ENABLED, IDC_GENERATE, "&Generate key pair"); AppendMenu(menu1, MF_SEPARATOR, 0, 0); AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH1, "SSH-&1 key (RSA)"); AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2RSA, "SSH-2 &RSA key"); AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2DSA, "SSH-2 &DSA key"); AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2ECDSA, "SSH-2 &ECDSA key"); AppendMenu(menu1, MF_ENABLED, IDC_KEYSSH2EDDSA, "SSH-2 EdD&SA key"); AppendMenu(menu1, MF_SEPARATOR, 0, 0); AppendMenu(menu1, MF_ENABLED, IDC_PRIMEGEN_PROB, "Use probable primes (fast)"); AppendMenu(menu1, MF_ENABLED, IDC_PRIMEGEN_MAURER_SIMPLE, "Use proven primes (slower)"); AppendMenu(menu1, MF_ENABLED, IDC_PRIMEGEN_MAURER_COMPLEX, "Use proven primes with even distribution (slowest)"); AppendMenu(menu1, MF_SEPARATOR, 0, 0); AppendMenu(menu1, MF_ENABLED, IDC_RSA_STRONG, "Use \"strong\" primes as RSA key factors"); AppendMenu(menu1, MF_SEPARATOR, 0, 0); AppendMenu(menu1, MF_ENABLED, IDC_PPK_PARAMS, "Parameters for saving key files..."); AppendMenu(menu1, MF_SEPARATOR, 0, 0); AppendMenu(menu1, MF_ENABLED, IDC_FPTYPE_SHA256, "Show fingerprint as SHA256"); AppendMenu(menu1, MF_ENABLED, IDC_FPTYPE_MD5, "Show fingerprint as MD5"); AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT_PTR) menu1, "&Key"); state->keymenu = menu1; menu1 = CreateMenu(); AppendMenu(menu1, MF_ENABLED, IDC_IMPORT, "&Import key"); AppendMenu(menu1, MF_SEPARATOR, 0, 0); AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_OPENSSH_AUTO, "Export &OpenSSH key"); AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_OPENSSH_NEW, "Export &OpenSSH key (force new file format)"); AppendMenu(menu1, MF_ENABLED, IDC_EXPORT_SSHCOM, "Export &ssh.com key"); AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT_PTR) menu1, "Con&versions"); state->cvtmenu = menu1; menu1 = CreateMenu(); AppendMenu(menu1, MF_ENABLED, IDC_ABOUT, "&About"); if (has_help()) AppendMenu(menu1, MF_ENABLED, IDC_GIVEHELP, "&Help"); AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT_PTR) menu1, "&Help"); SetMenu(hwnd, menu); } /* * Centre the window. */ { /* centre the window */ RECT rs, rd; HWND hw; hw = GetDesktopWindow(); if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd)) MoveWindow(hwnd, (rs.right + rs.left + rd.left - rd.right) / 2, (rs.bottom + rs.top + rd.top - rd.bottom) / 2, rd.right - rd.left, rd.bottom - rd.top, true); } { struct ctlpos cp, cp2; int ymax; /* Accelerators used: acglops1rbvde */ ctlposinit(&cp, hwnd, 4, 4, 4); beginbox(&cp, "Key", IDC_BOX_KEY); cp2 = cp; statictext(&cp2, "No key.", 1, IDC_NOKEY); cp2 = cp; statictext(&cp2, "", 1, IDC_GENERATING); progressbar(&cp2, IDC_PROGRESS); bigeditctrl(&cp, "&Public key for pasting into authorized_keys file:", IDC_PKSTATIC, IDC_KEYDISPLAY, 5); SendDlgItemMessage(hwnd, IDC_KEYDISPLAY, EM_SETREADONLY, 1, 0); staticedit(&cp, "Key f&ingerprint:", IDC_FPSTATIC, IDC_FINGERPRINT, 82); SendDlgItemMessage(hwnd, IDC_FINGERPRINT, EM_SETREADONLY, 1, 0); staticedit(&cp, "Key &comment:", IDC_COMMENTSTATIC, IDC_COMMENTEDIT, 82); staticpassedit(&cp, "Key p&assphrase:", IDC_PASSPHRASE1STATIC, IDC_PASSPHRASE1EDIT, 82); staticpassedit(&cp, "C&onfirm passphrase:", IDC_PASSPHRASE2STATIC, IDC_PASSPHRASE2EDIT, 82); endbox(&cp); beginbox(&cp, "Actions", IDC_BOX_ACTIONS); staticbtn(&cp, "Generate a public/private key pair", IDC_GENSTATIC, "&Generate", IDC_GENERATE); staticbtn(&cp, "Load an existing private key file", IDC_LOADSTATIC, "&Load", IDC_LOAD); static2btn(&cp, "Save the generated key", IDC_SAVESTATIC, "Save p&ublic key", IDC_SAVEPUB, "&Save private key", IDC_SAVE); endbox(&cp); beginbox(&cp, "Parameters", IDC_BOX_PARAMS); radioline(&cp, "Type of key to generate:", IDC_TYPESTATIC, 5, "&RSA", IDC_KEYSSH2RSA, "&DSA", IDC_KEYSSH2DSA, "&ECDSA", IDC_KEYSSH2ECDSA, "EdD&SA", IDC_KEYSSH2EDDSA, "SSH-&1 (RSA)", IDC_KEYSSH1, NULL); cp2 = cp; staticedit(&cp2, "Number of &bits in a generated key:", IDC_BITSSTATIC, IDC_BITS, 20); ymax = cp2.ypos; cp2 = cp; staticddl(&cp2, "Cur&ve to use for generating this key:", IDC_ECCURVESTATIC, IDC_ECCURVE, 30); SendDlgItemMessage(hwnd, IDC_ECCURVE, CB_RESETCONTENT, 0, 0); { int i, bits; const struct ec_curve *curve; const ssh_keyalg *alg; for (i = 0; i < n_ec_nist_curve_lengths; i++) { bits = ec_nist_curve_lengths[i]; ec_nist_alg_and_curve_by_bits(bits, &curve, &alg); SendDlgItemMessage(hwnd, IDC_ECCURVE, CB_ADDSTRING, 0, (LPARAM)curve->textname); } } ymax = ymax > cp2.ypos ? ymax : cp2.ypos; cp2 = cp; staticddl(&cp2, "Cur&ve to use for generating this key:", IDC_EDCURVESTATIC, IDC_EDCURVE, 30); SendDlgItemMessage(hwnd, IDC_EDCURVE, CB_RESETCONTENT, 0, 0); { int i, bits; const struct ec_curve *curve; const ssh_keyalg *alg; for (i = 0; i < n_ec_ed_curve_lengths; i++) { bits = ec_ed_curve_lengths[i]; ec_ed_alg_and_curve_by_bits(bits, &curve, &alg); char *desc = dupprintf("%s (%d bits)", curve->textname, bits); SendDlgItemMessage(hwnd, IDC_EDCURVE, CB_ADDSTRING, 0, (LPARAM)desc); sfree(desc); } } ymax = ymax > cp2.ypos ? ymax : cp2.ypos; cp2 = cp; statictext(&cp2, "(nothing to configure for this key type)", 1, IDC_NOTHINGSTATIC); ymax = ymax > cp2.ypos ? ymax : cp2.ypos; cp.ypos = ymax; endbox(&cp); } ui_set_key_type(hwnd, state, IDC_KEYSSH2RSA); ui_set_primepolicy(hwnd, state, IDC_PRIMEGEN_PROB); ui_set_rsa_strong(hwnd, state, false); ui_set_fptype(hwnd, state, fptype_to_idc(SSH_FPTYPE_DEFAULT)); SetDlgItemInt(hwnd, IDC_BITS, DEFAULT_KEY_BITS, false); SendDlgItemMessage(hwnd, IDC_ECCURVE, CB_SETCURSEL, DEFAULT_ECCURVE_INDEX, 0); SendDlgItemMessage(hwnd, IDC_EDCURVE, CB_SETCURSEL, DEFAULT_EDCURVE_INDEX, 0); /* * Initially, hide the progress bar and the key display, * and show the no-key display. Also disable the Save * buttons, because with no key we obviously can't save * 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, false); 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) { /* * Seed the entropy pool */ random_reseed( make_ptrlen(state->entropy, state->entropy_size)); smemclr(state->entropy, state->entropy_size); sfree(state->entropy); state->collecting_entropy = false; start_generating_key(hwnd, state); } } break; case WM_COMMAND: switch (LOWORD(wParam)) { case IDC_KEYSSH1: case IDC_KEYSSH2RSA: case IDC_KEYSSH2DSA: case IDC_KEYSSH2ECDSA: case IDC_KEYSSH2EDDSA: { state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA); ui_set_key_type(hwnd, state, LOWORD(wParam)); break; } case IDC_PRIMEGEN_PROB: case IDC_PRIMEGEN_MAURER_SIMPLE: case IDC_PRIMEGEN_MAURER_COMPLEX: { state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA); ui_set_primepolicy(hwnd, state, LOWORD(wParam)); break; } case IDC_FPTYPE_SHA256: case IDC_FPTYPE_MD5: { state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA); ui_set_fptype(hwnd, state, LOWORD(wParam)); break; } case IDC_RSA_STRONG: { state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA); ui_set_rsa_strong(hwnd, state, !state->rsa_strong); break; } case IDC_PPK_PARAMS: { struct PPKParams pp[1]; pp->params = save_params; if (pp->params.argon2_passes_auto) { pp->time_ms = pp->params.argon2_milliseconds; pp->time_passes = 13; } else { pp->time_ms = 100; pp->time_passes = pp->params.argon2_passes; } int dlgret = DialogBoxParam(hinst, MAKEINTRESOURCE(215), NULL, PPKParamsProc, (LPARAM)pp); if (dlgret) { if (pp->params.argon2_passes_auto) { pp->params.argon2_milliseconds = pp->time_ms; } else { pp->params.argon2_passes = pp->time_passes; } save_params = pp->params; } break; } case IDC_QUIT: PostMessage(hwnd, WM_CLOSE, 0, 0); break; case IDC_COMMENTEDIT: if (HIWORD(wParam) == EN_CHANGE) { state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA); if (state->key_exists) { HWND editctl = GetDlgItem(hwnd, IDC_COMMENTEDIT); int len = GetWindowTextLength(editctl); if (*state->commentptr) sfree(*state->commentptr); *state->commentptr = snewn(len + 1, char); GetWindowText(editctl, *state->commentptr, len + 1); if (state->ssh2) { setupbigedit2(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC, &state->ssh2key); } else { setupbigedit1(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC, &state->key); } } } break; case IDC_ABOUT: EnableWindow(hwnd, 0); DialogBox(hinst, MAKEINTRESOURCE(213), hwnd, AboutProc); EnableWindow(hwnd, 1); SetActiveWindow(hwnd); return 0; case IDC_GIVEHELP: if (HIWORD(wParam) == BN_CLICKED || HIWORD(wParam) == BN_DOUBLECLICKED) { launch_help(hwnd, WINHELP_CTX_puttygen_general); } return 0; case IDC_GENERATE: if (HIWORD(wParam) != BN_CLICKED && HIWORD(wParam) != BN_DOUBLECLICKED) break; state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA); if (!state->generation_thread_exists) { unsigned raw_entropy_required; unsigned char *raw_entropy_buf; BOOL ok; state->key_bits = GetDlgItemInt(hwnd, IDC_BITS, &ok, false); if (!ok) state->key_bits = DEFAULT_KEY_BITS; state->ssh2 = true; if (IsDlgButtonChecked(hwnd, IDC_KEYSSH1)) { state->ssh2 = false; state->keytype = RSA; } else if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2RSA)) { state->keytype = RSA; } else if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2DSA)) { state->keytype = DSA; } else if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2ECDSA)) { state->keytype = ECDSA; int curveindex = SendDlgItemMessage(hwnd, IDC_ECCURVE, CB_GETCURSEL, 0, 0); assert(curveindex >= 0); assert(curveindex < n_ec_nist_curve_lengths); state->curve_bits = ec_nist_curve_lengths[curveindex]; } else if (IsDlgButtonChecked(hwnd, IDC_KEYSSH2EDDSA)) { state->keytype = EDDSA; int curveindex = SendDlgItemMessage(hwnd, IDC_EDCURVE, CB_GETCURSEL, 0, 0); assert(curveindex >= 0); assert(curveindex < n_ec_ed_curve_lengths); state->curve_bits = ec_ed_curve_lengths[curveindex]; } else { /* Somehow, no button was checked */ break; } if ((state->keytype == RSA || state->keytype == DSA) && state->key_bits < 256) { char *message = dupprintf ("PuTTYgen will not generate a key smaller than 256" " bits.\nKey length reset to default %d. Continue?", DEFAULT_KEY_BITS); int ret = MessageBox(hwnd, message, "PuTTYgen Warning", MB_ICONWARNING | MB_OKCANCEL); sfree(message); if (ret != IDOK) break; state->key_bits = DEFAULT_KEY_BITS; SetDlgItemInt(hwnd, IDC_BITS, DEFAULT_KEY_BITS, false); } else if ((state->keytype == RSA || state->keytype == DSA) && state->key_bits < DEFAULT_KEY_BITS) { char *message = dupprintf ("Keys shorter than %d bits are not recommended. " "Really generate this key?", DEFAULT_KEY_BITS); int ret = MessageBox(hwnd, message, "PuTTYgen Warning", MB_ICONWARNING | MB_OKCANCEL); sfree(message); if (ret != IDOK) break; } if (state->keytype == RSA || state->keytype == DSA) raw_entropy_required = (state->key_bits / 2) * 2; else if (state->keytype == ECDSA || state->keytype == EDDSA) raw_entropy_required = (state->curve_bits / 2) * 2; else unreachable("we must have initialised keytype by now"); /* Bound the entropy collection above by the amount of * data we can actually fit into the PRNG. Any more * than that and it's doing no more good. */ if (raw_entropy_required > random_seed_bits()) raw_entropy_required = random_seed_bits(); raw_entropy_buf = snewn(raw_entropy_required, unsigned char); if (win_read_random(raw_entropy_buf, raw_entropy_required)) { /* * If we can get entropy from CryptGenRandom, use * it. But CryptGenRandom isn't a kernel-level * CPRNG (according to Wikipedia), and papers have * been published cryptanalysing it. So we'll * still do manual entropy collection; we'll just * do it _as well_ as this. */ random_reseed( make_ptrlen(raw_entropy_buf, raw_entropy_required)); } /* * Manual entropy input, by making the user wave the * mouse over the window a lot. * * My brief statistical tests on mouse movements * suggest that there are about 2.5 bits of randomness * in the x position, 2.5 in the y position, and 1.7 * in the message time, making 5.7 bits of * unpredictability per mouse movement. However, other * people have told me it's far less than that, so I'm * going to be stupidly cautious and knock that down * to a nice round 2. With this method, we require two * words per mouse movement, so with 2 bits per mouse * movement we expect 2 bits every 2 words, i.e. the * number of _words_ of mouse data we want to collect * is just the same as the number of _bits_ of entropy * we want. */ state->entropy_required = raw_entropy_required; ui_set_state(hwnd, state, 1); SetDlgItemText(hwnd, IDC_GENERATING, entropy_msg); state->key_exists = false; state->collecting_entropy = true; state->entropy_got = 0; state->entropy_size = (state->entropy_required * sizeof(unsigned)); state->entropy = snewn(state->entropy_required, unsigned); SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0, MAKELPARAM(0, state->entropy_required)); SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, 0, 0); smemclr(raw_entropy_buf, raw_entropy_required); sfree(raw_entropy_buf); } break; case IDC_SAVE: case IDC_EXPORT_OPENSSH_AUTO: case IDC_EXPORT_OPENSSH_NEW: 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; if (LOWORD(wParam) == IDC_EXPORT_OPENSSH_AUTO) type = SSH_KEYTYPE_OPENSSH_AUTO; else if (LOWORD(wParam) == IDC_EXPORT_OPENSSH_NEW) type = SSH_KEYTYPE_OPENSSH_NEW; else if (LOWORD(wParam) == IDC_EXPORT_SSHCOM) type = SSH_KEYTYPE_SSHCOM; else type = realtype; if (type != realtype && import_target_type(type) != realtype) { char msg[256]; sprintf(msg, "Cannot export an SSH-%d key in an SSH-%d" " 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, true, (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 = ppk_save_f(fn, &state->ssh2key, *passphrase ? passphrase : NULL, &save_params); 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 = rsa1_save_f(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); if (state->key_exists) { char filename[FILENAME_MAX]; if (prompt_keyfile(hwnd, "Save public key as:", filename, true, false)) { 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) break; } fp = fopen(filename, "w"); if (!fp) { MessageBox(hwnd, "Unable to open key file", "PuTTYgen Error", MB_OK | MB_ICONERROR); } else { if (state->ssh2) { strbuf *blob = strbuf_new(); ssh_key_public_blob( state->ssh2key.key, BinarySink_UPCAST(blob)); ssh2_write_pubkey(fp, state->ssh2key.comment, blob->u, blob->len, SSH_KEYTYPE_SSH2_PUBLIC_RFC4716); strbuf_free(blob); } else { ssh1_write_pubkey(fp, &state->key); } if (fclose(fp) < 0) { MessageBox(hwnd, "Unable to save key file", "PuTTYgen Error", MB_OK | MB_ICONERROR); } } } } break; case IDC_LOAD: case IDC_IMPORT: 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, false, 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; state->key_exists = true; SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0, MAKELPARAM(0, PROGRESSRANGE)); SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, PROGRESSRANGE, 0); if (state->ssh2) { if (state->keytype == DSA) { state->ssh2key.key = &state->dsskey.sshk; } else if (state->keytype == ECDSA) { state->ssh2key.key = &state->eckey.sshk; } else if (state->keytype == EDDSA) { state->ssh2key.key = &state->edkey.sshk; } else { state->ssh2key.key = &state->key.sshk; } state->commentptr = &state->ssh2key.comment; } else { state->commentptr = &state->key.comment; } /* * Invent a comment for the key. We'll do this by including * the date in it. This will be so horrifyingly ugly that * the user will immediately want to change it, which is * what we want :-) */ *state->commentptr = snewn(30, char); { struct tm tm; tm = ltime(); if (state->keytype == DSA) strftime(*state->commentptr, 30, "dsa-key-%Y%m%d", &tm); else if (state->keytype == ECDSA) strftime(*state->commentptr, 30, "ecdsa-key-%Y%m%d", &tm); else if (state->keytype == EDDSA) strftime(*state->commentptr, 30, "eddsa-key-%Y%m%d", &tm); else strftime(*state->commentptr, 30, "rsa-key-%Y%m%d", &tm); } /* * Now update the key controls with all the key data. */ { char *fp, *savecomment; /* * Blank passphrase, initially. This isn't dangerous, * because we will warn (Are You Sure?) before allowing * the user to save an unprotected private key. */ SetDlgItemText(hwnd, IDC_PASSPHRASE1EDIT, ""); SetDlgItemText(hwnd, IDC_PASSPHRASE2EDIT, ""); /* * Set the comment. */ SetDlgItemText(hwnd, IDC_COMMENTEDIT, *state->commentptr); /* * Set the key fingerprint. */ savecomment = *state->commentptr; *state->commentptr = NULL; if (state->ssh2) fp = ssh2_fingerprint(state->ssh2key.key, state->fptype); else fp = rsa_ssh1_fingerprint(&state->key); SetDlgItemText(hwnd, IDC_FINGERPRINT, fp); sfree(fp); *state->commentptr = savecomment; /* * Construct a decimal representation of the key, for * pasting into .ssh/authorized_keys or * .ssh/authorized_keys2 on a Unix box. */ if (state->ssh2) { setupbigedit2(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC, &state->ssh2key); } else { setupbigedit1(hwnd, IDC_KEYDISPLAY, IDC_PKSTATIC, &state->key); } } /* * Finally, hide the progress bar and show the key data. */ ui_set_state(hwnd, state, 2); break; case WM_HELP: { int id = ((LPHELPINFO)lParam)->iCtrlId; const char *topic = NULL; switch (id) { case IDC_GENERATING: case IDC_PROGRESS: case IDC_GENSTATIC: case IDC_GENERATE: topic = WINHELP_CTX_puttygen_generate; break; case IDC_PKSTATIC: case IDC_KEYDISPLAY: topic = WINHELP_CTX_puttygen_pastekey; break; case IDC_FPSTATIC: case IDC_FINGERPRINT: topic = WINHELP_CTX_puttygen_fingerprint; break; case IDC_COMMENTSTATIC: case IDC_COMMENTEDIT: topic = WINHELP_CTX_puttygen_comment; break; case IDC_PASSPHRASE1STATIC: case IDC_PASSPHRASE1EDIT: case IDC_PASSPHRASE2STATIC: case IDC_PASSPHRASE2EDIT: topic = WINHELP_CTX_puttygen_passphrase; break; case IDC_LOADSTATIC: case IDC_LOAD: topic = WINHELP_CTX_puttygen_load; break; case IDC_SAVESTATIC: case IDC_SAVE: topic = WINHELP_CTX_puttygen_savepriv; break; case IDC_SAVEPUB: topic = WINHELP_CTX_puttygen_savepub; break; case IDC_TYPESTATIC: case IDC_KEYSSH1: case IDC_KEYSSH2RSA: case IDC_KEYSSH2DSA: case IDC_KEYSSH2ECDSA: case IDC_KEYSSH2EDDSA: topic = WINHELP_CTX_puttygen_keytype; break; case IDC_BITSSTATIC: case IDC_BITS: topic = WINHELP_CTX_puttygen_bits; break; case IDC_IMPORT: case IDC_EXPORT_OPENSSH_AUTO: case IDC_EXPORT_OPENSSH_NEW: case IDC_EXPORT_SSHCOM: topic = WINHELP_CTX_puttygen_conversions; break; } if (topic) { launch_help(hwnd, topic); } else { MessageBeep(0); } break; } case WM_CLOSE: state = (struct MainDlgState *) GetWindowLongPtr(hwnd, GWLP_USERDATA); sfree(state); quit_help(hwnd); EndDialog(hwnd, 1); return 0; } return 0; } void cleanup_exit(int code) { shutdown_help(); exit(code); } HINSTANCE hinst; int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { int argc, i; char **argv; int ret; dll_hijacking_protection(); init_common_controls(); hinst = inst; /* * See if we can find our Help file. */ init_help(); split_into_argv(cmdline, &argc, &argv, NULL); for (i = 0; i < argc; i++) { if (!strcmp(argv[i], "-pgpfp")) { pgp_fingerprints_msgbox(NULL); return 1; } else if (!strcmp(argv[i], "-restrict-acl") || !strcmp(argv[i], "-restrict_acl") || !strcmp(argv[i], "-restrictacl")) { restrict_process_acl(); } else { /* * Assume the first argument to be a private key file, and * attempt to load it. */ cmdline_keyfile = argv[i]; break; } } save_params = ppk_save_default_parameters; random_setup_special(); ret = DialogBox(hinst, MAKEINTRESOURCE(201), NULL, MainDlgProc) != IDOK; cleanup_exit(ret); return ret; /* just in case optimiser complains */ } putty-0.76/windows/winpgnt.c0000644000175000017500000015302114072266314013107 00000000000000/* * Pageant: the PuTTY Authentication Agent. */ #include #include #include #include #include #include #include "putty.h" #include "ssh.h" #include "misc.h" #include "tree234.h" #include "winsecur.h" #include "wincapi.h" #include "pageant.h" #include "licence.h" #include "pageant-rc.h" #include #ifndef NO_SECURITY #include #ifdef DEBUG_IPC #define _WIN32_WINNT 0x0500 /* for ConvertSidToStringSid */ #include #endif #endif #define WM_SYSTRAY (WM_APP + 6) #define WM_SYSTRAY2 (WM_APP + 7) #define AGENT_COPYDATA_ID 0x804e50ba /* random goop */ #define APPNAME "Pageant" /* Titles and class names for invisible windows. IPCWINTITLE and * IPCCLASSNAME are critical to backwards compatibility: WM_COPYDATA * based Pageant clients will call FindWindow with those parameters * and expect to find the Pageant IPC receiver. */ #define TRAYWINTITLE "Pageant" #define TRAYCLASSNAME "PageantSysTray" #define IPCWINTITLE "Pageant" #define IPCCLASSNAME "Pageant" static HWND traywindow; static HWND keylist; static HWND aboutbox; static HMENU systray_menu, session_menu; static bool already_running; static FingerprintType fptype = SSH_FPTYPE_DEFAULT; static char *putty_path; static bool restrict_putty_acl = false; /* CWD for "add key" file requester. */ static filereq *keypath = NULL; /* From MSDN: In the WM_SYSCOMMAND message, the four low-order bits of * wParam are used by Windows, and should be masked off, so we shouldn't * attempt to store information in them. Hence all these identifiers have * the low 4 bits clear. Also, identifiers should < 0xF000. */ #define IDM_CLOSE 0x0010 #define IDM_VIEWKEYS 0x0020 #define IDM_ADDKEY 0x0030 #define IDM_ADDKEY_ENCRYPTED 0x0040 #define IDM_REMOVE_ALL 0x0050 #define IDM_REENCRYPT_ALL 0x0060 #define IDM_HELP 0x0070 #define IDM_ABOUT 0x0080 #define IDM_PUTTY 0x0090 #define IDM_SESSIONS_BASE 0x1000 #define IDM_SESSIONS_MAX 0x2000 #define PUTTY_REGKEY "Software\\SimonTatham\\PuTTY\\Sessions" #define PUTTY_DEFAULT "Default%20Settings" static int initial_menuitems_count; /* * Print a modal (Really Bad) message box and perform a fatal exit. */ void modalfatalbox(const char *fmt, ...) { va_list ap; char *buf; va_start(ap, fmt); buf = dupvprintf(fmt, ap); va_end(ap); MessageBox(traywindow, buf, "Pageant Fatal Error", MB_SYSTEMMODAL | MB_ICONERROR | MB_OK); sfree(buf); exit(1); } static bool has_security; struct PassphraseProcStruct { bool modal; const char *help_topic; PageantClientDialogId *dlgid; char *passphrase; const char *comment; }; /* * Dialog-box function for the Licence box. */ static INT_PTR CALLBACK LicenceProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_INITDIALOG: SetDlgItemText(hwnd, IDC_LICENCE_TEXTBOX, LICENCE_TEXT("\r\n\r\n")); return 1; case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: case IDCANCEL: EndDialog(hwnd, 1); return 0; } return 0; case WM_CLOSE: EndDialog(hwnd, 1); return 0; } return 0; } /* * Dialog-box function for the About box. */ static INT_PTR CALLBACK AboutProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_INITDIALOG: { char *buildinfo_text = buildinfo("\r\n"); char *text = dupprintf ("Pageant\r\n\r\n%s\r\n\r\n%s\r\n\r\n%s", ver, buildinfo_text, "\251 " SHORT_COPYRIGHT_DETAILS ". All rights reserved."); sfree(buildinfo_text); SetDlgItemText(hwnd, IDC_ABOUT_TEXTBOX, text); MakeDlgItemBorderless(hwnd, IDC_ABOUT_TEXTBOX); sfree(text); return 1; } case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: case IDCANCEL: aboutbox = NULL; DestroyWindow(hwnd); return 0; case IDC_ABOUT_LICENCE: EnableWindow(hwnd, 0); DialogBox(hinst, MAKEINTRESOURCE(IDD_LICENCE), hwnd, LicenceProc); EnableWindow(hwnd, 1); SetActiveWindow(hwnd); return 0; case IDC_ABOUT_WEBSITE: /* Load web browser */ ShellExecute(hwnd, "open", "https://www.chiark.greenend.org.uk/~sgtatham/putty/", 0, 0, SW_SHOWDEFAULT); return 0; } return 0; case WM_CLOSE: aboutbox = NULL; DestroyWindow(hwnd); return 0; } return 0; } static HWND modal_passphrase_hwnd = NULL; static HWND nonmodal_passphrase_hwnd = NULL; static void end_passphrase_dialog(HWND hwnd, INT_PTR result) { struct PassphraseProcStruct *p = (struct PassphraseProcStruct *) GetWindowLongPtr(hwnd, GWLP_USERDATA); if (p->modal) { EndDialog(hwnd, result); } else { /* * Destroy this passphrase dialog box before passing the * results back to pageant.c, to avoid re-entrancy issues. * * If we successfully got a passphrase from the user, but it * was _wrong_, then pageant_passphrase_request_success will * respond by calling back - synchronously - to our * ask_passphrase() implementation, which will expect the * previous value of nonmodal_passphrase_hwnd to have already * been cleaned up. */ SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) NULL); DestroyWindow(hwnd); nonmodal_passphrase_hwnd = NULL; if (result) pageant_passphrase_request_success( p->dlgid, ptrlen_from_asciz(p->passphrase)); else pageant_passphrase_request_refused(p->dlgid); burnstr(p->passphrase); sfree(p); } } /* * Dialog-box function for the passphrase box. */ static INT_PTR CALLBACK PassphraseProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { struct PassphraseProcStruct *p; if (msg == WM_INITDIALOG) { p = (struct PassphraseProcStruct *) lParam; SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) p); } else { p = (struct PassphraseProcStruct *) GetWindowLongPtr(hwnd, GWLP_USERDATA); } switch (msg) { case WM_INITDIALOG: { if (p->modal) modal_passphrase_hwnd = hwnd; /* * Centre the window. */ RECT rs, rd; HWND hw; hw = GetDesktopWindow(); if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd)) MoveWindow(hwnd, (rs.right + rs.left + rd.left - rd.right) / 2, (rs.bottom + rs.top + rd.top - rd.bottom) / 2, rd.right - rd.left, rd.bottom - rd.top, true); SetForegroundWindow(hwnd); SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); if (!p->modal) SetActiveWindow(hwnd); /* this won't have happened automatically */ if (p->comment) SetDlgItemText(hwnd, IDC_PASSPHRASE_FINGERPRINT, p->comment); burnstr(p->passphrase); p->passphrase = dupstr(""); SetDlgItemText(hwnd, IDC_PASSPHRASE_EDITBOX, p->passphrase); if (!p->help_topic || !has_help()) { HWND item = GetDlgItem(hwnd, IDHELP); if (item) DestroyWindow(item); } return 0; } case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: if (p->passphrase) end_passphrase_dialog(hwnd, 1); else MessageBeep(0); return 0; case IDCANCEL: end_passphrase_dialog(hwnd, 0); return 0; case IDHELP: if (p->help_topic) launch_help(hwnd, p->help_topic); return 0; case IDC_PASSPHRASE_EDITBOX: if ((HIWORD(wParam) == EN_CHANGE) && p->passphrase) { burnstr(p->passphrase); p->passphrase = GetDlgItemText_alloc( hwnd, IDC_PASSPHRASE_EDITBOX); } return 0; } return 0; case WM_CLOSE: end_passphrase_dialog(hwnd, 0); return 0; } return 0; } /* * Warn about the obsolescent key file format. */ void old_keyfile_warning(void) { static const char mbtitle[] = "PuTTY Key File Warning"; static const char message[] = "You are loading an SSH-2 private key which has an\n" "old version of the file format. This means your key\n" "file is not fully tamperproof. Future versions of\n" "PuTTY may stop supporting this private key format,\n" "so we recommend you convert your key to the new\n" "format.\n" "\n" "You can perform this conversion by loading the key\n" "into PuTTYgen and then saving it again."; MessageBox(NULL, message, mbtitle, MB_OK); } struct keylist_update_ctx { bool enable_remove_controls; bool enable_reencrypt_controls; }; static void keylist_update_callback( void *vctx, char **fingerprints, const char *comment, uint32_t ext_flags, struct pageant_pubkey *key) { struct keylist_update_ctx *ctx = (struct keylist_update_ctx *)vctx; FingerprintType this_type = ssh2_pick_fingerprint(fingerprints, fptype); const char *fingerprint = fingerprints[this_type]; strbuf *listentry = strbuf_new(); /* There is at least one key, so the controls for removing keys * should be enabled */ ctx->enable_remove_controls = true; switch (key->ssh_version) { case 1: { strbuf_catf(listentry, "ssh1\t%s\t%s", fingerprint, comment); /* * Replace the space in the fingerprint (between bit count and * hash) with a tab, for nice alignment in the box. */ char *p = strchr(listentry->s, ' '); if (p) *p = '\t'; break; } case 2: { /* * For nice alignment in the list box, we would ideally want * every entry to align to the tab stop settings, and have a * column for algorithm name, one for bit count, one for hex * fingerprint, and one for key comment. * * Unfortunately, some of the algorithm names are so long that * they overflow into the bit-count field. Fortunately, at the * moment, those are _precisely_ the algorithm names that * don't need a bit count displayed anyway (because for * NIST-style ECDSA the bit count is mentioned in the * algorithm name, and for ssh-ed25519 there is only one * possible value anyway). So we fudge this by simply omitting * the bit count field in that situation. * * This is fragile not only in the face of further key types * that don't follow this pattern, but also in the face of * font metrics changes - the Windows semantics for list box * tab stops is that \t aligns to the next one you haven't * already exceeded, so I have to guess when the key type will * overflow past the bit-count tab stop and leave out a tab * character. Urgh. */ BinarySource src[1]; BinarySource_BARE_INIT_PL(src, ptrlen_from_strbuf(key->blob)); ptrlen algname = get_string(src); const ssh_keyalg *alg = find_pubkey_alg_len(algname); bool include_bit_count = (alg == &ssh_dss || alg == &ssh_rsa); int wordnumber = 0; for (const char *p = fingerprint; *p; p++) { char c = *p; if (c == ' ') { if (wordnumber < 2) c = '\t'; wordnumber++; } if (include_bit_count || wordnumber != 1) put_byte(listentry, c); } strbuf_catf(listentry, "\t%s", comment); break; } } if (ext_flags & LIST_EXTENDED_FLAG_HAS_NO_CLEARTEXT_KEY) { strbuf_catf(listentry, "\t(encrypted)"); } else if (ext_flags & LIST_EXTENDED_FLAG_HAS_ENCRYPTED_KEY_FILE) { strbuf_catf(listentry, "\t(re-encryptable)"); /* At least one key can be re-encrypted */ ctx->enable_reencrypt_controls = true; } SendDlgItemMessage(keylist, IDC_KEYLIST_LISTBOX, LB_ADDSTRING, 0, (LPARAM)listentry->s); strbuf_free(listentry); } /* * Update the visible key list. */ void keylist_update(void) { if (keylist) { SendDlgItemMessage(keylist, IDC_KEYLIST_LISTBOX, LB_RESETCONTENT, 0, 0); char *errmsg; struct keylist_update_ctx ctx[1]; ctx->enable_remove_controls = false; ctx->enable_reencrypt_controls = false; int status = pageant_enum_keys(keylist_update_callback, ctx, &errmsg); assert(status == PAGEANT_ACTION_OK); assert(!errmsg); SendDlgItemMessage(keylist, IDC_KEYLIST_LISTBOX, LB_SETCURSEL, (WPARAM) - 1, 0); EnableWindow(GetDlgItem(keylist, IDC_KEYLIST_REMOVE), ctx->enable_remove_controls); EnableWindow(GetDlgItem(keylist, IDC_KEYLIST_REENCRYPT), ctx->enable_reencrypt_controls); } } static void win_add_keyfile(Filename *filename, bool encrypted) { char *err; int ret; /* * Try loading the key without a passphrase. (Or rather, without a * _new_ passphrase; pageant_add_keyfile will take care of trying * all the passphrases we've already stored.) */ ret = pageant_add_keyfile(filename, NULL, &err, encrypted); if (ret == PAGEANT_ACTION_OK) { goto done; } else if (ret == PAGEANT_ACTION_FAILURE) { goto error; } /* * OK, a passphrase is needed, and we've been given the key * comment to use in the passphrase prompt. */ while (1) { INT_PTR dlgret; struct PassphraseProcStruct pps; pps.modal = true; pps.help_topic = NULL; /* this dialog has no help button */ pps.dlgid = NULL; pps.passphrase = NULL; pps.comment = err; dlgret = DialogBoxParam( hinst, MAKEINTRESOURCE(IDD_LOAD_PASSPHRASE), NULL, PassphraseProc, (LPARAM) &pps); modal_passphrase_hwnd = NULL; if (!dlgret) { burnstr(pps.passphrase); goto done; /* operation cancelled */ } sfree(err); assert(pps.passphrase != NULL); ret = pageant_add_keyfile(filename, pps.passphrase, &err, false); burnstr(pps.passphrase); if (ret == PAGEANT_ACTION_OK) { goto done; } else if (ret == PAGEANT_ACTION_FAILURE) { goto error; } } error: message_box(traywindow, err, APPNAME, MB_OK | MB_ICONERROR, HELPCTXID(errors_cantloadkey)); done: sfree(err); return; } /* * Prompt for a key file to add, and add it. */ static void prompt_add_keyfile(bool encrypted) { OPENFILENAME of; char *filelist = snewn(8192, char); if (!keypath) keypath = filereq_new(); memset(&of, 0, sizeof(of)); of.hwndOwner = traywindow; of.lpstrFilter = FILTER_KEY_FILES; of.lpstrCustomFilter = NULL; of.nFilterIndex = 1; 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); win_add_keyfile(fn, encrypted); 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); Filename *fn = filename_from_str(filename); win_add_keyfile(fn, encrypted); filename_free(fn); sfree(filename); filewalker += strlen(filewalker) + 1; } } keylist_update(); pageant_forget_passphrases(); } sfree(filelist); } /* * Dialog-box function for the key list box. */ static INT_PTR CALLBACK KeyListProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { static const struct { const char *name; FingerprintType value; } fptypes[] = { {"SHA256", SSH_FPTYPE_SHA256}, {"MD5", SSH_FPTYPE_MD5}, }; switch (msg) { case WM_INITDIALOG: { /* * Centre the window. */ RECT rs, rd; HWND hw; hw = GetDesktopWindow(); if (GetWindowRect(hw, &rs) && GetWindowRect(hwnd, &rd)) MoveWindow(hwnd, (rs.right + rs.left + rd.left - rd.right) / 2, (rs.bottom + rs.top + rd.top - rd.bottom) / 2, rd.right - rd.left, rd.bottom - rd.top, true); if (has_help()) SetWindowLongPtr(hwnd, GWL_EXSTYLE, GetWindowLongPtr(hwnd, GWL_EXSTYLE) | WS_EX_CONTEXTHELP); else { HWND item = GetDlgItem(hwnd, IDC_KEYLIST_HELP); if (item) DestroyWindow(item); } keylist = hwnd; { static int tabs[] = { 35, 75, 300 }; SendDlgItemMessage(hwnd, IDC_KEYLIST_LISTBOX, LB_SETTABSTOPS, sizeof(tabs) / sizeof(*tabs), (LPARAM) tabs); } int selection = 0; for (size_t i = 0; i < lenof(fptypes); i++) { SendDlgItemMessage(hwnd, IDC_KEYLIST_FPTYPE, CB_ADDSTRING, 0, (LPARAM)fptypes[i].name); if (fptype == fptypes[i].value) selection = (int)i; } SendDlgItemMessage(hwnd, IDC_KEYLIST_FPTYPE, CB_SETCURSEL, 0, selection); keylist_update(); return 0; } case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: case IDCANCEL: keylist = NULL; DestroyWindow(hwnd); return 0; case IDC_KEYLIST_ADDKEY: case IDC_KEYLIST_ADDKEY_ENC: if (HIWORD(wParam) == BN_CLICKED || HIWORD(wParam) == BN_DOUBLECLICKED) { if (modal_passphrase_hwnd) { MessageBeep(MB_ICONERROR); SetForegroundWindow(modal_passphrase_hwnd); break; } prompt_add_keyfile(LOWORD(wParam) == IDC_KEYLIST_ADDKEY_ENC); } return 0; case IDC_KEYLIST_REMOVE: case IDC_KEYLIST_REENCRYPT: 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 = SendDlgItemMessage( hwnd, IDC_KEYLIST_LISTBOX, LB_GETSELCOUNT, 0, 0); /* none selected? that was silly */ if (numSelected == 0) { MessageBeep(0); break; } /* get item indices in an array */ selectedArray = snewn(numSelected, int); SendDlgItemMessage(hwnd, IDC_KEYLIST_LISTBOX, LB_GETSELITEMS, numSelected, (WPARAM)selectedArray); itemNum = numSelected - 1; rCount = pageant_count_ssh1_keys(); sCount = pageant_count_ssh2_keys(); /* 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--) { if (selectedArray[itemNum] == rCount + i) { switch (LOWORD(wParam)) { case IDC_KEYLIST_REMOVE: pageant_delete_nth_ssh2_key(i); break; case IDC_KEYLIST_REENCRYPT: pageant_reencrypt_nth_ssh2_key(i); break; } itemNum--; } } /* do the same for the rsa keys */ for (i = rCount - 1; (itemNum >= 0) && (i >= 0); i--) { if(selectedArray[itemNum] == i) { switch (LOWORD(wParam)) { case IDC_KEYLIST_REMOVE: pageant_delete_nth_ssh1_key(i); break; case IDC_KEYLIST_REENCRYPT: /* SSH-1 keys can't be re-encrypted */ break; } itemNum--; } } sfree(selectedArray); keylist_update(); } return 0; case IDC_KEYLIST_HELP: if (HIWORD(wParam) == BN_CLICKED || HIWORD(wParam) == BN_DOUBLECLICKED) { launch_help(hwnd, WINHELP_CTX_pageant_general); } return 0; case IDC_KEYLIST_FPTYPE: if (HIWORD(wParam) == CBN_SELCHANGE) { int selection = SendDlgItemMessage( hwnd, IDC_KEYLIST_FPTYPE, CB_GETCURSEL, 0, 0); if (selection >= 0 && (size_t)selection < lenof(fptypes)) { fptype = fptypes[selection].value; keylist_update(); } } return 0; } return 0; case WM_HELP: { int id = ((LPHELPINFO)lParam)->iCtrlId; const char *topic = NULL; switch (id) { case IDC_KEYLIST_LISTBOX: case IDC_KEYLIST_FPTYPE: case IDC_KEYLIST_FPTYPE_STATIC: topic = WINHELP_CTX_pageant_keylist; break; case IDC_KEYLIST_ADDKEY: topic = WINHELP_CTX_pageant_addkey; break; case IDC_KEYLIST_REMOVE: topic = WINHELP_CTX_pageant_remkey; break; case IDC_KEYLIST_ADDKEY_ENC: case IDC_KEYLIST_REENCRYPT: topic = WINHELP_CTX_pageant_deferred; break; } if (topic) { launch_help(hwnd, topic); } else { MessageBeep(0); } break; } case WM_CLOSE: keylist = NULL; DestroyWindow(hwnd); return 0; } return 0; } /* Set up a system tray icon */ static BOOL AddTrayIcon(HWND hwnd) { BOOL res; NOTIFYICONDATA tnid; HICON hicon; #ifdef NIM_SETVERSION tnid.uVersion = 0; res = Shell_NotifyIcon(NIM_SETVERSION, &tnid); #endif 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; } /* Update the saved-sessions menu. */ static void update_sessions(void) { int num_entries; HKEY hkey; TCHAR buf[MAX_PATH + 1]; MENUITEMINFO mii; strbuf *sb; int index_key, index_menu; if (!putty_path) return; if(ERROR_SUCCESS != RegOpenKey(HKEY_CURRENT_USER, PUTTY_REGKEY, &hkey)) return; for(num_entries = GetMenuItemCount(session_menu); num_entries > initial_menuitems_count; num_entries--) RemoveMenu(session_menu, 0, MF_BYPOSITION); index_key = 0; index_menu = 0; sb = strbuf_new(); while(ERROR_SUCCESS == RegEnumKey(hkey, index_key, buf, MAX_PATH)) { if(strcmp(buf, PUTTY_DEFAULT) != 0) { strbuf_clear(sb); unescape_registry_key(buf, sb); memset(&mii, 0, sizeof(mii)); mii.cbSize = sizeof(mii); mii.fMask = MIIM_TYPE | MIIM_STATE | MIIM_ID; mii.fType = MFT_STRING; mii.fState = MFS_ENABLED; mii.wID = (index_menu * 16) + IDM_SESSIONS_BASE; mii.dwTypeData = sb->s; InsertMenuItem(session_menu, index_menu, true, &mii); index_menu++; } index_key++; } strbuf_free(sb); RegCloseKey(hkey); if(index_menu == 0) { mii.cbSize = sizeof(mii); mii.fMask = MIIM_TYPE | MIIM_STATE; mii.fType = MFT_STRING; mii.fState = MFS_GRAYED; mii.dwTypeData = _T("(No sessions)"); InsertMenuItem(session_menu, index_menu, true, &mii); } } #ifndef NO_SECURITY /* * Versions of Pageant prior to 0.61 expected this SID on incoming * communications. For backwards compatibility, and more particularly * for compatibility with derived works of PuTTY still using the old * Pageant client code, we accept it as an alternative to the one * returned from get_user_sid() in winpgntc.c. */ PSID get_default_sid(void) { HANDLE proc = NULL; DWORD sidlen; PSECURITY_DESCRIPTOR psd = NULL; PSID sid = NULL, copy = NULL, ret = NULL; if ((proc = OpenProcess(MAXIMUM_ALLOWED, false, GetCurrentProcessId())) == NULL) goto cleanup; if (p_GetSecurityInfo(proc, SE_KERNEL_OBJECT, OWNER_SECURITY_INFORMATION, &sid, NULL, NULL, NULL, &psd) != ERROR_SUCCESS) goto cleanup; sidlen = GetLengthSid(sid); copy = (PSID)smalloc(sidlen); if (!CopySid(sidlen, copy, sid)) goto cleanup; /* Success. Move sid into the return value slot, and null it out * to stop the cleanup code freeing it. */ ret = copy; copy = NULL; cleanup: if (proc != NULL) CloseHandle(proc); if (psd != NULL) LocalFree(psd); if (copy != NULL) sfree(copy); return ret; } #endif struct WmCopydataTransaction { char *length, *body; size_t bodysize, bodylen; HANDLE ev_msg_ready, ev_reply_ready; } wmct; static struct PageantClient wmcpc; static void wm_copydata_got_msg(void *vctx) { pageant_handle_msg(&wmcpc, NULL, make_ptrlen(wmct.body, wmct.bodylen)); } static void wm_copydata_got_response( PageantClient *pc, PageantClientRequestId *reqid, ptrlen response) { if (response.len > wmct.bodysize) { /* Output would overflow message buffer. Replace with a * failure message. */ static const unsigned char failure[] = { SSH_AGENT_FAILURE }; response = make_ptrlen(failure, lenof(failure)); assert(response.len <= wmct.bodysize); } PUT_32BIT_MSB_FIRST(wmct.length, response.len); memcpy(wmct.body, response.ptr, response.len); SetEvent(wmct.ev_reply_ready); } static bool ask_passphrase_common(PageantClientDialogId *dlgid, const char *comment) { /* Pageant core should be serialising requests, so we never expect * a passphrase prompt to exist already at this point */ assert(!nonmodal_passphrase_hwnd); struct PassphraseProcStruct *pps = snew(struct PassphraseProcStruct); pps->modal = false; pps->help_topic = WINHELP_CTX_pageant_deferred; pps->dlgid = dlgid; pps->passphrase = NULL; pps->comment = comment; nonmodal_passphrase_hwnd = CreateDialogParam( hinst, MAKEINTRESOURCE(IDD_ONDEMAND_PASSPHRASE), NULL, PassphraseProc, (LPARAM)pps); /* * Try to put this passphrase prompt into the foreground. * * This will probably not succeed in giving it the actual keyboard * focus, because Windows is quite opposed to applications being * able to suddenly steal the focus on their own initiative. * * That makes sense in a lot of situations, as a defensive * measure. If you were about to type a password or other secret * data into the window you already had focused, and some * malicious app stole the focus, it might manage to trick you * into typing your secrets into _it_ instead. * * In this case it's possible to regard the same defensive measure * as counterproductive, because the effect if we _do_ steal focus * is that you type something into our passphrase prompt that * isn't the passphrase, and we fail to decrypt the key, and no * harm is done. Whereas the effect of the user wrongly _assuming_ * the new passphrase prompt has the focus is much worse: now you * type your highly secret passphrase into some other window you * didn't mean to trust with that information - such as the * agent-forwarded PuTTY in which you just ran an ssh command, * which the _whole point_ was to avoid telling your passphrase to! * * On the other hand, I'm sure _every_ application author can come * up with an argument for why they think _they_ should be allowed * to steal the focus. Probably most of them include the claim * that no harm is done if their application receives data * intended for something else, and of course that's not always * true! * * In any case, I don't know of anything I can do about it, or * anything I _should_ do about it if I could. If anyone thinks * they can improve on all this, patches are welcome. */ SetForegroundWindow(nonmodal_passphrase_hwnd); return true; } static bool wm_copydata_ask_passphrase( PageantClient *pc, PageantClientDialogId *dlgid, const char *comment) { return ask_passphrase_common(dlgid, comment); } static const PageantClientVtable wmcpc_vtable = { .log = NULL, /* no logging in this client */ .got_response = wm_copydata_got_response, .ask_passphrase = wm_copydata_ask_passphrase, }; static char *answer_filemapping_message(const char *mapname) { HANDLE maphandle = INVALID_HANDLE_VALUE; void *mapaddr = NULL; char *err = NULL; size_t mapsize; unsigned msglen; #ifndef NO_SECURITY PSID mapsid = NULL; PSID expectedsid = NULL; PSID expectedsid_bc = NULL; PSECURITY_DESCRIPTOR psd = NULL; #endif wmct.length = wmct.body = NULL; #ifdef DEBUG_IPC debug("mapname = \"%s\"\n", mapname); #endif maphandle = OpenFileMapping(FILE_MAP_ALL_ACCESS, false, mapname); if (maphandle == NULL || maphandle == INVALID_HANDLE_VALUE) { err = dupprintf("OpenFileMapping(\"%s\"): %s", mapname, win_strerror(GetLastError())); goto cleanup; } #ifdef DEBUG_IPC debug("maphandle = %p\n", maphandle); #endif #ifndef NO_SECURITY if (has_security) { DWORD retd; if ((expectedsid = get_user_sid()) == NULL) { err = dupstr("unable to get user SID"); goto cleanup; } if ((expectedsid_bc = get_default_sid()) == NULL) { err = dupstr("unable to get default SID"); goto cleanup; } if ((retd = p_GetSecurityInfo( maphandle, SE_KERNEL_OBJECT, OWNER_SECURITY_INFORMATION, &mapsid, NULL, NULL, NULL, &psd) != ERROR_SUCCESS)) { err = dupprintf("unable to get owner of file mapping: " "GetSecurityInfo returned: %s", win_strerror(retd)); goto cleanup; } #ifdef DEBUG_IPC { LPTSTR ours, ours2, theirs; ConvertSidToStringSid(mapsid, &theirs); ConvertSidToStringSid(expectedsid, &ours); ConvertSidToStringSid(expectedsid_bc, &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(mapsid, expectedsid) && !EqualSid(mapsid, expectedsid_bc)) { err = dupstr("wrong owning SID of file mapping"); goto cleanup; } } else #endif /* NO_SECURITY */ { #ifdef DEBUG_IPC debug("security APIs not present\n"); #endif } mapaddr = MapViewOfFile(maphandle, FILE_MAP_WRITE, 0, 0, 0); if (!mapaddr) { err = dupprintf("unable to obtain view of file mapping: %s", win_strerror(GetLastError())); goto cleanup; } #ifdef DEBUG_IPC debug("mapped address = %p\n", mapaddr); #endif { MEMORY_BASIC_INFORMATION mbi; size_t mbiSize = VirtualQuery(mapaddr, &mbi, sizeof(mbi)); if (mbiSize == 0) { err = dupprintf("unable to query view of file mapping: %s", win_strerror(GetLastError())); goto cleanup; } if (mbiSize < (offsetof(MEMORY_BASIC_INFORMATION, RegionSize) + sizeof(mbi.RegionSize))) { err = dupstr("VirtualQuery returned too little data to get " "region size"); goto cleanup; } mapsize = mbi.RegionSize; } #ifdef DEBUG_IPC debug("region size = %"SIZEu"\n", mapsize); #endif if (mapsize < 5) { err = dupstr("mapping smaller than smallest possible request"); goto cleanup; } wmct.length = (char *)mapaddr; msglen = GET_32BIT_MSB_FIRST(wmct.length); #ifdef DEBUG_IPC debug("msg length=%08x, msg type=%02x\n", msglen, (unsigned)((unsigned char *) mapaddr)[4]); #endif wmct.body = wmct.length + 4; wmct.bodysize = mapsize - 4; if (msglen > wmct.bodysize) { /* Incoming length field is too large. Emit a failure response * without even trying to handle the request. * * (We know this must fit, because we checked mapsize >= 5 * above.) */ PUT_32BIT_MSB_FIRST(wmct.length, 1); *wmct.body = SSH_AGENT_FAILURE; } else { wmct.bodylen = msglen; SetEvent(wmct.ev_msg_ready); WaitForSingleObject(wmct.ev_reply_ready, INFINITE); } cleanup: /* expectedsid has the lifetime of the program, so we don't free it */ sfree(expectedsid_bc); if (psd) LocalFree(psd); if (mapaddr) UnmapViewOfFile(mapaddr); if (maphandle != NULL && maphandle != INVALID_HANDLE_VALUE) CloseHandle(maphandle); return err; } static void create_keylist_window(void) { if (keylist) return; keylist = CreateDialog(hinst, MAKEINTRESOURCE(IDD_KEYLIST), NULL, KeyListProc); ShowWindow(keylist, SW_SHOWNORMAL); } static LRESULT CALLBACK TrayWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static bool menuinprogress; static UINT msgTaskbarCreated = 0; switch (message) { case WM_CREATE: msgTaskbarCreated = RegisterWindowMessage(_T("TaskbarCreated")); break; default: if (message==msgTaskbarCreated) { /* * Explorer has been restarted, so the tray icon will * have been lost. */ AddTrayIcon(hwnd); } break; case WM_SYSTRAY: if (lParam == WM_RBUTTONUP) { POINT cursorpos; GetCursorPos(&cursorpos); PostMessage(hwnd, WM_SYSTRAY2, cursorpos.x, cursorpos.y); } else if (lParam == WM_LBUTTONDBLCLK) { /* Run the default menu item. */ UINT menuitem = GetMenuDefaultItem(systray_menu, false, 0); if (menuitem != -1) PostMessage(hwnd, WM_COMMAND, menuitem, 0); } break; case WM_SYSTRAY2: if (!menuinprogress) { menuinprogress = true; update_sessions(); SetForegroundWindow(hwnd); TrackPopupMenu(systray_menu, TPM_RIGHTALIGN | TPM_BOTTOMALIGN | TPM_RIGHTBUTTON, wParam, lParam, 0, hwnd, NULL); menuinprogress = false; } break; case WM_COMMAND: case WM_SYSCOMMAND: { unsigned command = wParam & ~0xF; /* low 4 bits reserved to Windows */ switch (command) { case IDM_PUTTY: { TCHAR cmdline[10]; cmdline[0] = '\0'; if (restrict_putty_acl) strcat(cmdline, "&R"); if((INT_PTR)ShellExecute(hwnd, NULL, putty_path, cmdline, _T(""), SW_SHOW) <= 32) { MessageBox(NULL, "Unable to execute PuTTY!", "Error", MB_OK | MB_ICONERROR); } break; } case IDM_CLOSE: if (modal_passphrase_hwnd) SendMessage(modal_passphrase_hwnd, WM_CLOSE, 0, 0); SendMessage(hwnd, WM_CLOSE, 0, 0); break; case IDM_VIEWKEYS: create_keylist_window(); /* * Sometimes the window comes up minimised / hidden for * no obvious reason. Prevent this. This also brings it * to the front if it's already present (the user * selected View Keys because they wanted to _see_ the * thing). */ SetForegroundWindow(keylist); SetWindowPos(keylist, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); break; case IDM_ADDKEY: case IDM_ADDKEY_ENCRYPTED: if (modal_passphrase_hwnd) { MessageBeep(MB_ICONERROR); SetForegroundWindow(modal_passphrase_hwnd); break; } prompt_add_keyfile(command == IDM_ADDKEY_ENCRYPTED); break; case IDM_REMOVE_ALL: pageant_delete_all(); keylist_update(); break; case IDM_REENCRYPT_ALL: pageant_reencrypt_all(); keylist_update(); break; case IDM_ABOUT: if (!aboutbox) { aboutbox = CreateDialog(hinst, MAKEINTRESOURCE(IDD_ABOUT), NULL, AboutProc); ShowWindow(aboutbox, SW_SHOWNORMAL); /* * Sometimes the window comes up minimised / hidden * for no obvious reason. Prevent this. */ SetForegroundWindow(aboutbox); SetWindowPos(aboutbox, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); } break; case IDM_HELP: launch_help(hwnd, WINHELP_CTX_pageant_general); break; default: { if(wParam >= IDM_SESSIONS_BASE && wParam <= IDM_SESSIONS_MAX) { MENUITEMINFO mii; TCHAR buf[MAX_PATH + 1]; TCHAR param[MAX_PATH + 1]; memset(&mii, 0, sizeof(mii)); mii.cbSize = sizeof(mii); mii.fMask = MIIM_TYPE; mii.cch = MAX_PATH; mii.dwTypeData = buf; GetMenuItemInfo(session_menu, wParam, false, &mii); param[0] = '\0'; if (restrict_putty_acl) strcat(param, "&R"); strcat(param, "@"); strcat(param, mii.dwTypeData); if((INT_PTR)ShellExecute(hwnd, NULL, putty_path, param, _T(""), SW_SHOW) <= 32) { MessageBox(NULL, "Unable to execute PuTTY!", "Error", MB_OK | MB_ICONERROR); } } break; } } break; } case WM_DESTROY: quit_help(hwnd); PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, message, wParam, lParam); } static LRESULT CALLBACK wm_copydata_WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_COPYDATA: { COPYDATASTRUCT *cds; char *mapname, *err; cds = (COPYDATASTRUCT *) lParam; if (cds->dwData != AGENT_COPYDATA_ID) return 0; /* not our message, mate */ mapname = (char *) cds->lpData; if (mapname[cds->cbData - 1] != '\0') return 0; /* failure to be ASCIZ! */ err = answer_filemapping_message(mapname); if (err) { #ifdef DEBUG_IPC debug("IPC failed: %s\n", err); #endif sfree(err); return 0; } return 1; } } return DefWindowProc(hwnd, message, wParam, lParam); } static DWORD WINAPI wm_copydata_threadfunc(void *param) { HINSTANCE inst = *(HINSTANCE *)param; HWND ipchwnd = CreateWindow(IPCCLASSNAME, IPCWINTITLE, WS_OVERLAPPEDWINDOW | WS_VSCROLL, CW_USEDEFAULT, CW_USEDEFAULT, 100, 100, NULL, NULL, inst, NULL); ShowWindow(ipchwnd, SW_HIDE); MSG msg; while (GetMessage(&msg, NULL, 0, 0) == 1) { TranslateMessage(&msg); DispatchMessage(&msg); } return 0; } /* * Fork and Exec the command in cmdline. [DBW] */ void spawn_cmd(const char *cmdline, const char *args, int show) { if (ShellExecute(NULL, _T("open"), cmdline, args, NULL, show) <= (HINSTANCE) 32) { char *msg; msg = dupprintf("Failed to run \"%s\": %s", cmdline, win_strerror(GetLastError())); MessageBox(NULL, msg, APPNAME, MB_OK | MB_ICONEXCLAMATION); sfree(msg); } } void logevent(LogContext *logctx, const char *event) { unreachable("Pageant can't create a LogContext, so this can't be called"); } void noise_ultralight(NoiseSourceId id, unsigned long data) { /* Pageant doesn't use random numbers, so we ignore this */ } void cleanup_exit(int code) { shutdown_help(); exit(code); } static bool winpgnt_listener_ask_passphrase( PageantListenerClient *plc, PageantClientDialogId *dlgid, const char *comment) { return ask_passphrase_common(dlgid, comment); } struct winpgnt_client { PageantListenerClient plc; }; static const PageantListenerClientVtable winpgnt_vtable = { .log = NULL, /* no logging */ .ask_passphrase = winpgnt_listener_ask_passphrase, }; static struct winpgnt_client wpc[1]; HINSTANCE hinst; int WINAPI WinMain(HINSTANCE inst, HINSTANCE prev, LPSTR cmdline, int show) { MSG msg; const char *command = NULL; bool added_keys = false; bool show_keylist_on_startup = false; int argc, i; char **argv, **argstart; dll_hijacking_protection(); hinst = inst; /* * Determine whether we're an NT system (should have security * APIs) or a non-NT system (don't do security). */ init_winver(); has_security = (osPlatformId == VER_PLATFORM_WIN32_NT); 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(); /* * Look for the PuTTY binary (we will enable the saved session * submenu if we find it). */ { char b[2048], *p, *q, *r; FILE *fp; GetModuleFileName(NULL, b, sizeof(b) - 16); r = b; p = strrchr(b, '\\'); if (p && p >= r) r = p+1; q = strrchr(b, ':'); if (q && q >= r) r = q+1; strcpy(r, "putty.exe"); if ( (fp = fopen(b, "r")) != NULL) { putty_path = dupstr(b); fclose(fp); } else putty_path = NULL; } /* * Find out if Pageant is already running. */ already_running = agent_exists(); /* * Initialise the cross-platform Pageant code. */ if (!already_running) { pageant_init(); } /* * Process the command line and add keys as listed on it. */ split_into_argv(cmdline, &argc, &argv, &argstart); bool doing_opts = true; bool add_keys_encrypted = false; for (i = 0; i < argc; i++) { char *p = argv[i]; if (*p == '-' && doing_opts) { if (!strcmp(p, "-pgpfp")) { pgp_fingerprints_msgbox(NULL); return 1; } else if (!strcmp(p, "-restrict-acl") || !strcmp(p, "-restrict_acl") || !strcmp(p, "-restrictacl")) { restrict_process_acl(); } else if (!strcmp(p, "-restrict-putty-acl") || !strcmp(p, "-restrict_putty_acl")) { restrict_putty_acl = true; } else if (!strcmp(p, "--no-decrypt") || !strcmp(p, "-no-decrypt") || !strcmp(p, "--no_decrypt") || !strcmp(p, "-no_decrypt") || !strcmp(p, "--nodecrypt") || !strcmp(p, "-nodecrypt") || !strcmp(p, "--encrypted") || !strcmp(p, "-encrypted")) { add_keys_encrypted = true; } else if (!strcmp(p, "-keylist") || !strcmp(p, "--keylist")) { show_keylist_on_startup = true; } else if (!strcmp(p, "-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 if (!strcmp(p, "--")) { doing_opts = false; } else { char *msg = dupprintf("unrecognised command-line option\n" "'%s'", p); MessageBox(NULL, msg, "Pageant command-line syntax error", MB_ICONERROR | MB_OK); exit(1); } } else { Filename *fn = filename_from_str(p); win_add_keyfile(fn, add_keys_encrypted); filename_free(fn); added_keys = true; } } /* * Forget any passphrase that we retained while going over * command line keyfiles. */ pageant_forget_passphrases(); if (command) { char *args; if (command[0] == '"') args = strchr(++command, '"'); else args = strchr(command, ' '); if (args) { *args++ = 0; while(*args && isspace(*args)) args++; } spawn_cmd(command, args, show); } /* * If Pageant was already running, we leave now. If we haven't * even taken any auxiliary action (spawned a command or added * keys), complain. */ if (already_running) { if (!command && !added_keys) { MessageBox(NULL, "Pageant is already running", "Pageant Error", MB_ICONERROR | MB_OK); } return 0; } #if !defined NO_SECURITY /* * Set up a named-pipe listener. */ { Plug *pl_plug; wpc->plc.vt = &winpgnt_vtable; wpc->plc.suppress_logging = true; struct pageant_listen_state *pl = pageant_listener_new(&pl_plug, &wpc->plc); char *pipename = agent_named_pipe_name(); Socket *sock = new_named_pipe_listener(pipename, pl_plug); if (sk_socket_error(sock)) { char *err = dupprintf("Unable to open named pipe at %s " "for SSH agent:\n%s", pipename, sk_socket_error(sock)); MessageBox(NULL, err, "Pageant Error", MB_ICONERROR | MB_OK); return 1; } pageant_listener_got_socket(pl, sock); sfree(pipename); } #endif /* !defined NO_SECURITY */ /* * Set up window classes for two hidden windows: one that receives * all the messages to do with our presence in the system tray, * and one that receives the WM_COPYDATA message used by the * old-style Pageant IPC system. */ if (!prev) { WNDCLASS wndclass; memset(&wndclass, 0, sizeof(wndclass)); wndclass.lpfnWndProc = TrayWndProc; wndclass.hInstance = inst; wndclass.hIcon = LoadIcon(inst, MAKEINTRESOURCE(IDI_MAINICON)); wndclass.lpszClassName = TRAYCLASSNAME; RegisterClass(&wndclass); memset(&wndclass, 0, sizeof(wndclass)); wndclass.lpfnWndProc = wm_copydata_WndProc; wndclass.hInstance = inst; wndclass.lpszClassName = IPCCLASSNAME; RegisterClass(&wndclass); } keylist = NULL; traywindow = CreateWindow(TRAYCLASSNAME, TRAYWINTITLE, WS_OVERLAPPEDWINDOW | WS_VSCROLL, CW_USEDEFAULT, CW_USEDEFAULT, 100, 100, NULL, NULL, inst, NULL); winselgui_set_hwnd(traywindow); /* Set up a system tray icon */ AddTrayIcon(traywindow); /* Accelerators used: nsvkxa */ systray_menu = CreatePopupMenu(); if (putty_path) { session_menu = CreateMenu(); AppendMenu(systray_menu, MF_ENABLED, IDM_PUTTY, "&New Session"); AppendMenu(systray_menu, MF_POPUP | MF_ENABLED, (UINT_PTR) session_menu, "&Saved Sessions"); AppendMenu(systray_menu, MF_SEPARATOR, 0, 0); } AppendMenu(systray_menu, MF_ENABLED, IDM_VIEWKEYS, "&View Keys"); AppendMenu(systray_menu, MF_ENABLED, IDM_ADDKEY, "Add &Key"); AppendMenu(systray_menu, MF_ENABLED, IDM_ADDKEY_ENCRYPTED, "Add key (encrypted)"); AppendMenu(systray_menu, MF_SEPARATOR, 0, 0); AppendMenu(systray_menu, MF_ENABLED, IDM_REMOVE_ALL, "Remove All Keys"); AppendMenu(systray_menu, MF_ENABLED, IDM_REENCRYPT_ALL, "Re-encrypt All Keys"); AppendMenu(systray_menu, MF_SEPARATOR, 0, 0); if (has_help()) AppendMenu(systray_menu, MF_ENABLED, IDM_HELP, "&Help"); AppendMenu(systray_menu, MF_ENABLED, IDM_ABOUT, "&About"); AppendMenu(systray_menu, MF_SEPARATOR, 0, 0); AppendMenu(systray_menu, MF_ENABLED, IDM_CLOSE, "E&xit"); initial_menuitems_count = GetMenuItemCount(session_menu); /* Set the default menu item. */ SetMenuDefaultItem(systray_menu, IDM_VIEWKEYS, false); ShowWindow(traywindow, SW_HIDE); wmcpc.vt = &wmcpc_vtable; wmcpc.suppress_logging = true; pageant_register_client(&wmcpc); DWORD wm_copydata_threadid; wmct.ev_msg_ready = CreateEvent(NULL, false, false, NULL); wmct.ev_reply_ready = CreateEvent(NULL, false, false, NULL); HANDLE hThread = CreateThread(NULL, 0, wm_copydata_threadfunc, &inst, 0, &wm_copydata_threadid); if (hThread) CloseHandle(hThread); /* we don't need the thread handle */ handle_add_foreign_event(wmct.ev_msg_ready, wm_copydata_got_msg, NULL); if (show_keylist_on_startup) create_keylist_window(); /* * Main message loop. */ while (true) { HANDLE *handles; int nhandles, n; handles = handle_get_events(&nhandles); n = MsgWaitForMultipleObjects(nhandles, handles, false, INFINITE, QS_ALLINPUT); if ((unsigned)(n - WAIT_OBJECT_0) < (unsigned)nhandles) { handle_got_event(handles[n - WAIT_OBJECT_0]); sfree(handles); } else sfree(handles); while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) { if (msg.message == WM_QUIT) goto finished; /* two-level break */ if (IsWindow(keylist) && IsDialogMessage(keylist, &msg)) continue; if (IsWindow(aboutbox) && IsDialogMessage(aboutbox, &msg)) continue; if (IsWindow(nonmodal_passphrase_hwnd) && IsDialogMessage(nonmodal_passphrase_hwnd, &msg)) continue; TranslateMessage(&msg); DispatchMessage(&msg); } run_toplevel_callbacks(); } finished: /* Clean up the system tray icon */ { NOTIFYICONDATA tnid; tnid.cbSize = sizeof(NOTIFYICONDATA); tnid.hWnd = traywindow; tnid.uID = 1; 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 */ } putty-0.76/windows/winpgntc.c0000644000175000017500000002223114072266315013251 00000000000000/* * Pageant client code. */ #include #include #include #include "putty.h" #include "pageant.h" /* for AGENT_MAX_MSGLEN */ #ifndef NO_SECURITY #include "winsecur.h" #include "wincapi.h" #endif #define AGENT_COPYDATA_ID 0x804e50ba /* random goop */ static bool wm_copydata_agent_exists(void) { HWND hwnd; hwnd = FindWindow("Pageant", "Pageant"); if (!hwnd) return false; else return true; } static void wm_copydata_agent_query(strbuf *query, void **out, int *outlen) { HWND hwnd; char *mapname; HANDLE filemap; unsigned char *p, *ret; int id, retlen; COPYDATASTRUCT cds; SECURITY_ATTRIBUTES sa, *psa; PSECURITY_DESCRIPTOR psd = NULL; PSID usersid = NULL; *out = NULL; *outlen = 0; if (query->len > AGENT_MAX_MSGLEN) return; /* query too large */ hwnd = FindWindow("Pageant", "Pageant"); if (!hwnd) return; /* *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)) { sa.nLength = sizeof(sa); sa.bInheritHandle = true; sa.lpSecurityDescriptor = psd; psa = &sa; } else { LocalFree(psd); psd = NULL; } } } } #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; /* *out == NULL, so failure */ } p = MapViewOfFile(filemap, FILE_MAP_WRITE, 0, 0, 0); strbuf_finalise_agent_query(query); memcpy(p, query->s, query->len); cds.dwData = AGENT_COPYDATA_ID; cds.cbData = 1 + strlen(mapname); cds.lpData = mapname; /* * The user either passed a null callback (indicating that the * query is required to be synchronous) or CreateThread failed. * Either way, we need a synchronous request. */ id = SendMessage(hwnd, WM_COPYDATA, (WPARAM) NULL, (LPARAM) &cds); if (id > 0) { uint32_t length_field = GET_32BIT_MSB_FIRST(p); if (length_field > 0 && length_field <= AGENT_MAX_MSGLEN - 4) { retlen = length_field + 4; ret = snewn(retlen, unsigned char); memcpy(ret, p, retlen); *out = ret; *outlen = retlen; } else { /* * If we get here, we received an out-of-range length * field, either without space for a message type code or * overflowing the FileMapping. * * Treat this as if Pageant didn't answer at all - which * actually means we do nothing, and just don't fill in * out and outlen. */ } } UnmapViewOfFile(p); CloseHandle(filemap); sfree(mapname); if (psd) LocalFree(psd); } #ifndef NO_SECURITY char *agent_named_pipe_name(void) { char *username, *suffix, *pipename; username = get_username(); suffix = capi_obfuscate_string("Pageant"); pipename = dupprintf("\\\\.\\pipe\\pageant.%s.%s", username, suffix); sfree(username); sfree(suffix); return pipename; } Socket *agent_connect(Plug *plug) { char *pipename = agent_named_pipe_name(); Socket *s = new_named_pipe_client(pipename, plug); sfree(pipename); return s; } static bool named_pipe_agent_exists(void) { char *pipename = agent_named_pipe_name(); WIN32_FIND_DATA data; HANDLE ffh = FindFirstFile(pipename, &data); sfree(pipename); if (ffh == INVALID_HANDLE_VALUE) return false; FindClose(ffh); return true; } bool agent_exists(void) { return named_pipe_agent_exists() || wm_copydata_agent_exists(); } struct agent_pending_query { struct handle *handle; HANDLE os_handle; strbuf *response; void (*callback)(void *, void *, int); void *callback_ctx; }; static int named_pipe_agent_accumulate_response( strbuf *sb, const void *data, size_t len) { put_data(sb, data, len); if (sb->len >= 4) { uint32_t length_field = GET_32BIT_MSB_FIRST(sb->u); if (length_field > AGENT_MAX_MSGLEN) return -1; /* badly formatted message */ int overall_length = length_field + 4; if (sb->len >= overall_length) return overall_length; } return 0; /* not done yet */ } static size_t named_pipe_agent_gotdata( struct handle *h, const void *data, size_t len, int err) { agent_pending_query *pq = handle_get_privdata(h); if (err || len == 0) { pq->callback(pq->callback_ctx, NULL, 0); agent_cancel_query(pq); return 0; } int status = named_pipe_agent_accumulate_response(pq->response, data, len); if (status == -1) { pq->callback(pq->callback_ctx, NULL, 0); agent_cancel_query(pq); } else if (status > 0) { void *response_buf = strbuf_to_str(pq->response); pq->response = NULL; pq->callback(pq->callback_ctx, response_buf, status); agent_cancel_query(pq); } return 0; } static agent_pending_query *named_pipe_agent_query( strbuf *query, void **out, int *outlen, void (*callback)(void *, void *, int), void *callback_ctx) { agent_pending_query *pq = NULL; char *err = NULL, *pipename = NULL; strbuf *sb = NULL; HANDLE pipehandle; pipename = agent_named_pipe_name(); pipehandle = connect_to_named_pipe(pipename, &err); if (pipehandle == INVALID_HANDLE_VALUE) goto failure; strbuf_finalise_agent_query(query); for (DWORD done = 0; done < query->len ;) { DWORD nwritten; bool ret = WriteFile(pipehandle, query->s + done, query->len - done, &nwritten, NULL); if (!ret) goto failure; done += nwritten; } if (!callback) { int status; sb = strbuf_new_nm(); do { char buf[1024]; DWORD nread; bool ret = ReadFile(pipehandle, buf, sizeof(buf), &nread, NULL); if (!ret) goto failure; status = named_pipe_agent_accumulate_response(sb, buf, nread); } while (status == 0); if (status == -1) goto failure; *out = strbuf_to_str(sb); *outlen = status; sb = NULL; pq = NULL; goto out; } pq = snew(agent_pending_query); pq->handle = handle_input_new(pipehandle, named_pipe_agent_gotdata, pq, 0); pq->os_handle = pipehandle; pipehandle = INVALID_HANDLE_VALUE; /* prevent it being closed below */ pq->response = strbuf_new_nm(); pq->callback = callback; pq->callback_ctx = callback_ctx; goto out; failure: *out = NULL; *outlen = 0; pq = NULL; out: sfree(err); sfree(pipename); if (pipehandle != INVALID_HANDLE_VALUE) CloseHandle(pipehandle); if (sb) strbuf_free(sb); return pq; } void agent_cancel_query(agent_pending_query *pq) { handle_free(pq->handle); CloseHandle(pq->os_handle); if (pq->response) strbuf_free(pq->response); sfree(pq); } agent_pending_query *agent_query( strbuf *query, void **out, int *outlen, void (*callback)(void *, void *, int), void *callback_ctx) { agent_pending_query *pq = named_pipe_agent_query( query, out, outlen, callback, callback_ctx); if (pq || *out) return pq; wm_copydata_agent_query(query, out, outlen); return NULL; } #else /* NO_SECURITY */ Socket *agent_connect(void *vctx, Plug *plug) { unreachable("no agent_connect_ctx can be constructed on this platform"); } agent_connect_ctx *agent_get_connect_ctx(void) { return NULL; } void agent_free_connect_ctx(agent_connect_ctx *ctx) { } bool agent_exists(void) { return wm_copydata_agent_exists(); } agent_pending_query *agent_query( strbuf *query, void **out, int *outlen, void (*callback)(void *, void *, int), void *callback_ctx) { wm_copydata_agent_query(query, out, outlen); return NULL; } void agent_cancel_query(agent_pending_query *q) { unreachable("Windows agent queries are never asynchronous!"); } #endif /* NO_SECURITY */ putty-0.76/windows/winplink.c0000644000175000017500000004434514072266315013265 00000000000000/* * PLink - a Windows command-line (stdin/stdout) variant of PuTTY. */ #include #include #include #include #include "putty.h" #include "storage.h" #include "tree234.h" #include "winsecur.h" void cmdline_error(const char *fmt, ...) { va_list ap; va_start(ap, fmt); console_print_error_msg_fmt_v("plink", fmt, ap); va_end(ap); exit(1); } static HANDLE inhandle, outhandle, errhandle; static struct handle *stdin_handle, *stdout_handle, *stderr_handle; static handle_sink stdout_hs, stderr_hs; static StripCtrlChars *stdout_scc, *stderr_scc; static BinarySink *stdout_bs, *stderr_bs; static DWORD orig_console_mode; static Backend *backend; static LogContext *logctx; static Conf *conf; static void plink_echoedit_update(Seat *seat, bool echo, bool edit) { /* Update stdin read mode to reflect changes in line discipline. */ DWORD mode; mode = ENABLE_PROCESSED_INPUT; if (echo) mode = mode | ENABLE_ECHO_INPUT; else mode = mode & ~ENABLE_ECHO_INPUT; if (edit) mode = mode | ENABLE_LINE_INPUT; else mode = mode & ~ENABLE_LINE_INPUT; SetConsoleMode(inhandle, mode); } static size_t plink_output( Seat *seat, bool is_stderr, const void *data, size_t len) { BinarySink *bs = is_stderr ? stderr_bs : stdout_bs; put_data(bs, data, len); return handle_backlog(stdout_handle) + handle_backlog(stderr_handle); } static bool plink_eof(Seat *seat) { handle_write_eof(stdout_handle); return false; /* do not respond to incoming EOF with outgoing */ } static int plink_get_userpass_input(Seat *seat, prompts_t *p, bufchain *input) { int ret; ret = cmdline_get_passwd_input(p); if (ret == -1) ret = console_get_userpass_input(p); return ret; } static bool plink_seat_interactive(Seat *seat) { return (!*conf_get_str(conf, CONF_remote_cmd) && !*conf_get_str(conf, CONF_remote_cmd2) && !*conf_get_str(conf, CONF_ssh_nc_host)); } static const SeatVtable plink_seat_vt = { .output = plink_output, .eof = plink_eof, .get_userpass_input = plink_get_userpass_input, .notify_remote_exit = nullseat_notify_remote_exit, .connection_fatal = console_connection_fatal, .update_specials_menu = nullseat_update_specials_menu, .get_ttymode = nullseat_get_ttymode, .set_busy_status = nullseat_set_busy_status, .verify_ssh_host_key = console_verify_ssh_host_key, .confirm_weak_crypto_primitive = console_confirm_weak_crypto_primitive, .confirm_weak_cached_hostkey = console_confirm_weak_cached_hostkey, .is_utf8 = nullseat_is_never_utf8, .echoedit_update = plink_echoedit_update, .get_x_display = nullseat_get_x_display, .get_windowid = nullseat_get_windowid, .get_window_pixel_size = nullseat_get_window_pixel_size, .stripctrl_new = console_stripctrl_new, .set_trust_status = console_set_trust_status, .verbose = cmdline_seat_verbose, .interactive = plink_seat_interactive, .get_cursor_position = nullseat_get_cursor_position, }; static Seat plink_seat[1] = {{ &plink_seat_vt }}; static DWORD main_thread_id; /* * Short description of parameters. */ static void usage(void) { printf("Plink: command-line connection utility\n"); printf("%s\n", ver); printf("Usage: plink [options] [user@]host [command]\n"); printf(" (\"host\" can also be a PuTTY saved session name)\n"); printf("Options:\n"); printf(" -V print version information and exit\n"); printf(" -pgpfp print PGP key fingerprints and exit\n"); printf(" -v show verbose messages\n"); printf(" -load sessname Load settings from saved session\n"); printf(" -ssh -telnet -rlogin -raw -serial\n"); printf(" force use of a particular protocol\n"); printf(" -ssh-connection\n"); printf(" force use of the bare ssh-connection protocol\n"); printf(" -P port connect to specified port\n"); printf(" -l user connect with specified username\n"); printf(" -batch disable all interactive prompts\n"); printf(" -proxycmd command\n"); printf(" use 'command' as local proxy\n"); printf(" -sercfg configuration-string (e.g. 19200,8,n,1,X)\n"); printf(" Specify the serial configuration (serial only)\n"); printf("The following options only apply to SSH connections:\n"); printf(" -pw passw login with specified password\n"); printf(" -D [listen-IP:]listen-port\n"); printf(" Dynamic SOCKS-based port forwarding\n"); printf(" -L [listen-IP:]listen-port:host:port\n"); printf(" Forward local port to remote address\n"); printf(" -R [listen-IP:]listen-port:host:port\n"); printf(" Forward remote port to local address\n"); printf(" -X -x enable / disable X11 forwarding\n"); printf(" -A -a enable / disable agent forwarding\n"); printf(" -t -T enable / disable pty allocation\n"); printf(" -1 -2 force use of particular SSH protocol version\n"); printf(" -4 -6 force use of IPv4 or IPv6\n"); printf(" -C enable compression\n"); printf(" -i key private key file for user authentication\n"); printf(" -noagent disable use of Pageant\n"); printf(" -agent enable use of Pageant\n"); printf(" -no-trivial-auth\n"); printf(" disconnect if SSH authentication succeeds trivially\n"); printf(" -noshare disable use of connection sharing\n"); printf(" -share enable use of connection sharing\n"); printf(" -hostkey keyid\n"); printf(" manually specify a host key (may be repeated)\n"); printf(" -sanitise-stderr, -sanitise-stdout, " "-no-sanitise-stderr, -no-sanitise-stdout\n"); printf(" do/don't strip control chars from standard " "output/error\n"); printf(" -no-antispoof omit anti-spoofing prompt after " "authentication\n"); printf(" -m file read remote command(s) from file\n"); printf(" -s remote command is an SSH subsystem (SSH-2 only)\n"); printf(" -N don't start a shell/command (SSH-2 only)\n"); printf(" -nc host:port\n"); printf(" open tunnel in place of session (SSH-2 only)\n"); printf(" -sshlog file\n"); printf(" -sshrawlog file\n"); printf(" log protocol details to a file\n"); printf(" -logoverwrite\n"); printf(" -logappend\n"); printf(" control what happens when a log file already exists\n"); printf(" -shareexists\n"); printf(" test whether a connection-sharing upstream exists\n"); exit(1); } static void version(void) { char *buildinfo_text = buildinfo("\n"); printf("plink: %s\n%s\n", ver, buildinfo_text); sfree(buildinfo_text); exit(0); } size_t stdin_gotdata(struct handle *h, const void *data, size_t len, int err) { if (err) { char buf[4096]; FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err, 0, buf, lenof(buf), NULL); buf[lenof(buf)-1] = '\0'; if (buf[strlen(buf)-1] == '\n') buf[strlen(buf)-1] = '\0'; fprintf(stderr, "Unable to read from standard input: %s\n", buf); cleanup_exit(0); } noise_ultralight(NOISE_SOURCE_IOLEN, len); if (backend_connected(backend)) { if (len > 0) { return backend_send(backend, data, len); } else { backend_special(backend, SS_EOF, 0); return 0; } } else return 0; } void stdouterr_sent(struct handle *h, size_t new_backlog, int err) { if (err) { char buf[4096]; FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, err, 0, buf, lenof(buf), NULL); buf[lenof(buf)-1] = '\0'; if (buf[strlen(buf)-1] == '\n') buf[strlen(buf)-1] = '\0'; fprintf(stderr, "Unable to write to standard %s: %s\n", (h == stdout_handle ? "output" : "error"), buf); cleanup_exit(0); } if (backend_connected(backend)) { backend_unthrottle(backend, (handle_backlog(stdout_handle) + handle_backlog(stderr_handle))); } } const bool share_can_be_downstream = true; const bool share_can_be_upstream = true; const unsigned cmdline_tooltype = TOOLTYPE_HOST_ARG | TOOLTYPE_HOST_ARG_CAN_BE_SESSION | TOOLTYPE_HOST_ARG_PROTOCOL_PREFIX | TOOLTYPE_HOST_ARG_FROM_LAUNCHABLE_LOAD; static bool sending; static bool plink_mainloop_pre(void *vctx, const HANDLE **extra_handles, size_t *n_extra_handles) { if (!sending && backend_sendok(backend)) { stdin_handle = handle_input_new(inhandle, stdin_gotdata, NULL, 0); sending = true; } return true; } static bool plink_mainloop_post(void *vctx, size_t extra_handle_index) { if (sending) handle_unthrottle(stdin_handle, backend_sendbuffer(backend)); if (!backend_connected(backend) && handle_backlog(stdout_handle) + handle_backlog(stderr_handle) == 0) return false; /* we closed the connection */ return true; } int main(int argc, char **argv) { int exitcode; bool errors; bool use_subsystem = false; bool just_test_share_exists = false; enum TriState sanitise_stdout = AUTO, sanitise_stderr = AUTO; const struct BackendVtable *vt; dll_hijacking_protection(); /* * Initialise port and protocol to sensible defaults. (These * will be overridden by more or less anything.) */ settings_set_default_protocol(PROT_SSH); settings_set_default_port(22); /* * Process the command line. */ conf = conf_new(); do_defaults(NULL, conf); settings_set_default_protocol(conf_get_int(conf, CONF_protocol)); settings_set_default_port(conf_get_int(conf, CONF_port)); errors = false; { /* * Override the default protocol if PLINK_PROTOCOL is set. */ char *p = getenv("PLINK_PROTOCOL"); if (p) { const struct BackendVtable *vt = backend_vt_from_name(p); if (vt) { settings_set_default_protocol(vt->protocol); settings_set_default_port(vt->default_port); conf_set_int(conf, CONF_protocol, vt->protocol); conf_set_int(conf, CONF_port, vt->default_port); } } } while (--argc) { char *p = *++argv; 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 = true; } else if (ret == 2) { --argc, ++argv; } else if (ret == 1) { continue; } else if (!strcmp(p, "-batch")) { console_batch_mode = true; } else if (!strcmp(p, "-s")) { /* Save status to write to conf later. */ use_subsystem = true; } 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 if (!strcmp(p, "-shareexists")) { just_test_share_exists = true; } else if (!strcmp(p, "-sanitise-stdout") || !strcmp(p, "-sanitize-stdout")) { sanitise_stdout = FORCE_ON; } else if (!strcmp(p, "-no-sanitise-stdout") || !strcmp(p, "-no-sanitize-stdout")) { sanitise_stdout = FORCE_OFF; } else if (!strcmp(p, "-sanitise-stderr") || !strcmp(p, "-sanitize-stderr")) { sanitise_stderr = FORCE_ON; } else if (!strcmp(p, "-no-sanitise-stderr") || !strcmp(p, "-no-sanitize-stderr")) { sanitise_stderr = FORCE_OFF; } else if (!strcmp(p, "-no-antispoof")) { console_antispoof_prompt = false; } else if (*p != '-') { strbuf *cmdbuf = strbuf_new(); while (argc > 0) { if (cmdbuf->len > 0) put_byte(cmdbuf, ' '); /* add space separator */ put_datapl(cmdbuf, ptrlen_from_asciz(p)); if (--argc > 0) p = *++argv; } conf_set_str(conf, CONF_remote_cmd, cmdbuf->s); conf_set_str(conf, CONF_remote_cmd2, ""); conf_set_bool(conf, CONF_nopty, true); /* command => no tty */ strbuf_free(cmdbuf); break; /* done with cmdline */ } else { fprintf(stderr, "plink: unknown option \"%s\"\n", p); errors = true; } } if (errors) return 1; if (!cmdline_host_ok(conf)) { usage(); } prepare_session(conf); /* * Perform command-line overrides on session configuration. */ cmdline_run_saved(conf); /* * Apply subsystem status. */ if (use_subsystem) conf_set_bool(conf, CONF_ssh_subsys, true); /* * Select protocol. This is farmed out into a table in a * separate file to enable an ssh-free variant. */ vt = backend_vt_from_proto(conf_get_int(conf, CONF_protocol)); if (vt == NULL) { fprintf(stderr, "Internal fault: Unsupported protocol found\n"); return 1; } if (vt->flags & BACKEND_NEEDS_TERMINAL) { fprintf(stderr, "Plink doesn't support %s, which needs terminal emulation\n", vt->displayname); return 1; } sk_init(); if (p_WSAEventSelect == NULL) { fprintf(stderr, "Plink requires WinSock 2\n"); return 1; } /* * 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_bool(conf, CONF_x11_forward) && !conf_get_bool(conf, CONF_agentfwd) && !conf_get_str_nthstrkey(conf, CONF_portfwd, 0)) conf_set_bool(conf, CONF_ssh_simple, true); logctx = log_init(console_cli_logpolicy, conf); if (just_test_share_exists) { if (!vt->test_for_upstream) { fprintf(stderr, "Connection sharing not supported for this " "connection type (%s)'\n", vt->displayname); return 1; } if (vt->test_for_upstream(conf_get_str(conf, CONF_host), conf_get_int(conf, CONF_port), conf)) return 0; else return 1; } if (restricted_acl()) { lp_eventlog(console_cli_logpolicy, "Running with restricted process ACL"); } inhandle = GetStdHandle(STD_INPUT_HANDLE); outhandle = GetStdHandle(STD_OUTPUT_HANDLE); errhandle = GetStdHandle(STD_ERROR_HANDLE); /* * Turn off ECHO and LINE input modes. We don't care if this * call fails, because we know we aren't necessarily running in * a console. */ GetConsoleMode(inhandle, &orig_console_mode); SetConsoleMode(inhandle, ENABLE_PROCESSED_INPUT); /* * Pass the output handles to the handle-handling subsystem. * (The input one we leave until we're through the * authentication process.) */ stdout_handle = handle_output_new(outhandle, stdouterr_sent, NULL, 0); stderr_handle = handle_output_new(errhandle, stdouterr_sent, NULL, 0); handle_sink_init(&stdout_hs, stdout_handle); handle_sink_init(&stderr_hs, stderr_handle); stdout_bs = BinarySink_UPCAST(&stdout_hs); stderr_bs = BinarySink_UPCAST(&stderr_hs); /* * Decide whether to sanitise control sequences out of standard * output and standard error. * * If we weren't given a command-line override, we do this if (a) * the fd in question is pointing at a console, and (b) we aren't * trying to allocate a terminal as part of the session. * * (Rationale: the risk of control sequences is that they cause * confusion when sent to a local console, so if there isn't one, * no problem. Also, if we allocate a remote terminal, then we * sent a terminal type, i.e. we told it what kind of escape * sequences we _like_, i.e. we were expecting to receive some.) */ if (sanitise_stdout == FORCE_ON || (sanitise_stdout == AUTO && is_console_handle(outhandle) && conf_get_bool(conf, CONF_nopty))) { stdout_scc = stripctrl_new(stdout_bs, true, L'\0'); stdout_bs = BinarySink_UPCAST(stdout_scc); } if (sanitise_stderr == FORCE_ON || (sanitise_stderr == AUTO && is_console_handle(errhandle) && conf_get_bool(conf, CONF_nopty))) { stderr_scc = stripctrl_new(stderr_bs, true, L'\0'); stderr_bs = BinarySink_UPCAST(stderr_scc); } /* * Start up the connection. */ winselcli_setup(); /* ensure event object exists */ { char *error, *realhost; /* nodelay is only useful if stdin is a character device (console) */ bool nodelay = conf_get_bool(conf, CONF_tcp_nodelay) && (GetFileType(GetStdHandle(STD_INPUT_HANDLE)) == FILE_TYPE_CHAR); error = backend_init(vt, plink_seat, &backend, logctx, conf, conf_get_str(conf, CONF_host), conf_get_int(conf, CONF_port), &realhost, nodelay, conf_get_bool(conf, CONF_tcp_keepalives)); if (error) { fprintf(stderr, "Unable to open connection:\n%s", error); sfree(error); return 1; } ldisc_create(conf, NULL, backend, plink_seat); sfree(realhost); } main_thread_id = GetCurrentThreadId(); sending = false; cli_main_loop(plink_mainloop_pre, plink_mainloop_post, NULL); exitcode = backend_exitcode(backend); if (exitcode < 0) { fprintf(stderr, "Remote process exit code unavailable\n"); exitcode = 1; /* this is an error condition */ } cleanup_exit(exitcode); return 0; /* placate compiler warning */ } putty-0.76/windows/winprint.c0000644000175000017500000001370314072266315013276 00000000000000/* * Printing interface for PuTTY. */ #include "putty.h" #include struct printer_enum_tag { int nprinters; DWORD enum_level; union { LPPRINTER_INFO_4 i4; LPPRINTER_INFO_5 i5; } info; }; struct printer_job_tag { HANDLE hprinter; }; DECL_WINDOWS_FUNCTION(static, BOOL, EnumPrinters, (DWORD, LPTSTR, DWORD, LPBYTE, DWORD, LPDWORD, LPDWORD)); DECL_WINDOWS_FUNCTION(static, BOOL, OpenPrinter, (LPTSTR, LPHANDLE, LPPRINTER_DEFAULTS)); DECL_WINDOWS_FUNCTION(static, BOOL, ClosePrinter, (HANDLE)); DECL_WINDOWS_FUNCTION(static, DWORD, StartDocPrinter, (HANDLE, DWORD, LPBYTE)); DECL_WINDOWS_FUNCTION(static, BOOL, EndDocPrinter, (HANDLE)); DECL_WINDOWS_FUNCTION(static, BOOL, StartPagePrinter, (HANDLE)); DECL_WINDOWS_FUNCTION(static, BOOL, EndPagePrinter, (HANDLE)); DECL_WINDOWS_FUNCTION(static, BOOL, WritePrinter, (HANDLE, LPVOID, DWORD, LPDWORD)); static void init_winfuncs(void) { static bool initialised = false; if (initialised) return; { HMODULE winspool_module = load_system32_dll("winspool.drv"); /* Some MSDN documentation claims that some of the below functions * should be loaded from spoolss.dll, but this doesn't seem to * be reliable in practice. * Nevertheless, we load spoolss.dll ourselves using our safe * loading method, against the possibility that winspool.drv * later loads it unsafely. */ (void) load_system32_dll("spoolss.dll"); GET_WINDOWS_FUNCTION_PP(winspool_module, EnumPrinters); GET_WINDOWS_FUNCTION_PP(winspool_module, OpenPrinter); GET_WINDOWS_FUNCTION_PP(winspool_module, ClosePrinter); GET_WINDOWS_FUNCTION_PP(winspool_module, StartDocPrinter); GET_WINDOWS_FUNCTION_PP(winspool_module, EndDocPrinter); GET_WINDOWS_FUNCTION_PP(winspool_module, StartPagePrinter); GET_WINDOWS_FUNCTION_PP(winspool_module, EndPagePrinter); GET_WINDOWS_FUNCTION_PP(winspool_module, WritePrinter); } initialised = true; } static bool printer_add_enum(int param, DWORD level, char **buffer, int offset, int *nprinters_ptr) { DWORD needed = 0, nprinters = 0; init_winfuncs(); *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. */ p_EnumPrinters(param, NULL, level, (LPBYTE)((*buffer)+offset), 512, &needed, &nprinters); if (needed < 512) needed = 512; *buffer = sresize(*buffer, offset+needed, char); if (p_EnumPrinters(param, NULL, level, (LPBYTE)((*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 (osPlatformId != 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; break; } ret->nprinters = *nprinters_ptr; return ret; error: sfree(buffer); sfree(ret); *nprinters_ptr = 0; return NULL; } char *printer_get_name(printer_enum *pe, int i) { if (!pe) return NULL; if (i < 0 || i >= pe->nprinters) return NULL; switch (pe->enum_level) { case 4: return pe->info.i4[i].pPrinterName; case 5: return pe->info.i5[i].pPrinterName; default: return NULL; } } void printer_finish_enum(printer_enum *pe) { if (!pe) return; switch (pe->enum_level) { case 4: sfree(pe->info.i4); break; case 5: sfree(pe->info.i5); break; } sfree(pe); } printer_job *printer_start_job(char *printer) { printer_job *ret = snew(printer_job); DOC_INFO_1 docinfo; bool jobstarted = false, pagestarted = false; init_winfuncs(); ret->hprinter = NULL; if (!p_OpenPrinter(printer, &ret->hprinter, NULL)) goto error; docinfo.pDocName = "PuTTY remote printer output"; docinfo.pOutputFile = NULL; docinfo.pDatatype = "RAW"; if (!p_StartDocPrinter(ret->hprinter, 1, (LPBYTE)&docinfo)) goto error; jobstarted = true; if (!p_StartPagePrinter(ret->hprinter)) goto error; pagestarted = true; return ret; error: if (pagestarted) p_EndPagePrinter(ret->hprinter); if (jobstarted) p_EndDocPrinter(ret->hprinter); if (ret->hprinter) p_ClosePrinter(ret->hprinter); sfree(ret); return NULL; } void printer_job_data(printer_job *pj, const void *data, size_t len) { DWORD written; if (!pj) return; p_WritePrinter(pj->hprinter, (void *)data, len, &written); } void printer_finish_job(printer_job *pj) { if (!pj) return; p_EndPagePrinter(pj->hprinter); p_EndDocPrinter(pj->hprinter); p_ClosePrinter(pj->hprinter); sfree(pj); } putty-0.76/windows/winproxy.c0000644000175000017500000000623114072266315013321 00000000000000/* * winproxy.c: Windows implementation of platform_new_connection(), * supporting an OpenSSH-like proxy command via the winhandl.c * mechanism. */ #include #include #include "tree234.h" #include "putty.h" #include "network.h" #include "proxy.h" Socket *platform_new_connection(SockAddr *addr, const char *hostname, int port, bool privport, bool oobinline, bool nodelay, bool keepalive, Plug *plug, Conf *conf) { char *cmd; HANDLE us_to_cmd, cmd_from_us; HANDLE us_from_cmd, cmd_to_us; HANDLE us_from_cmd_err, cmd_err_to_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); plug_log(plug, PLUGLOG_PROXY_MSG, NULL, 0, msg, 0); 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)) { sfree(cmd); return new_error_socket_fmt( plug, "Unable to create pipes for proxy command: %s", win_strerror(GetLastError())); } if (!CreatePipe(&cmd_from_us, &us_to_cmd, &sa, 0)) { sfree(cmd); CloseHandle(us_from_cmd); CloseHandle(cmd_to_us); return new_error_socket_fmt( plug, "Unable to create pipes for proxy command: %s", win_strerror(GetLastError())); } if (!CreatePipe(&us_from_cmd_err, &cmd_err_to_us, &sa, 0)) { sfree(cmd); CloseHandle(us_from_cmd); CloseHandle(cmd_to_us); CloseHandle(us_to_cmd); CloseHandle(cmd_from_us); return new_error_socket_fmt( plug, "Unable to create pipes for proxy command: %s", win_strerror(GetLastError())); } SetHandleInformation(us_to_cmd, HANDLE_FLAG_INHERIT, 0); SetHandleInformation(us_from_cmd, HANDLE_FLAG_INHERIT, 0); if (us_from_cmd_err != NULL) SetHandleInformation(us_from_cmd_err, 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 = cmd_err_to_us; 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); if (cmd_err_to_us != NULL) CloseHandle(cmd_err_to_us); return make_handle_socket(us_to_cmd, us_from_cmd, us_from_cmd_err, plug, false); } putty-0.76/windows/winseat.h0000644000175000017500000000043714072266315013103 00000000000000/* * Small implementation of Seat and LogPolicy shared between window.c * and windlg.c. */ typedef struct WinGuiSeat WinGuiSeat; struct WinGuiSeat { HWND term_hwnd; Seat seat; LogPolicy logpolicy; }; extern const LogPolicyVtable win_gui_logpolicy_vt; /* in windlg.c */ putty-0.76/windows/winsecur.c0000644000175000017500000002306514072266315013265 00000000000000/* * winsecur.c: implementation of winsecur.h. */ #include #include #include "putty.h" #if !defined NO_SECURITY #include "winsecur.h" /* Initialised once, then kept around to reuse forever */ static PSID worldsid, networksid, usersid; DEF_WINDOWS_FUNCTION(OpenProcessToken); DEF_WINDOWS_FUNCTION(GetTokenInformation); DEF_WINDOWS_FUNCTION(InitializeSecurityDescriptor); DEF_WINDOWS_FUNCTION(SetSecurityDescriptorOwner); DEF_WINDOWS_FUNCTION(GetSecurityInfo); DEF_WINDOWS_FUNCTION(SetSecurityInfo); DEF_WINDOWS_FUNCTION(SetEntriesInAclA); bool got_advapi(void) { static bool attempted = false; static bool 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, SetSecurityInfo) && 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; } PSID get_user_sid(void) { HANDLE proc = NULL, tok = NULL; TOKEN_USER *user = NULL; DWORD toklen, sidlen; PSID sid = NULL, ret = NULL; if (usersid) return usersid; 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 = usersid = 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; } static bool getsids(char **error) { #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wmissing-braces" #endif SID_IDENTIFIER_AUTHORITY world_auth = SECURITY_WORLD_SID_AUTHORITY; SID_IDENTIFIER_AUTHORITY nt_auth = SECURITY_NT_AUTHORITY; #ifdef __clang__ #pragma clang diagnostic pop #endif bool ret = false; *error = NULL; if (!usersid) { if ((usersid = get_user_sid()) == NULL) { *error = dupprintf("unable to construct SID for current user: %s", win_strerror(GetLastError())); goto cleanup; } } if (!worldsid) { if (!AllocateAndInitializeSid(&world_auth, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &worldsid)) { *error = dupprintf("unable to construct SID for world: %s", win_strerror(GetLastError())); goto cleanup; } } if (!networksid) { 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; } } ret = true; cleanup: return ret; } bool make_private_security_descriptor(DWORD permissions, PSECURITY_DESCRIPTOR *psd, PACL *acl, char **error) { EXPLICIT_ACCESS ea[3]; int acl_err; bool ret = false; *psd = NULL; *acl = NULL; *error = NULL; if (!getsids(error)) 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_SID; ea[0].Trustee.ptstrName = (LPTSTR)worldsid; ea[1].grfAccessPermissions = permissions; ea[1].grfAccessMode = GRANT_ACCESS; ea[1].grfInheritance = NO_INHERITANCE; ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID; ea[1].Trustee.ptstrName = (LPTSTR)usersid; 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; acl_err = p_SetEntriesInAclA(3, ea, NULL, acl); if (acl_err != ERROR_SUCCESS || *acl == NULL) { *error = dupprintf("unable to construct ACL: %s", win_strerror(acl_err)); 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 (!SetSecurityDescriptorOwner(*psd, usersid, false)) { *error = dupprintf("unable to set owner in 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 (*acl) { LocalFree(*acl); *acl = NULL; } } else { sfree(*error); *error = NULL; } return ret; } static bool acl_restricted = false; bool restricted_acl(void) { return acl_restricted; } static bool really_restrict_process_acl(char **error) { EXPLICIT_ACCESS ea[2]; int acl_err; bool ret = false; PACL acl = NULL; static const DWORD nastyace=WRITE_DAC | WRITE_OWNER | PROCESS_CREATE_PROCESS | PROCESS_CREATE_THREAD | PROCESS_DUP_HANDLE | PROCESS_SET_QUOTA | PROCESS_SET_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_SUSPEND_RESUME; if (!getsids(error)) goto cleanup; memset(ea, 0, sizeof(ea)); /* Everyone: deny */ ea[0].grfAccessPermissions = nastyace; ea[0].grfAccessMode = DENY_ACCESS; ea[0].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT; ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID; ea[0].Trustee.ptstrName = (LPTSTR)worldsid; /* User: user ace */ ea[1].grfAccessPermissions = ~nastyace & 0x1fff; ea[1].grfAccessMode = GRANT_ACCESS; ea[1].grfInheritance = SUB_CONTAINERS_AND_OBJECTS_INHERIT; ea[1].Trustee.TrusteeForm = TRUSTEE_IS_SID; ea[1].Trustee.ptstrName = (LPTSTR)usersid; acl_err = p_SetEntriesInAclA(2, ea, NULL, &acl); if (acl_err != ERROR_SUCCESS || acl == NULL) { *error = dupprintf("unable to construct ACL: %s", win_strerror(acl_err)); goto cleanup; } if (ERROR_SUCCESS != p_SetSecurityInfo (GetCurrentProcess(), SE_KERNEL_OBJECT, OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, usersid, NULL, acl, NULL)) { *error = dupprintf("Unable to set process ACL: %s", win_strerror(GetLastError())); goto cleanup; } acl_restricted = true; ret=true; cleanup: if (!ret) { if (acl) { LocalFree(acl); acl = NULL; } } return ret; } #endif /* !defined NO_SECURITY */ /* * Lock down our process's ACL, to present an obstacle to malware * trying to write into its memory. This can't be a full defence, * because well timed malware could attack us before this code runs - * even if it was unconditionally run at the very start of main(), * which we wouldn't want to do anyway because it turns out in practie * that interfering with other processes in this way has significant * non-infringing uses on Windows (e.g. screen reader software). * * If we've been requested to do this and are unsuccessful, bomb out * via modalfatalbox rather than continue in a less protected mode. * * This function is intentionally outside the #ifndef NO_SECURITY that * covers the rest of this file, because when PuTTY is compiled * without the ability to restrict its ACL, we don't want it to * silently pretend to honour the instruction to do so. */ void restrict_process_acl(void) { char *error = NULL; bool ret; #if !defined NO_SECURITY ret = really_restrict_process_acl(&error); #else ret = false; error = dupstr("ACL restrictions not compiled into this binary"); #endif if (!ret) modalfatalbox("Could not restrict process ACL: %s", error); } putty-0.76/windows/winsecur.h0000644000175000017500000000406514072266315013271 00000000000000/* * 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 /* * Functions loaded from advapi32.dll. */ DECL_WINDOWS_FUNCTION(extern, BOOL, OpenProcessToken, (HANDLE, DWORD, PHANDLE)); DECL_WINDOWS_FUNCTION(extern, BOOL, GetTokenInformation, (HANDLE, TOKEN_INFORMATION_CLASS, LPVOID, DWORD, PDWORD)); DECL_WINDOWS_FUNCTION(extern, BOOL, InitializeSecurityDescriptor, (PSECURITY_DESCRIPTOR, DWORD)); DECL_WINDOWS_FUNCTION(extern, BOOL, SetSecurityDescriptorOwner, (PSECURITY_DESCRIPTOR, PSID, BOOL)); DECL_WINDOWS_FUNCTION(extern, DWORD, GetSecurityInfo, (HANDLE, SE_OBJECT_TYPE, SECURITY_INFORMATION, PSID *, PSID *, PACL *, PACL *, PSECURITY_DESCRIPTOR *)); DECL_WINDOWS_FUNCTION(extern, DWORD, SetSecurityInfo, (HANDLE, SE_OBJECT_TYPE, SECURITY_INFORMATION, PSID, PSID, PACL, PACL)); DECL_WINDOWS_FUNCTION(extern, DWORD, SetEntriesInAclA, (ULONG, PEXPLICIT_ACCESS, PACL, PACL *)); bool got_advapi(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' and 'acl' will 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. */ bool make_private_security_descriptor( DWORD permissions, PSECURITY_DESCRIPTOR *psd, PACL *acl, char **error); #endif putty-0.76/windows/winselcli.c0000644000175000017500000000376314072266315013422 00000000000000/* * Implementation of do_select() for winnet.c to use, suitable for use * when there's no GUI window to have network activity reported to. * * It uses WSAEventSelect, where available, to convert network * activity into activity on an event object, for integration into an * event loop that includes WaitForMultipleObjects. * * It also maintains a list of currently active sockets, which can be * retrieved by a front end that wants to use WinSock's synchronous * select() function. */ #include "putty.h" static tree234 *winselcli_sockets; static int socket_cmp(void *av, void *bv) { return memcmp(av, bv, sizeof(SOCKET)); } HANDLE winselcli_event = INVALID_HANDLE_VALUE; void winselcli_setup(void) { if (!winselcli_sockets) winselcli_sockets = newtree234(socket_cmp); if (p_WSAEventSelect && winselcli_event == INVALID_HANDLE_VALUE) winselcli_event = CreateEvent(NULL, false, false, NULL); } SOCKET winselcli_unique_socket(void) { if (!winselcli_sockets) return INVALID_SOCKET; assert(count234(winselcli_sockets) <= 1); SOCKET *p = index234(winselcli_sockets, 0); if (!p) return INVALID_SOCKET; return *p; } const char *do_select(SOCKET skt, bool enable) { /* Check everything's been set up, for convenience of callers. */ winselcli_setup(); if (enable) { SOCKET *ptr = snew(SOCKET); *ptr = skt; if (add234(winselcli_sockets, ptr) != ptr) sfree(ptr); /* already there */ } else { SOCKET *ptr = del234(winselcli_sockets, &skt); if (ptr) sfree(ptr); } if (p_WSAEventSelect) { int events; if (enable) { events = (FD_CONNECT | FD_READ | FD_WRITE | FD_OOB | FD_CLOSE | FD_ACCEPT); } else { events = 0; } if (p_WSAEventSelect(skt, winselcli_event, events) == SOCKET_ERROR) return winsock_error_string(p_WSAGetLastError()); } return NULL; } putty-0.76/windows/winselgui.c0000644000175000017500000000144014072266315013425 00000000000000/* * Implementation of do_select() for winnet.c to use, that uses * WSAAsyncSelect to convert network activity into window messages, * for integration into a GUI event loop. */ #include "putty.h" static HWND winsel_hwnd = NULL; void winselgui_set_hwnd(HWND hwnd) { winsel_hwnd = hwnd; } void winselgui_clear_hwnd(void) { winsel_hwnd = NULL; } const char *do_select(SOCKET skt, bool enable) { int msg, events; if (enable) { msg = WM_NETEVENT; events = (FD_CONNECT | FD_READ | FD_WRITE | FD_OOB | FD_CLOSE | FD_ACCEPT); } else { msg = events = 0; } assert(winsel_hwnd); if (p_WSAAsyncSelect(skt, winsel_hwnd, msg, events) == SOCKET_ERROR) return winsock_error_string(p_WSAGetLastError()); return NULL; } putty-0.76/windows/winser.c0000644000175000017500000003307714072266315012741 00000000000000/* * Serial back end (Windows-specific). */ #include #include #include #include "putty.h" #define SERIAL_MAX_BACKLOG 4096 typedef struct Serial Serial; struct Serial { HANDLE port; struct handle *out, *in; Seat *seat; LogContext *logctx; int bufsize; long clearbreak_time; bool break_in_progress; Backend backend; }; static void serial_terminate(Serial *serial) { if (serial->out) { handle_free(serial->out); serial->out = NULL; } if (serial->in) { handle_free(serial->in); serial->in = NULL; } if (serial->port != INVALID_HANDLE_VALUE) { if (serial->break_in_progress) ClearCommBreak(serial->port); CloseHandle(serial->port); serial->port = INVALID_HANDLE_VALUE; } } static size_t serial_gotdata( struct handle *h, const void *data, size_t len, int err) { Serial *serial = (Serial *)handle_get_privdata(h); if (err || len == 0) { const char *error_msg; /* * Currently, len==0 should never happen because we're * ignoring EOFs. However, it seems not totally impossible * that this same back end might be usable to talk to named * pipes or some other non-serial device, in which case EOF * may become meaningful here. */ if (!err) error_msg = "End of file reading from serial device"; else error_msg = "Error reading from serial device"; serial_terminate(serial); seat_notify_remote_exit(serial->seat); logevent(serial->logctx, error_msg); seat_connection_fatal(serial->seat, "%s", error_msg); return 0; } else { return seat_stdout(serial->seat, data, len); } } static void serial_sentdata(struct handle *h, size_t new_backlog, int err) { Serial *serial = (Serial *)handle_get_privdata(h); if (err) { const char *error_msg = "Error writing to serial device"; serial_terminate(serial); seat_notify_remote_exit(serial->seat); logevent(serial->logctx, error_msg); seat_connection_fatal(serial->seat, "%s", error_msg); } else { serial->bufsize = new_backlog; } } static 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 * user might have pointed us at some other type of two-way * device instead of a serial port. */ if (GetCommState(serport, &dcb)) { const char *str; /* * Boilerplate. */ dcb.fBinary = true; dcb.fDtrControl = DTR_CONTROL_ENABLE; dcb.fDsrSensitivity = false; dcb.fTXContinueOnXoff = false; dcb.fOutX = false; dcb.fInX = false; dcb.fErrorChar = false; dcb.fNull = false; dcb.fRtsControl = RTS_CONTROL_ENABLE; dcb.fAbortOnError = false; dcb.fOutxCtsFlow = false; dcb.fOutxDsrFlow = false; /* * Configurable parameters. */ dcb.BaudRate = conf_get_int(conf, CONF_serspeed); logeventf(serial->logctx, "Configuring baud rate %lu", (unsigned long)dcb.BaudRate); dcb.ByteSize = conf_get_int(conf, CONF_serdatabits); logeventf(serial->logctx, "Configuring %u data bits", (unsigned)dcb.ByteSize); switch (conf_get_int(conf, CONF_serstopbits)) { case 2: dcb.StopBits = ONESTOPBIT; str = "1 stop bit"; break; case 3: dcb.StopBits = ONE5STOPBITS; str = "1.5 stop bits"; break; case 4: dcb.StopBits = TWOSTOPBITS; str = "2 stop bits"; break; default: return dupstr("Invalid number of stop bits " "(need 1, 1.5 or 2)"); } logeventf(serial->logctx, "Configuring %s", str); 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; } logeventf(serial->logctx, "Configuring %s parity", str); 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; case SER_FLOW_RTSCTS: dcb.fRtsControl = RTS_CONTROL_HANDSHAKE; dcb.fOutxCtsFlow = true; str = "RTS/CTS"; break; case SER_FLOW_DSRDTR: dcb.fDtrControl = DTR_CONTROL_HANDSHAKE; dcb.fOutxDsrFlow = true; str = "DSR/DTR"; break; } logeventf(serial->logctx, "Configuring %s flow control", str); if (!SetCommState(serport, &dcb)) return dupprintf("Configuring serial port: %s", win_strerror(GetLastError())); timeouts.ReadIntervalTimeout = 1; timeouts.ReadTotalTimeoutMultiplier = 0; timeouts.ReadTotalTimeoutConstant = 0; timeouts.WriteTotalTimeoutMultiplier = 0; timeouts.WriteTotalTimeoutConstant = 0; if (!SetCommTimeouts(serport, &timeouts)) return dupprintf("Configuring serial timeouts: %s", win_strerror(GetLastError())); } return NULL; } /* * Called to set up the serial 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 char *serial_init(const BackendVtable *vt, Seat *seat, Backend **backend_handle, LogContext *logctx, Conf *conf, const char *host, int port, char **realhost, bool nodelay, bool keepalive) { Serial *serial; HANDLE serport; char *err; char *serline; /* No local authentication phase in this protocol */ seat_set_trust_status(seat, false); serial = snew(Serial); serial->port = INVALID_HANDLE_VALUE; serial->out = serial->in = NULL; serial->bufsize = 0; serial->break_in_progress = false; serial->backend.vt = vt; *backend_handle = &serial->backend; serial->seat = seat; serial->logctx = logctx; serline = conf_get_str(conf, CONF_serline); logeventf(serial->logctx, "Opening serial device %s", serline); /* * Munge the string supplied by the user into a Windows filename. * * Windows supports opening a few "legacy" devices (including * COM1-9) by specifying their names verbatim as a filename to * open. (Thus, no files can ever have these names. See * * ("Naming a File") for the complete list of reserved names.) * * However, this doesn't let you get at devices COM10 and above. * For that, you need to specify a filename like "\\.\COM10". * This is also necessary for special serial and serial-like * devices such as \\.\WCEUSBSH001. It also works for the "legacy" * names, so you can do \\.\COM1 (verified as far back as Win95). * See * (CreateFile() docs). * * So, we believe that prepending "\\.\" should always be the * Right Thing. However, just in case someone finds something to * talk to that doesn't exist under there, if the serial line * contains a backslash, we use it verbatim. (This also lets * existing configurations using \\.\ continue working.) */ char *serfilename = dupprintf("%s%s", strchr(serline, '\\') ? "" : "\\\\.\\", serline); serport = CreateFile(serfilename, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); if (serport == INVALID_HANDLE_VALUE) { err = dupprintf("Opening '%s': %s", serfilename, win_strerror(GetLastError())); sfree(serfilename); return err; } sfree(serfilename); 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. */ seat_update_specials_menu(serial->seat); return NULL; } static void serial_free(Backend *be) { Serial *serial = container_of(be, Serial, backend); serial_terminate(serial); expire_timer_context(serial); sfree(serial); } static void serial_reconfig(Backend *be, Conf *conf) { Serial *serial = container_of(be, Serial, backend); serial_configure(serial, serial->port, conf); /* * FIXME: what should we do if that call returned a non-NULL error * message? */ } /* * Called to send data down the serial connection. */ static size_t serial_send(Backend *be, const char *buf, size_t len) { Serial *serial = container_of(be, Serial, backend); if (serial->out == NULL) return 0; serial->bufsize = handle_write(serial->out, buf, len); return serial->bufsize; } /* * Called to query the current sendability status. */ static size_t serial_sendbuffer(Backend *be) { Serial *serial = container_of(be, Serial, backend); return serial->bufsize; } /* * Called to set the size of the window */ static void serial_size(Backend *be, 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->logctx, "Finished serial break"); } } /* * Send serial special codes. */ static void serial_special(Backend *be, SessionSpecialCode code, int arg) { Serial *serial = container_of(be, Serial, backend); if (serial->port && code == SS_BRK) { logevent(serial->logctx, "Starting serial break at user request"); SetCommBreak(serial->port); /* * To send a serial break on Windows, we call SetCommBreak * to begin the break, then wait a bit, and then call * ClearCommBreak to finish it. Hence, I must use timing.c * to arrange a callback when it's time to do the latter. * * SUS says that a default break length must be between 1/4 * and 1/2 second. FreeBSD apparently goes with 2/5 second, * and so will I. */ serial->clearbreak_time = schedule_timer(TICKSPERSEC * 2 / 5, serbreak_timer, serial); serial->break_in_progress = true; } return; } /* * Return a list of the special codes that make sense in this * protocol. */ static const SessionSpecial *serial_get_specials(Backend *be) { static const SessionSpecial specials[] = { {"Break", SS_BRK}, {NULL, SS_EXITMENU} }; return specials; } static bool serial_connected(Backend *be) { return true; /* always connected */ } static bool serial_sendok(Backend *be) { return true; } static void serial_unthrottle(Backend *be, size_t backlog) { Serial *serial = container_of(be, Serial, backend); if (serial->in) handle_unthrottle(serial->in, backlog); } static bool serial_ldisc(Backend *be, int option) { /* * Local editing and local echo are off by default. */ return false; } static void serial_provide_ldisc(Backend *be, Ldisc *ldisc) { /* This is a stub. */ } static int serial_exitcode(Backend *be) { Serial *serial = container_of(be, Serial, backend); if (serial->port != INVALID_HANDLE_VALUE) return -1; /* still connected */ else /* Exit codes are a meaningless concept with serial ports */ return INT_MAX; } /* * cfg_info for Serial does nothing at all. */ static int serial_cfg_info(Backend *be) { return 0; } const BackendVtable serial_backend = { .init = serial_init, .free = serial_free, .reconfig = serial_reconfig, .send = serial_send, .sendbuffer = serial_sendbuffer, .size = serial_size, .special = serial_special, .get_specials = serial_get_specials, .connected = serial_connected, .exitcode = serial_exitcode, .sendok = serial_sendok, .ldisc_option_state = serial_ldisc, .provide_ldisc = serial_provide_ldisc, .unthrottle = serial_unthrottle, .cfg_info = serial_cfg_info, .id = "serial", .displayname = "Serial", .protocol = PROT_SERIAL, .serial_parity_mask = ((1 << SER_PAR_NONE) | (1 << SER_PAR_ODD) | (1 << SER_PAR_EVEN) | (1 << SER_PAR_MARK) | (1 << SER_PAR_SPACE)), .serial_flow_mask = ((1 << SER_FLOW_NONE) | (1 << SER_FLOW_XONXOFF) | (1 << SER_FLOW_RTSCTS) | (1 << SER_FLOW_DSRDTR)), }; putty-0.76/windows/winsftp.c0000644000175000017500000003571114072266315013121 00000000000000/* * winsftp.c: the Windows-specific parts of PSFTP and PSCP. */ #include /* need to put this first, for winelib builds */ #include #define NEED_DECLARATION_OF_SELECT #include "putty.h" #include "psftp.h" #include "ssh.h" #include "winsecur.h" int filexfer_get_userpass_input(Seat *seat, prompts_t *p, bufchain *input) { int ret; ret = cmdline_get_passwd_input(p); if (ret == -1) ret = console_get_userpass_input(p); return ret; } void platform_get_x11_auth(struct X11Display *display, Conf *conf) { /* Do nothing, therefore no auth. */ } const bool platform_uses_x11_unix_by_default = true; /* ---------------------------------------------------------------------- * File access abstraction. */ /* * Set local current directory. Returns NULL on success, or else an * error message which must be freed after printing. */ char *psftp_lcd(char *dir) { char *ret = NULL; if (!SetCurrentDirectory(dir)) { LPVOID message; int i; FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&message, 0, NULL); i = strcspn((char *)message, "\n"); ret = dupprintf("%.*s", i, (LPCTSTR)message); LocalFree(message); } return ret; } /* * Get local current directory. Returns a string which must be * freed. */ char *psftp_getcwd(void) { char *ret = snewn(256, char); size_t len = GetCurrentDirectory(256, ret); if (len > 256) ret = sresize(ret, len, char); GetCurrentDirectory(len, ret); return ret; } static inline uint64_t uint64_from_words(uint32_t hi, uint32_t lo) { return (((uint64_t)hi) << 32) | lo; } #define TIME_POSIX_TO_WIN(t, ft) do { \ ULARGE_INTEGER uli; \ uli.QuadPart = ((ULONGLONG)(t) + 11644473600ull) * 10000000ull; \ (ft).dwLowDateTime = uli.LowPart; \ (ft).dwHighDateTime = uli.HighPart; \ } while(0) #define TIME_WIN_TO_POSIX(ft, t) do { \ ULARGE_INTEGER uli; \ uli.LowPart = (ft).dwLowDateTime; \ uli.HighPart = (ft).dwHighDateTime; \ uli.QuadPart = uli.QuadPart / 10000000ull - 11644473600ull; \ (t) = (unsigned long) uli.QuadPart; \ } while(0) struct RFile { HANDLE h; }; RFile *open_existing_file(const char *name, uint64_t *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) return NULL; ret = snew(RFile); ret->h = h; if (size) { DWORD lo, hi; lo = GetFileSize(h, &hi); *size = uint64_from_words(hi, lo); } if (mtime || atime) { FILETIME actime, wrtime; 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) { DWORD read; if (!ReadFile(f->h, buffer, length, &read, NULL)) return -1; /* error */ else return read; } void close_rfile(RFile *f) { CloseHandle(f->h); sfree(f); } struct WFile { HANDLE h; }; WFile *open_new_file(const 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) return NULL; ret = snew(WFile); ret->h = h; return ret; } WFile *open_existing_wfile(const char *name, uint64_t *size) { HANDLE h; WFile *ret; h = CreateFile(name, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0); if (h == INVALID_HANDLE_VALUE) return NULL; ret = snew(WFile); ret->h = h; if (size) { DWORD lo, hi; lo = GetFileSize(h, &hi); *size = uint64_from_words(hi, lo); } return ret; } int write_to_file(WFile *f, void *buffer, int length) { DWORD written; if (!WriteFile(f->h, buffer, length, &written, NULL)) return -1; /* error */ else return written; } void set_file_times(WFile *f, unsigned long mtime, unsigned long atime) { FILETIME actime, wrtime; TIME_POSIX_TO_WIN(atime, actime); TIME_POSIX_TO_WIN(mtime, wrtime); SetFileTime(f->h, NULL, &actime, &wrtime); } void close_wfile(WFile *f) { CloseHandle(f->h); sfree(f); } /* Seek offset bytes through file, from whence, where whence is FROM_START, FROM_CURRENT, or FROM_END */ int seek_file(WFile *f, uint64_t offset, int whence) { DWORD movemethod; switch (whence) { case FROM_START: movemethod = FILE_BEGIN; break; case FROM_CURRENT: movemethod = FILE_CURRENT; break; case FROM_END: movemethod = FILE_END; break; default: return -1; } { LONG lo = offset & 0xFFFFFFFFU, hi = offset >> 32; SetFilePointer(f->h, lo, &hi, movemethod); } if (GetLastError() != NO_ERROR) return -1; else return 0; } uint64_t get_file_posn(WFile *f) { LONG lo, hi = 0; lo = SetFilePointer(f->h, 0L, &hi, FILE_CURRENT); return uint64_from_words(hi, lo); } int file_type(const char *name) { DWORD attr; attr = GetFileAttributes(name); /* We know of no `weird' files under Windows. */ if (attr == (DWORD)-1) return FILE_TYPE_NONEXISTENT; else if (attr & FILE_ATTRIBUTE_DIRECTORY) return FILE_TYPE_DIRECTORY; else return FILE_TYPE_FILE; } struct DirHandle { HANDLE h; char *name; }; DirHandle *open_directory(const char *name, const char **errmsg) { HANDLE h; WIN32_FIND_DATA fdat; char *findfile; DirHandle *ret; /* Enumerate files in dir `foo'. */ findfile = dupcat(name, "/*"); h = FindFirstFile(findfile, &fdat); if (h == INVALID_HANDLE_VALUE) { *errmsg = win_strerror(GetLastError()); return NULL; } sfree(findfile); ret = snew(DirHandle); ret->h = h; ret->name = dupstr(fdat.cFileName); return ret; } char *read_filename(DirHandle *dir) { do { if (!dir->name) { WIN32_FIND_DATA fdat; if (!FindNextFile(dir->h, &fdat)) return NULL; else dir->name = dupstr(fdat.cFileName); } assert(dir->name); if (dir->name[0] == '.' && (dir->name[1] == '\0' || (dir->name[1] == '.' && dir->name[2] == '\0'))) { sfree(dir->name); dir->name = NULL; } } while (!dir->name); if (dir->name) { char *ret = dir->name; dir->name = NULL; return ret; } else return NULL; } void close_directory(DirHandle *dir) { FindClose(dir->h); if (dir->name) sfree(dir->name); sfree(dir); } int test_wildcard(const char *name, bool cmdline) { HANDLE fh; WIN32_FIND_DATA fdat; /* First see if the exact name exists. */ if (GetFileAttributes(name) != (DWORD)-1) return WCTYPE_FILENAME; /* Otherwise see if a wildcard match finds anything. */ fh = FindFirstFile(name, &fdat); if (fh == INVALID_HANDLE_VALUE) return WCTYPE_NONEXISTENT; FindClose(fh); return WCTYPE_WILDCARD; } struct WildcardMatcher { HANDLE h; char *name; char *srcpath; }; char *stripslashes(const char *str, bool local) { char *p; /* * On Windows, \ / : are all path component separators. */ if (local) { p = strchr(str, ':'); if (p) str = p+1; } p = strrchr(str, '/'); if (p) str = p+1; if (local) { p = strrchr(str, '\\'); if (p) str = p+1; } return (char *)str; } WildcardMatcher *begin_wildcard_matching(const char *name) { HANDLE h; WIN32_FIND_DATA fdat; WildcardMatcher *ret; char *last; h = FindFirstFile(name, &fdat); if (h == INVALID_HANDLE_VALUE) return NULL; ret = snew(WildcardMatcher); ret->h = h; ret->srcpath = dupstr(name); last = stripslashes(ret->srcpath, true); *last = '\0'; if (fdat.cFileName[0] == '.' && (fdat.cFileName[1] == '\0' || (fdat.cFileName[1] == '.' && fdat.cFileName[2] == '\0'))) ret->name = NULL; else ret->name = dupcat(ret->srcpath, fdat.cFileName); return ret; } char *wildcard_get_filename(WildcardMatcher *dir) { while (!dir->name) { WIN32_FIND_DATA fdat; if (!FindNextFile(dir->h, &fdat)) return NULL; if (fdat.cFileName[0] == '.' && (fdat.cFileName[1] == '\0' || (fdat.cFileName[1] == '.' && fdat.cFileName[2] == '\0'))) dir->name = NULL; else dir->name = dupcat(dir->srcpath, fdat.cFileName); } if (dir->name) { char *ret = dir->name; dir->name = NULL; return ret; } else return NULL; } void finish_wildcard_matching(WildcardMatcher *dir) { FindClose(dir->h); if (dir->name) sfree(dir->name); sfree(dir->srcpath); sfree(dir); } bool vet_filename(const char *name) { if (strchr(name, '/') || strchr(name, '\\') || strchr(name, ':')) return false; if (!name[strspn(name, ".")]) /* entirely composed of dots */ return false; return true; } bool create_directory(const char *name) { return CreateDirectory(name, NULL) != 0; } char *dir_file_cat(const char *dir, const char *file) { ptrlen dir_pl = ptrlen_from_asciz(dir); return dupcat( dir, (ptrlen_endswith(dir_pl, PTRLEN_LITERAL("\\"), NULL) || ptrlen_endswith(dir_pl, PTRLEN_LITERAL("/"), NULL)) ? "" : "\\", file); } /* ---------------------------------------------------------------------- * Platform-specific network handling. */ struct winsftp_cliloop_ctx { HANDLE other_event; int toret; }; static bool winsftp_cliloop_pre(void *vctx, const HANDLE **extra_handles, size_t *n_extra_handles) { struct winsftp_cliloop_ctx *ctx = (struct winsftp_cliloop_ctx *)vctx; if (ctx->other_event != INVALID_HANDLE_VALUE) { *extra_handles = &ctx->other_event; *n_extra_handles = 1; } return true; } static bool winsftp_cliloop_post(void *vctx, size_t extra_handle_index) { struct winsftp_cliloop_ctx *ctx = (struct winsftp_cliloop_ctx *)vctx; if (ctx->other_event != INVALID_HANDLE_VALUE && extra_handle_index == 0) ctx->toret = 1; /* other_event was set */ return false; /* always run only one loop iteration */ } int do_eventsel_loop(HANDLE other_event) { struct winsftp_cliloop_ctx ctx[1]; ctx->other_event = other_event; ctx->toret = 0; cli_main_loop(winsftp_cliloop_pre, winsftp_cliloop_post, ctx); return ctx->toret; } /* * Wait for some network data and process it. * * We have two variants of this function. One uses select() so that * it's compatible with WinSock 1. The other uses WSAEventSelect * and MsgWaitForMultipleObjects, so that we can consistently use * WSAEventSelect throughout; this enables us to also implement * 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; SOCKET skt = winselcli_unique_socket(); if (skt == INVALID_SOCKET) return -1; /* doom */ if (socket_writable(skt)) select_result((WPARAM) skt, (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; } FD_ZERO(&readfds); FD_SET(skt, &readfds); ret = p_select(1, &readfds, NULL, NULL, ptv); if (ret < 0) return -1; /* doom */ else if (ret == 0) now = next; else now = GETTICKCOUNT(); } while (ret == 0); select_result((WPARAM) skt, (LPARAM) FD_READ); return 0; } else { return do_eventsel_loop(INVALID_HANDLE_VALUE); } } /* * Read a command line from standard input. * * In the presence of WinSock 2, we can use WSAEventSelect to * mediate between the socket and stdin, meaning we can send * keepalives and respond to server events even while waiting at * the PSFTP command prompt. Without WS2, we fall back to a simple * fgets. */ struct command_read_ctx { HANDLE event; char *line; }; static DWORD WINAPI command_read_thread(void *param) { struct command_read_ctx *ctx = (struct command_read_ctx *) param; ctx->line = fgetline(stdin); SetEvent(ctx->event); return 0; } char *ssh_sftp_get_cmdline(const char *prompt, bool no_fds_ok) { int ret; struct command_read_ctx ctx[1]; DWORD threadid; HANDLE hThread; fputs(prompt, stdout); fflush(stdout); if ((winselcli_unique_socket() == INVALID_SOCKET && no_fds_ok) || p_WSAEventSelect == NULL) { return fgetline(stdin); /* very simple */ } /* * Create a second thread to read from stdin. Process network * and timing events until it terminates. */ ctx->event = CreateEvent(NULL, false, false, NULL); ctx->line = NULL; hThread = CreateThread(NULL, 0, command_read_thread, ctx, 0, &threadid); if (!hThread) { CloseHandle(ctx->event); fprintf(stderr, "Unable to create command input thread\n"); cleanup_exit(1); } do { ret = do_eventsel_loop(ctx->event); /* do_eventsel_loop can't return an error (unlike * ssh_sftp_loop_iteration, which can return -1 if select goes * wrong or if the socket doesn't exist). */ assert(ret >= 0); } while (ret == 0); CloseHandle(hThread); CloseHandle(ctx->event); return ctx->line; } void platform_psftp_pre_conn_setup(LogPolicy *lp) { if (restricted_acl()) { lp_eventlog(lp, "Running with restricted process ACL"); } } /* ---------------------------------------------------------------------- * Main program. Parse arguments etc. */ int main(int argc, char *argv[]) { int ret; dll_hijacking_protection(); ret = psftp_main(argc, argv); return ret; } putty-0.76/windows/winshare.c0000644000175000017500000000776614072266315013260 00000000000000/* * Windows implementation of SSH connection-sharing IPC setup. */ #include #include #if !defined NO_SECURITY #include "tree234.h" #include "putty.h" #include "network.h" #include "proxy.h" #include "ssh.h" #include "wincapi.h" #include "winsecur.h" #define CONNSHARE_PIPE_PREFIX "\\\\.\\pipe\\putty-connshare" #define CONNSHARE_MUTEX_PREFIX "Local\\putty-connshare-mutex" 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; } int platform_ssh_share(const char *pi_name, Conf *conf, Plug *downplug, Plug *upplug, Socket **sock, char **logtext, char **ds_err, char **us_err, bool can_upstream, bool can_downstream) { char *name, *mutexname, *pipename; HANDLE mutex; Socket *retsock; PSECURITY_DESCRIPTOR psd; PACL acl; /* * 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 = capi_obfuscate_string(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, &acl, logtext)) { sfree(mutexname); sfree(name); 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); sfree(name); LocalFree(psd); LocalFree(acl); return SHARE_NONE; } sfree(mutexname); LocalFree(psd); 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 */ putty-0.76/windows/winsocks.c0000644000175000017500000000070014072266315013255 00000000000000/* * Main program for Windows psocks. */ #include "putty.h" #include "ssh.h" #include "psocks.h" static const PsocksPlatform platform = { NULL /* open_pipes */, NULL /* start_subcommand */, }; int main(int argc, char **argv) { psocks_state *ps = psocks_new(&platform); psocks_cmdline(ps, argc, argv); sk_init(); winselcli_setup(); psocks_start(ps); cli_main_loop(cliloop_null_pre, cliloop_null_post, NULL); } putty-0.76/windows/winstore.c0000644000175000017500000006276714072266315013314 00000000000000/* * winstore.c: Windows-specific implementation of the interface * defined in storage.h. */ #include #include #include #include #include "putty.h" #include "storage.h" #include #ifndef CSIDL_APPDATA #define CSIDL_APPDATA 0x001a #endif #ifndef CSIDL_LOCAL_APPDATA #define CSIDL_LOCAL_APPDATA 0x001c #endif static const char *const reg_jumplist_key = PUTTY_REG_POS "\\Jumplist"; static const char *const reg_jumplist_value = "Recent sessions"; static const char *const puttystr = PUTTY_REG_POS "\\Sessions"; static bool tried_shgetfolderpath = false; static HMODULE shell32_module = NULL; DECL_WINDOWS_FUNCTION(static, HRESULT, SHGetFolderPathA, (HWND, int, HANDLE, DWORD, LPSTR)); struct settings_w { HKEY sesskey; }; settings_w *open_settings_w(const char *sessionname, char **errmsg) { HKEY subkey1, sesskey; int ret; strbuf *sb; *errmsg = NULL; if (!sessionname || !*sessionname) sessionname = "Default Settings"; sb = strbuf_new(); escape_registry_key(sessionname, sb); ret = RegCreateKey(HKEY_CURRENT_USER, puttystr, &subkey1); if (ret != ERROR_SUCCESS) { strbuf_free(sb); *errmsg = dupprintf("Unable to create registry key\n" "HKEY_CURRENT_USER\\%s", puttystr); return NULL; } ret = RegCreateKey(subkey1, sb->s, &sesskey); RegCloseKey(subkey1); if (ret != ERROR_SUCCESS) { *errmsg = dupprintf("Unable to create registry key\n" "HKEY_CURRENT_USER\\%s\\%s", puttystr, sb->s); strbuf_free(sb); return NULL; } strbuf_free(sb); settings_w *toret = snew(settings_w); toret->sesskey = sesskey; return toret; } void write_setting_s(settings_w *handle, const char *key, const char *value) { if (handle) RegSetValueEx(handle->sesskey, key, 0, REG_SZ, (CONST BYTE *)value, 1 + strlen(value)); } void write_setting_i(settings_w *handle, const char *key, int value) { if (handle) RegSetValueEx(handle->sesskey, key, 0, REG_DWORD, (CONST BYTE *) &value, sizeof(value)); } void close_settings_w(settings_w *handle) { RegCloseKey(handle->sesskey); sfree(handle); } struct settings_r { HKEY sesskey; }; settings_r *open_settings_r(const char *sessionname) { HKEY subkey1, sesskey; strbuf *sb; if (!sessionname || !*sessionname) sessionname = "Default Settings"; sb = strbuf_new(); escape_registry_key(sessionname, sb); if (RegOpenKey(HKEY_CURRENT_USER, puttystr, &subkey1) != ERROR_SUCCESS) { sesskey = NULL; } else { if (RegOpenKey(subkey1, sb->s, &sesskey) != ERROR_SUCCESS) { sesskey = NULL; } RegCloseKey(subkey1); } strbuf_free(sb); if (!sesskey) return NULL; settings_r *toret = snew(settings_r); toret->sesskey = sesskey; return toret; } char *read_setting_s(settings_r *handle, const char *key) { DWORD type, allocsize, size; char *ret; if (!handle) return NULL; /* Find out the type and size of the data. */ if (RegQueryValueEx(handle->sesskey, key, 0, &type, NULL, &size) != ERROR_SUCCESS || type != REG_SZ) return NULL; allocsize = size+1; /* allow for an extra NUL if needed */ ret = snewn(allocsize, char); if (RegQueryValueEx(handle->sesskey, key, 0, &type, (BYTE *)ret, &size) != ERROR_SUCCESS || type != REG_SZ) { sfree(ret); return NULL; } assert(size < allocsize); ret[size] = '\0'; /* add an extra NUL in case RegQueryValueEx * didn't supply one */ return ret; } int read_setting_i(settings_r *handle, const char *key, int defvalue) { DWORD type, val, size; size = sizeof(val); if (!handle || RegQueryValueEx(handle->sesskey, key, 0, &type, (BYTE *) &val, &size) != ERROR_SUCCESS || size != sizeof(val) || type != REG_DWORD) return defvalue; else return val; } FontSpec *read_setting_fontspec(settings_r *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"); isbold = read_setting_i(handle, settingname, -1); sfree(settingname); if (isbold == -1) { sfree(fontname); return NULL; } settingname = dupcat(name, "CharSet"); charset = read_setting_i(handle, settingname, -1); sfree(settingname); if (charset == -1) { sfree(fontname); return NULL; } settingname = dupcat(name, "Height"); 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(settings_w *handle, const char *name, FontSpec *font) { char *settingname; write_setting_s(handle, name, font->name); settingname = dupcat(name, "IsBold"); write_setting_i(handle, settingname, font->isbold); sfree(settingname); settingname = dupcat(name, "CharSet"); write_setting_i(handle, settingname, font->charset); sfree(settingname); settingname = dupcat(name, "Height"); write_setting_i(handle, settingname, font->height); sfree(settingname); } Filename *read_setting_filename(settings_r *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(settings_w *handle, const char *name, Filename *result) { write_setting_s(handle, name, result->path); } void close_settings_r(settings_r *handle) { if (handle) { RegCloseKey(handle->sesskey); sfree(handle); } } void del_settings(const char *sessionname) { HKEY subkey1; strbuf *sb; if (RegOpenKey(HKEY_CURRENT_USER, puttystr, &subkey1) != ERROR_SUCCESS) return; sb = strbuf_new(); escape_registry_key(sessionname, sb); RegDeleteKey(subkey1, sb->s); strbuf_free(sb); RegCloseKey(subkey1); remove_session_from_jumplist(sessionname); } struct settings_e { HKEY key; int i; }; settings_e *enum_settings_start(void) { settings_e *ret; HKEY key; if (RegOpenKey(HKEY_CURRENT_USER, puttystr, &key) != ERROR_SUCCESS) return NULL; ret = snew(settings_e); if (ret) { ret->key = key; ret->i = 0; } return ret; } bool enum_settings_next(settings_e *e, strbuf *sb) { size_t regbuf_size = MAX_PATH + 1; char *regbuf = snewn(regbuf_size, char); bool success; while (1) { DWORD retd = RegEnumKey(e->key, e->i, regbuf, regbuf_size); if (retd != ERROR_MORE_DATA) { success = (retd == ERROR_SUCCESS); break; } sgrowarray(regbuf, regbuf_size, regbuf_size); } if (success) unescape_registry_key(regbuf, sb); e->i++; sfree(regbuf); return success; } void enum_settings_finish(settings_e *e) { RegCloseKey(e->key); sfree(e); } static void hostkey_regname(strbuf *sb, const char *hostname, int port, const char *keytype) { strbuf_catf(sb, "%s@%d:", keytype, port); escape_registry_key(hostname, sb); } int verify_host_key(const char *hostname, int port, const char *keytype, const char *key) { char *otherstr; strbuf *regname; int len; HKEY rkey; DWORD readlen; DWORD type; int ret, compare; len = 1 + strlen(key); /* * Now read a saved key in from the registry and see what it * says. */ regname = strbuf_new(); hostkey_regname(regname, hostname, port, keytype); if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_POS "\\SshHostKeys", &rkey) != ERROR_SUCCESS) { strbuf_free(regname); return 1; /* key does not exist in registry */ } readlen = len; otherstr = snewn(len, char); ret = RegQueryValueEx(rkey, regname->s, NULL, &type, (BYTE *)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 * under just the hostname and translate that. */ char *justhost = regname->s + 1 + strcspn(regname->s, ":"); char *oldstyle = snewn(len + 10, char); /* safety margin */ readlen = len; ret = RegQueryValueEx(rkey, justhost, NULL, &type, (BYTE *)oldstyle, &readlen); if (ret == ERROR_SUCCESS && type == REG_SZ) { /* * The old format is two old-style bignums separated by * a slash. An old-style bignum is made of groups of * four hex digits: digits are ordered in sensible * (most to least significant) order within each group, * but groups are ordered in silly (least to most) * order within the bignum. The new format is two * ordinary C-format hex numbers (0xABCDEFG...XYZ, with * A nonzero except in the special case 0x0, which * doesn't appear anyway in RSA keys) separated by a * comma. All hex digits are lowercase in both formats. */ char *p = otherstr; char *q = oldstyle; int i, j; for (i = 0; i < 2; i++) { int ndigits, nwords; *p++ = '0'; *p++ = 'x'; ndigits = strcspn(q, "/"); /* find / or end of string */ nwords = ndigits / 4; /* now trim ndigits to remove leading zeros */ while (q[(ndigits - 1) ^ 3] == '0' && ndigits > 1) ndigits--; /* now move digits over to new string */ for (j = 0; j < ndigits; j++) p[ndigits - 1 - j] = q[j ^ 3]; p += ndigits; q += nwords * 4; if (*q) { q++; /* eat the slash */ *p++ = ','; /* add a comma */ } *p = '\0'; /* terminate the string */ } /* * Now _if_ this key matches, we'll enter it in the new * format. If not, we'll assume something odd went * wrong, and hyper-cautiously do nothing. */ if (!strcmp(otherstr, key)) RegSetValueEx(rkey, regname->s, 0, REG_SZ, (BYTE *)otherstr, strlen(otherstr) + 1); } sfree(oldstyle); } RegCloseKey(rkey); compare = strcmp(otherstr, key); sfree(otherstr); strbuf_free(regname); if (ret == ERROR_MORE_DATA || (ret == ERROR_SUCCESS && type == REG_SZ && compare)) return 2; /* key is different in registry */ else if (ret != ERROR_SUCCESS || type != REG_SZ) return 1; /* key does not exist in registry */ else return 0; /* key matched OK in registry */ } bool have_ssh_host_key(const char *hostname, int port, const char *keytype) { /* * If we have a host key, verify_host_key will return 0 or 2. * If we don't have one, it'll return 1. */ return verify_host_key(hostname, port, keytype, "") != 1; } void store_host_key(const char *hostname, int port, const char *keytype, const char *key) { strbuf *regname; HKEY rkey; regname = strbuf_new(); hostkey_regname(regname, hostname, port, keytype); if (RegCreateKey(HKEY_CURRENT_USER, PUTTY_REG_POS "\\SshHostKeys", &rkey) == ERROR_SUCCESS) { RegSetValueEx(rkey, regname->s, 0, REG_SZ, (BYTE *)key, strlen(key) + 1); RegCloseKey(rkey); } /* else key does not exist in registry */ strbuf_free(regname); } /* * Open (or delete) the random seed file. */ enum { DEL, OPEN_R, OPEN_W }; static bool 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 | FILE_SHARE_WRITE), NULL, action == OPEN_W ? CREATE_ALWAYS : OPEN_EXISTING, action == OPEN_W ? FILE_ATTRIBUTE_NORMAL : 0, NULL); return (*ret != INVALID_HANDLE_VALUE); } static bool try_random_seed_and_free(char *path, int action, HANDLE *hout) { bool retd = try_random_seed(path, action, hout); sfree(path); return retd; } static HANDLE access_random_seed(int action) { HKEY rkey; HANDLE rethandle; /* * Iterate over a selection of possible random seed paths until * we find one that works. * * We do this iteration separately for reading and writing, * meaning that we will automatically migrate random seed files * if a better location becomes available (by reading from the * best location in which we actually find one, and then * writing to the best location in which we can _create_ one). */ /* * First, try the location specified by the user in the * Registry, if any. */ { char regpath[MAX_PATH + 1]; DWORD type, size = sizeof(regpath); if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_POS, &rkey) == ERROR_SUCCESS) { int ret = RegQueryValueEx(rkey, "RandSeedFile", 0, &type, (BYTE *)regpath, &size); RegCloseKey(rkey); if (ret == ERROR_SUCCESS && type == REG_SZ && try_random_seed(regpath, action, &rethandle)) return rethandle; } } /* * Next, try the user's local Application Data directory, * followed by their non-local one. This is found using the * SHGetFolderPath function, which won't be present on all * versions of Windows. */ if (!tried_shgetfolderpath) { /* This is likely only to bear fruit on systems with IE5+ * installed, or WinMe/2K+. There is some faffing with * SHFOLDER.DLL we could do to try to find an equivalent * on older versions of Windows if we cared enough. * However, the invocation below requires IE5+ anyway, * so stuff that. */ shell32_module = load_system32_dll("shell32.dll"); GET_WINDOWS_FUNCTION(shell32_module, SHGetFolderPathA); tried_shgetfolderpath = true; } if (p_SHGetFolderPathA) { char profile[MAX_PATH + 1]; if (SUCCEEDED(p_SHGetFolderPathA(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, profile)) && try_random_seed_and_free(dupcat(profile, "\\PUTTY.RND"), action, &rethandle)) return rethandle; if (SUCCEEDED(p_SHGetFolderPathA(NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, profile)) && try_random_seed_and_free(dupcat(profile, "\\PUTTY.RND"), action, &rethandle)) return rethandle; } /* * Failing that, try %HOMEDRIVE%%HOMEPATH% as a guess at the * user's home directory. */ { char drv[MAX_PATH], path[MAX_PATH]; DWORD drvlen = GetEnvironmentVariable("HOMEDRIVE", drv, sizeof(drv)); DWORD pathlen = GetEnvironmentVariable("HOMEPATH", path, sizeof(path)); /* We permit %HOMEDRIVE% to expand to an empty string, but if * %HOMEPATH% does that, we abort the attempt. Same if either * variable overflows its buffer. */ if (drvlen == 0) drv[0] = '\0'; if (drvlen < lenof(drv) && pathlen < lenof(path) && pathlen > 0 && try_random_seed_and_free( dupcat(drv, path, "\\PUTTY.RND"), action, &rethandle)) return rethandle; } /* * And finally, fall back to C:\WINDOWS. */ { char windir[MAX_PATH]; DWORD len = GetWindowsDirectory(windir, sizeof(windir)); if (len < lenof(windir) && try_random_seed_and_free( dupcat(windir, "\\PUTTY.RND"), action, &rethandle)) return rethandle; } /* * If even that failed, give up. */ return INVALID_HANDLE_VALUE; } void read_random_seed(noise_consumer_t consumer) { HANDLE seedf = access_random_seed(OPEN_R); if (seedf != INVALID_HANDLE_VALUE) { while (1) { char buf[1024]; DWORD len; if (ReadFile(seedf, buf, sizeof(buf), &len, NULL) && len) consumer(buf, len); else break; } CloseHandle(seedf); } } void write_random_seed(void *data, int len) { HANDLE seedf = access_random_seed(OPEN_W); if (seedf != INVALID_HANDLE_VALUE) { DWORD lenwritten; WriteFile(seedf, data, len, &lenwritten, NULL); CloseHandle(seedf); } } /* * Internal function supporting the jump list registry code. All the * functions to add, remove and read the list have substantially * similar content, so this is a generalisation of all of them which * transforms the list in the registry by prepending 'add' (if * non-null), removing 'rem' from what's left (if non-null), and * returning the resulting concatenated list of strings in 'out' (if * non-null). */ static int transform_jumplist_registry (const char *add, const char *rem, char **out) { int ret; HKEY pjumplist_key; DWORD type; DWORD value_length; char *old_value, *new_value; char *piterator_old, *piterator_new, *piterator_tmp; ret = RegCreateKeyEx(HKEY_CURRENT_USER, reg_jumplist_key, 0, NULL, REG_OPTION_NON_VOLATILE, (KEY_READ | KEY_WRITE), NULL, &pjumplist_key, NULL); if (ret != ERROR_SUCCESS) { return JUMPLISTREG_ERROR_KEYOPENCREATE_FAILURE; } /* Get current list of saved sessions in the registry. */ value_length = 200; old_value = snewn(value_length, char); ret = RegQueryValueEx(pjumplist_key, reg_jumplist_value, NULL, &type, (BYTE *)old_value, &value_length); /* When the passed buffer is too small, ERROR_MORE_DATA is * returned and the required size is returned in the length * argument. */ if (ret == ERROR_MORE_DATA) { sfree(old_value); old_value = snewn(value_length, char); ret = RegQueryValueEx(pjumplist_key, reg_jumplist_value, NULL, &type, (BYTE *)old_value, &value_length); } if (ret == ERROR_FILE_NOT_FOUND) { /* Value doesn't exist yet. Start from an empty value. */ *old_value = '\0'; *(old_value + 1) = '\0'; } else if (ret != ERROR_SUCCESS) { /* Some non-recoverable error occurred. */ sfree(old_value); RegCloseKey(pjumplist_key); return JUMPLISTREG_ERROR_VALUEREAD_FAILURE; } else if (type != REG_MULTI_SZ) { /* The value present in the registry has the wrong type: we * try to delete it and start from an empty value. */ ret = RegDeleteValue(pjumplist_key, reg_jumplist_value); if (ret != ERROR_SUCCESS) { sfree(old_value); RegCloseKey(pjumplist_key); return JUMPLISTREG_ERROR_VALUEREAD_FAILURE; } *old_value = '\0'; *(old_value + 1) = '\0'; } /* Check validity of registry data: REG_MULTI_SZ value must end * with \0\0. */ piterator_tmp = old_value; while (((piterator_tmp - old_value) < (value_length - 1)) && !(*piterator_tmp == '\0' && *(piterator_tmp+1) == '\0')) { ++piterator_tmp; } if ((piterator_tmp - old_value) >= (value_length-1)) { /* Invalid value. Start from an empty value. */ *old_value = '\0'; *(old_value + 1) = '\0'; } /* * Modify the list, if we're modifying. */ if (add || rem) { /* Walk through the existing list and construct the new list of * saved sessions. */ new_value = snewn(value_length + (add ? strlen(add) + 1 : 0), char); piterator_new = new_value; piterator_old = old_value; /* First add the new item to the beginning of the list. */ if (add) { strcpy(piterator_new, add); piterator_new += strlen(piterator_new) + 1; } /* Now add the existing list, taking care to leave out the removed * item, if it was already in the existing list. */ while (*piterator_old != '\0') { if (!rem || strcmp(piterator_old, rem) != 0) { /* Check if this is a valid session, otherwise don't add. */ settings_r *psettings_tmp = open_settings_r(piterator_old); if (psettings_tmp != NULL) { close_settings_r(psettings_tmp); strcpy(piterator_new, piterator_old); piterator_new += strlen(piterator_new) + 1; } } piterator_old += strlen(piterator_old) + 1; } *piterator_new = '\0'; ++piterator_new; /* Save the new list to the registry. */ ret = RegSetValueEx(pjumplist_key, reg_jumplist_value, 0, REG_MULTI_SZ, (BYTE *)new_value, piterator_new - new_value); sfree(old_value); 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); if (ret != ERROR_SUCCESS) { return JUMPLISTREG_ERROR_VALUEWRITE_FAILURE; } else { return JUMPLISTREG_OK; } } /* Adds a new entry to the jumplist entries in the registry. */ int add_to_jumplist_registry(const char *item) { return transform_jumplist_registry(item, item, NULL); } /* Removes an item from the jumplist entries in the registry. */ int remove_from_jumplist_registry(const char *item) { return transform_jumplist_registry(NULL, item, NULL); } /* 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; } /* * Recursively delete a registry key and everything under it. */ static void registry_recursive_remove(HKEY key) { DWORD i; char name[MAX_PATH + 1]; HKEY subkey; i = 0; while (RegEnumKey(key, i, name, sizeof(name)) == ERROR_SUCCESS) { if (RegOpenKey(key, name, &subkey) == ERROR_SUCCESS) { registry_recursive_remove(subkey); RegCloseKey(subkey); } RegDeleteKey(key, name); } } void cleanup_all(void) { HKEY key; int ret; char name[MAX_PATH + 1]; /* ------------------------------------------------------------ * Wipe out the random seed file, in all of its possible * locations. */ access_random_seed(DEL); /* ------------------------------------------------------------ * Ask Windows to delete any jump list information associated * with this installation of PuTTY. */ clear_jumplist(); /* ------------------------------------------------------------ * Destroy all registry information associated with PuTTY. */ /* * Open the main PuTTY registry key and remove everything in it. */ if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_POS, &key) == ERROR_SUCCESS) { registry_recursive_remove(key); RegCloseKey(key); } /* * Now open the parent key and remove the PuTTY main key. Once * we've done that, see if the parent key has any other * children. */ if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_PARENT, &key) == ERROR_SUCCESS) { RegDeleteKey(key, PUTTY_REG_PARENT_CHILD); ret = RegEnumKey(key, 0, name, sizeof(name)); RegCloseKey(key); /* * If the parent key had no other children, we must delete * it in its turn. That means opening the _grandparent_ * key. */ if (ret != ERROR_SUCCESS) { if (RegOpenKey(HKEY_CURRENT_USER, PUTTY_REG_GPARENT, &key) == ERROR_SUCCESS) { RegDeleteKey(key, PUTTY_REG_GPARENT_CHILD); RegCloseKey(key); } } } /* * Now we're done. */ } putty-0.76/windows/winstuff.h0000644000175000017500000006077014072266315013304 00000000000000/* * winstuff.h: Windows-specific inter-module stuff. */ #ifndef PUTTY_WINSTUFF_H #define PUTTY_WINSTUFF_H #ifndef AUTO_WINSOCK #include #endif #include #include /* for FILENAME_MAX */ /* We use uintptr_t for Win32/Win64 portability, so we should in * principle include stdint.h, which defines it according to the C * standard. But older versions of Visual Studio - including the one * used for official PuTTY builds as of 2015-09-28 - don't provide * stdint.h at all, but do (non-standardly) define uintptr_t in * stddef.h. So here we try to make sure _some_ standard header is * included which defines uintptr_t. */ #include #if !defined _MSC_VER || _MSC_VER >= 1600 || defined __clang__ #include #endif #include "defs.h" #include "marshal.h" #include "tree234.h" #include "winhelp.h" #if defined _M_IX86 || defined _M_AMD64 #define BUILDINFO_PLATFORM "x86 Windows" #elif defined _M_ARM || defined _M_ARM64 #define BUILDINFO_PLATFORM "Arm Windows" #else #define BUILDINFO_PLATFORM "Windows" #endif struct Filename { char *path; }; static inline FILE *f_open(const Filename *filename, const char *mode, bool isprivate) { return fopen(filename->path, mode); } struct FontSpec { char *name; bool isbold; int height; int charset; }; struct FontSpec *fontspec_new( const char *name, bool 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 : \ (fq) == FQ_NONANTIALIASED ? NONANTIALIASED_QUALITY : \ CLEARTYPE_QUALITY) #define PLATFORM_IS_UTF16 /* enable UTF-16 processing when exchanging * wchar_t strings with environment */ #define PLATFORM_CLIPBOARDS(X) \ X(CLIP_SYSTEM, "system clipboard") \ /* end of list */ /* * Where we can, we use GetWindowLongPtr and friends because they're * more useful on 64-bit platforms, but they're a relatively recent * innovation, missing from VC++ 6 and older MinGW. Degrade nicely. * (NB that on some systems, some of these things are available but * not others...) */ #ifndef GCLP_HCURSOR /* GetClassLongPtr and friends */ #undef GetClassLongPtr #define GetClassLongPtr GetClassLong #undef SetClassLongPtr #define SetClassLongPtr SetClassLong #define GCLP_HCURSOR GCL_HCURSOR /* GetWindowLongPtr and friends */ #undef GetWindowLongPtr #define GetWindowLongPtr GetWindowLong #undef SetWindowLongPtr #define SetWindowLongPtr SetWindowLong #undef GWLP_USERDATA #define GWLP_USERDATA GWL_USERDATA #undef DWLP_MSGRESULT #define DWLP_MSGRESULT DWL_MSGRESULT /* Since we've clobbered the above functions, we should clobber the * associated type regardless of whether it's defined. */ #undef LONG_PTR #define LONG_PTR LONG #endif #define BOXFLAGS DLGWINDOWEXTRA #define BOXRESULT (DLGWINDOWEXTRA + sizeof(LONG_PTR)) #define DF_END 0x0001 #ifndef __WINE__ /* Up-to-date Windows headers warn that the unprefixed versions of * these names are deprecated. */ #define stricmp _stricmp #define strnicmp _strnicmp #else /* Compiling with winegcc, _neither_ version of these functions * exists. Use the POSIX names. */ #define stricmp strcasecmp #define strnicmp strncasecmp #endif #define BROKEN_PIPE_ERROR_CODE ERROR_BROKEN_PIPE /* used in sshshare.c */ /* * 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. * * - GET_WINDOWS_FUNCTION_PP allows "name" to be redirected via * preprocessor definitions like "#define foo bar"; this is principally * intended for the ANSI/Unicode DoSomething/DoSomethingA/DoSomethingW. * If your function has an argument of type "LPTSTR" or similar, this * is the variant to use. * (However, it can't always be used, as it trips over more complicated * macro trickery such as the WspiapiGetAddrInfo wrapper for getaddrinfo.) * * (DECL_WINDOWS_FUNCTION works with both these variants.) */ #define DECL_WINDOWS_FUNCTION(linkage, rettype, name, params) \ typedef rettype (WINAPI *t_##name) params; \ linkage t_##name p_##name /* If you DECL_WINDOWS_FUNCTION as extern in a header file, use this to * define the function pointer in a source file */ #define DEF_WINDOWS_FUNCTION(name) t_##name p_##name #define STR1(x) #x #define STR(x) STR1(x) #define GET_WINDOWS_FUNCTION_PP(module, name) \ TYPECHECK((t_##name)NULL == name, \ (p_##name = module ? \ (t_##name) GetProcAddress(module, STR(name)) : NULL)) #define GET_WINDOWS_FUNCTION(module, name) \ TYPECHECK((t_##name)NULL == name, \ (p_##name = module ? \ (t_##name) GetProcAddress(module, #name) : NULL)) #define GET_WINDOWS_FUNCTION_NO_TYPECHECK(module, name) \ (p_##name = module ? \ (t_##name) GetProcAddress(module, #name) : NULL) #define PUTTY_REG_POS "Software\\SimonTatham\\PuTTY" #define PUTTY_REG_PARENT "Software\\SimonTatham" #define PUTTY_REG_PARENT_CHILD "PuTTY" #define PUTTY_REG_GPARENT "Software" #define PUTTY_REG_GPARENT_CHILD "SimonTatham" /* Result values for the jumplist registry functions. */ #define JUMPLISTREG_OK 0 #define JUMPLISTREG_ERROR_INVALID_PARAMETER 1 #define JUMPLISTREG_ERROR_KEYOPENCREATE_FAILURE 2 #define JUMPLISTREG_ERROR_VALUEREAD_FAILURE 3 #define JUMPLISTREG_ERROR_VALUEWRITE_FAILURE 4 #define JUMPLISTREG_ERROR_INVALID_VALUE 5 #define PUTTY_CHM_FILE "putty.chm" #define GETTICKCOUNT GetTickCount #define CURSORBLINK GetCaretBlinkTime() #define TICKSPERSEC 1000 /* GetTickCount returns milliseconds */ #define DEFAULT_CODEPAGE CP_ACP #define USES_VTLINE_HACK #ifndef NO_GSSAPI /* * GSS-API stuff */ #define GSS_CC CALLBACK /* typedef struct Ssh_gss_buf { size_t length; char *value; } Ssh_gss_buf; #define SSH_GSS_EMPTY_BUF (Ssh_gss_buf) {0,NULL} typedef void *Ssh_gss_name; */ #endif /* * The all-important instance handle, saved from WinMain in every GUI * program and exported for other GUI code to pass back to the Windows * API. */ extern HINSTANCE hinst; /* * Help file stuff in winhelp.c. */ void init_help(void); void shutdown_help(void); bool has_help(void); void launch_help(HWND hwnd, const char *topic); void quit_help(HWND hwnd); int has_embedded_chm(void); /* 1 = yes, 0 = no, -1 = N/A */ /* * GUI seat methods in windlg.c, so that the vtable definition in * window.c can refer to them. */ int win_seat_verify_ssh_host_key( Seat *seat, const char *host, int port, const char *keytype, char *keystr, const char *keydisp, char **key_fingerprints, void (*callback)(void *ctx, int result), void *ctx); int win_seat_confirm_weak_crypto_primitive( Seat *seat, const char *algtype, const char *algname, void (*callback)(void *ctx, int result), void *ctx); int win_seat_confirm_weak_cached_hostkey( Seat *seat, const char *algname, const char *betteralgs, void (*callback)(void *ctx, int result), void *ctx); /* * Windows-specific clipboard helper function shared with windlg.c, * which takes the data string in the system code page instead of * Unicode. */ void write_aclip(int clipboard, char *, int, bool); #define WM_NETEVENT (WM_APP + 5) /* * On Windows, we send MA_2CLK as the only event marking the second * press of a mouse button. Compare unix.h. */ #define MULTICLICK_ONLY_EVENT 1 /* * On Windows, data written to the clipboard must be NUL-terminated. */ #define SELECTION_NUL_TERMINATED 1 /* * On Windows, copying to the clipboard terminates lines with CRLF. */ #define SEL_NL { 13, 10 } /* * sk_getxdmdata() does not exist under Windows (not that I * couldn't write it if I wanted to, but I haven't bothered), so * it's a macro which always returns NULL. With any luck this will * cause the compiler to notice it can optimise away the * implementation of XDM-AUTHORIZATION-1 in x11fwd.c :-) */ #define sk_getxdmdata(socket, lenp) (NULL) /* * File-selector filter strings used in the config box. On Windows, * these strings are of exactly the type needed to go in * `lpstrFilter' in an OPENFILENAME structure. */ #define FILTER_KEY_FILES ("PuTTY Private Key Files (*.ppk)\0*.ppk\0" \ "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. */ /* Report an event notification from WSA*Select */ void select_result(WPARAM, LPARAM); /* Enumerate all currently live OS-level SOCKETs */ SOCKET first_socket(int *); SOCKET next_socket(int *); /* Ask winnet.c whether we currently want to try to write to a SOCKET */ bool socket_writable(SOCKET skt); /* Force a refresh of the SOCKET list by re-calling do_select for each one */ void socket_reselect_all(void); /* Make a SockAddr which just holds a named pipe address. */ SockAddr *sk_namedpipe_addr(const char *pipename); /* Turn a WinSock error code into a string. */ const char *winsock_error_string(int error); /* * 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. */ DECL_WINDOWS_FUNCTION(extern, int, WSAAsyncSelect, (SOCKET, HWND, u_int, long)); DECL_WINDOWS_FUNCTION(extern, int, WSAEventSelect, (SOCKET, WSAEVENT, long)); DECL_WINDOWS_FUNCTION(extern, int, WSAGetLastError, (void)); DECL_WINDOWS_FUNCTION(extern, int, WSAEnumNetworkEvents, (SOCKET, WSAEVENT, LPWSANETWORKEVENTS)); #ifdef NEED_DECLARATION_OF_SELECT /* This declaration is protected by an ifdef for the sake of building * against winelib, in which you have to include winsock2.h before * stdlib.h so that the right fd_set type gets defined. It would be a * pain to do that throughout this codebase, so instead I arrange that * only a modules actually needing to use (or define, or initialise) * this function pointer will see its declaration, and _those_ modules * - which will be Windows-specific anyway - can take more care. */ DECL_WINDOWS_FUNCTION(extern, int, select, (int, fd_set FAR *, fd_set FAR *, fd_set FAR *, const struct timeval FAR *)); #endif /* * Implemented differently depending on the client of winnet.c, and * called by winnet.c to turn on or off WSA*Select for a given socket. */ const char *do_select(SOCKET skt, bool enable); /* * Exports from winselgui.c and winselcli.c, each of which provides an * implementation of do_select. */ void winselgui_set_hwnd(HWND hwnd); void winselgui_clear_hwnd(void); void winselcli_setup(void); SOCKET winselcli_unique_socket(void); extern HANDLE winselcli_event; /* * Network-subsystem-related functions provided in other Windows modules. */ Socket *make_handle_socket(HANDLE send_H, HANDLE recv_H, HANDLE stderr_H, Plug *plug, bool overlapped); /* winhsock */ Socket *new_named_pipe_client(const char *pipename, Plug *plug); /* winnpc */ Socket *new_named_pipe_listener(const char *pipename, Plug *plug); /* winnps */ /* A lower-level function in winnpc.c, which does most of the work of * new_named_pipe_client (including checking the ownership of what * it's connected to), but returns a plain HANDLE instead of wrapping * it into a Socket. */ HANDLE connect_to_named_pipe(const char *pipename, char **err); /* * Exports from winctrls.c. */ struct ctlpos { HWND hwnd; WPARAM font; int dlu4inpix; int ypos, width; int xoff; int boxystart, boxid; char *boxtext; }; void init_common_controls(void); /* also does some DLL-loading */ /* * Exports from winutils.c. */ typedef struct filereq_tag filereq; /* cwd for file requester */ bool request_file(filereq *state, OPENFILENAME *of, bool preserve, bool save); filereq *filereq_new(void); void filereq_free(filereq *state); void pgp_fingerprints_msgbox(HWND owner); int message_box(HWND owner, LPCTSTR text, LPCTSTR caption, DWORD style, DWORD helpctxid); void MakeDlgItemBorderless(HWND parent, int id); 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 { int listid, upbid, dnbid; int srcitem; int dummyitem; bool dragging; }; /* * This structure is passed to event handler functions as the `dlg' * parameter, and hence is passed back to winctrls access functions. */ struct dlgparam { HWND hwnd; /* the hwnd of the dialog box */ struct winctrls *controltrees[8]; /* can have several of these */ int nctrltrees; char *wintitle; /* title of actual window */ char *errtitle; /* title of error sub-messageboxes */ void *data; /* data to pass in refresh events */ union control *focused, *lastfocused; /* which ctrl has focus now/before */ bool shortcuts[128]; /* track which shortcuts in use */ bool coloursel_wanted; /* has an event handler asked for * a colour selector? */ struct { unsigned char r, g, b; /* 0-255 */ bool ok; } coloursel_result; tree234 *privdata; /* stores per-control private data */ bool ended; /* has the dialog been ended? */ int endresult; /* and if so, what was the result? */ bool fixed_pitch_fonts; /* are we constrained to fixed fonts? */ }; /* * Exports from winctrls.c. */ void ctlposinit(struct ctlpos *cp, HWND hwnd, int leftborder, int rightborder, int topborder); HWND doctl(struct ctlpos *cp, RECT r, char *wclass, int wstyle, int exstyle, char *wtext, int wid); void bartitle(struct ctlpos *cp, char *name, int id); void beginbox(struct ctlpos *cp, char *name, int idbox); void endbox(struct ctlpos *cp); void editboxfw(struct ctlpos *cp, bool password, char *text, int staticid, int editid); void radioline(struct ctlpos *cp, char *text, int id, int nacross, ...); void bareradioline(struct ctlpos *cp, int nacross, ...); void radiobig(struct ctlpos *cp, char *text, int id, ...); void checkbox(struct ctlpos *cp, char *text, int id); void statictext(struct ctlpos *cp, char *text, int lines, int id); void staticbtn(struct ctlpos *cp, char *stext, int sid, char *btext, int bid); void static2btn(struct ctlpos *cp, char *stext, int sid, char *btext1, int bid1, char *btext2, int bid2); void staticedit(struct ctlpos *cp, char *stext, int sid, int eid, int percentedit); void staticddl(struct ctlpos *cp, char *stext, int sid, int lid, int percentlist); void combobox(struct ctlpos *cp, char *text, int staticid, int listid); void staticpassedit(struct ctlpos *cp, char *stext, int sid, int eid, int percentedit); void bigeditctrl(struct ctlpos *cp, char *stext, int sid, int eid, int lines); void ersatztab(struct ctlpos *cp, char *stext, int sid, int lid, int s2id); void editbutton(struct ctlpos *cp, char *stext, int sid, int eid, char *btext, int bid); void sesssaver(struct ctlpos *cp, char *text, int staticid, int editid, int listid, ...); void envsetter(struct ctlpos *cp, char *stext, int sid, char *e1stext, int e1sid, int e1id, char *e2stext, int e2sid, int e2id, int listid, char *b1text, int b1id, char *b2text, int b2id); void charclass(struct ctlpos *cp, char *stext, int sid, int listid, char *btext, int bid, int eid, char *s2text, int s2id); void colouredit(struct ctlpos *cp, char *stext, int sid, int listid, char *btext, int bid, ...); void prefslist(struct prefslist *hdl, struct ctlpos *cp, int lines, char *stext, int sid, int listid, int upbid, int dnbid); int handle_prefslist(struct prefslist *hdl, int *array, int maxmemb, bool is_dlmsg, HWND hwnd, WPARAM wParam, LPARAM lParam); void progressbar(struct ctlpos *cp, int id); void fwdsetter(struct ctlpos *cp, int listid, char *stext, int sid, char *e1stext, int e1sid, int e1id, char *e2stext, int e2sid, int e2id, char *btext, int bid, char *r1text, int r1id, char *r2text, int r2id); void dlg_auto_set_fixed_pitch_flag(dlgparam *dlg); bool dlg_get_fixed_pitch_flag(dlgparam *dlg); void dlg_set_fixed_pitch_flag(dlgparam *dlg, bool flag); #define MAX_SHORTCUTS_PER_CTRL 16 /* * This structure is what's stored for each `union control' in the * portable-dialog interface. */ struct winctrl { union control *ctrl; /* * The control may have several components at the Windows * level, with different dialog IDs. To avoid needing N * separate platformsidectrl structures (which could be stored * separately in a tree234 so that lookup by ID worked), we * impose the constraint that those IDs must be in a contiguous * block. */ int base_id; int num_ids; /* * For vertical alignment, the id of a particular representative * control that has the y-extent of the sensible part of the * control. */ int align_id; /* * Remember what keyboard shortcuts were used by this control, * so that when we remove it again we can take them out of the * list in the dlgparam. */ char shortcuts[MAX_SHORTCUTS_PER_CTRL]; /* * Some controls need a piece of allocated memory in which to * store temporary data about the control. */ void *data; }; /* * And this structure holds a set of the above, in two separate * tree234s so that it can find an item by `union control' or by * dialog ID. */ struct winctrls { tree234 *byctrl, *byid; }; struct controlset; struct controlbox; void winctrl_init(struct winctrls *); void winctrl_cleanup(struct winctrls *); void winctrl_add(struct winctrls *, struct winctrl *); void winctrl_remove(struct winctrls *, struct winctrl *); struct winctrl *winctrl_findbyctrl(struct winctrls *, union control *); struct winctrl *winctrl_findbyid(struct winctrls *, int); struct winctrl *winctrl_findbyindex(struct winctrls *, int); void winctrl_layout(struct dlgparam *dp, struct winctrls *wc, struct ctlpos *cp, struct controlset *s, int *id); bool winctrl_handle_command(struct dlgparam *dp, UINT msg, WPARAM wParam, LPARAM lParam); void winctrl_rem_shortcuts(struct dlgparam *dp, struct winctrl *c); bool winctrl_context_help(struct dlgparam *dp, HWND hwnd, int id); void dp_init(struct dlgparam *dp); void dp_add_tree(struct dlgparam *dp, struct winctrls *tree); void dp_cleanup(struct dlgparam *dp); /* * Exports from wincfg.c. */ void win_setup_config_box(struct controlbox *b, HWND *hwndp, bool has_help, bool midsession, int protocol); /* * Exports from windlg.c. */ void defuse_showwindow(void); bool do_config(Conf *); bool do_reconfig(HWND, Conf *, int); void showeventlog(HWND); void showabout(HWND); void force_normal(HWND hwnd); void modal_about_box(HWND hwnd); void show_help(HWND hwnd); HWND event_log_window(void); /* * Exports from winmisc.c. */ extern DWORD osMajorVersion, osMinorVersion, osPlatformId; void init_winver(void); void dll_hijacking_protection(void); HMODULE load_system32_dll(const char *libname); const char *win_strerror(int error); void restrict_process_acl(void); bool restricted_acl(void); void escape_registry_key(const char *in, strbuf *out); void unescape_registry_key(const char *in, strbuf *out); bool is_console_handle(HANDLE); /* A few pieces of up-to-date Windows API definition needed for older * compilers. */ #ifndef LOAD_LIBRARY_SEARCH_SYSTEM32 #define LOAD_LIBRARY_SEARCH_SYSTEM32 0x00000800 #endif #ifndef LOAD_LIBRARY_SEARCH_USER_DIRS #define LOAD_LIBRARY_SEARCH_USER_DIRS 0x00000400 #endif #ifndef LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR #define LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR 0x00000100 #endif #ifndef DLL_DIRECTORY_COOKIE typedef PVOID DLL_DIRECTORY_COOKIE; DECLSPEC_IMPORT DLL_DIRECTORY_COOKIE WINAPI AddDllDirectory (PCWSTR NewDirectory); #endif /* * Exports from sizetip.c. */ void UpdateSizeTip(HWND src, int cx, int cy); void EnableSizeTip(bool 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 size_t (*handle_inputfn_t)( struct handle *h, const void *data, size_t len, int err); typedef void (*handle_outputfn_t)( struct handle *h, size_t new_backlog, int err); 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); size_t handle_write(struct handle *h, const void *data, size_t 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, size_t backlog); size_t 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); /* Analogue of stdio_sink in marshal.h, for a Windows handle */ struct handle_sink { struct handle *h; BinarySink_IMPLEMENTATION; }; void handle_sink_init(handle_sink *sink, struct handle *h); /* * Exports from winpgntc.c. */ char *agent_named_pipe_name(void); /* * Exports from winser.c. */ extern const struct BackendVtable serial_backend; /* * Exports from winjump.c. */ #define JUMPLIST_SUPPORTED /* suppress #defines in putty.h */ void add_session_to_jumplist(const char * const sessionname); void remove_session_from_jumplist(const char * const sessionname); void clear_jumplist(void); bool set_explicit_app_user_model_id(void); /* * Exports from winnoise.c. */ bool win_read_random(void *buf, unsigned wanted); /* returns true on success */ /* * Extra functions in winstore.c over and above the interface in * storage.h. * * These functions manipulate the Registry section which mirrors the * current Windows 7 jump list. (Because the real jump list storage is * write-only, we need to keep another copy of whatever we put in it, * so that we can put in a slightly modified version the next time.) */ /* Adds a saved session to the registry jump list mirror. 'item' is a * string naming a saved session. */ int add_to_jumplist_registry(const char *item); /* Removes an item from the registry jump list mirror. */ int remove_from_jumplist_registry(const char *item); /* Returns the current jump list entries from the registry. Caller * must free the returned pointer, which points to a contiguous * sequence of NUL-terminated strings in memory, terminated with an * empty one. */ char *get_jumplist_registry_entries(void); /* * Windows clipboard-UI wording. */ #define CLIPNAME_IMPLICIT "Last selected text" #define CLIPNAME_EXPLICIT "System clipboard" #define CLIPNAME_EXPLICIT_OBJECT "system clipboard" /* These defaults are the ones PuTTY has historically had */ #define CLIPUI_DEFAULT_AUTOCOPY true #define CLIPUI_DEFAULT_MOUSE CLIPUI_EXPLICIT #define CLIPUI_DEFAULT_INS CLIPUI_EXPLICIT /* In winmisc.c */ char *registry_get_string(HKEY root, const char *path, const char *leaf); /* In wincliloop.c */ typedef bool (*cliloop_pre_t)(void *vctx, const HANDLE **extra_handles, size_t *n_extra_handles); typedef bool (*cliloop_post_t)(void *vctx, size_t extra_handle_index); void cli_main_loop(cliloop_pre_t pre, cliloop_post_t post, void *ctx); bool cliloop_null_pre(void *vctx, const HANDLE **, size_t *); bool cliloop_null_post(void *vctx, size_t); #endif putty-0.76/windows/wintime.c0000644000175000017500000000115214072266315013073 00000000000000/* * wintime.c - Avoid trouble with time() returning (time_t)-1 on Windows. */ #include "putty.h" #include struct tm ltime(void) { SYSTEMTIME st; struct tm tm; memset(&tm, 0, sizeof(tm)); /* in case there are any other fields */ GetLocalTime(&st); tm.tm_sec=st.wSecond; tm.tm_min=st.wMinute; tm.tm_hour=st.wHour; tm.tm_mday=st.wDay; tm.tm_mon=st.wMonth-1; tm.tm_year=(st.wYear>=1900?st.wYear-1900:0); tm.tm_wday=st.wDayOfWeek; tm.tm_yday=-1; /* GetLocalTime doesn't tell us */ tm.tm_isdst=0; /* GetLocalTime doesn't tell us */ return tm; } putty-0.76/windows/winucs.c0000644000175000017500000013515414072266315012741 00000000000000#include #include #include #include #include #include "putty.h" #include "terminal.h" #include "misc.h" /* Character conversion arrays; they are usually taken from windows, * the xterm one has the four scanlines that have no unicode 2.0 * equivalents mapped to their unicode 3.0 locations. */ static const WCHAR unitab_xterm_std[32] = { 0x2666, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0, 0x00b1, 0x2424, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c, 0x23ba, 0x23bb, 0x2500, 0x23bc, 0x23bd, 0x251c, 0x2524, 0x2534, 0x252c, 0x2502, 0x2264, 0x2265, 0x03c0, 0x2260, 0x00a3, 0x00b7, 0x0020 }; /* * If the codepage is non-zero it's a window codepage, zero means use a * local codepage. The name is always converted to the first of any * duplicate definitions. */ /* * Tables for ISO-8859-{1-10,13-16} derived from those downloaded * 2001-10-02 from -- jtn * Table for ISO-8859-11 derived from same on 2002-11-18. -- bjh21 */ /* XXX: This could be done algorithmically, but I'm not sure it's * worth the hassle -- jtn */ /* ISO/IEC 8859-1:1998 (Latin-1, "Western", "West European") */ static const wchar_t iso_8859_1[] = { 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF, 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF }; /* ISO/IEC 8859-2:1999 (Latin-2, "Central European", "East European") */ static const wchar_t iso_8859_2[] = { 0x00A0, 0x0104, 0x02D8, 0x0141, 0x00A4, 0x013D, 0x015A, 0x00A7, 0x00A8, 0x0160, 0x015E, 0x0164, 0x0179, 0x00AD, 0x017D, 0x017B, 0x00B0, 0x0105, 0x02DB, 0x0142, 0x00B4, 0x013E, 0x015B, 0x02C7, 0x00B8, 0x0161, 0x015F, 0x0165, 0x017A, 0x02DD, 0x017E, 0x017C, 0x0154, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x0139, 0x0106, 0x00C7, 0x010C, 0x00C9, 0x0118, 0x00CB, 0x011A, 0x00CD, 0x00CE, 0x010E, 0x0110, 0x0143, 0x0147, 0x00D3, 0x00D4, 0x0150, 0x00D6, 0x00D7, 0x0158, 0x016E, 0x00DA, 0x0170, 0x00DC, 0x00DD, 0x0162, 0x00DF, 0x0155, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x013A, 0x0107, 0x00E7, 0x010D, 0x00E9, 0x0119, 0x00EB, 0x011B, 0x00ED, 0x00EE, 0x010F, 0x0111, 0x0144, 0x0148, 0x00F3, 0x00F4, 0x0151, 0x00F6, 0x00F7, 0x0159, 0x016F, 0x00FA, 0x0171, 0x00FC, 0x00FD, 0x0163, 0x02D9 }; /* ISO/IEC 8859-3:1999 (Latin-3, "South European", "Maltese & Esperanto") */ static const wchar_t iso_8859_3[] = { 0x00A0, 0x0126, 0x02D8, 0x00A3, 0x00A4, 0xFFFD, 0x0124, 0x00A7, 0x00A8, 0x0130, 0x015E, 0x011E, 0x0134, 0x00AD, 0xFFFD, 0x017B, 0x00B0, 0x0127, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x0125, 0x00B7, 0x00B8, 0x0131, 0x015F, 0x011F, 0x0135, 0x00BD, 0xFFFD, 0x017C, 0x00C0, 0x00C1, 0x00C2, 0xFFFD, 0x00C4, 0x010A, 0x0108, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, 0xFFFD, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x0120, 0x00D6, 0x00D7, 0x011C, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x016C, 0x015C, 0x00DF, 0x00E0, 0x00E1, 0x00E2, 0xFFFD, 0x00E4, 0x010B, 0x0109, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, 0xFFFD, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x0121, 0x00F6, 0x00F7, 0x011D, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x016D, 0x015D, 0x02D9 }; /* ISO/IEC 8859-4:1998 (Latin-4, "North European") */ static const wchar_t iso_8859_4[] = { 0x00A0, 0x0104, 0x0138, 0x0156, 0x00A4, 0x0128, 0x013B, 0x00A7, 0x00A8, 0x0160, 0x0112, 0x0122, 0x0166, 0x00AD, 0x017D, 0x00AF, 0x00B0, 0x0105, 0x02DB, 0x0157, 0x00B4, 0x0129, 0x013C, 0x02C7, 0x00B8, 0x0161, 0x0113, 0x0123, 0x0167, 0x014A, 0x017E, 0x014B, 0x0100, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x012E, 0x010C, 0x00C9, 0x0118, 0x00CB, 0x0116, 0x00CD, 0x00CE, 0x012A, 0x0110, 0x0145, 0x014C, 0x0136, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00D8, 0x0172, 0x00DA, 0x00DB, 0x00DC, 0x0168, 0x016A, 0x00DF, 0x0101, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x012F, 0x010D, 0x00E9, 0x0119, 0x00EB, 0x0117, 0x00ED, 0x00EE, 0x012B, 0x0111, 0x0146, 0x014D, 0x0137, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x0173, 0x00FA, 0x00FB, 0x00FC, 0x0169, 0x016B, 0x02D9 }; /* ISO/IEC 8859-5:1999 (Latin/Cyrillic) */ static const wchar_t iso_8859_5[] = { 0x00A0, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407, 0x0408, 0x0409, 0x040A, 0x040B, 0x040C, 0x00AD, 0x040E, 0x040F, 0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F, 0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 0x0428, 0x0429, 0x042A, 0x042B, 0x042C, 0x042D, 0x042E, 0x042F, 0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, 0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 0x0448, 0x0449, 0x044A, 0x044B, 0x044C, 0x044D, 0x044E, 0x044F, 0x2116, 0x0451, 0x0452, 0x0453, 0x0454, 0x0455, 0x0456, 0x0457, 0x0458, 0x0459, 0x045A, 0x045B, 0x045C, 0x00A7, 0x045E, 0x045F }; /* ISO/IEC 8859-6:1999 (Latin/Arabic) */ static const wchar_t iso_8859_6[] = { 0x00A0, 0xFFFD, 0xFFFD, 0xFFFD, 0x00A4, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0x060C, 0x00AD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0x061B, 0xFFFD, 0xFFFD, 0xFFFD, 0x061F, 0xFFFD, 0x0621, 0x0622, 0x0623, 0x0624, 0x0625, 0x0626, 0x0627, 0x0628, 0x0629, 0x062A, 0x062B, 0x062C, 0x062D, 0x062E, 0x062F, 0x0630, 0x0631, 0x0632, 0x0633, 0x0634, 0x0635, 0x0636, 0x0637, 0x0638, 0x0639, 0x063A, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0x0640, 0x0641, 0x0642, 0x0643, 0x0644, 0x0645, 0x0646, 0x0647, 0x0648, 0x0649, 0x064A, 0x064B, 0x064C, 0x064D, 0x064E, 0x064F, 0x0650, 0x0651, 0x0652, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD }; /* ISO 8859-7:1987 (Latin/Greek) */ static const wchar_t iso_8859_7[] = { 0x00A0, 0x2018, 0x2019, 0x00A3, 0xFFFD, 0xFFFD, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0xFFFD, 0x00AB, 0x00AC, 0x00AD, 0xFFFD, 0x2015, 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x0384, 0x0385, 0x0386, 0x00B7, 0x0388, 0x0389, 0x038A, 0x00BB, 0x038C, 0x00BD, 0x038E, 0x038F, 0x0390, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, 0x039D, 0x039E, 0x039F, 0x03A0, 0x03A1, 0xFFFD, 0x03A3, 0x03A4, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, 0x03AA, 0x03AB, 0x03AC, 0x03AD, 0x03AE, 0x03AF, 0x03B0, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8, 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0, 0x03C1, 0x03C2, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, 0x03C9, 0x03CA, 0x03CB, 0x03CC, 0x03CD, 0x03CE, 0xFFFD }; /* ISO/IEC 8859-8:1999 (Latin/Hebrew) */ static const wchar_t iso_8859_8[] = { 0x00A0, 0xFFFD, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00D7, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00F7, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0x2017, 0x05D0, 0x05D1, 0x05D2, 0x05D3, 0x05D4, 0x05D5, 0x05D6, 0x05D7, 0x05D8, 0x05D9, 0x05DA, 0x05DB, 0x05DC, 0x05DD, 0x05DE, 0x05DF, 0x05E0, 0x05E1, 0x05E2, 0x05E3, 0x05E4, 0x05E5, 0x05E6, 0x05E7, 0x05E8, 0x05E9, 0x05EA, 0xFFFD, 0xFFFD, 0x200E, 0x200F, 0xFFFD }; /* ISO/IEC 8859-9:1999 (Latin-5, "Turkish") */ static const wchar_t iso_8859_9[] = { 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF, 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, 0x011E, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x0130, 0x015E, 0x00DF, 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, 0x011F, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x0131, 0x015F, 0x00FF }; /* ISO/IEC 8859-10:1998 (Latin-6, "Nordic" [Sami, Inuit, Icelandic]) */ static const wchar_t iso_8859_10[] = { 0x00A0, 0x0104, 0x0112, 0x0122, 0x012A, 0x0128, 0x0136, 0x00A7, 0x013B, 0x0110, 0x0160, 0x0166, 0x017D, 0x00AD, 0x016A, 0x014A, 0x00B0, 0x0105, 0x0113, 0x0123, 0x012B, 0x0129, 0x0137, 0x00B7, 0x013C, 0x0111, 0x0161, 0x0167, 0x017E, 0x2015, 0x016B, 0x014B, 0x0100, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x012E, 0x010C, 0x00C9, 0x0118, 0x00CB, 0x0116, 0x00CD, 0x00CE, 0x00CF, 0x00D0, 0x0145, 0x014C, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x0168, 0x00D8, 0x0172, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF, 0x0101, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x012F, 0x010D, 0x00E9, 0x0119, 0x00EB, 0x0117, 0x00ED, 0x00EE, 0x00EF, 0x00F0, 0x0146, 0x014D, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x0169, 0x00F8, 0x0173, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x0138 }; /* ISO/IEC 8859-11:2001 ("Thai", "TIS620") */ static const wchar_t iso_8859_11[] = { 0x00A0, 0x0E01, 0x0E02, 0x0E03, 0x0E04, 0x0E05, 0x0E06, 0x0E07, 0x0E08, 0x0E09, 0x0E0A, 0x0E0B, 0x0E0C, 0x0E0D, 0x0E0E, 0x0E0F, 0x0E10, 0x0E11, 0x0E12, 0x0E13, 0x0E14, 0x0E15, 0x0E16, 0x0E17, 0x0E18, 0x0E19, 0x0E1A, 0x0E1B, 0x0E1C, 0x0E1D, 0x0E1E, 0x0E1F, 0x0E20, 0x0E21, 0x0E22, 0x0E23, 0x0E24, 0x0E25, 0x0E26, 0x0E27, 0x0E28, 0x0E29, 0x0E2A, 0x0E2B, 0x0E2C, 0x0E2D, 0x0E2E, 0x0E2F, 0x0E30, 0x0E31, 0x0E32, 0x0E33, 0x0E34, 0x0E35, 0x0E36, 0x0E37, 0x0E38, 0x0E39, 0x0E3A, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0x0E3F, 0x0E40, 0x0E41, 0x0E42, 0x0E43, 0x0E44, 0x0E45, 0x0E46, 0x0E47, 0x0E48, 0x0E49, 0x0E4A, 0x0E4B, 0x0E4C, 0x0E4D, 0x0E4E, 0x0E4F, 0x0E50, 0x0E51, 0x0E52, 0x0E53, 0x0E54, 0x0E55, 0x0E56, 0x0E57, 0x0E58, 0x0E59, 0x0E5A, 0x0E5B, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD }; /* ISO/IEC 8859-13:1998 (Latin-7, "Baltic Rim") */ static const wchar_t iso_8859_13[] = { 0x00A0, 0x201D, 0x00A2, 0x00A3, 0x00A4, 0x201E, 0x00A6, 0x00A7, 0x00D8, 0x00A9, 0x0156, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00C6, 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x201C, 0x00B5, 0x00B6, 0x00B7, 0x00F8, 0x00B9, 0x0157, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00E6, 0x0104, 0x012E, 0x0100, 0x0106, 0x00C4, 0x00C5, 0x0118, 0x0112, 0x010C, 0x00C9, 0x0179, 0x0116, 0x0122, 0x0136, 0x012A, 0x013B, 0x0160, 0x0143, 0x0145, 0x00D3, 0x014C, 0x00D5, 0x00D6, 0x00D7, 0x0172, 0x0141, 0x015A, 0x016A, 0x00DC, 0x017B, 0x017D, 0x00DF, 0x0105, 0x012F, 0x0101, 0x0107, 0x00E4, 0x00E5, 0x0119, 0x0113, 0x010D, 0x00E9, 0x017A, 0x0117, 0x0123, 0x0137, 0x012B, 0x013C, 0x0161, 0x0144, 0x0146, 0x00F3, 0x014D, 0x00F5, 0x00F6, 0x00F7, 0x0173, 0x0142, 0x015B, 0x016B, 0x00FC, 0x017C, 0x017E, 0x2019 }; /* ISO/IEC 8859-14:1998 (Latin-8, "Celtic", "Gaelic/Welsh") */ static const wchar_t iso_8859_14[] = { 0x00A0, 0x1E02, 0x1E03, 0x00A3, 0x010A, 0x010B, 0x1E0A, 0x00A7, 0x1E80, 0x00A9, 0x1E82, 0x1E0B, 0x1EF2, 0x00AD, 0x00AE, 0x0178, 0x1E1E, 0x1E1F, 0x0120, 0x0121, 0x1E40, 0x1E41, 0x00B6, 0x1E56, 0x1E81, 0x1E57, 0x1E83, 0x1E60, 0x1EF3, 0x1E84, 0x1E85, 0x1E61, 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, 0x0174, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x1E6A, 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x0176, 0x00DF, 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, 0x0175, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x1E6B, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x0177, 0x00FF }; /* ISO/IEC 8859-15:1999 (Latin-9 aka -0, "euro") */ static const wchar_t iso_8859_15[] = { 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x20AC, 0x00A5, 0x0160, 0x00A7, 0x0161, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF, 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x017D, 0x00B5, 0x00B6, 0x00B7, 0x017E, 0x00B9, 0x00BA, 0x00BB, 0x0152, 0x0153, 0x0178, 0x00BF, 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00DE, 0x00DF, 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF }; /* ISO/IEC 8859-16:2001 (Latin-10, "Balkan") */ static const wchar_t iso_8859_16[] = { 0x00A0, 0x0104, 0x0105, 0x0141, 0x20AC, 0x201E, 0x0160, 0x00A7, 0x0161, 0x00A9, 0x0218, 0x00AB, 0x0179, 0x00AD, 0x017A, 0x017B, 0x00B0, 0x00B1, 0x010C, 0x0142, 0x017D, 0x201D, 0x00B6, 0x00B7, 0x017E, 0x010D, 0x0219, 0x00BB, 0x0152, 0x0153, 0x0178, 0x017C, 0x00C0, 0x00C1, 0x00C2, 0x0102, 0x00C4, 0x0106, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, 0x0110, 0x0143, 0x00D2, 0x00D3, 0x00D4, 0x0150, 0x00D6, 0x015A, 0x0170, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x0118, 0x021A, 0x00DF, 0x00E0, 0x00E1, 0x00E2, 0x0103, 0x00E4, 0x0107, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, 0x0111, 0x0144, 0x00F2, 0x00F3, 0x00F4, 0x0151, 0x00F6, 0x015B, 0x0171, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x0119, 0x021B, 0x00FF }; static const wchar_t roman8[] = { 0x00A0, 0x00C0, 0x00C2, 0x00C8, 0x00CA, 0x00CB, 0x00CE, 0x00CF, 0x00B4, 0x02CB, 0x02C6, 0x00A8, 0x02DC, 0x00D9, 0x00DB, 0x20A4, 0x00AF, 0x00DD, 0x00FD, 0x00B0, 0x00C7, 0x00E7, 0x00D1, 0x00F1, 0x00A1, 0x00BF, 0x00A4, 0x00A3, 0x00A5, 0x00A7, 0x0192, 0x00A2, 0x00E2, 0x00EA, 0x00F4, 0x00FB, 0x00E1, 0x00E9, 0x00F3, 0x00FA, 0x00E0, 0x00E8, 0x00F2, 0x00F9, 0x00E4, 0x00EB, 0x00F6, 0x00FC, 0x00C5, 0x00EE, 0x00D8, 0x00C6, 0x00E5, 0x00ED, 0x00F8, 0x00E6, 0x00C4, 0x00EC, 0x00D6, 0x00DC, 0x00C9, 0x00EF, 0x00DF, 0x00D4, 0x00C1, 0x00C3, 0x00E3, 0x00D0, 0x00F0, 0x00CD, 0x00CC, 0x00D3, 0x00D2, 0x00D5, 0x00F5, 0x0160, 0x0161, 0x00DA, 0x0178, 0x00FF, 0x00DE, 0x00FE, 0x00B7, 0x00B5, 0x00B6, 0x00BE, 0x2014, 0x00BC, 0x00BD, 0x00AA, 0x00BA, 0x00AB, 0x25A0, 0x00BB, 0x00B1, 0xFFFD }; static const wchar_t koi8_u[] = { 0x2500, 0x2502, 0x250C, 0x2510, 0x2514, 0x2518, 0x251C, 0x2524, 0x252C, 0x2534, 0x253C, 0x2580, 0x2584, 0x2588, 0x258C, 0x2590, 0x2591, 0x2592, 0x2593, 0x2320, 0x25A0, 0x2022, 0x221A, 0x2248, 0x2264, 0x2265, 0x00A0, 0x2321, 0x00B0, 0x00B2, 0x00B7, 0x00F7, 0x2550, 0x2551, 0x2552, 0x0451, 0x0454, 0x2554, 0x0456, 0x0457, 0x2557, 0x2558, 0x2559, 0x255A, 0x255B, 0x0491, 0x255D, 0x255E, 0x255F, 0x2560, 0x2561, 0x0401, 0x0404, 0x2563, 0x0406, 0x0407, 0x2566, 0x2567, 0x2568, 0x2569, 0x256A, 0x0490, 0x256C, 0x00A9, 0x044E, 0x0430, 0x0431, 0x0446, 0x0434, 0x0435, 0x0444, 0x0433, 0x0445, 0x0438, 0x0439, 0x043A, 0x043B, 0x043C, 0x043D, 0x043E, 0x043F, 0x044F, 0x0440, 0x0441, 0x0442, 0x0443, 0x0436, 0x0432, 0x044C, 0x044B, 0x0437, 0x0448, 0x044D, 0x0449, 0x0447, 0x044A, 0x042E, 0x0410, 0x0411, 0x0426, 0x0414, 0x0415, 0x0424, 0x0413, 0x0425, 0x0418, 0x0419, 0x041A, 0x041B, 0x041C, 0x041D, 0x041E, 0x041F, 0x042F, 0x0420, 0x0421, 0x0422, 0x0423, 0x0416, 0x0412, 0x042C, 0x042B, 0x0417, 0x0428, 0x042D, 0x0429, 0x0427, 0x042A }; static const wchar_t vscii[] = { 0x0000, 0x0001, 0x1EB2, 0x0003, 0x0004, 0x1EB4, 0x1EAA, 0x0007, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0010, 0x0011, 0x0012, 0x0013, 0x1EF6, 0x0015, 0x0016, 0x0017, 0x0018, 0x1EF8, 0x001a, 0x001b, 0x001c, 0x001d, 0x1EF4, 0x001f, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007f, 0x1EA0, 0x1EAE, 0x1EB0, 0x1EB6, 0x1EA4, 0x1EA6, 0x1EA8, 0x1EAC, 0x1EBC, 0x1EB8, 0x1EBE, 0x1EC0, 0x1EC2, 0x1EC4, 0x1EC6, 0x1ED0, 0x1ED2, 0x1ED4, 0x1ED6, 0x1ED8, 0x1EE2, 0x1EDA, 0x1EDC, 0x1EDE, 0x1ECA, 0x1ECE, 0x1ECC, 0x1EC8, 0x1EE6, 0x0168, 0x1EE4, 0x1EF2, 0x00D5, 0x1EAF, 0x1EB1, 0x1EB7, 0x1EA5, 0x1EA7, 0x1EA8, 0x1EAD, 0x1EBD, 0x1EB9, 0x1EBF, 0x1EC1, 0x1EC3, 0x1EC5, 0x1EC7, 0x1ED1, 0x1ED3, 0x1ED5, 0x1ED7, 0x1EE0, 0x01A0, 0x1ED9, 0x1EDD, 0x1EDF, 0x1ECB, 0x1EF0, 0x1EE8, 0x1EEA, 0x1EEC, 0x01A1, 0x1EDB, 0x01AF, 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x1EA2, 0x0102, 0x1EB3, 0x1EB5, 0x00C8, 0x00C9, 0x00CA, 0x1EBA, 0x00CC, 0x00CD, 0x0128, 0x1EF3, 0x0110, 0x1EE9, 0x00D2, 0x00D3, 0x00D4, 0x1EA1, 0x1EF7, 0x1EEB, 0x1EED, 0x00D9, 0x00DA, 0x1EF9, 0x1EF5, 0x00DD, 0x1EE1, 0x01B0, 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x1EA3, 0x0103, 0x1EEF, 0x1EAB, 0x00E8, 0x00E9, 0x00EA, 0x1EBB, 0x00EC, 0x00ED, 0x0129, 0x1EC9, 0x0111, 0x1EF1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x1ECF, 0x1ECD, 0x1EE5, 0x00F9, 0x00FA, 0x0169, 0x1EE7, 0x00FD, 0x1EE3, 0x1EEE }; static const wchar_t dec_mcs[] = { 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0xFFFD, 0x00A5, 0xFFFD, 0x00A7, 0x00A4, 0x00A9, 0x00AA, 0x00AB, 0xFFFD, 0xFFFD, 0xFFFD, 0xFFFD, 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0xFFFD, 0x00B5, 0x00B6, 0x00B7, 0xFFFD, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0xFFFD, 0x00BF, 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF, 0xFFFD, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x0152, 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x0178, 0xFFFD, 0x00DF, 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF, 0xFFFD, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x0153, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FF, 0xFFFD, 0xFFFD }; /* Mazovia (Polish) aka CP620 * from "Mazowia to Unicode table", 04/24/96, Mikolaj Jedrzejak */ static const wchar_t mazovia[] = { /* Code point 0x9B is "zloty" symbol (zŽ), which is not * widely used and for which there is no Unicode equivalent. * One reference shows 0xA8 as U+00A7 SECTION SIGN, but we're * told that's incorrect. */ 0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x0105, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x0107, 0x00C4, 0x0104, 0x0118, 0x0119, 0x0142, 0x00F4, 0x00F6, 0x0106, 0x00FB, 0x00F9, 0x015a, 0x00D6, 0x00DC, 0xFFFD, 0x0141, 0x00A5, 0x015b, 0x0192, 0x0179, 0x017b, 0x00F3, 0x00d3, 0x0144, 0x0143, 0x017a, 0x017c, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB, 0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510, 0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567, 0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580, 0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229, 0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0 }; struct cp_list_item { 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; bool used_dtf = false; int vtmode; /* 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=false; } vtmode = conf_get_int(conf, CONF_vtmode); if (vtmode == VT_OEMONLY) { ucsdata->font_codepage = 437; ucsdata->dbcs_screenfont = false; 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 = true; 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); ucsdata->unitab_line[127] = (WCHAR) 127; } else { get_unitab(ucsdata->line_codepage, ucsdata->unitab_line, 0); } #if 0 debug("Line cp%d, Font cp%d%s\n", ucsdata->line_codepage, ucsdata->font_codepage, ucsdata->dbcs_screenfont ? " DBCS" : ""); for (i = 0; i < 256; i += 16) { for (j = 0; j < 16; j++) { debug("%04x%s", ucsdata->unitab_line[i + j], j == 15 ? "" : ","); } debug("\n"); } #endif /* VT100 graphics - NB: Broken for non-ascii CP's */ memcpy(ucsdata->unitab_xterm, ucsdata->unitab_line, sizeof(ucsdata->unitab_xterm)); memcpy(ucsdata->unitab_xterm + '`', unitab_xterm_std, sizeof(unitab_xterm_std)); ucsdata->unitab_xterm['_'] = ' '; /* Generate UCS ->line page table. */ if (ucsdata->uni_tbl) { for (i = 0; i < 256; i++) if (ucsdata->uni_tbl[i]) sfree(ucsdata->uni_tbl[i]); sfree(ucsdata->uni_tbl); ucsdata->uni_tbl = 0; } if (!used_dtf) { for (i = 0; i < 256; i++) { if (DIRECT_CHAR(ucsdata->unitab_line[i])) continue; if (DIRECT_FONT(ucsdata->unitab_line[i])) continue; if (!ucsdata->uni_tbl) { ucsdata->uni_tbl = snewn(256, char *); memset(ucsdata->uni_tbl, 0, 256 * sizeof(char *)); } j = ((ucsdata->unitab_line[i] >> 8) & 0xFF); if (!ucsdata->uni_tbl[j]) { ucsdata->uni_tbl[j] = snewn(256, char); memset(ucsdata->uni_tbl[j], 0, 256 * sizeof(char)); } ucsdata->uni_tbl[j][ucsdata->unitab_line[i] & 0xFF] = i; } } /* Find the line control characters. */ for (i = 0; i < 256; i++) if (ucsdata->unitab_line[i] < ' ' || (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++) if (!DIRECT_FONT(ucsdata->unitab_line[i]) && ucsdata->unitab_line[i] >= 160 && ucsdata->unitab_line[i] < 256) { ucsdata->unitab_line[i] = (WCHAR) (CSET_ACP + poorman_latin1[ucsdata->unitab_line[i] - 160]); } for (i = 96; i < 127; i++) if (!DIRECT_FONT(ucsdata->unitab_xterm[i])) ucsdata->unitab_xterm[i] = (WCHAR) (CSET_ACP + poorman_vt100[i - 96]); for(i=128;i<256;i++) if (!DIRECT_FONT(ucsdata->unitab_scoacs[i])) ucsdata->unitab_scoacs[i] = (WCHAR) (CSET_ACP + poorman_scoacs[i - 128]); } } static void link_font(WCHAR * line_tbl, WCHAR * font_tbl, WCHAR attr) { int font_index, line_index, i; for (line_index = 0; line_index < 256; line_index++) { if (DIRECT_FONT(line_tbl[line_index])) continue; for(i = 0; i < 256; i++) { font_index = ((32 + i) & 0xFF); if (line_tbl[line_index] == font_tbl[font_index]) { line_tbl[line_index] = (WCHAR) (attr + font_index); break; } } } } wchar_t xlat_uskbd2cyrllic(int ch) { static const wchar_t cyrtab[] = { 0, 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, 0x042d, 35, 36, 37, 38, 0x044d, 40, 41, 42, 0x0406, 0x0431, 0x0454, 0x044e, 0x002e, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 0x0416, 0x0436, 0x0411, 0x0456, 0x042e, 0x002c, 64, 0x0424, 0x0418, 0x0421, 0x0412, 0x0423, 0x0410, 0x041f, 0x0420, 0x0428, 0x041e, 0x041b, 0x0414, 0x042c, 0x0422, 0x0429, 0x0417, 0x0419, 0x041a, 0x042b, 0x0415, 0x0413, 0x041c, 0x0426, 0x0427, 0x041d, 0x042f, 0x0445, 0x0457, 0x044a, 94, 0x0404, 96, 0x0444, 0x0438, 0x0441, 0x0432, 0x0443, 0x0430, 0x043f, 0x0440, 0x0448, 0x043e, 0x043b, 0x0434, 0x044c, 0x0442, 0x0449, 0x0437, 0x0439, 0x043a, 0x044b, 0x0435, 0x0433, 0x043c, 0x0446, 0x0447, 0x043d, 0x044f, 0x0425, 0x0407, 0x042a, 126, 127 }; return cyrtab[ch&0x7F]; } int check_compose_internal(int first, int second, int recurse) { static const struct { char first, second; wchar_t composed; } composetbl[] = { {0x2b, 0x2b, 0x0023}, {0x41, 0x41, 0x0040}, {0x28, 0x28, 0x005b}, {0x2f, 0x2f, 0x005c}, {0x29, 0x29, 0x005d}, {0x28, 0x2d, 0x007b}, {0x2d, 0x29, 0x007d}, {0x2f, 0x5e, 0x007c}, {0x21, 0x21, 0x00a1}, {0x43, 0x2f, 0x00a2}, {0x43, 0x7c, 0x00a2}, {0x4c, 0x2d, 0x00a3}, {0x4c, 0x3d, 0x20a4}, {0x58, 0x4f, 0x00a4}, {0x58, 0x30, 0x00a4}, {0x59, 0x2d, 0x00a5}, {0x59, 0x3d, 0x00a5}, {0x7c, 0x7c, 0x00a6}, {0x53, 0x4f, 0x00a7}, {0x53, 0x21, 0x00a7}, {0x53, 0x30, 0x00a7}, {0x22, 0x22, 0x00a8}, {0x43, 0x4f, 0x00a9}, {0x43, 0x30, 0x00a9}, {0x41, 0x5f, 0x00aa}, {0x3c, 0x3c, 0x00ab}, {0x2c, 0x2d, 0x00ac}, {0x2d, 0x2d, 0x00ad}, {0x52, 0x4f, 0x00ae}, {0x2d, 0x5e, 0x00af}, {0x30, 0x5e, 0x00b0}, {0x2b, 0x2d, 0x00b1}, {0x32, 0x5e, 0x00b2}, {0x33, 0x5e, 0x00b3}, {0x27, 0x27, 0x00b4}, {0x2f, 0x55, 0x00b5}, {0x50, 0x21, 0x00b6}, {0x2e, 0x5e, 0x00b7}, {0x2c, 0x2c, 0x00b8}, {0x31, 0x5e, 0x00b9}, {0x4f, 0x5f, 0x00ba}, {0x3e, 0x3e, 0x00bb}, {0x31, 0x34, 0x00bc}, {0x31, 0x32, 0x00bd}, {0x33, 0x34, 0x00be}, {0x3f, 0x3f, 0x00bf}, {0x60, 0x41, 0x00c0}, {0x27, 0x41, 0x00c1}, {0x5e, 0x41, 0x00c2}, {0x7e, 0x41, 0x00c3}, {0x22, 0x41, 0x00c4}, {0x2a, 0x41, 0x00c5}, {0x41, 0x45, 0x00c6}, {0x2c, 0x43, 0x00c7}, {0x60, 0x45, 0x00c8}, {0x27, 0x45, 0x00c9}, {0x5e, 0x45, 0x00ca}, {0x22, 0x45, 0x00cb}, {0x60, 0x49, 0x00cc}, {0x27, 0x49, 0x00cd}, {0x5e, 0x49, 0x00ce}, {0x22, 0x49, 0x00cf}, {0x2d, 0x44, 0x00d0}, {0x7e, 0x4e, 0x00d1}, {0x60, 0x4f, 0x00d2}, {0x27, 0x4f, 0x00d3}, {0x5e, 0x4f, 0x00d4}, {0x7e, 0x4f, 0x00d5}, {0x22, 0x4f, 0x00d6}, {0x58, 0x58, 0x00d7}, {0x2f, 0x4f, 0x00d8}, {0x60, 0x55, 0x00d9}, {0x27, 0x55, 0x00da}, {0x5e, 0x55, 0x00db}, {0x22, 0x55, 0x00dc}, {0x27, 0x59, 0x00dd}, {0x48, 0x54, 0x00de}, {0x73, 0x73, 0x00df}, {0x60, 0x61, 0x00e0}, {0x27, 0x61, 0x00e1}, {0x5e, 0x61, 0x00e2}, {0x7e, 0x61, 0x00e3}, {0x22, 0x61, 0x00e4}, {0x2a, 0x61, 0x00e5}, {0x61, 0x65, 0x00e6}, {0x2c, 0x63, 0x00e7}, {0x60, 0x65, 0x00e8}, {0x27, 0x65, 0x00e9}, {0x5e, 0x65, 0x00ea}, {0x22, 0x65, 0x00eb}, {0x60, 0x69, 0x00ec}, {0x27, 0x69, 0x00ed}, {0x5e, 0x69, 0x00ee}, {0x22, 0x69, 0x00ef}, {0x2d, 0x64, 0x00f0}, {0x7e, 0x6e, 0x00f1}, {0x60, 0x6f, 0x00f2}, {0x27, 0x6f, 0x00f3}, {0x5e, 0x6f, 0x00f4}, {0x7e, 0x6f, 0x00f5}, {0x22, 0x6f, 0x00f6}, {0x3a, 0x2d, 0x00f7}, {0x6f, 0x2f, 0x00f8}, {0x60, 0x75, 0x00f9}, {0x27, 0x75, 0x00fa}, {0x5e, 0x75, 0x00fb}, {0x22, 0x75, 0x00fc}, {0x27, 0x79, 0x00fd}, {0x68, 0x74, 0x00fe}, {0x22, 0x79, 0x00ff}, /* Unicode extras. */ {0x6f, 0x65, 0x0153}, {0x4f, 0x45, 0x0152}, /* Compose pairs from UCS */ {0x41, 0x2D, 0x0100}, {0x61, 0x2D, 0x0101}, {0x43, 0x27, 0x0106}, {0x63, 0x27, 0x0107}, {0x43, 0x5E, 0x0108}, {0x63, 0x5E, 0x0109}, {0x45, 0x2D, 0x0112}, {0x65, 0x2D, 0x0113}, {0x47, 0x5E, 0x011C}, {0x67, 0x5E, 0x011D}, {0x47, 0x2C, 0x0122}, {0x67, 0x2C, 0x0123}, {0x48, 0x5E, 0x0124}, {0x68, 0x5E, 0x0125}, {0x49, 0x7E, 0x0128}, {0x69, 0x7E, 0x0129}, {0x49, 0x2D, 0x012A}, {0x69, 0x2D, 0x012B}, {0x4A, 0x5E, 0x0134}, {0x6A, 0x5E, 0x0135}, {0x4B, 0x2C, 0x0136}, {0x6B, 0x2C, 0x0137}, {0x4C, 0x27, 0x0139}, {0x6C, 0x27, 0x013A}, {0x4C, 0x2C, 0x013B}, {0x6C, 0x2C, 0x013C}, {0x4E, 0x27, 0x0143}, {0x6E, 0x27, 0x0144}, {0x4E, 0x2C, 0x0145}, {0x6E, 0x2C, 0x0146}, {0x4F, 0x2D, 0x014C}, {0x6F, 0x2D, 0x014D}, {0x52, 0x27, 0x0154}, {0x72, 0x27, 0x0155}, {0x52, 0x2C, 0x0156}, {0x72, 0x2C, 0x0157}, {0x53, 0x27, 0x015A}, {0x73, 0x27, 0x015B}, {0x53, 0x5E, 0x015C}, {0x73, 0x5E, 0x015D}, {0x53, 0x2C, 0x015E}, {0x73, 0x2C, 0x015F}, {0x54, 0x2C, 0x0162}, {0x74, 0x2C, 0x0163}, {0x55, 0x7E, 0x0168}, {0x75, 0x7E, 0x0169}, {0x55, 0x2D, 0x016A}, {0x75, 0x2D, 0x016B}, {0x55, 0x2A, 0x016E}, {0x75, 0x2A, 0x016F}, {0x57, 0x5E, 0x0174}, {0x77, 0x5E, 0x0175}, {0x59, 0x5E, 0x0176}, {0x79, 0x5E, 0x0177}, {0x59, 0x22, 0x0178}, {0x5A, 0x27, 0x0179}, {0x7A, 0x27, 0x017A}, {0x47, 0x27, 0x01F4}, {0x67, 0x27, 0x01F5}, {0x4E, 0x60, 0x01F8}, {0x6E, 0x60, 0x01F9}, {0x45, 0x2C, 0x0228}, {0x65, 0x2C, 0x0229}, {0x59, 0x2D, 0x0232}, {0x79, 0x2D, 0x0233}, {0x44, 0x2C, 0x1E10}, {0x64, 0x2C, 0x1E11}, {0x47, 0x2D, 0x1E20}, {0x67, 0x2D, 0x1E21}, {0x48, 0x22, 0x1E26}, {0x68, 0x22, 0x1E27}, {0x48, 0x2C, 0x1E28}, {0x68, 0x2C, 0x1E29}, {0x4B, 0x27, 0x1E30}, {0x6B, 0x27, 0x1E31}, {0x4D, 0x27, 0x1E3E}, {0x6D, 0x27, 0x1E3F}, {0x50, 0x27, 0x1E54}, {0x70, 0x27, 0x1E55}, {0x56, 0x7E, 0x1E7C}, {0x76, 0x7E, 0x1E7D}, {0x57, 0x60, 0x1E80}, {0x77, 0x60, 0x1E81}, {0x57, 0x27, 0x1E82}, {0x77, 0x27, 0x1E83}, {0x57, 0x22, 0x1E84}, {0x77, 0x22, 0x1E85}, {0x58, 0x22, 0x1E8C}, {0x78, 0x22, 0x1E8D}, {0x5A, 0x5E, 0x1E90}, {0x7A, 0x5E, 0x1E91}, {0x74, 0x22, 0x1E97}, {0x77, 0x2A, 0x1E98}, {0x79, 0x2A, 0x1E99}, {0x45, 0x7E, 0x1EBC}, {0x65, 0x7E, 0x1EBD}, {0x59, 0x60, 0x1EF2}, {0x79, 0x60, 0x1EF3}, {0x59, 0x7E, 0x1EF8}, {0x79, 0x7E, 0x1EF9}, /* Compatible/possibles from UCS */ {0x49, 0x4A, 0x0132}, {0x69, 0x6A, 0x0133}, {0x4C, 0x4A, 0x01C7}, {0x4C, 0x6A, 0x01C8}, {0x6C, 0x6A, 0x01C9}, {0x4E, 0x4A, 0x01CA}, {0x4E, 0x6A, 0x01CB}, {0x6E, 0x6A, 0x01CC}, {0x44, 0x5A, 0x01F1}, {0x44, 0x7A, 0x01F2}, {0x64, 0x7A, 0x01F3}, {0x2E, 0x2E, 0x2025}, {0x21, 0x21, 0x203C}, {0x3F, 0x21, 0x2048}, {0x21, 0x3F, 0x2049}, {0x52, 0x73, 0x20A8}, {0x4E, 0x6F, 0x2116}, {0x53, 0x4D, 0x2120}, {0x54, 0x4D, 0x2122}, {0x49, 0x49, 0x2161}, {0x49, 0x56, 0x2163}, {0x56, 0x49, 0x2165}, {0x49, 0x58, 0x2168}, {0x58, 0x49, 0x216A}, {0x69, 0x69, 0x2171}, {0x69, 0x76, 0x2173}, {0x76, 0x69, 0x2175}, {0x69, 0x78, 0x2178}, {0x78, 0x69, 0x217A}, {0x31, 0x30, 0x2469}, {0x31, 0x31, 0x246A}, {0x31, 0x32, 0x246B}, {0x31, 0x33, 0x246C}, {0x31, 0x34, 0x246D}, {0x31, 0x35, 0x246E}, {0x31, 0x36, 0x246F}, {0x31, 0x37, 0x2470}, {0x31, 0x38, 0x2471}, {0x31, 0x39, 0x2472}, {0x32, 0x30, 0x2473}, {0x31, 0x2E, 0x2488}, {0x32, 0x2E, 0x2489}, {0x33, 0x2E, 0x248A}, {0x34, 0x2E, 0x248B}, {0x35, 0x2E, 0x248C}, {0x36, 0x2E, 0x248D}, {0x37, 0x2E, 0x248E}, {0x38, 0x2E, 0x248F}, {0x39, 0x2E, 0x2490}, {0x64, 0x61, 0x3372}, {0x41, 0x55, 0x3373}, {0x6F, 0x56, 0x3375}, {0x70, 0x63, 0x3376}, {0x70, 0x41, 0x3380}, {0x6E, 0x41, 0x3381}, {0x6D, 0x41, 0x3383}, {0x6B, 0x41, 0x3384}, {0x4B, 0x42, 0x3385}, {0x4D, 0x42, 0x3386}, {0x47, 0x42, 0x3387}, {0x70, 0x46, 0x338A}, {0x6E, 0x46, 0x338B}, {0x6D, 0x67, 0x338E}, {0x6B, 0x67, 0x338F}, {0x48, 0x7A, 0x3390}, {0x66, 0x6D, 0x3399}, {0x6E, 0x6D, 0x339A}, {0x6D, 0x6D, 0x339C}, {0x63, 0x6D, 0x339D}, {0x6B, 0x6D, 0x339E}, {0x50, 0x61, 0x33A9}, {0x70, 0x73, 0x33B0}, {0x6E, 0x73, 0x33B1}, {0x6D, 0x73, 0x33B3}, {0x70, 0x56, 0x33B4}, {0x6E, 0x56, 0x33B5}, {0x6D, 0x56, 0x33B7}, {0x6B, 0x56, 0x33B8}, {0x4D, 0x56, 0x33B9}, {0x70, 0x57, 0x33BA}, {0x6E, 0x57, 0x33BB}, {0x6D, 0x57, 0x33BD}, {0x6B, 0x57, 0x33BE}, {0x4D, 0x57, 0x33BF}, {0x42, 0x71, 0x33C3}, {0x63, 0x63, 0x33C4}, {0x63, 0x64, 0x33C5}, {0x64, 0x42, 0x33C8}, {0x47, 0x79, 0x33C9}, {0x68, 0x61, 0x33CA}, {0x48, 0x50, 0x33CB}, {0x69, 0x6E, 0x33CC}, {0x4B, 0x4B, 0x33CD}, {0x4B, 0x4D, 0x33CE}, {0x6B, 0x74, 0x33CF}, {0x6C, 0x6D, 0x33D0}, {0x6C, 0x6E, 0x33D1}, {0x6C, 0x78, 0x33D3}, {0x6D, 0x62, 0x33D4}, {0x50, 0x48, 0x33D7}, {0x50, 0x52, 0x33DA}, {0x73, 0x72, 0x33DB}, {0x53, 0x76, 0x33DC}, {0x57, 0x62, 0x33DD}, {0x66, 0x66, 0xFB00}, {0x66, 0x69, 0xFB01}, {0x66, 0x6C, 0xFB02}, {0x73, 0x74, 0xFB06}, {0, 0, 0} }, *c; int nc = -1; for (c = composetbl; c->first; c++) { if (c->first == first && c->second == second) return c->composed; } if (recurse == 0) { nc = check_compose_internal(second, first, 1); if (nc == -1) nc = check_compose_internal(toupper(first), toupper(second), 1); if (nc == -1) nc = check_compose_internal(toupper(second), toupper(first), 1); } return nc; } int check_compose(int first, int second) { return check_compose_internal(first, second, 0); } 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) codepage = -3; } } if (codepage == -1 && *cp_name) codepage = -2; return codepage; } const char *cp_name(int codepage) { const struct cp_list_item *cpi, *cpno; static char buf[32]; if (codepage == -1) { sprintf(buf, "Use font encoding"); return buf; } if (codepage > 0 && codepage < 65536) sprintf(buf, "CP%03d", codepage); else *buf = 0; if (codepage >= 65536) { cpno = 0; for (cpi = cp_list; cpi->name; cpi++) if (cpi == cp_list + (codepage - 65536)) { cpno = cpi; break; } if (cpno) for (cpi = cp_list; cpi->name; cpi++) { if (cpno->cp_table == cpi->cp_table) return cpi->name; } } else { for (cpi = cp_list; cpi->name; cpi++) { if (codepage == cpi->codepage) return cpi->name; } } return buf; } /* * Return the nth code page in the list, for use in the GUI * configurer. */ const char *cp_enumerate(int index) { if (index < 0 || index >= lenof(cp_list)) return NULL; return cp_list[index].name; } void get_unitab(int codepage, wchar_t * unitab, int ftype) { char tbuf[4]; int i, max = 256, flg = MB_ERR_INVALID_CHARS; if (ftype) flg |= MB_USEGLYPHCHARS; if (ftype == 2) max = 128; if (codepage == CP_UTF8) { for (i = 0; i < max; i++) unitab[i] = i; return; } if (codepage == CP_ACP) codepage = GetACP(); else if (codepage == CP_OEMCP) codepage = GetOEMCP(); if (codepage > 0 && codepage < 65536) { for (i = 0; i < max; i++) { tbuf[0] = i; if (mb_to_wc(codepage, flg, tbuf, 1, unitab + i, 1) != 1) unitab[i] = 0xFFFD; } } else { int j = 256 - cp_list[codepage & 0xFFFF].cp_size; 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, const char *defchr, 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. */ if (wclen < 0) { for (wclen = 0; wcstr[wclen++] ;); /* will include the NUL */ } for (p = mbstr, i = 0; i < wclen; i++) { wchar_t ch = wcstr[i]; int by; char *p1; #define WRITECH(chr) do \ { \ assert(p - mbstr < mblen); \ *p++ = (char)(chr); \ } while (0) if (ucsdata->uni_tbl && (p1 = ucsdata->uni_tbl[(ch >> 8) & 0xFF]) != NULL && (by = p1[ch & 0xFF]) != '\0') WRITECH(by); else if (ch < 0x80) WRITECH(ch); else if (defchr) for (const char *q = defchr; *q; q++) WRITECH(*q); #if 1 else WRITECH('.'); #endif #undef WRITECH } return p - mbstr; } else { int defused; 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); } bool is_dbcs_leadbyte(int codepage, char byte) { return IsDBCSLeadByteEx(codepage, byte); } putty-0.76/windows/winutils.c0000644000175000017500000007450014072266315013304 00000000000000/* * winutils.c: miscellaneous Windows utilities for GUI apps */ #include #include #include #include "putty.h" #include "misc.h" #ifdef TESTMODE /* Definitions to allow this module to be compiled standalone for testing * split_into_argv(). */ #define smalloc malloc #define srealloc realloc #define sfree free #endif /* * GetOpenFileName/GetSaveFileName tend to muck around with the process' * working directory on at least some versions of Windows. * Here's a wrapper that gives more control over this, and hides a little * bit of other grottiness. */ struct filereq_tag { TCHAR cwd[MAX_PATH]; }; /* * `of' is expected to be initialised with most interesting fields, but * this function does some administrivia. (assume `of' was memset to 0) * save==1 -> GetSaveFileName; save==0 -> GetOpenFileName * `state' is optional. */ bool request_file(filereq *state, OPENFILENAME *of, bool preserve, bool save) { TCHAR cwd[MAX_PATH]; /* process CWD */ bool ret; /* Get process CWD */ if (preserve) { DWORD r = GetCurrentDirectory(lenof(cwd), cwd); if (r == 0 || r >= lenof(cwd)) /* Didn't work, oh well. Stop trying to be clever. */ preserve = false; } /* Open the file requester, maybe setting lpstrInitialDir */ { #ifdef OPENFILENAME_SIZE_VERSION_400 of->lStructSize = OPENFILENAME_SIZE_VERSION_400; #else of->lStructSize = sizeof(*of); #endif of->lpstrInitialDir = (state && state->cwd[0]) ? state->cwd : NULL; /* Actually put up the requester. */ ret = save ? GetSaveFileName(of) : GetOpenFileName(of); } /* Get CWD left by requester */ if (state) { DWORD r = GetCurrentDirectory(lenof(state->cwd), state->cwd); if (r == 0 || r >= lenof(state->cwd)) /* Didn't work, oh well. */ state->cwd[0] = '\0'; } /* Restore process CWD */ if (preserve) /* If it fails, there's not much we can do. */ (void) SetCurrentDirectory(cwd); return ret; } filereq *filereq_new(void) { filereq *ret = snew(filereq); ret->cwd[0] = '\0'; return ret; } void filereq_free(filereq *state) { sfree(state); } /* * Message box with optional context help. */ static HWND message_box_owner; /* Callback function to launch context help. */ static VOID CALLBACK message_box_help_callback(LPHELPINFO lpHelpInfo) { const char *context = NULL; #define CHECK_CTX(name) \ do { \ if (lpHelpInfo->dwContextId == WINHELP_CTXID_ ## name) \ context = WINHELP_CTX_ ## name; \ } while (0) CHECK_CTX(errors_hostkey_absent); CHECK_CTX(errors_hostkey_changed); CHECK_CTX(errors_cantloadkey); CHECK_CTX(option_cleanup); CHECK_CTX(pgp_fingerprints); #undef CHECK_CTX if (context) launch_help(message_box_owner, context); } int message_box(HWND owner, LPCTSTR text, LPCTSTR caption, DWORD style, DWORD helpctxid) { MSGBOXPARAMS mbox; /* * We use MessageBoxIndirect() because it allows us to specify a * callback function for the Help button. */ mbox.cbSize = sizeof(mbox); /* Assumes the globals `hinst' and `hwnd' have sensible values. */ mbox.hInstance = hinst; mbox.hwndOwner = message_box_owner = owner; mbox.lpfnMsgBoxCallback = &message_box_help_callback; mbox.dwLanguageId = LANG_NEUTRAL; mbox.lpszText = text; mbox.lpszCaption = caption; mbox.dwContextHelpId = helpctxid; mbox.dwStyle = style; if (helpctxid != 0 && has_help()) mbox.dwStyle |= MB_HELP; return MessageBoxIndirect(&mbox); } /* * Display the fingerprints of the PGP Master Keys to the user. */ void pgp_fingerprints_msgbox(HWND owner) { message_box( owner, "These are the fingerprints of the PuTTY PGP Master Keys. They can\n" "be used to establish a trust path from this executable to another\n" "one. See the manual for more information.\n" "(Note: these fingerprints have nothing to do with SSH!)\n" "\n" "PuTTY Master Key as of " PGP_MASTER_KEY_YEAR " (" PGP_MASTER_KEY_DETAILS "):\n" " " PGP_MASTER_KEY_FP "\n\n" "Previous Master Key (" PGP_PREV_MASTER_KEY_YEAR ", " PGP_PREV_MASTER_KEY_DETAILS "):\n" " " PGP_PREV_MASTER_KEY_FP, "PGP fingerprints", MB_ICONINFORMATION | MB_OK, HELPCTXID(pgp_fingerprints)); } /* * Helper function to remove the border around a dialog item such as * a read-only edit control. */ void MakeDlgItemBorderless(HWND parent, int id) { HWND child = GetDlgItem(parent, id); LONG_PTR style = GetWindowLongPtr(child, GWL_STYLE); LONG_PTR exstyle = GetWindowLongPtr(child, GWL_EXSTYLE); style &= ~WS_BORDER; exstyle &= ~(WS_EX_CLIENTEDGE | WS_EX_STATICEDGE | WS_EX_WINDOWEDGE); SetWindowLongPtr(child, GWL_STYLE, style); SetWindowLongPtr(child, GWL_EXSTYLE, exstyle); SetWindowPos(child, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); } /* * 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; size_t size = 0; do { sgrowarray_nm(ret, size, size); 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. */ /* * The precise argument-breaking rules vary with compiler version, or * rather, with the crt0-type startup code that comes with each * compiler's C library. We do our best to match the compiler version, * so that we faithfully imitate in our GUI utilities what the * corresponding set of CLI utilities can't be prevented from doing. * * The basic rules are: * * - 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 appear * just before a double quote. In this situation, they are treated * like C backslashes: so \" just gives a literal quote, \\" gives * a literal backslash and then opens or closes a double-quoted * segment, \\\" gives a literal backslash and then a literal * quote, \\\\" gives two literal backslashes and then opens/closes * a double-quoted segment, and so forth. Note that this behaviour * is identical inside and outside double quotes. * * - Two successive double quotes become one literal double quote, * but only _inside_ a double-quoted segment. Outside, they just * form an empty double-quoted segment (which may cause an empty * argument word). * * That only leaves the interesting question of what happens when one * or more backslashes precedes two or more double quotes, starting * inside a double-quoted string. * * I investigated this in an ordinary CLI program, using the * toolchain's crt0 to split a command line of the form * * "a\\\"""b c" d * * Here I tabulate number of backslashes (across the top) against * number of quotes (down the left), and indicate how many backslashes * are output, how many quotes are output, and whether a quoted * segment is open at the end of the sequence: * * backslashes * * 0 1 2 3 4 * * 0 0,0,y | 1,0,y 2,0,y 3,0,y 4,0,y * --------+----------------------------- * 1 0,0,n | 0,1,y 1,0,n 1,1,y 2,0,n * q 2 0,1,y | 0,1,n 1,1,y 1,1,n 2,1,y * u 3 0,1,n | 0,2,y 1,1,n 1,2,y 2,1,n * o 4 0,2,y | 0,2,n 1,2,y 1,2,n 2,2,y * t 5 0,2,n | 0,3,y 1,2,n 1,3,y 2,2,n * e 6 0,3,y | 0,3,n 1,3,y 1,3,n 2,3,y * s 7 0,3,n | 0,4,y 1,3,n 1,4,y 2,3,n * 8 0,4,y | 0,4,n 1,4,y 1,4,n 2,4,y * * The row at the top of this table, with quotes=0, demonstrates what * I claimed above, that when a sequence of backslashes are not * followed by a double quote, they don't act specially at all. The * rest of the table shows that the backslashes escape each other in * pairs (so that with 2n or 2n+1 input backslashes you get n output * ones); if there's an odd number of input backslashes then the last * one escapes the first double quote (so you get a literal quote and * enter a quoted string); thereafter, each input quote character * either opens or closes a quoted string, and if it closes one, it * generates a literal " as a side effect. * * But here's the corresponding table from the older Visual Studio 7: * * backslashes * * 0 1 2 3 4 * * 0 0,0,y | 1,0,y 2,0,y 3,0,y 4,0,y * --------+----------------------------- * 1 0,0,n | 0,1,y 1,0,n 1,1,y 2,0,n * q 2 0,1,n | 0,1,n 1,1,n 1,1,n 2,1,n * u 3 0,1,y | 0,2,n 1,1,y 1,2,n 2,1,y * o 4 0,1,n | 0,2,y 1,1,n 1,2,y 2,1,n * t 5 0,2,n | 0,2,n 1,2,n 1,2,n 2,2,n * e 6 0,2,y | 0,3,n 1,2,y 1,3,n 2,2,y * s 7 0,2,n | 0,3,y 1,2,n 1,3,y 2,2,n * 8 0,3,n | 0,3,n 1,3,n 1,3,n 2,3,n * 9 0,3,y | 0,4,n 1,3,y 1,4,n 2,3,y * 10 0,3,n | 0,4,y 1,3,n 1,4,y 2,3,n * 11 0,4,n | 0,4,n 1,4,n 1,4,n 2,4,n * * There is very weird mod-3 behaviour going on here in the * number of quotes, and it even applies when there aren't any * backslashes! How ghastly. * * With a bit of thought, this extremely odd diagram suddenly * coalesced itself into a coherent, if still ghastly, model of * how things work: * * - As before, backslashes are only special when one or more * of them appear contiguously before at least one double * quote. In this situation the backslashes do exactly what * you'd expect: each one quotes the next thing in front of * it, so you end up with n/2 literal backslashes (if n is * even) or (n-1)/2 literal backslashes and a literal quote * (if n is odd). In the latter case the double quote * character right after the backslashes is used up. * * - After that, any remaining double quotes are processed. A * string of contiguous unescaped double quotes has a mod-3 * behaviour: * * * inside a quoted segment, a quote ends the segment. * * _immediately_ after ending a quoted segment, a quote * simply produces a literal quote. * * otherwise, outside a quoted segment, a quote begins a * quoted segment. * * So, for example, if we started inside a quoted segment * then two contiguous quotes would close the segment and * produce a literal quote; three would close the segment, * produce a literal quote, and open a new segment. If we * started outside a quoted segment, then two contiguous * quotes would open and then close a segment, producing no * output (but potentially creating a zero-length argument); * but three quotes would open and close a segment and then * produce a literal quote. */ /* * We select between two behaviours depending on the version of Visual * Studio (see large comment below). I don't know exactly when the bug * fix happened, but I know that VS7 had the odd mod-3 behaviour. */ #if _MSC_VER < 1400 #define MOD3 1 #else #define MOD3 0 #endif void split_into_argv(char *cmdline, int *argc, char ***argv, char ***argstart) { char *p; char *outputline, *q; char **outputargv, **outputargstart; int outputargc; /* * First deal with the simplest of all special cases: if there * aren't any arguments, return 0,NULL,NULL. */ while (*cmdline && isspace(*cmdline)) cmdline++; if (!*cmdline) { if (argc) *argc = 0; if (argv) *argv = NULL; if (argstart) *argstart = NULL; return; } /* * This will guaranteeably be big enough; we can realloc it * down later. */ outputline = snewn(1+strlen(cmdline), char); outputargv = snewn(strlen(cmdline)+1 / 2, char *); outputargstart = snewn(strlen(cmdline)+1 / 2, char *); p = cmdline; q = outputline; outputargc = 0; while (*p) { bool quote; /* Skip whitespace searching for start of argument. */ while (*p && isspace(*p)) p++; if (!*p) break; /* We have an argument; start it. */ outputargv[outputargc] = q; outputargstart[outputargc] = p; outputargc++; quote = false; /* Copy data into the argument until it's finished. */ while (*p) { if (!quote && isspace(*p)) break; /* argument is finished */ if (*p == '"' || *p == '\\') { /* * We have a sequence of zero or more backslashes * followed by a sequence of zero or more quotes. * Count up how many of each, and then deal with * them as appropriate. */ int i, slashes = 0, quotes = 0; while (*p == '\\') slashes++, p++; while (*p == '"') quotes++, p++; if (!quotes) { /* * Special case: if there are no quotes, * slashes are not special at all, so just copy * n slashes to the output string. */ while (slashes--) *q++ = '\\'; } else { /* Slashes annihilate in pairs. */ while (slashes >= 2) slashes -= 2, *q++ = '\\'; /* One remaining slash takes out the first quote. */ if (slashes) quotes--, *q++ = '"'; if (quotes > 0) { /* Outside a quote segment, a quote starts one. */ if (!quote) quotes--; #if !MOD3 /* New behaviour: produce n/2 literal quotes... */ for (i = 2; i <= quotes; i += 2) *q++ = '"'; /* ... and end in a quote segment iff 2 divides n. */ quote = (quotes % 2 == 0); #else /* Old behaviour: produce (n+1)/3 literal quotes... */ for (i = 3; i <= quotes+1; i += 3) *q++ = '"'; /* ... and end in a quote segment iff 3 divides n. */ quote = (quotes % 3 == 0); #endif } } } else { *q++ = *p++; } } /* At the end of an argument, just append a trailing NUL. */ *q++ = '\0'; } outputargv = sresize(outputargv, outputargc, char *); outputargstart = sresize(outputargstart, outputargc, char *); if (argc) *argc = outputargc; if (argv) *argv = outputargv; else sfree(outputargv); if (argstart) *argstart = outputargstart; else sfree(outputargstart); } #ifdef TESTMODE const struct argv_test { const char *cmdline; const char *argv[10]; } argv_tests[] = { /* * We generate this set of tests by invoking ourself with * `-generate'. */ #if !MOD3 /* Newer behaviour, with no weird mod-3 glitch. */ {"ab c\" d", {"ab", "c d", NULL}}, {"a\"b c\" d", {"ab c", "d", NULL}}, {"a\"\"b c\" d", {"ab", "c d", NULL}}, {"a\"\"\"b c\" d", {"a\"b c", "d", NULL}}, {"a\"\"\"\"b c\" d", {"a\"b", "c d", NULL}}, {"a\"\"\"\"\"b c\" d", {"a\"\"b c", "d", NULL}}, {"a\"\"\"\"\"\"b c\" d", {"a\"\"b", "c d", NULL}}, {"a\"\"\"\"\"\"\"b c\" d", {"a\"\"\"b c", "d", NULL}}, {"a\"\"\"\"\"\"\"\"b c\" d", {"a\"\"\"b", "c d", NULL}}, {"a\\b c\" d", {"a\\b", "c d", NULL}}, {"a\\\"b c\" d", {"a\"b", "c d", NULL}}, {"a\\\"\"b c\" d", {"a\"b c", "d", NULL}}, {"a\\\"\"\"b c\" d", {"a\"b", "c d", NULL}}, {"a\\\"\"\"\"b c\" d", {"a\"\"b c", "d", NULL}}, {"a\\\"\"\"\"\"b c\" d", {"a\"\"b", "c d", NULL}}, {"a\\\"\"\"\"\"\"b c\" d", {"a\"\"\"b c", "d", NULL}}, {"a\\\"\"\"\"\"\"\"b c\" d", {"a\"\"\"b", "c d", NULL}}, {"a\\\"\"\"\"\"\"\"\"b c\" d", {"a\"\"\"\"b c", "d", NULL}}, {"a\\\\b c\" d", {"a\\\\b", "c d", NULL}}, {"a\\\\\"b c\" d", {"a\\b c", "d", NULL}}, {"a\\\\\"\"b c\" d", {"a\\b", "c d", NULL}}, {"a\\\\\"\"\"b c\" d", {"a\\\"b c", "d", NULL}}, {"a\\\\\"\"\"\"b c\" d", {"a\\\"b", "c d", NULL}}, {"a\\\\\"\"\"\"\"b c\" d", {"a\\\"\"b c", "d", NULL}}, {"a\\\\\"\"\"\"\"\"b c\" d", {"a\\\"\"b", "c d", NULL}}, {"a\\\\\"\"\"\"\"\"\"b c\" d", {"a\\\"\"\"b c", "d", NULL}}, {"a\\\\\"\"\"\"\"\"\"\"b c\" d", {"a\\\"\"\"b", "c d", NULL}}, {"a\\\\\\b c\" d", {"a\\\\\\b", "c d", NULL}}, {"a\\\\\\\"b c\" d", {"a\\\"b", "c d", NULL}}, {"a\\\\\\\"\"b c\" d", {"a\\\"b c", "d", NULL}}, {"a\\\\\\\"\"\"b c\" d", {"a\\\"b", "c d", NULL}}, {"a\\\\\\\"\"\"\"b c\" d", {"a\\\"\"b c", "d", NULL}}, {"a\\\\\\\"\"\"\"\"b c\" d", {"a\\\"\"b", "c d", NULL}}, {"a\\\\\\\"\"\"\"\"\"b c\" d", {"a\\\"\"\"b c", "d", NULL}}, {"a\\\\\\\"\"\"\"\"\"\"b c\" d", {"a\\\"\"\"b", "c d", NULL}}, {"a\\\\\\\"\"\"\"\"\"\"\"b c\" d", {"a\\\"\"\"\"b c", "d", NULL}}, {"a\\\\\\\\b c\" d", {"a\\\\\\\\b", "c d", NULL}}, {"a\\\\\\\\\"b c\" d", {"a\\\\b c", "d", NULL}}, {"a\\\\\\\\\"\"b c\" d", {"a\\\\b", "c d", NULL}}, {"a\\\\\\\\\"\"\"b c\" d", {"a\\\\\"b c", "d", NULL}}, {"a\\\\\\\\\"\"\"\"b c\" d", {"a\\\\\"b", "c d", NULL}}, {"a\\\\\\\\\"\"\"\"\"b c\" d", {"a\\\\\"\"b c", "d", NULL}}, {"a\\\\\\\\\"\"\"\"\"\"b c\" d", {"a\\\\\"\"b", "c d", NULL}}, {"a\\\\\\\\\"\"\"\"\"\"\"b c\" d", {"a\\\\\"\"\"b c", "d", NULL}}, {"a\\\\\\\\\"\"\"\"\"\"\"\"b c\" d", {"a\\\\\"\"\"b", "c d", NULL}}, {"\"ab c\" d", {"ab c", "d", NULL}}, {"\"a\"b c\" d", {"ab", "c d", NULL}}, {"\"a\"\"b c\" d", {"a\"b c", "d", NULL}}, {"\"a\"\"\"b c\" d", {"a\"b", "c d", NULL}}, {"\"a\"\"\"\"b c\" d", {"a\"\"b c", "d", NULL}}, {"\"a\"\"\"\"\"b c\" d", {"a\"\"b", "c d", NULL}}, {"\"a\"\"\"\"\"\"b c\" d", {"a\"\"\"b c", "d", NULL}}, {"\"a\"\"\"\"\"\"\"b c\" d", {"a\"\"\"b", "c d", NULL}}, {"\"a\"\"\"\"\"\"\"\"b c\" d", {"a\"\"\"\"b c", "d", NULL}}, {"\"a\\b c\" d", {"a\\b c", "d", NULL}}, {"\"a\\\"b c\" d", {"a\"b c", "d", NULL}}, {"\"a\\\"\"b c\" d", {"a\"b", "c d", NULL}}, {"\"a\\\"\"\"b c\" d", {"a\"\"b c", "d", NULL}}, {"\"a\\\"\"\"\"b c\" d", {"a\"\"b", "c d", NULL}}, {"\"a\\\"\"\"\"\"b c\" d", {"a\"\"\"b c", "d", NULL}}, {"\"a\\\"\"\"\"\"\"b c\" d", {"a\"\"\"b", "c d", NULL}}, {"\"a\\\"\"\"\"\"\"\"b c\" d", {"a\"\"\"\"b c", "d", NULL}}, {"\"a\\\"\"\"\"\"\"\"\"b c\" d", {"a\"\"\"\"b", "c d", NULL}}, {"\"a\\\\b c\" d", {"a\\\\b c", "d", NULL}}, {"\"a\\\\\"b c\" d", {"a\\b", "c d", NULL}}, {"\"a\\\\\"\"b c\" d", {"a\\\"b c", "d", NULL}}, {"\"a\\\\\"\"\"b c\" d", {"a\\\"b", "c d", NULL}}, {"\"a\\\\\"\"\"\"b c\" d", {"a\\\"\"b c", "d", NULL}}, {"\"a\\\\\"\"\"\"\"b c\" d", {"a\\\"\"b", "c d", NULL}}, {"\"a\\\\\"\"\"\"\"\"b c\" d", {"a\\\"\"\"b c", "d", NULL}}, {"\"a\\\\\"\"\"\"\"\"\"b c\" d", {"a\\\"\"\"b", "c d", NULL}}, {"\"a\\\\\"\"\"\"\"\"\"\"b c\" d", {"a\\\"\"\"\"b c", "d", NULL}}, {"\"a\\\\\\b c\" d", {"a\\\\\\b c", "d", NULL}}, {"\"a\\\\\\\"b c\" d", {"a\\\"b c", "d", NULL}}, {"\"a\\\\\\\"\"b c\" d", {"a\\\"b", "c d", NULL}}, {"\"a\\\\\\\"\"\"b c\" d", {"a\\\"\"b c", "d", NULL}}, {"\"a\\\\\\\"\"\"\"b c\" d", {"a\\\"\"b", "c d", NULL}}, {"\"a\\\\\\\"\"\"\"\"b c\" d", {"a\\\"\"\"b c", "d", NULL}}, {"\"a\\\\\\\"\"\"\"\"\"b c\" d", {"a\\\"\"\"b", "c d", NULL}}, {"\"a\\\\\\\"\"\"\"\"\"\"b c\" d", {"a\\\"\"\"\"b c", "d", NULL}}, {"\"a\\\\\\\"\"\"\"\"\"\"\"b c\" d", {"a\\\"\"\"\"b", "c d", NULL}}, {"\"a\\\\\\\\b c\" d", {"a\\\\\\\\b c", "d", NULL}}, {"\"a\\\\\\\\\"b c\" d", {"a\\\\b", "c d", NULL}}, {"\"a\\\\\\\\\"\"b c\" d", {"a\\\\\"b c", "d", NULL}}, {"\"a\\\\\\\\\"\"\"b c\" d", {"a\\\\\"b", "c d", NULL}}, {"\"a\\\\\\\\\"\"\"\"b c\" d", {"a\\\\\"\"b c", "d", NULL}}, {"\"a\\\\\\\\\"\"\"\"\"b c\" d", {"a\\\\\"\"b", "c d", NULL}}, {"\"a\\\\\\\\\"\"\"\"\"\"b c\" d", {"a\\\\\"\"\"b c", "d", NULL}}, {"\"a\\\\\\\\\"\"\"\"\"\"\"b c\" d", {"a\\\\\"\"\"b", "c d", NULL}}, {"\"a\\\\\\\\\"\"\"\"\"\"\"\"b c\" d", {"a\\\\\"\"\"\"b c", "d", NULL}}, #else /* MOD3 */ /* VS7 mod-3 behaviour. */ {"ab c\" d", {"ab", "c d", NULL}}, {"a\"b c\" d", {"ab c", "d", NULL}}, {"a\"\"b c\" d", {"ab", "c d", NULL}}, {"a\"\"\"b c\" d", {"a\"b", "c d", NULL}}, {"a\"\"\"\"b c\" d", {"a\"b c", "d", NULL}}, {"a\"\"\"\"\"b c\" d", {"a\"b", "c d", NULL}}, {"a\"\"\"\"\"\"b c\" d", {"a\"\"b", "c d", NULL}}, {"a\"\"\"\"\"\"\"b c\" d", {"a\"\"b c", "d", NULL}}, {"a\"\"\"\"\"\"\"\"b c\" d", {"a\"\"b", "c d", NULL}}, {"a\\b c\" d", {"a\\b", "c d", NULL}}, {"a\\\"b c\" d", {"a\"b", "c d", NULL}}, {"a\\\"\"b c\" d", {"a\"b c", "d", NULL}}, {"a\\\"\"\"b c\" d", {"a\"b", "c d", NULL}}, {"a\\\"\"\"\"b c\" d", {"a\"\"b", "c d", NULL}}, {"a\\\"\"\"\"\"b c\" d", {"a\"\"b c", "d", NULL}}, {"a\\\"\"\"\"\"\"b c\" d", {"a\"\"b", "c d", NULL}}, {"a\\\"\"\"\"\"\"\"b c\" d", {"a\"\"\"b", "c d", NULL}}, {"a\\\"\"\"\"\"\"\"\"b c\" d", {"a\"\"\"b c", "d", NULL}}, {"a\\\\b c\" d", {"a\\\\b", "c d", NULL}}, {"a\\\\\"b c\" d", {"a\\b c", "d", NULL}}, {"a\\\\\"\"b c\" d", {"a\\b", "c d", NULL}}, {"a\\\\\"\"\"b c\" d", {"a\\\"b", "c d", NULL}}, {"a\\\\\"\"\"\"b c\" d", {"a\\\"b c", "d", NULL}}, {"a\\\\\"\"\"\"\"b c\" d", {"a\\\"b", "c d", NULL}}, {"a\\\\\"\"\"\"\"\"b c\" d", {"a\\\"\"b", "c d", NULL}}, {"a\\\\\"\"\"\"\"\"\"b c\" d", {"a\\\"\"b c", "d", NULL}}, {"a\\\\\"\"\"\"\"\"\"\"b c\" d", {"a\\\"\"b", "c d", NULL}}, {"a\\\\\\b c\" d", {"a\\\\\\b", "c d", NULL}}, {"a\\\\\\\"b c\" d", {"a\\\"b", "c d", NULL}}, {"a\\\\\\\"\"b c\" d", {"a\\\"b c", "d", NULL}}, {"a\\\\\\\"\"\"b c\" d", {"a\\\"b", "c d", NULL}}, {"a\\\\\\\"\"\"\"b c\" d", {"a\\\"\"b", "c d", NULL}}, {"a\\\\\\\"\"\"\"\"b c\" d", {"a\\\"\"b c", "d", NULL}}, {"a\\\\\\\"\"\"\"\"\"b c\" d", {"a\\\"\"b", "c d", NULL}}, {"a\\\\\\\"\"\"\"\"\"\"b c\" d", {"a\\\"\"\"b", "c d", NULL}}, {"a\\\\\\\"\"\"\"\"\"\"\"b c\" d", {"a\\\"\"\"b c", "d", NULL}}, {"a\\\\\\\\b c\" d", {"a\\\\\\\\b", "c d", NULL}}, {"a\\\\\\\\\"b c\" d", {"a\\\\b c", "d", NULL}}, {"a\\\\\\\\\"\"b c\" d", {"a\\\\b", "c d", NULL}}, {"a\\\\\\\\\"\"\"b c\" d", {"a\\\\\"b", "c d", NULL}}, {"a\\\\\\\\\"\"\"\"b c\" d", {"a\\\\\"b c", "d", NULL}}, {"a\\\\\\\\\"\"\"\"\"b c\" d", {"a\\\\\"b", "c d", NULL}}, {"a\\\\\\\\\"\"\"\"\"\"b c\" d", {"a\\\\\"\"b", "c d", NULL}}, {"a\\\\\\\\\"\"\"\"\"\"\"b c\" d", {"a\\\\\"\"b c", "d", NULL}}, {"a\\\\\\\\\"\"\"\"\"\"\"\"b c\" d", {"a\\\\\"\"b", "c d", NULL}}, {"\"ab c\" d", {"ab c", "d", NULL}}, {"\"a\"b c\" d", {"ab", "c d", NULL}}, {"\"a\"\"b c\" d", {"a\"b", "c d", NULL}}, {"\"a\"\"\"b c\" d", {"a\"b c", "d", NULL}}, {"\"a\"\"\"\"b c\" d", {"a\"b", "c d", NULL}}, {"\"a\"\"\"\"\"b c\" d", {"a\"\"b", "c d", NULL}}, {"\"a\"\"\"\"\"\"b c\" d", {"a\"\"b c", "d", NULL}}, {"\"a\"\"\"\"\"\"\"b c\" d", {"a\"\"b", "c d", NULL}}, {"\"a\"\"\"\"\"\"\"\"b c\" d", {"a\"\"\"b", "c d", NULL}}, {"\"a\\b c\" d", {"a\\b c", "d", NULL}}, {"\"a\\\"b c\" d", {"a\"b c", "d", NULL}}, {"\"a\\\"\"b c\" d", {"a\"b", "c d", NULL}}, {"\"a\\\"\"\"b c\" d", {"a\"\"b", "c d", NULL}}, {"\"a\\\"\"\"\"b c\" d", {"a\"\"b c", "d", NULL}}, {"\"a\\\"\"\"\"\"b c\" d", {"a\"\"b", "c d", NULL}}, {"\"a\\\"\"\"\"\"\"b c\" d", {"a\"\"\"b", "c d", NULL}}, {"\"a\\\"\"\"\"\"\"\"b c\" d", {"a\"\"\"b c", "d", NULL}}, {"\"a\\\"\"\"\"\"\"\"\"b c\" d", {"a\"\"\"b", "c d", NULL}}, {"\"a\\\\b c\" d", {"a\\\\b c", "d", NULL}}, {"\"a\\\\\"b c\" d", {"a\\b", "c d", NULL}}, {"\"a\\\\\"\"b c\" d", {"a\\\"b", "c d", NULL}}, {"\"a\\\\\"\"\"b c\" d", {"a\\\"b c", "d", NULL}}, {"\"a\\\\\"\"\"\"b c\" d", {"a\\\"b", "c d", NULL}}, {"\"a\\\\\"\"\"\"\"b c\" d", {"a\\\"\"b", "c d", NULL}}, {"\"a\\\\\"\"\"\"\"\"b c\" d", {"a\\\"\"b c", "d", NULL}}, {"\"a\\\\\"\"\"\"\"\"\"b c\" d", {"a\\\"\"b", "c d", NULL}}, {"\"a\\\\\"\"\"\"\"\"\"\"b c\" d", {"a\\\"\"\"b", "c d", NULL}}, {"\"a\\\\\\b c\" d", {"a\\\\\\b c", "d", NULL}}, {"\"a\\\\\\\"b c\" d", {"a\\\"b c", "d", NULL}}, {"\"a\\\\\\\"\"b c\" d", {"a\\\"b", "c d", NULL}}, {"\"a\\\\\\\"\"\"b c\" d", {"a\\\"\"b", "c d", NULL}}, {"\"a\\\\\\\"\"\"\"b c\" d", {"a\\\"\"b c", "d", NULL}}, {"\"a\\\\\\\"\"\"\"\"b c\" d", {"a\\\"\"b", "c d", NULL}}, {"\"a\\\\\\\"\"\"\"\"\"b c\" d", {"a\\\"\"\"b", "c d", NULL}}, {"\"a\\\\\\\"\"\"\"\"\"\"b c\" d", {"a\\\"\"\"b c", "d", NULL}}, {"\"a\\\\\\\"\"\"\"\"\"\"\"b c\" d", {"a\\\"\"\"b", "c d", NULL}}, {"\"a\\\\\\\\b c\" d", {"a\\\\\\\\b c", "d", NULL}}, {"\"a\\\\\\\\\"b c\" d", {"a\\\\b", "c d", NULL}}, {"\"a\\\\\\\\\"\"b c\" d", {"a\\\\\"b", "c d", NULL}}, {"\"a\\\\\\\\\"\"\"b c\" d", {"a\\\\\"b c", "d", NULL}}, {"\"a\\\\\\\\\"\"\"\"b c\" d", {"a\\\\\"b", "c d", NULL}}, {"\"a\\\\\\\\\"\"\"\"\"b c\" d", {"a\\\\\"\"b", "c d", NULL}}, {"\"a\\\\\\\\\"\"\"\"\"\"b c\" d", {"a\\\\\"\"b c", "d", NULL}}, {"\"a\\\\\\\\\"\"\"\"\"\"\"b c\" d", {"a\\\\\"\"b", "c d", NULL}}, {"\"a\\\\\\\\\"\"\"\"\"\"\"\"b c\" d", {"a\\\\\"\"\"b", "c d", NULL}}, #endif /* MOD3 */ }; int main(int argc, char **argv) { int i, j; if (argc > 1) { /* * Generation of tests. * * Given `-splat ', we print out a C-style * representation of each argument (in the form "a", "b", * NULL), backslash-escaping each backslash and double * quote. * * Given `-split ', we first doctor `string' by * turning forward slashes into backslashes, single quotes * into double quotes and underscores into spaces; and then * we feed the resulting string to ourself with `-splat'. * * Given `-generate', we concoct a variety of fun test * cases, encode them in quote-safe form (mapping \, " and * space to /, ' and _ respectively) and feed each one to * `-split'. */ if (!strcmp(argv[1], "-splat")) { int i; char *p; for (i = 2; i < argc; i++) { putchar('"'); for (p = argv[i]; *p; p++) { if (*p == '\\' || *p == '"') putchar('\\'); putchar(*p); } printf("\", "); } printf("NULL"); return 0; } if (!strcmp(argv[1], "-split") && argc > 2) { char *str = malloc(20 + strlen(argv[0]) + strlen(argv[2])); char *p, *q; q = str + sprintf(str, "%s -splat ", argv[0]); printf(" {\""); for (p = argv[2]; *p; p++, q++) { switch (*p) { case '/': printf("\\\\"); *q = '\\'; break; case '\'': printf("\\\""); *q = '"'; break; case '_': printf(" "); *q = ' '; break; default: putchar(*p); *q = *p; break; } } *p = '\0'; printf("\", {"); fflush(stdout); system(str); printf("}},\n"); return 0; } if (!strcmp(argv[1], "-generate")) { char *teststr, *p; int i, initialquote, backslashes, quotes; teststr = malloc(200 + strlen(argv[0])); for (initialquote = 0; initialquote <= 1; initialquote++) { for (backslashes = 0; backslashes < 5; backslashes++) { for (quotes = 0; quotes < 9; quotes++) { p = teststr + sprintf(teststr, "%s -split ", argv[0]); if (initialquote) *p++ = '\''; *p++ = 'a'; for (i = 0; i < backslashes; i++) *p++ = '/'; for (i = 0; i < quotes; i++) *p++ = '\''; *p++ = 'b'; *p++ = '_'; *p++ = 'c'; *p++ = '\''; *p++ = '_'; *p++ = 'd'; *p = '\0'; system(teststr); } } } return 0; } fprintf(stderr, "unrecognised option: \"%s\"\n", argv[1]); return 1; } /* * If we get here, we were invoked with no arguments, so just * run the tests. */ for (i = 0; i < lenof(argv_tests); i++) { int ac; char **av; split_into_argv(argv_tests[i].cmdline, &ac, &av); for (j = 0; j < ac && argv_tests[i].argv[j]; j++) { if (strcmp(av[j], argv_tests[i].argv[j])) { printf("failed test %d (|%s|) arg %d: |%s| should be |%s|\n", i, argv_tests[i].cmdline, j, av[j], argv_tests[i].argv[j]); } #ifdef VERBOSE else { printf("test %d (|%s|) arg %d: |%s| == |%s|\n", i, argv_tests[i].cmdline, j, av[j], argv_tests[i].argv[j]); } #endif } if (j < ac) printf("failed test %d (|%s|): %d args returned, should be %d\n", i, argv_tests[i].cmdline, ac, j); if (argv_tests[i].argv[j]) printf("failed test %d (|%s|): %d args returned, should be more\n", i, argv_tests[i].cmdline, ac); } return 0; } #endif putty-0.76/windows/winx11.c0000644000175000017500000000065114072266315012551 00000000000000/* * winx11.c: fetch local auth data for X forwarding. */ #include #include #include #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 bool platform_uses_x11_unix_by_default = false; putty-0.76/windows/Makefile.clangcl0000644000175000017500000033704714072266315014334 00000000000000# Makefile for cross-compiling putty using clang-cl, lld-link, # and llvm-rc, using GNU make on Linux. # # This file was created by `mkfiles.pl' from the `Recipe' file. # DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead. # # Extra options you can set: # # - COMPAT=/DAUTO_WINSOCK (Windows only) # Causes PuTTY to assume that includes its own WinSock # header file, so that it won't try to include . # # - COMPAT=/DWINSOCK_TWO (Windows only) # Causes the PuTTY utilities to include instead of # , except Plink which _needs_ WinSock 2 so it already # does this. # # - COMPAT=/DNO_SECURITY (Windows only) # Disables use of , which is not available with some # development environments (such as very old versions of the # mingw/Cygwin GNU toolchain). This has the following effects: # - Pageant won't care about the local user ID of processes # accessing it; a version of Pageant built with this option # will therefore refuse to run under NT-series OSes on # security grounds (although it will run fine on Win95-series # OSes where there is no access control anyway). # - SSH connection sharing is disabled. # - There is no support for restriction of the process ACLs. # # - COMPAT=/DNO_MULTIMON (Windows only) # Disables PuTTY's use of , which is not available # with some development environments. This means that PuTTY's # full-screen mode (configurable to work on Alt-Enter) will # not behave usefully in a multi-monitor environment. # # - COMPAT=/DNO_HTMLHELP (Windows only) # Disables PuTTY's use of , which is not available # with some development environments. # # If you don't have this header, you may be able to use the copy # supplied with HTML Help Workshop. # # - RCFL=/DNO_MANIFESTS (Windows only) # Disables inclusion of XML application manifests in the PuTTY # binaries. This may be necessary to build for 64-bit Windows; # the manifests are only included to use the XP GUI style on # Windows XP, and the architecture tags are a lie on 64-bit. # # - COMPAT=/DNO_IPV6 # Disables PuTTY's ability to make IPv6 connections, enabling # it to compile under development environments which do not # support IPv6 in their header files. # # - COMPAT=/DNO_GSSAPI # Disables PuTTY's ability to use GSSAPI functions for # authentication and key exchange. # # - COMPAT=/DSTATIC_GSSAPI # Causes PuTTY to try to link statically against the GSSAPI # library instead of the default of doing it at run time. # # - COMPAT=/DMSVC4 (Windows only) # - RCFL=/DMSVC4 # Makes a couple of minor changes so that PuTTY compiles using # MSVC 4. You will also need /DNO_SECURITY and /DNO_MULTIMON. # # - COMPAT=/DNO_SECUREZEROMEMORY (Windows only) # Disables PuTTY's use of SecureZeroMemory(), which is missing # from some environments' header files. # # - XFLAGS=/DDEBUG # Causes PuTTY to enable internal debugging. # # - XFLAGS=/DMALLOC_LOG # Causes PuTTY to emit a file called putty_mem.log, logging every # memory allocation and free, so you can track memory leaks. # # - XFLAGS=/DMINEFIELD (Windows only) # Causes PuTTY to use a custom memory allocator, similar in # concept to Electric Fence, in place of regular malloc(). Wastes # huge amounts of RAM, but should cause heap-corruption bugs to # show up as GPFs at the point of failure rather than appearing # later on as second-level damage. # # - XFLAGS=/DFUZZING # Builds a version of PuTTY with some tweaks to make fuzz testing # easier: the SSH random number generator is replaced by one that # always returns the same thing. Note that this makes SSH # completely insecure -- a FUZZING build should never be used to # connect to a real server. CCCMD = clang-cl RCCMD = llvm-rc ifeq ($(Platform),x64) CCTARGET = x86_64-pc-windows-msvc18.0.0 PLATFORMCFLAGS = else ifeq ($(Platform),arm) CCTARGET = arm-pc-windows-msvc18.0.0 PLATFORMCFLAGS = /D_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE /GS- else ifeq ($(Platform),arm64) CCTARGET = arm64-pc-windows-msvc18.0.0 PLATFORMCFLAGS = /D_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE /GS- else CCTARGET = i386-pc-windows-msvc18.0.0 PLATFORMCFLAGS = endif CC = $(CCCMD) RC = $(RCCMD) /c 1252 RCPREPROC = $(CCCMD) /P /TC LD = lld-link # C compilation flags CFLAGS = --target=$(CCTARGET) /nologo /W3 /O1 -Wvla -I.././ -I../charset/ \ -I../windows/ -I../unix/ /D_WINDOWS /D_WIN32_WINDOWS=0x500 \ /DWINVER=0x500 /D_CRT_SECURE_NO_WARNINGS \ /D_WINSOCK_DEPRECATED_NO_WARNINGS $(PLATFORMCFLAGS) LFLAGS = /incremental:no /dynamicbase /nxcompat RCPPFLAGS = -I.././ -I../charset/ -I../windows/ -I../unix/ -DWIN32 -D_WIN32 \ -DWINVER=0x0400 $(RCFL) CFLAGS += /DHAS_GSSAPI all: $(BUILDDIR)pageant.exe $(BUILDDIR)plink.exe $(BUILDDIR)pscp.exe \ $(BUILDDIR)psftp.exe $(BUILDDIR)psocks.exe \ $(BUILDDIR)putty.exe $(BUILDDIR)puttygen.exe \ $(BUILDDIR)puttytel.exe $(BUILDDIR)testcrypt.exe $(BUILDDIR)pageant.exe: $(BUILDDIR)aqsync.obj $(BUILDDIR)be_misc.obj \ $(BUILDDIR)callback.obj $(BUILDDIR)conf.obj \ $(BUILDDIR)ecc.obj $(BUILDDIR)errsock.obj \ $(BUILDDIR)marshal.obj $(BUILDDIR)memory.obj \ $(BUILDDIR)misc.obj $(BUILDDIR)mpint.obj \ $(BUILDDIR)pageant.obj $(BUILDDIR)pageant.res \ $(BUILDDIR)sshaes.obj $(BUILDDIR)sshargon2.obj \ $(BUILDDIR)sshauxcrypt.obj $(BUILDDIR)sshblake2.obj \ $(BUILDDIR)sshdes.obj $(BUILDDIR)sshdss.obj \ $(BUILDDIR)sshecc.obj $(BUILDDIR)sshhmac.obj \ $(BUILDDIR)sshmd5.obj $(BUILDDIR)sshpubk.obj \ $(BUILDDIR)sshrsa.obj $(BUILDDIR)sshsh256.obj \ $(BUILDDIR)sshsh512.obj $(BUILDDIR)sshsha.obj \ $(BUILDDIR)sshsha3.obj $(BUILDDIR)stripctrl.obj \ $(BUILDDIR)tree234.obj $(BUILDDIR)utils.obj \ $(BUILDDIR)version.obj $(BUILDDIR)wcwidth.obj \ $(BUILDDIR)wincapi.obj $(BUILDDIR)winhandl.obj \ $(BUILDDIR)winhelp.obj $(BUILDDIR)winhsock.obj \ $(BUILDDIR)winmisc.obj $(BUILDDIR)winmiscs.obj \ $(BUILDDIR)winnet.obj $(BUILDDIR)winnpc.obj \ $(BUILDDIR)winnps.obj $(BUILDDIR)winpgnt.obj \ $(BUILDDIR)winpgntc.obj $(BUILDDIR)winsecur.obj \ $(BUILDDIR)winselgui.obj $(BUILDDIR)winutils.obj $(LD) $(LFLAGS) $(XLFLAGS) /out:$(BUILDDIR)pageant.exe \ /lldmap:$(BUILDDIR)pageant.map \ /subsystem:windows$(SUBSYSVER) $(EXTRA_windows) advapi32.lib \ $(BUILDDIR)aqsync.obj $(BUILDDIR)be_misc.obj \ $(BUILDDIR)callback.obj comdlg32.lib $(BUILDDIR)conf.obj \ $(BUILDDIR)ecc.obj $(BUILDDIR)errsock.obj gdi32.lib \ imm32.lib $(BUILDDIR)marshal.obj $(BUILDDIR)memory.obj \ $(BUILDDIR)misc.obj $(BUILDDIR)mpint.obj ole32.lib \ $(BUILDDIR)pageant.obj $(BUILDDIR)pageant.res shell32.lib \ $(BUILDDIR)sshaes.obj $(BUILDDIR)sshargon2.obj \ $(BUILDDIR)sshauxcrypt.obj $(BUILDDIR)sshblake2.obj \ $(BUILDDIR)sshdes.obj $(BUILDDIR)sshdss.obj \ $(BUILDDIR)sshecc.obj $(BUILDDIR)sshhmac.obj \ $(BUILDDIR)sshmd5.obj $(BUILDDIR)sshpubk.obj \ $(BUILDDIR)sshrsa.obj $(BUILDDIR)sshsh256.obj \ $(BUILDDIR)sshsh512.obj $(BUILDDIR)sshsha.obj \ $(BUILDDIR)sshsha3.obj $(BUILDDIR)stripctrl.obj \ $(BUILDDIR)tree234.obj user32.lib $(BUILDDIR)utils.obj \ $(BUILDDIR)version.obj $(BUILDDIR)wcwidth.obj \ $(BUILDDIR)wincapi.obj $(BUILDDIR)winhandl.obj \ $(BUILDDIR)winhelp.obj $(BUILDDIR)winhsock.obj \ $(BUILDDIR)winmisc.obj $(BUILDDIR)winmiscs.obj \ $(BUILDDIR)winnet.obj $(BUILDDIR)winnpc.obj \ $(BUILDDIR)winnps.obj $(BUILDDIR)winpgnt.obj \ $(BUILDDIR)winpgntc.obj $(BUILDDIR)winsecur.obj \ $(BUILDDIR)winselgui.obj $(BUILDDIR)winutils.obj \ $(EXTRA_libs) $(BUILDDIR)plink.exe: $(BUILDDIR)agentf.obj $(BUILDDIR)aqsync.obj \ $(BUILDDIR)be_all_s.obj $(BUILDDIR)be_misc.obj \ $(BUILDDIR)callback.obj $(BUILDDIR)clicons.obj \ $(BUILDDIR)cmdline.obj $(BUILDDIR)conf.obj \ $(BUILDDIR)console.obj $(BUILDDIR)cproxy.obj \ $(BUILDDIR)ecc.obj $(BUILDDIR)errsock.obj \ $(BUILDDIR)ldisc.obj $(BUILDDIR)logging.obj \ $(BUILDDIR)mainchan.obj $(BUILDDIR)marshal.obj \ $(BUILDDIR)memory.obj $(BUILDDIR)misc.obj \ $(BUILDDIR)miscucs.obj $(BUILDDIR)mpint.obj \ $(BUILDDIR)noterm.obj $(BUILDDIR)nullplug.obj \ $(BUILDDIR)pgssapi.obj $(BUILDDIR)pinger.obj \ $(BUILDDIR)plink.res $(BUILDDIR)portfwd.obj \ $(BUILDDIR)proxy.obj $(BUILDDIR)raw.obj \ $(BUILDDIR)rlogin.obj $(BUILDDIR)sessprep.obj \ $(BUILDDIR)settings.obj $(BUILDDIR)ssh.obj \ $(BUILDDIR)ssh1bpp.obj $(BUILDDIR)ssh1censor.obj \ $(BUILDDIR)ssh1connection.obj \ $(BUILDDIR)ssh1connection-client.obj \ $(BUILDDIR)ssh1login.obj $(BUILDDIR)ssh2bpp.obj \ $(BUILDDIR)ssh2bpp-bare.obj $(BUILDDIR)ssh2censor.obj \ $(BUILDDIR)ssh2connection.obj \ $(BUILDDIR)ssh2connection-client.obj \ $(BUILDDIR)ssh2kex-client.obj $(BUILDDIR)ssh2transhk.obj \ $(BUILDDIR)ssh2transport.obj $(BUILDDIR)ssh2userauth.obj \ $(BUILDDIR)sshaes.obj $(BUILDDIR)ssharcf.obj \ $(BUILDDIR)sshargon2.obj $(BUILDDIR)sshauxcrypt.obj \ $(BUILDDIR)sshblake2.obj $(BUILDDIR)sshblowf.obj \ $(BUILDDIR)sshccp.obj $(BUILDDIR)sshcommon.obj \ $(BUILDDIR)sshcrc.obj $(BUILDDIR)sshcrcda.obj \ $(BUILDDIR)sshdes.obj $(BUILDDIR)sshdh.obj \ $(BUILDDIR)sshdss.obj $(BUILDDIR)sshecc.obj \ $(BUILDDIR)sshgssc.obj $(BUILDDIR)sshhmac.obj \ $(BUILDDIR)sshmac.obj $(BUILDDIR)sshmd5.obj \ $(BUILDDIR)sshprng.obj $(BUILDDIR)sshpubk.obj \ $(BUILDDIR)sshrand.obj $(BUILDDIR)sshrsa.obj \ $(BUILDDIR)sshsh256.obj $(BUILDDIR)sshsh512.obj \ $(BUILDDIR)sshsha.obj $(BUILDDIR)sshsha3.obj \ $(BUILDDIR)sshshare.obj $(BUILDDIR)sshutils.obj \ $(BUILDDIR)sshverstring.obj $(BUILDDIR)sshzlib.obj \ $(BUILDDIR)stripctrl.obj $(BUILDDIR)supdup.obj \ $(BUILDDIR)telnet.obj $(BUILDDIR)timing.obj \ $(BUILDDIR)tree234.obj $(BUILDDIR)utils.obj \ $(BUILDDIR)version.obj $(BUILDDIR)wcwidth.obj \ $(BUILDDIR)wildcard.obj $(BUILDDIR)wincapi.obj \ $(BUILDDIR)wincliloop.obj $(BUILDDIR)wincons.obj \ $(BUILDDIR)windefs.obj $(BUILDDIR)wingss.obj \ $(BUILDDIR)winhandl.obj $(BUILDDIR)winhsock.obj \ $(BUILDDIR)winmisc.obj $(BUILDDIR)winmiscs.obj \ $(BUILDDIR)winnet.obj $(BUILDDIR)winnohlp.obj \ $(BUILDDIR)winnoise.obj $(BUILDDIR)winnojmp.obj \ $(BUILDDIR)winnpc.obj $(BUILDDIR)winnps.obj \ $(BUILDDIR)winpgntc.obj $(BUILDDIR)winplink.obj \ $(BUILDDIR)winproxy.obj $(BUILDDIR)winsecur.obj \ $(BUILDDIR)winselcli.obj $(BUILDDIR)winser.obj \ $(BUILDDIR)winshare.obj $(BUILDDIR)winstore.obj \ $(BUILDDIR)wintime.obj $(BUILDDIR)winucs.obj \ $(BUILDDIR)winx11.obj $(BUILDDIR)x11fwd.obj $(LD) $(LFLAGS) $(XLFLAGS) /out:$(BUILDDIR)plink.exe \ /lldmap:$(BUILDDIR)plink.map /subsystem:console$(SUBSYSVER) \ $(EXTRA_console) advapi32.lib $(BUILDDIR)agentf.obj \ $(BUILDDIR)aqsync.obj $(BUILDDIR)be_all_s.obj \ $(BUILDDIR)be_misc.obj $(BUILDDIR)callback.obj \ $(BUILDDIR)clicons.obj $(BUILDDIR)cmdline.obj comdlg32.lib \ $(BUILDDIR)conf.obj $(BUILDDIR)console.obj \ $(BUILDDIR)cproxy.obj $(BUILDDIR)ecc.obj \ $(BUILDDIR)errsock.obj gdi32.lib imm32.lib \ $(BUILDDIR)ldisc.obj $(BUILDDIR)logging.obj \ $(BUILDDIR)mainchan.obj $(BUILDDIR)marshal.obj \ $(BUILDDIR)memory.obj $(BUILDDIR)misc.obj \ $(BUILDDIR)miscucs.obj $(BUILDDIR)mpint.obj \ $(BUILDDIR)noterm.obj $(BUILDDIR)nullplug.obj ole32.lib \ $(BUILDDIR)pgssapi.obj $(BUILDDIR)pinger.obj \ $(BUILDDIR)plink.res $(BUILDDIR)portfwd.obj \ $(BUILDDIR)proxy.obj $(BUILDDIR)raw.obj \ $(BUILDDIR)rlogin.obj $(BUILDDIR)sessprep.obj \ $(BUILDDIR)settings.obj shell32.lib $(BUILDDIR)ssh.obj \ $(BUILDDIR)ssh1bpp.obj $(BUILDDIR)ssh1censor.obj \ $(BUILDDIR)ssh1connection.obj \ $(BUILDDIR)ssh1connection-client.obj \ $(BUILDDIR)ssh1login.obj $(BUILDDIR)ssh2bpp.obj \ $(BUILDDIR)ssh2bpp-bare.obj $(BUILDDIR)ssh2censor.obj \ $(BUILDDIR)ssh2connection.obj \ $(BUILDDIR)ssh2connection-client.obj \ $(BUILDDIR)ssh2kex-client.obj $(BUILDDIR)ssh2transhk.obj \ $(BUILDDIR)ssh2transport.obj $(BUILDDIR)ssh2userauth.obj \ $(BUILDDIR)sshaes.obj $(BUILDDIR)ssharcf.obj \ $(BUILDDIR)sshargon2.obj $(BUILDDIR)sshauxcrypt.obj \ $(BUILDDIR)sshblake2.obj $(BUILDDIR)sshblowf.obj \ $(BUILDDIR)sshccp.obj $(BUILDDIR)sshcommon.obj \ $(BUILDDIR)sshcrc.obj $(BUILDDIR)sshcrcda.obj \ $(BUILDDIR)sshdes.obj $(BUILDDIR)sshdh.obj \ $(BUILDDIR)sshdss.obj $(BUILDDIR)sshecc.obj \ $(BUILDDIR)sshgssc.obj $(BUILDDIR)sshhmac.obj \ $(BUILDDIR)sshmac.obj $(BUILDDIR)sshmd5.obj \ $(BUILDDIR)sshprng.obj $(BUILDDIR)sshpubk.obj \ $(BUILDDIR)sshrand.obj $(BUILDDIR)sshrsa.obj \ $(BUILDDIR)sshsh256.obj $(BUILDDIR)sshsh512.obj \ $(BUILDDIR)sshsha.obj $(BUILDDIR)sshsha3.obj \ $(BUILDDIR)sshshare.obj $(BUILDDIR)sshutils.obj \ $(BUILDDIR)sshverstring.obj $(BUILDDIR)sshzlib.obj \ $(BUILDDIR)stripctrl.obj $(BUILDDIR)supdup.obj \ $(BUILDDIR)telnet.obj $(BUILDDIR)timing.obj \ $(BUILDDIR)tree234.obj user32.lib $(BUILDDIR)utils.obj \ $(BUILDDIR)version.obj $(BUILDDIR)wcwidth.obj \ $(BUILDDIR)wildcard.obj $(BUILDDIR)wincapi.obj \ $(BUILDDIR)wincliloop.obj $(BUILDDIR)wincons.obj \ $(BUILDDIR)windefs.obj $(BUILDDIR)wingss.obj \ $(BUILDDIR)winhandl.obj $(BUILDDIR)winhsock.obj \ $(BUILDDIR)winmisc.obj $(BUILDDIR)winmiscs.obj \ $(BUILDDIR)winnet.obj $(BUILDDIR)winnohlp.obj \ $(BUILDDIR)winnoise.obj $(BUILDDIR)winnojmp.obj \ $(BUILDDIR)winnpc.obj $(BUILDDIR)winnps.obj \ $(BUILDDIR)winpgntc.obj $(BUILDDIR)winplink.obj \ $(BUILDDIR)winproxy.obj $(BUILDDIR)winsecur.obj \ $(BUILDDIR)winselcli.obj $(BUILDDIR)winser.obj \ $(BUILDDIR)winshare.obj $(BUILDDIR)winstore.obj \ $(BUILDDIR)wintime.obj $(BUILDDIR)winucs.obj \ $(BUILDDIR)winx11.obj $(BUILDDIR)x11fwd.obj $(EXTRA_libs) $(BUILDDIR)pscp.exe: $(BUILDDIR)agentf.obj $(BUILDDIR)aqsync.obj \ $(BUILDDIR)be_misc.obj $(BUILDDIR)be_ssh.obj \ $(BUILDDIR)callback.obj $(BUILDDIR)clicons.obj \ $(BUILDDIR)cmdline.obj $(BUILDDIR)conf.obj \ $(BUILDDIR)console.obj $(BUILDDIR)cproxy.obj \ $(BUILDDIR)ecc.obj $(BUILDDIR)errsock.obj \ $(BUILDDIR)logging.obj $(BUILDDIR)mainchan.obj \ $(BUILDDIR)marshal.obj $(BUILDDIR)memory.obj \ $(BUILDDIR)misc.obj $(BUILDDIR)miscucs.obj \ $(BUILDDIR)mpint.obj $(BUILDDIR)nullplug.obj \ $(BUILDDIR)pgssapi.obj $(BUILDDIR)pinger.obj \ $(BUILDDIR)portfwd.obj $(BUILDDIR)proxy.obj \ $(BUILDDIR)pscp.obj $(BUILDDIR)pscp.res \ $(BUILDDIR)psftpcommon.obj $(BUILDDIR)settings.obj \ $(BUILDDIR)sftp.obj $(BUILDDIR)sftpcommon.obj \ $(BUILDDIR)ssh.obj $(BUILDDIR)ssh1bpp.obj \ $(BUILDDIR)ssh1censor.obj $(BUILDDIR)ssh1connection.obj \ $(BUILDDIR)ssh1connection-client.obj \ $(BUILDDIR)ssh1login.obj $(BUILDDIR)ssh2bpp.obj \ $(BUILDDIR)ssh2bpp-bare.obj $(BUILDDIR)ssh2censor.obj \ $(BUILDDIR)ssh2connection.obj \ $(BUILDDIR)ssh2connection-client.obj \ $(BUILDDIR)ssh2kex-client.obj $(BUILDDIR)ssh2transhk.obj \ $(BUILDDIR)ssh2transport.obj $(BUILDDIR)ssh2userauth.obj \ $(BUILDDIR)sshaes.obj $(BUILDDIR)ssharcf.obj \ $(BUILDDIR)sshargon2.obj $(BUILDDIR)sshauxcrypt.obj \ $(BUILDDIR)sshblake2.obj $(BUILDDIR)sshblowf.obj \ $(BUILDDIR)sshccp.obj $(BUILDDIR)sshcommon.obj \ $(BUILDDIR)sshcrc.obj $(BUILDDIR)sshcrcda.obj \ $(BUILDDIR)sshdes.obj $(BUILDDIR)sshdh.obj \ $(BUILDDIR)sshdss.obj $(BUILDDIR)sshecc.obj \ $(BUILDDIR)sshgssc.obj $(BUILDDIR)sshhmac.obj \ $(BUILDDIR)sshmac.obj $(BUILDDIR)sshmd5.obj \ $(BUILDDIR)sshprng.obj $(BUILDDIR)sshpubk.obj \ $(BUILDDIR)sshrand.obj $(BUILDDIR)sshrsa.obj \ $(BUILDDIR)sshsh256.obj $(BUILDDIR)sshsh512.obj \ $(BUILDDIR)sshsha.obj $(BUILDDIR)sshsha3.obj \ $(BUILDDIR)sshshare.obj $(BUILDDIR)sshutils.obj \ $(BUILDDIR)sshverstring.obj $(BUILDDIR)sshzlib.obj \ $(BUILDDIR)stripctrl.obj $(BUILDDIR)timing.obj \ $(BUILDDIR)tree234.obj $(BUILDDIR)utils.obj \ $(BUILDDIR)version.obj $(BUILDDIR)wcwidth.obj \ $(BUILDDIR)wildcard.obj $(BUILDDIR)wincapi.obj \ $(BUILDDIR)wincliloop.obj $(BUILDDIR)wincons.obj \ $(BUILDDIR)windefs.obj $(BUILDDIR)wingss.obj \ $(BUILDDIR)winhandl.obj $(BUILDDIR)winhsock.obj \ $(BUILDDIR)winmisc.obj $(BUILDDIR)winmiscs.obj \ $(BUILDDIR)winnet.obj $(BUILDDIR)winnohlp.obj \ $(BUILDDIR)winnoise.obj $(BUILDDIR)winnojmp.obj \ $(BUILDDIR)winnpc.obj $(BUILDDIR)winnps.obj \ $(BUILDDIR)winpgntc.obj $(BUILDDIR)winproxy.obj \ $(BUILDDIR)winsecur.obj $(BUILDDIR)winselcli.obj \ $(BUILDDIR)winsftp.obj $(BUILDDIR)winshare.obj \ $(BUILDDIR)winstore.obj $(BUILDDIR)wintime.obj \ $(BUILDDIR)winucs.obj $(BUILDDIR)x11fwd.obj $(LD) $(LFLAGS) $(XLFLAGS) /out:$(BUILDDIR)pscp.exe \ /lldmap:$(BUILDDIR)pscp.map /subsystem:console$(SUBSYSVER) \ $(EXTRA_console) advapi32.lib $(BUILDDIR)agentf.obj \ $(BUILDDIR)aqsync.obj $(BUILDDIR)be_misc.obj \ $(BUILDDIR)be_ssh.obj $(BUILDDIR)callback.obj \ $(BUILDDIR)clicons.obj $(BUILDDIR)cmdline.obj comdlg32.lib \ $(BUILDDIR)conf.obj $(BUILDDIR)console.obj \ $(BUILDDIR)cproxy.obj $(BUILDDIR)ecc.obj \ $(BUILDDIR)errsock.obj gdi32.lib imm32.lib \ $(BUILDDIR)logging.obj $(BUILDDIR)mainchan.obj \ $(BUILDDIR)marshal.obj $(BUILDDIR)memory.obj \ $(BUILDDIR)misc.obj $(BUILDDIR)miscucs.obj \ $(BUILDDIR)mpint.obj $(BUILDDIR)nullplug.obj ole32.lib \ $(BUILDDIR)pgssapi.obj $(BUILDDIR)pinger.obj \ $(BUILDDIR)portfwd.obj $(BUILDDIR)proxy.obj \ $(BUILDDIR)pscp.obj $(BUILDDIR)pscp.res \ $(BUILDDIR)psftpcommon.obj $(BUILDDIR)settings.obj \ $(BUILDDIR)sftp.obj $(BUILDDIR)sftpcommon.obj shell32.lib \ $(BUILDDIR)ssh.obj $(BUILDDIR)ssh1bpp.obj \ $(BUILDDIR)ssh1censor.obj $(BUILDDIR)ssh1connection.obj \ $(BUILDDIR)ssh1connection-client.obj \ $(BUILDDIR)ssh1login.obj $(BUILDDIR)ssh2bpp.obj \ $(BUILDDIR)ssh2bpp-bare.obj $(BUILDDIR)ssh2censor.obj \ $(BUILDDIR)ssh2connection.obj \ $(BUILDDIR)ssh2connection-client.obj \ $(BUILDDIR)ssh2kex-client.obj $(BUILDDIR)ssh2transhk.obj \ $(BUILDDIR)ssh2transport.obj $(BUILDDIR)ssh2userauth.obj \ $(BUILDDIR)sshaes.obj $(BUILDDIR)ssharcf.obj \ $(BUILDDIR)sshargon2.obj $(BUILDDIR)sshauxcrypt.obj \ $(BUILDDIR)sshblake2.obj $(BUILDDIR)sshblowf.obj \ $(BUILDDIR)sshccp.obj $(BUILDDIR)sshcommon.obj \ $(BUILDDIR)sshcrc.obj $(BUILDDIR)sshcrcda.obj \ $(BUILDDIR)sshdes.obj $(BUILDDIR)sshdh.obj \ $(BUILDDIR)sshdss.obj $(BUILDDIR)sshecc.obj \ $(BUILDDIR)sshgssc.obj $(BUILDDIR)sshhmac.obj \ $(BUILDDIR)sshmac.obj $(BUILDDIR)sshmd5.obj \ $(BUILDDIR)sshprng.obj $(BUILDDIR)sshpubk.obj \ $(BUILDDIR)sshrand.obj $(BUILDDIR)sshrsa.obj \ $(BUILDDIR)sshsh256.obj $(BUILDDIR)sshsh512.obj \ $(BUILDDIR)sshsha.obj $(BUILDDIR)sshsha3.obj \ $(BUILDDIR)sshshare.obj $(BUILDDIR)sshutils.obj \ $(BUILDDIR)sshverstring.obj $(BUILDDIR)sshzlib.obj \ $(BUILDDIR)stripctrl.obj $(BUILDDIR)timing.obj \ $(BUILDDIR)tree234.obj user32.lib $(BUILDDIR)utils.obj \ $(BUILDDIR)version.obj $(BUILDDIR)wcwidth.obj \ $(BUILDDIR)wildcard.obj $(BUILDDIR)wincapi.obj \ $(BUILDDIR)wincliloop.obj $(BUILDDIR)wincons.obj \ $(BUILDDIR)windefs.obj $(BUILDDIR)wingss.obj \ $(BUILDDIR)winhandl.obj $(BUILDDIR)winhsock.obj \ $(BUILDDIR)winmisc.obj $(BUILDDIR)winmiscs.obj \ $(BUILDDIR)winnet.obj $(BUILDDIR)winnohlp.obj \ $(BUILDDIR)winnoise.obj $(BUILDDIR)winnojmp.obj \ $(BUILDDIR)winnpc.obj $(BUILDDIR)winnps.obj \ $(BUILDDIR)winpgntc.obj $(BUILDDIR)winproxy.obj \ $(BUILDDIR)winsecur.obj $(BUILDDIR)winselcli.obj \ $(BUILDDIR)winsftp.obj $(BUILDDIR)winshare.obj \ $(BUILDDIR)winstore.obj $(BUILDDIR)wintime.obj \ $(BUILDDIR)winucs.obj $(BUILDDIR)x11fwd.obj $(EXTRA_libs) $(BUILDDIR)psftp.exe: $(BUILDDIR)agentf.obj $(BUILDDIR)aqsync.obj \ $(BUILDDIR)be_misc.obj $(BUILDDIR)be_ssh.obj \ $(BUILDDIR)callback.obj $(BUILDDIR)clicons.obj \ $(BUILDDIR)cmdline.obj $(BUILDDIR)conf.obj \ $(BUILDDIR)console.obj $(BUILDDIR)cproxy.obj \ $(BUILDDIR)ecc.obj $(BUILDDIR)errsock.obj \ $(BUILDDIR)logging.obj $(BUILDDIR)mainchan.obj \ $(BUILDDIR)marshal.obj $(BUILDDIR)memory.obj \ $(BUILDDIR)misc.obj $(BUILDDIR)miscucs.obj \ $(BUILDDIR)mpint.obj $(BUILDDIR)nullplug.obj \ $(BUILDDIR)pgssapi.obj $(BUILDDIR)pinger.obj \ $(BUILDDIR)portfwd.obj $(BUILDDIR)proxy.obj \ $(BUILDDIR)psftp.obj $(BUILDDIR)psftp.res \ $(BUILDDIR)psftpcommon.obj $(BUILDDIR)settings.obj \ $(BUILDDIR)sftp.obj $(BUILDDIR)sftpcommon.obj \ $(BUILDDIR)ssh.obj $(BUILDDIR)ssh1bpp.obj \ $(BUILDDIR)ssh1censor.obj $(BUILDDIR)ssh1connection.obj \ $(BUILDDIR)ssh1connection-client.obj \ $(BUILDDIR)ssh1login.obj $(BUILDDIR)ssh2bpp.obj \ $(BUILDDIR)ssh2bpp-bare.obj $(BUILDDIR)ssh2censor.obj \ $(BUILDDIR)ssh2connection.obj \ $(BUILDDIR)ssh2connection-client.obj \ $(BUILDDIR)ssh2kex-client.obj $(BUILDDIR)ssh2transhk.obj \ $(BUILDDIR)ssh2transport.obj $(BUILDDIR)ssh2userauth.obj \ $(BUILDDIR)sshaes.obj $(BUILDDIR)ssharcf.obj \ $(BUILDDIR)sshargon2.obj $(BUILDDIR)sshauxcrypt.obj \ $(BUILDDIR)sshblake2.obj $(BUILDDIR)sshblowf.obj \ $(BUILDDIR)sshccp.obj $(BUILDDIR)sshcommon.obj \ $(BUILDDIR)sshcrc.obj $(BUILDDIR)sshcrcda.obj \ $(BUILDDIR)sshdes.obj $(BUILDDIR)sshdh.obj \ $(BUILDDIR)sshdss.obj $(BUILDDIR)sshecc.obj \ $(BUILDDIR)sshgssc.obj $(BUILDDIR)sshhmac.obj \ $(BUILDDIR)sshmac.obj $(BUILDDIR)sshmd5.obj \ $(BUILDDIR)sshprng.obj $(BUILDDIR)sshpubk.obj \ $(BUILDDIR)sshrand.obj $(BUILDDIR)sshrsa.obj \ $(BUILDDIR)sshsh256.obj $(BUILDDIR)sshsh512.obj \ $(BUILDDIR)sshsha.obj $(BUILDDIR)sshsha3.obj \ $(BUILDDIR)sshshare.obj $(BUILDDIR)sshutils.obj \ $(BUILDDIR)sshverstring.obj $(BUILDDIR)sshzlib.obj \ $(BUILDDIR)stripctrl.obj $(BUILDDIR)timing.obj \ $(BUILDDIR)tree234.obj $(BUILDDIR)utils.obj \ $(BUILDDIR)version.obj $(BUILDDIR)wcwidth.obj \ $(BUILDDIR)wildcard.obj $(BUILDDIR)wincapi.obj \ $(BUILDDIR)wincliloop.obj $(BUILDDIR)wincons.obj \ $(BUILDDIR)windefs.obj $(BUILDDIR)wingss.obj \ $(BUILDDIR)winhandl.obj $(BUILDDIR)winhsock.obj \ $(BUILDDIR)winmisc.obj $(BUILDDIR)winmiscs.obj \ $(BUILDDIR)winnet.obj $(BUILDDIR)winnohlp.obj \ $(BUILDDIR)winnoise.obj $(BUILDDIR)winnojmp.obj \ $(BUILDDIR)winnpc.obj $(BUILDDIR)winnps.obj \ $(BUILDDIR)winpgntc.obj $(BUILDDIR)winproxy.obj \ $(BUILDDIR)winsecur.obj $(BUILDDIR)winselcli.obj \ $(BUILDDIR)winsftp.obj $(BUILDDIR)winshare.obj \ $(BUILDDIR)winstore.obj $(BUILDDIR)wintime.obj \ $(BUILDDIR)winucs.obj $(BUILDDIR)x11fwd.obj $(LD) $(LFLAGS) $(XLFLAGS) /out:$(BUILDDIR)psftp.exe \ /lldmap:$(BUILDDIR)psftp.map /subsystem:console$(SUBSYSVER) \ $(EXTRA_console) advapi32.lib $(BUILDDIR)agentf.obj \ $(BUILDDIR)aqsync.obj $(BUILDDIR)be_misc.obj \ $(BUILDDIR)be_ssh.obj $(BUILDDIR)callback.obj \ $(BUILDDIR)clicons.obj $(BUILDDIR)cmdline.obj comdlg32.lib \ $(BUILDDIR)conf.obj $(BUILDDIR)console.obj \ $(BUILDDIR)cproxy.obj $(BUILDDIR)ecc.obj \ $(BUILDDIR)errsock.obj gdi32.lib imm32.lib \ $(BUILDDIR)logging.obj $(BUILDDIR)mainchan.obj \ $(BUILDDIR)marshal.obj $(BUILDDIR)memory.obj \ $(BUILDDIR)misc.obj $(BUILDDIR)miscucs.obj \ $(BUILDDIR)mpint.obj $(BUILDDIR)nullplug.obj ole32.lib \ $(BUILDDIR)pgssapi.obj $(BUILDDIR)pinger.obj \ $(BUILDDIR)portfwd.obj $(BUILDDIR)proxy.obj \ $(BUILDDIR)psftp.obj $(BUILDDIR)psftp.res \ $(BUILDDIR)psftpcommon.obj $(BUILDDIR)settings.obj \ $(BUILDDIR)sftp.obj $(BUILDDIR)sftpcommon.obj shell32.lib \ $(BUILDDIR)ssh.obj $(BUILDDIR)ssh1bpp.obj \ $(BUILDDIR)ssh1censor.obj $(BUILDDIR)ssh1connection.obj \ $(BUILDDIR)ssh1connection-client.obj \ $(BUILDDIR)ssh1login.obj $(BUILDDIR)ssh2bpp.obj \ $(BUILDDIR)ssh2bpp-bare.obj $(BUILDDIR)ssh2censor.obj \ $(BUILDDIR)ssh2connection.obj \ $(BUILDDIR)ssh2connection-client.obj \ $(BUILDDIR)ssh2kex-client.obj $(BUILDDIR)ssh2transhk.obj \ $(BUILDDIR)ssh2transport.obj $(BUILDDIR)ssh2userauth.obj \ $(BUILDDIR)sshaes.obj $(BUILDDIR)ssharcf.obj \ $(BUILDDIR)sshargon2.obj $(BUILDDIR)sshauxcrypt.obj \ $(BUILDDIR)sshblake2.obj $(BUILDDIR)sshblowf.obj \ $(BUILDDIR)sshccp.obj $(BUILDDIR)sshcommon.obj \ $(BUILDDIR)sshcrc.obj $(BUILDDIR)sshcrcda.obj \ $(BUILDDIR)sshdes.obj $(BUILDDIR)sshdh.obj \ $(BUILDDIR)sshdss.obj $(BUILDDIR)sshecc.obj \ $(BUILDDIR)sshgssc.obj $(BUILDDIR)sshhmac.obj \ $(BUILDDIR)sshmac.obj $(BUILDDIR)sshmd5.obj \ $(BUILDDIR)sshprng.obj $(BUILDDIR)sshpubk.obj \ $(BUILDDIR)sshrand.obj $(BUILDDIR)sshrsa.obj \ $(BUILDDIR)sshsh256.obj $(BUILDDIR)sshsh512.obj \ $(BUILDDIR)sshsha.obj $(BUILDDIR)sshsha3.obj \ $(BUILDDIR)sshshare.obj $(BUILDDIR)sshutils.obj \ $(BUILDDIR)sshverstring.obj $(BUILDDIR)sshzlib.obj \ $(BUILDDIR)stripctrl.obj $(BUILDDIR)timing.obj \ $(BUILDDIR)tree234.obj user32.lib $(BUILDDIR)utils.obj \ $(BUILDDIR)version.obj $(BUILDDIR)wcwidth.obj \ $(BUILDDIR)wildcard.obj $(BUILDDIR)wincapi.obj \ $(BUILDDIR)wincliloop.obj $(BUILDDIR)wincons.obj \ $(BUILDDIR)windefs.obj $(BUILDDIR)wingss.obj \ $(BUILDDIR)winhandl.obj $(BUILDDIR)winhsock.obj \ $(BUILDDIR)winmisc.obj $(BUILDDIR)winmiscs.obj \ $(BUILDDIR)winnet.obj $(BUILDDIR)winnohlp.obj \ $(BUILDDIR)winnoise.obj $(BUILDDIR)winnojmp.obj \ $(BUILDDIR)winnpc.obj $(BUILDDIR)winnps.obj \ $(BUILDDIR)winpgntc.obj $(BUILDDIR)winproxy.obj \ $(BUILDDIR)winsecur.obj $(BUILDDIR)winselcli.obj \ $(BUILDDIR)winsftp.obj $(BUILDDIR)winshare.obj \ $(BUILDDIR)winstore.obj $(BUILDDIR)wintime.obj \ $(BUILDDIR)winucs.obj $(BUILDDIR)x11fwd.obj $(EXTRA_libs) $(BUILDDIR)psocks.exe: $(BUILDDIR)be_misc.obj $(BUILDDIR)callback.obj \ $(BUILDDIR)conf.obj $(BUILDDIR)console.obj \ $(BUILDDIR)errsock.obj $(BUILDDIR)logging.obj \ $(BUILDDIR)marshal.obj $(BUILDDIR)memory.obj \ $(BUILDDIR)misc.obj $(BUILDDIR)nocproxy.obj \ $(BUILDDIR)norand.obj $(BUILDDIR)portfwd.obj \ $(BUILDDIR)proxy.obj $(BUILDDIR)psocks.obj \ $(BUILDDIR)sshutils.obj $(BUILDDIR)stripctrl.obj \ $(BUILDDIR)time.obj $(BUILDDIR)timing.obj \ $(BUILDDIR)tree234.obj $(BUILDDIR)utils.obj \ $(BUILDDIR)version.obj $(BUILDDIR)wcwidth.obj \ $(BUILDDIR)wincliloop.obj $(BUILDDIR)wincons.obj \ $(BUILDDIR)winhandl.obj $(BUILDDIR)winhsock.obj \ $(BUILDDIR)winmisc.obj $(BUILDDIR)winmiscs.obj \ $(BUILDDIR)winnet.obj $(BUILDDIR)winnohlp.obj \ $(BUILDDIR)winproxy.obj $(BUILDDIR)winselcli.obj \ $(BUILDDIR)winsocks.obj $(LD) $(LFLAGS) $(XLFLAGS) /out:$(BUILDDIR)psocks.exe \ /lldmap:$(BUILDDIR)psocks.map /subsystem:console$(SUBSYSVER) \ $(EXTRA_console) advapi32.lib $(BUILDDIR)be_misc.obj \ $(BUILDDIR)callback.obj comdlg32.lib $(BUILDDIR)conf.obj \ $(BUILDDIR)console.obj $(BUILDDIR)errsock.obj gdi32.lib \ imm32.lib $(BUILDDIR)logging.obj $(BUILDDIR)marshal.obj \ $(BUILDDIR)memory.obj $(BUILDDIR)misc.obj \ $(BUILDDIR)nocproxy.obj $(BUILDDIR)norand.obj ole32.lib \ $(BUILDDIR)portfwd.obj $(BUILDDIR)proxy.obj \ $(BUILDDIR)psocks.obj shell32.lib $(BUILDDIR)sshutils.obj \ $(BUILDDIR)stripctrl.obj $(BUILDDIR)time.obj \ $(BUILDDIR)timing.obj $(BUILDDIR)tree234.obj user32.lib \ $(BUILDDIR)utils.obj $(BUILDDIR)version.obj \ $(BUILDDIR)wcwidth.obj $(BUILDDIR)wincliloop.obj \ $(BUILDDIR)wincons.obj $(BUILDDIR)winhandl.obj \ $(BUILDDIR)winhsock.obj $(BUILDDIR)winmisc.obj \ $(BUILDDIR)winmiscs.obj $(BUILDDIR)winnet.obj \ $(BUILDDIR)winnohlp.obj $(BUILDDIR)winproxy.obj \ $(BUILDDIR)winselcli.obj $(BUILDDIR)winsocks.obj \ $(EXTRA_libs) $(BUILDDIR)putty.exe: $(BUILDDIR)agentf.obj $(BUILDDIR)aqsync.obj \ $(BUILDDIR)be_all_s.obj $(BUILDDIR)be_misc.obj \ $(BUILDDIR)callback.obj $(BUILDDIR)cmdline.obj \ $(BUILDDIR)conf.obj $(BUILDDIR)config.obj \ $(BUILDDIR)cproxy.obj $(BUILDDIR)dialog.obj \ $(BUILDDIR)ecc.obj $(BUILDDIR)errsock.obj \ $(BUILDDIR)ldisc.obj $(BUILDDIR)logging.obj \ $(BUILDDIR)mainchan.obj $(BUILDDIR)marshal.obj \ $(BUILDDIR)memory.obj $(BUILDDIR)minibidi.obj \ $(BUILDDIR)misc.obj $(BUILDDIR)miscucs.obj \ $(BUILDDIR)mpint.obj $(BUILDDIR)nullplug.obj \ $(BUILDDIR)pgssapi.obj $(BUILDDIR)pinger.obj \ $(BUILDDIR)portfwd.obj $(BUILDDIR)proxy.obj \ $(BUILDDIR)putty.res $(BUILDDIR)raw.obj \ $(BUILDDIR)rlogin.obj $(BUILDDIR)sessprep.obj \ $(BUILDDIR)settings.obj $(BUILDDIR)sizetip.obj \ $(BUILDDIR)ssh.obj $(BUILDDIR)ssh1bpp.obj \ $(BUILDDIR)ssh1censor.obj $(BUILDDIR)ssh1connection.obj \ $(BUILDDIR)ssh1connection-client.obj \ $(BUILDDIR)ssh1login.obj $(BUILDDIR)ssh2bpp.obj \ $(BUILDDIR)ssh2bpp-bare.obj $(BUILDDIR)ssh2censor.obj \ $(BUILDDIR)ssh2connection.obj \ $(BUILDDIR)ssh2connection-client.obj \ $(BUILDDIR)ssh2kex-client.obj $(BUILDDIR)ssh2transhk.obj \ $(BUILDDIR)ssh2transport.obj $(BUILDDIR)ssh2userauth.obj \ $(BUILDDIR)sshaes.obj $(BUILDDIR)ssharcf.obj \ $(BUILDDIR)sshargon2.obj $(BUILDDIR)sshauxcrypt.obj \ $(BUILDDIR)sshblake2.obj $(BUILDDIR)sshblowf.obj \ $(BUILDDIR)sshccp.obj $(BUILDDIR)sshcommon.obj \ $(BUILDDIR)sshcrc.obj $(BUILDDIR)sshcrcda.obj \ $(BUILDDIR)sshdes.obj $(BUILDDIR)sshdh.obj \ $(BUILDDIR)sshdss.obj $(BUILDDIR)sshecc.obj \ $(BUILDDIR)sshgssc.obj $(BUILDDIR)sshhmac.obj \ $(BUILDDIR)sshmac.obj $(BUILDDIR)sshmd5.obj \ $(BUILDDIR)sshprng.obj $(BUILDDIR)sshpubk.obj \ $(BUILDDIR)sshrand.obj $(BUILDDIR)sshrsa.obj \ $(BUILDDIR)sshsh256.obj $(BUILDDIR)sshsh512.obj \ $(BUILDDIR)sshsha.obj $(BUILDDIR)sshsha3.obj \ $(BUILDDIR)sshshare.obj $(BUILDDIR)sshutils.obj \ $(BUILDDIR)sshverstring.obj $(BUILDDIR)sshzlib.obj \ $(BUILDDIR)stripctrl.obj $(BUILDDIR)supdup.obj \ $(BUILDDIR)telnet.obj $(BUILDDIR)terminal.obj \ $(BUILDDIR)timing.obj $(BUILDDIR)tree234.obj \ $(BUILDDIR)utils.obj $(BUILDDIR)version.obj \ $(BUILDDIR)wcwidth.obj $(BUILDDIR)wildcard.obj \ $(BUILDDIR)wincapi.obj $(BUILDDIR)wincfg.obj \ $(BUILDDIR)winctrls.obj $(BUILDDIR)windefs.obj \ $(BUILDDIR)windlg.obj $(BUILDDIR)window.obj \ $(BUILDDIR)wingss.obj $(BUILDDIR)winhandl.obj \ $(BUILDDIR)winhelp.obj $(BUILDDIR)winhsock.obj \ $(BUILDDIR)winjump.obj $(BUILDDIR)winmisc.obj \ $(BUILDDIR)winmiscs.obj $(BUILDDIR)winnet.obj \ $(BUILDDIR)winnoise.obj $(BUILDDIR)winnpc.obj \ $(BUILDDIR)winnps.obj $(BUILDDIR)winpgntc.obj \ $(BUILDDIR)winprint.obj $(BUILDDIR)winproxy.obj \ $(BUILDDIR)winsecur.obj $(BUILDDIR)winselgui.obj \ $(BUILDDIR)winser.obj $(BUILDDIR)winshare.obj \ $(BUILDDIR)winstore.obj $(BUILDDIR)wintime.obj \ $(BUILDDIR)winucs.obj $(BUILDDIR)winutils.obj \ $(BUILDDIR)winx11.obj $(BUILDDIR)x11fwd.obj $(LD) $(LFLAGS) $(XLFLAGS) /out:$(BUILDDIR)putty.exe \ /lldmap:$(BUILDDIR)putty.map /subsystem:windows$(SUBSYSVER) \ $(EXTRA_windows) advapi32.lib $(BUILDDIR)agentf.obj \ $(BUILDDIR)aqsync.obj $(BUILDDIR)be_all_s.obj \ $(BUILDDIR)be_misc.obj $(BUILDDIR)callback.obj \ $(BUILDDIR)cmdline.obj comdlg32.lib $(BUILDDIR)conf.obj \ $(BUILDDIR)config.obj $(BUILDDIR)cproxy.obj \ $(BUILDDIR)dialog.obj $(BUILDDIR)ecc.obj \ $(BUILDDIR)errsock.obj gdi32.lib imm32.lib \ $(BUILDDIR)ldisc.obj $(BUILDDIR)logging.obj \ $(BUILDDIR)mainchan.obj $(BUILDDIR)marshal.obj \ $(BUILDDIR)memory.obj $(BUILDDIR)minibidi.obj \ $(BUILDDIR)misc.obj $(BUILDDIR)miscucs.obj \ $(BUILDDIR)mpint.obj $(BUILDDIR)nullplug.obj ole32.lib \ $(BUILDDIR)pgssapi.obj $(BUILDDIR)pinger.obj \ $(BUILDDIR)portfwd.obj $(BUILDDIR)proxy.obj \ $(BUILDDIR)putty.res $(BUILDDIR)raw.obj \ $(BUILDDIR)rlogin.obj $(BUILDDIR)sessprep.obj \ $(BUILDDIR)settings.obj shell32.lib $(BUILDDIR)sizetip.obj \ $(BUILDDIR)ssh.obj $(BUILDDIR)ssh1bpp.obj \ $(BUILDDIR)ssh1censor.obj $(BUILDDIR)ssh1connection.obj \ $(BUILDDIR)ssh1connection-client.obj \ $(BUILDDIR)ssh1login.obj $(BUILDDIR)ssh2bpp.obj \ $(BUILDDIR)ssh2bpp-bare.obj $(BUILDDIR)ssh2censor.obj \ $(BUILDDIR)ssh2connection.obj \ $(BUILDDIR)ssh2connection-client.obj \ $(BUILDDIR)ssh2kex-client.obj $(BUILDDIR)ssh2transhk.obj \ $(BUILDDIR)ssh2transport.obj $(BUILDDIR)ssh2userauth.obj \ $(BUILDDIR)sshaes.obj $(BUILDDIR)ssharcf.obj \ $(BUILDDIR)sshargon2.obj $(BUILDDIR)sshauxcrypt.obj \ $(BUILDDIR)sshblake2.obj $(BUILDDIR)sshblowf.obj \ $(BUILDDIR)sshccp.obj $(BUILDDIR)sshcommon.obj \ $(BUILDDIR)sshcrc.obj $(BUILDDIR)sshcrcda.obj \ $(BUILDDIR)sshdes.obj $(BUILDDIR)sshdh.obj \ $(BUILDDIR)sshdss.obj $(BUILDDIR)sshecc.obj \ $(BUILDDIR)sshgssc.obj $(BUILDDIR)sshhmac.obj \ $(BUILDDIR)sshmac.obj $(BUILDDIR)sshmd5.obj \ $(BUILDDIR)sshprng.obj $(BUILDDIR)sshpubk.obj \ $(BUILDDIR)sshrand.obj $(BUILDDIR)sshrsa.obj \ $(BUILDDIR)sshsh256.obj $(BUILDDIR)sshsh512.obj \ $(BUILDDIR)sshsha.obj $(BUILDDIR)sshsha3.obj \ $(BUILDDIR)sshshare.obj $(BUILDDIR)sshutils.obj \ $(BUILDDIR)sshverstring.obj $(BUILDDIR)sshzlib.obj \ $(BUILDDIR)stripctrl.obj $(BUILDDIR)supdup.obj \ $(BUILDDIR)telnet.obj $(BUILDDIR)terminal.obj \ $(BUILDDIR)timing.obj $(BUILDDIR)tree234.obj user32.lib \ $(BUILDDIR)utils.obj $(BUILDDIR)version.obj \ $(BUILDDIR)wcwidth.obj $(BUILDDIR)wildcard.obj \ $(BUILDDIR)wincapi.obj $(BUILDDIR)wincfg.obj \ $(BUILDDIR)winctrls.obj $(BUILDDIR)windefs.obj \ $(BUILDDIR)windlg.obj $(BUILDDIR)window.obj \ $(BUILDDIR)wingss.obj $(BUILDDIR)winhandl.obj \ $(BUILDDIR)winhelp.obj $(BUILDDIR)winhsock.obj \ $(BUILDDIR)winjump.obj $(BUILDDIR)winmisc.obj \ $(BUILDDIR)winmiscs.obj $(BUILDDIR)winnet.obj \ $(BUILDDIR)winnoise.obj $(BUILDDIR)winnpc.obj \ $(BUILDDIR)winnps.obj $(BUILDDIR)winpgntc.obj \ $(BUILDDIR)winprint.obj $(BUILDDIR)winproxy.obj \ $(BUILDDIR)winsecur.obj $(BUILDDIR)winselgui.obj \ $(BUILDDIR)winser.obj $(BUILDDIR)winshare.obj \ $(BUILDDIR)winstore.obj $(BUILDDIR)wintime.obj \ $(BUILDDIR)winucs.obj $(BUILDDIR)winutils.obj \ $(BUILDDIR)winx11.obj $(BUILDDIR)x11fwd.obj $(EXTRA_libs) $(BUILDDIR)puttygen.exe: $(BUILDDIR)conf.obj $(BUILDDIR)ecc.obj \ $(BUILDDIR)import.obj $(BUILDDIR)marshal.obj \ $(BUILDDIR)memory.obj $(BUILDDIR)millerrabin.obj \ $(BUILDDIR)misc.obj $(BUILDDIR)mpint.obj \ $(BUILDDIR)mpunsafe.obj $(BUILDDIR)notiming.obj \ $(BUILDDIR)pockle.obj $(BUILDDIR)primecandidate.obj \ $(BUILDDIR)puttygen.res $(BUILDDIR)smallprimes.obj \ $(BUILDDIR)sshaes.obj $(BUILDDIR)sshargon2.obj \ $(BUILDDIR)sshauxcrypt.obj $(BUILDDIR)sshbcrypt.obj \ $(BUILDDIR)sshblake2.obj $(BUILDDIR)sshblowf.obj \ $(BUILDDIR)sshdes.obj $(BUILDDIR)sshdss.obj \ $(BUILDDIR)sshdssg.obj $(BUILDDIR)sshecc.obj \ $(BUILDDIR)sshecdsag.obj $(BUILDDIR)sshhmac.obj \ $(BUILDDIR)sshmd5.obj $(BUILDDIR)sshprime.obj \ $(BUILDDIR)sshprng.obj $(BUILDDIR)sshpubk.obj \ $(BUILDDIR)sshrand.obj $(BUILDDIR)sshrsa.obj \ $(BUILDDIR)sshrsag.obj $(BUILDDIR)sshsh256.obj \ $(BUILDDIR)sshsh512.obj $(BUILDDIR)sshsha.obj \ $(BUILDDIR)sshsha3.obj $(BUILDDIR)stripctrl.obj \ $(BUILDDIR)tree234.obj $(BUILDDIR)utils.obj \ $(BUILDDIR)version.obj $(BUILDDIR)wcwidth.obj \ $(BUILDDIR)winctrls.obj $(BUILDDIR)winhelp.obj \ $(BUILDDIR)winmisc.obj $(BUILDDIR)winmiscs.obj \ $(BUILDDIR)winnoise.obj $(BUILDDIR)winnojmp.obj \ $(BUILDDIR)winpgen.obj $(BUILDDIR)winsecur.obj \ $(BUILDDIR)winstore.obj $(BUILDDIR)wintime.obj \ $(BUILDDIR)winutils.obj $(LD) $(LFLAGS) $(XLFLAGS) /out:$(BUILDDIR)puttygen.exe \ /lldmap:$(BUILDDIR)puttygen.map \ /subsystem:windows$(SUBSYSVER) $(EXTRA_windows) advapi32.lib \ comdlg32.lib $(BUILDDIR)conf.obj $(BUILDDIR)ecc.obj \ gdi32.lib imm32.lib $(BUILDDIR)import.obj \ $(BUILDDIR)marshal.obj $(BUILDDIR)memory.obj \ $(BUILDDIR)millerrabin.obj $(BUILDDIR)misc.obj \ $(BUILDDIR)mpint.obj $(BUILDDIR)mpunsafe.obj \ $(BUILDDIR)notiming.obj ole32.lib $(BUILDDIR)pockle.obj \ $(BUILDDIR)primecandidate.obj $(BUILDDIR)puttygen.res \ shell32.lib $(BUILDDIR)smallprimes.obj $(BUILDDIR)sshaes.obj \ $(BUILDDIR)sshargon2.obj $(BUILDDIR)sshauxcrypt.obj \ $(BUILDDIR)sshbcrypt.obj $(BUILDDIR)sshblake2.obj \ $(BUILDDIR)sshblowf.obj $(BUILDDIR)sshdes.obj \ $(BUILDDIR)sshdss.obj $(BUILDDIR)sshdssg.obj \ $(BUILDDIR)sshecc.obj $(BUILDDIR)sshecdsag.obj \ $(BUILDDIR)sshhmac.obj $(BUILDDIR)sshmd5.obj \ $(BUILDDIR)sshprime.obj $(BUILDDIR)sshprng.obj \ $(BUILDDIR)sshpubk.obj $(BUILDDIR)sshrand.obj \ $(BUILDDIR)sshrsa.obj $(BUILDDIR)sshrsag.obj \ $(BUILDDIR)sshsh256.obj $(BUILDDIR)sshsh512.obj \ $(BUILDDIR)sshsha.obj $(BUILDDIR)sshsha3.obj \ $(BUILDDIR)stripctrl.obj $(BUILDDIR)tree234.obj user32.lib \ $(BUILDDIR)utils.obj $(BUILDDIR)version.obj \ $(BUILDDIR)wcwidth.obj $(BUILDDIR)winctrls.obj \ $(BUILDDIR)winhelp.obj $(BUILDDIR)winmisc.obj \ $(BUILDDIR)winmiscs.obj $(BUILDDIR)winnoise.obj \ $(BUILDDIR)winnojmp.obj $(BUILDDIR)winpgen.obj \ $(BUILDDIR)winsecur.obj $(BUILDDIR)winstore.obj \ $(BUILDDIR)wintime.obj $(BUILDDIR)winutils.obj $(EXTRA_libs) $(BUILDDIR)puttytel.exe: $(BUILDDIR)be_misc.obj $(BUILDDIR)be_nos_s.obj \ $(BUILDDIR)callback.obj $(BUILDDIR)cmdline.obj \ $(BUILDDIR)conf.obj $(BUILDDIR)config.obj \ $(BUILDDIR)dialog.obj $(BUILDDIR)errsock.obj \ $(BUILDDIR)ldisc.obj $(BUILDDIR)logging.obj \ $(BUILDDIR)marshal.obj $(BUILDDIR)memory.obj \ $(BUILDDIR)minibidi.obj $(BUILDDIR)misc.obj \ $(BUILDDIR)miscucs.obj $(BUILDDIR)nocproxy.obj \ $(BUILDDIR)nogss.obj $(BUILDDIR)norand.obj \ $(BUILDDIR)pinger.obj $(BUILDDIR)proxy.obj \ $(BUILDDIR)puttytel.res $(BUILDDIR)raw.obj \ $(BUILDDIR)rlogin.obj $(BUILDDIR)sessprep.obj \ $(BUILDDIR)settings.obj $(BUILDDIR)sizetip.obj \ $(BUILDDIR)stripctrl.obj $(BUILDDIR)supdup.obj \ $(BUILDDIR)telnet.obj $(BUILDDIR)terminal.obj \ $(BUILDDIR)timing.obj $(BUILDDIR)tree234.obj \ $(BUILDDIR)utils.obj $(BUILDDIR)version.obj \ $(BUILDDIR)wcwidth.obj $(BUILDDIR)wincfg.obj \ $(BUILDDIR)winctrls.obj $(BUILDDIR)windefs.obj \ $(BUILDDIR)windlg.obj $(BUILDDIR)window.obj \ $(BUILDDIR)winhandl.obj $(BUILDDIR)winhelp.obj \ $(BUILDDIR)winhsock.obj $(BUILDDIR)winjump.obj \ $(BUILDDIR)winmisc.obj $(BUILDDIR)winmiscs.obj \ $(BUILDDIR)winnet.obj $(BUILDDIR)winprint.obj \ $(BUILDDIR)winproxy.obj $(BUILDDIR)winsecur.obj \ $(BUILDDIR)winselgui.obj $(BUILDDIR)winser.obj \ $(BUILDDIR)winstore.obj $(BUILDDIR)wintime.obj \ $(BUILDDIR)winucs.obj $(BUILDDIR)winutils.obj $(LD) $(LFLAGS) $(XLFLAGS) /out:$(BUILDDIR)puttytel.exe \ /lldmap:$(BUILDDIR)puttytel.map \ /subsystem:windows$(SUBSYSVER) $(EXTRA_windows) advapi32.lib \ $(BUILDDIR)be_misc.obj $(BUILDDIR)be_nos_s.obj \ $(BUILDDIR)callback.obj $(BUILDDIR)cmdline.obj comdlg32.lib \ $(BUILDDIR)conf.obj $(BUILDDIR)config.obj \ $(BUILDDIR)dialog.obj $(BUILDDIR)errsock.obj gdi32.lib \ imm32.lib $(BUILDDIR)ldisc.obj $(BUILDDIR)logging.obj \ $(BUILDDIR)marshal.obj $(BUILDDIR)memory.obj \ $(BUILDDIR)minibidi.obj $(BUILDDIR)misc.obj \ $(BUILDDIR)miscucs.obj $(BUILDDIR)nocproxy.obj \ $(BUILDDIR)nogss.obj $(BUILDDIR)norand.obj ole32.lib \ $(BUILDDIR)pinger.obj $(BUILDDIR)proxy.obj \ $(BUILDDIR)puttytel.res $(BUILDDIR)raw.obj \ $(BUILDDIR)rlogin.obj $(BUILDDIR)sessprep.obj \ $(BUILDDIR)settings.obj shell32.lib $(BUILDDIR)sizetip.obj \ $(BUILDDIR)stripctrl.obj $(BUILDDIR)supdup.obj \ $(BUILDDIR)telnet.obj $(BUILDDIR)terminal.obj \ $(BUILDDIR)timing.obj $(BUILDDIR)tree234.obj user32.lib \ $(BUILDDIR)utils.obj $(BUILDDIR)version.obj \ $(BUILDDIR)wcwidth.obj $(BUILDDIR)wincfg.obj \ $(BUILDDIR)winctrls.obj $(BUILDDIR)windefs.obj \ $(BUILDDIR)windlg.obj $(BUILDDIR)window.obj \ $(BUILDDIR)winhandl.obj $(BUILDDIR)winhelp.obj \ $(BUILDDIR)winhsock.obj $(BUILDDIR)winjump.obj \ $(BUILDDIR)winmisc.obj $(BUILDDIR)winmiscs.obj \ $(BUILDDIR)winnet.obj $(BUILDDIR)winprint.obj \ $(BUILDDIR)winproxy.obj $(BUILDDIR)winsecur.obj \ $(BUILDDIR)winselgui.obj $(BUILDDIR)winser.obj \ $(BUILDDIR)winstore.obj $(BUILDDIR)wintime.obj \ $(BUILDDIR)winucs.obj $(BUILDDIR)winutils.obj $(EXTRA_libs) $(BUILDDIR)testcrypt.exe: $(BUILDDIR)ecc.obj $(BUILDDIR)marshal.obj \ $(BUILDDIR)memory.obj $(BUILDDIR)millerrabin.obj \ $(BUILDDIR)mpint.obj $(BUILDDIR)mpunsafe.obj \ $(BUILDDIR)pockle.obj $(BUILDDIR)primecandidate.obj \ $(BUILDDIR)smallprimes.obj $(BUILDDIR)sshaes.obj \ $(BUILDDIR)ssharcf.obj $(BUILDDIR)sshargon2.obj \ $(BUILDDIR)sshauxcrypt.obj $(BUILDDIR)sshblake2.obj \ $(BUILDDIR)sshblowf.obj $(BUILDDIR)sshccp.obj \ $(BUILDDIR)sshcrc.obj $(BUILDDIR)sshcrcda.obj \ $(BUILDDIR)sshdes.obj $(BUILDDIR)sshdh.obj \ $(BUILDDIR)sshdss.obj $(BUILDDIR)sshdssg.obj \ $(BUILDDIR)sshecc.obj $(BUILDDIR)sshecdsag.obj \ $(BUILDDIR)sshhmac.obj $(BUILDDIR)sshmd5.obj \ $(BUILDDIR)sshprime.obj $(BUILDDIR)sshprng.obj \ $(BUILDDIR)sshpubk.obj $(BUILDDIR)sshrsa.obj \ $(BUILDDIR)sshrsag.obj $(BUILDDIR)sshsh256.obj \ $(BUILDDIR)sshsh512.obj $(BUILDDIR)sshsha.obj \ $(BUILDDIR)sshsha3.obj $(BUILDDIR)testcrypt.obj \ $(BUILDDIR)tree234.obj $(BUILDDIR)utils.obj \ $(BUILDDIR)winmiscs.obj $(LD) $(LFLAGS) $(XLFLAGS) /out:$(BUILDDIR)testcrypt.exe \ /lldmap:$(BUILDDIR)testcrypt.map \ /subsystem:console$(SUBSYSVER) $(EXTRA_console) \ $(BUILDDIR)ecc.obj $(BUILDDIR)marshal.obj \ $(BUILDDIR)memory.obj $(BUILDDIR)millerrabin.obj \ $(BUILDDIR)mpint.obj $(BUILDDIR)mpunsafe.obj \ $(BUILDDIR)pockle.obj $(BUILDDIR)primecandidate.obj \ $(BUILDDIR)smallprimes.obj $(BUILDDIR)sshaes.obj \ $(BUILDDIR)ssharcf.obj $(BUILDDIR)sshargon2.obj \ $(BUILDDIR)sshauxcrypt.obj $(BUILDDIR)sshblake2.obj \ $(BUILDDIR)sshblowf.obj $(BUILDDIR)sshccp.obj \ $(BUILDDIR)sshcrc.obj $(BUILDDIR)sshcrcda.obj \ $(BUILDDIR)sshdes.obj $(BUILDDIR)sshdh.obj \ $(BUILDDIR)sshdss.obj $(BUILDDIR)sshdssg.obj \ $(BUILDDIR)sshecc.obj $(BUILDDIR)sshecdsag.obj \ $(BUILDDIR)sshhmac.obj $(BUILDDIR)sshmd5.obj \ $(BUILDDIR)sshprime.obj $(BUILDDIR)sshprng.obj \ $(BUILDDIR)sshpubk.obj $(BUILDDIR)sshrsa.obj \ $(BUILDDIR)sshrsag.obj $(BUILDDIR)sshsh256.obj \ $(BUILDDIR)sshsh512.obj $(BUILDDIR)sshsha.obj \ $(BUILDDIR)sshsha3.obj $(BUILDDIR)testcrypt.obj \ $(BUILDDIR)tree234.obj $(BUILDDIR)utils.obj \ $(BUILDDIR)winmiscs.obj $(EXTRA_libs) $(BUILDDIR)agentf.obj: ../agentf.c ../putty.h ../ssh.h ../pageant.h \ ../sshchan.h ../defs.h ../puttyps.h ../network.h ../misc.h \ ../marshal.h ../sshsignals.h ../puttymem.h ../tree234.h \ ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)aqsync.obj: ../aqsync.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)be_all_s.obj: ../be_all_s.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)be_misc.obj: ../be_misc.c ../putty.h ../network.h ../defs.h \ ../puttyps.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)be_none.obj: ../be_none.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)be_nos_s.obj: ../be_nos_s.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)be_ssh.obj: ../be_ssh.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)callback.obj: ../callback.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)cgtest.obj: ../cgtest.c ../cmdgen.c ../putty.h ../ssh.h \ ../sshkeygen.h ../mpint.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)clicons.obj: ../clicons.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)cmdgen.obj: ../cmdgen.c ../putty.h ../ssh.h ../sshkeygen.h \ ../mpint.h ../defs.h ../puttyps.h ../network.h ../misc.h \ ../marshal.h ../sshsignals.h ../puttymem.h ../tree234.h \ ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)cmdline.obj: ../cmdline.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)conf.obj: ../conf.c ../tree234.h ../putty.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)config.obj: ../config.c ../putty.h ../dialog.h ../storage.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)console.obj: ../console.c ../putty.h ../misc.h ../console.h \ ../defs.h ../puttyps.h ../network.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../windows/winstuff.h \ ../unix/unix.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)cproxy.obj: ../cproxy.c ../putty.h ../ssh.h ../network.h \ ../proxy.h ../marshal.h ../defs.h ../puttyps.h ../misc.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)dialog.obj: ../dialog.c ../putty.h ../dialog.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)ecc.obj: ../ecc.c ../ssh.h ../mpint.h ../ecc.h ../puttymem.h \ ../tree234.h ../network.h ../misc.h ../sshttymodes.h \ ../defs.h ../marshal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)errsock.obj: ../errsock.c ../tree234.h ../putty.h ../network.h \ ../defs.h ../puttyps.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)fromucs.obj: ../charset/fromucs.c ../charset/charset.h \ ../charset/internal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)fuzzterm.obj: ../fuzzterm.c ../putty.h ../dialog.h ../terminal.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../tree234.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)gtkapp.obj: ../unix/gtkapp.c ../putty.h ../unix/gtkmisc.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)gtkask.obj: ../unix/gtkask.c ../defs.h ../unix/gtkfont.h \ ../unix/gtkcompat.h ../unix/gtkmisc.h ../putty.h ../ssh.h \ ../misc.h ../puttyps.h ../network.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)gtkcfg.obj: ../unix/gtkcfg.c ../putty.h ../dialog.h ../storage.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)gtkcols.obj: ../unix/gtkcols.c ../defs.h ../unix/gtkcompat.h \ ../unix/gtkcols.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)gtkcomm.obj: ../unix/gtkcomm.c ../putty.h ../terminal.h \ ../unix/gtkcompat.h ../unix/gtkfont.h ../unix/gtkmisc.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../tree234.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)gtkdlg.obj: ../unix/gtkdlg.c ../putty.h ../unix/gtkcompat.h \ ../unix/gtkcols.h ../unix/gtkfont.h ../unix/gtkmisc.h \ ../unix/x11misc.h ../storage.h ../dialog.h ../tree234.h \ ../licence.h ../ssh.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)gtkfont.obj: ../unix/gtkfont.c ../putty.h ../unix/gtkfont.h \ ../unix/gtkcompat.h ../unix/gtkmisc.h ../tree234.h \ ../unix/x11misc.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)gtkmain.obj: ../unix/gtkmain.c ../putty.h ../terminal.h \ ../unix/gtkcompat.h ../unix/gtkfont.h ../unix/gtkmisc.h \ ../unix/x11misc.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../tree234.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)gtkmisc.obj: ../unix/gtkmisc.c ../putty.h ../unix/gtkcompat.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)gtkwin.obj: ../unix/gtkwin.c ../putty.h ../terminal.h \ ../unix/gtkcompat.h ../unix/gtkfont.h ../unix/gtkmisc.h \ ../unix/x11misc.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../tree234.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)import.obj: ../import.c ../putty.h ../ssh.h ../mpint.h ../misc.h \ ../defs.h ../puttyps.h ../network.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)ldisc.obj: ../ldisc.c ../putty.h ../terminal.h ../ldisc.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../tree234.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)localenc.obj: ../charset/localenc.c ../charset/charset.h \ ../charset/internal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)logging.obj: ../logging.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)macenc.obj: ../charset/macenc.c ../charset/charset.h \ ../charset/internal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)mainchan.obj: ../mainchan.c ../putty.h ../ssh.h ../sshppl.h \ ../sshchan.h ../sshsignals.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../puttymem.h \ ../tree234.h ../sshttymodes.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)marshal.obj: ../marshal.c ../marshal.h ../misc.h ../defs.h \ ../puttymem.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)memory.obj: ../memory.c ../defs.h ../puttymem.h ../misc.h \ ../marshal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)millerrabin.obj: ../millerrabin.c ../ssh.h ../sshkeygen.h \ ../mpint.h ../mpunsafe.h ../puttymem.h ../tree234.h \ ../network.h ../misc.h ../sshttymodes.h ../defs.h \ ../marshal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)mimeenc.obj: ../charset/mimeenc.c ../charset/charset.h \ ../charset/internal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)minibidi.obj: ../minibidi.c ../putty.h ../misc.h ../defs.h \ ../puttyps.h ../network.h ../marshal.h ../sshsignals.h \ ../puttymem.h ../windows/winstuff.h ../unix/unix.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)misc.obj: ../misc.c ../defs.h ../putty.h ../misc.h ../puttyps.h \ ../network.h ../marshal.h ../sshsignals.h ../puttymem.h \ ../windows/winstuff.h ../unix/unix.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)miscucs.obj: ../miscucs.c ../putty.h ../misc.h ../defs.h \ ../puttyps.h ../network.h ../marshal.h ../sshsignals.h \ ../puttymem.h ../windows/winstuff.h ../unix/unix.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)mpint.obj: ../mpint.c ../defs.h ../misc.h ../puttymem.h \ ../mpint.h ../mpint_i.h ../marshal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)mpunsafe.obj: ../mpunsafe.c ../defs.h ../misc.h ../puttymem.h \ ../mpint.h ../mpint_i.h ../marshal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)nocmdline.obj: ../nocmdline.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)nocproxy.obj: ../nocproxy.c ../putty.h ../network.h ../proxy.h \ ../defs.h ../puttyps.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)nogss.obj: ../nogss.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)norand.obj: ../norand.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)noterm.obj: ../noterm.c ../putty.h ../terminal.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../tree234.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)notiming.obj: ../notiming.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)nullplug.obj: ../nullplug.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)osxlaunch.obj: ../unix/osxlaunch.c $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)pageant.obj: ../pageant.c ../putty.h ../mpint.h ../ssh.h \ ../sshcr.h ../pageant.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ ../tree234.h ../sshttymodes.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)pageant.res: $(BUILDDIR)pageant.rcpp ../windows/rcstuff.h \ ../windows/pageant-rc.h ../windows/pageant.ico \ ../windows/pageants.ico ../windows/pageant.mft \ ../windows/win_res.h ../version.h ../licence.h $(RC) $(BUILDDIR)pageant.rcpp /FO $(BUILDDIR)pageant.res $(BUILDDIR)pgssapi.obj: ../pgssapi.c ../putty.h ../pgssapi.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)pinger.obj: ../pinger.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)plink.res: $(BUILDDIR)plink.rcpp ../windows/rcstuff.h \ ../windows/putty.ico ../version.h ../licence.h $(RC) $(BUILDDIR)plink.rcpp /FO $(BUILDDIR)plink.res $(BUILDDIR)pockle.obj: ../pockle.c ../ssh.h ../sshkeygen.h ../mpint.h \ ../mpunsafe.h ../tree234.h ../puttymem.h ../network.h \ ../misc.h ../sshttymodes.h ../defs.h ../marshal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)portfwd.obj: ../portfwd.c ../putty.h ../ssh.h ../sshchan.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)primecandidate.obj: ../primecandidate.c ../ssh.h ../mpint.h \ ../mpunsafe.h ../sshkeygen.h ../puttymem.h ../tree234.h \ ../network.h ../misc.h ../sshttymodes.h ../defs.h \ ../marshal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)procnet.obj: ../unix/procnet.c ../misc.h ../defs.h ../puttymem.h \ ../marshal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)proxy.obj: ../proxy.c ../putty.h ../network.h ../proxy.h \ ../defs.h ../puttyps.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)pscp.obj: ../pscp.c ../putty.h ../psftp.h ../ssh.h ../sftp.h \ ../storage.h ../defs.h ../puttyps.h ../network.h ../misc.h \ ../marshal.h ../sshsignals.h ../puttymem.h ../tree234.h \ ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)pscp.res: $(BUILDDIR)pscp.rcpp ../windows/rcstuff.h \ ../windows/pscp.ico ../version.h ../licence.h $(RC) $(BUILDDIR)pscp.rcpp /FO $(BUILDDIR)pscp.res $(BUILDDIR)psftp.obj: ../psftp.c ../putty.h ../psftp.h ../storage.h ../ssh.h \ ../sftp.h ../defs.h ../puttyps.h ../network.h ../misc.h \ ../marshal.h ../sshsignals.h ../puttymem.h ../tree234.h \ ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)psftp.res: $(BUILDDIR)psftp.rcpp ../windows/rcstuff.h \ ../windows/pscp.ico ../version.h ../licence.h $(RC) $(BUILDDIR)psftp.rcpp /FO $(BUILDDIR)psftp.res $(BUILDDIR)psftpcommon.obj: ../psftpcommon.c ../putty.h ../sftp.h ../psftp.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)psocks.obj: ../psocks.c ../putty.h ../misc.h ../ssh.h \ ../sshchan.h ../psocks.h ../defs.h ../puttyps.h ../network.h \ ../marshal.h ../sshsignals.h ../puttymem.h ../tree234.h \ ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)putty.res: $(BUILDDIR)putty.rcpp ../windows/rcstuff.h \ ../windows/putty.mft ../windows/win_res.h \ ../windows/putty.ico ../windows/puttycfg.ico ../version.h \ ../licence.h $(RC) $(BUILDDIR)putty.rcpp /FO $(BUILDDIR)putty.res $(BUILDDIR)puttygen.res: $(BUILDDIR)puttygen.rcpp ../windows/rcstuff.h \ ../windows/puttygen-rc.h ../windows/puttygen.ico \ ../windows/puttygen.mft ../windows/win_res.h ../version.h \ ../licence.h $(RC) $(BUILDDIR)puttygen.rcpp /FO $(BUILDDIR)puttygen.res $(BUILDDIR)puttytel.res: $(BUILDDIR)puttytel.rcpp ../windows/rcstuff.h \ ../windows/puttytel.mft ../windows/win_res.h \ ../windows/putty.ico ../windows/puttycfg.ico ../version.h \ ../licence.h $(RC) $(BUILDDIR)puttytel.rcpp /FO $(BUILDDIR)puttytel.res $(BUILDDIR)raw.obj: ../raw.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)rlogin.obj: ../rlogin.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)sbcs.obj: ../charset/sbcs.c ../charset/charset.h \ ../charset/internal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)sbcsdat.obj: ../charset/sbcsdat.c ../charset/charset.h \ ../charset/internal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)scpserver.obj: ../scpserver.c ../putty.h ../ssh.h ../sshcr.h \ ../sshchan.h ../sftp.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ ../tree234.h ../sshttymodes.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)sesschan.obj: ../sesschan.c ../putty.h ../ssh.h ../sshchan.h \ ../sshserver.h ../sftp.h ../sshsignals.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)sessprep.obj: ../sessprep.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)settings.obj: ../settings.c ../putty.h ../storage.h ../sshgssc.h \ ../sshgss.h ../defs.h ../puttyps.h ../network.h ../misc.h \ ../marshal.h ../sshsignals.h ../pgssapi.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)sftp.obj: ../sftp.c ../misc.h ../tree234.h ../sftp.h ../defs.h \ ../puttymem.h ../marshal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)sftpcommon.obj: ../sftpcommon.c ../misc.h ../sftp.h ../defs.h \ ../puttymem.h ../marshal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)sftpserver.obj: ../sftpserver.c ../putty.h ../ssh.h ../sftp.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)sizetip.obj: ../windows/sizetip.c ../putty.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)slookup.obj: ../charset/slookup.c ../charset/charset.h \ ../charset/internal.h ../charset/enum.c ../charset/sbcsdat.c \ ../charset/utf8.c $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)smallprimes.obj: ../smallprimes.c ../ssh.h ../sshkeygen.h \ ../puttymem.h ../tree234.h ../network.h ../misc.h \ ../sshttymodes.h ../defs.h ../marshal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)ssh.obj: ../ssh.c ../putty.h ../pageant.h ../tree234.h \ ../storage.h ../marshal.h ../ssh.h ../sshcr.h ../sshbpp.h \ ../sshppl.h ../sshchan.h ../sshgssc.h ../sshgss.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../sshsignals.h \ ../puttymem.h ../sshttymodes.h ../pgssapi.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)ssh1bpp.obj: ../ssh1bpp.c ../putty.h ../ssh.h ../sshbpp.h \ ../sshcr.h ../defs.h ../puttyps.h ../network.h ../misc.h \ ../marshal.h ../sshsignals.h ../puttymem.h ../tree234.h \ ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)ssh1censor.obj: ../ssh1censor.c ../putty.h ../ssh.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)ssh1connection.obj: ../ssh1connection.c ../putty.h ../ssh.h \ ../sshbpp.h ../sshppl.h ../sshchan.h ../sshcr.h \ ../ssh1connection.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ ../tree234.h ../sshttymodes.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)ssh1connection-client.obj: ../ssh1connection-client.c ../putty.h \ ../ssh.h ../sshbpp.h ../sshppl.h ../sshchan.h ../sshcr.h \ ../ssh1connection.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ ../tree234.h ../sshttymodes.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)ssh1connection-server.obj: ../ssh1connection-server.c ../putty.h \ ../ssh.h ../sshbpp.h ../sshppl.h ../sshchan.h ../sshcr.h \ ../ssh1connection.h ../sshserver.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)ssh1login.obj: ../ssh1login.c ../putty.h ../ssh.h ../mpint.h \ ../sshbpp.h ../sshppl.h ../sshcr.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)ssh1login-server.obj: ../ssh1login-server.c ../putty.h ../mpint.h \ ../ssh.h ../sshbpp.h ../sshppl.h ../sshcr.h ../sshserver.h \ ../sshkeygen.h ../defs.h ../puttyps.h ../network.h ../misc.h \ ../marshal.h ../sshsignals.h ../puttymem.h ../tree234.h \ ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)ssh2bpp.obj: ../ssh2bpp.c ../putty.h ../ssh.h ../sshbpp.h \ ../sshcr.h ../defs.h ../puttyps.h ../network.h ../misc.h \ ../marshal.h ../sshsignals.h ../puttymem.h ../tree234.h \ ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)ssh2bpp-bare.obj: ../ssh2bpp-bare.c ../putty.h ../ssh.h \ ../sshbpp.h ../sshcr.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ ../tree234.h ../sshttymodes.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)ssh2censor.obj: ../ssh2censor.c ../putty.h ../ssh.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)ssh2connection.obj: ../ssh2connection.c ../putty.h ../ssh.h \ ../sshbpp.h ../sshppl.h ../sshchan.h ../sshcr.h \ ../ssh2connection.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ ../tree234.h ../sshttymodes.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)ssh2connection-client.obj: ../ssh2connection-client.c ../putty.h \ ../ssh.h ../sshbpp.h ../sshppl.h ../sshchan.h ../sshcr.h \ ../ssh2connection.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ ../tree234.h ../sshttymodes.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)ssh2connection-server.obj: ../ssh2connection-server.c ../putty.h \ ../ssh.h ../sshbpp.h ../sshppl.h ../sshchan.h ../sshcr.h \ ../ssh2connection.h ../sshserver.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)ssh2kex-client.obj: ../ssh2kex-client.c ../putty.h ../ssh.h \ ../sshbpp.h ../sshppl.h ../sshcr.h ../storage.h \ ../ssh2transport.h ../mpint.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../puttymem.h ../tree234.h ../sshttymodes.h ../sshgssc.h \ ../sshgss.h ../windows/winstuff.h ../unix/unix.h \ ../pgssapi.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)ssh2kex-server.obj: ../ssh2kex-server.c ../putty.h ../ssh.h \ ../sshbpp.h ../sshppl.h ../sshcr.h ../sshserver.h \ ../sshkeygen.h ../storage.h ../ssh2transport.h ../mpint.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../sshgssc.h ../sshgss.h ../windows/winstuff.h \ ../unix/unix.h ../pgssapi.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)ssh2transhk.obj: ../ssh2transhk.c ../putty.h ../ssh.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)ssh2transport.obj: ../ssh2transport.c ../putty.h ../ssh.h \ ../sshbpp.h ../sshppl.h ../sshcr.h ../sshserver.h \ ../storage.h ../ssh2transport.h ../mpint.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../sshgssc.h ../sshgss.h ../windows/winstuff.h \ ../unix/unix.h ../pgssapi.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)ssh2userauth.obj: ../ssh2userauth.c ../putty.h ../ssh.h \ ../sshbpp.h ../sshppl.h ../sshcr.h ../sshgssc.h ../sshgss.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../pgssapi.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)ssh2userauth-server.obj: ../ssh2userauth-server.c ../putty.h \ ../ssh.h ../sshbpp.h ../sshppl.h ../sshcr.h ../sshserver.h \ ../sshgssc.h ../sshgss.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ ../tree234.h ../sshttymodes.h ../pgssapi.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)sshaes.obj: ../sshaes.c ../ssh.h ../mpint_i.h ../puttymem.h \ ../tree234.h ../network.h ../misc.h ../sshttymodes.h \ ../defs.h ../marshal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)ssharcf.obj: ../ssharcf.c ../ssh.h ../puttymem.h ../tree234.h \ ../network.h ../misc.h ../sshttymodes.h ../defs.h \ ../marshal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)sshargon2.obj: ../sshargon2.c ../putty.h ../ssh.h ../marshal.h \ ../defs.h ../puttyps.h ../network.h ../misc.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)sshauxcrypt.obj: ../sshauxcrypt.c ../ssh.h ../puttymem.h \ ../tree234.h ../network.h ../misc.h ../sshttymodes.h \ ../defs.h ../marshal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)sshbcrypt.obj: ../sshbcrypt.c ../ssh.h ../sshblowf.h \ ../puttymem.h ../tree234.h ../network.h ../misc.h \ ../sshttymodes.h ../defs.h ../marshal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)sshblake2.obj: ../sshblake2.c ../ssh.h ../puttymem.h ../tree234.h \ ../network.h ../misc.h ../sshttymodes.h ../defs.h \ ../marshal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)sshblowf.obj: ../sshblowf.c ../ssh.h ../sshblowf.h ../puttymem.h \ ../tree234.h ../network.h ../misc.h ../sshttymodes.h \ ../defs.h ../marshal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)sshccp.obj: ../sshccp.c ../ssh.h ../mpint_i.h ../puttymem.h \ ../tree234.h ../network.h ../misc.h ../sshttymodes.h \ ../defs.h ../marshal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)sshcommon.obj: ../sshcommon.c ../putty.h ../mpint.h ../ssh.h \ ../sshbpp.h ../sshppl.h ../sshchan.h ../sshttymodes.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)sshcrc.obj: ../sshcrc.c ../ssh.h ../puttymem.h ../tree234.h \ ../network.h ../misc.h ../sshttymodes.h ../defs.h \ ../marshal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)sshcrcda.obj: ../sshcrcda.c ../misc.h ../ssh.h ../defs.h \ ../puttymem.h ../marshal.h ../tree234.h ../network.h \ ../sshttymodes.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)sshdes.obj: ../sshdes.c ../ssh.h ../mpint_i.h ../puttymem.h \ ../tree234.h ../network.h ../misc.h ../sshttymodes.h \ ../defs.h ../marshal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)sshdh.obj: ../sshdh.c ../ssh.h ../misc.h ../mpint.h ../puttymem.h \ ../tree234.h ../network.h ../sshttymodes.h ../defs.h \ ../marshal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)sshdss.obj: ../sshdss.c ../ssh.h ../mpint.h ../misc.h \ ../puttymem.h ../tree234.h ../network.h ../sshttymodes.h \ ../defs.h ../marshal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)sshdssg.obj: ../sshdssg.c ../misc.h ../ssh.h ../sshkeygen.h \ ../mpint.h ../defs.h ../puttymem.h ../marshal.h ../tree234.h \ ../network.h ../sshttymodes.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)sshecc.obj: ../sshecc.c ../ssh.h ../mpint.h ../ecc.h \ ../puttymem.h ../tree234.h ../network.h ../misc.h \ ../sshttymodes.h ../defs.h ../marshal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)sshecdsag.obj: ../sshecdsag.c ../ssh.h ../sshkeygen.h ../mpint.h \ ../puttymem.h ../tree234.h ../network.h ../misc.h \ ../sshttymodes.h ../defs.h ../marshal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)sshgssc.obj: ../sshgssc.c ../putty.h ../sshgssc.h ../misc.h \ ../defs.h ../puttyps.h ../network.h ../marshal.h \ ../sshsignals.h ../pgssapi.h ../sshgss.h ../puttymem.h \ ../windows/winstuff.h ../unix/unix.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)sshhmac.obj: ../sshhmac.c ../ssh.h ../puttymem.h ../tree234.h \ ../network.h ../misc.h ../sshttymodes.h ../defs.h \ ../marshal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)sshmac.obj: ../sshmac.c ../ssh.h ../puttymem.h ../tree234.h \ ../network.h ../misc.h ../sshttymodes.h ../defs.h \ ../marshal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)sshmd5.obj: ../sshmd5.c ../ssh.h ../puttymem.h ../tree234.h \ ../network.h ../misc.h ../sshttymodes.h ../defs.h \ ../marshal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)sshprime.obj: ../sshprime.c ../ssh.h ../mpint.h ../mpunsafe.h \ ../sshkeygen.h ../puttymem.h ../tree234.h ../network.h \ ../misc.h ../sshttymodes.h ../defs.h ../marshal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)sshprng.obj: ../sshprng.c ../putty.h ../ssh.h ../mpint_i.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)sshpubk.obj: ../sshpubk.c ../putty.h ../mpint.h ../ssh.h \ ../misc.h ../defs.h ../puttyps.h ../network.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)sshrand.obj: ../sshrand.c ../putty.h ../ssh.h ../storage.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)sshrsa.obj: ../sshrsa.c ../ssh.h ../mpint.h ../misc.h \ ../puttymem.h ../tree234.h ../network.h ../sshttymodes.h \ ../defs.h ../marshal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)sshrsag.obj: ../sshrsag.c ../ssh.h ../sshkeygen.h ../mpint.h \ ../puttymem.h ../tree234.h ../network.h ../misc.h \ ../sshttymodes.h ../defs.h ../marshal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)sshserver.obj: ../sshserver.c ../putty.h ../ssh.h ../sshbpp.h \ ../sshppl.h ../sshchan.h ../sshserver.h ../sshgssc.h \ ../sshgss.h ../defs.h ../puttyps.h ../network.h ../misc.h \ ../marshal.h ../sshsignals.h ../puttymem.h ../tree234.h \ ../sshttymodes.h ../pgssapi.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)sshsh256.obj: ../sshsh256.c ../ssh.h ../puttymem.h ../tree234.h \ ../network.h ../misc.h ../sshttymodes.h ../defs.h \ ../marshal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)sshsh512.obj: ../sshsh512.c ../ssh.h ../puttymem.h ../tree234.h \ ../network.h ../misc.h ../sshttymodes.h ../defs.h \ ../marshal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)sshsha.obj: ../sshsha.c ../ssh.h ../puttymem.h ../tree234.h \ ../network.h ../misc.h ../sshttymodes.h ../defs.h \ ../marshal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)sshsha3.obj: ../sshsha3.c ../ssh.h ../puttymem.h ../tree234.h \ ../network.h ../misc.h ../sshttymodes.h ../defs.h \ ../marshal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)sshshare.obj: ../sshshare.c ../putty.h ../tree234.h ../ssh.h \ ../sshcr.h ../defs.h ../puttyps.h ../network.h ../misc.h \ ../marshal.h ../sshsignals.h ../puttymem.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)sshutils.obj: ../sshutils.c ../putty.h ../ssh.h ../sshchan.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)sshverstring.obj: ../sshverstring.c ../putty.h ../ssh.h \ ../sshbpp.h ../sshcr.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ ../tree234.h ../sshttymodes.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)sshzlib.obj: ../sshzlib.c ../defs.h ../ssh.h ../puttymem.h \ ../tree234.h ../network.h ../misc.h ../sshttymodes.h \ ../marshal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)stripctrl.obj: ../stripctrl.c ../putty.h ../terminal.h ../misc.h \ ../marshal.h ../defs.h ../puttyps.h ../network.h \ ../sshsignals.h ../tree234.h ../puttymem.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)supdup.obj: ../supdup.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)telnet.obj: ../telnet.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)terminal.obj: ../terminal.c ../putty.h ../terminal.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../tree234.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)testcrypt.obj: ../testcrypt.c ../defs.h ../ssh.h ../sshkeygen.h \ ../misc.h ../mpint.h ../ecc.h ../testcrypt.h ../puttymem.h \ ../tree234.h ../network.h ../sshttymodes.h ../marshal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)testsc.obj: ../testsc.c ../defs.h ../putty.h ../ssh.h ../misc.h \ ../mpint.h ../ecc.h ../puttyps.h ../network.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)testzlib.obj: ../testzlib.c ../defs.h ../ssh.h ../puttymem.h \ ../tree234.h ../network.h ../misc.h ../sshttymodes.h \ ../marshal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)time.obj: ../time.c $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)timing.obj: ../timing.c ../putty.h ../tree234.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)toucs.obj: ../charset/toucs.c ../charset/charset.h \ ../charset/internal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)tree234.obj: ../tree234.c ../defs.h ../tree234.h ../puttymem.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)utf8.obj: ../charset/utf8.c ../charset/charset.h \ ../charset/internal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)utils.obj: ../utils.c ../defs.h ../misc.h ../ssh.h ../puttymem.h \ ../marshal.h ../tree234.h ../network.h ../sshttymodes.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)ux_x11.obj: ../unix/ux_x11.c ../putty.h ../ssh.h ../network.h \ ../defs.h ../puttyps.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)uxagentc.obj: ../unix/uxagentc.c ../putty.h ../misc.h \ ../tree234.h ../puttymem.h ../defs.h ../puttyps.h \ ../network.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)uxagentsock.obj: ../unix/uxagentsock.c ../putty.h ../ssh.h \ ../misc.h ../pageant.h ../defs.h ../puttyps.h ../network.h \ ../marshal.h ../sshsignals.h ../puttymem.h ../tree234.h \ ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)uxcfg.obj: ../unix/uxcfg.c ../putty.h ../dialog.h ../storage.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)uxcliloop.obj: ../unix/uxcliloop.c ../putty.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)uxcons.obj: ../unix/uxcons.c ../putty.h ../storage.h ../ssh.h \ ../console.h ../defs.h ../puttyps.h ../network.h ../misc.h \ ../marshal.h ../sshsignals.h ../puttymem.h ../tree234.h \ ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)uxfdsock.obj: ../unix/uxfdsock.c ../tree234.h ../putty.h \ ../network.h ../defs.h ../puttyps.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)uxgen.obj: ../unix/uxgen.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)uxgss.obj: ../unix/uxgss.c ../putty.h ../pgssapi.h ../sshgss.h \ ../sshgssc.h ../defs.h ../puttyps.h ../network.h ../misc.h \ ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)uxmisc.obj: ../unix/uxmisc.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)uxnet.obj: ../unix/uxnet.c ../putty.h ../network.h ../tree234.h \ ../defs.h ../puttyps.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)uxnogtk.obj: ../unix/uxnogtk.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)uxnoise.obj: ../unix/uxnoise.c ../putty.h ../ssh.h ../storage.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)uxpeer.obj: ../unix/uxpeer.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)uxpgnt.obj: ../unix/uxpgnt.c ../putty.h ../ssh.h ../misc.h \ ../pageant.h ../defs.h ../puttyps.h ../network.h \ ../marshal.h ../sshsignals.h ../puttymem.h ../tree234.h \ ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)uxplink.obj: ../unix/uxplink.c ../putty.h ../ssh.h ../storage.h \ ../tree234.h ../defs.h ../puttyps.h ../network.h ../misc.h \ ../marshal.h ../sshsignals.h ../puttymem.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)uxpoll.obj: ../unix/uxpoll.c ../putty.h ../tree234.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)uxprint.obj: ../unix/uxprint.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)uxproxy.obj: ../unix/uxproxy.c ../tree234.h ../putty.h \ ../network.h ../proxy.h ../defs.h ../puttyps.h ../misc.h \ ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)uxpsusan.obj: ../unix/uxpsusan.c ../putty.h ../mpint.h ../ssh.h \ ../sshserver.h ../defs.h ../puttyps.h ../network.h ../misc.h \ ../marshal.h ../sshsignals.h ../puttymem.h ../tree234.h \ ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)uxpterm.obj: ../unix/uxpterm.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)uxpty.obj: ../unix/uxpty.c ../putty.h ../ssh.h ../sshserver.h \ ../tree234.h ../sshttymodes.h ../sshsignals.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../puttymem.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)uxputty.obj: ../unix/uxputty.c ../putty.h ../ssh.h ../storage.h \ ../unix/gtkcompat.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ ../tree234.h ../sshttymodes.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)uxsel.obj: ../unix/uxsel.c ../putty.h ../tree234.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)uxser.obj: ../unix/uxser.c ../putty.h ../tree234.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)uxserver.obj: ../unix/uxserver.c ../putty.h ../mpint.h ../ssh.h \ ../sshserver.h ../defs.h ../puttyps.h ../network.h ../misc.h \ ../marshal.h ../sshsignals.h ../puttymem.h ../tree234.h \ ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)uxsftp.obj: ../unix/uxsftp.c ../putty.h ../ssh.h ../psftp.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)uxsftpserver.obj: ../unix/uxsftpserver.c ../putty.h ../ssh.h \ ../sshserver.h ../sftp.h ../tree234.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../puttymem.h ../sshttymodes.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)uxshare.obj: ../unix/uxshare.c ../tree234.h ../putty.h \ ../network.h ../proxy.h ../ssh.h ../defs.h ../puttyps.h \ ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)uxsignal.obj: ../unix/uxsignal.c ../defs.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)uxsocks.obj: ../unix/uxsocks.c ../putty.h ../ssh.h ../psocks.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)uxstore.obj: ../unix/uxstore.c ../putty.h ../storage.h \ ../tree234.h ../defs.h ../puttyps.h ../network.h ../misc.h \ ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)uxucs.obj: ../unix/uxucs.c ../putty.h ../charset/charset.h \ ../terminal.h ../misc.h ../defs.h ../puttyps.h ../network.h \ ../marshal.h ../sshsignals.h ../tree234.h ../puttymem.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)uxutils.obj: ../unix/uxutils.c ../putty.h ../ssh.h \ ../unix/uxutils.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ ../tree234.h ../sshttymodes.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)version.obj: ../version.c ../putty.h ../ssh.h ../empty.h \ ../version.h ../defs.h ../puttyps.h ../network.h ../misc.h \ ../marshal.h ../sshsignals.h ../puttymem.h ../tree234.h \ ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)wcwidth.obj: ../wcwidth.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)wildcard.obj: ../wildcard.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)wincapi.obj: ../windows/wincapi.c ../putty.h ../ssh.h \ ../windows/wincapi.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ ../tree234.h ../sshttymodes.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)wincfg.obj: ../windows/wincfg.c ../putty.h ../dialog.h \ ../storage.h ../defs.h ../puttyps.h ../network.h ../misc.h \ ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)wincliloop.obj: ../windows/wincliloop.c ../putty.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)wincons.obj: ../windows/wincons.c ../putty.h ../storage.h \ ../ssh.h ../console.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ ../tree234.h ../sshttymodes.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)winctrls.obj: ../windows/winctrls.c ../putty.h ../misc.h \ ../dialog.h ../defs.h ../puttyps.h ../network.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../windows/winstuff.h \ ../unix/unix.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)windefs.obj: ../windows/windefs.c ../putty.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)windlg.obj: ../windows/windlg.c ../putty.h ../ssh.h \ ../windows/win_res.h ../windows/winseat.h ../storage.h \ ../dialog.h ../licence.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ ../tree234.h ../sshttymodes.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)window.obj: ../windows/window.c ../putty.h ../terminal.h \ ../storage.h ../windows/win_res.h ../windows/winsecur.h \ ../windows/winseat.h ../tree234.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)wingss.obj: ../windows/wingss.c ../putty.h ../pgssapi.h \ ../sshgss.h ../sshgssc.h ../misc.h ../defs.h ../puttyps.h \ ../network.h ../marshal.h ../sshsignals.h ../puttymem.h \ ../windows/winstuff.h ../unix/unix.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)winhandl.obj: ../windows/winhandl.c ../putty.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)winhelp.obj: ../windows/winhelp.c ../putty.h ../windows/win_res.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)winhsock.obj: ../windows/winhsock.c ../tree234.h ../putty.h \ ../network.h ../defs.h ../puttyps.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)winjump.obj: ../windows/winjump.c ../putty.h ../storage.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)winmisc.obj: ../windows/winmisc.c ../putty.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)winmiscs.obj: ../windows/winmiscs.c ../putty.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)winnet.obj: ../windows/winnet.c ../putty.h ../network.h \ ../tree234.h ../ssh.h ../defs.h ../puttyps.h ../misc.h \ ../marshal.h ../sshsignals.h ../puttymem.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)winnohlp.obj: ../windows/winnohlp.c ../putty.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)winnoise.obj: ../windows/winnoise.c ../putty.h ../ssh.h \ ../storage.h ../defs.h ../puttyps.h ../network.h ../misc.h \ ../marshal.h ../sshsignals.h ../puttymem.h ../tree234.h \ ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)winnojmp.obj: ../windows/winnojmp.c $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)winnpc.obj: ../windows/winnpc.c ../tree234.h ../putty.h \ ../network.h ../proxy.h ../ssh.h ../windows/winsecur.h \ ../defs.h ../puttyps.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)winnps.obj: ../windows/winnps.c ../tree234.h ../putty.h \ ../network.h ../proxy.h ../ssh.h ../windows/winsecur.h \ ../defs.h ../puttyps.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)winpgen.obj: ../windows/winpgen.c ../putty.h ../ssh.h \ ../sshkeygen.h ../licence.h ../windows/winsecur.h \ ../windows/puttygen-rc.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ ../tree234.h ../sshttymodes.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)winpgnt.obj: ../windows/winpgnt.c ../putty.h ../ssh.h ../misc.h \ ../tree234.h ../windows/winsecur.h ../windows/wincapi.h \ ../pageant.h ../licence.h ../windows/pageant-rc.h ../defs.h \ ../puttyps.h ../network.h ../marshal.h ../sshsignals.h \ ../puttymem.h ../sshttymodes.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)winpgntc.obj: ../windows/winpgntc.c ../putty.h ../pageant.h \ ../windows/winsecur.h ../windows/wincapi.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)winplink.obj: ../windows/winplink.c ../putty.h ../storage.h \ ../tree234.h ../windows/winsecur.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)winprint.obj: ../windows/winprint.c ../putty.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)winproxy.obj: ../windows/winproxy.c ../tree234.h ../putty.h \ ../network.h ../proxy.h ../defs.h ../puttyps.h ../misc.h \ ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)winsecur.obj: ../windows/winsecur.c ../putty.h \ ../windows/winsecur.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)winselcli.obj: ../windows/winselcli.c ../putty.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)winselgui.obj: ../windows/winselgui.c ../putty.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)winser.obj: ../windows/winser.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)winsftp.obj: ../windows/winsftp.c ../putty.h ../psftp.h ../ssh.h \ ../windows/winsecur.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ ../tree234.h ../sshttymodes.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)winshare.obj: ../windows/winshare.c ../tree234.h ../putty.h \ ../network.h ../proxy.h ../ssh.h ../windows/wincapi.h \ ../windows/winsecur.h ../noshare.c ../defs.h ../puttyps.h \ ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)winsocks.obj: ../windows/winsocks.c ../putty.h ../ssh.h \ ../psocks.h ../defs.h ../puttyps.h ../network.h ../misc.h \ ../marshal.h ../sshsignals.h ../puttymem.h ../tree234.h \ ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)winstore.obj: ../windows/winstore.c ../putty.h ../storage.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)wintime.obj: ../windows/wintime.c ../putty.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)winucs.obj: ../windows/winucs.c ../putty.h ../terminal.h \ ../misc.h ../defs.h ../puttyps.h ../network.h ../marshal.h \ ../sshsignals.h ../tree234.h ../puttymem.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)winutils.obj: ../windows/winutils.c ../putty.h ../misc.h \ ../defs.h ../puttyps.h ../network.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../windows/winstuff.h \ ../unix/unix.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)winx11.obj: ../windows/winx11.c ../putty.h ../ssh.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)x11fwd.obj: ../x11fwd.c ../putty.h ../ssh.h ../sshchan.h \ ../tree234.h ../defs.h ../puttyps.h ../network.h ../misc.h \ ../marshal.h ../sshsignals.h ../puttymem.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)x11misc.obj: ../unix/x11misc.c ../putty.h ../unix/x11misc.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)xenc.obj: ../charset/xenc.c ../charset/charset.h \ ../charset/internal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)xkeysym.obj: ../unix/xkeysym.c ../misc.h ../defs.h ../puttymem.h \ ../marshal.h $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)xpmptcfg.obj: ../unix/xpmptcfg.c $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)xpmpterm.obj: ../unix/xpmpterm.c $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)xpmpucfg.obj: ../unix/xpmpucfg.c $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)xpmputty.obj: ../unix/xpmputty.c $(CC) /Fo$(BUILDDIR) $(COMPAT) $(CFLAGS) $(XFLAGS) /c $< $(BUILDDIR)pageant.rcpp: ../windows/pageant.rc ../windows/winhelp.rc2 \ ../windows/version.rc2 $(RCPREPROC) $(RCPPFLAGS) /Fi$@ $< $(BUILDDIR)plink.rcpp: ../windows/plink.rc ../windows/version.rc2 $(RCPREPROC) $(RCPPFLAGS) /Fi$@ $< $(BUILDDIR)pscp.rcpp: ../windows/pscp.rc ../windows/version.rc2 $(RCPREPROC) $(RCPPFLAGS) /Fi$@ $< $(BUILDDIR)psftp.rcpp: ../windows/psftp.rc ../windows/version.rc2 $(RCPREPROC) $(RCPPFLAGS) /Fi$@ $< $(BUILDDIR)putty.rcpp: ../windows/putty.rc ../windows/winhelp.rc2 \ ../windows/win_res.rc2 ../windows/version.rc2 $(RCPREPROC) $(RCPPFLAGS) /Fi$@ $< $(BUILDDIR)puttygen.rcpp: ../windows/puttygen.rc ../windows/winhelp.rc2 \ ../windows/version.rc2 $(RCPREPROC) $(RCPPFLAGS) /Fi$@ $< $(BUILDDIR)puttytel.rcpp: ../windows/puttytel.rc ../windows/winhelp.rc2 \ ../windows/win_res.rc2 ../windows/version.rc2 $(RCPREPROC) $(RCPPFLAGS) /Fi$@ $< cleantestprogs: -rm -f $(BUILDDIR)testcrypt.exe $(BUILDDIR)psocks.exe clean: rm -f $(BUILDDIR)*.obj $(BUILDDIR)*.exe $(BUILDDIR)*.rcpp $(BUILDDIR)*.res \ $(BUILDDIR)*.map $(BUILDDIR)*.exe.manifest putty-0.76/windows/Makefile.mgw0000644000175000017500000025665314072266315013526 00000000000000# Makefile for putty under MinGW, Cygwin, or Winelib. # # This file was created by `mkfiles.pl' from the `Recipe' file. # DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead. # # Extra options you can set: # # - COMPAT=-DAUTO_WINSOCK (Windows only) # Causes PuTTY to assume that includes its own WinSock # header file, so that it won't try to include . # # - COMPAT=-DWINSOCK_TWO (Windows only) # Causes the PuTTY utilities to include instead of # , except Plink which _needs_ WinSock 2 so it already # does this. # # - COMPAT=-DNO_SECURITY (Windows only) # Disables use of , which is not available with some # development environments (such as very old versions of the # mingw/Cygwin GNU toolchain). This has the following effects: # - Pageant won't care about the local user ID of processes # accessing it; a version of Pageant built with this option # will therefore refuse to run under NT-series OSes on # security grounds (although it will run fine on Win95-series # OSes where there is no access control anyway). # - SSH connection sharing is disabled. # - There is no support for restriction of the process ACLs. # # - COMPAT=-DNO_MULTIMON (Windows only) # Disables PuTTY's use of , which is not available # with some development environments. This means that PuTTY's # full-screen mode (configurable to work on Alt-Enter) will # not behave usefully in a multi-monitor environment. # # - COMPAT=-DNO_HTMLHELP (Windows only) # Disables PuTTY's use of , which is not available # with some development environments. # # If you don't have this header, you may be able to use the copy # supplied with HTML Help Workshop. # # - RCFL=-DNO_MANIFESTS (Windows only) # Disables inclusion of XML application manifests in the PuTTY # binaries. This may be necessary to build for 64-bit Windows; # the manifests are only included to use the XP GUI style on # Windows XP, and the architecture tags are a lie on 64-bit. # # - COMPAT=-DNO_IPV6 # Disables PuTTY's ability to make IPv6 connections, enabling # it to compile under development environments which do not # support IPv6 in their header files. # # - COMPAT=-DNO_GSSAPI # Disables PuTTY's ability to use GSSAPI functions for # authentication and key exchange. # # - COMPAT=-DSTATIC_GSSAPI # Causes PuTTY to try to link statically against the GSSAPI # library instead of the default of doing it at run time. # # - COMPAT=-DMSVC4 (Windows only) # - RCFL=-DMSVC4 # Makes a couple of minor changes so that PuTTY compiles using # MSVC 4. You will also need -DNO_SECURITY and -DNO_MULTIMON. # # - COMPAT=-DNO_SECUREZEROMEMORY (Windows only) # Disables PuTTY's use of SecureZeroMemory(), which is missing # from some environments' header files. # # - XFLAGS=-DDEBUG # Causes PuTTY to enable internal debugging. # # - XFLAGS=-DMALLOC_LOG # Causes PuTTY to emit a file called putty_mem.log, logging every # memory allocation and free, so you can track memory leaks. # # - XFLAGS=-DMINEFIELD (Windows only) # Causes PuTTY to use a custom memory allocator, similar in # concept to Electric Fence, in place of regular malloc(). Wastes # huge amounts of RAM, but should cause heap-corruption bugs to # show up as GPFs at the point of failure rather than appearing # later on as second-level damage. # # - XFLAGS=-DFUZZING # Builds a version of PuTTY with some tweaks to make fuzz testing # easier: the SSH random number generator is replaced by one that # always returns the same thing. Note that this makes SSH # completely insecure -- a FUZZING build should never be used to # connect to a real server. # You can define this path to point at your tools if you need to # TOOLPATH = c:\cygwin\bin\ # or similar, if you're running Windows # TOOLPATH = /pkg/mingw32msvc/i386-mingw32msvc/bin/ # TOOLPATH = i686-w64-mingw32- CC = $(TOOLPATH)gcc RC = $(TOOLPATH)windres # Uncomment the following two lines to compile under Winelib # CC = winegcc # RC = wrc # You may also need to tell windres where to find include files: # RCINC = --include-dir c:\cygwin\include\ CFLAGS = -Wall -O2 -std=gnu99 -Wvla -D_WINDOWS -DWIN32S_COMPAT \ -D_NO_OLDNAMES -D__USE_MINGW_ANSI_STDIO=1 -I.././ \ -I../charset/ -I../windows/ -I../unix/ LDFLAGS = -s RCFLAGS = $(RCINC) --define WIN32=1 --define _WIN32=1 --define WINVER=0x0400 \ -I.././ -I../charset/ -I../windows/ -I../unix/ # _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 .SUFFIXES: all: pageant.exe plink.exe pscp.exe psftp.exe psocks.exe putty.exe \ puttygen.exe puttytel.exe testcrypt.exe pageant.exe: aqsync.o be_misc.o callback.o conf.o ecc.o errsock.o marshal.o \ memory.o misc.o mpint.o pageant.o pageant.res.o sshaes.o \ sshargon2.o sshauxcrypt.o sshblake2.o sshdes.o sshdss.o \ sshecc.o sshhmac.o sshmd5.o sshpubk.o sshrsa.o sshsh256.o \ sshsh512.o sshsha.o sshsha3.o stripctrl.o tree234.o utils.o \ version.o wcwidth.o wincapi.o winhandl.o winhelp.o \ winhsock.o winmisc.o winmiscs.o winnet.o winnpc.o winnps.o \ winpgnt.o winpgntc.o winsecur.o winselgui.o winutils.o $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,pageant.map aqsync.o \ be_misc.o callback.o conf.o ecc.o errsock.o marshal.o \ memory.o misc.o mpint.o pageant.o pageant.res.o sshaes.o \ sshargon2.o sshauxcrypt.o sshblake2.o sshdes.o sshdss.o \ sshecc.o sshhmac.o sshmd5.o sshpubk.o sshrsa.o sshsh256.o \ sshsh512.o sshsha.o sshsha3.o stripctrl.o tree234.o utils.o \ version.o wcwidth.o wincapi.o winhandl.o winhelp.o \ winhsock.o winmisc.o winmiscs.o winnet.o winnpc.o winnps.o \ winpgnt.o winpgntc.o winsecur.o winselgui.o winutils.o \ -ladvapi32 -lcomdlg32 -lgdi32 -limm32 -lole32 -lshell32 \ -luser32 plink.exe: agentf.o aqsync.o be_all_s.o be_misc.o callback.o clicons.o \ cmdline.o conf.o console.o cproxy.o ecc.o errsock.o ldisc.o \ logging.o mainchan.o marshal.o memory.o misc.o miscucs.o \ mpint.o noterm.o nullplug.o pgssapi.o pinger.o plink.res.o \ portfwd.o proxy.o raw.o rlogin.o sessprep.o settings.o ssh.o \ ssh1bpp.o ssh1censor.o ssh1connection.o \ ssh1connection-client.o ssh1login.o ssh2bpp.o ssh2bpp-bare.o \ ssh2censor.o ssh2connection.o ssh2connection-client.o \ ssh2kex-client.o ssh2transhk.o ssh2transport.o \ ssh2userauth.o sshaes.o ssharcf.o sshargon2.o sshauxcrypt.o \ sshblake2.o sshblowf.o sshccp.o sshcommon.o sshcrc.o \ sshcrcda.o sshdes.o sshdh.o sshdss.o sshecc.o sshgssc.o \ sshhmac.o sshmac.o sshmd5.o sshprng.o sshpubk.o sshrand.o \ sshrsa.o sshsh256.o sshsh512.o sshsha.o sshsha3.o sshshare.o \ sshutils.o sshverstring.o sshzlib.o stripctrl.o supdup.o \ telnet.o timing.o tree234.o utils.o version.o wcwidth.o \ wildcard.o wincapi.o wincliloop.o wincons.o windefs.o \ wingss.o winhandl.o winhsock.o winmisc.o winmiscs.o winnet.o \ winnohlp.o winnoise.o winnojmp.o winnpc.o winnps.o \ winpgntc.o winplink.o winproxy.o winsecur.o winselcli.o \ winser.o winshare.o winstore.o wintime.o winucs.o winx11.o \ x11fwd.o $(CC) $(LDFLAGS) -o $@ -Wl,-Map,plink.map agentf.o aqsync.o \ be_all_s.o be_misc.o callback.o clicons.o cmdline.o conf.o \ console.o cproxy.o ecc.o errsock.o ldisc.o logging.o \ mainchan.o marshal.o memory.o misc.o miscucs.o mpint.o \ noterm.o nullplug.o pgssapi.o pinger.o plink.res.o portfwd.o \ proxy.o raw.o rlogin.o sessprep.o settings.o ssh.o ssh1bpp.o \ ssh1censor.o ssh1connection.o ssh1connection-client.o \ ssh1login.o ssh2bpp.o ssh2bpp-bare.o ssh2censor.o \ ssh2connection.o ssh2connection-client.o ssh2kex-client.o \ ssh2transhk.o ssh2transport.o ssh2userauth.o sshaes.o \ ssharcf.o sshargon2.o sshauxcrypt.o sshblake2.o sshblowf.o \ sshccp.o sshcommon.o sshcrc.o sshcrcda.o sshdes.o sshdh.o \ sshdss.o sshecc.o sshgssc.o sshhmac.o sshmac.o sshmd5.o \ sshprng.o sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o \ sshsha.o sshsha3.o sshshare.o sshutils.o sshverstring.o \ sshzlib.o stripctrl.o supdup.o telnet.o timing.o tree234.o \ utils.o version.o wcwidth.o wildcard.o wincapi.o \ wincliloop.o wincons.o windefs.o wingss.o winhandl.o \ winhsock.o winmisc.o winmiscs.o winnet.o winnohlp.o \ winnoise.o winnojmp.o winnpc.o winnps.o winpgntc.o \ winplink.o winproxy.o winsecur.o winselcli.o winser.o \ winshare.o winstore.o wintime.o winucs.o winx11.o x11fwd.o \ -ladvapi32 -lcomdlg32 -lgdi32 -limm32 -lole32 -lshell32 \ -luser32 pscp.exe: agentf.o aqsync.o be_misc.o be_ssh.o callback.o clicons.o \ cmdline.o conf.o console.o cproxy.o ecc.o errsock.o \ logging.o mainchan.o marshal.o memory.o misc.o miscucs.o \ mpint.o nullplug.o pgssapi.o pinger.o portfwd.o proxy.o \ pscp.o pscp.res.o psftpcommon.o settings.o sftp.o \ sftpcommon.o ssh.o ssh1bpp.o ssh1censor.o ssh1connection.o \ ssh1connection-client.o ssh1login.o ssh2bpp.o ssh2bpp-bare.o \ ssh2censor.o ssh2connection.o ssh2connection-client.o \ ssh2kex-client.o ssh2transhk.o ssh2transport.o \ ssh2userauth.o sshaes.o ssharcf.o sshargon2.o sshauxcrypt.o \ sshblake2.o sshblowf.o sshccp.o sshcommon.o sshcrc.o \ sshcrcda.o sshdes.o sshdh.o sshdss.o sshecc.o sshgssc.o \ sshhmac.o sshmac.o sshmd5.o sshprng.o sshpubk.o sshrand.o \ sshrsa.o sshsh256.o sshsh512.o sshsha.o sshsha3.o sshshare.o \ sshutils.o sshverstring.o sshzlib.o stripctrl.o timing.o \ tree234.o utils.o version.o wcwidth.o wildcard.o wincapi.o \ wincliloop.o wincons.o windefs.o wingss.o winhandl.o \ winhsock.o winmisc.o winmiscs.o winnet.o winnohlp.o \ winnoise.o winnojmp.o winnpc.o winnps.o winpgntc.o \ winproxy.o winsecur.o winselcli.o winsftp.o winshare.o \ winstore.o wintime.o winucs.o x11fwd.o $(CC) $(LDFLAGS) -o $@ -Wl,-Map,pscp.map agentf.o aqsync.o be_misc.o \ be_ssh.o callback.o clicons.o cmdline.o conf.o console.o \ cproxy.o ecc.o errsock.o logging.o mainchan.o marshal.o \ memory.o misc.o miscucs.o mpint.o nullplug.o pgssapi.o \ pinger.o portfwd.o proxy.o pscp.o pscp.res.o psftpcommon.o \ settings.o sftp.o sftpcommon.o ssh.o ssh1bpp.o ssh1censor.o \ ssh1connection.o ssh1connection-client.o ssh1login.o \ ssh2bpp.o ssh2bpp-bare.o ssh2censor.o ssh2connection.o \ ssh2connection-client.o ssh2kex-client.o ssh2transhk.o \ ssh2transport.o ssh2userauth.o sshaes.o ssharcf.o \ sshargon2.o sshauxcrypt.o sshblake2.o sshblowf.o sshccp.o \ sshcommon.o sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o \ sshecc.o sshgssc.o sshhmac.o sshmac.o sshmd5.o sshprng.o \ sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o sshsha.o \ sshsha3.o sshshare.o sshutils.o sshverstring.o sshzlib.o \ stripctrl.o timing.o tree234.o utils.o version.o wcwidth.o \ wildcard.o wincapi.o wincliloop.o wincons.o windefs.o \ wingss.o winhandl.o winhsock.o winmisc.o winmiscs.o winnet.o \ winnohlp.o winnoise.o winnojmp.o winnpc.o winnps.o \ winpgntc.o winproxy.o winsecur.o winselcli.o winsftp.o \ winshare.o winstore.o wintime.o winucs.o x11fwd.o -ladvapi32 \ -lcomdlg32 -lgdi32 -limm32 -lole32 -lshell32 -luser32 psftp.exe: agentf.o aqsync.o be_misc.o be_ssh.o callback.o clicons.o \ cmdline.o conf.o console.o cproxy.o ecc.o errsock.o \ logging.o mainchan.o marshal.o memory.o misc.o miscucs.o \ mpint.o nullplug.o pgssapi.o pinger.o portfwd.o proxy.o \ psftp.o psftp.res.o psftpcommon.o settings.o sftp.o \ sftpcommon.o ssh.o ssh1bpp.o ssh1censor.o ssh1connection.o \ ssh1connection-client.o ssh1login.o ssh2bpp.o ssh2bpp-bare.o \ ssh2censor.o ssh2connection.o ssh2connection-client.o \ ssh2kex-client.o ssh2transhk.o ssh2transport.o \ ssh2userauth.o sshaes.o ssharcf.o sshargon2.o sshauxcrypt.o \ sshblake2.o sshblowf.o sshccp.o sshcommon.o sshcrc.o \ sshcrcda.o sshdes.o sshdh.o sshdss.o sshecc.o sshgssc.o \ sshhmac.o sshmac.o sshmd5.o sshprng.o sshpubk.o sshrand.o \ sshrsa.o sshsh256.o sshsh512.o sshsha.o sshsha3.o sshshare.o \ sshutils.o sshverstring.o sshzlib.o stripctrl.o timing.o \ tree234.o utils.o version.o wcwidth.o wildcard.o wincapi.o \ wincliloop.o wincons.o windefs.o wingss.o winhandl.o \ winhsock.o winmisc.o winmiscs.o winnet.o winnohlp.o \ winnoise.o winnojmp.o winnpc.o winnps.o winpgntc.o \ winproxy.o winsecur.o winselcli.o winsftp.o winshare.o \ winstore.o wintime.o winucs.o x11fwd.o $(CC) $(LDFLAGS) -o $@ -Wl,-Map,psftp.map agentf.o aqsync.o \ be_misc.o be_ssh.o callback.o clicons.o cmdline.o conf.o \ console.o cproxy.o ecc.o errsock.o logging.o mainchan.o \ marshal.o memory.o misc.o miscucs.o mpint.o nullplug.o \ pgssapi.o pinger.o portfwd.o proxy.o psftp.o psftp.res.o \ psftpcommon.o settings.o sftp.o sftpcommon.o ssh.o ssh1bpp.o \ ssh1censor.o ssh1connection.o ssh1connection-client.o \ ssh1login.o ssh2bpp.o ssh2bpp-bare.o ssh2censor.o \ ssh2connection.o ssh2connection-client.o ssh2kex-client.o \ ssh2transhk.o ssh2transport.o ssh2userauth.o sshaes.o \ ssharcf.o sshargon2.o sshauxcrypt.o sshblake2.o sshblowf.o \ sshccp.o sshcommon.o sshcrc.o sshcrcda.o sshdes.o sshdh.o \ sshdss.o sshecc.o sshgssc.o sshhmac.o sshmac.o sshmd5.o \ sshprng.o sshpubk.o sshrand.o sshrsa.o sshsh256.o sshsh512.o \ sshsha.o sshsha3.o sshshare.o sshutils.o sshverstring.o \ sshzlib.o stripctrl.o timing.o tree234.o utils.o version.o \ wcwidth.o wildcard.o wincapi.o wincliloop.o wincons.o \ windefs.o wingss.o winhandl.o winhsock.o winmisc.o \ winmiscs.o winnet.o winnohlp.o winnoise.o winnojmp.o \ winnpc.o winnps.o winpgntc.o winproxy.o winsecur.o \ winselcli.o winsftp.o winshare.o winstore.o wintime.o \ winucs.o x11fwd.o -ladvapi32 -lcomdlg32 -lgdi32 -limm32 \ -lole32 -lshell32 -luser32 psocks.exe: be_misc.o callback.o conf.o console.o errsock.o logging.o \ marshal.o memory.o misc.o nocproxy.o norand.o portfwd.o \ proxy.o psocks.o sshutils.o stripctrl.o time.o timing.o \ tree234.o utils.o version.o wcwidth.o wincliloop.o wincons.o \ winhandl.o winhsock.o winmisc.o winmiscs.o winnet.o \ winnohlp.o winproxy.o winselcli.o winsocks.o $(CC) $(LDFLAGS) -o $@ -Wl,-Map,psocks.map be_misc.o callback.o \ conf.o console.o errsock.o logging.o marshal.o memory.o \ misc.o nocproxy.o norand.o portfwd.o proxy.o psocks.o \ sshutils.o stripctrl.o time.o timing.o tree234.o utils.o \ version.o wcwidth.o wincliloop.o wincons.o winhandl.o \ winhsock.o winmisc.o winmiscs.o winnet.o winnohlp.o \ winproxy.o winselcli.o winsocks.o -ladvapi32 -lcomdlg32 \ -lgdi32 -limm32 -lole32 -lshell32 -luser32 putty.exe: agentf.o aqsync.o be_all_s.o be_misc.o callback.o cmdline.o \ conf.o config.o cproxy.o dialog.o ecc.o errsock.o ldisc.o \ logging.o mainchan.o marshal.o memory.o minibidi.o misc.o \ miscucs.o mpint.o nullplug.o pgssapi.o pinger.o portfwd.o \ proxy.o putty.res.o raw.o rlogin.o sessprep.o settings.o \ sizetip.o ssh.o ssh1bpp.o ssh1censor.o ssh1connection.o \ ssh1connection-client.o ssh1login.o ssh2bpp.o ssh2bpp-bare.o \ ssh2censor.o ssh2connection.o ssh2connection-client.o \ ssh2kex-client.o ssh2transhk.o ssh2transport.o \ ssh2userauth.o sshaes.o ssharcf.o sshargon2.o sshauxcrypt.o \ sshblake2.o sshblowf.o sshccp.o sshcommon.o sshcrc.o \ sshcrcda.o sshdes.o sshdh.o sshdss.o sshecc.o sshgssc.o \ sshhmac.o sshmac.o sshmd5.o sshprng.o sshpubk.o sshrand.o \ sshrsa.o sshsh256.o sshsh512.o sshsha.o sshsha3.o sshshare.o \ sshutils.o sshverstring.o sshzlib.o stripctrl.o supdup.o \ telnet.o terminal.o timing.o tree234.o utils.o version.o \ wcwidth.o wildcard.o wincapi.o wincfg.o winctrls.o windefs.o \ windlg.o window.o wingss.o winhandl.o winhelp.o winhsock.o \ winjump.o winmisc.o winmiscs.o winnet.o winnoise.o winnpc.o \ winnps.o winpgntc.o winprint.o winproxy.o winsecur.o \ winselgui.o winser.o winshare.o winstore.o wintime.o \ winucs.o winutils.o winx11.o x11fwd.o $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,putty.map agentf.o \ aqsync.o be_all_s.o be_misc.o callback.o cmdline.o conf.o \ config.o cproxy.o dialog.o ecc.o errsock.o ldisc.o logging.o \ mainchan.o marshal.o memory.o minibidi.o misc.o miscucs.o \ mpint.o nullplug.o pgssapi.o pinger.o portfwd.o proxy.o \ putty.res.o raw.o rlogin.o sessprep.o settings.o sizetip.o \ ssh.o ssh1bpp.o ssh1censor.o ssh1connection.o \ ssh1connection-client.o ssh1login.o ssh2bpp.o ssh2bpp-bare.o \ ssh2censor.o ssh2connection.o ssh2connection-client.o \ ssh2kex-client.o ssh2transhk.o ssh2transport.o \ ssh2userauth.o sshaes.o ssharcf.o sshargon2.o sshauxcrypt.o \ sshblake2.o sshblowf.o sshccp.o sshcommon.o sshcrc.o \ sshcrcda.o sshdes.o sshdh.o sshdss.o sshecc.o sshgssc.o \ sshhmac.o sshmac.o sshmd5.o sshprng.o sshpubk.o sshrand.o \ sshrsa.o sshsh256.o sshsh512.o sshsha.o sshsha3.o sshshare.o \ sshutils.o sshverstring.o sshzlib.o stripctrl.o supdup.o \ telnet.o terminal.o timing.o tree234.o utils.o version.o \ wcwidth.o wildcard.o wincapi.o wincfg.o winctrls.o windefs.o \ windlg.o window.o wingss.o winhandl.o winhelp.o winhsock.o \ winjump.o winmisc.o winmiscs.o winnet.o winnoise.o winnpc.o \ winnps.o winpgntc.o winprint.o winproxy.o winsecur.o \ winselgui.o winser.o winshare.o winstore.o wintime.o \ winucs.o winutils.o winx11.o x11fwd.o -ladvapi32 -lcomdlg32 \ -lgdi32 -limm32 -lole32 -lshell32 -luser32 puttygen.exe: conf.o ecc.o import.o marshal.o memory.o millerrabin.o misc.o \ mpint.o mpunsafe.o notiming.o pockle.o primecandidate.o \ puttygen.res.o smallprimes.o sshaes.o sshargon2.o \ sshauxcrypt.o sshbcrypt.o sshblake2.o sshblowf.o sshdes.o \ sshdss.o sshdssg.o sshecc.o sshecdsag.o sshhmac.o sshmd5.o \ sshprime.o sshprng.o sshpubk.o sshrand.o sshrsa.o sshrsag.o \ sshsh256.o sshsh512.o sshsha.o sshsha3.o stripctrl.o \ tree234.o utils.o version.o wcwidth.o winctrls.o winhelp.o \ winmisc.o winmiscs.o winnoise.o winnojmp.o winpgen.o \ winsecur.o winstore.o wintime.o winutils.o $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,puttygen.map conf.o ecc.o \ import.o marshal.o memory.o millerrabin.o misc.o mpint.o \ mpunsafe.o notiming.o pockle.o primecandidate.o \ puttygen.res.o smallprimes.o sshaes.o sshargon2.o \ sshauxcrypt.o sshbcrypt.o sshblake2.o sshblowf.o sshdes.o \ sshdss.o sshdssg.o sshecc.o sshecdsag.o sshhmac.o sshmd5.o \ sshprime.o sshprng.o sshpubk.o sshrand.o sshrsa.o sshrsag.o \ sshsh256.o sshsh512.o sshsha.o sshsha3.o stripctrl.o \ tree234.o utils.o version.o wcwidth.o winctrls.o winhelp.o \ winmisc.o winmiscs.o winnoise.o winnojmp.o winpgen.o \ winsecur.o winstore.o wintime.o winutils.o -ladvapi32 \ -lcomdlg32 -lgdi32 -limm32 -lole32 -lshell32 -luser32 puttytel.exe: be_misc.o be_nos_s.o callback.o cmdline.o conf.o config.o \ dialog.o errsock.o ldisc.o logging.o marshal.o memory.o \ minibidi.o misc.o miscucs.o nocproxy.o nogss.o norand.o \ pinger.o proxy.o puttytel.res.o raw.o rlogin.o sessprep.o \ settings.o sizetip.o stripctrl.o supdup.o telnet.o \ terminal.o timing.o tree234.o utils.o version.o wcwidth.o \ wincfg.o winctrls.o windefs.o windlg.o window.o winhandl.o \ winhelp.o winhsock.o winjump.o winmisc.o winmiscs.o winnet.o \ winprint.o winproxy.o winsecur.o winselgui.o winser.o \ winstore.o wintime.o winucs.o winutils.o $(CC) -mwindows $(LDFLAGS) -o $@ -Wl,-Map,puttytel.map be_misc.o \ be_nos_s.o callback.o cmdline.o conf.o config.o dialog.o \ errsock.o ldisc.o logging.o marshal.o memory.o minibidi.o \ misc.o miscucs.o nocproxy.o nogss.o norand.o pinger.o \ proxy.o puttytel.res.o raw.o rlogin.o sessprep.o settings.o \ sizetip.o stripctrl.o supdup.o telnet.o terminal.o timing.o \ tree234.o utils.o version.o wcwidth.o wincfg.o winctrls.o \ windefs.o windlg.o window.o winhandl.o winhelp.o winhsock.o \ winjump.o winmisc.o winmiscs.o winnet.o winprint.o \ winproxy.o winsecur.o winselgui.o winser.o winstore.o \ wintime.o winucs.o winutils.o -ladvapi32 -lcomdlg32 -lgdi32 \ -limm32 -lole32 -lshell32 -luser32 testcrypt.exe: ecc.o marshal.o memory.o millerrabin.o mpint.o mpunsafe.o \ pockle.o primecandidate.o smallprimes.o sshaes.o ssharcf.o \ sshargon2.o sshauxcrypt.o sshblake2.o sshblowf.o sshccp.o \ sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o sshdssg.o \ sshecc.o sshecdsag.o sshhmac.o sshmd5.o sshprime.o sshprng.o \ sshpubk.o sshrsa.o sshrsag.o sshsh256.o sshsh512.o sshsha.o \ sshsha3.o testcrypt.o tree234.o utils.o winmiscs.o $(CC) $(LDFLAGS) -o $@ -Wl,-Map,testcrypt.map ecc.o marshal.o \ memory.o millerrabin.o mpint.o mpunsafe.o pockle.o \ primecandidate.o smallprimes.o sshaes.o ssharcf.o \ sshargon2.o sshauxcrypt.o sshblake2.o sshblowf.o sshccp.o \ sshcrc.o sshcrcda.o sshdes.o sshdh.o sshdss.o sshdssg.o \ sshecc.o sshecdsag.o sshhmac.o sshmd5.o sshprime.o sshprng.o \ sshpubk.o sshrsa.o sshrsag.o sshsh256.o sshsh512.o sshsha.o \ sshsha3.o testcrypt.o tree234.o utils.o winmiscs.o agentf.o: ../agentf.c ../putty.h ../ssh.h ../pageant.h ../sshchan.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../agentf.c aqsync.o: ../aqsync.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../aqsync.c be_all_s.o: ../be_all_s.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../be_all_s.c be_misc.o: ../be_misc.c ../putty.h ../network.h ../defs.h ../puttyps.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../be_misc.c be_none.o: ../be_none.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../be_none.c be_nos_s.o: ../be_nos_s.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../be_nos_s.c be_ssh.o: ../be_ssh.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../be_ssh.c callback.o: ../callback.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../callback.c cgtest.o: ../cgtest.c ../cmdgen.c ../putty.h ../ssh.h ../sshkeygen.h \ ../mpint.h ../defs.h ../puttyps.h ../network.h ../misc.h \ ../marshal.h ../sshsignals.h ../puttymem.h ../tree234.h \ ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../cgtest.c clicons.o: ../clicons.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../clicons.c cmdgen.o: ../cmdgen.c ../putty.h ../ssh.h ../sshkeygen.h ../mpint.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../cmdgen.c cmdline.o: ../cmdline.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../cmdline.c conf.o: ../conf.c ../tree234.h ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../conf.c config.o: ../config.c ../putty.h ../dialog.h ../storage.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../config.c console.o: ../console.c ../putty.h ../misc.h ../console.h ../defs.h \ ../puttyps.h ../network.h ../marshal.h ../sshsignals.h \ ../puttymem.h ../windows/winstuff.h ../unix/unix.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../console.c cproxy.o: ../cproxy.c ../putty.h ../ssh.h ../network.h ../proxy.h \ ../marshal.h ../defs.h ../puttyps.h ../misc.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../cproxy.c dialog.o: ../dialog.c ../putty.h ../dialog.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../dialog.c ecc.o: ../ecc.c ../ssh.h ../mpint.h ../ecc.h ../puttymem.h ../tree234.h \ ../network.h ../misc.h ../sshttymodes.h ../defs.h \ ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ecc.c errsock.o: ../errsock.c ../tree234.h ../putty.h ../network.h ../defs.h \ ../puttyps.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../errsock.c fromucs.o: ../charset/fromucs.c ../charset/charset.h ../charset/internal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/fromucs.c fuzzterm.o: ../fuzzterm.c ../putty.h ../dialog.h ../terminal.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../tree234.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../fuzzterm.c gtkapp.o: ../unix/gtkapp.c ../putty.h ../unix/gtkmisc.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkapp.c gtkask.o: ../unix/gtkask.c ../defs.h ../unix/gtkfont.h ../unix/gtkcompat.h \ ../unix/gtkmisc.h ../putty.h ../ssh.h ../misc.h ../puttyps.h \ ../network.h ../marshal.h ../sshsignals.h ../puttymem.h \ ../tree234.h ../sshttymodes.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkask.c gtkcfg.o: ../unix/gtkcfg.c ../putty.h ../dialog.h ../storage.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkcfg.c gtkcols.o: ../unix/gtkcols.c ../defs.h ../unix/gtkcompat.h ../unix/gtkcols.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkcols.c gtkcomm.o: ../unix/gtkcomm.c ../putty.h ../terminal.h ../unix/gtkcompat.h \ ../unix/gtkfont.h ../unix/gtkmisc.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../tree234.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkcomm.c gtkdlg.o: ../unix/gtkdlg.c ../putty.h ../unix/gtkcompat.h ../unix/gtkcols.h \ ../unix/gtkfont.h ../unix/gtkmisc.h ../unix/x11misc.h \ ../storage.h ../dialog.h ../tree234.h ../licence.h ../ssh.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkdlg.c gtkfont.o: ../unix/gtkfont.c ../putty.h ../unix/gtkfont.h \ ../unix/gtkcompat.h ../unix/gtkmisc.h ../tree234.h \ ../unix/x11misc.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkfont.c gtkmain.o: ../unix/gtkmain.c ../putty.h ../terminal.h ../unix/gtkcompat.h \ ../unix/gtkfont.h ../unix/gtkmisc.h ../unix/x11misc.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../tree234.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkmain.c gtkmisc.o: ../unix/gtkmisc.c ../putty.h ../unix/gtkcompat.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkmisc.c gtkwin.o: ../unix/gtkwin.c ../putty.h ../terminal.h ../unix/gtkcompat.h \ ../unix/gtkfont.h ../unix/gtkmisc.h ../unix/x11misc.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../tree234.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/gtkwin.c import.o: ../import.c ../putty.h ../ssh.h ../mpint.h ../misc.h ../defs.h \ ../puttyps.h ../network.h ../marshal.h ../sshsignals.h \ ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../import.c ldisc.o: ../ldisc.c ../putty.h ../terminal.h ../ldisc.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../tree234.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ldisc.c localenc.o: ../charset/localenc.c ../charset/charset.h ../charset/internal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/localenc.c logging.o: ../logging.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../logging.c macenc.o: ../charset/macenc.c ../charset/charset.h ../charset/internal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/macenc.c mainchan.o: ../mainchan.c ../putty.h ../ssh.h ../sshppl.h ../sshchan.h \ ../sshsignals.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../puttymem.h ../tree234.h \ ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../mainchan.c marshal.o: ../marshal.c ../marshal.h ../misc.h ../defs.h ../puttymem.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../marshal.c memory.o: ../memory.c ../defs.h ../puttymem.h ../misc.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../memory.c millerrabin.o: ../millerrabin.c ../ssh.h ../sshkeygen.h ../mpint.h \ ../mpunsafe.h ../puttymem.h ../tree234.h ../network.h \ ../misc.h ../sshttymodes.h ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../millerrabin.c mimeenc.o: ../charset/mimeenc.c ../charset/charset.h ../charset/internal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/mimeenc.c minibidi.o: ../minibidi.c ../putty.h ../misc.h ../defs.h ../puttyps.h \ ../network.h ../marshal.h ../sshsignals.h ../puttymem.h \ ../windows/winstuff.h ../unix/unix.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../minibidi.c misc.o: ../misc.c ../defs.h ../putty.h ../misc.h ../puttyps.h ../network.h \ ../marshal.h ../sshsignals.h ../puttymem.h \ ../windows/winstuff.h ../unix/unix.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../misc.c miscucs.o: ../miscucs.c ../putty.h ../misc.h ../defs.h ../puttyps.h \ ../network.h ../marshal.h ../sshsignals.h ../puttymem.h \ ../windows/winstuff.h ../unix/unix.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../miscucs.c mpint.o: ../mpint.c ../defs.h ../misc.h ../puttymem.h ../mpint.h \ ../mpint_i.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../mpint.c mpunsafe.o: ../mpunsafe.c ../defs.h ../misc.h ../puttymem.h ../mpint.h \ ../mpint_i.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../mpunsafe.c nocmdline.o: ../nocmdline.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../nocmdline.c nocproxy.o: ../nocproxy.c ../putty.h ../network.h ../proxy.h ../defs.h \ ../puttyps.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../nocproxy.c nogss.o: ../nogss.c ../putty.h ../defs.h ../puttyps.h ../network.h ../misc.h \ ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../nogss.c norand.o: ../norand.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../norand.c noterm.o: ../noterm.c ../putty.h ../terminal.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../tree234.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../noterm.c notiming.o: ../notiming.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../notiming.c nullplug.o: ../nullplug.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../nullplug.c osxlaunch.o: ../unix/osxlaunch.c $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/osxlaunch.c pageant.o: ../pageant.c ../putty.h ../mpint.h ../ssh.h ../sshcr.h \ ../pageant.h ../defs.h ../puttyps.h ../network.h ../misc.h \ ../marshal.h ../sshsignals.h ../puttymem.h ../tree234.h \ ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../pageant.c pageant.res.o: ../windows/pageant.rc ../windows/rcstuff.h \ ../windows/pageant-rc.h ../windows/winhelp.rc2 \ ../windows/pageant.ico ../windows/pageants.ico \ ../windows/version.rc2 ../windows/pageant.mft \ ../windows/win_res.h ../version.h ../licence.h $(RC) $(RCFL) $(RCFLAGS) ../windows/pageant.rc -o pageant.res.o pgssapi.o: ../pgssapi.c ../putty.h ../pgssapi.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../pgssapi.c pinger.o: ../pinger.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../pinger.c plink.res.o: ../windows/plink.rc ../windows/rcstuff.h ../windows/putty.ico \ ../windows/version.rc2 ../version.h ../licence.h $(RC) $(RCFL) $(RCFLAGS) ../windows/plink.rc -o plink.res.o pockle.o: ../pockle.c ../ssh.h ../sshkeygen.h ../mpint.h ../mpunsafe.h \ ../tree234.h ../puttymem.h ../network.h ../misc.h \ ../sshttymodes.h ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../pockle.c portfwd.o: ../portfwd.c ../putty.h ../ssh.h ../sshchan.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../portfwd.c primecandidate.o: ../primecandidate.c ../ssh.h ../mpint.h ../mpunsafe.h \ ../sshkeygen.h ../puttymem.h ../tree234.h ../network.h \ ../misc.h ../sshttymodes.h ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../primecandidate.c procnet.o: ../unix/procnet.c ../misc.h ../defs.h ../puttymem.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/procnet.c proxy.o: ../proxy.c ../putty.h ../network.h ../proxy.h ../defs.h \ ../puttyps.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../proxy.c pscp.o: ../pscp.c ../putty.h ../psftp.h ../ssh.h ../sftp.h ../storage.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../pscp.c pscp.res.o: ../windows/pscp.rc ../windows/rcstuff.h ../windows/pscp.ico \ ../windows/version.rc2 ../version.h ../licence.h $(RC) $(RCFL) $(RCFLAGS) ../windows/pscp.rc -o pscp.res.o psftp.o: ../psftp.c ../putty.h ../psftp.h ../storage.h ../ssh.h ../sftp.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../psftp.c psftp.res.o: ../windows/psftp.rc ../windows/rcstuff.h ../windows/pscp.ico \ ../windows/version.rc2 ../version.h ../licence.h $(RC) $(RCFL) $(RCFLAGS) ../windows/psftp.rc -o psftp.res.o psftpcommon.o: ../psftpcommon.c ../putty.h ../sftp.h ../psftp.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../psftpcommon.c psocks.o: ../psocks.c ../putty.h ../misc.h ../ssh.h ../sshchan.h ../psocks.h \ ../defs.h ../puttyps.h ../network.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../psocks.c putty.res.o: ../windows/putty.rc ../windows/rcstuff.h ../windows/winhelp.rc2 \ ../windows/win_res.rc2 ../windows/putty.mft \ ../windows/win_res.h ../windows/putty.ico \ ../windows/puttycfg.ico ../windows/version.rc2 ../version.h \ ../licence.h $(RC) $(RCFL) $(RCFLAGS) ../windows/putty.rc -o putty.res.o puttygen.res.o: ../windows/puttygen.rc ../windows/rcstuff.h \ ../windows/winhelp.rc2 ../windows/puttygen-rc.h \ ../windows/puttygen.ico ../windows/version.rc2 \ ../windows/puttygen.mft ../windows/win_res.h ../version.h \ ../licence.h $(RC) $(RCFL) $(RCFLAGS) ../windows/puttygen.rc -o puttygen.res.o puttytel.res.o: ../windows/puttytel.rc ../windows/rcstuff.h \ ../windows/winhelp.rc2 ../windows/win_res.rc2 \ ../windows/puttytel.mft ../windows/win_res.h \ ../windows/putty.ico ../windows/puttycfg.ico \ ../windows/version.rc2 ../version.h ../licence.h $(RC) $(RCFL) $(RCFLAGS) ../windows/puttytel.rc -o puttytel.res.o raw.o: ../raw.c ../putty.h ../defs.h ../puttyps.h ../network.h ../misc.h \ ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../raw.c rlogin.o: ../rlogin.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../rlogin.c sbcs.o: ../charset/sbcs.c ../charset/charset.h ../charset/internal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/sbcs.c sbcsdat.o: ../charset/sbcsdat.c ../charset/charset.h ../charset/internal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/sbcsdat.c scpserver.o: ../scpserver.c ../putty.h ../ssh.h ../sshcr.h ../sshchan.h \ ../sftp.h ../defs.h ../puttyps.h ../network.h ../misc.h \ ../marshal.h ../sshsignals.h ../puttymem.h ../tree234.h \ ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../scpserver.c sesschan.o: ../sesschan.c ../putty.h ../ssh.h ../sshchan.h ../sshserver.h \ ../sftp.h ../sshsignals.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../puttymem.h \ ../tree234.h ../sshttymodes.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sesschan.c sessprep.o: ../sessprep.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sessprep.c settings.o: ../settings.c ../putty.h ../storage.h ../sshgssc.h ../sshgss.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../pgssapi.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../settings.c sftp.o: ../sftp.c ../misc.h ../tree234.h ../sftp.h ../defs.h ../puttymem.h \ ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sftp.c sftpcommon.o: ../sftpcommon.c ../misc.h ../sftp.h ../defs.h ../puttymem.h \ ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sftpcommon.c sftpserver.o: ../sftpserver.c ../putty.h ../ssh.h ../sftp.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sftpserver.c sizetip.o: ../windows/sizetip.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/sizetip.c slookup.o: ../charset/slookup.c ../charset/charset.h ../charset/internal.h \ ../charset/enum.c ../charset/sbcsdat.c ../charset/utf8.c $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/slookup.c smallprimes.o: ../smallprimes.c ../ssh.h ../sshkeygen.h ../puttymem.h \ ../tree234.h ../network.h ../misc.h ../sshttymodes.h \ ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../smallprimes.c ssh.o: ../ssh.c ../putty.h ../pageant.h ../tree234.h ../storage.h \ ../marshal.h ../ssh.h ../sshcr.h ../sshbpp.h ../sshppl.h \ ../sshchan.h ../sshgssc.h ../sshgss.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../sshsignals.h ../puttymem.h \ ../sshttymodes.h ../pgssapi.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh.c ssh1bpp.o: ../ssh1bpp.c ../putty.h ../ssh.h ../sshbpp.h ../sshcr.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh1bpp.c ssh1censor.o: ../ssh1censor.c ../putty.h ../ssh.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh1censor.c ssh1connection.o: ../ssh1connection.c ../putty.h ../ssh.h ../sshbpp.h \ ../sshppl.h ../sshchan.h ../sshcr.h ../ssh1connection.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh1connection.c ssh1connection-client.o: ../ssh1connection-client.c ../putty.h ../ssh.h \ ../sshbpp.h ../sshppl.h ../sshchan.h ../sshcr.h \ ../ssh1connection.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ ../tree234.h ../sshttymodes.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh1connection-client.c ssh1connection-server.o: ../ssh1connection-server.c ../putty.h ../ssh.h \ ../sshbpp.h ../sshppl.h ../sshchan.h ../sshcr.h \ ../ssh1connection.h ../sshserver.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh1connection-server.c ssh1login.o: ../ssh1login.c ../putty.h ../ssh.h ../mpint.h ../sshbpp.h \ ../sshppl.h ../sshcr.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ ../tree234.h ../sshttymodes.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh1login.c ssh1login-server.o: ../ssh1login-server.c ../putty.h ../mpint.h ../ssh.h \ ../sshbpp.h ../sshppl.h ../sshcr.h ../sshserver.h \ ../sshkeygen.h ../defs.h ../puttyps.h ../network.h ../misc.h \ ../marshal.h ../sshsignals.h ../puttymem.h ../tree234.h \ ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh1login-server.c ssh2bpp.o: ../ssh2bpp.c ../putty.h ../ssh.h ../sshbpp.h ../sshcr.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh2bpp.c ssh2bpp-bare.o: ../ssh2bpp-bare.c ../putty.h ../ssh.h ../sshbpp.h ../sshcr.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh2bpp-bare.c ssh2censor.o: ../ssh2censor.c ../putty.h ../ssh.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh2censor.c ssh2connection.o: ../ssh2connection.c ../putty.h ../ssh.h ../sshbpp.h \ ../sshppl.h ../sshchan.h ../sshcr.h ../ssh2connection.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh2connection.c ssh2connection-client.o: ../ssh2connection-client.c ../putty.h ../ssh.h \ ../sshbpp.h ../sshppl.h ../sshchan.h ../sshcr.h \ ../ssh2connection.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ ../tree234.h ../sshttymodes.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh2connection-client.c ssh2connection-server.o: ../ssh2connection-server.c ../putty.h ../ssh.h \ ../sshbpp.h ../sshppl.h ../sshchan.h ../sshcr.h \ ../ssh2connection.h ../sshserver.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh2connection-server.c ssh2kex-client.o: ../ssh2kex-client.c ../putty.h ../ssh.h ../sshbpp.h \ ../sshppl.h ../sshcr.h ../storage.h ../ssh2transport.h \ ../mpint.h ../defs.h ../puttyps.h ../network.h ../misc.h \ ../marshal.h ../sshsignals.h ../puttymem.h ../tree234.h \ ../sshttymodes.h ../sshgssc.h ../sshgss.h \ ../windows/winstuff.h ../unix/unix.h ../pgssapi.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh2kex-client.c ssh2kex-server.o: ../ssh2kex-server.c ../putty.h ../ssh.h ../sshbpp.h \ ../sshppl.h ../sshcr.h ../sshserver.h ../sshkeygen.h \ ../storage.h ../ssh2transport.h ../mpint.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../sshgssc.h ../sshgss.h ../windows/winstuff.h \ ../unix/unix.h ../pgssapi.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh2kex-server.c ssh2transhk.o: ../ssh2transhk.c ../putty.h ../ssh.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh2transhk.c ssh2transport.o: ../ssh2transport.c ../putty.h ../ssh.h ../sshbpp.h \ ../sshppl.h ../sshcr.h ../sshserver.h ../storage.h \ ../ssh2transport.h ../mpint.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../puttymem.h ../tree234.h ../sshttymodes.h ../sshgssc.h \ ../sshgss.h ../windows/winstuff.h ../unix/unix.h \ ../pgssapi.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh2transport.c ssh2userauth.o: ../ssh2userauth.c ../putty.h ../ssh.h ../sshbpp.h \ ../sshppl.h ../sshcr.h ../sshgssc.h ../sshgss.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../pgssapi.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh2userauth.c ssh2userauth-server.o: ../ssh2userauth-server.c ../putty.h ../ssh.h \ ../sshbpp.h ../sshppl.h ../sshcr.h ../sshserver.h \ ../sshgssc.h ../sshgss.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ ../tree234.h ../sshttymodes.h ../pgssapi.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssh2userauth-server.c sshaes.o: ../sshaes.c ../ssh.h ../mpint_i.h ../puttymem.h ../tree234.h \ ../network.h ../misc.h ../sshttymodes.h ../defs.h \ ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshaes.c ssharcf.o: ../ssharcf.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ ../misc.h ../sshttymodes.h ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../ssharcf.c sshargon2.o: ../sshargon2.c ../putty.h ../ssh.h ../marshal.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../sshsignals.h \ ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshargon2.c sshauxcrypt.o: ../sshauxcrypt.c ../ssh.h ../puttymem.h ../tree234.h \ ../network.h ../misc.h ../sshttymodes.h ../defs.h \ ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshauxcrypt.c sshbcrypt.o: ../sshbcrypt.c ../ssh.h ../sshblowf.h ../puttymem.h \ ../tree234.h ../network.h ../misc.h ../sshttymodes.h \ ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshbcrypt.c sshblake2.o: ../sshblake2.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ ../misc.h ../sshttymodes.h ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshblake2.c sshblowf.o: ../sshblowf.c ../ssh.h ../sshblowf.h ../puttymem.h ../tree234.h \ ../network.h ../misc.h ../sshttymodes.h ../defs.h \ ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshblowf.c sshccp.o: ../sshccp.c ../ssh.h ../mpint_i.h ../puttymem.h ../tree234.h \ ../network.h ../misc.h ../sshttymodes.h ../defs.h \ ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshccp.c sshcommon.o: ../sshcommon.c ../putty.h ../mpint.h ../ssh.h ../sshbpp.h \ ../sshppl.h ../sshchan.h ../sshttymodes.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshcommon.c sshcrc.o: ../sshcrc.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ ../misc.h ../sshttymodes.h ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshcrc.c sshcrcda.o: ../sshcrcda.c ../misc.h ../ssh.h ../defs.h ../puttymem.h \ ../marshal.h ../tree234.h ../network.h ../sshttymodes.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshcrcda.c sshdes.o: ../sshdes.c ../ssh.h ../mpint_i.h ../puttymem.h ../tree234.h \ ../network.h ../misc.h ../sshttymodes.h ../defs.h \ ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshdes.c sshdh.o: ../sshdh.c ../ssh.h ../misc.h ../mpint.h ../puttymem.h ../tree234.h \ ../network.h ../sshttymodes.h ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshdh.c sshdss.o: ../sshdss.c ../ssh.h ../mpint.h ../misc.h ../puttymem.h \ ../tree234.h ../network.h ../sshttymodes.h ../defs.h \ ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshdss.c sshdssg.o: ../sshdssg.c ../misc.h ../ssh.h ../sshkeygen.h ../mpint.h \ ../defs.h ../puttymem.h ../marshal.h ../tree234.h \ ../network.h ../sshttymodes.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshdssg.c sshecc.o: ../sshecc.c ../ssh.h ../mpint.h ../ecc.h ../puttymem.h \ ../tree234.h ../network.h ../misc.h ../sshttymodes.h \ ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshecc.c sshecdsag.o: ../sshecdsag.c ../ssh.h ../sshkeygen.h ../mpint.h ../puttymem.h \ ../tree234.h ../network.h ../misc.h ../sshttymodes.h \ ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshecdsag.c sshgssc.o: ../sshgssc.c ../putty.h ../sshgssc.h ../misc.h ../defs.h \ ../puttyps.h ../network.h ../marshal.h ../sshsignals.h \ ../pgssapi.h ../sshgss.h ../puttymem.h ../windows/winstuff.h \ ../unix/unix.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshgssc.c sshhmac.o: ../sshhmac.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ ../misc.h ../sshttymodes.h ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshhmac.c sshmac.o: ../sshmac.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ ../misc.h ../sshttymodes.h ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshmac.c sshmd5.o: ../sshmd5.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ ../misc.h ../sshttymodes.h ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshmd5.c sshprime.o: ../sshprime.c ../ssh.h ../mpint.h ../mpunsafe.h ../sshkeygen.h \ ../puttymem.h ../tree234.h ../network.h ../misc.h \ ../sshttymodes.h ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshprime.c sshprng.o: ../sshprng.c ../putty.h ../ssh.h ../mpint_i.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshprng.c sshpubk.o: ../sshpubk.c ../putty.h ../mpint.h ../ssh.h ../misc.h ../defs.h \ ../puttyps.h ../network.h ../marshal.h ../sshsignals.h \ ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshpubk.c sshrand.o: ../sshrand.c ../putty.h ../ssh.h ../storage.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshrand.c sshrsa.o: ../sshrsa.c ../ssh.h ../mpint.h ../misc.h ../puttymem.h \ ../tree234.h ../network.h ../sshttymodes.h ../defs.h \ ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshrsa.c sshrsag.o: ../sshrsag.c ../ssh.h ../sshkeygen.h ../mpint.h ../puttymem.h \ ../tree234.h ../network.h ../misc.h ../sshttymodes.h \ ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshrsag.c sshserver.o: ../sshserver.c ../putty.h ../ssh.h ../sshbpp.h ../sshppl.h \ ../sshchan.h ../sshserver.h ../sshgssc.h ../sshgss.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../pgssapi.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshserver.c sshsh256.o: ../sshsh256.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ ../misc.h ../sshttymodes.h ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshsh256.c sshsh512.o: ../sshsh512.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ ../misc.h ../sshttymodes.h ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshsh512.c sshsha.o: ../sshsha.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ ../misc.h ../sshttymodes.h ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshsha.c sshsha3.o: ../sshsha3.c ../ssh.h ../puttymem.h ../tree234.h ../network.h \ ../misc.h ../sshttymodes.h ../defs.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshsha3.c sshshare.o: ../sshshare.c ../putty.h ../tree234.h ../ssh.h ../sshcr.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshshare.c sshutils.o: ../sshutils.c ../putty.h ../ssh.h ../sshchan.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshutils.c sshverstring.o: ../sshverstring.c ../putty.h ../ssh.h ../sshbpp.h ../sshcr.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshverstring.c sshzlib.o: ../sshzlib.c ../defs.h ../ssh.h ../puttymem.h ../tree234.h \ ../network.h ../misc.h ../sshttymodes.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../sshzlib.c stripctrl.o: ../stripctrl.c ../putty.h ../terminal.h ../misc.h ../marshal.h \ ../defs.h ../puttyps.h ../network.h ../sshsignals.h \ ../tree234.h ../puttymem.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../stripctrl.c supdup.o: ../supdup.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../supdup.c telnet.o: ../telnet.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../telnet.c terminal.o: ../terminal.c ../putty.h ../terminal.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../tree234.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../terminal.c testcrypt.o: ../testcrypt.c ../defs.h ../ssh.h ../sshkeygen.h ../misc.h \ ../mpint.h ../ecc.h ../testcrypt.h ../puttymem.h \ ../tree234.h ../network.h ../sshttymodes.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../testcrypt.c testsc.o: ../testsc.c ../defs.h ../putty.h ../ssh.h ../misc.h ../mpint.h \ ../ecc.h ../puttyps.h ../network.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../testsc.c testzlib.o: ../testzlib.c ../defs.h ../ssh.h ../puttymem.h ../tree234.h \ ../network.h ../misc.h ../sshttymodes.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../testzlib.c time.o: ../time.c $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../time.c timing.o: ../timing.c ../putty.h ../tree234.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../timing.c toucs.o: ../charset/toucs.c ../charset/charset.h ../charset/internal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/toucs.c tree234.o: ../tree234.c ../defs.h ../tree234.h ../puttymem.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../tree234.c utf8.o: ../charset/utf8.c ../charset/charset.h ../charset/internal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/utf8.c utils.o: ../utils.c ../defs.h ../misc.h ../ssh.h ../puttymem.h ../marshal.h \ ../tree234.h ../network.h ../sshttymodes.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../utils.c ux_x11.o: ../unix/ux_x11.c ../putty.h ../ssh.h ../network.h ../defs.h \ ../puttyps.h ../misc.h ../marshal.h ../sshsignals.h \ ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/ux_x11.c uxagentc.o: ../unix/uxagentc.c ../putty.h ../misc.h ../tree234.h \ ../puttymem.h ../defs.h ../puttyps.h ../network.h \ ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxagentc.c uxagentsock.o: ../unix/uxagentsock.c ../putty.h ../ssh.h ../misc.h \ ../pageant.h ../defs.h ../puttyps.h ../network.h \ ../marshal.h ../sshsignals.h ../puttymem.h ../tree234.h \ ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxagentsock.c uxcfg.o: ../unix/uxcfg.c ../putty.h ../dialog.h ../storage.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxcfg.c uxcliloop.o: ../unix/uxcliloop.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxcliloop.c uxcons.o: ../unix/uxcons.c ../putty.h ../storage.h ../ssh.h ../console.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxcons.c uxfdsock.o: ../unix/uxfdsock.c ../tree234.h ../putty.h ../network.h \ ../defs.h ../puttyps.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxfdsock.c uxgen.o: ../unix/uxgen.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxgen.c uxgss.o: ../unix/uxgss.c ../putty.h ../pgssapi.h ../sshgss.h ../sshgssc.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxgss.c uxmisc.o: ../unix/uxmisc.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxmisc.c uxnet.o: ../unix/uxnet.c ../putty.h ../network.h ../tree234.h ../defs.h \ ../puttyps.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxnet.c uxnogtk.o: ../unix/uxnogtk.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxnogtk.c uxnoise.o: ../unix/uxnoise.c ../putty.h ../ssh.h ../storage.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxnoise.c uxpeer.o: ../unix/uxpeer.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxpeer.c uxpgnt.o: ../unix/uxpgnt.c ../putty.h ../ssh.h ../misc.h ../pageant.h \ ../defs.h ../puttyps.h ../network.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxpgnt.c uxplink.o: ../unix/uxplink.c ../putty.h ../ssh.h ../storage.h ../tree234.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxplink.c uxpoll.o: ../unix/uxpoll.c ../putty.h ../tree234.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxpoll.c uxprint.o: ../unix/uxprint.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxprint.c uxproxy.o: ../unix/uxproxy.c ../tree234.h ../putty.h ../network.h ../proxy.h \ ../defs.h ../puttyps.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxproxy.c uxpsusan.o: ../unix/uxpsusan.c ../putty.h ../mpint.h ../ssh.h ../sshserver.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxpsusan.c uxpterm.o: ../unix/uxpterm.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxpterm.c uxpty.o: ../unix/uxpty.c ../putty.h ../ssh.h ../sshserver.h ../tree234.h \ ../sshttymodes.h ../sshsignals.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../puttymem.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxpty.c uxputty.o: ../unix/uxputty.c ../putty.h ../ssh.h ../storage.h \ ../unix/gtkcompat.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ ../tree234.h ../sshttymodes.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxputty.c uxsel.o: ../unix/uxsel.c ../putty.h ../tree234.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxsel.c uxser.o: ../unix/uxser.c ../putty.h ../tree234.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxser.c uxserver.o: ../unix/uxserver.c ../putty.h ../mpint.h ../ssh.h ../sshserver.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxserver.c uxsftp.o: ../unix/uxsftp.c ../putty.h ../ssh.h ../psftp.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxsftp.c uxsftpserver.o: ../unix/uxsftpserver.c ../putty.h ../ssh.h ../sshserver.h \ ../sftp.h ../tree234.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxsftpserver.c uxshare.o: ../unix/uxshare.c ../tree234.h ../putty.h ../network.h ../proxy.h \ ../ssh.h ../defs.h ../puttyps.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxshare.c uxsignal.o: ../unix/uxsignal.c ../defs.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxsignal.c uxsocks.o: ../unix/uxsocks.c ../putty.h ../ssh.h ../psocks.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxsocks.c uxstore.o: ../unix/uxstore.c ../putty.h ../storage.h ../tree234.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxstore.c uxucs.o: ../unix/uxucs.c ../putty.h ../charset/charset.h ../terminal.h \ ../misc.h ../defs.h ../puttyps.h ../network.h ../marshal.h \ ../sshsignals.h ../tree234.h ../puttymem.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxucs.c uxutils.o: ../unix/uxutils.c ../putty.h ../ssh.h ../unix/uxutils.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/uxutils.c version.o: ../version.c ../putty.h ../ssh.h ../empty.h ../version.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../version.c wcwidth.o: ../wcwidth.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../wcwidth.c wildcard.o: ../wildcard.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../wildcard.c wincapi.o: ../windows/wincapi.c ../putty.h ../ssh.h ../windows/wincapi.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/wincapi.c wincfg.o: ../windows/wincfg.c ../putty.h ../dialog.h ../storage.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/wincfg.c wincliloop.o: ../windows/wincliloop.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/wincliloop.c wincons.o: ../windows/wincons.c ../putty.h ../storage.h ../ssh.h \ ../console.h ../defs.h ../puttyps.h ../network.h ../misc.h \ ../marshal.h ../sshsignals.h ../puttymem.h ../tree234.h \ ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/wincons.c winctrls.o: ../windows/winctrls.c ../putty.h ../misc.h ../dialog.h ../defs.h \ ../puttyps.h ../network.h ../marshal.h ../sshsignals.h \ ../puttymem.h ../windows/winstuff.h ../unix/unix.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winctrls.c windefs.o: ../windows/windefs.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/windefs.c windlg.o: ../windows/windlg.c ../putty.h ../ssh.h ../windows/win_res.h \ ../windows/winseat.h ../storage.h ../dialog.h ../licence.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/windlg.c window.o: ../windows/window.c ../putty.h ../terminal.h ../storage.h \ ../windows/win_res.h ../windows/winsecur.h \ ../windows/winseat.h ../tree234.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/window.c wingss.o: ../windows/wingss.c ../putty.h ../pgssapi.h ../sshgss.h \ ../sshgssc.h ../misc.h ../defs.h ../puttyps.h ../network.h \ ../marshal.h ../sshsignals.h ../puttymem.h \ ../windows/winstuff.h ../unix/unix.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/wingss.c winhandl.o: ../windows/winhandl.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winhandl.c winhelp.o: ../windows/winhelp.c ../putty.h ../windows/win_res.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winhelp.c winhsock.o: ../windows/winhsock.c ../tree234.h ../putty.h ../network.h \ ../defs.h ../puttyps.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winhsock.c winjump.o: ../windows/winjump.c ../putty.h ../storage.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winjump.c winmisc.o: ../windows/winmisc.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winmisc.c winmiscs.o: ../windows/winmiscs.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winmiscs.c winnet.o: ../windows/winnet.c ../putty.h ../network.h ../tree234.h ../ssh.h \ ../defs.h ../puttyps.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winnet.c winnohlp.o: ../windows/winnohlp.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winnohlp.c winnoise.o: ../windows/winnoise.c ../putty.h ../ssh.h ../storage.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winnoise.c winnojmp.o: ../windows/winnojmp.c $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winnojmp.c winnpc.o: ../windows/winnpc.c ../tree234.h ../putty.h ../network.h \ ../proxy.h ../ssh.h ../windows/winsecur.h ../defs.h \ ../puttyps.h ../misc.h ../marshal.h ../sshsignals.h \ ../puttymem.h ../sshttymodes.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winnpc.c winnps.o: ../windows/winnps.c ../tree234.h ../putty.h ../network.h \ ../proxy.h ../ssh.h ../windows/winsecur.h ../defs.h \ ../puttyps.h ../misc.h ../marshal.h ../sshsignals.h \ ../puttymem.h ../sshttymodes.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winnps.c winpgen.o: ../windows/winpgen.c ../putty.h ../ssh.h ../sshkeygen.h \ ../licence.h ../windows/winsecur.h ../windows/puttygen-rc.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winpgen.c winpgnt.o: ../windows/winpgnt.c ../putty.h ../ssh.h ../misc.h ../tree234.h \ ../windows/winsecur.h ../windows/wincapi.h ../pageant.h \ ../licence.h ../windows/pageant-rc.h ../defs.h ../puttyps.h \ ../network.h ../marshal.h ../sshsignals.h ../puttymem.h \ ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winpgnt.c winpgntc.o: ../windows/winpgntc.c ../putty.h ../pageant.h \ ../windows/winsecur.h ../windows/wincapi.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winpgntc.c winplink.o: ../windows/winplink.c ../putty.h ../storage.h ../tree234.h \ ../windows/winsecur.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winplink.c winprint.o: ../windows/winprint.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winprint.c winproxy.o: ../windows/winproxy.c ../tree234.h ../putty.h ../network.h \ ../proxy.h ../defs.h ../puttyps.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winproxy.c winsecur.o: ../windows/winsecur.c ../putty.h ../windows/winsecur.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winsecur.c winselcli.o: ../windows/winselcli.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winselcli.c winselgui.o: ../windows/winselgui.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winselgui.c winser.o: ../windows/winser.c ../putty.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../windows/winstuff.h \ ../unix/unix.h ../puttymem.h ../tree234.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winser.c winsftp.o: ../windows/winsftp.c ../putty.h ../psftp.h ../ssh.h \ ../windows/winsecur.h ../defs.h ../puttyps.h ../network.h \ ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ ../tree234.h ../sshttymodes.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winsftp.c winshare.o: ../windows/winshare.c ../tree234.h ../putty.h ../network.h \ ../proxy.h ../ssh.h ../windows/wincapi.h \ ../windows/winsecur.h ../noshare.c ../defs.h ../puttyps.h \ ../misc.h ../marshal.h ../sshsignals.h ../puttymem.h \ ../sshttymodes.h ../windows/winstuff.h ../unix/unix.h \ ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winshare.c winsocks.o: ../windows/winsocks.c ../putty.h ../ssh.h ../psocks.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winsocks.c winstore.o: ../windows/winstore.c ../putty.h ../storage.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winstore.c wintime.o: ../windows/wintime.c ../putty.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../windows/winstuff.h ../unix/unix.h ../puttymem.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/wintime.c winucs.o: ../windows/winucs.c ../putty.h ../terminal.h ../misc.h ../defs.h \ ../puttyps.h ../network.h ../marshal.h ../sshsignals.h \ ../tree234.h ../puttymem.h ../windows/winstuff.h \ ../unix/unix.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winucs.c winutils.o: ../windows/winutils.c ../putty.h ../misc.h ../defs.h \ ../puttyps.h ../network.h ../marshal.h ../sshsignals.h \ ../puttymem.h ../windows/winstuff.h ../unix/unix.h \ ../tree234.h ../windows/winhelp.h ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winutils.c winx11.o: ../windows/winx11.c ../putty.h ../ssh.h ../defs.h ../puttyps.h \ ../network.h ../misc.h ../marshal.h ../sshsignals.h \ ../puttymem.h ../tree234.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../windows/winx11.c x11fwd.o: ../x11fwd.c ../putty.h ../ssh.h ../sshchan.h ../tree234.h \ ../defs.h ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../puttymem.h ../sshttymodes.h \ ../windows/winstuff.h ../unix/unix.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../x11fwd.c x11misc.o: ../unix/x11misc.c ../putty.h ../unix/x11misc.h ../defs.h \ ../puttyps.h ../network.h ../misc.h ../marshal.h \ ../sshsignals.h ../windows/winstuff.h ../unix/unix.h \ ../puttymem.h ../tree234.h ../windows/winhelp.h \ ../charset/charset.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/x11misc.c xenc.o: ../charset/xenc.c ../charset/charset.h ../charset/internal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../charset/xenc.c xkeysym.o: ../unix/xkeysym.c ../misc.h ../defs.h ../puttymem.h ../marshal.h $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/xkeysym.c xpmptcfg.o: ../unix/xpmptcfg.c $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/xpmptcfg.c xpmpterm.o: ../unix/xpmpterm.c $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/xpmpterm.c xpmpucfg.o: ../unix/xpmpucfg.c $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/xpmpucfg.c xpmputty.o: ../unix/xpmputty.c $(CC) $(COMPAT) $(CFLAGS) $(XFLAGS) -c ../unix/xpmputty.c clean: rm -f *.o *.exe *.res.o *.so *.map FORCE: putty-0.76/windows/Makefile.vc0000644000175000017500000030133714072266315013332 00000000000000# Makefile for putty under Visual C. # # This file was created by `mkfiles.pl' from the `Recipe' file. # DO NOT EDIT THIS FILE DIRECTLY; edit Recipe or mkfiles.pl instead. # # Extra options you can set: # # - COMPAT=/DAUTO_WINSOCK (Windows only) # Causes PuTTY to assume that includes its own WinSock # header file, so that it won't try to include . # # - COMPAT=/DWINSOCK_TWO (Windows only) # Causes the PuTTY utilities to include instead of # , except Plink which _needs_ WinSock 2 so it already # does this. # # - COMPAT=/DNO_SECURITY (Windows only) # Disables use of , which is not available with some # development environments (such as very old versions of the # mingw/Cygwin GNU toolchain). This has the following effects: # - Pageant won't care about the local user ID of processes # accessing it; a version of Pageant built with this option # will therefore refuse to run under NT-series OSes on # security grounds (although it will run fine on Win95-series # OSes where there is no access control anyway). # - SSH connection sharing is disabled. # - There is no support for restriction of the process ACLs. # # - COMPAT=/DNO_MULTIMON (Windows only) # Disables PuTTY's use of , which is not available # with some development environments. This means that PuTTY's # full-screen mode (configurable to work on Alt-Enter) will # not behave usefully in a multi-monitor environment. # # - COMPAT=/DNO_HTMLHELP (Windows only) # Disables PuTTY's use of , which is not available # with some development environments. # # If you don't have this header, you may be able to use the copy # supplied with HTML Help Workshop. # # - RCFL=/DNO_MANIFESTS (Windows only) # Disables inclusion of XML application manifests in the PuTTY # binaries. This may be necessary to build for 64-bit Windows; # the manifests are only included to use the XP GUI style on # Windows XP, and the architecture tags are a lie on 64-bit. # # - COMPAT=/DNO_IPV6 # Disables PuTTY's ability to make IPv6 connections, enabling # it to compile under development environments which do not # support IPv6 in their header files. # # - COMPAT=/DNO_GSSAPI # Disables PuTTY's ability to use GSSAPI functions for # authentication and key exchange. # # - COMPAT=/DSTATIC_GSSAPI # Causes PuTTY to try to link statically against the GSSAPI # library instead of the default of doing it at run time. # # - COMPAT=/DMSVC4 (Windows only) # - RCFL=/DMSVC4 # Makes a couple of minor changes so that PuTTY compiles using # MSVC 4. You will also need /DNO_SECURITY and /DNO_MULTIMON. # # - COMPAT=/DNO_SECUREZEROMEMORY (Windows only) # Disables PuTTY's use of SecureZeroMemory(), which is missing # from some environments' header files. # # - XFLAGS=/DDEBUG # Causes PuTTY to enable internal debugging. # # - XFLAGS=/DMALLOC_LOG # Causes PuTTY to emit a file called putty_mem.log, logging every # memory allocation and free, so you can track memory leaks. # # - XFLAGS=/DMINEFIELD (Windows only) # Causes PuTTY to use a custom memory allocator, similar in # concept to Electric Fence, in place of regular malloc(). Wastes # huge amounts of RAM, but should cause heap-corruption bugs to # show up as GPFs at the point of failure rather than appearing # later on as second-level damage. # # - XFLAGS=/DFUZZING # Builds a version of PuTTY with some tweaks to make fuzz testing # easier: the SSH random number generator is replaced by one that # always returns the same thing. Note that this makes SSH # completely insecure -- a FUZZING build should never be used to # connect to a real server. # If you rename this file to `Makefile', you should change this line, # so that the .rsp files still depend on the correct makefile. MAKEFILE = Makefile.vc # C compilation flags CFLAGS = /nologo /W3 /O1 -I..\./ -I..\charset/ -I..\windows/ -I..\unix/ /D_WINDOWS /D_WIN32_WINDOWS=0x500 /DWINVER=0x500 /D_CRT_SECURE_NO_WARNINGS /D_ARM_WINAPI_PARTITION_DESKTOP_SDK_AVAILABLE LFLAGS = /incremental:no /dynamicbase /nxcompat RCFLAGS = -I..\./ -I..\charset/ -I..\windows/ -I..\unix/ -DWIN32 -D_WIN32 -DWINVER=0x0400 CFLAGS = $(CFLAGS) /DHAS_GSSAPI all: $(BUILDDIR)pageant.exe $(BUILDDIR)plink.exe $(BUILDDIR)pscp.exe \ $(BUILDDIR)psftp.exe $(BUILDDIR)psocks.exe \ $(BUILDDIR)putty.exe $(BUILDDIR)puttygen.exe \ $(BUILDDIR)puttytel.exe $(BUILDDIR)testcrypt.exe $(BUILDDIR)pageant.exe: $(BUILDDIR)aqsync.obj $(BUILDDIR)be_misc.obj \ $(BUILDDIR)callback.obj $(BUILDDIR)conf.obj \ $(BUILDDIR)ecc.obj $(BUILDDIR)errsock.obj \ $(BUILDDIR)marshal.obj $(BUILDDIR)memory.obj \ $(BUILDDIR)misc.obj $(BUILDDIR)mpint.obj \ $(BUILDDIR)pageant.obj $(BUILDDIR)pageant.res \ $(BUILDDIR)sshaes.obj $(BUILDDIR)sshargon2.obj \ $(BUILDDIR)sshauxcrypt.obj $(BUILDDIR)sshblake2.obj \ $(BUILDDIR)sshdes.obj $(BUILDDIR)sshdss.obj \ $(BUILDDIR)sshecc.obj $(BUILDDIR)sshhmac.obj \ $(BUILDDIR)sshmd5.obj $(BUILDDIR)sshpubk.obj \ $(BUILDDIR)sshrsa.obj $(BUILDDIR)sshsh256.obj \ $(BUILDDIR)sshsh512.obj $(BUILDDIR)sshsha.obj \ $(BUILDDIR)sshsha3.obj $(BUILDDIR)stripctrl.obj \ $(BUILDDIR)tree234.obj $(BUILDDIR)utils.obj \ $(BUILDDIR)version.obj $(BUILDDIR)wcwidth.obj \ $(BUILDDIR)wincapi.obj $(BUILDDIR)winhandl.obj \ $(BUILDDIR)winhelp.obj $(BUILDDIR)winhsock.obj \ $(BUILDDIR)winmisc.obj $(BUILDDIR)winmiscs.obj \ $(BUILDDIR)winnet.obj $(BUILDDIR)winnpc.obj \ $(BUILDDIR)winnps.obj $(BUILDDIR)winpgnt.obj \ $(BUILDDIR)winpgntc.obj $(BUILDDIR)winsecur.obj \ $(BUILDDIR)winselgui.obj $(BUILDDIR)winutils.obj type < includes its own WinSock # header file, so that it won't try to include . # # - COMPAT=-DWINSOCK_TWO (Windows only) # Causes the PuTTY utilities to include instead of # , except Plink which _needs_ WinSock 2 so it already # does this. # # - COMPAT=-DNO_SECURITY (Windows only) # Disables use of , which is not available with some # development environments (such as very old versions of the # mingw/Cygwin GNU toolchain). This has the following effects: # - Pageant won't care about the local user ID of processes # accessing it; a version of Pageant built with this option # will therefore refuse to run under NT-series OSes on # security grounds (although it will run fine on Win95-series # OSes where there is no access control anyway). # - SSH connection sharing is disabled. # - There is no support for restriction of the process ACLs. # # - COMPAT=-DNO_MULTIMON (Windows only) # Disables PuTTY's use of , which is not available # with some development environments. This means that PuTTY's # full-screen mode (configurable to work on Alt-Enter) will # not behave usefully in a multi-monitor environment. # # - COMPAT=-DNO_HTMLHELP (Windows only) # Disables PuTTY's use of , which is not available # with some development environments. # # If you don't have this header, you may be able to use the copy # supplied with HTML Help Workshop. # # - RCFL=-DNO_MANIFESTS (Windows only) # Disables inclusion of XML application manifests in the PuTTY # binaries. This may be necessary to build for 64-bit Windows; # the manifests are only included to use the XP GUI style on # Windows XP, and the architecture tags are a lie on 64-bit. # # - COMPAT=-DNO_IPV6 # Disables PuTTY's ability to make IPv6 connections, enabling # it to compile under development environments which do not # support IPv6 in their header files. # # - COMPAT=-DNO_GSSAPI # Disables PuTTY's ability to use GSSAPI functions for # authentication and key exchange. # # - COMPAT=-DSTATIC_GSSAPI # Causes PuTTY to try to link statically against the GSSAPI # library instead of the default of doing it at run time. # # - COMPAT=-DMSVC4 (Windows only) # - RCFL=-DMSVC4 # Makes a couple of minor changes so that PuTTY compiles using # MSVC 4. You will also need -DNO_SECURITY and -DNO_MULTIMON. # # - COMPAT=-DNO_SECUREZEROMEMORY (Windows only) # Disables PuTTY's use of SecureZeroMemory(), which is missing # from some environments' header files. # # - XFLAGS=-DDEBUG # Causes PuTTY to enable internal debugging. # # - XFLAGS=-DMALLOC_LOG # Causes PuTTY to emit a file called putty_mem.log, logging every # memory allocation and free, so you can track memory leaks. # # - XFLAGS=-DMINEFIELD (Windows only) # Causes PuTTY to use a custom memory allocator, similar in # concept to Electric Fence, in place of regular malloc(). Wastes # huge amounts of RAM, but should cause heap-corruption bugs to # show up as GPFs at the point of failure rather than appearing # later on as second-level damage. # # - XFLAGS=-DFUZZING # Builds a version of PuTTY with some tweaks to make fuzz testing # easier: the SSH random number generator is replaced by one that # always returns the same thing. Note that this makes SSH # completely insecure -- a FUZZING build should never be used to # connect to a real server. # If you rename this file to `Makefile', you should change this line, # so that the .rsp files still depend on the correct makefile. MAKEFILE = Makefile.lcc # C compilation flags CFLAGS = -D_WINDOWS -I..\./ -I..\charset/ -I..\windows/ -I..\unix/ # Resource compilation flags RCFLAGS = -I..\./ -I..\charset/ -I..\windows/ -I..\unix/ # Get include directory for resource compiler all: pageant.exe plink.exe pscp.exe psftp.exe psocks.exe putty.exe \ puttygen.exe puttytel.exe testcrypt.exe pageant.exe: aqsync.obj be_misc.obj callback.obj conf.obj ecc.obj \ errsock.obj marshal.obj memory.obj misc.obj mpint.obj \ pageant.obj pageant.res sshaes.obj sshargon2.obj \ sshauxcrypt.obj sshblake2.obj sshdes.obj sshdss.obj \ sshecc.obj sshhmac.obj sshmd5.obj sshpubk.obj sshrsa.obj \ sshsh256.obj sshsh512.obj sshsha.obj sshsha3.obj \ stripctrl.obj tree234.obj utils.obj version.obj wcwidth.obj \ wincapi.obj winhandl.obj winhelp.obj winhsock.obj \ winmisc.obj winmiscs.obj winnet.obj winnpc.obj winnps.obj \ winpgnt.obj winpgntc.obj winsecur.obj winselgui.obj \ winutils.obj lcclnk -subsystem windows -o pageant.exe aqsync.obj be_misc.obj \ callback.obj conf.obj ecc.obj errsock.obj marshal.obj \ memory.obj misc.obj mpint.obj pageant.obj pageant.res \ sshaes.obj sshargon2.obj sshauxcrypt.obj sshblake2.obj \ sshdes.obj sshdss.obj sshecc.obj sshhmac.obj sshmd5.obj \ sshpubk.obj sshrsa.obj sshsh256.obj sshsh512.obj sshsha.obj \ sshsha3.obj stripctrl.obj tree234.obj utils.obj version.obj \ wcwidth.obj wincapi.obj winhandl.obj winhelp.obj \ winhsock.obj winmisc.obj winmiscs.obj winnet.obj winnpc.obj \ winnps.obj winpgnt.obj winpgntc.obj winsecur.obj \ winselgui.obj winutils.obj shell32.lib wsock32.lib \ ws2_32.lib winspool.lib winmm.lib imm32.lib plink.exe: agentf.obj aqsync.obj be_all_s.obj be_misc.obj callback.obj \ clicons.obj cmdline.obj conf.obj console.obj cproxy.obj \ ecc.obj errsock.obj ldisc.obj logging.obj mainchan.obj \ marshal.obj memory.obj misc.obj miscucs.obj mpint.obj \ noterm.obj nullplug.obj pgssapi.obj pinger.obj plink.res \ portfwd.obj proxy.obj raw.obj rlogin.obj sessprep.obj \ settings.obj ssh.obj ssh1bpp.obj ssh1censor.obj \ ssh1connection.obj ssh1connection-client.obj ssh1login.obj \ ssh2bpp.obj ssh2bpp-bare.obj ssh2censor.obj \ ssh2connection.obj ssh2connection-client.obj \ ssh2kex-client.obj ssh2transhk.obj ssh2transport.obj \ ssh2userauth.obj sshaes.obj ssharcf.obj sshargon2.obj \ sshauxcrypt.obj sshblake2.obj sshblowf.obj sshccp.obj \ sshcommon.obj sshcrc.obj sshcrcda.obj sshdes.obj sshdh.obj \ sshdss.obj sshecc.obj sshgssc.obj sshhmac.obj sshmac.obj \ sshmd5.obj sshprng.obj sshpubk.obj sshrand.obj sshrsa.obj \ sshsh256.obj sshsh512.obj sshsha.obj sshsha3.obj \ sshshare.obj sshutils.obj sshverstring.obj sshzlib.obj \ stripctrl.obj supdup.obj telnet.obj timing.obj tree234.obj \ utils.obj version.obj wcwidth.obj wildcard.obj wincapi.obj \ wincliloop.obj wincons.obj windefs.obj wingss.obj \ winhandl.obj winhsock.obj winmisc.obj winmiscs.obj \ winnet.obj winnohlp.obj winnoise.obj winnojmp.obj winnpc.obj \ winnps.obj winpgntc.obj winplink.obj winproxy.obj \ winsecur.obj winselcli.obj winser.obj winshare.obj \ winstore.obj wintime.obj winucs.obj winx11.obj x11fwd.obj lcclnk -o plink.exe agentf.obj aqsync.obj be_all_s.obj be_misc.obj \ callback.obj clicons.obj cmdline.obj conf.obj console.obj \ cproxy.obj ecc.obj errsock.obj ldisc.obj logging.obj \ mainchan.obj marshal.obj memory.obj misc.obj miscucs.obj \ mpint.obj noterm.obj nullplug.obj pgssapi.obj pinger.obj \ plink.res portfwd.obj proxy.obj raw.obj rlogin.obj \ sessprep.obj settings.obj ssh.obj ssh1bpp.obj ssh1censor.obj \ ssh1connection.obj ssh1connection-client.obj ssh1login.obj \ ssh2bpp.obj ssh2bpp-bare.obj ssh2censor.obj \ ssh2connection.obj ssh2connection-client.obj \ ssh2kex-client.obj ssh2transhk.obj ssh2transport.obj \ ssh2userauth.obj sshaes.obj ssharcf.obj sshargon2.obj \ sshauxcrypt.obj sshblake2.obj sshblowf.obj sshccp.obj \ sshcommon.obj sshcrc.obj sshcrcda.obj sshdes.obj sshdh.obj \ sshdss.obj sshecc.obj sshgssc.obj sshhmac.obj sshmac.obj \ sshmd5.obj sshprng.obj sshpubk.obj sshrand.obj sshrsa.obj \ sshsh256.obj sshsh512.obj sshsha.obj sshsha3.obj \ sshshare.obj sshutils.obj sshverstring.obj sshzlib.obj \ stripctrl.obj supdup.obj telnet.obj timing.obj tree234.obj \ utils.obj version.obj wcwidth.obj wildcard.obj wincapi.obj \ wincliloop.obj wincons.obj windefs.obj wingss.obj \ winhandl.obj winhsock.obj winmisc.obj winmiscs.obj \ winnet.obj winnohlp.obj winnoise.obj winnojmp.obj winnpc.obj \ winnps.obj winpgntc.obj winplink.obj winproxy.obj \ winsecur.obj winselcli.obj winser.obj winshare.obj \ winstore.obj wintime.obj winucs.obj winx11.obj x11fwd.obj \ shell32.lib wsock32.lib ws2_32.lib winspool.lib winmm.lib \ imm32.lib pscp.exe: agentf.obj aqsync.obj be_misc.obj be_ssh.obj callback.obj \ clicons.obj cmdline.obj conf.obj console.obj cproxy.obj \ ecc.obj errsock.obj logging.obj mainchan.obj marshal.obj \ memory.obj misc.obj miscucs.obj mpint.obj nullplug.obj \ pgssapi.obj pinger.obj portfwd.obj proxy.obj pscp.obj \ pscp.res psftpcommon.obj settings.obj sftp.obj \ sftpcommon.obj ssh.obj ssh1bpp.obj ssh1censor.obj \ ssh1connection.obj ssh1connection-client.obj ssh1login.obj \ ssh2bpp.obj ssh2bpp-bare.obj ssh2censor.obj \ ssh2connection.obj ssh2connection-client.obj \ ssh2kex-client.obj ssh2transhk.obj ssh2transport.obj \ ssh2userauth.obj sshaes.obj ssharcf.obj sshargon2.obj \ sshauxcrypt.obj sshblake2.obj sshblowf.obj sshccp.obj \ sshcommon.obj sshcrc.obj sshcrcda.obj sshdes.obj sshdh.obj \ sshdss.obj sshecc.obj sshgssc.obj sshhmac.obj sshmac.obj \ sshmd5.obj sshprng.obj sshpubk.obj sshrand.obj sshrsa.obj \ sshsh256.obj sshsh512.obj sshsha.obj sshsha3.obj \ sshshare.obj sshutils.obj sshverstring.obj sshzlib.obj \ stripctrl.obj timing.obj tree234.obj utils.obj version.obj \ wcwidth.obj wildcard.obj wincapi.obj wincliloop.obj \ wincons.obj windefs.obj wingss.obj winhandl.obj winhsock.obj \ winmisc.obj winmiscs.obj winnet.obj winnohlp.obj \ winnoise.obj winnojmp.obj winnpc.obj winnps.obj winpgntc.obj \ winproxy.obj winsecur.obj winselcli.obj winsftp.obj \ winshare.obj winstore.obj wintime.obj winucs.obj x11fwd.obj lcclnk -o pscp.exe agentf.obj aqsync.obj be_misc.obj be_ssh.obj \ callback.obj clicons.obj cmdline.obj conf.obj console.obj \ cproxy.obj ecc.obj errsock.obj logging.obj mainchan.obj \ marshal.obj memory.obj misc.obj miscucs.obj mpint.obj \ nullplug.obj pgssapi.obj pinger.obj portfwd.obj proxy.obj \ pscp.obj pscp.res psftpcommon.obj settings.obj sftp.obj \ sftpcommon.obj ssh.obj ssh1bpp.obj ssh1censor.obj \ ssh1connection.obj ssh1connection-client.obj ssh1login.obj \ ssh2bpp.obj ssh2bpp-bare.obj ssh2censor.obj \ ssh2connection.obj ssh2connection-client.obj \ ssh2kex-client.obj ssh2transhk.obj ssh2transport.obj \ ssh2userauth.obj sshaes.obj ssharcf.obj sshargon2.obj \ sshauxcrypt.obj sshblake2.obj sshblowf.obj sshccp.obj \ sshcommon.obj sshcrc.obj sshcrcda.obj sshdes.obj sshdh.obj \ sshdss.obj sshecc.obj sshgssc.obj sshhmac.obj sshmac.obj \ sshmd5.obj sshprng.obj sshpubk.obj sshrand.obj sshrsa.obj \ sshsh256.obj sshsh512.obj sshsha.obj sshsha3.obj \ sshshare.obj sshutils.obj sshverstring.obj sshzlib.obj \ stripctrl.obj timing.obj tree234.obj utils.obj version.obj \ wcwidth.obj wildcard.obj wincapi.obj wincliloop.obj \ wincons.obj windefs.obj wingss.obj winhandl.obj winhsock.obj \ winmisc.obj winmiscs.obj winnet.obj winnohlp.obj \ winnoise.obj winnojmp.obj winnpc.obj winnps.obj winpgntc.obj \ winproxy.obj winsecur.obj winselcli.obj winsftp.obj \ winshare.obj winstore.obj wintime.obj winucs.obj x11fwd.obj \ shell32.lib wsock32.lib ws2_32.lib winspool.lib winmm.lib \ imm32.lib psftp.exe: agentf.obj aqsync.obj be_misc.obj be_ssh.obj callback.obj \ clicons.obj cmdline.obj conf.obj console.obj cproxy.obj \ ecc.obj errsock.obj logging.obj mainchan.obj marshal.obj \ memory.obj misc.obj miscucs.obj mpint.obj nullplug.obj \ pgssapi.obj pinger.obj portfwd.obj proxy.obj psftp.obj \ psftp.res psftpcommon.obj settings.obj sftp.obj \ sftpcommon.obj ssh.obj ssh1bpp.obj ssh1censor.obj \ ssh1connection.obj ssh1connection-client.obj ssh1login.obj \ ssh2bpp.obj ssh2bpp-bare.obj ssh2censor.obj \ ssh2connection.obj ssh2connection-client.obj \ ssh2kex-client.obj ssh2transhk.obj ssh2transport.obj \ ssh2userauth.obj sshaes.obj ssharcf.obj sshargon2.obj \ sshauxcrypt.obj sshblake2.obj sshblowf.obj sshccp.obj \ sshcommon.obj sshcrc.obj sshcrcda.obj sshdes.obj sshdh.obj \ sshdss.obj sshecc.obj sshgssc.obj sshhmac.obj sshmac.obj \ sshmd5.obj sshprng.obj sshpubk.obj sshrand.obj sshrsa.obj \ sshsh256.obj sshsh512.obj sshsha.obj sshsha3.obj \ sshshare.obj sshutils.obj sshverstring.obj sshzlib.obj \ stripctrl.obj timing.obj tree234.obj utils.obj version.obj \ wcwidth.obj wildcard.obj wincapi.obj wincliloop.obj \ wincons.obj windefs.obj wingss.obj winhandl.obj winhsock.obj \ winmisc.obj winmiscs.obj winnet.obj winnohlp.obj \ winnoise.obj winnojmp.obj winnpc.obj winnps.obj winpgntc.obj \ winproxy.obj winsecur.obj winselcli.obj winsftp.obj \ winshare.obj winstore.obj wintime.obj winucs.obj x11fwd.obj lcclnk -o psftp.exe agentf.obj aqsync.obj be_misc.obj be_ssh.obj \ callback.obj clicons.obj cmdline.obj conf.obj console.obj \ cproxy.obj ecc.obj errsock.obj logging.obj mainchan.obj \ marshal.obj memory.obj misc.obj miscucs.obj mpint.obj \ nullplug.obj pgssapi.obj pinger.obj portfwd.obj proxy.obj \ psftp.obj psftp.res psftpcommon.obj settings.obj sftp.obj \ sftpcommon.obj ssh.obj ssh1bpp.obj ssh1censor.obj \ ssh1connection.obj ssh1connection-client.obj ssh1login.obj \ ssh2bpp.obj ssh2bpp-bare.obj ssh2censor.obj \ ssh2connection.obj ssh2connection-client.obj \ ssh2kex-client.obj ssh2transhk.obj ssh2transport.obj \ ssh2userauth.obj sshaes.obj ssharcf.obj sshargon2.obj \ sshauxcrypt.obj sshblake2.obj sshblowf.obj sshccp.obj \ sshcommon.obj sshcrc.obj sshcrcda.obj sshdes.obj sshdh.obj \ sshdss.obj sshecc.obj sshgssc.obj sshhmac.obj sshmac.obj \ sshmd5.obj sshprng.obj sshpubk.obj sshrand.obj sshrsa.obj \ sshsh256.obj sshsh512.obj sshsha.obj sshsha3.obj \ sshshare.obj sshutils.obj sshverstring.obj sshzlib.obj \ stripctrl.obj timing.obj tree234.obj utils.obj version.obj \ wcwidth.obj wildcard.obj wincapi.obj wincliloop.obj \ wincons.obj windefs.obj wingss.obj winhandl.obj winhsock.obj \ winmisc.obj winmiscs.obj winnet.obj winnohlp.obj \ winnoise.obj winnojmp.obj winnpc.obj winnps.obj winpgntc.obj \ winproxy.obj winsecur.obj winselcli.obj winsftp.obj \ winshare.obj winstore.obj wintime.obj winucs.obj x11fwd.obj \ shell32.lib wsock32.lib ws2_32.lib winspool.lib winmm.lib \ imm32.lib psocks.exe: be_misc.obj callback.obj conf.obj console.obj errsock.obj \ logging.obj marshal.obj memory.obj misc.obj nocproxy.obj \ norand.obj portfwd.obj proxy.obj psocks.obj sshutils.obj \ stripctrl.obj time.obj timing.obj tree234.obj utils.obj \ version.obj wcwidth.obj wincliloop.obj wincons.obj \ winhandl.obj winhsock.obj winmisc.obj winmiscs.obj \ winnet.obj winnohlp.obj winproxy.obj winselcli.obj \ winsocks.obj lcclnk -o psocks.exe be_misc.obj callback.obj conf.obj console.obj \ errsock.obj logging.obj marshal.obj memory.obj misc.obj \ nocproxy.obj norand.obj portfwd.obj proxy.obj psocks.obj \ sshutils.obj stripctrl.obj time.obj timing.obj tree234.obj \ utils.obj version.obj wcwidth.obj wincliloop.obj wincons.obj \ winhandl.obj winhsock.obj winmisc.obj winmiscs.obj \ winnet.obj winnohlp.obj winproxy.obj winselcli.obj \ winsocks.obj shell32.lib wsock32.lib ws2_32.lib winspool.lib \ winmm.lib imm32.lib putty.exe: agentf.obj aqsync.obj be_all_s.obj be_misc.obj callback.obj \ cmdline.obj conf.obj config.obj cproxy.obj dialog.obj \ ecc.obj errsock.obj ldisc.obj logging.obj mainchan.obj \ marshal.obj memory.obj minibidi.obj misc.obj miscucs.obj \ mpint.obj nullplug.obj pgssapi.obj pinger.obj portfwd.obj \ proxy.obj putty.res raw.obj rlogin.obj sessprep.obj \ settings.obj sizetip.obj ssh.obj ssh1bpp.obj ssh1censor.obj \ ssh1connection.obj ssh1connection-client.obj ssh1login.obj \ ssh2bpp.obj ssh2bpp-bare.obj ssh2censor.obj \ ssh2connection.obj ssh2connection-client.obj \ ssh2kex-client.obj ssh2transhk.obj ssh2transport.obj \ ssh2userauth.obj sshaes.obj ssharcf.obj sshargon2.obj \ sshauxcrypt.obj sshblake2.obj sshblowf.obj sshccp.obj \ sshcommon.obj sshcrc.obj sshcrcda.obj sshdes.obj sshdh.obj \ sshdss.obj sshecc.obj sshgssc.obj sshhmac.obj sshmac.obj \ sshmd5.obj sshprng.obj sshpubk.obj sshrand.obj sshrsa.obj \ sshsh256.obj sshsh512.obj sshsha.obj sshsha3.obj \ sshshare.obj sshutils.obj sshverstring.obj sshzlib.obj \ stripctrl.obj supdup.obj telnet.obj terminal.obj timing.obj \ tree234.obj utils.obj version.obj wcwidth.obj wildcard.obj \ wincapi.obj wincfg.obj winctrls.obj windefs.obj windlg.obj \ window.obj wingss.obj winhandl.obj winhelp.obj winhsock.obj \ winjump.obj winmisc.obj winmiscs.obj winnet.obj winnoise.obj \ winnpc.obj winnps.obj winpgntc.obj winprint.obj winproxy.obj \ winsecur.obj winselgui.obj winser.obj winshare.obj \ winstore.obj wintime.obj winucs.obj winutils.obj winx11.obj \ x11fwd.obj lcclnk -subsystem windows -o putty.exe agentf.obj aqsync.obj be_all_s.obj \ be_misc.obj callback.obj cmdline.obj conf.obj config.obj \ cproxy.obj dialog.obj ecc.obj errsock.obj ldisc.obj \ logging.obj mainchan.obj marshal.obj memory.obj minibidi.obj \ misc.obj miscucs.obj mpint.obj nullplug.obj pgssapi.obj \ pinger.obj portfwd.obj proxy.obj putty.res raw.obj \ rlogin.obj sessprep.obj settings.obj sizetip.obj ssh.obj \ ssh1bpp.obj ssh1censor.obj ssh1connection.obj \ ssh1connection-client.obj ssh1login.obj ssh2bpp.obj \ ssh2bpp-bare.obj ssh2censor.obj ssh2connection.obj \ ssh2connection-client.obj ssh2kex-client.obj ssh2transhk.obj \ ssh2transport.obj ssh2userauth.obj sshaes.obj ssharcf.obj \ sshargon2.obj sshauxcrypt.obj sshblake2.obj sshblowf.obj \ sshccp.obj sshcommon.obj sshcrc.obj sshcrcda.obj sshdes.obj \ sshdh.obj sshdss.obj sshecc.obj sshgssc.obj sshhmac.obj \ sshmac.obj sshmd5.obj sshprng.obj sshpubk.obj sshrand.obj \ sshrsa.obj sshsh256.obj sshsh512.obj sshsha.obj sshsha3.obj \ sshshare.obj sshutils.obj sshverstring.obj sshzlib.obj \ stripctrl.obj supdup.obj telnet.obj terminal.obj timing.obj \ tree234.obj utils.obj version.obj wcwidth.obj wildcard.obj \ wincapi.obj wincfg.obj winctrls.obj windefs.obj windlg.obj \ window.obj wingss.obj winhandl.obj winhelp.obj winhsock.obj \ winjump.obj winmisc.obj winmiscs.obj winnet.obj winnoise.obj \ winnpc.obj winnps.obj winpgntc.obj winprint.obj winproxy.obj \ winsecur.obj winselgui.obj winser.obj winshare.obj \ winstore.obj wintime.obj winucs.obj winutils.obj winx11.obj \ x11fwd.obj shell32.lib wsock32.lib ws2_32.lib winspool.lib \ winmm.lib imm32.lib puttygen.exe: conf.obj ecc.obj import.obj marshal.obj memory.obj \ millerrabin.obj misc.obj mpint.obj mpunsafe.obj notiming.obj \ pockle.obj primecandidate.obj puttygen.res smallprimes.obj \ sshaes.obj sshargon2.obj sshauxcrypt.obj sshbcrypt.obj \ sshblake2.obj sshblowf.obj sshdes.obj sshdss.obj sshdssg.obj \ sshecc.obj sshecdsag.obj sshhmac.obj sshmd5.obj sshprime.obj \ sshprng.obj sshpubk.obj sshrand.obj sshrsa.obj sshrsag.obj \ sshsh256.obj sshsh512.obj sshsha.obj sshsha3.obj \ stripctrl.obj tree234.obj utils.obj version.obj wcwidth.obj \ winctrls.obj winhelp.obj winmisc.obj winmiscs.obj \ winnoise.obj winnojmp.obj winpgen.obj winsecur.obj \ winstore.obj wintime.obj winutils.obj lcclnk -subsystem windows -o puttygen.exe conf.obj ecc.obj import.obj \ marshal.obj memory.obj millerrabin.obj misc.obj mpint.obj \ mpunsafe.obj notiming.obj pockle.obj primecandidate.obj \ puttygen.res smallprimes.obj sshaes.obj sshargon2.obj \ sshauxcrypt.obj sshbcrypt.obj sshblake2.obj sshblowf.obj \ sshdes.obj sshdss.obj sshdssg.obj sshecc.obj sshecdsag.obj \ sshhmac.obj sshmd5.obj sshprime.obj sshprng.obj sshpubk.obj \ sshrand.obj sshrsa.obj sshrsag.obj sshsh256.obj sshsh512.obj \ sshsha.obj sshsha3.obj stripctrl.obj tree234.obj utils.obj \ version.obj wcwidth.obj winctrls.obj winhelp.obj winmisc.obj \ winmiscs.obj winnoise.obj winnojmp.obj winpgen.obj \ winsecur.obj winstore.obj wintime.obj winutils.obj \ shell32.lib wsock32.lib ws2_32.lib winspool.lib winmm.lib \ imm32.lib puttytel.exe: be_misc.obj be_nos_s.obj callback.obj cmdline.obj conf.obj \ config.obj dialog.obj errsock.obj ldisc.obj logging.obj \ marshal.obj memory.obj minibidi.obj misc.obj miscucs.obj \ nocproxy.obj nogss.obj norand.obj pinger.obj proxy.obj \ puttytel.res raw.obj rlogin.obj sessprep.obj settings.obj \ sizetip.obj stripctrl.obj supdup.obj telnet.obj terminal.obj \ timing.obj tree234.obj utils.obj version.obj wcwidth.obj \ wincfg.obj winctrls.obj windefs.obj windlg.obj window.obj \ winhandl.obj winhelp.obj winhsock.obj winjump.obj \ winmisc.obj winmiscs.obj winnet.obj winprint.obj \ winproxy.obj winsecur.obj winselgui.obj winser.obj \ winstore.obj wintime.obj winucs.obj winutils.obj lcclnk -subsystem windows -o puttytel.exe be_misc.obj be_nos_s.obj \ callback.obj cmdline.obj conf.obj config.obj dialog.obj \ errsock.obj ldisc.obj logging.obj marshal.obj memory.obj \ minibidi.obj misc.obj miscucs.obj nocproxy.obj nogss.obj \ norand.obj pinger.obj proxy.obj puttytel.res raw.obj \ rlogin.obj sessprep.obj settings.obj sizetip.obj \ stripctrl.obj supdup.obj telnet.obj terminal.obj timing.obj \ tree234.obj utils.obj version.obj wcwidth.obj wincfg.obj \ winctrls.obj windefs.obj windlg.obj window.obj winhandl.obj \ winhelp.obj winhsock.obj winjump.obj winmisc.obj \ winmiscs.obj winnet.obj winprint.obj winproxy.obj \ winsecur.obj winselgui.obj winser.obj winstore.obj \ wintime.obj winucs.obj winutils.obj shell32.lib wsock32.lib \ ws2_32.lib winspool.lib winmm.lib imm32.lib testcrypt.exe: ecc.obj marshal.obj memory.obj millerrabin.obj mpint.obj \ mpunsafe.obj pockle.obj primecandidate.obj smallprimes.obj \ sshaes.obj ssharcf.obj sshargon2.obj sshauxcrypt.obj \ sshblake2.obj sshblowf.obj sshccp.obj sshcrc.obj \ sshcrcda.obj sshdes.obj sshdh.obj sshdss.obj sshdssg.obj \ sshecc.obj sshecdsag.obj sshhmac.obj sshmd5.obj sshprime.obj \ sshprng.obj sshpubk.obj sshrsa.obj sshrsag.obj sshsh256.obj \ sshsh512.obj sshsha.obj sshsha3.obj testcrypt.obj \ tree234.obj utils.obj winmiscs.obj lcclnk -o testcrypt.exe ecc.obj marshal.obj memory.obj millerrabin.obj \ mpint.obj mpunsafe.obj pockle.obj primecandidate.obj \ smallprimes.obj sshaes.obj ssharcf.obj sshargon2.obj \ sshauxcrypt.obj sshblake2.obj sshblowf.obj sshccp.obj \ sshcrc.obj sshcrcda.obj sshdes.obj sshdh.obj sshdss.obj \ sshdssg.obj sshecc.obj sshecdsag.obj sshhmac.obj sshmd5.obj \ sshprime.obj sshprng.obj sshpubk.obj sshrsa.obj sshrsag.obj \ sshsh256.obj sshsh512.obj sshsha.obj sshsha3.obj \ testcrypt.obj tree234.obj utils.obj winmiscs.obj shell32.lib \ wsock32.lib ws2_32.lib winspool.lib winmm.lib imm32.lib agentf.obj: ..\agentf.c ..\putty.h ..\ssh.h ..\pageant.h ..\sshchan.h \ ..\defs.h ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\agentf.c aqsync.obj: ..\aqsync.c ..\putty.h ..\defs.h ..\puttyps.h ..\network.h \ ..\misc.h ..\marshal.h ..\sshsignals.h ..\windows\winstuff.h \ ..\unix\unix.h ..\puttymem.h ..\tree234.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\aqsync.c be_all_s.obj: ..\be_all_s.c ..\putty.h ..\defs.h ..\puttyps.h ..\network.h \ ..\misc.h ..\marshal.h ..\sshsignals.h ..\windows\winstuff.h \ ..\unix\unix.h ..\puttymem.h ..\tree234.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\be_all_s.c be_misc.obj: ..\be_misc.c ..\putty.h ..\network.h ..\defs.h ..\puttyps.h \ ..\misc.h ..\marshal.h ..\sshsignals.h ..\windows\winstuff.h \ ..\unix\unix.h ..\puttymem.h ..\tree234.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\be_misc.c be_none.obj: ..\be_none.c ..\putty.h ..\defs.h ..\puttyps.h ..\network.h \ ..\misc.h ..\marshal.h ..\sshsignals.h ..\windows\winstuff.h \ ..\unix\unix.h ..\puttymem.h ..\tree234.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\be_none.c be_nos_s.obj: ..\be_nos_s.c ..\putty.h ..\defs.h ..\puttyps.h ..\network.h \ ..\misc.h ..\marshal.h ..\sshsignals.h ..\windows\winstuff.h \ ..\unix\unix.h ..\puttymem.h ..\tree234.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\be_nos_s.c be_ssh.obj: ..\be_ssh.c ..\putty.h ..\defs.h ..\puttyps.h ..\network.h \ ..\misc.h ..\marshal.h ..\sshsignals.h ..\windows\winstuff.h \ ..\unix\unix.h ..\puttymem.h ..\tree234.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\be_ssh.c callback.obj: ..\callback.c ..\putty.h ..\defs.h ..\puttyps.h ..\network.h \ ..\misc.h ..\marshal.h ..\sshsignals.h ..\windows\winstuff.h \ ..\unix\unix.h ..\puttymem.h ..\tree234.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\callback.c cgtest.obj: ..\cgtest.c ..\cmdgen.c ..\putty.h ..\ssh.h ..\sshkeygen.h \ ..\mpint.h ..\defs.h ..\puttyps.h ..\network.h ..\misc.h \ ..\marshal.h ..\sshsignals.h ..\puttymem.h ..\tree234.h \ ..\sshttymodes.h ..\windows\winstuff.h ..\unix\unix.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\cgtest.c clicons.obj: ..\clicons.c ..\putty.h ..\defs.h ..\puttyps.h ..\network.h \ ..\misc.h ..\marshal.h ..\sshsignals.h ..\windows\winstuff.h \ ..\unix\unix.h ..\puttymem.h ..\tree234.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\clicons.c cmdgen.obj: ..\cmdgen.c ..\putty.h ..\ssh.h ..\sshkeygen.h ..\mpint.h \ ..\defs.h ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\cmdgen.c cmdline.obj: ..\cmdline.c ..\putty.h ..\defs.h ..\puttyps.h ..\network.h \ ..\misc.h ..\marshal.h ..\sshsignals.h ..\windows\winstuff.h \ ..\unix\unix.h ..\puttymem.h ..\tree234.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\cmdline.c conf.obj: ..\conf.c ..\tree234.h ..\putty.h ..\defs.h ..\puttyps.h \ ..\network.h ..\misc.h ..\marshal.h ..\sshsignals.h \ ..\windows\winstuff.h ..\unix\unix.h ..\puttymem.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\conf.c config.obj: ..\config.c ..\putty.h ..\dialog.h ..\storage.h ..\defs.h \ ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\windows\winstuff.h ..\unix\unix.h \ ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\config.c console.obj: ..\console.c ..\putty.h ..\misc.h ..\console.h ..\defs.h \ ..\puttyps.h ..\network.h ..\marshal.h ..\sshsignals.h \ ..\puttymem.h ..\windows\winstuff.h ..\unix\unix.h \ ..\tree234.h ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\console.c cproxy.obj: ..\cproxy.c ..\putty.h ..\ssh.h ..\network.h ..\proxy.h \ ..\marshal.h ..\defs.h ..\puttyps.h ..\misc.h \ ..\sshsignals.h ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\cproxy.c dialog.obj: ..\dialog.c ..\putty.h ..\dialog.h ..\defs.h ..\puttyps.h \ ..\network.h ..\misc.h ..\marshal.h ..\sshsignals.h \ ..\windows\winstuff.h ..\unix\unix.h ..\puttymem.h \ ..\tree234.h ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\dialog.c ecc.obj: ..\ecc.c ..\ssh.h ..\mpint.h ..\ecc.h ..\puttymem.h ..\tree234.h \ ..\network.h ..\misc.h ..\sshttymodes.h ..\defs.h \ ..\marshal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\ecc.c errsock.obj: ..\errsock.c ..\tree234.h ..\putty.h ..\network.h ..\defs.h \ ..\puttyps.h ..\misc.h ..\marshal.h ..\sshsignals.h \ ..\windows\winstuff.h ..\unix\unix.h ..\puttymem.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\errsock.c fromucs.obj: ..\charset\fromucs.c ..\charset\charset.h ..\charset\internal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\charset\fromucs.c fuzzterm.obj: ..\fuzzterm.c ..\putty.h ..\dialog.h ..\terminal.h ..\defs.h \ ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\tree234.h ..\windows\winstuff.h \ ..\unix\unix.h ..\puttymem.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\fuzzterm.c gtkapp.obj: ..\unix\gtkapp.c ..\putty.h ..\unix\gtkmisc.h ..\defs.h \ ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\windows\winstuff.h ..\unix\unix.h \ ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\gtkapp.c gtkask.obj: ..\unix\gtkask.c ..\defs.h ..\unix\gtkfont.h ..\unix\gtkcompat.h \ ..\unix\gtkmisc.h ..\putty.h ..\ssh.h ..\misc.h ..\puttyps.h \ ..\network.h ..\marshal.h ..\sshsignals.h ..\puttymem.h \ ..\tree234.h ..\sshttymodes.h ..\windows\winstuff.h \ ..\unix\unix.h ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\gtkask.c gtkcfg.obj: ..\unix\gtkcfg.c ..\putty.h ..\dialog.h ..\storage.h ..\defs.h \ ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\windows\winstuff.h ..\unix\unix.h \ ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\gtkcfg.c gtkcols.obj: ..\unix\gtkcols.c ..\defs.h ..\unix\gtkcompat.h \ ..\unix\gtkcols.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\gtkcols.c gtkcomm.obj: ..\unix\gtkcomm.c ..\putty.h ..\terminal.h ..\unix\gtkcompat.h \ ..\unix\gtkfont.h ..\unix\gtkmisc.h ..\defs.h ..\puttyps.h \ ..\network.h ..\misc.h ..\marshal.h ..\sshsignals.h \ ..\tree234.h ..\windows\winstuff.h ..\unix\unix.h \ ..\puttymem.h ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\gtkcomm.c gtkdlg.obj: ..\unix\gtkdlg.c ..\putty.h ..\unix\gtkcompat.h \ ..\unix\gtkcols.h ..\unix\gtkfont.h ..\unix\gtkmisc.h \ ..\unix\x11misc.h ..\storage.h ..\dialog.h ..\tree234.h \ ..\licence.h ..\ssh.h ..\defs.h ..\puttyps.h ..\network.h \ ..\misc.h ..\marshal.h ..\sshsignals.h ..\puttymem.h \ ..\sshttymodes.h ..\windows\winstuff.h ..\unix\unix.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\gtkdlg.c gtkfont.obj: ..\unix\gtkfont.c ..\putty.h ..\unix\gtkfont.h \ ..\unix\gtkcompat.h ..\unix\gtkmisc.h ..\tree234.h \ ..\unix\x11misc.h ..\defs.h ..\puttyps.h ..\network.h \ ..\misc.h ..\marshal.h ..\sshsignals.h ..\windows\winstuff.h \ ..\unix\unix.h ..\puttymem.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\gtkfont.c gtkmain.obj: ..\unix\gtkmain.c ..\putty.h ..\terminal.h ..\unix\gtkcompat.h \ ..\unix\gtkfont.h ..\unix\gtkmisc.h ..\unix\x11misc.h \ ..\defs.h ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\tree234.h ..\windows\winstuff.h \ ..\unix\unix.h ..\puttymem.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\gtkmain.c gtkmisc.obj: ..\unix\gtkmisc.c ..\putty.h ..\unix\gtkcompat.h ..\defs.h \ ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\windows\winstuff.h ..\unix\unix.h \ ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\gtkmisc.c gtkwin.obj: ..\unix\gtkwin.c ..\putty.h ..\terminal.h ..\unix\gtkcompat.h \ ..\unix\gtkfont.h ..\unix\gtkmisc.h ..\unix\x11misc.h \ ..\defs.h ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\tree234.h ..\windows\winstuff.h \ ..\unix\unix.h ..\puttymem.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\gtkwin.c import.obj: ..\import.c ..\putty.h ..\ssh.h ..\mpint.h ..\misc.h ..\defs.h \ ..\puttyps.h ..\network.h ..\marshal.h ..\sshsignals.h \ ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\import.c ldisc.obj: ..\ldisc.c ..\putty.h ..\terminal.h ..\ldisc.h ..\defs.h \ ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\tree234.h ..\windows\winstuff.h \ ..\unix\unix.h ..\puttymem.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\ldisc.c localenc.obj: ..\charset\localenc.c ..\charset\charset.h \ ..\charset\internal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\charset\localenc.c logging.obj: ..\logging.c ..\putty.h ..\defs.h ..\puttyps.h ..\network.h \ ..\misc.h ..\marshal.h ..\sshsignals.h ..\windows\winstuff.h \ ..\unix\unix.h ..\puttymem.h ..\tree234.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\logging.c macenc.obj: ..\charset\macenc.c ..\charset\charset.h ..\charset\internal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\charset\macenc.c mainchan.obj: ..\mainchan.c ..\putty.h ..\ssh.h ..\sshppl.h ..\sshchan.h \ ..\sshsignals.h ..\defs.h ..\puttyps.h ..\network.h \ ..\misc.h ..\marshal.h ..\puttymem.h ..\tree234.h \ ..\sshttymodes.h ..\windows\winstuff.h ..\unix\unix.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\mainchan.c marshal.obj: ..\marshal.c ..\marshal.h ..\misc.h ..\defs.h ..\puttymem.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\marshal.c memory.obj: ..\memory.c ..\defs.h ..\puttymem.h ..\misc.h ..\marshal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\memory.c millerrabin.obj: ..\millerrabin.c ..\ssh.h ..\sshkeygen.h ..\mpint.h \ ..\mpunsafe.h ..\puttymem.h ..\tree234.h ..\network.h \ ..\misc.h ..\sshttymodes.h ..\defs.h ..\marshal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\millerrabin.c mimeenc.obj: ..\charset\mimeenc.c ..\charset\charset.h ..\charset\internal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\charset\mimeenc.c minibidi.obj: ..\minibidi.c ..\putty.h ..\misc.h ..\defs.h ..\puttyps.h \ ..\network.h ..\marshal.h ..\sshsignals.h ..\puttymem.h \ ..\windows\winstuff.h ..\unix\unix.h ..\tree234.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\minibidi.c misc.obj: ..\misc.c ..\defs.h ..\putty.h ..\misc.h ..\puttyps.h ..\network.h \ ..\marshal.h ..\sshsignals.h ..\puttymem.h \ ..\windows\winstuff.h ..\unix\unix.h ..\tree234.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\misc.c miscucs.obj: ..\miscucs.c ..\putty.h ..\misc.h ..\defs.h ..\puttyps.h \ ..\network.h ..\marshal.h ..\sshsignals.h ..\puttymem.h \ ..\windows\winstuff.h ..\unix\unix.h ..\tree234.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\miscucs.c mpint.obj: ..\mpint.c ..\defs.h ..\misc.h ..\puttymem.h ..\mpint.h \ ..\mpint_i.h ..\marshal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\mpint.c mpunsafe.obj: ..\mpunsafe.c ..\defs.h ..\misc.h ..\puttymem.h ..\mpint.h \ ..\mpint_i.h ..\marshal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\mpunsafe.c nocmdline.obj: ..\nocmdline.c ..\putty.h ..\defs.h ..\puttyps.h ..\network.h \ ..\misc.h ..\marshal.h ..\sshsignals.h ..\windows\winstuff.h \ ..\unix\unix.h ..\puttymem.h ..\tree234.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\nocmdline.c nocproxy.obj: ..\nocproxy.c ..\putty.h ..\network.h ..\proxy.h ..\defs.h \ ..\puttyps.h ..\misc.h ..\marshal.h ..\sshsignals.h \ ..\windows\winstuff.h ..\unix\unix.h ..\puttymem.h \ ..\tree234.h ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\nocproxy.c nogss.obj: ..\nogss.c ..\putty.h ..\defs.h ..\puttyps.h ..\network.h \ ..\misc.h ..\marshal.h ..\sshsignals.h ..\windows\winstuff.h \ ..\unix\unix.h ..\puttymem.h ..\tree234.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\nogss.c norand.obj: ..\norand.c ..\putty.h ..\defs.h ..\puttyps.h ..\network.h \ ..\misc.h ..\marshal.h ..\sshsignals.h ..\windows\winstuff.h \ ..\unix\unix.h ..\puttymem.h ..\tree234.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\norand.c noterm.obj: ..\noterm.c ..\putty.h ..\terminal.h ..\defs.h ..\puttyps.h \ ..\network.h ..\misc.h ..\marshal.h ..\sshsignals.h \ ..\tree234.h ..\windows\winstuff.h ..\unix\unix.h \ ..\puttymem.h ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\noterm.c notiming.obj: ..\notiming.c ..\putty.h ..\defs.h ..\puttyps.h ..\network.h \ ..\misc.h ..\marshal.h ..\sshsignals.h ..\windows\winstuff.h \ ..\unix\unix.h ..\puttymem.h ..\tree234.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\notiming.c nullplug.obj: ..\nullplug.c ..\putty.h ..\defs.h ..\puttyps.h ..\network.h \ ..\misc.h ..\marshal.h ..\sshsignals.h ..\windows\winstuff.h \ ..\unix\unix.h ..\puttymem.h ..\tree234.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\nullplug.c osxlaunch.obj: ..\unix\osxlaunch.c lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\osxlaunch.c pageant.obj: ..\pageant.c ..\putty.h ..\mpint.h ..\ssh.h ..\sshcr.h \ ..\pageant.h ..\defs.h ..\puttyps.h ..\network.h ..\misc.h \ ..\marshal.h ..\sshsignals.h ..\puttymem.h ..\tree234.h \ ..\sshttymodes.h ..\windows\winstuff.h ..\unix\unix.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\pageant.c pageant.res: ..\windows\pageant.rc ..\windows\rcstuff.h \ ..\windows\pageant-rc.h ..\windows\winhelp.rc2 \ ..\windows\pageant.ico ..\windows\pageants.ico \ ..\windows\version.rc2 ..\windows\pageant.mft \ ..\windows\win_res.h ..\version.h ..\licence.h lrc $(RCFL) -r $(RCFLAGS) ..\windows\pageant.rc pgssapi.obj: ..\pgssapi.c ..\putty.h ..\pgssapi.h ..\defs.h ..\puttyps.h \ ..\network.h ..\misc.h ..\marshal.h ..\sshsignals.h \ ..\windows\winstuff.h ..\unix\unix.h ..\puttymem.h \ ..\tree234.h ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\pgssapi.c pinger.obj: ..\pinger.c ..\putty.h ..\defs.h ..\puttyps.h ..\network.h \ ..\misc.h ..\marshal.h ..\sshsignals.h ..\windows\winstuff.h \ ..\unix\unix.h ..\puttymem.h ..\tree234.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\pinger.c plink.res: ..\windows\plink.rc ..\windows\rcstuff.h ..\windows\putty.ico \ ..\windows\version.rc2 ..\version.h ..\licence.h lrc $(RCFL) -r $(RCFLAGS) ..\windows\plink.rc pockle.obj: ..\pockle.c ..\ssh.h ..\sshkeygen.h ..\mpint.h ..\mpunsafe.h \ ..\tree234.h ..\puttymem.h ..\network.h ..\misc.h \ ..\sshttymodes.h ..\defs.h ..\marshal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\pockle.c portfwd.obj: ..\portfwd.c ..\putty.h ..\ssh.h ..\sshchan.h ..\defs.h \ ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\portfwd.c primecandidate.obj: ..\primecandidate.c ..\ssh.h ..\mpint.h ..\mpunsafe.h \ ..\sshkeygen.h ..\puttymem.h ..\tree234.h ..\network.h \ ..\misc.h ..\sshttymodes.h ..\defs.h ..\marshal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\primecandidate.c procnet.obj: ..\unix\procnet.c ..\misc.h ..\defs.h ..\puttymem.h \ ..\marshal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\procnet.c proxy.obj: ..\proxy.c ..\putty.h ..\network.h ..\proxy.h ..\defs.h \ ..\puttyps.h ..\misc.h ..\marshal.h ..\sshsignals.h \ ..\windows\winstuff.h ..\unix\unix.h ..\puttymem.h \ ..\tree234.h ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\proxy.c pscp.obj: ..\pscp.c ..\putty.h ..\psftp.h ..\ssh.h ..\sftp.h ..\storage.h \ ..\defs.h ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\pscp.c pscp.res: ..\windows\pscp.rc ..\windows\rcstuff.h ..\windows\pscp.ico \ ..\windows\version.rc2 ..\version.h ..\licence.h lrc $(RCFL) -r $(RCFLAGS) ..\windows\pscp.rc psftp.obj: ..\psftp.c ..\putty.h ..\psftp.h ..\storage.h ..\ssh.h ..\sftp.h \ ..\defs.h ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\psftp.c psftp.res: ..\windows\psftp.rc ..\windows\rcstuff.h ..\windows\pscp.ico \ ..\windows\version.rc2 ..\version.h ..\licence.h lrc $(RCFL) -r $(RCFLAGS) ..\windows\psftp.rc psftpcommon.obj: ..\psftpcommon.c ..\putty.h ..\sftp.h ..\psftp.h ..\defs.h \ ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\windows\winstuff.h ..\unix\unix.h \ ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\psftpcommon.c psocks.obj: ..\psocks.c ..\putty.h ..\misc.h ..\ssh.h ..\sshchan.h \ ..\psocks.h ..\defs.h ..\puttyps.h ..\network.h ..\marshal.h \ ..\sshsignals.h ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\psocks.c putty.res: ..\windows\putty.rc ..\windows\rcstuff.h ..\windows\winhelp.rc2 \ ..\windows\win_res.rc2 ..\windows\putty.mft \ ..\windows\win_res.h ..\windows\putty.ico \ ..\windows\puttycfg.ico ..\windows\version.rc2 ..\version.h \ ..\licence.h lrc $(RCFL) -r $(RCFLAGS) ..\windows\putty.rc puttygen.res: ..\windows\puttygen.rc ..\windows\rcstuff.h \ ..\windows\winhelp.rc2 ..\windows\puttygen-rc.h \ ..\windows\puttygen.ico ..\windows\version.rc2 \ ..\windows\puttygen.mft ..\windows\win_res.h ..\version.h \ ..\licence.h lrc $(RCFL) -r $(RCFLAGS) ..\windows\puttygen.rc puttytel.res: ..\windows\puttytel.rc ..\windows\rcstuff.h \ ..\windows\winhelp.rc2 ..\windows\win_res.rc2 \ ..\windows\puttytel.mft ..\windows\win_res.h \ ..\windows\putty.ico ..\windows\puttycfg.ico \ ..\windows\version.rc2 ..\version.h ..\licence.h lrc $(RCFL) -r $(RCFLAGS) ..\windows\puttytel.rc raw.obj: ..\raw.c ..\putty.h ..\defs.h ..\puttyps.h ..\network.h ..\misc.h \ ..\marshal.h ..\sshsignals.h ..\windows\winstuff.h \ ..\unix\unix.h ..\puttymem.h ..\tree234.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\raw.c rlogin.obj: ..\rlogin.c ..\putty.h ..\defs.h ..\puttyps.h ..\network.h \ ..\misc.h ..\marshal.h ..\sshsignals.h ..\windows\winstuff.h \ ..\unix\unix.h ..\puttymem.h ..\tree234.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\rlogin.c sbcs.obj: ..\charset\sbcs.c ..\charset\charset.h ..\charset\internal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\charset\sbcs.c sbcsdat.obj: ..\charset\sbcsdat.c ..\charset\charset.h ..\charset\internal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\charset\sbcsdat.c scpserver.obj: ..\scpserver.c ..\putty.h ..\ssh.h ..\sshcr.h ..\sshchan.h \ ..\sftp.h ..\defs.h ..\puttyps.h ..\network.h ..\misc.h \ ..\marshal.h ..\sshsignals.h ..\puttymem.h ..\tree234.h \ ..\sshttymodes.h ..\windows\winstuff.h ..\unix\unix.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\scpserver.c sesschan.obj: ..\sesschan.c ..\putty.h ..\ssh.h ..\sshchan.h ..\sshserver.h \ ..\sftp.h ..\sshsignals.h ..\defs.h ..\puttyps.h \ ..\network.h ..\misc.h ..\marshal.h ..\puttymem.h \ ..\tree234.h ..\sshttymodes.h ..\windows\winstuff.h \ ..\unix\unix.h ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sesschan.c sessprep.obj: ..\sessprep.c ..\putty.h ..\defs.h ..\puttyps.h ..\network.h \ ..\misc.h ..\marshal.h ..\sshsignals.h ..\windows\winstuff.h \ ..\unix\unix.h ..\puttymem.h ..\tree234.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sessprep.c settings.obj: ..\settings.c ..\putty.h ..\storage.h ..\sshgssc.h ..\sshgss.h \ ..\defs.h ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\pgssapi.h ..\windows\winstuff.h \ ..\unix\unix.h ..\puttymem.h ..\tree234.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\settings.c sftp.obj: ..\sftp.c ..\misc.h ..\tree234.h ..\sftp.h ..\defs.h ..\puttymem.h \ ..\marshal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sftp.c sftpcommon.obj: ..\sftpcommon.c ..\misc.h ..\sftp.h ..\defs.h ..\puttymem.h \ ..\marshal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sftpcommon.c sftpserver.obj: ..\sftpserver.c ..\putty.h ..\ssh.h ..\sftp.h ..\defs.h \ ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sftpserver.c sizetip.obj: ..\windows\sizetip.c ..\putty.h ..\defs.h ..\puttyps.h \ ..\network.h ..\misc.h ..\marshal.h ..\sshsignals.h \ ..\windows\winstuff.h ..\unix\unix.h ..\puttymem.h \ ..\tree234.h ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\sizetip.c slookup.obj: ..\charset\slookup.c ..\charset\charset.h ..\charset\internal.h \ ..\charset\enum.c ..\charset\sbcsdat.c ..\charset\utf8.c lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\charset\slookup.c smallprimes.obj: ..\smallprimes.c ..\ssh.h ..\sshkeygen.h ..\puttymem.h \ ..\tree234.h ..\network.h ..\misc.h ..\sshttymodes.h \ ..\defs.h ..\marshal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\smallprimes.c ssh.obj: ..\ssh.c ..\putty.h ..\pageant.h ..\tree234.h ..\storage.h \ ..\marshal.h ..\ssh.h ..\sshcr.h ..\sshbpp.h ..\sshppl.h \ ..\sshchan.h ..\sshgssc.h ..\sshgss.h ..\defs.h ..\puttyps.h \ ..\network.h ..\misc.h ..\sshsignals.h ..\puttymem.h \ ..\sshttymodes.h ..\pgssapi.h ..\windows\winstuff.h \ ..\unix\unix.h ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\ssh.c ssh1bpp.obj: ..\ssh1bpp.c ..\putty.h ..\ssh.h ..\sshbpp.h ..\sshcr.h \ ..\defs.h ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\ssh1bpp.c ssh1censor.obj: ..\ssh1censor.c ..\putty.h ..\ssh.h ..\defs.h ..\puttyps.h \ ..\network.h ..\misc.h ..\marshal.h ..\sshsignals.h \ ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\ssh1censor.c ssh1connection.obj: ..\ssh1connection.c ..\putty.h ..\ssh.h ..\sshbpp.h \ ..\sshppl.h ..\sshchan.h ..\sshcr.h ..\ssh1connection.h \ ..\defs.h ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\ssh1connection.c ssh1connection-client.obj: ..\ssh1connection-client.c ..\putty.h ..\ssh.h \ ..\sshbpp.h ..\sshppl.h ..\sshchan.h ..\sshcr.h \ ..\ssh1connection.h ..\defs.h ..\puttyps.h ..\network.h \ ..\misc.h ..\marshal.h ..\sshsignals.h ..\puttymem.h \ ..\tree234.h ..\sshttymodes.h ..\windows\winstuff.h \ ..\unix\unix.h ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\ssh1connection-client.c ssh1connection-server.obj: ..\ssh1connection-server.c ..\putty.h ..\ssh.h \ ..\sshbpp.h ..\sshppl.h ..\sshchan.h ..\sshcr.h \ ..\ssh1connection.h ..\sshserver.h ..\defs.h ..\puttyps.h \ ..\network.h ..\misc.h ..\marshal.h ..\sshsignals.h \ ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\ssh1connection-server.c ssh1login.obj: ..\ssh1login.c ..\putty.h ..\ssh.h ..\mpint.h ..\sshbpp.h \ ..\sshppl.h ..\sshcr.h ..\defs.h ..\puttyps.h ..\network.h \ ..\misc.h ..\marshal.h ..\sshsignals.h ..\puttymem.h \ ..\tree234.h ..\sshttymodes.h ..\windows\winstuff.h \ ..\unix\unix.h ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\ssh1login.c ssh1login-server.obj: ..\ssh1login-server.c ..\putty.h ..\mpint.h ..\ssh.h \ ..\sshbpp.h ..\sshppl.h ..\sshcr.h ..\sshserver.h \ ..\sshkeygen.h ..\defs.h ..\puttyps.h ..\network.h ..\misc.h \ ..\marshal.h ..\sshsignals.h ..\puttymem.h ..\tree234.h \ ..\sshttymodes.h ..\windows\winstuff.h ..\unix\unix.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\ssh1login-server.c ssh2bpp.obj: ..\ssh2bpp.c ..\putty.h ..\ssh.h ..\sshbpp.h ..\sshcr.h \ ..\defs.h ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\ssh2bpp.c ssh2bpp-bare.obj: ..\ssh2bpp-bare.c ..\putty.h ..\ssh.h ..\sshbpp.h \ ..\sshcr.h ..\defs.h ..\puttyps.h ..\network.h ..\misc.h \ ..\marshal.h ..\sshsignals.h ..\puttymem.h ..\tree234.h \ ..\sshttymodes.h ..\windows\winstuff.h ..\unix\unix.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\ssh2bpp-bare.c ssh2censor.obj: ..\ssh2censor.c ..\putty.h ..\ssh.h ..\defs.h ..\puttyps.h \ ..\network.h ..\misc.h ..\marshal.h ..\sshsignals.h \ ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\ssh2censor.c ssh2connection.obj: ..\ssh2connection.c ..\putty.h ..\ssh.h ..\sshbpp.h \ ..\sshppl.h ..\sshchan.h ..\sshcr.h ..\ssh2connection.h \ ..\defs.h ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\ssh2connection.c ssh2connection-client.obj: ..\ssh2connection-client.c ..\putty.h ..\ssh.h \ ..\sshbpp.h ..\sshppl.h ..\sshchan.h ..\sshcr.h \ ..\ssh2connection.h ..\defs.h ..\puttyps.h ..\network.h \ ..\misc.h ..\marshal.h ..\sshsignals.h ..\puttymem.h \ ..\tree234.h ..\sshttymodes.h ..\windows\winstuff.h \ ..\unix\unix.h ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\ssh2connection-client.c ssh2connection-server.obj: ..\ssh2connection-server.c ..\putty.h ..\ssh.h \ ..\sshbpp.h ..\sshppl.h ..\sshchan.h ..\sshcr.h \ ..\ssh2connection.h ..\sshserver.h ..\defs.h ..\puttyps.h \ ..\network.h ..\misc.h ..\marshal.h ..\sshsignals.h \ ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\ssh2connection-server.c ssh2kex-client.obj: ..\ssh2kex-client.c ..\putty.h ..\ssh.h ..\sshbpp.h \ ..\sshppl.h ..\sshcr.h ..\storage.h ..\ssh2transport.h \ ..\mpint.h ..\defs.h ..\puttyps.h ..\network.h ..\misc.h \ ..\marshal.h ..\sshsignals.h ..\puttymem.h ..\tree234.h \ ..\sshttymodes.h ..\sshgssc.h ..\sshgss.h \ ..\windows\winstuff.h ..\unix\unix.h ..\pgssapi.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\ssh2kex-client.c ssh2kex-server.obj: ..\ssh2kex-server.c ..\putty.h ..\ssh.h ..\sshbpp.h \ ..\sshppl.h ..\sshcr.h ..\sshserver.h ..\sshkeygen.h \ ..\storage.h ..\ssh2transport.h ..\mpint.h ..\defs.h \ ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ ..\sshgssc.h ..\sshgss.h ..\windows\winstuff.h \ ..\unix\unix.h ..\pgssapi.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\ssh2kex-server.c ssh2transhk.obj: ..\ssh2transhk.c ..\putty.h ..\ssh.h ..\defs.h ..\puttyps.h \ ..\network.h ..\misc.h ..\marshal.h ..\sshsignals.h \ ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\ssh2transhk.c ssh2transport.obj: ..\ssh2transport.c ..\putty.h ..\ssh.h ..\sshbpp.h \ ..\sshppl.h ..\sshcr.h ..\sshserver.h ..\storage.h \ ..\ssh2transport.h ..\mpint.h ..\defs.h ..\puttyps.h \ ..\network.h ..\misc.h ..\marshal.h ..\sshsignals.h \ ..\puttymem.h ..\tree234.h ..\sshttymodes.h ..\sshgssc.h \ ..\sshgss.h ..\windows\winstuff.h ..\unix\unix.h \ ..\pgssapi.h ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\ssh2transport.c ssh2userauth.obj: ..\ssh2userauth.c ..\putty.h ..\ssh.h ..\sshbpp.h \ ..\sshppl.h ..\sshcr.h ..\sshgssc.h ..\sshgss.h ..\defs.h \ ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ ..\pgssapi.h ..\windows\winstuff.h ..\unix\unix.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\ssh2userauth.c ssh2userauth-server.obj: ..\ssh2userauth-server.c ..\putty.h ..\ssh.h \ ..\sshbpp.h ..\sshppl.h ..\sshcr.h ..\sshserver.h \ ..\sshgssc.h ..\sshgss.h ..\defs.h ..\puttyps.h ..\network.h \ ..\misc.h ..\marshal.h ..\sshsignals.h ..\puttymem.h \ ..\tree234.h ..\sshttymodes.h ..\pgssapi.h \ ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\ssh2userauth-server.c sshaes.obj: ..\sshaes.c ..\ssh.h ..\mpint_i.h ..\puttymem.h ..\tree234.h \ ..\network.h ..\misc.h ..\sshttymodes.h ..\defs.h \ ..\marshal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshaes.c ssharcf.obj: ..\ssharcf.c ..\ssh.h ..\puttymem.h ..\tree234.h ..\network.h \ ..\misc.h ..\sshttymodes.h ..\defs.h ..\marshal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\ssharcf.c sshargon2.obj: ..\sshargon2.c ..\putty.h ..\ssh.h ..\marshal.h ..\defs.h \ ..\puttyps.h ..\network.h ..\misc.h ..\sshsignals.h \ ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshargon2.c sshauxcrypt.obj: ..\sshauxcrypt.c ..\ssh.h ..\puttymem.h ..\tree234.h \ ..\network.h ..\misc.h ..\sshttymodes.h ..\defs.h \ ..\marshal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshauxcrypt.c sshbcrypt.obj: ..\sshbcrypt.c ..\ssh.h ..\sshblowf.h ..\puttymem.h \ ..\tree234.h ..\network.h ..\misc.h ..\sshttymodes.h \ ..\defs.h ..\marshal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshbcrypt.c sshblake2.obj: ..\sshblake2.c ..\ssh.h ..\puttymem.h ..\tree234.h \ ..\network.h ..\misc.h ..\sshttymodes.h ..\defs.h \ ..\marshal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshblake2.c sshblowf.obj: ..\sshblowf.c ..\ssh.h ..\sshblowf.h ..\puttymem.h \ ..\tree234.h ..\network.h ..\misc.h ..\sshttymodes.h \ ..\defs.h ..\marshal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshblowf.c sshccp.obj: ..\sshccp.c ..\ssh.h ..\mpint_i.h ..\puttymem.h ..\tree234.h \ ..\network.h ..\misc.h ..\sshttymodes.h ..\defs.h \ ..\marshal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshccp.c sshcommon.obj: ..\sshcommon.c ..\putty.h ..\mpint.h ..\ssh.h ..\sshbpp.h \ ..\sshppl.h ..\sshchan.h ..\sshttymodes.h ..\defs.h \ ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\puttymem.h ..\tree234.h \ ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshcommon.c sshcrc.obj: ..\sshcrc.c ..\ssh.h ..\puttymem.h ..\tree234.h ..\network.h \ ..\misc.h ..\sshttymodes.h ..\defs.h ..\marshal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshcrc.c sshcrcda.obj: ..\sshcrcda.c ..\misc.h ..\ssh.h ..\defs.h ..\puttymem.h \ ..\marshal.h ..\tree234.h ..\network.h ..\sshttymodes.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshcrcda.c sshdes.obj: ..\sshdes.c ..\ssh.h ..\mpint_i.h ..\puttymem.h ..\tree234.h \ ..\network.h ..\misc.h ..\sshttymodes.h ..\defs.h \ ..\marshal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshdes.c sshdh.obj: ..\sshdh.c ..\ssh.h ..\misc.h ..\mpint.h ..\puttymem.h \ ..\tree234.h ..\network.h ..\sshttymodes.h ..\defs.h \ ..\marshal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshdh.c sshdss.obj: ..\sshdss.c ..\ssh.h ..\mpint.h ..\misc.h ..\puttymem.h \ ..\tree234.h ..\network.h ..\sshttymodes.h ..\defs.h \ ..\marshal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshdss.c sshdssg.obj: ..\sshdssg.c ..\misc.h ..\ssh.h ..\sshkeygen.h ..\mpint.h \ ..\defs.h ..\puttymem.h ..\marshal.h ..\tree234.h \ ..\network.h ..\sshttymodes.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshdssg.c sshecc.obj: ..\sshecc.c ..\ssh.h ..\mpint.h ..\ecc.h ..\puttymem.h \ ..\tree234.h ..\network.h ..\misc.h ..\sshttymodes.h \ ..\defs.h ..\marshal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshecc.c sshecdsag.obj: ..\sshecdsag.c ..\ssh.h ..\sshkeygen.h ..\mpint.h \ ..\puttymem.h ..\tree234.h ..\network.h ..\misc.h \ ..\sshttymodes.h ..\defs.h ..\marshal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshecdsag.c sshgssc.obj: ..\sshgssc.c ..\putty.h ..\sshgssc.h ..\misc.h ..\defs.h \ ..\puttyps.h ..\network.h ..\marshal.h ..\sshsignals.h \ ..\pgssapi.h ..\sshgss.h ..\puttymem.h ..\windows\winstuff.h \ ..\unix\unix.h ..\tree234.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshgssc.c sshhmac.obj: ..\sshhmac.c ..\ssh.h ..\puttymem.h ..\tree234.h ..\network.h \ ..\misc.h ..\sshttymodes.h ..\defs.h ..\marshal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshhmac.c sshmac.obj: ..\sshmac.c ..\ssh.h ..\puttymem.h ..\tree234.h ..\network.h \ ..\misc.h ..\sshttymodes.h ..\defs.h ..\marshal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshmac.c sshmd5.obj: ..\sshmd5.c ..\ssh.h ..\puttymem.h ..\tree234.h ..\network.h \ ..\misc.h ..\sshttymodes.h ..\defs.h ..\marshal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshmd5.c sshprime.obj: ..\sshprime.c ..\ssh.h ..\mpint.h ..\mpunsafe.h ..\sshkeygen.h \ ..\puttymem.h ..\tree234.h ..\network.h ..\misc.h \ ..\sshttymodes.h ..\defs.h ..\marshal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshprime.c sshprng.obj: ..\sshprng.c ..\putty.h ..\ssh.h ..\mpint_i.h ..\defs.h \ ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshprng.c sshpubk.obj: ..\sshpubk.c ..\putty.h ..\mpint.h ..\ssh.h ..\misc.h ..\defs.h \ ..\puttyps.h ..\network.h ..\marshal.h ..\sshsignals.h \ ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshpubk.c sshrand.obj: ..\sshrand.c ..\putty.h ..\ssh.h ..\storage.h ..\defs.h \ ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshrand.c sshrsa.obj: ..\sshrsa.c ..\ssh.h ..\mpint.h ..\misc.h ..\puttymem.h \ ..\tree234.h ..\network.h ..\sshttymodes.h ..\defs.h \ ..\marshal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshrsa.c sshrsag.obj: ..\sshrsag.c ..\ssh.h ..\sshkeygen.h ..\mpint.h ..\puttymem.h \ ..\tree234.h ..\network.h ..\misc.h ..\sshttymodes.h \ ..\defs.h ..\marshal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshrsag.c sshserver.obj: ..\sshserver.c ..\putty.h ..\ssh.h ..\sshbpp.h ..\sshppl.h \ ..\sshchan.h ..\sshserver.h ..\sshgssc.h ..\sshgss.h \ ..\defs.h ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ ..\pgssapi.h ..\windows\winstuff.h ..\unix\unix.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshserver.c sshsh256.obj: ..\sshsh256.c ..\ssh.h ..\puttymem.h ..\tree234.h ..\network.h \ ..\misc.h ..\sshttymodes.h ..\defs.h ..\marshal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshsh256.c sshsh512.obj: ..\sshsh512.c ..\ssh.h ..\puttymem.h ..\tree234.h ..\network.h \ ..\misc.h ..\sshttymodes.h ..\defs.h ..\marshal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshsh512.c sshsha.obj: ..\sshsha.c ..\ssh.h ..\puttymem.h ..\tree234.h ..\network.h \ ..\misc.h ..\sshttymodes.h ..\defs.h ..\marshal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshsha.c sshsha3.obj: ..\sshsha3.c ..\ssh.h ..\puttymem.h ..\tree234.h ..\network.h \ ..\misc.h ..\sshttymodes.h ..\defs.h ..\marshal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshsha3.c sshshare.obj: ..\sshshare.c ..\putty.h ..\tree234.h ..\ssh.h ..\sshcr.h \ ..\defs.h ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\puttymem.h ..\sshttymodes.h \ ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshshare.c sshutils.obj: ..\sshutils.c ..\putty.h ..\ssh.h ..\sshchan.h ..\defs.h \ ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshutils.c sshverstring.obj: ..\sshverstring.c ..\putty.h ..\ssh.h ..\sshbpp.h \ ..\sshcr.h ..\defs.h ..\puttyps.h ..\network.h ..\misc.h \ ..\marshal.h ..\sshsignals.h ..\puttymem.h ..\tree234.h \ ..\sshttymodes.h ..\windows\winstuff.h ..\unix\unix.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshverstring.c sshzlib.obj: ..\sshzlib.c ..\defs.h ..\ssh.h ..\puttymem.h ..\tree234.h \ ..\network.h ..\misc.h ..\sshttymodes.h ..\marshal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\sshzlib.c stripctrl.obj: ..\stripctrl.c ..\putty.h ..\terminal.h ..\misc.h \ ..\marshal.h ..\defs.h ..\puttyps.h ..\network.h \ ..\sshsignals.h ..\tree234.h ..\puttymem.h \ ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\stripctrl.c supdup.obj: ..\supdup.c ..\putty.h ..\defs.h ..\puttyps.h ..\network.h \ ..\misc.h ..\marshal.h ..\sshsignals.h ..\windows\winstuff.h \ ..\unix\unix.h ..\puttymem.h ..\tree234.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\supdup.c telnet.obj: ..\telnet.c ..\putty.h ..\defs.h ..\puttyps.h ..\network.h \ ..\misc.h ..\marshal.h ..\sshsignals.h ..\windows\winstuff.h \ ..\unix\unix.h ..\puttymem.h ..\tree234.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\telnet.c terminal.obj: ..\terminal.c ..\putty.h ..\terminal.h ..\defs.h ..\puttyps.h \ ..\network.h ..\misc.h ..\marshal.h ..\sshsignals.h \ ..\tree234.h ..\windows\winstuff.h ..\unix\unix.h \ ..\puttymem.h ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\terminal.c testcrypt.obj: ..\testcrypt.c ..\defs.h ..\ssh.h ..\sshkeygen.h ..\misc.h \ ..\mpint.h ..\ecc.h ..\testcrypt.h ..\puttymem.h \ ..\tree234.h ..\network.h ..\sshttymodes.h ..\marshal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\testcrypt.c testsc.obj: ..\testsc.c ..\defs.h ..\putty.h ..\ssh.h ..\misc.h ..\mpint.h \ ..\ecc.h ..\puttyps.h ..\network.h ..\marshal.h \ ..\sshsignals.h ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\testsc.c testzlib.obj: ..\testzlib.c ..\defs.h ..\ssh.h ..\puttymem.h ..\tree234.h \ ..\network.h ..\misc.h ..\sshttymodes.h ..\marshal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\testzlib.c time.obj: ..\time.c lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\time.c timing.obj: ..\timing.c ..\putty.h ..\tree234.h ..\defs.h ..\puttyps.h \ ..\network.h ..\misc.h ..\marshal.h ..\sshsignals.h \ ..\windows\winstuff.h ..\unix\unix.h ..\puttymem.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\timing.c toucs.obj: ..\charset\toucs.c ..\charset\charset.h ..\charset\internal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\charset\toucs.c tree234.obj: ..\tree234.c ..\defs.h ..\tree234.h ..\puttymem.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\tree234.c utf8.obj: ..\charset\utf8.c ..\charset\charset.h ..\charset\internal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\charset\utf8.c utils.obj: ..\utils.c ..\defs.h ..\misc.h ..\ssh.h ..\puttymem.h \ ..\marshal.h ..\tree234.h ..\network.h ..\sshttymodes.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\utils.c ux_x11.obj: ..\unix\ux_x11.c ..\putty.h ..\ssh.h ..\network.h ..\defs.h \ ..\puttyps.h ..\misc.h ..\marshal.h ..\sshsignals.h \ ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\ux_x11.c uxagentc.obj: ..\unix\uxagentc.c ..\putty.h ..\misc.h ..\tree234.h \ ..\puttymem.h ..\defs.h ..\puttyps.h ..\network.h \ ..\marshal.h ..\sshsignals.h ..\windows\winstuff.h \ ..\unix\unix.h ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxagentc.c uxagentsock.obj: ..\unix\uxagentsock.c ..\putty.h ..\ssh.h ..\misc.h \ ..\pageant.h ..\defs.h ..\puttyps.h ..\network.h \ ..\marshal.h ..\sshsignals.h ..\puttymem.h ..\tree234.h \ ..\sshttymodes.h ..\windows\winstuff.h ..\unix\unix.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxagentsock.c uxcfg.obj: ..\unix\uxcfg.c ..\putty.h ..\dialog.h ..\storage.h ..\defs.h \ ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\windows\winstuff.h ..\unix\unix.h \ ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxcfg.c uxcliloop.obj: ..\unix\uxcliloop.c ..\putty.h ..\defs.h ..\puttyps.h \ ..\network.h ..\misc.h ..\marshal.h ..\sshsignals.h \ ..\windows\winstuff.h ..\unix\unix.h ..\puttymem.h \ ..\tree234.h ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxcliloop.c uxcons.obj: ..\unix\uxcons.c ..\putty.h ..\storage.h ..\ssh.h ..\console.h \ ..\defs.h ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxcons.c uxfdsock.obj: ..\unix\uxfdsock.c ..\tree234.h ..\putty.h ..\network.h \ ..\defs.h ..\puttyps.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\windows\winstuff.h ..\unix\unix.h \ ..\puttymem.h ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxfdsock.c uxgen.obj: ..\unix\uxgen.c ..\putty.h ..\defs.h ..\puttyps.h ..\network.h \ ..\misc.h ..\marshal.h ..\sshsignals.h ..\windows\winstuff.h \ ..\unix\unix.h ..\puttymem.h ..\tree234.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxgen.c uxgss.obj: ..\unix\uxgss.c ..\putty.h ..\pgssapi.h ..\sshgss.h ..\sshgssc.h \ ..\defs.h ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\windows\winstuff.h ..\unix\unix.h \ ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxgss.c uxmisc.obj: ..\unix\uxmisc.c ..\putty.h ..\defs.h ..\puttyps.h ..\network.h \ ..\misc.h ..\marshal.h ..\sshsignals.h ..\windows\winstuff.h \ ..\unix\unix.h ..\puttymem.h ..\tree234.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxmisc.c uxnet.obj: ..\unix\uxnet.c ..\putty.h ..\network.h ..\tree234.h ..\defs.h \ ..\puttyps.h ..\misc.h ..\marshal.h ..\sshsignals.h \ ..\windows\winstuff.h ..\unix\unix.h ..\puttymem.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxnet.c uxnogtk.obj: ..\unix\uxnogtk.c ..\putty.h ..\defs.h ..\puttyps.h \ ..\network.h ..\misc.h ..\marshal.h ..\sshsignals.h \ ..\windows\winstuff.h ..\unix\unix.h ..\puttymem.h \ ..\tree234.h ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxnogtk.c uxnoise.obj: ..\unix\uxnoise.c ..\putty.h ..\ssh.h ..\storage.h ..\defs.h \ ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxnoise.c uxpeer.obj: ..\unix\uxpeer.c ..\putty.h ..\defs.h ..\puttyps.h ..\network.h \ ..\misc.h ..\marshal.h ..\sshsignals.h ..\windows\winstuff.h \ ..\unix\unix.h ..\puttymem.h ..\tree234.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxpeer.c uxpgnt.obj: ..\unix\uxpgnt.c ..\putty.h ..\ssh.h ..\misc.h ..\pageant.h \ ..\defs.h ..\puttyps.h ..\network.h ..\marshal.h \ ..\sshsignals.h ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxpgnt.c uxplink.obj: ..\unix\uxplink.c ..\putty.h ..\ssh.h ..\storage.h ..\tree234.h \ ..\defs.h ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\puttymem.h ..\sshttymodes.h \ ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxplink.c uxpoll.obj: ..\unix\uxpoll.c ..\putty.h ..\tree234.h ..\defs.h ..\puttyps.h \ ..\network.h ..\misc.h ..\marshal.h ..\sshsignals.h \ ..\windows\winstuff.h ..\unix\unix.h ..\puttymem.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxpoll.c uxprint.obj: ..\unix\uxprint.c ..\putty.h ..\defs.h ..\puttyps.h \ ..\network.h ..\misc.h ..\marshal.h ..\sshsignals.h \ ..\windows\winstuff.h ..\unix\unix.h ..\puttymem.h \ ..\tree234.h ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxprint.c uxproxy.obj: ..\unix\uxproxy.c ..\tree234.h ..\putty.h ..\network.h \ ..\proxy.h ..\defs.h ..\puttyps.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\windows\winstuff.h ..\unix\unix.h \ ..\puttymem.h ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxproxy.c uxpsusan.obj: ..\unix\uxpsusan.c ..\putty.h ..\mpint.h ..\ssh.h \ ..\sshserver.h ..\defs.h ..\puttyps.h ..\network.h ..\misc.h \ ..\marshal.h ..\sshsignals.h ..\puttymem.h ..\tree234.h \ ..\sshttymodes.h ..\windows\winstuff.h ..\unix\unix.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxpsusan.c uxpterm.obj: ..\unix\uxpterm.c ..\putty.h ..\defs.h ..\puttyps.h \ ..\network.h ..\misc.h ..\marshal.h ..\sshsignals.h \ ..\windows\winstuff.h ..\unix\unix.h ..\puttymem.h \ ..\tree234.h ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxpterm.c uxpty.obj: ..\unix\uxpty.c ..\putty.h ..\ssh.h ..\sshserver.h ..\tree234.h \ ..\sshttymodes.h ..\sshsignals.h ..\defs.h ..\puttyps.h \ ..\network.h ..\misc.h ..\marshal.h ..\puttymem.h \ ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxpty.c uxputty.obj: ..\unix\uxputty.c ..\putty.h ..\ssh.h ..\storage.h \ ..\unix\gtkcompat.h ..\defs.h ..\puttyps.h ..\network.h \ ..\misc.h ..\marshal.h ..\sshsignals.h ..\puttymem.h \ ..\tree234.h ..\sshttymodes.h ..\windows\winstuff.h \ ..\unix\unix.h ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxputty.c uxsel.obj: ..\unix\uxsel.c ..\putty.h ..\tree234.h ..\defs.h ..\puttyps.h \ ..\network.h ..\misc.h ..\marshal.h ..\sshsignals.h \ ..\windows\winstuff.h ..\unix\unix.h ..\puttymem.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxsel.c uxser.obj: ..\unix\uxser.c ..\putty.h ..\tree234.h ..\defs.h ..\puttyps.h \ ..\network.h ..\misc.h ..\marshal.h ..\sshsignals.h \ ..\windows\winstuff.h ..\unix\unix.h ..\puttymem.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxser.c uxserver.obj: ..\unix\uxserver.c ..\putty.h ..\mpint.h ..\ssh.h \ ..\sshserver.h ..\defs.h ..\puttyps.h ..\network.h ..\misc.h \ ..\marshal.h ..\sshsignals.h ..\puttymem.h ..\tree234.h \ ..\sshttymodes.h ..\windows\winstuff.h ..\unix\unix.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxserver.c uxsftp.obj: ..\unix\uxsftp.c ..\putty.h ..\ssh.h ..\psftp.h ..\defs.h \ ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxsftp.c uxsftpserver.obj: ..\unix\uxsftpserver.c ..\putty.h ..\ssh.h ..\sshserver.h \ ..\sftp.h ..\tree234.h ..\defs.h ..\puttyps.h ..\network.h \ ..\misc.h ..\marshal.h ..\sshsignals.h ..\puttymem.h \ ..\sshttymodes.h ..\windows\winstuff.h ..\unix\unix.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxsftpserver.c uxshare.obj: ..\unix\uxshare.c ..\tree234.h ..\putty.h ..\network.h \ ..\proxy.h ..\ssh.h ..\defs.h ..\puttyps.h ..\misc.h \ ..\marshal.h ..\sshsignals.h ..\puttymem.h ..\sshttymodes.h \ ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxshare.c uxsignal.obj: ..\unix\uxsignal.c ..\defs.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxsignal.c uxsocks.obj: ..\unix\uxsocks.c ..\putty.h ..\ssh.h ..\psocks.h ..\defs.h \ ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxsocks.c uxstore.obj: ..\unix\uxstore.c ..\putty.h ..\storage.h ..\tree234.h \ ..\defs.h ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\windows\winstuff.h ..\unix\unix.h \ ..\puttymem.h ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxstore.c uxucs.obj: ..\unix\uxucs.c ..\putty.h ..\charset\charset.h ..\terminal.h \ ..\misc.h ..\defs.h ..\puttyps.h ..\network.h ..\marshal.h \ ..\sshsignals.h ..\tree234.h ..\puttymem.h \ ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxucs.c uxutils.obj: ..\unix\uxutils.c ..\putty.h ..\ssh.h ..\unix\uxutils.h \ ..\defs.h ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\uxutils.c version.obj: ..\version.c ..\putty.h ..\ssh.h ..\empty.h ..\version.h \ ..\defs.h ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\version.c wcwidth.obj: ..\wcwidth.c ..\putty.h ..\defs.h ..\puttyps.h ..\network.h \ ..\misc.h ..\marshal.h ..\sshsignals.h ..\windows\winstuff.h \ ..\unix\unix.h ..\puttymem.h ..\tree234.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\wcwidth.c wildcard.obj: ..\wildcard.c ..\putty.h ..\defs.h ..\puttyps.h ..\network.h \ ..\misc.h ..\marshal.h ..\sshsignals.h ..\windows\winstuff.h \ ..\unix\unix.h ..\puttymem.h ..\tree234.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\wildcard.c wincapi.obj: ..\windows\wincapi.c ..\putty.h ..\ssh.h ..\windows\wincapi.h \ ..\defs.h ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\wincapi.c wincfg.obj: ..\windows\wincfg.c ..\putty.h ..\dialog.h ..\storage.h \ ..\defs.h ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\windows\winstuff.h ..\unix\unix.h \ ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\wincfg.c wincliloop.obj: ..\windows\wincliloop.c ..\putty.h ..\defs.h ..\puttyps.h \ ..\network.h ..\misc.h ..\marshal.h ..\sshsignals.h \ ..\windows\winstuff.h ..\unix\unix.h ..\puttymem.h \ ..\tree234.h ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\wincliloop.c wincons.obj: ..\windows\wincons.c ..\putty.h ..\storage.h ..\ssh.h \ ..\console.h ..\defs.h ..\puttyps.h ..\network.h ..\misc.h \ ..\marshal.h ..\sshsignals.h ..\puttymem.h ..\tree234.h \ ..\sshttymodes.h ..\windows\winstuff.h ..\unix\unix.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\wincons.c winctrls.obj: ..\windows\winctrls.c ..\putty.h ..\misc.h ..\dialog.h \ ..\defs.h ..\puttyps.h ..\network.h ..\marshal.h \ ..\sshsignals.h ..\puttymem.h ..\windows\winstuff.h \ ..\unix\unix.h ..\tree234.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winctrls.c windefs.obj: ..\windows\windefs.c ..\putty.h ..\defs.h ..\puttyps.h \ ..\network.h ..\misc.h ..\marshal.h ..\sshsignals.h \ ..\windows\winstuff.h ..\unix\unix.h ..\puttymem.h \ ..\tree234.h ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\windefs.c windlg.obj: ..\windows\windlg.c ..\putty.h ..\ssh.h ..\windows\win_res.h \ ..\windows\winseat.h ..\storage.h ..\dialog.h ..\licence.h \ ..\defs.h ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\windlg.c window.obj: ..\windows\window.c ..\putty.h ..\terminal.h ..\storage.h \ ..\windows\win_res.h ..\windows\winsecur.h \ ..\windows\winseat.h ..\tree234.h ..\defs.h ..\puttyps.h \ ..\network.h ..\misc.h ..\marshal.h ..\sshsignals.h \ ..\windows\winstuff.h ..\unix\unix.h ..\puttymem.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\window.c wingss.obj: ..\windows\wingss.c ..\putty.h ..\pgssapi.h ..\sshgss.h \ ..\sshgssc.h ..\misc.h ..\defs.h ..\puttyps.h ..\network.h \ ..\marshal.h ..\sshsignals.h ..\puttymem.h \ ..\windows\winstuff.h ..\unix\unix.h ..\tree234.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\wingss.c winhandl.obj: ..\windows\winhandl.c ..\putty.h ..\defs.h ..\puttyps.h \ ..\network.h ..\misc.h ..\marshal.h ..\sshsignals.h \ ..\windows\winstuff.h ..\unix\unix.h ..\puttymem.h \ ..\tree234.h ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winhandl.c winhelp.obj: ..\windows\winhelp.c ..\putty.h ..\windows\win_res.h ..\defs.h \ ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\windows\winstuff.h ..\unix\unix.h \ ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winhelp.c winhsock.obj: ..\windows\winhsock.c ..\tree234.h ..\putty.h ..\network.h \ ..\defs.h ..\puttyps.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\windows\winstuff.h ..\unix\unix.h \ ..\puttymem.h ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winhsock.c winjump.obj: ..\windows\winjump.c ..\putty.h ..\storage.h ..\defs.h \ ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\windows\winstuff.h ..\unix\unix.h \ ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winjump.c winmisc.obj: ..\windows\winmisc.c ..\putty.h ..\defs.h ..\puttyps.h \ ..\network.h ..\misc.h ..\marshal.h ..\sshsignals.h \ ..\windows\winstuff.h ..\unix\unix.h ..\puttymem.h \ ..\tree234.h ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winmisc.c winmiscs.obj: ..\windows\winmiscs.c ..\putty.h ..\defs.h ..\puttyps.h \ ..\network.h ..\misc.h ..\marshal.h ..\sshsignals.h \ ..\windows\winstuff.h ..\unix\unix.h ..\puttymem.h \ ..\tree234.h ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winmiscs.c winnet.obj: ..\windows\winnet.c ..\putty.h ..\network.h ..\tree234.h \ ..\ssh.h ..\defs.h ..\puttyps.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\puttymem.h ..\sshttymodes.h \ ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winnet.c winnohlp.obj: ..\windows\winnohlp.c ..\putty.h ..\defs.h ..\puttyps.h \ ..\network.h ..\misc.h ..\marshal.h ..\sshsignals.h \ ..\windows\winstuff.h ..\unix\unix.h ..\puttymem.h \ ..\tree234.h ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winnohlp.c winnoise.obj: ..\windows\winnoise.c ..\putty.h ..\ssh.h ..\storage.h \ ..\defs.h ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winnoise.c winnojmp.obj: ..\windows\winnojmp.c lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winnojmp.c winnpc.obj: ..\windows\winnpc.c ..\tree234.h ..\putty.h ..\network.h \ ..\proxy.h ..\ssh.h ..\windows\winsecur.h ..\defs.h \ ..\puttyps.h ..\misc.h ..\marshal.h ..\sshsignals.h \ ..\puttymem.h ..\sshttymodes.h ..\windows\winstuff.h \ ..\unix\unix.h ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winnpc.c winnps.obj: ..\windows\winnps.c ..\tree234.h ..\putty.h ..\network.h \ ..\proxy.h ..\ssh.h ..\windows\winsecur.h ..\defs.h \ ..\puttyps.h ..\misc.h ..\marshal.h ..\sshsignals.h \ ..\puttymem.h ..\sshttymodes.h ..\windows\winstuff.h \ ..\unix\unix.h ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winnps.c winpgen.obj: ..\windows\winpgen.c ..\putty.h ..\ssh.h ..\sshkeygen.h \ ..\licence.h ..\windows\winsecur.h ..\windows\puttygen-rc.h \ ..\defs.h ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winpgen.c winpgnt.obj: ..\windows\winpgnt.c ..\putty.h ..\ssh.h ..\misc.h ..\tree234.h \ ..\windows\winsecur.h ..\windows\wincapi.h ..\pageant.h \ ..\licence.h ..\windows\pageant-rc.h ..\defs.h ..\puttyps.h \ ..\network.h ..\marshal.h ..\sshsignals.h ..\puttymem.h \ ..\sshttymodes.h ..\windows\winstuff.h ..\unix\unix.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winpgnt.c winpgntc.obj: ..\windows\winpgntc.c ..\putty.h ..\pageant.h \ ..\windows\winsecur.h ..\windows\wincapi.h ..\defs.h \ ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\windows\winstuff.h ..\unix\unix.h \ ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winpgntc.c winplink.obj: ..\windows\winplink.c ..\putty.h ..\storage.h ..\tree234.h \ ..\windows\winsecur.h ..\defs.h ..\puttyps.h ..\network.h \ ..\misc.h ..\marshal.h ..\sshsignals.h ..\windows\winstuff.h \ ..\unix\unix.h ..\puttymem.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winplink.c winprint.obj: ..\windows\winprint.c ..\putty.h ..\defs.h ..\puttyps.h \ ..\network.h ..\misc.h ..\marshal.h ..\sshsignals.h \ ..\windows\winstuff.h ..\unix\unix.h ..\puttymem.h \ ..\tree234.h ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winprint.c winproxy.obj: ..\windows\winproxy.c ..\tree234.h ..\putty.h ..\network.h \ ..\proxy.h ..\defs.h ..\puttyps.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\windows\winstuff.h ..\unix\unix.h \ ..\puttymem.h ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winproxy.c winsecur.obj: ..\windows\winsecur.c ..\putty.h ..\windows\winsecur.h \ ..\defs.h ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\windows\winstuff.h ..\unix\unix.h \ ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winsecur.c winselcli.obj: ..\windows\winselcli.c ..\putty.h ..\defs.h ..\puttyps.h \ ..\network.h ..\misc.h ..\marshal.h ..\sshsignals.h \ ..\windows\winstuff.h ..\unix\unix.h ..\puttymem.h \ ..\tree234.h ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winselcli.c winselgui.obj: ..\windows\winselgui.c ..\putty.h ..\defs.h ..\puttyps.h \ ..\network.h ..\misc.h ..\marshal.h ..\sshsignals.h \ ..\windows\winstuff.h ..\unix\unix.h ..\puttymem.h \ ..\tree234.h ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winselgui.c winser.obj: ..\windows\winser.c ..\putty.h ..\defs.h ..\puttyps.h \ ..\network.h ..\misc.h ..\marshal.h ..\sshsignals.h \ ..\windows\winstuff.h ..\unix\unix.h ..\puttymem.h \ ..\tree234.h ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winser.c winsftp.obj: ..\windows\winsftp.c ..\putty.h ..\psftp.h ..\ssh.h \ ..\windows\winsecur.h ..\defs.h ..\puttyps.h ..\network.h \ ..\misc.h ..\marshal.h ..\sshsignals.h ..\puttymem.h \ ..\tree234.h ..\sshttymodes.h ..\windows\winstuff.h \ ..\unix\unix.h ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winsftp.c winshare.obj: ..\windows\winshare.c ..\tree234.h ..\putty.h ..\network.h \ ..\proxy.h ..\ssh.h ..\windows\wincapi.h \ ..\windows\winsecur.h ..\noshare.c ..\defs.h ..\puttyps.h \ ..\misc.h ..\marshal.h ..\sshsignals.h ..\puttymem.h \ ..\sshttymodes.h ..\windows\winstuff.h ..\unix\unix.h \ ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winshare.c winsocks.obj: ..\windows\winsocks.c ..\putty.h ..\ssh.h ..\psocks.h \ ..\defs.h ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winsocks.c winstore.obj: ..\windows\winstore.c ..\putty.h ..\storage.h ..\defs.h \ ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\windows\winstuff.h ..\unix\unix.h \ ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winstore.c wintime.obj: ..\windows\wintime.c ..\putty.h ..\defs.h ..\puttyps.h \ ..\network.h ..\misc.h ..\marshal.h ..\sshsignals.h \ ..\windows\winstuff.h ..\unix\unix.h ..\puttymem.h \ ..\tree234.h ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\wintime.c winucs.obj: ..\windows\winucs.c ..\putty.h ..\terminal.h ..\misc.h ..\defs.h \ ..\puttyps.h ..\network.h ..\marshal.h ..\sshsignals.h \ ..\tree234.h ..\puttymem.h ..\windows\winstuff.h \ ..\unix\unix.h ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winucs.c winutils.obj: ..\windows\winutils.c ..\putty.h ..\misc.h ..\defs.h \ ..\puttyps.h ..\network.h ..\marshal.h ..\sshsignals.h \ ..\puttymem.h ..\windows\winstuff.h ..\unix\unix.h \ ..\tree234.h ..\windows\winhelp.h ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winutils.c winx11.obj: ..\windows\winx11.c ..\putty.h ..\ssh.h ..\defs.h ..\puttyps.h \ ..\network.h ..\misc.h ..\marshal.h ..\sshsignals.h \ ..\puttymem.h ..\tree234.h ..\sshttymodes.h \ ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\windows\winx11.c x11fwd.obj: ..\x11fwd.c ..\putty.h ..\ssh.h ..\sshchan.h ..\tree234.h \ ..\defs.h ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\puttymem.h ..\sshttymodes.h \ ..\windows\winstuff.h ..\unix\unix.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\x11fwd.c x11misc.obj: ..\unix\x11misc.c ..\putty.h ..\unix\x11misc.h ..\defs.h \ ..\puttyps.h ..\network.h ..\misc.h ..\marshal.h \ ..\sshsignals.h ..\windows\winstuff.h ..\unix\unix.h \ ..\puttymem.h ..\tree234.h ..\windows\winhelp.h \ ..\charset\charset.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\x11misc.c xenc.obj: ..\charset\xenc.c ..\charset\charset.h ..\charset\internal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\charset\xenc.c xkeysym.obj: ..\unix\xkeysym.c ..\misc.h ..\defs.h ..\puttymem.h \ ..\marshal.h lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\xkeysym.c xpmptcfg.obj: ..\unix\xpmptcfg.c lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\xpmptcfg.c xpmpterm.obj: ..\unix\xpmpterm.c lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\xpmpterm.c xpmpucfg.obj: ..\unix\xpmpucfg.c lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\xpmpucfg.c xpmputty.obj: ..\unix\xpmputty.c lcc -O -p6 $(COMPAT) $(CFLAGS) $(XFLAGS) ..\unix\xpmputty.c clean: -del *.obj -del *.exe -del *.res FORCE: putty-0.76/windows/MSVC/0000755000175000017500000000000014072266316012105 500000000000000putty-0.76/windows/MSVC/putty.dsw0000644000175000017500000000202114072266315013723 00000000000000Microsoft Developer Studio Workspace File, Format Version 6.00 # WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! ############################################################################### Project: "pageant"=".\pageant\pageant.dsp" - Package Owner=<4> Project: "plink"=".\plink\plink.dsp" - Package Owner=<4> Project: "pscp"=".\pscp\pscp.dsp" - Package Owner=<4> Project: "psftp"=".\psftp\psftp.dsp" - Package Owner=<4> Project: "psocks"=".\psocks\psocks.dsp" - Package Owner=<4> Project: "putty"=".\putty\putty.dsp" - Package Owner=<4> Project: "puttygen"=".\puttygen\puttygen.dsp" - Package Owner=<4> Project: "puttytel"=".\puttytel\puttytel.dsp" - Package Owner=<4> Project: "testcrypt"=".\testcrypt\testcrypt.dsp" - Package Owner=<4> Package=<5> {{{ }}} Package=<4> {{{ }}} ############################################################################### Global: Package=<5> {{{ }}} Package=<3> {{{ }}} ############################################################################### putty-0.76/windows/MSVC/pageant/0000755000175000017500000000000014072266316013524 500000000000000putty-0.76/windows/MSVC/pageant/pageant.dsp0000644000175000017500000002304314072266315015574 00000000000000# Microsoft Developer Studio Project File - Name="pageant" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Application" 0x0101 CFG=pageant - Win32 Debug !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "pageant.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "pageant.mak" CFG="pageant - Win32 Debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "pageant - Win32 Release" (based on "Win32 (x86) Application") !MESSAGE "pageant - Win32 Debug" (based on "Win32 (x86) Application") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe MTL=midl.exe RSC=rc.exe !IF "$(CFG)" == "pageant - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" # PROP Intermediate_Dir "Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c # ADD CPP /nologo /W3 /GX /O2 /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x809 /d "NDEBUG" # ADD RSC /l 0x809 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 # ADD LINK32 advapi32.lib comdlg32.lib gdi32.lib imm32.lib ole32.lib shell32.lib user32.lib /nologo /subsystem:windows /machine:I386 # SUBTRACT LINK32 /pdb:none !ELSEIF "$(CFG)" == "pageant - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "Debug" # PROP BASE Intermediate_Dir "Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "Debug" # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c # ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x809 /d "_DEBUG" # ADD RSC /l 0x809 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept # ADD LINK32 advapi32.lib comdlg32.lib gdi32.lib imm32.lib ole32.lib shell32.lib user32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept # SUBTRACT LINK32 /pdb:none !ENDIF # Begin Target # Name "pageant - Win32 Release" # Name "pageant - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=..\..\..\aqsync.c # End Source File # Begin Source File SOURCE=..\..\..\be_misc.c # End Source File # Begin Source File SOURCE=..\..\..\callback.c # End Source File # Begin Source File SOURCE=..\..\..\conf.c # End Source File # Begin Source File SOURCE=..\..\..\ecc.c # End Source File # Begin Source File SOURCE=..\..\..\errsock.c # End Source File # Begin Source File SOURCE=..\..\..\marshal.c # End Source File # Begin Source File SOURCE=..\..\..\memory.c # End Source File # Begin Source File SOURCE=..\..\..\misc.c # End Source File # Begin Source File SOURCE=..\..\..\mpint.c # End Source File # Begin Source File SOURCE=..\..\..\pageant.c # End Source File # Begin Source File SOURCE=..\..\..\sshaes.c # End Source File # Begin Source File SOURCE=..\..\..\sshargon2.c # End Source File # Begin Source File SOURCE=..\..\..\sshauxcrypt.c # End Source File # Begin Source File SOURCE=..\..\..\sshblake2.c # End Source File # Begin Source File SOURCE=..\..\..\sshdes.c # End Source File # Begin Source File SOURCE=..\..\..\sshdss.c # End Source File # Begin Source File SOURCE=..\..\..\sshecc.c # End Source File # Begin Source File SOURCE=..\..\..\sshhmac.c # End Source File # Begin Source File SOURCE=..\..\..\sshmd5.c # End Source File # Begin Source File SOURCE=..\..\..\sshpubk.c # End Source File # Begin Source File SOURCE=..\..\..\sshrsa.c # End Source File # Begin Source File SOURCE=..\..\..\sshsh256.c # End Source File # Begin Source File SOURCE=..\..\..\sshsh512.c # End Source File # Begin Source File SOURCE=..\..\..\sshsha.c # End Source File # Begin Source File SOURCE=..\..\..\sshsha3.c # End Source File # Begin Source File SOURCE=..\..\..\stripctrl.c # End Source File # Begin Source File SOURCE=..\..\..\tree234.c # End Source File # Begin Source File SOURCE=..\..\..\utils.c # End Source File # Begin Source File SOURCE=..\..\..\version.c # End Source File # Begin Source File SOURCE=..\..\..\wcwidth.c # End Source File # Begin Source File SOURCE=..\..\..\windows\wincapi.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winhandl.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winhelp.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winhsock.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winmisc.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winmiscs.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winnet.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winnpc.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winnps.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winpgnt.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winpgntc.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winsecur.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winselgui.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winutils.c # End Source File # End Group # Begin Group "Header Files" # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File SOURCE=..\..\..\charset\charset.h # End Source File # Begin Source File SOURCE=..\..\..\defs.h # End Source File # Begin Source File SOURCE=..\..\..\ecc.h # End Source File # Begin Source File SOURCE=..\..\..\empty.h # End Source File # Begin Source File SOURCE=..\..\..\licence.h # End Source File # Begin Source File SOURCE=..\..\..\marshal.h # End Source File # Begin Source File SOURCE=..\..\..\misc.h # End Source File # Begin Source File SOURCE=..\..\..\mpint.h # End Source File # Begin Source File SOURCE=..\..\..\mpint_i.h # End Source File # Begin Source File SOURCE=..\..\..\network.h # End Source File # Begin Source File SOURCE=..\..\..\pageant.h # End Source File # Begin Source File SOURCE=..\..\..\proxy.h # End Source File # Begin Source File SOURCE=..\..\..\putty.h # End Source File # Begin Source File SOURCE=..\..\..\puttymem.h # End Source File # Begin Source File SOURCE=..\..\..\puttyps.h # End Source File # Begin Source File SOURCE=..\..\..\ssh.h # End Source File # Begin Source File SOURCE=..\..\..\sshcr.h # End Source File # Begin Source File SOURCE=..\..\..\sshsignals.h # End Source File # Begin Source File SOURCE=..\..\..\sshttymodes.h # End Source File # Begin Source File SOURCE=..\..\..\terminal.h # End Source File # Begin Source File SOURCE=..\..\..\tree234.h # End Source File # Begin Source File SOURCE=..\..\..\unix\unix.h # End Source File # Begin Source File SOURCE=..\..\..\version.h # End Source File # Begin Source File SOURCE=..\..\..\windows\pageant-rc.h # End Source File # Begin Source File SOURCE=..\..\..\windows\rcstuff.h # End Source File # Begin Source File SOURCE=..\..\..\windows\win_res.h # End Source File # Begin Source File SOURCE=..\..\..\windows\wincapi.h # End Source File # Begin Source File SOURCE=..\..\..\windows\winhelp.h # End Source File # Begin Source File SOURCE=..\..\..\windows\winsecur.h # End Source File # Begin Source File SOURCE=..\..\..\windows\winstuff.h # End Source File # End Group # Begin Group "Resource Files" # PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" # Begin Source File SOURCE=..\..\..\windows\pageant.ico # End Source File # Begin Source File SOURCE=..\..\..\windows\pageant.rc # End Source File # Begin Source File SOURCE=..\..\..\windows\pageants.ico # End Source File # End Group # End Target # End Project putty-0.76/windows/MSVC/plink/0000755000175000017500000000000014072266316013222 500000000000000putty-0.76/windows/MSVC/plink/plink.dsp0000644000175000017500000003604414072266315014775 00000000000000# Microsoft Developer Studio Project File - Name="plink" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Application" 0x0101 CFG=plink - Win32 Debug !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "plink.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "plink.mak" CFG="plink - Win32 Debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "plink - Win32 Release" (based on "Win32 (x86) Application") !MESSAGE "plink - Win32 Debug" (based on "Win32 (x86) Application") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe MTL=midl.exe RSC=rc.exe !IF "$(CFG)" == "plink - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" # PROP Intermediate_Dir "Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c # ADD CPP /nologo /W3 /GX /O2 /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x809 /d "NDEBUG" # ADD RSC /l 0x809 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 # ADD LINK32 advapi32.lib comdlg32.lib gdi32.lib imm32.lib ole32.lib shell32.lib user32.lib /nologo /subsystem:console /machine:I386 # SUBTRACT LINK32 /pdb:none !ELSEIF "$(CFG)" == "plink - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "Debug" # PROP BASE Intermediate_Dir "Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "Debug" # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c # ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x809 /d "_DEBUG" # ADD RSC /l 0x809 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept # ADD LINK32 advapi32.lib comdlg32.lib gdi32.lib imm32.lib ole32.lib shell32.lib user32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept # SUBTRACT LINK32 /pdb:none !ENDIF # Begin Target # Name "plink - Win32 Release" # Name "plink - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=..\..\..\agentf.c # End Source File # Begin Source File SOURCE=..\..\..\aqsync.c # End Source File # Begin Source File SOURCE=..\..\..\be_all_s.c # End Source File # Begin Source File SOURCE=..\..\..\be_misc.c # End Source File # Begin Source File SOURCE=..\..\..\callback.c # End Source File # Begin Source File SOURCE=..\..\..\clicons.c # End Source File # Begin Source File SOURCE=..\..\..\cmdline.c # End Source File # Begin Source File SOURCE=..\..\..\conf.c # End Source File # Begin Source File SOURCE=..\..\..\console.c # End Source File # Begin Source File SOURCE=..\..\..\cproxy.c # End Source File # Begin Source File SOURCE=..\..\..\ecc.c # End Source File # Begin Source File SOURCE=..\..\..\errsock.c # End Source File # Begin Source File SOURCE=..\..\..\ldisc.c # End Source File # Begin Source File SOURCE=..\..\..\logging.c # End Source File # Begin Source File SOURCE=..\..\..\mainchan.c # End Source File # Begin Source File SOURCE=..\..\..\marshal.c # End Source File # Begin Source File SOURCE=..\..\..\memory.c # End Source File # Begin Source File SOURCE=..\..\..\misc.c # End Source File # Begin Source File SOURCE=..\..\..\miscucs.c # End Source File # Begin Source File SOURCE=..\..\..\mpint.c # End Source File # Begin Source File SOURCE=..\..\..\noshare.c # End Source File # Begin Source File SOURCE=..\..\..\noterm.c # End Source File # Begin Source File SOURCE=..\..\..\nullplug.c # End Source File # Begin Source File SOURCE=..\..\..\pgssapi.c # End Source File # Begin Source File SOURCE=..\..\..\pinger.c # End Source File # Begin Source File SOURCE=..\..\..\portfwd.c # End Source File # Begin Source File SOURCE=..\..\..\proxy.c # End Source File # Begin Source File SOURCE=..\..\..\raw.c # End Source File # Begin Source File SOURCE=..\..\..\rlogin.c # End Source File # Begin Source File SOURCE=..\..\..\sessprep.c # End Source File # Begin Source File SOURCE=..\..\..\settings.c # End Source File # Begin Source File SOURCE=..\..\..\ssh.c !IF "$(CFG)" == "plink - Win32 Release" !ELSEIF "$(CFG)" == "plink - Win32 Debug" # ADD CPP /Zi !ENDIF # End Source File # Begin Source File SOURCE=..\..\..\ssh1bpp.c # End Source File # Begin Source File SOURCE=..\..\..\ssh1censor.c # End Source File # Begin Source File SOURCE=..\..\..\ssh1connection-client.c # End Source File # Begin Source File SOURCE=..\..\..\ssh1connection.c # End Source File # Begin Source File SOURCE=..\..\..\ssh1login.c # End Source File # Begin Source File SOURCE=..\..\..\ssh2bpp-bare.c # End Source File # Begin Source File SOURCE=..\..\..\ssh2bpp.c # End Source File # Begin Source File SOURCE=..\..\..\ssh2censor.c # End Source File # Begin Source File SOURCE=..\..\..\ssh2connection-client.c # End Source File # Begin Source File SOURCE=..\..\..\ssh2connection.c # End Source File # Begin Source File SOURCE=..\..\..\ssh2kex-client.c # End Source File # Begin Source File SOURCE=..\..\..\ssh2transhk.c # End Source File # Begin Source File SOURCE=..\..\..\ssh2transport.c # End Source File # Begin Source File SOURCE=..\..\..\ssh2userauth.c # End Source File # Begin Source File SOURCE=..\..\..\sshaes.c # End Source File # Begin Source File SOURCE=..\..\..\ssharcf.c # End Source File # Begin Source File SOURCE=..\..\..\sshargon2.c # End Source File # Begin Source File SOURCE=..\..\..\sshauxcrypt.c # End Source File # Begin Source File SOURCE=..\..\..\sshblake2.c # End Source File # Begin Source File SOURCE=..\..\..\sshblowf.c # End Source File # Begin Source File SOURCE=..\..\..\sshccp.c # End Source File # Begin Source File SOURCE=..\..\..\sshcommon.c # End Source File # Begin Source File SOURCE=..\..\..\sshcrc.c # End Source File # Begin Source File SOURCE=..\..\..\sshcrcda.c # End Source File # Begin Source File SOURCE=..\..\..\sshdes.c # End Source File # Begin Source File SOURCE=..\..\..\sshdh.c # End Source File # Begin Source File SOURCE=..\..\..\sshdss.c # End Source File # Begin Source File SOURCE=..\..\..\sshecc.c # End Source File # Begin Source File SOURCE=..\..\..\sshgssc.c # End Source File # Begin Source File SOURCE=..\..\..\sshhmac.c # End Source File # Begin Source File SOURCE=..\..\..\sshmac.c # End Source File # Begin Source File SOURCE=..\..\..\sshmd5.c # End Source File # Begin Source File SOURCE=..\..\..\sshprng.c # End Source File # Begin Source File SOURCE=..\..\..\sshpubk.c # End Source File # Begin Source File SOURCE=..\..\..\sshrand.c # End Source File # Begin Source File SOURCE=..\..\..\sshrsa.c # End Source File # Begin Source File SOURCE=..\..\..\sshsh256.c # End Source File # Begin Source File SOURCE=..\..\..\sshsh512.c # End Source File # Begin Source File SOURCE=..\..\..\sshsha.c # End Source File # Begin Source File SOURCE=..\..\..\sshsha3.c # End Source File # Begin Source File SOURCE=..\..\..\sshshare.c # End Source File # Begin Source File SOURCE=..\..\..\sshutils.c # End Source File # Begin Source File SOURCE=..\..\..\sshverstring.c # End Source File # Begin Source File SOURCE=..\..\..\sshzlib.c # End Source File # Begin Source File SOURCE=..\..\..\stripctrl.c # End Source File # Begin Source File SOURCE=..\..\..\supdup.c # End Source File # Begin Source File SOURCE=..\..\..\telnet.c # End Source File # Begin Source File SOURCE=..\..\..\timing.c # End Source File # Begin Source File SOURCE=..\..\..\tree234.c # End Source File # Begin Source File SOURCE=..\..\..\utils.c # End Source File # Begin Source File SOURCE=..\..\..\version.c # End Source File # Begin Source File SOURCE=..\..\..\wcwidth.c # End Source File # Begin Source File SOURCE=..\..\..\wildcard.c # End Source File # Begin Source File SOURCE=..\..\..\windows\wincapi.c # End Source File # Begin Source File SOURCE=..\..\..\windows\wincliloop.c # End Source File # Begin Source File SOURCE=..\..\..\windows\wincons.c # End Source File # Begin Source File SOURCE=..\..\..\windows\windefs.c # End Source File # Begin Source File SOURCE=..\..\..\windows\wingss.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winhandl.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winhsock.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winmisc.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winmiscs.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winnet.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winnohlp.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winnoise.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winnojmp.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winnpc.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winnps.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winpgntc.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winplink.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winproxy.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winsecur.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winselcli.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winser.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winshare.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winstore.c # End Source File # Begin Source File SOURCE=..\..\..\windows\wintime.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winucs.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winx11.c # End Source File # Begin Source File SOURCE=..\..\..\x11fwd.c # End Source File # End Group # Begin Group "Header Files" # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File SOURCE=..\..\..\charset\charset.h # End Source File # Begin Source File SOURCE=..\..\..\console.h # End Source File # Begin Source File SOURCE=..\..\..\defs.h # End Source File # Begin Source File SOURCE=..\..\..\ecc.h # End Source File # Begin Source File SOURCE=..\..\..\empty.h # End Source File # Begin Source File SOURCE=..\..\..\ldisc.h # End Source File # Begin Source File SOURCE=..\..\..\licence.h # End Source File # Begin Source File SOURCE=..\..\..\marshal.h # End Source File # Begin Source File SOURCE=..\..\..\misc.h # End Source File # Begin Source File SOURCE=..\..\..\mpint.h # End Source File # Begin Source File SOURCE=..\..\..\mpint_i.h # End Source File # Begin Source File SOURCE=..\..\..\network.h # End Source File # Begin Source File SOURCE=..\..\..\pageant.h # End Source File # Begin Source File SOURCE=..\..\..\pgssapi.h # End Source File # Begin Source File SOURCE=..\..\..\proxy.h # End Source File # Begin Source File SOURCE=..\..\..\putty.h # End Source File # Begin Source File SOURCE=..\..\..\puttymem.h # End Source File # Begin Source File SOURCE=..\..\..\puttyps.h # End Source File # Begin Source File SOURCE=..\..\..\ssh.h # End Source File # Begin Source File SOURCE=..\..\..\ssh1connection.h # End Source File # Begin Source File SOURCE=..\..\..\ssh2connection.h # End Source File # Begin Source File SOURCE=..\..\..\ssh2transport.h # End Source File # Begin Source File SOURCE=..\..\..\sshblowf.h # End Source File # Begin Source File SOURCE=..\..\..\sshbpp.h # End Source File # Begin Source File SOURCE=..\..\..\sshchan.h # End Source File # Begin Source File SOURCE=..\..\..\sshcr.h # End Source File # Begin Source File SOURCE=..\..\..\sshgss.h # End Source File # Begin Source File SOURCE=..\..\..\sshgssc.h # End Source File # Begin Source File SOURCE=..\..\..\sshppl.h # End Source File # Begin Source File SOURCE=..\..\..\sshserver.h # End Source File # Begin Source File SOURCE=..\..\..\sshsignals.h # End Source File # Begin Source File SOURCE=..\..\..\sshttymodes.h # End Source File # Begin Source File SOURCE=..\..\..\storage.h # End Source File # Begin Source File SOURCE=..\..\..\terminal.h # End Source File # Begin Source File SOURCE=..\..\..\tree234.h # End Source File # Begin Source File SOURCE=..\..\..\unix\unix.h # End Source File # Begin Source File SOURCE=..\..\..\version.h # End Source File # Begin Source File SOURCE=..\..\..\windows\rcstuff.h # End Source File # Begin Source File SOURCE=..\..\..\windows\wincapi.h # End Source File # Begin Source File SOURCE=..\..\..\windows\winhelp.h # End Source File # Begin Source File SOURCE=..\..\..\windows\winsecur.h # End Source File # Begin Source File SOURCE=..\..\..\windows\winstuff.h # End Source File # End Group # Begin Group "Resource Files" # PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" # Begin Source File SOURCE=..\..\..\windows\plink.rc # End Source File # Begin Source File SOURCE=..\..\..\windows\putty.ico # End Source File # End Group # End Target # End Project putty-0.76/windows/MSVC/pscp/0000755000175000017500000000000014072266316013052 500000000000000putty-0.76/windows/MSVC/pscp/pscp.dsp0000644000175000017500000003555514072266315014463 00000000000000# Microsoft Developer Studio Project File - Name="pscp" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Application" 0x0101 CFG=pscp - Win32 Debug !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "pscp.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "pscp.mak" CFG="pscp - Win32 Debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "pscp - Win32 Release" (based on "Win32 (x86) Application") !MESSAGE "pscp - Win32 Debug" (based on "Win32 (x86) Application") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe MTL=midl.exe RSC=rc.exe !IF "$(CFG)" == "pscp - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" # PROP Intermediate_Dir "Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c # ADD CPP /nologo /W3 /GX /O2 /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x809 /d "NDEBUG" # ADD RSC /l 0x809 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 # ADD LINK32 advapi32.lib comdlg32.lib gdi32.lib imm32.lib ole32.lib shell32.lib user32.lib /nologo /subsystem:console /machine:I386 # SUBTRACT LINK32 /pdb:none !ELSEIF "$(CFG)" == "pscp - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "Debug" # PROP BASE Intermediate_Dir "Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "Debug" # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c # ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x809 /d "_DEBUG" # ADD RSC /l 0x809 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept # ADD LINK32 advapi32.lib comdlg32.lib gdi32.lib imm32.lib ole32.lib shell32.lib user32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept # SUBTRACT LINK32 /pdb:none !ENDIF # Begin Target # Name "pscp - Win32 Release" # Name "pscp - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=..\..\..\agentf.c # End Source File # Begin Source File SOURCE=..\..\..\aqsync.c # End Source File # Begin Source File SOURCE=..\..\..\be_misc.c # End Source File # Begin Source File SOURCE=..\..\..\be_ssh.c !IF "$(CFG)" == "pscp - Win32 Release" !ELSEIF "$(CFG)" == "pscp - Win32 Debug" # ADD CPP /Zi !ENDIF # End Source File # Begin Source File SOURCE=..\..\..\callback.c # End Source File # Begin Source File SOURCE=..\..\..\clicons.c # End Source File # Begin Source File SOURCE=..\..\..\cmdline.c # End Source File # Begin Source File SOURCE=..\..\..\conf.c # End Source File # Begin Source File SOURCE=..\..\..\console.c # End Source File # Begin Source File SOURCE=..\..\..\cproxy.c # End Source File # Begin Source File SOURCE=..\..\..\ecc.c # End Source File # Begin Source File SOURCE=..\..\..\errsock.c # End Source File # Begin Source File SOURCE=..\..\..\logging.c # End Source File # Begin Source File SOURCE=..\..\..\mainchan.c # End Source File # Begin Source File SOURCE=..\..\..\marshal.c # End Source File # Begin Source File SOURCE=..\..\..\memory.c # End Source File # Begin Source File SOURCE=..\..\..\misc.c # End Source File # Begin Source File SOURCE=..\..\..\miscucs.c # End Source File # Begin Source File SOURCE=..\..\..\mpint.c # End Source File # Begin Source File SOURCE=..\..\..\noshare.c # End Source File # Begin Source File SOURCE=..\..\..\nullplug.c # End Source File # Begin Source File SOURCE=..\..\..\pgssapi.c # End Source File # Begin Source File SOURCE=..\..\..\pinger.c # End Source File # Begin Source File SOURCE=..\..\..\portfwd.c # End Source File # Begin Source File SOURCE=..\..\..\proxy.c # End Source File # Begin Source File SOURCE=..\..\..\pscp.c # End Source File # Begin Source File SOURCE=..\..\..\psftpcommon.c # End Source File # Begin Source File SOURCE=..\..\..\settings.c # End Source File # Begin Source File SOURCE=..\..\..\sftp.c # End Source File # Begin Source File SOURCE=..\..\..\sftpcommon.c # End Source File # Begin Source File SOURCE=..\..\..\ssh.c !IF "$(CFG)" == "pscp - Win32 Release" !ELSEIF "$(CFG)" == "pscp - Win32 Debug" # ADD CPP /Zi !ENDIF # End Source File # Begin Source File SOURCE=..\..\..\ssh1bpp.c # End Source File # Begin Source File SOURCE=..\..\..\ssh1censor.c # End Source File # Begin Source File SOURCE=..\..\..\ssh1connection-client.c # End Source File # Begin Source File SOURCE=..\..\..\ssh1connection.c # End Source File # Begin Source File SOURCE=..\..\..\ssh1login.c # End Source File # Begin Source File SOURCE=..\..\..\ssh2bpp-bare.c # End Source File # Begin Source File SOURCE=..\..\..\ssh2bpp.c # End Source File # Begin Source File SOURCE=..\..\..\ssh2censor.c # End Source File # Begin Source File SOURCE=..\..\..\ssh2connection-client.c # End Source File # Begin Source File SOURCE=..\..\..\ssh2connection.c # End Source File # Begin Source File SOURCE=..\..\..\ssh2kex-client.c # End Source File # Begin Source File SOURCE=..\..\..\ssh2transhk.c # End Source File # Begin Source File SOURCE=..\..\..\ssh2transport.c # End Source File # Begin Source File SOURCE=..\..\..\ssh2userauth.c # End Source File # Begin Source File SOURCE=..\..\..\sshaes.c # End Source File # Begin Source File SOURCE=..\..\..\ssharcf.c # End Source File # Begin Source File SOURCE=..\..\..\sshargon2.c # End Source File # Begin Source File SOURCE=..\..\..\sshauxcrypt.c # End Source File # Begin Source File SOURCE=..\..\..\sshblake2.c # End Source File # Begin Source File SOURCE=..\..\..\sshblowf.c # End Source File # Begin Source File SOURCE=..\..\..\sshccp.c # End Source File # Begin Source File SOURCE=..\..\..\sshcommon.c # End Source File # Begin Source File SOURCE=..\..\..\sshcrc.c # End Source File # Begin Source File SOURCE=..\..\..\sshcrcda.c # End Source File # Begin Source File SOURCE=..\..\..\sshdes.c # End Source File # Begin Source File SOURCE=..\..\..\sshdh.c # End Source File # Begin Source File SOURCE=..\..\..\sshdss.c # End Source File # Begin Source File SOURCE=..\..\..\sshecc.c # End Source File # Begin Source File SOURCE=..\..\..\sshgssc.c # End Source File # Begin Source File SOURCE=..\..\..\sshhmac.c # End Source File # Begin Source File SOURCE=..\..\..\sshmac.c # End Source File # Begin Source File SOURCE=..\..\..\sshmd5.c # End Source File # Begin Source File SOURCE=..\..\..\sshprng.c # End Source File # Begin Source File SOURCE=..\..\..\sshpubk.c # End Source File # Begin Source File SOURCE=..\..\..\sshrand.c # End Source File # Begin Source File SOURCE=..\..\..\sshrsa.c # End Source File # Begin Source File SOURCE=..\..\..\sshsh256.c # End Source File # Begin Source File SOURCE=..\..\..\sshsh512.c # End Source File # Begin Source File SOURCE=..\..\..\sshsha.c # End Source File # Begin Source File SOURCE=..\..\..\sshsha3.c # End Source File # Begin Source File SOURCE=..\..\..\sshshare.c # End Source File # Begin Source File SOURCE=..\..\..\sshutils.c # End Source File # Begin Source File SOURCE=..\..\..\sshverstring.c # End Source File # Begin Source File SOURCE=..\..\..\sshzlib.c # End Source File # Begin Source File SOURCE=..\..\..\stripctrl.c # End Source File # Begin Source File SOURCE=..\..\..\timing.c # End Source File # Begin Source File SOURCE=..\..\..\tree234.c # End Source File # Begin Source File SOURCE=..\..\..\utils.c # End Source File # Begin Source File SOURCE=..\..\..\version.c # End Source File # Begin Source File SOURCE=..\..\..\wcwidth.c # End Source File # Begin Source File SOURCE=..\..\..\wildcard.c # End Source File # Begin Source File SOURCE=..\..\..\windows\wincapi.c # End Source File # Begin Source File SOURCE=..\..\..\windows\wincliloop.c # End Source File # Begin Source File SOURCE=..\..\..\windows\wincons.c # End Source File # Begin Source File SOURCE=..\..\..\windows\windefs.c # End Source File # Begin Source File SOURCE=..\..\..\windows\wingss.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winhandl.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winhsock.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winmisc.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winmiscs.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winnet.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winnohlp.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winnoise.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winnojmp.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winnpc.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winnps.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winpgntc.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winproxy.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winsecur.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winselcli.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winsftp.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winshare.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winstore.c # End Source File # Begin Source File SOURCE=..\..\..\windows\wintime.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winucs.c # End Source File # Begin Source File SOURCE=..\..\..\x11fwd.c # End Source File # End Group # Begin Group "Header Files" # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File SOURCE=..\..\..\charset\charset.h # End Source File # Begin Source File SOURCE=..\..\..\console.h # End Source File # Begin Source File SOURCE=..\..\..\defs.h # End Source File # Begin Source File SOURCE=..\..\..\ecc.h # End Source File # Begin Source File SOURCE=..\..\..\empty.h # End Source File # Begin Source File SOURCE=..\..\..\licence.h # End Source File # Begin Source File SOURCE=..\..\..\marshal.h # End Source File # Begin Source File SOURCE=..\..\..\misc.h # End Source File # Begin Source File SOURCE=..\..\..\mpint.h # End Source File # Begin Source File SOURCE=..\..\..\mpint_i.h # End Source File # Begin Source File SOURCE=..\..\..\network.h # End Source File # Begin Source File SOURCE=..\..\..\pageant.h # End Source File # Begin Source File SOURCE=..\..\..\pgssapi.h # End Source File # Begin Source File SOURCE=..\..\..\proxy.h # End Source File # Begin Source File SOURCE=..\..\..\psftp.h # End Source File # Begin Source File SOURCE=..\..\..\putty.h # End Source File # Begin Source File SOURCE=..\..\..\puttymem.h # End Source File # Begin Source File SOURCE=..\..\..\puttyps.h # End Source File # Begin Source File SOURCE=..\..\..\sftp.h # End Source File # Begin Source File SOURCE=..\..\..\ssh.h # End Source File # Begin Source File SOURCE=..\..\..\ssh1connection.h # End Source File # Begin Source File SOURCE=..\..\..\ssh2connection.h # End Source File # Begin Source File SOURCE=..\..\..\ssh2transport.h # End Source File # Begin Source File SOURCE=..\..\..\sshblowf.h # End Source File # Begin Source File SOURCE=..\..\..\sshbpp.h # End Source File # Begin Source File SOURCE=..\..\..\sshchan.h # End Source File # Begin Source File SOURCE=..\..\..\sshcr.h # End Source File # Begin Source File SOURCE=..\..\..\sshgss.h # End Source File # Begin Source File SOURCE=..\..\..\sshgssc.h # End Source File # Begin Source File SOURCE=..\..\..\sshppl.h # End Source File # Begin Source File SOURCE=..\..\..\sshserver.h # End Source File # Begin Source File SOURCE=..\..\..\sshsignals.h # End Source File # Begin Source File SOURCE=..\..\..\sshttymodes.h # End Source File # Begin Source File SOURCE=..\..\..\storage.h # End Source File # Begin Source File SOURCE=..\..\..\terminal.h # End Source File # Begin Source File SOURCE=..\..\..\tree234.h # End Source File # Begin Source File SOURCE=..\..\..\unix\unix.h # End Source File # Begin Source File SOURCE=..\..\..\version.h # End Source File # Begin Source File SOURCE=..\..\..\windows\rcstuff.h # End Source File # Begin Source File SOURCE=..\..\..\windows\wincapi.h # End Source File # Begin Source File SOURCE=..\..\..\windows\winhelp.h # End Source File # Begin Source File SOURCE=..\..\..\windows\winsecur.h # End Source File # Begin Source File SOURCE=..\..\..\windows\winstuff.h # End Source File # End Group # Begin Group "Resource Files" # PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" # Begin Source File SOURCE=..\..\..\windows\pscp.ico # End Source File # Begin Source File SOURCE=..\..\..\windows\pscp.rc # End Source File # End Group # End Target # End Project putty-0.76/windows/MSVC/psftp/0000755000175000017500000000000014072266316013241 500000000000000putty-0.76/windows/MSVC/psftp/psftp.dsp0000644000175000017500000003557614072266315015044 00000000000000# Microsoft Developer Studio Project File - Name="psftp" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Application" 0x0101 CFG=psftp - Win32 Debug !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "psftp.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "psftp.mak" CFG="psftp - Win32 Debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "psftp - Win32 Release" (based on "Win32 (x86) Application") !MESSAGE "psftp - Win32 Debug" (based on "Win32 (x86) Application") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe MTL=midl.exe RSC=rc.exe !IF "$(CFG)" == "psftp - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" # PROP Intermediate_Dir "Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c # ADD CPP /nologo /W3 /GX /O2 /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x809 /d "NDEBUG" # ADD RSC /l 0x809 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 # ADD LINK32 advapi32.lib comdlg32.lib gdi32.lib imm32.lib ole32.lib shell32.lib user32.lib /nologo /subsystem:console /machine:I386 # SUBTRACT LINK32 /pdb:none !ELSEIF "$(CFG)" == "psftp - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "Debug" # PROP BASE Intermediate_Dir "Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "Debug" # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c # ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x809 /d "_DEBUG" # ADD RSC /l 0x809 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept # ADD LINK32 advapi32.lib comdlg32.lib gdi32.lib imm32.lib ole32.lib shell32.lib user32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept # SUBTRACT LINK32 /pdb:none !ENDIF # Begin Target # Name "psftp - Win32 Release" # Name "psftp - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=..\..\..\agentf.c # End Source File # Begin Source File SOURCE=..\..\..\aqsync.c # End Source File # Begin Source File SOURCE=..\..\..\be_misc.c # End Source File # Begin Source File SOURCE=..\..\..\be_ssh.c !IF "$(CFG)" == "psftp - Win32 Release" !ELSEIF "$(CFG)" == "psftp - Win32 Debug" # ADD CPP /Zi !ENDIF # End Source File # Begin Source File SOURCE=..\..\..\callback.c # End Source File # Begin Source File SOURCE=..\..\..\clicons.c # End Source File # Begin Source File SOURCE=..\..\..\cmdline.c # End Source File # Begin Source File SOURCE=..\..\..\conf.c # End Source File # Begin Source File SOURCE=..\..\..\console.c # End Source File # Begin Source File SOURCE=..\..\..\cproxy.c # End Source File # Begin Source File SOURCE=..\..\..\ecc.c # End Source File # Begin Source File SOURCE=..\..\..\errsock.c # End Source File # Begin Source File SOURCE=..\..\..\logging.c # End Source File # Begin Source File SOURCE=..\..\..\mainchan.c # End Source File # Begin Source File SOURCE=..\..\..\marshal.c # End Source File # Begin Source File SOURCE=..\..\..\memory.c # End Source File # Begin Source File SOURCE=..\..\..\misc.c # End Source File # Begin Source File SOURCE=..\..\..\miscucs.c # End Source File # Begin Source File SOURCE=..\..\..\mpint.c # End Source File # Begin Source File SOURCE=..\..\..\noshare.c # End Source File # Begin Source File SOURCE=..\..\..\nullplug.c # End Source File # Begin Source File SOURCE=..\..\..\pgssapi.c # End Source File # Begin Source File SOURCE=..\..\..\pinger.c # End Source File # Begin Source File SOURCE=..\..\..\portfwd.c # End Source File # Begin Source File SOURCE=..\..\..\proxy.c # End Source File # Begin Source File SOURCE=..\..\..\psftp.c # End Source File # Begin Source File SOURCE=..\..\..\psftpcommon.c # End Source File # Begin Source File SOURCE=..\..\..\settings.c # End Source File # Begin Source File SOURCE=..\..\..\sftp.c # End Source File # Begin Source File SOURCE=..\..\..\sftpcommon.c # End Source File # Begin Source File SOURCE=..\..\..\ssh.c !IF "$(CFG)" == "psftp - Win32 Release" !ELSEIF "$(CFG)" == "psftp - Win32 Debug" # ADD CPP /Zi !ENDIF # End Source File # Begin Source File SOURCE=..\..\..\ssh1bpp.c # End Source File # Begin Source File SOURCE=..\..\..\ssh1censor.c # End Source File # Begin Source File SOURCE=..\..\..\ssh1connection-client.c # End Source File # Begin Source File SOURCE=..\..\..\ssh1connection.c # End Source File # Begin Source File SOURCE=..\..\..\ssh1login.c # End Source File # Begin Source File SOURCE=..\..\..\ssh2bpp-bare.c # End Source File # Begin Source File SOURCE=..\..\..\ssh2bpp.c # End Source File # Begin Source File SOURCE=..\..\..\ssh2censor.c # End Source File # Begin Source File SOURCE=..\..\..\ssh2connection-client.c # End Source File # Begin Source File SOURCE=..\..\..\ssh2connection.c # End Source File # Begin Source File SOURCE=..\..\..\ssh2kex-client.c # End Source File # Begin Source File SOURCE=..\..\..\ssh2transhk.c # End Source File # Begin Source File SOURCE=..\..\..\ssh2transport.c # End Source File # Begin Source File SOURCE=..\..\..\ssh2userauth.c # End Source File # Begin Source File SOURCE=..\..\..\sshaes.c # End Source File # Begin Source File SOURCE=..\..\..\ssharcf.c # End Source File # Begin Source File SOURCE=..\..\..\sshargon2.c # End Source File # Begin Source File SOURCE=..\..\..\sshauxcrypt.c # End Source File # Begin Source File SOURCE=..\..\..\sshblake2.c # End Source File # Begin Source File SOURCE=..\..\..\sshblowf.c # End Source File # Begin Source File SOURCE=..\..\..\sshccp.c # End Source File # Begin Source File SOURCE=..\..\..\sshcommon.c # End Source File # Begin Source File SOURCE=..\..\..\sshcrc.c # End Source File # Begin Source File SOURCE=..\..\..\sshcrcda.c # End Source File # Begin Source File SOURCE=..\..\..\sshdes.c # End Source File # Begin Source File SOURCE=..\..\..\sshdh.c # End Source File # Begin Source File SOURCE=..\..\..\sshdss.c # End Source File # Begin Source File SOURCE=..\..\..\sshecc.c # End Source File # Begin Source File SOURCE=..\..\..\sshgssc.c # End Source File # Begin Source File SOURCE=..\..\..\sshhmac.c # End Source File # Begin Source File SOURCE=..\..\..\sshmac.c # End Source File # Begin Source File SOURCE=..\..\..\sshmd5.c # End Source File # Begin Source File SOURCE=..\..\..\sshprng.c # End Source File # Begin Source File SOURCE=..\..\..\sshpubk.c # End Source File # Begin Source File SOURCE=..\..\..\sshrand.c # End Source File # Begin Source File SOURCE=..\..\..\sshrsa.c # End Source File # Begin Source File SOURCE=..\..\..\sshsh256.c # End Source File # Begin Source File SOURCE=..\..\..\sshsh512.c # End Source File # Begin Source File SOURCE=..\..\..\sshsha.c # End Source File # Begin Source File SOURCE=..\..\..\sshsha3.c # End Source File # Begin Source File SOURCE=..\..\..\sshshare.c # End Source File # Begin Source File SOURCE=..\..\..\sshutils.c # End Source File # Begin Source File SOURCE=..\..\..\sshverstring.c # End Source File # Begin Source File SOURCE=..\..\..\sshzlib.c # End Source File # Begin Source File SOURCE=..\..\..\stripctrl.c # End Source File # Begin Source File SOURCE=..\..\..\timing.c # End Source File # Begin Source File SOURCE=..\..\..\tree234.c # End Source File # Begin Source File SOURCE=..\..\..\utils.c # End Source File # Begin Source File SOURCE=..\..\..\version.c # End Source File # Begin Source File SOURCE=..\..\..\wcwidth.c # End Source File # Begin Source File SOURCE=..\..\..\wildcard.c # End Source File # Begin Source File SOURCE=..\..\..\windows\wincapi.c # End Source File # Begin Source File SOURCE=..\..\..\windows\wincliloop.c # End Source File # Begin Source File SOURCE=..\..\..\windows\wincons.c # End Source File # Begin Source File SOURCE=..\..\..\windows\windefs.c # End Source File # Begin Source File SOURCE=..\..\..\windows\wingss.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winhandl.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winhsock.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winmisc.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winmiscs.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winnet.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winnohlp.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winnoise.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winnojmp.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winnpc.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winnps.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winpgntc.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winproxy.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winsecur.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winselcli.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winsftp.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winshare.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winstore.c # End Source File # Begin Source File SOURCE=..\..\..\windows\wintime.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winucs.c # End Source File # Begin Source File SOURCE=..\..\..\x11fwd.c # End Source File # End Group # Begin Group "Header Files" # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File SOURCE=..\..\..\charset\charset.h # End Source File # Begin Source File SOURCE=..\..\..\console.h # End Source File # Begin Source File SOURCE=..\..\..\defs.h # End Source File # Begin Source File SOURCE=..\..\..\ecc.h # End Source File # Begin Source File SOURCE=..\..\..\empty.h # End Source File # Begin Source File SOURCE=..\..\..\licence.h # End Source File # Begin Source File SOURCE=..\..\..\marshal.h # End Source File # Begin Source File SOURCE=..\..\..\misc.h # End Source File # Begin Source File SOURCE=..\..\..\mpint.h # End Source File # Begin Source File SOURCE=..\..\..\mpint_i.h # End Source File # Begin Source File SOURCE=..\..\..\network.h # End Source File # Begin Source File SOURCE=..\..\..\pageant.h # End Source File # Begin Source File SOURCE=..\..\..\pgssapi.h # End Source File # Begin Source File SOURCE=..\..\..\proxy.h # End Source File # Begin Source File SOURCE=..\..\..\psftp.h # End Source File # Begin Source File SOURCE=..\..\..\putty.h # End Source File # Begin Source File SOURCE=..\..\..\puttymem.h # End Source File # Begin Source File SOURCE=..\..\..\puttyps.h # End Source File # Begin Source File SOURCE=..\..\..\sftp.h # End Source File # Begin Source File SOURCE=..\..\..\ssh.h # End Source File # Begin Source File SOURCE=..\..\..\ssh1connection.h # End Source File # Begin Source File SOURCE=..\..\..\ssh2connection.h # End Source File # Begin Source File SOURCE=..\..\..\ssh2transport.h # End Source File # Begin Source File SOURCE=..\..\..\sshblowf.h # End Source File # Begin Source File SOURCE=..\..\..\sshbpp.h # End Source File # Begin Source File SOURCE=..\..\..\sshchan.h # End Source File # Begin Source File SOURCE=..\..\..\sshcr.h # End Source File # Begin Source File SOURCE=..\..\..\sshgss.h # End Source File # Begin Source File SOURCE=..\..\..\sshgssc.h # End Source File # Begin Source File SOURCE=..\..\..\sshppl.h # End Source File # Begin Source File SOURCE=..\..\..\sshserver.h # End Source File # Begin Source File SOURCE=..\..\..\sshsignals.h # End Source File # Begin Source File SOURCE=..\..\..\sshttymodes.h # End Source File # Begin Source File SOURCE=..\..\..\storage.h # End Source File # Begin Source File SOURCE=..\..\..\terminal.h # End Source File # Begin Source File SOURCE=..\..\..\tree234.h # End Source File # Begin Source File SOURCE=..\..\..\unix\unix.h # End Source File # Begin Source File SOURCE=..\..\..\version.h # End Source File # Begin Source File SOURCE=..\..\..\windows\rcstuff.h # End Source File # Begin Source File SOURCE=..\..\..\windows\wincapi.h # End Source File # Begin Source File SOURCE=..\..\..\windows\winhelp.h # End Source File # Begin Source File SOURCE=..\..\..\windows\winsecur.h # End Source File # Begin Source File SOURCE=..\..\..\windows\winstuff.h # End Source File # End Group # Begin Group "Resource Files" # PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" # Begin Source File SOURCE=..\..\..\windows\pscp.ico # End Source File # Begin Source File SOURCE=..\..\..\windows\psftp.rc # End Source File # End Group # End Target # End Project putty-0.76/windows/MSVC/psocks/0000755000175000017500000000000014072266316013407 500000000000000putty-0.76/windows/MSVC/psocks/psocks.dsp0000644000175000017500000001772214072266315015351 00000000000000# Microsoft Developer Studio Project File - Name="psocks" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Application" 0x0101 CFG=psocks - Win32 Debug !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "psocks.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "psocks.mak" CFG="psocks - Win32 Debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "psocks - Win32 Release" (based on "Win32 (x86) Application") !MESSAGE "psocks - Win32 Debug" (based on "Win32 (x86) Application") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe MTL=midl.exe RSC=rc.exe !IF "$(CFG)" == "psocks - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" # PROP Intermediate_Dir "Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c # ADD CPP /nologo /W3 /GX /O2 /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x809 /d "NDEBUG" # ADD RSC /l 0x809 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 # ADD LINK32 advapi32.lib comdlg32.lib gdi32.lib imm32.lib ole32.lib shell32.lib user32.lib /nologo /subsystem:console /machine:I386 # SUBTRACT LINK32 /pdb:none !ELSEIF "$(CFG)" == "psocks - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "Debug" # PROP BASE Intermediate_Dir "Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "Debug" # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c # ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x809 /d "_DEBUG" # ADD RSC /l 0x809 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept # ADD LINK32 advapi32.lib comdlg32.lib gdi32.lib imm32.lib ole32.lib shell32.lib user32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept # SUBTRACT LINK32 /pdb:none !ENDIF # Begin Target # Name "psocks - Win32 Release" # Name "psocks - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=..\..\..\be_misc.c # End Source File # Begin Source File SOURCE=..\..\..\callback.c # End Source File # Begin Source File SOURCE=..\..\..\conf.c # End Source File # Begin Source File SOURCE=..\..\..\console.c # End Source File # Begin Source File SOURCE=..\..\..\errsock.c # End Source File # Begin Source File SOURCE=..\..\..\logging.c # End Source File # Begin Source File SOURCE=..\..\..\marshal.c # End Source File # Begin Source File SOURCE=..\..\..\memory.c # End Source File # Begin Source File SOURCE=..\..\..\misc.c # End Source File # Begin Source File SOURCE=..\..\..\nocproxy.c # End Source File # Begin Source File SOURCE=..\..\..\norand.c # End Source File # Begin Source File SOURCE=..\..\..\portfwd.c # End Source File # Begin Source File SOURCE=..\..\..\proxy.c # End Source File # Begin Source File SOURCE=..\..\..\psocks.c # End Source File # Begin Source File SOURCE=..\..\..\sshutils.c # End Source File # Begin Source File SOURCE=..\..\..\stripctrl.c # End Source File # Begin Source File SOURCE=..\..\..\time.c # End Source File # Begin Source File SOURCE=..\..\..\timing.c # End Source File # Begin Source File SOURCE=..\..\..\tree234.c # End Source File # Begin Source File SOURCE=..\..\..\utils.c # End Source File # Begin Source File SOURCE=..\..\..\version.c # End Source File # Begin Source File SOURCE=..\..\..\wcwidth.c # End Source File # Begin Source File SOURCE=..\..\..\windows\wincliloop.c # End Source File # Begin Source File SOURCE=..\..\..\windows\wincons.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winhandl.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winhsock.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winmisc.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winmiscs.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winnet.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winnohlp.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winproxy.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winselcli.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winsocks.c # End Source File # End Group # Begin Group "Header Files" # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File SOURCE=..\..\..\charset\charset.h # End Source File # Begin Source File SOURCE=..\..\..\console.h # End Source File # Begin Source File SOURCE=..\..\..\defs.h # End Source File # Begin Source File SOURCE=..\..\..\empty.h # End Source File # Begin Source File SOURCE=..\..\..\marshal.h # End Source File # Begin Source File SOURCE=..\..\..\misc.h # End Source File # Begin Source File SOURCE=..\..\..\network.h # End Source File # Begin Source File SOURCE=..\..\..\proxy.h # End Source File # Begin Source File SOURCE=..\..\..\psocks.h # End Source File # Begin Source File SOURCE=..\..\..\putty.h # End Source File # Begin Source File SOURCE=..\..\..\puttymem.h # End Source File # Begin Source File SOURCE=..\..\..\puttyps.h # End Source File # Begin Source File SOURCE=..\..\..\ssh.h # End Source File # Begin Source File SOURCE=..\..\..\sshchan.h # End Source File # Begin Source File SOURCE=..\..\..\sshsignals.h # End Source File # Begin Source File SOURCE=..\..\..\sshttymodes.h # End Source File # Begin Source File SOURCE=..\..\..\storage.h # End Source File # Begin Source File SOURCE=..\..\..\terminal.h # End Source File # Begin Source File SOURCE=..\..\..\tree234.h # End Source File # Begin Source File SOURCE=..\..\..\unix\unix.h # End Source File # Begin Source File SOURCE=..\..\..\version.h # End Source File # Begin Source File SOURCE=..\..\..\windows\winhelp.h # End Source File # Begin Source File SOURCE=..\..\..\windows\winstuff.h # End Source File # End Group # Begin Group "Resource Files" # PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" # End Group # End Target # End Project putty-0.76/windows/MSVC/putty/0000755000175000017500000000000014072266316013272 500000000000000putty-0.76/windows/MSVC/putty/putty.dsp0000644000175000017500000003720114072266315015111 00000000000000# Microsoft Developer Studio Project File - Name="putty" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Application" 0x0101 CFG=putty - Win32 Debug !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "putty.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "putty.mak" CFG="putty - Win32 Debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "putty - Win32 Release" (based on "Win32 (x86) Application") !MESSAGE "putty - Win32 Debug" (based on "Win32 (x86) Application") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe MTL=midl.exe RSC=rc.exe !IF "$(CFG)" == "putty - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" # PROP Intermediate_Dir "Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c # ADD CPP /nologo /W3 /GX /O2 /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x809 /d "NDEBUG" # ADD RSC /l 0x809 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 # ADD LINK32 advapi32.lib comdlg32.lib gdi32.lib imm32.lib ole32.lib shell32.lib user32.lib /nologo /subsystem:windows /machine:I386 # SUBTRACT LINK32 /pdb:none !ELSEIF "$(CFG)" == "putty - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "Debug" # PROP BASE Intermediate_Dir "Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "Debug" # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c # ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x809 /d "_DEBUG" # ADD RSC /l 0x809 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept # ADD LINK32 advapi32.lib comdlg32.lib gdi32.lib imm32.lib ole32.lib shell32.lib user32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept # SUBTRACT LINK32 /pdb:none !ENDIF # Begin Target # Name "putty - Win32 Release" # Name "putty - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=..\..\..\agentf.c # End Source File # Begin Source File SOURCE=..\..\..\aqsync.c # End Source File # Begin Source File SOURCE=..\..\..\be_all_s.c # End Source File # Begin Source File SOURCE=..\..\..\be_misc.c # End Source File # Begin Source File SOURCE=..\..\..\callback.c # End Source File # Begin Source File SOURCE=..\..\..\cmdline.c # End Source File # Begin Source File SOURCE=..\..\..\conf.c # End Source File # Begin Source File SOURCE=..\..\..\config.c # End Source File # Begin Source File SOURCE=..\..\..\cproxy.c # End Source File # Begin Source File SOURCE=..\..\..\dialog.c # End Source File # Begin Source File SOURCE=..\..\..\ecc.c # End Source File # Begin Source File SOURCE=..\..\..\errsock.c # End Source File # Begin Source File SOURCE=..\..\..\ldisc.c # End Source File # Begin Source File SOURCE=..\..\..\logging.c # End Source File # Begin Source File SOURCE=..\..\..\mainchan.c # End Source File # Begin Source File SOURCE=..\..\..\marshal.c # End Source File # Begin Source File SOURCE=..\..\..\memory.c # End Source File # Begin Source File SOURCE=..\..\..\minibidi.c # End Source File # Begin Source File SOURCE=..\..\..\misc.c # End Source File # Begin Source File SOURCE=..\..\..\miscucs.c # End Source File # Begin Source File SOURCE=..\..\..\mpint.c # End Source File # Begin Source File SOURCE=..\..\..\noshare.c # End Source File # Begin Source File SOURCE=..\..\..\nullplug.c # End Source File # Begin Source File SOURCE=..\..\..\pgssapi.c # End Source File # Begin Source File SOURCE=..\..\..\pinger.c # End Source File # Begin Source File SOURCE=..\..\..\portfwd.c # End Source File # Begin Source File SOURCE=..\..\..\proxy.c # End Source File # Begin Source File SOURCE=..\..\..\raw.c # End Source File # Begin Source File SOURCE=..\..\..\rlogin.c # End Source File # Begin Source File SOURCE=..\..\..\sessprep.c # End Source File # Begin Source File SOURCE=..\..\..\settings.c # End Source File # Begin Source File SOURCE=..\..\..\ssh.c !IF "$(CFG)" == "putty - Win32 Release" !ELSEIF "$(CFG)" == "putty - Win32 Debug" # ADD CPP /Zi !ENDIF # End Source File # Begin Source File SOURCE=..\..\..\ssh1bpp.c # End Source File # Begin Source File SOURCE=..\..\..\ssh1censor.c # End Source File # Begin Source File SOURCE=..\..\..\ssh1connection-client.c # End Source File # Begin Source File SOURCE=..\..\..\ssh1connection.c # End Source File # Begin Source File SOURCE=..\..\..\ssh1login.c # End Source File # Begin Source File SOURCE=..\..\..\ssh2bpp-bare.c # End Source File # Begin Source File SOURCE=..\..\..\ssh2bpp.c # End Source File # Begin Source File SOURCE=..\..\..\ssh2censor.c # End Source File # Begin Source File SOURCE=..\..\..\ssh2connection-client.c # End Source File # Begin Source File SOURCE=..\..\..\ssh2connection.c # End Source File # Begin Source File SOURCE=..\..\..\ssh2kex-client.c # End Source File # Begin Source File SOURCE=..\..\..\ssh2transhk.c # End Source File # Begin Source File SOURCE=..\..\..\ssh2transport.c # End Source File # Begin Source File SOURCE=..\..\..\ssh2userauth.c # End Source File # Begin Source File SOURCE=..\..\..\sshaes.c # End Source File # Begin Source File SOURCE=..\..\..\ssharcf.c # End Source File # Begin Source File SOURCE=..\..\..\sshargon2.c # End Source File # Begin Source File SOURCE=..\..\..\sshauxcrypt.c # End Source File # Begin Source File SOURCE=..\..\..\sshblake2.c # End Source File # Begin Source File SOURCE=..\..\..\sshblowf.c # End Source File # Begin Source File SOURCE=..\..\..\sshccp.c # End Source File # Begin Source File SOURCE=..\..\..\sshcommon.c # End Source File # Begin Source File SOURCE=..\..\..\sshcrc.c # End Source File # Begin Source File SOURCE=..\..\..\sshcrcda.c # End Source File # Begin Source File SOURCE=..\..\..\sshdes.c # End Source File # Begin Source File SOURCE=..\..\..\sshdh.c # End Source File # Begin Source File SOURCE=..\..\..\sshdss.c # End Source File # Begin Source File SOURCE=..\..\..\sshecc.c # End Source File # Begin Source File SOURCE=..\..\..\sshgssc.c # End Source File # Begin Source File SOURCE=..\..\..\sshhmac.c # End Source File # Begin Source File SOURCE=..\..\..\sshmac.c # End Source File # Begin Source File SOURCE=..\..\..\sshmd5.c # End Source File # Begin Source File SOURCE=..\..\..\sshprng.c # End Source File # Begin Source File SOURCE=..\..\..\sshpubk.c # End Source File # Begin Source File SOURCE=..\..\..\sshrand.c # End Source File # Begin Source File SOURCE=..\..\..\sshrsa.c # End Source File # Begin Source File SOURCE=..\..\..\sshsh256.c # End Source File # Begin Source File SOURCE=..\..\..\sshsh512.c # End Source File # Begin Source File SOURCE=..\..\..\sshsha.c # End Source File # Begin Source File SOURCE=..\..\..\sshsha3.c # End Source File # Begin Source File SOURCE=..\..\..\sshshare.c # End Source File # Begin Source File SOURCE=..\..\..\sshutils.c # End Source File # Begin Source File SOURCE=..\..\..\sshverstring.c # End Source File # Begin Source File SOURCE=..\..\..\sshzlib.c # End Source File # Begin Source File SOURCE=..\..\..\stripctrl.c # End Source File # Begin Source File SOURCE=..\..\..\supdup.c # End Source File # Begin Source File SOURCE=..\..\..\telnet.c # End Source File # Begin Source File SOURCE=..\..\..\terminal.c # End Source File # Begin Source File SOURCE=..\..\..\timing.c # End Source File # Begin Source File SOURCE=..\..\..\tree234.c # End Source File # Begin Source File SOURCE=..\..\..\utils.c # End Source File # Begin Source File SOURCE=..\..\..\version.c # End Source File # Begin Source File SOURCE=..\..\..\wcwidth.c # End Source File # Begin Source File SOURCE=..\..\..\wildcard.c # End Source File # Begin Source File SOURCE=..\..\..\windows\sizetip.c # End Source File # Begin Source File SOURCE=..\..\..\windows\wincapi.c # End Source File # Begin Source File SOURCE=..\..\..\windows\wincfg.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winctrls.c # End Source File # Begin Source File SOURCE=..\..\..\windows\windefs.c # End Source File # Begin Source File SOURCE=..\..\..\windows\windlg.c # End Source File # Begin Source File SOURCE=..\..\..\windows\window.c # End Source File # Begin Source File SOURCE=..\..\..\windows\wingss.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winhandl.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winhelp.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winhsock.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winjump.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winmisc.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winmiscs.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winnet.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winnoise.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winnpc.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winnps.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winpgntc.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winprint.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winproxy.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winsecur.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winselgui.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winser.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winshare.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winstore.c # End Source File # Begin Source File SOURCE=..\..\..\windows\wintime.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winucs.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winutils.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winx11.c # End Source File # Begin Source File SOURCE=..\..\..\x11fwd.c # End Source File # End Group # Begin Group "Header Files" # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File SOURCE=..\..\..\charset\charset.h # End Source File # Begin Source File SOURCE=..\..\..\defs.h # End Source File # Begin Source File SOURCE=..\..\..\dialog.h # End Source File # Begin Source File SOURCE=..\..\..\ecc.h # End Source File # Begin Source File SOURCE=..\..\..\empty.h # End Source File # Begin Source File SOURCE=..\..\..\ldisc.h # End Source File # Begin Source File SOURCE=..\..\..\licence.h # End Source File # Begin Source File SOURCE=..\..\..\marshal.h # End Source File # Begin Source File SOURCE=..\..\..\misc.h # End Source File # Begin Source File SOURCE=..\..\..\mpint.h # End Source File # Begin Source File SOURCE=..\..\..\mpint_i.h # End Source File # Begin Source File SOURCE=..\..\..\network.h # End Source File # Begin Source File SOURCE=..\..\..\pageant.h # End Source File # Begin Source File SOURCE=..\..\..\pgssapi.h # End Source File # Begin Source File SOURCE=..\..\..\proxy.h # End Source File # Begin Source File SOURCE=..\..\..\putty.h # End Source File # Begin Source File SOURCE=..\..\..\puttymem.h # End Source File # Begin Source File SOURCE=..\..\..\puttyps.h # End Source File # Begin Source File SOURCE=..\..\..\ssh.h # End Source File # Begin Source File SOURCE=..\..\..\ssh1connection.h # End Source File # Begin Source File SOURCE=..\..\..\ssh2connection.h # End Source File # Begin Source File SOURCE=..\..\..\ssh2transport.h # End Source File # Begin Source File SOURCE=..\..\..\sshblowf.h # End Source File # Begin Source File SOURCE=..\..\..\sshbpp.h # End Source File # Begin Source File SOURCE=..\..\..\sshchan.h # End Source File # Begin Source File SOURCE=..\..\..\sshcr.h # End Source File # Begin Source File SOURCE=..\..\..\sshgss.h # End Source File # Begin Source File SOURCE=..\..\..\sshgssc.h # End Source File # Begin Source File SOURCE=..\..\..\sshppl.h # End Source File # Begin Source File SOURCE=..\..\..\sshserver.h # End Source File # Begin Source File SOURCE=..\..\..\sshsignals.h # End Source File # Begin Source File SOURCE=..\..\..\sshttymodes.h # End Source File # Begin Source File SOURCE=..\..\..\storage.h # End Source File # Begin Source File SOURCE=..\..\..\terminal.h # End Source File # Begin Source File SOURCE=..\..\..\tree234.h # End Source File # Begin Source File SOURCE=..\..\..\unix\unix.h # End Source File # Begin Source File SOURCE=..\..\..\version.h # End Source File # Begin Source File SOURCE=..\..\..\windows\rcstuff.h # End Source File # Begin Source File SOURCE=..\..\..\windows\win_res.h # End Source File # Begin Source File SOURCE=..\..\..\windows\wincapi.h # End Source File # Begin Source File SOURCE=..\..\..\windows\winhelp.h # End Source File # Begin Source File SOURCE=..\..\..\windows\winseat.h # End Source File # Begin Source File SOURCE=..\..\..\windows\winsecur.h # End Source File # Begin Source File SOURCE=..\..\..\windows\winstuff.h # End Source File # End Group # Begin Group "Resource Files" # PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" # Begin Source File SOURCE=..\..\..\windows\putty.ico # End Source File # Begin Source File SOURCE=..\..\..\windows\putty.rc # End Source File # Begin Source File SOURCE=..\..\..\windows\puttycfg.ico # End Source File # End Group # End Target # End Project putty-0.76/windows/MSVC/puttygen/0000755000175000017500000000000014072266316013764 500000000000000putty-0.76/windows/MSVC/puttygen/puttygen.dsp0000644000175000017500000002400714072266315016275 00000000000000# Microsoft Developer Studio Project File - Name="puttygen" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Application" 0x0101 CFG=puttygen - Win32 Debug !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "puttygen.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "puttygen.mak" CFG="puttygen - Win32 Debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "puttygen - Win32 Release" (based on "Win32 (x86) Application") !MESSAGE "puttygen - Win32 Debug" (based on "Win32 (x86) Application") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe MTL=midl.exe RSC=rc.exe !IF "$(CFG)" == "puttygen - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" # PROP Intermediate_Dir "Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c # ADD CPP /nologo /W3 /GX /O2 /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x809 /d "NDEBUG" # ADD RSC /l 0x809 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 # ADD LINK32 advapi32.lib comdlg32.lib gdi32.lib imm32.lib ole32.lib shell32.lib user32.lib /nologo /subsystem:windows /machine:I386 # SUBTRACT LINK32 /pdb:none !ELSEIF "$(CFG)" == "puttygen - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "Debug" # PROP BASE Intermediate_Dir "Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "Debug" # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c # ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x809 /d "_DEBUG" # ADD RSC /l 0x809 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept # ADD LINK32 advapi32.lib comdlg32.lib gdi32.lib imm32.lib ole32.lib shell32.lib user32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept # SUBTRACT LINK32 /pdb:none !ENDIF # Begin Target # Name "puttygen - Win32 Release" # Name "puttygen - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=..\..\..\conf.c # End Source File # Begin Source File SOURCE=..\..\..\ecc.c # End Source File # Begin Source File SOURCE=..\..\..\import.c # End Source File # Begin Source File SOURCE=..\..\..\marshal.c # End Source File # Begin Source File SOURCE=..\..\..\memory.c # End Source File # Begin Source File SOURCE=..\..\..\millerrabin.c # End Source File # Begin Source File SOURCE=..\..\..\misc.c # End Source File # Begin Source File SOURCE=..\..\..\mpint.c # End Source File # Begin Source File SOURCE=..\..\..\mpunsafe.c # End Source File # Begin Source File SOURCE=..\..\..\notiming.c # End Source File # Begin Source File SOURCE=..\..\..\pockle.c # End Source File # Begin Source File SOURCE=..\..\..\primecandidate.c # End Source File # Begin Source File SOURCE=..\..\..\smallprimes.c # End Source File # Begin Source File SOURCE=..\..\..\sshaes.c # End Source File # Begin Source File SOURCE=..\..\..\sshargon2.c # End Source File # Begin Source File SOURCE=..\..\..\sshauxcrypt.c # End Source File # Begin Source File SOURCE=..\..\..\sshbcrypt.c # End Source File # Begin Source File SOURCE=..\..\..\sshblake2.c # End Source File # Begin Source File SOURCE=..\..\..\sshblowf.c # End Source File # Begin Source File SOURCE=..\..\..\sshdes.c # End Source File # Begin Source File SOURCE=..\..\..\sshdss.c # End Source File # Begin Source File SOURCE=..\..\..\sshdssg.c # End Source File # Begin Source File SOURCE=..\..\..\sshecc.c # End Source File # Begin Source File SOURCE=..\..\..\sshecdsag.c # End Source File # Begin Source File SOURCE=..\..\..\sshhmac.c # End Source File # Begin Source File SOURCE=..\..\..\sshmd5.c # End Source File # Begin Source File SOURCE=..\..\..\sshprime.c # End Source File # Begin Source File SOURCE=..\..\..\sshprng.c # End Source File # Begin Source File SOURCE=..\..\..\sshpubk.c # End Source File # Begin Source File SOURCE=..\..\..\sshrand.c # End Source File # Begin Source File SOURCE=..\..\..\sshrsa.c # End Source File # Begin Source File SOURCE=..\..\..\sshrsag.c # End Source File # Begin Source File SOURCE=..\..\..\sshsh256.c # End Source File # Begin Source File SOURCE=..\..\..\sshsh512.c # End Source File # Begin Source File SOURCE=..\..\..\sshsha.c # End Source File # Begin Source File SOURCE=..\..\..\sshsha3.c # End Source File # Begin Source File SOURCE=..\..\..\stripctrl.c # End Source File # Begin Source File SOURCE=..\..\..\tree234.c # End Source File # Begin Source File SOURCE=..\..\..\utils.c # End Source File # Begin Source File SOURCE=..\..\..\version.c # End Source File # Begin Source File SOURCE=..\..\..\wcwidth.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winctrls.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winhelp.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winmisc.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winmiscs.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winnoise.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winnojmp.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winpgen.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winsecur.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winstore.c # End Source File # Begin Source File SOURCE=..\..\..\windows\wintime.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winutils.c # End Source File # End Group # Begin Group "Header Files" # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File SOURCE=..\..\..\charset\charset.h # End Source File # Begin Source File SOURCE=..\..\..\defs.h # End Source File # Begin Source File SOURCE=..\..\..\dialog.h # End Source File # Begin Source File SOURCE=..\..\..\ecc.h # End Source File # Begin Source File SOURCE=..\..\..\empty.h # End Source File # Begin Source File SOURCE=..\..\..\licence.h # End Source File # Begin Source File SOURCE=..\..\..\marshal.h # End Source File # Begin Source File SOURCE=..\..\..\misc.h # End Source File # Begin Source File SOURCE=..\..\..\mpint.h # End Source File # Begin Source File SOURCE=..\..\..\mpint_i.h # End Source File # Begin Source File SOURCE=..\..\..\mpunsafe.h # End Source File # Begin Source File SOURCE=..\..\..\network.h # End Source File # Begin Source File SOURCE=..\..\..\putty.h # End Source File # Begin Source File SOURCE=..\..\..\puttymem.h # End Source File # Begin Source File SOURCE=..\..\..\puttyps.h # End Source File # Begin Source File SOURCE=..\..\..\ssh.h # End Source File # Begin Source File SOURCE=..\..\..\sshblowf.h # End Source File # Begin Source File SOURCE=..\..\..\sshkeygen.h # End Source File # Begin Source File SOURCE=..\..\..\sshsignals.h # End Source File # Begin Source File SOURCE=..\..\..\sshttymodes.h # End Source File # Begin Source File SOURCE=..\..\..\storage.h # End Source File # Begin Source File SOURCE=..\..\..\terminal.h # End Source File # Begin Source File SOURCE=..\..\..\tree234.h # End Source File # Begin Source File SOURCE=..\..\..\unix\unix.h # End Source File # Begin Source File SOURCE=..\..\..\version.h # End Source File # Begin Source File SOURCE=..\..\..\windows\puttygen-rc.h # End Source File # Begin Source File SOURCE=..\..\..\windows\rcstuff.h # End Source File # Begin Source File SOURCE=..\..\..\windows\win_res.h # End Source File # Begin Source File SOURCE=..\..\..\windows\winhelp.h # End Source File # Begin Source File SOURCE=..\..\..\windows\winsecur.h # End Source File # Begin Source File SOURCE=..\..\..\windows\winstuff.h # End Source File # End Group # Begin Group "Resource Files" # PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" # Begin Source File SOURCE=..\..\..\windows\puttygen.ico # End Source File # Begin Source File SOURCE=..\..\..\windows\puttygen.rc # End Source File # End Group # End Target # End Project putty-0.76/windows/MSVC/puttytel/0000755000175000017500000000000014072266316013777 500000000000000putty-0.76/windows/MSVC/puttytel/puttytel.dsp0000644000175000017500000002442014072266315016322 00000000000000# Microsoft Developer Studio Project File - Name="puttytel" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Application" 0x0101 CFG=puttytel - Win32 Debug !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "puttytel.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "puttytel.mak" CFG="puttytel - Win32 Debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "puttytel - Win32 Release" (based on "Win32 (x86) Application") !MESSAGE "puttytel - Win32 Debug" (based on "Win32 (x86) Application") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe MTL=midl.exe RSC=rc.exe !IF "$(CFG)" == "puttytel - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" # PROP Intermediate_Dir "Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c # ADD CPP /nologo /W3 /GX /O2 /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x809 /d "NDEBUG" # ADD RSC /l 0x809 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386 # ADD LINK32 advapi32.lib comdlg32.lib gdi32.lib imm32.lib ole32.lib shell32.lib user32.lib /nologo /subsystem:windows /machine:I386 # SUBTRACT LINK32 /pdb:none !ELSEIF "$(CFG)" == "puttytel - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "Debug" # PROP BASE Intermediate_Dir "Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "Debug" # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c # ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x809 /d "_DEBUG" # ADD RSC /l 0x809 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept # ADD LINK32 advapi32.lib comdlg32.lib gdi32.lib imm32.lib ole32.lib shell32.lib user32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept # SUBTRACT LINK32 /pdb:none !ENDIF # Begin Target # Name "puttytel - Win32 Release" # Name "puttytel - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=..\..\..\be_misc.c # End Source File # Begin Source File SOURCE=..\..\..\be_nos_s.c # End Source File # Begin Source File SOURCE=..\..\..\callback.c # End Source File # Begin Source File SOURCE=..\..\..\cmdline.c # End Source File # Begin Source File SOURCE=..\..\..\conf.c # End Source File # Begin Source File SOURCE=..\..\..\config.c # End Source File # Begin Source File SOURCE=..\..\..\dialog.c # End Source File # Begin Source File SOURCE=..\..\..\errsock.c # End Source File # Begin Source File SOURCE=..\..\..\ldisc.c # End Source File # Begin Source File SOURCE=..\..\..\logging.c # End Source File # Begin Source File SOURCE=..\..\..\marshal.c # End Source File # Begin Source File SOURCE=..\..\..\memory.c # End Source File # Begin Source File SOURCE=..\..\..\minibidi.c # End Source File # Begin Source File SOURCE=..\..\..\misc.c # End Source File # Begin Source File SOURCE=..\..\..\miscucs.c # End Source File # Begin Source File SOURCE=..\..\..\nocproxy.c # End Source File # Begin Source File SOURCE=..\..\..\nogss.c # End Source File # Begin Source File SOURCE=..\..\..\norand.c # End Source File # Begin Source File SOURCE=..\..\..\pinger.c # End Source File # Begin Source File SOURCE=..\..\..\proxy.c # End Source File # Begin Source File SOURCE=..\..\..\raw.c # End Source File # Begin Source File SOURCE=..\..\..\rlogin.c # End Source File # Begin Source File SOURCE=..\..\..\sessprep.c # End Source File # Begin Source File SOURCE=..\..\..\settings.c # End Source File # Begin Source File SOURCE=..\..\..\stripctrl.c # End Source File # Begin Source File SOURCE=..\..\..\supdup.c # End Source File # Begin Source File SOURCE=..\..\..\telnet.c # End Source File # Begin Source File SOURCE=..\..\..\terminal.c # End Source File # Begin Source File SOURCE=..\..\..\timing.c # End Source File # Begin Source File SOURCE=..\..\..\tree234.c # End Source File # Begin Source File SOURCE=..\..\..\utils.c # End Source File # Begin Source File SOURCE=..\..\..\version.c # End Source File # Begin Source File SOURCE=..\..\..\wcwidth.c # End Source File # Begin Source File SOURCE=..\..\..\windows\sizetip.c # End Source File # Begin Source File SOURCE=..\..\..\windows\wincfg.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winctrls.c # End Source File # Begin Source File SOURCE=..\..\..\windows\windefs.c # End Source File # Begin Source File SOURCE=..\..\..\windows\windlg.c # End Source File # Begin Source File SOURCE=..\..\..\windows\window.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winhandl.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winhelp.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winhsock.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winjump.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winmisc.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winmiscs.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winnet.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winprint.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winproxy.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winsecur.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winselgui.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winser.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winstore.c # End Source File # Begin Source File SOURCE=..\..\..\windows\wintime.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winucs.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winutils.c # End Source File # End Group # Begin Group "Header Files" # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File SOURCE=..\..\..\charset\charset.h # End Source File # Begin Source File SOURCE=..\..\..\defs.h # End Source File # Begin Source File SOURCE=..\..\..\dialog.h # End Source File # Begin Source File SOURCE=..\..\..\empty.h # End Source File # Begin Source File SOURCE=..\..\..\ldisc.h # End Source File # Begin Source File SOURCE=..\..\..\licence.h # End Source File # Begin Source File SOURCE=..\..\..\marshal.h # End Source File # Begin Source File SOURCE=..\..\..\misc.h # End Source File # Begin Source File SOURCE=..\..\..\network.h # End Source File # Begin Source File SOURCE=..\..\..\pgssapi.h # End Source File # Begin Source File SOURCE=..\..\..\proxy.h # End Source File # Begin Source File SOURCE=..\..\..\putty.h # End Source File # Begin Source File SOURCE=..\..\..\puttymem.h # End Source File # Begin Source File SOURCE=..\..\..\puttyps.h # End Source File # Begin Source File SOURCE=..\..\..\ssh.h # End Source File # Begin Source File SOURCE=..\..\..\sshgss.h # End Source File # Begin Source File SOURCE=..\..\..\sshgssc.h # End Source File # Begin Source File SOURCE=..\..\..\sshsignals.h # End Source File # Begin Source File SOURCE=..\..\..\sshttymodes.h # End Source File # Begin Source File SOURCE=..\..\..\storage.h # End Source File # Begin Source File SOURCE=..\..\..\terminal.h # End Source File # Begin Source File SOURCE=..\..\..\tree234.h # End Source File # Begin Source File SOURCE=..\..\..\unix\unix.h # End Source File # Begin Source File SOURCE=..\..\..\version.h # End Source File # Begin Source File SOURCE=..\..\..\windows\rcstuff.h # End Source File # Begin Source File SOURCE=..\..\..\windows\win_res.h # End Source File # Begin Source File SOURCE=..\..\..\windows\winhelp.h # End Source File # Begin Source File SOURCE=..\..\..\windows\winseat.h # End Source File # Begin Source File SOURCE=..\..\..\windows\winsecur.h # End Source File # Begin Source File SOURCE=..\..\..\windows\winstuff.h # End Source File # End Group # Begin Group "Resource Files" # PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" # Begin Source File SOURCE=..\..\..\windows\putty.ico # End Source File # Begin Source File SOURCE=..\..\..\windows\puttycfg.ico # End Source File # Begin Source File SOURCE=..\..\..\windows\puttytel.rc # End Source File # End Group # End Target # End Project putty-0.76/windows/MSVC/testcrypt/0000755000175000017500000000000014072266316014146 500000000000000putty-0.76/windows/MSVC/testcrypt/testcrypt.dsp0000644000175000017500000002016314072266315016640 00000000000000# Microsoft Developer Studio Project File - Name="testcrypt" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Application" 0x0101 CFG=testcrypt - Win32 Debug !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "testcrypt.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "testcrypt.mak" CFG="testcrypt - Win32 Debug" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "testcrypt - Win32 Release" (based on "Win32 (x86) Application") !MESSAGE "testcrypt - Win32 Debug" (based on "Win32 (x86) Application") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe MTL=midl.exe RSC=rc.exe !IF "$(CFG)" == "testcrypt - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" # PROP Intermediate_Dir "Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c # ADD CPP /nologo /W3 /GX /O2 /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x809 /d "NDEBUG" # ADD RSC /l 0x809 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 # ADD LINK32 /nologo /subsystem:console /machine:I386 # SUBTRACT LINK32 /pdb:none !ELSEIF "$(CFG)" == "testcrypt - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "Debug" # PROP BASE Intermediate_Dir "Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "Debug" # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c # ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "..\..\..\./" /I "..\..\..\charset/" /I "..\..\..\windows/" /I "..\..\..\unix/" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x809 /d "_DEBUG" # ADD RSC /l 0x809 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept # ADD LINK32 /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept # SUBTRACT LINK32 /pdb:none !ENDIF # Begin Target # Name "testcrypt - Win32 Release" # Name "testcrypt - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=..\..\..\ecc.c # End Source File # Begin Source File SOURCE=..\..\..\marshal.c # End Source File # Begin Source File SOURCE=..\..\..\memory.c # End Source File # Begin Source File SOURCE=..\..\..\millerrabin.c # End Source File # Begin Source File SOURCE=..\..\..\mpint.c # End Source File # Begin Source File SOURCE=..\..\..\mpunsafe.c # End Source File # Begin Source File SOURCE=..\..\..\pockle.c # End Source File # Begin Source File SOURCE=..\..\..\primecandidate.c # End Source File # Begin Source File SOURCE=..\..\..\smallprimes.c # End Source File # Begin Source File SOURCE=..\..\..\sshaes.c # End Source File # Begin Source File SOURCE=..\..\..\ssharcf.c # End Source File # Begin Source File SOURCE=..\..\..\sshargon2.c # End Source File # Begin Source File SOURCE=..\..\..\sshauxcrypt.c # End Source File # Begin Source File SOURCE=..\..\..\sshblake2.c # End Source File # Begin Source File SOURCE=..\..\..\sshblowf.c # End Source File # Begin Source File SOURCE=..\..\..\sshccp.c # End Source File # Begin Source File SOURCE=..\..\..\sshcrc.c # End Source File # Begin Source File SOURCE=..\..\..\sshcrcda.c # End Source File # Begin Source File SOURCE=..\..\..\sshdes.c # End Source File # Begin Source File SOURCE=..\..\..\sshdh.c # End Source File # Begin Source File SOURCE=..\..\..\sshdss.c # End Source File # Begin Source File SOURCE=..\..\..\sshdssg.c # End Source File # Begin Source File SOURCE=..\..\..\sshecc.c # End Source File # Begin Source File SOURCE=..\..\..\sshecdsag.c # End Source File # Begin Source File SOURCE=..\..\..\sshhmac.c # End Source File # Begin Source File SOURCE=..\..\..\sshmd5.c # End Source File # Begin Source File SOURCE=..\..\..\sshprime.c # End Source File # Begin Source File SOURCE=..\..\..\sshprng.c # End Source File # Begin Source File SOURCE=..\..\..\sshpubk.c # End Source File # Begin Source File SOURCE=..\..\..\sshrsa.c # End Source File # Begin Source File SOURCE=..\..\..\sshrsag.c # End Source File # Begin Source File SOURCE=..\..\..\sshsh256.c # End Source File # Begin Source File SOURCE=..\..\..\sshsh512.c # End Source File # Begin Source File SOURCE=..\..\..\sshsha.c # End Source File # Begin Source File SOURCE=..\..\..\sshsha3.c # End Source File # Begin Source File SOURCE=..\..\..\testcrypt.c # End Source File # Begin Source File SOURCE=..\..\..\tree234.c # End Source File # Begin Source File SOURCE=..\..\..\utils.c # End Source File # Begin Source File SOURCE=..\..\..\windows\winmiscs.c # End Source File # End Group # Begin Group "Header Files" # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File SOURCE=..\..\..\charset\charset.h # End Source File # Begin Source File SOURCE=..\..\..\defs.h # End Source File # Begin Source File SOURCE=..\..\..\ecc.h # End Source File # Begin Source File SOURCE=..\..\..\marshal.h # End Source File # Begin Source File SOURCE=..\..\..\misc.h # End Source File # Begin Source File SOURCE=..\..\..\mpint.h # End Source File # Begin Source File SOURCE=..\..\..\mpint_i.h # End Source File # Begin Source File SOURCE=..\..\..\mpunsafe.h # End Source File # Begin Source File SOURCE=..\..\..\network.h # End Source File # Begin Source File SOURCE=..\..\..\putty.h # End Source File # Begin Source File SOURCE=..\..\..\puttymem.h # End Source File # Begin Source File SOURCE=..\..\..\puttyps.h # End Source File # Begin Source File SOURCE=..\..\..\ssh.h # End Source File # Begin Source File SOURCE=..\..\..\sshblowf.h # End Source File # Begin Source File SOURCE=..\..\..\sshkeygen.h # End Source File # Begin Source File SOURCE=..\..\..\sshsignals.h # End Source File # Begin Source File SOURCE=..\..\..\sshttymodes.h # End Source File # Begin Source File SOURCE=..\..\..\testcrypt.h # End Source File # Begin Source File SOURCE=..\..\..\tree234.h # End Source File # Begin Source File SOURCE=..\..\..\unix\unix.h # End Source File # Begin Source File SOURCE=..\..\..\windows\winhelp.h # End Source File # Begin Source File SOURCE=..\..\..\windows\winstuff.h # End Source File # End Group # Begin Group "Resource Files" # PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" # End Group # End Target # End Project putty-0.76/windows/VS2010/0000755000175000017500000000000014072266316012170 500000000000000putty-0.76/windows/VS2010/putty.sln0000644000175000017500000001117214072266315014014 00000000000000Microsoft Visual Studio Solution File, Format Version 11.00 # Visual Studio 2010 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pageant", "pageant\pageant.vcxproj", "{27e8f930-2567-4fdc-82c1-0662af3cdf6d}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plink", "plink\plink.vcxproj", "{4478087b-1dc8-4c3e-b624-13661de1947e}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pscp", "pscp\pscp.vcxproj", "{26d200f3-250a-494f-aaa4-c992ec4345aa}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "psftp", "psftp\psftp.vcxproj", "{3caa8123-13fe-450d-bfe7-fcda524d6830}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "psocks", "psocks\psocks.vcxproj", "{0ce7217e-7277-408e-add3-4e2f6072486e}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "putty", "putty\putty.vcxproj", "{8f44624a-d188-4188-b9aa-5f99f20723e1}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "puttygen", "puttygen\puttygen.vcxproj", "{617b1bef-aa10-42b4-8f69-4c183bc1ec0f}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "puttytel", "puttytel\puttytel.vcxproj", "{b7dd98ff-b336-4d23-9c07-e5fa7a9f3130}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testcrypt", "testcrypt\testcrypt.vcxproj", "{232ff6d5-a626-4562-977c-2ab72baa29fd}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 Release|Win32 = Release|Win32 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {0ce7217e-7277-408e-add3-4e2f6072486e}.Debug|Win32.ActiveCfg = Debug|Win32 {0ce7217e-7277-408e-add3-4e2f6072486e}.Debug|Win32.Build.0 = Debug|Win32 {0ce7217e-7277-408e-add3-4e2f6072486e}.Release|Win32.ActiveCfg = Release|Win32 {0ce7217e-7277-408e-add3-4e2f6072486e}.Release|Win32.Build.0 = Release|Win32 {3caa8123-13fe-450d-bfe7-fcda524d6830}.Debug|Win32.ActiveCfg = Debug|Win32 {3caa8123-13fe-450d-bfe7-fcda524d6830}.Debug|Win32.Build.0 = Debug|Win32 {3caa8123-13fe-450d-bfe7-fcda524d6830}.Release|Win32.ActiveCfg = Release|Win32 {3caa8123-13fe-450d-bfe7-fcda524d6830}.Release|Win32.Build.0 = Release|Win32 {26d200f3-250a-494f-aaa4-c992ec4345aa}.Debug|Win32.ActiveCfg = Debug|Win32 {26d200f3-250a-494f-aaa4-c992ec4345aa}.Debug|Win32.Build.0 = Debug|Win32 {26d200f3-250a-494f-aaa4-c992ec4345aa}.Release|Win32.ActiveCfg = Release|Win32 {26d200f3-250a-494f-aaa4-c992ec4345aa}.Release|Win32.Build.0 = Release|Win32 {b7dd98ff-b336-4d23-9c07-e5fa7a9f3130}.Debug|Win32.ActiveCfg = Debug|Win32 {b7dd98ff-b336-4d23-9c07-e5fa7a9f3130}.Debug|Win32.Build.0 = Debug|Win32 {b7dd98ff-b336-4d23-9c07-e5fa7a9f3130}.Release|Win32.ActiveCfg = Release|Win32 {b7dd98ff-b336-4d23-9c07-e5fa7a9f3130}.Release|Win32.Build.0 = Release|Win32 {8f44624a-d188-4188-b9aa-5f99f20723e1}.Debug|Win32.ActiveCfg = Debug|Win32 {8f44624a-d188-4188-b9aa-5f99f20723e1}.Debug|Win32.Build.0 = Debug|Win32 {8f44624a-d188-4188-b9aa-5f99f20723e1}.Release|Win32.ActiveCfg = Release|Win32 {8f44624a-d188-4188-b9aa-5f99f20723e1}.Release|Win32.Build.0 = Release|Win32 {27e8f930-2567-4fdc-82c1-0662af3cdf6d}.Debug|Win32.ActiveCfg = Debug|Win32 {27e8f930-2567-4fdc-82c1-0662af3cdf6d}.Debug|Win32.Build.0 = Debug|Win32 {27e8f930-2567-4fdc-82c1-0662af3cdf6d}.Release|Win32.ActiveCfg = Release|Win32 {27e8f930-2567-4fdc-82c1-0662af3cdf6d}.Release|Win32.Build.0 = Release|Win32 {617b1bef-aa10-42b4-8f69-4c183bc1ec0f}.Debug|Win32.ActiveCfg = Debug|Win32 {617b1bef-aa10-42b4-8f69-4c183bc1ec0f}.Debug|Win32.Build.0 = Debug|Win32 {617b1bef-aa10-42b4-8f69-4c183bc1ec0f}.Release|Win32.ActiveCfg = Release|Win32 {617b1bef-aa10-42b4-8f69-4c183bc1ec0f}.Release|Win32.Build.0 = Release|Win32 {4478087b-1dc8-4c3e-b624-13661de1947e}.Debug|Win32.ActiveCfg = Debug|Win32 {4478087b-1dc8-4c3e-b624-13661de1947e}.Debug|Win32.Build.0 = Debug|Win32 {4478087b-1dc8-4c3e-b624-13661de1947e}.Release|Win32.ActiveCfg = Release|Win32 {4478087b-1dc8-4c3e-b624-13661de1947e}.Release|Win32.Build.0 = Release|Win32 {232ff6d5-a626-4562-977c-2ab72baa29fd}.Debug|Win32.ActiveCfg = Debug|Win32 {232ff6d5-a626-4562-977c-2ab72baa29fd}.Debug|Win32.Build.0 = Debug|Win32 {232ff6d5-a626-4562-977c-2ab72baa29fd}.Release|Win32.ActiveCfg = Release|Win32 {232ff6d5-a626-4562-977c-2ab72baa29fd}.Release|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal putty-0.76/windows/VS2010/pageant/0000755000175000017500000000000014072266316013607 500000000000000putty-0.76/windows/VS2010/pageant/pageant.vcxproj0000644000175000017500000002534714072266315016575 00000000000000 Debug Win32 Release Win32 {27e8f930-2567-4fdc-82c1-0662af3cdf6d} Application false MultiByte v100 Application false MultiByte v100 .\Release\ .\Release\ false .\Debug\ .\Debug\ true MultiThreaded OnlyExplicitInline true true MaxSpeed true Level3 ..\..\..\./;..\..\..\charset/;..\..\..\windows/;..\..\..\unix/;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_WINDOWS;POSIX;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) .\Release\ .\Release\pageant.pch .\Release\ .\Release\ true NDEBUG;%(PreprocessorDefinitions) .\Release\pageant.tlb true Win32 0x0809 NDEBUG;%(PreprocessorDefinitions) true .\Release\pageant.bsc true Windows .\Release\pageant.exe advapi32.lib;comdlg32.lib;gdi32.lib;imm32.lib;ole32.lib;shell32.lib;user32.lib;%(AdditionalDependencies) MultiThreadedDebug Default false Disabled true Level3 true ProgramDatabase ..\..\..\./;..\..\..\charset/;..\..\..\windows/;..\..\..\unix/;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_WINDOWS;POSIX;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) .\Debug\ .\Debug\pageant.pch .\Debug\ .\Debug\ EnableFastChecks true _DEBUG;%(PreprocessorDefinitions) .\Debug\pageant.tlb true Win32 0x0809 _DEBUG;%(PreprocessorDefinitions) true .\Debug\pageant.bsc true true Windows $(TargetPath) advapi32.lib;comdlg32.lib;gdi32.lib;imm32.lib;ole32.lib;shell32.lib;user32.lib;%(AdditionalDependencies) ..\..;%(AdditionalIncludeDirectories) ..\..;%(AdditionalIncludeDirectories) putty-0.76/windows/VS2010/pageant/pageant.vcxproj.filters0000644000175000017500000002001514072266315020227 00000000000000 Resource Files Resource Files Resource Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files {700cfdc0-1885-4a48-9760-6fd8f3f62b09} {25d39962-0c67-4b83-a85f-ca32bded1354} {f84c3ab7-54fe-48c4-be65-5115ae325892} putty-0.76/windows/VS2010/plink/0000755000175000017500000000000014072266316013305 500000000000000putty-0.76/windows/VS2010/plink/plink.vcxproj0000644000175000017500000003453714072266315015772 00000000000000 Debug Win32 Release Win32 {4478087b-1dc8-4c3e-b624-13661de1947e} Application false MultiByte v100 Application false MultiByte v100 .\Release\ .\Release\ false .\Debug\ .\Debug\ true MultiThreaded OnlyExplicitInline true true MaxSpeed true Level3 ..\..\..\./;..\..\..\charset/;..\..\..\windows/;..\..\..\unix/;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_WINDOWS;POSIX;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) .\Release\ .\Release\plink.pch .\Release\ .\Release\ true NDEBUG;%(PreprocessorDefinitions) .\Release\plink.tlb true Win32 0x0809 NDEBUG;%(PreprocessorDefinitions) true .\Release\plink.bsc true Console .\Release\plink.exe advapi32.lib;comdlg32.lib;gdi32.lib;imm32.lib;ole32.lib;shell32.lib;user32.lib;%(AdditionalDependencies) MultiThreadedDebug Default false Disabled true Level3 true ProgramDatabase ..\..\..\./;..\..\..\charset/;..\..\..\windows/;..\..\..\unix/;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_WINDOWS;POSIX;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) .\Debug\ .\Debug\plink.pch .\Debug\ .\Debug\ EnableFastChecks true _DEBUG;%(PreprocessorDefinitions) .\Debug\plink.tlb true Win32 0x0809 _DEBUG;%(PreprocessorDefinitions) true .\Debug\plink.bsc true true Console $(TargetPath) advapi32.lib;comdlg32.lib;gdi32.lib;imm32.lib;ole32.lib;shell32.lib;user32.lib;%(AdditionalDependencies) ..\..;%(AdditionalIncludeDirectories) ..\..;%(AdditionalIncludeDirectories) putty-0.76/windows/VS2010/plink/plink.vcxproj.filters0000644000175000017500000003671214072266315017436 00000000000000 Resource Files Resource Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files {cabf3930-a192-46fa-9d0b-c12a3c48fb64} {211b9eeb-2f7b-4f17-ba17-7fa34bd6d53f} {7398cce3-d4c6-47d7-8876-968d1b521c8d} putty-0.76/windows/VS2010/pscp/0000755000175000017500000000000014072266316013135 500000000000000putty-0.76/windows/VS2010/pscp/pscp.vcxproj0000644000175000017500000003424614072266315015447 00000000000000 Debug Win32 Release Win32 {26d200f3-250a-494f-aaa4-c992ec4345aa} Application false MultiByte v100 Application false MultiByte v100 .\Release\ .\Release\ false .\Debug\ .\Debug\ true MultiThreaded OnlyExplicitInline true true MaxSpeed true Level3 ..\..\..\./;..\..\..\charset/;..\..\..\windows/;..\..\..\unix/;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_WINDOWS;POSIX;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) .\Release\ .\Release\pscp.pch .\Release\ .\Release\ true NDEBUG;%(PreprocessorDefinitions) .\Release\pscp.tlb true Win32 0x0809 NDEBUG;%(PreprocessorDefinitions) true .\Release\pscp.bsc true Console .\Release\pscp.exe advapi32.lib;comdlg32.lib;gdi32.lib;imm32.lib;ole32.lib;shell32.lib;user32.lib;%(AdditionalDependencies) MultiThreadedDebug Default false Disabled true Level3 true ProgramDatabase ..\..\..\./;..\..\..\charset/;..\..\..\windows/;..\..\..\unix/;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_WINDOWS;POSIX;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) .\Debug\ .\Debug\pscp.pch .\Debug\ .\Debug\ EnableFastChecks true _DEBUG;%(PreprocessorDefinitions) .\Debug\pscp.tlb true Win32 0x0809 _DEBUG;%(PreprocessorDefinitions) true .\Debug\pscp.bsc true true Console $(TargetPath) advapi32.lib;comdlg32.lib;gdi32.lib;imm32.lib;ole32.lib;shell32.lib;user32.lib;%(AdditionalDependencies) ..\..;%(AdditionalIncludeDirectories) ..\..;%(AdditionalIncludeDirectories) putty-0.76/windows/VS2010/pscp/pscp.vcxproj.filters0000644000175000017500000003612014072266315017107 00000000000000 Resource Files Resource Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files {22ad16c0-10f2-4c9e-88ce-19909db06db1} {1939563c-d990-4021-aabb-fe01df059ed9} {a470e852-120c-47b4-8b4b-2caf18dec71c} putty-0.76/windows/VS2010/psftp/0000755000175000017500000000000014072266316013324 500000000000000putty-0.76/windows/VS2010/psftp/psftp.vcxproj0000644000175000017500000003425714072266315016027 00000000000000 Debug Win32 Release Win32 {3caa8123-13fe-450d-bfe7-fcda524d6830} Application false MultiByte v100 Application false MultiByte v100 .\Release\ .\Release\ false .\Debug\ .\Debug\ true MultiThreaded OnlyExplicitInline true true MaxSpeed true Level3 ..\..\..\./;..\..\..\charset/;..\..\..\windows/;..\..\..\unix/;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_WINDOWS;POSIX;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) .\Release\ .\Release\psftp.pch .\Release\ .\Release\ true NDEBUG;%(PreprocessorDefinitions) .\Release\psftp.tlb true Win32 0x0809 NDEBUG;%(PreprocessorDefinitions) true .\Release\psftp.bsc true Console .\Release\psftp.exe advapi32.lib;comdlg32.lib;gdi32.lib;imm32.lib;ole32.lib;shell32.lib;user32.lib;%(AdditionalDependencies) MultiThreadedDebug Default false Disabled true Level3 true ProgramDatabase ..\..\..\./;..\..\..\charset/;..\..\..\windows/;..\..\..\unix/;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_WINDOWS;POSIX;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) .\Debug\ .\Debug\psftp.pch .\Debug\ .\Debug\ EnableFastChecks true _DEBUG;%(PreprocessorDefinitions) .\Debug\psftp.tlb true Win32 0x0809 _DEBUG;%(PreprocessorDefinitions) true .\Debug\psftp.bsc true true Console $(TargetPath) advapi32.lib;comdlg32.lib;gdi32.lib;imm32.lib;ole32.lib;shell32.lib;user32.lib;%(AdditionalDependencies) ..\..;%(AdditionalIncludeDirectories) ..\..;%(AdditionalIncludeDirectories) putty-0.76/windows/VS2010/psftp/psftp.vcxproj.filters0000644000175000017500000003612214072266315017467 00000000000000 Resource Files Resource Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files {156ce40f-dc1b-4721-b08f-6336b9f4d290} {198936a4-3dd8-46c7-8266-b3a081e1e0fc} {a85261db-1733-4f46-b1b9-cf01dd3da697} putty-0.76/windows/VS2010/psocks/0000755000175000017500000000000014072266316013472 500000000000000putty-0.76/windows/VS2010/psocks/psocks.vcxproj0000644000175000017500000002262014072266315016332 00000000000000 Debug Win32 Release Win32 {0ce7217e-7277-408e-add3-4e2f6072486e} Application false MultiByte v100 Application false MultiByte v100 .\Release\ .\Release\ false .\Debug\ .\Debug\ true MultiThreaded OnlyExplicitInline true true MaxSpeed true Level3 ..\..\..\./;..\..\..\charset/;..\..\..\windows/;..\..\..\unix/;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_WINDOWS;POSIX;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) .\Release\ .\Release\psocks.pch .\Release\ .\Release\ true NDEBUG;%(PreprocessorDefinitions) .\Release\psocks.tlb true Win32 0x0809 NDEBUG;%(PreprocessorDefinitions) true .\Release\psocks.bsc true Console .\Release\psocks.exe advapi32.lib;comdlg32.lib;gdi32.lib;imm32.lib;ole32.lib;shell32.lib;user32.lib;%(AdditionalDependencies) MultiThreadedDebug Default false Disabled true Level3 true ProgramDatabase ..\..\..\./;..\..\..\charset/;..\..\..\windows/;..\..\..\unix/;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_WINDOWS;POSIX;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) .\Debug\ .\Debug\psocks.pch .\Debug\ .\Debug\ EnableFastChecks true _DEBUG;%(PreprocessorDefinitions) .\Debug\psocks.tlb true Win32 0x0809 _DEBUG;%(PreprocessorDefinitions) true .\Debug\psocks.bsc true true Console $(TargetPath) advapi32.lib;comdlg32.lib;gdi32.lib;imm32.lib;ole32.lib;shell32.lib;user32.lib;%(AdditionalDependencies) putty-0.76/windows/VS2010/psocks/psocks.vcxproj.filters0000644000175000017500000001367314072266315020011 00000000000000 Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files {cbddcf51-a122-4b3b-9666-3c7319e4236d} {f6732610-4305-4bfe-9c1b-f63396c38c4d} {e639ecc0-211b-4aaa-9fca-83ad6cda2ba7} putty-0.76/windows/VS2010/putty/0000755000175000017500000000000014072266316013355 500000000000000putty-0.76/windows/VS2010/putty/putty.vcxproj0000644000175000017500000003530114072266315016100 00000000000000 Debug Win32 Release Win32 {8f44624a-d188-4188-b9aa-5f99f20723e1} Application false MultiByte v100 Application false MultiByte v100 .\Release\ .\Release\ false .\Debug\ .\Debug\ true MultiThreaded OnlyExplicitInline true true MaxSpeed true Level3 ..\..\..\./;..\..\..\charset/;..\..\..\windows/;..\..\..\unix/;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_WINDOWS;POSIX;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) .\Release\ .\Release\putty.pch .\Release\ .\Release\ true NDEBUG;%(PreprocessorDefinitions) .\Release\putty.tlb true Win32 0x0809 NDEBUG;%(PreprocessorDefinitions) true .\Release\putty.bsc true Windows .\Release\putty.exe advapi32.lib;comdlg32.lib;gdi32.lib;imm32.lib;ole32.lib;shell32.lib;user32.lib;%(AdditionalDependencies) MultiThreadedDebug Default false Disabled true Level3 true ProgramDatabase ..\..\..\./;..\..\..\charset/;..\..\..\windows/;..\..\..\unix/;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_WINDOWS;POSIX;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) .\Debug\ .\Debug\putty.pch .\Debug\ .\Debug\ EnableFastChecks true _DEBUG;%(PreprocessorDefinitions) .\Debug\putty.tlb true Win32 0x0809 _DEBUG;%(PreprocessorDefinitions) true .\Debug\putty.bsc true true Windows $(TargetPath) advapi32.lib;comdlg32.lib;gdi32.lib;imm32.lib;ole32.lib;shell32.lib;user32.lib;%(AdditionalDependencies) ..\..;%(AdditionalIncludeDirectories) ..\..;%(AdditionalIncludeDirectories) putty-0.76/windows/VS2010/putty/putty.vcxproj.filters0000644000175000017500000004030114072266315017543 00000000000000 Resource Files Resource Files Resource Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files {382c3c77-0fea-4132-a5ae-2550d8a5f395} {8c940eae-8579-4a10-b1a1-bd19ff6e8c9c} {e34b5d5d-ec5e-422e-9244-e7dd2783eead} putty-0.76/windows/VS2010/puttygen/0000755000175000017500000000000014072266316014047 500000000000000putty-0.76/windows/VS2010/puttygen/puttygen.vcxproj0000644000175000017500000002614214072266315017267 00000000000000 Debug Win32 Release Win32 {617b1bef-aa10-42b4-8f69-4c183bc1ec0f} Application false MultiByte v100 Application false MultiByte v100 .\Release\ .\Release\ false .\Debug\ .\Debug\ true MultiThreaded OnlyExplicitInline true true MaxSpeed true Level3 ..\..\..\./;..\..\..\charset/;..\..\..\windows/;..\..\..\unix/;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_WINDOWS;POSIX;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) .\Release\ .\Release\puttygen.pch .\Release\ .\Release\ true NDEBUG;%(PreprocessorDefinitions) .\Release\puttygen.tlb true Win32 0x0809 NDEBUG;%(PreprocessorDefinitions) true .\Release\puttygen.bsc true Windows .\Release\puttygen.exe advapi32.lib;comdlg32.lib;gdi32.lib;imm32.lib;ole32.lib;shell32.lib;user32.lib;%(AdditionalDependencies) MultiThreadedDebug Default false Disabled true Level3 true ProgramDatabase ..\..\..\./;..\..\..\charset/;..\..\..\windows/;..\..\..\unix/;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_WINDOWS;POSIX;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) .\Debug\ .\Debug\puttygen.pch .\Debug\ .\Debug\ EnableFastChecks true _DEBUG;%(PreprocessorDefinitions) .\Debug\puttygen.tlb true Win32 0x0809 _DEBUG;%(PreprocessorDefinitions) true .\Debug\puttygen.bsc true true Windows $(TargetPath) advapi32.lib;comdlg32.lib;gdi32.lib;imm32.lib;ole32.lib;shell32.lib;user32.lib;%(AdditionalDependencies) ..\..;%(AdditionalIncludeDirectories) ..\..;%(AdditionalIncludeDirectories) putty-0.76/windows/VS2010/puttygen/puttygen.vcxproj.filters0000644000175000017500000002133214072266315020732 00000000000000 Resource Files Resource Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files {c7e8416f-274c-464e-9d2e-049a34f7e1ef} {ea7fa817-a012-4ecd-8900-9d9896b44c96} {1f675268-5bec-451b-b9c9-30ca628ac7d7} putty-0.76/windows/VS2010/puttytel/0000755000175000017500000000000014072266316014062 500000000000000putty-0.76/windows/VS2010/puttytel/puttytel.vcxproj0000644000175000017500000002624714072266315017323 00000000000000 Debug Win32 Release Win32 {b7dd98ff-b336-4d23-9c07-e5fa7a9f3130} Application false MultiByte v100 Application false MultiByte v100 .\Release\ .\Release\ false .\Debug\ .\Debug\ true MultiThreaded OnlyExplicitInline true true MaxSpeed true Level3 ..\..\..\./;..\..\..\charset/;..\..\..\windows/;..\..\..\unix/;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_WINDOWS;POSIX;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) .\Release\ .\Release\puttytel.pch .\Release\ .\Release\ true NDEBUG;%(PreprocessorDefinitions) .\Release\puttytel.tlb true Win32 0x0809 NDEBUG;%(PreprocessorDefinitions) true .\Release\puttytel.bsc true Windows .\Release\puttytel.exe advapi32.lib;comdlg32.lib;gdi32.lib;imm32.lib;ole32.lib;shell32.lib;user32.lib;%(AdditionalDependencies) MultiThreadedDebug Default false Disabled true Level3 true ProgramDatabase ..\..\..\./;..\..\..\charset/;..\..\..\windows/;..\..\..\unix/;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_WINDOWS;POSIX;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) .\Debug\ .\Debug\puttytel.pch .\Debug\ .\Debug\ EnableFastChecks true _DEBUG;%(PreprocessorDefinitions) .\Debug\puttytel.tlb true Win32 0x0809 _DEBUG;%(PreprocessorDefinitions) true .\Debug\puttytel.bsc true true Windows $(TargetPath) advapi32.lib;comdlg32.lib;gdi32.lib;imm32.lib;ole32.lib;shell32.lib;user32.lib;%(AdditionalDependencies) ..\..;%(AdditionalIncludeDirectories) ..\..;%(AdditionalIncludeDirectories) putty-0.76/windows/VS2010/puttytel/puttytel.vcxproj.filters0000644000175000017500000002167214072266315020767 00000000000000 Resource Files Resource Files Resource Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files {f8a8578e-e0a7-4047-85b7-4063a874d8f1} {8e1311d1-7c8d-4ba8-bf25-a7d452a53090} {02db3bf1-02b5-4667-b7f8-455e6493b006} putty-0.76/windows/VS2010/testcrypt/0000755000175000017500000000000014072266316014231 500000000000000putty-0.76/windows/VS2010/testcrypt/testcrypt.vcxproj0000644000175000017500000002304514072266315017632 00000000000000 Debug Win32 Release Win32 {232ff6d5-a626-4562-977c-2ab72baa29fd} Application false MultiByte v100 Application false MultiByte v100 .\Release\ .\Release\ false .\Debug\ .\Debug\ true MultiThreaded OnlyExplicitInline true true MaxSpeed true Level3 ..\..\..\./;..\..\..\charset/;..\..\..\windows/;..\..\..\unix/;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_WINDOWS;POSIX;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) .\Release\ .\Release\testcrypt.pch .\Release\ .\Release\ true NDEBUG;%(PreprocessorDefinitions) .\Release\testcrypt.tlb true Win32 0x0809 NDEBUG;%(PreprocessorDefinitions) true .\Release\testcrypt.bsc true Console .\Release\testcrypt.exe ;%(AdditionalDependencies) MultiThreadedDebug Default false Disabled true Level3 true ProgramDatabase ..\..\..\./;..\..\..\charset/;..\..\..\windows/;..\..\..\unix/;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_WINDOWS;POSIX;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) .\Debug\ .\Debug\testcrypt.pch .\Debug\ .\Debug\ EnableFastChecks true _DEBUG;%(PreprocessorDefinitions) .\Debug\testcrypt.tlb true Win32 0x0809 _DEBUG;%(PreprocessorDefinitions) true .\Debug\testcrypt.bsc true true Console $(TargetPath) ;%(AdditionalDependencies) putty-0.76/windows/VS2010/testcrypt/testcrypt.vcxproj.filters0000644000175000017500000001472114072266315021302 00000000000000 Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files {da1388df-79d3-4aa3-bb29-c40cd08b764d} {b1161d2a-d615-4e3c-b4a4-790cdcd86977} {d873d947-5884-4fb8-a70a-6a227fee257d} putty-0.76/windows/VS2012/0000755000175000017500000000000014072266316012172 500000000000000putty-0.76/windows/VS2012/putty.sln0000644000175000017500000001117214072266315014016 00000000000000Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2012 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pageant", "pageant\pageant.vcxproj", "{27e8f930-2567-4fdc-82c1-0662af3cdf6d}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "plink", "plink\plink.vcxproj", "{4478087b-1dc8-4c3e-b624-13661de1947e}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pscp", "pscp\pscp.vcxproj", "{26d200f3-250a-494f-aaa4-c992ec4345aa}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "psftp", "psftp\psftp.vcxproj", "{3caa8123-13fe-450d-bfe7-fcda524d6830}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "psocks", "psocks\psocks.vcxproj", "{0ce7217e-7277-408e-add3-4e2f6072486e}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "putty", "putty\putty.vcxproj", "{8f44624a-d188-4188-b9aa-5f99f20723e1}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "puttygen", "puttygen\puttygen.vcxproj", "{617b1bef-aa10-42b4-8f69-4c183bc1ec0f}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "puttytel", "puttytel\puttytel.vcxproj", "{b7dd98ff-b336-4d23-9c07-e5fa7a9f3130}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testcrypt", "testcrypt\testcrypt.vcxproj", "{232ff6d5-a626-4562-977c-2ab72baa29fd}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 Release|Win32 = Release|Win32 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {26d200f3-250a-494f-aaa4-c992ec4345aa}.Debug|Win32.ActiveCfg = Debug|Win32 {26d200f3-250a-494f-aaa4-c992ec4345aa}.Debug|Win32.Build.0 = Debug|Win32 {26d200f3-250a-494f-aaa4-c992ec4345aa}.Release|Win32.ActiveCfg = Release|Win32 {26d200f3-250a-494f-aaa4-c992ec4345aa}.Release|Win32.Build.0 = Release|Win32 {3caa8123-13fe-450d-bfe7-fcda524d6830}.Debug|Win32.ActiveCfg = Debug|Win32 {3caa8123-13fe-450d-bfe7-fcda524d6830}.Debug|Win32.Build.0 = Debug|Win32 {3caa8123-13fe-450d-bfe7-fcda524d6830}.Release|Win32.ActiveCfg = Release|Win32 {3caa8123-13fe-450d-bfe7-fcda524d6830}.Release|Win32.Build.0 = Release|Win32 {0ce7217e-7277-408e-add3-4e2f6072486e}.Debug|Win32.ActiveCfg = Debug|Win32 {0ce7217e-7277-408e-add3-4e2f6072486e}.Debug|Win32.Build.0 = Debug|Win32 {0ce7217e-7277-408e-add3-4e2f6072486e}.Release|Win32.ActiveCfg = Release|Win32 {0ce7217e-7277-408e-add3-4e2f6072486e}.Release|Win32.Build.0 = Release|Win32 {4478087b-1dc8-4c3e-b624-13661de1947e}.Debug|Win32.ActiveCfg = Debug|Win32 {4478087b-1dc8-4c3e-b624-13661de1947e}.Debug|Win32.Build.0 = Debug|Win32 {4478087b-1dc8-4c3e-b624-13661de1947e}.Release|Win32.ActiveCfg = Release|Win32 {4478087b-1dc8-4c3e-b624-13661de1947e}.Release|Win32.Build.0 = Release|Win32 {232ff6d5-a626-4562-977c-2ab72baa29fd}.Debug|Win32.ActiveCfg = Debug|Win32 {232ff6d5-a626-4562-977c-2ab72baa29fd}.Debug|Win32.Build.0 = Debug|Win32 {232ff6d5-a626-4562-977c-2ab72baa29fd}.Release|Win32.ActiveCfg = Release|Win32 {232ff6d5-a626-4562-977c-2ab72baa29fd}.Release|Win32.Build.0 = Release|Win32 {617b1bef-aa10-42b4-8f69-4c183bc1ec0f}.Debug|Win32.ActiveCfg = Debug|Win32 {617b1bef-aa10-42b4-8f69-4c183bc1ec0f}.Debug|Win32.Build.0 = Debug|Win32 {617b1bef-aa10-42b4-8f69-4c183bc1ec0f}.Release|Win32.ActiveCfg = Release|Win32 {617b1bef-aa10-42b4-8f69-4c183bc1ec0f}.Release|Win32.Build.0 = Release|Win32 {b7dd98ff-b336-4d23-9c07-e5fa7a9f3130}.Debug|Win32.ActiveCfg = Debug|Win32 {b7dd98ff-b336-4d23-9c07-e5fa7a9f3130}.Debug|Win32.Build.0 = Debug|Win32 {b7dd98ff-b336-4d23-9c07-e5fa7a9f3130}.Release|Win32.ActiveCfg = Release|Win32 {b7dd98ff-b336-4d23-9c07-e5fa7a9f3130}.Release|Win32.Build.0 = Release|Win32 {27e8f930-2567-4fdc-82c1-0662af3cdf6d}.Debug|Win32.ActiveCfg = Debug|Win32 {27e8f930-2567-4fdc-82c1-0662af3cdf6d}.Debug|Win32.Build.0 = Debug|Win32 {27e8f930-2567-4fdc-82c1-0662af3cdf6d}.Release|Win32.ActiveCfg = Release|Win32 {27e8f930-2567-4fdc-82c1-0662af3cdf6d}.Release|Win32.Build.0 = Release|Win32 {8f44624a-d188-4188-b9aa-5f99f20723e1}.Debug|Win32.ActiveCfg = Debug|Win32 {8f44624a-d188-4188-b9aa-5f99f20723e1}.Debug|Win32.Build.0 = Debug|Win32 {8f44624a-d188-4188-b9aa-5f99f20723e1}.Release|Win32.ActiveCfg = Release|Win32 {8f44624a-d188-4188-b9aa-5f99f20723e1}.Release|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal putty-0.76/windows/VS2012/pageant/0000755000175000017500000000000014072266316013611 500000000000000putty-0.76/windows/VS2012/pageant/pageant.vcxproj0000644000175000017500000002534714072266315016577 00000000000000 Debug Win32 Release Win32 {27e8f930-2567-4fdc-82c1-0662af3cdf6d} Application false MultiByte v110 Application false MultiByte v110 .\Release\ .\Release\ false .\Debug\ .\Debug\ true MultiThreaded OnlyExplicitInline true true MaxSpeed true Level3 ..\..\..\./;..\..\..\charset/;..\..\..\windows/;..\..\..\unix/;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_WINDOWS;POSIX;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) .\Release\ .\Release\pageant.pch .\Release\ .\Release\ true NDEBUG;%(PreprocessorDefinitions) .\Release\pageant.tlb true Win32 0x0809 NDEBUG;%(PreprocessorDefinitions) true .\Release\pageant.bsc true Windows .\Release\pageant.exe advapi32.lib;comdlg32.lib;gdi32.lib;imm32.lib;ole32.lib;shell32.lib;user32.lib;%(AdditionalDependencies) MultiThreadedDebug Default false Disabled true Level3 true ProgramDatabase ..\..\..\./;..\..\..\charset/;..\..\..\windows/;..\..\..\unix/;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_WINDOWS;POSIX;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) .\Debug\ .\Debug\pageant.pch .\Debug\ .\Debug\ EnableFastChecks true _DEBUG;%(PreprocessorDefinitions) .\Debug\pageant.tlb true Win32 0x0809 _DEBUG;%(PreprocessorDefinitions) true .\Debug\pageant.bsc true true Windows $(TargetPath) advapi32.lib;comdlg32.lib;gdi32.lib;imm32.lib;ole32.lib;shell32.lib;user32.lib;%(AdditionalDependencies) ..\..;%(AdditionalIncludeDirectories) ..\..;%(AdditionalIncludeDirectories) putty-0.76/windows/VS2012/pageant/pageant.vcxproj.filters0000644000175000017500000002001514072266315020231 00000000000000 Resource Files Resource Files Resource Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files {700cfdc0-1885-4a48-9760-6fd8f3f62b09} {25d39962-0c67-4b83-a85f-ca32bded1354} {f84c3ab7-54fe-48c4-be65-5115ae325892} putty-0.76/windows/VS2012/plink/0000755000175000017500000000000014072266316013307 500000000000000putty-0.76/windows/VS2012/plink/plink.vcxproj0000644000175000017500000003453714072266315015774 00000000000000 Debug Win32 Release Win32 {4478087b-1dc8-4c3e-b624-13661de1947e} Application false MultiByte v110 Application false MultiByte v110 .\Release\ .\Release\ false .\Debug\ .\Debug\ true MultiThreaded OnlyExplicitInline true true MaxSpeed true Level3 ..\..\..\./;..\..\..\charset/;..\..\..\windows/;..\..\..\unix/;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_WINDOWS;POSIX;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) .\Release\ .\Release\plink.pch .\Release\ .\Release\ true NDEBUG;%(PreprocessorDefinitions) .\Release\plink.tlb true Win32 0x0809 NDEBUG;%(PreprocessorDefinitions) true .\Release\plink.bsc true Console .\Release\plink.exe advapi32.lib;comdlg32.lib;gdi32.lib;imm32.lib;ole32.lib;shell32.lib;user32.lib;%(AdditionalDependencies) MultiThreadedDebug Default false Disabled true Level3 true ProgramDatabase ..\..\..\./;..\..\..\charset/;..\..\..\windows/;..\..\..\unix/;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_WINDOWS;POSIX;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) .\Debug\ .\Debug\plink.pch .\Debug\ .\Debug\ EnableFastChecks true _DEBUG;%(PreprocessorDefinitions) .\Debug\plink.tlb true Win32 0x0809 _DEBUG;%(PreprocessorDefinitions) true .\Debug\plink.bsc true true Console $(TargetPath) advapi32.lib;comdlg32.lib;gdi32.lib;imm32.lib;ole32.lib;shell32.lib;user32.lib;%(AdditionalDependencies) ..\..;%(AdditionalIncludeDirectories) ..\..;%(AdditionalIncludeDirectories) putty-0.76/windows/VS2012/plink/plink.vcxproj.filters0000644000175000017500000003671214072266315017440 00000000000000 Resource Files Resource Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files {cabf3930-a192-46fa-9d0b-c12a3c48fb64} {211b9eeb-2f7b-4f17-ba17-7fa34bd6d53f} {7398cce3-d4c6-47d7-8876-968d1b521c8d} putty-0.76/windows/VS2012/pscp/0000755000175000017500000000000014072266316013137 500000000000000putty-0.76/windows/VS2012/pscp/pscp.vcxproj0000644000175000017500000003424614072266315015451 00000000000000 Debug Win32 Release Win32 {26d200f3-250a-494f-aaa4-c992ec4345aa} Application false MultiByte v110 Application false MultiByte v110 .\Release\ .\Release\ false .\Debug\ .\Debug\ true MultiThreaded OnlyExplicitInline true true MaxSpeed true Level3 ..\..\..\./;..\..\..\charset/;..\..\..\windows/;..\..\..\unix/;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_WINDOWS;POSIX;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) .\Release\ .\Release\pscp.pch .\Release\ .\Release\ true NDEBUG;%(PreprocessorDefinitions) .\Release\pscp.tlb true Win32 0x0809 NDEBUG;%(PreprocessorDefinitions) true .\Release\pscp.bsc true Console .\Release\pscp.exe advapi32.lib;comdlg32.lib;gdi32.lib;imm32.lib;ole32.lib;shell32.lib;user32.lib;%(AdditionalDependencies) MultiThreadedDebug Default false Disabled true Level3 true ProgramDatabase ..\..\..\./;..\..\..\charset/;..\..\..\windows/;..\..\..\unix/;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_WINDOWS;POSIX;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) .\Debug\ .\Debug\pscp.pch .\Debug\ .\Debug\ EnableFastChecks true _DEBUG;%(PreprocessorDefinitions) .\Debug\pscp.tlb true Win32 0x0809 _DEBUG;%(PreprocessorDefinitions) true .\Debug\pscp.bsc true true Console $(TargetPath) advapi32.lib;comdlg32.lib;gdi32.lib;imm32.lib;ole32.lib;shell32.lib;user32.lib;%(AdditionalDependencies) ..\..;%(AdditionalIncludeDirectories) ..\..;%(AdditionalIncludeDirectories) putty-0.76/windows/VS2012/pscp/pscp.vcxproj.filters0000644000175000017500000003612014072266315017111 00000000000000 Resource Files Resource Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files {22ad16c0-10f2-4c9e-88ce-19909db06db1} {1939563c-d990-4021-aabb-fe01df059ed9} {a470e852-120c-47b4-8b4b-2caf18dec71c} putty-0.76/windows/VS2012/psftp/0000755000175000017500000000000014072266316013326 500000000000000putty-0.76/windows/VS2012/psftp/psftp.vcxproj0000644000175000017500000003425714072266315016031 00000000000000 Debug Win32 Release Win32 {3caa8123-13fe-450d-bfe7-fcda524d6830} Application false MultiByte v110 Application false MultiByte v110 .\Release\ .\Release\ false .\Debug\ .\Debug\ true MultiThreaded OnlyExplicitInline true true MaxSpeed true Level3 ..\..\..\./;..\..\..\charset/;..\..\..\windows/;..\..\..\unix/;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_WINDOWS;POSIX;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) .\Release\ .\Release\psftp.pch .\Release\ .\Release\ true NDEBUG;%(PreprocessorDefinitions) .\Release\psftp.tlb true Win32 0x0809 NDEBUG;%(PreprocessorDefinitions) true .\Release\psftp.bsc true Console .\Release\psftp.exe advapi32.lib;comdlg32.lib;gdi32.lib;imm32.lib;ole32.lib;shell32.lib;user32.lib;%(AdditionalDependencies) MultiThreadedDebug Default false Disabled true Level3 true ProgramDatabase ..\..\..\./;..\..\..\charset/;..\..\..\windows/;..\..\..\unix/;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_WINDOWS;POSIX;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) .\Debug\ .\Debug\psftp.pch .\Debug\ .\Debug\ EnableFastChecks true _DEBUG;%(PreprocessorDefinitions) .\Debug\psftp.tlb true Win32 0x0809 _DEBUG;%(PreprocessorDefinitions) true .\Debug\psftp.bsc true true Console $(TargetPath) advapi32.lib;comdlg32.lib;gdi32.lib;imm32.lib;ole32.lib;shell32.lib;user32.lib;%(AdditionalDependencies) ..\..;%(AdditionalIncludeDirectories) ..\..;%(AdditionalIncludeDirectories) putty-0.76/windows/VS2012/psftp/psftp.vcxproj.filters0000644000175000017500000003612214072266315017471 00000000000000 Resource Files Resource Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files {156ce40f-dc1b-4721-b08f-6336b9f4d290} {198936a4-3dd8-46c7-8266-b3a081e1e0fc} {a85261db-1733-4f46-b1b9-cf01dd3da697} putty-0.76/windows/VS2012/psocks/0000755000175000017500000000000014072266316013474 500000000000000putty-0.76/windows/VS2012/psocks/psocks.vcxproj0000644000175000017500000002262014072266315016334 00000000000000 Debug Win32 Release Win32 {0ce7217e-7277-408e-add3-4e2f6072486e} Application false MultiByte v110 Application false MultiByte v110 .\Release\ .\Release\ false .\Debug\ .\Debug\ true MultiThreaded OnlyExplicitInline true true MaxSpeed true Level3 ..\..\..\./;..\..\..\charset/;..\..\..\windows/;..\..\..\unix/;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_WINDOWS;POSIX;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) .\Release\ .\Release\psocks.pch .\Release\ .\Release\ true NDEBUG;%(PreprocessorDefinitions) .\Release\psocks.tlb true Win32 0x0809 NDEBUG;%(PreprocessorDefinitions) true .\Release\psocks.bsc true Console .\Release\psocks.exe advapi32.lib;comdlg32.lib;gdi32.lib;imm32.lib;ole32.lib;shell32.lib;user32.lib;%(AdditionalDependencies) MultiThreadedDebug Default false Disabled true Level3 true ProgramDatabase ..\..\..\./;..\..\..\charset/;..\..\..\windows/;..\..\..\unix/;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_WINDOWS;POSIX;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) .\Debug\ .\Debug\psocks.pch .\Debug\ .\Debug\ EnableFastChecks true _DEBUG;%(PreprocessorDefinitions) .\Debug\psocks.tlb true Win32 0x0809 _DEBUG;%(PreprocessorDefinitions) true .\Debug\psocks.bsc true true Console $(TargetPath) advapi32.lib;comdlg32.lib;gdi32.lib;imm32.lib;ole32.lib;shell32.lib;user32.lib;%(AdditionalDependencies) putty-0.76/windows/VS2012/psocks/psocks.vcxproj.filters0000644000175000017500000001367314072266315020013 00000000000000 Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files {cbddcf51-a122-4b3b-9666-3c7319e4236d} {f6732610-4305-4bfe-9c1b-f63396c38c4d} {e639ecc0-211b-4aaa-9fca-83ad6cda2ba7} putty-0.76/windows/VS2012/putty/0000755000175000017500000000000014072266316013357 500000000000000putty-0.76/windows/VS2012/putty/putty.vcxproj0000644000175000017500000003530114072266315016102 00000000000000 Debug Win32 Release Win32 {8f44624a-d188-4188-b9aa-5f99f20723e1} Application false MultiByte v110 Application false MultiByte v110 .\Release\ .\Release\ false .\Debug\ .\Debug\ true MultiThreaded OnlyExplicitInline true true MaxSpeed true Level3 ..\..\..\./;..\..\..\charset/;..\..\..\windows/;..\..\..\unix/;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_WINDOWS;POSIX;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) .\Release\ .\Release\putty.pch .\Release\ .\Release\ true NDEBUG;%(PreprocessorDefinitions) .\Release\putty.tlb true Win32 0x0809 NDEBUG;%(PreprocessorDefinitions) true .\Release\putty.bsc true Windows .\Release\putty.exe advapi32.lib;comdlg32.lib;gdi32.lib;imm32.lib;ole32.lib;shell32.lib;user32.lib;%(AdditionalDependencies) MultiThreadedDebug Default false Disabled true Level3 true ProgramDatabase ..\..\..\./;..\..\..\charset/;..\..\..\windows/;..\..\..\unix/;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_WINDOWS;POSIX;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) .\Debug\ .\Debug\putty.pch .\Debug\ .\Debug\ EnableFastChecks true _DEBUG;%(PreprocessorDefinitions) .\Debug\putty.tlb true Win32 0x0809 _DEBUG;%(PreprocessorDefinitions) true .\Debug\putty.bsc true true Windows $(TargetPath) advapi32.lib;comdlg32.lib;gdi32.lib;imm32.lib;ole32.lib;shell32.lib;user32.lib;%(AdditionalDependencies) ..\..;%(AdditionalIncludeDirectories) ..\..;%(AdditionalIncludeDirectories) putty-0.76/windows/VS2012/putty/putty.vcxproj.filters0000644000175000017500000004030114072266315017545 00000000000000 Resource Files Resource Files Resource Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files {382c3c77-0fea-4132-a5ae-2550d8a5f395} {8c940eae-8579-4a10-b1a1-bd19ff6e8c9c} {e34b5d5d-ec5e-422e-9244-e7dd2783eead} putty-0.76/windows/VS2012/puttygen/0000755000175000017500000000000014072266316014051 500000000000000putty-0.76/windows/VS2012/puttygen/puttygen.vcxproj0000644000175000017500000002614214072266315017271 00000000000000 Debug Win32 Release Win32 {617b1bef-aa10-42b4-8f69-4c183bc1ec0f} Application false MultiByte v110 Application false MultiByte v110 .\Release\ .\Release\ false .\Debug\ .\Debug\ true MultiThreaded OnlyExplicitInline true true MaxSpeed true Level3 ..\..\..\./;..\..\..\charset/;..\..\..\windows/;..\..\..\unix/;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_WINDOWS;POSIX;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) .\Release\ .\Release\puttygen.pch .\Release\ .\Release\ true NDEBUG;%(PreprocessorDefinitions) .\Release\puttygen.tlb true Win32 0x0809 NDEBUG;%(PreprocessorDefinitions) true .\Release\puttygen.bsc true Windows .\Release\puttygen.exe advapi32.lib;comdlg32.lib;gdi32.lib;imm32.lib;ole32.lib;shell32.lib;user32.lib;%(AdditionalDependencies) MultiThreadedDebug Default false Disabled true Level3 true ProgramDatabase ..\..\..\./;..\..\..\charset/;..\..\..\windows/;..\..\..\unix/;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_WINDOWS;POSIX;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) .\Debug\ .\Debug\puttygen.pch .\Debug\ .\Debug\ EnableFastChecks true _DEBUG;%(PreprocessorDefinitions) .\Debug\puttygen.tlb true Win32 0x0809 _DEBUG;%(PreprocessorDefinitions) true .\Debug\puttygen.bsc true true Windows $(TargetPath) advapi32.lib;comdlg32.lib;gdi32.lib;imm32.lib;ole32.lib;shell32.lib;user32.lib;%(AdditionalDependencies) ..\..;%(AdditionalIncludeDirectories) ..\..;%(AdditionalIncludeDirectories) putty-0.76/windows/VS2012/puttygen/puttygen.vcxproj.filters0000644000175000017500000002133214072266315020734 00000000000000 Resource Files Resource Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files {c7e8416f-274c-464e-9d2e-049a34f7e1ef} {ea7fa817-a012-4ecd-8900-9d9896b44c96} {1f675268-5bec-451b-b9c9-30ca628ac7d7} putty-0.76/windows/VS2012/puttytel/0000755000175000017500000000000014072266316014064 500000000000000putty-0.76/windows/VS2012/puttytel/puttytel.vcxproj0000644000175000017500000002624714072266315017325 00000000000000 Debug Win32 Release Win32 {b7dd98ff-b336-4d23-9c07-e5fa7a9f3130} Application false MultiByte v110 Application false MultiByte v110 .\Release\ .\Release\ false .\Debug\ .\Debug\ true MultiThreaded OnlyExplicitInline true true MaxSpeed true Level3 ..\..\..\./;..\..\..\charset/;..\..\..\windows/;..\..\..\unix/;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_WINDOWS;POSIX;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) .\Release\ .\Release\puttytel.pch .\Release\ .\Release\ true NDEBUG;%(PreprocessorDefinitions) .\Release\puttytel.tlb true Win32 0x0809 NDEBUG;%(PreprocessorDefinitions) true .\Release\puttytel.bsc true Windows .\Release\puttytel.exe advapi32.lib;comdlg32.lib;gdi32.lib;imm32.lib;ole32.lib;shell32.lib;user32.lib;%(AdditionalDependencies) MultiThreadedDebug Default false Disabled true Level3 true ProgramDatabase ..\..\..\./;..\..\..\charset/;..\..\..\windows/;..\..\..\unix/;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_WINDOWS;POSIX;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) .\Debug\ .\Debug\puttytel.pch .\Debug\ .\Debug\ EnableFastChecks true _DEBUG;%(PreprocessorDefinitions) .\Debug\puttytel.tlb true Win32 0x0809 _DEBUG;%(PreprocessorDefinitions) true .\Debug\puttytel.bsc true true Windows $(TargetPath) advapi32.lib;comdlg32.lib;gdi32.lib;imm32.lib;ole32.lib;shell32.lib;user32.lib;%(AdditionalDependencies) ..\..;%(AdditionalIncludeDirectories) ..\..;%(AdditionalIncludeDirectories) putty-0.76/windows/VS2012/puttytel/puttytel.vcxproj.filters0000644000175000017500000002167214072266315020771 00000000000000 Resource Files Resource Files Resource Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files {f8a8578e-e0a7-4047-85b7-4063a874d8f1} {8e1311d1-7c8d-4ba8-bf25-a7d452a53090} {02db3bf1-02b5-4667-b7f8-455e6493b006} putty-0.76/windows/VS2012/testcrypt/0000755000175000017500000000000014072266316014233 500000000000000putty-0.76/windows/VS2012/testcrypt/testcrypt.vcxproj0000644000175000017500000002304514072266315017634 00000000000000 Debug Win32 Release Win32 {232ff6d5-a626-4562-977c-2ab72baa29fd} Application false MultiByte v110 Application false MultiByte v110 .\Release\ .\Release\ false .\Debug\ .\Debug\ true MultiThreaded OnlyExplicitInline true true MaxSpeed true Level3 ..\..\..\./;..\..\..\charset/;..\..\..\windows/;..\..\..\unix/;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_WINDOWS;POSIX;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) .\Release\ .\Release\testcrypt.pch .\Release\ .\Release\ true NDEBUG;%(PreprocessorDefinitions) .\Release\testcrypt.tlb true Win32 0x0809 NDEBUG;%(PreprocessorDefinitions) true .\Release\testcrypt.bsc true Console .\Release\testcrypt.exe ;%(AdditionalDependencies) MultiThreadedDebug Default false Disabled true Level3 true ProgramDatabase ..\..\..\./;..\..\..\charset/;..\..\..\windows/;..\..\..\unix/;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_WINDOWS;POSIX;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) .\Debug\ .\Debug\testcrypt.pch .\Debug\ .\Debug\ EnableFastChecks true _DEBUG;%(PreprocessorDefinitions) .\Debug\testcrypt.tlb true Win32 0x0809 _DEBUG;%(PreprocessorDefinitions) true .\Debug\testcrypt.bsc true true Console $(TargetPath) ;%(AdditionalDependencies) putty-0.76/windows/VS2012/testcrypt/testcrypt.vcxproj.filters0000644000175000017500000001472114072266315021304 00000000000000 Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files {da1388df-79d3-4aa3-bb29-c40cd08b764d} {b1161d2a-d615-4e3c-b4a4-790cdcd86977} {d873d947-5884-4fb8-a70a-6a227fee257d} putty-0.76/windows/DEVCPP/0000755000175000017500000000000014072266315012315 500000000000000putty-0.76/windows/DEVCPP/pageant/0000755000175000017500000000000014072266315013734 500000000000000putty-0.76/windows/DEVCPP/pageant/pageant.dev0000644000175000017500000003010614072266315015773 00000000000000# DEV-C++ 5 Project File - pageant.dev # ** DO NOT EDIT ** [Project] FileName=pageant.dev Name=pageant Ver=1 IsCpp=1 Type=0 Compiler=-W -D__GNUWIN32__ -DWIN32 -DNDEBUG -D_WINDOWS -DNO_MULTIMON -D_MBCS_@@_ CppCompiler=-W -D__GNUWIN32__ -DWIN32 -DNDEBUG -D_WINDOWS -DNO_MULTIMON -D_MBCS_@@_ Includes=..\..\..\.;..\..\..\charset;..\..\..\windows;..\..\..\unix Linker=-ladvapi32 -lcomctl32 -lcomdlg32 -lgdi32 -limm32 -lshell32 -luser32 -lwinmm -lwinspool_@@_ Libs= UnitCount=78 Folders="Header Files","Resource Files","Source Files" ObjFiles= PrivateResource=pageant_private.rc ResourceIncludes=..\..\..\WINDOWS MakeIncludes= Icon= ExeOutput= ObjectOutput= OverrideOutput=0 OverrideOutputName=pageant.exe HostApplication= CommandLine= UseCustomMakefile=0 CustomMakefile= IncludeVersionInfo=0 SupportXPThemes=0 CompilerSet=0 CompilerSettings=0000000000000000000000 [Unit1] FileName=..\..\..\aqsync.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit2] FileName=..\..\..\be_misc.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit3] FileName=..\..\..\callback.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit4] FileName=..\..\..\conf.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit5] FileName=..\..\..\ecc.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit6] FileName=..\..\..\errsock.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit7] FileName=..\..\..\marshal.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit8] FileName=..\..\..\memory.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit9] FileName=..\..\..\misc.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit10] FileName=..\..\..\mpint.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit11] FileName=..\..\..\pageant.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit12] FileName=..\..\..\sshaes.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit13] FileName=..\..\..\sshargon2.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit14] FileName=..\..\..\sshauxcrypt.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit15] FileName=..\..\..\sshblake2.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit16] FileName=..\..\..\sshdes.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit17] FileName=..\..\..\sshdss.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit18] FileName=..\..\..\sshecc.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit19] FileName=..\..\..\sshhmac.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit20] FileName=..\..\..\sshmd5.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit21] FileName=..\..\..\sshpubk.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit22] FileName=..\..\..\sshrsa.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit23] FileName=..\..\..\sshsh256.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit24] FileName=..\..\..\sshsh512.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit25] FileName=..\..\..\sshsha.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit26] FileName=..\..\..\sshsha3.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit27] FileName=..\..\..\stripctrl.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit28] FileName=..\..\..\tree234.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit29] FileName=..\..\..\utils.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit30] FileName=..\..\..\version.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit31] FileName=..\..\..\wcwidth.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit32] FileName=..\..\..\windows\wincapi.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit33] FileName=..\..\..\windows\winhandl.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit34] FileName=..\..\..\windows\winhelp.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit35] FileName=..\..\..\windows\winhsock.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit36] FileName=..\..\..\windows\winmisc.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit37] FileName=..\..\..\windows\winmiscs.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit38] FileName=..\..\..\windows\winnet.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit39] FileName=..\..\..\windows\winnpc.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit40] FileName=..\..\..\windows\winnps.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit41] FileName=..\..\..\windows\winpgnt.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit42] FileName=..\..\..\windows\winpgntc.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit43] FileName=..\..\..\windows\winsecur.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit44] FileName=..\..\..\windows\winselgui.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit45] FileName=..\..\..\windows\winutils.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit46] FileName=..\..\..\charset\charset.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit47] FileName=..\..\..\defs.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit48] FileName=..\..\..\ecc.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit49] FileName=..\..\..\empty.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit50] FileName=..\..\..\licence.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit51] FileName=..\..\..\marshal.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit52] FileName=..\..\..\misc.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit53] FileName=..\..\..\mpint.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit54] FileName=..\..\..\mpint_i.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit55] FileName=..\..\..\network.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit56] FileName=..\..\..\pageant.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit57] FileName=..\..\..\proxy.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit58] FileName=..\..\..\putty.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit59] FileName=..\..\..\puttymem.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit60] FileName=..\..\..\puttyps.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit61] FileName=..\..\..\ssh.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit62] FileName=..\..\..\sshcr.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit63] FileName=..\..\..\sshsignals.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit64] FileName=..\..\..\sshttymodes.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit65] FileName=..\..\..\terminal.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit66] FileName=..\..\..\tree234.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit67] FileName=..\..\..\unix\unix.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit68] FileName=..\..\..\version.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit69] FileName=..\..\..\windows\pageant-rc.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit70] FileName=..\..\..\windows\rcstuff.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit71] FileName=..\..\..\windows\win_res.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit72] FileName=..\..\..\windows\wincapi.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit73] FileName=..\..\..\windows\winhelp.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit74] FileName=..\..\..\windows\winsecur.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit75] FileName=..\..\..\windows\winstuff.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit76] FileName=..\..\..\windows\pageant.ico Folder=Resource Files Compile=0 CompileCpp=0 Link=0 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit77] FileName=..\..\..\windows\pageant.rc Folder=Resource Files Compile=1 CompileCpp=1 Link=0 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit78] FileName=..\..\..\windows\pageants.ico Folder=Resource Files Compile=0 CompileCpp=0 Link=0 Priority=1000 OverrideBuildCmd=0 BuildCmd= [VersionInfo] Major=0 Minor=0 Release=1 Build=1 LanguageID=1033 CharsetID=1252 CompanyName= FileVersion=0.1 FileDescription= InternalName= LegalCopyright= LegalTrademarks= OriginalFilename=pageant.exe ProductName=pageant ProductVersion=0.1 AutoIncBuildNr=0 putty-0.76/windows/DEVCPP/plink/0000755000175000017500000000000014072266315013432 500000000000000putty-0.76/windows/DEVCPP/plink/plink.dev0000644000175000017500000005577714072266315015214 00000000000000# DEV-C++ 5 Project File - plink.dev # ** DO NOT EDIT ** [Project] FileName=plink.dev Name=plink Ver=1 IsCpp=1 Type=1 Compiler=-W -D__GNUWIN32__ -DWIN32 -DNDEBUG -D_WINDOWS -DNO_MULTIMON -D_MBCS_@@_ CppCompiler=-W -D__GNUWIN32__ -DWIN32 -DNDEBUG -D_WINDOWS -DNO_MULTIMON -D_MBCS_@@_ Includes=..\..\..\.;..\..\..\charset;..\..\..\windows;..\..\..\unix Linker=-ladvapi32 -lcomctl32 -lcomdlg32 -lgdi32 -limm32 -lshell32 -luser32 -lwinmm -lwinspool_@@_ Libs= UnitCount=156 Folders="Header Files","Resource Files","Source Files" ObjFiles= PrivateResource=plink_private.rc ResourceIncludes=..\..\..\WINDOWS MakeIncludes= Icon= ExeOutput= ObjectOutput= OverrideOutput=0 OverrideOutputName=plink.exe HostApplication= CommandLine= UseCustomMakefile=0 CustomMakefile= IncludeVersionInfo=0 SupportXPThemes=0 CompilerSet=0 CompilerSettings=0000000000000000000000 [Unit1] FileName=..\..\..\agentf.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit2] FileName=..\..\..\aqsync.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit3] FileName=..\..\..\be_all_s.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit4] FileName=..\..\..\be_misc.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit5] FileName=..\..\..\callback.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit6] FileName=..\..\..\clicons.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit7] FileName=..\..\..\cmdline.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit8] FileName=..\..\..\conf.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit9] FileName=..\..\..\console.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit10] FileName=..\..\..\cproxy.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit11] FileName=..\..\..\ecc.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit12] FileName=..\..\..\errsock.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit13] FileName=..\..\..\ldisc.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit14] FileName=..\..\..\logging.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit15] FileName=..\..\..\mainchan.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit16] FileName=..\..\..\marshal.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit17] FileName=..\..\..\memory.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit18] FileName=..\..\..\misc.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit19] FileName=..\..\..\miscucs.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit20] FileName=..\..\..\mpint.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit21] FileName=..\..\..\noshare.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit22] FileName=..\..\..\noterm.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit23] FileName=..\..\..\nullplug.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit24] FileName=..\..\..\pgssapi.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit25] FileName=..\..\..\pinger.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit26] FileName=..\..\..\portfwd.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit27] FileName=..\..\..\proxy.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit28] FileName=..\..\..\raw.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit29] FileName=..\..\..\rlogin.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit30] FileName=..\..\..\sessprep.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit31] FileName=..\..\..\settings.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit32] FileName=..\..\..\ssh.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit33] FileName=..\..\..\ssh1bpp.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit34] FileName=..\..\..\ssh1censor.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit35] FileName=..\..\..\ssh1connection-client.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit36] FileName=..\..\..\ssh1connection.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit37] FileName=..\..\..\ssh1login.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit38] FileName=..\..\..\ssh2bpp-bare.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit39] FileName=..\..\..\ssh2bpp.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit40] FileName=..\..\..\ssh2censor.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit41] FileName=..\..\..\ssh2connection-client.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit42] FileName=..\..\..\ssh2connection.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit43] FileName=..\..\..\ssh2kex-client.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit44] FileName=..\..\..\ssh2transhk.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit45] FileName=..\..\..\ssh2transport.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit46] FileName=..\..\..\ssh2userauth.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit47] FileName=..\..\..\sshaes.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit48] FileName=..\..\..\ssharcf.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit49] FileName=..\..\..\sshargon2.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit50] FileName=..\..\..\sshauxcrypt.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit51] FileName=..\..\..\sshblake2.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit52] FileName=..\..\..\sshblowf.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit53] FileName=..\..\..\sshccp.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit54] FileName=..\..\..\sshcommon.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit55] FileName=..\..\..\sshcrc.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit56] FileName=..\..\..\sshcrcda.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit57] FileName=..\..\..\sshdes.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit58] FileName=..\..\..\sshdh.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit59] FileName=..\..\..\sshdss.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit60] FileName=..\..\..\sshecc.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit61] FileName=..\..\..\sshgssc.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit62] FileName=..\..\..\sshhmac.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit63] FileName=..\..\..\sshmac.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit64] FileName=..\..\..\sshmd5.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit65] FileName=..\..\..\sshprng.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit66] FileName=..\..\..\sshpubk.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit67] FileName=..\..\..\sshrand.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit68] FileName=..\..\..\sshrsa.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit69] FileName=..\..\..\sshsh256.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit70] FileName=..\..\..\sshsh512.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit71] FileName=..\..\..\sshsha.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit72] FileName=..\..\..\sshsha3.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit73] FileName=..\..\..\sshshare.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit74] FileName=..\..\..\sshutils.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit75] FileName=..\..\..\sshverstring.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit76] FileName=..\..\..\sshzlib.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit77] FileName=..\..\..\stripctrl.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit78] FileName=..\..\..\supdup.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit79] FileName=..\..\..\telnet.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit80] FileName=..\..\..\timing.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit81] FileName=..\..\..\tree234.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit82] FileName=..\..\..\utils.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit83] FileName=..\..\..\version.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit84] FileName=..\..\..\wcwidth.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit85] FileName=..\..\..\wildcard.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit86] FileName=..\..\..\windows\wincapi.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit87] FileName=..\..\..\windows\wincliloop.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit88] FileName=..\..\..\windows\wincons.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit89] FileName=..\..\..\windows\windefs.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit90] FileName=..\..\..\windows\wingss.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit91] FileName=..\..\..\windows\winhandl.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit92] FileName=..\..\..\windows\winhsock.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit93] FileName=..\..\..\windows\winmisc.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit94] FileName=..\..\..\windows\winmiscs.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit95] FileName=..\..\..\windows\winnet.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit96] FileName=..\..\..\windows\winnohlp.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit97] FileName=..\..\..\windows\winnoise.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit98] FileName=..\..\..\windows\winnojmp.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit99] FileName=..\..\..\windows\winnpc.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit100] FileName=..\..\..\windows\winnps.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit101] FileName=..\..\..\windows\winpgntc.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit102] FileName=..\..\..\windows\winplink.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit103] FileName=..\..\..\windows\winproxy.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit104] FileName=..\..\..\windows\winsecur.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit105] FileName=..\..\..\windows\winselcli.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit106] FileName=..\..\..\windows\winser.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit107] FileName=..\..\..\windows\winshare.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit108] FileName=..\..\..\windows\winstore.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit109] FileName=..\..\..\windows\wintime.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit110] FileName=..\..\..\windows\winucs.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit111] FileName=..\..\..\windows\winx11.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit112] FileName=..\..\..\x11fwd.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit113] FileName=..\..\..\charset\charset.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit114] FileName=..\..\..\console.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit115] FileName=..\..\..\defs.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit116] FileName=..\..\..\ecc.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit117] FileName=..\..\..\empty.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit118] FileName=..\..\..\ldisc.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit119] FileName=..\..\..\licence.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit120] FileName=..\..\..\marshal.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit121] FileName=..\..\..\misc.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit122] FileName=..\..\..\mpint.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit123] FileName=..\..\..\mpint_i.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit124] FileName=..\..\..\network.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit125] FileName=..\..\..\pageant.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit126] FileName=..\..\..\pgssapi.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit127] FileName=..\..\..\proxy.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit128] FileName=..\..\..\putty.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit129] FileName=..\..\..\puttymem.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit130] FileName=..\..\..\puttyps.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit131] FileName=..\..\..\ssh.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit132] FileName=..\..\..\ssh1connection.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit133] FileName=..\..\..\ssh2connection.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit134] FileName=..\..\..\ssh2transport.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit135] FileName=..\..\..\sshblowf.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit136] FileName=..\..\..\sshbpp.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit137] FileName=..\..\..\sshchan.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit138] FileName=..\..\..\sshcr.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit139] FileName=..\..\..\sshgss.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit140] FileName=..\..\..\sshgssc.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit141] FileName=..\..\..\sshppl.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit142] FileName=..\..\..\sshserver.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit143] FileName=..\..\..\sshsignals.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit144] FileName=..\..\..\sshttymodes.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit145] FileName=..\..\..\storage.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit146] FileName=..\..\..\terminal.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit147] FileName=..\..\..\tree234.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit148] FileName=..\..\..\unix\unix.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit149] FileName=..\..\..\version.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit150] FileName=..\..\..\windows\rcstuff.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit151] FileName=..\..\..\windows\wincapi.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit152] FileName=..\..\..\windows\winhelp.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit153] FileName=..\..\..\windows\winsecur.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit154] FileName=..\..\..\windows\winstuff.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit155] FileName=..\..\..\windows\plink.rc Folder=Resource Files Compile=1 CompileCpp=1 Link=0 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit156] FileName=..\..\..\windows\putty.ico Folder=Resource Files Compile=0 CompileCpp=0 Link=0 Priority=1000 OverrideBuildCmd=0 BuildCmd= [VersionInfo] Major=0 Minor=0 Release=1 Build=1 LanguageID=1033 CharsetID=1252 CompanyName= FileVersion=0.1 FileDescription= InternalName= LegalCopyright= LegalTrademarks= OriginalFilename=plink.exe ProductName=plink ProductVersion=0.1 AutoIncBuildNr=0 putty-0.76/windows/DEVCPP/pscp/0000755000175000017500000000000014072266315013262 500000000000000putty-0.76/windows/DEVCPP/pscp/pscp.dev0000644000175000017500000005466414072266315014666 00000000000000# DEV-C++ 5 Project File - pscp.dev # ** DO NOT EDIT ** [Project] FileName=pscp.dev Name=pscp Ver=1 IsCpp=1 Type=1 Compiler=-W -D__GNUWIN32__ -DWIN32 -DNDEBUG -D_WINDOWS -DNO_MULTIMON -D_MBCS_@@_ CppCompiler=-W -D__GNUWIN32__ -DWIN32 -DNDEBUG -D_WINDOWS -DNO_MULTIMON -D_MBCS_@@_ Includes=..\..\..\.;..\..\..\charset;..\..\..\windows;..\..\..\unix Linker=-ladvapi32 -lcomctl32 -lcomdlg32 -lgdi32 -limm32 -lshell32 -luser32 -lwinmm -lwinspool_@@_ Libs= UnitCount=152 Folders="Header Files","Resource Files","Source Files" ObjFiles= PrivateResource=pscp_private.rc ResourceIncludes=..\..\..\WINDOWS MakeIncludes= Icon= ExeOutput= ObjectOutput= OverrideOutput=0 OverrideOutputName=pscp.exe HostApplication= CommandLine= UseCustomMakefile=0 CustomMakefile= IncludeVersionInfo=0 SupportXPThemes=0 CompilerSet=0 CompilerSettings=0000000000000000000000 [Unit1] FileName=..\..\..\agentf.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit2] FileName=..\..\..\aqsync.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit3] FileName=..\..\..\be_misc.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit4] FileName=..\..\..\be_ssh.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit5] FileName=..\..\..\callback.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit6] FileName=..\..\..\clicons.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit7] FileName=..\..\..\cmdline.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit8] FileName=..\..\..\conf.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit9] FileName=..\..\..\console.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit10] FileName=..\..\..\cproxy.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit11] FileName=..\..\..\ecc.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit12] FileName=..\..\..\errsock.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit13] FileName=..\..\..\logging.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit14] FileName=..\..\..\mainchan.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit15] FileName=..\..\..\marshal.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit16] FileName=..\..\..\memory.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit17] FileName=..\..\..\misc.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit18] FileName=..\..\..\miscucs.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit19] FileName=..\..\..\mpint.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit20] FileName=..\..\..\noshare.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit21] FileName=..\..\..\nullplug.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit22] FileName=..\..\..\pgssapi.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit23] FileName=..\..\..\pinger.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit24] FileName=..\..\..\portfwd.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit25] FileName=..\..\..\proxy.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit26] FileName=..\..\..\pscp.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit27] FileName=..\..\..\psftpcommon.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit28] FileName=..\..\..\settings.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit29] FileName=..\..\..\sftp.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit30] FileName=..\..\..\sftpcommon.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit31] FileName=..\..\..\ssh.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit32] FileName=..\..\..\ssh1bpp.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit33] FileName=..\..\..\ssh1censor.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit34] FileName=..\..\..\ssh1connection-client.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit35] FileName=..\..\..\ssh1connection.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit36] FileName=..\..\..\ssh1login.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit37] FileName=..\..\..\ssh2bpp-bare.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit38] FileName=..\..\..\ssh2bpp.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit39] FileName=..\..\..\ssh2censor.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit40] FileName=..\..\..\ssh2connection-client.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit41] FileName=..\..\..\ssh2connection.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit42] FileName=..\..\..\ssh2kex-client.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit43] FileName=..\..\..\ssh2transhk.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit44] FileName=..\..\..\ssh2transport.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit45] FileName=..\..\..\ssh2userauth.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit46] FileName=..\..\..\sshaes.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit47] FileName=..\..\..\ssharcf.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit48] FileName=..\..\..\sshargon2.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit49] FileName=..\..\..\sshauxcrypt.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit50] FileName=..\..\..\sshblake2.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit51] FileName=..\..\..\sshblowf.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit52] FileName=..\..\..\sshccp.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit53] FileName=..\..\..\sshcommon.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit54] FileName=..\..\..\sshcrc.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit55] FileName=..\..\..\sshcrcda.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit56] FileName=..\..\..\sshdes.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit57] FileName=..\..\..\sshdh.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit58] FileName=..\..\..\sshdss.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit59] FileName=..\..\..\sshecc.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit60] FileName=..\..\..\sshgssc.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit61] FileName=..\..\..\sshhmac.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit62] FileName=..\..\..\sshmac.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit63] FileName=..\..\..\sshmd5.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit64] FileName=..\..\..\sshprng.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit65] FileName=..\..\..\sshpubk.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit66] FileName=..\..\..\sshrand.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit67] FileName=..\..\..\sshrsa.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit68] FileName=..\..\..\sshsh256.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit69] FileName=..\..\..\sshsh512.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit70] FileName=..\..\..\sshsha.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit71] FileName=..\..\..\sshsha3.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit72] FileName=..\..\..\sshshare.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit73] FileName=..\..\..\sshutils.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit74] FileName=..\..\..\sshverstring.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit75] FileName=..\..\..\sshzlib.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit76] FileName=..\..\..\stripctrl.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit77] FileName=..\..\..\timing.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit78] FileName=..\..\..\tree234.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit79] FileName=..\..\..\utils.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit80] FileName=..\..\..\version.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit81] FileName=..\..\..\wcwidth.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit82] FileName=..\..\..\wildcard.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit83] FileName=..\..\..\windows\wincapi.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit84] FileName=..\..\..\windows\wincliloop.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit85] FileName=..\..\..\windows\wincons.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit86] FileName=..\..\..\windows\windefs.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit87] FileName=..\..\..\windows\wingss.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit88] FileName=..\..\..\windows\winhandl.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit89] FileName=..\..\..\windows\winhsock.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit90] FileName=..\..\..\windows\winmisc.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit91] FileName=..\..\..\windows\winmiscs.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit92] FileName=..\..\..\windows\winnet.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit93] FileName=..\..\..\windows\winnohlp.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit94] FileName=..\..\..\windows\winnoise.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit95] FileName=..\..\..\windows\winnojmp.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit96] FileName=..\..\..\windows\winnpc.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit97] FileName=..\..\..\windows\winnps.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit98] FileName=..\..\..\windows\winpgntc.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit99] FileName=..\..\..\windows\winproxy.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit100] FileName=..\..\..\windows\winsecur.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit101] FileName=..\..\..\windows\winselcli.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit102] FileName=..\..\..\windows\winsftp.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit103] FileName=..\..\..\windows\winshare.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit104] FileName=..\..\..\windows\winstore.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit105] FileName=..\..\..\windows\wintime.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit106] FileName=..\..\..\windows\winucs.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit107] FileName=..\..\..\x11fwd.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit108] FileName=..\..\..\charset\charset.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit109] FileName=..\..\..\console.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit110] FileName=..\..\..\defs.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit111] FileName=..\..\..\ecc.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit112] FileName=..\..\..\empty.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit113] FileName=..\..\..\licence.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit114] FileName=..\..\..\marshal.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit115] FileName=..\..\..\misc.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit116] FileName=..\..\..\mpint.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit117] FileName=..\..\..\mpint_i.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit118] FileName=..\..\..\network.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit119] FileName=..\..\..\pageant.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit120] FileName=..\..\..\pgssapi.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit121] FileName=..\..\..\proxy.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit122] FileName=..\..\..\psftp.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit123] FileName=..\..\..\putty.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit124] FileName=..\..\..\puttymem.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit125] FileName=..\..\..\puttyps.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit126] FileName=..\..\..\sftp.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit127] FileName=..\..\..\ssh.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit128] FileName=..\..\..\ssh1connection.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit129] FileName=..\..\..\ssh2connection.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit130] FileName=..\..\..\ssh2transport.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit131] FileName=..\..\..\sshblowf.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit132] FileName=..\..\..\sshbpp.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit133] FileName=..\..\..\sshchan.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit134] FileName=..\..\..\sshcr.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit135] FileName=..\..\..\sshgss.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit136] FileName=..\..\..\sshgssc.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit137] FileName=..\..\..\sshppl.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit138] FileName=..\..\..\sshserver.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit139] FileName=..\..\..\sshsignals.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit140] FileName=..\..\..\sshttymodes.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit141] FileName=..\..\..\storage.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit142] FileName=..\..\..\terminal.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit143] FileName=..\..\..\tree234.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit144] FileName=..\..\..\unix\unix.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit145] FileName=..\..\..\version.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit146] FileName=..\..\..\windows\rcstuff.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit147] FileName=..\..\..\windows\wincapi.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit148] FileName=..\..\..\windows\winhelp.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit149] FileName=..\..\..\windows\winsecur.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit150] FileName=..\..\..\windows\winstuff.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit151] FileName=..\..\..\windows\pscp.ico Folder=Resource Files Compile=0 CompileCpp=0 Link=0 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit152] FileName=..\..\..\windows\pscp.rc Folder=Resource Files Compile=1 CompileCpp=1 Link=0 Priority=1000 OverrideBuildCmd=0 BuildCmd= [VersionInfo] Major=0 Minor=0 Release=1 Build=1 LanguageID=1033 CharsetID=1252 CompanyName= FileVersion=0.1 FileDescription= InternalName= LegalCopyright= LegalTrademarks= OriginalFilename=pscp.exe ProductName=pscp ProductVersion=0.1 AutoIncBuildNr=0 putty-0.76/windows/DEVCPP/psftp/0000755000175000017500000000000014072266315013451 500000000000000putty-0.76/windows/DEVCPP/psftp/psftp.dev0000644000175000017500000005467514072266315015246 00000000000000# DEV-C++ 5 Project File - psftp.dev # ** DO NOT EDIT ** [Project] FileName=psftp.dev Name=psftp Ver=1 IsCpp=1 Type=1 Compiler=-W -D__GNUWIN32__ -DWIN32 -DNDEBUG -D_WINDOWS -DNO_MULTIMON -D_MBCS_@@_ CppCompiler=-W -D__GNUWIN32__ -DWIN32 -DNDEBUG -D_WINDOWS -DNO_MULTIMON -D_MBCS_@@_ Includes=..\..\..\.;..\..\..\charset;..\..\..\windows;..\..\..\unix Linker=-ladvapi32 -lcomctl32 -lcomdlg32 -lgdi32 -limm32 -lshell32 -luser32 -lwinmm -lwinspool_@@_ Libs= UnitCount=152 Folders="Header Files","Resource Files","Source Files" ObjFiles= PrivateResource=psftp_private.rc ResourceIncludes=..\..\..\WINDOWS MakeIncludes= Icon= ExeOutput= ObjectOutput= OverrideOutput=0 OverrideOutputName=psftp.exe HostApplication= CommandLine= UseCustomMakefile=0 CustomMakefile= IncludeVersionInfo=0 SupportXPThemes=0 CompilerSet=0 CompilerSettings=0000000000000000000000 [Unit1] FileName=..\..\..\agentf.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit2] FileName=..\..\..\aqsync.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit3] FileName=..\..\..\be_misc.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit4] FileName=..\..\..\be_ssh.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit5] FileName=..\..\..\callback.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit6] FileName=..\..\..\clicons.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit7] FileName=..\..\..\cmdline.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit8] FileName=..\..\..\conf.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit9] FileName=..\..\..\console.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit10] FileName=..\..\..\cproxy.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit11] FileName=..\..\..\ecc.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit12] FileName=..\..\..\errsock.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit13] FileName=..\..\..\logging.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit14] FileName=..\..\..\mainchan.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit15] FileName=..\..\..\marshal.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit16] FileName=..\..\..\memory.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit17] FileName=..\..\..\misc.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit18] FileName=..\..\..\miscucs.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit19] FileName=..\..\..\mpint.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit20] FileName=..\..\..\noshare.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit21] FileName=..\..\..\nullplug.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit22] FileName=..\..\..\pgssapi.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit23] FileName=..\..\..\pinger.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit24] FileName=..\..\..\portfwd.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit25] FileName=..\..\..\proxy.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit26] FileName=..\..\..\psftp.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit27] FileName=..\..\..\psftpcommon.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit28] FileName=..\..\..\settings.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit29] FileName=..\..\..\sftp.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit30] FileName=..\..\..\sftpcommon.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit31] FileName=..\..\..\ssh.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit32] FileName=..\..\..\ssh1bpp.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit33] FileName=..\..\..\ssh1censor.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit34] FileName=..\..\..\ssh1connection-client.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit35] FileName=..\..\..\ssh1connection.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit36] FileName=..\..\..\ssh1login.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit37] FileName=..\..\..\ssh2bpp-bare.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit38] FileName=..\..\..\ssh2bpp.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit39] FileName=..\..\..\ssh2censor.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit40] FileName=..\..\..\ssh2connection-client.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit41] FileName=..\..\..\ssh2connection.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit42] FileName=..\..\..\ssh2kex-client.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit43] FileName=..\..\..\ssh2transhk.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit44] FileName=..\..\..\ssh2transport.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit45] FileName=..\..\..\ssh2userauth.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit46] FileName=..\..\..\sshaes.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit47] FileName=..\..\..\ssharcf.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit48] FileName=..\..\..\sshargon2.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit49] FileName=..\..\..\sshauxcrypt.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit50] FileName=..\..\..\sshblake2.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit51] FileName=..\..\..\sshblowf.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit52] FileName=..\..\..\sshccp.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit53] FileName=..\..\..\sshcommon.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit54] FileName=..\..\..\sshcrc.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit55] FileName=..\..\..\sshcrcda.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit56] FileName=..\..\..\sshdes.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit57] FileName=..\..\..\sshdh.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit58] FileName=..\..\..\sshdss.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit59] FileName=..\..\..\sshecc.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit60] FileName=..\..\..\sshgssc.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit61] FileName=..\..\..\sshhmac.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit62] FileName=..\..\..\sshmac.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit63] FileName=..\..\..\sshmd5.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit64] FileName=..\..\..\sshprng.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit65] FileName=..\..\..\sshpubk.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit66] FileName=..\..\..\sshrand.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit67] FileName=..\..\..\sshrsa.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit68] FileName=..\..\..\sshsh256.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit69] FileName=..\..\..\sshsh512.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit70] FileName=..\..\..\sshsha.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit71] FileName=..\..\..\sshsha3.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit72] FileName=..\..\..\sshshare.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit73] FileName=..\..\..\sshutils.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit74] FileName=..\..\..\sshverstring.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit75] FileName=..\..\..\sshzlib.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit76] FileName=..\..\..\stripctrl.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit77] FileName=..\..\..\timing.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit78] FileName=..\..\..\tree234.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit79] FileName=..\..\..\utils.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit80] FileName=..\..\..\version.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit81] FileName=..\..\..\wcwidth.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit82] FileName=..\..\..\wildcard.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit83] FileName=..\..\..\windows\wincapi.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit84] FileName=..\..\..\windows\wincliloop.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit85] FileName=..\..\..\windows\wincons.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit86] FileName=..\..\..\windows\windefs.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit87] FileName=..\..\..\windows\wingss.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit88] FileName=..\..\..\windows\winhandl.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit89] FileName=..\..\..\windows\winhsock.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit90] FileName=..\..\..\windows\winmisc.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit91] FileName=..\..\..\windows\winmiscs.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit92] FileName=..\..\..\windows\winnet.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit93] FileName=..\..\..\windows\winnohlp.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit94] FileName=..\..\..\windows\winnoise.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit95] FileName=..\..\..\windows\winnojmp.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit96] FileName=..\..\..\windows\winnpc.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit97] FileName=..\..\..\windows\winnps.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit98] FileName=..\..\..\windows\winpgntc.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit99] FileName=..\..\..\windows\winproxy.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit100] FileName=..\..\..\windows\winsecur.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit101] FileName=..\..\..\windows\winselcli.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit102] FileName=..\..\..\windows\winsftp.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit103] FileName=..\..\..\windows\winshare.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit104] FileName=..\..\..\windows\winstore.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit105] FileName=..\..\..\windows\wintime.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit106] FileName=..\..\..\windows\winucs.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit107] FileName=..\..\..\x11fwd.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit108] FileName=..\..\..\charset\charset.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit109] FileName=..\..\..\console.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit110] FileName=..\..\..\defs.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit111] FileName=..\..\..\ecc.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit112] FileName=..\..\..\empty.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit113] FileName=..\..\..\licence.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit114] FileName=..\..\..\marshal.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit115] FileName=..\..\..\misc.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit116] FileName=..\..\..\mpint.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit117] FileName=..\..\..\mpint_i.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit118] FileName=..\..\..\network.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit119] FileName=..\..\..\pageant.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit120] FileName=..\..\..\pgssapi.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit121] FileName=..\..\..\proxy.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit122] FileName=..\..\..\psftp.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit123] FileName=..\..\..\putty.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit124] FileName=..\..\..\puttymem.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit125] FileName=..\..\..\puttyps.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit126] FileName=..\..\..\sftp.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit127] FileName=..\..\..\ssh.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit128] FileName=..\..\..\ssh1connection.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit129] FileName=..\..\..\ssh2connection.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit130] FileName=..\..\..\ssh2transport.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit131] FileName=..\..\..\sshblowf.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit132] FileName=..\..\..\sshbpp.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit133] FileName=..\..\..\sshchan.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit134] FileName=..\..\..\sshcr.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit135] FileName=..\..\..\sshgss.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit136] FileName=..\..\..\sshgssc.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit137] FileName=..\..\..\sshppl.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit138] FileName=..\..\..\sshserver.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit139] FileName=..\..\..\sshsignals.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit140] FileName=..\..\..\sshttymodes.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit141] FileName=..\..\..\storage.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit142] FileName=..\..\..\terminal.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit143] FileName=..\..\..\tree234.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit144] FileName=..\..\..\unix\unix.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit145] FileName=..\..\..\version.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit146] FileName=..\..\..\windows\rcstuff.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit147] FileName=..\..\..\windows\wincapi.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit148] FileName=..\..\..\windows\winhelp.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit149] FileName=..\..\..\windows\winsecur.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit150] FileName=..\..\..\windows\winstuff.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit151] FileName=..\..\..\windows\pscp.ico Folder=Resource Files Compile=0 CompileCpp=0 Link=0 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit152] FileName=..\..\..\windows\psftp.rc Folder=Resource Files Compile=1 CompileCpp=1 Link=0 Priority=1000 OverrideBuildCmd=0 BuildCmd= [VersionInfo] Major=0 Minor=0 Release=1 Build=1 LanguageID=1033 CharsetID=1252 CompanyName= FileVersion=0.1 FileDescription= InternalName= LegalCopyright= LegalTrademarks= OriginalFilename=psftp.exe ProductName=psftp ProductVersion=0.1 AutoIncBuildNr=0 putty-0.76/windows/DEVCPP/psocks/0000755000175000017500000000000014072266315013617 500000000000000putty-0.76/windows/DEVCPP/psocks/psocks.dev0000644000175000017500000002170314072266315015544 00000000000000# DEV-C++ 5 Project File - psocks.dev # ** DO NOT EDIT ** [Project] FileName=psocks.dev Name=psocks Ver=1 IsCpp=1 Type=1 Compiler=-W -D__GNUWIN32__ -DWIN32 -DNDEBUG -D_WINDOWS -DNO_MULTIMON -D_MBCS_@@_ CppCompiler=-W -D__GNUWIN32__ -DWIN32 -DNDEBUG -D_WINDOWS -DNO_MULTIMON -D_MBCS_@@_ Includes=..\..\..\.;..\..\..\charset;..\..\..\windows;..\..\..\unix Linker=-ladvapi32 -lcomctl32 -lcomdlg32 -lgdi32 -limm32 -lshell32 -luser32 -lwinmm -lwinspool_@@_ Libs= UnitCount=56 Folders="Header Files","Resource Files","Source Files" ObjFiles= PrivateResource=psocks_private.rc ResourceIncludes=..\..\..\WINDOWS MakeIncludes= Icon= ExeOutput= ObjectOutput= OverrideOutput=0 OverrideOutputName=psocks.exe HostApplication= CommandLine= UseCustomMakefile=0 CustomMakefile= IncludeVersionInfo=0 SupportXPThemes=0 CompilerSet=0 CompilerSettings=0000000000000000000000 [Unit1] FileName=..\..\..\be_misc.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit2] FileName=..\..\..\callback.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit3] FileName=..\..\..\conf.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit4] FileName=..\..\..\console.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit5] FileName=..\..\..\errsock.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit6] FileName=..\..\..\logging.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit7] FileName=..\..\..\marshal.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit8] FileName=..\..\..\memory.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit9] FileName=..\..\..\misc.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit10] FileName=..\..\..\nocproxy.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit11] FileName=..\..\..\norand.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit12] FileName=..\..\..\portfwd.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit13] FileName=..\..\..\proxy.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit14] FileName=..\..\..\psocks.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit15] FileName=..\..\..\sshutils.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit16] FileName=..\..\..\stripctrl.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit17] FileName=..\..\..\time.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit18] FileName=..\..\..\timing.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit19] FileName=..\..\..\tree234.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit20] FileName=..\..\..\utils.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit21] FileName=..\..\..\version.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit22] FileName=..\..\..\wcwidth.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit23] FileName=..\..\..\windows\wincliloop.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit24] FileName=..\..\..\windows\wincons.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit25] FileName=..\..\..\windows\winhandl.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit26] FileName=..\..\..\windows\winhsock.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit27] FileName=..\..\..\windows\winmisc.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit28] FileName=..\..\..\windows\winmiscs.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit29] FileName=..\..\..\windows\winnet.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit30] FileName=..\..\..\windows\winnohlp.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit31] FileName=..\..\..\windows\winproxy.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit32] FileName=..\..\..\windows\winselcli.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit33] FileName=..\..\..\windows\winsocks.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit34] FileName=..\..\..\charset\charset.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit35] FileName=..\..\..\console.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit36] FileName=..\..\..\defs.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit37] FileName=..\..\..\empty.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit38] FileName=..\..\..\marshal.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit39] FileName=..\..\..\misc.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit40] FileName=..\..\..\network.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit41] FileName=..\..\..\proxy.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit42] FileName=..\..\..\psocks.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit43] FileName=..\..\..\putty.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit44] FileName=..\..\..\puttymem.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit45] FileName=..\..\..\puttyps.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit46] FileName=..\..\..\ssh.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit47] FileName=..\..\..\sshchan.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit48] FileName=..\..\..\sshsignals.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit49] FileName=..\..\..\sshttymodes.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit50] FileName=..\..\..\storage.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit51] FileName=..\..\..\terminal.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit52] FileName=..\..\..\tree234.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit53] FileName=..\..\..\unix\unix.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit54] FileName=..\..\..\version.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit55] FileName=..\..\..\windows\winhelp.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit56] FileName=..\..\..\windows\winstuff.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [VersionInfo] Major=0 Minor=0 Release=1 Build=1 LanguageID=1033 CharsetID=1252 CompanyName= FileVersion=0.1 FileDescription= InternalName= LegalCopyright= LegalTrademarks= OriginalFilename=psocks.exe ProductName=psocks ProductVersion=0.1 AutoIncBuildNr=0 putty-0.76/windows/DEVCPP/putty/0000755000175000017500000000000014072266315013502 500000000000000putty-0.76/windows/DEVCPP/putty/putty.dev0000644000175000017500000006024614072266315015317 00000000000000# DEV-C++ 5 Project File - putty.dev # ** DO NOT EDIT ** [Project] FileName=putty.dev Name=putty Ver=1 IsCpp=1 Type=0 Compiler=-W -D__GNUWIN32__ -DWIN32 -DNDEBUG -D_WINDOWS -DNO_MULTIMON -D_MBCS_@@_ CppCompiler=-W -D__GNUWIN32__ -DWIN32 -DNDEBUG -D_WINDOWS -DNO_MULTIMON -D_MBCS_@@_ Includes=..\..\..\.;..\..\..\charset;..\..\..\windows;..\..\..\unix Linker=-ladvapi32 -lcomctl32 -lcomdlg32 -lgdi32 -limm32 -lshell32 -luser32 -lwinmm -lwinspool_@@_ Libs= UnitCount=164 Folders="Header Files","Resource Files","Source Files" ObjFiles= PrivateResource=putty_private.rc ResourceIncludes=..\..\..\WINDOWS MakeIncludes= Icon= ExeOutput= ObjectOutput= OverrideOutput=0 OverrideOutputName=putty.exe HostApplication= CommandLine= UseCustomMakefile=0 CustomMakefile= IncludeVersionInfo=0 SupportXPThemes=0 CompilerSet=0 CompilerSettings=0000000000000000000000 [Unit1] FileName=..\..\..\agentf.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit2] FileName=..\..\..\aqsync.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit3] FileName=..\..\..\be_all_s.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit4] FileName=..\..\..\be_misc.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit5] FileName=..\..\..\callback.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit6] FileName=..\..\..\cmdline.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit7] FileName=..\..\..\conf.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit8] FileName=..\..\..\config.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit9] FileName=..\..\..\cproxy.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit10] FileName=..\..\..\dialog.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit11] FileName=..\..\..\ecc.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit12] FileName=..\..\..\errsock.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit13] FileName=..\..\..\ldisc.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit14] FileName=..\..\..\logging.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit15] FileName=..\..\..\mainchan.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit16] FileName=..\..\..\marshal.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit17] FileName=..\..\..\memory.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit18] FileName=..\..\..\minibidi.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit19] FileName=..\..\..\misc.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit20] FileName=..\..\..\miscucs.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit21] FileName=..\..\..\mpint.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit22] FileName=..\..\..\noshare.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit23] FileName=..\..\..\nullplug.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit24] FileName=..\..\..\pgssapi.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit25] FileName=..\..\..\pinger.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit26] FileName=..\..\..\portfwd.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit27] FileName=..\..\..\proxy.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit28] FileName=..\..\..\raw.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit29] FileName=..\..\..\rlogin.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit30] FileName=..\..\..\sessprep.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit31] FileName=..\..\..\settings.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit32] FileName=..\..\..\ssh.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit33] FileName=..\..\..\ssh1bpp.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit34] FileName=..\..\..\ssh1censor.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit35] FileName=..\..\..\ssh1connection-client.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit36] FileName=..\..\..\ssh1connection.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit37] FileName=..\..\..\ssh1login.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit38] FileName=..\..\..\ssh2bpp-bare.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit39] FileName=..\..\..\ssh2bpp.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit40] FileName=..\..\..\ssh2censor.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit41] FileName=..\..\..\ssh2connection-client.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit42] FileName=..\..\..\ssh2connection.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit43] FileName=..\..\..\ssh2kex-client.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit44] FileName=..\..\..\ssh2transhk.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit45] FileName=..\..\..\ssh2transport.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit46] FileName=..\..\..\ssh2userauth.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit47] FileName=..\..\..\sshaes.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit48] FileName=..\..\..\ssharcf.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit49] FileName=..\..\..\sshargon2.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit50] FileName=..\..\..\sshauxcrypt.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit51] FileName=..\..\..\sshblake2.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit52] FileName=..\..\..\sshblowf.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit53] FileName=..\..\..\sshccp.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit54] FileName=..\..\..\sshcommon.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit55] FileName=..\..\..\sshcrc.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit56] FileName=..\..\..\sshcrcda.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit57] FileName=..\..\..\sshdes.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit58] FileName=..\..\..\sshdh.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit59] FileName=..\..\..\sshdss.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit60] FileName=..\..\..\sshecc.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit61] FileName=..\..\..\sshgssc.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit62] FileName=..\..\..\sshhmac.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit63] FileName=..\..\..\sshmac.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit64] FileName=..\..\..\sshmd5.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit65] FileName=..\..\..\sshprng.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit66] FileName=..\..\..\sshpubk.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit67] FileName=..\..\..\sshrand.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit68] FileName=..\..\..\sshrsa.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit69] FileName=..\..\..\sshsh256.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit70] FileName=..\..\..\sshsh512.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit71] FileName=..\..\..\sshsha.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit72] FileName=..\..\..\sshsha3.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit73] FileName=..\..\..\sshshare.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit74] FileName=..\..\..\sshutils.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit75] FileName=..\..\..\sshverstring.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit76] FileName=..\..\..\sshzlib.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit77] FileName=..\..\..\stripctrl.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit78] FileName=..\..\..\supdup.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit79] FileName=..\..\..\telnet.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit80] FileName=..\..\..\terminal.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit81] FileName=..\..\..\timing.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit82] FileName=..\..\..\tree234.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit83] FileName=..\..\..\utils.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit84] FileName=..\..\..\version.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit85] FileName=..\..\..\wcwidth.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit86] FileName=..\..\..\wildcard.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit87] FileName=..\..\..\windows\sizetip.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit88] FileName=..\..\..\windows\wincapi.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit89] FileName=..\..\..\windows\wincfg.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit90] FileName=..\..\..\windows\winctrls.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit91] FileName=..\..\..\windows\windefs.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit92] FileName=..\..\..\windows\windlg.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit93] FileName=..\..\..\windows\window.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit94] FileName=..\..\..\windows\wingss.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit95] FileName=..\..\..\windows\winhandl.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit96] FileName=..\..\..\windows\winhelp.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit97] FileName=..\..\..\windows\winhsock.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit98] FileName=..\..\..\windows\winjump.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit99] FileName=..\..\..\windows\winmisc.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit100] FileName=..\..\..\windows\winmiscs.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit101] FileName=..\..\..\windows\winnet.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit102] FileName=..\..\..\windows\winnoise.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit103] FileName=..\..\..\windows\winnpc.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit104] FileName=..\..\..\windows\winnps.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit105] FileName=..\..\..\windows\winpgntc.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit106] FileName=..\..\..\windows\winprint.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit107] FileName=..\..\..\windows\winproxy.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit108] FileName=..\..\..\windows\winsecur.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit109] FileName=..\..\..\windows\winselgui.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit110] FileName=..\..\..\windows\winser.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit111] FileName=..\..\..\windows\winshare.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit112] FileName=..\..\..\windows\winstore.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit113] FileName=..\..\..\windows\wintime.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit114] FileName=..\..\..\windows\winucs.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit115] FileName=..\..\..\windows\winutils.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit116] FileName=..\..\..\windows\winx11.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit117] FileName=..\..\..\x11fwd.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit118] FileName=..\..\..\charset\charset.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit119] FileName=..\..\..\defs.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit120] FileName=..\..\..\dialog.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit121] FileName=..\..\..\ecc.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit122] FileName=..\..\..\empty.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit123] FileName=..\..\..\ldisc.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit124] FileName=..\..\..\licence.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit125] FileName=..\..\..\marshal.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit126] FileName=..\..\..\misc.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit127] FileName=..\..\..\mpint.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit128] FileName=..\..\..\mpint_i.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit129] FileName=..\..\..\network.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit130] FileName=..\..\..\pageant.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit131] FileName=..\..\..\pgssapi.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit132] FileName=..\..\..\proxy.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit133] FileName=..\..\..\putty.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit134] FileName=..\..\..\puttymem.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit135] FileName=..\..\..\puttyps.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit136] FileName=..\..\..\ssh.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit137] FileName=..\..\..\ssh1connection.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit138] FileName=..\..\..\ssh2connection.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit139] FileName=..\..\..\ssh2transport.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit140] FileName=..\..\..\sshblowf.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit141] FileName=..\..\..\sshbpp.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit142] FileName=..\..\..\sshchan.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit143] FileName=..\..\..\sshcr.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit144] FileName=..\..\..\sshgss.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit145] FileName=..\..\..\sshgssc.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit146] FileName=..\..\..\sshppl.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit147] FileName=..\..\..\sshserver.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit148] FileName=..\..\..\sshsignals.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit149] FileName=..\..\..\sshttymodes.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit150] FileName=..\..\..\storage.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit151] FileName=..\..\..\terminal.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit152] FileName=..\..\..\tree234.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit153] FileName=..\..\..\unix\unix.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit154] FileName=..\..\..\version.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit155] FileName=..\..\..\windows\rcstuff.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit156] FileName=..\..\..\windows\win_res.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit157] FileName=..\..\..\windows\wincapi.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit158] FileName=..\..\..\windows\winhelp.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit159] FileName=..\..\..\windows\winseat.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit160] FileName=..\..\..\windows\winsecur.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit161] FileName=..\..\..\windows\winstuff.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit162] FileName=..\..\..\windows\putty.ico Folder=Resource Files Compile=0 CompileCpp=0 Link=0 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit163] FileName=..\..\..\windows\putty.rc Folder=Resource Files Compile=1 CompileCpp=1 Link=0 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit164] FileName=..\..\..\windows\puttycfg.ico Folder=Resource Files Compile=0 CompileCpp=0 Link=0 Priority=1000 OverrideBuildCmd=0 BuildCmd= [VersionInfo] Major=0 Minor=0 Release=1 Build=1 LanguageID=1033 CharsetID=1252 CompanyName= FileVersion=0.1 FileDescription= InternalName= LegalCopyright= LegalTrademarks= OriginalFilename=putty.exe ProductName=putty ProductVersion=0.1 AutoIncBuildNr=0 putty-0.76/windows/DEVCPP/puttygen/0000755000175000017500000000000014072266315014174 500000000000000putty-0.76/windows/DEVCPP/puttygen/puttygen.dev0000644000175000017500000003203414072266315016475 00000000000000# DEV-C++ 5 Project File - puttygen.dev # ** DO NOT EDIT ** [Project] FileName=puttygen.dev Name=puttygen Ver=1 IsCpp=1 Type=0 Compiler=-W -D__GNUWIN32__ -DWIN32 -DNDEBUG -D_WINDOWS -DNO_MULTIMON -D_MBCS_@@_ CppCompiler=-W -D__GNUWIN32__ -DWIN32 -DNDEBUG -D_WINDOWS -DNO_MULTIMON -D_MBCS_@@_ Includes=..\..\..\.;..\..\..\charset;..\..\..\windows;..\..\..\unix Linker=-ladvapi32 -lcomctl32 -lcomdlg32 -lgdi32 -limm32 -lshell32 -luser32 -lwinmm -lwinspool_@@_ Libs= UnitCount=85 Folders="Header Files","Resource Files","Source Files" ObjFiles= PrivateResource=puttygen_private.rc ResourceIncludes=..\..\..\WINDOWS MakeIncludes= Icon= ExeOutput= ObjectOutput= OverrideOutput=0 OverrideOutputName=puttygen.exe HostApplication= CommandLine= UseCustomMakefile=0 CustomMakefile= IncludeVersionInfo=0 SupportXPThemes=0 CompilerSet=0 CompilerSettings=0000000000000000000000 [Unit1] FileName=..\..\..\conf.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit2] FileName=..\..\..\ecc.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit3] FileName=..\..\..\import.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit4] FileName=..\..\..\marshal.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit5] FileName=..\..\..\memory.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit6] FileName=..\..\..\millerrabin.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit7] FileName=..\..\..\misc.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit8] FileName=..\..\..\mpint.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit9] FileName=..\..\..\mpunsafe.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit10] FileName=..\..\..\notiming.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit11] FileName=..\..\..\pockle.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit12] FileName=..\..\..\primecandidate.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit13] FileName=..\..\..\smallprimes.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit14] FileName=..\..\..\sshaes.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit15] FileName=..\..\..\sshargon2.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit16] FileName=..\..\..\sshauxcrypt.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit17] FileName=..\..\..\sshbcrypt.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit18] FileName=..\..\..\sshblake2.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit19] FileName=..\..\..\sshblowf.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit20] FileName=..\..\..\sshdes.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit21] FileName=..\..\..\sshdss.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit22] FileName=..\..\..\sshdssg.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit23] FileName=..\..\..\sshecc.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit24] FileName=..\..\..\sshecdsag.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit25] FileName=..\..\..\sshhmac.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit26] FileName=..\..\..\sshmd5.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit27] FileName=..\..\..\sshprime.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit28] FileName=..\..\..\sshprng.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit29] FileName=..\..\..\sshpubk.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit30] FileName=..\..\..\sshrand.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit31] FileName=..\..\..\sshrsa.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit32] FileName=..\..\..\sshrsag.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit33] FileName=..\..\..\sshsh256.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit34] FileName=..\..\..\sshsh512.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit35] FileName=..\..\..\sshsha.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit36] FileName=..\..\..\sshsha3.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit37] FileName=..\..\..\stripctrl.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit38] FileName=..\..\..\tree234.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit39] FileName=..\..\..\utils.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit40] FileName=..\..\..\version.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit41] FileName=..\..\..\wcwidth.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit42] FileName=..\..\..\windows\winctrls.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit43] FileName=..\..\..\windows\winhelp.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit44] FileName=..\..\..\windows\winmisc.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit45] FileName=..\..\..\windows\winmiscs.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit46] FileName=..\..\..\windows\winnoise.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit47] FileName=..\..\..\windows\winnojmp.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit48] FileName=..\..\..\windows\winpgen.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit49] FileName=..\..\..\windows\winsecur.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit50] FileName=..\..\..\windows\winstore.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit51] FileName=..\..\..\windows\wintime.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit52] FileName=..\..\..\windows\winutils.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit53] FileName=..\..\..\charset\charset.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit54] FileName=..\..\..\defs.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit55] FileName=..\..\..\dialog.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit56] FileName=..\..\..\ecc.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit57] FileName=..\..\..\empty.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit58] FileName=..\..\..\licence.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit59] FileName=..\..\..\marshal.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit60] FileName=..\..\..\misc.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit61] FileName=..\..\..\mpint.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit62] FileName=..\..\..\mpint_i.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit63] FileName=..\..\..\mpunsafe.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit64] FileName=..\..\..\network.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit65] FileName=..\..\..\putty.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit66] FileName=..\..\..\puttymem.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit67] FileName=..\..\..\puttyps.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit68] FileName=..\..\..\ssh.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit69] FileName=..\..\..\sshblowf.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit70] FileName=..\..\..\sshkeygen.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit71] FileName=..\..\..\sshsignals.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit72] FileName=..\..\..\sshttymodes.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit73] FileName=..\..\..\storage.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit74] FileName=..\..\..\terminal.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit75] FileName=..\..\..\tree234.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit76] FileName=..\..\..\unix\unix.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit77] FileName=..\..\..\version.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit78] FileName=..\..\..\windows\puttygen-rc.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit79] FileName=..\..\..\windows\rcstuff.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit80] FileName=..\..\..\windows\win_res.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit81] FileName=..\..\..\windows\winhelp.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit82] FileName=..\..\..\windows\winsecur.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit83] FileName=..\..\..\windows\winstuff.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit84] FileName=..\..\..\windows\puttygen.ico Folder=Resource Files Compile=0 CompileCpp=0 Link=0 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit85] FileName=..\..\..\windows\puttygen.rc Folder=Resource Files Compile=1 CompileCpp=1 Link=0 Priority=1000 OverrideBuildCmd=0 BuildCmd= [VersionInfo] Major=0 Minor=0 Release=1 Build=1 LanguageID=1033 CharsetID=1252 CompanyName= FileVersion=0.1 FileDescription= InternalName= LegalCopyright= LegalTrademarks= OriginalFilename=puttygen.exe ProductName=puttygen ProductVersion=0.1 AutoIncBuildNr=0 putty-0.76/windows/DEVCPP/puttytel/0000755000175000017500000000000014072266315014207 500000000000000putty-0.76/windows/DEVCPP/puttytel/puttytel.dev0000644000175000017500000003277714072266315016541 00000000000000# DEV-C++ 5 Project File - puttytel.dev # ** DO NOT EDIT ** [Project] FileName=puttytel.dev Name=puttytel Ver=1 IsCpp=1 Type=0 Compiler=-W -D__GNUWIN32__ -DWIN32 -DNDEBUG -D_WINDOWS -DNO_MULTIMON -D_MBCS_@@_ CppCompiler=-W -D__GNUWIN32__ -DWIN32 -DNDEBUG -D_WINDOWS -DNO_MULTIMON -D_MBCS_@@_ Includes=..\..\..\.;..\..\..\charset;..\..\..\windows;..\..\..\unix Linker=-ladvapi32 -lcomctl32 -lcomdlg32 -lgdi32 -limm32 -lshell32 -luser32 -lwinmm -lwinspool_@@_ Libs= UnitCount=88 Folders="Header Files","Resource Files","Source Files" ObjFiles= PrivateResource=puttytel_private.rc ResourceIncludes=..\..\..\WINDOWS MakeIncludes= Icon= ExeOutput= ObjectOutput= OverrideOutput=0 OverrideOutputName=puttytel.exe HostApplication= CommandLine= UseCustomMakefile=0 CustomMakefile= IncludeVersionInfo=0 SupportXPThemes=0 CompilerSet=0 CompilerSettings=0000000000000000000000 [Unit1] FileName=..\..\..\be_misc.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit2] FileName=..\..\..\be_nos_s.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit3] FileName=..\..\..\callback.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit4] FileName=..\..\..\cmdline.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit5] FileName=..\..\..\conf.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit6] FileName=..\..\..\config.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit7] FileName=..\..\..\dialog.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit8] FileName=..\..\..\errsock.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit9] FileName=..\..\..\ldisc.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit10] FileName=..\..\..\logging.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit11] FileName=..\..\..\marshal.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit12] FileName=..\..\..\memory.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit13] FileName=..\..\..\minibidi.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit14] FileName=..\..\..\misc.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit15] FileName=..\..\..\miscucs.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit16] FileName=..\..\..\nocproxy.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit17] FileName=..\..\..\nogss.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit18] FileName=..\..\..\norand.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit19] FileName=..\..\..\pinger.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit20] FileName=..\..\..\proxy.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit21] FileName=..\..\..\raw.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit22] FileName=..\..\..\rlogin.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit23] FileName=..\..\..\sessprep.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit24] FileName=..\..\..\settings.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit25] FileName=..\..\..\stripctrl.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit26] FileName=..\..\..\supdup.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit27] FileName=..\..\..\telnet.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit28] FileName=..\..\..\terminal.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit29] FileName=..\..\..\timing.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit30] FileName=..\..\..\tree234.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit31] FileName=..\..\..\utils.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit32] FileName=..\..\..\version.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit33] FileName=..\..\..\wcwidth.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit34] FileName=..\..\..\windows\sizetip.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit35] FileName=..\..\..\windows\wincfg.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit36] FileName=..\..\..\windows\winctrls.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit37] FileName=..\..\..\windows\windefs.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit38] FileName=..\..\..\windows\windlg.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit39] FileName=..\..\..\windows\window.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit40] FileName=..\..\..\windows\winhandl.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit41] FileName=..\..\..\windows\winhelp.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit42] FileName=..\..\..\windows\winhsock.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit43] FileName=..\..\..\windows\winjump.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit44] FileName=..\..\..\windows\winmisc.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit45] FileName=..\..\..\windows\winmiscs.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit46] FileName=..\..\..\windows\winnet.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit47] FileName=..\..\..\windows\winprint.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit48] FileName=..\..\..\windows\winproxy.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit49] FileName=..\..\..\windows\winsecur.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit50] FileName=..\..\..\windows\winselgui.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit51] FileName=..\..\..\windows\winser.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit52] FileName=..\..\..\windows\winstore.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit53] FileName=..\..\..\windows\wintime.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit54] FileName=..\..\..\windows\winucs.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit55] FileName=..\..\..\windows\winutils.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit56] FileName=..\..\..\charset\charset.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit57] FileName=..\..\..\defs.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit58] FileName=..\..\..\dialog.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit59] FileName=..\..\..\empty.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit60] FileName=..\..\..\ldisc.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit61] FileName=..\..\..\licence.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit62] FileName=..\..\..\marshal.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit63] FileName=..\..\..\misc.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit64] FileName=..\..\..\network.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit65] FileName=..\..\..\pgssapi.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit66] FileName=..\..\..\proxy.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit67] FileName=..\..\..\putty.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit68] FileName=..\..\..\puttymem.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit69] FileName=..\..\..\puttyps.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit70] FileName=..\..\..\ssh.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit71] FileName=..\..\..\sshgss.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit72] FileName=..\..\..\sshgssc.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit73] FileName=..\..\..\sshsignals.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit74] FileName=..\..\..\sshttymodes.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit75] FileName=..\..\..\storage.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit76] FileName=..\..\..\terminal.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit77] FileName=..\..\..\tree234.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit78] FileName=..\..\..\unix\unix.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit79] FileName=..\..\..\version.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit80] FileName=..\..\..\windows\rcstuff.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit81] FileName=..\..\..\windows\win_res.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit82] FileName=..\..\..\windows\winhelp.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit83] FileName=..\..\..\windows\winseat.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit84] FileName=..\..\..\windows\winsecur.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit85] FileName=..\..\..\windows\winstuff.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit86] FileName=..\..\..\windows\putty.ico Folder=Resource Files Compile=0 CompileCpp=0 Link=0 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit87] FileName=..\..\..\windows\puttycfg.ico Folder=Resource Files Compile=0 CompileCpp=0 Link=0 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit88] FileName=..\..\..\windows\puttytel.rc Folder=Resource Files Compile=1 CompileCpp=1 Link=0 Priority=1000 OverrideBuildCmd=0 BuildCmd= [VersionInfo] Major=0 Minor=0 Release=1 Build=1 LanguageID=1033 CharsetID=1252 CompanyName= FileVersion=0.1 FileDescription= InternalName= LegalCopyright= LegalTrademarks= OriginalFilename=puttytel.exe ProductName=puttytel ProductVersion=0.1 AutoIncBuildNr=0 putty-0.76/windows/DEVCPP/testcrypt/0000755000175000017500000000000014072266315014356 500000000000000putty-0.76/windows/DEVCPP/testcrypt/testcrypt.dev0000644000175000017500000002313414072266315017042 00000000000000# DEV-C++ 5 Project File - testcrypt.dev # ** DO NOT EDIT ** [Project] FileName=testcrypt.dev Name=testcrypt Ver=1 IsCpp=1 Type=1 Compiler=-W -D__GNUWIN32__ -DWIN32 -DNDEBUG -D_WINDOWS -DNO_MULTIMON -D_MBCS_@@_ CppCompiler=-W -D__GNUWIN32__ -DWIN32 -DNDEBUG -D_WINDOWS -DNO_MULTIMON -D_MBCS_@@_ Includes=..\..\..\.;..\..\..\charset;..\..\..\windows;..\..\..\unix Linker=-ladvapi32 -lcomctl32 -lcomdlg32 -lgdi32 -limm32 -lshell32 -luser32 -lwinmm -lwinspool_@@_ Libs= UnitCount=61 Folders="Header Files","Resource Files","Source Files" ObjFiles= PrivateResource=testcrypt_private.rc ResourceIncludes=..\..\..\WINDOWS MakeIncludes= Icon= ExeOutput= ObjectOutput= OverrideOutput=0 OverrideOutputName=testcrypt.exe HostApplication= CommandLine= UseCustomMakefile=0 CustomMakefile= IncludeVersionInfo=0 SupportXPThemes=0 CompilerSet=0 CompilerSettings=0000000000000000000000 [Unit1] FileName=..\..\..\ecc.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit2] FileName=..\..\..\marshal.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit3] FileName=..\..\..\memory.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit4] FileName=..\..\..\millerrabin.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit5] FileName=..\..\..\mpint.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit6] FileName=..\..\..\mpunsafe.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit7] FileName=..\..\..\pockle.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit8] FileName=..\..\..\primecandidate.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit9] FileName=..\..\..\smallprimes.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit10] FileName=..\..\..\sshaes.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit11] FileName=..\..\..\ssharcf.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit12] FileName=..\..\..\sshargon2.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit13] FileName=..\..\..\sshauxcrypt.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit14] FileName=..\..\..\sshblake2.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit15] FileName=..\..\..\sshblowf.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit16] FileName=..\..\..\sshccp.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit17] FileName=..\..\..\sshcrc.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit18] FileName=..\..\..\sshcrcda.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit19] FileName=..\..\..\sshdes.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit20] FileName=..\..\..\sshdh.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit21] FileName=..\..\..\sshdss.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit22] FileName=..\..\..\sshdssg.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit23] FileName=..\..\..\sshecc.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit24] FileName=..\..\..\sshecdsag.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit25] FileName=..\..\..\sshhmac.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit26] FileName=..\..\..\sshmd5.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit27] FileName=..\..\..\sshprime.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit28] FileName=..\..\..\sshprng.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit29] FileName=..\..\..\sshpubk.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit30] FileName=..\..\..\sshrsa.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit31] FileName=..\..\..\sshrsag.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit32] FileName=..\..\..\sshsh256.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit33] FileName=..\..\..\sshsh512.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit34] FileName=..\..\..\sshsha.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit35] FileName=..\..\..\sshsha3.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit36] FileName=..\..\..\testcrypt.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit37] FileName=..\..\..\tree234.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit38] FileName=..\..\..\utils.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit39] FileName=..\..\..\windows\winmiscs.c Folder=Source Files Compile=1 CompileCpp=0 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit40] FileName=..\..\..\charset\charset.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit41] FileName=..\..\..\defs.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit42] FileName=..\..\..\ecc.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit43] FileName=..\..\..\marshal.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit44] FileName=..\..\..\misc.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit45] FileName=..\..\..\mpint.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit46] FileName=..\..\..\mpint_i.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit47] FileName=..\..\..\mpunsafe.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit48] FileName=..\..\..\network.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit49] FileName=..\..\..\putty.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit50] FileName=..\..\..\puttymem.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit51] FileName=..\..\..\puttyps.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit52] FileName=..\..\..\ssh.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit53] FileName=..\..\..\sshblowf.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit54] FileName=..\..\..\sshkeygen.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit55] FileName=..\..\..\sshsignals.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit56] FileName=..\..\..\sshttymodes.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit57] FileName=..\..\..\testcrypt.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit58] FileName=..\..\..\tree234.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit59] FileName=..\..\..\unix\unix.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit60] FileName=..\..\..\windows\winhelp.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [Unit61] FileName=..\..\..\windows\winstuff.h Folder=Header Files Compile=1 CompileCpp=1 Link=1 Priority=1000 OverrideBuildCmd=0 BuildCmd= [VersionInfo] Major=0 Minor=0 Release=1 Build=1 LanguageID=1033 CharsetID=1252 CompanyName= FileVersion=0.1 FileDescription= InternalName= LegalCopyright= LegalTrademarks= OriginalFilename=testcrypt.exe ProductName=testcrypt ProductVersion=0.1 AutoIncBuildNr=0